diff --git a/.gitignore b/.gitignore index 47abad82be..8227118ed7 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,8 @@ /config/gpg/*.key /config/license /config/subscription_key.txt +/config/jwt/jwt.key +/config/jwt/jwt.pem # JS Dependencies # ################ @@ -37,12 +39,19 @@ debian/passbolt-api-pro/ debian/*debhelper* debian/*substvars* +# RPM Package # +############### +/_passbolt-configure +/cron.d +/logrotate.d + # CakePHP specific files # ########################## /config/app_local.php /config/.env /logs/* /tmp/* +!/tmp/avatars/empty /vendor/* # IDE and editor specific files # @@ -115,3 +124,6 @@ atlassian-ide-plugin.xml ## File-based project format: *.ipr *.iws + +# Tilt +tilt_modules* \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7c703b15c6..d28870af5c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,10 +1,11 @@ image: alpine:latest + variables: + DOCKER_HOST: tcp://localhost:2375 + DOCKER_DRIVER: overlay2 DOCKER_TLS_CERTDIR: "" - stages: -# - test - unit-test - package-deps - package-build @@ -13,14 +14,13 @@ stages: - publish-artifacts include: -# - template: Code-Quality.gitlab-ci.yml + - template: Code-Quality.gitlab-ci.yml - local: '/.gitlab-ci/Jobs/php_unit_tests.yml' -# - local: '/.gitlab-ci/Jobs/artifact_build.yml' -# - local: '/.gitlab-ci/Jobs/debian_package_build.yml' + - local: '/.gitlab-ci/Jobs/artifact_build.yml' + - local: '/.gitlab-ci/Jobs/debian_package_build.yml' + - local: '/.gitlab-ci/Jobs/packages_tests.yml' - local: '/.gitlab-ci/Jobs/style_check.yml' + - local: '/.gitlab-ci/Jobs/rpm_package_build.yml' -cache: - paths: - - vendor/ - - +code_quality: + stage: unit-test diff --git a/.gitlab-ci/Jobs/debian_package_build.yml b/.gitlab-ci/Jobs/debian_package_build.yml index 13a900bf85..f5485a7873 100644 --- a/.gitlab-ci/Jobs/debian_package_build.yml +++ b/.gitlab-ci/Jobs/debian_package_build.yml @@ -3,7 +3,7 @@ services: .rules: rules: - - if: '$CI_COMMIT_BRANCH =~ /^(feature\/debian\_*|master|release)/ || ($CI_COMMIT_BRANCH == "develop" && $CI_PIPELINE_SOURCE == "schedule")' + - if: '$CI_COMMIT_BRANCH =~ /^(snyk-fix*|feature\/debian\_*|master|release)/ || ($CI_COMMIT_BRANCH == "develop" && $CI_PIPELINE_SOURCE == "schedule")' when: on_success .dependencies: @@ -58,17 +58,6 @@ build-debian: cp ../*.buildinfo . cp ../*.changes . -.kitchen-test: - extends: .rules - image: chef/chefdk:latest - stage: package-test - dependencies: - - composer - - build-debian - script: - - gem install bundler:2.1.2 - - kitchen verify $KITCHEN_JOB -t tests/integration - .publish: image: registry.gitlab.com/passbolt/passbolt-ops/passbolt-aptly stage: publish @@ -117,57 +106,3 @@ ubuntu-stable: - if: '$CI_COMMIT_MESSAGE =~ /publish-package/' when: on_success -debian-fs: - extends: .kitchen-test - dependencies: - - composer - variables: - KITCHEN_JOB: filesystem-benchmarks-debian-buster - -debian-purge: - extends: .kitchen-test - variables: - KITCHEN_JOB: purge-benchmarks-debian-buster - -debian-runtime: - extends: .kitchen-test - variables: - KITCHEN_JOB: runtime-benchmarks-debian-buster - -debian-break: - extends: .kitchen-test - variables: - KITCHEN_JOB: break-benchmarks-debian-buster - -ubuntu-1804-fs: - extends: .kitchen-test - allow_failure: true - variables: - KITCHEN_JOB: filesystem-benchmarks-ubuntu-1804 - -ubuntu-1804-purge: - extends: .kitchen-test - allow_failure: true - variables: - KITCHEN_JOB: purge-benchmarks-ubuntu-1804 - -ubuntu-1804-runtime: - extends: .kitchen-test - allow_failure: true - variables: - KITCHEN_JOB: runtime-benchmarks-ubuntu-1804 - -ubuntu-2004-fs: - extends: .kitchen-test - variables: - KITCHEN_JOB: filesystem-benchmarks-ubuntu-2004 - -ubuntu-2004-purge: - extends: .kitchen-test - variables: - KITCHEN_JOB: purge-benchmarks-ubuntu-2004 - -ubuntu-2004-runtime: - extends: .kitchen-test - variables: - KITCHEN_JOB: runtime-benchmarks-ubuntu-2004 diff --git a/.gitlab-ci/Jobs/packages_tests.yml b/.gitlab-ci/Jobs/packages_tests.yml new file mode 100644 index 0000000000..25103165e5 --- /dev/null +++ b/.gitlab-ci/Jobs/packages_tests.yml @@ -0,0 +1,175 @@ +.kitchen-test: + extends: .rules + image: chef/chefdk:latest + stage: package-test + script: + - gem install bundler:2.1.2 + - kitchen verify $KITCHEN_JOB -t tests/integration + +# Debian/Ubuntu package testing +# +.kitchen-test-debian: + extends: .kitchen-test + dependencies: + - composer + - build-debian + script: + - ls -las + - gem install bundler:2.1.2 + - kitchen verify $KITCHEN_JOB -t tests/integration + +debian-buster-fs: + extends: .kitchen-test-debian + dependencies: + - composer + variables: + KITCHEN_JOB: filesystem-benchmarks-debian-buster + +debian-buster-purge: + extends: .kitchen-test-debian + variables: + KITCHEN_JOB: purge-benchmarks-debian-buster + +debian-buster-runtime: + extends: .kitchen-test-debian + variables: + KITCHEN_JOB: runtime-benchmarks-debian-buster + +debian-buster-break: + extends: .kitchen-test-debian + variables: + KITCHEN_JOB: break-benchmarks-debian-buster + +debian-bullseye-fs: + extends: .kitchen-test-debian + dependencies: + - composer + variables: + KITCHEN_JOB: filesystem-benchmarks-debian-bullseye + +debian-bullseye-purge: + extends: .kitchen-test-debian + variables: + KITCHEN_JOB: purge-benchmarks-debian-bullseye + +debian-bullseye-runtime: + extends: .kitchen-test-debian + variables: + KITCHEN_JOB: runtime-benchmarks-debian-bullseye + +debian-bullseye-break: + extends: .kitchen-test-debian + variables: + KITCHEN_JOB: break-benchmarks-debian-bullseye + +ubuntu-1804-fs: + extends: .kitchen-test-debian + allow_failure: true + variables: + KITCHEN_JOB: filesystem-benchmarks-ubuntu-1804 + +ubuntu-1804-purge: + extends: .kitchen-test-debian + allow_failure: true + variables: + KITCHEN_JOB: purge-benchmarks-ubuntu-1804 + +ubuntu-1804-runtime: + extends: .kitchen-test-debian + allow_failure: true + variables: + KITCHEN_JOB: runtime-benchmarks-ubuntu-1804 + +ubuntu-2004-fs: + extends: .kitchen-test-debian + variables: + KITCHEN_JOB: filesystem-benchmarks-ubuntu-2004 + +ubuntu-2004-purge: + extends: .kitchen-test-debian + variables: + KITCHEN_JOB: purge-benchmarks-ubuntu-2004 + +ubuntu-2004-runtime: + extends: .kitchen-test-debian + variables: + KITCHEN_JOB: runtime-benchmarks-ubuntu-2004 + +# Yum package testing + +.kitchen-test-yum: + extends: .kitchen-test + needs: + - job: composer + artifacts: true + - job: build-yum + artifacts: true + +.kitchen-test-yum-7: + extends: .kitchen-test + needs: + - job: composer + artifacts: true + - job: build-yum-7 + artifacts: true + +centos-7-fs: + extends: .kitchen-test-yum-7 + variables: + KITCHEN_JOB: filesystem-benchmarks-centos-7 + +centos-7-purge: + extends: .kitchen-test-yum-7 + variables: + KITCHEN_JOB: purge-benchmarks-centos-7 + +centos-7-runtime: + extends: .kitchen-test-yum-7 + variables: + KITCHEN_JOB: runtime-benchmarks-centos-7 + +centos-7-break: + extends: .kitchen-test-yum-7 + allow_failure: true + variables: + KITCHEN_JOB: break-benchmarks-centos-7 + +centos-8-fs: + extends: .kitchen-test-yum + variables: + KITCHEN_JOB: filesystem-benchmarks-centos-8 + +centos-8-purge: + extends: .kitchen-test-yum + variables: + KITCHEN_JOB: purge-benchmarks-centos-8 + +centos-8-runtime: + extends: .kitchen-test-yum + variables: + KITCHEN_JOB: runtime-benchmarks-centos-8 + +centos-8-break: + extends: .kitchen-test-yum + variables: + KITCHEN_JOB: break-benchmarks-centos-8 + +rockylinux-8-fs: + extends: .kitchen-test-yum + variables: + KITCHEN_JOB: filesystem-benchmarks-rockylinux-8 + +rockylinux-8-purge: + extends: .kitchen-test-yum + variables: + KITCHEN_JOB: purge-benchmarks-rockylinux-8 + +rockylinux-8-runtime: + extends: .kitchen-test-yum + variables: + KITCHEN_JOB: runtime-benchmarks-rockylinux-8 + +rockylinux-8-break: + extends: .kitchen-test-yum + variables: + KITCHEN_JOB: break-benchmarks-rockylinux-8 diff --git a/.gitlab-ci/Jobs/php_unit_tests.yml b/.gitlab-ci/Jobs/php_unit_tests.yml index 1cd824981a..14f58a30ff 100644 --- a/.gitlab-ci/Jobs/php_unit_tests.yml +++ b/.gitlab-ci/Jobs/php_unit_tests.yml @@ -1,159 +1,88 @@ -.php_common_prepare: &php_prepare - - apt-get update -yqq - - apt-get install mariadb-client -yqq - - apt-get install wget git unzip -yqq - - apt-get install libgpgme11 libgpgme-dev -yqq - - pecl install gnupg - - apt-get install zlib1g-dev libicu-dev -yqq - - docker-php-ext-install intl - - docker-php-ext-enable gnupg - - docker-php-ext-enable intl - - apt-get install libjpeg-dev libpng-dev libfreetype6-dev -yqq - - |- - if [[ $PHP_VERSION = "7.4" ]]; then - docker-php-ext-configure gd --with-freetype --with-jpeg - else - docker-php-ext-configure gd --with-freetype-dir=/usr --with-jpeg-dir=/usr - fi - - docker-php-ext-install gd - - docker-php-ext-enable gd - - wget https://composer.github.io/installer.sig -O - -q | tr -d '\n' > installer.sig - - php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" - - php -r "if (hash_file('SHA384', 'composer-setup.php') === file_get_contents('installer.sig')) { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" - - php composer-setup.php - - php -r "unlink('composer-setup.php'); unlink('installer.sig');" - - cp config/app.default.php config/app.php - - php composer.phar install --dev --no-interaction -.custom_functions: &custom_functions | - - function init_test_db() { - mysql -h $DATASOURCES_DEFAULT_HOST -u root -p$MYSQL_ROOT_PASSWORD -e "ALTER USER '$DATASOURCES_TEST_USERNAME'@'%' identified by '$DATASOURCES_TEST_PASSWORD';" mysql - mysql -h $DATASOURCES_DEFAULT_HOST -u root -p$MYSQL_ROOT_PASSWORD -e "GRANT ALL ON *.* to $DATASOURCES_TEST_USERNAME;" mysql - } -.php_mysql_prepare: &php_mysql_prepare - - docker-php-ext-install pdo_mysql - - docker-php-ext-enable pdo_mysql -.php_postgres_prepare: &php_postgres_prepare - # Create the file repository configuration: - - sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt buster-pgdg main" > /etc/apt/sources.list.d/pgdg.list' - -# Import the repository signing key: - - wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | tee ~/apt-pg.key - - apt-key add ~/apt-pg.key - -# Update the package lists: - - apt-get update +.mysql-template: + services: + - name: $DATABASE_ENGINE_VERSION + command: + - /bin/bash + - -c + - | + mount -t tmpfs -o size=1024m tmpfs /var/lib/mysql + docker-entrypoint.sh mysqld --default-authentication-plugin=mysql_native_password --log-bin-trust-function-creators=1 + # MySQL services variables + variables: + MYSQL_USER: user + MYSQL_PASSWORD: testing-password + MYSQL_ROOT_PASSWORD: testing-password2 + MYSQL_DATABASE: test + DATASOURCES_DEFAULT_DATABASE: non_existing_database # to ensure that all tests run on the test datasource + DATASOURCES_DEFAULT_USERNAME: $MYSQL_USER + DATASOURCES_DEFAULT_PASSWORD: $MYSQL_PASSWORD + DATASOURCES_DEFAULT_HOST: 127.0.0.1 + DATASOURCES_TEST_DATABASE: $MYSQL_DATABASE + DATASOURCES_TEST_USERNAME: $MYSQL_USER + DATASOURCES_TEST_PASSWORD: $MYSQL_PASSWORD + DATASOURCES_TEST_HOST: 127.0.0.1 + before_script: + - mysql -h $DATASOURCES_DEFAULT_HOST -u root -p$MYSQL_ROOT_PASSWORD -e "ALTER USER '$DATASOURCES_TEST_USERNAME'@'%' identified by '$DATASOURCES_TEST_PASSWORD';" mysql + - mysql -h $DATASOURCES_DEFAULT_HOST -u root -p$MYSQL_ROOT_PASSWORD -e "GRANT ALL ON *.* to $DATASOURCES_TEST_USERNAME;" mysql -# Install the latest version of PostgreSQL. -# If you want a specific version, use 'postgresql-12' or similar instead of 'postgresql': - - apt-get install postgresql-client-12 -yqq - - apt-get install libpq-dev -yqq - - docker-php-ext-install pdo_pgsql - - docker-php-ext-enable pdo_pgsql - - apt-get install postgresql-client-12 -yqq .postgres-template: - stage: unit-test + services: + - postgres:12.2-alpine + # Postgres services variables variables: - POSTGRES_USER: user - POSTGRES_DATABASE: test POSTGRES_DB: test - POSTGRES_PASSWORD: "testing-password" + POSTGRES_USER: user + POSTGRES_PASSWORD: testing-password POSTGRES_HOST_AUTH_METHOD: trust - # Passbolt unit test job common variables - DEBUG: "true" - DATASOURCES_DEFAULT_DATABASE: $POSTGRES_DATABASE + DATASOURCES_DEFAULT_DATABASE: $POSTGRES_DB DATASOURCES_DEFAULT_USERNAME: $POSTGRES_USER DATASOURCES_DEFAULT_PASSWORD: $POSTGRES_PASSWORD DATASOURCES_DEFAULT_HOST: postgres - DATASOURCES_TEST_DATABASE: $POSTGRES_DATABASE + DATASOURCES_TEST_DATABASE: $POSTGRES_DB DATASOURCES_TEST_USERNAME: $POSTGRES_USER DATASOURCES_TEST_PASSWORD: $POSTGRES_PASSWORD DATASOURCES_TEST_HOST: postgres - PASSBOLT_GPG_SERVER_KEY_PUBLIC: config/gpg/unsecure.key - PASSBOLT_GPG_SERVER_KEY_PRIVATE: config/gpg/unsecure_private.key - APP_FULL_BASE_URL: http://127.0.0.1 - PASSBOLT_REGISTRATION_PUBLIC: 1 - PASSBOLT_SELENIUM_ACTIVE: 1 - # This is the development dummy fingerprint - PASSBOLT_GPG_SERVER_KEY_FINGERPRINT: 2FC8945833C51946E937F9FED47B0811573EE67E - PHPUNIT_COMMAND: vendor/bin/phpunit --log-junit unitreport.xml - image: php:$PHP_VERSION - services: - - name: $DATABASE_ENGINE_VERSION - alias: postgres - script: - - *php_prepare - - *php_postgres_prepare - - export DATASOURCES_DRIVER="Cake\Database\Driver\Postgres" - - export DATASOURCES_TEST_DRIVER="Cake\Database\Driver\Postgres" - - export DATASOURCES_DEFAULT_DRIVER="Cake\Database\Driver\Postgres" - - export DATASOURCES_PORT=5432 - - export DATASOURCES_DEFAULT_PORT=5432 - - export DATASOURCES_DEFAULT_ENCODING='utf8' - - export DATASOURCES_TEST_ENCODING='utf8' - - export DATASOURCES_TEST_PORT=5432 - - gpg --import config/gpg/unsecure_private.key - - gpg --import config/gpg/unsecure.key + DATASOURCES_DRIVER: Cake\Database\Driver\Postgres + DATASOURCES_TEST_DRIVER: Cake\Database\Driver\Postgres + DATASOURCES_DEFAULT_DRIVER: Cake\Database\Driver\Postgres + DATASOURCES_PORT: 5432 + DATASOURCES_DEFAULT_PORT: 5432 + DATASOURCES_DEFAULT_ENCODING: 'utf8' + DATASOURCES_TEST_ENCODING: 'utf8' + DATASOURCES_TEST_PORT: 5432 + before_script: + - apt-get install wget git unzip -yqq + - sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt buster-pgdg main" > /etc/apt/sources.list.d/pgdg.list' + - wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | tee ~/apt-pg.key + - apt-key add ~/apt-pg.key + - apt-get update + - apt-get install libpq-dev postgresql-client-12 -yqq + - docker-php-ext-install pdo_pgsql + - docker-php-ext-enable pdo_pgsql - printf "postgres:5432:test:user:testing-password" > ~/.pgpass - chmod 0600 ~/.pgpass - - $PHPUNIT_COMMAND - only: - - branches - artifacts: - reports: - junit: unitreport.xml - except: - variables: - - $CI_COMMIT_MESSAGE =~ /skip-unit/ + .test-template: stage: unit-test variables: - # MySQL services variables - MYSQL_USER: user - MYSQL_PASSWORD: testing-password - MYSQL_ROOT_PASSWORD: testing-password2 - MYSQL_DATABASE: test - MYSQL_HOST: mysql - POSTGRES_USER: user - POSTGRES_DB: test - POSTGRES_PASSWORD: "testing-password" - POSTGRES_HOST_AUTH_METHOD: md5 - # Passbolt unit test job common variables DEBUG: "true" - DATASOURCES_DEFAULT_DATABASE: non_existing_database # to ensure that all tests run on the test datasource - DATASOURCES_DEFAULT_USERNAME: $MYSQL_USER - DATASOURCES_DEFAULT_PASSWORD: $MYSQL_PASSWORD - DATASOURCES_DEFAULT_HOST: mysql - DATASOURCES_TEST_DATABASE: $MYSQL_DATABASE - DATASOURCES_TEST_USERNAME: $MYSQL_USER - DATASOURCES_TEST_PASSWORD: $MYSQL_PASSWORD - DATASOURCES_TEST_HOST: mysql PASSBOLT_GPG_SERVER_KEY_PUBLIC: config/gpg/unsecure.key PASSBOLT_GPG_SERVER_KEY_PRIVATE: config/gpg/unsecure_private.key APP_FULL_BASE_URL: http://127.0.0.1 PASSBOLT_REGISTRATION_PUBLIC: 1 PASSBOLT_SELENIUM_ACTIVE: 1 + PASSBOLT_PLUGINS_JWT_AUTHENTICATION_ENABLED: 1 # Remove that line once JWT is enabled by default # This is the development dummy fingerprint PASSBOLT_GPG_SERVER_KEY_FINGERPRINT: 2FC8945833C51946E937F9FED47B0811573EE67E PHPUNIT_COMMAND: vendor/bin/phpunit --log-junit unitreport.xml - image: php:$PHP_VERSION - services: - - name: $DATABASE_ENGINE_VERSION - command: #needed for mysql8 --log-bin-trust-function-creators=1 - - /bin/bash - - -c - - | - mount -t tmpfs -o size=1024m tmpfs /var/lib/mysql - docker-entrypoint.sh mysqld --default-authentication-plugin=mysql_native_password --log-bin-trust-function-creators=1 - alias: mysql + image: $CI_REGISTRY_IMAGE_TEST:$PHP_VERSION script: - - *custom_functions - - *php_prepare - - *php_mysql_prepare - #- export DATASOURCES_TEST_HOST=mysql + - cp config/app.default.php config/app.php + - composer install --dev --no-interaction - gpg --import config/gpg/unsecure_private.key - gpg --import config/gpg/unsecure.key + - ./bin/cake passbolt create_jwt_keys -q - $PHPUNIT_COMMAND only: - branches @@ -169,7 +98,9 @@ php7.4-mariadb10.5: variables: PHP_VERSION: "7.4" DATABASE_ENGINE_VERSION: "mariadb:10.5" - extends: .test-template + extends: + - .mysql-template + - .test-template except: variables: - $TEST_DISABLED @@ -181,7 +112,9 @@ php7.4-mariadb10.4: variables: PHP_VERSION: "7.4" DATABASE_ENGINE_VERSION: "mariadb:10.4" - extends: .test-template + extends: + - .mysql-template + - .test-template except: variables: - $TEST_DISABLED @@ -193,7 +126,9 @@ php7.3-mariadb10.4: variables: PHP_VERSION: "7.3" DATABASE_ENGINE_VERSION: "mariadb:10.4" - extends: .test-template + extends: + - .mysql-template + - .test-template except: variables: - $TEST_DISABLED @@ -205,19 +140,25 @@ php7.4-mariadb10.3: variables: PHP_VERSION: "7.4" DATABASE_ENGINE_VERSION: "mariadb:10.3" - extends: .test-template + extends: + - .mysql-template + - .test-template php7.3-mysql5.7: variables: PHP_VERSION: "7.3" DATABASE_ENGINE_VERSION: "mysql:5.7" - extends: .test-template + extends: + - .mysql-template + - .test-template php7.4-mysql8: variables: PHP_VERSION: "7.4" DATABASE_ENGINE_VERSION: "mysql:8.0" - extends: .test-template + extends: + - .mysql-template + - .test-template except: variables: - $TEST_DISABLED @@ -228,5 +169,6 @@ php7.4-mysql8: php7.4-postgres: variables: PHP_VERSION: "7.4" - DATABASE_ENGINE_VERSION: "postgres:12.2-alpine" - extends: .postgres-template + extends: + - .postgres-template + - .test-template diff --git a/.gitlab-ci/Jobs/rpm_package_build.yml b/.gitlab-ci/Jobs/rpm_package_build.yml new file mode 100644 index 0000000000..67bab9240f --- /dev/null +++ b/.gitlab-ci/Jobs/rpm_package_build.yml @@ -0,0 +1,146 @@ +.gpg-functions: &gpg-functions | + kms_decrypt() { + payload="$1" + echo "$payload" | base64 -d | \ + google-cloud-sdk/bin/gcloud kms decrypt \ + --project "$CRYPTOKEYS_PROJECT" \ + --location "europe-west1" \ + --keyring "$CRYPTOKEYS_KEYRING" \ + --key "$KMS_KEY_NAME" \ + --plaintext-file - \ + --ciphertext-file - + } + +.install-jq-rhel-8: &install-jq-rhel-8 + yum install -y jq ${DEPENDENCIES} + +.install-jq-rhel-7: &install-jq-rhel-7 + - yum install -y epel-release + - yum install -y jq ${DEPENDENCIES} + +.build-passbolt-rpm-package: &build-passbolt-rpm-package | + rpmdev-setuptree + PKG_VERSION=$(jq -r '.version' package.json) \ + PASSBOLT_VERSION=$(jq -r '.version' package.json) \ + /bin/sh rpm/scripts/build-passbolt-server.sh + PKG_VERSION=0.1 /bin/sh rpm/scripts/build-passbolt-selinux.sh + cp ~/rpmbuild/RPMS/noarch/passbolt-* . + +.build-rpm: + extends: .rules + image: rockylinux/rockylinux:8 + stage: package-build + dependencies: + - composer + artifacts: + paths: + - '*.rpm' + expire_in: 1 week + when: on_success + +build-yum: + extends: .build-rpm + variables: + DEPENDENCIES: "rpmdevtools rpmlint rsync selinux-policy-devel rpm-build bc createrepo firewalld" + script: + - *install-jq-rhel-8 + - *build-passbolt-rpm-package + +build-yum-7: + extends: .build-rpm + image: centos:7 + variables: + DEPENDENCIES: "rpmdevtools rpmlint rsync selinux-policy-devel rpm-build bc createrepo firewalld" + script: + - *install-jq-rhel-7 + - *build-passbolt-rpm-package + +# gcloud sdk https://cloud.google.com/sdk/docs/downloads-versioned-archives +.clone-repo-bucket: &clone-repo-bucket | + yum install python39 wget -y + wget https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/$GCLOUD_PACKAGE_VERSION + echo "$GCLOUD_SHA256SUM $GCLOUD_PACKAGE_VERSION" | sha256sum -c - + tar xvf $GCLOUD_PACKAGE_VERSION + ./google-cloud-sdk/install.sh --quiet + google-cloud-sdk/bin/gcloud auth activate-service-account --key-file "$PACKAGE_SVC_ACC" + google-cloud-sdk/bin/gsutil -m cp -r "gs://$REPO" . + +.gpg-setup: &gpg-setup + - *gpg-functions + - yum install gpg -y && mkdir ~/.gnupg + - echo "allow-preset-passphrase" >> ~/.gnupg/gpg-agent.conf + - gpg-connect-agent reloadagent /bye + - gpg --batch --import <(kms_decrypt "$GPG_KEY" | base64 -d) + - /usr/libexec/gpg-preset-passphrase --passphrase "$(kms_decrypt $GPG_PASS | base64 -d)" --preset $GPG_KEYGRIP + - echo "%_signature gpg" > ~/.rpmmacros + - echo "%_gpg_name $GPG_KEY_ID" >> ~/.rpmmacros + +.publish-script: &publish-script | + yum install -y createrepo rpm-sign + mkdir -p $COMPONENT/$PACKAGE_ARCH + [ $COMPONENT == "testing" ] && rm $COMPONENT/$PACKAGE_ARCH/*rpm + cp passbolt-*.rpm $COMPONENT/$PACKAGE_ARCH + rpm --resign $COMPONENT/$PACKAGE_ARCH/*rpm + createrepo --update $COMPONENT + google-cloud-sdk/bin/gsutil -m rsync -r -d $COMPONENT "gs://$REPO" + google-cloud-sdk/bin/gsutil -m setmeta -r -h "Content-Type:text/html" \ + -h "Cache-Control:no-cache" \ + "gs://$REPO" + +.publish-rpm: + image: rockylinux/rockylinux:8 + stage: publish + variables: + GCLOUD_SHA256SUM: "81d0ad64dca3e97d02e873d386bafcc77b416d7c9b45e5ec2387e5075b133fc0" + GCLOUD_PACKAGE_VERSION: "google-cloud-sdk-365.0.0-linux-x86_64.tar.gz" + COMPONENT: "testing" + BUCKET_NAME: "download.passbolt.com" + EL_VERSION: "el8" + REPO_PATH: "$PASSBOLT_FLAVOUR/rpm/$EL_VERSION" + REPO: "$BUCKET_NAME/$REPO_PATH/$COMPONENT" + PACKAGE_ARCH: "noarch" + before_script: + - *clone-repo-bucket + - *gpg-setup + script: + - *publish-script + +publish-passbolt-yum-el8-testing: + extends: .publish-rpm + dependencies: + - build-yum + rules: + - if: '$CI_PIPELINE_SOURCE == "schedule"' + when: on_success + +publish-passbolt-yum-el7-testing: + extends: .publish-rpm + variables: + EL_VERSION: "el7" + COMPONENT: "testing" + dependencies: + - build-yum-7 + rules: + - if: '$CI_PIPELINE_SOURCE == "schedule"' + when: on_success + +publish-passbolt-yum-el8-stable: + extends: .publish-rpm + variables: + COMPONENT: "stable" + dependencies: + - build-yum + rules: + - if: '$CI_COMMIT_MESSAGE =~ /publish-package/' + when: on_success + +publish-passbolt-yum-el7-stable: + extends: .publish-rpm + variables: + EL_VERSION: "el7" + COMPONENT: "stable" + dependencies: + - build-yum-7 + rules: + - if: '$CI_COMMIT_MESSAGE =~ /publish-package/' + when: on_success diff --git a/.gitlab-ci/Jobs/style_check.yml b/.gitlab-ci/Jobs/style_check.yml index 88361a142a..558edd696c 100644 --- a/.gitlab-ci/Jobs/style_check.yml +++ b/.gitlab-ci/Jobs/style_check.yml @@ -2,12 +2,14 @@ stage: unit-test variables: PHP_VERSION: "7.3" - image: php:$PHP_VERSION + image: $CI_REGISTRY_IMAGE_TEST:$PHP_VERSION allow_failure: true script: + - composer config --global process-timeout 2000 - composer install --dev --no-interaction - composer cs-check - composer stan + - composer psalm check-style-strict: extends: .cs-check diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb2..0d25fb5884 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1,950 @@ +# Change Log +All notable changes to this project will be documented in this file. +This project adheres to [Semantic Versioning](http://semver.org/). + +## [3.4.0] - 2021-12-07 +### Added +- PB-9826 As a user I want to use passbolt natively on Edge +- PB-8371 As LU I want to see the login/MFA/recover/register screens in dark mode + +### Improvement +- PB-8522 As LU I should see the MFA verify field having focus +- PB-9730 As AD I should be able to check avatars read issues from the healthcheck + +### Fix +- PB-8932 Fix as LU I should see an animation when I successfully configured MFA +- PB-9286 Fix as LU I should see the locale dropdown field of the setup/recover screen well positioned +- PB-9397 Fix as AD I shouldn't see an error on the healthcheck if the JWT auth is disabled and I never configured it +- PB-9114 Fix as lu I should be able to upload a transparent avatar in .png format. +- PB-9750 Fix spelling mistakes reported by the community +- PB-9762 Fix requesting /auth/login.json should not trigger an unexpected error +- PB-9888 Fix MFA & JWT refresh token issue, remove Bearer from the hashed session identifier +- PB-12817 Fix as LU I should be able to update jpeg avatar + +### Security +- PB-7374 As soft deleted but logged in user I should be forbidden to request the API +- PB-9340 Fix email queue data should be stored and deserialized as json and not php + +### Maintenance +- PB-9311 Refactor JWT and MFA plugins for better code maintainability. +- PB-8320 Implement the tests that are marked as incomplete for cleaner continuous integration test reports +- PB-8211 Psalm set to level 4 +- PB-9726 Fix do not load cleanup tasks unless in CLI mode +- PB-9753 Improve table fields validation tests, do not save entity when testing the validation of properties +- PB-9310 Move avatar file_storage logic into AvatarsTable +- PB-9785 Update JWT healthcheck help messages +- PB-9656 Migrate fields from utf8mb4 to a more performant encoding when possible + +## [3.3.1] - 2021-11-24 +### Security fixes +- PB-9820 / PBL-06-008 WP3: JWT key confusion leads to authentication bypass (High) [experimental][disabled by default] + +## [3.3.0] - 2021-10-25 +### Added +- PB-7815 As a server administrator I should be able to enable / disable the in-form menu feature, enabled by default +- PB-6072 As a server administrator I should be able to enable / disable the password generator feature, enabled by default +- PB-8189 As a user I should be able to use the application in German or Swedish +- PB-7847 As AN I should be able to authenticate to passbolt via JWT access and refresh tokens [experimental][disabled by default] +- PB-6034 As LU I should be able to configure my mobile app [experimental][disabled by default] + +### Improvement +- PB-8908 As a user I should see the footer of the passbolt emails translated with my locale +- PB-8364 As a user I should see the subject of the passbolt emails translated with my locale +- PB-6032 As API user I shouldn’t see the _joinData properties in the resource entry points responses +- PB-8281 Add Debian 11 bullseye support +- PB-7750 As AD I should be notified by the healthcheck when a tmp files is executable +- PB-7760 Increase PHPStan level to 6 +- PB-8081 As AD I should be able to configure passbolt over IPv6 while installing a passbolt package +- PB-5866 As AD I should be able to detect avatar data discrepancies using the passbolt cleanup command +- PB-7605 As a developer I should be able to enable/disable a plugin easily + +### Fixed +- PB-5457 Fix as LU importing a batch of passwords I should not get an internal errors because of database deadlock +- PB-7840 Fix as AD I can install/reconfigure the passbolt package if ssl certificates are already present + +### Security +- PB-8047 Fix PBL-02-002 As LU I should logout by posting to the API and the entry point should should be protected by CSRF +- PB-7751 Updates FlySystem dependency to v2.1.1 +- SEC-181 Fix information disclosure: recover endpoint should not return user role and name. + +### Maintenance +- PB-8488 Remove user agent unnecessary check associated with MFA token +- PB-8336 Clean phpunit.xml file +- PB-8448 Hashes the session ID prior to passord_hash +- PB-8210 Replaces PHPSESSID with session_name() + +## [3.2.1] - 2021-06-04 +### Fixed +- GITHUB-402 Fix API v3 regression, login must accept JSON data + +## [3.2.0] - 2021-05-31 +### Added +- PB-5054 French internationalization +- PB-5171 As logged-in user I can paginate the result of the users and resources index controllers +- PB-5854 As logged-in user I can save the locale of a user as account setting +- PB-5854 As admin I can save the locale the organization as organization setting + +### Fixed +- PB-5523 Fix as system administrator I should see the healthcheck errors colored in red +- PB-5860 Fix password max length should be set to 4096 in resource type definitions +- PB-6031 Fix as LU I shouldn't get a fatal error when using a scalar instead of an array for some filters values +- PB-6131 Fix healthcheck error messages display + +### Improved +- PB-5975 Test code with PHPStan - level 4 +- Avatar table should use created and modified for timestamp and not created_at and modified_at +- Move avatar in database + +### Maintenance +- PB-5527 Migration to CakePHP4 + +### Security +- Remove X-XSS-Protection as per Cure53 audit recommendations + +## [3.1.0] - 2021-03-17 +### Added +- Add preview password plugin feature flag + +## [3.0.2] - 2021-03-09 +### Fixed +- GITHUB-378 Fix healthcheck ssl fullBaseUrl check +- Fix email digest email preview should accept empty (null) template +- Fix send test email command should accept undefined username and password + +## [3.0.1] - 2021-02-24 +### Fixed +- Fix resources population of resource_type_id field migration + +## [3.0.0] - 2021-02-18 +### Deprecated +- Drop support for API format v1, api-version parameter is deprecated +- Remove title from API response envelope format +- Drop support for PHP < v7.3, application require PHP v7.3 by default +- Drop support for Composer < v2, application requires Composer v2 by default + +## Added +- Add dark theme to the community edition +- Add new system check utilities in ./bin, for example ./bin/status-report +- Add web installer automatically populates mysql credentials (VM / Debian Package) +- Add support for multiple resource types +- Add resource with encrypted description as resource type +- Add generic cron job task in ./bin/cron +- Add support for untracked personal shell scripts under ./bin/my +- Add support for configurable footer link in config +- Add permissions filters on resource view and index +- Add permissions contain options on resource view + +### Chores +- Update OpenPGP-PHP dependencies to provide PHP 7.4 compatibility +- Remove unmaintained user agent parser library +- Fix PHP 7.4 warnings + +### Improvements +- Improve testsuite execution times +- Refactor testsuite to not install data model from fixtures but use migrations instead +- Refactor testsuite to remove unused fixtures +- Migrate administration and mfa settings screen to React +- Add placeholder application skeleton when webextension is still loading +- Redesign of login and recover screens +- Add Mysql 8 support + +## [2.13.5] 2020-07-30 +### Fixed +- Fix allow overriding rememberMe options in passbolt.php configuration file +- Fix all target blank link should contain rel noopener noreferrer +- Fix email sender, email subject should not exceed 255 characters. +- Fix secret access log on resource view with contain secret +- GITHUB-376 Fix missing route prefix on the recovery button +- GITHUB-373 Fix API format for create group (previously v1 instead of v2 format) +- GITHUB-372 Fix after modifying passwd, the modification time should be changed +- GITHUB-370 Fix metadata should be deleted for deleted resources +- GITHUB-369 Fix Notification Emails Have Wrong Tense In Subject/Body +- GITHUB-368 Clarify PHP extension requirements +- GITHUB-362 Fix wrong filename on healthcheck HELP message for assertConfigFiles +- GITHUB-356 As a user I shouldn't be able to export folders if export plugin is disabled +- GITHUB-350 Fix no mails are sent when providers offer AUTH PLAIN authentication only +- GITHUB-339 Fix web installer urls do not work when passbolt is installed in a directory +- Fix performance issues on resource / folder activity log + +## [2.13.5] 2019-07-29 +### Fixed +- Fix display a validation error when db password contains a quote or db name contain a dash +- Fix email notification settings bootstrap messes up non persistent database connection in wizard +- Bump dependencies versions + +## [2.13.1] 2020-07-06 +### Fixed +- PB-1372 Fix user setup completed admin email notification + +## [2.13.0] 2020-06-23 +### Added +- PB-1168 Add baseline code and tests for Debian package build +- PB-1067 As a user I can receive digest emails when creating a lot of resources +- PB-1067 As a user I can receive digest emails when added/removed from a lot of groups +- PB-1284 Add tasks and services to re-validate existing data + +### Improved +- Pro Styleguide version bump v2.13.13 +- Appjs version bump v2.13.7 +- PB-1046 Adapt Cleanup test runner to take in account cleanup that are adding records +- PB-1046 Adapt Cleanup shell task to allow external sources to add cleanup tasks +- PB-1046 Remove empty EmailTraits files +- Delete unused default keys (cleanup) +- Update to latest passbolt_test_data version. +- Misc refactoring for email notifications +- Misc refactoring to split model logic into services +- Clear plugins in tearDown of application test cases + +### Fixed +- GITHUB-350 No mails are sent when providers offer AUTH PLAIN authentication only +- Fix appjs plugin requestUntilSuccess bug +- Fix load webinstaller plugin manually in plugin tests +- Fix composer php version. +- Fix misc checkstyle issues +- PB-980: Fix "secret access logging in password activity log should not display other resources secret access after a multiple share" + +## [2.12.1] - 2020-04-14 +### Security fixes +- PB-1209: Update javascript client dependencies + +## [2.12.0] - 2019-12-06 +### Added +- PB-687: As an admin I can resend an invitation for a user that didn't complete the setup + +### Improved +- PB-878: Update Openpgp.js to v4.7 +- PB-893: Update CakePHP to v3.8.6 + +### Fixed +- PB-771: Added purify subject for the email subscribers +- PB-856: Added migration fix to remove unused tables +- GITHUB-84: Fix gc_maxlifetime versus Session.timeout units + +## [2.11.0] - 2019-08-08 +### Security fixes +- PB-661: Fix tab nabbing when clicking on "open in a new tab" in password grid +- PB-607: Fix XSS on first name or last name during setup + +### Improvements +- PB-587: Add baseline support for multiple openpgp backends +- PB-391: Display the name and email of the user an admin is going to delete in the delete dialog +- PB-396: Display the label of the password a user is going to delete in the delete dialog +- PB-397: Display a relevant feedback in the user details group section if the user is not member of any group +- PB-533: Add a new session check endpoint that does not extend the session expiry +- PB-607: Add option for an administrator to configure CSP using environment variable +- PB-242: Improve the passwords grid (passwords fetch peformance, search reactivity, selectbox area enlarged) + +### Fixes +- PB-349: Fix health check fails if using custom GNUPGHOME env set by application +- PB-330: Fix migration issue from CE to PRO in v2.10 +- PB-567: Fix appjs auto logout +- PB-601: Fix some incomplete unit tests +- PB-427: Fix email sender shell task and organization settings table unnecessary coupling +- PB-349: Fix OpenPGP results health checks + +### Maintenance +- PB-505: Upgrade cake 3.8 +- PB-504: Upgrade Javascript dependencies +- PB-472: Cleanup test dependencies + +## [2.10.0] - 2019-05-15 +### Added +- PB-165: As AD I should be able to change my organization email notification settings via an administration screen + +### Improved +- PB-276: Merge organization settings code into CE. Ground work for administration features. + +## [2.9.0] - 2019-04-24 +### Fixed +- PB-220: Upgrade to CakePHP 3.7.7 + +## [2.8.4] - 2019-04-17 +### Improved +- PB-48: Improve the performance by removing the creator/modifier from the passwords workspace grid query +- PB-159: Remove the usage of canjs connect-hydrate module + +### Fixed +- GITHUB-315: The permalink of password don't work anymore +- PB-147: Update appjs steal dependencies +- PB-152: The webinstaller should work with Firefox ESR +- GITHUB-299: The passwords are shown twice in passwords workspace grid +- GITHUB-10: Selecting a group on the users workspace should not reset the grid "Last Logged In" column to "Never" +- GITHUB-62: Sorting the users on the users workspace should not break the infinite scroll +- PB-160: Update appjs jquery dependencies +- PB-163: Update jquery dependency +- PB-171: Fix entities history trait should not trigger internal error if user action is undefined +- PB-102: Fix install process should not create shema dump lock file +- PB-204: Escape shell variables of the passbolt mysql export shell command + +## [2.8.3] - 2019-04-02 +### Fixed +- PB-101: Fix version number +- PB-104: Implement enable / disable config switch for import export in default config + +## [2.8.2] - 2019-04-01 +### Fixed +- Fix - Disable Auditlog when passbolt is not configured + +## [2.8.1] - 2019-04-01 +### Fixed +- Remove PassboltTestData dev tool call from PassboltShell + +## [2.8.0] - 2019-04-01 +### Added +- Import your passwords from other password managers +- Export your passwords to other password managers +- PB-3: Quickaccess: Simplified app to access passwords from the browser extension + +### Improved +- PB-2: Upgrade to CakePHP 3.7 +- PB-95: Implement Import / Export enable switch + +### Fixed +- PASSBOLT-2121: Fix passbolt should run in a subdirectory +- Fix short tag use in the webinstaller server gpg key import screen +- Username and password should not be compulsory in email settings, in web installer + +## [2.7.1] - 2019-02-13 +### Fixed +- PASSBOLT-3416: Fix the uses of php shortags in the webinstaller template files + +## [2.7.0] - 2019-02-12 +### Added +- PASSBOLT-2995: As LU I should be able to copy the permalink of a password + +### Improved +- PASSBOLT-3403: As LU I should export only selected passwords +- PASSBOLT-3397: Remove the list of secrets from the API request while loading the list of passwords +- PASSBOLT-3319: As LU I should retrieve a secret when I'm editing it +- PASSBOLT-3318: As LU I should retrieve a secret when I'm copying it +- PASSBOLT-3317: Display significant information as soon as possible while opening the application +- PASSBOLT-3312: As GM adding a user to a group I should see a relevant feedback in case of network/proxy errors +- PASSBOLT-3314: Improve the performance of the application by adding missing indexes +- PASSBOLT-2974: As LU I should be able to follow links targeting passwords from my emails + +### Fixed +- PASSBOLT-3363: The webinstaller should not use the exec php primitive to create/import the gpg server key +- PASSBOLT-3370: Auth verify error should not leak data +- PASSBOLT-3368 Fix html injection in email + +## [2.5.0] - 2018-11-14 +### Added +- PASSBOLT-2694: Implement the Web Installer feature +- PASSBOLT-3093: As LU I can select all passwords to perform a bulk operation + +### Improved +- PASSBOLT-3166: Add PHP 7.3 job on travis +- PASSBOLT-3119: The Web Installer should control the route with a middleware +- PASSBOLT-3153: The Web Installer healtchecks should ensure the config files can be written before continuing +- PASSBOLT-3120: Improve the Web Installer code coverage +- PASSBOLT-3127: The Web Installer should change the config folder permissions after the installation is completed +- PASSBOLT-3152: As AN completing the registration process, if I'm following the link to download the browser extension I cannot go back easily to the registration process +- PASSBOLT-3189: As AD migrating passbolt to the latest version I would like the CakePHP cache to be cleared with the same operation + +### Fixed +- PASSBOLT-3150: I should not see duplicates rows when I filter my passwords by keywords +- GITHUB-290: A user who have not completed the setup should be allowed to request a new token using recover +- PASSBOLT-3188: As LU the UI shouldn't crash if the uri of a password cannot be parsed + +## [2.4.0] - 2018-10-12 +### Added +- PASSBOLT-2709: Implement the remember me feature +- PASSBOLT-2951: Merge the remember me on CE +- PASSBOLT-2972: As LU I should be able to delete multiple passwords in bulk +- PASSBOLT-2329: As an administrator deleting a group which is sole owner of one or several passwords, I should be requested to select a new owner for these passwords +- PASSBOLT-2983: As LU I should be able to share multiple passwords in bulk + +### Improved +- PASSBOLT-3009: Add types to authentication tokens +- GITHUB#275: Adding SSL configuration environment variables for cake mysql driver +- PASSBOLT-2534: As LU I should not be able to copy to clipboard empty login/url +- PASSBOLT-2017: As LU when I save a password (create/edit) the dialog shouldn't persist until the request is processed by the API +- PASSBOLT-3073: As LU I should get a visual feedback directly after filtering the passwords or the users workspace +- PASSBOLT-2972: As LU I should be able to select multiple passwords with classic keyboard interactions (command and shift keys) +- PASSBOLT-3090: Extend the CSRF protection + +### Fixed +- PASSBOLT-2966: As LU I can't see passwords shared with me clicking on the "shared with me" shortcut filter +- GITHUB#246: Fix healthcheck tips relative to tmp folder +- Fix Email notifications being sent several times when an AppShell is instantiated inside an AppShell +- PASSBOLT-3063: Fix appjs base url and subfolder +- PASSBOLT-3074: As a logged in user selecting a "remember me" duration the checkbox should be selected automatically +- PASSBOLT-2976: Fix API requests issues when the user is going to another workspace +- PASSBOLT-3082: ezyang/htmlpurifier cache should be stored in the application cache directory +- PASSBOLT-2982: Fix session expired check +- PASSBOLT-3086: As LU when I have 100+ passwords I cannot see the passwords after the 100th more than once + +## [2.3.0] - 2018-08-30 +### Fixed +- PASSBOLT-2965: Group filter link stays active after switching to a non group filter +- Route rewriting of the appjs should take in account passbolt installed in a subdirectory +- Fix the loading bar stuck in the initialization state in some cases +- PASSBOLT-2969: Enforce steal to load the latest version of the appjs + +### Improved +- PASSBOLT-2950: Display empty feedbacks content +- PASSBOLT-2971: Reset the workspaces filters when a resource or a user is created +- PASSBOLT-2267: As an admin deleting a user I can transfer ownership of this user shared passwords to another user or a group that have read access. + +## [2.2.0] - 2018-08-13 +### Added +- PASSBOLT-2906: Enable CSRF protection +- PASSBOLT-2940: Implement app-js primary routes + +### Fixed +- PASSBOLT-2805: Sort by date fix and sort by user first_name by default +- PASSBOLT-2896: Fix filter by tag from the password details sidebar +- PASSBOLT-2903: Fix logout link. It should target a full based url link +- PASSBOLT-2926: Fix session timeout check +- PASSBOLT-2927: Fix appjs ajax error handler +- PASSBOLT-2941: Grid performance fix + +### Improved +- PASSBOLT-2933: Upgrade to canjs 4 + +## [2.1.0] - 2018-06-14 +### Added +- PASSBOLT-2878: Integrate dark theme +- PASSBOLT-2861: Make username clickable for copy to clipboard + +### Fixed +- GITHUB-101: Fix the readme should point to the documentation for how to upgrade passbolt +- PASSBOLT-2682: Fix healthcheck entry point when logged in as admin and debug is false +- PASSBOLT-2869: Fix GPG wrapper should recognize the correct type and bit length +- PASSBOLT-1917: Migrate to canjs 3.x +- PASSBOLT-2883: Fix logout link should not prevent event propagation +- PASSBOLT-2886: Fix fingerprint tooltips in user group management dialog +- PASSBOLT-2894: Fix missing div breaking elipsis on long url in password workspace +- PASSBOLT-2891: Fix group edit users tooltips +- PASSBOLT-2884: Update header left menu. Remove home and add help. +- PASSBOLT-2885: Update user settings menus +- PASSBOLT-2895: Fix notifications homogeneity +- PASSBOLT-1337: Fix a logged in user should not be allowed to login or recover +- PASSBOLT-1337: Remove gpg json sign middleware +- PASSBOLT-1337: Wordsmithing healthcheck GPG feedback + +## [2.0.7] - 2018-05-09 +### Fixed +- Fix missing css on error pages and add version numbers to CSS and JS files calls to prevent caching +- Fix do not enable debugKit when debug is set to true + +## [2.0.5] - 2018-05-08 +### Fixed +- PASSBOLT-2764: Fix Groups autocomplete doesn't work with less than 3 characters +- PASSBOLT-2826: Upgrade styleguide to v2.1.0 +- PASSBOLT-2812: Rebuild fixtures with updated public keys + +## [2.0.4] - 2018-04-25 +### Fixed +- COMMUNITY-599: Make email MX validation optional and not enabled by default +- GITHUB-247: Fix secrets are not deleted when deleting a group or a user + +## [2.0.3] - 2018-04-20 +### Fixed +- PASSBOLT-2849: Fix issue ResourcesTable::_filterByPermissionType and MariaDB 5.5 +- PASSBOLT-2848: Fix unsafe mode and ssl offloading + +## [2.0.2] - 2018-04-16 +### Improved +- GITHUB-242: Add Auto-Submitted header to the email notifications + +### Fixed +- PASSBOLT-2806: Force database columns charset and collation +- PASSBOLT-2781: Increase length of resource uri field in model validation +- PASSBOLT-2696: Fix regression: placeholders in registration form are missing +- PASSBOLT-2791: Fix providing a string instead of an array in Email. From configuration generates a warning in SendTestEmailTask.php + +## [2.0.1] - 2018-04-09 +### Fixed +- GITHUB-239: Fix unsafe mode logic +- GITHUB-240: Make sure unconfigured 'passbolt.plugins' doesn't break the extension +- PASSBOLT-2511: Improve healthcheck tables list so that tables are listed per major version number + +## [2.0.0] - 2018-04-09 +### Added +- PASSBOLT-2725: Implement start page when passbolt is not configured +- PASSBOLT-2740: Update <3 link and add unsafe mode warning +- PASSBOLT-2697: Add passbolt migrate shell with backup option prior migration +- PASSBOLT-2803: Make the privacy policy footer link configurable in the settings +- PASSBOLT-2720 Move dev dependencies out of the passbolt_api repo +- PASSBOLT-2511: passbolt pro bootstrap is moved in a separate folder + +### Fixed +- GITHUB-229: Fix passbolt can not run in a subdirectory +- COMMUNITY-533: Fix plaintext should be initialized prior verification +- PASSBOLT-2776: Fix: As AN, settings entry point should be able to have plugins settings whitelisted +- PASSBOLT-2762: Fix unexpected error on resource share +- PASSBOLT-2754: Change the way to define if passbolt is installed while running the unit tests +- PASSBOLT-2571: Delete secrets when a password is soft deleted +- PASSBOLT-2688: Fix healtcheck warning if the development plugin passbolt_test_data is not loaded +- PASSBOLT-2711: Delete orphans secrets +- PASSBOLT-2678: Edit Appjs API calls to use version number +- PASSBOLT-2694: Improve GPG lib to handle private keys validation +- PASSBOLT-2744: Favorites delete on group user delete +- PASSBOLT-2743: Favorites delete on permissions update +- PASSBOLT-2705: Increase coverage, ensure all users who lost access to a resource have no a secret in db for this resource +- PASSBOLT-2735: Display a specific message if a sidebar section has not content to display +- PASSBOLT-2664: Change cakephpConfig into settings entry point and adjusted app-js to work with it + +## [2.0.0-rc2] - 2018-02-20 +### Added +- PASSBOLT-2638: Added command to test email configuration and SMTP communication +- PASSBOLT-2608: Implement Sidebar v2 in the Appjs +- PASSBOLT-2660: Add codacy badge +- PASSBOLT-1741: Add more GPG healthchecks +- PASSBOLT-1741: Add PHP extension checks to the healthcheck +- PASSBOLT-2597: Add check before upgrade to ensure passbolt is already in latest 1.x +- PASSBOLT-2631: Add an env var to control which email transport to use and defaults to Smtp +- PASSBOLT-2601: Add Travis v2: phpunit, coverage, phpcs + +### Fixed +- PASSBOLT-2618: Fixes for PHP 7.2 compatibility +- PASSBOLT-2624: PR#219 Fixed use CONFIG instead of "ROOT . DS . 'config'" +- PASSBOLT-2631: Fixed default class for EmailTransport to Smtp in configuration +- PASSBOLT-2640: Fixed incomplete urls in email templates +- PASSBOLT-2640: Fixed escaping of non safe characters in emails +- PASSBOLT-2667: Fixed regression: create a user that has been deleted previously returns an error +- PASSBOLT-2673: Fixed regression: as AD I cannot create a group with the name of previously deleted group +- PASSBOLT-2545: Fixed regression: As AD deleting a group I should be notified that all members of the group gonna lose access to the passwords shared with the group +- PASSBOLT-2139: Fixed check sessions calls are logged as error +- PASSBOLT-2139: Fixed not found image on password workspace +- PASSBOLT-1741: Fixed set license to AGPL-3.0-or-later for composer compatibility +- PASSBOLT-2589: Fixed App-js should check request response code from the http response header and not from the body header +- PASSBOLT-2533: Fixed resource name, username, uri, description min length should be 1 char not 3 +- PASSBOLT-2660: Fixed remove flash message from login layout + +## [2.0.0-rc1] - 2018-01-12 +### Security +- XSS protection improvements, with a new test suite dedicated for XSS. +- HTTP security headers are enabled by default and can be disabled using configuration options. +- Json responses server signature (experimental). + +### Improved +- An expired setup link can be re-sent through the recovery procedure. +- Dropped SQL views (will allow supporting additional database backends). +- Simplified configuration system. The entire configuration will be done in one dedicated file with safer defaults. +- Most configuration items are now available as environment variables. +- Install commands perform additional health checks prior to running. +- CakePHP and other dependencies have been removed from the repository and are now installed with composer. +- More flexible validation rules for inputs in most fields. +- Emojis support where it make sense (comments, descriptions, etc). +- Some notifications will not be sent if the user is the one doing the action (ex. delete password). +- The App-JS code is now available on a dedicated repository. +- Misc javascript foundation code refactoring. +- Added missing tables index to speed up some database queries. +- “Owner” has been replaced by “Created by” in the password sidebar to be more relevant. +- API supports a more standard response format (documentation coming soon). +- Additional settings for controlling what is displayed in email notifications. +- Added created date information in password sidebar. + +### Changed +- Passbolt api migration to CakePHP 3. +- PHP 7.0 is now the minimum supported version. +- Dropped table “controller_logs”. It will be soon replaced by the Audit Logs feature. +- Dropped table “schema_migrations”. +- Dropped table “cake_sessions”. +- Dropped “anonymous statistics” feature (nobody opted in…). + +### Fixed +- “Passwords I own” filter displays all the passwords for which I have “is owner” permission. +- An admin can delete a user if the user is the sole group member of a group owning passwords that are not shared. +- An admin can delete a user if the user is the sole owner of a password that is not shared. + +## [1.6.9] - 2018-01-12 +### Fixed +- PASSBOLT-2599: PR#209: Expose the 'client' variable in the default email conf +- PASSBOLT-2599: PR#211: Remove stray apostrophe in the filter by group component +- PASSBOLT-2599: PR#214: Remove html purifier submodule +- PASSBOLT-2599: PR#208: Fix typos in emails +- PASSBOLT-2599: PR#159: Rename license file +- PASSBOLT-2599 Fixed Travis +- PASSBOLT-1453: Add optional predictable UUID for auth token in selenium testing +- PASSBOLT-2474 New contributing guidelines for community forum + +## [1.6.5] - 2017-09-12 +### Added +- PASSBOLT-2383: Add + and \ to the list of allowed characters for the Resource fields: name, username and description + +### Fixed +- PASSBOLT-2371: Force the charset of the cake_sessions table in utf8 +- PASSBOLT-2325: As system administrator I shouldn't be able to execute passbolt CLI commands as root +- PASSBOLT-2397: As system administrator I should see in the healthcheck if app/tmp content and app/webroot/img/public content are writable +- PASSBOLT-1991: As system administrator I should see in the healthcheck if the server key can be used for encrypting/decrypting + +### Security +- PASSBOLT-2409: Noopener on resource url in password workspace +- PASSBOLT-2402: XSS on resource url in password workspace + +## [1.6.4] - 2017-08-31 +### Fixed +- PASSBOLT-2358: As a user registering on the demo instance I must understand the disclaimer + +## [1.6.3] - 2017-08-21 +### Fixed +- PASSBOLT-2316: Merge the selenium & phpunit dummy data sets +- PASSBOLT-2317: Speed up dummy secret creation task +- PASSBOLT-2327: Add a large set of dummy data for performance testing +- PASSBOLT-2282: As admin on the user workspace, I should be able to distinguish visually the users who haven't activated their account yet + +## [1.6.2] - 2017-08-12 +### Added +- PASSBOLT-2284: As an administrator I can set which notifications are enabled for my organization #98 +- PASSBOLT-2284: As an administrator I can prevent encrypted secret or username to be sent in email notification #114 + +### Fixed +- PASSBOLT-2301: Remove additional slashes in passbolt.js urls such as model/users::find #142 +- PASSBOLT-2270: Fix modified_by not set on resource edit regression +- PASSBOLT-2271: Fix no wrap issue on resource description +- PASSBOLT-1943: As an administrator I should not be able to install passbolt on a hostname that is not RFC3986 compliant +- PASSBOLT-1937: As an administrator I should not be be able to install passbolt with a server key without an email id +- PASSBOLT-2002: Refactor install script to reuse healthcheck library + +## [1.6.1] - 2017-07-26 +### Added +- PASSBOLT-2147: As a group member I should receive a notification when my role in the group has changed +- PASSBOLT-2148: As a group manager I should receive a notification when a user who is part of one (or more) groups I manage is deleted +- PASSBOLT-2225: As a demo user it should be explicit that I need to use a throway email account +- PASSBOLT-2133: As LU I should be able to filter passwords by group on the passwords workspace +- PASSBOLT-2012: As a user I can see which groups a user is a member of from the sidebar + +### Fixed +- PASSBOLT-2171: The group list component should be marked as ready once the API request is completed +- PASSBOLT-2172: Newly added group manager shouldn't receive the group update summary notification +- PASSBOLT-2174: Edit group dialog should be marked as ready if an admin edit a group the admin is not group manager +- PASSBOLT-2155: As AD I shouldn't be able to delete as user if the user is the sole group manager of a group +- PASSBOLT-2075: Users should be removed from the groups they are member of after a soft delete operation +- PASSBOLT-1934: GITHUB-40, GITHUB-120: As a user I should be allowed to add the a ldap path as username +- PASSBOLT-2156: GITHUB-94: As a user I should be allowed to add text in JSON format in the description +- PASSBOLT-2122: GITHUB-85: Username should be Minimum 1 characters in length (and not 3) +- PASSBOLT-2180: GITHUB-85: As a user I should be allowed to add a space in a resource username +- PASSBOLT-2125: GITHUB-86: As a logged in user creating/editing a password I should be able to use new line characters in the description +- PASSBOLT-2188: Regression: As LU when I search for a user it shouldn't make an API request +- PASSBOLT-2234: Regression: As newly added GM I shouldn't receive the group update summary when I'm just added as GM +- PASSBOLT-2235: As AD editing a group the dialog shouldn't be marked as ready until the members list is loaded +- PASSBOLT-2105: Anonymous statistics: fix "Warning Error: file_put_contents" issue at installation +- PASSBOLT-2005: PR#44: Update allowed characters in a uri + +## [1.6.0] - 2017-06-21 +### Added +- PASSBOLT-2099: As a user I should receive a notification when I am added to a group +- PASSBOLT-2100: As a user I should receive a notification when I am deleted of a group +- PASSBOLT-2102: As a group manager I should receive a notification when another group manager added a user to a group I manage +- PASSBOLT-2103: As a group manager I should receive a notification when another group manager removed a user from a group I manage +- PASSBOLT-2140: As a group manager I should receive a notification when another group manager changed the role of a user of a group I manage +- PASSBOLT-2138: The TLS parameter should be part of the default email configuration + +### Fixed +- PASSBOLT-2044: As an admin I shouldn’t be able to delete a user who is the sole owner of passwords shared with others +- PASSBOLT-2078: As GM/AD I shouldn't be able to add a user who didn't complete the registration process to a group I edit/create +- PASSBOLT-2111: As an admin I should be able to install passbolt under mydomain.tld/passbolt +- PASSBOLT-2142: As an admin I should not see multiple ASCII banner when running the install script +- PASSBOLT-1959: As LU when I unshare a password with a user or a group, associated secrets should be destroyed +- PASSBOLT-1954: Security: Trackable behavior should override created_by and deleted_by when provided + +## [1.5.1] - 2017-05-23 +### Fixed +- PASSBOLT-2070: Delete unused code / exclude external libs from coverage +- PASSBOLT-2071: Drop exec bits from files which don't need them (@OdyX GITHUB PR #67) +- PASSBOLT-2073: As AP I should see a warning on the login page if the plugin and the api are not compatible +- PASSBOLT-2029: PHP7 compatibility, fix deprecated cakePHP String class calls (@leomazzo GITHUB-64) +- PASSBOLT-2074: Delete confirmation dialogs should fit the latest styleguide + +## [1.5.0] - 2017-05-16 +### Added +- PASSBOLT-1950: As a user I can see which groups a password is shared with from the sidebar +- PASSBOLT-1953: As a user I can share a password with a group +- PASSBOLT-1940: As a user when editing a password for a group, the secret should be encrypted for all the members +- PASSBOLT-1639: As a user editing a password description in the right sidebar should not get duplicated items in shared with section +- PASSBOLT-1938: As a user I can browse the list of groups in the groups section of the user workspace +- PASSBOLT-2000: As a user I can see which users are part of a given group from the sidebar and the users section +- PASSBOLT-1960: As a user I can see the list of users that are part of the group in the users grid by using the group filter +- PASSBOLT-1838: As a group manager I can edit the membership roles +- PASSBOLT-1838: As a group manager I can add a user to a group +- PASSBOLT-1838: As a group manager I can remove a user from a group using the edit group dialog +- PASSBOLT-1969: As a group manager I can edit a group from the contextual menu and from the groups sidebar +- PASSBOLT-1969: As a group manager I can see which users are part of a given group from the group edit dialog +- PASSBOLT-2000: As a group manager I can see which users are part of a given group from the sidebar and the users section +- PASSBOLT-2006: As an administrator I can delete a group from the group contextual menu +- PASSBOLT-1969: As an administrator I can edit a group +- PASSBOLT-2006: As an administrator I can delete a group +- PASSBOLT-1955: As an administrator I can create a group using the new button in the users workspace +- PASSBOLT-1939: As an administrator the healthcheck should be accessible in command line +- PASSBOLT-1943: As an administrator the healthcheck should tell if not using a proper domain name as base url +- PASSBOLT-1943: As an administrator the healthcheck should tell if SSL certificate is invalid +- PASSBOLT-1885: As an administrator the healthcheck should tell if the full base url is not reachable +- PASSBOLT-1838: Add v1.5.0 migration script +- PASSBOLT-1881: Add support for groups in the permission system +- PASSBOLT-1952: Add support for groups in the fixtures +- PASSBOLT-1928: Deploy styleguide with groups support + +### Fixed +- PASSBOLT-1614: Abstract user/password grid functions into the mad framework grid library +- PASSBOLT-1571: API query string filters: better naming conventions and implementation +- PASSBOLT-1915: Remove legacy references related to old user passwords +- PASSBOLT-1761: Remove legacy references to throttle login +- PASSBOLT-1268: Remove legacy dictionary controller +- PASSBOLT-1268: Use exceptions instead of message component errors and misc refactoring +- PASSBOLT-2036: Fix travis database configuration issue +- PASSBOLT-2037: Schema should allow resources fields username and uri to be null +- PASSBOLT-2038: Travis and php54 + +## [1.4.0] - 2017-02-07 +### Fixed +- PASSBOLT-1863: Remove references to legacy features Category and Tags +- PASSBOLT-1883: Fix wrong usage of the permission entry point viewByAco +- PASSBOLT-1887: Remove the entry point PermissionController::simulateAcoPermissionsAfterChange +- PASSBOLT-1886: Remove the controller component PermissionHelperComponent +- PASSBOLT-1888: Remove the model behavior function PermissionableBehavior::getUsersWithAPermissionSet +- PASSBOLT-1889: Remove references to legacy models and tables (AuthenticationLogs, AuthenticationBlackList, Email, Adress, PhoneNumber) +- PASSBOLT-1890: Clean the Permission model validation functions & augment coverage +- PASSBOLT-1894: Reorganize ACL models tests +- PASSBOLT-1896: Remove references to legacy permission types CREATE and DENY +- PASSBOLT-1511: removed tracking of config file Config/email.php (@BaumannMisys GITHUB-34) +- PASSBOLT-1835: As a user I should be able to create an account with the same username as an account that was previously deleted (@bestlibre GITHUB-33) +- PASSBOLT-1646: GITHUB-20 Permissions views and queries do not work with Mysql57 / only_full_group_by enabled + +## [1.3.2] - 2017-01-16 +### Fixed +- PASSBOLT-811: Error message look and feel is not consistent on register / recover + +## [1.3.1] - 2017-01-03 +### Fixed +- PASSBOLT-1758: As LU sharing a password I should be able to filter users based on first name and last name +- PASSBOLT-1779: Remove debug statement +- PASSBOLT-1585: As AN I should be allowed to register if my lastname or firstname are 2 chars in length +- PASSBOLT-1783: Form validation and translation: malformed error messages +- PASSBOLT-1619: As AP I should not be allowed to recover my account if I have not completed the setup first +- PASSBOLT-1767: As a AD installing passbolt I should be told if webroot/img/public is not writable. +- PASSBOLT-1793: Upgrade to CakePHP v2.9.4 +- PASSBOLT-1784: GITHUB-29 PHP7 compatibility issue in migration console tasks +- PASSBOLT-1790: Fixed update context sent by anonymous usage statistics + +## [1.3.0] - 2016-25-11 +### Fixed +- PASSBOLT-1721: SSL detection not working in healthcheck +- PASSBOLT-1708: Accept JSON data content type for HTTP PUT during setup + +### Added +- PASSBOLT-1725: Misc changes for Chrome support +- PASSBOLT-1726: Implement anonymous usage data + +## [1.2.1] - 2016-10-19 +### Fixed +- PASSBOLT-1719: GITHUB-14 The "." is not allowed in email address field +- PASSBOLT-1525: Remove unused controllers and components +- PASSBOLT-1718: Tidy up readme and contribution guidelines + +## [1.2.0] - 2016-10-17 +### Added +- PASSBOLT-1706: GITHUB-18 Resource Description length is too short, should be 10K characters +- PASSBOLT-1658: GITHUB-18 Resource URI length is too short, should be 1024 characters +- PASSBOLT-1637: GITHUB-14 The "+" is not allowed in the email address field while adding a new user +- PASSBOLT-1525: Test coverage for SetupControllerTest & CakeErrorController +- PASSBOLT-1694: Default config change: debug should be set to 0 +- PASSBOLT-1660: Refactoring to simplify Chrome plugin development +- PASSBOLT-1649: Adjusted coveralls markup +- PASSBOLT-1648: Upgrade to Cakephp 2.9.1 +- PASSBOLT-1250: Contribution guidelines + +### Fixed +- PASSBOLT-1700: Event names should stay backward compatible +- PASSBOLT-1668: Remove GPGAuth debug count +- PASSBOLT-1673: Restore avatars during quick install + +## [1.1.0] - 2016-08-09 +### Added +- PASSBOLT-1124: As LU on user workspace I should be able to see the last logged in date of a user. +- PASSBOLT-1216: As LU I should be able to sort the tableview in passwords workspace +- PASSBOLT-1217: As LU I should be able to sort the tableview in users workspace. +- PASSBOLT-1535: Fix mysql 5.7 schema issues and improve compatibility. +- PASSBOLT-1633: Travis and Coveralls integration. +- PASSBOLT-1597: Implemented schema versioning and migration tool. + +### Fixed +- PASSBOLT-1604: As a AD I should be able to see the healthcheck page when debug is set to 0 +- PASSBOLT-1525: Misc unit test code coverage & phpcs cleanup +- PASSBOLT-1653: After migration, Gpgkey.uid should be sanitized in DB. +- PASSBOLT-1634: Authentication logs are moved in each authentication stage. +- PASSBOLT-1383: Cleanup cakephp config & prevent future regressions like PASSBOLT-1621 with a default. +- PASSBOLT-1486: After deleting a user, I should be able to recreate a user with the same username. +- PASSBOLT-1620: Duplicate users in the list when selecting a user and using filters. +- PASSBOLT-1652: As LU I cannot use passbolt with long public key. + +### Tests +- PASSBOLT-1642: Increased selenium tests coverage when browser is restarted. +- PASSBOLT-1643: Increased selenium tests coverage when passbolt tab is closed and restored. + + +## [1.0.14] - 2016-07-06 +### Fixed +- PASSBOLT-1616: Fixed bad merge during the previous release. +- PASSBOLT-1599: GITHUB-10 passbolt.js requesting wrong path for config.json. + +## [1.0.13] - 2016-06-30 +### Fixed +- PASSBOLT-1605: Set::extract to Hash::extract refactoring regression. +- PASSBOLT-1601: ControllerLog Model should support IVP6 addresses. +- PASSBOLT-1366: Worker bug when multiple passbolt instances are open in multiple windows. +- PASSBOLT-1590: Styleguide bump to v1.0.38. +- PASSBOLT-1613: As a user losing access to a password I selected, I shouldn't encounter an error. +- PASSBOLT-1569: Cleanup: remove SetupController::ping. + +### Added +- PASSBOLT-1077: As a LU searching for a password (or a user) search results should filter as I type. +- PASSBOLT-1588: As AN it should be possible to recover a passbolt account on a new device. + +## [1.0.12] - 2016-05-31 +### Fixed +- PASSBOLT-1439: Email is sent as anonymous when a user is created from the console. +- PASSBOLT-1509: As LU, when a password is shared with me in read only, I should not see the delete menu available in more. +- PASSBOLT-1407: As LU, there is no visual feedback when I upload a picture and that the process is in progress. +- PASSBOLT-1579: Segfault at the end of setup when trying to display login form. +- PASSBOLT-1576: Fixed Hash component warning message in EmailQueue. +- PASSBOLT-1322: Insertion of comments in unittest dataset display an error in the console. +- PASSBOLT-1234: Authentication token used for account registration expiracy check. + +### Added +- PASSBOLT-1572: As LU, I should be able to see which users a password is shared with directly from the sidebar. + +## [1.0.11] - 2016-05-16 +### Added +- PASSBOLT-1388: As a user I should receive an email notification when a password is updated. +- PASSBOLT-1389: As a user I should receive an email notification when a password is created. +- PASSBOLT-1390: As a user I should receive an email notification when a password is deleted. +- PASSBOLT-1544: As a user I should receive an email notification when someone comments on a password. +- PASSBOLT-1221: API documentation with Swagger (Part I: models). + +### Fixed +- PASSBOLT-1094: Frontend : Server errors happening during a request should give a visual feedback. +- PASSBOLT-1438: Retry button is not working at setup first step (when user doesn't have the plugin installed). +- PASSBOLT-1564: As a sysop, installing passbolt with quiet mode should not output any information. +- PASSBOLT-1434: Wordsmithing: rename master password to passphrase. + +## [1.0.10] - 2016-05-03 +### Fixed +- PASSBOLT-1502: String is depracated in Cakephp since version 2.7 use CakeText instead. +- PASSBOLT-1466: GET /auth/verify.json Content-Type should not be text/html but JSON. +- PASSBOLT-1443: Copy to clipboard icon is misleading + +### Changed +- PASSBOLT-1419: Cleanup config.json for js client and remove useless config. +- PASSBOLT-1514: By default passbolt app should not be indexed by search engines. +- PASSBOLT-1474: API: Upgrade cakephp to 2.8.3. +- PASSBOLT-1288: As an AD during install I should have status page to help me. + +## [1.0.9] - 2016-04-25 +### Fixed +- PASSBOLT-1505: As AP, I should not get an error during setup if my key has been generated on a system that is not exactly on time. +- PASSBOLT-1457: As LU, I should not be able to create a resource without password. +- PASSBOLT-1441: Wordsmithing: a parenthesis is missing on set a security token step. +- PASSBOLT-1158: Remove all errors (plugin/client) from the browser console at passbolt start. + +### Changed +- PASSBOLT-1456: When generating a password automatically it only generates a "fair" level password. +- PASSBOLT-1495: Passbolt: update installation instructions in README file. + +## [1.0.8] - 2016-04-15 +### Fixed +- PASSBOLT-1445: As a LU viewing someone else comment I should not see the delete comment button. +- PASSBOLT-1402: As LU, In the comment thread I should not see a hyperlink on people's name that leads to nowhere. + +## [1.0.7] - 2016-04-04 +### Added +- PASSBOLT-1223: Implemented state for empty password workspace. + +### Changed +- PASSBOLT-1450: Change information button icon. Eye becomes information. + +## [1.0.6] - 2016-03-28 +### Added +- PASSBOLT-1343: Confirmation email link opened in chrome does not explain that passbolt works only in firefox. +- PASSBOLT-1416: Improved coverage : API / Token should not be disabled when validateAccount fails. +- PASSBOLT-1444: Slack plugin for passbolt to keep track of demo registrations. + +### Fixed +- PASSBOLT-1395: Regression : As LU I should not be able to select two password. +- PASSBOLT-1396: As LU I should not see a mix of two dashboards if I click quickly on the users and passwords menu links. +- PASSBOLT-1406: Space missing between first name and last name in registration email. + +## [1.0.5] - 2016-03-21 +### Added +- PASSBOLT-1384: Admin user should be registered during installation. +- PASSBOLT-1310: As user whose account is deleted I should get feedback on login. + +### Fixed +- PASSBOLT-1415: Please register links are broken for AP. +- PASSBOLT-1157: An error page should not include any scripts. +- PASSBOLT-1243: I should see an error when I try to upload an avatar with a wrong file type / size + +# Terminology +- AN: Anonymous user +- LU: Logged in user +- AP: User with plugin installed +- AD: Admin + +[Unreleased]: https://github.com/passbolt/passbolt_api/compare/v3.4.0...HEAD +[3.4.0]: https://github.com/passbolt/passbolt_api/compare/v3.3.1...v3.4.0 +[3.3.1]: https://github.com/passbolt/passbolt_api/compare/v3.3.0...v3.3.1 +[3.3.0]: https://github.com/passbolt/passbolt_api/compare/v3.2.1...v3.3.0 +[3.2.1]: https://github.com/passbolt/passbolt_api/compare/v3.2.0...v3.2.1 +[3.2.0]: https://github.com/passbolt/passbolt_api/compare/v3.1.0...v3.2.0 +[3.1.0]: https://github.com/passbolt/passbolt_api/compare/v3.0.2...v3.1.0 +[3.0.2]: https://github.com/passbolt/passbolt_api/compare/v3.0.1...v3.0.2 +[3.0.1]: https://github.com/passbolt/passbolt_api/compare/v3.0.0...v3.0.1 +[3.0.0]: https://github.com/passbolt/passbolt_api/compare/v2.13.5...v3.0.0 +[2.13.5]: https://github.com/passbolt/passbolt_api/compare/v2.13.1...v2.13.5 +[2.13.1]: https://github.com/passbolt/passbolt_api/compare/v2.13.0...v2.13.1 +[2.13.0]: https://github.com/passbolt/passbolt_api/compare/v2.12.1...v2.13.0 +[2.12.1]: https://github.com/passbolt/passbolt_api/compare/v2.12.0...v2.12.1 +[2.12.0]: https://github.com/passbolt/passbolt_api/compare/v2.11.0...v2.12.0 +[2.11.0]: https://github.com/passbolt/passbolt_api/compare/v2.10.0...v2.11.0 +[2.10.0]: https://github.com/passbolt/passbolt_api/compare/v2.9.0...v2.10.0 +[2.9.0]: https://github.com/passbolt/passbolt_api/compare/v2.8.4...v2.9.0 +[2.8.4]: https://github.com/passbolt/passbolt_api/compare/v2.8.3...v2.8.4 +[2.8.3]: https://github.com/passbolt/passbolt_api/compare/v2.8.2...v2.8.3 +[2.8.2]: https://github.com/passbolt/passbolt_api/compare/v2.8.1...v2.8.2 +[2.8.1]: https://github.com/passbolt/passbolt_api/compare/v2.8.0...v2.8.1 +[2.8.0]: https://github.com/passbolt/passbolt_api/compare/v2.7.1...v2.8.0 +[2.7.1]: https://github.com/passbolt/passbolt_api/compare/v2.7.0...v2.7.1 +[2.7.0]: https://github.com/passbolt/passbolt_api/compare/v2.5.0...v2.7.0 +[2.5.0]: https://github.com/passbolt/passbolt_api/compare/v2.4.0...v2.5.0 +[2.4.0]: https://github.com/passbolt/passbolt_api/compare/v2.3.0...v2.4.0 +[2.3.0]: https://github.com/passbolt/passbolt_api/compare/v2.2.0...v2.3.0 +[2.2.0]: https://github.com/passbolt/passbolt_api/compare/v2.1.0...v2.2.0 +[2.1.0]: https://github.com/passbolt/passbolt_api/compare/v2.0.7...v2.1.0 +[2.0.7]: https://github.com/passbolt/passbolt_api/compare/v2.0.5...v2.0.7 +[2.0.5]: https://github.com/passbolt/passbolt_api/compare/v2.0.4...v2.0.5 +[2.0.4]: https://github.com/passbolt/passbolt_api/compare/v2.0.3...v2.0.4 +[2.0.3]: https://github.com/passbolt/passbolt_api/compare/v2.0.2...v2.0.3 +[2.0.2]: https://github.com/passbolt/passbolt_api/compare/v2.0.1...v2.0.2 +[2.0.1]: https://github.com/passbolt/passbolt_api/compare/v2.0.0...v2.0.1 +[2.0.0]: https://github.com/passbolt/passbolt_api/compare/v2.0.0-rc2...v2.0.0 +[2.0.0-rc2]: https://github.com/passbolt/passbolt_api/compare/v2.0.0-rc1...v2.0.0-rc2 +[2.0.0-rc1]: https://github.com/passbolt/passbolt_api/compare/v1.6.9...v2.0.0-rc1 +[1.6.9]: https://github.com/passbolt/passbolt_api/compare/v1.6.5...v1.6.9 +[1.6.5]: https://github.com/passbolt/passbolt_api/compare/v1.6.4...v1.6.5 +[1.6.4]: https://github.com/passbolt/passbolt_api/compare/v1.6.3...v1.6.4 +[1.6.3]: https://github.com/passbolt/passbolt_api/compare/v1.6.2...v1.6.3 +[1.6.2]: https://github.com/passbolt/passbolt_api/compare/v1.6.1...v1.6.2 +[1.6.1]: https://github.com/passbolt/passbolt_api/compare/v1.6.0...v1.6.1 +[1.6.0]: https://github.com/passbolt/passbolt_api/compare/v1.5.1...v1.6.0 +[1.5.1]: https://github.com/passbolt/passbolt_api/compare/v1.5.0...v1.5.1 +[1.5.0]: https://github.com/passbolt/passbolt_api/compare/v1.4.0...v1.5.0 +[1.4.0]: https://github.com/passbolt/passbolt_api/compare/v1.3.2...v1.4.0 +[1.3.2]: https://github.com/passbolt/passbolt_api/compare/v1.3.1...v1.3.2 +[1.3.1]: https://github.com/passbolt/passbolt_api/compare/v1.3.0...v1.3.1 +[1.3.0]: https://github.com/passbolt/passbolt_api/compare/v1.2.1...v1.3.0 +[1.2.1]: https://github.com/passbolt/passbolt_api/compare/v1.2.0...v1.2.1 +[1.2.0]: https://github.com/passbolt/passbolt_api/compare/v1.1.1...v1.2.0 +[1.1.1]: https://github.com/passbolt/passbolt_api/compare/v1.1.0...v1.1.1 +[1.1.0]: https://github.com/passbolt/passbolt_api/compare/v1.0.14...v1.1.0 +[1.0.14]: https://github.com/passbolt/passbolt_api/compare/v1.0.13...v1.0.14 +[1.0.13]: https://github.com/passbolt/passbolt_api/compare/v1.0.12...v1.0.13 +[1.0.12]: https://github.com/passbolt/passbolt_api/compare/v1.0.11...v1.0.12 +[1.0.11]: https://github.com/passbolt/passbolt_api/compare/v1.0.10...v1.0.11 +[1.0.10]: https://github.com/passbolt/passbolt_api/compare/v1.0.9...v1.0.10 +[1.0.9]: https://github.com/passbolt/passbolt_api/compare/v1.0.8...v1.0.9 +[1.0.8]: https://github.com/passbolt/passbolt_api/compare/v1.0.7...v1.0.8 +[1.0.7]: https://github.com/passbolt/passbolt_api/compare/v1.0.6...v1.0.7 +[1.0.6]: https://github.com/passbolt/passbolt_api/compare/v1.0.5...v1.0.6 +[1.0.5]: https://github.com/passbolt/passbolt_api/compare/6a92766...v1.0.5 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ab6ce0ddd4..0310fc0076 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,7 +27,7 @@ composer run-script cs-fix sudo su -s /bin/bash -c "./bin/cake PassboltTestData.fixturize default" www-data ``` -## How do I contribute to the the js application +## How do I contribute to the js application Clone the appjs repository in a separate folder ``` @@ -61,4 +61,4 @@ npm install grunt-browser-sync Listen to the appjs change and refresh the browser ``` grunt appjs-watch-browser-sync -``` \ No newline at end of file +``` diff --git a/Gemfile.lock b/Gemfile.lock index c84eca8c18..7dda9519cf 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,179 +1,259 @@ GEM remote: https://rubygems.org/ specs: - activesupport (5.2.4.3) + activesupport (6.1.4.1) concurrent-ruby (~> 1.0, >= 1.0.2) - i18n (>= 0.7, < 2) - minitest (~> 5.1) - tzinfo (~> 1.1) - addressable (2.7.0) + i18n (>= 1.6, < 2) + minitest (>= 5.1) + tzinfo (~> 2.0) + zeitwerk (~> 2.3) + addressable (2.8.0) public_suffix (>= 2.0.2, < 5.0) - aws-eventstream (1.1.0) - aws-partitions (1.324.0) - aws-sdk-apigateway (1.41.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-eventstream (1.2.0) + aws-partitions (1.516.0) + aws-sdk-alexaforbusiness (1.51.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sdk-apigatewayv2 (1.21.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-amplify (1.32.0) + aws-sdk-core (~> 3, >= 3.120.0) aws-sigv4 (~> 1.1) - aws-sdk-athena (1.27.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-apigateway (1.68.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sdk-autoscaling (1.22.0) - aws-sdk-core (~> 3, >= 3.52.1) + aws-sdk-apigatewayv2 (1.37.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sdk-budgets (1.30.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-applicationautoscaling (1.51.0) + aws-sdk-core (~> 3, >= 3.112.0) aws-sigv4 (~> 1.1) - aws-sdk-cloudformation (1.36.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-athena (1.42.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sdk-cloudhsm (1.22.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-autoscaling (1.63.0) + aws-sdk-core (~> 3, >= 3.112.0) aws-sigv4 (~> 1.1) - aws-sdk-cloudhsmv2 (1.23.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-batch (1.47.0) + aws-sdk-core (~> 3, >= 3.112.0) aws-sigv4 (~> 1.1) - aws-sdk-cloudtrail (1.23.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-budgets (1.42.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sdk-cloudwatch (1.38.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-cloudformation (1.59.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sdk-cloudwatchlogs (1.31.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-cloudfront (1.57.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sdk-codecommit (1.33.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-cloudhsm (1.34.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sdk-codedeploy (1.31.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-cloudhsmv2 (1.37.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sdk-codepipeline (1.31.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-cloudtrail (1.39.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sdk-configservice (1.45.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-cloudwatch (1.56.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sdk-core (3.97.1) + aws-sdk-cloudwatchevents (1.46.0) + aws-sdk-core (~> 3, >= 3.112.0) + aws-sigv4 (~> 1.1) + aws-sdk-cloudwatchlogs (1.46.0) + aws-sdk-core (~> 3, >= 3.121.2) + aws-sigv4 (~> 1.1) + aws-sdk-codecommit (1.46.0) + aws-sdk-core (~> 3, >= 3.121.2) + aws-sigv4 (~> 1.1) + aws-sdk-codedeploy (1.44.0) + aws-sdk-core (~> 3, >= 3.121.2) + aws-sigv4 (~> 1.1) + aws-sdk-codepipeline (1.48.0) + aws-sdk-core (~> 3, >= 3.121.2) + aws-sigv4 (~> 1.1) + aws-sdk-cognitoidentity (1.31.0) + aws-sdk-core (~> 3, >= 3.112.0) + aws-sigv4 (~> 1.1) + aws-sdk-cognitoidentityprovider (1.53.0) + aws-sdk-core (~> 3, >= 3.112.0) + aws-sigv4 (~> 1.1) + aws-sdk-configservice (1.68.0) + aws-sdk-core (~> 3, >= 3.121.2) + aws-sigv4 (~> 1.1) + aws-sdk-core (3.121.2) aws-eventstream (~> 1, >= 1.0.2) aws-partitions (~> 1, >= 1.239.0) aws-sigv4 (~> 1.1) jmespath (~> 1.0) - aws-sdk-costandusagereportservice (1.21.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-costandusagereportservice (1.35.0) + aws-sdk-core (~> 3, >= 3.121.2) + aws-sigv4 (~> 1.1) + aws-sdk-databasemigrationservice (1.53.0) + aws-sdk-core (~> 3, >= 3.112.0) + aws-sigv4 (~> 1.1) + aws-sdk-dynamodb (1.64.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sdk-dynamodb (1.48.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-ec2 (1.271.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sdk-ec2 (1.164.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-ecr (1.48.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sdk-ecr (1.30.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-ecrpublic (1.7.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sdk-ecs (1.63.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-ecs (1.87.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sdk-efs (1.29.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-efs (1.47.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sdk-eks (1.37.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-eks (1.64.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sdk-elasticache (1.36.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-elasticache (1.63.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sdk-elasticbeanstalk (1.30.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-elasticbeanstalk (1.46.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sdk-elasticloadbalancing (1.22.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-elasticloadbalancing (1.35.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sdk-elasticloadbalancingv2 (1.44.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-elasticloadbalancingv2 (1.71.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sdk-elasticsearchservice (1.36.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-elasticsearchservice (1.57.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sdk-firehose (1.28.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-emr (1.53.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sdk-iam (1.39.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-eventbridge (1.24.0) + aws-sdk-core (~> 3, >= 3.112.0) aws-sigv4 (~> 1.1) - aws-sdk-kafka (1.21.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-firehose (1.43.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sdk-kinesis (1.23.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-glue (1.88.0) + aws-sdk-core (~> 3, >= 3.112.0) aws-sigv4 (~> 1.1) - aws-sdk-kms (1.33.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-guardduty (1.49.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sdk-lambda (1.42.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-iam (1.62.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sdk-organizations (1.17.0) - aws-sdk-core (~> 3, >= 3.39.0) - aws-sigv4 (~> 1.0) - aws-sdk-rds (1.85.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-kafka (1.42.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sdk-redshift (1.43.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-kinesis (1.36.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sdk-route53 (1.35.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-kms (1.50.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sdk-route53domains (1.22.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-lambda (1.70.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sdk-route53resolver (1.14.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-mq (1.40.0) + aws-sdk-core (~> 3, >= 3.120.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.67.1) - aws-sdk-core (~> 3, >= 3.96.1) + aws-sdk-networkfirewall (1.9.0) + aws-sdk-core (~> 3, >= 3.121.2) + aws-sigv4 (~> 1.1) + aws-sdk-networkmanager (1.15.0) + aws-sdk-core (~> 3, >= 3.121.2) + aws-sigv4 (~> 1.1) + aws-sdk-organizations (1.59.0) + aws-sdk-core (~> 3, >= 3.112.0) + aws-sigv4 (~> 1.1) + aws-sdk-ram (1.26.0) + aws-sdk-core (~> 3, >= 3.112.0) + aws-sigv4 (~> 1.1) + aws-sdk-rds (1.128.0) + aws-sdk-core (~> 3, >= 3.121.2) + aws-sigv4 (~> 1.1) + aws-sdk-redshift (1.70.0) + aws-sdk-core (~> 3, >= 3.121.2) + aws-sigv4 (~> 1.1) + aws-sdk-route53 (1.56.0) + aws-sdk-core (~> 3, >= 3.121.2) + aws-sigv4 (~> 1.1) + aws-sdk-route53domains (1.34.0) + aws-sdk-core (~> 3, >= 3.121.2) + aws-sigv4 (~> 1.1) + aws-sdk-route53resolver (1.31.0) + aws-sdk-core (~> 3, >= 3.121.2) + aws-sigv4 (~> 1.1) + aws-sdk-s3 (1.104.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sdk-kms (~> 1) + aws-sigv4 (~> 1.4) + aws-sdk-secretsmanager (1.46.0) + aws-sdk-core (~> 3, >= 3.112.0) + aws-sigv4 (~> 1.1) + aws-sdk-securityhub (1.54.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sdk-securityhub (1.26.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-servicecatalog (1.60.0) + aws-sdk-core (~> 3, >= 3.112.0) aws-sigv4 (~> 1.1) - aws-sdk-ses (1.30.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-ses (1.41.0) + aws-sdk-core (~> 3, >= 3.120.0) aws-sigv4 (~> 1.1) - aws-sdk-sms (1.20.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-shield (1.42.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sdk-sns (1.24.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-signer (1.32.0) + aws-sdk-core (~> 3, >= 3.120.0) aws-sigv4 (~> 1.1) - aws-sdk-sqs (1.26.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-simpledb (1.29.0) + aws-sdk-core (~> 3, >= 3.120.0) + aws-sigv2 (~> 1.0) + aws-sdk-sms (1.33.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sdk-ssm (1.79.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-sns (1.46.0) + aws-sdk-core (~> 3, >= 3.121.2) aws-sigv4 (~> 1.1) - aws-sigv4 (1.1.4) - aws-eventstream (~> 1.0, >= 1.0.2) + aws-sdk-sqs (1.45.0) + aws-sdk-core (~> 3, >= 3.121.2) + aws-sigv4 (~> 1.1) + aws-sdk-ssm (1.120.0) + aws-sdk-core (~> 3, >= 3.121.2) + aws-sigv4 (~> 1.1) + aws-sdk-states (1.39.0) + aws-sdk-core (~> 3, >= 3.112.0) + aws-sigv4 (~> 1.1) + aws-sdk-synthetics (1.19.0) + aws-sdk-core (~> 3, >= 3.121.2) + aws-sigv4 (~> 1.1) + aws-sdk-transfer (1.34.0) + aws-sdk-core (~> 3, >= 3.112.0) + aws-sigv4 (~> 1.1) + aws-sigv2 (1.1.0) + aws-sigv4 (1.4.0) + aws-eventstream (~> 1, >= 1.0.2) azure_graph_rbac (0.17.2) ms_rest_azure (~> 0.12.0) - azure_mgmt_key_vault (0.17.6) + azure_mgmt_key_vault (0.17.7) ms_rest_azure (~> 0.12.0) - azure_mgmt_resources (0.17.9) + azure_mgmt_resources (0.18.2) ms_rest_azure (~> 0.12.0) - azure_mgmt_security (0.18.1) + azure_mgmt_security (0.19.0) ms_rest_azure (~> 0.12.0) - azure_mgmt_storage (0.21.1) + azure_mgmt_storage (0.23.0) ms_rest_azure (~> 0.12.0) - bcrypt_pbkdf (1.1.0.rc1) - berkshelf (7.0.10) - chef (>= 13.6.52) + bcrypt_pbkdf (1.1.0) + berkshelf (7.2.2) + chef (>= 15.7.32) chef-config cleanroom (~> 1.0) concurrent-ruby (~> 1.0) minitar (>= 0.6) - mixlib-archive (>= 0.4, < 2.0) + mixlib-archive (>= 1.1.4, < 2.0) mixlib-config (>= 2.2.5) mixlib-shellout (>= 2.0, < 4.0) octokit (~> 4.0) @@ -181,214 +261,214 @@ GEM solve (~> 4.0) thor (>= 0.20) bigdecimal (2.0.0) + bson (4.12.1) builder (3.2.4) - chef (16.1.16) + chef (17.6.18) addressable - bcrypt_pbkdf (= 1.1.0.rc1) - bundler (>= 1.10) - chef-config (= 16.1.16) - chef-utils (= 16.1.16) + aws-sdk-s3 (~> 1.91) + aws-sdk-secretsmanager (~> 1.46) + chef-config (= 17.6.18) + chef-utils (= 17.6.18) chef-vault chef-zero (>= 14.0.11) - diff-lcs (~> 1.2, >= 1.2.4) - ed25519 (~> 1.2) + diff-lcs (>= 1.2.4, < 1.4.0) erubis (~> 2.7) - ffi (~> 1.9, >= 1.9.25) - ffi-libarchive + ffi (>= 1.5.0) + ffi-libarchive (~> 1.0, >= 1.0.3) ffi-yajl (~> 2.2) - highline (>= 1.6.9, < 3) iniparse (~> 1.4) - license-acceptance (~> 1.0, >= 1.0.5) + inspec-core (~> 4.23) + license-acceptance (>= 1.0.5, < 3) mixlib-archive (>= 0.4, < 2.0) mixlib-authentication (>= 2.1, < 4) mixlib-cli (>= 2.1.1, < 3.0) mixlib-log (>= 2.0.3, < 4.0) - mixlib-shellout (>= 3.0.3, < 4.0) - net-sftp (~> 2.1, >= 2.1.2) - net-ssh (>= 4.2, < 6) - net-ssh-multi (~> 1.2, >= 1.2.1) - ohai (~> 16.0) - pastel + mixlib-shellout (>= 3.1.1, < 4.0) + net-sftp (>= 2.1.2, < 4.0) + ohai (~> 17.0) plist (~> 3.2) proxifier (~> 1.0) syslog-logger (~> 1.6) train-core (~> 3.2, >= 3.2.28) train-winrm (>= 0.2.5) - tty-screen (~> 0.6) - uuidtools (~> 2.1.5) - chef-cli (3.0.1) - addressable (>= 2.3.5, < 2.8) - chef (>= 15.0) + uuidtools (>= 2.1.5, < 3.0) + vault (~> 0.16) + chef-cli (5.4.2) + addressable (>= 2.3.5, < 2.9) + chef (>= 16.0) cookbook-omnifetch (~> 0.5) - diff-lcs (~> 1.0) + diff-lcs (>= 1.0, < 1.4) ffi-yajl (>= 1.0, < 3.0) - license-acceptance (~> 1.0, >= 1.0.11) + license-acceptance (>= 1.0.11, < 3) minitar (~> 0.6) mixlib-cli (>= 1.7, < 3.0) mixlib-shellout (>= 2.0, < 4.0) - paint (>= 1, < 3) + pastel (~> 0.7) solve (> 2.0, < 5.0) - chef-config (16.1.16) + chef-config (17.6.18) addressable - chef-utils (= 16.1.16) + chef-utils (= 17.6.18) fuzzyurl mixlib-config (>= 2.2.12, < 4.0) mixlib-shellout (>= 2.0, < 4.0) tomlrb (~> 1.2) - chef-telemetry (1.0.8) + chef-telemetry (1.1.1) chef-config concurrent-ruby (~> 1.0) - ffi-yajl (~> 2.2) - chef-utils (16.1.16) - chef-vault (4.0.1) - chef-zero (15.0.0) + chef-utils (17.6.18) + concurrent-ruby + chef-vault (4.1.4) + chef-zero (15.0.11) ffi-yajl (~> 2.2) hashie (>= 2.0, < 5.0) mixlib-log (>= 2.0, < 4.0) rack (~> 2.0, >= 2.0.6) uuidtools (~> 2.1) + webrick cleanroom (1.0.0) coderay (1.1.3) - concurrent-ruby (1.1.6) - cookbook-omnifetch (0.9.1) + concurrent-ruby (1.1.9) + cookbook-omnifetch (0.11.1) mixlib-archive (>= 0.4, < 2.0) - declarative (0.0.10) - declarative-option (0.1.0) + declarative (0.0.20) diff-lcs (1.3) docker-api (1.34.2) excon (>= 0.47.0) multi_json domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) - ecma-re-validator (0.2.1) - regexp_parser (~> 1.2) ed25519 (1.2.4) - equatable (0.6.1) - erubi (1.9.0) + erubi (1.10.0) erubis (2.7.0) - excon (0.73.0) - faraday (0.17.3) + excon (0.87.0) + faraday (1.4.3) + faraday-em_http (~> 1.0) + faraday-em_synchrony (~> 1.0) + faraday-excon (~> 1.1) + faraday-net_http (~> 1.0) + faraday-net_http_persistent (~> 1.1) multipart-post (>= 1.2, < 3) - faraday-cookie_jar (0.0.6) - faraday (>= 0.7.4) + ruby2_keywords (>= 0.0.4) + faraday-cookie_jar (0.0.7) + faraday (>= 0.8.0) http-cookie (~> 1.0.0) - faraday_middleware (0.12.2) - faraday (>= 0.7.4, < 1.0) - ffi (1.13.0) - ffi-libarchive (1.0.0) + faraday-em_http (1.0.0) + faraday-em_synchrony (1.0.0) + faraday-excon (1.1.0) + faraday-net_http (1.0.1) + faraday-net_http_persistent (1.2.0) + faraday_middleware (1.0.0) + faraday (~> 1.0) + ffi (1.15.4) + ffi-libarchive (1.1.3) ffi (~> 1.0) - ffi-yajl (2.3.3) - libyajl2 (~> 1.2) + ffi-yajl (2.4.0) + libyajl2 (>= 1.2) fuzzyurl (0.9.0) - google-api-client (0.34.1) + google-api-client (0.52.0) addressable (~> 2.5, >= 2.5.1) googleauth (~> 0.9) httpclient (>= 2.8.1, < 3.0) mini_mime (~> 1.0) representable (~> 3.0) retriable (>= 2.0, < 4.0) + rexml signet (~> 0.12) - googleauth (0.10.0) - faraday (~> 0.12) + googleauth (0.14.0) + faraday (>= 0.17.3, < 2.0) jwt (>= 1.4, < 3.0) memoist (~> 0.16) multi_json (~> 1.11) os (>= 0.9, < 2.0) - signet (~> 0.12) - gssapi (1.3.0) + signet (~> 0.14) + gssapi (1.3.1) ffi (>= 1.0.1) gyoku (1.3.1) builder (>= 2.1.2) - hana (1.3.6) - hashie (3.6.0) - highline (2.0.3) - htmlentities (4.3.4) - http-cookie (1.0.3) + hashie (4.1.0) + http-cookie (1.0.4) domain_name (~> 0.5) httpclient (2.8.3) - i18n (1.8.2) + i18n (1.8.10) concurrent-ruby (~> 1.0) inifile (3.0.0) iniparse (1.5.0) - inspec (4.19.0) - faraday_middleware (~> 0.12.2) - inspec-core (= 4.19.0) + inspec (4.46.13) + faraday_middleware (>= 0.12.2, < 1.1) + inspec-core (= 4.46.13) + mongo (= 2.13.2) train (~> 3.0) - train-aws (~> 0.1) + train-aws (~> 0.2) train-habitat (~> 0.1) train-winrm (~> 0.2) - inspec-core (4.19.0) + inspec-core (4.46.13) addressable (~> 2.4) - chef-telemetry (~> 1.0) - faraday (>= 0.9.0) - hashie (~> 3.4) - htmlentities (~> 4.3) - json_schemer (~> 0.2.1) - license-acceptance (>= 0.2.13, < 2.0) + chef-telemetry (~> 1.0, >= 1.0.8) + faraday (>= 0.9.0, < 1.5) + faraday_middleware (~> 1.0) + hashie (>= 3.4, < 5.0) + license-acceptance (>= 0.2.13, < 3.0) method_source (>= 0.8, < 2.0) mixlib-log (~> 3.0) multipart-post (~> 2.0) parallel (~> 1.9) - parslet (~> 1.5) + parslet (>= 1.5, < 2.0) pry (~> 0.13) - rspec (~> 3.9) + rspec (>= 3.9, < 3.11) rspec-its (~> 1.2) - rubyzip (~> 1.2, >= 1.2.2) + rubyzip (>= 1.2.2, < 3.0) semverse (~> 3.0) sslshake (~> 1.2) - term-ansicolor (~> 1.7) thor (>= 0.20, < 2.0) - tomlrb (~> 1.2) + tomlrb (>= 1.2, < 2.1) train-core (~> 3.0) tty-prompt (~> 0.17) tty-table (~> 0.10) ipaddress (0.8.3) jmespath (1.4.0) - json (2.3.0) - json_schemer (0.2.11) - ecma-re-validator (~> 0.2) - hana (~> 1.3) - regexp_parser (~> 1.5) - uri_template (~> 0.7) - jwt (2.2.1) + json (2.6.0) + jwt (2.3.0) kitchen-dokken (2.9.0) docker-api (~> 1.33) lockfile (~> 2.1) test-kitchen (>= 1.15, < 3) - kitchen-inspec (1.3.2) - hashie (~> 3.4) - inspec (>= 1.47, < 5.0) - test-kitchen (>= 1.6, < 3) - libyajl2 (1.2.0) - license-acceptance (1.0.19) + kitchen-inspec (2.5.0) + hashie (>= 3.4, <= 5.0) + inspec (>= 2.2.64, < 5.0) + test-kitchen (>= 2.7, < 4) + libyajl2 (2.1.0) + license-acceptance (2.1.13) pastel (~> 0.7) - tomlrb (~> 1.2) - tty-box (~> 0.3) - tty-prompt (~> 0.18) + tomlrb (>= 1.2, < 3.0) + tty-box (~> 0.6) + tty-prompt (~> 0.20) little-plugger (1.1.4) lockfile (2.1.3) - logging (2.2.2) + logging (2.3.0) little-plugger (~> 1.1) - multi_json (~> 1.10) + multi_json (~> 1.14) memoist (0.16.2) method_source (1.0.0) - mini_mime (1.0.2) + mini_mime (1.1.2) minitar (0.9) - minitest (5.14.1) - mixlib-archive (1.0.5) + minitest (5.14.4) + mixlib-archive (1.1.7) mixlib-log - mixlib-authentication (3.0.6) - mixlib-cli (2.1.6) - mixlib-config (3.0.6) + mixlib-authentication (3.0.10) + mixlib-cli (2.1.8) + mixlib-config (3.0.9) tomlrb - mixlib-install (3.12.1) + mixlib-install (3.12.16) mixlib-shellout mixlib-versioning thor - mixlib-log (3.0.8) - mixlib-shellout (3.0.9) + mixlib-log (3.0.9) + mixlib-shellout (3.2.5) + chef-utils mixlib-versioning (1.2.12) - molinillo (0.6.6) + molinillo (0.8.0) + mongo (2.13.2) + bson (>= 4.8.2, < 5.0.0) ms_rest (0.7.6) concurrent-ruby (~> 1.0) faraday (>= 0.9, < 2.0.0) @@ -398,151 +478,151 @@ GEM faraday (>= 0.9, < 2.0.0) faraday-cookie_jar (~> 0.0.6) ms_rest (~> 0.7.6) - multi_json (1.14.1) + multi_json (1.15.0) multipart-post (2.1.1) - necromancer (0.5.1) - net-scp (2.0.0) - net-ssh (>= 2.6.5, < 6.0.0) - net-sftp (2.1.2) - net-ssh (>= 2.6.5) - net-ssh (5.2.0) + net-scp (3.0.0) + net-ssh (>= 2.6.5, < 7.0.0) + net-sftp (3.0.0) + net-ssh (>= 5.0.0, < 7.0.0) + net-ssh (6.1.0) net-ssh-gateway (2.0.0) net-ssh (>= 4.0.0) - net-ssh-multi (1.2.1) - net-ssh (>= 2.6.5) - net-ssh-gateway (>= 1.2.0) nori (2.6.0) - octokit (4.18.0) + octokit (4.21.0) faraday (>= 0.9) sawyer (~> 0.8.0, >= 0.5.3) - ohai (16.1.1) - chef-config (>= 12.8, < 17) - chef-utils (>= 16.0, < 17) + ohai (17.6.0) + chef-config (>= 14.12, < 18) + chef-utils (>= 16.0, < 18) ffi (~> 1.9) ffi-yajl (~> 2.2) ipaddress mixlib-cli (>= 1.7.0) mixlib-config (>= 2.0, < 4.0) mixlib-log (>= 2.0.1, < 4.0) - mixlib-shellout (>= 2.0, < 4.0) + mixlib-shellout (~> 3.2, >= 3.2.5) plist (~> 3.1) - systemu (~> 2.6.4) + train-core wmi-lite (~> 1.0) - os (1.1.0) - paint (2.2.0) - parallel (1.19.1) + os (1.1.1) + parallel (1.21.0) parslet (1.8.2) - pastel (0.7.4) - equatable (~> 0.6) + pastel (0.8.0) tty-color (~> 0.5) - plist (3.5.0) + plist (3.6.0) proxifier (1.0.3) - pry (0.13.1) + pry (0.14.1) coderay (~> 1.1) method_source (~> 1.0) - public_suffix (4.0.5) - rack (2.2.2) - regexp_parser (1.7.0) - representable (3.0.4) + public_suffix (4.0.6) + rack (2.2.3) + representable (3.1.1) declarative (< 0.1.0) - declarative-option (< 0.2.0) + trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) retriable (3.1.2) retryable (3.0.5) - rspec (3.9.0) - rspec-core (~> 3.9.0) - rspec-expectations (~> 3.9.0) - rspec-mocks (~> 3.9.0) - rspec-core (3.9.2) - rspec-support (~> 3.9.3) - rspec-expectations (3.9.2) + rexml (3.2.5) + rspec (3.10.0) + rspec-core (~> 3.10.0) + rspec-expectations (~> 3.10.0) + rspec-mocks (~> 3.10.0) + rspec-core (3.10.1) + rspec-support (~> 3.10.0) + rspec-expectations (3.10.1) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.9.0) + rspec-support (~> 3.10.0) rspec-its (1.3.0) rspec-core (>= 3.0.0) rspec-expectations (>= 3.0.0) - rspec-mocks (3.9.1) + rspec-mocks (3.10.2) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.9.0) - rspec-support (3.9.3) - rubyntlm (0.6.2) - rubyzip (1.3.0) + rspec-support (~> 3.10.0) + rspec-support (3.10.2) + ruby2_keywords (0.0.5) + rubyntlm (0.6.3) + rubyzip (2.3.2) sawyer (0.8.2) addressable (>= 2.3.5) faraday (> 0.8, < 2.0) semverse (3.0.0) - signet (0.14.0) - addressable (~> 2.3) + signet (0.16.0) + addressable (~> 2.8) faraday (>= 0.17.3, < 2.0) jwt (>= 1.5, < 3.0) multi_json (~> 1.10) - solve (4.0.3) + solve (4.0.4) molinillo (~> 0.6) semverse (>= 1.1, < 4.0) - sslshake (1.3.0) - strings (0.1.8) - strings-ansi (~> 0.1) - unicode-display_width (~> 1.5) + sslshake (1.3.1) + strings (0.2.1) + strings-ansi (~> 0.2) + unicode-display_width (>= 1.5, < 3.0) unicode_utils (~> 1.4) strings-ansi (0.2.0) - sync (0.5.0) syslog-logger (1.6.8) - systemu (2.6.5) - term-ansicolor (1.7.1) - tins (~> 1.0) - test-kitchen (2.5.1) + test-kitchen (2.12.0) bcrypt_pbkdf (~> 1.0) + chef-utils (>= 16.4.35) ed25519 (~> 1.2) - license-acceptance (~> 1.0, >= 1.0.11) + license-acceptance (>= 1.0.11, < 3.0) mixlib-install (~> 3.6) mixlib-shellout (>= 1.2, < 4.0) net-scp (>= 1.1, < 4.0) net-ssh (>= 2.9, < 7.0) net-ssh-gateway (>= 1.2, < 3.0) - thor (~> 0.19) + thor (>= 0.19, < 2.0) winrm (~> 2.0) winrm-elevated (~> 1.0) winrm-fs (~> 1.1) - thor (0.20.3) - thread_safe (0.3.6) + thor (1.1.0) timeliness (0.3.10) - tins (1.25.0) - sync tomlrb (1.3.0) - train (3.2.28) - activesupport (~> 5.2.3) + trailblazer-option (0.1.1) + train (3.8.1) + activesupport (>= 6.0.3.1) azure_graph_rbac (~> 0.16) azure_mgmt_key_vault (~> 0.17) azure_mgmt_resources (~> 0.15) azure_mgmt_security (~> 0.18) azure_mgmt_storage (~> 0.18) - docker-api (~> 1.26) - google-api-client (>= 0.23.9, < 0.35.0) - googleauth (>= 0.6.6, < 0.11.0) + docker-api (>= 1.26, < 3.0) + google-api-client (>= 0.23.9, <= 0.52.0) + googleauth (>= 0.6.6, <= 0.14.0) inifile (~> 3.0) - train-core (= 3.2.28) + train-core (= 3.8.1) train-winrm (~> 0.2) - train-aws (0.1.16) + train-aws (0.2.22) + aws-sdk-alexaforbusiness (~> 1.0) + aws-sdk-amplify (~> 1.32.0) aws-sdk-apigateway (~> 1.0) aws-sdk-apigatewayv2 (~> 1.0) + aws-sdk-applicationautoscaling (>= 1.46, < 1.52) aws-sdk-athena (~> 1.0) - aws-sdk-autoscaling (~> 1.22.0) + aws-sdk-autoscaling (>= 1.22, < 1.64) + aws-sdk-batch (>= 1.36, < 1.48) aws-sdk-budgets (~> 1.0) aws-sdk-cloudformation (~> 1.0) + aws-sdk-cloudfront (~> 1.0) aws-sdk-cloudhsm (~> 1.0) aws-sdk-cloudhsmv2 (~> 1.0) aws-sdk-cloudtrail (~> 1.8) aws-sdk-cloudwatch (~> 1.13) + aws-sdk-cloudwatchevents (>= 1.36, < 1.47) aws-sdk-cloudwatchlogs (~> 1.13) aws-sdk-codecommit (~> 1.0) aws-sdk-codedeploy (~> 1.0) aws-sdk-codepipeline (~> 1.0) + aws-sdk-cognitoidentity (>= 1.26, < 1.32) + aws-sdk-cognitoidentityprovider (>= 1.46, < 1.54) aws-sdk-configservice (~> 1.21) aws-sdk-core (~> 3.0) aws-sdk-costandusagereportservice (~> 1.6) + aws-sdk-databasemigrationservice (>= 1.42, < 1.54) aws-sdk-dynamodb (~> 1.31) aws-sdk-ec2 (~> 1.70) aws-sdk-ecr (~> 1.18) + aws-sdk-ecrpublic (~> 1.3) aws-sdk-ecs (~> 1.30) aws-sdk-efs (~> 1.0) aws-sdk-eks (~> 1.9) @@ -551,67 +631,84 @@ GEM aws-sdk-elasticloadbalancing (~> 1.8) aws-sdk-elasticloadbalancingv2 (~> 1.0) aws-sdk-elasticsearchservice (~> 1.0) + aws-sdk-emr (~> 1.53.0) + aws-sdk-eventbridge (~> 1.24.0) aws-sdk-firehose (~> 1.0) + aws-sdk-glue (>= 1.71, < 1.89) + aws-sdk-guardduty (~> 1.31) aws-sdk-iam (~> 1.13) aws-sdk-kafka (~> 1.0) aws-sdk-kinesis (~> 1.0) aws-sdk-kms (~> 1.13) aws-sdk-lambda (~> 1.0) - aws-sdk-organizations (~> 1.17.0) + aws-sdk-mq (~> 1.40.0) + aws-sdk-networkfirewall (>= 1.6.0) + aws-sdk-networkmanager (>= 1.13.0) + aws-sdk-organizations (>= 1.17, < 1.60) + aws-sdk-ram (>= 1.21, < 1.27) aws-sdk-rds (~> 1.43) aws-sdk-redshift (~> 1.0) aws-sdk-route53 (~> 1.0) aws-sdk-route53domains (~> 1.0) aws-sdk-route53resolver (~> 1.0) aws-sdk-s3 (~> 1.30) + aws-sdk-secretsmanager (>= 1.42, < 1.47) aws-sdk-securityhub (~> 1.0) - aws-sdk-ses (~> 1.0) + aws-sdk-servicecatalog (>= 1.48, < 1.61) + aws-sdk-ses (~> 1.41.0) + aws-sdk-shield (~> 1.30) + aws-sdk-signer (~> 1.32.0) + aws-sdk-simpledb (~> 1.29.0) aws-sdk-sms (~> 1.0) aws-sdk-sns (~> 1.9) aws-sdk-sqs (~> 1.10) aws-sdk-ssm (~> 1.0) - train-core (3.2.28) + aws-sdk-states (>= 1.35, < 1.40) + aws-sdk-synthetics (~> 1.19.0) + aws-sdk-transfer (>= 1.26, < 1.35) + train-core (3.8.1) addressable (~> 2.5) + ffi (!= 1.13.0) json (>= 1.8, < 3.0) mixlib-shellout (>= 2.0, < 4.0) - net-scp (>= 1.2, < 3.0) - net-ssh (>= 2.9, < 6.0) - train-habitat (0.2.13) - train-winrm (0.2.6) - winrm (~> 2.0) + net-scp (>= 1.2, < 4.0) + net-ssh (>= 2.9, < 7.0) + train-habitat (0.2.22) + train-winrm (0.2.12) + winrm (>= 2.3.6, < 3.0) + winrm-elevated (~> 1.2.2) winrm-fs (~> 1.0) - tty-box (0.5.0) - pastel (~> 0.7.2) - strings (~> 0.1.6) + tty-box (0.7.0) + pastel (~> 0.8) + strings (~> 0.2.0) tty-cursor (~> 0.7) - tty-color (0.5.1) + tty-color (0.6.0) tty-cursor (0.7.1) - tty-prompt (0.21.0) - necromancer (~> 0.5.0) - pastel (~> 0.7.0) - tty-reader (~> 0.7.0) - tty-reader (0.7.0) + tty-prompt (0.23.1) + pastel (~> 0.8) + tty-reader (~> 0.8) + tty-reader (0.9.0) tty-cursor (~> 0.7) - tty-screen (~> 0.7) - wisper (~> 2.0.0) - tty-screen (0.8.0) - tty-table (0.11.0) - equatable (~> 0.6) - necromancer (~> 0.5) - pastel (~> 0.7.2) - strings (~> 0.1.5) - tty-screen (~> 0.7) - tzinfo (1.2.7) - thread_safe (~> 0.1) + tty-screen (~> 0.8) + wisper (~> 2.0) + tty-screen (0.8.1) + tty-table (0.12.0) + pastel (~> 0.8) + strings (~> 0.2.0) + tty-screen (~> 0.8) + tzinfo (2.0.4) + concurrent-ruby (~> 1.0) uber (0.1.0) unf (0.1.4) unf_ext - unf_ext (0.0.7.7) - unicode-display_width (1.7.0) + unf_ext (0.0.8) + unicode-display_width (2.1.0) unicode_utils (1.4.0) - uri_template (0.7.0) - uuidtools (2.1.5) - winrm (2.3.4) + uuidtools (2.2.0) + vault (0.16.0) + aws-sigv4 + webrick (1.7.0) + winrm (2.3.6) builder (>= 2.1.2) erubi (~> 1.8) gssapi (~> 1.2) @@ -619,18 +716,19 @@ GEM httpclient (~> 2.2, >= 2.2.0.2) logging (>= 1.6.1, < 3.0) nori (~> 2.0) - rubyntlm (~> 0.6.0, >= 0.6.1) - winrm-elevated (1.2.1) + rubyntlm (~> 0.6.0, >= 0.6.3) + winrm-elevated (1.2.3) erubi (~> 1.8) winrm (~> 2.0) winrm-fs (~> 1.0) - winrm-fs (1.3.3) + winrm-fs (1.3.5) erubi (~> 1.8) logging (>= 1.6.1, < 3.0) - rubyzip (~> 1.1) + rubyzip (~> 2.0) winrm (~> 2.0) wisper (2.0.1) wmi-lite (1.0.5) + zeitwerk (2.5.0) PLATFORMS ruby @@ -647,4 +745,4 @@ DEPENDENCIES test-kitchen! BUNDLED WITH - 2.1.2 + 2.1.4 diff --git a/Gruntfile.js b/Gruntfile.js index 3868784096..1f5695ece2 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -116,8 +116,10 @@ module.exports = function(grunt) { 'third_party/firefox_logo.png', 'third_party/FirefoxAMO_black.svg', 'third_party/FirefoxAMO_white.svg', - 'third_party/ChromeWebStore_black.png', - 'third_party/ChromeWebStore_white.png', + 'third_party/ChromeWebStore_black.svg', + 'third_party/ChromeWebStore_white.svg', + 'third_party/edge-addon-black.svg', + 'third_party/edge-addon-white.svg', 'third_party/chosen-sprite.png', 'third_party/chosen-sprite@2x.png', // Setup @@ -130,13 +132,13 @@ module.exports = function(grunt) { }, { // CSS cwd: paths.node_modules_styleguide + 'build/css/themes/default', - src: ['api_main.min.css', 'api_webinstaller.min.css', 'api_authentication.min.css'], + src: ['api_main.min.css', 'api_webinstaller.min.css', 'api_authentication.min.css', 'ext_authentication.min.css'], dest: paths.webroot + 'css/themes/default', expand: true }, { // Midgar css theme cwd: paths.node_modules_styleguide + 'build/css/themes/midgar', - src: ['api_main.min.css'], + src: ['api_main.min.css', 'api_webinstaller.min.css', 'api_authentication.min.css', 'ext_authentication.min.css'], dest: paths.webroot + 'css/themes/midgar', expand: true },{ diff --git a/README.md b/README.md index 0f44b6b3d9..0748a9e3b3 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,9 @@ Copyright (c) 2021 Passbolt SA https://www.passbolt.com +[![PHPStan Enabled](./webroot/img/third_party/phpstan.svg)](https://github.com/phpstan/phpstan) +[![Psalm level](./webroot/img/third_party/psalm.svg)](https://psalm.dev/) + ## License Passbolt - Open source password manager for teams @@ -32,7 +35,7 @@ see [GNU Affero General Public License v3](http://www.gnu.org/licenses/agpl-3.0. Passbolt is an open source password manager for teams. It allows you to securely share and store credentials. For instance, the wifi password of your -office, the administrator password of a router or your organisation's social +office, the administrator password of a router or your organization's social media account passwords, all of them can be secured using passbolt. Passbolt is different from the other password managers because: diff --git a/Tiltfile b/Tiltfile new file mode 100644 index 0000000000..183ef6905e --- /dev/null +++ b/Tiltfile @@ -0,0 +1,33 @@ +load("ext://docker_build_sub", "docker_build_sub") +# Allow certain kubernetes contexts to deploy to and avoid the "accidental prod deploy" +allow_k8s_contexts('kind-kind') + +docker_build_sub('passbolt-ce', '../passbolt_docker', child_context='.', + extra_cmds=['COPY . /var/www/passbolt', + 'RUN apt-get update && apt-get install -y git unzip \ + && EXPECTED_SIGNATURE=$(curl -s https://composer.github.io/installer.sig) \ + && curl -o composer-setup.php https://getcomposer.org/installer \ + && php composer-setup.php --1 \ + && mv composer.phar /usr/local/bin/composer \ + && composer install -n \ + && chown -R www-data:www-data vendor' + ], + live_update=[ + sync('.', '/var/www/passbolt'), + run('cd /var/www/passbolt && composer install -n && chown -R www-data:www-data vendor', trigger=['./composer.json']) + ]) +# Helm chart path +path = '../../charts/charts-passbolt' +watch_file(path) +watch_file('../passbolt_docker') +yaml = helm( + path, + name = 'passbolt-ce', + namespace = 'on-prem', + values = ["{}/values-local-ce.yaml".format(path)], + ) +k8s_yaml(yaml) + +k8s_resource('passbolt-ce-job-enable-selenium', resource_deps=['passbolt-ce-depl-srv']) +k8s_resource('passbolt-ce-depl-srv', resource_deps=['passbolt-ce-job-init-databases']) +k8s_resource('passbolt-ce-job-init-databases', resource_deps=['mariadb']) diff --git a/bin/cron b/bin/cron index 9142e5a5b5..b7decea3fc 100755 --- a/bin/cron +++ b/bin/cron @@ -11,5 +11,5 @@ DIR=$(dirname "$(readlink -f "$0")") if [[ "$DIGEST_ENABLED" = 0 ]]; then "$DIR"/cake EmailQueue.sender else - "$DIR"/cake Passbolt/EmailDigest.sender + "$DIR"/cake passbolt email_digest send fi diff --git a/composer.json b/composer.json index e1c61882e4..0a12dc1604 100644 --- a/composer.json +++ b/composer.json @@ -49,19 +49,22 @@ "ext-intl" : "*", "ext-mbstring" : "*", "ext-gnupg" : "*", - "cakephp/cakephp": "4.2.8", + "ext-pdo": "*", + "ext-openssl": "*", + "cakephp/cakephp": "4.2.9", "cakephp/migrations": "^3.0.0", - "robmorgan/phinx":"^0.12.8", + "robmorgan/phinx":"^0.12.8", "cakephp/plugin-installer": "^1.3.0", "mobiledetect/mobiledetectlib": "^2.8.34", "ramsey/uuid": "^4.1.1", - "singpolyma/openpgp-php": "dev-master#c961eca13df86a4e9af6ef1ebd9da7d3858d75c8", + "singpolyma/openpgp-php": "0.5.0", "donatj/phpuseragentparser": "^1.1.0", "lorenzo/cakephp-email-queue": "^4.0.0", "imagine/imagine": "^1.2.4", - "league/flysystem": "^2.0.3", - "cakephp/authentication": "^2.6.0", - "bcrowe/cakephp-api-pagination": "^2.1.0" + "league/flysystem": "^2.1.1", + "cakephp/authentication": "^2.7.0", + "bcrowe/cakephp-api-pagination": "^2.1.0", + "firebase/php-jwt": "^5.2" }, "require-dev": { "phpstan/phpstan": "0.12.81", @@ -70,23 +73,27 @@ "cakephp/bake": "^2.3.0", "phpunit/phpunit": "^9.5.2", "passbolt/cakephp-codesniffer": "dev-master", - "passbolt/passbolt-selenium-api": "^3.1.1", + "passbolt/passbolt-selenium-api": "^3.1.2", "passbolt/passbolt-test-data": "^3.2.0", - "vierge-noire/cakephp-fixture-factories": "^2.2", - "vierge-noire/cakephp-test-migrator": "^2.1.3", - "cakephp/localized": "4.0.0-beta" + "vierge-noire/cakephp-fixture-factories": "v2.4", + "cakephp/localized": "4.0.0", + "vimeo/psalm": "^4.9" }, "autoload": { "psr-4": { "App\\": "src", "Passbolt\\AccountSettings\\": "./plugins/Passbolt/AccountSettings/src", + "Passbolt\\InFormIntegration\\": "./plugins/Passbolt/InFormIntegration/src", "Passbolt\\Locale\\": "./plugins/Passbolt/Locale/src", + "Passbolt\\PasswordGenerator\\": "./plugins/Passbolt/PasswordGenerator/src", "Passbolt\\RememberMe\\": "./plugins/Passbolt/RememberMe/src", "Passbolt\\WebInstaller\\": "./plugins/Passbolt/WebInstaller/src", "Passbolt\\Log\\": "./plugins/Passbolt/Log/src", "Passbolt\\EmailNotificationSettings\\": "./plugins/Passbolt/EmailNotificationSettings/src", "Passbolt\\EmailDigest\\": "plugins/Passbolt/EmailDigest/src", - "Passbolt\\Reports\\": "./plugins/Passbolt/Reports/src" + "Passbolt\\Reports\\": "./plugins/Passbolt/Reports/src", + "Passbolt\\Mobile\\": "./plugins/Passbolt/Mobile/src", + "Passbolt\\JwtAuthentication\\": "./plugins/Passbolt/JwtAuthentication/src" } }, "autoload-dev": { @@ -94,12 +101,16 @@ "App\\Test\\": "tests", "Cake\\Test\\": "./vendor/cakephp/cakephp/tests", "Passbolt\\AccountSettings\\Test\\": "./plugins/Passbolt/AccountSettings/tests", + "Passbolt\\InFormIntegration\\Test\\": "./plugins/Passbolt/InFormIntegration/tests", "Passbolt\\Locale\\Test\\": "./plugins/Passbolt/Locale/tests", + "Passbolt\\PasswordGenerator\\Test\\": "./plugins/Passbolt/PasswordGenerator/tests", "Passbolt\\WebInstaller\\Test\\": "./plugins/Passbolt/WebInstaller/tests", "Passbolt\\Log\\Test\\": "./plugins/Passbolt/Log/tests", "Passbolt\\EmailNotificationSettings\\Test\\": "./plugins/Passbolt/EmailNotificationSettings/tests", "Passbolt\\EmailDigest\\Test\\": "plugins/Passbolt/EmailDigest/tests", - "Passbolt\\Reports\\Test\\": "./plugins/Passbolt/Reports/tests" + "Passbolt\\Reports\\Test\\": "./plugins/Passbolt/Reports/tests", + "Passbolt\\Mobile\\Test\\": "./plugins/Passbolt/Mobile/tests", + "Passbolt\\JwtAuthentication\\Test\\": "./plugins/Passbolt/JwtAuthentication/tests" } }, "scripts": { @@ -118,6 +129,7 @@ "./node_modules/.bin/grunt styleguide-update" ], "stan": "phpstan analyse --memory-limit=-1", + "psalm": "psalm", "test": "phpunit --colors=always", "externalize-locale-strings": [ "./bin/cake i18n extract --app ./ --paths src,plugins,templates --output resources/locales/en_UK --exclude /tests,/vendors,/src/Command --overwrite --extract-core no --no-location --merge yes", diff --git a/composer.lock b/composer.lock index 055409b052..271f5f6285 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ebbd688809069a688ba9e042cf00ccf4", + "content-hash": "feb015b1b5c9f0d0c2dbe76230d531f5", "packages": [ { "name": "bcrowe/cakephp-api-pagination", @@ -129,16 +129,16 @@ }, { "name": "cakephp/authentication", - "version": "2.6.1", + "version": "2.7.0", "source": { "type": "git", "url": "https://github.com/cakephp/authentication.git", - "reference": "e4c17b4ad3005a9c14a1c76e772e5d2006864d2f" + "reference": "6c6291bad56add2a947eaaf01a0d68d1c350f66e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/authentication/zipball/e4c17b4ad3005a9c14a1c76e772e5d2006864d2f", - "reference": "e4c17b4ad3005a9c14a1c76e772e5d2006864d2f", + "url": "https://api.github.com/repos/cakephp/authentication/zipball/6c6291bad56add2a947eaaf01a0d68d1c350f66e", + "reference": "6c6291bad56add2a947eaaf01a0d68d1c350f66e", "shasum": "" }, "require": { @@ -152,7 +152,7 @@ "require-dev": { "cakephp/cakephp": "^4.0", "cakephp/cakephp-codesniffer": "^4.0", - "firebase/php-jwt": "^5.0", + "firebase/php-jwt": "^5.5", "phpunit/phpunit": "^8.5 || ^9.3" }, "suggest": { @@ -193,20 +193,20 @@ "issues": "https://github.com/cakephp/authentication/issues", "source": "https://github.com/cakephp/authentication" }, - "time": "2021-04-23T14:31:23+00:00" + "time": "2021-11-25T18:06:07+00:00" }, { "name": "cakephp/cakephp", - "version": "4.2.8", + "version": "4.2.9", "source": { "type": "git", "url": "https://github.com/cakephp/cakephp.git", - "reference": "f667f708baeaf700bb7f3934fe2cc75c240d77bf" + "reference": "6cb068fd328219d79a02ef6df439b73459071368" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/cakephp/zipball/f667f708baeaf700bb7f3934fe2cc75c240d77bf", - "reference": "f667f708baeaf700bb7f3934fe2cc75c240d77bf", + "url": "https://api.github.com/repos/cakephp/cakephp/zipball/6cb068fd328219d79a02ef6df439b73459071368", + "reference": "6cb068fd328219d79a02ef6df439b73459071368", "shasum": "" }, "require": { @@ -296,20 +296,20 @@ "issues": "https://github.com/cakephp/cakephp/issues", "source": "https://github.com/cakephp/cakephp" }, - "time": "2021-07-17T02:15:55+00:00" + "time": "2021-09-11T00:24:13+00:00" }, { "name": "cakephp/chronos", - "version": "2.2.0", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/cakephp/chronos.git", - "reference": "556e14da67307ffc2e68beeb7df0694dc8d1207d" + "reference": "3ecd6e7ae191c676570cd1bed51fd561de4606dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/chronos/zipball/556e14da67307ffc2e68beeb7df0694dc8d1207d", - "reference": "556e14da67307ffc2e68beeb7df0694dc8d1207d", + "url": "https://api.github.com/repos/cakephp/chronos/zipball/3ecd6e7ae191c676570cd1bed51fd561de4606dd", + "reference": "3ecd6e7ae191c676570cd1bed51fd561de4606dd", "shasum": "" }, "require": { @@ -355,7 +355,7 @@ "issues": "https://github.com/cakephp/chronos/issues", "source": "https://github.com/cakephp/chronos" }, - "time": "2021-06-17T13:49:10+00:00" + "time": "2021-10-17T02:44:05+00:00" }, { "name": "cakephp/migrations", @@ -468,16 +468,16 @@ }, { "name": "composer/ca-bundle", - "version": "1.2.10", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "9fdb22c2e97a614657716178093cd1da90a64aa8" + "reference": "4c679186f2aca4ab6a0f1b0b9cf9252decb44d0b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/9fdb22c2e97a614657716178093cd1da90a64aa8", - "reference": "9fdb22c2e97a614657716178093cd1da90a64aa8", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/4c679186f2aca4ab6a0f1b0b9cf9252decb44d0b", + "reference": "4c679186f2aca4ab6a0f1b0b9cf9252decb44d0b", "shasum": "" }, "require": { @@ -489,7 +489,7 @@ "phpstan/phpstan": "^0.12.55", "psr/log": "^1.0", "symfony/phpunit-bridge": "^4.2 || ^5", - "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0" + "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0 || ^6.0" }, "type": "library", "extra": { @@ -524,7 +524,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/ca-bundle/issues", - "source": "https://github.com/composer/ca-bundle/tree/1.2.10" + "source": "https://github.com/composer/ca-bundle/tree/1.3.1" }, "funding": [ { @@ -540,24 +540,24 @@ "type": "tidelift" } ], - "time": "2021-06-07T13:58:28+00:00" + "time": "2021-10-28T20:44:15+00:00" }, { "name": "donatj/phpuseragentparser", - "version": "v1.4.0", + "version": "v1.5.0", "source": { "type": "git", "url": "https://github.com/donatj/PhpUserAgent.git", - "reference": "246c1cf0a44f07168c702203bf30d5f48f17bab0" + "reference": "cc9d872cddfc180c52d084d0dff1e4aad653d37f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/donatj/PhpUserAgent/zipball/246c1cf0a44f07168c702203bf30d5f48f17bab0", - "reference": "246c1cf0a44f07168c702203bf30d5f48f17bab0", + "url": "https://api.github.com/repos/donatj/PhpUserAgent/zipball/cc9d872cddfc180c52d084d0dff1e4aad653d37f", + "reference": "cc9d872cddfc180c52d084d0dff1e4aad653d37f", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=5.4.0" }, "require-dev": { "camspiers/json-pretty": "~1.0", @@ -596,7 +596,7 @@ ], "support": { "issues": "https://github.com/donatj/PhpUserAgent/issues", - "source": "https://github.com/donatj/PhpUserAgent/tree/v1.4.0" + "source": "https://github.com/donatj/PhpUserAgent/tree/v1.5.0" }, "funding": [ { @@ -608,19 +608,76 @@ "type": "github" } ], - "time": "2021-03-16T16:25:14+00:00" + "time": "2021-09-16T17:05:03+00:00" + }, + { + "name": "firebase/php-jwt", + "version": "v5.5.1", + "source": { + "type": "git", + "url": "https://github.com/firebase/php-jwt.git", + "reference": "83b609028194aa042ea33b5af2d41a7427de80e6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/firebase/php-jwt/zipball/83b609028194aa042ea33b5af2d41a7427de80e6", + "reference": "83b609028194aa042ea33b5af2d41a7427de80e6", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": ">=4.8 <=9" + }, + "suggest": { + "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present" + }, + "type": "library", + "autoload": { + "psr-4": { + "Firebase\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Neuman Vong", + "email": "neuman+pear@twilio.com", + "role": "Developer" + }, + { + "name": "Anant Narayanan", + "email": "anant@php.net", + "role": "Developer" + } + ], + "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", + "homepage": "https://github.com/firebase/php-jwt", + "keywords": [ + "jwt", + "php" + ], + "support": { + "issues": "https://github.com/firebase/php-jwt/issues", + "source": "https://github.com/firebase/php-jwt/tree/v5.5.1" + }, + "time": "2021-11-08T20:18:51+00:00" }, { "name": "imagine/imagine", "version": "1.2.4", "source": { "type": "git", - "url": "https://github.com/avalanche123/Imagine.git", + "url": "https://github.com/php-imagine/Imagine.git", "reference": "d2e18be6e930ca169e4f921ef73ebfc061bf55d8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/avalanche123/Imagine/zipball/d2e18be6e930ca169e4f921ef73ebfc061bf55d8", + "url": "https://api.github.com/repos/php-imagine/Imagine/zipball/d2e18be6e930ca169e4f921ef73ebfc061bf55d8", "reference": "d2e18be6e930ca169e4f921ef73ebfc061bf55d8", "shasum": "" }, @@ -667,41 +724,38 @@ "image processing" ], "support": { - "issues": "https://github.com/avalanche123/Imagine/issues", - "source": "https://github.com/avalanche123/Imagine/tree/1.2.4" + "issues": "https://github.com/php-imagine/Imagine/issues", + "source": "https://github.com/php-imagine/Imagine/tree/1.2.4" }, "time": "2020-11-03T22:35:03+00:00" }, { "name": "laminas/laminas-diactoros", - "version": "2.6.0", + "version": "2.8.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-diactoros.git", - "reference": "7d2034110ae18afe05050b796a3ee4b3fe177876" + "reference": "0c26ef1d95b6d7e6e3943a243ba3dc0797227199" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/7d2034110ae18afe05050b796a3ee4b3fe177876", - "reference": "7d2034110ae18afe05050b796a3ee4b3fe177876", + "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/0c26ef1d95b6d7e6e3943a243ba3dc0797227199", + "reference": "0c26ef1d95b6d7e6e3943a243ba3dc0797227199", "shasum": "" }, "require": { - "laminas/laminas-zendframework-bridge": "^1.0", - "php": "^7.3 || ~8.0.0", + "php": "^7.3 || ~8.0.0 || ~8.1.0", "psr/http-factory": "^1.0", "psr/http-message": "^1.0" }, "conflict": { - "phpspec/prophecy": "<1.9.0" + "phpspec/prophecy": "<1.9.0", + "zendframework/zend-diactoros": "*" }, "provide": { "psr/http-factory-implementation": "1.0", "psr/http-message-implementation": "1.0" }, - "replace": { - "zendframework/zend-diactoros": "^2.2.1" - }, "require-dev": { "ext-curl": "*", "ext-dom": "*", @@ -772,25 +826,25 @@ "type": "community_bridge" } ], - "time": "2021-05-18T14:41:54+00:00" + "time": "2021-09-22T03:54:36+00:00" }, { "name": "laminas/laminas-httphandlerrunner", - "version": "1.4.0", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-httphandlerrunner.git", - "reference": "6a2dd33e4166469ade07ad1283b45924383b224b" + "reference": "5f94e55d93f756e8ad07b9049aeb3d6d84582d0e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-httphandlerrunner/zipball/6a2dd33e4166469ade07ad1283b45924383b224b", - "reference": "6a2dd33e4166469ade07ad1283b45924383b224b", + "url": "https://api.github.com/repos/laminas/laminas-httphandlerrunner/zipball/5f94e55d93f756e8ad07b9049aeb3d6d84582d0e", + "reference": "5f94e55d93f756e8ad07b9049aeb3d6d84582d0e", "shasum": "" }, "require": { "laminas/laminas-zendframework-bridge": "^1.0", - "php": "^7.3 || ~8.0.0", + "php": "^7.3 || ~8.0.0 || ~8.1.0", "psr/http-message": "^1.0", "psr/http-message-implementation": "^1.0", "psr/http-server-handler": "^1.0" @@ -800,10 +854,10 @@ }, "require-dev": { "laminas/laminas-coding-standard": "~1.0.0", - "laminas/laminas-diactoros": "^2.1.1", - "phpunit/phpunit": "^9.3", - "psalm/plugin-phpunit": "^0.15.1", - "vimeo/psalm": "^4.6" + "laminas/laminas-diactoros": "^2.8.0", + "phpunit/phpunit": "^9.5.9", + "psalm/plugin-phpunit": "^0.16.1", + "vimeo/psalm": "^4.10.0" }, "type": "library", "extra": { @@ -843,27 +897,27 @@ "type": "community_bridge" } ], - "time": "2021-04-08T13:52:56+00:00" + "time": "2021-09-22T09:17:54+00:00" }, { "name": "laminas/laminas-zendframework-bridge", - "version": "1.3.0", + "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-zendframework-bridge.git", - "reference": "13af2502d9bb6f7d33be2de4b51fb68c6cdb476e" + "reference": "bf180a382393e7db5c1e8d0f2ec0c4af9c724baf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-zendframework-bridge/zipball/13af2502d9bb6f7d33be2de4b51fb68c6cdb476e", - "reference": "13af2502d9bb6f7d33be2de4b51fb68c6cdb476e", + "url": "https://api.github.com/repos/laminas/laminas-zendframework-bridge/zipball/bf180a382393e7db5c1e8d0f2ec0c4af9c724baf", + "reference": "bf180a382393e7db5c1e8d0f2ec0c4af9c724baf", "shasum": "" }, "require": { - "php": "^7.3 || ^8.0" + "php": "^7.3 || ~8.0.0 || ~8.1.0" }, "require-dev": { - "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.1 || ^9.3", + "phpunit/phpunit": "^9.3", "psalm/plugin-phpunit": "^0.15.1", "squizlabs/php_codesniffer": "^3.5", "vimeo/psalm": "^4.6" @@ -905,7 +959,7 @@ "type": "community_bridge" } ], - "time": "2021-06-24T12:49:22+00:00" + "time": "2021-09-03T17:53:30+00:00" }, { "name": "league/container", @@ -988,16 +1042,16 @@ }, { "name": "league/flysystem", - "version": "2.2.3", + "version": "2.3.1", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "811bdc2d52a07eafbb6cb68a7b368b0668b362c8" + "reference": "8f037e8f3321d5fd4178dcd4d91498220cebd688" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/811bdc2d52a07eafbb6cb68a7b368b0668b362c8", - "reference": "811bdc2d52a07eafbb6cb68a7b368b0668b362c8", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/8f037e8f3321d5fd4178dcd4d91498220cebd688", + "reference": "8f037e8f3321d5fd4178dcd4d91498220cebd688", "shasum": "" }, "require": { @@ -1014,7 +1068,7 @@ "aws/aws-sdk-php": "^3.132.4", "composer/semver": "^3.0", "ext-fileinfo": "*", - "friendsofphp/php-cs-fixer": "^2.16", + "friendsofphp/php-cs-fixer": "^3.2", "google/cloud-storage": "^1.23", "phpseclib/phpseclib": "^2.0", "phpstan/phpstan": "^0.12.26", @@ -1053,7 +1107,7 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/2.2.3" + "source": "https://github.com/thephpleague/flysystem/tree/2.3.1" }, "funding": [ { @@ -1069,20 +1123,20 @@ "type": "tidelift" } ], - "time": "2021-08-18T19:59:31+00:00" + "time": "2021-11-04T20:29:23+00:00" }, { "name": "league/mime-type-detection", - "version": "1.7.0", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/thephpleague/mime-type-detection.git", - "reference": "3b9dff8aaf7323590c1d2e443db701eb1f9aa0d3" + "reference": "b38b25d7b372e9fddb00335400467b223349fd7e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/3b9dff8aaf7323590c1d2e443db701eb1f9aa0d3", - "reference": "3b9dff8aaf7323590c1d2e443db701eb1f9aa0d3", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/b38b25d7b372e9fddb00335400467b223349fd7e", + "reference": "b38b25d7b372e9fddb00335400467b223349fd7e", "shasum": "" }, "require": { @@ -1113,7 +1167,7 @@ "description": "Mime-type detection for Flysystem", "support": { "issues": "https://github.com/thephpleague/mime-type-detection/issues", - "source": "https://github.com/thephpleague/mime-type-detection/tree/1.7.0" + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.8.0" }, "funding": [ { @@ -1125,7 +1179,7 @@ "type": "tidelift" } ], - "time": "2021-01-18T20:58:21+00:00" + "time": "2021-09-25T08:23:19+00:00" }, { "name": "lorenzo/cakephp-email-queue", @@ -1244,16 +1298,16 @@ }, { "name": "phpseclib/phpseclib", - "version": "2.0.33", + "version": "2.0.34", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "fb53b7889497ec7c1362c94e61d8127ac67ea094" + "reference": "98a6fe587f3481aea319eef7e656d02cfe1675ec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/fb53b7889497ec7c1362c94e61d8127ac67ea094", - "reference": "fb53b7889497ec7c1362c94e61d8127ac67ea094", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/98a6fe587f3481aea319eef7e656d02cfe1675ec", + "reference": "98a6fe587f3481aea319eef7e656d02cfe1675ec", "shasum": "" }, "require": { @@ -1333,7 +1387,7 @@ ], "support": { "issues": "https://github.com/phpseclib/phpseclib/issues", - "source": "https://github.com/phpseclib/phpseclib/tree/2.0.33" + "source": "https://github.com/phpseclib/phpseclib/tree/2.0.34" }, "funding": [ { @@ -1349,7 +1403,7 @@ "type": "tidelift" } ], - "time": "2021-08-16T04:20:12+00:00" + "time": "2021-10-27T02:46:30+00:00" }, { "name": "psr/container", @@ -1776,16 +1830,16 @@ }, { "name": "ramsey/collection", - "version": "1.2.1", + "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/ramsey/collection.git", - "reference": "eaca1dc1054ddd10cbd83c1461907bee6fb528fa" + "reference": "cccc74ee5e328031b15640b51056ee8d3bb66c0a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/collection/zipball/eaca1dc1054ddd10cbd83c1461907bee6fb528fa", - "reference": "eaca1dc1054ddd10cbd83c1461907bee6fb528fa", + "url": "https://api.github.com/repos/ramsey/collection/zipball/cccc74ee5e328031b15640b51056ee8d3bb66c0a", + "reference": "cccc74ee5e328031b15640b51056ee8d3bb66c0a", "shasum": "" }, "require": { @@ -1839,7 +1893,7 @@ ], "support": { "issues": "https://github.com/ramsey/collection/issues", - "source": "https://github.com/ramsey/collection/tree/1.2.1" + "source": "https://github.com/ramsey/collection/tree/1.2.2" }, "funding": [ { @@ -1851,28 +1905,29 @@ "type": "tidelift" } ], - "time": "2021-08-06T03:41:06+00:00" + "time": "2021-10-10T03:01:02+00:00" }, { "name": "ramsey/uuid", - "version": "4.2.1", + "version": "4.2.3", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "fe665a03df4f056aa65af552a96e1976df8c8dae" + "reference": "fc9bb7fb5388691fd7373cd44dcb4d63bbcf24df" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/fe665a03df4f056aa65af552a96e1976df8c8dae", - "reference": "fe665a03df4f056aa65af552a96e1976df8c8dae", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/fc9bb7fb5388691fd7373cd44dcb4d63bbcf24df", + "reference": "fc9bb7fb5388691fd7373cd44dcb4d63bbcf24df", "shasum": "" }, "require": { "brick/math": "^0.8 || ^0.9", "ext-json": "*", - "php": "^7.2 || ^8", + "php": "^7.2 || ^8.0", "ramsey/collection": "^1.0", - "symfony/polyfill-ctype": "^1.8" + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-php80": "^1.14" }, "replace": { "rhumsaa/uuid": "self.version" @@ -1936,7 +1991,7 @@ ], "support": { "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.2.1" + "source": "https://github.com/ramsey/uuid/tree/4.2.3" }, "funding": [ { @@ -1948,20 +2003,20 @@ "type": "tidelift" } ], - "time": "2021-08-11T01:06:55+00:00" + "time": "2021-09-25T23:10:38+00:00" }, { "name": "robmorgan/phinx", - "version": "0.12.8", + "version": "0.12.9", "source": { "type": "git", "url": "https://github.com/cakephp/phinx.git", - "reference": "d2ed1b452cc90f4cae4ea6b5976b94fb9e5ed2a2" + "reference": "5a0146a74c1bc195d1f5da86afa3b68badf7d90e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/phinx/zipball/d2ed1b452cc90f4cae4ea6b5976b94fb9e5ed2a2", - "reference": "d2ed1b452cc90f4cae4ea6b5976b94fb9e5ed2a2", + "url": "https://api.github.com/repos/cakephp/phinx/zipball/5a0146a74c1bc195d1f5da86afa3b68badf7d90e", + "reference": "5a0146a74c1bc195d1f5da86afa3b68badf7d90e", "shasum": "" }, "require": { @@ -2032,22 +2087,22 @@ ], "support": { "issues": "https://github.com/cakephp/phinx/issues", - "source": "https://github.com/cakephp/phinx/tree/0.12.8" + "source": "https://github.com/cakephp/phinx/tree/0.12.9" }, - "time": "2021-07-28T15:31:39+00:00" + "time": "2021-10-12T10:49:25+00:00" }, { "name": "singpolyma/openpgp-php", - "version": "dev-master", + "version": "0.5.0", "source": { "type": "git", "url": "https://github.com/singpolyma/openpgp-php.git", - "reference": "c961eca13df86a4e9af6ef1ebd9da7d3858d75c8" + "reference": "69292f6a46ed7f687083bfb8974b161a41ab213c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/singpolyma/openpgp-php/zipball/c961eca13df86a4e9af6ef1ebd9da7d3858d75c8", - "reference": "c961eca13df86a4e9af6ef1ebd9da7d3858d75c8", + "url": "https://api.github.com/repos/singpolyma/openpgp-php/zipball/69292f6a46ed7f687083bfb8974b161a41ab213c", + "reference": "69292f6a46ed7f687083bfb8974b161a41ab213c", "shasum": "" }, "require": { @@ -2060,7 +2115,6 @@ "suggest": { "ext-mcrypt": "required if you use encryption cast5" }, - "default-branch": true, "type": "library", "autoload": { "classmap": [ @@ -2104,16 +2158,16 @@ }, { "name": "symfony/config", - "version": "v5.3.4", + "version": "v5.3.10", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "4268f3059c904c61636275182707f81645517a37" + "reference": "ac23c2f24d5634966d665d836c3933d54347e5d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/4268f3059c904c61636275182707f81645517a37", - "reference": "4268f3059c904c61636275182707f81645517a37", + "url": "https://api.github.com/repos/symfony/config/zipball/ac23c2f24d5634966d665d836c3933d54347e5d4", + "reference": "ac23c2f24d5634966d665d836c3933d54347e5d4", "shasum": "" }, "require": { @@ -2163,7 +2217,7 @@ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/config/tree/v5.3.4" + "source": "https://github.com/symfony/config/tree/v5.3.10" }, "funding": [ { @@ -2179,20 +2233,20 @@ "type": "tidelift" } ], - "time": "2021-07-21T12:40:44+00:00" + "time": "2021-10-22T09:06:52+00:00" }, { "name": "symfony/console", - "version": "v5.3.7", + "version": "v5.3.10", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "8b1008344647462ae6ec57559da166c2bfa5e16a" + "reference": "d4e409d9fbcfbf71af0e5a940abb7b0b4bad0bd3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/8b1008344647462ae6ec57559da166c2bfa5e16a", - "reference": "8b1008344647462ae6ec57559da166c2bfa5e16a", + "url": "https://api.github.com/repos/symfony/console/zipball/d4e409d9fbcfbf71af0e5a940abb7b0b4bad0bd3", + "reference": "d4e409d9fbcfbf71af0e5a940abb7b0b4bad0bd3", "shasum": "" }, "require": { @@ -2262,7 +2316,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.3.7" + "source": "https://github.com/symfony/console/tree/v5.3.10" }, "funding": [ { @@ -2278,7 +2332,7 @@ "type": "tidelift" } ], - "time": "2021-08-25T20:02:16+00:00" + "time": "2021-10-26T09:30:15+00:00" }, { "name": "symfony/deprecation-contracts", @@ -3056,16 +3110,16 @@ }, { "name": "symfony/string", - "version": "v5.3.7", + "version": "v5.3.10", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "8d224396e28d30f81969f083a58763b8b9ceb0a5" + "reference": "d70c35bb20bbca71fc4ab7921e3c6bda1a82a60c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/8d224396e28d30f81969f083a58763b8b9ceb0a5", - "reference": "8d224396e28d30f81969f083a58763b8b9ceb0a5", + "url": "https://api.github.com/repos/symfony/string/zipball/d70c35bb20bbca71fc4ab7921e3c6bda1a82a60c", + "reference": "d70c35bb20bbca71fc4ab7921e3c6bda1a82a60c", "shasum": "" }, "require": { @@ -3119,7 +3173,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v5.3.7" + "source": "https://github.com/symfony/string/tree/v5.3.10" }, "funding": [ { @@ -3135,10 +3189,176 @@ "type": "tidelift" } ], - "time": "2021-08-26T08:00:08+00:00" + "time": "2021-10-27T18:21:46+00:00" } ], "packages-dev": [ + { + "name": "amphp/amp", + "version": "v2.6.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/amp.git", + "reference": "c5fc66a78ee38d7ac9195a37bacaf940eb3f65ae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/amp/zipball/c5fc66a78ee38d7ac9195a37bacaf940eb3f65ae", + "reference": "c5fc66a78ee38d7ac9195a37bacaf940eb3f65ae", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1", + "ext-json": "*", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^7 | ^8 | ^9", + "psalm/phar": "^3.11@dev", + "react/promise": "^2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Amp\\": "lib" + }, + "files": [ + "lib/functions.php", + "lib/Internal/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A non-blocking concurrency framework for PHP applications.", + "homepage": "http://amphp.org/amp", + "keywords": [ + "async", + "asynchronous", + "awaitable", + "concurrency", + "event", + "event-loop", + "future", + "non-blocking", + "promise" + ], + "support": { + "irc": "irc://irc.freenode.org/amphp", + "issues": "https://github.com/amphp/amp/issues", + "source": "https://github.com/amphp/amp/tree/v2.6.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2021-09-23T18:43:08+00:00" + }, + { + "name": "amphp/byte-stream", + "version": "v1.8.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/byte-stream.git", + "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/byte-stream/zipball/acbd8002b3536485c997c4e019206b3f10ca15bd", + "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd", + "shasum": "" + }, + "require": { + "amphp/amp": "^2", + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1.4", + "friendsofphp/php-cs-fixer": "^2.3", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^6 || ^7 || ^8", + "psalm/phar": "^3.11.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Amp\\ByteStream\\": "lib" + }, + "files": [ + "lib/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A stream abstraction to make working with non-blocking I/O simple.", + "homepage": "http://amphp.org/byte-stream", + "keywords": [ + "amp", + "amphp", + "async", + "io", + "non-blocking", + "stream" + ], + "support": { + "irc": "irc://irc.freenode.org/amphp", + "issues": "https://github.com/amphp/byte-stream/issues", + "source": "https://github.com/amphp/byte-stream/tree/v1.8.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2021-03-30T17:13:30+00:00" + }, { "name": "brick/varexporter", "version": "0.3.5", @@ -3240,16 +3460,16 @@ }, { "name": "cakephp/debug_kit", - "version": "4.4.3", + "version": "4.4.4", "source": { "type": "git", "url": "https://github.com/cakephp/debug_kit.git", - "reference": "d8a5552096b09a1cdf192b454ec06dc0157a3515" + "reference": "10d7d9ba36945844211f1d8763e59618917e1784" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/debug_kit/zipball/d8a5552096b09a1cdf192b454ec06dc0157a3515", - "reference": "d8a5552096b09a1cdf192b454ec06dc0157a3515", + "url": "https://api.github.com/repos/cakephp/debug_kit/zipball/10d7d9ba36945844211f1d8763e59618917e1784", + "reference": "10d7d9ba36945844211f1d8763e59618917e1784", "shasum": "" }, "require": { @@ -3302,24 +3522,24 @@ "issues": "https://github.com/cakephp/debug_kit/issues", "source": "https://github.com/cakephp/debug_kit" }, - "time": "2021-05-09T01:21:26+00:00" + "time": "2021-09-12T20:06:14+00:00" }, { "name": "cakephp/localized", - "version": "4.0.0-beta", + "version": "4.0.0", "source": { "type": "git", "url": "https://github.com/cakephp/localized.git", - "reference": "efb90ae35e02224d977db3fe5814610412d678c9" + "reference": "d3a8a8a62a1ae9d81d0277546730f706e887f6e1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/localized/zipball/efb90ae35e02224d977db3fe5814610412d678c9", - "reference": "efb90ae35e02224d977db3fe5814610412d678c9", + "url": "https://api.github.com/repos/cakephp/localized/zipball/d3a8a8a62a1ae9d81d0277546730f706e887f6e1", + "reference": "d3a8a8a62a1ae9d81d0277546730f706e887f6e1", "shasum": "" }, "require": { - "cakephp/cakephp": "^4.0.0", + "cakephp/cakephp": "^4.1.0", "php": ">=7.2" }, "require-dev": { @@ -3329,7 +3549,7 @@ "type": "cakephp-plugin", "autoload": { "psr-4": { - "Cake\\Localized\\": "src" + "Cake\\Localized\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3351,25 +3571,24 @@ "localized" ], "support": { - "forum": "http://stackoverflow.com/tags/cakephp", - "irc": "irc://irc.freenode.org/cakephp", + "forum": "https://stackoverflow.com/tags/cakephp", "issues": "https://github.com/cakephp/localized/issues", "source": "https://github.com/cakephp/localized" }, - "time": "2020-04-02T22:34:51+00:00" + "time": "2021-05-14T18:09:47+00:00" }, { "name": "cakephp/twig-view", - "version": "1.2.0", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/cakephp/twig-view.git", - "reference": "668dd6aee43dd616b1e83cb9ba166f094c10fbce" + "reference": "14df50360b809a171d0688020fbdfe513763f89b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/twig-view/zipball/668dd6aee43dd616b1e83cb9ba166f094c10fbce", - "reference": "668dd6aee43dd616b1e83cb9ba166f094c10fbce", + "url": "https://api.github.com/repos/cakephp/twig-view/zipball/14df50360b809a171d0688020fbdfe513763f89b", + "reference": "14df50360b809a171d0688020fbdfe513763f89b", "shasum": "" }, "require": { @@ -3419,20 +3638,20 @@ "issues": "https://github.com/cakephp/twig-view/issues", "source": "https://github.com/cakephp/twig-view" }, - "time": "2020-12-13T19:57:31+00:00" + "time": "2021-09-17T14:07:52+00:00" }, { "name": "composer/composer", - "version": "2.1.6", + "version": "2.1.12", "source": { "type": "git", "url": "https://github.com/composer/composer.git", - "reference": "e5cac5f9d2354d08b67f1d21c664ae70d748c603" + "reference": "6e3c2b122e0ec41a7e885fcaf19fa15e2e0819a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/composer/zipball/e5cac5f9d2354d08b67f1d21c664ae70d748c603", - "reference": "e5cac5f9d2354d08b67f1d21c664ae70d748c603", + "url": "https://api.github.com/repos/composer/composer/zipball/6e3c2b122e0ec41a7e885fcaf19fa15e2e0819a0", + "reference": "6e3c2b122e0ec41a7e885fcaf19fa15e2e0819a0", "shasum": "" }, "require": { @@ -3443,7 +3662,7 @@ "composer/xdebug-handler": "^2.0", "justinrainbow/json-schema": "^5.2.11", "php": "^5.3.2 || ^7.0 || ^8.0", - "psr/log": "^1.0", + "psr/log": "^1.0 || ^2.0", "react/promise": "^1.2 || ^2.7", "seld/jsonlint": "^1.4", "seld/phar-utils": "^1.0", @@ -3467,7 +3686,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1-dev" + "dev-main": "2.1-dev" } }, "autoload": { @@ -3501,7 +3720,7 @@ "support": { "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/composer/issues", - "source": "https://github.com/composer/composer/tree/2.1.6" + "source": "https://github.com/composer/composer/tree/2.1.12" }, "funding": [ { @@ -3517,7 +3736,7 @@ "type": "tidelift" } ], - "time": "2021-08-19T15:11:08+00:00" + "time": "2021-11-09T15:02:04+00:00" }, { "name": "composer/metadata-minifier", @@ -3588,18 +3807,91 @@ ], "time": "2021-04-07T13:37:33+00:00" }, + { + "name": "composer/package-versions-deprecated", + "version": "1.11.99.4", + "source": { + "type": "git", + "url": "https://github.com/composer/package-versions-deprecated.git", + "reference": "b174585d1fe49ceed21928a945138948cb394600" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b174585d1fe49ceed21928a945138948cb394600", + "reference": "b174585d1fe49ceed21928a945138948cb394600", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.1.0 || ^2.0", + "php": "^7 || ^8" + }, + "replace": { + "ocramius/package-versions": "1.11.99" + }, + "require-dev": { + "composer/composer": "^1.9.3 || ^2.0@dev", + "ext-zip": "^1.13", + "phpunit/phpunit": "^6.5 || ^7" + }, + "type": "composer-plugin", + "extra": { + "class": "PackageVersions\\Installer", + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "PackageVersions\\": "src/PackageVersions" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" + } + ], + "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", + "support": { + "issues": "https://github.com/composer/package-versions-deprecated/issues", + "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.4" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2021-09-13T08:41:34+00:00" + }, { "name": "composer/semver", - "version": "3.2.5", + "version": "3.2.6", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "31f3ea725711245195f62e54ffa402d8ef2fdba9" + "reference": "83e511e247de329283478496f7a1e114c9517506" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/31f3ea725711245195f62e54ffa402d8ef2fdba9", - "reference": "31f3ea725711245195f62e54ffa402d8ef2fdba9", + "url": "https://api.github.com/repos/composer/semver/zipball/83e511e247de329283478496f7a1e114c9517506", + "reference": "83e511e247de329283478496f7a1e114c9517506", "shasum": "" }, "require": { @@ -3651,7 +3943,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.2.5" + "source": "https://github.com/composer/semver/tree/3.2.6" }, "funding": [ { @@ -3667,7 +3959,7 @@ "type": "tidelift" } ], - "time": "2021-05-24T12:41:47+00:00" + "time": "2021-10-25T11:34:17+00:00" }, { "name": "composer/spdx-licenses", @@ -3882,6 +4174,43 @@ }, "time": "2020-12-07T18:04:37+00:00" }, + { + "name": "dnoegel/php-xdg-base-dir", + "version": "v0.1.1", + "source": { + "type": "git", + "url": "https://github.com/dnoegel/php-xdg-base-dir.git", + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "~7.0|~6.0|~5.0|~4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "XdgBaseDir\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "implementation of xdg base directory specification for php", + "support": { + "issues": "https://github.com/dnoegel/php-xdg-base-dir/issues", + "source": "https://github.com/dnoegel/php-xdg-base-dir/tree/v0.1.1" + }, + "time": "2019-12-04T15:06:13+00:00" + }, { "name": "doctrine/instantiator", "version": "1.4.0", @@ -3953,21 +4282,21 @@ }, { "name": "fakerphp/faker", - "version": "v1.15.0", + "version": "v1.16.0", "source": { "type": "git", "url": "https://github.com/FakerPHP/Faker.git", - "reference": "89c6201c74db25fa759ff16e78a4d8f32547770e" + "reference": "271d384d216e5e5c468a6b28feedf95d49f83b35" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/89c6201c74db25fa759ff16e78a4d8f32547770e", - "reference": "89c6201c74db25fa759ff16e78a4d8f32547770e", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/271d384d216e5e5c468a6b28feedf95d49f83b35", + "reference": "271d384d216e5e5c468a6b28feedf95d49f83b35", "shasum": "" }, "require": { "php": "^7.1 || ^8.0", - "psr/container": "^1.0", + "psr/container": "^1.0 || ^2.0", "symfony/deprecation-contracts": "^2.2" }, "conflict": { @@ -3987,7 +4316,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "v1.15-dev" + "dev-main": "v1.16-dev" } }, "autoload": { @@ -4012,9 +4341,110 @@ ], "support": { "issues": "https://github.com/FakerPHP/Faker/issues", - "source": "https://github.com/FakerPHP/Faker/tree/v1.15.0" + "source": "https://github.com/FakerPHP/Faker/tree/v1.16.0" + }, + "time": "2021-09-06T14:53:37+00:00" + }, + { + "name": "felixfbecker/advanced-json-rpc", + "version": "v3.2.1", + "source": { + "type": "git", + "url": "https://github.com/felixfbecker/php-advanced-json-rpc.git", + "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/felixfbecker/php-advanced-json-rpc/zipball/b5f37dbff9a8ad360ca341f3240dc1c168b45447", + "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447", + "shasum": "" + }, + "require": { + "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", + "php": "^7.1 || ^8.0", + "phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "AdvancedJsonRpc\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Felix Becker", + "email": "felix.b@outlook.com" + } + ], + "description": "A more advanced JSONRPC implementation", + "support": { + "issues": "https://github.com/felixfbecker/php-advanced-json-rpc/issues", + "source": "https://github.com/felixfbecker/php-advanced-json-rpc/tree/v3.2.1" + }, + "time": "2021-06-11T22:34:44+00:00" + }, + { + "name": "felixfbecker/language-server-protocol", + "version": "1.5.1", + "source": { + "type": "git", + "url": "https://github.com/felixfbecker/php-language-server-protocol.git", + "reference": "9d846d1f5cf101deee7a61c8ba7caa0a975cd730" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/9d846d1f5cf101deee7a61c8ba7caa0a975cd730", + "reference": "9d846d1f5cf101deee7a61c8ba7caa0a975cd730", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpstan/phpstan": "*", + "squizlabs/php_codesniffer": "^3.1", + "vimeo/psalm": "^4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "LanguageServerProtocol\\": "src/" + } }, - "time": "2021-07-06T20:39:40+00:00" + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Felix Becker", + "email": "felix.b@outlook.com" + } + ], + "description": "PHP classes for the Language Server Protocol", + "keywords": [ + "language", + "microsoft", + "php", + "server" + ], + "support": { + "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues", + "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/1.5.1" + }, + "time": "2021-02-22T14:02:09+00:00" }, { "name": "jasny/twig-extensions", @@ -4262,18 +4692,69 @@ ], "time": "2020-11-13T09:40:50+00:00" }, + { + "name": "netresearch/jsonmapper", + "version": "v4.0.0", + "source": { + "type": "git", + "url": "https://github.com/cweiske/jsonmapper.git", + "reference": "8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d", + "reference": "8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0", + "squizlabs/php_codesniffer": "~3.5" + }, + "type": "library", + "autoload": { + "psr-0": { + "JsonMapper": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "OSL-3.0" + ], + "authors": [ + { + "name": "Christian Weiske", + "email": "cweiske@cweiske.de", + "homepage": "http://github.com/cweiske/jsonmapper/", + "role": "Developer" + } + ], + "description": "Map nested JSON structures onto PHP classes", + "support": { + "email": "cweiske@cweiske.de", + "issues": "https://github.com/cweiske/jsonmapper/issues", + "source": "https://github.com/cweiske/jsonmapper/tree/v4.0.0" + }, + "time": "2020-12-01T19:48:11+00:00" + }, { "name": "nikic/php-parser", - "version": "v4.12.0", + "version": "v4.13.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "6608f01670c3cc5079e18c1dab1104e002579143" + "reference": "63a79e8daa781cac14e5195e63ed8ae231dd10fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6608f01670c3cc5079e18c1dab1104e002579143", - "reference": "6608f01670c3cc5079e18c1dab1104e002579143", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/63a79e8daa781cac14e5195e63ed8ae231dd10fd", + "reference": "63a79e8daa781cac14e5195e63ed8ae231dd10fd", "shasum": "" }, "require": { @@ -4314,9 +4795,62 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.12.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.13.1" + }, + "time": "2021-11-03T20:52:16+00:00" + }, + { + "name": "openlss/lib-array2xml", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/nullivex/lib-array2xml.git", + "reference": "a91f18a8dfc69ffabe5f9b068bc39bb202c81d90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nullivex/lib-array2xml/zipball/a91f18a8dfc69ffabe5f9b068bc39bb202c81d90", + "reference": "a91f18a8dfc69ffabe5f9b068bc39bb202c81d90", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "type": "library", + "autoload": { + "psr-0": { + "LSS": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Bryan Tong", + "email": "bryan@nullivex.com", + "homepage": "https://www.nullivex.com" + }, + { + "name": "Tony Butler", + "email": "spudz76@gmail.com", + "homepage": "https://www.nullivex.com" + } + ], + "description": "Array2XML conversion library credit to lalit.org", + "homepage": "https://www.nullivex.com", + "keywords": [ + "array", + "array conversion", + "xml", + "xml conversion" + ], + "support": { + "issues": "https://github.com/nullivex/lib-array2xml/issues", + "source": "https://github.com/nullivex/lib-array2xml/tree/master" }, - "time": "2021-07-21T10:44:31+00:00" + "time": "2019-03-29T20:06:56+00:00" }, { "name": "passbolt/cakephp-codesniffer", @@ -4399,16 +4933,16 @@ }, { "name": "passbolt/passbolt-selenium-api", - "version": "3.1.1", + "version": "3.1.2", "source": { "type": "git", "url": "https://github.com/passbolt/passbolt-selenium-api", - "reference": "cfcbd39f2c2685cde81b5cf9fc3cd5358223a357" + "reference": "56faddc72a0331c0cb4aaaf33589b607fc050f61" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/passbolt/passbolt-selenium-api/zipball/cfcbd39f2c2685cde81b5cf9fc3cd5358223a357", - "reference": "cfcbd39f2c2685cde81b5cf9fc3cd5358223a357", + "url": "https://api.github.com/repos/passbolt/passbolt-selenium-api/zipball/56faddc72a0331c0cb4aaaf33589b607fc050f61", + "reference": "56faddc72a0331c0cb4aaaf33589b607fc050f61", "shasum": "" }, "require": { @@ -4450,7 +4984,7 @@ "help": "https://www.passbolt.com/help", "source": "https://github.com/passbolt/passbolt" }, - "time": "2021-05-05T20:43:55+00:00" + "time": "2021-11-14T15:35:11+00:00" }, { "name": "passbolt/passbolt-test-data", @@ -4673,16 +5207,16 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.2.2", + "version": "5.3.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556" + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/069a785b2141f5bcf49f3e353548dc1cce6df556", - "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", "shasum": "" }, "require": { @@ -4693,7 +5227,8 @@ "webmozart/assert": "^1.9.1" }, "require-dev": { - "mockery/mockery": "~1.3.2" + "mockery/mockery": "~1.3.2", + "psalm/phar": "^4.8" }, "type": "library", "extra": { @@ -4723,22 +5258,22 @@ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/master" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" }, - "time": "2020-09-03T19:13:55+00:00" + "time": "2021-10-19T17:43:47+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "1.4.0", + "version": "1.5.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0" + "reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0", - "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/a12f7e301eb7258bb68acd89d4aefa05c2906cae", + "reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae", "shasum": "" }, "require": { @@ -4746,7 +5281,8 @@ "phpdocumentor/reflection-common": "^2.0" }, "require-dev": { - "ext-tokenizer": "*" + "ext-tokenizer": "*", + "psalm/phar": "^4.8" }, "type": "library", "extra": { @@ -4772,39 +5308,39 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.4.0" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.5.1" }, - "time": "2020-09-17T18:55:26+00:00" + "time": "2021-10-02T14:08:47+00:00" }, { "name": "phpspec/prophecy", - "version": "1.13.0", + "version": "1.14.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "be1996ed8adc35c3fd795488a653f4b518be70ea" + "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/be1996ed8adc35c3fd795488a653f4b518be70ea", - "reference": "be1996ed8adc35c3fd795488a653f4b518be70ea", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", + "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", "shasum": "" }, "require": { "doctrine/instantiator": "^1.2", - "php": "^7.2 || ~8.0, <8.1", + "php": "^7.2 || ~8.0, <8.2", "phpdocumentor/reflection-docblock": "^5.2", "sebastian/comparator": "^3.0 || ^4.0", "sebastian/recursion-context": "^3.0 || ^4.0" }, "require-dev": { - "phpspec/phpspec": "^6.0", + "phpspec/phpspec": "^6.0 || ^7.0", "phpunit/phpunit": "^8.0 || ^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.11.x-dev" + "dev-master": "1.x-dev" } }, "autoload": { @@ -4839,9 +5375,9 @@ ], "support": { "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/1.13.0" + "source": "https://github.com/phpspec/prophecy/tree/1.14.0" }, - "time": "2021-03-17T13:42:18+00:00" + "time": "2021-09-10T09:02:12+00:00" }, { "name": "phpstan/phpdoc-parser", @@ -4958,23 +5494,23 @@ }, { "name": "phpunit/php-code-coverage", - "version": "9.2.6", + "version": "9.2.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "f6293e1b30a2354e8428e004689671b83871edde" + "reference": "cf04e88a2e3c56fc1a65488afd493325b4c1bc3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f6293e1b30a2354e8428e004689671b83871edde", - "reference": "f6293e1b30a2354e8428e004689671b83871edde", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/cf04e88a2e3c56fc1a65488afd493325b4c1bc3e", + "reference": "cf04e88a2e3c56fc1a65488afd493325b4c1bc3e", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.10.2", + "nikic/php-parser": "^4.13.0", "php": ">=7.3", "phpunit/php-file-iterator": "^3.0.3", "phpunit/php-text-template": "^2.0.2", @@ -5023,7 +5559,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.6" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.8" }, "funding": [ { @@ -5031,7 +5567,7 @@ "type": "github" } ], - "time": "2021-03-28T07:26:59+00:00" + "time": "2021-10-30T08:01:38+00:00" }, { "name": "phpunit/php-file-iterator", @@ -5276,16 +5812,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.5.9", + "version": "9.5.10", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "ea8c2dfb1065eb35a79b3681eee6e6fb0a6f273b" + "reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ea8c2dfb1065eb35a79b3681eee6e6fb0a6f273b", - "reference": "ea8c2dfb1065eb35a79b3681eee6e6fb0a6f273b", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c814a05837f2edb0d1471d6e3f4ab3501ca3899a", + "reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a", "shasum": "" }, "require": { @@ -5301,7 +5837,7 @@ "phar-io/version": "^3.0.2", "php": ">=7.3", "phpspec/prophecy": "^1.12.1", - "phpunit/php-code-coverage": "^9.2.3", + "phpunit/php-code-coverage": "^9.2.7", "phpunit/php-file-iterator": "^3.0.5", "phpunit/php-invoker": "^3.1.1", "phpunit/php-text-template": "^2.0.3", @@ -5363,7 +5899,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.9" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.10" }, "funding": [ { @@ -5375,20 +5911,20 @@ "type": "github" } ], - "time": "2021-08-31T06:47:40+00:00" + "time": "2021-09-25T07:38:51+00:00" }, { "name": "psy/psysh", - "version": "v0.10.8", + "version": "v0.10.9", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "e4573f47750dd6c92dca5aee543fa77513cbd8d3" + "reference": "01281336c4ae557fe4a994544f30d3a1bc204375" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/e4573f47750dd6c92dca5aee543fa77513cbd8d3", - "reference": "e4573f47750dd6c92dca5aee543fa77513cbd8d3", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/01281336c4ae557fe4a994544f30d3a1bc204375", + "reference": "01281336c4ae557fe4a994544f30d3a1bc204375", "shasum": "" }, "require": { @@ -5448,9 +5984,9 @@ ], "support": { "issues": "https://github.com/bobthecow/psysh/issues", - "source": "https://github.com/bobthecow/psysh/tree/v0.10.8" + "source": "https://github.com/bobthecow/psysh/tree/v0.10.9" }, - "time": "2021-04-10T16:23:39+00:00" + "time": "2021-10-10T13:37:39+00:00" }, { "name": "react/promise", @@ -5931,16 +6467,16 @@ }, { "name": "sebastian/exporter", - "version": "4.0.3", + "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65" + "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/d89cc98761b8cb5a1a235a6b703ae50d34080e65", - "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/65e8b7db476c5dd267e65eea9cab77584d3cfff9", + "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9", "shasum": "" }, "require": { @@ -5989,14 +6525,14 @@ } ], "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://www.github.com/sebastianbergmann/exporter", + "homepage": "https://www.github.com/sebastianbergmann/exporter", "keywords": [ "export", "exporter" ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.3" + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.4" }, "funding": [ { @@ -6004,7 +6540,7 @@ "type": "github" } ], - "time": "2020-09-28T05:24:23+00:00" + "time": "2021-11-11T14:18:36+00:00" }, { "name": "sebastian/global-state", @@ -6820,16 +7356,16 @@ }, { "name": "symfony/var-dumper", - "version": "v5.3.7", + "version": "v5.3.10", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "3ad5af4aed07d0a0201bbcfc42658fe6c5b2fb8f" + "reference": "875432adb5f5570fff21036fd22aee244636b7d1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/3ad5af4aed07d0a0201bbcfc42658fe6c5b2fb8f", - "reference": "3ad5af4aed07d0a0201bbcfc42658fe6c5b2fb8f", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/875432adb5f5570fff21036fd22aee244636b7d1", + "reference": "875432adb5f5570fff21036fd22aee244636b7d1", "shasum": "" }, "require": { @@ -6888,7 +7424,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v5.3.7" + "source": "https://github.com/symfony/var-dumper/tree/v5.3.10" }, "funding": [ { @@ -6904,7 +7440,7 @@ "type": "tidelift" } ], - "time": "2021-08-04T23:19:25+00:00" + "time": "2021-10-26T09:30:15+00:00" }, { "name": "theseer/tokenizer", @@ -6958,16 +7494,16 @@ }, { "name": "twig/markdown-extra", - "version": "v3.3.1", + "version": "v3.3.3", "source": { "type": "git", "url": "https://github.com/twigphp/markdown-extra.git", - "reference": "a9fe276dbb7f837c3f4ecc6dad89bcccb9fc8bc9" + "reference": "725a4ef89d93bb80fc63c67cf261bf7512649290" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/markdown-extra/zipball/a9fe276dbb7f837c3f4ecc6dad89bcccb9fc8bc9", - "reference": "a9fe276dbb7f837c3f4ecc6dad89bcccb9fc8bc9", + "url": "https://api.github.com/repos/twigphp/markdown-extra/zipball/725a4ef89d93bb80fc63c67cf261bf7512649290", + "reference": "725a4ef89d93bb80fc63c67cf261bf7512649290", "shasum": "" }, "require": { @@ -6979,7 +7515,7 @@ "league/commonmark": "^1.0", "league/html-to-markdown": "^4.8|^5.0", "michelf/php-markdown": "^1.8", - "symfony/phpunit-bridge": "^4.4.9|^5.0.9" + "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0" }, "type": "library", "extra": { @@ -7015,7 +7551,7 @@ "twig" ], "support": { - "source": "https://github.com/twigphp/markdown-extra/tree/v3.3.1" + "source": "https://github.com/twigphp/markdown-extra/tree/v3.3.3" }, "funding": [ { @@ -7027,20 +7563,20 @@ "type": "tidelift" } ], - "time": "2021-04-01T12:19:25+00:00" + "time": "2021-07-07T07:08:18+00:00" }, { "name": "twig/twig", - "version": "v3.3.2", + "version": "v3.3.3", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "21578f00e83d4a82ecfa3d50752b609f13de6790" + "reference": "a27fa056df8a6384316288ca8b0fa3a35fdeb569" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/21578f00e83d4a82ecfa3d50752b609f13de6790", - "reference": "21578f00e83d4a82ecfa3d50752b609f13de6790", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/a27fa056df8a6384316288ca8b0fa3a35fdeb569", + "reference": "a27fa056df8a6384316288ca8b0fa3a35fdeb569", "shasum": "" }, "require": { @@ -7050,7 +7586,7 @@ }, "require-dev": { "psr/container": "^1.0", - "symfony/phpunit-bridge": "^4.4.9|^5.0.9" + "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0" }, "type": "library", "extra": { @@ -7091,7 +7627,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.3.2" + "source": "https://github.com/twigphp/Twig/tree/v3.3.3" }, "funding": [ { @@ -7103,20 +7639,20 @@ "type": "tidelift" } ], - "time": "2021-05-16T12:14:13+00:00" + "time": "2021-09-17T08:44:23+00:00" }, { "name": "vierge-noire/cakephp-fixture-factories", - "version": "2.3.1", + "version": "v2.4", "source": { "type": "git", "url": "https://github.com/vierge-noire/cakephp-fixture-factories.git", - "reference": "01337dd69d4779608f95eee7c09c7bbb73f09c8f" + "reference": "87d8eb48865f6d6848627bafa845b0529d676257" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vierge-noire/cakephp-fixture-factories/zipball/01337dd69d4779608f95eee7c09c7bbb73f09c8f", - "reference": "01337dd69d4779608f95eee7c09c7bbb73f09c8f", + "url": "https://api.github.com/repos/vierge-noire/cakephp-fixture-factories/zipball/87d8eb48865f6d6848627bafa845b0529d676257", + "reference": "87d8eb48865f6d6848627bafa845b0529d676257", "shasum": "" }, "require": { @@ -7163,22 +7699,22 @@ ], "support": { "issues": "https://github.com/vierge-noire/cakephp-fixture-factories/issues", - "source": "https://github.com/vierge-noire/cakephp-fixture-factories/tree/2.3.1" + "source": "https://github.com/vierge-noire/cakephp-fixture-factories/tree/v2.4" }, - "time": "2021-07-08T14:25:29+00:00" + "time": "2021-09-08T12:15:32+00:00" }, { "name": "vierge-noire/cakephp-test-migrator", - "version": "v2.4", + "version": "v2.5", "source": { "type": "git", "url": "https://github.com/vierge-noire/cakephp-test-migrator.git", - "reference": "280abc2c9989929ac5b4c619c2977f8d1d97927d" + "reference": "4fd852809bc60fd716a1620b6ad6f252e8f8f6b1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vierge-noire/cakephp-test-migrator/zipball/280abc2c9989929ac5b4c619c2977f8d1d97927d", - "reference": "280abc2c9989929ac5b4c619c2977f8d1d97927d", + "url": "https://api.github.com/repos/vierge-noire/cakephp-test-migrator/zipball/4fd852809bc60fd716a1620b6ad6f252e8f8f6b1", + "reference": "4fd852809bc60fd716a1620b6ad6f252e8f8f6b1", "shasum": "" }, "require": { @@ -7186,7 +7722,7 @@ }, "require-dev": { "cakephp/cakephp-codesniffer": "^4.0", - "cakephp/migrations": "^3.0", + "cakephp/migrations": "^3.1", "josegonzalez/dotenv": "dev-master", "phpunit/phpunit": "^8.0" }, @@ -7209,9 +7745,9 @@ "description": "Migration helper for the CakePHP Test Suite Light", "support": { "issues": "https://github.com/vierge-noire/cakephp-test-migrator/issues", - "source": "https://github.com/vierge-noire/cakephp-test-migrator/tree/v2.4" + "source": "https://github.com/vierge-noire/cakephp-test-migrator/tree/v2.5" }, - "time": "2021-07-26T21:50:44+00:00" + "time": "2021-09-19T22:25:57+00:00" }, { "name": "vierge-noire/cakephp-test-suite-light", @@ -7269,6 +7805,111 @@ }, "time": "2021-05-24T19:55:28+00:00" }, + { + "name": "vimeo/psalm", + "version": "4.12.0", + "source": { + "type": "git", + "url": "https://github.com/vimeo/psalm.git", + "reference": "e42bc4a23f67acba28a23bb09c348e2ff38a1d87" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/e42bc4a23f67acba28a23bb09c348e2ff38a1d87", + "reference": "e42bc4a23f67acba28a23bb09c348e2ff38a1d87", + "shasum": "" + }, + "require": { + "amphp/amp": "^2.4.2", + "amphp/byte-stream": "^1.5", + "composer/package-versions-deprecated": "^1.8.0", + "composer/semver": "^1.4 || ^2.0 || ^3.0", + "composer/xdebug-handler": "^1.1 || ^2.0", + "dnoegel/php-xdg-base-dir": "^0.1.1", + "ext-ctype": "*", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-simplexml": "*", + "ext-tokenizer": "*", + "felixfbecker/advanced-json-rpc": "^3.0.3", + "felixfbecker/language-server-protocol": "^1.5", + "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", + "nikic/php-parser": "^4.13", + "openlss/lib-array2xml": "^1.0", + "php": "^7.1|^8", + "sebastian/diff": "^3.0 || ^4.0", + "symfony/console": "^3.4.17 || ^4.1.6 || ^5.0 || ^6.0", + "webmozart/path-util": "^2.3" + }, + "provide": { + "psalm/psalm": "self.version" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.2", + "brianium/paratest": "^4.0||^6.0", + "ext-curl": "*", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpdocumentor/reflection-docblock": "^5", + "phpmyadmin/sql-parser": "5.1.0||dev-master", + "phpspec/prophecy": ">=1.9.0", + "phpunit/phpunit": "^9.0", + "psalm/plugin-phpunit": "^0.16", + "slevomat/coding-standard": "^7.0", + "squizlabs/php_codesniffer": "^3.5", + "symfony/process": "^4.3 || ^5.0 || ^6.0", + "weirdan/prophecy-shim": "^1.0 || ^2.0" + }, + "suggest": { + "ext-igbinary": "^2.0.5" + }, + "bin": [ + "psalm", + "psalm-language-server", + "psalm-plugin", + "psalm-refactor", + "psalter" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.x-dev", + "dev-3.x": "3.x-dev", + "dev-2.x": "2.x-dev", + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psalm\\": "src/Psalm/" + }, + "files": [ + "src/functions.php", + "src/spl_object_id.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthew Brown" + } + ], + "description": "A static analysis tool for finding errors in PHP applications", + "keywords": [ + "code", + "inspection", + "php" + ], + "support": { + "issues": "https://github.com/vimeo/psalm/issues", + "source": "https://github.com/vimeo/psalm/tree/4.12.0" + }, + "time": "2021-11-06T10:31:17+00:00" + }, { "name": "webmozart/assert", "version": "1.10.0", @@ -7326,15 +7967,64 @@ "source": "https://github.com/webmozarts/assert/tree/1.10.0" }, "time": "2021-03-09T10:59:23+00:00" + }, + { + "name": "webmozart/path-util", + "version": "2.3.0", + "source": { + "type": "git", + "url": "https://github.com/webmozart/path-util.git", + "reference": "d939f7edc24c9a1bb9c0dee5cb05d8e859490725" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozart/path-util/zipball/d939f7edc24c9a1bb9c0dee5cb05d8e859490725", + "reference": "d939f7edc24c9a1bb9c0dee5cb05d8e859490725", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "webmozart/assert": "~1.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.6", + "sebastian/version": "^1.0.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\PathUtil\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "A robust cross-platform utility for normalizing, comparing and modifying file paths.", + "support": { + "issues": "https://github.com/webmozart/path-util/issues", + "source": "https://github.com/webmozart/path-util/tree/2.3.0" + }, + "abandoned": "symfony/filesystem", + "time": "2015-12-17T08:42:14+00:00" } ], "aliases": [], "minimum-stability": "stable", "stability-flags": { - "singpolyma/openpgp-php": 20, "psy/psysh": 0, - "passbolt/cakephp-codesniffer": 20, - "cakephp/localized": 10 + "passbolt/cakephp-codesniffer": 20 }, "prefer-stable": true, "prefer-lowest": false, @@ -7344,7 +8034,9 @@ "ext-posix": "*", "ext-intl": "*", "ext-mbstring": "*", - "ext-gnupg": "*" + "ext-gnupg": "*", + "ext-pdo": "*", + "ext-openssl": "*" }, "platform-dev": [], "platform-overrides": { diff --git a/config/Migrations/20200806110204_V300AddResourceTypesToResources.php b/config/Migrations/20200806110204_V300AddResourceTypesToResources.php index f0833fdb30..1e220ed63a 100644 --- a/config/Migrations/20200806110204_V300AddResourceTypesToResources.php +++ b/config/Migrations/20200806110204_V300AddResourceTypesToResources.php @@ -25,13 +25,19 @@ class V300AddResourceTypesToResources extends AbstractMigration */ public function up() { - $defaultType = $this->fetchAll("SELECT id FROM resource_types WHERE slug='password-string'"); + $id = $this->getAdapter()->quoteColumnName('id'); + $resourceTypeId = $this->getAdapter()->quoteColumnName('resource_type_id'); + $slug = $this->getAdapter()->quoteColumnName('slug'); + $resourceTypes = $this->getAdapter()->quoteTableName('resource_types'); + $resources = $this->getAdapter()->quoteTableName('resources'); + + $defaultType = $this->fetchAll("SELECT $id FROM $resourceTypes WHERE $slug='password-string'"); if (empty($defaultType) || !Validation::uuid($defaultType[0]['id'])) { return; } - $this->execute("UPDATE resources SET resource_type_id='{$defaultType[0]['id']}' WHERE resource_type_id IS NULL"); + $this->execute("UPDATE $resources SET $resourceTypeId='{$defaultType[0]['id']}' WHERE $resourceTypeId IS NULL"); } } // @codingStandardsIgnoreEnd diff --git a/config/Migrations/20210121140000_V300PostgresUUID.php b/config/Migrations/20210121140000_V300PostgresUUID.php deleted file mode 100644 index c531f0272d..0000000000 --- a/config/Migrations/20210121140000_V300PostgresUUID.php +++ /dev/null @@ -1,145 +0,0 @@ -table('account_settings') - ->changeColumn('id', 'uuid') - ->changeColumn('property_id', 'uuid') - ->changeColumn('user_id', 'uuid') - ->save(); - - $this->table('action_logs') - ->changeColumn('action_id', 'uuid') - ->changeColumn('id', 'uuid') - ->changeColumn('user_id', 'uuid', ['null' => true]) - ->save(); - - $this->table('actions') - ->changeColumn('id', 'uuid') - ->save(); - - $this->table('authentication_tokens') - ->changeColumn('id', 'uuid') - ->changeColumn('token', 'uuid') - ->changeColumn('user_id', 'uuid') - ->save(); - - $this->table('comments') - ->changeColumn('created_by', 'uuid') - ->changeColumn('foreign_key', 'uuid') - ->changeColumn('id', 'uuid') - ->changeColumn('modified_by', 'uuid') - ->changeColumn('parent_id', 'uuid', ['null' => true]) - ->changeColumn('user_id', 'uuid') - ->save(); - - $this->table('entities_history') - ->changeColumn('action_log_id', 'uuid') - ->changeColumn('foreign_key', 'uuid') - ->changeColumn('id', 'uuid') - ->save(); - - $this->table('favorites') - ->changeColumn('foreign_key', 'uuid') - ->changeColumn('id', 'uuid') - ->changeColumn('user_id', 'uuid', ['null' => true]) - ->save(); - - $this->table('gpgkeys') - ->changeColumn('id', 'uuid') - ->changeColumn('user_id', 'uuid') - ->save(); - - $this->table('groups') - ->changeColumn('created_by', 'uuid') - ->changeColumn('id', 'uuid') - ->changeColumn('modified_by', 'uuid') - ->save(); - - $this->table('groups_users') - ->changeColumn('group_id', 'uuid', ['null' => true]) - ->changeColumn('id', 'uuid') - ->changeColumn('user_id', 'uuid', ['null' => true]) - ->save(); - - $this->table('organization_settings') - ->changeColumn('id', 'uuid') - ->changeColumn('property_id', 'uuid') - ->changeColumn('created_by', 'uuid') - ->changeColumn('modified_by', 'uuid') - ->save(); - - $this->table('permissions') - ->changeColumn('aco_foreign_key', 'uuid') - ->changeColumn('aro_foreign_key', 'uuid', ['null' => true]) - ->changeColumn('id', 'uuid') - ->save(); - - $this->table('permissions_history') - ->changeColumn('id','uuid') - ->changeColumn('aco_foreign_key','uuid') - ->changeColumn('aro_foreign_key','uuid', ['null' => true, 'default' => null]) - ->save(); - - $this->table('profiles') - ->changeColumn('id', 'uuid') - ->changeColumn('user_id', 'uuid') - ->save(); - - $this->table('resource_types') - ->changeColumn('id', 'uuid') - ->save(); - - $this->table('resources') - ->changeColumn('created_by', 'uuid') - ->changeColumn('id', 'uuid') - ->changeColumn('modified_by', 'uuid') - ->changeColumn('resource_type_id', 'uuid', ['null' => true]) - ->save(); - - $this->table('roles') - ->changeColumn('id', 'uuid') - ->save(); - - $this->table('secret_accesses') - ->changeColumn('id', 'uuid') - ->changeColumn('resource_id', 'uuid') - ->changeColumn('secret_id', 'uuid') - ->changeColumn('user_id', 'uuid') - ->save(); - - $this->table('secrets') - ->changeColumn('id', 'uuid') - ->changeColumn('resource_id', 'uuid') - ->changeColumn('user_id', 'uuid') - ->save(); - - $this->table('secrets_history') - ->changeColumn('id', 'uuid') - ->changeColumn('resource_id', 'uuid') - ->changeColumn('user_id', 'uuid') - ->save(); - - $this->table('user_agents') - ->changeColumn('id', 'uuid') - ->save(); - - $this->table('users') - ->changeColumn('id', 'uuid') - ->changeColumn('role_id', 'uuid') - ->save(); - } -} - diff --git a/config/Migrations/20210427124200_V330AddMobileTransferTable.php b/config/Migrations/20210427124200_V330AddMobileTransferTable.php new file mode 100644 index 0000000000..a5e97d8631 --- /dev/null +++ b/config/Migrations/20210427124200_V330AddMobileTransferTable.php @@ -0,0 +1,81 @@ +table('transfers', ['id' => false, 'primary_key' => ['id'], 'collation' => 'utf8mb4_unicode_ci']) + ->addColumn('id', 'char', [ + 'default' => null, + 'limit' => 36, + 'null' => false, + ]) + ->addColumn('user_id', 'char', [ + 'default' => null, + 'limit' => 36, + 'null' => false, + ]) + ->addColumn('authentication_token_id', 'char', [ + 'default' => null, + 'limit' => 36, + 'null' => false, + ]) + ->addColumn('current_page', 'integer', [ + 'default' => null, + 'limit' => MysqlAdapter::INT_SMALL, // See. Transfer::TRANSFER_MAX_PAGES + 'null' => false, + ]) + ->addColumn('total_pages', 'integer', [ + 'default' => null, + 'limit' => MysqlAdapter::INT_SMALL, // See. Transfer::TRANSFER_MAX_PAGES + 'null' => false, + ]) + ->addColumn('status', 'string', [ + 'default' => null, + 'limit' => 16, + 'null' => false, + ]) + ->addColumn('hash', 'char', [ + 'default' => null, + 'limit' => 128, // See. Transfer::TRANSFER_HASH_SIZE + 'null' => false, + ]) + ->addColumn('created', 'datetime', [ + 'default' => null, + 'limit' => null, + 'null' => false, + ]) + ->addColumn('modified', 'datetime', [ + 'default' => null, + 'limit' => null, + 'null' => false, + ]) + ->addIndex( + 'id', + ['unique' => true] + ) + ->create(); + } +} +// @codingStandardsIgnoreEnd diff --git a/config/Migrations/20211027202137_V331ConvertEmailVariablesToJson.php b/config/Migrations/20211027202137_V331ConvertEmailVariablesToJson.php new file mode 100644 index 0000000000..93cde7a08c --- /dev/null +++ b/config/Migrations/20211027202137_V331ConvertEmailVariablesToJson.php @@ -0,0 +1,37 @@ +convert(); + } catch (Throwable $e) { + Log::error('There was an error in V331ConvertEmailVariablesToJson'); + Log::error($e->getMessage()); + } + } +} diff --git a/config/Migrations/20211121231300_V340MigrateASCIIFieldsEncoding.php b/config/Migrations/20211121231300_V340MigrateASCIIFieldsEncoding.php new file mode 100644 index 0000000000..cd33c5d1ec --- /dev/null +++ b/config/Migrations/20211121231300_V340MigrateASCIIFieldsEncoding.php @@ -0,0 +1,640 @@ +table('account_settings') + ->changeColumn('id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('user_id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('property_id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->save(); + + $this->table('action_logs') + ->changeColumn('id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('user_id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => true, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('action_id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->save(); + + $this->table('actions') + ->changeColumn('id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->save(); + + $this->table('authentication_tokens') + ->changeColumn('id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('token', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('user_id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('type', 'string', [ + 'default' => null, + 'limit' => 16, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->save(); + + $this->table('avatars') + ->changeColumn('id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('profile_id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->save(); + + $this->table('comments') + ->changeColumn('id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('parent_id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => true, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('foreign_key', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('foreign_model', 'string', [ + 'default' => null, + 'limit' => 36, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('created_by', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('modified_by', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('user_id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->save(); + + $this->table('entities_history') + ->changeColumn('id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('action_log_id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('foreign_model', 'string', [ + 'default' => null, + 'limit' => 36, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('foreign_key', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('crud', 'char', [ + 'default' => null, + 'limit' => 1, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->save(); + + $this->table('favorites') + ->changeColumn('id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('user_id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => true, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('foreign_key', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('foreign_model', 'string', [ + 'default' => null, + 'limit' => 36, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->save(); + + $this->table('gpgkeys') + ->changeColumn('id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('user_id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + + ->save(); + + $this->table('groups') + ->changeColumn('id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('created_by', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('modified_by', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->save(); + + $this->table('groups_users') + ->changeColumn('id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('group_id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => true, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('user_id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => true, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->save(); + + $this->table('organization_settings') + ->changeColumn('id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('property_id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('created_by', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('modified_by', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->save(); + + $this->table('permissions') + ->changeColumn('id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('aco', 'string', [ + 'default' => null, + 'limit' => 30, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('aco_foreign_key', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('aro', 'string', [ + 'default' => null, + 'limit' => 30, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('aro_foreign_key', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => true, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->save(); + + $this->table('permissions_history') + ->changeColumn('id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('aco', 'string', [ + 'default' => null, + 'limit' => 30, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('aco_foreign_key', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('aro', 'string', [ + 'default' => null, + 'limit' => 30, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('aro_foreign_key', 'string', [ + 'default' => null, + 'limit' => null, + 'null' => true, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->save(); + + $this->table('profiles') + ->changeColumn('id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('user_id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->save(); + + $this->table('resource_types') + ->changeColumn('id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->save(); + + $this->table('resources') + ->changeColumn('id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('created_by', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('modified_by', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('resource_type_id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => true, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->save(); + + $this->table('roles') + ->changeColumn('id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->save(); + + $this->table('secret_accesses') + ->changeColumn('id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('user_id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('resource_id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('secret_id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->save(); + + $this->table('secrets') + ->changeColumn('id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('user_id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('resource_id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->save(); + + $this->table('secrets_history') + ->changeColumn('id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('user_id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('resource_id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->save(); + + $this->table('transfers') + ->changeColumn('id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('user_id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('authentication_token_id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('status', 'string', [ + 'default' => null, + 'limit' => 16, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('hash', 'char', [ + 'default' => null, + 'limit' => 128, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->save(); + + $this->table('user_agents') + ->changeColumn('id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->save(); + + $this->table('users') + ->changeColumn('id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->changeColumn('role_id', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->save(); + } + + /** + * Down Method. + * + * More information on this method is available here: + * https://book.cakephp.org/phinx/0/en/migrations.html#the-down-method + * @return void + */ + public function down() + { + } +} diff --git a/config/Migrations/20211122732400_V350ConvertIdFieldsToUuidFields.php b/config/Migrations/20211122732400_V350ConvertIdFieldsToUuidFields.php new file mode 100644 index 0000000000..3b16bb4a51 --- /dev/null +++ b/config/Migrations/20211122732400_V350ConvertIdFieldsToUuidFields.php @@ -0,0 +1,38 @@ +table('permissions_history') + ->changeColumn('aro_foreign_key', 'uuid', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'encoding' => 'ascii', + 'collation' => 'ascii_general_ci' + ]) + ->save(); + } + + /** + * Down Method. + * + * More information on this method is available here: + * https://book.cakephp.org/phinx/0/en/migrations.html#the-down-method + * @return void + */ + public function down() + { + } +} diff --git a/config/bootstrap.php b/config/bootstrap.php index 747a679ca1..93c6ea5ea8 100644 --- a/config/bootstrap.php +++ b/config/bootstrap.php @@ -197,5 +197,3 @@ // Are we running passbolt pro? define('PASSBOLT_PRO', Configure::read('passbolt.edition') === 'pro'); - -require __DIR__ . '/file_storage.php'; diff --git a/config/default.php b/config/default.php index 7208f20cd6..d3d908c441 100644 --- a/config/default.php +++ b/config/default.php @@ -15,6 +15,7 @@ use App\Model\Entity\AuthenticationToken; use App\Utility\AuthToken\AuthTokenExpiryConfigValidator; +use Passbolt\JwtAuthentication\Service\AccessToken\JwtAbstractService; $authTokenExpiryConfigValidator = new AuthTokenExpiryConfigValidator(); @@ -53,6 +54,18 @@ AuthenticationToken::TYPE_LOGIN => [ 'expiry' => filter_var(env('PASSBOLT_AUTH_LOGIN_TOKEN_EXPIRY', '5 minutes'), FILTER_CALLBACK, ['options' => $authTokenExpiryConfigValidator]) ], + AuthenticationToken::TYPE_MOBILE_TRANSFER => [ + 'expiry' => filter_var(env('PASSBOLT_AUTH_MOBILE_TRANSFER_TOKEN_EXPIRY', '5 minutes'), FILTER_CALLBACK, ['options' => $authTokenExpiryConfigValidator]) + ], + AuthenticationToken::TYPE_REFRESH_TOKEN => [ + 'expiry' => filter_var(env('PASSBOLT_AUTH_JWT_REFRESH_TOKEN', '1 month'), FILTER_CALLBACK, ['options' => $authTokenExpiryConfigValidator]) + ], + JwtAbstractService::USER_ACCESS_TOKEN_KEY => [ + 'expiry' => filter_var(env('PASSBOLT_AUTH_JWT_ACCESS_TOKEN', '5 minutes'), FILTER_CALLBACK, ['options' => $authTokenExpiryConfigValidator]) + ], + AuthenticationToken::TYPE_VERIFY_TOKEN => [ + 'expiry' => filter_var(env('PASSBOLT_AUTH_JWT_VERIFY_TOKEN', '1 hour'), FILTER_CALLBACK, ['options' => $authTokenExpiryConfigValidator]) + ], ] ], @@ -186,6 +199,12 @@ 'resourceTypes' => [ 'enabled' => filter_var(env('PASSBOLT_PLUGINS_RESOURCE_TYPES_ENABLED', true), FILTER_VALIDATE_BOOLEAN) ], + 'mobile' => [ + 'enabled' => filter_var(env('PASSBOLT_PLUGINS_MOBILE_ENABLED', false), FILTER_VALIDATE_BOOLEAN) + ], + 'jwtAuthentication' => [ + 'enabled' => filter_var(env('PASSBOLT_PLUGINS_JWT_AUTHENTICATION_ENABLED', false), FILTER_VALIDATE_BOOLEAN) + ], ], // Is public registration allowed. @@ -201,6 +220,10 @@ // Security. 'security' => [ + 'cookies' => [ + // force cookie secure flag even if request is not https + 'secure' => filter_var(env('PASSBOLT_SECURITY_COOKIE_SECURE', true), FILTER_VALIDATE_BOOLEAN) + ], 'setHeaders' => filter_var(env('PASSBOLT_SECURITY_SET_HEADERS', true), FILTER_VALIDATE_BOOLEAN), 'csrfProtection' => [ 'active' => true, @@ -208,6 +231,7 @@ 'AuthLogin' => ['loginPost'], 'RecoverComplete' => ['complete'], 'SetupComplete' => ['complete'], + 'TransfersUpdate' => ['updateNoSession'], ] ], 'csp' => env('PASSBOLT_SECURITY_CSP', true) diff --git a/config/file_storage.php b/config/file_storage.php deleted file mode 100644 index a09c946f3e..0000000000 --- a/config/file_storage.php +++ /dev/null @@ -1,45 +0,0 @@ -get('Avatars'); -$AvatarsTable->setFilesystem(new LocalFilesystemAdapter(TMP . 'avatars')); - -// File storage and images -Configure::write('ImageStorage.basePath', WWW_ROOT . 'img' . DS . 'public' . DS); -Configure::write('ImageStorage.publicPath', 'img' . DS . 'public' . DS); -Configure::write('FileStorage', [ - 'imageDefaults' => [ - 'Avatar' => [ - AvatarsTable::FORMAT_MEDIUM => 'img' . DS . 'avatar' . DS . 'user_medium.png', - AvatarsTable::FORMAT_SMALL => 'img' . DS . 'avatar' . DS . 'user.png', - ] - ], - // Configure image versions on a per model base - 'imageSizes' => [ - 'Avatar' => [ - AvatarsTable::FORMAT_MEDIUM => [ - 'thumbnail' => [ - 'mode' => 'outbound', - 'width' => 200, - 'height' => 200 - ], - ], - AvatarsTable::FORMAT_SMALL => [ - 'thumbnail' => [ - 'mode' => 'outbound', - 'width' => 80, - 'height' => 80 - ], - 'crop' => [ - 'width' => 80, - 'height' => 80 - ], - ], - ] - ] -]); diff --git a/config/jwt/empty b/config/jwt/empty new file mode 100644 index 0000000000..e69de29bb2 diff --git a/config/routes.php b/config/routes.php index 996530b434..cc39d0f6d7 100644 --- a/config/routes.php +++ b/config/routes.php @@ -50,6 +50,7 @@ Router::scope('/auth', function (RouteBuilder $routes) { $routes->setExtensions(['json']); + // Session based $routes->connect('/login', ['prefix' => 'Auth', 'controller' => 'AuthLogin', 'action' => 'loginGet']) ->setMethods(['GET']); @@ -65,8 +66,8 @@ $routes->connect('/is-authenticated', ['prefix' => 'Auth', 'controller' => 'AuthIsAuthenticated', 'action' => 'isAuthenticated']) ->setMethods(['GET']); - $routes->connect('/logout', ['prefix' => 'Auth', 'controller' => 'AuthLogout', 'action' => 'logoutGet']) - ->setMethods(['GET']); + $routes->connect('/logout', ['prefix' => 'Auth', 'controller' => 'AuthLogout', 'action' => 'logout']) + ->setMethods(['GET', 'POST']); }); /** @@ -374,17 +375,3 @@ $routes->connect('/settings/mfa', ['prefix' => 'Pages', 'controller' => 'Home', 'action' => 'apiApp']); $routes->connect('/*', ['prefix' => 'Pages', 'controller' => 'Home', 'action' => 'apiExtApp']); }); - -/** - * Other editions fallback routes - * Use this section to silently fail a route used by other versions without logging error - */ -if(!file_exists(PLUGINS . 'Passbolt' . DS . 'AccountSettings')) { - Router::scope('/account/settings', function ($routes) { - $routes->setExtensions(['json']); - - // See. https://github.com/passbolt/passbolt_api/issues/270 - $routes->connect('/', ['prefix' => 'Errors', 'controller' => 'ErrorNotFound', 'action' => 'notSupported']) - ->setMethods(['GET']); - }); -} diff --git a/config/version.php b/config/version.php index d531a52f3e..3929956dc5 100644 --- a/config/version.php +++ b/config/version.php @@ -1,7 +1,7 @@ [ - 'version' => '3.0.0', - 'name' => 'Our House' + 'version' => '3.4.0', + 'name' => 'Black Sunrise' ] ]; diff --git a/debian/changelog b/debian/changelog index d40595c858..6fa7cc7dbb 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,199 @@ -passbolt-pro-server (2.12.0-1) stretch; urgency=medium +passbolt-ce-server (3.4.0-2) buster; urgency=medium + * Synchronising with RPM package + + -- Gerold Mougenel Tue, 07 Dec 2021 14:00:00 +0000 + +passbolt-ce-server (3.4.0) buster; urgency=medium + + * PB-9826 As a user I want to use passbolt natively on Edge + * PB-8371 As LU I want to see the login/MFA/recover/register screens in dark mode + * PB-8522 As LU I should see the MFA verify field having focus + * PB-9730 As AD I should be able to check avatars read issues from the healthcheck + * PB-8932 Fix as LU I should see an animation when I successfully configured MFA + * PB-9286 Fix as LU I should see the locale dropdown field of the setup/recover screen well positioned + * PB-9397 Fix as AD I shouldn't see an error on the healthcheck if the JWT auth is disabled and I never configured it + * PB-9114 Fix as lu I should be able to upload a transparent avatar in .png format. + * PB-9750 Fix spelling mistakes reported by the community + * PB-9762 Fix requesting /auth/login.json should not trigger an unexpected error + * PB-9888 Fix MFA & JWT refresh token issue, remove Bearer from the hashed session identifier + * PB-12817 Fix as LU I should be able to update jpeg avatar + * PB-7374 As soft deleted but logged in user I should be forbidden to request the API + * PB-9340 Fix email queue data should be stored and deserialized as json and not php + * PB-9311 Refactor JWT and MFA plugins for better code maintainability. + * PB-8320 Implement the tests that are marked as incomplete for cleaner continuous integration test reports + * PB-8211 Psalm set to level 4 + * PB-9726 Fix do not load cleanup tasks unless in CLI mode + * PB-9753 Improve table fields validation tests, do not save entity when testing the validation of properties + * PB-9310 Move avatar file_storage logic into AvatarsTable + * PB-9785 Update JWT healthcheck help messages + * PB-9656 Migrate fields from utf8mb4 to a more performant encoding when possible + + -- Gerold Mougenel Tue, 07 Dec 2021 13:30:00 +0000 + +passbolt-ce-server (3.3.1-1) buster; urgency=medium + * PB-9820 / PBL-06-008 WP3: JWT key confusion leads to authentication bypass (High) [experimental][disabled by default] + + -- Daniel del Rio Wed, 24 Nov 2021 15:30:00 +0000 + +passbolt-ce-server (3.3.0-1) buster; urgency=medium + * PB-8414 Adapting package to be compatible with Debian 11 + * PB-7815 As a server administrator I should be able to enable / disable the in-form menu feature, enabled by default + * PB-6072 As a server administrator I should be able to enable / disable the password generator feature, enabled by default + * PB-8189 As a user I should be able to use the application in German or Swedish + * PB-7847 As AN I should be able to authenticate to passbolt via JWT access and refresh tokens [experimental][disabled by default] + * PB-6034 As LU I should be able to configure my mobile app [experimental][disabled by default] + * PB-8908 As a user I should see the footer of the passbolt emails translated with my locale + * PB-8364 As a user I should see the subject of the passbolt emails translated with my locale + * PB-6032 As API user I shouldn’t see the _joinData properties in the resource entry points responses + * PB-8281 Add Debian 11 bullseye support + * PB-7750 As AD I should be notified by the healthcheck when a tmp files is executable + * PB-7760 Increase PHPStan level to 6 + * PB-8081 As AD I should be able to configure passbolt over IPv6 while installing a passbolt package + * PB-5866 As AD I should be able to detect avatar data discrepancies using the passbolt cleanup command + * PB-7605 As a developer I should be able to enable/disable a plugin easily + * PB-5457 Fix as LU importing a batch of passwords I should not get an internal errors because of database deadlock + * PB-7840 Fix as AD I can install/reconfigure the passbolt package if ssl certificates are already present + * PB-8047 Fix PBL-02-002 As LU I should logout by posting to the API and the entry point should should be protected by CSRF + * PB-7751 Updates FlySystem dependency to v2.1.1 + * SEC-181 Fix information disclosure: recover endpoint should not return user role and name. + * PB-8488 Remove user agent unnecessary check associated with MFA token + * PB-8336 Clean phpunit.xml file + * PB-8448 Hashes the session ID prior to passord_hash + * PB-8210 Replaces PHPSESSID with session_name() + + -- Gerold Mougenel Tue, 26 Oct 2021 10:30:00 +0000 + +passbolt-ce-server (3.2.1-1) buster; urgency=medium + * GITHUB-402 Fix API v3 regression, login must accept JSON data + + -- Daniel Del Rio Tue, 4 Jun 2021 19:30:00 +0000 + +passbolt-ce-server (3.2.0-1) buster; urgency=medium + * PB-5054 French internationalization + * PB-5171 As logged-in user I can paginate the result of the users and resources index controllers + * PB-5854 As logged-in user I can save the locale of a user as account setting + * PB-5854 As admin I can save the locale the organization as organization setting + * PB-5523 Fix as system administrator I should see the healthcheck errors colored in red + * PB-5860 Fix password max length should be set to 4096 in resource type definitions + * PB-6031 Fix as LU I shouldn't get a fatal error when using a scalar instead of an array for some filters values + * PB-6131 Fix healthcheck error messages display + * PB-5975 Test code with PHPStan - level 4 + * Avatar table should use created and modified for timestamp and not created_at and modified_at + * Move avatar in database + * PB-5527 Migration to CakePHP4 + * Remove X-XSS-Protection as per Cure53 audit recommendations + + -- Daniel Del Rio Tue, 1 Jun 2021 16:00:00 +0000 + +passbolt-ce-server (3.1.0-1) buster; urgency=medium + * Add preview password plugin feature flag + + -- Daniel Del Rio Fri, 18 Mar 2021 10:28:00 +0000 + +passbolt-ce-server (3.0.2-2) buster; urgency=medium + * Include missed commits + + -- Daniel Del Rio Fri, 9 Mar 2021 17:48:00 +0000 + +passbolt-ce-server (3.0.2-1) buster; urgency=medium + * GITHUB-378 Fix healthcheck ssl fullBaseUrl check + * Fix email digest email preview should accept empty (null) template + * Fix send test email command should accept undefined username and password + + -- Daniel Del Rio Fri, 9 Mar 2021 16:26:00 +0000 + +passbolt-ce-server (3.0.1-1) buster; urgency=medium + * Fix resources population of resource_type_id field migration + + -- Daniel Del Rio Wed, 24 Feb 2021 13:50:00 +0000 + +passbolt-ce-server (3.0.0-2) buster; urgency=medium + * Drop support for API format v1, api-version parameter is deprecated + * Remove title from API response envelope format + * Drop support for PHP < v7.3, application require PHP v7.3 by default + * Drop support for Composer < v2, application requires Composer v2 by default + * Add dark theme to the community edition + * Add new system check utilities in ./bin, for example ./bin/status-report + * Add web installer automatically populates mysql credentials (VM / Debian Package) + * Add support for multiple resource types + * Add resource with encrypted description as resource type + * Add generic cron job task in ./bin/cron + * Add support for untracked personal shell scripts under ./bin/my + * Add support for configurable footer link in config + * Add permissions filters on resource view and index + * Add permissions contain options on resource view + * Update OpenPGP-PHP dependencies to provide PHP 7.4 compatibility + * Remove unmaintained user agent parser library + * Fix PHP 7.4 warnings + * Improve testsuite execution times + * Refactor testsuite to not install data model from fixtures but use migrations instead + * Refactor testsuite to remove unused fixtures + * Migrate administration and mfa settings screen to React + * Add placeholder application skeleton when webextension is still loading + * Redesign of login and recover screens + * Add Mysql 8 support + * Fix allow overriding rememberMe options in passbolt.php configuration file + * Fix all target blank link should contain rel noopener noreferrer + * Fix email sender, email subject should not exceed 255 characters. + * Fix secret access log on resource view with contain secret + * GITHUB-376 Fix missing route prefix on the recovery button + * GITHUB-373 Fix API format for create group (previously v1 instead of v2 format) + * GITHUB-372 Fix after modifying passwd, the modification time should be changed + * GITHUB-370 Fix metadata should be deleted for deleted resources + * GITHUB-369 Fix Notification Emails Have Wrong Tense In Subject/Body + * GITHUB-368 Clarify PHP extension requirements + * GITHUB-362 Fix wrong filename on healthcheck HELP message for assertConfigFiles + * GITHUB-356 As a user I shouldn't be able to export folders if export plugin is disabled + * GITHUB-350 Fix no mails are sent when providers offer AUTH PLAIN authentication only + * GITHUB-339 Fix web installer urls do not work when passbolt is installed in a directory + * Fix performance issues on resource / folder activity log + * Add run cleanup after migrations on postinst + + -- Diego Lendoiro Thu, 18 Feb 2021 15:52:00 +0000 + +passbolt-ce-server (3.0.0-1) buster; urgency=medium + * Cron job executes bin/cron script. + * Add cake passbolt show_logs command to display error.log path. + * Add bin/utils.sh script. + + -- Daniel Del Rio Fri, 22 Jan 2021 18:15:00 +0000 + +passbolt-ce-server (2.13.5-1) stretch; urgency=medium + * Fix email notification settings bootstrap messes up non persistent database connection in wizard + * Bump dependencies versions + + -- Diego Lendoiro Thu, 13 Aug 2020 10:24:00 +0000 + +passbolt-ce-server (2.13.1-1) stretch; urgency=medium + * PB-1372 Fix user setup completed admin email notification + + -- Diego Lendoiro Thu, 18 Feb 2021 15:52:00 +0000 + +passbolt-ce-server (2.13.0-1) stretch; urgency=medium + * PB-1168 Add baseline code and tests for Debian package build + * PB-1067 As a user I can receive digest emails when creating a lot of resources + * PB-1067 As a user I can receive digest emails when added/removed from a lot of groups + * PB-1284 Add tasks and services to re-validate existing data + * Pro Styleguide version bump v2.13.13 + * Appjs version bump v2.13.7 + * PB-1046 Adapt Cleanup test runner to take in account cleanup that are adding records + * PB-1046 Adapt Cleanup shell task to allow external sources to add cleanup tasks + * PB-1046 Remove empty EmailTraits files + * Delete unused default keys (cleanup) + * Update to latest passbolt_test_data version. + * Misc refactoring for email notifications + * Misc refactoring to split model logic into services + * Clear plugins in tearDown of application test cases + * GITHUB-350 No mails are sent when providers offer AUTH PLAIN authentication only + * Fix appjs plugin requestUntilSuccess bug + * Fix load webinstaller plugin manually in plugin tests + * Fix composer php version. + * Fix misc checkstyle issues + * PB-980: Fix "secret access logging in password activity log should not display other resources secret access after a multiple share" + + -- Diego Lendoiro Thu, 25 Jun 2020 17:24:00 +0000 + +passbolt-ce-server (2.12.1-1+beta) stretch; urgency=medium * Refactor config script * Refactor postinst script * Added questions for nginx setup @@ -9,15 +204,15 @@ passbolt-pro-server (2.12.0-1) stretch; urgency=medium * Added logrotation for /var/log/passbolt/error.log * Introduced triggers for mysql and nginx - -- Diego Lendoiro Thu, 21 May 2020 18:24:00 +0000 + -- Diego Lendoiro Thu, 21 May 2020 18:24:00 +0000 -passbolt-pro-server (2.12.0-0) stretch; urgency=medium +passbolt-ce-server (2.12.0-0) stretch; urgency=medium * Created an automated debian package -- Albert Casals Fri, 27 Dec 2019 01:15:00 +0100 -passbolt-pro-server (2.7.1-0) stretch; urgency=medium +passbolt-ce-server (2.7.1-0) stretch; urgency=medium * Initial release. diff --git a/debian/conf/nginx-passbolt.conf b/debian/conf/nginx-passbolt.conf index ae36b20bb1..1c970c7b78 100644 --- a/debian/conf/nginx-passbolt.conf +++ b/debian/conf/nginx-passbolt.conf @@ -5,6 +5,7 @@ server { listen 80; + listen [::]:80; # Managed by Passbolt # server_name diff --git a/debian/postinst b/debian/postinst index 9234acc564..c77c6b6658 100755 --- a/debian/postinst +++ b/debian/postinst @@ -40,6 +40,7 @@ NGINX_SITES_AVAILABLE="/etc/nginx/sites-available" NGINX_SITES_ENABLED="/etc/nginx/sites-enabled" PASSBOLT_SSL_CERT_PATH="/etc/ssl/certs" PASSBOLT_SSL_KEY_PATH="/etc/ssl/private" +RAND_ID="$(date +%s)" ########################################### # Functions to debug debconf responses @@ -182,16 +183,16 @@ configure_nginx_local_certs() { configure_nginx_config_file _echo "nginx: activating https with local certificates" - # copy user certificate files to local passbolt namespace - cp "${nginx_values[cert_file]}" "$PASSBOLT_SSL_CERT_PATH" - cp "${nginx_values[cert_key_file]}" "$PASSBOLT_SSL_KEY_PATH" + # replace paths to pull from the locally copied files + local_cert_file="${PASSBOLT_SSL_CERT_PATH}/$RAND_ID-$(basename "${nginx_values[cert_file]}")" + local_cert_key_file="${PASSBOLT_SSL_KEY_PATH}/$RAND_ID-$(basename "${nginx_values[cert_key_file]}")" # copy nginx https sample configuration for ssl options cp "$NGINX_SSL_EXAMPLE" "$NGINX_SSL" - # replace paths to pull from the locally copied files - local_cert_file="${PASSBOLT_SSL_CERT_PATH}/$(basename "${nginx_values[cert_file]}")" - local_cert_key_file="${PASSBOLT_SSL_KEY_PATH}/$(basename "${nginx_values[cert_key_file]}")" + # copy user certificate files to local passbolt namespace + cp "${nginx_values[cert_file]}" "$local_cert_file" + cp "${nginx_values[cert_key_file]}" "$local_cert_key_file" # apply changes to nginx virtual host file, and ssl include file sed -i "s|listen 80;|listen 443 ssl http2;|g" "$NGINX_CONF" diff --git a/kitchen.yml b/kitchen.yml index bc2c381aa0..28e6cfa6bc 100644 --- a/kitchen.yml +++ b/kitchen.yml @@ -13,6 +13,7 @@ provisioner: attributes: dest_dir: '/tmp/passbolt' passbolt_flavour: <%= ENV['PASSBOLT_FLAVOUR'] %> + passbolt_version: <%= ENV['PASSBOLT_VERSION'] %> verifier: name: inspec @@ -25,8 +26,54 @@ platforms: image: debian:buster ports: - '127.0.0.1:8080:8080' + - name: debian-bullseye - name: ubuntu-18.04 - name: ubuntu-20.04 + - name: redhat-8 + driver: + image: redhat/ubi8 + privileged: true + pid_one_command: /usr/lib/systemd/systemd + - name: redhat-7 + driver: + image: redhat/ubi7 + privileged: true + pid_one_command: /usr/lib/systemd/systemd + - name: rockylinux-8 + driver: + image: rockylinux/rockylinux:8 + privileged: true + pid_one_command: /usr/lib/systemd/systemd + - name: almalinux-8 + driver: + privileged: true + pid_one_command: /usr/lib/systemd/systemd + - name: centos-8 + driver: + privileged: true + pid_one_command: /usr/lib/systemd/systemd + - name: centos-7 + driver: + privileged: true + pid_one_command: /usr/lib/systemd/systemd + - name: oraclelinux-8 + driver: + privileged: true + pid_one_command: /usr/lib/systemd/systemd + - name: oraclelinux-7 + driver: + privileged: true + pid_one_command: /usr/lib/systemd/systemd + - name: fedora-33 + driver: + image: anatomicjc/fedora:33 + privileged: true + pid_one_command: /usr/lib/systemd/systemd + - name: fedora-34 + driver: + image: anatomicjc/fedora:34 + privileged: true + pid_one_command: /usr/lib/systemd/systemd suites: - name: filesystem-benchmarks diff --git a/package-lock.json b/package-lock.json index 426ba52114..6353ab3577 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,230 +1,31 @@ { "name": "passbolt_api", - "version": "3.0.0", + "version": "3.4.0", "lockfileVersion": 1, "requires": true, "dependencies": { "@babel/code-frame": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", - "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz", + "integrity": "sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==", "dev": true, "requires": { - "@babel/highlight": "^7.12.13" - } - }, - "@babel/compat-data": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.0.tgz", - "integrity": "sha512-vu9V3uMM/1o5Hl5OekMUowo3FqXLJSw+s+66nt0fSWVWTtmosdzn45JHOB3cPtZoe6CTBDzvSw0RdOY85Q37+Q==", - "dev": true - }, - "@babel/core": { - "version": "7.14.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.3.tgz", - "integrity": "sha512-jB5AmTKOCSJIZ72sd78ECEhuPiDMKlQdDI/4QRI6lzYATx5SSogS1oQA2AoPecRCknm30gHi2l+QVvNUu3wZAg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.14.3", - "@babel/helper-compilation-targets": "^7.13.16", - "@babel/helper-module-transforms": "^7.14.2", - "@babel/helpers": "^7.14.0", - "@babel/parser": "^7.14.3", - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.14.2", - "@babel/types": "^7.14.2", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0", - "source-map": "^0.5.0" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.14.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.3.tgz", - "integrity": "sha512-bn0S6flG/j0xtQdz3hsjJ624h3W0r3llttBMfyHX3YrZ/KtLYr15bjA0FXkgW7FpvrDuTuElXeVjiKlYRpnOFA==", - "dev": true, - "requires": { - "@babel/types": "^7.14.2", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "@babel/helper-compilation-targets": { - "version": "7.13.16", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.16.tgz", - "integrity": "sha512-3gmkYIrpqsLlieFwjkGgLaSHmhnvlAYzZLlYVjlW+QwI+1zE17kGxuJGmIqDQdYp56XdmGeD+Bswx0UTyG18xA==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.13.15", - "@babel/helper-validator-option": "^7.12.17", - "browserslist": "^4.14.5", - "semver": "^6.3.0" - } - }, - "@babel/helper-function-name": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.2.tgz", - "integrity": "sha512-NYZlkZRydxw+YT56IlhIcS8PAhb+FEUiOzuhFTfqDyPmzAhRge6ua0dQYT/Uh0t/EDHq05/i+e5M2d4XvjgarQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.12.13", - "@babel/template": "^7.12.13", - "@babel/types": "^7.14.2" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", - "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", - "dev": true, - "requires": { - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz", - "integrity": "sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw==", - "dev": true, - "requires": { - "@babel/types": "^7.13.12" - } - }, - "@babel/helper-module-imports": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz", - "integrity": "sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA==", - "dev": true, - "requires": { - "@babel/types": "^7.13.12" - } - }, - "@babel/helper-module-transforms": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.14.2.tgz", - "integrity": "sha512-OznJUda/soKXv0XhpvzGWDnml4Qnwp16GN+D/kZIdLsWoHj05kyu8Rm5kXmMef+rVJZ0+4pSGLkeixdqNUATDA==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.13.12", - "@babel/helper-replace-supers": "^7.13.12", - "@babel/helper-simple-access": "^7.13.12", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/helper-validator-identifier": "^7.14.0", - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.14.2", - "@babel/types": "^7.14.2" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz", - "integrity": "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==", - "dev": true, - "requires": { - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz", - "integrity": "sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==", - "dev": true - }, - "@babel/helper-replace-supers": { - "version": "7.14.3", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.14.3.tgz", - "integrity": "sha512-Rlh8qEWZSTfdz+tgNV/N4gz1a0TMNwCUcENhMjHTHKp3LseYH5Jha0NSlyTQWMnjbYcwFt+bqAMqSLHVXkQ6UA==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.13.12", - "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/traverse": "^7.14.2", - "@babel/types": "^7.14.2" - } - }, - "@babel/helper-simple-access": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz", - "integrity": "sha512-7FEjbrx5SL9cWvXioDbnlYTppcZGuCY6ow3/D5vMggb2Ywgu4dMrpTJX0JdQAIcRRUElOIxF3yEooa9gUb9ZbA==", - "dev": true, - "requires": { - "@babel/types": "^7.13.12" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", - "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", - "dev": true, - "requires": { - "@babel/types": "^7.12.13" + "@babel/highlight": "^7.16.0" } }, "@babel/helper-validator-identifier": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz", - "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.12.17", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz", - "integrity": "sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", "dev": true }, - "@babel/helpers": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.0.tgz", - "integrity": "sha512-+ufuXprtQ1D1iZTO/K9+EBRn+qPWMJjZSw/S0KlFrxCw4tkrzv9grgpDHkY9MeQTjTY8i2sp7Jep8DfU6tN9Mg==", - "dev": true, - "requires": { - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.14.0", - "@babel/types": "^7.14.0" - } - }, "@babel/highlight": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.0.tgz", - "integrity": "sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", + "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.14.0", + "@babel/helper-validator-identifier": "^7.15.7", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, @@ -281,3077 +82,929 @@ } } }, - "@babel/parser": { - "version": "7.14.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.3.tgz", - "integrity": "sha512-7MpZDIfI7sUC5zWo2+foJ50CSI5lcqDehZ0lVgIhSi4bFEk94fLAKlF3Q0nzSQQ+ca0lm+O6G9ztKVBeu8PMRQ==", - "dev": true - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "@babel/runtime": { + "version": "7.16.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.3.tgz", + "integrity": "sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.0" + "regenerator-runtime": "^0.13.4" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "dev": true + } } }, - "@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "@babel/runtime-corejs3": { + "version": "7.16.3", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.16.3.tgz", + "integrity": "sha512-IAdDC7T0+wEB4y2gbIL0uOXEYpiZEeuFUTVbdGq+UwCcF35T/tS8KrmMomEwEc5wBbyfH3PJVpTSUqrhPDXFcQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.0" + "core-js-pure": "^3.19.0", + "regenerator-runtime": "^0.13.4" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "dev": true + } } }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } + "@icons/material": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz", + "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==", + "dev": true }, - "@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" } }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } + "@juggle/resize-observer": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.3.1.tgz", + "integrity": "sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw==", + "dev": true }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "@testing-library/dom": { + "version": "7.31.2", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.31.2.tgz", + "integrity": "sha512-3UqjCpey6HiTZT92vODYLPxTBWlM8ZOOjr3LX5F37/VRipW2M1kX6I/Cm4VXzteZqfGfagg8yXywpcOgQBlNsQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^4.2.0", + "aria-query": "^4.2.2", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.6", + "lz-string": "^1.4.4", + "pretty-format": "^26.6.2" } }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "@testing-library/react": { + "version": "10.4.9", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-10.4.9.tgz", + "integrity": "sha512-pHZKkqUy0tmiD81afs8xfiuseXfU/N7rAX3iKjeZYje86t9VaB0LrxYVa+OOsvkrveX5jCK3IjajVn2MbePvqA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.0" + "@babel/runtime": "^7.10.3", + "@testing-library/dom": "^7.22.3" } }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } + "@types/aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==", + "dev": true }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } + "@types/istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", + "dev": true }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.0" + "@types/istanbul-lib-coverage": "*" } }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.0" + "@types/istanbul-lib-report": "*" } }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz", - "integrity": "sha512-A81F9pDwyS7yM//KwbCSDqy3Uj4NMIurtplxphWxoYtNPov7cJsDkAFNNyVlIZ3jwGycVsurZ+LtOA8gZ376iQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } + "@types/node": { + "version": "16.11.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.11.tgz", + "integrity": "sha512-KB0sixD67CeecHC33MYn+eYARkqTheIRNuu97y2XMjR7Wu3XibO1vaY6VBV6O/a89SPI81cEUIYT87UqUWlZNw==", + "dev": true }, - "@babel/runtime": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.0.tgz", - "integrity": "sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA==", + "@types/yargs": { + "version": "15.0.14", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.14.tgz", + "integrity": "sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ==", "dev": true, "requires": { - "regenerator-runtime": "^0.13.4" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - } + "@types/yargs-parser": "*" } }, - "@babel/runtime-corejs3": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.14.0.tgz", - "integrity": "sha512-0R0HTZWHLk6G8jIk0FtoX+AatCtKnswS98VhXwGImFc759PJRp4Tru0PQYZofyijTFUr+gT8Mu7sgXVJLQ0ceg==", - "dev": true, - "requires": { - "core-js-pure": "^3.0.0", - "regenerator-runtime": "^0.13.4" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - } - } + "@types/yargs-parser": { + "version": "20.2.1", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", + "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==", + "dev": true + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true }, - "@babel/template": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", - "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/parser": "^7.12.13", - "@babel/types": "^7.12.13" + "color-convert": "^2.0.1" } }, - "@babel/traverse": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.2.tgz", - "integrity": "sha512-TsdRgvBFHMyHOOzcP9S6QU0QQtjxlRpEYOy3mcCO5RgmC305ki42aSAmfZEMSSYBla2oZ9BMqYlncBaKmD/7iA==", + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.14.2", - "@babel/helper-function-name": "^7.14.2", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/parser": "^7.14.2", - "@babel/types": "^7.14.2", - "debug": "^4.1.0", - "globals": "^11.1.0" + "sprintf-js": "~1.0.2" }, "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true } } }, - "@babel/types": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.2.tgz", - "integrity": "sha512-SdjAG/3DikRHpUOjxZgnkbR11xUlyDMUFJdvnIgZEE16mqmY0BINMmc4//JMJglEmn6i7sq6p+mGrFWyZ98EEw==", + "aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.14.0", - "to-fast-properties": "^2.0.0" + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" } }, - "@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "array-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", + "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", "dev": true }, - "@cnakazawa/watch": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", - "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", - "dev": true, - "requires": { - "exec-sh": "^0.3.2", - "minimist": "^1.2.0" - } - }, - "@icons/material": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz", - "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==", + "array-slice": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", + "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", "dev": true }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", "dev": true, "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" } }, - "@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "async": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.1.tgz", + "integrity": "sha512-XdD5lRO/87udXCMC9meWdYiR+Nq6ZjUfXidViUZGu2F1MO4T3XwZ1et0hb2++BgLfhyJwy44BGB/yx80ABx8hg==", "dev": true }, - "@jest/console": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", - "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", + "babel-polyfill": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", + "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=", "dev": true, "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^26.6.2", - "jest-util": "^26.6.2", - "slash": "^3.0.0" + "babel-runtime": "^6.26.0", + "core-js": "^2.5.0", + "regenerator-runtime": "^0.10.5" } }, - "@jest/core": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.6.3.tgz", - "integrity": "sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw==", + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "@jest/console": "^26.6.2", - "@jest/reporters": "^26.6.2", - "@jest/test-result": "^26.6.2", - "@jest/transform": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "jest-changed-files": "^26.6.2", - "jest-config": "^26.6.3", - "jest-haste-map": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.6.2", - "jest-resolve-dependencies": "^26.6.3", - "jest-runner": "^26.6.3", - "jest-runtime": "^26.6.3", - "jest-snapshot": "^26.6.2", - "jest-util": "^26.6.2", - "jest-validate": "^26.6.2", - "jest-watcher": "^26.6.2", - "micromatch": "^4.0.2", - "p-each-series": "^2.1.0", - "rimraf": "^3.0.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" }, "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", + "dev": true } } }, - "@jest/environment": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", - "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", - "dev": true, - "requires": { - "@jest/fake-timers": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "jest-mock": "^26.6.2" - } + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true }, - "@jest/fake-timers": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz", - "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@sinonjs/fake-timers": "^6.0.1", - "@types/node": "*", - "jest-message-util": "^26.6.2", - "jest-mock": "^26.6.2", - "jest-util": "^26.6.2" - } + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true }, - "@jest/globals": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.6.2.tgz", - "integrity": "sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA==", - "dev": true, - "requires": { - "@jest/environment": "^26.6.2", - "@jest/types": "^26.6.2", - "expect": "^26.6.2" - } + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true }, - "@jest/reporters": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.6.2.tgz", - "integrity": "sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw==", + "body": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/body/-/body-5.1.0.tgz", + "integrity": "sha1-5LoM5BCkaTYyM2dgnstOZVMSUGk=", "dev": true, "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^26.6.2", - "@jest/test-result": "^26.6.2", - "@jest/transform": "^26.6.2", - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.2", - "graceful-fs": "^4.2.4", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^4.0.3", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.2", - "jest-haste-map": "^26.6.2", - "jest-resolve": "^26.6.2", - "jest-util": "^26.6.2", - "jest-worker": "^26.6.2", - "node-notifier": "^8.0.0", - "slash": "^3.0.0", - "source-map": "^0.6.0", - "string-length": "^4.0.1", - "terminal-link": "^2.0.0", - "v8-to-istanbul": "^7.0.0" + "continuable-cache": "^0.3.1", + "error": "^7.0.0", + "raw-body": "~1.1.0", + "safe-json-parse": "~1.0.1" } }, - "@jest/source-map": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz", - "integrity": "sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA==", + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.2.4", - "source-map": "^0.6.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "@jest/test-result": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", - "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "requires": { - "@jest/console": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" + "fill-range": "^7.0.1" } }, - "@jest/test-sequencer": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz", - "integrity": "sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw==", + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "dev": true, "requires": { - "@jest/test-result": "^26.6.2", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^26.6.2", - "jest-runner": "^26.6.3", - "jest-runtime": "^26.6.3" + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" } }, - "@jest/transform": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.6.2.tgz", - "integrity": "sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==", + "buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", "dev": true, "requires": { - "@babel/core": "^7.1.0", - "@jest/types": "^26.6.2", - "babel-plugin-istanbul": "^6.0.0", - "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^26.6.2", - "jest-regex-util": "^26.0.0", - "jest-util": "^26.6.2", - "micromatch": "^4.0.2", - "pirates": "^4.0.1", - "slash": "^3.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "^3.0.0" - }, - "dependencies": { - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - } + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" } }, - "@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } + "buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "dev": true }, - "@sinonjs/commons": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", - "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } + "buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", + "dev": true + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz", + "integrity": "sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g=", + "dev": true }, - "@sinonjs/fake-timers": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", - "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", "dev": true, "requires": { - "@sinonjs/commons": "^1.7.0" + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" } }, - "@testing-library/dom": { - "version": "7.31.0", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.31.0.tgz", - "integrity": "sha512-0X7ACg4YvTRDFMIuTOEj6B4NpN7i3F/4j5igOcTI5NC5J+N4TribNdErCHOZF1LBWhhcyfwxelVwvoYNMUXTOA==", + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "can-use-dom": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/can-use-dom/-/can-use-dom-0.1.0.tgz", + "integrity": "sha1-IsxKNKCrxDlQ9CxkEQJKP2NmtFo=", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^4.2.0", - "aria-query": "^4.2.2", - "chalk": "^4.1.0", - "dom-accessibility-api": "^0.5.4", - "lz-string": "^1.4.4", - "pretty-format": "^26.6.2" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, - "@testing-library/jest-dom": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.12.0.tgz", - "integrity": "sha512-N9Y82b2Z3j6wzIoAqajlKVF1Zt7sOH0pPee0sUHXHc5cv2Fdn23r+vpWm0MBBoGJtPOly5+Bdx1lnc3CD+A+ow==", + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", "dev": true, "requires": { - "@babel/runtime": "^7.9.2", - "@types/testing-library__jest-dom": "^5.9.1", - "aria-query": "^4.2.2", - "chalk": "^3.0.0", - "css": "^3.0.0", - "css.escape": "^1.5.1", - "lodash": "^4.17.15", - "redent": "^3.0.0" + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" }, "dependencies": { - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "ansi-regex": "^4.1.0" } } } }, - "@testing-library/react": { - "version": "10.4.9", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-10.4.9.tgz", - "integrity": "sha512-pHZKkqUy0tmiD81afs8xfiuseXfU/N7rAX3iKjeZYje86t9VaB0LrxYVa+OOsvkrveX5jCK3IjajVn2MbePvqA==", + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "@babel/runtime": "^7.10.3", - "@testing-library/dom": "^7.22.3" + "color-name": "~1.1.4" } }, - "@types/aria-query": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.1.tgz", - "integrity": "sha512-S6oPal772qJZHoRZLFc/XoZW2gFvwXusYUmXPXkgxJLuEk2vOt7jc4Yo6z/vtI0EBkbPBVrJJ0B+prLIKiWqHg==", + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "@types/babel__core": { - "version": "7.1.14", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.14.tgz", - "integrity": "sha512-zGZJzzBUVDo/eV6KgbE0f0ZI7dInEYvo12Rb70uNQDshC3SkRMb67ja0GgRHZgAX3Za6rhaWlvbDO8rrGyAb1g==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } + "colors": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", + "dev": true }, - "@types/babel__generator": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.2.tgz", - "integrity": "sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true }, - "@types/babel__template": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.0.tgz", - "integrity": "sha512-NTPErx4/FiPCGScH7foPyr+/1Dkzkni+rHiYHHoTjvwou7AQzJkNeD60A9CXRy+ZEN2B1bggmkTMCDb+Mv5k+A==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } + "continuable-cache": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/continuable-cache/-/continuable-cache-0.3.1.tgz", + "integrity": "sha1-vXJ6f67XfnH/OYWskzUakSczrQ8=", + "dev": true }, - "@types/babel__traverse": { - "version": "7.11.1", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.11.1.tgz", - "integrity": "sha512-Vs0hm0vPahPMYi9tDjtP66llufgO3ST16WXaSTtDGEl9cewAl3AibmxWw6TINOqHPT9z0uABKAYjT9jNSg4npw==", - "dev": true, - "requires": { - "@babel/types": "^7.3.0" - } + "core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "dev": true + }, + "core-js-pure": { + "version": "3.19.2", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.19.2.tgz", + "integrity": "sha512-5LkcgQEy8pFeVnd/zomkUBSwnmIxuF1C8E9KrMAbOc8f34IBT9RGvTYeNDdp1PnvMJrrVhvk1hg/yVV5h/znlg==", + "dev": true }, - "@types/graceful-fs": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", - "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "cross-fetch": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.4.tgz", + "integrity": "sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ==", "dev": true, "requires": { - "@types/node": "*" + "node-fetch": "2.6.1" + }, + "dependencies": { + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "dev": true + } } }, - "@types/istanbul-lib-coverage": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", - "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", + "csstype": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz", + "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==", "dev": true }, - "@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*" - } + "dateformat": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", + "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", + "dev": true }, - "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } + "debounce-promise": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/debounce-promise/-/debounce-promise-3.1.2.tgz", + "integrity": "sha512-rZHcgBkbYavBeD9ej6sP56XfG53d51CD4dnaw989YX/nZ/ZJfgRx/9ePKmTNiUiyQvh4mtrMoS3OAWW+yoYtpg==", + "dev": true }, - "@types/jest": { - "version": "26.0.23", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.23.tgz", - "integrity": "sha512-ZHLmWMJ9jJ9PTiT58juykZpL7KjwJywFN3Rr2pTSkyQfydf/rk22yS7W8p5DaVUMQ2BQC7oYiU3FjbTM/mYrOA==", + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "requires": { - "jest-diff": "^26.0.0", - "pretty-format": "^26.0.0" + "ms": "^2.1.1" } }, - "@types/node": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-15.3.0.tgz", - "integrity": "sha512-8/bnjSZD86ZfpBsDlCIkNXIvm+h6wi9g7IqL+kmFkQ+Wvu3JrasgLElfiPgoo8V8vVfnEi0QVS12gbl94h9YsQ==", + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true }, - "@types/normalize-package-data": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", + "detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", "dev": true }, - "@types/prettier": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.2.3.tgz", - "integrity": "sha512-PijRCG/K3s3w1We6ynUKdxEc5AcuuH3NBmMDP8uvKVp6X43UY7NQlTzczakXP3DJR0F4dfNQIGjU2cUeRYs2AA==", + "dijkstrajs": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.2.tgz", + "integrity": "sha512-QV6PMaHTCNmKSeP6QoXhVTw9snc9VD8MulTT0Bd99Pacp4SS1cjcrYPgBPmibqKVtMJJfqC6XvOXgPMEEPH/fg==", "dev": true }, - "@types/stack-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", - "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", + "dom-accessibility-api": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.10.tgz", + "integrity": "sha512-Xu9mD0UjrJisTmv7lmVSDMagQcU9R5hwAbxsaAE/35XPnPLJobbuREfV/rraiSaEj/UOvgrzQs66zyTWTlyd+g==", "dev": true }, - "@types/testing-library__jest-dom": { - "version": "5.9.5", - "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.9.5.tgz", - "integrity": "sha512-ggn3ws+yRbOHog9GxnXiEZ/35Mow6YtPZpd7Z5mKDeZS/o7zx3yAle0ov/wjhVB5QT4N2Dt+GNoGCdqkBGCajQ==", + "dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", "dev": true, "requires": { - "@types/jest": "*" + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" } }, - "@types/yargs": { - "version": "15.0.13", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.13.tgz", - "integrity": "sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ==", + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "error": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/error/-/error-7.2.1.tgz", + "integrity": "sha512-fo9HBvWnx3NGUKMvMwB/CBCMMrfEJgbDTVDEkPygA3Bdd3lM1OyCd+rbQ8BwnpF6GdVeOLDNmyL4N5Bg80ZvdA==", "dev": true, "requires": { - "@types/yargs-parser": "*" + "string-template": "~0.2.1" } }, - "@types/yargs-parser": { - "version": "20.2.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.0.tgz", - "integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==", + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, - "abab": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", - "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "eventemitter2": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", + "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=", "dev": true }, - "acorn": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.2.4.tgz", - "integrity": "sha512-Ibt84YwBDDA890eDiDCEqcbwvHlBvzzDkU2cGBBDDI1QWT12jTiXIOn2CIw5KK4i6N5Z2HUxwYjzriDyqaqqZg==", + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", "dev": true }, - "acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", "dev": true, "requires": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" - }, - "dependencies": { - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - } + "homedir-polyfill": "^1.0.1" } }, - "acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "faye-websocket": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", "dev": true, "requires": { - "type-fest": "^0.21.3" - }, - "dependencies": { - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true - } + "websocket-driver": ">=0.5.1" } }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "file-sync-cmp": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/file-sync-cmp/-/file-sync-cmp-0.1.1.tgz", + "integrity": "sha1-peeo/7+kk7Q7kju9TKiaU7Y7YSs=", "dev": true }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "requires": { - "color-convert": "^2.0.1" + "to-regex-range": "^5.0.1" } }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "locate-path": "^3.0.0" } }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "findup-sync": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", + "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=", "dev": true, "requires": { - "sprintf-js": "~1.0.2" + "glob": "~5.0.0" }, "dependencies": { - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "dev": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } } } }, - "aria-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "fined": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", + "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", "dev": true, "requires": { - "@babel/runtime": "^7.10.2", - "@babel/runtime-corejs3": "^7.10.2" + "expand-tilde": "^2.0.2", + "is-plain-object": "^2.0.3", + "object.defaults": "^1.1.0", + "object.pick": "^1.2.0", + "parse-filepath": "^1.0.1" } }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true - }, - "array-each": { + "flagged-respawn": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", - "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", - "dev": true - }, - "array-slice": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", - "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", + "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", "dev": true }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", "dev": true }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dev": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "asn1.js": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", - "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", "dev": true, "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "safer-buffer": "^2.1.0" + "for-in": "^1.0.1" } }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - }, - "assign-symbols": { + "fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true - }, - "async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true + "gaze": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", + "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", + "dev": true, + "requires": { + "globule": "^1.0.0" + } }, - "aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, - "babel-jest": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz", - "integrity": "sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA==", + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", "dev": true, "requires": { - "@jest/transform": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/babel__core": "^7.1.7", - "babel-plugin-istanbul": "^6.0.0", - "babel-preset-jest": "^26.6.2", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "slash": "^3.0.0" + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" } }, - "babel-plugin-istanbul": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", - "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", + "getobject": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/getobject/-/getobject-1.0.2.tgz", + "integrity": "sha512-2zblDBaFcb3rB4rF77XVnuINOE2h2k/OnqXAiy0IrTxUfV1iFp3la33oAQVY9pCpWU268WFYVt2t71hlMuLsOg==", + "dev": true + }, + "glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^4.0.0", - "test-exclude": "^6.0.0" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, - "babel-plugin-jest-hoist": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz", - "integrity": "sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw==", + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", "dev": true, "requires": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.0.0", - "@types/babel__traverse": "^7.0.6" + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" } }, - "babel-polyfill": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", - "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=", + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", "dev": true, "requires": { - "babel-runtime": "^6.26.0", - "core-js": "^2.5.0", - "regenerator-runtime": "^0.10.5" + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" } }, - "babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "globule": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.3.tgz", + "integrity": "sha512-mb1aYtDbIjTu4ShMB85m3UzjX9BVKe9WCzsnfMSZk+K5GpIbBOexgg4PPCt5eHDEG5/ZQAUX2Kct02zfiPLsKg==", "dev": true, "requires": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" + "glob": "~7.1.1", + "lodash": "~4.17.10", + "minimatch": "~3.0.2" } }, - "babel-preset-jest": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz", - "integrity": "sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ==", + "graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "dev": true + }, + "grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "grunt": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.4.1.tgz", + "integrity": "sha512-ZXIYXTsAVrA7sM+jZxjQdrBOAg7DyMUplOMhTaspMRExei+fD0BTwdWXnn0W5SXqhb/Q/nlkzXclSi3IH55PIA==", "dev": true, "requires": { - "babel-plugin-jest-hoist": "^26.6.2", - "babel-preset-current-node-syntax": "^1.0.0" - } - }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "dev": true, - "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", - "dev": true - } - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - }, - "body": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/body/-/body-5.1.0.tgz", - "integrity": "sha1-5LoM5BCkaTYyM2dgnstOZVMSUGk=", - "dev": true, - "requires": { - "continuable-cache": "^0.3.1", - "error": "^7.0.0", - "raw-body": "~1.1.0", - "safe-json-parse": "~1.0.1" - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", - "dev": true - }, - "browserslist": { - "version": "4.16.6", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", - "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001219", - "colorette": "^1.2.2", - "electron-to-chromium": "^1.3.723", - "escalade": "^3.1.1", - "node-releases": "^1.1.71" - } - }, - "bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "requires": { - "node-int64": "^0.4.0" - } - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "bytes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz", - "integrity": "sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g=", - "dev": true - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - } - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "can-use-dom": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/can-use-dom/-/can-use-dom-0.1.0.tgz", - "integrity": "sha1-IsxKNKCrxDlQ9CxkEQJKP2NmtFo=", - "dev": true - }, - "caniuse-lite": { - "version": "1.0.30001228", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001228.tgz", - "integrity": "sha512-QQmLOGJ3DEgokHbMSA8cj2a+geXqmnpyOFT0lhQV6P3/YOJvGDEwoedcwxEQ30gJIwIIunHIicunJ2rzK5gB2A==", - "dev": true - }, - "capture-exit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", - "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", - "dev": true, - "requires": { - "rsvp": "^4.8.4" - } - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true - }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "cjs-module-lexer": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz", - "integrity": "sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw==", - "dev": true - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, - "collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", - "dev": true - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dev": true, - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "colorette": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", - "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", - "dev": true - }, - "colors": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", - "dev": true - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "continuable-cache": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/continuable-cache/-/continuable-cache-0.3.1.tgz", - "integrity": "sha1-vXJ6f67XfnH/OYWskzUakSczrQ8=", - "dev": true - }, - "convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - } - } - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true - }, - "core-js": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", - "dev": true - }, - "core-js-pure": { - "version": "3.12.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.12.1.tgz", - "integrity": "sha512-1cch+qads4JnDSWsvc7d6nzlKAippwjUlf6vykkTLW53VSV+NkE6muGBToAjEA8pG90cSfcud3JgVmW2ds5TaQ==", - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "cross-fetch": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.4.tgz", - "integrity": "sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ==", - "dev": true, - "requires": { - "node-fetch": "2.6.1" - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "css": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", - "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==", - "dev": true, - "requires": { - "inherits": "^2.0.4", - "source-map": "^0.6.1", - "source-map-resolve": "^0.6.0" - } - }, - "css.escape": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", - "integrity": "sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s=", - "dev": true - }, - "cssom": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", - "dev": true - }, - "cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "dev": true, - "requires": { - "cssom": "~0.3.6" - }, - "dependencies": { - "cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "dev": true - } - } - }, - "csstype": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.8.tgz", - "integrity": "sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==", - "dev": true - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "data-urls": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", - "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", - "dev": true, - "requires": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" - } - }, - "dateformat": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", - "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", - "dev": true - }, - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "decimal.js": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.1.tgz", - "integrity": "sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw==", - "dev": true - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "dev": true - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, - "detect-file": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", - "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", - "dev": true - }, - "detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true - }, - "diff-sequences": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", - "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", - "dev": true - }, - "dom-accessibility-api": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.4.tgz", - "integrity": "sha512-TvrjBckDy2c6v6RLxPv5QXOnU+SmF9nBII5621Ve5fu6Z/BDrENurBEvlC1f44lKEUVqOpK4w9E5Idc5/EgkLQ==", - "dev": true - }, - "dom-helpers": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", - "dev": true, - "requires": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" - } - }, - "domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", - "dev": true, - "requires": { - "webidl-conversions": "^5.0.0" - }, - "dependencies": { - "webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "dev": true - } - } - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "electron-to-chromium": { - "version": "1.3.731", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.731.tgz", - "integrity": "sha512-dn1Nyd0DuFa3xhqZJr6/L9phyk+YXJpvrz6Vcu6mFxFqr5TQ9r/F3yvOYFUrEwY4Tbb1YBjN19TDKnSVCQvalA==", - "dev": true - }, - "emittery": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.7.2.tgz", - "integrity": "sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "error": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/error/-/error-7.2.1.tgz", - "integrity": "sha512-fo9HBvWnx3NGUKMvMwB/CBCMMrfEJgbDTVDEkPygA3Bdd3lM1OyCd+rbQ8BwnpF6GdVeOLDNmyL4N5Bg80ZvdA==", - "dev": true, - "requires": { - "string-template": "~0.2.1" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "escodegen": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", - "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", - "dev": true, - "requires": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "eventemitter2": { - "version": "0.4.14", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", - "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=", - "dev": true - }, - "exec-sh": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.6.tgz", - "integrity": "sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w==", - "dev": true - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", - "dev": true, - "requires": { - "homedir-polyfill": "^1.0.1" - } - }, - "expect": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", - "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-styles": "^4.0.0", - "jest-get-type": "^26.3.0", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-regex-util": "^26.0.0" - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "faye-websocket": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", - "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", - "dev": true, - "requires": { - "websocket-driver": ">=0.5.1" - } - }, - "fb-watchman": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", - "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", - "dev": true, - "requires": { - "bser": "2.1.1" - } - }, - "file-sync-cmp": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/file-sync-cmp/-/file-sync-cmp-0.1.1.tgz", - "integrity": "sha1-peeo/7+kk7Q7kju9TKiaU7Y7YSs=", - "dev": true - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "findup-sync": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", - "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=", - "dev": true, - "requires": { - "glob": "~5.0.0" - }, - "dependencies": { - "glob": { - "version": "5.0.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", - "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", - "dev": true, - "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "fined": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", - "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", - "dev": true, - "requires": { - "expand-tilde": "^2.0.2", - "is-plain-object": "^2.0.3", - "object.defaults": "^1.1.0", - "object.pick": "^1.2.0", - "parse-filepath": "^1.0.1" - } - }, - "flagged-respawn": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", - "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", - "dev": true - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true - }, - "for-own": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", - "dev": true, - "requires": { - "for-in": "^1.0.1" - } - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, - "requires": { - "map-cache": "^0.2.2" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "gaze": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", - "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", - "dev": true, - "requires": { - "globule": "^1.0.0" - } - }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - } - }, - "get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true - }, - "getobject": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/getobject/-/getobject-1.0.1.tgz", - "integrity": "sha512-tj18lLe+917AACr6BdVoUuHnBPTVd9BEJp1vxnMZ58ztNvuxz9Ufa+wf3g37tlGITH35jggwZ2d9lcgHJJgXfQ==", - "dev": true - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", - "dev": true, - "requires": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" - } - }, - "global-prefix": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", - "dev": true, - "requires": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "globule": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.2.tgz", - "integrity": "sha512-7IDTQTIu2xzXkT+6mlluidnWo+BypnbSoEVVQCGfzqnl5Ik8d3e1d4wycb8Rj9tWW+Z39uPWsdlquqiqPCd/pA==", - "dev": true, - "requires": { - "glob": "~7.1.1", - "lodash": "~4.17.10", - "minimatch": "~3.0.2" - } - }, - "graceful-fs": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", - "dev": true - }, - "growly": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", - "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", - "dev": true, - "optional": true - }, - "grunt": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.4.0.tgz", - "integrity": "sha512-yRFc0GVCDu9yxqOFzpuXQ2pEdgtLDnFv5Qz54jfIcNnpJ8Z7B7P7kPkT4VMuRvm+N+QOsI8C4v/Q0DSaoj3LgQ==", - "dev": true, - "requires": { - "dateformat": "~3.0.3", - "eventemitter2": "~0.4.13", - "exit": "~0.1.2", - "findup-sync": "~0.3.0", - "glob": "~7.1.6", - "grunt-cli": "~1.4.2", - "grunt-known-options": "~1.1.1", - "grunt-legacy-log": "~3.0.0", - "grunt-legacy-util": "~2.0.1", - "iconv-lite": "~0.4.13", - "js-yaml": "~3.14.0", - "minimatch": "~3.0.4", - "mkdirp": "~1.0.4", - "nopt": "~3.0.6", - "rimraf": "~3.0.2" - }, - "dependencies": { - "grunt-cli": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.4.2.tgz", - "integrity": "sha512-wsu6BZh7KCnfeaSkDrKIAvOlqGKxNRTZjc8xfZlvxCByQIqUfZ31kh5uHpPnhQ4NdVgvaWaVxa1LUbVU80nACw==", - "dev": true, - "requires": { - "grunt-known-options": "~1.1.1", - "interpret": "~1.1.0", - "liftup": "~3.0.1", - "nopt": "~4.0.1", - "v8flags": "~3.2.0" - }, - "dependencies": { - "nopt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", - "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", - "dev": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - } - } - } - } - }, - "grunt-contrib-copy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/grunt-contrib-copy/-/grunt-contrib-copy-1.0.0.tgz", - "integrity": "sha1-cGDGWB6QS4qw0A8HbgqPbj58NXM=", - "dev": true, - "requires": { - "chalk": "^1.1.1", - "file-sync-cmp": "^0.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "grunt-contrib-watch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-1.1.0.tgz", - "integrity": "sha512-yGweN+0DW5yM+oo58fRu/XIRrPcn3r4tQx+nL7eMRwjpvk+rQY6R8o94BPK0i2UhTg9FN21hS+m8vR8v9vXfeg==", - "dev": true, - "requires": { - "async": "^2.6.0", - "gaze": "^1.1.0", - "lodash": "^4.17.10", - "tiny-lr": "^1.1.1" - }, - "dependencies": { - "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "dev": true, - "requires": { - "lodash": "^4.17.14" - } - } - } - }, - "grunt-known-options": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-1.1.1.tgz", - "integrity": "sha512-cHwsLqoighpu7TuYj5RonnEuxGVFnztcUqTqp5rXFGYL4OuPFofwC4Ycg7n9fYwvK6F5WbYgeVOwph9Crs2fsQ==", - "dev": true - }, - "grunt-legacy-log": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-3.0.0.tgz", - "integrity": "sha512-GHZQzZmhyq0u3hr7aHW4qUH0xDzwp2YXldLPZTCjlOeGscAOWWPftZG3XioW8MasGp+OBRIu39LFx14SLjXRcA==", - "dev": true, - "requires": { - "colors": "~1.1.2", - "grunt-legacy-log-utils": "~2.1.0", - "hooker": "~0.2.3", - "lodash": "~4.17.19" - } - }, - "grunt-legacy-log-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-2.1.0.tgz", - "integrity": "sha512-lwquaPXJtKQk0rUM1IQAop5noEpwFqOXasVoedLeNzaibf/OPWjKYvvdqnEHNmU+0T0CaReAXIbGo747ZD+Aaw==", - "dev": true, - "requires": { - "chalk": "~4.1.0", - "lodash": "~4.17.19" - } - }, - "grunt-legacy-util": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-2.0.1.tgz", - "integrity": "sha512-2bQiD4fzXqX8rhNdXkAywCadeqiPiay0oQny77wA2F3WF4grPJXCvAcyoWUJV+po/b15glGkxuSiQCK299UC2w==", - "dev": true, - "requires": { - "async": "~3.2.0", - "exit": "~0.1.2", - "getobject": "~1.0.0", - "hooker": "~0.2.3", - "lodash": "~4.17.21", - "underscore.string": "~3.3.5", - "which": "~2.0.2" - }, - "dependencies": { - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true - }, - "har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "dev": true, - "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", - "dev": true, - "requires": { - "parse-passwd": "^1.0.0" - } - }, - "hooker": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", - "integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk=", - "dev": true - }, - "hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "html-encoding-sniffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", - "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", - "dev": true, - "requires": { - "whatwg-encoding": "^1.0.5" - } - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "html-parse-stringify": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", - "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", - "dev": true, - "requires": { - "void-elements": "3.1.0" - } - }, - "http-parser-js": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz", - "integrity": "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==", - "dev": true - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", - "dev": true - }, - "i18next": { - "version": "19.9.2", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-19.9.2.tgz", - "integrity": "sha512-0i6cuo6ER6usEOtKajUUDj92zlG+KArFia0857xxiEHAQcUwh/RtOQocui1LPJwunSYT574Pk64aNva1kwtxZg==", - "dev": true, - "requires": { - "@babel/runtime": "^7.12.0" - } - }, - "i18next-http-backend": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-1.2.4.tgz", - "integrity": "sha512-ewvodowF2oBP0/vVAerpVF6aaIdAqH594K/ThA4Kl2A5Gm4QvUQuakvrFV5KMaKOggykGd9MuQ4xMcTFayVF1w==", - "dev": true, - "requires": { - "cross-fetch": "3.1.4" - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "import-local": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", - "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", - "dev": true, - "requires": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - }, - "interpret": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", - "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", - "dev": true - }, - "is-absolute": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", - "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", - "dev": true, - "requires": { - "is-relative": "^1.0.0", - "is-windows": "^1.0.1" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "requires": { - "ci-info": "^2.0.0" - } - }, - "is-core-module": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", - "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true, - "optional": true - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" + "dateformat": "~3.0.3", + "eventemitter2": "~0.4.13", + "exit": "~0.1.2", + "findup-sync": "~0.3.0", + "glob": "~7.1.6", + "grunt-cli": "~1.4.2", + "grunt-known-options": "~2.0.0", + "grunt-legacy-log": "~3.0.0", + "grunt-legacy-util": "~2.0.1", + "iconv-lite": "~0.4.13", + "js-yaml": "~3.14.0", + "minimatch": "~3.0.4", + "mkdirp": "~1.0.4", + "nopt": "~3.0.6", + "rimraf": "~3.0.2" + }, + "dependencies": { + "grunt-cli": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.4.3.tgz", + "integrity": "sha512-9Dtx/AhVeB4LYzsViCjUQkd0Kw0McN2gYpdmGYKtE2a5Yt7v1Q+HYZVWhqXc/kGnxlMtqKDxSwotiGeFmkrCoQ==", + "dev": true, + "requires": { + "grunt-known-options": "~2.0.0", + "interpret": "~1.1.0", + "liftup": "~3.0.1", + "nopt": "~4.0.1", + "v8flags": "~3.2.0" + }, + "dependencies": { + "nopt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", + "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", + "dev": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + } + } + } } }, - "is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true - }, - "is-relative": { + "grunt-contrib-copy": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", - "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", + "resolved": "https://registry.npmjs.org/grunt-contrib-copy/-/grunt-contrib-copy-1.0.0.tgz", + "integrity": "sha1-cGDGWB6QS4qw0A8HbgqPbj58NXM=", "dev": true, "requires": { - "is-unc-path": "^1.0.0" + "chalk": "^1.1.1", + "file-sync-cmp": "^0.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } } }, - "is-stream": { + "grunt-contrib-watch": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "is-unc-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", - "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", - "dev": true, - "requires": { - "unc-path-regex": "^0.1.2" - } - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true - }, - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "optional": true, - "requires": { - "is-docker": "^2.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", - "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "dev": true, - "requires": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - } - }, - "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - } - }, - "istanbul-lib-source-maps": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", - "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", + "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-1.1.0.tgz", + "integrity": "sha512-yGweN+0DW5yM+oo58fRu/XIRrPcn3r4tQx+nL7eMRwjpvk+rQY6R8o94BPK0i2UhTg9FN21hS+m8vR8v9vXfeg==", "dev": true, "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" + "async": "^2.6.0", + "gaze": "^1.1.0", + "lodash": "^4.17.10", + "tiny-lr": "^1.1.1" }, "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", "dev": true, "requires": { - "ms": "2.1.2" + "lodash": "^4.17.14" } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true } } }, - "istanbul-reports": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", - "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", + "grunt-known-options": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-2.0.0.tgz", + "integrity": "sha512-GD7cTz0I4SAede1/+pAbmJRG44zFLPipVtdL9o3vqx9IEyb7b4/Y3s7r6ofI3CchR5GvYJ+8buCSioDv5dQLiA==", + "dev": true + }, + "grunt-legacy-log": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-3.0.0.tgz", + "integrity": "sha512-GHZQzZmhyq0u3hr7aHW4qUH0xDzwp2YXldLPZTCjlOeGscAOWWPftZG3XioW8MasGp+OBRIu39LFx14SLjXRcA==", "dev": true, "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" + "colors": "~1.1.2", + "grunt-legacy-log-utils": "~2.1.0", + "hooker": "~0.2.3", + "lodash": "~4.17.19" } }, - "jest": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest/-/jest-26.6.3.tgz", - "integrity": "sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q==", + "grunt-legacy-log-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-2.1.0.tgz", + "integrity": "sha512-lwquaPXJtKQk0rUM1IQAop5noEpwFqOXasVoedLeNzaibf/OPWjKYvvdqnEHNmU+0T0CaReAXIbGo747ZD+Aaw==", "dev": true, "requires": { - "@jest/core": "^26.6.3", - "import-local": "^3.0.2", - "jest-cli": "^26.6.3" - }, - "dependencies": { - "jest-cli": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.6.3.tgz", - "integrity": "sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg==", - "dev": true, - "requires": { - "@jest/core": "^26.6.3", - "@jest/test-result": "^26.6.2", - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "import-local": "^3.0.2", - "is-ci": "^2.0.0", - "jest-config": "^26.6.3", - "jest-util": "^26.6.2", - "jest-validate": "^26.6.2", - "prompts": "^2.0.1", - "yargs": "^15.4.1" - } - } + "chalk": "~4.1.0", + "lodash": "~4.17.19" } }, - "jest-changed-files": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.6.2.tgz", - "integrity": "sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ==", + "grunt-legacy-util": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-2.0.1.tgz", + "integrity": "sha512-2bQiD4fzXqX8rhNdXkAywCadeqiPiay0oQny77wA2F3WF4grPJXCvAcyoWUJV+po/b15glGkxuSiQCK299UC2w==", "dev": true, "requires": { - "@jest/types": "^26.6.2", - "execa": "^4.0.0", - "throat": "^5.0.0" + "async": "~3.2.0", + "exit": "~0.1.2", + "getobject": "~1.0.0", + "hooker": "~0.2.3", + "lodash": "~4.17.21", + "underscore.string": "~3.3.5", + "which": "~2.0.2" }, "dependencies": { - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - } - }, - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "dev": true - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -3363,407 +1016,267 @@ } } }, - "jest-config": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.6.3.tgz", - "integrity": "sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg==", + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "requires": { - "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^26.6.3", - "@jest/types": "^26.6.2", - "babel-jest": "^26.6.3", - "chalk": "^4.0.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.1", - "graceful-fs": "^4.2.4", - "jest-environment-jsdom": "^26.6.2", - "jest-environment-node": "^26.6.2", - "jest-get-type": "^26.3.0", - "jest-jasmine2": "^26.6.3", - "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.6.2", - "jest-util": "^26.6.2", - "jest-validate": "^26.6.2", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2" + "function-bind": "^1.1.1" } }, - "jest-diff": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", - "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", "dev": true, "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" + "ansi-regex": "^2.0.0" } }, - "jest-docblock": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-26.0.0.tgz", - "integrity": "sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==", - "dev": true, - "requires": { - "detect-newline": "^3.0.0" - } + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true }, - "jest-each": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.6.2.tgz", - "integrity": "sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A==", + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true + }, + "history": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", + "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", "dev": true, "requires": { - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "jest-get-type": "^26.3.0", - "jest-util": "^26.6.2", - "pretty-format": "^26.6.2" + "@babel/runtime": "^7.1.2", + "loose-envify": "^1.2.0", + "resolve-pathname": "^3.0.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0", + "value-equal": "^1.0.1" } }, - "jest-environment-jsdom": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz", - "integrity": "sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q==", + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", "dev": true, "requires": { - "@jest/environment": "^26.6.2", - "@jest/fake-timers": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "jest-mock": "^26.6.2", - "jest-util": "^26.6.2", - "jsdom": "^16.4.0" + "react-is": "^16.7.0" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + } } }, - "jest-environment-node": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.6.2.tgz", - "integrity": "sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag==", + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", "dev": true, "requires": { - "@jest/environment": "^26.6.2", - "@jest/fake-timers": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "jest-mock": "^26.6.2", - "jest-util": "^26.6.2" + "parse-passwd": "^1.0.0" } }, - "jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", + "hooker": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", + "integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk=", "dev": true }, - "jest-haste-map": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz", - "integrity": "sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==", + "html-parse-stringify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", + "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", "dev": true, "requires": { - "@jest/types": "^26.6.2", - "@types/graceful-fs": "^4.1.2", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.1.2", - "graceful-fs": "^4.2.4", - "jest-regex-util": "^26.0.0", - "jest-serializer": "^26.6.2", - "jest-util": "^26.6.2", - "jest-worker": "^26.6.2", - "micromatch": "^4.0.2", - "sane": "^4.0.3", - "walker": "^1.0.7" + "void-elements": "3.1.0" } }, - "jest-jasmine2": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz", - "integrity": "sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg==", - "dev": true, - "requires": { - "@babel/traverse": "^7.1.0", - "@jest/environment": "^26.6.2", - "@jest/source-map": "^26.6.2", - "@jest/test-result": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "expect": "^26.6.2", - "is-generator-fn": "^2.0.0", - "jest-each": "^26.6.2", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-runtime": "^26.6.3", - "jest-snapshot": "^26.6.2", - "jest-util": "^26.6.2", - "pretty-format": "^26.6.2", - "throat": "^5.0.0" - } + "http-parser-js": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz", + "integrity": "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==", + "dev": true }, - "jest-leak-detector": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz", - "integrity": "sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg==", + "i18next": { + "version": "19.9.2", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-19.9.2.tgz", + "integrity": "sha512-0i6cuo6ER6usEOtKajUUDj92zlG+KArFia0857xxiEHAQcUwh/RtOQocui1LPJwunSYT574Pk64aNva1kwtxZg==", "dev": true, "requires": { - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" + "@babel/runtime": "^7.12.0" } }, - "jest-matcher-utils": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", - "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", + "i18next-http-backend": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-1.3.1.tgz", + "integrity": "sha512-o79n4GBBRpl20hByC+ne/S1UaSZ4iGAn59Hu2TEZGjN0WLB72L7WrM39Cshziyrssp6MQfdI8wjToU2Q6kpSvA==", "dev": true, "requires": { - "chalk": "^4.0.0", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" + "cross-fetch": "3.1.4" } }, - "jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" + "safer-buffer": ">= 2.1.2 < 3" } }, - "jest-mock": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", - "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==", + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*" + "once": "^1.3.0", + "wrappy": "1" } }, - "jest-pnp-resolver": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", - "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "jest-regex-util": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", - "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, - "jest-resolve": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", - "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^26.6.2", - "read-pkg-up": "^7.0.1", - "resolve": "^1.18.1", - "slash": "^3.0.0" - } + "interpret": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", + "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", + "dev": true }, - "jest-resolve-dependencies": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz", - "integrity": "sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "jest-regex-util": "^26.0.0", - "jest-snapshot": "^26.6.2" - } + "ip-regex": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", + "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==", + "dev": true }, - "jest-runner": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.6.3.tgz", - "integrity": "sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ==", + "is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", "dev": true, "requires": { - "@jest/console": "^26.6.2", - "@jest/environment": "^26.6.2", - "@jest/test-result": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.7.1", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "jest-config": "^26.6.3", - "jest-docblock": "^26.0.0", - "jest-haste-map": "^26.6.2", - "jest-leak-detector": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-resolve": "^26.6.2", - "jest-runtime": "^26.6.3", - "jest-util": "^26.6.2", - "jest-worker": "^26.6.2", - "source-map-support": "^0.5.6", - "throat": "^5.0.0" + "is-relative": "^1.0.0", + "is-windows": "^1.0.1" } }, - "jest-runtime": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.6.3.tgz", - "integrity": "sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==", + "is-core-module": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.7.0.tgz", + "integrity": "sha512-ByY+tjCciCr+9nLryBYcSD50EOGWt95c7tIsKTG1J2ixKKXPvF7Ej3AVd+UfDydAJom3biBGDBALaO79ktwgEQ==", "dev": true, "requires": { - "@jest/console": "^26.6.2", - "@jest/environment": "^26.6.2", - "@jest/fake-timers": "^26.6.2", - "@jest/globals": "^26.6.2", - "@jest/source-map": "^26.6.2", - "@jest/test-result": "^26.6.2", - "@jest/transform": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0", - "cjs-module-lexer": "^0.6.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.4", - "jest-config": "^26.6.3", - "jest-haste-map": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-mock": "^26.6.2", - "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.6.2", - "jest-snapshot": "^26.6.2", - "jest-util": "^26.6.2", - "jest-validate": "^26.6.2", - "slash": "^3.0.0", - "strip-bom": "^4.0.0", - "yargs": "^15.4.1" + "has": "^1.0.3" } }, - "jest-serializer": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.6.2.tgz", - "integrity": "sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==", - "dev": true, - "requires": { - "@types/node": "*", - "graceful-fs": "^4.2.4" - } + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true }, - "jest-snapshot": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.6.2.tgz", - "integrity": "sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/babel__traverse": "^7.0.4", - "@types/prettier": "^2.0.0", - "chalk": "^4.0.0", - "expect": "^26.6.2", - "graceful-fs": "^4.2.4", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "jest-haste-map": "^26.6.2", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-resolve": "^26.6.2", - "natural-compare": "^1.4.0", - "pretty-format": "^26.6.2", - "semver": "^7.3.2" - }, - "dependencies": { - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true }, - "jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" + "is-extglob": "^2.1.1" } }, - "jest-validate": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.6.2.tgz", - "integrity": "sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ==", + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "requires": { - "@jest/types": "^26.6.2", - "camelcase": "^6.0.0", - "chalk": "^4.0.0", - "jest-get-type": "^26.3.0", - "leven": "^3.1.0", - "pretty-format": "^26.6.2" - }, - "dependencies": { - "camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true - } + "isobject": "^3.0.1" } }, - "jest-watcher": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.6.2.tgz", - "integrity": "sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ==", + "is-relative": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", "dev": true, "requires": { - "@jest/test-result": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "jest-util": "^26.6.2", - "string-length": "^4.0.1" + "is-unc-path": "^1.0.0" } }, - "jest-worker": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", - "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "is-unc-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", "dev": true, "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" + "unc-path-regex": "^0.1.2" } }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, "jquery": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz", @@ -3786,97 +1299,12 @@ "esprima": "^4.0.0" } }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true - }, - "jsdom": { - "version": "16.5.3", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.5.3.tgz", - "integrity": "sha512-Qj1H+PEvUsOtdPJ056ewXM4UJPCi4hhLA8wpiz9F2YvsRBhuFsXxtrIFAgGBDynQA9isAMGE91PfUYbdMPXuTA==", - "dev": true, - "requires": { - "abab": "^2.0.5", - "acorn": "^8.1.0", - "acorn-globals": "^6.0.0", - "cssom": "^0.4.4", - "cssstyle": "^2.3.0", - "data-urls": "^2.0.0", - "decimal.js": "^10.2.1", - "domexception": "^2.0.1", - "escodegen": "^2.0.0", - "html-encoding-sniffer": "^2.0.1", - "is-potential-custom-element-name": "^1.0.0", - "nwsapi": "^2.2.0", - "parse5": "6.0.1", - "request": "^2.88.2", - "request-promise-native": "^1.0.9", - "saxes": "^5.0.1", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.0.0", - "w3c-hr-time": "^1.0.2", - "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.1.0", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.5.0", - "ws": "^7.4.4", - "xml-name-validator": "^3.0.0" - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "json-format": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-format/-/json-format-1.0.1.tgz", + "integrity": "sha1-FD9n5irxKda//tKIpGJl6iPQ3ww=", "dev": true }, - "json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, "jssha": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/jssha/-/jssha-3.2.0.tgz", @@ -3889,28 +1317,6 @@ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true }, - "kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true - }, - "leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, "liftup": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/liftup/-/liftup-3.0.1.tgz", @@ -3941,12 +1347,6 @@ } } }, - "lines-and-columns": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", - "dev": true - }, "livereload-js": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-2.4.0.tgz", @@ -3954,12 +1354,13 @@ "dev": true }, "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, "requires": { - "p-locate": "^4.1.0" + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" } }, "lodash": { @@ -4001,19 +1402,10 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, "luxon": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.27.0.tgz", - "integrity": "sha512-VKsFsPggTA0DvnxtJdiExAucKdAnwbCCNlMM5ENvHlxubqWd0xhZcdb4XgZ7QFNhaRhilXCFxHuoObP5BNA4PA==", + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.0.tgz", + "integrity": "sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ==", "dev": true }, "lz-string": { @@ -4022,15 +1414,6 @@ "integrity": "sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=", "dev": true }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - } - }, "make-iterator": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", @@ -4040,29 +1423,11 @@ "kind-of": "^6.0.2" } }, - "makeerror": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", - "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", - "dev": true, - "requires": { - "tmpl": "1.0.x" - } - }, "map-cache": { "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, - "requires": { - "object-visit": "^1.0.0" - } + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true }, "material-colors": { "version": "1.2.6", @@ -4070,12 +1435,6 @@ "integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==", "dev": true }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, "micromatch": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", @@ -4086,33 +1445,16 @@ "picomatch": "^2.2.3" } }, - "mime-db": { - "version": "1.47.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz", - "integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==", - "dev": true - }, - "mime-types": { - "version": "2.1.30", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz", - "integrity": "sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==", + "mini-create-react-context": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz", + "integrity": "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==", "dev": true, "requires": { - "mime-db": "1.47.0" + "@babel/runtime": "^7.12.1", + "tiny-warning": "^1.0.3" } }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true - }, "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -4128,33 +1470,6 @@ "brace-expansion": "^1.1.7" } }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, - "mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dev": true, - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, "mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -4167,49 +1482,15 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "node-fetch": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz", + "integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==", "dev": true, "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "whatwg-url": "^5.0.0" } }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", - "dev": true - }, - "node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", - "dev": true - }, "node-localstorage": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-localstorage/-/node-localstorage-1.3.1.tgz", @@ -4219,55 +1500,6 @@ "write-file-atomic": "^1.1.4" } }, - "node-modules-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", - "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", - "dev": true - }, - "node-notifier": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.2.tgz", - "integrity": "sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg==", - "dev": true, - "optional": true, - "requires": { - "growly": "^1.3.0", - "is-wsl": "^2.2.0", - "semver": "^7.3.2", - "shellwords": "^0.1.1", - "uuid": "^8.3.0", - "which": "^2.0.2" - }, - "dependencies": { - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "optional": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "optional": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "node-releases": { - "version": "1.1.72", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.72.tgz", - "integrity": "sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw==", - "dev": true - }, "nopt": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", @@ -4277,105 +1509,29 @@ "abbrev": "1" } }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "npm-force-resolutions": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/npm-force-resolutions/-/npm-force-resolutions-0.0.10.tgz", + "integrity": "sha512-Jscex+xIU6tw3VsyrwxM1TeT+dd9Fd3UOMAjy6J1TMpuYeEqg4LQZnATQO5vjPrsARm3und6zc6Dii/GUyRE5A==", "dev": true, "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "^2.0.0" + "json-format": "^1.0.1", + "source-map-support": "^0.5.5", + "xmlhttprequest": "^1.8.0" } }, - "nwsapi": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", - "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", - "dev": true - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true - }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, "object-inspect": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz", - "integrity": "sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", + "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", "dev": true }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, - "requires": { - "isobject": "^3.0.0" - } - }, "object.defaults": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", @@ -4416,15 +1572,6 @@ "wrappy": "1" } }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, "openpgp": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/openpgp/-/openpgp-4.6.2.tgz", @@ -4436,20 +1583,6 @@ "node-localstorage": "~1.3.0" } }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - } - }, "os-homedir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", @@ -4472,18 +1605,6 @@ "os-tmpdir": "^1.0.0" } }, - "p-each-series": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", - "integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==", - "dev": true - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, "p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -4494,12 +1615,12 @@ } }, "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, "requires": { - "p-limit": "^2.2.0" + "p-limit": "^2.0.0" } }, "p-try": { @@ -4519,64 +1640,47 @@ "path-root": "^0.1.1" } }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, "parse-passwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", "dev": true }, - "parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true - }, "passbolt-styleguide": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/passbolt-styleguide/-/passbolt-styleguide-3.2.1.tgz", - "integrity": "sha512-OEdq2nBdrKyk/6j3Xc7ib6wSXCCRtUdf05HvM1U7rZeLr8VCVLs9dzIs1VhlJ7Lcl+wFJWk+XMlgcoklKAIZ1g==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/passbolt-styleguide/-/passbolt-styleguide-3.4.0.tgz", + "integrity": "sha512-ZEgI3rwHAocDlqn+sj3WFkjvE+bJJTgMLlx/paAGIgHU8SrHkMeJffhy2JOVh4yCsU59SBoZGZNNmJB/DAmxGQ==", "dev": true, "requires": { - "@testing-library/dom": "^7.24.2", - "@testing-library/jest-dom": "^5.11.4", + "@testing-library/dom": "^7.31.0", "@testing-library/react": "^10.4.9", + "debounce-promise": "^3.1.2", + "grapheme-splitter": "^1.0.4", "i18next": "^19.8.5", - "i18next-http-backend": "^1.1.0", - "jest": "^26.4.2", + "i18next-http-backend": "^1.2.4", + "ip-regex": "^4.3.0", "jssha": "^3.2.0", - "luxon": "^1.26.0", - "node-fetch": "^2.6.1", + "luxon": "^1.27.0", + "npm-force-resolutions": "0.0.10", + "prop-types": "^15.7.2", + "qrcode": "1.4.4", + "react": "16.14.0", "react-color": "^2.19.3", + "react-dom": "16.14.0", "react-i18next": "^11.8.5", "react-list": "^0.8.15", + "react-router-dom": "^5.2.0", "react-transition-group": "^4.4.1", "simplebar": "^5.2.1", + "uuid": "^8.3.0", "webextension-polyfill": "^0.6.0", "xregexp": "^4.3.0" } }, "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", "dev": true }, "path-is-absolute": { @@ -4585,16 +1689,10 @@ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, "path-root": { @@ -4612,46 +1710,33 @@ "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", "dev": true }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, - "picomatch": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", - "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==", - "dev": true - }, - "pirates": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", - "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", - "dev": true, - "requires": { - "node-modules-regexp": "^1.0.0" - } - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", "dev": true, "requires": { - "find-up": "^4.0.0" + "isarray": "0.0.1" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + } } }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", "dev": true }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "pngjs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", + "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", "dev": true }, "pretty-format": { @@ -4667,23 +1752,13 @@ }, "dependencies": { "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true } } }, - "prompts": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.1.tgz", - "integrity": "sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ==", - "dev": true, - "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - } - }, "prop-types": { "version": "15.7.2", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", @@ -4703,28 +1778,21 @@ } } }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", - "dev": true - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "qrcode": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.4.4.tgz", + "integrity": "sha512-oLzEC5+NKFou9P0bMj5+v6Z40evexeE29Z9cummZXZ9QXyMr3lphkURzxjXgPJC5azpxcshoDWV1xE46z+/c3Q==", "dev": true, "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "buffer": "^5.4.3", + "buffer-alloc": "^1.2.0", + "buffer-from": "^1.1.1", + "dijkstrajs": "^1.0.1", + "isarray": "^2.0.1", + "pngjs": "^3.3.0", + "yargs": "^13.2.4" } }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, "qs": { "version": "6.10.1", "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", @@ -4744,6 +1812,17 @@ "string_decoder": "0.10" } }, + "react": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", + "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", + "dev": true, + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2" + } + }, "react-color": { "version": "2.19.3", "resolved": "https://registry.npmjs.org/react-color/-/react-color-2.19.3.tgz", @@ -4759,13 +1838,25 @@ "tinycolor2": "^1.4.1" } }, + "react-dom": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", + "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==", + "dev": true, + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "scheduler": "^0.19.1" + } + }, "react-i18next": { - "version": "11.8.15", - "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-11.8.15.tgz", - "integrity": "sha512-ZbKcbYYKukgDL0MiUWKJTEsEftjSTNVZv67/V+SjPqTRwuF/aL4NbUtuEcb4WjHk0HyZ1M+2wGd07Fp0RUNHKA==", + "version": "11.14.3", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-11.14.3.tgz", + "integrity": "sha512-Hf2aanbKgYxPjG8ZdKr+PBz9sY6sxXuZWizxCYyJD2YzvJ0W9JTQcddVEjDaKyBoCyd3+5HTerdhc9ehFugc6g==", "dev": true, "requires": { - "@babel/runtime": "^7.13.6", + "@babel/runtime": "^7.14.5", "html-parse-stringify": "^3.0.1" } }, @@ -4784,10 +1875,51 @@ "prop-types": "15" } }, + "react-router": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.1.tgz", + "integrity": "sha512-lIboRiOtDLFdg1VTemMwud9vRVuOCZmUIT/7lUoZiSpPODiiH1UQlfXy+vPLC/7IWdFYnhRwAyNqA/+I7wnvKQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.12.13", + "history": "^4.9.0", + "hoist-non-react-statics": "^3.1.0", + "loose-envify": "^1.3.1", + "mini-create-react-context": "^0.4.0", + "path-to-regexp": "^1.7.0", + "prop-types": "^15.6.2", + "react-is": "^16.6.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + } + } + }, + "react-router-dom": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.0.tgz", + "integrity": "sha512-ObVBLjUZsphUUMVycibxgMdh5jJ1e3o+KpAZBVeHcNQZ4W+uUGGWsokurzlF4YOldQYRQL4y6yFRWM4m3svmuQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.12.13", + "history": "^4.9.0", + "loose-envify": "^1.3.1", + "prop-types": "^15.6.2", + "react-router": "5.2.1", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + } + }, "react-transition-group": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz", - "integrity": "sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz", + "integrity": "sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==", "dev": true, "requires": { "@babel/runtime": "^7.5.5", @@ -4805,173 +1937,20 @@ "lodash": "^4.0.1" } }, - "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "dependencies": { - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true - } - } - }, - "read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "requires": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - } - }, "rechoir": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.0.tgz", - "integrity": "sha512-ADsDEH2bvbjltXEP+hTIAmeFekTFK0V2BTxMkok6qILyAJEXV0AFfoWcAq4yfll5VdIMd/RVXq0lR+wQi5ZU3Q==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", + "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", "dev": true, "requires": { "resolve": "^1.9.0" } }, - "redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, - "requires": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - } - }, "regenerator-runtime": { "version": "0.10.5", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=", - "dev": true - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } - }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true - }, - "repeat-element": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", - "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", - "dev": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true - }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true - }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true - } - } - }, - "request-promise-core": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", - "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", - "dev": true, - "requires": { - "lodash": "^4.17.19" - } - }, - "request-promise-native": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", - "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", - "dev": true, - "requires": { - "request-promise-core": "1.1.4", - "stealthy-require": "^1.1.1", - "tough-cookie": "^2.3.3" - }, - "dependencies": { - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - } - } + "dev": true }, "require-directory": { "version": "2.1.1", @@ -4985,12 +1964,6 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, - "resize-observer-polyfill": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", - "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", - "dev": true - }, "resolve": { "version": "1.20.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", @@ -5001,15 +1974,6 @@ "path-parse": "^1.0.6" } }, - "resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "requires": { - "resolve-from": "^5.0.0" - } - }, "resolve-dir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", @@ -5020,22 +1984,10 @@ "global-modules": "^1.0.0" } }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "resolve-pathname": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", + "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==", "dev": true }, "rimraf": { @@ -5047,12 +1999,6 @@ "glob": "^7.1.3" } }, - "rsvp": { - "version": "4.8.5", - "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", - "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", - "dev": true - }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -5065,228 +2011,28 @@ "integrity": "sha1-PnZyPjjf3aE8mx0poeB//uSzC1c=", "dev": true }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, - "requires": { - "ret": "~0.1.10" - } - }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, - "sane": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", - "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", - "dev": true, - "requires": { - "@cnakazawa/watch": "^1.0.3", - "anymatch": "^2.0.0", - "capture-exit": "^2.0.0", - "exec-sh": "^0.3.2", - "execa": "^1.0.0", - "fb-watchman": "^2.0.0", - "micromatch": "^3.1.4", - "minimist": "^1.1.1", - "walker": "~1.0.5" - }, - "dependencies": { - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - } - } - }, - "saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "scheduler": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", + "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", "dev": true, "requires": { - "xmlchars": "^2.2.0" + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" } }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, - "set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "shellwords": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", - "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", - "dev": true, - "optional": true - }, "side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -5294,364 +2040,60 @@ "dev": true, "requires": { "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "dev": true - }, - "simplebar": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/simplebar/-/simplebar-5.3.3.tgz", - "integrity": "sha512-OfuSX47Axq9aR6rp9WK3YefAg+1Qw3UKKxS46PdElPpd+FWXMj17/nispYxsHtU3F7mv+ilmqELWmRt7KUgHgg==", - "dev": true, - "requires": { - "can-use-dom": "^0.1.0", - "core-js": "^3.0.1", - "lodash.debounce": "^4.0.8", - "lodash.memoize": "^4.1.2", - "lodash.throttle": "^4.1.1", - "resize-observer-polyfill": "^1.5.1" - }, - "dependencies": { - "core-js": { - "version": "3.12.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.12.1.tgz", - "integrity": "sha512-Ne9DKPHTObRuB09Dru5AjwKjY4cJHVGu+y5f7coGn1E9Grkc3p2iBwE9AI/nJzsE29mQF7oq+mhYYRqOMFN1Bw==", - "dev": true - } - } - }, - "sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "slide": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", - "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=", - "dev": true - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "dev": true, - "requires": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, - "requires": { - "kind-of": "^3.2.0" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-resolve": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", - "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", - "dev": true, - "requires": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0" - } - }, - "source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "source-map-url": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", - "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", - "dev": true - }, - "spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.8.tgz", - "integrity": "sha512-NDgA96EnaLSvtbM7trJj+t1LUR3pirkDCcz9nOUlPb5DMBGsH7oES6C3hs3j7R9oHEa1EMvReS/BUAIT5Tcr0g==", - "dev": true - }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.0" - } - }, - "sprintf-js": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", - "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", - "dev": true - }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dev": true, - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - } + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" } }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "simplebar": { + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/simplebar/-/simplebar-5.3.6.tgz", + "integrity": "sha512-FJUMbV+hNDd/m+1/fvD41TXKd5mSdlI5zgBygkaQIV3SffNbcLhSbJT6ufTs8ZNRLJ6i+qc/KCFMqWmvlGWMhA==", "dev": true, "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" + "@juggle/resize-observer": "^3.3.1", + "can-use-dom": "^0.1.0", + "core-js": "^3.0.1", + "lodash.debounce": "^4.0.8", + "lodash.memoize": "^4.1.2", + "lodash.throttle": "^4.1.1" }, "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } + "core-js": { + "version": "3.19.2", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.19.2.tgz", + "integrity": "sha512-ciYCResnLIATSsXuXnIOH4CbdfgV+H1Ltg16hJFN7/v6OxqnFr/IFGeLacaZ+fHLAm0TBbXwNK9/DNBzBUrO/g==", + "dev": true } } }, - "stealthy-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", + "slide": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", + "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, - "string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "requires": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, + "sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "dev": true + }, "string-template": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz", @@ -5659,29 +2101,29 @@ "dev": true }, "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "dev": true, "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" }, "dependencies": { "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^4.1.0" } } } @@ -5701,33 +2143,6 @@ "ansi-regex": "^2.0.0" } }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - }, - "strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "requires": { - "min-indent": "^1.0.0" - } - }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -5737,47 +2152,10 @@ "has-flag": "^4.0.0" } }, - "supports-hyperlinks": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", - "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", - "dev": true, - "requires": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - } - }, - "symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true - }, - "terminal-link": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", - "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "supports-hyperlinks": "^2.0.0" - } - }, - "test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - } - }, - "throat": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", - "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", + "tiny-invariant": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz", + "integrity": "sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg==", "dev": true }, "tiny-lr": { @@ -5794,56 +2172,18 @@ "qs": "^6.4.0" } }, + "tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", + "dev": true + }, "tinycolor2": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.2.tgz", "integrity": "sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA==", "dev": true }, - "tmpl": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", - "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } - }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -5853,71 +2193,12 @@ "is-number": "^7.0.0" } }, - "tough-cookie": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", - "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", - "dev": true, - "requires": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.1.2" - } - }, "tr46": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.0.2.tgz", - "integrity": "sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg==", - "dev": true, - "requires": { - "punycode": "^2.1.1" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", "dev": true }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, - "requires": { - "is-typedarray": "^1.0.0" - } - }, "unc-path-regex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", @@ -5934,85 +2215,6 @@ "util-deprecate": "^1.0.2" } }, - "union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - } - }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true - } - } - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true - }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true - }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -6023,27 +2225,7 @@ "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "optional": true - }, - "v8-to-istanbul": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.1.2.tgz", - "integrity": "sha512-TxNb7YEUwkLXCQYeudi6lgQ/SZrzNO4kMdlqVxaZPUIUjCv6iSSypUQX70kNBSERpQ8fk48+d61FXk+tgqcWow==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0", - "source-map": "^0.7.3" - }, - "dependencies": { - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true - } - } + "dev": true }, "v8flags": { "version": "3.2.0", @@ -6054,26 +2236,11 @@ "homedir-polyfill": "^1.0.1" } }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } + "value-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", + "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==", + "dev": true }, "void-elements": { "version": "3.1.0", @@ -6081,33 +2248,6 @@ "integrity": "sha1-YU9/v42AHwu18GYfWy9XhXUOTwk=", "dev": true }, - "w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", - "dev": true, - "requires": { - "browser-process-hrtime": "^1.0.0" - } - }, - "w3c-xmlserializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", - "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", - "dev": true, - "requires": { - "xml-name-validator": "^3.0.0" - } - }, - "walker": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", - "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", - "dev": true, - "requires": { - "makeerror": "1.0.x" - } - }, "webextension-polyfill": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/webextension-polyfill/-/webextension-polyfill-0.6.0.tgz", @@ -6115,9 +2255,9 @@ "dev": true }, "webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", "dev": true }, "websocket-driver": { @@ -6137,30 +2277,14 @@ "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", "dev": true }, - "whatwg-encoding": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", - "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", - "dev": true, - "requires": { - "iconv-lite": "0.4.24" - } - }, - "whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", - "dev": true - }, "whatwg-url": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.5.0.tgz", - "integrity": "sha512-fy+R77xWv0AiqfLl4nuGUlQ3/6b5uNfQ4WAbGQVMYshCTCCPK9psC1nWh3XHuxGVCtlcDDQPQW1csmmIQo+fwg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", "dev": true, "requires": { - "lodash": "^4.7.0", - "tr46": "^2.0.2", - "webidl-conversions": "^6.1.0" + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" } }, "which": { @@ -6178,36 +2302,54 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", "dev": true, "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" }, "dependencies": { "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^4.1.0" } } } @@ -6229,22 +2371,10 @@ "slide": "^1.1.5" } }, - "ws": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.5.tgz", - "integrity": "sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g==", - "dev": true - }, - "xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", - "dev": true - }, - "xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "xmlhttprequest": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", + "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=", "dev": true }, "xregexp": { @@ -6262,35 +2392,28 @@ "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", "dev": true }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", "dev": true, "requires": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", + "cliui": "^5.0.0", + "find-up": "^3.0.0", "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", - "string-width": "^4.2.0", + "string-width": "^3.0.0", "which-module": "^2.0.0", "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" + "yargs-parser": "^13.1.2" } }, "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", "dev": true, "requires": { "camelcase": "^5.0.0", diff --git a/package.json b/package.json index 4be12e6aa4..46c46ab40c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "passbolt_api", - "version": "3.0.0", + "version": "3.4.0", "description": "The backend application for the open source password manager for teams", "repository": { "url": "https://github.com/passbolt/passbolt_api" @@ -19,6 +19,6 @@ "grunt-contrib-watch": "^1.1.0", "jquery": "^3.5.1", "openpgp": "4.6.2", - "passbolt-styleguide": "^3.2.1" + "passbolt-styleguide": "3.4.0" } } diff --git a/phpstan.neon b/phpstan.neon index 39fb5269bd..75a32ee8fd 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,7 +1,8 @@ parameters: - level: 4 + level: 6 checkMissingIterableValueType: false treatPhpDocTypesAsCertain: false + checkGenericClassInNonGenericObjectType: false paths: - src - plugins diff --git a/phpunit.xml.dist b/phpunit.xml.dist index e50424641e..aae0e6b948 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,17 +1,5 @@ - - - ./src/ - ./plugins/AccountSettings/src/ - ./plugins/WebInstaller/src/ - ./plugins/Log/src/ - ./plugins/EmailNotificationSettings/src/ - ./plugins/EmailDigest/src/ - ./plugins/Reports/src/ - ./plugins/Locale/src/ - - @@ -23,12 +11,8 @@ ./tests/TestCase/Command - ./plugins/Passbolt/AccountSettings/tests/TestCase - ./plugins/Passbolt/Log/tests/TestCase - ./plugins/Passbolt/EmailNotificationSettings/tests/TestCase - ./plugins/Passbolt/EmailDigest/tests/TestCase - ./plugins/Passbolt/Reports/tests/TestCase - ./plugins/Passbolt/Locale/tests/TestCase + ./plugins + ./plugins/Passbolt/WebInstaller ./plugins/Passbolt/WebInstaller/tests/TestCase diff --git a/plugins/Passbolt/AccountSettings/src/Model/Table/AccountSettingsTable.php b/plugins/Passbolt/AccountSettings/src/Model/Table/AccountSettingsTable.php index b544d13146..9396596006 100644 --- a/plugins/Passbolt/AccountSettings/src/Model/Table/AccountSettingsTable.php +++ b/plugins/Passbolt/AccountSettings/src/Model/Table/AccountSettingsTable.php @@ -18,7 +18,6 @@ namespace Passbolt\AccountSettings\Model\Table; use App\Error\Exception\ValidationException; -use App\Model\Table\UsersTable; use App\Utility\UuidFactory; use Cake\Http\Exception\BadRequestException; use Cake\Http\Exception\InternalErrorException; @@ -72,7 +71,6 @@ public function initialize(array $config): void $this->belongsTo('Users', [ 'foreignKey' => 'user_id', 'joinType' => 'INNER', - 'className' => UsersTable::class, ]); } @@ -164,18 +162,21 @@ public function findIndex(string $userId, array $whitelist) * * @param string $userId uuid * @param string $property The name of the property to get - * @return \Cake\Datasource\EntityInterface|array The first result from the ResultSet. + * @return \Passbolt\AccountSettings\Model\Entity\AccountSetting The first result from the ResultSet. * @throws \Cake\Datasource\Exception\RecordNotFoundException When there is no first record. */ - public function getFirstPropertyOrFail(string $userId, string $property) + public function getFirstPropertyOrFail(string $userId, string $property): AccountSetting { if (!Validation::uuid($userId)) { throw new BadRequestException(__('The user identifier should be a valid UUID.')); } - return $this->find('byProperty', compact('property')) + /** @var \Passbolt\AccountSettings\Model\Entity\AccountSetting $entity */ + $entity = $this->find('byProperty', compact('property')) ->where([$this->aliasField('user_id') => $userId]) ->firstOrFail(); + + return $entity; } /** @@ -242,7 +243,7 @@ public function deleteByProperty(string $userId, string $property) * * @param string $userId user uuid * @param string $property user property - * @return \Cake\Datasource\EntityInterface|null + * @return \Cake\Datasource\EntityInterface|array|null */ public function getByProperty(string $userId, string $property) { diff --git a/plugins/Passbolt/AccountSettings/tests/Factory/AccountSettingFactory.php b/plugins/Passbolt/AccountSettings/tests/Factory/AccountSettingFactory.php index 6b55ad445d..93119a2e8a 100644 --- a/plugins/Passbolt/AccountSettings/tests/Factory/AccountSettingFactory.php +++ b/plugins/Passbolt/AccountSettings/tests/Factory/AccountSettingFactory.php @@ -9,7 +9,7 @@ use Faker\Generator; /** - * ProfileFactory + * AccountSettingFactory */ class AccountSettingFactory extends CakephpBaseFactory { diff --git a/plugins/Passbolt/AccountSettings/tests/TestCase/Controller/AccountSettings/AccountSettingsIndexControllerTest.php b/plugins/Passbolt/AccountSettings/tests/TestCase/Controller/AccountSettings/AccountSettingsIndexControllerTest.php index 12614e6fe4..f5c30a1f4d 100644 --- a/plugins/Passbolt/AccountSettings/tests/TestCase/Controller/AccountSettings/AccountSettingsIndexControllerTest.php +++ b/plugins/Passbolt/AccountSettings/tests/TestCase/Controller/AccountSettings/AccountSettingsIndexControllerTest.php @@ -19,22 +19,10 @@ use App\Test\Factory\UserFactory; use App\Test\Lib\AppIntegrationTestCase; -use Cake\ORM\TableRegistry; use Passbolt\AccountSettings\Test\Factory\AccountSettingFactory; class AccountSettingsIndexControllerTest extends AppIntegrationTestCase { - /** - * @var AccountSettingsTable - */ - public $AccountSettings; - - public function setUp(): void - { - parent::setUp(); - $this->AccountSettings = TableRegistry::getTableLocator()->get('Passbolt/AccountSettings.AccountSettings'); - } - /** * @Given I have a theme, a locale and a dummy property set in my settings * @When I get '/account/settings.json' diff --git a/plugins/Passbolt/AccountSettings/tests/TestCase/Controller/Themes/ThemesIndexControllerTest.php b/plugins/Passbolt/AccountSettings/tests/TestCase/Controller/Themes/ThemesIndexControllerTest.php index a13dabde47..973b1036aa 100644 --- a/plugins/Passbolt/AccountSettings/tests/TestCase/Controller/Themes/ThemesIndexControllerTest.php +++ b/plugins/Passbolt/AccountSettings/tests/TestCase/Controller/Themes/ThemesIndexControllerTest.php @@ -18,14 +18,15 @@ namespace Passbolt\AccountSettings\Test\TestCase\Controller\Themes; use App\Test\Lib\AppIntegrationTestCase; -use Cake\ORM\TableRegistry; +use Cake\Datasource\ModelAwareTrait; /** * @uses \Passbolt\AccountSettings\Controller\Themes\ThemesIndexController + * @property \Passbolt\AccountSettings\Model\Table\AccountSettingsTable $AccountSettings */ class ThemesIndexControllerTest extends AppIntegrationTestCase { - public $AccountSettings; + use ModelAwareTrait; public $fixtures = [ 'plugin.Passbolt/AccountSettings.AccountSettings', @@ -34,7 +35,7 @@ class ThemesIndexControllerTest extends AppIntegrationTestCase public function setUp(): void { parent::setUp(); - $this->AccountSettings = TableRegistry::getTableLocator()->get('AccountSettings'); + $this->loadModel('AccountSettings'); } public function testThemesIndexSuccess() diff --git a/plugins/Passbolt/AccountSettings/tests/TestCase/Controller/Themes/ThemesSelectControllerTest.php b/plugins/Passbolt/AccountSettings/tests/TestCase/Controller/Themes/ThemesSelectControllerTest.php index f5da90c472..c388f9dd39 100644 --- a/plugins/Passbolt/AccountSettings/tests/TestCase/Controller/Themes/ThemesSelectControllerTest.php +++ b/plugins/Passbolt/AccountSettings/tests/TestCase/Controller/Themes/ThemesSelectControllerTest.php @@ -19,14 +19,15 @@ use App\Test\Lib\AppIntegrationTestCase; use App\Utility\UuidFactory; -use Cake\ORM\TableRegistry; +use Cake\Datasource\ModelAwareTrait; /** * @uses \Passbolt\AccountSettings\Controller\Themes\ThemesSelectController + * @property \Passbolt\AccountSettings\Model\Table\AccountSettingsTable $AccountSettings */ class ThemesSelectControllerTest extends AppIntegrationTestCase { - public $AccountSettings; + use ModelAwareTrait; public $fixtures = [ 'app.Base/Users', 'app.Base/Roles', 'app.Base/Profiles', @@ -36,7 +37,7 @@ class ThemesSelectControllerTest extends AppIntegrationTestCase public function setUp(): void { parent::setUp(); - $this->AccountSettings = TableRegistry::getTableLocator()->get('AccountSettings'); + $this->loadModel('AccountSettings'); } public function testThemesSelectSuccess() diff --git a/plugins/Passbolt/AccountSettings/tests/TestCase/Model/Table/AccountSettingsTableTest.php b/plugins/Passbolt/AccountSettings/tests/TestCase/Model/Table/AccountSettingsTableTest.php index 7b79d87bfa..c13381e3b0 100644 --- a/plugins/Passbolt/AccountSettings/tests/TestCase/Model/Table/AccountSettingsTableTest.php +++ b/plugins/Passbolt/AccountSettings/tests/TestCase/Model/Table/AccountSettingsTableTest.php @@ -17,23 +17,20 @@ namespace Passbolt\AccountSettings\Test\TestCase\Model\Table; -use Cake\Http\Exception\InternalErrorException; -use Cake\ORM\TableRegistry; +use BadMethodCallException; +use Cake\Datasource\ModelAwareTrait; use Passbolt\AccountSettings\Model\Entity\AccountSetting; use Passbolt\AccountSettings\Test\Factory\AccountSettingFactory; use Passbolt\AccountSettings\Test\Lib\AccountSettingsPluginTestCase; /** * Passbolt\AccountSettings\Model\Table\AccountSettingsTable Test Case + * + * @property \Passbolt\AccountSettings\Model\Table\AccountSettingsTable $AccountSettings */ class AccountSettingsTableTest extends AccountSettingsPluginTestCase { - /** - * Test subject - * - * @var \Passbolt\AccountSettings\Model\Table\AccountSettingsTable - */ - public $AccountSettings; + use ModelAwareTrait; /** * setUp method @@ -43,7 +40,7 @@ class AccountSettingsTableTest extends AccountSettingsPluginTestCase public function setUp(): void { parent::setUp(); - $this->AccountSettings = TableRegistry::getTableLocator()->get('Passbolt/AccountSettings.AccountSettings'); + $this->loadModel('AccountSettings'); } /** @@ -64,15 +61,17 @@ public function testFindByProperty(): void $value = 'Bar'; // Expect exception if no property is passed - $this->expectException(InternalErrorException::class); + $this->expectException(BadMethodCallException::class); $this->AccountSettings->find('byProperty'); // Return null if the property does not exist + /** @var mixed|null $result */ $result = $this->AccountSettings->find('byProperty', compact('property')); $this->assertSame(null, $result); // Now all good with an existing acount setting AccountSettingFactory::make()->setPropertyValue($property, $value)->persist(); + /** @var mixed|null $result */ $result = $this->AccountSettings->find('byProperty', compact('property')); $this->assertInstanceOf(AccountSetting::class, $result); $this->assertSame($property, $result->get('property')); diff --git a/plugins/Passbolt/EmailDigest/config/config.php b/plugins/Passbolt/EmailDigest/config/config.php index 47204ada42..a60ff2bf24 100644 --- a/plugins/Passbolt/EmailDigest/config/config.php +++ b/plugins/Passbolt/EmailDigest/config/config.php @@ -19,7 +19,7 @@ 'version' => '1.0.0', 'enabled' => env('PASSBOLT_PLUGINS_EMAIL_DIGEST_ENABLED', true), 'batchSizeLimit' => filter_var( - env('PASSBOLT_PLUGINS_EMAIL_DIGEST_BATCH_SIZE_LIMIT', 100), + env('PASSBOLT_PLUGINS_EMAIL_DIGEST_BATCH_SIZE_LIMIT', '100'), FILTER_VALIDATE_INT ), ], diff --git a/plugins/Passbolt/EmailDigest/src/Command/SenderCommand.php b/plugins/Passbolt/EmailDigest/src/Command/SenderCommand.php index 556e7d3554..d5b02b7fd1 100644 --- a/plugins/Passbolt/EmailDigest/src/Command/SenderCommand.php +++ b/plugins/Passbolt/EmailDigest/src/Command/SenderCommand.php @@ -49,7 +49,7 @@ public function execute(Arguments $args, ConsoleIo $io): ?int { $emailSenderService = new SendEmailBatchService(); - $emailSenderService->sendNextEmailsBatch($args->getOption('limit')); + $emailSenderService->sendNextEmailsBatch((int)$args->getOption('limit')); return self::CODE_SUCCESS; } diff --git a/plugins/Passbolt/EmailDigest/src/Controller/EmailDigest/PreviewNextEmailsBatchController.php b/plugins/Passbolt/EmailDigest/src/Controller/EmailDigest/PreviewNextEmailsBatchController.php index 90524236cb..6a30f26802 100644 --- a/plugins/Passbolt/EmailDigest/src/Controller/EmailDigest/PreviewNextEmailsBatchController.php +++ b/plugins/Passbolt/EmailDigest/src/Controller/EmailDigest/PreviewNextEmailsBatchController.php @@ -24,8 +24,7 @@ class PreviewNextEmailsBatchController extends AppController { /** - * @param \Cake\Event\Event $event Event to use - * @return \Cake\Http\Response|null + * @inheritDoc */ public function beforeFilter(\Cake\Event\EventInterface $event) { diff --git a/plugins/Passbolt/EmailDigest/src/Plugin.php b/plugins/Passbolt/EmailDigest/src/Plugin.php new file mode 100644 index 0000000000..6bdf0be13b --- /dev/null +++ b/plugins/Passbolt/EmailDigest/src/Plugin.php @@ -0,0 +1,61 @@ +setEmailTemplateVariablesSerializationType(); + } + + /** + * Emails are stored serialized in Json format. + * Decoding is made with associative array. + * + * @see EmailQueueTable::_initializeSchema() + * @return void + */ + public function setEmailTemplateVariablesSerializationType(): void + { + // Set the EmailQueue Serialization type to json. + Configure::write('EmailQueue.serialization_type', 'email_queue.json'); + } + + /** + * @inheritDoc + */ + public function console(CommandCollection $commands): CommandCollection + { + // Alias commands + $commands->add('passbolt email_digest preview', PreviewCommand::class); + $commands->add('passbolt email_digest send', SenderCommand::class); + + return $commands; + } +} diff --git a/plugins/Passbolt/EmailDigest/src/Service/ConvertEmailVariablesToJsonService.php b/plugins/Passbolt/EmailDigest/src/Service/ConvertEmailVariablesToJsonService.php new file mode 100644 index 0000000000..c33a692f4f --- /dev/null +++ b/plugins/Passbolt/EmailDigest/src/Service/ConvertEmailVariablesToJsonService.php @@ -0,0 +1,123 @@ +findUnsentEmails(); + if ($emails === false) { + return; + } + + foreach ($emails as $email) { + $this->convertEmail($email); + } + } + + /** + * Find unsent emails in the queue. + * We do not use the table's find method that would parse data to JSON. + * + * @return array|false + */ + public function findUnsentEmails() + { + $EmailQueueTable = TableRegistry::getTableLocator()->get('EmailQueue.EmailQueue'); + + return $EmailQueueTable->getConnection() + ->newQuery() + ->select(['id', 'template_vars']) + ->from($EmailQueueTable->getTable()) + ->where(['sent' => 0]) + ->execute() + ->fetchAll('assoc'); + } + + /** + * Convert variables of this email from serialize to json. + * + * @param array $email Email + */ + protected function convertEmail(array $email): void + { + $templateVars = $email['template_vars']; + + $toArray = $this->toArray($templateVars ?? ''); + if (empty($toArray)) { + return; + } + + $EmailQueueTable = TableRegistry::getTableLocator()->get('EmailQueue.EmailQueue'); + $email = $EmailQueueTable->get($email['id']); + $email = $EmailQueueTable->patchEntity($email, ['template_vars' => $toArray]); + $EmailQueueTable->save($email); + } + + /** + * Convert serialized string v3.3.0 encoded + * into an array of arrays. + * + * @param string $vars String to convert + * @return array + */ + public function toArray(string $vars): array + { + try { + $vars = @unserialize($vars); + } catch (\Throwable $exception) { + $vars = []; + } + + if (empty($vars) || !is_array($vars)) { + return []; + } + + foreach ($vars as $var => $value) { + if ($value instanceof EntityInterface) { + $vars[$var] = $value->toArray(); + } + if (is_array($value)) { + foreach ($value as $k => $v) { + if ($v instanceof EntityInterface) { + $vars[$var][$k] = $v->toArray(); + } + } + } + } + + return $vars; + } +} diff --git a/plugins/Passbolt/EmailDigest/src/Service/PreviewEmailBatchService.php b/plugins/Passbolt/EmailDigest/src/Service/PreviewEmailBatchService.php index e09d4b565c..a356c06a43 100644 --- a/plugins/Passbolt/EmailDigest/src/Service/PreviewEmailBatchService.php +++ b/plugins/Passbolt/EmailDigest/src/Service/PreviewEmailBatchService.php @@ -66,7 +66,7 @@ public function __construct( * Get and send the next emails batch from the email queue. The size of the email batch is determined by $limit. * * @param int $limit Size of the emails batch. - * @return array + * @return \Passbolt\EmailDigest\Utility\Mailer\EmailPreview[] * @throws \Exception */ public function previewNextEmailsBatch(int $limit = 10): array diff --git a/plugins/Passbolt/EmailDigest/src/Utility/Digest/Digest.php b/plugins/Passbolt/EmailDigest/src/Utility/Digest/Digest.php index b1418a809c..31ae9c0e7b 100644 --- a/plugins/Passbolt/EmailDigest/src/Utility/Digest/Digest.php +++ b/plugins/Passbolt/EmailDigest/src/Utility/Digest/Digest.php @@ -17,6 +17,7 @@ namespace Passbolt\EmailDigest\Utility\Digest; use Cake\ORM\Entity; +use Cake\Validation\Validation; use Passbolt\EmailDigest\Exception\UnsupportedEmailDigestDataException; use Passbolt\EmailDigest\Utility\Factory\EmailPreviewFactory; use Passbolt\EmailDigest\Utility\Mailer\EmailDigest; @@ -114,7 +115,11 @@ public function addEmailEntity(Entity $emailQueueEntity) } $operator = $this->getOperatorFromEmail($emailQueueEntity); - $operator = $operator->username; + if (!isset($operator['username']) || !Validation::email($operator['username'])) { + throw new UnsupportedEmailDigestDataException($emailQueueEntity); + } + + $operator = $operator['username']; $username = $emailQueueEntity['email']; $this->addToUserCount($username, $operator); @@ -154,6 +159,7 @@ public function marshalEmails(): array $digest->setContent($this->renderDigestContentFromEmailPreview($this->emailPreviewFactory, $digest)); } + /** @var \Passbolt\EmailDigest\Utility\Mailer\EmailDigestInterface[] $result */ return $result; } @@ -166,7 +172,11 @@ public function buildMultipleEmailDigest(array $emails) $digest = new EmailDigest(); foreach ($emails as $i => $emailQueueEntity) { $operator = $this->getOperatorFromEmail($emailQueueEntity); - $firstName = $operator->profile->first_name; + if (!isset($operator['profile']['first_name']) || !is_string($operator['profile']['first_name'])) { + throw new UnsupportedEmailDigestDataException($emailQueueEntity); + } + + $firstName = $operator['profile']['first_name']; $digest->addEmailData($emailQueueEntity) ->setSubject(__($this->subject, $firstName)) ->setEmailRecipient($emailQueueEntity->email); diff --git a/plugins/Passbolt/EmailDigest/src/Utility/Digest/DigestRegisterEvent.php b/plugins/Passbolt/EmailDigest/src/Utility/Digest/DigestRegisterEvent.php index 7c1f28149a..c68b60f4ac 100644 --- a/plugins/Passbolt/EmailDigest/src/Utility/Digest/DigestRegisterEvent.php +++ b/plugins/Passbolt/EmailDigest/src/Utility/Digest/DigestRegisterEvent.php @@ -32,23 +32,22 @@ class DigestRegisterEvent extends Event /** * @param string $name Name of the event - * @param null $subject Subject of the dispatched event - * @param null $data Data for the event + * @param \Passbolt\EmailDigest\Utility\Digest\DigestsPool $subject Subject of the dispatched event */ - final public function __construct($name, $subject = null, $data = null) + final public function __construct(string $name, $subject) { if (!$subject instanceof DigestsPool) { throw new InvalidArgumentException('`subject` must be an instance of ' . DigestsPool::class); } - parent::__construct($name, $subject, $data); + parent::__construct($name, $subject); } /** * @param \Passbolt\EmailDigest\Utility\Digest\DigestsPool $digestsPool Digests Pool - * @return \Passbolt\EmailDigest\Utility\Digest\DigestRegisterEvent + * @return self */ - public static function create(DigestsPool $digestsPool) + public static function create(DigestsPool $digestsPool): self { return new static(static::EVENT_NAME, $digestsPool); } @@ -56,7 +55,7 @@ public static function create(DigestsPool $digestsPool) /** * @return \Passbolt\EmailDigest\Utility\Digest\DigestsPool */ - public function getEmailDigestsPool() + public function getEmailDigestsPool(): DigestsPool { return $this->getSubject(); } diff --git a/plugins/Passbolt/EmailDigest/src/Utility/Digest/DigestsPool.php b/plugins/Passbolt/EmailDigest/src/Utility/Digest/DigestsPool.php index e4bb42c7a2..82c0236ede 100644 --- a/plugins/Passbolt/EmailDigest/src/Utility/Digest/DigestsPool.php +++ b/plugins/Passbolt/EmailDigest/src/Utility/Digest/DigestsPool.php @@ -31,9 +31,9 @@ class DigestsPool public const LOWEST_PRIORITY = -1; /** - * @var static + * @var static|null */ - private static $instance; + private static $instance = null; /** * @var array @@ -61,6 +61,16 @@ public static function getInstance() return static::$instance; } + /** + * Clear the singleton of the DigestsPool + * + * @return void + */ + public static function clearInstance(): void + { + static::$instance = null; + } + /** * Add a digest instance to the pool of digests with an optional priority. * diff --git a/plugins/Passbolt/EmailDigest/src/Utility/Factory/DigestFactory.php b/plugins/Passbolt/EmailDigest/src/Utility/Factory/DigestFactory.php index 5f7690a50c..fcd8041725 100644 --- a/plugins/Passbolt/EmailDigest/src/Utility/Factory/DigestFactory.php +++ b/plugins/Passbolt/EmailDigest/src/Utility/Factory/DigestFactory.php @@ -32,9 +32,9 @@ class DigestFactory { /** - * @var static + * @var static|null */ - private static $instance; + private static $instance = null; /** * @var \Passbolt\EmailDigest\Utility\Digest\DigestsPool @@ -75,7 +75,7 @@ public static function getInstance() * * @return \Passbolt\EmailDigest\Utility\Digest\SingleDigest */ - public function createSingleDigest() + public function createSingleDigest(): SingleDigest { return new SingleDigest(); } @@ -85,7 +85,7 @@ public function createSingleDigest() * * @return \Passbolt\EmailDigest\Utility\Digest\DigestsCollection */ - public function createDigestsCollection() + public function createDigestsCollection(): DigestsCollection { if (!$this->isDigestRegisterEventDispatched) { // Dispatch an event to offer possibility to other components to register more digests. diff --git a/plugins/Passbolt/EmailDigest/src/Utility/Factory/EmailPreviewFactory.php b/plugins/Passbolt/EmailDigest/src/Utility/Factory/EmailPreviewFactory.php index 39cb0e6408..c982ed976a 100644 --- a/plugins/Passbolt/EmailDigest/src/Utility/Factory/EmailPreviewFactory.php +++ b/plugins/Passbolt/EmailDigest/src/Utility/Factory/EmailPreviewFactory.php @@ -84,7 +84,7 @@ public function renderEmailPreviewFromEmailEntity(Entity $emailData, ?string $la * @see Email::send() * @see DebugTransport::send() */ - private function renderEmailContent(Mailer $email) + private function renderEmailContent(Mailer $email): EmailPreview { $email->setTransport('Debug'); @@ -143,7 +143,6 @@ private function mapEmailEntityToMailerEmail(Mailer $email, Entity $emailData) { $headers = empty($emailData->headers) ? [] : (array)$emailData->headers; $viewVars = empty($emailData->template_vars) ? [] : $emailData->template_vars; - $emailDataErrorMessage = null; if (!empty($emailData->from_email) && !empty($emailData->from_name)) { $email->setFrom($emailData->from_email, $emailData->from_name); diff --git a/plugins/Passbolt/EmailDigest/src/Utility/Mailer/EmailDigest.php b/plugins/Passbolt/EmailDigest/src/Utility/Mailer/EmailDigest.php index 72d9105751..2df7f90604 100644 --- a/plugins/Passbolt/EmailDigest/src/Utility/Mailer/EmailDigest.php +++ b/plugins/Passbolt/EmailDigest/src/Utility/Mailer/EmailDigest.php @@ -58,9 +58,9 @@ class EmailDigest implements EmailDigestInterface private $subject; /** - * @var string Template to use to compose the email + * @var string|null Template to use to compose the email */ - private $template; + private $template = null; /** * @var array @@ -149,7 +149,7 @@ public function getContent() * Return the email recipient * * @param string $recipient Email Recipient of the digest, i.e: ada@passbolt.com - * @return \Passbolt\EmailDigest\Utility\Mailer\EmailDigest + * @return $this */ public function setEmailRecipient(string $recipient) { @@ -170,7 +170,7 @@ public function getEmailRecipient() /** * @param string $subject Subject of the digest - * @return \Passbolt\EmailDigest\Utility\Mailer\EmailDigest + * @return $this */ public function setSubject(string $subject) { diff --git a/plugins/Passbolt/EmailDigest/src/Utility/Mailer/EmailDigestInterface.php b/plugins/Passbolt/EmailDigest/src/Utility/Mailer/EmailDigestInterface.php index 204de85cfc..d5a992cdc4 100644 --- a/plugins/Passbolt/EmailDigest/src/Utility/Mailer/EmailDigestInterface.php +++ b/plugins/Passbolt/EmailDigest/src/Utility/Mailer/EmailDigestInterface.php @@ -59,4 +59,10 @@ public function getEmailIds(); * @return $this */ public function setContent(string $digestContent); + + /** + * @param string $template Template + * @return $this + */ + public function setTemplate(string $template); } diff --git a/plugins/Passbolt/EmailDigest/src/Utility/Mailer/EmailPreview.php b/plugins/Passbolt/EmailDigest/src/Utility/Mailer/EmailPreview.php index 48b425027a..b7c79830e8 100644 --- a/plugins/Passbolt/EmailDigest/src/Utility/Mailer/EmailPreview.php +++ b/plugins/Passbolt/EmailDigest/src/Utility/Mailer/EmailPreview.php @@ -49,7 +49,7 @@ public function __construct(string $headers, string $content) * * @return string */ - public function getContent() + public function getContent(): string { return $this->content; } @@ -59,7 +59,7 @@ public function getContent() * * @return string */ - public function getHeaders() + public function getHeaders(): string { return $this->headers; } diff --git a/plugins/Passbolt/EmailDigest/tests/Factory/EmailQueueFactory.php b/plugins/Passbolt/EmailDigest/tests/Factory/EmailQueueFactory.php index 46b9f9153d..a107f68832 100644 --- a/plugins/Passbolt/EmailDigest/tests/Factory/EmailQueueFactory.php +++ b/plugins/Passbolt/EmailDigest/tests/Factory/EmailQueueFactory.php @@ -10,6 +10,9 @@ /** * EmailQueueFactory + * + * @method \Cake\Datasource\EntityInterface persist() + * @method \Cake\Datasource\EntityInterface getEntity() */ class EmailQueueFactory extends CakephpBaseFactory { @@ -32,20 +35,21 @@ protected function getRootTableRegistryName(): string protected function setDefaultTemplate(): void { $this->configureEmailTemplateFixturePath(); + $this->configureSerializationType(); $this->setDefaultData(function (Generator $faker) { - $email = $faker->email; - $from_email = $faker->email; + $email = $faker->email(); + $from_email = $faker->email(); return [ 'email' => $email, - 'subject' => $faker->sentence, - 'from_name' => $faker->name, + 'subject' => $faker->sentence(), + 'from_name' => $faker->name(), 'from_email' => $from_email, 'config' => 'default', 'template' => 'test_email', 'layout' => 'default', - 'template_vars' => serialize(compact('email', 'from_email')), + 'template_vars' => json_encode(compact('email', 'from_email')), 'theme' => '', 'format' => 'html', 'sent' => 0, @@ -57,13 +61,26 @@ protected function setDefaultTemplate(): void }); } - public function configureEmailTemplateFixturePath() + /** + * Sets the path to test the email + */ + public function configureEmailTemplateFixturePath(): void { $templatePaths = Configure::readOrFail('App.paths.templates'); array_push($templatePaths, $this->getEmailDigestTestFixturePath()); Configure::write('App.paths.templates', $templatePaths); } + /** + * Sets the serialization type to json by default + */ + public function configureSerializationType() + { + if (!Configure::check('EmailQueue.serialization_type')) { + Configure::write('EmailQueue.serialization_type', 'email_queue.json'); + } + } + public function getEmailDigestTestFixturePath(): string { return str_replace('/', DS, PLUGINS . 'Passbolt/EmailDigest/tests/Fixture/templates/'); @@ -97,4 +114,14 @@ public function listeningToBeforeSave() { return $this->listeningToModelEvents('Model.beforeSave'); } + + /** + * Mark the email as sent. + * + * @return $this + */ + public function sent() + { + return $this->setField('sent', true); + } } diff --git a/plugins/Passbolt/EmailDigest/tests/Fixture/templates/email/html/test_email.php b/plugins/Passbolt/EmailDigest/tests/Fixture/templates/email/html/test_email.php index 3869c4b336..3f4a6ef920 100644 --- a/plugins/Passbolt/EmailDigest/tests/Fixture/templates/email/html/test_email.php +++ b/plugins/Passbolt/EmailDigest/tests/Fixture/templates/email/html/test_email.php @@ -1,3 +1,23 @@ +persist(); +} + +?> +element('Email/module/avatar',[ + 'url' => AvatarHelper::getAvatarUrl($user['profile']['avatar']), + 'text' => $this->element('Email/module/avatar_text', [ + 'user' => $user, + 'datetime' => $user['created'], + 'text' => __('This is an email used by tests only!') + ]) +]); ?> +

diff --git a/plugins/Passbolt/EmailDigest/tests/Lib/EmailDigestMockTestTrait.php b/plugins/Passbolt/EmailDigest/tests/Lib/EmailDigestMockTestTrait.php index d4b9dd309a..81c9f44483 100644 --- a/plugins/Passbolt/EmailDigest/tests/Lib/EmailDigestMockTestTrait.php +++ b/plugins/Passbolt/EmailDigest/tests/Lib/EmailDigestMockTestTrait.php @@ -20,6 +20,7 @@ use App\Model\Entity\User; use Cake\ORM\Entity; use Passbolt\EmailDigest\Utility\Digest\AbstractDigest; +use Passbolt\EmailDigest\Utility\Digest\DigestInterface; use Passbolt\EmailDigest\Utility\Mailer\EmailDigest; trait EmailDigestMockTestTrait @@ -29,9 +30,9 @@ trait EmailDigestMockTestTrait * * @param bool $canAddToDigests Returned by canAddToDigests method of the digest. * @param array $digests Some email digests that the digest must return - * @return DigestInterface + * @return \Passbolt\EmailDigest\Utility\Digest\DigestInterface */ - public function createDigest(bool $canAddToDigests, array $digests) + public function createDigest(bool $canAddToDigests, array $digests): DigestInterface { return new class ($canAddToDigests, $digests) extends AbstractDigest { @@ -88,7 +89,7 @@ protected function createEmailQueueEntity(?array $properties = [], ?array $templ * @param array|null $properties props * @return EmailDigest */ - protected function createEmailDigest(array $emailsData = [], ?array $properties = []): EmailDigest + protected function createEmailDigest(?array $emailsData = [], ?array $properties = []): EmailDigest { $defaultProperties = [ 'recipient' => 'digest_recipient@passbolt.com', diff --git a/plugins/Passbolt/EmailDigest/tests/TestCase/Command/EmailQueueSenderCommandTest.php b/plugins/Passbolt/EmailDigest/tests/TestCase/Command/EmailQueueSenderCommandTest.php new file mode 100644 index 0000000000..e608c8e50b --- /dev/null +++ b/plugins/Passbolt/EmailDigest/tests/TestCase/Command/EmailQueueSenderCommandTest.php @@ -0,0 +1,50 @@ +useCommandRunner(); + } + + /** + * Basic help test + */ + public function testSenderCommandHelp() + { + $this->exec('sender -h'); + $this->assertExitSuccess(); + $this->assertOutputContains('Sends queued emails in a batch'); + $this->assertOutputContains('cake sender [subcommand] [options]'); + } +} diff --git a/plugins/Passbolt/EmailDigest/tests/TestCase/Command/PreviewCommandTest.php b/plugins/Passbolt/EmailDigest/tests/TestCase/Command/PreviewCommandTest.php index 88f864d466..ec7b06204b 100644 --- a/plugins/Passbolt/EmailDigest/tests/TestCase/Command/PreviewCommandTest.php +++ b/plugins/Passbolt/EmailDigest/tests/TestCase/Command/PreviewCommandTest.php @@ -17,6 +17,8 @@ namespace Passbolt\EmailDigest\Test\TestCase\Command; use App\Test\Factory\UserFactory; +use App\View\Helper\AvatarHelper; +use Cake\Core\Configure; use Cake\I18n\I18n; use Cake\TestSuite\ConsoleIntegrationTestTrait; use Cake\TestSuite\EmailTrait; @@ -43,6 +45,7 @@ public function setUp(): void parent::setUp(); $this->useCommandRunner(); $this->setDummyFrenchTranslator(); + $this->loadPlugins(['Passbolt/EmailDigest']); } /** @@ -50,22 +53,30 @@ public function setUp(): void */ public function testPreviewCommandHelp(): void { - $this->exec('passbolt preview -h'); + $this->exec('passbolt email_digest preview -h'); $this->assertExitSuccess(); $this->assertOutputContains('Preview a batch of queued emails as emails digests.'); - $this->assertOutputContains('cake passbolt preview'); + $this->assertOutputContains('cake passbolt email_digest preview'); } /** * Basic Preview test. + * + * @covers \App\Service\Avatars\AvatarsConfigurationService::loadConfiguration */ public function testPreviewCommandPreview(): void { + // Ensure that avatar image configs are null and + // will be correctly loaded by the command. + Configure::delete('FileStorage'); + + /** @var \Cake\Datasource\EntityInterface $email */ $email = EmailQueueFactory::make()->persist(); - $this->exec('passbolt preview --body true'); + $this->exec('passbolt email_digest preview --body true'); $this->assertExitSuccess(); - $this->assertOutputContains('Sending email from: ' . $email->from_email); - $this->assertOutputContains('Sending email to: ' . $email->email); + $this->assertOutputContains('Sending email from: ' . $email->get('from_email')); + $this->assertOutputContains('Sending email to: ' . $email->get('email')); + $this->assertOutputContains(AvatarHelper::getAvatarFallBackUrl()); } /** @@ -83,7 +94,7 @@ public function testPreviewCommandLocale(): void EmailQueueFactory::make()->listeningToBeforeSave()->persist(); EmailQueueFactory::make()->listeningToBeforeSave()->setRecipient($frenchSpeakingUser->username)->persist(); - $this->exec('passbolt preview --body true'); + $this->exec('passbolt email_digest preview --body true'); $this->assertExitSuccess(); diff --git a/plugins/Passbolt/EmailDigest/tests/TestCase/Command/SenderCommandTest.php b/plugins/Passbolt/EmailDigest/tests/TestCase/Command/SenderCommandTest.php index 97f252e547..05ba8e27df 100644 --- a/plugins/Passbolt/EmailDigest/tests/TestCase/Command/SenderCommandTest.php +++ b/plugins/Passbolt/EmailDigest/tests/TestCase/Command/SenderCommandTest.php @@ -44,6 +44,7 @@ public function setUp(): void parent::setUp(); $this->useCommandRunner(); $this->setDummyFrenchTranslator(); + $this->loadPlugins(['Passbolt/EmailDigest']); } /** @@ -51,10 +52,10 @@ public function setUp(): void */ public function testSenderCommandHelp() { - $this->exec('passbolt sender -h'); + $this->exec('passbolt email_digest send -h'); $this->assertExitSuccess(); $this->assertOutputContains('Sends a batch of queued emails as emails digests.'); - $this->assertOutputContains('cake passbolt sender'); + $this->assertOutputContains('cake passbolt email_digest send'); } /** @@ -62,15 +63,16 @@ public function testSenderCommandHelp() */ public function testSenderCommandSender() { + /** @var \Cake\Datasource\EntityInterface $email */ $email = EmailQueueFactory::make()->persist(); - $this->exec('passbolt sender'); + $this->exec('passbolt email_digest send'); $this->assertExitSuccess(); - $this->assertMailSentFrom($email->from_email); - $this->assertMailSentTo($email->email); - $this->assertMailContains('Sending email from: ' . $email->from_email); - $this->assertMailContains('Sending email to: ' . $email->email); + $this->assertMailSentFrom($email->get('from_email')); + $this->assertMailSentTo($email->get('email')); + $this->assertMailContains('Sending email from: ' . $email->get('from_email')); + $this->assertMailContains('Sending email to: ' . $email->get('email')); } /** @@ -94,7 +96,7 @@ public function testSenderCommandLocale() ->setRecipient($frenchSpeakingUser->username) ->persist(); - $this->exec('passbolt sender'); + $this->exec('passbolt email_digest send'); $this->assertExitSuccess(); $this->assertMailContainsAt(0, $this->getDummyEnglishEmailSentence()); diff --git a/plugins/Passbolt/EmailDigest/tests/TestCase/Service/ConvertEmailVariablesToJsonServiceTest.php b/plugins/Passbolt/EmailDigest/tests/TestCase/Service/ConvertEmailVariablesToJsonServiceTest.php new file mode 100644 index 0000000000..23f53176fb --- /dev/null +++ b/plugins/Passbolt/EmailDigest/tests/TestCase/Service/ConvertEmailVariablesToJsonServiceTest.php @@ -0,0 +1,153 @@ +loadRoutes(); + + // Create an email with objects in v3.3.0 mode + // Forces the serialization to serialize (no Json) + Configure::write('EmailQueue.serialization_type', 'email_queue.serialize'); + FactoryTableRegistry::getTableLocator()->clear(); + + $users = UserFactory::make(2)->with('Profiles.Avatars', ['data' => 'Foo'])->persist(); + $resource = ResourceFactory::make()->persist()->toArray(); + + $originalUnsentEmail = EmailQueueFactory::make() + ->setField('template_vars', compact('users', 'resource')) + ->persist(); + + + // Swith to v3.3.1 mode, with variables saved in Json + FactoryTableRegistry::getTableLocator()->clear(); + TableRegistry::getTableLocator()->clear(); + $this->loadPlugins(['Passbolt/EmailDigest']); // This sets the serialize type to Json + + $service = new ConvertEmailVariablesToJsonService(); + $service->convert(); + $this->assertThatEmailsAreInJsonFormat($originalUnsentEmail, $users, $resource); + + // Make a second run in case this conversion is performed multiple times on the same queue. + // No error should be triggered. + $service->convert(); + $this->assertThatEmailsAreInJsonFormat($originalUnsentEmail, $users, $resource); + } + + private function assertThatEmailsAreInJsonFormat( + EntityInterface $originalUnsentEmail, + array $users, + array $resource + ): void { + // ConvertEmailVariablesToJsonService was meant to migrate MySQL serialized email variables + // into JSON. PostGRES came after and is therefore not concerned. + if (ConnectionManager::get('test')->getDriver() instanceof Postgres) { + $this->expectNotToPerformAssertions(); + return; + } + $hydratedVars = EmailQueueFactory::find()->where([ + 'id' => $originalUnsentEmail->get('id'), + 'email' => $originalUnsentEmail->get('email'), + ])->firstOrFail()->get('template_vars'); + + $rawVars = ConnectionManager::get('default') + ->newQuery() + ->select('template_vars') + ->from('email_queue') + ->execute() + ->fetchAll('assoc')[0]['template_vars']; + + $assertVars = function (array $vars) use ($users, $resource) { + $this->assertSame($users[0]->username, $vars['users'][0]['username']); + $this->assertSame($users[0]->role->name, $vars['users'][0]['role']['name']); + $this->assertNull($vars['users'][0]['profile']['avatar']['data'] ?? null); + $this->assertSame($users[1]->username, $vars['users'][1]['username']); + $this->assertSame($users[1]->role->name, $vars['users'][1]['role']['name']); + $this->assertNull($vars['users'][1]['profile']['avatar']['data'] ?? null); + $this->assertSame($resource['name'], $vars['resource']['name']); + // Dates are accessible under the $date['date] key! + $this->assertInstanceOf(FrozenTime::class, FrozenTime::parse($vars['resource']['created']['date'])); + }; + + $assertVars($hydratedVars); + $assertVars(json_decode($rawVars, true)); + } + + public function testConvertEmailVariablesToJsonService_findUnsentEmails() + { + $service = new ConvertEmailVariablesToJsonService(); + $nSent = 3; + $nNotSent = 2; + EmailQueueFactory::make($nSent)->sent()->persist(); + $notSentEmails = EmailQueueFactory::make($nNotSent)->persist(); + $notSentEmailIds = Hash::extract($notSentEmails, '{n}.id'); + + $retrievedEmails = $service->findUnsentEmails(); + $this->assertSame($nNotSent, count($retrievedEmails)); + foreach ($retrievedEmails as $email) { + $this->assertTrue(in_array($email['id'], $notSentEmailIds)); + } + } + + public function dataFor_testConvertEmailVariablesToJsonService_serializeStringToJson() + { + $entity = UserFactory::make()->with('Roles')->getEntity(); + $entities = UserFactory::make(2)->with('Roles')->getEntities(); + + return [ + ['', []], + [ + serialize(compact('entity')), + ['entity' => $entity->toArray()], + ], + [ + serialize([$entity, $entity, ['Foo']]), + [$entity->toArray(), $entity->toArray(), ['Foo']], + ], + [ + serialize(compact('entities')), + ['entities' => [$entities[0]->toArray(), $entities[1]->toArray()]], + ], + ]; + } + + /** + * @dataProvider dataFor_testConvertEmailVariablesToJsonService_serializeStringToJson + */ + public function testConvertEmailVariablesToJsonService_serializeStringToJson($templateVars, $jsonVars) + { + $service = new ConvertEmailVariablesToJsonService(); + $result = $service->toArray($templateVars); + $this->assertEquals($jsonVars, $result); + } +} diff --git a/plugins/Passbolt/EmailDigest/tests/TestCase/Unit/Service/PreviewEmailBatchServiceTest.php b/plugins/Passbolt/EmailDigest/tests/TestCase/Service/PreviewEmailBatchServiceTest.php similarity index 94% rename from plugins/Passbolt/EmailDigest/tests/TestCase/Unit/Service/PreviewEmailBatchServiceTest.php rename to plugins/Passbolt/EmailDigest/tests/TestCase/Service/PreviewEmailBatchServiceTest.php index 419896c544..f28796785e 100644 --- a/plugins/Passbolt/EmailDigest/tests/TestCase/Unit/Service/PreviewEmailBatchServiceTest.php +++ b/plugins/Passbolt/EmailDigest/tests/TestCase/Service/PreviewEmailBatchServiceTest.php @@ -15,8 +15,9 @@ * @since 3.2.0 */ -namespace Passbolt\EmailDigest\Test\TestCase\Unit\Service; +namespace Passbolt\EmailDigest\Test\TestCase\Service; +use App\Service\Avatars\AvatarsConfigurationService; use App\Test\Factory\UserFactory; use Cake\Chronos\Chronos; use Cake\TestSuite\TestCase; @@ -41,6 +42,7 @@ public function setUp(): void $this->loadPlugins(['Passbolt/EmailDigest']); $this->setDummyFrenchTranslator(); $this->previewEmailBatchService = new PreviewEmailBatchService(); + (new AvatarsConfigurationService())->loadConfiguration(); } public function tearDown(): void diff --git a/plugins/Passbolt/EmailDigest/tests/TestCase/Unit/Service/SendEmailBatchServiceTest.php b/plugins/Passbolt/EmailDigest/tests/TestCase/Service/SendEmailBatchServiceTest.php similarity index 97% rename from plugins/Passbolt/EmailDigest/tests/TestCase/Unit/Service/SendEmailBatchServiceTest.php rename to plugins/Passbolt/EmailDigest/tests/TestCase/Service/SendEmailBatchServiceTest.php index ac4eb8abeb..695b3255fa 100644 --- a/plugins/Passbolt/EmailDigest/tests/TestCase/Unit/Service/SendEmailBatchServiceTest.php +++ b/plugins/Passbolt/EmailDigest/tests/TestCase/Service/SendEmailBatchServiceTest.php @@ -14,7 +14,7 @@ * @link https://www.passbolt.com Passbolt(tm) * @since 2.13.0 */ -namespace Passbolt\EmailDigest\Test\TestCase\Unit\Service; +namespace Passbolt\EmailDigest\Test\TestCase\Service; use App\Test\Lib\AppIntegrationTestCase; use Cake\Mailer\Message; @@ -33,17 +33,17 @@ class SendEmailBatchServiceTest extends AppIntegrationTestCase use EmailTrait; /** - * @var SendEmailBatchService + * @var \Passbolt\EmailDigest\Service\SendEmailBatchService */ private $sut; /** - * @var MockObject|EmailDigestService + * @var \PHPUnit\Framework\MockObject\MockObject */ private $emailDigestServiceMock; /** - * @var EmailQueueTable|MockObject + * @var \Cake\ORM\Table|\PHPUnit\Framework\MockObject\MockObject */ private $emailQueueTableMock; diff --git a/plugins/Passbolt/EmailDigest/tests/TestCase/Unit/Utility/Digest/DigestsCollectionTest.php b/plugins/Passbolt/EmailDigest/tests/TestCase/Utility/Digest/DigestsCollectionTest.php similarity index 97% rename from plugins/Passbolt/EmailDigest/tests/TestCase/Unit/Utility/Digest/DigestsCollectionTest.php rename to plugins/Passbolt/EmailDigest/tests/TestCase/Utility/Digest/DigestsCollectionTest.php index d1cd878c00..69d68842ec 100644 --- a/plugins/Passbolt/EmailDigest/tests/TestCase/Unit/Utility/Digest/DigestsCollectionTest.php +++ b/plugins/Passbolt/EmailDigest/tests/TestCase/Utility/Digest/DigestsCollectionTest.php @@ -15,7 +15,7 @@ * @since 2.13.0 */ -namespace Passbolt\EmailDigest\Test\TestCase\Unit\Utility\Digest; +namespace Passbolt\EmailDigest\Test\TestCase\Utility\Digest; use Cake\ORM\Entity; use Cake\TestSuite\TestCase; @@ -36,7 +36,7 @@ class DigestsCollectionTest extends TestCase private $sut; /** - * @var MockObject|DigestsPool + * @var \PHPUnit\Framework\MockObject\MockObject|DigestsPool */ private $digestsCollectionMock; diff --git a/plugins/Passbolt/EmailDigest/tests/TestCase/Unit/Utility/Digest/DigestsPoolTest.php b/plugins/Passbolt/EmailDigest/tests/TestCase/Utility/Digest/DigestsPoolTest.php similarity index 90% rename from plugins/Passbolt/EmailDigest/tests/TestCase/Unit/Utility/Digest/DigestsPoolTest.php rename to plugins/Passbolt/EmailDigest/tests/TestCase/Utility/Digest/DigestsPoolTest.php index c1ad252558..e632f8b5f6 100644 --- a/plugins/Passbolt/EmailDigest/tests/TestCase/Unit/Utility/Digest/DigestsPoolTest.php +++ b/plugins/Passbolt/EmailDigest/tests/TestCase/Utility/Digest/DigestsPoolTest.php @@ -15,7 +15,7 @@ * @since 2.13.0 */ -namespace Passbolt\EmailDigest\Test\TestCase\Unit\Utility\Digest; +namespace Passbolt\EmailDigest\Test\TestCase\Utility\Digest; use Cake\TestSuite\TestCase; use Passbolt\EmailDigest\Test\Lib\EmailDigestMockTestTrait; @@ -36,6 +36,13 @@ public function setUp(): void parent::setUp(); } + public function tearDown(): void + { + unset($this->digestsPool); + DigestsPool::clearInstance(); + parent::tearDown(); + } + public function testEmailDigestDigestsPoolAddDigest() { // Create a lambda digest diff --git a/plugins/Passbolt/EmailDigest/tests/TestCase/Unit/Utility/Digest/SingleEmailDigestTest.php b/plugins/Passbolt/EmailDigest/tests/TestCase/Utility/Digest/SingleEmailDigestTest.php similarity index 93% rename from plugins/Passbolt/EmailDigest/tests/TestCase/Unit/Utility/Digest/SingleEmailDigestTest.php rename to plugins/Passbolt/EmailDigest/tests/TestCase/Utility/Digest/SingleEmailDigestTest.php index 682ff39a2f..5a7f7d4427 100644 --- a/plugins/Passbolt/EmailDigest/tests/TestCase/Unit/Utility/Digest/SingleEmailDigestTest.php +++ b/plugins/Passbolt/EmailDigest/tests/TestCase/Utility/Digest/SingleEmailDigestTest.php @@ -15,7 +15,7 @@ * @since 2.13.0 */ -namespace Passbolt\EmailDigest\Test\TestCase\Unit\Utility\Digest; +namespace Passbolt\EmailDigest\Test\TestCase\Utility\Digest; use Cake\TestSuite\TestCase; use Passbolt\EmailDigest\Test\Lib\EmailDigestMockTestTrait; @@ -28,12 +28,12 @@ class SingleEmailDigestTest extends TestCase use EmailDigestMockTestTrait; /** - * @var Digest + * @var \Passbolt\EmailDigest\Utility\Digest\DigestInterface */ private $sut; /** - * @var MockObject|EmailPreviewFactory + * @var \PHPUnit\Framework\MockObject\MockObject */ private $emailPreviewFactoryMock; diff --git a/plugins/Passbolt/EmailNotificationSettings/src/Controller/NotificationOrgSettings/NotificationOrgSettingsPostController.php b/plugins/Passbolt/EmailNotificationSettings/src/Controller/NotificationOrgSettings/NotificationOrgSettingsPostController.php index 73b11bf88a..e132e06741 100644 --- a/plugins/Passbolt/EmailNotificationSettings/src/Controller/NotificationOrgSettings/NotificationOrgSettingsPostController.php +++ b/plugins/Passbolt/EmailNotificationSettings/src/Controller/NotificationOrgSettings/NotificationOrgSettingsPostController.php @@ -82,7 +82,7 @@ private function _validateRequestData(): array if (!$form->validate($data)) { $errors = $form->getErrors(); - throw new CustomValidationException('The supplied email notification settings are not valid', $errors); + throw new CustomValidationException(__('The supplied email notification settings are not valid'), $errors); } $data = EmailNotificationSettingsForm::formatFormDataToOrgSettings($data); diff --git a/plugins/Passbolt/EmailNotificationSettings/src/Database/Type/SerializedType.php b/plugins/Passbolt/EmailNotificationSettings/src/Database/Type/SerializedType.php deleted file mode 100755 index c37dfaf7a2..0000000000 --- a/plugins/Passbolt/EmailNotificationSettings/src/Database/Type/SerializedType.php +++ /dev/null @@ -1,97 +0,0 @@ -organizationSettingsTable->createOrUpdateSetting(EmailNotificationSettings::NAMESPACE, $data, $uac); + $this->organizationSettingsTable->createOrUpdateSetting( + EmailNotificationSettings::NAMESPACE, + $data, + $userAccessControl + ); } /** diff --git a/plugins/Passbolt/EmailNotificationSettings/tests/Lib/EmailNotificationSettingsTestTrait.php b/plugins/Passbolt/EmailNotificationSettings/tests/Lib/EmailNotificationSettingsTestTrait.php index e8c6cdddec..4ba64a4d7b 100644 --- a/plugins/Passbolt/EmailNotificationSettings/tests/Lib/EmailNotificationSettingsTestTrait.php +++ b/plugins/Passbolt/EmailNotificationSettings/tests/Lib/EmailNotificationSettingsTestTrait.php @@ -44,17 +44,15 @@ protected function unloadNotificationSettings() */ protected function setEmailNotificationSetting(string $config, bool $value) { - $settings = []; - $settings[$config] = $value; - $this->setEmailNotificationSettings($settings); + $this->setEmailNotificationSettings([$config => $value]); } /** * Set email notification settings * - * @param array|null $settings Array of settings + * @param array $settings Array of settings */ - protected function setEmailNotificationSettings(?array $settings = []) + protected function setEmailNotificationSettings(array $settings) { $settingsToSave = []; foreach ($settings as $key => $setting) { diff --git a/plugins/Passbolt/EmailNotificationSettings/tests/TestCase/Controllers/NotificationOrgSettingsPostControllerTest.php b/plugins/Passbolt/EmailNotificationSettings/tests/TestCase/Controllers/NotificationOrgSettingsPostControllerTest.php index 5a05f15fc8..d183a4f9d7 100644 --- a/plugins/Passbolt/EmailNotificationSettings/tests/TestCase/Controllers/NotificationOrgSettingsPostControllerTest.php +++ b/plugins/Passbolt/EmailNotificationSettings/tests/TestCase/Controllers/NotificationOrgSettingsPostControllerTest.php @@ -14,17 +14,28 @@ * @link https://www.passbolt.com Passbolt(tm) * @since 2.10.0 */ -namespace Passbolt\MultiFactorAuthentication\Test\TestCase\Controllers\OrgSettings; +namespace Passbolt\EmailNotificationSettings\Test\TestCase\Controllers; +use App\Notification\Email\EmailSubscriptionDispatcher; use App\Test\Lib\AppIntegrationTestCase; -use Passbolt\EmailNotificationSettings\Utility\EmailNotificationSettings; +use Passbolt\EmailNotificationSettings\Test\Lib\EmailNotificationSettingsTestTrait; class NotificationOrgSettingsPostControllerTest extends AppIntegrationTestCase { + use EmailNotificationSettingsTestTrait; + + public function setUp(): void + { + parent::setUp(); + + $this->loadNotificationSettings(); + (new EmailSubscriptionDispatcher())->collectSubscribedEmailRedactors(); + } + public function tearDown(): void { - EmailNotificationSettings::flushCache(); parent::tearDown(); + $this->unloadNotificationSettings(); } /** diff --git a/plugins/Passbolt/EmailNotificationSettings/tests/TestCase/Database/Type/SerializedTypeTest.php b/plugins/Passbolt/EmailNotificationSettings/tests/TestCase/Database/Type/SerializedTypeTest.php deleted file mode 100644 index cf8f338650..0000000000 --- a/plugins/Passbolt/EmailNotificationSettings/tests/TestCase/Database/Type/SerializedTypeTest.php +++ /dev/null @@ -1,60 +0,0 @@ -loadPlugins(['Passbolt/EmailNotificationSettings' => ['bootstrap' => true,]]); - $this->EmailQueue = TableRegistry::getTableLocator()->get('EmailQueue.EmailQueue'); - } - - public function testThatEmailsGetCorrectlySerializedAndUnserialized() - { - \Cake\Core\Configure::write("passbolt.plugins.locale.options",[]); - $users = UserFactory::make(2)->user()->getEntities(); - $someText = 'Foo bar'; - $someInteger = 123; - - // Sets variables in the email. - $data = compact('users', 'someText', 'someInteger'); - $result = $this->EmailQueue->enqueue('foo@bar.test', $data); - $this->assertTrue($result); - - \Cake\Core\Configure::write("passbolt.plugins.locale.options",[]); - $email = $this->EmailQueue->find()->firstOrFail(); - $vars = $email->get('template_vars'); - - $this->assertEquals($users, $vars['users']); - $this->assertInstanceOf(User::class, $vars['users'][0]); - $this->assertInstanceOf(User::class, $vars['users'][1]); - - $this->assertSame($someText, $vars['someText']); - $this->assertSame($someInteger, $vars['someInteger']); - } -} diff --git a/plugins/Passbolt/EmailNotificationSettings/tests/TestCase/PluginTest.php b/plugins/Passbolt/EmailNotificationSettings/tests/TestCase/PluginTest.php deleted file mode 100644 index 748fe4c5f1..0000000000 --- a/plugins/Passbolt/EmailNotificationSettings/tests/TestCase/PluginTest.php +++ /dev/null @@ -1,54 +0,0 @@ -loadPlugins(['Passbolt/EmailNotificationSettings' => ['bootstrap' => true,]]); - $this->EmailQueue = TableRegistry::getTableLocator()->get('EmailQueue.EmailQueue'); - } - - public function testEmailNotificationSettingsSerializationConfig() - { - $this->assertSame( - 'passbolt.serialized', - $this->EmailQueue->getSchema()->getColumnType('template_vars') - ); - $this->assertSame( - 'passbolt.serialized', - $this->EmailQueue->getSchema()->getColumnType('headers') - ); - $this->assertSame( - 'passbolt.serialized', - $this->EmailQueue->getSchema()->getColumnType('attachments') - ); - - $this->assertSame(SerializedType::class, Type::getMap('passbolt.serialized')); - } -} diff --git a/plugins/Passbolt/InFormIntegration/config/bootstrap.php b/plugins/Passbolt/InFormIntegration/config/bootstrap.php new file mode 100644 index 0000000000..bd88ce4f84 --- /dev/null +++ b/plugins/Passbolt/InFormIntegration/config/bootstrap.php @@ -0,0 +1,18 @@ + [ + 'plugins' => [ + 'inFormIntegration' => [ + 'enabled' => Configure::read( + 'passbolt.plugins.inFormIntegration.enabled', + env('PASSBOLT_PLUGINS_IN_FORM_INTEGRATION_ENABLED', true) + ), + 'version' => '1.0.0', + 'settingsVisibility' => [ + 'whiteListPublic' => [ + 'enabled', + ], + ], + ], + ], + ], +]; diff --git a/plugins/Passbolt/JwtAuthentication/config/bootstrap.php b/plugins/Passbolt/JwtAuthentication/config/bootstrap.php new file mode 100644 index 0000000000..d49314236f --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/config/bootstrap.php @@ -0,0 +1,18 @@ + [ + 'plugins' => [ + 'jwtAuthentication' => [ + 'version' => '3.3.0', + ], + ], + ], +]; diff --git a/plugins/Passbolt/JwtAuthentication/config/routes.php b/plugins/Passbolt/JwtAuthentication/config/routes.php new file mode 100644 index 0000000000..55c08f42fa --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/config/routes.php @@ -0,0 +1,43 @@ +redirect('.well-known/jwks.json', '/auth/jwt/jwks.json'); +}); + +Router::plugin('Passbolt/JwtAuthentication', ['path' => '/auth/jwt'], function (RouteBuilder $routes) { + $routes->setExtensions(['json']); + + // WARNING - if you add routes, check whether it should be included in + // JwtRequestDetectionService::useJwtAuthentication / isJwtLoginRoute + + $routes->connect('/rsa', ['controller' => 'Jwks', 'action' => 'rsa']) + ->setMethods(['GET']); + + $routes->connect('/jwks', ['controller' => 'Jwks', 'action' => 'jwks']) + ->setMethods(['GET']); + + $routes->connect('/login', ['controller' => 'JwtLogin', 'action' => 'loginPost']) + ->setMethods(['POST']); + + $routes->connect('/refresh', ['controller' => 'RefreshToken', 'action' => 'refreshPost']) + ->setMethods(['POST']); + + $routes->connect('/logout', ['controller' => 'JwtLogout', 'action' => 'logoutPost']) + ->setMethods(['POST']); +}); diff --git a/plugins/Passbolt/JwtAuthentication/src/Authenticator/GpgJwtAuthenticator.php b/plugins/Passbolt/JwtAuthentication/src/Authenticator/GpgJwtAuthenticator.php new file mode 100644 index 0000000000..76f87ca9a3 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Authenticator/GpgJwtAuthenticator.php @@ -0,0 +1,496 @@ +is('json')) { + throw new ForbiddenException(__('You need to login to access this location.')); + } + // Otherwise we let the controller handle the redirections + } + + /** + * Authenticate + * + * @param \Psr\Http\Message\ServerRequestInterface $request interface for accessing request parameters + * @return \Authentication\Authenticator\ResultInterface User|false the user or false if authentication failed + */ + public function authenticate(ServerRequestInterface $request): ResultInterface + { + /** @var \Cake\Http\ServerRequest $request */ + + try { + $this->setRequest($request); + $this->init(); + $verifyToken = $this->verifyChallenge(); + + return $this->successResult($verifyToken); + } catch (\InvalidArgumentException $exception) { + return $this->errorResult($exception, Result::FAILURE_CREDENTIALS_MISSING); + } catch (NotFoundException $exception) { + return $this->errorResult($exception, Result::FAILURE_IDENTITY_NOT_FOUND); + } catch (BadRequestException $exception) { + return $this->errorResult($exception, Result::FAILURE_CREDENTIALS_INVALID); + } catch (\Exception $exception) { + return $this->errorResult($exception, Result::FAILURE_OTHER); + } + } + + /** + * Authentication process initialization + * + * @throws \Cake\Http\Exception\InternalErrorException if the server or user keys cannot be loaded + * @throws \Cake\Http\Exception\BadRequestException if the user data is not valid, if the user id is not provided + * @throws \Cake\Http\Exception\NotFoundException if the user cannot be found, is deleted, is not active + * @return void + */ + public function init(): void + { + $this->setOpenPGPBackend(); + $this->setServerKey(); + $this->loadUserData(); + $this->setUserKey(); + } + + /** + * Format success results + * + * @param string $verifyToken token + * @return \Authentication\Authenticator\Result + * @access private + */ + public function successResult(string $verifyToken): Result + { + $armoredChallenge = $this->makeArmoredChallenge($verifyToken); + $data = ['challenge' => $armoredChallenge, 'user' => $this->user]; + + return new Result($data, Result::SUCCESS); + } + + /** + * Create an encrypted challenge. + * + * @param string $verifyToken verify token. + * @return string encrypted challenge + */ + public function makeArmoredChallenge(string $verifyToken): string + { + /** @var \Passbolt\JwtAuthentication\Authenticator\JwtArmoredChallengeInterface $armoredChallengeService */ + $armoredChallengeService = $this->getContainer($this->getRequest())->get(JwtArmoredChallengeInterface::class); + $challenge = $armoredChallengeService->makeArmoredChallenge($this->request, $this->user, $verifyToken); + + $this->dispatchEvent(self::JWT_AUTHENTICATION_AFTER_IDENTIFY, $challenge, $this); + + return $this->gpg->encryptSign(json_encode($challenge)); + } + + /** + * Format error result + * Log additional information about the error for the administrator + * + * @param \Exception $exception exception + * @param string $reason example Result::FAILURE_CREDENTIALS_MISSING + * @return \Authentication\Authenticator\Result + * @access private + */ + public function errorResult(\Exception $exception, string $reason): Result + { + Log::error($exception->getMessage()); + + return new Result(null, $reason); + } + + /** + * @throws \Cake\Http\Exception\InternalErrorException if backend cannot be loaded + * @return void + */ + public function setOpenPGPBackend(): void + { + $this->gpg = OpenPGPBackendFactory::get(); + } + + /** + * @throws \Cake\Http\Exception\InternalErrorException if the server key cannot be loaded + * @return void + */ + public function setServerKey(): void + { + // Check if config contains fingerprint + $fingerprint = Configure::read('passbolt.gpg.serverKey.fingerprint'); + $this->assertServerFingerprint($fingerprint); + + // Check if config contains valid passphrase + $passphrase = Configure::read('passbolt.gpg.serverKey.passphrase'); + $this->assertServerPassphrase($passphrase); + + // set the key to be used for decrypting + try { + $this->gpg->setSignKeyFromFingerprint($fingerprint, $passphrase); + $this->gpg->setDecryptKeyFromFingerprint($fingerprint, $passphrase); + } catch (\Exception $exception) { + try { + $this->gpg->importServerKeyInKeyring(); + $this->gpg->setSignKeyFromFingerprint($fingerprint, $passphrase); + $this->gpg->setDecryptKeyFromFingerprint($fingerprint, $passphrase); + } catch (\Exception $exception) { + $msg = __('The OpenPGP server key defined in the config cannot be used to decrypt.') . ' '; + $msg .= $exception->getMessage(); + throw new InternalErrorException($msg); + } + } + } + + /** + * Set user key + * + * @throws \Cake\Http\Exception\BadRequestException if the user data is not valid + * @throws \Cake\Http\Exception\InternalErrorException if the user key cannot be loaded + * @return void + */ + public function setUserKey(): void + { + try { + $this->gpg->setVerifyKeyFromFingerprint($this->user->gpgkey->fingerprint); + $this->gpg->setEncryptKeyFromFingerprint($this->user->gpgkey->fingerprint); + } catch (\Exception $exception) { + // Try to import the key in keyring again + try { + $this->gpg->importKeyIntoKeyring($this->user->gpgkey->armored_key); + $this->gpg->setVerifyKeyFromFingerprint($this->user->gpgkey->fingerprint); + $this->gpg->setEncryptKeyFromFingerprint($this->user->gpgkey->fingerprint); + } catch (\Exception $exception) { + $msg = __('Could not import the user OpenPGP key.'); + throw new InternalErrorException($msg); + } + } + } + + /** + * Load user data including OpenPGP key in $user props + * + * @throws \Cake\Http\Exception\BadRequestException if the user id is missing in the request + * @throws \Cake\Http\Exception\NotFoundException if the user cannot be found, is deleted, is not active + * @return void + * @access private + */ + public function loadUserData(): void + { + $userId = $this->request->getData('user_id'); + $this->assertUserId($userId); + $userData = $this->findUser($userId); + $this->assertUserData($userData); + $this->user = $userData; + } + + /** + * @param string $userId uuid + * @throws \Cake\Http\Exception\NotFoundException if the user cannot be found, is deleted, is not active + * @return \App\Model\Entity\User + */ + private function findUser(string $userId): User + { + try { + /** @var \App\Model\Table\UsersTable $Users */ + $Users = TableRegistry::getTableLocator()->get('Users'); + + /** @var \App\Model\Entity\User|null $userData */ + $userData = $Users->findView($userId, Role::GUEST) + ->contain('Gpgkeys') + ->first(); + } catch (\Exception $exception) { + Log::error($exception->getMessage()); + throw new NotFoundException(__('The user does not exist or has been deleted.')); + } + + if (!isset($userData)) { + throw new NotFoundException(__('The user does not exist or has been deleted.')); + } + + return $userData; + } + + /** + * @throws \InvalidArgumentException if the challenge is missing + * @throws \Cake\Http\Exception\BadRequestException if the challenge is invalid + * @return string + */ + public function verifyChallenge(): string + { + // Sanity check + $armoredChallenge = $this->request->getData('challenge'); + $this->assertArmoredChallenge($armoredChallenge); + + // Verify signature + $this->assertUserSignature($armoredChallenge); + + // Decrypt + try { + $clearTextChallenge = $this->gpg->decrypt($armoredChallenge); + } catch (\Exception $exception) { + Log::error($exception->getMessage()); + throw new BadRequestException(__('The challenge cannot be decrypted.')); + } + + // Deserialize JSON + try { + $jsonChallenge = json_decode($clearTextChallenge, true, 2, JSON_THROW_ON_ERROR); + [ + 'version' => $version, + 'domain' => $domain, + 'verify_token' => $verifyToken, + 'verify_token_expiry' => $verifyTokenExpiry, + ] = $jsonChallenge; + } catch (\Exception $exception) { + Log::error($exception->getMessage() . "\n" . $clearTextChallenge); + throw new BadRequestException(__('The challenge is invalid. Deserialization failed.')); + } + + // Challenge sanity check + + // If domain is not known, let the exception be thrown. It will send email alerts. + $this->assertDomain($domain); + try { + $this->assertVersion($version); + (new VerifyTokenValidationService())->validateToken( + $verifyTokenExpiry, + $verifyToken, + $this->request->getData('user_id') + ); + } catch (\Exception $exception) { + Log::error($exception->getMessage() . "\n" . $clearTextChallenge); + throw new BadRequestException(__('The challenge is invalid. Validation Failed.')); + } + + return $verifyToken; + } + + /** + * @param mixed $fingerprint fingerprint + * @throws \Cake\Http\Exception\InternalErrorException + * @return void + */ + public function assertServerFingerprint($fingerprint): void + { + if (!is_string($fingerprint) || !GpgkeysTable::isValidFingerprint($fingerprint)) { + $msg = __('The config for the server private key fingerprint is not available or incomplete.'); + throw new InternalErrorException($msg); + } + } + + /** + * @param mixed $passphrase passphrase + * @throws \Cake\Http\Exception\InternalErrorException + * @return void + */ + public function assertServerPassphrase($passphrase): void + { + if (!is_string($passphrase)) { + $msg = __('The config for the server private key passphrase is invalid.'); + throw new InternalErrorException($msg); + } + } + + /** + * @param mixed $userId uuid + * @throws \Cake\Http\Exception\BadRequestException + * @return void + */ + public function assertUserId($userId): void + { + if (!is_string($userId) || !Validation::uuid($userId)) { + $msg = __('The user id is missing or invalid.'); + throw new BadRequestException($msg); + } + } + + /** + * @param mixed $userData data + * @throws \Cake\Http\Exception\BadRequestException + * @return void + */ + public function assertUserData($userData): void + { + if ( + !isset($userData->gpgkey) || + !isset($userData->gpgkey->fingerprint) || + !isset($userData->gpgkey->armored_key) || + !is_string($userData->gpgkey->fingerprint) || + !GpgkeysTable::isValidFingerprint($userData->gpgkey->fingerprint) || + !is_string($userData->gpgkey->armored_key) + ) { + $msg = __('The user OpenPGP key does not exist, or is invalid, or has been deleted.'); + throw new BadRequestException($msg); + } + } + + /** + * @param string $armoredChallenge challenge + * @throws \Exception if armored challenge is invalid + * @return void + */ + public function assertUserSignature(string $armoredChallenge): void + { + try { + $this->gpg->verify($armoredChallenge); + } catch (\Exception $exception) { + throw new InvalidUserSignatureException(__('The user signature is invalid.')); + } + } + + /** + * @param mixed $armoredChallenge challenge + * @throws \InvalidArgumentException if armored challenge is invalid + * @return void + */ + public function assertArmoredChallenge($armoredChallenge): void + { + if ( + !isset($armoredChallenge) || + !is_string($armoredChallenge) || + !$this->gpg->isValidMessage($armoredChallenge) + ) { + throw new BadRequestException(__('The user challenge is missing or invalid.')); + } + } + + /** + * @param mixed $version version + * @throws \Exception if version is not supported + * @return void + */ + public function assertVersion($version): void + { + if (!isset($version) || !is_string($version) || $version !== self::PROTOCOL_VERSION) { + throw new \Exception(__('The version is invalid.')); + } + } + + /** + * Assert domain + * + * @param mixed $domain domain + * @return void + * @throws \Passbolt\JwtAuthentication\Error\Exception\Challenge\InvalidDomainException if domain is invalid + */ + public function assertDomain($domain): void + { + if (!isset($domain) || !is_string($domain)) { + throw new InvalidDomainException(__('The domain is invalid.')); + } + + if (rtrim($domain, '/') !== rtrim(Router::url('/', true), '/')) { + $expect = rtrim(Router::url('/', true)); + $got = rtrim($domain, '/'); + throw new InvalidDomainException(__('The domain is invalid. Expected: {0} and got {1}', $expect, $got)); + } + } + + /** + * @return \App\Utility\OpenPGP\OpenPGPBackend + */ + public function getGpg(): \App\Utility\OpenPGP\OpenPGPBackend + { + return $this->gpg; + } + + /** + * @return \App\Model\Entity\User|null + */ + public function getUser(): ?User + { + return $this->user; + } + + /** + * @return \Cake\Http\ServerRequest + */ + public function getRequest(): ServerRequest + { + return $this->request; + } + + /** + * @param \Cake\Http\ServerRequest $request Server request + * @return void + */ + public function setRequest(ServerRequest $request): void + { + $this->request = $request; + } +} diff --git a/plugins/Passbolt/JwtAuthentication/src/Authenticator/JwtArmoredChallengeInterface.php b/plugins/Passbolt/JwtAuthentication/src/Authenticator/JwtArmoredChallengeInterface.php new file mode 100644 index 0000000000..f14a3ed0ce --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Authenticator/JwtArmoredChallengeInterface.php @@ -0,0 +1,35 @@ +createToken($user->id); + $refreshToken = (new RefreshTokenCreateService())->createToken($request, $user->id, $accessToken)->token; + $verifyToken = (new VerifyTokenCreateService())->createToken($verifyToken, $user->id)->token; + + return [ + 'version' => GpgJwtAuthenticator::PROTOCOL_VERSION, + 'domain' => Router::url('/', true), + 'access_token' => $accessToken, + 'refresh_token' => $refreshToken, + 'verify_token' => $verifyToken, + ]; + } +} diff --git a/plugins/Passbolt/JwtAuthentication/src/Authenticator/JwtRefreshTokenAuthenticator.php b/plugins/Passbolt/JwtAuthentication/src/Authenticator/JwtRefreshTokenAuthenticator.php new file mode 100644 index 0000000000..6ea897f0c8 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Authenticator/JwtRefreshTokenAuthenticator.php @@ -0,0 +1,146 @@ +readRefreshTokenInRequest($request); + $user = $this->findUser($refreshToken->user_id); + } catch (RecordNotFoundException $exception) { + throw new RefreshTokenNotFoundException(); + } catch (AbstractJwtAttackException $exception) { + throw $exception; + } catch (\Exception $e) { + throw new BadRequestException($e->getMessage()); + } + + $this->extendSessionIdentificationServiceInterfaceInDIC($request, $refreshToken); + + return new Result(compact('user'), Result::SUCCESS); + } + + /** + * @param string $userId uuid + * @return \App\Model\Entity\User + * @throws \Cake\Datasource\Exception\RecordNotFoundException if the user cannot be found, is deleted, is not active + */ + protected function findUser(string $userId): User + { + /** @var \App\Model\Entity\User $user */ + $user = TableRegistry::getTableLocator() + ->get('Users') + ->find('activeNotDeletedContainRole') + ->where(['Users.id' => $userId]) + ->firstOrFail(); + + return $user; + } + + /** + * Reads the refresh token in the payload or in the cookies + * + * @param \Cake\Http\ServerRequest $request Server request + * @return \App\Model\Entity\AuthenticationToken + * @throws \Cake\Http\Exception\NotFoundException if the user token cannot be found in the cookie + * @throws \Passbolt\JwtAuthentication\Error\Exception\RefreshToken\RefreshTokenNotFoundException if the token is not found + * @throws \Passbolt\JwtAuthentication\Error\Exception\RefreshToken\ConsumedRefreshTokenAccessException if the token was already consumed + * @throws \Passbolt\JwtAuthentication\Error\Exception\RefreshToken\ExpiredRefreshTokenAccessException if the token is expired + */ + protected function readRefreshTokenInRequest(ServerRequest $request): AuthenticationToken + { + $service = new RefreshTokenAuthenticationService(); + if ($this->isValidPayloadProvided($request, $service)) { + $refreshToken = $service->getActiveRefreshToken( + $request->getData(RefreshTokenAbstractService::REFRESH_TOKEN_DATA_KEY), + $request->getData('user_id') + ); + } else { + /** @var \App\Model\Entity\AuthenticationToken $refreshToken */ + $refreshToken = $service->queryRefreshToken( + $request->getCookie(RefreshTokenAbstractService::REFRESH_TOKEN_COOKIE) + )->firstOrFail(); + } + + $service->throwSecurityExceptionsOnInvalidRefreshToken($refreshToken); + + return $refreshToken; + } + + /** + * @param \Cake\Http\ServerRequest $request Server request + * @param \App\Model\Entity\AuthenticationToken $refreshToken Valid user authenticating refresh token + * @return void + */ + protected function extendSessionIdentificationServiceInterfaceInDIC( + ServerRequest $request, + AuthenticationToken $refreshToken + ): void { + $this + ->getContainer($request) + ->extend(SessionIdentificationServiceInterface::class) + ->setConcrete(RefreshTokenSessionIdentificationService::class) + ->addArgument($refreshToken); + } + + /** + * Is the user ID and the refresh token in the payload. + * + * @param \Cake\Http\ServerRequest $request Server Request + * @param \Passbolt\JwtAuthentication\Service\RefreshToken\RefreshTokenAuthenticationService $service Helping service + * @return bool + * @throws \InvalidArgumentException if the refresh token is not valid + * @throws \InvalidArgumentException if the user id is not valid + */ + protected function isValidPayloadProvided(ServerRequest $request, RefreshTokenAuthenticationService $service): bool + { + $token = $request->getData(RefreshTokenAbstractService::REFRESH_TOKEN_DATA_KEY); + $userId = $request->getData('user_id'); + $isPayloadProvided = is_string($token) || is_string($userId); + if ($isPayloadProvided) { + $service->validateRefreshToken($token); + $service->validateUserId($userId); + } + + return $isPayloadProvided; + } +} diff --git a/plugins/Passbolt/JwtAuthentication/src/Authenticator/JwtSessionIdentificationService.php b/plugins/Passbolt/JwtAuthentication/src/Authenticator/JwtSessionIdentificationService.php new file mode 100644 index 0000000000..530bbd87bb --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Authenticator/JwtSessionIdentificationService.php @@ -0,0 +1,59 @@ +accessToken = $accessToken; + } + + /** + * @inheritDoc + */ + public function getSessionIdentifier(ServerRequest $request): ?string + { + // The access token generated is injected after logging in. This is considered as the session ID + if (isset($this->accessToken)) { + return $this->accessToken; + } + if (!$this->isAuthenticated($request)) { + return null; + } + + $token = $request->getHeaderLine(JwtAuthenticationService::JWT_HEADER); + + return str_replace('Bearer ', '', $token); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/src/Authenticator/RefreshTokenSessionIdentificationService.php b/plugins/Passbolt/JwtAuthentication/src/Authenticator/RefreshTokenSessionIdentificationService.php new file mode 100644 index 0000000000..b9808e478f --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Authenticator/RefreshTokenSessionIdentificationService.php @@ -0,0 +1,52 @@ +refreshToken = $refreshToken; + } + + /** + * @inheritDoc + */ + public function getSessionIdentifier(ServerRequest $request): ?AuthenticationToken + { + if (!$this->isAuthenticated($request)) { + return null; + } + + return $this->refreshToken; + } +} diff --git a/plugins/Passbolt/JwtAuthentication/src/Command/CreateAccessTokenCommand.php b/plugins/Passbolt/JwtAuthentication/src/Command/CreateAccessTokenCommand.php new file mode 100644 index 0000000000..3c0ea2fc48 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Command/CreateAccessTokenCommand.php @@ -0,0 +1,136 @@ +loadModel('Users'); + } + + /** + * @inheritDoc + */ + public function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionParser + { + $parser + ->setDescription(__('Create a JSON Web Token.')) + ->addOption('username', [ + 'help' => 'The username to create a user for.', + 'short' => 'u', + ]) + ->addOption('user-id', [ + 'help' => 'The user ID to create a user for.', + 'short' => 'i', + ]) + ->addOption('expiry', [ + 'help' => 'The token\'s validity time in minutes, or any time expressed in words.', + 'default' => Configure::read(JwtTokenCreateService::JWT_EXPIRY_CONFIG_KEY), + 'short' => 'e', + ]); + + return $parser; + } + + /** + * @inheritDoc + */ + public function execute(Arguments $args, ConsoleIo $io): ?int + { + parent::execute($args, $io); + + $this->abortIfNotInDebugMode($io); + + $expiry = $this->getExpiry($args); + $user = null; + try { + $user = $this->getUserId($args, $io); + } catch (\Throwable $e) { + $io->abort($e->getMessage()); + } + $token = (new JwtTokenCreateService())->createToken($user->id, $expiry); + $io->out("Access token for {$user->username} valid {$expiry}:"); + $io->hr(); + $io->success($token); + $io->hr(); + + return $this->successCode(); + } + + /** + * Fetch the user. + * + * @param \Cake\Console\Arguments $args Arguments passed. + * @param \Cake\Console\ConsoleIo $io Console IO + * @return \App\Model\Entity\User + * @throws \Cake\Datasource\Exception\RecordNotFoundException when there is no record. + * * @throws \Cake\Console\Exception\StopException when options are missing. + */ + protected function getUserId(Arguments $args, ConsoleIo $io): User + { + $query = $this->Users->findActive(); + + if ($args->hasOption('user-id')) { + $query->where([$this->Users->aliasField('id') => $args->getOption('user-id')]); + } elseif ($args->hasOption('username')) { + $query->where([$this->Users->aliasField('username') => $args->getOption('username')]); + } else { + $io->abort('Please specify a valid id or a username.'); + } + + /** @var \App\Model\Entity\User $user */ + $user = $query->firstOrFail(); + + return $user; + } + + /** + * Fetch the expiry time in string format. + * + * @param \Cake\Console\Arguments $args Arguments passed. + * @return string + */ + protected function getExpiry(Arguments $args): string + { + /** @var string $expiry */ + $expiry = $args->getOption('expiry'); + if (is_numeric($expiry)) { + $expiry .= ' minutes'; + } + + return $expiry; + } +} diff --git a/plugins/Passbolt/JwtAuthentication/src/Command/CreateJwtKeysCommand.php b/plugins/Passbolt/JwtAuthentication/src/Command/CreateJwtKeysCommand.php new file mode 100644 index 0000000000..a111734a55 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Command/CreateJwtKeysCommand.php @@ -0,0 +1,81 @@ +setDescription(__('Create a JWT key pair.')) + ->addOption('force', [ + 'help' => 'Override the key files if found.', + 'default' => 'false', + 'short' => 'f', + 'boolean' => true, + ]); + + return $parser; + } + + /** + * @inheritDoc + */ + public function execute(Arguments $args, ConsoleIo $io): ?int + { + parent::execute($args, $io); + + $force = $args->getOption('force'); + $service = new JwtKeyPairService(); + + if ($service->keyPairExists() && !$force) { + if (file_exists($service->getPublicKeyPath())) { + $io->warning('Public key path: ' . $service->getPublicKeyPath()); + } + if (file_exists($service->getPublicKeyPath())) { + $io->warning('Secret key path: ' . $service->getSecretKeyPath()); + } + $msg = "A JWT key pair was found. \n"; + $msg .= "Use the force option to overwrite with a fresh new pair. \n"; + $msg .= 'This will log out all users currently logged in with JWT Authentication.'; + $io->abort($msg); + } + + try { + $service->createKeyPair($force); + $service->validateKeyPair(); + } catch (InvalidJwtKeyPairException $e) { + $io->abort($e->getMessage()); + } + + $io->success('A JWT key pair was successfully created.'); + $io->success('Public key path: ' . $service->getPublicKeyPath()); + $io->success('Secret key path: ' . $service->getSecretKeyPath()); + + return $this->successCode(); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/src/Controller/JwksController.php b/plugins/Passbolt/JwtAuthentication/src/Controller/JwksController.php new file mode 100644 index 0000000000..5e09292d4f --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Controller/JwksController.php @@ -0,0 +1,89 @@ +Authentication->allowUnauthenticated(['rsa', 'jwks']); + } + + /** + * Serve the JWT public key + * + * @return void + */ + public function rsa(): void + { + $jwksGetService = new JwksGetService(); + $keydata = []; + try { + $keydata['keydata'] = $jwksGetService->getRawPublicKey(); + } catch (InvalidJwtKeyPairException $e) { + $this->logAndThrowInvalidJwtKeyPairException($e); + } + $this->success(__('The operation was successful.'), $keydata); + } + + /** + * Serve the JWT public key + * + * @return void + */ + public function jwks(): void + { + $jwksGetService = new JwksGetService(); + $keys = []; + try { + $keys['keys'][] = $jwksGetService->getPublicKey(); + } catch (InvalidJwtKeyPairException $e) { + $this->logAndThrowInvalidJwtKeyPairException($e); + } + + // Do not use regular envelope as this is a normalized endpoint + $this->set(compact('keys')); + $this->viewBuilder()->setOption('serialize', 'keys'); + $this->setViewBuilderOptions(); + } + + /** + * Logs the message encountered by the user and logs + * further information for the admins. + * + * @param \Passbolt\JwtAuthentication\Error\Exception\AccessToken\InvalidJwtKeyPairException $e Exception to be logged + * @return void + * @throws \Passbolt\JwtAuthentication\Error\Exception\AccessToken\InvalidJwtKeyPairException the exception to be logged + */ + protected function logAndThrowInvalidJwtKeyPairException(InvalidJwtKeyPairException $e): void + { + Log::alert($e->getMessage()); + Log::error(__('The following file could not be read: {0}.', JwksGetService::PUBLIC_KEY_PATH)); + throw $e; + } +} diff --git a/plugins/Passbolt/JwtAuthentication/src/Controller/JwtLoginController.php b/plugins/Passbolt/JwtAuthentication/src/Controller/JwtLoginController.php new file mode 100644 index 0000000000..2e9fc3bfd1 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Controller/JwtLoginController.php @@ -0,0 +1,78 @@ +Authentication->allowUnauthenticated([ + 'loginPost', + ]); + + return parent::beforeFilter($event); + } + + /** + * User login post action + * + * @return void + */ + public function loginPost() + { + if (!$this->request->is('json')) { + throw new BadRequestException(__('This is not a valid Ajax/Json request.')); + } + + $result = $this->Authentication->getResult(); + if ($result->isValid()) { + $challenge = $result->getData()['challenge']; + $user = $result->getData()['user']; + $uac = new UserAccessControl($user['role']['name'], $user['id']); + UserAction::getInstance()->setUserAccessControl($uac); + $this->success(__('The authentication was a success.'), compact('challenge')); + } else { + $message = __('The authentication failed.') . ' '; + switch ($result->getStatus()) { + case Result::FAILURE_CREDENTIALS_MISSING: + $message .= __('The credentials are missing.'); + throw new BadRequestException($message); + case Result::FAILURE_IDENTITY_NOT_FOUND: + $message = __('The user does not exist or is not active or has been deleted.'); + throw new NotFoundException($message); + case Result::FAILURE_CREDENTIALS_INVALID: + $message = __('The credentials are invalid.'); + throw new BadRequestException($message); + default: + case Result::FAILURE_OTHER: + $message = __('An internal error occurred.'); + throw new InternalErrorException($message); + } + } + } +} diff --git a/plugins/Passbolt/JwtAuthentication/src/Controller/JwtLogoutController.php b/plugins/Passbolt/JwtAuthentication/src/Controller/JwtLogoutController.php new file mode 100644 index 0000000000..5049a9eab3 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Controller/JwtLogoutController.php @@ -0,0 +1,50 @@ +logout($this->User->id(), $this->getRequest()); + $this->removeRefreshTokenFromCookies(); + $this->Authentication->logout(); + $this->success(); + } + + /** + * Ensures that no refresh token cookie is placed in the header + * + * @return void + */ + protected function removeRefreshTokenFromCookies(): void + { + $cookiesCollection = $this->getResponse()->getCookieCollection()->remove( + RefreshTokenAbstractService::REFRESH_TOKEN_COOKIE + ); + $this->setResponse($this->getResponse()->withCookieCollection($cookiesCollection)); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/src/Controller/RefreshTokenController.php b/plugins/Passbolt/JwtAuthentication/src/Controller/RefreshTokenController.php new file mode 100644 index 0000000000..4ab4d78af7 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Controller/RefreshTokenController.php @@ -0,0 +1,70 @@ +getSessionIdentifier($this->getRequest()); + $accessToken = $this->renewRefreshTokenAndSetInResponseAsSecureCookie($refreshToken); + } catch (\Exception $e) { + throw new BadRequestException($e->getMessage()); + } + + $this->success(null, ['access_token' => $accessToken]); + } + + /** + * Consume the refresh token provided in the request, consume it and generate a new one. + * Set that new refresh token in a secure http only cookie. + * + * @param \App\Model\Entity\AuthenticationToken $oldRefreshToken Refresh token passed in the request + * @return string Access token newlyy created and associated to the new refresh token + * @throws \Passbolt\JwtAuthentication\Error\Exception\RefreshToken\RefreshTokenNotFoundException if the token is not found + * @throws \Passbolt\JwtAuthentication\Error\Exception\RefreshToken\ConsumedRefreshTokenAccessException if the token was already consumed + * @throws \Passbolt\JwtAuthentication\Error\Exception\RefreshToken\ExpiredRefreshTokenAccessException if the token is expired + */ + protected function renewRefreshTokenAndSetInResponseAsSecureCookie(AuthenticationToken $oldRefreshToken): string + { + $accessToken = (new JwtTokenCreateService())->createToken($oldRefreshToken->user_id); + $refreshService = (new RefreshTokenRenewalService()); + $refreshedToken = $refreshService->renewToken($this->getRequest(), $oldRefreshToken, $accessToken); + $refreshHttpOnlySecureCookie = $refreshService->createHttpOnlySecureCookie($refreshedToken); + $this->setResponse($this->getResponse()->withCookie($refreshHttpOnlySecureCookie)); + + return $accessToken; + } +} diff --git a/plugins/Passbolt/JwtAuthentication/src/Error/Exception/AbstractJwtAttackException.php b/plugins/Passbolt/JwtAuthentication/src/Error/Exception/AbstractJwtAttackException.php new file mode 100644 index 0000000000..e034fb5085 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Error/Exception/AbstractJwtAttackException.php @@ -0,0 +1,100 @@ +getController()->getRequest(); + if ($request->getData('user_id')) { + return $request->getData('user_id'); + } + + // Get the user ID from the refresh token in cookie + $token = $request->getCookie(RefreshTokenAbstractService::REFRESH_TOKEN_COOKIE); + if (!empty($token)) { + try { + $userId = (new RefreshTokenAuthenticationService())->getUserIdFromToken($token); + } catch (RefreshTokenNotFoundException $e) { + $userId = null; + } + } + if (!empty($userId)) { + return $userId; + } + + return $this->getController()->User->id(); + } + + /** + * @inheritDoc + */ + public function getUserEmailTemplate(): string + { + return 'Passbolt/JwtAuthentication.User/jwt_attack'; + } + + /** + * @inheritDoc + */ + public function getAdminEmailTemplate(): string + { + return 'Passbolt/JwtAuthentication.Admin/jwt_attack'; + } + + /** + * @inheritDoc + */ + public function getUserEmailSubject(): string + { + return __('Authentication security alert'); + } + + /** + * @inheritDoc + */ + public function getAdminEmailSubject(): string + { + return $this->getUserEmailSubject(); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/src/Error/Exception/AccessToken/InvalidJwtKeyPairException.php b/plugins/Passbolt/JwtAuthentication/src/Error/Exception/AccessToken/InvalidJwtKeyPairException.php new file mode 100644 index 0000000000..7a4a4fe949 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Error/Exception/AccessToken/InvalidJwtKeyPairException.php @@ -0,0 +1,27 @@ + 'removeCsrfCookieOnJwt', + ]; + } + + /** + * When a user logs in, set the session ID as the access token generated. + * + * @param \Cake\Event\EventInterface $event Event + * @return void + */ + public function removeCsrfCookieOnJwt(EventInterface $event): void + { + /** @var \Cake\Controller\Controller $controller */ + $controller = $event->getSubject(); + $response = $controller->getResponse(); + $request = $controller->getRequest(); + $isCsrfRequired = Configure::read(CsrfProtectionMiddleware::PASSBOLT_SECURITY_CSRF_PROTECTION_ACTIVE_CONFIG); + + $service = new JwtRequestDetectionService($request); + if ($service->useJwtAuthentication() && $isCsrfRequired !== true) { + $controller->setResponse( + $response->withExpiredCookie(new Cookie('csrfToken')) + ); + } + } +} diff --git a/plugins/Passbolt/JwtAuthentication/src/Event/RemoveSessionCookiesOnJwt.php b/plugins/Passbolt/JwtAuthentication/src/Event/RemoveSessionCookiesOnJwt.php new file mode 100644 index 0000000000..566f938636 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Event/RemoveSessionCookiesOnJwt.php @@ -0,0 +1,61 @@ + 'removeSessionIdCookieIfOnJwtAuth', + ]; + } + + /** + * Remove all session related cookies. + * + * @param \Cake\Event\EventInterface $event Event + * @return void + */ + public function removeSessionIdCookieIfOnJwtAuth(EventInterface $event): void + { + /** @var \Cake\Controller\Controller $controller */ + $controller = $event->getSubject(); + $response = $controller->getResponse(); + $request = $controller->getRequest(); + $service = new JwtRequestDetectionService($request); + if ($service->useJwtAuthentication()) { + $sessionCookie = Configure::read('Session.cookie', session_name()); + if (is_string($sessionCookie)) { + $response = $response->withExpiredCookie(new Cookie($sessionCookie)); + } + $controller->setResponse($response); + } + } +} diff --git a/plugins/Passbolt/JwtAuthentication/src/Event/SetSessionIdentifierOnLogin.php b/plugins/Passbolt/JwtAuthentication/src/Event/SetSessionIdentifierOnLogin.php new file mode 100644 index 0000000000..cfddba095f --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Event/SetSessionIdentifierOnLogin.php @@ -0,0 +1,57 @@ + 'setSessionIdentifierOnLogin', + ]; + } + + /** + * When a user logs in, set the session ID as the access token generated. + * + * @param \Cake\Event\EventInterface $event Event + * @return void + */ + public function setSessionIdentifierOnLogin(EventInterface $event): void + { + /** @var \Passbolt\JwtAuthentication\Authenticator\GpgJwtAuthenticator $authenticator */ + $authenticator = $event->getSubject(); + $accessToken = $event->getData('access_token'); + $this->getContainer($authenticator->getRequest()) + ->extend(SessionIdentificationServiceInterface::class) + ->setConcrete(JwtSessionIdentificationService::class) + ->addArgument($accessToken) + ->isShared(); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/src/Middleware/JwtAuthDetectionMiddleware.php b/plugins/Passbolt/JwtAuthentication/src/Middleware/JwtAuthDetectionMiddleware.php new file mode 100644 index 0000000000..cfa941ea62 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Middleware/JwtAuthDetectionMiddleware.php @@ -0,0 +1,74 @@ +useJwtAuthentication(); + + if ($usesJWTAuthentication) { + $this->services($this->getContainer($request)); + } + + /** @var \Cake\Http\ServerRequest $request */ + $request = $request->withAttribute( + JwtRequestDetectionService::IS_JWT_AUTH_REQUEST, + $usesJWTAuthentication + ); + + return $handler->handle($request); + } + + /** + * @inheritDoc + */ + public function services(ContainerInterface $container): void + { + $container + ->extend(AuthenticationServiceInterface::class) + ->setConcrete(JwtAuthenticationService::class); + + $container + ->extend(SessionIdentificationServiceInterface::class) + ->setConcrete(JwtSessionIdentificationService::class); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/src/Middleware/JwtCsrfDetectionMiddleware.php b/plugins/Passbolt/JwtAuthentication/src/Middleware/JwtCsrfDetectionMiddleware.php new file mode 100644 index 0000000000..2a5e55909b --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Middleware/JwtCsrfDetectionMiddleware.php @@ -0,0 +1,88 @@ +isBrowserRefreshEndpoint($request, $requestService)) { + $this->setCsrfProtectionActiveInConfig(true); + } elseif ($requestService->useJwtAuthentication() === true) { + $this->setCsrfProtectionActiveInConfig(false); + } + + return $handler->handle($request); + } + + /** + * The refresh endpoint requested by browser should have Csrf protection. + * Check the route, the method (no GET), and the presence of the token in Cookie. + * + * @param \Cake\Http\ServerRequest $request Request + * @param \Passbolt\JwtAuthentication\Service\Middleware\JwtRequestDetectionService $service Jwt Request Service + * @return bool + */ + protected function isBrowserRefreshEndpoint(ServerRequest $request, JwtRequestDetectionService $service): bool + { + if ($request->is('get')) { + return false; + } + $isRefreshEndPoint = $request->getAttribute('params')['_matchedRoute'] === '/auth/jwt/refresh'; + $isBrowserSolution = $service->isJwtRefreshTokenSetInCookie(); + + return $isRefreshEndPoint && $isBrowserSolution; + } + + /** + * See the + * + * @see \App\Middleware\CsrfProtectionMiddleware::skipCsrfProtection() + * @param bool $isActive value to set + * @return void + */ + protected function setCsrfProtectionActiveInConfig(bool $isActive): void + { + Configure::write(CsrfProtectionMiddleware::PASSBOLT_SECURITY_CSRF_PROTECTION_ACTIVE_CONFIG, $isActive); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/src/Middleware/JwtDestroySessionMiddleware.php b/plugins/Passbolt/JwtAuthentication/src/Middleware/JwtDestroySessionMiddleware.php new file mode 100644 index 0000000000..fb9bb0ad9f --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Middleware/JwtDestroySessionMiddleware.php @@ -0,0 +1,45 @@ +useJwtAuthentication()) { + $request->getSession()->destroy(); + } + + return $handler->handle($request); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/src/Middleware/JwtRouteFilterMiddleware.php b/plugins/Passbolt/JwtAuthentication/src/Middleware/JwtRouteFilterMiddleware.php new file mode 100644 index 0000000000..e0264f2a08 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Middleware/JwtRouteFilterMiddleware.php @@ -0,0 +1,81 @@ +throwExceptionIfRouteIsNotAllowedWithJwtAuth($request); + + return $handler->handle($request); + } + + /** + * Returns the routes that cannot be accessed if a JWT access token os placed in the header + * + * @return string[] + */ + protected function getBlockedRoutes(): array + { + return [ + '/auth/login', + '/auth/logout', + ]; + } + + /** + * Checks that the request is JWT related, and throws an exception if the required routes are not allowed. + * + * @param \Cake\Http\ServerRequest $request Request + * @return void + * @throws \Cake\Http\Exception\BadRequestException + */ + protected function throwExceptionIfRouteIsNotAllowedWithJwtAuth(ServerRequest $request): void + { + if ($request->getAttribute(JwtRequestDetectionService::IS_JWT_AUTH_REQUEST)) { + $route = $request->getAttribute('params')['_matchedRoute'] ?? null; + if (in_array($route, $this->getBlockedRoutes())) { + throw new BadRequestException( + __('The route {0} is not permitted with JWT authentication.', $route) + ); + } + } + } +} diff --git a/plugins/Passbolt/JwtAuthentication/src/Notification/Email/Redactor/JwtAuthenticationAttackEmailRedactor.php b/plugins/Passbolt/JwtAuthentication/src/Notification/Email/Redactor/JwtAuthenticationAttackEmailRedactor.php new file mode 100644 index 0000000000..7a67a820c9 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Notification/Email/Redactor/JwtAuthenticationAttackEmailRedactor.php @@ -0,0 +1,160 @@ +loadModel('Users'); + /** @var \Passbolt\JwtAuthentication\Error\Exception\AbstractJwtAttackException $exception */ + $exception = $event->getSubject(); + + $emailCollection = new EmailCollection(); + + $attackedUserId = $exception->getUserId(); + + if (!empty($attackedUserId)) { + $user = $this->Users->findFirstForEmail($attackedUserId); + if (!empty($user)) { + $this->addEmailToUser($emailCollection, $exception, $user); + $this->addEmailToAdmins($emailCollection, $exception, $user); + } + } + + return $emailCollection; + } + + /** + * @param \App\Notification\Email\EmailCollection $collection User + * @param \Passbolt\JwtAuthentication\Error\Exception\AbstractJwtAttackException $exception Exception triggering the email + * @param \App\Model\Entity\User $user User targeted by the attack + * @return void + */ + private function addEmailToUser( + EmailCollection $collection, + AbstractJwtAttackException $exception, + User $user + ): void { + $subject = (new LocaleService())->translateString( + $user->locale, + function () use ($exception) { + return $exception->getUserEmailSubject(); + } + ); + $email = new Email( + $user->username, + $subject, + [ + 'body' => [ + 'user' => $user, + 'ip' => $exception->getController()->getRequest()->clientIp(), + 'message' => $exception->getMessage(), + ], + 'title' => $subject, + ], + $exception->getUserEmailTemplate() + ); + + $collection->addEmail($email); + } + + /** + * @param \App\Notification\Email\EmailCollection $collection User + * @param \Passbolt\JwtAuthentication\Error\Exception\AbstractJwtAttackException $exception Exception triggering the email + * @param \App\Model\Entity\User $user User targeted by the attack + * @return void + */ + private function addEmailToAdmins( + EmailCollection $collection, + AbstractJwtAttackException $exception, + User $user + ): void { + $admins = $this->Users + ->findAdmins() + ->find('locale') + ->where(['Users.id !=' => $user->id]); + + foreach ($admins as $admin) { + $subject = (new LocaleService())->translateString( + $admin->locale, + function () use ($exception) { + return $exception->getAdminEmailSubject(); + } + ); + $email = new Email( + $admin->username, + $subject, + [ + 'body' => [ + 'user' => $user, + 'ip' => $exception->getController()->getRequest()->clientIp(), + 'message' => $exception->getMessage(), + ], + 'title' => $subject, + ], + $exception->getAdminEmailTemplate() + ); + $collection->addEmail($email); + } + } +} diff --git a/plugins/Passbolt/JwtAuthentication/src/Notification/Email/Redactor/JwtAuthenticationEmailRedactorPool.php b/plugins/Passbolt/JwtAuthentication/src/Notification/Email/Redactor/JwtAuthenticationEmailRedactorPool.php new file mode 100644 index 0000000000..b2a0bb68ca --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Notification/Email/Redactor/JwtAuthenticationEmailRedactorPool.php @@ -0,0 +1,48 @@ +isJwtAuthRedactorEnabled()) { + $redactors[] = new JwtAuthenticationAttackEmailRedactor(); + } + + return $redactors; + } +} diff --git a/plugins/Passbolt/JwtAuthentication/src/Plugin.php b/plugins/Passbolt/JwtAuthentication/src/Plugin.php new file mode 100644 index 0000000000..d761981d71 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Plugin.php @@ -0,0 +1,85 @@ +registerListeners($app); + } + + /** + * @inheritDoc + */ + public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue + { + $middlewareQueue + ->insertAfter(RoutingMiddleware::class, JwtAuthDetectionMiddleware::class) + ->insertAfter(JwtAuthDetectionMiddleware::class, JwtRouteFilterMiddleware::class) + ->insertBefore(AuthenticationMiddleware::class, JwtDestroySessionMiddleware::class) + ->insertBefore(CsrfProtectionMiddleware::class, JwtCsrfDetectionMiddleware::class); + + return $middlewareQueue; + } + + /** + * Register JWT related listeners. + * + * @param \Cake\Core\PluginApplicationInterface $app App + * @return void + */ + public function registerListeners(PluginApplicationInterface $app): void + { + $app->getEventManager() + ->on(new JwtAuthenticationEmailRedactorPool()) + ->on(new RemoveSessionCookiesOnJwt()) + ->on(new RemoveCsrfCookieOnJwt()) + ->on(new SetSessionIdentifierOnLogin()); + } + + /** + * @inheritDoc + */ + public function services(ContainerInterface $container): void + { + $container->add(JwtArmoredChallengeInterface::class, JwtArmoredChallengeService::class); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/src/Service/AccessToken/JwksGetService.php b/plugins/Passbolt/JwtAuthentication/src/Service/AccessToken/JwksGetService.php new file mode 100644 index 0000000000..7a2f9a45bd --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Service/AccessToken/JwksGetService.php @@ -0,0 +1,57 @@ +readKeyFileContent(); + $res = openssl_pkey_get_public($pubKey); + $detail = openssl_pkey_get_details($res); + + return [ + 'kty' => 'RSA', + 'alg' => JwtTokenCreateService::JWT_ALG, + 'use' => 'sig', + 'e' => JWT::urlsafeB64Encode($detail['rsa']['e']), + 'n' => JWT::urlsafeB64Encode($detail['rsa']['n']), + ]; + } + + /** + * @return string + * @throws \Passbolt\JwtAuthentication\Error\Exception\AccessToken\InvalidJwtKeyPairException if the public key file is not found or not readable. + */ + public function getRawPublicKey(): string + { + return $this->readKeyFileContent(); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/src/Service/AccessToken/JwtAbstractService.php b/plugins/Passbolt/JwtAuthentication/src/Service/AccessToken/JwtAbstractService.php new file mode 100644 index 0000000000..c42a87bbc5 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Service/AccessToken/JwtAbstractService.php @@ -0,0 +1,63 @@ +keyPath = $path; + + return $this; + } + + /** + * @return string Path to the secret/private key file + */ + public function getKeyPath(): string + { + return $this->keyPath; + } + + /** + * @return string Content of the secret/private key file + * @throws \Passbolt\JwtAuthentication\Error\Exception\AccessToken\InvalidJwtKeyPairException if the file is not found or not readable. + */ + public function readKeyFileContent(): string + { + if (!is_readable($this->getKeyPath())) { + $userErrorMessage = __('The key pair for JWT Authentication is not complete.'); + throw new InvalidJwtKeyPairException($userErrorMessage); + } + + return file_get_contents($this->getKeyPath()); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/src/Service/AccessToken/JwtKeyPairService.php b/plugins/Passbolt/JwtAuthentication/src/Service/AccessToken/JwtKeyPairService.php new file mode 100644 index 0000000000..3f6016276c --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Service/AccessToken/JwtKeyPairService.php @@ -0,0 +1,205 @@ +secretService = $secretService ?? new JwtTokenCreateService(); + $this->publicService = $publicService ?? new JwksGetService(); + } + + /** + * Will perform no action if key pair is found and force is set to false, + * + * @param bool $force Force the creation of a new pair. + * @return void + * @throws \Passbolt\JwtAuthentication\Error\Exception\AccessToken\InvalidJwtKeyPairException if the pair could not be created. + */ + public function createKeyPair(bool $force = false): void + { + // If pair exists but force to false, exit silently + if ($this->keyPairExists() && !$force) { + return; + } + + $config = [ + 'digest_alg' => JwtTokenCreateService::JWT_ALG, + 'private_key_bits' => $this->getKeyLength(), + 'private_key_type' => OPENSSL_KEYTYPE_RSA, + ]; + + try { + $pk = openssl_pkey_new($config); + if ($pk === false) { + throw new \Exception('The JWT private key could not be created.'); + } + $export = openssl_pkey_export_to_file($pk, $this->getSecretKeyPath()); + if ($export === false) { + throw new \Exception('The JWT private key could not be written.'); + } + $publicKey = openssl_pkey_get_details($pk)['key'] ?? false; + if ($publicKey === false) { + throw new \Exception('The JWT public key could not be extracted.'); + } + $export = file_put_contents($this->getPublicKeyPath(), $publicKey); + if ($export === false) { + throw new \Exception('The JWT public key could not be written.'); + } + } catch (\Throwable $e) { + throw new InvalidJwtKeyPairException($e->getMessage()); + } + } + + /** + * Validate the key pair validity as defined by the public and secret services. + * + * @param string|null $uuid Uuid for testing aim + * @return object + * @throws \Passbolt\JwtAuthentication\Error\Exception\AccessToken\InvalidJwtKeyPairException + */ + public function validateKeyPair(?string $uuid = null) + { + // Minimal size of the private key + $minSecretKeySize = JwtTokenCreateService::JWT_KEY_LENGTH; + $uuid = $uuid ?? UuidFactory::uuid(); + try { + if (!is_readable($this->publicService->getKeyPath())) { + throw new \Exception(__('The JWT public key could not be read or is not valid.')); + } + $publicKey = file_get_contents($this->publicService->getKeyPath()); + $details = openssl_pkey_get_details( + openssl_pkey_get_public($publicKey) + ); + + $secretKeySize = $details['bits'] ?? 0; + + if ($secretKeySize === 0) { + throw new \Exception(__('The JWT public key could not be read or is not valid.')); + } + + if ($secretKeySize < $minSecretKeySize) { + throw new \Exception(__( + 'The JWT private key should be at least {0} bytes long.', + $this->secretService::JWT_KEY_LENGTH + )); + } + + $jwt = $this->secretService->createToken($uuid, '2 seconds'); + + return JWT::decode($jwt, $publicKey, [$this->secretService::JWT_ALG]); + } catch (\Throwable $e) { + throw new InvalidJwtKeyPairException($e->getMessage()); + } + } + + /** + * @return bool if a key pair exists + */ + public function keyPairExists(): bool + { + return is_readable($this->getPublicKeyPath()) && is_readable($this->getSecretKeyPath()); + } + + /** + * @return string + */ + public function getPublicKeyPath(): string + { + return $this->publicService->getKeyPath(); + } + + /** + * @return string + */ + public function getSecretKeyPath(): string + { + return $this->secretService->getKeyPath(); + } + + /** + * @return string + */ + public function readPublicKey(): string + { + return $this->publicService->readKeyFileContent(); + } + + /** + * @return string + */ + public function readSecretKey(): string + { + return $this->secretService->readKeyFileContent(); + } + + /** + * @return string + */ + public function getCreateJwtKeysCommand(): string + { + return ROOT . DS . 'bin/cake ' . CreateJwtKeysCommand::defaultName(); + } + + /** + * @return int + */ + public function getKeyLength(): int + { + return $this->keyLength; + } + + /** + * @param int $keyLength Length of the JWT key + * @return self + */ + public function setKeyLength(int $keyLength): self + { + $this->keyLength = $keyLength; + + return $this; + } +} diff --git a/plugins/Passbolt/JwtAuthentication/src/Service/AccessToken/JwtTokenCreateService.php b/plugins/Passbolt/JwtAuthentication/src/Service/AccessToken/JwtTokenCreateService.php new file mode 100644 index 0000000000..8eadf899db --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Service/AccessToken/JwtTokenCreateService.php @@ -0,0 +1,81 @@ +readKeyFileContent(); + $payload = [ + 'iss' => Router::url('/', true), + 'sub' => $userId, + 'exp' => $this->createExpiryDate($expiration), + ]; + + return JWT::encode($payload, $privateKey, self::JWT_ALG); + } + + /** + * Create a UNIX time from a time expressed in words. + * This should return an integer. + * + * @param string|null $expirationPeriod Expiration period in words. + * @return int Unix time + */ + public function createExpiryDate(?string $expirationPeriod = null): int + { + $expiryPeriod = $expirationPeriod ?? Configure::read(JwtTokenCreateService::JWT_EXPIRY_CONFIG_KEY); + try { + return (int)(new FrozenTime('+' . $expiryPeriod))->toUnixString(); + } catch (\Throwable $e) { + throw new InternalErrorException(__( + 'The configuration {0} is not correctly set.', + JwtTokenCreateService::JWT_EXPIRY_CONFIG_KEY + )); + } + } +} diff --git a/plugins/Passbolt/JwtAuthentication/src/Service/Middleware/JwtAuthenticationService.php b/plugins/Passbolt/JwtAuthentication/src/Service/Middleware/JwtAuthenticationService.php new file mode 100644 index 0000000000..09734056d1 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Service/Middleware/JwtAuthenticationService.php @@ -0,0 +1,96 @@ +loadIdentifier('Authentication.JwtSubject', [ + 'resolver' => [ + 'className' => 'Authentication.Orm', + 'finder' => 'activeNotDeletedContainRole', + ], + ]); + } + + /** + * @inheritDoc + */ + public function authenticate(ServerRequestInterface $request): ResultInterface + { + /** @var \Cake\Http\ServerRequest $request */ + if ($this->isLoginEndpointPost($request)) { + $this->loadAuthenticator('Passbolt/JwtAuthentication.GpgJwt'); + } elseif ($this->isRefreshEndpointPost($request)) { + $this->loadAuthenticator('Passbolt/JwtAuthentication.JwtRefreshToken'); + } else { + $this->loadAuthenticator('Authentication.Jwt', [ + 'header' => self::JWT_HEADER, + 'algorithm' => JwtTokenCreateService::JWT_ALG, + 'secretKey' => file_get_contents(JwksGetService::PUBLIC_KEY_PATH), + 'returnPayload' => false, + ]); + } + + return parent::authenticate($request); + } + + /** + * Is the user attempting to log in. + * If so, the authentication with access token in header is ignored. + * + * @param \Cake\Http\ServerRequest $request Server Request + * @return bool + */ + public function isLoginEndpointPost(ServerRequest $request): bool + { + $path = str_replace('.json', '', $request->getUri()->getPath()); + $isLoginPath = ($path === '/auth/jwt/login'); + + return $isLoginPath && $request->is('POST'); + } + + /** + * Is the user attempting to get a refresh token. + * If so, the user is identified based on the refresh token provided. + * + * @param \Cake\Http\ServerRequest $request Server Request + * @return bool + */ + public function isRefreshEndpointPost(ServerRequest $request): bool + { + $path = str_replace('.json', '', $request->getUri()->getPath()); + $isRefreshEndpoint = ($path === '/auth/jwt/refresh'); + + return $isRefreshEndpoint && $request->is('POST'); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/src/Service/Middleware/JwtRequestDetectionService.php b/plugins/Passbolt/JwtAuthentication/src/Service/Middleware/JwtRequestDetectionService.php new file mode 100644 index 0000000000..2651b37fae --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Service/Middleware/JwtRequestDetectionService.php @@ -0,0 +1,145 @@ +request = $request; + } + + /** + * Passes an attribute to the request to inform other layers using the request + * that the request is either: + * - with JWT Authentication + * - with a refresh token is found in the cookies + * - the route matches one of the JwtAuthentication plugin + * + * @return bool + */ + public function useJwtAuthentication(): bool + { + // Already cached + if ($this->request->getAttribute(self::IS_JWT_AUTH_REQUEST)) { + return true; + } + + if (!$this->isFeaturePluginEnabled('JwtAuthentication')) { + return false; + } + + // JWT cannot be used because of configuration issue + if (!$this->isJwtServerKeyUsable()) { + return false; + } + + // Uses if it's a JWT login route + if ($this->isJWTAuthRoute()) { + return true; + } + + // Uses if the access token is set in header or if a refresh_token is set in the cookies + if ($this->isJwtAccessTokenSetInHeader() || $this->isJwtRefreshTokenSetInCookie()) { + return true; + } + + return false; + } + + /** + * @return bool + */ + public function isJwtServerKeyUsable() + { + try { + (new JwksGetService())->getPublicKey(); + } catch (InvalidJwtKeyPairException $e) { + return false; + } + + return true; + } + + /** + * @return bool + */ + public function isJWTAuthRoute() + { + $params = $this->request->getAttribute('params', null); + if (isset($params) && !$this->request->is('get')) { + $plugin = Hash::get($params, 'plugin'); + if ($plugin === 'Passbolt/JwtAuthentication') { + return true; + } + } + + return false; + } + + /** + * Checks if the access token is set in header. + * + * @return bool + */ + public function isJwtAccessTokenSetInHeader(): bool + { + return !empty($this->request->getHeaderLine(JwtAuthenticationService::JWT_HEADER)); + } + + /** + * Checks if a refresh token is set as cookies. + * + * @return bool + */ + public function isJwtRefreshTokenSetInCookie(): bool + { + return !empty($this->request->getCookie(RefreshTokenAbstractService::REFRESH_TOKEN_COOKIE)); + } + + /** + * CSRF token is disabled (not necessary) anytime an access token is set in header. + * + * @return bool + */ + public function skipCsrfProtectionForJwtRequests(): bool + { + return $this->isJwtAccessTokenSetInHeader(); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/src/Service/RefreshToken/RefreshTokenAbstractService.php b/plugins/Passbolt/JwtAuthentication/src/Service/RefreshToken/RefreshTokenAbstractService.php new file mode 100644 index 0000000000..adaa4da9d4 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Service/RefreshToken/RefreshTokenAbstractService.php @@ -0,0 +1,194 @@ +loadModel('AuthenticationTokens'); + } + + /** + * @param \App\Model\Entity\AuthenticationToken $token token + * @return \Cake\Http\Cookie\Cookie + */ + public function createHttpOnlySecureCookie(AuthenticationToken $token): Cookie + { + $cookie = new Cookie(self::REFRESH_TOKEN_COOKIE, $token->token); + $expiry = new FrozenTime( + '+' . Configure::read(self::REFRESH_TOKEN_EXPIRY_CONFIG_KEY) + ); + + return $cookie + ->withSecure(true) + ->withHttpOnly(true) + ->withExpiry($expiry); + } + + /** + * Find the token corresponding to the user and refresh token. + * If no user id is provided (e.g. if only the token is provided in cookie), + * fetch the user id. + * + * Throw a security error if this token is inactive. + * Set it to inactive. + * + * @param \App\Model\Entity\AuthenticationToken $refreshToken User ID. + * @return \App\Model\Entity\AuthenticationToken + * @throws \Passbolt\JwtAuthentication\Error\Exception\RefreshToken\RefreshTokenNotFoundException if the token is not found + * @throws \Passbolt\JwtAuthentication\Error\Exception\RefreshToken\ConsumedRefreshTokenAccessException if the token was already consumed + * @throws \Passbolt\JwtAuthentication\Error\Exception\RefreshToken\ExpiredRefreshTokenAccessException if the token is expired + */ + protected function consumeToken(AuthenticationToken $refreshToken): AuthenticationToken + { + $this->throwSecurityExceptionsOnInvalidRefreshToken($refreshToken); + $refreshToken->set('active', false); + + return $this->AuthenticationTokens->saveOrFail($refreshToken); + } + + /** + * @param mixed $token Refresh token to be validated. + * @return void + * @throws \InvalidArgumentException if the $token is not valid + */ + public function validateRefreshToken($token): void + { + if (!Validation::uuid($token)) { + throw new \InvalidArgumentException(__('The refresh token should be a valid UUID.')); + } + } + + /** + * @param mixed $userId User id to be validated. + * @return void + * @throws \InvalidArgumentException if the $id is not valid + */ + public function validateUserId($userId): void + { + if (!Validation::uuid($userId)) { + throw new \InvalidArgumentException(__('The user ID should be a valid UUID.')); + } + } + + /** + * @param ?string $token Refresh token + * @return \Cake\ORM\Query + */ + public function queryRefreshToken(?string $token): Query + { + $this->validateRefreshToken($token); + + return $this->AuthenticationTokens->find()->where([ + 'token' => $token, + 'type' => AuthenticationToken::TYPE_REFRESH_TOKEN, + ]); + } + + /** + * @param string $token Refresh token + * @param string $userId User ID + * @return \Cake\ORM\Query + * @throws \InvalidArgumentException if the $id is not valid + */ + public function queryRefreshTokenWithUserId(string $token, string $userId): Query + { + $this->validateUserId($userId); + + return $this + ->queryRefreshToken($token) + ->where([$this->AuthenticationTokens->aliasField('user_id') => $userId]); + } + + /** + * @param string $token token to retrieve + * @param string $userId user ID + * @return \App\Model\Entity\AuthenticationToken Refresh token + * @throws \Passbolt\JwtAuthentication\Error\Exception\RefreshToken\RefreshTokenNotFoundException if the token is not found + * @throws \Passbolt\JwtAuthentication\Error\Exception\RefreshToken\ConsumedRefreshTokenAccessException if the token was already consumed + * @throws \Passbolt\JwtAuthentication\Error\Exception\RefreshToken\ExpiredRefreshTokenAccessException if the token is expired + */ + public function getActiveRefreshToken(string $token, string $userId): AuthenticationToken + { + /** @var \App\Model\Entity\AuthenticationToken|null $refreshToken */ + $refreshToken = $this->queryRefreshTokenWithUserId($token, $userId)->contain('Users')->first(); + + if ($refreshToken === null) { + throw new RefreshTokenNotFoundException(); + } + + // Check if the user was not deleted or deactivated since the refresh token was issued. + $user = $refreshToken->user; + if ($user->deleted) { + throw new UserDeletedException(); + } elseif (!$user->active) { + throw new UserDeactivatedException(); + } + + $this->throwSecurityExceptionsOnInvalidRefreshToken($refreshToken); + + return $refreshToken; + } + + /** + * @param \App\Model\Entity\AuthenticationToken $refreshToken Refresh token + * @return void + * @throws \Passbolt\JwtAuthentication\Error\Exception\RefreshToken\ConsumedRefreshTokenAccessException if the token was already consumed + * @throws \Passbolt\JwtAuthentication\Error\Exception\RefreshToken\ExpiredRefreshTokenAccessException if the token is expired + */ + public function throwSecurityExceptionsOnInvalidRefreshToken(AuthenticationToken $refreshToken): void + { + if ($refreshToken->isNotActive()) { + throw new ConsumedRefreshTokenAccessException( + __('The refresh token provided was already used.') + ); + } + + if ($refreshToken->isExpired()) { + throw new ExpiredRefreshTokenAccessException( + __('Expired refresh token provided.') + ); + } + } +} diff --git a/plugins/Passbolt/JwtAuthentication/src/Service/RefreshToken/RefreshTokenAuthenticationService.php b/plugins/Passbolt/JwtAuthentication/src/Service/RefreshToken/RefreshTokenAuthenticationService.php new file mode 100644 index 0000000000..94f055c0a2 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Service/RefreshToken/RefreshTokenAuthenticationService.php @@ -0,0 +1,45 @@ +validateRefreshToken($token); + + try { + return $this->queryRefreshToken($token)->firstOrFail()->get('user_id'); + } catch (RecordNotFoundException $e) { + throw new RefreshTokenNotFoundException(); + } + } +} diff --git a/plugins/Passbolt/JwtAuthentication/src/Service/RefreshToken/RefreshTokenCreateService.php b/plugins/Passbolt/JwtAuthentication/src/Service/RefreshToken/RefreshTokenCreateService.php new file mode 100644 index 0000000000..a1f36128fc --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Service/RefreshToken/RefreshTokenCreateService.php @@ -0,0 +1,86 @@ +getTokenData($accessToken); + $refreshToken = $this->AuthenticationTokens->generate( + $userId, + AuthenticationToken::TYPE_REFRESH_TOKEN, + null, + $data + ); + $this->dispatchRefreshTokenCreatedEvent($request, $refreshToken, $accessToken); + + return $refreshToken; + } + + /** + * Dispatch an event when a new refresh token has been created. + * + * @param \Cake\Http\ServerRequest $request Server request. + * @param \App\Model\Entity\AuthenticationToken $refreshToken Refresh token issued + * @param string $accessToken Access token associated to the refresh token issued + * @return void + */ + protected function dispatchRefreshTokenCreatedEvent( + ServerRequest $request, + AuthenticationToken $refreshToken, + string $accessToken + ): void { + $event = new Event(self::REFRESH_TOKEN_CREATED_EVENT, $refreshToken, [ + self::ACCESS_TOKEN_DATA_KEY => $accessToken, + self::REQUEST_DATA_KEY => $request, + ]); + $this->AuthenticationTokens->getEventManager()->dispatch($event); + } + + /** + * Use the logic in the authentication token entity + * to hash the session. + * + * @param string $accessToken Access token to hash and store as session ID + * @return array + */ + protected function getTokenData(string $accessToken): array + { + $token = new AuthenticationToken(); + $token->hashAndSetSessionId($accessToken); + + return $token->getJsonDecodedData(); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/src/Service/RefreshToken/RefreshTokenLogoutService.php b/plugins/Passbolt/JwtAuthentication/src/Service/RefreshToken/RefreshTokenLogoutService.php new file mode 100644 index 0000000000..1c806f02b1 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Service/RefreshToken/RefreshTokenLogoutService.php @@ -0,0 +1,67 @@ +getRefreshTokenInRequest($request); + + // If no refresh token is specified in the request, deactivate all the user's refresh token + if (!is_string($token)) { + return $this->AuthenticationTokens->updateAll( + ['active' => false], + [ + $this->AuthenticationTokens->aliasField('user_id') => $userId, + $this->AuthenticationTokens->aliasField('type') => AuthenticationToken::TYPE_REFRESH_TOKEN, + ] + ); + } + + $token = $this->getActiveRefreshToken($token, $userId); + $this->consumeToken($token); + + return 1; + } + + /** + * Read the refresh token in the payload or in the cookies. + * + * @param \Cake\Http\ServerRequest $request Server request + * @return string|null + */ + protected function getRefreshTokenInRequest(ServerRequest $request): ?string + { + /** @var string|null $refreshToken */ + $refreshToken = $request->getData(self::REFRESH_TOKEN_DATA_KEY); + if ($refreshToken === null) { + /** @var string|null $refreshToken */ + $refreshToken = $request->getCookie(self::REFRESH_TOKEN_COOKIE); + } + + return $refreshToken; + } +} diff --git a/plugins/Passbolt/JwtAuthentication/src/Service/RefreshToken/RefreshTokenRenewalService.php b/plugins/Passbolt/JwtAuthentication/src/Service/RefreshToken/RefreshTokenRenewalService.php new file mode 100644 index 0000000000..1787939b9f --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Service/RefreshToken/RefreshTokenRenewalService.php @@ -0,0 +1,48 @@ +consumeToken($oldRefreshToken); + + return (new RefreshTokenCreateService())->createToken($request, $oldRefreshToken->user_id, $accessToken); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/src/Service/VerifyToken/VerifyTokenCreateService.php b/plugins/Passbolt/JwtAuthentication/src/Service/VerifyToken/VerifyTokenCreateService.php new file mode 100644 index 0000000000..d44772625a --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Service/VerifyToken/VerifyTokenCreateService.php @@ -0,0 +1,69 @@ +loadModel('AuthenticationTokens'); + } + + /** + * @param string $verifyToken Verify token + * @param string $userId user UUID + * @return \App\Model\Entity\AuthenticationToken + * @throws \App\Error\Exception\ValidationException if the token could not be generated. + */ + public function createToken(string $verifyToken, string $userId): AuthenticationToken + { + $this->cleanupAllExpiredTokens(); + + return $this->AuthenticationTokens->generate( + $userId, + AuthenticationToken::TYPE_VERIFY_TOKEN, + $verifyToken + ); + } + + /** + * @return void + */ + protected function cleanupAllExpiredTokens(): void + { + $minTokenCreationTime = FrozenTime::now() + ->modify('-' . Configure::read(VerifyTokenValidationService::VERIFY_TOKEN_EXPIRY_CONFIG_KEY)); + + $this->AuthenticationTokens->deleteAll([ + $this->AuthenticationTokens->aliasField('type') => AuthenticationToken::TYPE_VERIFY_TOKEN, + $this->AuthenticationTokens->aliasField('created') . ' <' => $minTokenCreationTime, + ]); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/src/Service/VerifyToken/VerifyTokenValidationService.php b/plugins/Passbolt/JwtAuthentication/src/Service/VerifyToken/VerifyTokenValidationService.php new file mode 100644 index 0000000000..28d73dcac4 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/src/Service/VerifyToken/VerifyTokenValidationService.php @@ -0,0 +1,134 @@ +validateTokenExpiry($verifyTokenExpiry); + $this->validateFormat($verifyToken); + $this->validateUserId($userId); + $this->validateNonce($verifyToken, $userId); + } + + /** + * Assert that the token expiry is valid and not set too far in the future. + * + * @param mixed $verifyTokenExpiry unix timestamp + * @return void + * @throws \Passbolt\JwtAuthentication\Error\Exception\VerifyToken\InvalidVerifyTokenException if the token is expired. + */ + protected function validateTokenExpiry($verifyTokenExpiry): void + { + $maxTokenExpiry = FrozenTime::now() + ->modify('+' . Configure::read(self::VERIFY_TOKEN_EXPIRY_CONFIG_KEY)) + ->toUnixString(); + if ( + !isset($verifyTokenExpiry) || + !is_numeric($verifyTokenExpiry) || + $verifyTokenExpiry > $maxTokenExpiry + ) { + throw new InvalidVerifyTokenException(__('Invalid verify token expiry.')); + } + if ($verifyTokenExpiry < time()) { + throw new ExpiredVerifyTokenAccessException( + __('Attempt to access an expired verify token.') + ); + } + } + + /** + * Assert verify token is a UUID + * + * @param mixed $verifyToken token + * @return void + * @throws \Passbolt\JwtAuthentication\Error\Exception\VerifyToken\InvalidVerifyTokenException if the format is not valid. + * @throws \Cake\ORM\Exception\PersistenceFailedException + */ + protected function validateFormat($verifyToken): void + { + if ( + !isset($verifyToken) || + !is_string($verifyToken) || + !Validation::uuid($verifyToken) + ) { + throw new InvalidVerifyTokenException(__('Invalid verify token format.')); + } + } + + /** + * Assert verify token is a UUID + * + * @param string $userId User ID + * @return void + * @throws \Passbolt\JwtAuthentication\Error\Exception\VerifyToken\InvalidVerifyTokenException if the user ID is not a UUID. + * @throws \Cake\ORM\Exception\PersistenceFailedException + */ + protected function validateUserId(string $userId): void + { + if (!Validation::uuid($userId)) { + throw new InvalidVerifyTokenException(__('Invalid user ID format.')); + } + } + + /** + * Check that this token - userId pair does not exist. + * + * @param string $verifyToken Verify Token + * @param string $userId User ID + * @return void + * @throws \Cake\Http\Exception\BadRequestException if the token has already been used. + */ + protected function validateNonce(string $verifyToken, string $userId): void + { + $AuthenticationTokens = TableRegistry::getTableLocator()->get('AuthenticationTokens'); + $existingTokenWithSameValue = $AuthenticationTokens + ->find() + ->where([ + $AuthenticationTokens->aliasField('token') => $verifyToken, + $AuthenticationTokens->aliasField('user_id') => $userId, + $AuthenticationTokens->aliasField('type') => AuthenticationToken::TYPE_VERIFY_TOKEN, + ]) + ->count(); + + if ($existingTokenWithSameValue !== 0) { + throw new ConsumedVerifyTokenAccessException(__('Verify token has been already used in the past.')); + } + } +} diff --git a/plugins/Passbolt/JwtAuthentication/templates/email/html/Admin/jwt_attack.php b/plugins/Passbolt/JwtAuthentication/templates/email/html/Admin/jwt_attack.php new file mode 100644 index 0000000000..0e94fd0fb9 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/templates/email/html/Admin/jwt_attack.php @@ -0,0 +1,39 @@ +element('Email/module/avatar',[ + 'url' => AvatarHelper::getAvatarUrl($user['profile']['avatar']), + 'text' => $this->element('Email/module/avatar_text', [ + 'user' => $user, + 'datetime' => FrozenTime::now(), + 'text' => __('Security warning!') + ]) +]); + +$text = '

' . __('Security warning!') . '


'; +$text = '

' . $message . '


'; +$text .= __('An unknown user with IP: {0} attempted to identify as {1}.', $ip, $user['username']); +$text .= ' ' . __('This is a potential security issue.'); +$text .= ' ' . __('Please investigate!'); +echo $this->element('Email/module/text', [ + 'text' => $text +]); diff --git a/plugins/Passbolt/JwtAuthentication/templates/email/html/User/jwt_attack.php b/plugins/Passbolt/JwtAuthentication/templates/email/html/User/jwt_attack.php new file mode 100644 index 0000000000..987e398115 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/templates/email/html/User/jwt_attack.php @@ -0,0 +1,38 @@ +element('Email/module/avatar',[ + 'url' => AvatarHelper::getAvatarUrl($user['profile']['avatar']), + 'text' => $this->element('Email/module/avatar_text', [ + 'user' => $user, + 'datetime' => FrozenTime::now(), + 'text' => __('Security warning!') + ]) +]); + +$text = '

' . __('Security warning!') . '


'; +$text = '

' . $message . '


'; +$text .= __('An unknown user with IP: {0} attempted to steal your login data.', $ip); +$text .= ' ' . __('Please get in touch with one of your administrators.'); +echo $this->element('Email/module/text', [ + 'text' => $text +]); diff --git a/plugins/Passbolt/JwtAuthentication/tests/Factory/RefreshTokenAuthenticationTokenFactory.php b/plugins/Passbolt/JwtAuthentication/tests/Factory/RefreshTokenAuthenticationTokenFactory.php new file mode 100644 index 0000000000..dde5467874 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/tests/Factory/RefreshTokenAuthenticationTokenFactory.php @@ -0,0 +1,41 @@ +type(AuthenticationToken::TYPE_REFRESH_TOKEN); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/tests/TestCase/Authenticator/GpgJwtAuthenticatorTest.php b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Authenticator/GpgJwtAuthenticatorTest.php new file mode 100644 index 0000000000..f2710955ec --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Authenticator/GpgJwtAuthenticatorTest.php @@ -0,0 +1,430 @@ +sut = new GpgJwtAuthenticator(new TokenIdentifier()); + (new JwtKeyPairService())->createKeyPair(); + EventManager::instance()->setEventList(new EventList()); + } + + public function testGpgJwtAuthenticatorAuthenticateError_NoData() + { + $this->gpgSetup(); + $request = new ServerRequest(); + $result = $this->sut->authenticate($request); + $this->assertEquals($result->getStatus(), Result::FAILURE_CREDENTIALS_INVALID); + } + + public function testGpgJwtAuthenticatorAuthenticateError_InvalidUserId() + { + $this->gpgSetup(); + $request = new ServerRequest(); + $request = $request->withData('user_id', 'nope'); + $result = $this->sut->authenticate($request); + $this->assertEquals($result->getStatus(), Result::FAILURE_CREDENTIALS_INVALID); + } + + public function testGpgJwtAuthenticatorAuthenticateError_NotFoundUser() + { + $this->gpgSetup(); + $request = new ServerRequest(); + $request = $request->withData('user_id', UuidFactory::uuid()); + $result = $this->sut->authenticate($request); + $this->assertEquals($result->getStatus(), Result::FAILURE_IDENTITY_NOT_FOUND); + } + + public function testGpgJwtAuthenticatorAuthenticateError_NoChallenge() + { + $this->gpgSetup(); + $request = new ServerRequest(); + $request = $request->withData('user_id', UuidFactory::uuid('user.id.ada')); + $result = $this->sut->authenticate($request); + $this->assertEquals($result->getStatus(), Result::FAILURE_CREDENTIALS_INVALID); + } + + public function testGpgJwtAuthenticatorAuthenticateError_NotOpenPGPChallenge() + { + $this->gpgSetup(); + $request = new ServerRequest(); + + $request = $request->withData('user_id', UuidFactory::uuid('user.id.ada')); + $request = $request->withData('challenge', 'nope'); + $result = $this->sut->authenticate($request); + + $this->assertEquals($result->getStatus(), Result::FAILURE_CREDENTIALS_INVALID); + } + + public function testGpgJwtAuthenticatorAuthenticateError_WrongSignatureChallenge() + { + $this->gpgSetup(); + $request = new ServerRequest(); + + $this->gpg->setEncryptKeyFromFingerprint($this->serverKeyId); + $this->gpg->setSignKeyFromFingerprint($this->adaKeyId, ''); + $msg = $this->gpg->encrypt('no sig'); + + $request = $request->withData('user_id', UuidFactory::uuid('user.id.ada')); + $request = $request->withData('challenge', $msg); + $result = $this->sut->authenticate($request); + + $this->assertEquals($result->getStatus(), Result::FAILURE_CREDENTIALS_INVALID); + } + + public function testGpgJwtAuthenticatorAuthenticateError_WrongChallengeFormat() + { + $this->gpgSetup(); + $request = new ServerRequest(); + + $this->gpg->setEncryptKeyFromFingerprint($this->serverKeyId); + $this->gpg->setSignKeyFromFingerprint($this->adaKeyId, ''); + $msg = $this->gpg->encryptSign('wrong format'); + + $request = $request->withData('user_id', UuidFactory::uuid('user.id.ada')); + $request = $request->withData('challenge', $msg); + $result = $this->sut->authenticate($request); + + $this->assertEquals($result->getStatus(), Result::FAILURE_CREDENTIALS_INVALID); + } + + public function testGpgJwtAuthenticatorAuthenticateError_WrongVersion() + { + $this->gpgSetup(); + $request = new ServerRequest(); + + $this->gpg->setEncryptKeyFromFingerprint($this->serverKeyId); + $this->gpg->setSignKeyFromFingerprint($this->adaKeyId, ''); + $challenge = [ + 'version' => '2.0.0', + 'domain' => Router::url('/', true), + 'verify_token' => UuidFactory::uuid(), + 'verify_token_expiry' => time() + 60, + ]; + $msg = $this->gpg->encryptSign(json_encode($challenge)); + + $request = $request->withData('user_id', UuidFactory::uuid('user.id.ada')); + $request = $request->withData('challenge', $msg); + $result = $this->sut->authenticate($request); + + $this->assertEquals($result->getStatus(), Result::FAILURE_CREDENTIALS_INVALID); + } + + public function testGpgJwtAuthenticatorAuthenticateError_WrongDomain() + { + $this->gpgSetup(); + $request = new ServerRequest(); + + $this->gpg->setEncryptKeyFromFingerprint($this->serverKeyId); + $this->gpg->setSignKeyFromFingerprint($this->adaKeyId, ''); + $challenge = [ + 'version' => '1.0.0', + 'domain' => 'https://cloud.passbolt.com/test', + 'verify_token' => UuidFactory::uuid(), + 'verify_token_expiry' => time() + 60, + ]; + $msg = $this->gpg->encryptSign(json_encode($challenge)); + + $request = $request->withData('user_id', UuidFactory::uuid('user.id.ada')); + $request = $request->withData('challenge', $msg); + $result = $this->sut->authenticate($request); + + $this->assertEquals($result->getStatus(), Result::FAILURE_CREDENTIALS_INVALID); + } + + public function testGpgJwtAuthenticatorAuthenticateError_ExpiredToken() + { + $this->gpgSetup(); + $request = new ServerRequest(); + + $this->gpg->setEncryptKeyFromFingerprint($this->serverKeyId); + $this->gpg->setSignKeyFromFingerprint($this->adaKeyId, ''); + $challenge = [ + 'version' => '1.0.0', + 'domain' => Router::url('/', true), + 'verify_token' => UuidFactory::uuid(), + 'verify_token_expiry' => time() - 60, + ]; + $msg = $this->gpg->encryptSign(json_encode($challenge)); + + $request = $request->withData('user_id', UuidFactory::uuid('user.id.ada')); + $request = $request->withData('challenge', $msg); + $result = $this->sut->authenticate($request); + + $this->assertEquals($result->getStatus(), Result::FAILURE_CREDENTIALS_INVALID); + } + + public function testGpgJwtAuthenticatorAuthenticateError_WrongVerifyToken() + { + $this->gpgSetup(); + $request = new ServerRequest(); + + $this->gpg->setEncryptKeyFromFingerprint($this->serverKeyId); + $this->gpg->setSignKeyFromFingerprint($this->adaKeyId, ''); + $challenge = [ + 'version' => '1.0.0', + 'domain' => Router::url('/', true), + 'verify_token' => 'nope', + 'verify_token_expiry' => time() + 60, + ]; + $msg = $this->gpg->encryptSign(json_encode($challenge)); + + $request = $request->withData('user_id', UuidFactory::uuid('user.id.ada')); + $request = $request->withData('challenge', $msg); + $result = $this->sut->authenticate($request); + + $this->assertEquals($result->getStatus(), Result::FAILURE_CREDENTIALS_INVALID); + } + + public function testGpgJwtAuthenticatorAuthenticateSuccess() + { + $this->gpgSetup(); + $request = new ServerRequest(); + + $this->gpg->setEncryptKeyFromFingerprint($this->serverKeyId); + $this->gpg->setSignKeyFromFingerprint($this->adaKeyId, ''); + $userChallenge = [ + 'version' => '1.0.0', + 'domain' => Router::url('/', true), + 'verify_token' => UuidFactory::uuid(), + 'verify_token_expiry' => time() + 60, + ]; + $msg = $this->gpg->encryptSign(json_encode($userChallenge)); + + $container = new Container(); + $container->add(JwtArmoredChallengeInterface::class, JwtArmoredChallengeService::class); + $request = $request + ->withData('user_id', UuidFactory::uuid('user.id.ada')) + ->withData('challenge', $msg) + ->withAttribute(ContainerInjectorMiddleware::CONTAINER_ATTRIBUTE, $container); + $result = $this->sut->authenticate($request); + + $this->assertEquals(Result::SUCCESS, $result->getStatus()); + $data = $result->getData(); + $this->assertNotEmpty($data['user']); + $this->assertNotEmpty($data['challenge']); + + // Assert user is part of results + $user = $data['user']; + $this->assertEquals(UuidFactory::uuid('user.id.ada'), $user['id']); + $this->assertEquals('ada@passbolt.com', $user['username']); + $this->assertEquals('Lovelace', $user['profile']['last_name']); + $this->assertEquals('03F60E958F4CB29723ACDF761353B5B15D9B054F', $user['gpgkey']['fingerprint']); + + // Assert challenge is signed and can be decrypted + $serverChallenge = $data['challenge']; + $this->gpg->clearDecryptKeys(); + $this->gpg->setVerifyKeyFromFingerprint(Configure::read('passbolt.gpg.serverKey.fingerprint')); + $this->gpg->verify($serverChallenge); + $this->gpg->setDecryptKeyFromFingerprint($user['gpgkey']['fingerprint'], ''); + $decryptedChallenge = $this->gpg->decrypt($serverChallenge); + + // Assert challenge content contains required info + $this->assertNotEmpty($decryptedChallenge); + $deserializedChallenge = json_decode($decryptedChallenge); + $this->assertTextEquals(GpgJwtAuthenticator::PROTOCOL_VERSION, $deserializedChallenge->version); + $this->assertTextEquals(Router::url('/', true), $deserializedChallenge->domain); + $this->assertEquals($userChallenge['verify_token'], $deserializedChallenge->verify_token); + $this->assertNotEmpty($deserializedChallenge->access_token); + $this->assertNotEmpty($deserializedChallenge->refresh_token); + $this->assertEventFired(GpgJwtAuthenticator::JWT_AUTHENTICATION_AFTER_IDENTIFY); + } + + // ======================================================================== + // Assert checks + // ======================================================================== + // Server fingerprint + + public function testGpgJwtAuthenticatorAssertServerFingerprint_EmptyError() + { + $this->expectException(InternalErrorException::class); + $this->sut->assertServerFingerprint(null); + } + + public function testGpgJwtAuthenticatorAssertServerFingerprint_NotFingerprintError() + { + $this->expectException(InternalErrorException::class); + $this->sut->assertServerFingerprint('nope'); + } + + public function testGpgJwtAuthenticatorAssertServerFingerprint_Success() + { + $this->sut->assertServerFingerprint('E8FE388E385841B382B674ADB02DADCD9565E1B8'); + $this->assertTrue(true); + } + + // Server passphrase + + public function testGpgJwtAuthenticatorAssertServerPassphrase_EmptyError() + { + $this->expectException(InternalErrorException::class); + $this->sut->assertServerPassphrase(null); + } + + public function testGpgJwtAuthenticatorAssertServerPassphrase_NotStringError() + { + $this->expectException(InternalErrorException::class); + $this->sut->assertServerPassphrase([]); + } + + public function testGpgJwtAuthenticatorAssertServerPassphrase_Success() + { + $this->sut->assertServerPassphrase('cofveve'); + $this->assertTrue(true); + } + + // User id + + public function testGpgJwtAuthenticatorAssertUserId_EmptyError() + { + $this->expectException(BadRequestException::class); + $this->sut->assertUserId(null); + } + + public function testGpgJwtAuthenticatorAssertUserId_NotStringError() + { + $this->expectException(BadRequestException::class); + $this->sut->assertUserId([]); + } + + public function testGpgJwtAuthenticatorAssertServerPassphrase_NotUuid() + { + $this->expectException(BadRequestException::class); + $this->sut->assertUserId(''); + } + + public function testGpgJwtAuthenticatorAssertServerPassphrase_NotUuid2() + { + $this->expectException(BadRequestException::class); + $this->sut->assertUserId('test'); + } + + public function testGpgJwtAuthenticatorAssertUserId_Success() + { + $this->sut->assertUserId(UuidFactory::uuid()); + $this->assertTrue(true); + } + + // Armored challenge + + public function testGpgJwtAuthenticatorAssertArmoredChallenge_EmptyError() + { + $this->expectException(BadRequestException::class); + $this->sut->assertArmoredChallenge(null); + } + + public function testGpgJwtAuthenticatorAssertArmoredChallenge_NotStringError() + { + $this->expectException(BadRequestException::class); + $this->sut->assertArmoredChallenge([]); + } + + public function testGpgJwtAuthenticatorAssertArmoredChallenge_NotOpenpgpMessageError() + { + $this->expectException(BadRequestException::class); + $this->sut->setOpenPGPBackend(); + $this->sut->assertArmoredChallenge('test'); + } + + // Protocol version + + public function testGpgJwtAuthenticatorAssertVersion_EmptyError() + { + $this->expectException(\Exception::class); + $this->sut->assertVersion(null); + } + + public function testGpgJwtAuthenticatorAssertVersion_NotStringError() + { + $this->expectException(\Exception::class); + $this->sut->assertVersion([]); + } + + public function testGpgJwtAuthenticatorAssertVersion_NotSemverError() + { + $this->expectException(\Exception::class); + $this->sut->assertVersion('test'); + } + + public function testGpgJwtAuthenticatorAssertVersion_Success() + { + $this->sut->assertVersion(GpgJwtAuthenticator::PROTOCOL_VERSION); + $this->assertTrue(true); + } + + // Domain + + public function testGpgJwtAuthenticatorAssertDomain_EmptyError() + { + $this->expectException(\Exception::class); + $this->sut->assertDomain(null); + } + + public function testGpgJwtAuthenticatorAssertDomain_NotStringError() + { + $this->expectException(\Exception::class); + $this->sut->assertDomain([]); + } + + public function testGpgJwtAuthenticatorAssertDomain_NotDomainError() + { + $this->expectException(\Exception::class); + $this->sut->assertDomain('nope'); + } + + public function testGpgJwtAuthenticatorAssertDomain_WrongDomainError() + { + $this->expectException(\Exception::class); + $this->sut->assertDomain('https://www.google.com'); + } + + public function testGpgJwtAuthenticatorAssertDomain_Success() + { + $this->sut->assertDomain(Router::url('/', true)); + $this->assertTrue(true); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/tests/TestCase/Authenticator/JwtAuthenticatorTest.php b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Authenticator/JwtAuthenticatorTest.php new file mode 100644 index 0000000000..055b2e7ce5 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Authenticator/JwtAuthenticatorTest.php @@ -0,0 +1,62 @@ +persist(); + + $url = Configure::read('App.fullBaseUrl'); + $userId = $attackedUser->id; + $publicKey = (new JwksGetService())->getRawPublicKey(); + + $head = '{"typ":"JWT","alg":"HS256"}'; + $t = Time::now()->addHours(3)->toUnixString(); + $body = '{"iss":"' . $url . '","sub":"' . $userId . '","exp":' . $t . '}'; + $msg = $this->urlsafeB64Encode($head) . '.' . $this->urlsafeB64Encode($body); + $hash = \hash_hmac('SHA256', $msg, $publicKey, true); + $hackedToken = $msg . '.' . $this->urlsafeB64Encode($hash) . "\n"; + + $this->setJwtTokenInHeader($hackedToken); + $this->getJson('/auth/is-authenticated.json'); + $this->assertResponseError(); + } + + public function urlsafeB64Encode($input) + { + return str_replace('=', '', \strtr(\base64_encode($input), '+/', '-_')); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/tests/TestCase/Authenticator/JwtRefreshTokenAuthenticatorTest.php b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Authenticator/JwtRefreshTokenAuthenticatorTest.php new file mode 100644 index 0000000000..95dcb1e4fd --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Authenticator/JwtRefreshTokenAuthenticatorTest.php @@ -0,0 +1,260 @@ +authenticator = new JwtRefreshTokenAuthenticator( + $this->createMock(IdentifierInterface::class) + ); + } + + public function tearDown(): void + { + parent::tearDown(); + unset($this->authenticator); + } + + public function testJwtRefreshTokenAuthenticator_Success_With_Cookie() + { + $refreshToken = RefreshTokenAuthenticationTokenFactory::make() + ->with('Users', UserFactory::make()->user()) + ->active() + ->persist(); + + $cookies = (new CookieCollection())->add( + new Cookie(RefreshTokenAbstractService::REFRESH_TOKEN_COOKIE, $refreshToken->token) + ); + $container = new Container(); + $container->add(SessionIdentificationServiceInterface::class, SessionIdentificationService::class); + + $request = (new ServerRequest()) + ->withCookieCollection($cookies) + ->withAttribute('container', $container); + + $result = $this->authenticator->authenticate($request); + + $this->assertSuccess($request, $container, $refreshToken, $result); + } + + public function testJwtRefreshTokenAuthenticator_Success_With_Payload() + { + $refreshToken = RefreshTokenAuthenticationTokenFactory::make() + ->with('Users', UserFactory::make()->user()) + ->active() + ->persist(); + + $container = new Container(); + $container->add(SessionIdentificationServiceInterface::class, SessionIdentificationService::class); + + $request = (new ServerRequest()) + ->withData(RefreshTokenAbstractService::REFRESH_TOKEN_DATA_KEY, $refreshToken->token) + ->withData('user_id', $refreshToken->user_id) + ->withAttribute('container', $container); + + $result = $this->authenticator->authenticate($request); + + $this->assertSuccess($request, $container, $refreshToken, $result); + } + + public function testJwtRefreshTokenAuthenticator_Empty_Request_Should_Return_Bad_Request() + { + $this->expectException(BadRequestException::class); + $this->expectExceptionMessage('The refresh token should be a valid UUID'); + $this->authenticator->authenticate(new ServerRequest()); + } + + public function testJwtRefreshTokenAuthenticator_Empty_Token_Should_Return_Bad_Request() + { + $request = (new ServerRequest())->withData(RefreshTokenAbstractService::REFRESH_TOKEN_DATA_KEY, 'Blah'); + $this->expectException(BadRequestException::class); + $this->expectExceptionMessage('The refresh token should be a valid UUID'); + $this->authenticator->authenticate($request); + } + + public function testJwtRefreshTokenAuthenticator_Payload_With_Token_Only_Should_Return_Bad_Request() + { + $request = (new ServerRequest())->withData(RefreshTokenAbstractService::REFRESH_TOKEN_DATA_KEY, UuidFactory::uuid()); + $this->expectException(BadRequestException::class); + $this->expectExceptionMessage('The user ID should be a valid UUID.'); + $this->authenticator->authenticate($request); + } + + public function testJwtRefreshTokenAuthenticator_Payload_With_UserID_Only_Should_Return_Bad_Request() + { + $request = (new ServerRequest())->withData('user_id', UuidFactory::uuid()); + $this->expectException(BadRequestException::class); + $this->expectExceptionMessage('The refresh token should be a valid UUID.'); + $this->authenticator->authenticate($request); + } + + public function testJwtRefreshTokenAuthenticator_Non_Existing_Token_Should_Return_Security_Exception() + { + $request = (new ServerRequest()) + ->withData(RefreshTokenAbstractService::REFRESH_TOKEN_DATA_KEY, UuidFactory::uuid()) + ->withData('user_id', UuidFactory::uuid()); + $this->expectException(RefreshTokenNotFoundException::class); + $this->expectExceptionMessage('No active refresh token matching the request could be found.'); + $this->authenticator->authenticate($request); + } + + public function testJwtRefreshTokenAuthenticator_Non_Existing_Token_Should_Return_Security_Exception_Cookie_Variant() + { + $cookies = (new CookieCollection())->add( + new Cookie(RefreshTokenAbstractService::REFRESH_TOKEN_COOKIE, UuidFactory::uuid()) + ); + $request = (new ServerRequest())->withCookieCollection($cookies); + $this->expectException(RefreshTokenNotFoundException::class); + $this->expectExceptionMessage('No active refresh token matching the request could be found.'); + $this->authenticator->authenticate($request); + } + + public function testJwtRefreshTokenAuthenticator_Non_Active_Token_Should_Return_Security_Exception() + { + $token = RefreshTokenAuthenticationTokenFactory::make()->with('Users')->inactive()->persist(); + $request = (new ServerRequest()) + ->withData(RefreshTokenAbstractService::REFRESH_TOKEN_DATA_KEY, $token->token) + ->withData('user_id', $token->user_id); + $this->expectException(ConsumedRefreshTokenAccessException::class); + $this->expectExceptionMessage('The refresh token provided was already used.'); + $this->authenticator->authenticate($request); + } + + public function testJwtRefreshTokenAuthenticator_Non_Active_Token_Should_Return_Security_Exception_Cookie_Variant() + { + $token = RefreshTokenAuthenticationTokenFactory::make()->inactive()->persist(); + $cookies = (new CookieCollection())->add( + new Cookie(RefreshTokenAbstractService::REFRESH_TOKEN_COOKIE, $token->token) + ); + $request = (new ServerRequest())->withCookieCollection($cookies); + $this->expectException(ConsumedRefreshTokenAccessException::class); + $this->expectExceptionMessage('The refresh token provided was already used.'); + $this->authenticator->authenticate($request); + } + + public function testJwtRefreshTokenAuthenticator_Expired_Token_Should_Return_Security_Exception() + { + $token = RefreshTokenAuthenticationTokenFactory::make()->with('Users')->active()->expired()->persist(); + $request = (new ServerRequest()) + ->withData(RefreshTokenAbstractService::REFRESH_TOKEN_DATA_KEY, $token->token) + ->withData('user_id', $token->user_id); + $this->expectException(ExpiredRefreshTokenAccessException::class); + $this->expectExceptionMessage('Expired refresh token provided.'); + $this->authenticator->authenticate($request); + } + + public function testJwtRefreshTokenAuthenticator_Expired_Token_Should_Return_Security_Exception_Cookie_Variant() + { + $token = RefreshTokenAuthenticationTokenFactory::make()->active()->expired()->persist(); + $cookies = (new CookieCollection())->add( + new Cookie(RefreshTokenAbstractService::REFRESH_TOKEN_COOKIE, $token->token) + ); + $request = (new ServerRequest())->withCookieCollection($cookies); + $this->expectException(ExpiredRefreshTokenAccessException::class); + $this->expectExceptionMessage('Expired refresh token provided.'); + $this->authenticator->authenticate($request); + } + + public function testJwtRefreshTokenAuthenticator_User_Id_Refresh_Token_Mismatch_Should_Return_Security_Exception() + { + $token = RefreshTokenAuthenticationTokenFactory::make()->active()->expired()->persist(); + $user = UserFactory::make()->persist(); + $request = (new ServerRequest()) + ->withData(RefreshTokenAbstractService::REFRESH_TOKEN_DATA_KEY, $token->token) + ->withData('user_id', $user->id); + $this->expectException(RefreshTokenNotFoundException::class); + $this->expectExceptionMessage('No active refresh token matching the request could be found.'); + $this->authenticator->authenticate($request); + } + + public function testJwtRefreshTokenAuthenticator_User_Deleted_Should_Return_No_Security_Exception() + { + $token = RefreshTokenAuthenticationTokenFactory::make() + ->with('Users', UserFactory::make()->deleted()) + ->persist(); + $request = (new ServerRequest()) + ->withData(RefreshTokenAbstractService::REFRESH_TOKEN_DATA_KEY, $token->token) + ->withData('user_id', $token->user_id); + $this->expectException(BadRequestException::class); + $this->expectExceptionMessage('The user is deleted.'); + $this->authenticator->authenticate($request); + } + + public function testJwtRefreshTokenAuthenticator_User_Deactivated_Should_Return_No_Security_Exception() + { + $token = RefreshTokenAuthenticationTokenFactory::make() + ->with('Users', UserFactory::make()->inactive()) + ->persist(); + $request = (new ServerRequest()) + ->withData(RefreshTokenAbstractService::REFRESH_TOKEN_DATA_KEY, $token->token) + ->withData('user_id', $token->user_id); + $this->expectException(BadRequestException::class); + $this->expectExceptionMessage('The user is deactivated.'); + $this->authenticator->authenticate($request); + } + + private function assertSuccess( + ServerRequest $request, + Container $container, + AuthenticationToken $refreshToken, + Result $result + ): void { + $user = $refreshToken->user; + $this->assertTrue($result->isValid()); + + /** @var \Passbolt\JwtAuthentication\Authenticator\RefreshTokenSessionIdentificationService $sessionIdentification */ + $sessionIdentification = $container->get(SessionIdentificationServiceInterface::class); + $successfulAuthentication = $this->createMock(AuthenticationService::class); + $successfulAuthentication->method('getResult')->willReturn($result); + $request = $request->withAttribute('authentication', $successfulAuthentication); + $this->assertSame($refreshToken->id, $sessionIdentification->getSessionIdentifier($request)->id); + $this->assertSame($refreshToken->token, $sessionIdentification->getSessionIdentifier($request)->token); + $this->assertSame($refreshToken->user_id, $sessionIdentification->getSessionIdentifier($request)->user_id); + + $identifiedUser = $result->getData()['user']; + $this->assertSame($user->id, $identifiedUser['id']); + $this->assertSame($user->role->name, $identifiedUser['role']['name']); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/tests/TestCase/Authenticator/JwtSessionIdentificationServiceTest.php b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Authenticator/JwtSessionIdentificationServiceTest.php new file mode 100644 index 0000000000..17500bf2d2 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Authenticator/JwtSessionIdentificationServiceTest.php @@ -0,0 +1,69 @@ +getMockBuilder(ResultInterface::class)->getMock(); + $resultStub->expects($this->any())->method('isValid')->willReturn($isAuthenticated); + $authStub = $this->getMockBuilder(AuthenticationService::class)->getMock(); + $authStub->expects($this->any())->method('getResult')->willReturn($resultStub); + + $accessToken = 'Foo'; + + $request = (new ServerRequest()) + ->withAttribute('authentication', $authStub) + ->withHeader(JwtAuthenticationService::JWT_HEADER, $accessToken); + + $SessionService = new JwtSessionIdentificationService(); + $expected = $isAuthenticated ? $accessToken : null; + $this->assertSame($expected, $SessionService->getSessionIdentifier($request)); + } + + public function testJwtSessionIdentificationService_No_Authentication_Service() + { + $accessToken = 'Foo'; + + $request = new ServerRequest(); + $request->withHeader(JwtAuthenticationService::JWT_HEADER, $accessToken); + + $SessionService = new JwtSessionIdentificationService(); + $this->assertNull($SessionService->getSessionIdentifier($request)); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/tests/TestCase/Command/CreateAccessTokenCommandTest.php b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Command/CreateAccessTokenCommandTest.php new file mode 100644 index 0000000000..7ef00f5fc7 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Command/CreateAccessTokenCommandTest.php @@ -0,0 +1,121 @@ +createKeyPair(); + $this->useCommandRunner(); + $this->enableFeaturePlugin('JwtAuthentication'); + } + + public function tearDown(): void + { + parent::tearDown(); + $this->disableFeaturePlugin('JwtAuthentication'); + } + + /** + * Basic help test + */ + public function testCreateAccessTokenCommandHelp() + { + $this->exec('passbolt create_access_token -h'); + $this->assertExitSuccess(); + $this->assertOutputContains('Create a JSON Web Token.'); + $this->assertOutputContains('cake passbolt create_access_token'); + } + + public function testCreateAccessTokenCommandWithUsername() + { + $user = UserFactory::make()->user()->persist(); + $this->exec('passbolt create_access_token -u ' . $user->username); + $this->assertExitSuccess(); + $this->assertOutputContains('Access token for ' . $user->username . ' valid 5 minutes:'); + } + + public function testCreateAccessTokenCommandWithUserId() + { + $user = UserFactory::make()->user()->persist(); + $this->exec('passbolt create_access_token -i ' . $user->id); + $this->assertExitSuccess(); + $this->assertOutputContains('Access token for ' . $user->username . ' valid 5 minutes:'); + } + + public function testCreateAccessTokenCommandWithExpiry() + { + $user = UserFactory::make()->user()->persist(); + $expiry = 10; + $this->exec('passbolt create_access_token -i ' . $user->id . ' -e ' . $expiry); + $this->assertExitSuccess(); + $this->assertOutputContains('Access token for ' . $user->username . " valid {$expiry} minutes:"); + } + + public function testCreateAccessTokenCommandWithExpiryInWordFormat() + { + $user = UserFactory::make()->user()->persist(); + $expiry = '5 seconds'; + $this->exec('passbolt create_access_token -i ' . $user->id . ' -e "' . $expiry . '"'); + $this->assertExitSuccess(); + $this->assertOutputContains('Access token for ' . $user->username . " valid {$expiry}:"); + } + + public function testCreateAccessTokenCommandWithNoUserParams() + { + $this->exec('passbolt create_access_token'); + $this->assertExitError(); + } + + public function testCreateAccessTokenCommandWithWrongUsername() + { + $this->exec('passbolt create_access_token -u foo'); + $this->assertExitError(); + } + + public function testCreateAccessTokenCommandInProdMod() + { + $user = UserFactory::make()->user()->persist(); + $debug = Configure::read('debug'); + Configure::write('debug', false); + $this->exec('passbolt create_access_token -i ' . $user->id); + $this->assertExitError(); + Configure::write('debug', $debug); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/tests/TestCase/Command/CreateJwtKeysCommandTest.php b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Command/CreateJwtKeysCommandTest.php new file mode 100644 index 0000000000..8971950d17 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Command/CreateJwtKeysCommandTest.php @@ -0,0 +1,90 @@ +useCommandRunner(); + $this->enableFeaturePlugin('JwtAuthentication'); + } + + public function tearDown(): void + { + parent::tearDown(); + $this->disableFeaturePlugin('JwtAuthentication'); + } + + /** + * Basic help test + */ + public function testCreateJwtKeysCommandHelp() + { + $this->exec('passbolt create_jwt_keys -h'); + $this->assertExitSuccess(); + $this->assertOutputContains('Create a JWT key pair.'); + $this->assertOutputContains('cake passbolt create_jwt_keys'); + } + + public function testCreateJwtKeysCommandNoForce() + { + $this->deleteFileIfExists(CONFIG . 'jwt' . DS . 'jwt.key'); + $this->deleteFileIfExists(CONFIG . 'jwt' . DS . 'jwt.perm'); + + // Will succeed if no keys exist + $this->exec('passbolt create_jwt_keys -q'); + $this->assertExitSuccess(); + + // Will fail if keys already exist + $this->exec('passbolt create_jwt_keys -q'); + $this->assertExitError(); + } + + public function testCreateJwtKeysCommandForce() + { + $this->exec('passbolt create_jwt_keys -q -f'); + $this->assertExitSuccess(); + } + + private function deleteFileIfExists(string $file): void + { + if (file_exists($file)) { + unlink($file); + } + } +} diff --git a/plugins/Passbolt/JwtAuthentication/tests/TestCase/Controller/Auth/AuthIsAuthenticatedControllerTest.php b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Controller/Auth/AuthIsAuthenticatedControllerTest.php new file mode 100644 index 0000000000..8589a678bd --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Controller/Auth/AuthIsAuthenticatedControllerTest.php @@ -0,0 +1,48 @@ +user()->persist(); + $this->createJwtTokenAndSetInHeader($user->id); + $this->getJson('/auth/is-authenticated.json'); + $this->assertResponseOk(); + $this->assertTextContains('success', $this->_responseJsonHeader->status); + } + + public function testIsAuthenticatedWithJwt_ErrorWithInactiveUser() + { + $user = UserFactory::make()->user()->inactive()->persist(); + $this->createJwtTokenAndSetInHeader($user->id); + $this->getJson('/auth/is-authenticated.json'); + $this->assertResponseError(); + } + + public function testIsAuthenticatedWithJwt_ErrorWithDeletedUser() + { + $user = UserFactory::make()->user()->deleted()->persist(); + $this->createJwtTokenAndSetInHeader($user->id); + $this->getJson('/auth/is-authenticated.json'); + $this->assertResponseError(); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/tests/TestCase/Controller/JwksControllerTest.php b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Controller/JwksControllerTest.php new file mode 100644 index 0000000000..fa5014f129 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Controller/JwksControllerTest.php @@ -0,0 +1,65 @@ +getJson('/auth/jwt/rsa.json'); + $this->assertResponseOk(); + $responseKeys = $this->_responseJsonBody->keydata; + $this->assertTextContains('-----BEGIN PUBLIC KEY-----', $responseKeys); + } + + public function testAuthJwksControllerJwksSuccess() + { + $this->get('/auth/jwt/jwks.json'); + $this->_responseJsonBody = json_decode($this->_getBodyAsString()); + $this->assertResponseOk(); + $this->assertCount(1, $this->_responseJsonBody->keys); + $responseKey = $this->_responseJsonBody->keys[0]; + $this->assertSame('RSA', $responseKey->kty); + $this->assertSame('RS256', $responseKey->alg); + } + + public function testAuthJwksControllerJwksRedirect() + { + $this->get('/.well-known/jwks.json'); + $this->assertResponseCode(301); + } + + public function testAuthJwksControllerJwks_Key_Missing() + { + DirectoryUtility::removeRecursively(JwksGetService::PUBLIC_KEY_PATH); + $this->getJson('/auth/jwt/jwks.json'); + $this->assertInternalError('The key pair for JWT Authentication is not complete.'); + $this->assertSame('The key pair for JWT Authentication is not complete.', $this->_responseJsonHeader->message); + } + + public function testAuthJwksControllerRsa_Key_Missing() + { + DirectoryUtility::removeRecursively(JwksGetService::PUBLIC_KEY_PATH); + $this->getJson('/auth/jwt/rsa.json'); + $this->assertInternalError('The key pair for JWT Authentication is not complete.'); + $this->assertSame('The key pair for JWT Authentication is not complete.', $this->_responseJsonHeader->message); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/tests/TestCase/Controller/JwtLoginControllerTest.php b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Controller/JwtLoginControllerTest.php new file mode 100644 index 0000000000..908f03af19 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Controller/JwtLoginControllerTest.php @@ -0,0 +1,232 @@ +loadModel('AuthenticationTokens'); + $this->loadModel('Users'); + $this->loadModel('Passbolt/Log.ActionLogs'); + $this->enableFeaturePlugin('Log'); + RoleFactory::make()->guest()->persist(); + EventManager::instance()->setEventList(new EventList()); + } + + public function testJwtLoginControllerTest_Success() + { + $user = UserFactory::make() + ->user() + ->with('Gpgkeys', GpgkeyFactory::make()->validFingerprint()) + ->persist(); + + $this->postJson('/auth/jwt/login.json', [ + 'user_id' => $user->id, + 'challenge' => $this->makeChallenge($user, UuidFactory::uuid()), + ]); + + $this->assertResponseOk('The authentication was a success.'); + $this->assertEmailQueueCount(0); + $this->assertEventFired(GpgJwtAuthenticator::JWT_AUTHENTICATION_AFTER_IDENTIFY); + + $challenge = json_decode($this->decryptChallenge($user, $this->_responseJsonBody->challenge)); + $this->assertSame(Router::url('/', true), $challenge->domain); + $this->assertSame(GpgJwtAuthenticator::PROTOCOL_VERSION, $challenge->version); + $this->assertIsString($challenge->access_token); + $this->assertTrue(Validation::uuid($challenge->refresh_token)); + $this->assertTrue(Validation::uuid($challenge->verify_token)); + $this->assertSame(1, AuthenticationTokenFactory::find()->where(['token' => $challenge->refresh_token, 'user_id' => $user->id])->count()); + $this->assertSame(1, AuthenticationTokenFactory::find()->where(['token' => $challenge->verify_token, 'user_id' => $user->id])->count()); + + // Assert login action log + $this->assertOneActionLog(); + $this->assertActionLogExists([ + 'user_id' => $user->id, + 'context' => 'POST /auth/jwt/login.json', + ]); + } + + public function testJwtLoginControllerTest_Consumed_Verify_Token() + { + $user = UserFactory::make() + ->user() + ->with('Gpgkeys', GpgkeyFactory::make()->validFingerprint()) + ->with( + 'AuthenticationTokens', + AuthenticationTokenFactory::make()->type(AuthenticationToken::TYPE_VERIFY_TOKEN) + ) + ->persist(); + + $verifyToken = $user->authentication_tokens[0]->token; + + $this->postJson('/auth/jwt/login.json', [ + 'user_id' => $user->id, + 'challenge' => $this->makeChallenge($user, $verifyToken), + ]); + + $this->assertResponseError('The credentials are invalid.'); + $this->assertEmailQueueCount(1); + $this->assertEmailIsInQueue([ + 'email' => $user->username, + 'subject' => 'Authentication security alert', + 'template' => 'Passbolt/JwtAuthentication.User/jwt_attack', + ]); + $this->assertEmailInBatchContains('Verify token has been already used in the past.'); + + // Assert login action log + $this->assertOneActionLog(); + $this->assertActionLogExists([ + 'user_id IS' => null, + 'context' => 'POST /auth/jwt/login.json', + ]); + } + + public function testJwtLoginControllerTest_Failure_On_Deleted_User() + { + $user = UserFactory::make() + ->user() + ->with('Gpgkeys', GpgkeyFactory::make()->validFingerprint()) + ->persist(); + + $challenge = $this->makeChallenge($user, UuidFactory::uuid()); + + // Delete this user + $this->Users->softDelete($user); + + $this->postJson('/auth/jwt/login.json', [ + 'user_id' => $user->id, + 'challenge' => $challenge, + ]); + + $this->assertResponseError('The user does not exist or has been deleted.'); + } + + public function testJwtLoginControllerTest_Failure_On_Inactive_User() + { + $user = UserFactory::make() + ->user() + ->with('Gpgkeys', GpgkeyFactory::make()->validFingerprint()) + ->persist(); + + $challenge = $this->makeChallenge($user, UuidFactory::uuid()); + + // Deactivate this user + $this->Users->patchEntity($user, ['active' => false]); + $this->Users->saveOrFail($user); + + $this->postJson('/auth/jwt/login.json', [ + 'user_id' => $user->id, + 'challenge' => $challenge, + ]); + + $this->assertResponseError('The user does not exist or has been deleted.'); + } + + public function testJwtLoginControllerTest_FAILURE_CREDENTIALS_MISSING() + { + $this->postJson('/auth/jwt/login.json'); + $this->assertResponseError('The credentials are missing.'); + } + + public function testJwtLoginControllerTest_FAILURE_IDENTITY_NOT_FOUND() + { + $this->postJson('/auth/jwt/login.json', [ + 'user_id' => UuidFactory::uuid(), + ]); + $this->assertResponseError('The user does not exist or is not active or has been deleted.'); + } + + public function testJwtLoginControllerTest_User_Is_Already_LoggedIn_In_Session() + { + $user = UserFactory::make() + ->user() + ->with('Gpgkeys', GpgkeyFactory::make()->validFingerprint()) + ->persist(); + + $this->logInAs($user); + $this->getJson('/auth/is-authenticated.json'); + $this->assertResponseOk(); + + $this->postJson('/auth/jwt/login.json', [ + 'user_id' => $user->id, + 'challenge' => $this->makeChallenge($user, UuidFactory::uuid()), + ]); + $this->assertResponseSuccess(); + $challenge = json_decode($this->decryptChallenge($user, $this->_responseJsonBody->challenge)); + $accessToken = $challenge->access_token; + + $this->setJwtTokenInHeader($accessToken); + $this->getJson('/auth/is-authenticated.json'); + $this->assertResponseOk(); + + $this->assertResponseOk('The authentication was a success.'); + } + + public function testSessionLoginWithJwtTokenInHeaderIsNotPermitted() + { + $this->createJwtTokenAndSetInHeader(); + $this->getJson('/auth/login.json'); + $this->assertResponseError('The route /auth/login is not permitted with JWT authentication.'); + } + + public function testJwtLoginController_Authentication_Should_Even_If_Valid_Access_Token_Set_In_Header() + { + $user = UserFactory::make() + ->user() + ->with('Gpgkeys', GpgkeyFactory::make()->validFingerprint()) + ->persist(); + + $this->createJwtTokenAndSetInHeader($user->id); + + $this->postJson('/auth/jwt/login.json', [ + 'user_id' => $user->id, + 'challenge' => 'Bar', + ]); + + $this->assertBadRequestError('The credentials are invalid.'); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/tests/TestCase/Controller/JwtLogoutControllerTest.php b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Controller/JwtLogoutControllerTest.php new file mode 100644 index 0000000000..5df55e7394 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Controller/JwtLogoutControllerTest.php @@ -0,0 +1,183 @@ +loadModel('AuthenticationTokens'); + } + + public function testAuthJwtLogoutControllerUnauthenticated() + { + $this->postJson('/auth/jwt/logout.json'); + $this->assertResponseError(); + } + + public function testAuthJwtLogoutControllerNoPayload() + { + $user = UserFactory::make()->user()->persist(); + $this->createJwtTokenAndSetInHeader($user->id); + $nToken = 3; + AuthenticationTokenFactory::make($nToken) + ->active() + ->type(AuthenticationToken::TYPE_REFRESH_TOKEN) + ->userId($user->id) + ->persist(); + + $this->postJson('/auth/jwt/logout.json'); + $this->assertResponseSuccess(); + + $this->assertSame(0, $this->countActiveAuthenticationTokens($user->id)); + $this->assertSame($nToken, $this->countInactiveAuthenticationTokens($user->id)); + } + + public function testAuthJwtLogoutControllerWithPayload() + { + $userId = UserFactory::make()->user()->persist()->id; + $this->createJwtTokenAndSetInHeader($userId); + $nToken = 3; + /** @var array $tokens */ + $tokens = AuthenticationTokenFactory::make($nToken) + ->active() + ->type(AuthenticationToken::TYPE_REFRESH_TOKEN) + ->userId($userId) + ->persist(); + + $tokenToDeactivate = $tokens[0]->token; + + $payload = [RefreshTokenAbstractService::REFRESH_TOKEN_DATA_KEY => $tokenToDeactivate,]; + + $this->postJson('/auth/jwt/logout.json', $payload); + $this->assertResponseSuccess(); + $this->assertCookieNotSet(RefreshTokenRenewalService::REFRESH_TOKEN_COOKIE); + + $this->assertSame($nToken - 1, $this->countActiveAuthenticationTokens($userId)); + $this->assertSame(1, $this->countInactiveAuthenticationTokens($userId)); + $this->assertFalse($this->AuthenticationTokens->isValid($tokenToDeactivate, $userId)); + + $this->getJson('/auth/is-authenticated.json'); + $this->assertResponseError(); + } + + public function testAuthJwtLogoutControllerWithCookie() + { + $userId = UserFactory::make()->user()->persist()->id; + $this->createJwtTokenAndSetInHeader($userId); + $nToken = 3; + /** @var array $tokens */ + $tokens = AuthenticationTokenFactory::make($nToken) + ->active() + ->type(AuthenticationToken::TYPE_REFRESH_TOKEN) + ->userId($userId) + ->persist(); + + $tokenToDeactivate = $tokens[0]->token; + + $this->cookie( + RefreshTokenRenewalService::REFRESH_TOKEN_COOKIE, + $tokenToDeactivate + ); + + $this->postJson('/auth/jwt/logout.json'); + $this->assertResponseSuccess(); + $this->assertCookieNotSet(RefreshTokenRenewalService::REFRESH_TOKEN_COOKIE); + + $this->assertSame($nToken - 1, $this->countActiveAuthenticationTokens($userId)); + $this->assertSame(1, $this->countInactiveAuthenticationTokens($userId)); + $this->assertFalse($this->AuthenticationTokens->isValid($tokenToDeactivate, $userId)); + + $this->getJson('/auth/is-authenticated.json'); + $this->assertResponseError(); + } + + /** + * @Given a user is logged in with JWT access token + * @When the regular auth/logout endpoint is called with a valid access token in header + * @Then the user is logged out + * @And her access token is deactivated + */ + public function testAuthJwtLogoutController_Logout_From_Session_Endpoint() + { + $userId = UserFactory::make()->user()->persist()->id; + $this->createJwtTokenAndSetInHeader($userId); + AuthenticationTokenFactory::make() + ->active() + ->type(AuthenticationToken::TYPE_REFRESH_TOKEN) + ->userId($userId) + ->persist(); + + $bearerToken = $this->_request['headers'][JwtAuthenticationService::JWT_HEADER]; + + $this->post('/auth/logout'); + $this->assertResponseError('The route /auth/logout is not permitted with JWT authentication.'); + + $this->setJwtTokenInHeader($bearerToken); + $this->getJson('/auth/is-authenticated.json'); + $this->assertResponseSuccess(); + + $this->assertSame(1, $this->countActiveAuthenticationTokens($userId)); + } + + /** + * @Given a user is login in session + * @When the auth/jwt/logout.json is called + * @Then the end point is not accessible and the user is not logged out from session + */ + public function testAuthJwtLogoutController_LoggedIn_In_Session_And_Logout_From_JWT_Endpoint() + { + $this->logInAsUser(); + $this->postJson('/auth/jwt/logout.json'); + $this->assertResponseError('Authentication is required to continue'); + } + + private function countActiveAuthenticationTokens(string $userId): int + { + return AuthenticationTokenFactory::find()->where([ + 'user_id' => $userId, + 'active' => true, + 'type' => AuthenticationToken::TYPE_REFRESH_TOKEN, + ])->count(); + } + + private function countInactiveAuthenticationTokens(string $userId): int + { + return AuthenticationTokenFactory::find()->where([ + 'user_id' => $userId, + 'active' => false, + 'type' => AuthenticationToken::TYPE_REFRESH_TOKEN, + ])->count(); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/tests/TestCase/Controller/RefreshTokenControllerTest.php b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Controller/RefreshTokenControllerTest.php new file mode 100644 index 0000000000..2c3d21bc55 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Controller/RefreshTokenControllerTest.php @@ -0,0 +1,298 @@ +loadModel('AuthenticationTokens'); + $this->loadModel('Users'); + } + + public function testAuthRefreshTokenController_No_Data() + { + $this->postJson('/auth/jwt/refresh.json'); + $this->assertBadRequestError('The refresh token should be a valid UUID.'); + } + + public function testAuthRefreshTokenControllerIncompletePayload() + { + $this->postJson('/auth/jwt/refresh.json', ['user_id' => UuidFactory::uuid()]); + $this->assertResponseError(); + } + + public function testAuthRefreshTokenController_InvalidRefreshTokenCookie() + { + $this->enableCsrfToken(); + $this->cookie(RefreshTokenRenewalService::REFRESH_TOKEN_COOKIE, 'foo'); + $this->postJson('/auth/jwt/refresh.json'); + $this->assertBadRequestError('The refresh token should be a valid UUID.'); + } + + public function testAuthRefreshTokenController_RandomRefreshTokenCookie() + { + $nAdmins = 3; + UserFactory::make($nAdmins)->admin()->persist(); + $this->enableCsrfToken(); + $this->cookie(RefreshTokenRenewalService::REFRESH_TOKEN_COOKIE, UuidFactory::uuid()); + $this->postJson('/auth/jwt/refresh.json'); + $this->assertBadRequestError('No active refresh token matching the request could be found.'); + $this->assertEmailQueueCount(0); + } + + public function dataProviderWithAndWithoutAccessToken(): array + { + return [ + [true], [false], + ]; + } + + /** + * @dataProvider dataProviderWithAndWithoutAccessToken + * @param bool $withAccessToken With valid access token in header or no access token + */ + public function testAuthRefreshTokenControllerWithValidRefreshTokenCookie(bool $withAccessToken) + { + $user = UserFactory::make()->user()->persist(); + if ($withAccessToken) { + $this->createJwtTokenAndSetInHeader($user->id); + } + $oldRefreshToken = AuthenticationTokenFactory::make() + ->active() + ->type(AuthenticationToken::TYPE_REFRESH_TOKEN) + ->userId($user->id) + ->persist() + ->token; + + $this->cookie(RefreshTokenRenewalService::REFRESH_TOKEN_COOKIE, $oldRefreshToken); + + // This route, with cookie, should have CSRF protection + $this->enableCsrfToken(); + $this->postJson('/auth/jwt/refresh.json'); + $this->assertResponseOk(); + + $jwt = $this->_responseJsonBody->access_token; + + $newRefreshToken = AuthenticationTokenFactory::find()->where([ + 'active' => true, + 'user_id' => $user->id, + 'type' => AuthenticationToken::TYPE_REFRESH_TOKEN, + ])->firstOrFail()->get('token'); + $this->assertCookieIsSecure($newRefreshToken, 'refresh_token'); + // Get a fresh request + $this->cleanup(); + $this->setJwtTokenInHeader($jwt); + // Check that the delivered access token is valid. + $this->getJson('/auth/is-authenticated.json'); + $this->assertResponseOk(); + } + + /** + * @dataProvider dataProviderWithAndWithoutAccessToken + * @param bool $withAccessToken With valid access token in header or no access token + */ + public function testAuthRefreshTokenControllerWithValidPayload(bool $withAccessToken) + { + $user = UserFactory::make()->user()->persist(); + if ($withAccessToken) { + $this->createJwtTokenAndSetInHeader($user->id); + } + $oldRefreshToken = AuthenticationTokenFactory::make() + ->active() + ->type(AuthenticationToken::TYPE_REFRESH_TOKEN) + ->userId($user->id) + ->persist() + ->token; + + $this->postJson('/auth/jwt/refresh.json', [ + 'user_id' => $user->id, + 'refresh_token' => $oldRefreshToken, + ]); + + $this->assertResponseSuccess(); + $jwt = $this->_responseJsonBody->access_token; + + // Get a fresh request + $this->cleanup(); + $this->setJwtTokenInHeader($jwt); + // Check that the delivered JWT is valid. + $this->getJson('/auth/is-authenticated.json'); + $this->assertResponseOk(); + } + + public function testAuthRefreshTokenControllerWithValidRefreshTokenCookie_ButNoCsrf() + { + $user = UserFactory::make()->user()->persist(); + $oldRefreshToken = AuthenticationTokenFactory::make() + ->active() + ->type(AuthenticationToken::TYPE_REFRESH_TOKEN) + ->userId($user->id) + ->persist() + ->token; + + $this->cookie(RefreshTokenRenewalService::REFRESH_TOKEN_COOKIE, $oldRefreshToken); + + $this->postJson('/auth/jwt/refresh.json'); + + $this->assertResponseError('Missing or incorrect CSRF cookie type.'); + } + + public function testAuthRefreshTokenControllerWithExpiredCookie() + { + $nAdmins = 3; + /** @var array $admins */ + $admins = UserFactory::make($nAdmins)->admin()->persist(); + // We suppose one of the admins is hacked, and will check that 3 mails, and not 4 get sent. + $user = $admins[0]; + $oldRefreshToken = AuthenticationTokenFactory::make() + ->active() + ->type(AuthenticationToken::TYPE_REFRESH_TOKEN) + ->userId($user->id) + ->expired() + ->persist() + ->token; + + // Set the refresh key in the cookies + $this->cookie(RefreshTokenRenewalService::REFRESH_TOKEN_COOKIE, $oldRefreshToken); + + $this->enableCsrfToken(); + $this->postJson('/auth/jwt/refresh.json'); + $this->assertBadRequestError('Expired refresh token provided.'); + $this->assertEmailQueueCount($nAdmins); + $this->assertEmailIsInQueue([ + 'email' => $user->username, + 'subject' => 'Authentication security alert', + 'template' => 'Passbolt/JwtAuthentication.User/jwt_attack', + ]); + foreach ($admins as $i => $admin) { + if ($i === 0) { + $this->assertEmailInBatchContains('Please get in touch with one of your administrators.'); + continue; + } + $this->assertEmailIsInQueue([ + 'email' => $admin->username, + 'subject' => 'Authentication security alert', + 'template' => 'Passbolt/JwtAuthentication.Admin/jwt_attack', + ]); + $this->assertEmailInBatchContains('This is a potential security issue. Please investigate!', $i); + } + } + + public function testAuthRefreshTokenControllerWithExpiredPayload() + { + UserFactory::make(3)->admin()->persist(); + $admins = $this->Users->findAdmins()->toArray(); + $user = $admins[0]; + $nAdmins = count($admins); + + $userId = $user->id; + $oldRefreshToken = AuthenticationTokenFactory::make() + ->active() + ->type(AuthenticationToken::TYPE_REFRESH_TOKEN) + ->userId($userId) + ->expired() + ->persist() + ->token; + + $this->postJson('/auth/jwt/refresh.json', [ + 'user_id' => $userId, + 'refresh_token' => $oldRefreshToken, + ]); + $this->assertBadRequestError('Expired refresh token provided.'); + + $this->assertEmailQueueCount($nAdmins); + $this->assertEmailIsInQueue([ + 'email' => $user->username, + 'subject' => 'Authentication security alert', + 'template' => 'Passbolt/JwtAuthentication.User/jwt_attack', + ]); + foreach ($admins as $i => $admin) { + if ($i === 0) { + continue; + } + $this->assertEmailIsInQueue([ + 'email' => $admin->username, + 'subject' => 'Authentication security alert', + 'template' => 'Passbolt/JwtAuthentication.Admin/jwt_attack', + ]); + } + } + + public function testAuthRefreshTokenControllerWithConsumedCookie() + { + $nAdmins = 3; + $admins = UserFactory::make($nAdmins)->admin()->persist(); + // We suppose one of the admins is hacked, and will check that 3 mails, and not 4 get sent. + /** @var array $admins */ + $user = $admins[0]; + $oldRefreshToken = AuthenticationTokenFactory::make() + ->inactive() + ->type(AuthenticationToken::TYPE_REFRESH_TOKEN) + ->userId($user->id) + ->expired() + ->persist() + ->token; + + // Set the refresh key in the cookies + $this->cookie( + RefreshTokenRenewalService::REFRESH_TOKEN_COOKIE, + $oldRefreshToken + ); + + $this->enableCsrfToken(); + $this->postJson('/auth/jwt/refresh.json'); + $this->assertBadRequestError('The refresh token provided was already used.'); + $this->assertEmailQueueCount($nAdmins); + $this->assertEmailIsInQueue([ + 'email' => $user->username, + 'subject' => 'Authentication security alert', + 'template' => 'Passbolt/JwtAuthentication.User/jwt_attack', + ]); + foreach ($admins as $i => $admin) { + if ($i === 0) { + continue; + } + $this->assertEmailIsInQueue([ + 'email' => $admin->username, + 'subject' => 'Authentication security alert', + 'template' => 'Passbolt/JwtAuthentication.Admin/jwt_attack', + ]); + } + } +} diff --git a/plugins/Passbolt/JwtAuthentication/tests/TestCase/Middleware/JwtDestroySessionMiddlewareTest.php b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Middleware/JwtDestroySessionMiddlewareTest.php new file mode 100644 index 0000000000..1eb2e00545 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Middleware/JwtDestroySessionMiddlewareTest.php @@ -0,0 +1,56 @@ +enableCsrfToken(); + + $fooSessionKey = 'foo'; + $authSessionKey = 'Auth'; + $csrfTokenSessionKey = 'csrfToken'; + $this->session([$fooSessionKey => 'sessionValue',]); + + // In session mode, session is set + $this->logInAsUser(); + $this->getJson('/auth/is-authenticated.json'); + $this->assertResponseSuccess(); + $this->assertSessionHasKey($fooSessionKey); + $this->assertSessionHasKey($authSessionKey); + $this->assertSessionHasKey($csrfTokenSessionKey); + + // In JWT, no session, and session related cookies + // are expired. + $this->createJwtTokenAndSetInHeader(UuidFactory::uuid()); + $this->getJson('/auth/is-authenticated.json'); + $this->assertResponseError(); + + $this->assertSessionNotHasKey($fooSessionKey); + $this->assertSessionNotHasKey($authSessionKey); + $this->assertSessionNotHasKey($csrfTokenSessionKey); + $this->assertSession(null, ''); + $this->assertCookieExpired('csrfToken'); + $sessionCookie = Configure::read('Session.cookie', session_name()); + $this->assertCookieExpired($sessionCookie); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/tests/TestCase/Middleware/JwtRouteFilterMiddlewareTest.php b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Middleware/JwtRouteFilterMiddlewareTest.php new file mode 100644 index 0000000000..8ac810f77d --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Middleware/JwtRouteFilterMiddlewareTest.php @@ -0,0 +1,52 @@ +createJwtTokenAndSetInHeader(); + } + + public function testJwtRouteFilterMiddleware_Not_Allowed_Logout_Route_GET() + { + $this->getJson('auth/logout.json'); + $this->assertResponseError('The route /auth/logout is not permitted with JWT authentication.'); + } + + public function testJwtRouteFilterMiddleware_Not_Allowed_Logout_Route_POST() + { + $this->postJson('auth/logout.json'); + $this->assertResponseError('The route /auth/logout is not permitted with JWT authentication.'); + } + + public function testJwtRouteFilterMiddleware_Not_Allowed_Login_Route_GET() + { + $this->getJson('auth/login.json'); + $this->assertResponseError('The route /auth/login is not permitted with JWT authentication.'); + } + + public function testJwtRouteFilterMiddleware_Not_Allowed_Login_Route_POST() + { + $this->postJson('auth/login.json'); + $this->assertResponseError('The route /auth/login is not permitted with JWT authentication.'); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/tests/TestCase/Middleware/SkipGpgAuthHeadersMiddlewareTest.php b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Middleware/SkipGpgAuthHeadersMiddlewareTest.php new file mode 100644 index 0000000000..8965734b61 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Middleware/SkipGpgAuthHeadersMiddlewareTest.php @@ -0,0 +1,42 @@ +logInAsUser(); + $this->getJson('/auth/is-authenticated.json'); + $this->assertSuccess(); + + $hasHeader = $this->_response->hasHeader('X-GPGAuth-Version'); + $this->assertTrue($hasHeader); + } + + public function testSkipGpgAuthHeaders_DoSkip() + { + $this->createJwtTokenAndSetInHeader(); + $this->getJson('/auth/is-authenticated.json'); + $this->assertSuccess(); + + $hasHeader = $this->_response->hasHeader('X-GPGAuth-Version'); + $this->assertFalse($hasHeader); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/tests/TestCase/Service/AccessToken/JwtKeyPairServiceTest.php b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Service/AccessToken/JwtKeyPairServiceTest.php new file mode 100644 index 0000000000..369c14839a --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Service/AccessToken/JwtKeyPairServiceTest.php @@ -0,0 +1,130 @@ +service = new JwtKeyPairService( + (new JwtTokenCreateService())->setKeyPath(self::$secretFilePath), + (new JwksGetService())->setKeyPath(self::$publicFilePath), + ); + DirectoryUtility::removeRecursively(TMP . 'jwt'); + mkdir(TMP . 'jwt'); + } + + public function tearDown(): void + { + unset($this->service); + } + + public function testJwtKeyPairService_Valid() + { + $this->service->createKeyPair(); + $this->assertTrue(file_exists(self::$publicFilePath)); + $this->assertTrue(file_exists(self::$secretFilePath)); + $uuid = UuidFactory::uuid(); + $token = $this->service->validateKeyPair($uuid); + $this->assertSame($uuid, $token->sub); + $this->assertSame(Router::url('/', true), $token->iss); + } + + public function testJwtKeyPairService_KeyExist() + { + $this->service->createKeyPair(); + $publicKey1 = $this->service->readPublicKey(); + $secretKey1 = $this->service->readSecretKey(); + + $this->service->createKeyPair(true); // Force: no exceptions + $publicKey2 = $this->service->readPublicKey(); + $secretKey2 = $this->service->readSecretKey(); + $this->assertTextNotEquals($publicKey1, $publicKey2); + $this->assertTextNotEquals($secretKey1, $secretKey2); + + $this->service->createKeyPair(); // No force: no exception, keys did not change + $publicKey3 = $this->service->readPublicKey(); + $secretKey3 = $this->service->readSecretKey(); + $this->assertTextEquals($publicKey2, $publicKey3); + $this->assertTextEquals($secretKey2, $secretKey3); + } + + public function testJwtKeyPairService_NotValid() + { + $this->service->createKeyPair(); + + $secretFileContentCorrupted = 'blah' . file_get_contents(self::$secretFilePath); + file_put_contents(self::$secretFilePath, $secretFileContentCorrupted); + $this->assertTrue(file_exists(self::$publicFilePath)); + $this->assertTrue(file_exists(self::$secretFilePath)); + $this->expectException(InvalidJwtKeyPairException::class); + + // suppress warning + $errorReportingBackup = error_reporting(); + error_reporting(0); + + $this->service->validateKeyPair(); + + // reinstate errors + error_reporting($errorReportingBackup); + } + + public function testJwtKeyPairService_Key_Length_Too_Short() + { + $this->service + ->setKeyLength(4095) + ->createKeyPair(true); + + $this->expectException(InvalidJwtKeyPairException::class); + $this->expectExceptionMessage( + 'The JWT private key should be at least ' . JwtTokenCreateService::JWT_KEY_LENGTH . ' bytes long.' + ); + $this->service->validateKeyPair(); + } + + public function testJwtKeyPairService_getCreateJwtKeysCommand() + { + $this->assertSame( + ROOT . DS . 'bin/cake passbolt create_jwt_keys', + $this->service->getCreateJwtKeysCommand() + ); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/tests/TestCase/Service/AccessToken/JwtTokenCreateServiceTest.php b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Service/AccessToken/JwtTokenCreateServiceTest.php new file mode 100644 index 0000000000..02f65db4b1 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Service/AccessToken/JwtTokenCreateServiceTest.php @@ -0,0 +1,102 @@ +expectException($exception); + } + + $secretToken = (new JwtTokenCreateService())->createToken($userId); + $this->assertAccessTokenIsValid($secretToken, $userId); + Configure::write(JwtTokenCreateService::JWT_EXPIRY_CONFIG_KEY, $backUp); + } + + public function dataForTestJwtTokenCreateService_createExpiryDate() + { + return [ + [null, 300], + ['60 seconds', 60], + ['2 minutes', 120], + ]; + } + + /** + * @dataProvider dataForTestJwtTokenCreateService_createExpiryDate + */ + public function testJwtTokenCreateService_createExpiryDate($date, $expectedTime) + { + $expiryDate = (new JwtTokenCreateService())->createExpiryDate($date); + $expectedUXTime = time() + $expectedTime; + + // Allow a difference of 1 second for test processing time. + $this->assertLessThanOrEqual(1, $expiryDate - $expectedUXTime); + } + + /** + * JWT access tokens generated within the same second are identical, provided the user ID + * is the same. If we generate 3 JWT access tokens, we are sure that at two will be + * in the same second, and at least two shall be identical. + */ + public function testJwtTokenCreateService_Multiple_Token_Within_One_Same_Second_Should_Be_Identical() + { + $n = 3; + $userId = UuidFactory::uuid(); + $allTokens = []; + for ($i = 0; $i < $n; $i++) { + $allTokens[] = (new JwtTokenCreateService())->createToken($userId); + } + + $this->assertNotSame($allTokens, array_unique($allTokens)); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/tests/TestCase/Service/RefreshToken/RefreshTokenCreateServiceTest.php b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Service/RefreshToken/RefreshTokenCreateServiceTest.php new file mode 100644 index 0000000000..d35fed101d --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Service/RefreshToken/RefreshTokenCreateServiceTest.php @@ -0,0 +1,63 @@ +setEventList(new EventList()); + } + + public function testRefreshTokenCreateService() + { + $cookieExpirationTime = '15 days'; + Configure::write(RefreshTokenAbstractService::REFRESH_TOKEN_EXPIRY_CONFIG_KEY, $cookieExpirationTime); + $expectedExpiration = (new FrozenTime('+' . $cookieExpirationTime))->toUnixString(); + $userId = UserFactory::make()->persist()->id; + $accessToken = 'Foo'; + + $token = (new RefreshTokenCreateService())->createToken(new ServerRequest(), $userId, $accessToken); + $cookie = (new RefreshTokenCreateService())->createHttpOnlySecureCookie($token); + /** @var FrozenTime $expiration */ + $expiration = $cookie->getExpiry(); + + $this->assertTrue($token->checkSessionId($accessToken)); + $this->assertTrue($cookie->isSecure()); + $this->assertTrue($cookie->isHttpOnly()); + $this->assertFalse($cookie->isExpired()); + // Allow a difference of five seconds second for CPU process + $this->assertLessThanOrEqual(5, (int)$expiration->toUnixString() - (int)$expectedExpiration); + + // Assert that the create refresh token event is dispatched + $this->assertEventFired(RefreshTokenCreateService::REFRESH_TOKEN_CREATED_EVENT); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/tests/TestCase/Service/RefreshToken/RefreshTokenFetchUserServiceTest.php b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Service/RefreshToken/RefreshTokenFetchUserServiceTest.php new file mode 100644 index 0000000000..fd9847236b --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Service/RefreshToken/RefreshTokenFetchUserServiceTest.php @@ -0,0 +1,66 @@ +loadModel('AuthenticationTokens'); + } + + public function testRefreshTokenFetchUserService_getUserIdFromToken_Success() + { + $refreshToken = AuthenticationTokenFactory::make() + ->active() + ->type(AuthenticationToken::TYPE_REFRESH_TOKEN) + ->persist(); + + $service = (new RefreshTokenAuthenticationService()); + $this->assertSame($refreshToken->user_id, $service->getUserIdFromToken($refreshToken->token)); + } + + /** + * No users are found for the refresh token type. + */ + public function testRefreshTokenFetchUserService_getUserIdFromToken_No_User_Found() + { + $refreshToken = AuthenticationTokenFactory::make() + ->active() + ->type(AuthenticationToken::TYPE_RECOVER) + ->persist(); + + $this->expectException(RefreshTokenNotFoundException::class); + $this->expectExceptionMessage('No active refresh token matching the request could be found.'); + + (new RefreshTokenAuthenticationService())->getUserIdFromToken($refreshToken->token); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/tests/TestCase/Service/RefreshToken/RefreshTokenLogoutServiceTest.php b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Service/RefreshToken/RefreshTokenLogoutServiceTest.php new file mode 100644 index 0000000000..254b9f588c --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Service/RefreshToken/RefreshTokenLogoutServiceTest.php @@ -0,0 +1,135 @@ +loadModel('AuthenticationTokens'); + $this->service = new RefreshTokenLogoutService(); + } + + public function tearDown(): void + { + unset($this->service); + } + + public function testRefreshTokenLogoutServiceTest_Logout_With_NonUUID_Refresh_Token() + { + $this->expectExceptionMessage(RefreshTokenNotFoundException::class); + $this->expectExceptionMessage('The refresh token should be a valid UUID.'); + $request = (new ServerRequest())->withData($this->service::REFRESH_TOKEN_DATA_KEY, 'Bar'); + $this->service->logout(UuidFactory::uuid(), $request); + } + + public function testRefreshTokenLogoutServiceTest_Logout_With_WrongToken() + { + $this->expectExceptionMessage(RefreshTokenNotFoundException::class); + $this->expectExceptionMessage('No active refresh token matching the request could be found.'); + $request = (new ServerRequest())->withData($this->service::REFRESH_TOKEN_DATA_KEY, UuidFactory::uuid()); + $this->service->logout(UuidFactory::uuid(), $request); + } + + public function testRefreshTokenLogoutServiceTest_Logout_With_Valid_Token() + { + $user = UserFactory::make()->persist(); + $tokenToDeactivate = $this->persistTokenSet($user); + $request = (new ServerRequest()) + ->withData($this->service::REFRESH_TOKEN_DATA_KEY, $tokenToDeactivate->token) + ->withData('user_id', UuidFactory::uuid()); // This mismatch is ignored by the JwtLogoutController + + $deactivated = $this->service->logout($user->id, $request); + + $this->assertSame(1, $deactivated); + $this->assertSame(5, AuthenticationTokenFactory::find()->where(['active' => true])->count()); + $this->assertTrue($this->AuthenticationTokens->exists([ + 'active' => false, + 'id' => $tokenToDeactivate->id, + ])); + } + + public function testRefreshTokenLogoutServiceTest_Logout_Without_Token() + { + $user = UserFactory::make()->persist(); + $this->persistTokenSet($user); + + $deactivated = $this->service->logout($user->id, new ServerRequest()); + + $this->assertSame(2, $deactivated); + $this->assertSame(4, $this->AuthenticationTokens->find()->where(['active' => true])->count()); + $this->assertSame(2, $this->AuthenticationTokens->find()->where([ + 'active' => false, + 'user_id' => $user->id, + 'type' => AuthenticationToken::TYPE_REFRESH_TOKEN, + ])->count()); + } + + /** + * Create a set of token. + * 1 active target + * 1 active of the same user as the target + * 2 active same user as the target of another type + * 2 active of another user and another type + * + * @param User $user + * @return AuthenticationToken A refresh token. + */ + private function persistTokenSet(User $user): AuthenticationToken + { + // Refresh tokens associated to user + /** @var array $refreshTokens */ + $refreshTokens = AuthenticationTokenFactory::make(2) + ->active() + ->userId($user->id) + ->type(AuthenticationToken::TYPE_REFRESH_TOKEN) + ->persist(); + + // Random tokens associated to user + AuthenticationTokenFactory::make(2) + ->active() + ->userId($user->id) + ->persist(); + + // Random tokens + AuthenticationTokenFactory::make(2) + ->active() + ->persist(); + + return $refreshTokens[0]; + } +} diff --git a/plugins/Passbolt/JwtAuthentication/tests/TestCase/Service/RefreshToken/RefreshTokenRenewalServiceTest.php b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Service/RefreshToken/RefreshTokenRenewalServiceTest.php new file mode 100644 index 0000000000..599841c6d4 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Service/RefreshToken/RefreshTokenRenewalServiceTest.php @@ -0,0 +1,94 @@ +loadModel('AuthenticationTokens'); + EventManager::instance()->setEventList(new EventList()); + } + + public function testRefreshTokenRenewalService_WithNoExistingRefreshCookie() + { + $userId = UserFactory::make()->persist()->id; + $newAccessToken = 'Bar'; + $authToken = (new RefreshTokenCreateService())->createToken(new ServerRequest(), $userId, 'Foo'); + + $tokenInTheRequest = $this->AuthenticationTokens->find()->firstOrFail(); + + $someUserTokenNotInvolvedInTheRenewal = AuthenticationTokenFactory::make() + ->type(AuthenticationToken::TYPE_REFRESH_TOKEN) + ->active() + ->userId($userId) + ->persist(); + + $service = new RefreshTokenRenewalService(); + $newToken = $service->renewToken(new ServerRequest(), $authToken, $newAccessToken); + $cookie = $service->createHttpOnlySecureCookie($newToken); + + $this->assertTrue($this->AuthenticationTokens->exists(['id' => $someUserTokenNotInvolvedInTheRenewal->id])); + /** @var AuthenticationToken $newRefreshToken */ + $newRefreshToken = $this->AuthenticationTokens->find()->where([ + 'type' => AuthenticationToken::TYPE_REFRESH_TOKEN, + 'token' => $cookie->getValue(), + 'active' => true, + 'user_id' => $userId, + ])->firstOrFail(); + + $this->assertTrue($newRefreshToken->checkSessionId($newAccessToken)); + $this->assertTrue($this->AuthenticationTokens->exists(['id' => $tokenInTheRequest->get('id'), 'active' => false])); + } + + public function testRefreshTokenRenewalService_Renew_On_Consumed_Token() + { + $userId = UserFactory::make()->persist()->id; + $authToken = (new RefreshTokenCreateService())->createToken(new ServerRequest(), $userId, 'Foo'); + + $service = new RefreshTokenRenewalService(); + // This is O.K. to renew once + $service->renewToken(new ServerRequest(), $authToken, 'Bar'); + + // This is not O.K. to renew again, should throw an exception and should send an Email to both user and admin + $this->expectException(ConsumedRefreshTokenAccessException::class); + $this->expectExceptionMessage('The refresh token provided was already used.'); + $service->renewToken(new ServerRequest(), $authToken, ''); + $this->assertEventFired(ConsumedRefreshTokenAccessException::class); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/tests/TestCase/Service/VerifyToken/VerifyTokenCreateServiceTest.php b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Service/VerifyToken/VerifyTokenCreateServiceTest.php new file mode 100644 index 0000000000..735a6789fd --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Service/VerifyToken/VerifyTokenCreateServiceTest.php @@ -0,0 +1,92 @@ +service = new VerifyTokenCreateService(); + $this->loadModel('AuthenticationTokens'); + } + + public function tearDown(): void + { + unset($this->service); + } + + public function testVerifyTokenCreateService_Valid() + { + $userId = UserFactory::make()->user()->persist()->id; + + // Old token for that user: should be deleted + AuthenticationTokenFactory::make() + ->type(AuthenticationToken::TYPE_VERIFY_TOKEN) + ->created(FrozenTime::now()->subHour()->subSecond()) + ->userId($userId) + ->persist(); + + // Old token for another user: should be deleted + AuthenticationTokenFactory::make() + ->type(AuthenticationToken::TYPE_VERIFY_TOKEN) + ->created(FrozenTime::now()->subHour()->subSecond()) + ->userId(UuidFactory::uuid()) + ->persist(); + + // Valid token for that user of another type: should not be deleted + AuthenticationTokenFactory::make() + ->type('Foo') + ->created(FrozenTime::now()->addMinute()) + ->userId($userId) + ->persist(); + + $token = UuidFactory::uuid(); + $this->service->createToken($token, $userId); + + $this->assertSame(2, $this->AuthenticationTokens->find()->count()); + $this->assertTrue($this->AuthenticationTokens->exists(compact('token'))); + $this->assertTrue($this->AuthenticationTokens->exists(['type' => 'Foo'])); + } +} diff --git a/plugins/Passbolt/JwtAuthentication/tests/TestCase/Service/VerifyToken/VerifyTokenValidationServiceTest.php b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Service/VerifyToken/VerifyTokenValidationServiceTest.php new file mode 100644 index 0000000000..f0a83ad227 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/tests/TestCase/Service/VerifyToken/VerifyTokenValidationServiceTest.php @@ -0,0 +1,145 @@ +service = new VerifyTokenValidationService(); + EventManager::instance()->setEventList(new EventList()); + } + + public function tearDown(): void + { + unset($this->service); + } + + /** + * Do not use data provider here, as data provider is compiled prior to the test suite. + * We need a one minute old (or something small) token, and thus since the suite takes some time, + * this token gets expired by the time the test is run. + * + * @throws \Exception + */ + public function testVerifyTokenValidationService_Valid() + { + $this->expectNotToPerformAssertions(); + + $expiry = FrozenTime::now()->addMinute()->toUnixString(); + $this->service->validateToken($expiry, UuidFactory::uuid(), UuidFactory::uuid()); + $this->service->validateToken((int)$expiry, UuidFactory::uuid(), UuidFactory::uuid()); + } + + /** + * @dataProvider invalidExpiryDates + */ + public function testVerifyTokenValidationService_InvalidExpiryDate($expiry) + { + $this->expectException(InvalidVerifyTokenException::class); + $this->expectExceptionMessage('Invalid verify token expiry.'); + $this->service->validateToken($expiry, 'Foo', 'Bar'); + } + + /** + * @dataProvider expiredExpiryDates + */ + public function testVerifyTokenValidationService_ExpiredExpiryDate($expiry) + { + $this->expectException(ExpiredVerifyTokenAccessException::class); + $this->expectExceptionMessage('Attempt to access an expired verify token.'); + $this->service->validateToken($expiry, 'Foo', 'Bar'); + $this->assertEventFired(ExpiredVerifyTokenAccessException::class); + } + + /** + * @dataProvider invalidFormats + */ + public function testVerifyTokenValidationService_InvalidFormat($token) + { + $this->expectException(InvalidVerifyTokenException::class); + $this->expectExceptionMessage('Invalid verify token format.'); + $this->service->validateToken(FrozenTime::now()->addMinute()->toUnixString(), $token, 'Bar'); + } + + public function testVerifyTokenValidationService_IsNotNonce() + { + $existingToken = AuthenticationTokenFactory::make(['id' => UuidFactory::uuid()]) + ->type(AuthenticationToken::TYPE_VERIFY_TOKEN) + ->persist(); + $userId = $existingToken->user_id; + $token = $existingToken->token; + $this->expectException(ConsumedVerifyTokenAccessException::class); + $this->expectExceptionMessage('Verify token has been already used in the past.'); + $this->service->validateToken(FrozenTime::now()->addMinute()->toUnixString(), $token, $userId); + $this->assertEventFired(ConsumedVerifyTokenAccessException::class); + } + + public function invalidExpiryDates(): array + { + return [ + [null], + [''], + [FrozenTime::now()->addHour(5)->toUnixString()], // This is past the max validity of one hour + [FrozenTime::now()->addMinute()], // This is not a unix time! + ]; + } + + public function expiredExpiryDates(): array + { + return [ + [FrozenTime::yesterday()->toUnixString()], + [FrozenTime::now()->subSecond()->toUnixString()], + ]; + } + + public function invalidFormats(): array + { + return [ + [null], + [''], + ['ABC'], + ]; + } +} diff --git a/plugins/Passbolt/JwtAuthentication/tests/Utility/JwtAuthTestTrait.php b/plugins/Passbolt/JwtAuthentication/tests/Utility/JwtAuthTestTrait.php new file mode 100644 index 0000000000..563f1b2f2d --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/tests/Utility/JwtAuthTestTrait.php @@ -0,0 +1,90 @@ +configRequest([ + 'headers' => [JwtAuthenticationService::JWT_HEADER => 'Bearer ' . $token], // Prefix with Bearer + ]); + } + + /** + * Creates a JWT access token and sets it in the request header. + * Returns that token + * + * @param string|null $userId + * @return string + */ + public function createJwtTokenAndSetInHeader(?string $userId = null): string + { + if ($userId === null) { + $userId = UserFactory::make()->user()->persist()->id; + } + $token = (new JwtTokenCreateService())->createToken($userId); + $this->setJwtTokenInHeader($token); + + return $token; + } + + ////////////// GPG Utils /////////////////// + + protected function getGpgJwtAuth(User $user): GpgJwtAuthenticator + { + $request = new ServerRequest(); + $request = $request->withData('user_id', $user->id); + + $GpgJwtAuth = new GpgJwtAuthenticator($this->createMock(IdentifierInterface::class)); + $GpgJwtAuth->setRequest($request); + $GpgJwtAuth->init(); + + return $GpgJwtAuth; + } + + protected function makeChallenge(User $user, string $verifyToken): string + { + return $this->getGpgJwtAuth($user)->getGpg()->encryptSign(json_encode([ + 'version' => GpgJwtAuthenticator::PROTOCOL_VERSION, + 'domain' => Router::url('/', true), + 'verify_token' => $verifyToken, + 'verify_token_expiry' => FrozenTime::now()->addMinute()->toUnixString(), + ])); + } + + protected function decryptChallenge(User $user, string $challenge): string + { + return $this->getGpgJwtAuth($user)->getGpg()->decrypt($challenge); + } +} diff --git a/plugins/Passbolt/EmailDigest/tests/TestCase/Unit/Service/EmailDigestServiceTest.php b/plugins/Passbolt/JwtAuthentication/tests/Utility/JwtAuthenticationIntegrationTestCase.php similarity index 53% rename from plugins/Passbolt/EmailDigest/tests/TestCase/Unit/Service/EmailDigestServiceTest.php rename to plugins/Passbolt/JwtAuthentication/tests/Utility/JwtAuthenticationIntegrationTestCase.php index 4fe98fa1a5..d11a9c1cf3 100644 --- a/plugins/Passbolt/EmailDigest/tests/TestCase/Unit/Service/EmailDigestServiceTest.php +++ b/plugins/Passbolt/JwtAuthentication/tests/Utility/JwtAuthenticationIntegrationTestCase.php @@ -12,39 +12,34 @@ * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) * @license https://opensource.org/licenses/AGPL-3.0 AGPL License * @link https://www.passbolt.com Passbolt(tm) - * @since 2.13.0 + * @since 2.0.0 */ - -namespace Passbolt\EmailDigest\Test\TestCase\Unit\Service; +namespace Passbolt\JwtAuthentication\Test\Utility; use App\Test\Lib\AppIntegrationTestCase; -use Passbolt\EmailDigest\Test\Lib\EmailDigestMockTestTrait; +use Passbolt\JwtAuthentication\Service\AccessToken\JwtKeyPairService; -class EmailDigestServiceTest extends AppIntegrationTestCase +abstract class JwtAuthenticationIntegrationTestCase extends AppIntegrationTestCase { - use EmailDigestMockTestTrait; + use JwtAuthTestTrait; /** - * @var MockObject|DigestFactory + * Setup. */ - //private $digestFactoryMock; - - /** - * @var EmailDigestService - */ - //private $emailDigestService; - public function setUp(): void { - //$this->digestFactoryMock = $this->createMock(DigestFactory::class); - - //$this->emailDigestService = new EmailDigestService($this->digestFactoryMock); - parent::setUp(); + (new JwtKeyPairService())->createKeyPair(); + $this->enableFeaturePlugin('JwtAuthentication'); + $this->disableCsrfToken(); } - public function testThatEmailsAreGroupedByRecipient() + /** + * Tear down + */ + public function tearDown(): void { - $this->markTestIncomplete(); + parent::tearDown(); + $this->disableFeaturePlugin('JwtAuthentication'); } } diff --git a/plugins/Passbolt/JwtAuthentication/tests/Utility/JwtTestTrait.php b/plugins/Passbolt/JwtAuthentication/tests/Utility/JwtTestTrait.php new file mode 100644 index 0000000000..2dc45b47c2 --- /dev/null +++ b/plugins/Passbolt/JwtAuthentication/tests/Utility/JwtTestTrait.php @@ -0,0 +1,30 @@ +getKeyPath()); + $res = JWT::decode($accessToken, $publicKey, array_keys(JWT::$supported_algs)); + $this->assertSame($userId, $res->sub); + } +} diff --git a/plugins/Passbolt/Locale/config/config.php b/plugins/Passbolt/Locale/config/config.php index 2aeae14e1f..23bdf12c7a 100644 --- a/plugins/Passbolt/Locale/config/config.php +++ b/plugins/Passbolt/Locale/config/config.php @@ -17,10 +17,18 @@ 'locale' => 'en-UK', 'label' => 'English', ], + [ + 'locale' => 'de-DE', + 'label' => 'Deutsch', + ], [ 'locale' => 'fr-FR', 'label' => 'Français', ], + [ + 'locale' => 'sv-SE', + 'label' => 'Svenska', + ], ], ], ], diff --git a/plugins/Passbolt/Locale/src/Event/AddLocaleBehavior.php b/plugins/Passbolt/Locale/src/Event/AddLocaleBehavior.php deleted file mode 100644 index 184f6de136..0000000000 --- a/plugins/Passbolt/Locale/src/Event/AddLocaleBehavior.php +++ /dev/null @@ -1,62 +0,0 @@ - $this, - ]; - } - - /** - * @param \Cake\Event\EventInterface $event Event - * @return void - */ - public function __invoke(EventInterface $event) - { - foreach (static::TABLES_TO_ADD as $allowed) { - if ($event->getSubject() instanceof $allowed) { - /** @var \Cake\ORM\Table $table */ - $table = $event->getSubject(); - $table->addBehavior(LocaleBehavior::class); - } - } - } -} diff --git a/plugins/Passbolt/Locale/src/Event/LocaleEmailQueueListener.php b/plugins/Passbolt/Locale/src/Event/LocaleEmailQueueListener.php index 1a7556386e..d754effc20 100644 --- a/plugins/Passbolt/Locale/src/Event/LocaleEmailQueueListener.php +++ b/plugins/Passbolt/Locale/src/Event/LocaleEmailQueueListener.php @@ -63,13 +63,13 @@ public function setLocaleInEmailTemplateVars(EventInterface $event, EntityInterf $template_vars = $entity->get('template_vars') ?? []; $isArray = is_array($template_vars); if (!$isArray) { - $template_vars = unserialize($template_vars); + $template_vars = json_decode($template_vars, true); } $template_vars[self::VIEW_VAR_KEY] = $locale; if (!$isArray) { - $template_vars = serialize($template_vars); + $template_vars = json_encode($template_vars); } $entity->set(compact('template_vars')); diff --git a/plugins/Passbolt/Locale/src/Event/LocaleRenderListener.php b/plugins/Passbolt/Locale/src/Event/LocaleRenderListener.php index 834bab1788..27160436b7 100644 --- a/plugins/Passbolt/Locale/src/Event/LocaleRenderListener.php +++ b/plugins/Passbolt/Locale/src/Event/LocaleRenderListener.php @@ -18,7 +18,7 @@ use Cake\Event\EventInterface; use Cake\Event\EventListenerInterface; -use Cake\I18n\I18n; +use Passbolt\Locale\Service\GetOrgLocaleService; use Passbolt\Locale\Service\LocaleService; class LocaleRenderListener implements EventListenerInterface @@ -42,8 +42,7 @@ public function implementedEvents(): array /** * Sets the locale if defined in the view variables. * This is required when sending emails. - * The locale previous to rendering is kept in memory in order - * to be reset after the rendering is done. + * Note that this is enables for CLI tools only and at this point no user is logged in. * * @param \Cake\Event\EventInterface $event Event. * @return void @@ -56,21 +55,17 @@ public function setLocaleBeforeRenderIfDefinedInTemplateVar(EventInterface $even $locale = $View->get(LocaleEmailQueueListener::VIEW_VAR_KEY); $service = new LocaleService(); if ($service->isValidLocale($locale)) { - // Remember the locale before render. - self::$localeBeforeRender = self::$localeBeforeRender ?? I18n::getLocale(); $service->setLocale($locale); } } /** - * Reset the locale if set on memory prior to rendering. + * Reset the locale to the organization default locale. * * @return void */ public function resetLocaleAfterLayoutIfDefinedInTemplateVar(): void { - $service = new LocaleService(); - $service->setLocaleIfIsValid(self::$localeBeforeRender); - self::$localeBeforeRender = null; + (new LocaleService())->setLocale(GetOrgLocaleService::getLocale()); } } diff --git a/plugins/Passbolt/Locale/src/Event/SaveUserLocaleListener.php b/plugins/Passbolt/Locale/src/Event/SaveUserLocaleListener.php index 3de651703e..50c22ffa5d 100644 --- a/plugins/Passbolt/Locale/src/Event/SaveUserLocaleListener.php +++ b/plugins/Passbolt/Locale/src/Event/SaveUserLocaleListener.php @@ -58,6 +58,7 @@ public function setUserLocaleIfFoundInPayload(EventInterface $event): void return; } + /** @var \App\Model\Entity\User $user */ $user = $event->getData('user'); $service = new SetUserLocaleService(); if ($service->isValidLocale($locale)) { diff --git a/plugins/Passbolt/Locale/src/Model/Behavior/LocaleBehavior.php b/plugins/Passbolt/Locale/src/Model/Behavior/LocaleBehavior.php index 7d36188458..9e97b8fbed 100644 --- a/plugins/Passbolt/Locale/src/Model/Behavior/LocaleBehavior.php +++ b/plugins/Passbolt/Locale/src/Model/Behavior/LocaleBehavior.php @@ -17,11 +17,11 @@ namespace Passbolt\Locale\Model\Behavior; -use App\Model\Entity\User; use Cake\Core\InstanceConfigTrait; use Cake\Datasource\EntityInterface; use Cake\ORM\Behavior; use Cake\ORM\Query; +use Passbolt\Locale\Service\GetOrgLocaleService; /** * Decorate a Table class to add the "locale" property on its entities. @@ -64,10 +64,10 @@ class LocaleBehavior extends Behavior */ public function initialize(array $config): void { - $this->_table->hasOne('Locale', [ + $this->table()->hasOne('Locale', [ 'className' => 'Passbolt/AccountSettings.AccountSettings', 'foreignKey' => 'user_id', - 'conditions' => ['property' => self::LOCALE_PROPERTY], + 'conditions' => ['Locale.property' => self::LOCALE_PROPERTY], ]); } @@ -87,6 +87,8 @@ public function findLocale(Query $query): Query /** * Format a query result and associate to each item its locale. + * The locale is either found in the association, or if not + * the organization locale is taken. * * @param \Cake\ORM\Query $query The target query. * @return \Cake\ORM\Query @@ -95,9 +97,12 @@ public function formatResults(Query $query): Query { return $query->formatResults(function (\Cake\Collection\CollectionInterface $results) { return $results->map(function ($entity) { - if ($entity instanceof User && !is_null($entity->locale)) { - $entity = $this->addLocalePropertyToEntity($entity, $entity->locale->value); + if (is_null($entity->locale)) { + $locale = GetOrgLocaleService::getLocale(); + } else { + $locale = $entity->locale->value; } + $entity = $this->addLocalePropertyToEntity($entity, $locale); return $entity; }); diff --git a/plugins/Passbolt/Locale/src/Plugin.php b/plugins/Passbolt/Locale/src/Plugin.php index 7ff05ca6f2..e017456f6e 100644 --- a/plugins/Passbolt/Locale/src/Plugin.php +++ b/plugins/Passbolt/Locale/src/Plugin.php @@ -21,7 +21,6 @@ use Cake\Core\PluginApplicationInterface; use Cake\Event\EventManager; use Cake\Http\MiddlewareQueue; -use Passbolt\Locale\Event\AddLocaleBehavior; use Passbolt\Locale\Event\LocaleEmailQueueListener; use Passbolt\Locale\Event\LocaleRenderListener; use Passbolt\Locale\Event\SaveUserLocaleListener; @@ -61,8 +60,7 @@ public function attachListeners(EventManager $eventManager): void $eventManager ->on(new LocaleEmailQueueListener()) ->on(new SaveUserLocaleListener()) - ->on(new ValidateLocaleOnBeforeSaveListener()) - ->on(new AddLocaleBehavior()); + ->on(new ValidateLocaleOnBeforeSaveListener()); if (PHP_SAPI === 'cli') { $eventManager->on(new LocaleRenderListener()); diff --git a/plugins/Passbolt/Locale/src/Service/GetOrgLocaleService.php b/plugins/Passbolt/Locale/src/Service/GetOrgLocaleService.php index 1320d94bb0..5e64daa54b 100644 --- a/plugins/Passbolt/Locale/src/Service/GetOrgLocaleService.php +++ b/plugins/Passbolt/Locale/src/Service/GetOrgLocaleService.php @@ -18,10 +18,11 @@ namespace Passbolt\Locale\Service; use App\Model\Entity\OrganizationSetting; -use Cake\I18n\I18n; class GetOrgLocaleService extends LocaleService { + public const DEFAULT_LOCALE = 'en-UK'; + /** * @var string|null */ @@ -81,7 +82,7 @@ protected function getOrganizationLocale(): string if ($setting instanceof OrganizationSetting) { self::$organisationLocale = $setting->get('value'); } else { - self::$organisationLocale = static::dasherizeLocale(I18n::getDefaultLocale()); + self::$organisationLocale = self::DEFAULT_LOCALE; } return self::$organisationLocale; diff --git a/plugins/Passbolt/Locale/src/Service/LocaleService.php b/plugins/Passbolt/Locale/src/Service/LocaleService.php index 93ffa644f6..0d0a95ee38 100644 --- a/plugins/Passbolt/Locale/src/Service/LocaleService.php +++ b/plugins/Passbolt/Locale/src/Service/LocaleService.php @@ -34,9 +34,9 @@ class LocaleService /** * Read in configuration the locales available * - * @return array + * @return array|\ArrayAccess */ - public static function getSystemLocales(): array + public static function getSystemLocales() { return Hash::extract( Configure::readOrFail('passbolt.plugins.locale.options'), @@ -119,4 +119,32 @@ public function underscoreLocale(?string $locale = ''): string { return str_replace('-', '_', $locale); } + + /** + * Translates a string in a given locale. + * This is useful for example when translating the subject of + * emails in the recipient's locale. + * + * The callable should return a string. + * Example: + * $subject = (new LocaleService())->translateString( + * $user->locale, + * function () use ($user) { + * return __('Welcome to passbolt, {0}!', $user->profile->first_name); + * } + * ); + * + * @param string $locale Destination locale + * @param callable $callable returns the __("string to be translated") + * @return string + */ + public function translateString(string $locale, callable $callable): string + { + $backupLocale = I18n::getLocale(); + $this->setLocaleIfIsValid($locale); + $subject = $callable(); + $this->setLocaleIfIsValid($backupLocale); + + return $subject; + } } diff --git a/plugins/Passbolt/Locale/tests/Lib/DummyTranslationTestTrait.php b/plugins/Passbolt/Locale/tests/Lib/DummyTranslationTestTrait.php index 6922ef97a1..62769711cb 100644 --- a/plugins/Passbolt/Locale/tests/Lib/DummyTranslationTestTrait.php +++ b/plugins/Passbolt/Locale/tests/Lib/DummyTranslationTestTrait.php @@ -18,25 +18,22 @@ use App\Utility\Filesystem\DirectoryUtility; use Cake\I18n\I18n; -use Cake\I18n\Package; trait DummyTranslationTestTrait { /** * Set some translations for testing purpose. + * + * @psalm-suppress InternalMethod */ public function setDummyFrenchTranslator(): void { - I18n::setTranslator('test', function () { - $package = new Package(); - $package->setMessages([ - 'Sending email from: {0}' => 'Courriel envoyé de: {0}', - 'Sending email to: {0}' => 'Courriel envoyé à: {0}', - $this->getDummyEnglishEmailSentence() => $this->getDummyFrenchEmailSentence(), - ]); - - return $package; - }, 'fr_FR'); + /** @psalm-suppress InternalMethod */ + I18n::getTranslator('default', 'fr_FR')->getPackage()->addMessages([ + 'Sending email from: {0}' => 'Courriel envoyé de: {0}', + 'Sending email to: {0}' => 'Courriel envoyé à: {0}', + $this->getDummyEnglishEmailSentence() => $this->getDummyFrenchEmailSentence(), + ]); } public function clearTranslationCache() diff --git a/plugins/Passbolt/Locale/tests/TestCase/Controller/AccountLocalesSelectControllerTest.php b/plugins/Passbolt/Locale/tests/TestCase/Controller/AccountLocalesSelectControllerTest.php index e9bef41568..23f24a301f 100644 --- a/plugins/Passbolt/Locale/tests/TestCase/Controller/AccountLocalesSelectControllerTest.php +++ b/plugins/Passbolt/Locale/tests/TestCase/Controller/AccountLocalesSelectControllerTest.php @@ -19,21 +19,23 @@ use App\Test\Factory\UserFactory; use App\Test\Lib\AppIntegrationTestCase; -use Cake\ORM\TableRegistry; +use Cake\Datasource\ModelAwareTrait; use Passbolt\Locale\Service\GetOrgLocaleService; use Passbolt\Locale\Service\LocaleService; +/** + * Class AccountLocalesSelectControllerTest + * + * @property \Passbolt\AccountSettings\Model\Table\AccountSettingsTable $AccountSettings + */ class AccountLocalesSelectControllerTest extends AppIntegrationTestCase { - /** - * @var AccountSettingsTable - */ - public $accountSettings; + use ModelAwareTrait; public function setUp(): void { parent::setUp(); - $this->accountSettings = TableRegistry::getTableLocator()->get('Passbolt/AccountSettings.AccountSettings'); + $this->loadModel('Passbolt/AccountSettings.AccountSettings'); } public function tearDown(): void @@ -58,7 +60,7 @@ public function testAccountLocalesSelectSuccess() $this->assertResponseSuccess(); $this->assertSame( $value, - $this->accountSettings->getByProperty($user->id, LocaleService::SETTING_PROPERTY)->get('value') + $this->AccountSettings->getByProperty($user->id, LocaleService::SETTING_PROPERTY)->get('value') ); } diff --git a/plugins/Passbolt/Locale/tests/TestCase/Controller/OrganizationLocalesSelectControllerTest.php b/plugins/Passbolt/Locale/tests/TestCase/Controller/OrganizationLocalesSelectControllerTest.php index 9b5a3be04a..a39542a51d 100644 --- a/plugins/Passbolt/Locale/tests/TestCase/Controller/OrganizationLocalesSelectControllerTest.php +++ b/plugins/Passbolt/Locale/tests/TestCase/Controller/OrganizationLocalesSelectControllerTest.php @@ -18,21 +18,23 @@ namespace Passbolt\Locale\Test\TestCase\Controller; use App\Test\Lib\AppIntegrationTestCase; -use Cake\ORM\TableRegistry; +use Cake\Datasource\ModelAwareTrait; use Passbolt\Locale\Service\GetOrgLocaleService; use Passbolt\Locale\Service\LocaleService; +/** + * Class OrganizationLocalesSelectControllerTest + * + * @property \App\Model\Table\OrganizationSettingsTable $OrganizationSettings + */ class OrganizationLocalesSelectControllerTest extends AppIntegrationTestCase { - /** - * @var OrganizationSettingsTable - */ - public $organizationSettings; + use ModelAwareTrait; public function setUp(): void { parent::setUp(); - $this->organizationSettings = TableRegistry::getTableLocator()->get('OrganizationSettings'); + $this->loadModel('OrganizationSettings'); } public function tearDown(): void @@ -56,7 +58,7 @@ public function testOrganizationLocalesSelectSuccess() $this->assertResponseSuccess(); $this->assertSame( $value, - $this->organizationSettings->getByProperty(LocaleService::SETTING_PROPERTY)->get('value') + $this->OrganizationSettings->getByProperty(LocaleService::SETTING_PROPERTY)->get('value') ); } diff --git a/plugins/Passbolt/Locale/tests/TestCase/Event/ValidateLocaleOnBeforeSaveListenerTest.php b/plugins/Passbolt/Locale/tests/TestCase/Event/ValidateLocaleOnBeforeSaveListenerTest.php index df813ba692..b1b8b4ee56 100644 --- a/plugins/Passbolt/Locale/tests/TestCase/Event/ValidateLocaleOnBeforeSaveListenerTest.php +++ b/plugins/Passbolt/Locale/tests/TestCase/Event/ValidateLocaleOnBeforeSaveListenerTest.php @@ -28,11 +28,13 @@ class ValidateLocaleOnBeforeSaveListenerTest extends TestCase { public function setUp(): void { + parent::setUp(); $this->loadPlugins(['Passbolt/Locale']); } public function tearDown(): void { + parent::tearDown(); GetOrgLocaleService::clearOrganisationLocale(); } @@ -52,7 +54,7 @@ public function dataForTestEmailLocaleServiceGetLocale(): array /** * @param string|null $recipient The email's recipient - * @param bool|null $expectException + * @param string $expectException * @throws \Exception * @dataProvider dataForTestEmailLocaleServiceGetLocale */ diff --git a/plugins/Passbolt/Locale/tests/TestCase/Middleware/LocaleMiddlewareTest.php b/plugins/Passbolt/Locale/tests/TestCase/Middleware/LocaleMiddlewareTest.php index c8abd87e73..000ebb820d 100644 --- a/plugins/Passbolt/Locale/tests/TestCase/Middleware/LocaleMiddlewareTest.php +++ b/plugins/Passbolt/Locale/tests/TestCase/Middleware/LocaleMiddlewareTest.php @@ -28,7 +28,7 @@ class LocaleMiddlewareTest extends AppIntegrationTestCase public function tearDown(): void { parent::tearDown(); - I18n::setLocale(\Locale::getDefault()); + I18n::setLocale('en_UK'); } public function testLocaleMiddlewareUnauthenticatedRequestWithOrgSetting() diff --git a/plugins/Passbolt/Locale/tests/TestCase/Model/Behavior/LocaleBehaviorTest.php b/plugins/Passbolt/Locale/tests/TestCase/Model/Behavior/LocaleBehaviorTest.php index 885f4e0d49..5a4b3254dc 100644 --- a/plugins/Passbolt/Locale/tests/TestCase/Model/Behavior/LocaleBehaviorTest.php +++ b/plugins/Passbolt/Locale/tests/TestCase/Model/Behavior/LocaleBehaviorTest.php @@ -20,6 +20,7 @@ use App\Test\Factory\UserFactory; use Cake\ORM\TableRegistry; use Cake\TestSuite\TestCase; +use Passbolt\Locale\Service\GetOrgLocaleService; use Passbolt\Locale\Test\Lib\DummySystemLocaleTestTrait; class LocaleBehaviorTest extends TestCase @@ -43,6 +44,8 @@ public function setUp(): void */ public function testFindContainLocale(): void { + GetOrgLocaleService::clearOrganisationLocale(); + UserFactory::make(['username' => 'ada@passbolt.com']) ->withLocale('fr-FR') ->persist(); @@ -58,18 +61,18 @@ public function testFindContainLocale(): void ->where(['username' => 'ada@passbolt.com']) ->contain('Locale') ->first(); - $this->assertEquals('fr-FR', $user->locale); + $this->assertEquals('fr-FR', $user->get('locale')); $user = $this->usersTable->find('locale') ->where(['username' => 'betty@passbolt.com']) ->contain('Locale') ->first(); - $this->assertEquals('en-UK', $user->locale); + $this->assertEquals('en-UK', $user->get('locale')); $user = $this->usersTable->find('locale') ->where(['username' => 'carol@passbolt.com']) ->contain('Locale') ->first(); - $this->assertNull($user->locale); + $this->assertEquals('en-UK', $user->get('locale')); } } diff --git a/plugins/Passbolt/Locale/tests/TestCase/Notification/ResourcesAddAndShareControllerTest.php b/plugins/Passbolt/Locale/tests/TestCase/Notification/ResourcesAddAndShareControllerTest.php new file mode 100644 index 0000000000..7ccb68b724 --- /dev/null +++ b/plugins/Passbolt/Locale/tests/TestCase/Notification/ResourcesAddAndShareControllerTest.php @@ -0,0 +1,100 @@ +default()->persist(); + GetOrgLocaleService::clearOrganisationLocale(); + $this->setEmailNotificationsSetting('password.create', true); + } + + public function tearDown(): void + { + parent::tearDown(); + $this->restoreEmailNotificationsSettings(); + } + + public function testResourcesAdd_Should_Send_Email_In_User_Locale() + { + $frenchLocale = 'fr-FR'; + $frenchUser = UserFactory::make()->user()->withLocale($frenchLocale)->persist(); + + $this->logInAs($frenchUser); + + $data = $this->getDummyResourcesPostData([ + 'name' => '新的專用資源名稱', + 'username' => 'username@domain.com', + 'uri' => 'https://www.域.com', + 'description' => '新的資源描述', + ]); + + $this->postJson('/resources.json?api-version=2', $data); + $this->assertSuccess(); + $this->assertEmailQueueCount(1); + $this->assetEmailLocale($frenchUser->username, $frenchLocale); + } + + public function testResourcesShare_Should_Send_Email_In_User_Locale() + { + $frenchLocale = 'fr-FR'; + $englishLocale = GetOrgLocaleService::DEFAULT_LOCALE; + $this->assertSame($englishLocale, GetOrgLocaleService::getLocale()); + + $defaultUser = UserFactory::make()->user()->persist(); + $frenchUser = UserFactory::make()->user()->withLocale($frenchLocale)->persist(); + $englishUser = UserFactory::make()->user()->withLocale($englishLocale)->persist(); + $frenchUser2 = UserFactory::make()->user()->withLocale($frenchLocale)->persist(); + + $resource = ResourceFactory::make()->withCreatorAndPermission($frenchUser)->persist(); + + $data = []; + $data['permissions'][] = ['aro' => 'User', 'aro_foreign_key' => $defaultUser->id, 'type' => Permission::OWNER]; + $data['permissions'][] = ['aro' => 'User', 'aro_foreign_key' => $englishUser->id, 'type' => Permission::OWNER]; + $data['permissions'][] = ['aro' => 'User', 'aro_foreign_key' => $frenchUser2->id, 'type' => Permission::OWNER]; + $data['secrets'][] = ['user_id' => $defaultUser->id, 'data' => SecretFactory::make()->getEntity()->data]; + $data['secrets'][] = ['user_id' => $englishUser->id, 'data' => SecretFactory::make()->getEntity()->data]; + $data['secrets'][] = ['user_id' => $frenchUser2->id, 'data' => SecretFactory::make()->getEntity()->data]; + + $this->logInAs($frenchUser); + $this->putJson("/share/resource/{$resource->id}.json?api-version=v2", $data); + $this->assertResponseOk(); + + $this->assertEmailQueueCount(3); + $this->assetEmailLocale($defaultUser->username, $englishLocale); + $this->assetEmailLocale($englishUser->username, $englishLocale); + $this->assetEmailLocale($frenchUser2->username, $frenchLocale); + } +} diff --git a/plugins/Passbolt/Locale/tests/TestCase/Service/GetUserLocaleServiceTest.php b/plugins/Passbolt/Locale/tests/TestCase/Service/GetUserLocaleServiceTest.php index 538db985fa..5581ff7ffa 100644 --- a/plugins/Passbolt/Locale/tests/TestCase/Service/GetUserLocaleServiceTest.php +++ b/plugins/Passbolt/Locale/tests/TestCase/Service/GetUserLocaleServiceTest.php @@ -30,6 +30,7 @@ class GetUserLocaleServiceTest extends TestCase public function setUp(): void { + parent::setUp(); $this->loadPlugins(['Passbolt/Locale']); $this->addFooSystemLocale(); } @@ -38,6 +39,7 @@ public function tearDown(): void { GetOrgLocaleService::clearOrganisationLocale(); $this->removeFooSystemLocale(); + parent::tearDown(); } public function dataForTestGetUserLocaleServiceGetLocale(): array diff --git a/plugins/Passbolt/Locale/tests/TestCase/Service/LocaleServiceTest.php b/plugins/Passbolt/Locale/tests/TestCase/Service/LocaleServiceTest.php index 9627ec1adc..5876396a69 100644 --- a/plugins/Passbolt/Locale/tests/TestCase/Service/LocaleServiceTest.php +++ b/plugins/Passbolt/Locale/tests/TestCase/Service/LocaleServiceTest.php @@ -17,14 +17,20 @@ namespace Passbolt\Locale\Test\TestCase\Service; +use Cake\I18n\I18n; use Cake\TestSuite\TestCase; use Passbolt\Locale\Service\LocaleService; +use Passbolt\Locale\Test\Lib\DummyTranslationTestTrait; class LocaleServiceTest extends TestCase { + use DummyTranslationTestTrait; + public function setUp(): void { + parent::setUp(); $this->loadPlugins(['Passbolt/Locale']); + I18n::setLocale('en_UK'); } /** @@ -34,11 +40,13 @@ public function testGetSystemLocales(): void { $this->assertSame([ 'en-UK', + 'de-DE', 'fr-FR', + 'sv-SE', ], LocaleService::getSystemLocales()); } - public function dataForTestLocaleUtilityLocaleIsValid(): array + public function dataForTestLocaleServiceLocaleIsValid(): array { return [ ['en-UK', true], @@ -53,9 +61,9 @@ public function dataForTestLocaleUtilityLocaleIsValid(): array /** * @param string|null $locale * @param bool $expected - * @dataProvider dataForTestLocaleUtilityLocaleIsValid + * @dataProvider dataForTestLocaleServiceLocaleIsValid */ - public function testLocaleUtilityLocaleIsValid(?string $locale, bool $expected): void + public function testLocaleServiceLocaleIsValid(?string $locale, bool $expected): void { $service = new LocaleService(); $this->assertSame( @@ -63,4 +71,80 @@ public function testLocaleUtilityLocaleIsValid(?string $locale, bool $expected): $service->isValidLocale($locale) ); } + + public function dataProviderForTestLocaleServiceLocaleTranslateString_On_Existing_Locale_English_Default() + { + return [ + ['fr-FR', 'Courriel envoyé de: admin@passbolt.com'], + ['fr_FR', 'Courriel envoyé de: admin@passbolt.com'], + ['en-UK', 'Sending email from: admin@passbolt.com'], + ['en_UK', 'Sending email from: admin@passbolt.com'], + ['foo_BAR', 'Sending email from: admin@passbolt.com'], + ['', 'Sending email from: admin@passbolt.com'], + ]; + } + + public function dataProviderForTestLocaleServiceLocaleTranslateString_On_Existing_Locale_French_Default() + { + return [ + ['fr-FR', 'Courriel envoyé de: admin@passbolt.com'], + ['fr_FR', 'Courriel envoyé de: admin@passbolt.com'], + ['en-UK', 'Sending email from: admin@passbolt.com'], + ['en_UK', 'Sending email from: admin@passbolt.com'], + ['foo_BAR', 'Courriel envoyé de: admin@passbolt.com'], + ['', 'Courriel envoyé de: admin@passbolt.com'], + ]; + } + + public function testLocaleServiceLocaleTranslateString_Plain_String() + { + $this->setDummyFrenchTranslator(); + $service = new LocaleService(); + $translation = $service->translateString('fr-FR', function () { + return __('This is an email in english.'); + }); + + $this->assertSame($this->getDummyFrenchEmailSentence(), $translation); + // Ensure that the locale is set to the original one. + $this->assertSame('en_UK', I18n::getLocale()); + } + + /** + * @param string $locale locale to translate + * @param string $expectedSubject expected translation + * @dataProvider dataProviderForTestLocaleServiceLocaleTranslateString_On_Existing_Locale_English_Default + */ + public function testLocaleServiceLocaleTranslateString(string $locale, string $expectedSubject) + { + $this->setDummyFrenchTranslator(); + + $service = new LocaleService(); + $translation = $service->translateString($locale, function () { + return __('Sending email from: {0}', 'admin@passbolt.com'); + }); + + $this->assertSame($expectedSubject, $translation); + // Ensure that the locale is set to the original one. + $this->assertSame('en_UK', I18n::getLocale()); + } + + /** + * @param string $locale locale to translate + * @param string $expectedSubject expected translation + * @dataProvider dataProviderForTestLocaleServiceLocaleTranslateString_On_Existing_Locale_French_Default + */ + public function testLocaleServiceLocaleTranslateString_With_French_Org_Setting(string $locale, string $expectedSubject) + { + I18n::setLocale('fr_FR'); + $this->setDummyFrenchTranslator(); + + $service = new LocaleService(); + $translation = $service->translateString($locale, function () { + return __('Sending email from: {0}', 'admin@passbolt.com'); + }); + + $this->assertSame($expectedSubject, $translation); + // Ensure that the locale is set to the original one. + $this->assertSame('fr_FR', I18n::getLocale()); + } } diff --git a/plugins/Passbolt/Log/config/config.php b/plugins/Passbolt/Log/config/config.php index 5dc74dd780..31cda94648 100644 --- a/plugins/Passbolt/Log/config/config.php +++ b/plugins/Passbolt/Log/config/config.php @@ -9,6 +9,7 @@ // The actions listed in the blacklist will not be logged. 'blackList' => [ 'AuthIsAuthenticated.isAuthenticated', + 'TransfersView.view', ], ], ], diff --git a/plugins/Passbolt/Log/src/Events/Traits/EntitiesHistoryTrait.php b/plugins/Passbolt/Log/src/Events/Traits/EntitiesHistoryTrait.php index 3bc653fbec..1b67a0e48e 100644 --- a/plugins/Passbolt/Log/src/Events/Traits/EntitiesHistoryTrait.php +++ b/plugins/Passbolt/Log/src/Events/Traits/EntitiesHistoryTrait.php @@ -24,6 +24,9 @@ trait EntitiesHistoryTrait { + /** + * @var array + */ private $config = [ 'Share.share' => [ 'models' => [ diff --git a/plugins/Passbolt/Log/src/Model/Table/SecretsHistoryTable.php b/plugins/Passbolt/Log/src/Model/Table/SecretsHistoryTable.php index 4f2821b3ea..b15f118872 100644 --- a/plugins/Passbolt/Log/src/Model/Table/SecretsHistoryTable.php +++ b/plugins/Passbolt/Log/src/Model/Table/SecretsHistoryTable.php @@ -146,16 +146,16 @@ public function create(array $data): SecretHistory $secretHistory = $this->save($secretHistory); - // Check for validation errors. (associated models too). - if (!empty($secretHistory->getErrors())) { - throw new ValidationException(__('Could not validate secret history data.'), $secretHistory, $this); - } - // Check for errors while saving. if (!$secretHistory) { throw new InternalErrorException('Could not save the secret history.'); } + // Check for validation errors. (associated models too). + if (!empty($secretHistory->getErrors())) { + throw new ValidationException(__('Could not validate secret history data.'), $secretHistory, $this); + } + return $secretHistory; } } diff --git a/plugins/Passbolt/Log/tests/Factory/ActionLogFactory.php b/plugins/Passbolt/Log/tests/Factory/ActionLogFactory.php index 036e51aa5c..fe0450a116 100644 --- a/plugins/Passbolt/Log/tests/Factory/ActionLogFactory.php +++ b/plugins/Passbolt/Log/tests/Factory/ActionLogFactory.php @@ -33,8 +33,8 @@ protected function setDefaultTemplate(): void { $this->setDefaultData(function (Generator $faker) { return [ - 'user_id' => $faker->uuid, - 'action_id' => $faker->uuid, + 'user_id' => $faker->uuid(), + 'action_id' => $faker->uuid(), 'context' => $faker->text(255), 'status' => 1, 'created' => Chronos::now()->subDay($faker->randomNumber(4)), diff --git a/plugins/Passbolt/Log/tests/Lib/LogIntegrationTestCase.php b/plugins/Passbolt/Log/tests/Lib/LogIntegrationTestCase.php index 8697bdb96b..65623a528d 100644 --- a/plugins/Passbolt/Log/tests/Lib/LogIntegrationTestCase.php +++ b/plugins/Passbolt/Log/tests/Lib/LogIntegrationTestCase.php @@ -16,10 +16,13 @@ */ namespace Passbolt\Log\Test\Lib; +use App\Model\Entity\User; use App\Test\Lib\AppIntegrationTestCase; use App\Utility\UserAction; use Cake\Core\Configure; use Cake\ORM\TableRegistry; +use Passbolt\JwtAuthentication\Service\AccessToken\JwtKeyPairService; +use Passbolt\JwtAuthentication\Test\Utility\JwtAuthTestTrait; use Passbolt\Log\Test\Lib\Traits\ActionLogsTrait; use Passbolt\Log\Test\Lib\Traits\EntitiesHistoryTrait; @@ -27,34 +30,38 @@ abstract class LogIntegrationTestCase extends AppIntegrationTestCase { use ActionLogsTrait; use EntitiesHistoryTrait; + use JwtAuthTestTrait; + + public const JWT_LOGIN = 'jwt_login'; + public const SESSION_LOGIN = 'session_login'; /** - * @var ResourcesTable + * @var \App\Model\Table\ResourcesTable */ protected $Resources; /** - * @var PermissionsTable + * @var \App\Model\Table\PermissionsTable */ protected $Permissions; /** - * @var SecretsTable + * @var \App\Model\Table\SecretsTable */ protected $Secrets; /** - * @var SecretAccesses + * @var \Passbolt\Log\Model\Table\SecretAccessesTable */ protected $SecretAccesses; /** - * @var EntitiesHistoryTable + * @var \Passbolt\Log\Model\Table\EntitiesHistoryTable */ protected $EntitiesHistory; /** - * @var ActionLog + * @var \Passbolt\Log\Model\Table\ActionLogsTable */ protected $ActionLogs; @@ -89,11 +96,32 @@ public function setUp(): void $this->SecretAccesses->belongsTo('Passbolt/Log.EntitiesHistory', [ 'foreignKey' => 'foreign_key', ]); + $this->enableFeaturePlugin('JwtAuthentication'); + (new JwtKeyPairService())->createKeyPair(); } public function tearDown(): void { // Remove dynamically added associations TableRegistry::getTableLocator()->clear(); + $this->disableFeaturePlugin('JwtAuthentication'); + } + + public function dataProviderForLoginType(): array + { + return [[self::SESSION_LOGIN], [self::JWT_LOGIN]]; + } + + /** + * @param string $loginType Login Type (JWT or SESSION) + * @param User $user User to log in + */ + public function loginWithDataProviderLoginTypeValue(string $loginType, User $user) + { + if ($loginType === self::JWT_LOGIN) { + $this->createJwtTokenAndSetInHeader($user->id); + } else { + $this->logInAs($user); + } } } diff --git a/plugins/Passbolt/Log/tests/Lib/Traits/ActionLogsTrait.php b/plugins/Passbolt/Log/tests/Lib/Traits/ActionLogsTrait.php index 5dbe58212f..82dcdcb2f5 100644 --- a/plugins/Passbolt/Log/tests/Lib/Traits/ActionLogsTrait.php +++ b/plugins/Passbolt/Log/tests/Lib/Traits/ActionLogsTrait.php @@ -49,6 +49,7 @@ public function addActionLog(?array $data = [], ?array $options = []): ActionLog */ public function getDummyActionLogEntity(?array $data = [], ?array $options = []): ActionLog { + /** @var \Passbolt\Log\Model\Table\ActionLogsTable $actionLogsTable */ $actionLogsTable = TableRegistry::getTableLocator()->get('Passbolt/Log.ActionLogs'); $defaultOptions = [ 'checkRules' => true, @@ -105,7 +106,7 @@ public function assertActionLogsCount($expectedCount) public function assertOneActionLog() { - return $this->assertActionLogsCount(1); + $this->assertActionLogsCount(1); } public function assertActionLogIdMatchesResponse($id, $response) diff --git a/plugins/Passbolt/Log/tests/TestCase/Controller/Resources/ResourcesControllerLogTest.php b/plugins/Passbolt/Log/tests/TestCase/Controller/Resources/ResourcesControllerLogTest.php index a66f44fd98..84b7ac8d27 100644 --- a/plugins/Passbolt/Log/tests/TestCase/Controller/Resources/ResourcesControllerLogTest.php +++ b/plugins/Passbolt/Log/tests/TestCase/Controller/Resources/ResourcesControllerLogTest.php @@ -17,6 +17,8 @@ namespace Passbolt\Log\Test\TestCase\Controller\Resources; +use App\Test\Factory\ResourceTypeFactory; +use App\Test\Factory\UserFactory; use App\Utility\UuidFactory; use Cake\Utility\Hash; use Passbolt\Log\Model\Entity\EntityHistory; @@ -30,10 +32,16 @@ class ResourcesControllerLogTest extends LogIntegrationTestCase 'app.Base/Secrets', 'app.Base/Favorites', ]; - public function testLogResourcesAddSuccessWithSecrets() + public $autoFixtures = false; + + /** + * @dataProvider dataProviderForLoginType + */ + public function testLogResourcesAddSuccessWithSecrets(string $loginType) { - $this->authenticateAs('ada'); - $userId = UuidFactory::uuid('user.id.ada'); + ResourceTypeFactory::make()->default()->persist(); + $user = UserFactory::make()->user()->persist(); + $this->loginWithDataProviderLoginTypeValue($loginType, $user); $data = [ 'name' => 'new resource name', 'username' => 'username@domain.com', @@ -53,7 +61,7 @@ public function testLogResourcesAddSuccessWithSecrets() $this->assertOneActionLog(); $actionLog = $this->assertActionLogExists([ 'action_id' => UuidFactory::uuid('ResourcesAdd.add'), - 'user_id' => $userId, + 'user_id' => $user->id, 'status' => 1, ]); $this->assertActionLogIdMatchesResponse($actionLog['id'], $this->_responseJsonHeader); @@ -69,8 +77,29 @@ public function testLogResourcesAddSuccessWithSecrets() $this->assertEntityHistoryExists($expectedEntityHistory); } + public function testLogResourcesAddSuccessWithSecretsErrorShouldHaveNoLogs() + { + ResourceTypeFactory::make()->default()->persist(); + $user = UserFactory::make()->user()->persist(); + $this->logInAs($user); + $data = [ + 'name' => 'new resource name', + 'username' => 'username@domain.com', + 'uri' => 'https://www.domain.com', + 'description' => 'new resource description', + 'resource_type_id' => UuidFactory::uuid(), + 'secrets' => [[ + 'data' => Hash::get(self::getDummySecretData(), 'data'), + ]], + ]; + $this->postJson('/resources.json?api-version=v2', $data); + $this->assertError(400, 'Could not validate resource data'); + $this->assertEntitiesHistoryEmpty(); + } + public function testLogResourcesUpdateSuccessWithoutSecrets() { + $this->loadFixtures(); $this->authenticateAs('ada'); $resourceId = UuidFactory::uuid('resource.id.apache'); $resource = [ @@ -105,6 +134,7 @@ public function testLogResourcesUpdateSuccessWithoutSecrets() public function testLogResourcesUpdateSuccessWithSecrets() { + $this->loadFixtures(); $this->authenticateAs('ada'); $resourceId = UuidFactory::uuid('resource.id.apache'); $adaId = UuidFactory::uuid('user.id.ada'); @@ -161,6 +191,7 @@ public function testLogResourcesUpdateSuccessWithSecrets() public function testLogResourcesDeleteSuccess() { + $this->loadFixtures(); $this->authenticateAs('ada'); $resourceId = UuidFactory::uuid('resource.id.apache'); $this->deleteJson("/resources/$resourceId.json"); diff --git a/plugins/Passbolt/Log/tests/TestCase/Controller/Share/ShareControllerLogTest.php b/plugins/Passbolt/Log/tests/TestCase/Controller/Share/ShareControllerLogTest.php index bc84a5979b..0515d01346 100644 --- a/plugins/Passbolt/Log/tests/TestCase/Controller/Share/ShareControllerLogTest.php +++ b/plugins/Passbolt/Log/tests/TestCase/Controller/Share/ShareControllerLogTest.php @@ -18,23 +18,26 @@ use App\Model\Entity\Permission; use App\Utility\UuidFactory; -use Cake\ORM\TableRegistry; +use Cake\Datasource\ModelAwareTrait; use Cake\Utility\Hash; use Passbolt\Log\Model\Entity\EntityHistory; use Passbolt\Log\Test\Lib\LogIntegrationTestCase; use Passbolt\Log\Test\Lib\Traits\PermissionsHistoryTrait; use Passbolt\Log\Test\Lib\Traits\SecretsHistoryTrait; +/** + * Class ShareControllerLogTest + * + * @property \Passbolt\Log\Model\Table\PermissionsHistoryTable $PermissionsHistory + * @property \App\Model\Table\SecretsTable $Secrets + * @property \Passbolt\Log\Model\Table\SecretsHistoryTable $SecretsHistory + */ class ShareControllerLogTest extends LogIntegrationTestCase { + use ModelAwareTrait; use PermissionsHistoryTrait; use SecretsHistoryTrait; - /** - * @var PermissionsHistoryTable - */ - protected $PermissionHistory; - public $fixtures = [ 'app.Base/Users', 'app.Base/Gpgkeys', 'app.Base/Profiles', 'app.Base/Roles', 'app.Base/Groups', 'app.Base/GroupsUsers', 'app.Base/Resources', 'app.Base/Permissions', 'app.Base/Secrets', @@ -44,9 +47,9 @@ class ShareControllerLogTest extends LogIntegrationTestCase public function setUp(): void { parent::setUp(); - $this->PermissionsHistory = TableRegistry::getTableLocator()->get('Passbolt/Log.PermissionsHistory'); - $this->Secrets = TableRegistry::getTableLocator()->get('Secrets'); - $this->SecretsHistory = TableRegistry::getTableLocator()->get('Passbolt/Log.SecretsHistory'); + $this->loadModel('Passbolt/Log.PermissionsHistory'); + $this->loadModel('Secrets'); + $this->loadModel('Passbolt/Log.SecretsHistory'); } public function testLogShareAddSuccess() @@ -71,6 +74,7 @@ public function testLogShareAddSuccess() $this->authenticateAs('ada'); $this->putJson("/share/resource/$resourceId.json", $data); $this->assertSuccess(); + /** @var \App\Model\Entity\Secret $secret */ $secret = $this->Secrets->findByResourceIdAndUserId($resourceId, $userEId)->first(); // Assert action log is correct. diff --git a/plugins/Passbolt/Log/tests/TestCase/Model/ActionLogs/ActionLogsTest.php b/plugins/Passbolt/Log/tests/TestCase/Model/ActionLogs/ActionLogsTest.php index 0c78105c53..8610080144 100644 --- a/plugins/Passbolt/Log/tests/TestCase/Model/ActionLogs/ActionLogsTest.php +++ b/plugins/Passbolt/Log/tests/TestCase/Model/ActionLogs/ActionLogsTest.php @@ -22,25 +22,23 @@ use App\Utility\UserAction; use App\Utility\UuidFactory; use Cake\Core\Configure; -use Cake\ORM\TableRegistry; +use Cake\Datasource\ModelAwareTrait; +/** + * Class ActionLogsTest + * + * @property \Passbolt\Log\Model\Table\ActionsTable $Actions + * @property \Passbolt\Log\Model\Table\ActionLogsTable $ActionLogs + */ class ActionLogsTest extends AppTestCase { - /** - * @var ActionsTable - */ - public $Actions; - - /** - * @var ActionLogsTable - */ - public $ActionLogs; + use ModelAwareTrait; public function setUp(): void { parent::setUp(); - $this->Actions = TableRegistry::getTableLocator()->get('Passbolt/Log.Actions'); - $this->ActionLogs = TableRegistry::getTableLocator()->get('Passbolt/Log.ActionLogs'); + $this->loadModel('Passbolt/Log.Actions'); + $this->loadModel('Passbolt/Log.ActionLogs'); } /** @@ -55,6 +53,7 @@ public function testCreate() $actionLog = $this->ActionLogs->create($userAction, 1); + /** @psalm-suppress UndefinedMagicMethod magic method exists */ $action = $this->Actions->findByName('Resources.Index')->first(); $this->assertEquals($action->id, UserAction::actionId('Resources.Index')); $this->assertEquals($action->name, 'Resources.Index'); @@ -78,6 +77,7 @@ public function testCreateWithBlackList() $accessControl = new UserAccessControl(Role::USER, UuidFactory::uuid('user.id.ada')); $userAction = UserAction::getInstance($accessControl, $actionName, 'GET Resources.json'); + /** @psalm-suppress UndefinedMagicMethod magic method exists */ $action = $this->Actions->findByName($actionName)->first(); $this->assertEmpty($action); diff --git a/plugins/Passbolt/Log/tests/TestCase/Model/Actions/FindOrCreateTest.php b/plugins/Passbolt/Log/tests/TestCase/Model/Actions/FindOrCreateTest.php index c1e301cbc4..95fd151841 100644 --- a/plugins/Passbolt/Log/tests/TestCase/Model/Actions/FindOrCreateTest.php +++ b/plugins/Passbolt/Log/tests/TestCase/Model/Actions/FindOrCreateTest.php @@ -18,14 +18,21 @@ use App\Test\Lib\AppTestCase; use App\Utility\UuidFactory; -use Cake\ORM\TableRegistry; +use Cake\Datasource\ModelAwareTrait; +/** + * Class FindOrCreateTest + * + * @property \Passbolt\Log\Model\Table\ActionsTable $Actions + */ class FindOrCreateTest extends AppTestCase { + use ModelAwareTrait; + public function setUp(): void { parent::setUp(); - $this->Actions = TableRegistry::getTableLocator()->get('Passbolt/Log.Actions'); + $this->loadModel('Passbolt/Log.Actions'); } /** diff --git a/plugins/Passbolt/Mobile/composer.json b/plugins/Passbolt/Mobile/composer.json new file mode 100644 index 0000000000..898f5be2b2 --- /dev/null +++ b/plugins/Passbolt/Mobile/composer.json @@ -0,0 +1,23 @@ +{ + "name": "Passbolt/Mobile", + "description": "Passbolt/Mobile plugin for CakePHP", + "type": "cakephp-plugin", + "license": "AGPL-3.0-or-later", + "require": { + "cakephp/cakephp": "^3.5" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "autoload": { + "psr-4": { + "Passbolt\\Mobile\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Passbolt\\Mobile\\Test\\": "tests/", + "Cake\\Test\\": "vendor/cakephp/cakephp/tests/" + } + } +} diff --git a/plugins/Passbolt/Mobile/config/bootstrap.php b/plugins/Passbolt/Mobile/config/bootstrap.php new file mode 100644 index 0000000000..e4bd083943 --- /dev/null +++ b/plugins/Passbolt/Mobile/config/bootstrap.php @@ -0,0 +1,18 @@ + [ + 'plugins' => [ + 'mobile' => [ + 'version' => '1.0.0', + ], + ], + ], +]; diff --git a/plugins/Passbolt/Mobile/config/routes.php b/plugins/Passbolt/Mobile/config/routes.php new file mode 100644 index 0000000000..14f544007f --- /dev/null +++ b/plugins/Passbolt/Mobile/config/routes.php @@ -0,0 +1,49 @@ + '/mobile'], function (RouteBuilder $routes) { + $routes->setExtensions(['json']); + + /** + * Data transfers using 3rd party channels such as QR Codes + */ + // start transfer + $routes->connect('/transfers', ['prefix' => 'Transfers', 'controller' => 'TransfersCreate', 'action' => 'create']) + ->setMethods(['POST']); + + // update transfer + $routes + ->connect('/transfers/:id', [ + 'prefix' => 'Transfers', 'controller' => 'TransfersUpdate', 'action' => 'update', + ]) + ->setMethods(['POST', 'PUT']) + ->setPass(['id']); + + // without authToken + $routes + ->connect('/transfers/:id/:authToken', [ + 'prefix' => 'Transfers', 'controller' => 'TransfersUpdate', 'action' => 'updateNoSession', + ]) + ->setMethods(['POST', 'PUT']) + ->setPass(['id', 'authToken']); + + // view transfer status + $routes->connect('/transfers/:id', ['prefix' => 'Transfers', 'controller' => 'TransfersView', 'action' => 'view']) + ->setMethods(['GET']) + ->setPass(['id']); +}); diff --git a/plugins/Passbolt/Mobile/phpunit.xml.dist b/plugins/Passbolt/Mobile/phpunit.xml.dist new file mode 100644 index 0000000000..19facd71d8 --- /dev/null +++ b/plugins/Passbolt/Mobile/phpunit.xml.dist @@ -0,0 +1,36 @@ + + + + + + + + + + + tests/TestCase/ + + + + + + + + + + + + + + + src/ + + + + diff --git a/plugins/Passbolt/Mobile/src/Controller/Transfers/TransfersCreateController.php b/plugins/Passbolt/Mobile/src/Controller/Transfers/TransfersCreateController.php new file mode 100644 index 0000000000..81e1d212f3 --- /dev/null +++ b/plugins/Passbolt/Mobile/src/Controller/Transfers/TransfersCreateController.php @@ -0,0 +1,51 @@ +request->getData(); + if (!isset($data) || empty($data) || !is_array($data)) { + throw new BadRequestException(__('Information about the transfer is required.')); + } + + $uac = $this->User->getAccessControl(); + $createService = new TransfersCreateService(); + $transfer = $createService->create($data, $uac); + + $this->success(__('The operation was successful'), $transfer); + } +} diff --git a/plugins/Passbolt/Mobile/src/Controller/Transfers/TransfersUpdateController.php b/plugins/Passbolt/Mobile/src/Controller/Transfers/TransfersUpdateController.php new file mode 100644 index 0000000000..455f6d2e92 --- /dev/null +++ b/plugins/Passbolt/Mobile/src/Controller/Transfers/TransfersUpdateController.php @@ -0,0 +1,157 @@ +Authentication->allowUnauthenticated(['updateNoSession']); + $this->loadModel('Passbolt/Mobile.Transfers'); + + return parent::beforeFilter($event); + } + + /** + * Update a transfer without sessions + * + * Allow a user on a non configured device to perform update without being logged in + * using an authentication token provided via another channel, a QR code for example + * + * @param string $id uuid + * @param string $authToken uuid + * @return void + */ + public function updateNoSession(string $id, string $authToken): void + { + $this->main($id, $authToken); + } + + /** + * Update a transfer + * + * @param string $id transfer uuid + * @return void + */ + public function update(string $id): void + { + $this->main($id); + } + + /** + * Main update controller method + * + * @param string $id transfer uuid + * @param string|null $authToken token + * @throws \Cake\Http\Exception\BadRequestException if data is missing or transfer id is not valid + * @throws \Cake\Datasource\Exception\RecordNotFoundException if transfer does not exist + * @throws \Cake\Http\Exception\UnauthorizedException if transfer auth token is expired + * @throws \App\Error\Exception\ValidationException if data do not validate + * @throws \Cake\Http\Exception\InternalErrorException if saving data is not possible + * @return void + */ + protected function main(string $id, ?string $authToken = null): void + { + $this->assertRequestData($id); + $this->transfer = $this->Transfers->get($id, ['contain' => ['AuthenticationTokens', 'Users']]); + if (isset($authToken)) { + $uac = $this->assertAuthToken($authToken); + } else { + $uac = $this->User->getAccessControl(); + } + + $updateService = new TransfersUpdateService($this->Transfers); + $updateService->update($this->transfer, $this->request->getData(), $uac); + + // Contain options + $whitelist = ['contain' => ['user', 'user.profile']]; + $options = $this->QueryString->get($whitelist); + $contain = empty($options['contain']['user']) ? [] : ['Users']; + $contain = empty($options['contain']['user.profile']) ? $contain : [ + 'Users.Profiles' => AvatarsTable::addContainAvatar(), + ]; + + $updatedTransfer = $this->Transfers->get($id, ['contain' => $contain]); + $this->success(__('The operation was successful.'), $updatedTransfer); + } + + /** + * Check request sanity and set $transfer + * + * @param string $id uuid + * @throws \Cake\Http\Exception\BadRequestException if transfer id is invalid or data is not set + * @throws \Cake\Http\Exception\UnauthorizedException if transfer auth token is expired + * @return void + */ + protected function assertRequestData(string $id): void + { + if (!Validation::uuid($id)) { + throw new BadRequestException(__('The transfer id is not valid.')); + } + + $data = $this->request->getData(); + if (!isset($data) || empty($data) || !is_array($data)) { + throw new BadRequestException(__('Information about the transfer is required.')); + } + } + + /** + * Assert auth token + * We only check if the authentication token exists and if it matches the one provide by the user + * Expiry and logical validity of the authentication token is checked in the TransfersUpdateService service + * + * @param string $authToken uuid + * @throws \Cake\Http\Exception\BadRequestException if no authentication token is expired or invalid + * @return \App\Utility\UserAccessControl + */ + protected function assertAuthToken(string $authToken): UserAccessControl + { + if (!Validation::uuid($authToken)) { + throw new UnauthorizedException(__('The authentication token should be a valid uuid.')); + } + if ($this->transfer->authentication_token->token !== $authToken) { + throw new UnauthorizedException(__('The authentication token is invalid.')); + } + + $userId = $this->transfer->authentication_token->user_id; + + return new UserAccessControl(Role::USER, $userId); + } +} diff --git a/plugins/Passbolt/Mobile/src/Controller/Transfers/TransfersViewController.php b/plugins/Passbolt/Mobile/src/Controller/Transfers/TransfersViewController.php new file mode 100644 index 0000000000..260b15e149 --- /dev/null +++ b/plugins/Passbolt/Mobile/src/Controller/Transfers/TransfersViewController.php @@ -0,0 +1,60 @@ +loadModel('Passbolt/Mobile.Transfers'); + + // Contain options + $whitelist = ['contain' => ['user', 'user.profile']]; + $options = $this->QueryString->get($whitelist); + $contain = empty($options['contain']['user']) ? [] : ['Users']; + $contain = empty($options['contain']['user.profile']) ? $contain : [ + 'Users.Profiles' => AvatarsTable::addContainAvatar(), + ]; + + $transfer = $this->Transfers->get($id, ['contain' => $contain]); + $this->success(__('The operation was successful.'), $transfer); + } +} diff --git a/plugins/Passbolt/Mobile/src/Model/Entity/Transfer.php b/plugins/Passbolt/Mobile/src/Model/Entity/Transfer.php new file mode 100644 index 0000000000..d04238a2bd --- /dev/null +++ b/plugins/Passbolt/Mobile/src/Model/Entity/Transfer.php @@ -0,0 +1,83 @@ + false, + 'current_page' => false, + 'total_pages' => false, + 'hash' => false, + 'status' => false, + + // associated data + 'user_id' => false, + 'user' => false, + 'authentication_token' => false, + 'authentication_token_id' => false, + ]; +} diff --git a/plugins/Passbolt/Mobile/src/Model/Table/TransfersTable.php b/plugins/Passbolt/Mobile/src/Model/Table/TransfersTable.php new file mode 100644 index 0000000000..dc04dff6d1 --- /dev/null +++ b/plugins/Passbolt/Mobile/src/Model/Table/TransfersTable.php @@ -0,0 +1,184 @@ +setTable('transfers'); + $this->setDisplayField('id'); + $this->setPrimaryKey('id'); + + $this->addBehavior('Timestamp'); + + $this->belongsTo('Users', [ + 'foreignKey' => 'user_id', + ]); + + $this->belongsTo('AuthenticationTokens', [ + 'foreignKey' => 'authentication_token_id', + ]); + } + + /** + * Default validation rules. + * + * @param \Cake\Validation\Validator $validator Validator instance. + * @return \Cake\Validation\Validator + */ + public function validationDefault(Validator $validator): Validator + { + $validator + ->uuid('id', __('The transfer id should be a uuid.')) + ->allowEmptyString('id', null, 'create'); + + $validator + ->uuid('user_id', __('The user id should be a uuid.')) + ->requirePresence('user_id', 'create', __('A user id is required')); + + $validator + ->ascii('hash', __('The hash should be an ascii string.')) + ->lengthBetween( + 'hash', + [Transfer::TRANSFER_HASH_SIZE, Transfer::TRANSFER_HASH_SIZE], + __('The hash should be {0} characters in length.', Transfer::TRANSFER_HASH_SIZE) + ) + ->requirePresence('hash', 'create', __('The data transfer hash is required.')); + + $validator + ->nonNegativeInteger('current_page', __('The current page should be a non negative integer.')) + ->requirePresence('current_page', 'update', __('The current page number is required')) + ->add('current_page', 'inferior_to_total', [ + 'rule' => function ($value, $context) { + if (isset($context['data']['total_pages'])) { + return $value < $context['data']['total_pages']; + } + + return true; + }, + 'message' => __('The current page number should be equal or inferior to the total number of pages.'), + ]) + ->lessThan( + 'current_page', + Transfer::TRANSFER_MAX_PAGES, + __('The current page cannot be greater than {0}', Transfer::TRANSFER_MAX_PAGES) + ); + + $validator + ->nonNegativeInteger('total_pages', __('The total number of pages should be a non negative integer.')) + ->requirePresence('total_pages', 'create', __('The total number of pages is required.')) + ->greaterThan('total_pages', 0, __('The total number of pages should be greater than 0.')) + ->lessThan( + 'total_pages', + Transfer::TRANSFER_MAX_PAGES, + __('The total number of pages cannot be greater than {0}', Transfer::TRANSFER_MAX_PAGES) + ); + + $validator + ->notEmptyString('status', __('The status should not be empty.')) + ->requirePresence('status', true, __('The status is required.')) + ->inList('status', Transfer::TRANSFER_STATUSES, __( + 'The status must be one of the following: {0}.', + implode(', ', Transfer::TRANSFER_STATUSES) + )); + + return $validator; + } + + /** + * Returns a rules checker object that will be used for validating + * application integrity. + * + * @param \Cake\ORM\RulesChecker $rules The rules object to be modified. + * @return \Cake\ORM\RulesChecker + */ + public function buildRules(RulesChecker $rules): RulesChecker + { + $rules->addCreate($rules->existsIn('user_id', 'Users'), 'user_exists'); + $rules->addCreate(new IsNotSoftDeletedRule(), 'user_is_soft_deleted', [ + 'table' => 'Users', + 'errorField' => 'user_id', + 'message' => __('The user must be active.'), + ]); + $rules->addCreate(new IsActiveRule(), 'user_is_active', [ + 'table' => 'Users', + 'errorField' => 'user_id', + 'message' => __('The user must be active.'), + ]); + + return $rules; + } + + /** + * Cancel all the transfers that are started or in progress and that cannot be + * completed because the authentication token has expired. + * + * @return void + */ + public function cancelAllTransfersWithInactiveAuthenticationToken(): void + { + $transfers = $this->find() + ->select(['id']) + ->distinct() + ->contain(['AuthenticationTokens']) + ->where(['Transfers.status in' => [ + Transfer::TRANSFER_STATUS_IN_PROGRESS, Transfer::TRANSFER_STATUS_START, + ], 'AuthenticationTokens.active' => false]) + ->all() + ->toArray(); + + if (count($transfers)) { + $this->query() + ->update() + ->set(['status' => Transfer::TRANSFER_STATUS_CANCEL]) + ->where(['id in' => Hash::extract($transfers, '{n}.id')]) + ->execute(); + } + } +} diff --git a/plugins/Passbolt/Mobile/src/Service/Transfers/TransfersCreateService.php b/plugins/Passbolt/Mobile/src/Service/Transfers/TransfersCreateService.php new file mode 100644 index 0000000000..d13e784530 --- /dev/null +++ b/plugins/Passbolt/Mobile/src/Service/Transfers/TransfersCreateService.php @@ -0,0 +1,127 @@ +Transfers = $transfersTable ?? TableRegistry::getTableLocator()->get('Passbolt/Mobile.Transfers'); + } + + /** + * Create a transfer and the the associated authentication token + * + * @param array $data entity data + * @param \App\Utility\UserAccessControl $uac user access control + * @throws \App\Error\Exception\ValidationException if data do not validate + * @throws \Cake\Http\Exception\InternalErrorException if saving data is not possible + * @return \Passbolt\Mobile\Model\Entity\Transfer + */ + public function create(array $data, UserAccessControl $uac): Transfer + { + // Check for validation errors + $transfer = $this->buildTransferEntity($data, $uac); + if (!empty($transfer->getErrors())) { + $msg = __('Could not validate the transfer data.'); + throw new ValidationException($msg, $transfer, $this->Transfers); + } + + // Save and check for build rules errors. + $transferSaved = $this->Transfers->save($transfer); + if (!empty($transfer->getErrors())) { + $msg = __('Could not validate the transfer data.'); + throw new ValidationException($msg, $transfer, $this->Transfers); + } + + // Check for errors while saving. + if (!$transferSaved) { + throw new InternalErrorException(__('The transfer could not be created.')); + } + + return $transfer; + } + + /** + * Return a transfer entity. + * + * @param array $data entity data + * @param \App\Utility\UserAccessControl $uac user access control + * @return \Passbolt\Mobile\Model\Entity\Transfer + */ + private function buildTransferEntity(array $data, UserAccessControl $uac): Transfer + { + $data['current_page'] = 0; + $data['status'] = Transfer::TRANSFER_STATUS_START; + $data['user_id'] = $uac->getId(); + $data['authentication_token'] = [ + 'user_id' => $uac->getId(), + 'token' => UuidFactory::uuid(), + 'active' => true, + 'type' => AuthenticationToken::TYPE_MOBILE_TRANSFER, + ]; + + return $this->Transfers->newEntity($data, [ + 'accessibleFields' => [ + 'id' => false, + 'user_id' => true, + 'current_page' => true, + 'total_pages' => true, + 'hash' => true, + 'status' => true, + 'authentication_token' => true, + ], + 'associated' => [ + 'AuthenticationTokens' => [ + 'accessibleFields' => [ + 'user_id' => true, + 'token' => true, + 'active' => true, + 'type' => true, + ], + ], + ], + ]); + } +} diff --git a/plugins/Passbolt/Mobile/src/Service/Transfers/TransfersUpdateService.php b/plugins/Passbolt/Mobile/src/Service/Transfers/TransfersUpdateService.php new file mode 100644 index 0000000000..8e08f236ec --- /dev/null +++ b/plugins/Passbolt/Mobile/src/Service/Transfers/TransfersUpdateService.php @@ -0,0 +1,206 @@ +Transfers = $transfersTable ?? TableRegistry::getTableLocator()->get('Passbolt/Mobile.Transfers'); + /** @phpstan-ignore-next-line */ + $this->AuthenticationTokens = $authTable ?? TableRegistry::getTableLocator()->get('AuthenticationTokens'); + + // Cleanup the tokens if needed + // @later (tm) could be moved in a cron job + /** @phpstan-ignore-next-line */ + $this->AuthenticationTokens->setActiveExpiredTokenToInactive(AuthenticationToken::TYPE_MOBILE_TRANSFER); + /** @phpstan-ignore-next-line */ + $this->Transfers->cancelAllTransfersWithInactiveAuthenticationToken(); + } + + /** + * Create a transfer and the the associated authentication token + * + * @param \Passbolt\Mobile\Model\Entity\Transfer $transfer entity + * @param array $data entity data + * @param \App\Utility\UserAccessControl $uac user access control object + * @throws \App\Error\Exception\ValidationException if data do not validate + * @throws \Cake\Http\Exception\InternalErrorException if saving data is not possible + * @return \Passbolt\Mobile\Model\Entity\Transfer + */ + public function update(Transfer $transfer, array $data, UserAccessControl $uac): Transfer + { + $this->assertOperationIsAllowed($transfer, $uac); + + // Check for validation errors + $originalTransfer = clone $transfer; + $transfer = $this->patchTransferEntity($transfer, $data); + $this->assertTransitionAllowed($originalTransfer, $transfer); + + if (!empty($transfer->getErrors())) { + $msg = __('Could not validate the transfer data.'); + throw new ValidationException($msg, $transfer, $this->Transfers); + } + + // Save and check for application rules errors. + $transferSaved = $this->Transfers->save($transfer); + if (!empty($transfer->getErrors())) { + $msg = __('Could not update the transfer data.'); + throw new ValidationException($msg, $transfer, $this->Transfers); + } + + // Check for errors while saving. + if (!$transferSaved) { + throw new InternalErrorException(__('The transfer could not be updated.')); + } + + return $transfer; + } + + /** + * Validate possible transitions + * TODO transform into build rules? + * + * @param \Passbolt\Mobile\Model\Entity\Transfer $original entity + * @param \Passbolt\Mobile\Model\Entity\Transfer $updated entity + * @return void + */ + public function assertTransitionAllowed(Transfer $original, Transfer $updated): void + { + // Cannot "restart" + if ($updated->status === Transfer::TRANSFER_STATUS_START) { + $msg = __('This operation is not allowed.') . ' '; + $msg .= __('Restarting a transfer is not allowed.'); + throw new ForbiddenException($msg); + } + + // Cannot update a cancelled or completed transfer + if ( + ($updated->status === Transfer::TRANSFER_STATUS_IN_PROGRESS + || $updated->status === Transfer::TRANSFER_STATUS_CANCEL + || $updated->status === Transfer::TRANSFER_STATUS_COMPLETE + || $updated->status === Transfer::TRANSFER_STATUS_ERROR) + && + ($original->status === Transfer::TRANSFER_STATUS_CANCEL + || $original->status === Transfer::TRANSFER_STATUS_COMPLETE) + ) { + $msg = __('This operation is not allowed.') . ' '; + $msg .= __('The operation is already over.'); + throw new ForbiddenException($msg); + } + + // Cannot "complete" without being on last page + if ( + $updated->status === Transfer::TRANSFER_STATUS_COMPLETE && + $updated->current_page !== $original->total_pages - 1 + ) { + $msg = __('This operation is not allowed.') . ' '; + $msg .= __('The current page does not match the total number of pages.'); + throw new ForbiddenException($msg); + } + } + + /** + * Check if operation is allowed + * + * @param \Passbolt\Mobile\Model\Entity\Transfer $transfer entity + * @param \App\Utility\UserAccessControl $uac user access control object + * @throws \Cake\Http\Exception\ForbiddenException if operation is not allowed for example: + * - Transfer or AuthToken is for another user + * - Authentication token is expired + * @return void + */ + private function assertOperationIsAllowed(Transfer $transfer, UserAccessControl $uac): void + { + if ($transfer->user_id !== $uac->getId()) { + throw new ForbiddenException(__('This operation is not allowed for this user.')); + } + if (!isset($transfer->authentication_token)) { + throw new ForbiddenException(__('The authentication token is missing.')); + } + if ($transfer->authentication_token->user_id !== $uac->getId()) { + throw new ForbiddenException(__('The authentication token is not valid for this user.')); + } + if ($transfer->authentication_token->type !== AuthenticationToken::TYPE_MOBILE_TRANSFER) { + throw new ForbiddenException(__('The authentication token type is invalid.')); + } + if ($transfer->authentication_token->active !== true) { + throw new ForbiddenException(__('The authentication token is not active.')); + } + if ($this->AuthenticationTokens->isExpired($transfer->authentication_token)) { + throw new ForbiddenException(__('The authentication token is expired.')); + } + } + + /** + * Return an updated transfer entity. + * + * @param \Passbolt\Mobile\Model\Entity\Transfer $transfer entity + * @param array $data data + * @return \Passbolt\Mobile\Model\Entity\Transfer + */ + private function patchTransferEntity(Transfer $transfer, array $data): Transfer + { + $data['total_pages'] = $transfer->total_pages; + + return $this->Transfers->patchEntity($transfer, $data, [ + 'accessibleFields' => [ + 'id' => true, + 'user_id' => false, + 'current_page' => true, + 'total_pages' => false, + 'hash' => false, + 'status' => true, + 'authentication_token' => false, + 'authentication_token_id' => false, + ], + ]); + } +} diff --git a/plugins/Passbolt/Mobile/tests/Factory/TransferFactory.php b/plugins/Passbolt/Mobile/tests/Factory/TransferFactory.php new file mode 100644 index 0000000000..142b5965c0 --- /dev/null +++ b/plugins/Passbolt/Mobile/tests/Factory/TransferFactory.php @@ -0,0 +1,140 @@ +setDefaultData(function (Generator $faker) use ($userId) { + return [ + 'user_id' => $userId, + 'total_pages' => 2, + 'current_page' => 0, + 'status' => Transfer::TRANSFER_STATUS_START, + 'hash' => hash('sha512', Security::randomBytes(16), false), + 'created' => Chronos::now(), + 'modified' => Chronos::now(), + ]; + }); + + $this->with( + 'AuthenticationTokens', + AuthenticationTokenFactory::make() + ->userId($userId) + ->type(AuthenticationToken::TYPE_MOBILE_TRANSFER) + ->active() + ); + } + + /** + * @param AuthenticationTokenFactory $token authentication token + * @return $this + */ + public function withAuthenticationToken(AuthenticationTokenFactory $token) + { + return $this->with('AuthenticationTokens', $token); + } + + /** + * @param int $totalPages total pages + * @return $this + */ + public function totalPages(int $totalPages) + { + return $this->patchData(['total_pages' => $totalPages]); + } + + /** + * @param int $currentPage current page + * @return $this + */ + public function currentPage(int $currentPage) + { + return $this->patchData(['current_page' => $currentPage]); + } + + /** + * @param ChronosInterface $modified token type + * @return $this + */ + public function modified(ChronosInterface $modified) + { + return $this->patchData(compact('modified')); + } + + /** + * @param ChronosInterface $created token type + * @return $this + */ + public function created(ChronosInterface $created) + { + return $this->patchData(compact('created')); + } + + /** + * @param string $status status + * @return $this + */ + public function status(string $status) + { + return $this->setField('status', $status); + } + + /** + * @param string $userId user id + * @return $this + */ + public function userId(string $userId) + { + return $this->setField('user_id', $userId); + } +} diff --git a/plugins/Passbolt/Mobile/tests/Lib/Model/TransfersModelTrait.php b/plugins/Passbolt/Mobile/tests/Lib/Model/TransfersModelTrait.php new file mode 100644 index 0000000000..c56d58b9d1 --- /dev/null +++ b/plugins/Passbolt/Mobile/tests/Lib/Model/TransfersModelTrait.php @@ -0,0 +1,127 @@ +insertTransferFixture($this->getDummyTransfer()); + } + + /** + * @param string|null $user + * @param string|null $status + * @param int|null $currentPage + * @param int|null $totalPages + * @return array + */ + public function getDummyTransfer( + ?string $user = 'ada', + ?string $status = Transfer::TRANSFER_STATUS_IN_PROGRESS, + ?int $currentPage = 1, + ?int $totalPages = 2 + ): array { + $userId = UuidFactory::uuid("user.id.$user"); + + return [ + 'user_id' => $userId, + 'current_page' => $currentPage, + 'status' => $status, + 'total_pages' => $totalPages, + 'hash' => Security::hash('test', 'sha512', true), + 'authentication_token' => [ + 'user_id' => $userId, + 'token' => UuidFactory::uuid(), + 'active' => true, + 'type' => AuthenticationToken::TYPE_MOBILE_TRANSFER, + ], + ]; + } + + /** + * Create a transfer fixture + * The transfer data must passe a default validation. + * + * @param array|null $data Custom data that will be merged with the default dummy comment. + * @return Transfer transfer entity + */ + public function insertTransferFixture(?array $data = []): Transfer + { + /** @var \Passbolt\Mobile\Model\Table\TransfersTable $transfersTable */ + $transfersTable = TableRegistry::getTableLocator()->get('Passbolt/Mobile.Transfers'); + /** @var \Passbolt\Mobile\Model\Entity\Transfer $transfer */ + $transfer = $transfersTable->newEntity($data, $this->getTransferEntityAccessibleFields()); + + $transfersTable->save($transfer, ['checkRules' => false]); + + return $transfer; + } + + /** + * @return array accessibleFields + */ + protected function getTransferEntityAccessibleFields() + { + return [ + 'accessibleFields' => [ + 'id' => false, + 'user_id' => true, + 'current_page' => true, + 'total_pages' => true, + 'hash' => true, + 'status' => true, + 'authentication_token' => true, + ], + 'associated' => [ + 'AuthenticationTokens' => [ + 'accessibleFields' => [ + 'user_id' => true, + 'token' => true, + 'active' => true, + 'type' => true, + 'created' => true, + ], + ], + ], + ]; + } + + /** + * Asserts that an object has all the attributes a transfer should have. + * + * @param object $transfer + */ + protected function assertTransferAttributes($transfer) + { + $attributes = [ + 'id', 'user_id', 'current_page', 'total_pages', 'hash', 'status', + ]; + $this->assertObjectHasAttributes($attributes, $transfer); + } +} diff --git a/plugins/Passbolt/Mobile/tests/TestCase/Controller/Transfers/TransfersCreateControllerTest.php b/plugins/Passbolt/Mobile/tests/TestCase/Controller/Transfers/TransfersCreateControllerTest.php new file mode 100644 index 0000000000..31843a5a1c --- /dev/null +++ b/plugins/Passbolt/Mobile/tests/TestCase/Controller/Transfers/TransfersCreateControllerTest.php @@ -0,0 +1,97 @@ +enableFeaturePlugin('mobile'); + } + + public function tearDown(): void + { + parent::tearDown(); + $this->disableFeaturePlugin('mobile'); + } + + public function testMobileTransfersCreateController_Success() + { + $data = [ + 'total_pages' => 1, + 'status' => Transfer::TRANSFER_STATUS_START, + 'hash' => Security::hash('test', 'sha512', true), + ]; + $this->authenticateAs('ada'); + $this->postJson('/mobile/transfers.json', $data); + $this->assertSuccess(); + $this->assertTransferAttributes($this->_responseJsonBody); + $this->assertAuthTokenAttributes($this->_responseJsonBody->authentication_token); + } + + public function testMobileTransfersCreateController_ErrorNoData() + { + $this->authenticateAs('ada'); + $this->postJson('/mobile/transfers.json', []); + $this->assertError(400); + } + + public function testMobileTransfersCreateController_ErrorUserIsDeleted() + { + $data = [ + 'total_pages' => 1, + 'status' => Transfer::TRANSFER_STATUS_START, + 'hash' => Security::hash('test', 'sha512', true), + ]; + $this->logInAs(UserFactory::make()->deleted()->persist()); + $this->postJson('/mobile/transfers.json', $data); + $this->assertAuthenticationError(); + } + + public function testMobileTransfersCreateController_ErrorEmptyData() + { + $this->authenticateAs('ada'); + $this->postJson('/mobile/transfers.json', []); + $this->assertError(400); + } + + public function testMobileTransfersCreateController_ErrorNotAuthenticated() + { + $data = [ + 'total_pages' => 1, + 'status' => Transfer::TRANSFER_STATUS_START, + 'hash' => Security::hash('test', 'sha512', true), + ]; + $this->postJson('/mobile/transfers.json', $data); + $this->assertAuthenticationError(); + } +} diff --git a/plugins/Passbolt/Mobile/tests/TestCase/Controller/Transfers/TransfersUpdateControllerTest.php b/plugins/Passbolt/Mobile/tests/TestCase/Controller/Transfers/TransfersUpdateControllerTest.php new file mode 100644 index 0000000000..1268f3bc03 --- /dev/null +++ b/plugins/Passbolt/Mobile/tests/TestCase/Controller/Transfers/TransfersUpdateControllerTest.php @@ -0,0 +1,266 @@ +enableFeaturePlugin('mobile'); + } + + public function tearDown(): void + { + parent::tearDown(); + $this->disableFeaturePlugin('mobile'); + } + + public function testMobileTransfersUpdateController_Success() + { + $transfer = $this->insertTransferFixture($this->getDummyTransfer()); + $id = $transfer->id; + $data = [ + 'status' => Transfer::TRANSFER_STATUS_COMPLETE, + 'current_page' => 1, + ]; + $this->authenticateAs('ada'); + $this->postJson("/mobile/transfers/$id.json", $data); + $this->assertSuccess(); + $this->assertTransferAttributes($this->_responseJsonBody); + $this->assertFalse(isset($this->_responseJsonBody->authentication_token)); + $this->assertFalse(isset($this->_responseJsonBody->user)); + } + + public function testMobileTransfersUpdateController_SuccessWithContainAvatar() + { + $transfer = $this->insertTransferFixture($this->getDummyTransfer()); + $id = $transfer->id; + $data = [ + 'status' => Transfer::TRANSFER_STATUS_COMPLETE, + 'current_page' => 1, + ]; + $this->authenticateAs('ada'); + $this->postJson("/mobile/transfers/$id.json?contain[user.profile]=1", $data); + $this->assertSuccess(); + $this->assertTransferAttributes($this->_responseJsonBody); + $this->assertFalse(isset($this->_responseJsonBody->authentication_token)); + $this->assertUserAttributes($this->_responseJsonBody->user); + $this->assertProfileAttributes($this->_responseJsonBody->user->profile); + $this->assertAvatarAttributes($this->_responseJsonBody->user->profile->avatar); + } + + public function testMobileTransfersUpdateController_ErrorNoData() + { + $transfer = $this->insertTransferFixture($this->getDummyTransfer()); + $id = $transfer->id; + $this->authenticateAs('ada'); + $this->postJson("/mobile/transfers/$id.json", []); + $this->assertError(400); + } + + public function testMobileTransfersUpdateController_ErrorTransferDoesNotBelongToUser() + { + $transfer = $this->insertTransferFixture($this->getDummyTransfer()); + $id = $transfer->id; + $data = [ + 'status' => Transfer::TRANSFER_STATUS_COMPLETE, + 'current_page' => 1, + ]; + $this->authenticateAs('betty'); + $this->postJson("/mobile/transfers/$id.json", $data); + $this->assertError(403); + } + + public function testMobileTransfersUpdateController_ErrorAuthDoesNotBelongToUser() + { + $transfer = $this->insertTransferFixture($this->getDummyTransfer()); + $id = $transfer->id; + $data = [ + 'status' => Transfer::TRANSFER_STATUS_COMPLETE, + 'current_page' => 1, + ]; + $this->authenticateAs('betty'); + $this->postJson("/mobile/transfers/$id.json", $data); + $this->assertError(403); + } + + public function testMobileTransfersUpdateController_ErrorEmptyData() + { + $transfer = $this->insertTransferFixture($this->getDummyTransfer()); + $id = $transfer->id; + $data = []; + $this->authenticateAs('ada'); + $this->postJson("/mobile/transfers/$id.json", $data); + $this->assertError(400); + } + + public function testMobileTransfersUpdateController_ErrorNotAuthenticated() + { + $transfer = $this->insertTransferFixture($this->getDummyTransfer()); + $id = $transfer->id; + $data = [ + 'status' => Transfer::TRANSFER_STATUS_COMPLETE, + 'current_page' => 1, + ]; + $this->postJson("/mobile/transfers/$id.json", $data); + $this->assertAuthenticationError(); + } + + public function testMobileTransfersUpdateController_ErrorTransferFordbidden_TokenInactive() + { + $transfer = $this->insertTransferFixture([ + 'user_id' => UuidFactory::uuid('user.id.ada'), + 'current_page' => 1, + 'status' => Transfer::TRANSFER_STATUS_IN_PROGRESS, + 'total_pages' => 2, + 'hash' => Security::hash('test', 'sha512', true), + 'authentication_token' => [ + 'user_id' => UuidFactory::uuid('user.id.ada'), + 'token' => UuidFactory::uuid(), + 'active' => false, + 'type' => AuthenticationToken::TYPE_MOBILE_TRANSFER, + ], + ]); + $id = $transfer->id; + $data = [ + 'status' => Transfer::TRANSFER_STATUS_COMPLETE, + 'current_page' => 1, + ]; + $this->postJson("/mobile/transfers/$id.json", $data); + $this->assertAuthenticationError(); + } + + public function testMobileTransfersUpdateController_ErrorTransferForbidden_TokenExpired() + { + $transfer = $this->insertTransferFixture([ + 'user_id' => UuidFactory::uuid('user.id.ada'), + 'current_page' => 1, + 'status' => Transfer::TRANSFER_STATUS_IN_PROGRESS, + 'total_pages' => 2, + 'hash' => Security::hash('test', 'sha512', true), + 'authentication_token' => [ + 'user_id' => UuidFactory::uuid('user.id.ada'), + 'token' => UuidFactory::uuid(), + 'active' => true, + 'type' => AuthenticationToken::TYPE_MOBILE_TRANSFER, + 'created' => new FrozenDate('last year'), + ], + ]); + $id = $transfer->id; + $data = [ + 'status' => Transfer::TRANSFER_STATUS_COMPLETE, + 'current_page' => 1, + ]; + $this->authenticateAs('ada'); + $this->postJson("/mobile/transfers/$id.json", $data); + $this->assertError(403); + } + + public function testMobileTransfersUpdateController_ErrorTransferForbidden_TokenWrongType() + { + $transfer = $this->insertTransferFixture([ + 'user_id' => UuidFactory::uuid('user.id.ada'), + 'current_page' => 1, + 'status' => Transfer::TRANSFER_STATUS_IN_PROGRESS, + 'total_pages' => 2, + 'hash' => Security::hash('test', 'sha512', true), + 'authentication_token' => [ + 'user_id' => UuidFactory::uuid('user.id.ada'), + 'token' => UuidFactory::uuid(), + 'active' => true, + 'type' => AuthenticationToken::TYPE_RECOVER, + 'created' => new FrozenDate('last year'), + ], + ]); + $id = $transfer->id; + $data = [ + 'status' => Transfer::TRANSFER_STATUS_COMPLETE, + 'current_page' => 1, + ]; + $this->authenticateAs('ada'); + $this->postJson("/mobile/transfers/$id.json", $data); + $this->assertError(403); + } + + // No session but with auth token instead + + public function testMobileTransfersUpdateController_NoSession_Success() + { + $transfer = $this->insertTransferFixture($this->getDummyTransfer()); + $id = $transfer->id; + $token = $transfer->authentication_token->token; + $data = [ + 'status' => Transfer::TRANSFER_STATUS_COMPLETE, + 'current_page' => 1, + ]; + $this->authenticateAs('ada'); + $this->postJson("/mobile/transfers/$id/$token.json", $data); + $this->assertSuccess(); + $this->assertTransferAttributes($this->_responseJsonBody); + $this->assertTrue(!isset($this->_responseJsonBody->authentication_token)); + } + + public function testMobileTransfersUpdateController_NoSession_ErrorInvalidToken() + { + $transfer = $this->insertTransferFixture($this->getDummyTransfer()); + $id = $transfer->id; + $token = UuidFactory::uuid('nope'); + $data = [ + 'status' => Transfer::TRANSFER_STATUS_COMPLETE, + 'current_page' => 1, + ]; + $this->postJson("/mobile/transfers/$id/$token.json", $data); + $this->assertError(401); + } + + public function testMobileTransfersUpdateController_NoSession_ErrorInvalidToken2() + { + $transfer = $this->insertTransferFixture($this->getDummyTransfer()); + $id = $transfer->id; + $data = [ + 'status' => Transfer::TRANSFER_STATUS_COMPLETE, + 'current_page' => 1, + ]; + $this->postJson("/mobile/transfers/$id/nope.json", $data); + $this->assertError(401); + } +} diff --git a/plugins/Passbolt/Mobile/tests/TestCase/Controller/Transfers/TransfersViewControllerTest.php b/plugins/Passbolt/Mobile/tests/TestCase/Controller/Transfers/TransfersViewControllerTest.php new file mode 100644 index 0000000000..9e98d99ca1 --- /dev/null +++ b/plugins/Passbolt/Mobile/tests/TestCase/Controller/Transfers/TransfersViewControllerTest.php @@ -0,0 +1,113 @@ +enableFeaturePlugin('mobile'); + } + + public function tearDown(): void + { + parent::tearDown(); + $this->disableFeaturePlugin('mobile'); + } + + public function testMobileTransfersViewController_Success() + { + $transfer = $this->insertTransferFixture($this->getDummyTransfer()); + $id = $transfer->id; + $this->authenticateAs('ada'); + $this->getJson("/mobile/transfers/$id.json"); + $this->assertSuccess(); + $this->assertTransferAttributes($this->_responseJsonBody); + $this->assertFalse(isset($this->_responseJsonBody->user)); + $this->assertFalse(isset($this->_responseJsonBody->user->profile)); + $this->assertFalse(isset($this->_responseJsonBody->user->profile->avatar)); + } + + public function testMobileTransfersViewController_SuccessWithContainUser() + { + $transfer = $this->insertTransferFixture($this->getDummyTransfer()); + $id = $transfer->id; + $this->authenticateAs('ada'); + $this->getJson("/mobile/transfers/$id.json?contain[user]=1"); + $this->assertSuccess(); + $this->assertTransferAttributes($this->_responseJsonBody); + $this->assertUserAttributes($this->_responseJsonBody->user); + $this->assertFalse(isset($this->_responseJsonBody->user->profile)); + $this->assertFalse(isset($this->_responseJsonBody->user->profile->avatar)); + } + + public function testMobileTransfersViewController_SuccessWithContainProfileAvatar() + { + $transfer = $this->insertTransferFixture($this->getDummyTransfer()); + $id = $transfer->id; + $this->authenticateAs('ada'); + $this->getJson("/mobile/transfers/$id.json?contain[user]=1&contain[user.profile]=1"); + $this->assertSuccess(); + $this->assertTransferAttributes($this->_responseJsonBody); + $this->assertUserAttributes($this->_responseJsonBody->user); + $this->assertProfileAttributes($this->_responseJsonBody->user->profile); + $this->assertAvatarAttributes($this->_responseJsonBody->user->profile->avatar); + } + + public function testMobileTransfersViewController_ErrorNotFound() + { + $this->authenticateAs('ada'); + $id = UuidFactory::uuid('nope'); + $this->getJson("/mobile/transfers/$id.json?api-version=2"); + $this->assertError(404); + } + + public function testMobileTransfersViewController_ErrorWrongUuidParameter() + { + $this->authenticateAs('ada'); + $this->getJson('/mobile/transfers/not_uuid.json?api-version=2'); + $this->assertError(400); + } + + public function testMobileTransfersViewController_ErrorNotAuthenticated() + { + $transfer = $this->insertTransferFixture($this->getDummyTransfer()); + $id = $transfer->id; + + $this->getJson("/mobile/transfers/$id.json"); + $this->assertAuthenticationError(); + } +} diff --git a/plugins/Passbolt/Mobile/tests/TestCase/Model/Table/Transfers/CancelAllTransfersWithInactiveAuthenticationTokenTest.php b/plugins/Passbolt/Mobile/tests/TestCase/Model/Table/Transfers/CancelAllTransfersWithInactiveAuthenticationTokenTest.php new file mode 100644 index 0000000000..0d9c55b316 --- /dev/null +++ b/plugins/Passbolt/Mobile/tests/TestCase/Model/Table/Transfers/CancelAllTransfersWithInactiveAuthenticationTokenTest.php @@ -0,0 +1,76 @@ +exists('Passbolt/Mobile.Transfers') ? + [] : ['className' => TransfersTable::class]; + $this->Transfers = TableRegistry::getTableLocator()->get('Passbolt/Mobile.Transfers', $config); + } + + public function testTransfersTablesCancelAllTransfersWithInactiveAuthenticationToken(): void + { + $inactiveToken = AuthenticationTokenFactory::make() + ->type(AuthenticationToken::TYPE_MOBILE_TRANSFER) + ->inactive(); + + TransferFactory::make() + ->status(Transfer::TRANSFER_STATUS_START) + ->withAuthenticationToken($inactiveToken) + ->persist(); + + $activeToken = AuthenticationTokenFactory::make() + ->type(AuthenticationToken::TYPE_MOBILE_TRANSFER) + ->active(); + + TransferFactory::make() + ->status(Transfer::TRANSFER_STATUS_START) + ->withAuthenticationToken($activeToken) + ->persist(); + + $this->Transfers->cancelAllTransfersWithInactiveAuthenticationToken(); + + $transfers = $this->Transfers->find() + ->all() + ->toArray(); + + $this->Transfers->cancelAllTransfersWithInactiveAuthenticationToken(); + $this->assertTrue(count(Hash::extract($transfers, '{n}[status=cancel].status')) === 1); + $this->assertTrue(count(Hash::extract($transfers, '{n}[status=start].status')) === 1); + } +} diff --git a/plugins/Passbolt/Mobile/tests/TestCase/Model/Table/Transfers/SaveTest.php b/plugins/Passbolt/Mobile/tests/TestCase/Model/Table/Transfers/SaveTest.php new file mode 100644 index 0000000000..a332194fc3 --- /dev/null +++ b/plugins/Passbolt/Mobile/tests/TestCase/Model/Table/Transfers/SaveTest.php @@ -0,0 +1,131 @@ +Transfers = TableRegistry::getTableLocator()->get('Passbolt/Mobile.Transfers'); + } + + public function tearDown(): void + { + unset($this->Transfers); + parent::tearDown(); + } + + public function getEntityDefaultOptions() + { + $entityOptions = $this->getTransferEntityAccessibleFields(); + + return $entityOptions; + } + + public function runTestsForField(string $fieldName, array $testCases) + { + $this->assertFieldFormatValidation( + $this->Transfers, + $fieldName, + $this->getDummyTransfer(), + $this->getEntityDefaultOptions(), + $testCases + ); + } + + /* FORMAT VALIDATION TESTS */ + + public function testMobileTransfersModel_ValidateId() + { + $this->runTestsForField('id', [ + 'uuid' => self::getUuidTestCases(), + 'allowEmpty' => self::getAllowEmptyTestCases(), + ]); + } + + public function testMobileTransfersModel_ValidateUserId() + { + $this->runTestsForField('user_id', [ + 'uuid' => self::getUuidTestCases(), + 'requirePresence' => self::getRequirePresenceTestCases(), + //'notEmpty' => self::getNotEmptyTestCases(), + ]); + } + + public function testMobileTransfersModel_ValidateHash() + { + $this->runTestsForField('hash', [ + 'ascii' => self::getAsciiTestCases(Transfer::TRANSFER_HASH_SIZE), + 'requirePresence' => self::getRequirePresenceTestCases(), + 'lengthBetween' => [ + 'rule_name' => 'lengthBetween', + 'test_cases' => [ + self::getStringMask('alphaASCII', Transfer::TRANSFER_HASH_SIZE) => true, + self::getStringMask('alphaASCII', Transfer::TRANSFER_HASH_SIZE - 1) => false, + self::getStringMask('alphaASCII', Transfer::TRANSFER_HASH_SIZE + 1) => false, + ], + ], + //'notEmpty' => self::getNotEmptyTestCases(), + ]); + } + + public function testMobileTransfersModel_ValidateCurrentPage() + { + $t = $this->Transfers->newEntity([ + 'current_page' => 50000000000000, + 'total_pages' => 1, + ]); + $errors = $t->getError('current_page'); + $this->assertNotEmpty($errors['inferior_to_total']); + $this->assertNotEmpty($errors['lessThan']); + } + + public function testMobileTransfersModel_ValidateTotalPage() + { + $t = $this->Transfers->newEntity([ + 'total_pages' => 50000000000000, + ]); + $errors = $t->getError('total_pages'); + $this->assertNotEmpty($errors['lessThan']); + } + + public function testMobileTransfersModel_ValidateStatus() + { + $t = $this->Transfers->newEntity([ + 'status' => 50000000000000, + ]); + $errors = $t->getError('status'); + $this->assertNotEmpty($errors['inList']); + } +} diff --git a/plugins/Passbolt/Mobile/tests/TestCase/Service/Transfers/TransfersCreateServiceTest.php b/plugins/Passbolt/Mobile/tests/TestCase/Service/Transfers/TransfersCreateServiceTest.php new file mode 100644 index 0000000000..88b18ae0a5 --- /dev/null +++ b/plugins/Passbolt/Mobile/tests/TestCase/Service/Transfers/TransfersCreateServiceTest.php @@ -0,0 +1,124 @@ + 1, + 'hash' => Security::hash('test', 'sha512', true), + ]; + + $accessControl = new UserAccessControl(Role::USER, UuidFactory::uuid('user.id.ada')); + $transfer = $service->create($data, $accessControl); + + // Check transfer entity + $this->assertEquals($transfer->user_id, UuidFactory::uuid('user.id.ada')); + $this->assertNotEmpty(Validation::uuid($transfer->id)); + $this->assertEquals($transfer->total_pages, 1); + $this->assertEquals($transfer->current_page, 0); + $this->assertEquals($transfer->status, Transfer::TRANSFER_STATUS_START); + $this->assertNotEmpty($transfer->created); + $this->assertNotEmpty($transfer->modified); + + // Check associated auth token + $this->assertTrue(Validation::uuid($transfer->authentication_token->id)); + $this->assertEquals($transfer->authentication_token->user_id, UuidFactory::uuid('user.id.ada')); + $this->assertEquals($transfer->authentication_token->type, AuthenticationToken::TYPE_MOBILE_TRANSFER); + $this->assertTrue($transfer->authentication_token->active); + $this->assertNotEmpty($transfer->authentication_token->created); + $this->assertNotEmpty($transfer->authentication_token->modified); + } + + public function testMobileTransfersCreateService_ValidationError() + { + $service = new TransfersCreateService(); + $data = [ + 'user_id' => 'nope', + 'total_pages' => 500000000000, + 'current_page' => -1, + 'hash' => 'nope', + ]; + $accessControl = new UserAccessControl(Role::USER, UuidFactory::uuid('user.id.ada')); + try { + $service->create($data, $accessControl); + $this->fail('Expect an exception'); + } catch (ValidationException $exception) { + $errors = $exception->getErrors(); + $this->assertNotEmpty($errors); + $this->assertNotEmpty($errors['hash']['lengthBetween']); + $this->assertNotEmpty($errors['total_pages']['lessThan']); + // shouldn't fail / overridden + $this->assertFalse(isset($errors['user_id'])); + $this->assertFalse(isset($errors['current_page'])); + + return; + } + + $this->fail('Expect a validation exception'); + } + + public function testMobileTransfersCreateService_BuildRulesError() + { + $service = new TransfersCreateService(); + $data = [ + 'total_pages' => 1, + 'hash' => Security::hash('test', 'sha512', true), + ]; + $accessControl = new UserAccessControl(Role::USER, UuidFactory::uuid('user.id.nope')); + try { + $service->create($data, $accessControl); + $this->fail('Expect an exception'); + } catch (ValidationException $exception) { + $errors = $exception->getErrors(); + $this->assertNotEmpty($errors['user_id']['user_exists']); + + return; + } + + $this->fail('Expect a validation exception'); + } +} diff --git a/plugins/Passbolt/Mobile/tests/TestCase/Service/Transfers/TransfersUpdateServiceTest.php b/plugins/Passbolt/Mobile/tests/TestCase/Service/Transfers/TransfersUpdateServiceTest.php new file mode 100644 index 0000000000..2b366f4470 --- /dev/null +++ b/plugins/Passbolt/Mobile/tests/TestCase/Service/Transfers/TransfersUpdateServiceTest.php @@ -0,0 +1,242 @@ +status(Transfer::TRANSFER_STATUS_IN_PROGRESS)->persist(); + $uac = new UserAccessControl(Role::USER, $transfer->user_id); + + $data = [ + 'status' => Transfer::TRANSFER_STATUS_COMPLETE, + 'current_page' => 1, + ]; + + $service = new TransfersUpdateService(); + $transferOriginal = clone $transfer; + $transferUpdated = $service->update($transfer, $data, $uac); + + // Unchanged data + $this->assertTextEquals($transferOriginal->id, $transferUpdated->id); + $this->assertTextEquals($transferOriginal->user_id, $transferUpdated->user_id); + $this->assertEquals($transferOriginal->total_pages, $transferUpdated->total_pages); + $this->assertEquals($transferOriginal->created, $transferUpdated->created); + + // Updated data + $this->assertNotEquals($transferOriginal->current_page, $transferUpdated->current_page); + $this->assertNotEquals($transferOriginal->status, $transferUpdated->status); + $this->assertNotEquals($transferOriginal->modified, $transferUpdated->modified); + + $this->assertEquals($transferUpdated->current_page, 1); + $this->assertTextEquals($transferUpdated->status, Transfer::TRANSFER_STATUS_COMPLETE); + } + + public function testMobileTransfersUpdateService_Success_TestTransitions() + { + $this->loadFixtures(); + $userId = UuidFactory::uuid('user.id.ada'); + $uac = new UserAccessControl(Role::USER, $userId); + $transfer = $this->insertTransferFixture($this->getDummyTransfer( + 'ada', + Transfer::TRANSFER_STATUS_START, + 1, + 2 + )); + + $service = new TransfersUpdateService(); + + // Update - in progress + $data = [ + 'status' => Transfer::TRANSFER_STATUS_IN_PROGRESS, + 'current_page' => 1, + ]; + $transferUpdated = $service->update($transfer, $data, $uac); + $this->assertEquals($transferUpdated->current_page, 1); + $this->assertTextEquals($transferUpdated->status, Transfer::TRANSFER_STATUS_IN_PROGRESS); + + // Update - error + $data = [ + 'status' => Transfer::TRANSFER_STATUS_ERROR, + 'current_page' => 1, + ]; + $transferUpdated = $service->update($transfer, $data, $uac); + $this->assertEquals($transferUpdated->current_page, 1); + $this->assertTextEquals($transferUpdated->status, Transfer::TRANSFER_STATUS_ERROR); + + // Update - completed + $data = [ + 'status' => Transfer::TRANSFER_STATUS_COMPLETE, + 'current_page' => 1, + ]; + $transferUpdated = $service->update($transfer, $data, $uac); + $this->assertEquals($transferUpdated->current_page, 1); + $this->assertTextEquals($transferUpdated->status, Transfer::TRANSFER_STATUS_COMPLETE); + } + + public function testMobileTransfersCreateService_Error_TransitionsNotAllowed() + { + $this->loadFixtures(); + $service = new TransfersUpdateService(); + $transfer = $service->Transfers->newEntity($this->getDummyTransfer( + 'ada', + Transfer::TRANSFER_STATUS_START, + 0, + 2 + ), ['accessibleFields' => [ + 'id' => false, + 'user_id' => true, + 'current_page' => true, + 'total_pages' => true, + 'hash' => true, + 'status' => true, + 'authentication_token' => true, + ]]); + $transferUpdated = clone $transfer; + + $fails = [ + // Original -> Updated + // Cannot update status of completed transfer + [Transfer::TRANSFER_STATUS_COMPLETE, Transfer::TRANSFER_STATUS_COMPLETE], + [Transfer::TRANSFER_STATUS_COMPLETE, Transfer::TRANSFER_STATUS_CANCEL], + [Transfer::TRANSFER_STATUS_COMPLETE, Transfer::TRANSFER_STATUS_IN_PROGRESS], + [Transfer::TRANSFER_STATUS_COMPLETE, Transfer::TRANSFER_STATUS_ERROR], + [Transfer::TRANSFER_STATUS_COMPLETE, Transfer::TRANSFER_STATUS_START], + // Cannot update status of cancelled transfer + [Transfer::TRANSFER_STATUS_CANCEL, Transfer::TRANSFER_STATUS_COMPLETE], + [Transfer::TRANSFER_STATUS_CANCEL, Transfer::TRANSFER_STATUS_CANCEL], + [Transfer::TRANSFER_STATUS_CANCEL, Transfer::TRANSFER_STATUS_IN_PROGRESS], + [Transfer::TRANSFER_STATUS_CANCEL, Transfer::TRANSFER_STATUS_ERROR], + [Transfer::TRANSFER_STATUS_CANCEL, Transfer::TRANSFER_STATUS_START], + // Cannot restart + [Transfer::TRANSFER_STATUS_ERROR, Transfer::TRANSFER_STATUS_START], + [Transfer::TRANSFER_STATUS_IN_PROGRESS, Transfer::TRANSFER_STATUS_START], + // Cannot complete without being on last page + [Transfer::TRANSFER_STATUS_IN_PROGRESS, Transfer::TRANSFER_STATUS_COMPLETE], + ]; + + foreach ($fails as $i => $failTuple) { + $transfer->status = $failTuple[0]; + $transferUpdated->status = $failTuple[1]; + try { + $service->assertTransitionAllowed($transfer, $transferUpdated); + $this->fail('Expect an exception'); + } catch (ForbiddenException $exception) { + $this->assertTrue(true); + } catch (\Exception $exception) { + $msg = __('Expect a forbidden exception for assertion {0} {1} {2}', $i, $failTuple[0], $failTuple[1]); + $this->fail($msg); + } + } + } + + public function testMobileTransfersCreateService_Error_TransitionsNotAllowed_CurrentPageBiggerThanTotal() + { + $this->loadFixtures(); + $userId = UuidFactory::uuid('user.id.ada'); + $uac = new UserAccessControl(Role::USER, $userId); + $transfer = $this->insertTransferFixture($this->getDummyTransfer( + 'ada', + Transfer::TRANSFER_STATUS_START, + 1, + 2 + )); + + $service = new TransfersUpdateService(); + + // Update - in progress + $data = [ + 'status' => Transfer::TRANSFER_STATUS_IN_PROGRESS, + 'current_page' => 2, + ]; + $this->expectException(ValidationException::class); + $service->update($transfer, $data, $uac); + } + + public function testMobileTransfersUpdateService_Error_ValidationError() + { + $transfer = TransferFactory::make()->status(Transfer::TRANSFER_STATUS_IN_PROGRESS)->persist(); + $service = new TransfersUpdateService(); + $uac = new UserAccessControl(Role::USER, $transfer->user_id); + $data = []; + + $this->expectException(ValidationException::class); + $this->expectExceptionMessage('Could not validate the transfer data.'); + $service->update($transfer, $data, $uac); + } + + public function testMobileTransfersUpdateService_Error_ExpiredAuthToken() + { + $userId = UuidFactory::uuid(); + $transfer = TransferFactory::make() + ->status(Transfer::TRANSFER_STATUS_IN_PROGRESS) + ->userId($userId) + ->withAuthenticationToken( + AuthenticationTokenFactory::make() + ->type(AuthenticationToken::TYPE_MOBILE_TRANSFER) + ->userId($userId) + ->active() + ->expired() + ) + ->persist(); + + $service = new TransfersUpdateService(); + $uac = new UserAccessControl(Role::USER, $transfer->user_id); + $data = []; + + $this->expectException(ForbiddenException::class); + $this->expectExceptionMessage('The authentication token is expired.'); + $service->update($transfer, $data, $uac); + } +} diff --git a/plugins/Passbolt/Mobile/tests/bootstrap.php b/plugins/Passbolt/Mobile/tests/bootstrap.php new file mode 100644 index 0000000000..b0fa51015d --- /dev/null +++ b/plugins/Passbolt/Mobile/tests/bootstrap.php @@ -0,0 +1,34 @@ + [ + 'plugins' => [ + 'passwordGenerator' => [ + 'version' => '3.3.0', + ], + ], + ], +]; diff --git a/plugins/Passbolt/PasswordGenerator/config/routes.php b/plugins/Passbolt/PasswordGenerator/config/routes.php new file mode 100644 index 0000000000..1f1e16126c --- /dev/null +++ b/plugins/Passbolt/PasswordGenerator/config/routes.php @@ -0,0 +1,24 @@ + '/password-generator'], function (RouteBuilder $routes) { + $routes->setExtensions(['json']); + + $routes->connect('/settings', ['controller' => 'PasswordGeneratorSettings', 'action' => 'index']) + ->setMethods(['GET']); +}); diff --git a/plugins/Passbolt/PasswordGenerator/src/Controller/PasswordGeneratorSettingsController.php b/plugins/Passbolt/PasswordGenerator/src/Controller/PasswordGeneratorSettingsController.php new file mode 100644 index 0000000000..2552241622 --- /dev/null +++ b/plugins/Passbolt/PasswordGenerator/src/Controller/PasswordGeneratorSettingsController.php @@ -0,0 +1,38 @@ + $service->getPasswordGenerator(), + ]; + $this->success(__('The operation was successful.'), $setting); + } +} diff --git a/plugins/Passbolt/PasswordGenerator/src/Plugin.php b/plugins/Passbolt/PasswordGenerator/src/Plugin.php new file mode 100644 index 0000000000..b1f1a6146b --- /dev/null +++ b/plugins/Passbolt/PasswordGenerator/src/Plugin.php @@ -0,0 +1,64 @@ +loadConfigs(); + } + + /** + * Load the plugin's configs. + * Check if some config are defined in config/passbolt.php + * + * @return void + */ + public function loadConfigs(): void + { + Configure::load('Passbolt/PasswordGenerator.config', 'default', true); + + if ( + !Configure::check(self::DEFAULT_PASSWORD_GENERATOR_CONFIG_KEY) && + !empty(env('PASSBOLT_PLUGINS_PASSWORD_GENERATOR_DEFAULT_GENERATOR')) + ) { + Configure::write( + self::DEFAULT_PASSWORD_GENERATOR_CONFIG_KEY, + env('PASSBOLT_PLUGINS_PASSWORD_GENERATOR_DEFAULT_GENERATOR') + ); + } + if (!Configure::check(self::PASSWORD_GENERATOR_ENABLED_CONFIG_KEY)) { + Configure::write( + self::PASSWORD_GENERATOR_ENABLED_CONFIG_KEY, + env('PASSBOLT_PLUGINS_PASSWORD_GENERATOR_ENABLED', true) + ); + } + } +} diff --git a/plugins/Passbolt/PasswordGenerator/src/Service/GetPasswordGeneratorService.php b/plugins/Passbolt/PasswordGenerator/src/Service/GetPasswordGeneratorService.php new file mode 100644 index 0000000000..e1e9980c50 --- /dev/null +++ b/plugins/Passbolt/PasswordGenerator/src/Service/GetPasswordGeneratorService.php @@ -0,0 +1,98 @@ +readInConfig()); + $this->validateGenerator($envGenerator); + + return $envGenerator; + } + + /** + * Read the password generator in the plugin's config. + * + * @return string + */ + protected function readInConfig(): string + { + $envGenerator = Configure::read(Plugin::DEFAULT_PASSWORD_GENERATOR_CONFIG_KEY); + if (empty($envGenerator)) { + return $this->getDefaultGenerator(); + } + + return $envGenerator; + } + + /** + * Checks that the generator passed is supported by Passbolt. + * + * @param string $generator Generator to be validated. + * @return void + * @throws \Cake\Http\Exception\InternalErrorException if the password generator provided is not valid. + */ + protected function validateGenerator(string $generator): void + { + if (!in_array($generator, $this->getSupportedGenerators())) { + throw new InternalErrorException(__('The password generator value "{0}" is not valid.', $generator)); + } + } + + /** + * Lists the password generators supported by Passbolt. + * + * @return string[] + */ + protected function getSupportedGenerators(): array + { + return [ + self::PASSWORD_GENERATOR_SETTING_PASSPHRASE, + self::PASSWORD_GENERATOR_SETTING_PASSWORD, + ]; + } + + /** + * The default password generator. + * + * @return string + */ + protected function getDefaultGenerator(): string + { + return self::PASSWORD_GENERATOR_SETTING_PASSWORD; + } +} diff --git a/plugins/Passbolt/PasswordGenerator/tests/TestCase/Controller/PasswordGeneratorSettingsControllerTest.php b/plugins/Passbolt/PasswordGenerator/tests/TestCase/Controller/PasswordGeneratorSettingsControllerTest.php new file mode 100644 index 0000000000..49f68d410e --- /dev/null +++ b/plugins/Passbolt/PasswordGenerator/tests/TestCase/Controller/PasswordGeneratorSettingsControllerTest.php @@ -0,0 +1,64 @@ +logInAsUser(); + + $this->getJson('/password-generator/settings.json'); + + if ($generator === 'exception') { + $this->assertResponseFailure('The password generator value "' . $env . '" is not valid.'); + } else { + $this->assertResponseSuccess(); + $this->assertSame($generator, $this->_responseJsonBody->default_generator); + } + } + + public function dataForTestPasswordDefaultGeneratorSettings(): array + { + return [ + [null, GetPasswordGeneratorService::PASSWORD_GENERATOR_SETTING_PASSWORD], + ['', GetPasswordGeneratorService::PASSWORD_GENERATOR_SETTING_PASSWORD], + ['foo', 'exception'], + [ucfirst(GetPasswordGeneratorService::PASSWORD_GENERATOR_SETTING_PASSPHRASE), GetPasswordGeneratorService::PASSWORD_GENERATOR_SETTING_PASSPHRASE], + [GetPasswordGeneratorService::PASSWORD_GENERATOR_SETTING_PASSWORD, GetPasswordGeneratorService::PASSWORD_GENERATOR_SETTING_PASSWORD], + ]; + } +} diff --git a/plugins/Passbolt/PasswordGenerator/tests/TestCase/Service/GetPasswordGeneratorServiceTest.php b/plugins/Passbolt/PasswordGenerator/tests/TestCase/Service/GetPasswordGeneratorServiceTest.php new file mode 100644 index 0000000000..e148cf2fa8 --- /dev/null +++ b/plugins/Passbolt/PasswordGenerator/tests/TestCase/Service/GetPasswordGeneratorServiceTest.php @@ -0,0 +1,67 @@ +assertSame($generator, $service->getPasswordGenerator()); + } + + /** + * @dataProvider dataForTestGetPasswordGeneratorError + */ + public function testGetPasswordGeneratorError($env) + { + Configure::write(Plugin::DEFAULT_PASSWORD_GENERATOR_CONFIG_KEY, $env); + $service = new GetPasswordGeneratorService(); + + $this->expectException(InternalErrorException::class); + $this->expectExceptionMessage('The password generator value "' . $env . '" is not valid.'); + $service->getPasswordGenerator(); + } + + public function dataForTestGetPasswordGeneratorHappyPath(): array + { + return [ + [null, GetPasswordGeneratorService::PASSWORD_GENERATOR_SETTING_PASSWORD], + ['', GetPasswordGeneratorService::PASSWORD_GENERATOR_SETTING_PASSWORD], + [ucfirst(GetPasswordGeneratorService::PASSWORD_GENERATOR_SETTING_PASSPHRASE), GetPasswordGeneratorService::PASSWORD_GENERATOR_SETTING_PASSPHRASE], + [GetPasswordGeneratorService::PASSWORD_GENERATOR_SETTING_PASSWORD, GetPasswordGeneratorService::PASSWORD_GENERATOR_SETTING_PASSWORD], + ]; + } + + public function dataForTestGetPasswordGeneratorError(): array + { + return [ + ['foo'], + ]; + } +} diff --git a/plugins/Passbolt/Reports/src/Controller/Reports/ReportsViewController.php b/plugins/Passbolt/Reports/src/Controller/Reports/ReportsViewController.php index 9632f7f3f8..490c949a20 100644 --- a/plugins/Passbolt/Reports/src/Controller/Reports/ReportsViewController.php +++ b/plugins/Passbolt/Reports/src/Controller/Reports/ReportsViewController.php @@ -112,7 +112,7 @@ private function renderReportInHtml(ReportInterface $report) $this->viewBuilder() ->setTemplatePath('Reports/html') ->setLayout(static::DEFAULT_LAYOUT) - ->setTheme(false) + ->setTheme(null) ->setTemplate($report->getTemplate()); $this->set('report', $report->createReport()); } diff --git a/plugins/Passbolt/Reports/src/Service/ReportPool.php b/plugins/Passbolt/Reports/src/Service/ReportPool.php index 306712a8dd..7f622ad9e6 100644 --- a/plugins/Passbolt/Reports/src/Service/ReportPool.php +++ b/plugins/Passbolt/Reports/src/Service/ReportPool.php @@ -29,7 +29,7 @@ class ReportPool /** * Instance of class used for singleton. * - * @var \Passbolt\Reports\Service\ReportPool + * @var \Passbolt\Reports\Service\ReportPool|null */ private static $instance; diff --git a/plugins/Passbolt/Reports/src/Service/ReportViewService.php b/plugins/Passbolt/Reports/src/Service/ReportViewService.php index 5fb049242a..2a27dd07e5 100644 --- a/plugins/Passbolt/Reports/src/Service/ReportViewService.php +++ b/plugins/Passbolt/Reports/src/Service/ReportViewService.php @@ -49,6 +49,7 @@ public function __construct(?ReportPool $reportPool = null) public function getReport(string $reportSlug, ?array $parameters = []) { $reports = $this->reportPool->getReports(); + /** @var class-string $reportClass */ $reportClass = $reports[$reportSlug] ?? false; if (!$reportClass) { diff --git a/plugins/Passbolt/Reports/src/Utility/AbstractCombinedReport.php b/plugins/Passbolt/Reports/src/Utility/AbstractCombinedReport.php index e74e913692..e3ed5ba392 100644 --- a/plugins/Passbolt/Reports/src/Utility/AbstractCombinedReport.php +++ b/plugins/Passbolt/Reports/src/Utility/AbstractCombinedReport.php @@ -33,6 +33,7 @@ abstract class AbstractCombinedReport extends AbstractReport implements Combined */ public function getTemplate(): string { + /** @psalm-suppress RedundantPropertyInitializationCheck */ return $this->template ?? self::COMBINED_REPORT_TEMPLATE; } diff --git a/plugins/Passbolt/Reports/src/Utility/AbstractReport.php b/plugins/Passbolt/Reports/src/Utility/AbstractReport.php index 213ef5eec2..e0d18523e7 100644 --- a/plugins/Passbolt/Reports/src/Utility/AbstractReport.php +++ b/plugins/Passbolt/Reports/src/Utility/AbstractReport.php @@ -76,7 +76,7 @@ public function setName(string $name) /** * @inheritDoc */ - public function setDescriptigetReportson(string $description) + public function setDescription(string $description) { $this->description = $description; @@ -142,6 +142,7 @@ public function getDescription() */ public function getOptions() { + /** @psalm-suppress RedundantPropertyInitializationCheck */ return $this->options ?? new FindIndexOptions(); } @@ -150,6 +151,7 @@ public function getOptions() */ public function getCreator() { + /** @psalm-suppress RedundantPropertyInitializationCheck */ return $this->creator ?? null; } diff --git a/plugins/Passbolt/Reports/src/Utility/AbstractSingleReport.php b/plugins/Passbolt/Reports/src/Utility/AbstractSingleReport.php index 9aa48800e0..0a7a79fc5b 100644 --- a/plugins/Passbolt/Reports/src/Utility/AbstractSingleReport.php +++ b/plugins/Passbolt/Reports/src/Utility/AbstractSingleReport.php @@ -32,6 +32,7 @@ abstract class AbstractSingleReport extends AbstractReport */ public function getTemplate(): string { + /** @psalm-suppress RedundantPropertyInitializationCheck */ return $this->template ?? self::SINGLE_REPORT_TEMPLATE; } diff --git a/plugins/Passbolt/Reports/src/View/Helper/ReportHelper.php b/plugins/Passbolt/Reports/src/View/Helper/ReportHelper.php index 032065b0c4..87310aa90e 100644 --- a/plugins/Passbolt/Reports/src/View/Helper/ReportHelper.php +++ b/plugins/Passbolt/Reports/src/View/Helper/ReportHelper.php @@ -16,6 +16,7 @@ class ReportHelper extends Helper */ public static function getSubReport(array $reportData, string $slug) { + /** @var array $report */ foreach ($reportData as $report) { if (isset($report['slug']) && $report['slug'] === $slug) { return $report; diff --git a/plugins/Passbolt/Reports/tests/TestCase/Controller/ReportsViewControllerTest.php b/plugins/Passbolt/Reports/tests/TestCase/Controller/ReportsViewControllerTest.php index fead68c526..9882acd14d 100644 --- a/plugins/Passbolt/Reports/tests/TestCase/Controller/ReportsViewControllerTest.php +++ b/plugins/Passbolt/Reports/tests/TestCase/Controller/ReportsViewControllerTest.php @@ -45,11 +45,13 @@ public function getData() return []; } + /** @psalm-suppress InvalidReturnType no return type */ public function setDescription(string $description) { } }; + /** @psalm-suppress UndefinedClass unconventional stuff is happening here */ $report = new $ReportClass(); $report->setSlug($slug) ->setName(__('Sample report')) @@ -105,11 +107,13 @@ public function getData() return []; } + /** @psalm-suppress InvalidReturnType no return type */ public function setDescription(string $description) { } }; + /** @psalm-suppress UndefinedClass unconventional stuff is happening here */ $report = new $ReportClass(); $report->setSlug($slug) ->setName(__('Sample report with argument')) diff --git a/plugins/Passbolt/WebInstaller/src/Controller/DatabaseController.php b/plugins/Passbolt/WebInstaller/src/Controller/DatabaseController.php index 995b37f808..30d3240a50 100644 --- a/plugins/Passbolt/WebInstaller/src/Controller/DatabaseController.php +++ b/plugins/Passbolt/WebInstaller/src/Controller/DatabaseController.php @@ -32,8 +32,10 @@ class DatabaseController extends WebInstallerController { /** * Default password to use in the UI in case the config is provided through a .ini config file. + * + * @var string */ - private $defaultPassword = null; + private $defaultPassword; /** * Ini config file content (if ini file provided). diff --git a/plugins/Passbolt/WebInstaller/src/Controller/EmailController.php b/plugins/Passbolt/WebInstaller/src/Controller/EmailController.php index db70672718..71761b24cd 100644 --- a/plugins/Passbolt/WebInstaller/src/Controller/EmailController.php +++ b/plugins/Passbolt/WebInstaller/src/Controller/EmailController.php @@ -38,6 +38,8 @@ class EmailController extends WebInstallerController /** * Contains the email class. + * + * @var \Cake\Mailer\Mailer */ protected $email = null; @@ -137,7 +139,9 @@ protected function sendTestEmail($data) ->setSubject(__('passbolt test email')) ->deliver($this->_getDefaultMessage()); } catch (\Exception $e) { - $trace = $this->email->getTransport()->getTrace(); + /** @var \App\Mailer\Transport\DebugSmtpTransport $transport */ + $transport = $this->email->getTransport(); + $trace = $transport->getTrace(); $this->set([ 'test_email_status' => false, 'test_email_error' => $e->getMessage(), diff --git a/plugins/Passbolt/WebInstaller/src/Controller/WebInstallerController.php b/plugins/Passbolt/WebInstaller/src/Controller/WebInstallerController.php index b464f0e482..cf55ef6322 100644 --- a/plugins/Passbolt/WebInstaller/src/Controller/WebInstallerController.php +++ b/plugins/Passbolt/WebInstaller/src/Controller/WebInstallerController.php @@ -33,6 +33,8 @@ class WebInstallerController extends Controller { /** * The web installer model + * + * @var \Passbolt\WebInstaller\Utility\WebInstaller */ protected $webInstaller; @@ -67,7 +69,7 @@ public function initialize(): void * Do not let the user proceed if the configuration is not found in the session. * Instead redirect him to the first step. * - * @param \Cake\Event\Event $event event + * @param \Cake\Event\EventInterface $event event * @return \Cake\Http\Response|null */ public function beforeFilter(\Cake\Event\EventInterface $event): ?Response @@ -89,7 +91,7 @@ public function beforeFilter(\Cake\Event\EventInterface $event): ?Response /** * Before render. * - * @param \Cake\Event\Event $event event + * @param \Cake\Event\EventInterface $event event * @return void */ public function beforeRender(\Cake\Event\EventInterface $event): void diff --git a/plugins/Passbolt/WebInstaller/src/Utility/DatabaseConfiguration.php b/plugins/Passbolt/WebInstaller/src/Utility/DatabaseConfiguration.php index 7baed50491..534cbb9d04 100644 --- a/plugins/Passbolt/WebInstaller/src/Utility/DatabaseConfiguration.php +++ b/plugins/Passbolt/WebInstaller/src/Utility/DatabaseConfiguration.php @@ -87,7 +87,7 @@ public static function testConnection() /** * Get the database tables names * - * @return array + * @return array|\ArrayAccess */ public static function getTables() { diff --git a/plugins/Passbolt/WebInstaller/src/Utility/WebInstaller.php b/plugins/Passbolt/WebInstaller/src/Utility/WebInstaller.php index e5c9ba9b91..ef0e81ceeb 100644 --- a/plugins/Passbolt/WebInstaller/src/Utility/WebInstaller.php +++ b/plugins/Passbolt/WebInstaller/src/Utility/WebInstaller.php @@ -31,10 +31,15 @@ class WebInstaller { + /** + * @var \Cake\Http\Session|null + */ protected $session = null; + + /** + * @var array|mixed + */ protected $settings = []; - public $createdUser = null; - public $createdUserToken = null; /** * WebInstaller constructor. diff --git a/plugins/Passbolt/WebInstaller/templates/Pages/account_creation.php b/plugins/Passbolt/WebInstaller/templates/Pages/account_creation.php index d03b4f48a1..f8bcf758a8 100644 --- a/plugins/Passbolt/WebInstaller/templates/Pages/account_creation.php +++ b/plugins/Passbolt/WebInstaller/templates/Pages/account_creation.php @@ -56,7 +56,7 @@
- +
diff --git a/plugins/Passbolt/WebInstaller/templates/Pages/database.php b/plugins/Passbolt/WebInstaller/templates/Pages/database.php index ee638ded6c..b72574f12e 100644 --- a/plugins/Passbolt/WebInstaller/templates/Pages/database.php +++ b/plugins/Passbolt/WebInstaller/templates/Pages/database.php @@ -19,7 +19,7 @@
-

+

Flash->render() ?> -
+
-
+
Form->control('type', [ 'type' => 'select', 'options' => ['mysql' => 'mysql://'], @@ -54,19 +54,19 @@ 'label' => false, 'class' => 'required fluid', ]); ?> -
- Form->control('port', [ - 'type' => 'number', - 'templates' => [ - 'inputContainer' => '
{{content}}
', - ], - 'required' => 'required', - 'placeholder' => '3306', - 'label' => false, - 'class' => 'required fluid', - 'default' => '3306', + Form->control('port', [ + 'type' => 'number', + 'templates' => [ + 'inputContainer' => '
{{content}}
', + ], + 'required' => 'required', + 'placeholder' => '3306', + 'label' => false, + 'class' => 'required fluid', + 'default' => '3306', - ]); ?> + ]); ?> +
@@ -103,15 +103,13 @@
-

Existing installation?

-

If you want to use an existing passbolt database, the installer will take care of updating it to the current passbolt version while keeping your data.

-

As a precaution, do not forget to backup your database before you continue.

+ element('sidebar/existing_installation') ?>
- - + +
diff --git a/plugins/Passbolt/WebInstaller/templates/Pages/email.php b/plugins/Passbolt/WebInstaller/templates/Pages/email.php index fcb04a7264..97da89fb4c 100644 --- a/plugins/Passbolt/WebInstaller/templates/Pages/email.php +++ b/plugins/Passbolt/WebInstaller/templates/Pages/email.php @@ -18,7 +18,7 @@
-

+

Flash->render() ?> Form->control('sender_name', [ 'type' => 'text', @@ -36,7 +36,7 @@ 'type' => 'email', ]); ?> -

+

Form->control('host', [ 'type' => 'text', 'required' => 'required', @@ -76,61 +76,14 @@

 

-
- Pro tip: a cron job is required
- Once your installation is complete, do not forget to set a cron job in order to have your emails sent automatically.
Read the doc -
-

Why do I need a SMTP server?

-

Passbolt needs an smtp server in order to send invitation emails after an account creation and to send email notifications.

- -

Send test email

-

Test your configuration by sending a test email.

- - - -
- -
- -
- - - -
- - - -
- - -
- Form->control('email_test_to', [ - 'placeholder' => __('Your email address'), - 'label' => false, - 'class' => 'required fluid', - 'type' => 'email', - ]); - ?> - -
+ element('sidebar/smtp_explanations') ?> + element('sidebar/send_test_email') ?>
- - + +
diff --git a/plugins/Passbolt/WebInstaller/templates/Pages/getting_started.php b/plugins/Passbolt/WebInstaller/templates/Pages/getting_started.php index 695c73f6f5..6f7ef389c3 100644 --- a/plugins/Passbolt/WebInstaller/templates/Pages/getting_started.php +++ b/plugins/Passbolt/WebInstaller/templates/Pages/getting_started.php @@ -30,7 +30,8 @@

 

- + +

diff --git a/plugins/Passbolt/WebInstaller/templates/Pages/gpg_key_generate.php b/plugins/Passbolt/WebInstaller/templates/Pages/gpg_key_generate.php index 5df622a25a..913470b764 100644 --- a/plugins/Passbolt/WebInstaller/templates/Pages/gpg_key_generate.php +++ b/plugins/Passbolt/WebInstaller/templates/Pages/gpg_key_generate.php @@ -16,12 +16,12 @@ Form->create($formExecuteResult); ?> - Form->setTemplates(['inputContainer' => '
{{content}}
']); ?> + Form->setTemplates(['inputContainer' => '
{{content}}
']); ?>
-

+

Flash->render() ?> Form->control('public_key_armored', ['type' => 'hidden']); @@ -50,7 +50,7 @@ ?>
-

Advanced settings

+

Advanced settings

- -
- - - -
- - + +
diff --git a/plugins/Passbolt/WebInstaller/templates/Pages/gpg_key_import.php b/plugins/Passbolt/WebInstaller/templates/Pages/gpg_key_import.php index 8fd3193951..5f9e6d0b92 100644 --- a/plugins/Passbolt/WebInstaller/templates/Pages/gpg_key_import.php +++ b/plugins/Passbolt/WebInstaller/templates/Pages/gpg_key_import.php @@ -27,12 +27,12 @@
-

+

Flash->render() ?> Form->control('armored_key', [ 'type' => 'textarea', 'templates' => [ - 'inputContainer' => '
{{content}}
', + 'inputContainer' => '
{{content}}
', ], ]); ?>
@@ -44,8 +44,8 @@
- - + +
diff --git a/plugins/Passbolt/WebInstaller/templates/Pages/license_key.php b/plugins/Passbolt/WebInstaller/templates/Pages/license_key.php index ed9ac14186..9ed1c4b453 100644 --- a/plugins/Passbolt/WebInstaller/templates/Pages/license_key.php +++ b/plugins/Passbolt/WebInstaller/templates/Pages/license_key.php @@ -18,7 +18,7 @@
-

+

Flash->render() ?>
Form->control('license_key', ['type' => 'textarea', 'class' => ['key-content']]); ?> @@ -36,8 +36,8 @@
- - + +
diff --git a/plugins/Passbolt/WebInstaller/templates/Pages/options.php b/plugins/Passbolt/WebInstaller/templates/Pages/options.php index cadba8ce10..7535193c17 100644 --- a/plugins/Passbolt/WebInstaller/templates/Pages/options.php +++ b/plugins/Passbolt/WebInstaller/templates/Pages/options.php @@ -15,7 +15,7 @@
-

+

Flash->render() ?> Form->control('full_base_url', [ @@ -25,7 +25,7 @@ 'label' => __('Full base url'), 'class' => 'required fluid', 'templates' => [ - 'inputContainer' => '
{{content}}
' . __('This is the url where passbolt will be accessible. This url will be used for places where the passbolt url cannot be guessed automatically, such as links in emails. No trailing slash.') . '
', + 'inputContainer' => '
{{content}}
' . __('This is the url where passbolt will be accessible. This url will be used for places where the passbolt url cannot be guessed automatically, such as links in emails. No trailing slash.') . '
', ], ]); ?> @@ -41,7 +41,7 @@ ] ); ?> -
+
'required fluid', ]); ?> -
+
@@ -62,8 +62,8 @@
- - + +
diff --git a/plugins/Passbolt/WebInstaller/templates/Pages/system_check.php b/plugins/Passbolt/WebInstaller/templates/Pages/system_check.php index 109f8e8add..51f471e489 100644 --- a/plugins/Passbolt/WebInstaller/templates/Pages/system_check.php +++ b/plugins/Passbolt/WebInstaller/templates/Pages/system_check.php @@ -72,9 +72,9 @@ - + - + diff --git a/plugins/Passbolt/WebInstaller/templates/element/header.php b/plugins/Passbolt/WebInstaller/templates/element/header.php index 6443f9a7f2..63435b84ca 100644 --- a/plugins/Passbolt/WebInstaller/templates/element/header.php +++ b/plugins/Passbolt/WebInstaller/templates/element/header.php @@ -16,11 +16,9 @@
- +
-

+

diff --git a/plugins/Passbolt/WebInstaller/templates/element/install/complete.php b/plugins/Passbolt/WebInstaller/templates/element/install/complete.php index 9342481f71..1959fff7dd 100644 --- a/plugins/Passbolt/WebInstaller/templates/element/install/complete.php +++ b/plugins/Passbolt/WebInstaller/templates/element/install/complete.php @@ -3,7 +3,7 @@

- + @@ -17,8 +17,8 @@
- +
-
\ No newline at end of file +
diff --git a/plugins/Passbolt/WebInstaller/templates/element/install/errors.php b/plugins/Passbolt/WebInstaller/templates/element/install/errors.php index 829381d300..d502147b73 100644 --- a/plugins/Passbolt/WebInstaller/templates/element/install/errors.php +++ b/plugins/Passbolt/WebInstaller/templates/element/install/errors.php @@ -4,14 +4,14 @@

- Error! + Error!

-

See debug info

+

See debug info

- \ No newline at end of file + diff --git a/plugins/Passbolt/WebInstaller/templates/element/install/installing.php b/plugins/Passbolt/WebInstaller/templates/element/install/installing.php index 1157536dfc..6e61356543 100644 --- a/plugins/Passbolt/WebInstaller/templates/element/install/installing.php +++ b/plugins/Passbolt/WebInstaller/templates/element/install/installing.php @@ -14,7 +14,7 @@
- +
diff --git a/plugins/Passbolt/WebInstaller/templates/element/sidebar/existing_installation.php b/plugins/Passbolt/WebInstaller/templates/element/sidebar/existing_installation.php new file mode 100644 index 0000000000..675103eea9 --- /dev/null +++ b/plugins/Passbolt/WebInstaller/templates/element/sidebar/existing_installation.php @@ -0,0 +1,5 @@ + diff --git a/plugins/Passbolt/WebInstaller/templates/element/sidebar/help_box.php b/plugins/Passbolt/WebInstaller/templates/element/sidebar/help_box.php index be069ad840..d1510592b2 100644 --- a/plugins/Passbolt/WebInstaller/templates/element/sidebar/help_box.php +++ b/plugins/Passbolt/WebInstaller/templates/element/sidebar/help_box.php @@ -1,6 +1,14 @@ -

-

- - - - + diff --git a/plugins/Passbolt/WebInstaller/templates/element/sidebar/license_key_explanations.php b/plugins/Passbolt/WebInstaller/templates/element/sidebar/license_key_explanations.php index a2279d16d2..d2d434bede 100644 --- a/plugins/Passbolt/WebInstaller/templates/element/sidebar/license_key_explanations.php +++ b/plugins/Passbolt/WebInstaller/templates/element/sidebar/license_key_explanations.php @@ -1,6 +1,14 @@ -

-

- - - - + diff --git a/plugins/Passbolt/WebInstaller/templates/element/sidebar/send_test_email.php b/plugins/Passbolt/WebInstaller/templates/element/sidebar/send_test_email.php new file mode 100644 index 0000000000..ee8419520e --- /dev/null +++ b/plugins/Passbolt/WebInstaller/templates/element/sidebar/send_test_email.php @@ -0,0 +1,44 @@ + diff --git a/plugins/Passbolt/WebInstaller/templates/element/sidebar/smtp_explanations.php b/plugins/Passbolt/WebInstaller/templates/element/sidebar/smtp_explanations.php new file mode 100644 index 0000000000..5d83418477 --- /dev/null +++ b/plugins/Passbolt/WebInstaller/templates/element/sidebar/smtp_explanations.php @@ -0,0 +1,4 @@ + diff --git a/plugins/Passbolt/WebInstaller/tests/Lib/ConfigurationTrait.php b/plugins/Passbolt/WebInstaller/tests/Lib/ConfigurationTrait.php index 0ee68598e6..56a0f4ba49 100644 --- a/plugins/Passbolt/WebInstaller/tests/Lib/ConfigurationTrait.php +++ b/plugins/Passbolt/WebInstaller/tests/Lib/ConfigurationTrait.php @@ -95,9 +95,32 @@ protected function restoreConfiguration() return; } + if (is_dir(CONFIG)) { + chmod(CONFIG, 0770); + } if (file_exists(CONFIG . 'passbolt.php')) { - chmod(CONFIG . 'passbolt.php', 0777); + chmod(CONFIG . 'passbolt.php', 0770); + } + if (file_exists(CONFIG . 'license')) { + chmod(CONFIG . 'license', 0644); + } + if (file_exists(CONFIG . 'subscription_key.txt')) { + chmod(CONFIG . 'subscription_key.txt', 0644); + } + if (is_dir(CONFIG . 'gpg')) { + chmod(CONFIG . 'gpg', 0770); } + if (file_exists(CONFIG . 'gpg/unsecure.key')) { + chmod(CONFIG . 'gpg/unsecure.key', 0644); + } + if (file_exists(CONFIG . 'gpg/unsecure_private.key')) { + chmod(CONFIG . 'gpg/unsecure_private.key', 0644); + } + + if (is_dir(CONFIG . 'jwt')) { + chmod(CONFIG . 'jwt', 0770); + } + if (isset($this->backupConfig['passboltConfig'])) { file_put_contents(CONFIG . 'passbolt.php', $this->backupConfig['passboltConfig']); } else { diff --git a/plugins/Passbolt/WebInstaller/tests/TestCase/Form/GpgKeyFormTest.php b/plugins/Passbolt/WebInstaller/tests/TestCase/Form/GpgKeyFormTest.php index 48601c688d..1ea6b86532 100644 --- a/plugins/Passbolt/WebInstaller/tests/TestCase/Form/GpgKeyFormTest.php +++ b/plugins/Passbolt/WebInstaller/tests/TestCase/Form/GpgKeyFormTest.php @@ -42,13 +42,13 @@ public function testGpgKeyFormTestFieldPublicKeyArmored() public function testGpgKeyFormTestFieldPrivateKeyArmored() { - $this->markTestSkipped('Test produce test output in CLI on Travis'); $testCases = [ 'requirePresence' => self::getRequirePresenceTestCases(), 'notEmpty' => self::getNotEmptyTestCases(), 'isPrivateKey' => $this->getServerKeyPrivateArmoredTestCases(), 'hasNoExpiry' => $this->getServerKeyPrivateArmoredHasNoExpiryTestCases(), - 'canDecrypt' => $this->getServerKeyPrivateArmoredCanDecryptTestCases(), + // Keys with passphrases are not supported yet + // 'canDecrypt' => $this->getServerKeyPrivateArmoredCanDecryptTestCases(), ]; $this->assertFormFieldFormatValidation(GpgKeyForm::class, 'private_key_armored', $this->getDummyGpgkey(), $testCases); } @@ -326,7 +326,7 @@ protected function getServerKeyPublicArmoredHasNoExpiryTestCases() } /** - * Test cases for server private ket armored validation rule. + * Test cases for server private key armored validation rule. * * @return array */ @@ -443,7 +443,6 @@ protected function getServerKeyPrivateArmoredHasNoExpiryTestCases() LWN2IVKPj2mia4lQHLub9OTlGkkO+pcgU1wQ =hgWr -----END PGP PRIVATE KEY BLOCK-----' => false, - $this->getDummyGpgkey()['private_key_armored'] => true, ], ]; } @@ -582,7 +581,6 @@ protected function getServerKeyPrivateArmoredCanDecryptTestCases() Wg== =Z91U -----END PGP PRIVATE KEY BLOCK-----' => false, - $this->getDummyGpgkey()['private_key_armored'] => true, ], ]; } diff --git a/plugins/Passbolt/WebInstaller/tests/TestCase/Utility/WebInstallerTest.php b/plugins/Passbolt/WebInstaller/tests/TestCase/Utility/WebInstallerTest.php index 3116a6d269..9e3d72f3b1 100644 --- a/plugins/Passbolt/WebInstaller/tests/TestCase/Utility/WebInstallerTest.php +++ b/plugins/Passbolt/WebInstaller/tests/TestCase/Utility/WebInstallerTest.php @@ -157,6 +157,7 @@ public function testWebInstallerUtilityInstallDatabaseSuccessAndCreateFirstUserS $webInstaller->setSettings('first_user', $userSettings); $webInstaller->createFirstUser(); + /** @var \App\Model\Entity\User $user */ $user = $Users->find() ->where(['username' => 'aurore@passbolt.com']) ->contain(['Profiles', 'AuthenticationTokens']) diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 0000000000..a7866847fd --- /dev/null +++ b/psalm.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + diff --git a/resources/locales/de_DE/default.po b/resources/locales/de_DE/default.po new file mode 100644 index 0000000000..c9e3c1483c --- /dev/null +++ b/resources/locales/de_DE/default.po @@ -0,0 +1,2508 @@ +msgid "" +msgstr "" +"Project-Id-Version: 41c2572bd9bd4cc908d3e09e0cbed6e5\n" +"POT-Creation-Date: 2021-10-06 14:32+0000\n" +"PO-Revision-Date: 2021-10-13 14:14\n" +"Last-Translator: NAME \n" +"Language-Team: German\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Crowdin-Project: 41c2572bd9bd4cc908d3e09e0cbed6e5\n" +"X-Crowdin-Project-ID: 2\n" +"X-Crowdin-Language: de\n" +"X-Crowdin-File: /[passbolt.passbolt-ce-api] release/resources/locales/en_UK/default.po\n" +"X-Crowdin-File-ID: 212\n" +"Language: de_DE\n" + +msgid "You need to login to access this location." +msgstr "Sie müssen sich anmelden, um auf diesen Standort zuzugreifen." + +msgid "Decryption failed." +msgstr "Entschlüsselung fehlgeschlagen." + +msgid "Failed to create token." +msgstr "Fehler beim Erstellen des Tokens." + +msgid "The user token result should be a valid UUID." +msgstr "Der Benutzer-Token sollte eine gültige UUID sein." + +msgid "The user token result could not be found " +msgstr "Der Benutzer-Token konnte nicht gefunden werden " + +msgid "The OpenPGP server key defined in the config cannot be used to decrypt." +msgstr "Der in der Konfiguration definierte OpenPGP-Server-Schlüssel kann nicht zum Entschlüsseln verwendet werden." + +msgid "Could not import the user OpenPGP key." +msgstr "Der Benutzer OpenPGP-Schlüssel konnte nicht importiert werden." + +msgid "Invalid verify token format, " +msgstr "Ungültiges Bestätigungs-Token-Format, " + +msgid "sections are missing or using wrong delimiters." +msgstr "Abschnitte fehlen oder falsche Trennzeichen." + +msgid "the version numbers do not match." +msgstr "die Versionsnummern stimmen nicht überein." + +msgid "wrong version number." +msgstr "falsche Versionsnummer." + +msgid "it is not a UUID." +msgstr "es ist keine UUID." + +msgid "wrong token data length." +msgstr "falsche Token-Datenlänge." + +msgid "There is no user associated with this key." +msgstr "Es ist kein*e Benutzer*in mit diesem Schlüssel verknüpft." + +msgid "Page not found." +msgstr "Seite nicht gefunden." + +msgid "This is not a valid Ajax/Json request." +msgstr "Dies ist keine gültige Ajax/Json-Anfrage." + +msgid "You are successfully logged in." +msgstr "Sie sind erfolgreich angemeldet." + +msgid "The OpenPGP public key information was not found in config." +msgstr "Die Informationen zum öffentlichen OpenPGP Schlüssel wurden nicht in der Konfiguration gefunden." + +msgid "The operation was successful." +msgstr "Der Vorgang war erfolgreich." + +msgid "The resource identifier should be a valid UUID." +msgstr "Die Ressourcen-Kennung sollte eine gültige UUID sein." + +msgid "Could not save the comment, please try again later." +msgstr "Kommentar konnte nicht gespeichert werden, bitte versuchen Sie es später erneut." + +msgid "The comment was successfully added." +msgstr "Der Kommentar wurde erfolgreich hinzugefügt." + +msgid "The resource does not exist." +msgstr "Die Ressource existiert nicht." + +msgid "Could not validate comment data." +msgstr "Konnte die Kommentardaten nicht validieren." + +msgid "The comment id is not valid." +msgstr "Die Kommentar-Id ist ungültig." + +msgid "The comment does not exist." +msgstr "Der Kommentar existiert nicht." + +msgid "The comment was deleted." +msgstr "Der Kommentar wurde gelöscht." + +msgid "Could not delete comment." +msgstr "Konnte Kommentar nicht löschen." + +msgid "The comment was successfully updated." +msgstr "Der Kommentar wurde erfolgreich aktualisiert." + +msgid "You are not allowed to edit this comment." +msgstr "Sie sind nicht berechtigt, diesen Kommentar zu bearbeiten." + +msgid "Could not find comments for the requested model." +msgstr "Konnte keine Kommentare für das angeforderte Modell finden." + +msgid "Invalid order. \"{0}\" is not in the list of allowed order." +msgstr "Ungültiger Befehl. \"{0}\" ist nicht in der Liste der zulässigen Befehle." + +msgid "Invalid order. \"{0}\" is not a valid order." +msgstr "Ungültiger Befehl. \"{0}\" ist kein gültiger Befehl." + +msgid "Invalid order. \"{0}\" is not a valid field." +msgstr "Ungültiger Befehl. \"{0}\" ist kein gültiges Feld." + +msgid "Invalid pagination." +msgstr "Ungültige Seitenangabe." + +msgid "Invalid query string. The filter parameter should be an array." +msgstr "Ungültiger Abfrage-String. Der Filter-Parameter sollte ein Array sein." + +msgid "Invalid query string. The contain parameter should be an array." +msgstr "Ungültiger Abfrage-String. Der Beinhaltet-Parameter sollte ein Array sein." + +msgid "Invalid filter." +msgstr "Ungültiger Filter." + +msgid "Invalid order." +msgstr "Ungültiger Befehl." + +msgid "Invalid contain." +msgstr "Ungültiger Contain." + +msgid "No validation rule for filter {0}. Please create one." +msgstr "Keine Validierungsregel für Filter {0}. Bitte erstellen Sie eine." + +msgid "Filter {0} is not valid." +msgstr "Filter {0} ist ungültig." + +msgid "\"{0}\" is not a valid value for filter {1}." +msgstr "\"{0}\" ist kein gültiger Wert für Filter {1}." + +msgid "\"{0}\" is not a valid search filter." +msgstr "\"{0}\" ist kein gültiger Suchfilter." + +msgid "\"{0}\" is not a valid search filter. It is not a UTF8 string." +msgstr "\"{0}\" ist kein gültiger Suchfilter. Es ist kein UTF8-String." + +msgid "It should be between 1 and 64 char in length." +msgstr "Es sollte zwischen 1 und 64 Zeichen lang sein." + +msgid "\"{0}\" is not a valid user filter." +msgstr "\"{0}\" ist kein gültiger Benutzerfilter." + +msgid "\"{0}\" is not a valid user id for filter {1}." +msgstr "\"{0}\" ist keine gültige Benutzer*innen-ID für Filter {1}." + +msgid "\"{0}\" is not a valid group filter." +msgstr "\"{0}\" ist kein gültiger Gruppenfilter." + +msgid "\"{0}\" is not a valid resource id for filter {1}." +msgstr "\"{0}\" ist keine gültige Ressourcen-ID für Filter {1}." + +msgid "\"{0}\" is not a valid group id for filter {1}." +msgstr "\"{0}\" ist keine gültige Gruppen-ID für Filter {1}." + +msgid "\"{0}\" is not a valid timestamp for filter {1}." +msgstr "\"{0}\" ist kein gültiger Zeitstempel für Filter {1}." + +msgid "\"{0}\" is not a valid datetime for filter {1}." +msgstr "\"{0}\" ist keine gültige Datumszeit-Angabe für Filter {1}." + +msgid "\"{0}\" is not a valid order." +msgstr "\"{0}\" ist kein gültiger Befehl." + +msgid "\"{0}\" is not in the list of allowed order." +msgstr "\"{0}\" ist nicht in der Liste der zulässigen Befehle." + +msgid "\"{0}\" is not a valid contain value." +msgstr "\"{0}\" ist kein gültiger Contain-Wert." + +msgid "undefined user agent" +msgstr "undefinierter Benutzer-Agent" + +msgid "Access restricted to administrators." +msgstr "Zugriff beschränkt auf Administratoren." + +msgid "This feature is not supported by your version of passbolt." +msgstr "Diese Funktion wird von Ihrer Passbolt-Version nicht unterstützt." + +msgid "The resource was marked as favorite." +msgstr "Die Ressource wurde als Favorit markiert." + +msgid "This record is already marked as favorite." +msgstr "Dieser Eintrag ist bereits als Favorit markiert." + +msgid "Could not validate favorite data." +msgstr "Konnte Favoritendaten nicht validieren." + +msgid "The favorite id is not valid." +msgstr "Die Favoriten-ID ist ungültig." + +msgid "The favorite does not exist." +msgstr "Der Favorit existiert nicht." + +msgid "The favorite was deleted." +msgstr "Der Favorit wurde gelöscht." + +msgid "Could not delete favorite." +msgstr "Favorit konnte nicht gelöscht werden." + +msgid "The OpenPGP key identifier should be a valid UUID." +msgstr "Der OpenPGP-Schlüssel-Identifikator sollte eine gültige UUID sein." + +msgid "The OpenPGP key does not exist." +msgstr "Der OpenPGP-Schlüssel existiert nicht." + +msgid "The group has been added successfully." +msgstr "Die Gruppe wurde erfolgreich hinzugefügt." + +msgid "The group can be deleted." +msgstr "Die Gruppe kann gelöscht werden." + +msgid "The group was deleted successfully." +msgstr "Die Gruppe wurde erfolgreich gelöscht." + +msgid "You are not authorized to access that location." +msgstr "Sie sind nicht berechtigt, auf diese Seite zuzugreifen." + +msgid "The group identifier should be a valid UUID." +msgstr "Die Gruppen Identifikator sollte eine gültige UUID sein." + +msgid "The group does not exist or has been already deleted." +msgstr "Die Gruppe existiert nicht oder wurde bereits gelöscht." + +msgid "The group cannot be deleted." +msgstr "Die Gruppe kann nicht gelöscht werden." + +msgid "The permissions identifiers must be valid UUID." +msgstr "Die Berechtigungs-Identifikatoren müssen eine gültige UUID sein." + +msgid "The group id is not valid." +msgstr "Die Gruppen-ID ist ungültig." + +msgid "The group does not exist." +msgstr "Die Gruppe existiert nicht." + +msgid "All checks ran successfully!" +msgstr "Alle Prüfungen wurden erfolgreich ausgeführt!" + +msgid "OK" +msgstr "OK" + +msgid "The identifier should be a valid UUID." +msgstr "Der Identifier sollte eine gültige UUID sein." + +msgid "The resource type does not exist." +msgstr "Der Ressourcentyp existiert nicht." + +msgid "The resource has been added successfully." +msgstr "Die Ressource wurde erfolgreich hinzugefügt." + +msgid "The resource has been deleted successfully." +msgstr "Die Ressource wurde erfolgreich gelöscht." + +msgid "You do not have the permission to delete this resource." +msgstr "Sie haben nicht die Berechtigung diese Ressource zu löschen." + +msgid "Could not delete the resource." +msgstr "Konnte die Ressource nicht löschen." + +msgid "The resource has been updated successfully." +msgstr "Die Ressource wurde erfolgreich aktualisiert." + +msgid "The secret does not exist." +msgstr "Das Geheimnis existiert nicht." + +msgid "The key provided does not belong to given user." +msgstr "Der eingegebene Schlüssel gehört nicht zum angegebenen Benutzer." + +msgid "The recovery was completed successfully." +msgstr "Die Wiederherstellung wurde erfolgreich abgeschlossen." + +msgid "The user identifier should be a valid UUID." +msgstr "Der Benutzer-Identifikator sollte eine gültige UUID sein." + +msgid "The user does not exist, has not completed the setup or was deleted." +msgstr "Der Benutzer existiert nicht, hat das Setup nicht abgeschlossen oder wurde gelöscht." + +msgid "The user does not exist or is not active." +msgstr "Benutzer existiert nicht oder ist nicht aktiv." + +msgid "The authentication token is not valid." +msgstr "Das Authentifizierungs-Token ist ungültig." + +msgid "The token is expired." +msgstr "Der Token ist abgelaufen." + +msgid "The OpenPGP key data is not valid." +msgstr "Die OpenPGP-Schlüsseldaten sind ungültig." + +msgid "The setup was completed successfully." +msgstr "Die Einrichtung wurde erfolgreich abgeschlossen." + +msgid "An authentication token should be provided." +msgstr "Es sollte ein Authentifizierungstoken angegeben werden." + +msgid "The authentication token should be a valid UUID." +msgstr "Der Authentifizierungs-Token sollte eine gültige UUID sein." + +msgid "The authentication token is not valid or has expired." +msgstr "Das Authentifizierungs-Token ist ungültig oder ist abgelaufen." + +msgid "The user does not exist, is already active or has been deleted." +msgstr "Benutzer existiert nicht, ist bereits aktiv oder wurde gelöscht." + +msgid "An OpenPGP key must be provided." +msgstr "Es muss ein OpenPGP Schlüssel angegeben werden." + +msgid "A valid OpenPGP key must be provided." +msgstr "Es muss ein gültiger OpenPGP Schlüssel angegeben werden." + +msgid "The token should be a valid UUID." +msgstr "Der Token sollte eine gültige UUID sein." + +msgid "The user does not exist or is active." +msgstr "Benutzer existiert nicht oder ist aktiv." + +msgid "You are not authorized to share this resource." +msgstr "Sie sind nicht berechtigt, diese Ressource zu teilen." + +msgid "Only administrators can add new users." +msgstr "Nur Administratoren dürfen neue Benutzer hinzufügen." + +msgid "The user was successfully added. This user now need to complete the setup." +msgstr "Benutzer wurde erfolgreich hinzugefügt. Dieser Benutzer muss nun die Einrichtung abschließen." + +msgid "The user can be deleted." +msgstr "Der Benutzer kann gelöscht werden." + +msgid "The user has been deleted successfully." +msgstr "Der Benutzer wurde erfolgreich gelöscht." + +msgid "You are not allowed to delete yourself." +msgstr "Sie sind nicht berechtigt, sich selbst zu löschen." + +msgid "The user does not exist or has been already deleted." +msgstr "Der Benutzer existiert nicht oder wurde bereits gelöscht." + +msgid "The user cannot be deleted." +msgstr "Benutzer kann nicht gelöscht werden." + +msgid "The groups users identifiers must be valid UUID." +msgstr "Der Gruppen-Benutzer-Identifikator muss eine gültige UUID sein." + +msgid "The user does not exist or has been deleted." +msgstr "Benutzer existiert nicht oder wurde gelöscht." + +msgid "Could not validate user data." +msgstr "Konnte Benutzer-Daten nicht validieren." + +msgid "Could not find the user data after save. Maybe it has been deleted in the meantime." +msgstr "Die Benutzer-Daten konnten nach dem Speichern nicht gefunden werden. Möglicherweise wurden sie in der Zwischenzeit gelöscht." + +msgid "The user has been updated successfully." +msgstr "Benutzer wurde erfolgreich aktualisiert." + +msgid "Some user data should be provided." +msgstr "Irgendwelche Benutzer-Daten müssen angegeben werden." + +msgid "Updating the OpenPGP key is not allowed." +msgstr "Es ist nicht erlaubt den OpenPGG-Schlüssel zu bearbeiten." + +msgid "Updating the groups is not allowed." +msgstr "Es ist nicht erlaubt die Gruppe zu bearbeiten." + +msgid "You are not authorized to edit the role." +msgstr "Sie sind nicht berechtigt, die Rolle zu bearbeiten." + +msgid "Only guest are allowed to recover an account. Please logout first." +msgstr "Nur Gäste können ein Konto wiederherstellen. Bitte melden Sie sich zuerst ab." + +msgid "Recovery process started, check your email." +msgstr "Wiederherstellungsprozess gestartet, überprüfen Sie Ihre E-Mail." + +msgid "Please provide a valid email address." +msgstr "Bitte geben Sie eine gültige E-Mail-Adresse ein." + +msgid "This user does not exist or has been deleted." +msgstr "Benutzer existiert nicht oder wurde gelöscht." + +msgid "Please register and complete the setup first." +msgstr "Bitte registrieren Sie sich und schließen Sie das Setup zuerst ab." + +msgid "Please contact your administrator." +msgstr "Bitte kontaktieren Sie Ihren Administrator." + +msgid "Registration is not opened to public. Please contact your administrator." +msgstr "Die Registrierung ist nicht für die Öffentlichkeit zugänglich. Bitte wenden Sie sich an Ihre Administrator." + +msgid "Only guest are allowed to register." +msgstr "Nur Gäste dürfen sich registrieren." + +msgid "The user identifier should be a valid UUID or \"me\"." +msgstr "Der Benutzer-Identifikator sollte eine gültige UUID oder \"me\" sein." + +msgid "The user does not exist." +msgstr "Benutzer existiert nicht." + +msgid "The identifier should not be empty." +msgstr "Der Identifikator sollte nicht leer sein." + +msgid "A token is required." +msgstr "Ein Token ist erforderlich." + +msgid "The token should not be empty." +msgstr "Der Token sollte nicht leer sein." + +msgid "The type should be one of the following: {0}." +msgstr "Der Typ sollte einer der folgenden Werte sein: {0}." + +msgid "A type is required." +msgstr "Ein Typ ist erforderlich." + +msgid "The type should not be empty." +msgstr "Der Typ soll nicht leer sein." + +msgid "A user identifier is required." +msgstr "Ein Benutzer-Identifikator ist erforderlich." + +msgid "The user identifier should not be empty." +msgstr "Der Benutzer-Identifikator sollte nicht leer sein." + +msgid "The active status should be a valid boolean." +msgstr "Der Aktiv-Status sollte ein Boolescher-Wert sein." + +msgid "An active status is required" +msgstr "Ein Aktiv-Status ist erforderlich" + +msgid "It is not possible to create an authentication token for this user." +msgstr "Es ist nicht möglich, ein Authentifizierungs-Token für diesen Benutzer zu erstellen." + +msgid "A file is required." +msgstr "Eine Datei ist erforderlich." + +msgid "The file should not be empty" +msgstr "Die Datei darf nicht leer sein" + +msgid "The file mime type should be one of the following: {0}." +msgstr "Der Datei-Mime-Typ sollte einer der folgenden Werte sein: {0}." + +msgid "The file extension should be one of the following: {0}." +msgstr "Die Dateiendung sollte einer der folgenden Werte sein: {0}." + +msgid "The file is not valid, or exceeds max size of {0} bytes." +msgstr "Die Datei ist ungültig oder überschreitet die maximale Größe von {0} Byte." + +msgid "Could not save the data in {0} format." +msgstr "Konnte die Daten nicht im Format {0} speichern." + +msgid "The parent comment identifier should be a valid UUID." +msgstr "Der übergeordnete Kommentar-Bezeichner sollte eine gültige UID sein." + +msgid "The commented object type should be one of the following: {0}." +msgstr "Der kommentierte Objekttyp sollte einer der folgenden Werte sein: {0}." + +msgid "The commented object type is required." +msgstr "Der kommentierte Objekttyp ist erforderlich." + +msgid "The commented object type should not be empty." +msgstr "Der kommentierte Objekttyp darf nicht leer sein." + +msgid "The commented object identifier should be a valid UUID." +msgstr "Der kommentierte Objekt-Identifikator muss eine gültige UUID sein." + +msgid "The commented object identifier is required." +msgstr "Der kommentierte Objekt-Identifikator ist erforderlich." + +msgid "The commented object identifier should not be empty." +msgstr "Der kommentierte Objekt-Identifikator darf nicht leer sein." + +msgid "The content should be a valid UTF8 string." +msgstr "Der Inhalt sollte eine gültige UTF8 Zeichenkette sein." + +msgid "A content is required" +msgstr "Ein Inhalt ist erforderlich" + +msgid "The content should not be empty" +msgstr "Der Inhalt darf nicht leer sein" + +msgid "The content length should be between {0} and {1} characters." +msgstr "Der Inhalt sollte zwischen {0} und {1} Zeichen lang sein." + +msgid "The identifier of the user who created the comment should be a valid UUID." +msgstr "Der Identifikator des Benutzers, welcher die Gruppe erstellt hat, muss eine gültige UUID sein." + +msgid "The identifier of the user who created the comment is required." +msgstr "Der Identifikator des Benutzers, der den Kommentar erstellt hat, muss angegeben werden." + +msgid "The identifier of the user who created the comment should not be empty." +msgstr "Der Identifikator des Benutzers, der den Kommentar erstellt hat, sollte nicht leer sein." + +msgid "The identifier of the user who modified the comment should be a valid UUID." +msgstr "Der Identifikator des Benutzers, der den Kommentar geändert hat, sollte eine gültige UUID sein." + +msgid "The identifier of the user who modified the comment required." +msgstr "Der Identifikator des Benutzers, der den Kommentar bearbeitet hat, muss angegeben werden." + +msgid "The identifier of the user who modified the comment should not be empty." +msgstr "Der Identifikator des Benutzers, der den Kommentar bearbeitet hat, sollte nicht leer sein." + +msgid "Access denied." +msgstr "Zugriff verweigert." + +msgid "The parent comment does not exist." +msgstr "Der übergeordnete Kommentar existiert nicht." + +msgid "The user cannot update this comment." +msgstr "Der Benutzer kann diesen Kommentar nicht aktualisieren." + +msgid "The user cannot delete this comment." +msgstr "Der Benutzer kann diesen Kommentar nicht löschen." + +msgid "The commented object type does not exist." +msgstr "Der kommentierte Objekttyp existiert nicht." + +msgid "The favorite object type should be one of the following: {0}." +msgstr "Der bevorzugte Objekttyp sollte einer der folgenden sein: {0}." + +msgid "The favorite object type is required." +msgstr "Der bevorzugte Objekttyp ist erforderlich." + +msgid "The favorite object type should not be empty" +msgstr "Der bevorzugte Objekttyp sollte nicht leer sein" + +msgid "The favorite object identifier should be a valid UUID." +msgstr "Der bevorzugte Objekt Identifikator sollte eine gültige UUID sein." + +msgid "The favorite object identifier is required." +msgstr "Der bevorzugte Objekt Identifikator wird benötigt." + +msgid "The favorite object identifier should not be empty." +msgstr "Der bevorzugte Objekt Identifikator darf nicht leer sein." + +msgid "The resource is already marked as favorite." +msgstr "Die Ressource ist bereits als Favorit markiert." + +msgid "The user cannot delete this favorite." +msgstr "Der Benutzer kann diesen Favoriten nicht löschen." + +msgid "The armored key should be a valid ASCII string." +msgstr "Der gepanzerte Schlüssel muss eine gültige ASCII-Zeichenkette sein." + +msgid "An armored key is required." +msgstr "Ein gepanzerter Schlüssel ist erforderlich." + +msgid "The armored key should not be empty." +msgstr "Der gepanzerte Schlüssel darf nicht leer sein." + +msgid "The armored key should be a valid ASCII-armored OpenPGP key." +msgstr "Der gepanzerte Schlüssel muss ein gültiger ASCII-gepanzerter OpenPGP-Schlüssel sein." + +msgid "A length is required." +msgstr "Eine Länge ist erforderlich." + +msgid "The identifier should be a valid BMP-UTF8 string." +msgstr "Der Identifikator sollte eine gültige BMP-UTF8-Zeichenkette sein." + +msgid "The key identifier should be a valid ASCII string." +msgstr "Der Schlüssel Identifikator muss eine gültige ASCII-Zeichenkette sein." + +msgid "A key identifier is required." +msgstr "Ein Schlüssel Identifikator ist erforderlich." + +msgid "The key identifier should not be empty." +msgstr "Der Schlüssel Identifikator darf nicht leer sein." + +msgid "The key identifier should be a string of 8 hexadecimal characters." +msgstr "Der Schlüssel Identifikator muss eine Zeichenkette aus 8 Hexadezimalzeichen sein." + +msgid "The fingerprint should be a valid ASCII string." +msgstr "Der Fingerabdruck muss eine gültige ASCII-Zeichenkette sein." + +msgid "A fingerprint is required" +msgstr "Ein Fingerabdruck ist erforderlich" + +msgid "The fingerprint should not be empty" +msgstr "Der Fingerabdruck darf nicht leer sein" + +msgid "The fingerprint should be a string of 40 hexadecimal characters." +msgstr "Der Fingerabdruck muss eine Zeichenkette mit 40 Hexadezimalzeichen sein." + +msgid "The type should be a valid ASCII string." +msgstr "Der Typ muss eine gültige ASCII-Zeichenkette sein." + +msgid "A type is required" +msgstr "Ein Typ ist erforderlich" + +msgid "The type should not be empty" +msgstr "Der Typ darf nicht leer sein" + +msgid "The type should be one of the following: RSA, DSA, ECC, ELGAMAL, ECDSA, DH." +msgstr "Der Typ muss einer der folgenden sein: RSA, DSA, ECC, ELGAMAL, ECDSA, DH." + +msgid "The expiry should be a valid date." +msgstr "Das Ablaufdatum muss ein gültiges Datum sein." + +msgid "The key should not already be expired." +msgstr "Der Schlüssel darf nicht abgelaufen sein." + +msgid "The creation date should be a valid date." +msgstr "Das Erstellungsdatum muss ein gültiges Datum sein." + +msgid "A creation date is required." +msgstr "Erstellungsdatum ist erforderlich." + +msgid "The creation date should not be empty." +msgstr "Das Erstellungsdatum darf nicht leer sein." + +msgid "The creation date should be set in the past." +msgstr "Das Erstellungsdatum muss in der Vergangenheit liegen." + +msgid "The deleted status should be a valid boolean." +msgstr "Der \"Gelöscht\"-Status muss ein boolescher Wert sein." + +msgid "A deleted status is required" +msgstr "Ein Gelöscht-Status ist erforderlich" + +msgid "A user identifier is required" +msgstr "Ein Benutzer-Identifikator ist erforderlich" + +msgid "The user identifier should not be empty" +msgstr "Der Benutzer Identifikator darf nicht leer sein" + +msgid "Could not parse the key info." +msgstr "Konnte die Schlüsselinformation nicht parsen." + +msgid "The name should be a valid UTF8 string." +msgstr "Der Name muss eine gültige UTF8-Zeichenkette sein." + +msgid "The name length should be maximum {0} characters." +msgstr "Der Name darf maximal {0} Zeichen lang sein." + +msgid "A name is required." +msgstr "Ein Name ist erforderlich." + +msgid "The name should not be empty." +msgstr "Der Name darf nicht leer sein." + +msgid "The identifier of the user who created the group should be a valid UUID." +msgstr "Der Identifikator der Person, welche die Gruppe erstellt hat, muss eine gültige UUID sein." + +msgid "The identifier of the user who created the group is required." +msgstr "Der Identifikator des Benutzers, welcher die Gruppe erstellt hat, muss angegeben werden." + +msgid "The identifier of the user who created the group should not be empty." +msgstr "Der Identifikator des Benutzers, welcher die Gruppe angelegt hat, darf nicht leer sein." + +msgid "The identifier of the user who modified the group should be a valid UUID." +msgstr "Der Identifikator des Benutzers, der die Gruppe geändert hat, sollte eine gültige UUID sein." + +msgid "The identifier of the user who modified the group is required." +msgstr "Der Identifikator der Person, welche die Gruppe bearbeitet hat, muss angegeben werden." + +msgid "The identifier of the user who modified the group should not be empty." +msgstr "Der Bezeichner des Benutzers, der die Gruppe geändert hat, darf nicht leer sein." + +msgid "The name is already used by another group." +msgstr "Der Name wird bereits von einer anderen Gruppe verwendet." + +msgid "A group manager should be provided." +msgstr "Ein Gruppenmanager muss angegeben werden." + +msgid "The group should not be soft deleted." +msgstr "Die Gruppe darf nicht weich gelöscht werden." + +msgid "The group should not be sole owner of shared content, transfer the ownership to other users." +msgstr "Die Gruppe darf nicht alleiniger Eigentümer gemeinsamer Inhalte sein. Übertragen Sie das Eigentum auf andere Benutzer." + +msgid "Could not validate group data." +msgstr "Konnte die Gruppendaten nicht validieren." + +msgid "Could not delete the group {0}, please try again later." +msgstr "Die Gruppe {0} konnte nicht gelöscht werden, bitte versuchen Sie es später erneut." + +msgid "A group identifier is required." +msgstr "Ein Gruppen-Identifikator ist erforderlich." + +msgid "The group identifier should not be empty." +msgstr "Der Gruppen-Identifikator darf nicht leer sein." + +msgid "The group manager status should be a valid boolean." +msgstr "Der Gruppen-Manager-Status muss ein gültiger Boolescher-Wert sein." + +msgid "A group manager status is required." +msgstr "Ein Gruppen-Manager-Status muss angegeben werden." + +msgid "The group manager status should not be empty." +msgstr "Der Gruppen-Manager-Status darf nicht leer sein." + +msgid "The user is already member of this group." +msgstr "Der Benutzer ist bereits Mitglied dieser Gruppe." + +msgid "The property length should be maximum {0} characters." +msgstr "Die Eigenschaftslänge sollte maximal {0} Zeichen lang sein." + +msgid "A property is required." +msgstr "Eine Eigenschaft wird benötigt." + +msgid "The property should not be empty." +msgstr "Die Eigenschaft darf nicht leer sein." + +msgid "The property should be an alphabetical string and only point is accepted as special characters." +msgstr "Die Eigenschaft muss eine alphabetische Zeichenkette sein, nur Punkt als Sonderzeichen erlaubt." + +msgid "The property identifier should be a valid UUID." +msgstr "Der Eigenschaftsidentifikator muss eine gültige UUID sein." + +msgid "A property identifier is required." +msgstr "Ein Eigenschaftsidentifikator ist erforderlich." + +msgid "The property identifier should not be empty." +msgstr "Der Eigenschaftsidentifikator darf nicht leer sein." + +msgid "The value should be a valid UTF8 string." +msgstr "Der Wert muss eine gültige UTF8-Zeichenkette sein." + +msgid "The value length should be maximum {0} characters." +msgstr "Der Wert darf maximal {0} Zeichen lang sein." + +msgid "A value is required." +msgstr "Ein Wert wird benötigt." + +msgid "The value should not be empty." +msgstr "Der Wert darf nicht leer sein." + +msgid "This property id is already in use." +msgstr "Diese Eigenschafts-ID wird bereits verwendet." + +msgid "Only admin can create or update organization settings." +msgstr "Nur Administratoren können Organisationseinstellungen erstellen oder aktualisieren." + +msgid "This is not a valid setting." +msgstr "Dies ist keine gültige Einstellung." + +msgid "The type of the access control object should be one of the following: {0}." +msgstr "Der Typ des Zugriffssteuerungsobjekts muss einer der folgenden sein: {0}." + +msgid "The type of the access control object is required." +msgstr "Der Typ des Zugriffssteuerungsobjekts ist erforderlich." + +msgid "The type of the access control object should not be empty." +msgstr "Der Typ des Zugriffssteuerungsobjekts sollte nicht leer sein." + +msgid "The identifier of the access control object should be a valid UUID." +msgstr "Der Identifikator des Zugrifssteuerungsobjekts sollte eine gültige UUID sein." + +msgid "The identifier of the access control object is required." +msgstr "Der Identifikator des Zugriffssteuerungsobjekts ist erforderlich." + +msgid "The identifier of the access control object should not be empty." +msgstr "Der Identifikator des Zugriffssteuerungsobjekts darf nicht leer sein." + +msgid "The access request object type should be one of the following: {0}." +msgstr "Der Zugriffsobjektstyp sollte einer der folgenden sein: {0}." + +msgid "The type of the access request object is required." +msgstr "Der Typ des Zugriffsanforderungsobjekts ist erforderlich." + +msgid "The access request object type should not be empty." +msgstr "Der Zugriffsanforderungs-Objekttyp darf nicht leer sein." + +msgid "The identifier of the access request object should be a valid UUID." +msgstr "Der Identifikator der Zugriffsanfrage sollte eine gültige UUID sein." + +msgid "The identifier of the access request object is required." +msgstr "Der Identifikator der Zugriffsanfrage ist erforderlich." + +msgid "The identifier of the access request object should not be empty." +msgstr "Der Identifikator des Zugriffsanforderungsobjekts sollte nicht leer sein." + +msgid "A permission already exists for the given access control object and access request object." +msgstr "Für die angegebene Zugriffssteuerung und das Zugriffsanforderungsobjekt existiert bereits eine Berechtigung." + +msgid "The access control object does not exist." +msgstr "Das Zugriffskontrollobjekt existiert nicht." + +msgid "The access request object does not exist." +msgstr "Das Zugriffsanforderungsobjektst existiert nicht." + +msgid "A first name is required." +msgstr "Ein Vorname ist erforderlich." + +msgid "The first name should not be empty." +msgstr "Der Vorname darf nicht leer sein." + +msgid "The first name should be a valid BMP-UTF8 string." +msgstr "Der Vorname sollte eine gültige BMP-UTF8-Zeichenkette sein." + +msgid "The first name length should be maximum {0} characters." +msgstr "Die Länge des Vornamens sollte maximal {0} Zeichen lang sein." + +msgid "A last name is required." +msgstr "Ein Nachname ist erforderlich." + +msgid "The last name should not be empty." +msgstr "Der Nachname darf nicht leer sein." + +msgid "The last name should be a valid BMP-UTF8 string." +msgstr "Der Nachname sollte eine gültige BMP-UTF8-Zeichenkette sein." + +msgid "The last name length should be maximum {0} characters." +msgstr "Die Länge des Nachnamens sollte maximal {0} Zeichen enthalten." + +msgid "The name should be a valid BMP-UTF8 string." +msgstr "Der Name sollte eine gültige BMP-UTF8-Zeichenkette sein." + +msgid "The slug should be a valid BMP-UTF8 string." +msgstr "Der Slug sollte eine gültige BMP-UTF8-Zeichenkette sein." + +msgid "A slug is required." +msgstr "Ein Slug ist erforderlich." + +msgid "The slug length should be maximum {0} characters." +msgstr "Die Länge des Slug sollte maximal {0} Zeichen enthalten." + +msgid "The slug should not be empty" +msgstr "Der Slug darf nicht leer sein" + +msgid "The description should be a valid BMP-UTF8 string." +msgstr "Die Beschreibung sollte eine gültige BMP-UTF8-Zeichenkette sein." + +msgid "The description length should be maximum {0} characters." +msgstr "Die Beschreibung sollte maximal {0} Zeichen lang sein." + +msgid "The definition should be a valid BMP-UTF8 string." +msgstr "Die Definition sollte eine gültige BMP-UTF8-Zeichenkette sein." + +msgid "A definition is required." +msgstr "Eine Definition ist erforderlich." + +msgid "The definition should not be empty." +msgstr "Die Definition darf nicht leer sein." + +msgid "The message should be valid JSON message." +msgstr "Die Nachricht sollte eine gültige JSON-Nachricht sein." + +msgid "A resource type already exists with this slug." +msgstr "Ein Ressourcentyp existiert bereits mit diesem Slug." + +msgid "A resource type already exists with this definition." +msgstr "Es existiert bereits ein Ressourcentyp mit dieser Definition." + +msgid "The username should be a valid UTF8 string." +msgstr "Der Benutzername sollte eine gültige UTF8-Zeichenkette sein." + +msgid "The username length should be maximum {0} characters." +msgstr "Der Benutzername darf maximal {0} Zeichen lang sein." + +msgid "The uri should be a valid BMP-UTF8 string." +msgstr "Der Uri sollte eine gültige BMP-UTF8-Zeichenkette sein." + +msgid "The uri length should be maximum {0} characters." +msgstr "Die Uri-Länge sollte maximal {0} Zeichen lang sein." + +msgid "The description should be a valid UTF8 string." +msgstr "Die Beschreibung sollte eine gültige BMP-UTF8-Zeichenkette sein." + +msgid "The deleted status should not be empty" +msgstr "Der gelöschte Status darf nicht leer sein" + +msgid "The identifier of the user who created the resource should be a valid UUID." +msgstr "Der Identifikator des Benutzers, der die Ressource erstellt hat, sollte eine gültige UUID sein." + +msgid "The identifier of the user who created the resource is required." +msgstr "Der Identifikator des Benutzers, der die Ressource erstellt hat, ist erforderlich." + +msgid "The identifier of the user who created the resource should not be empty." +msgstr "Der Identifikator des Benutzers, der die Ressource erstellt hat, sollte nicht leer sein." + +msgid "The identifier of the user who last modified the resource should be a valid UUID." +msgstr "Der Identifikator des Benutzers, der die Ressource zuletzt geändert hat, sollte eine gültige UUID sein." + +msgid "The identifier of the user who last modified the resource required." +msgstr "Der Identifikator des Benutzers, der die Ressource zuletzt geändert hat, ist erforderlich." + +msgid "The identifier of the user who last modified the resource should not be empty." +msgstr "Der Identifikator des Benutzers, der die Ressource zuletzt geändert hat, darf nicht leer sein." + +msgid "The resource type identifier should be a valid UUID." +msgstr "Die Ressourcentyp-Identifikator sollte eine gültige UUID sein." + +msgid "A resource type identifier is required." +msgstr "Ein Ressourcentyp-Identifikator ist erforderlich." + +msgid "The permissions are required." +msgstr "Die Berechtigungen sind erforderlich." + +msgid "The permissions should not be empty." +msgstr "Die Berechtigungen dürfen nicht leer sein." + +msgid "The permissions should contain only the permission of the owner." +msgstr "Die Berechtigungen sollten nur die Berechtigung des Eigentümers enthalten." + +msgid "The owner secret is required." +msgstr "Das Geheimnis des Eigentümers ist erforderlich." + +msgid "The secrets should not be empty." +msgstr "Die Geheimnisse dürfen nicht leer sein." + +msgid "The secrets should contain only the secret of the owner." +msgstr "Die Geheimnisse sollten nur das Geheimnis des Eigentümers enthalten." + +msgid "The permissions should contain the owner permission." +msgstr "Die Berechtigungen müssen die Berechtigung des Eigentümers enthalten." + +msgid "The secrets should contain the owner secret." +msgstr "Die Geheimnisse müssen das Geheimnis des Eigentümers enthalten." + +msgid "The secrets should contain the secrets of all the users having access to the resource." +msgstr "Die Geheimnisse müssen die Geheimnisse aller Benutzer enthalten, die Zugriff auf die Ressource haben." + +msgid "The permissions should contain at least the owner permission." +msgstr "Die Berechtigungen müssen mindestens die Berechtigung des Eigentümers enthalten." + +msgid "The resource should not be already soft deleted." +msgstr "Die Ressource darf nicht bereits sanft gelöscht sein." + +msgid "The user cannot delete this resource." +msgstr "Der Benutzer kann diese Ressource nicht löschen." + +msgid "The name should be a valid ASCII string." +msgstr "Der Name muss eine gültige ASCII-Zeichenkette sein." + +msgid "A role already exists for the given name." +msgstr "Für diesen Namen existiert bereits eine Rolle." + +msgid "The role name should be from the list of allowed role names." +msgstr "Der Rollenname muss aus der Liste zulässiger Rollennamen stammen." + +msgid "The resource identifier is required." +msgstr "Der Ressourcenbezeichner ist erforderlich." + +msgid "The resource identifier should not be empty." +msgstr "Der Ressourcenbezeichner darf nicht leer sein." + +msgid "The message should be a valid ASCII string." +msgstr "Die Nachricht muss eine gültige ASCII-Zeichenkette sein." + +msgid "The message should be a valid ASCII-armored gpg message." +msgstr "Die Nachricht muss eine gültige, ASCII-gepanzerte GPG-Nachricht sein." + +msgid "A message is required." +msgstr "Eine Nachricht ist erforderlich." + +msgid "The message should not be empty." +msgstr "Die Nachricht darf nicht leer sein." + +msgid "A secret already exists for the given user and resource." +msgstr "Für den angegebenen Benutzer und die Ressource existiert bereits ein Geheimnis." + +msgid "The user identifier by should be a valid UUID." +msgstr "Die Benutzerkennung muss eine gültige UUID sein." + +msgid "A username is required." +msgstr "Ein Benutzername ist erforderlich." + +msgid "The username should be a valid email address." +msgstr "Der Benutzername muss eine gültige E-Mail-Adresse sein." + +msgid "The role identifier should be a valid UUID." +msgstr "Der Rollenbezeichner muss eine gültige UUID sein." + +msgid "A role identifier is required." +msgstr "Ein Rollenbezeichner ist erforderlich." + +msgid "The profile should not be empty." +msgstr "Das Profil darf nicht leer sein." + +msgid "The username should not be empty." +msgstr "Der Benutzername darf nicht leer sein." + +msgid "The username length should be maximum 255 characters." +msgstr "Der Benutzername darf maximal 255 Zeichen lang sein." + +msgid "The username is already in use." +msgstr "Dieser Benutzername wird bereits verwendet." + +msgid "The role identifier does not exist." +msgstr "Der Rollenbezeichner existiert nicht." + +msgid "The user should not be sole owner of shared content, transfer the ownership to other users." +msgstr "Der Benutzer darf nicht alleiniger Eigentümer geteilter Inhalte sein. Übertragen Sie das Eigentum auf andere Benutzer." + +msgid "The user should not be sole group manager of group(s), transfer the management to other users." +msgstr "Der Benutzer darf nicht alleiniger Gruppenverwalter der Gruppe(n) sein. Übertragen Sie die Verwaltung auf andere Benutzer." + +msgid "Could not delete the user {0}, please try again later." +msgstr "Der Benutzer {0} konnte nicht gelöscht werden, bitte versuchen Sie es später erneut." + +msgid "{0} just activated their account on passbolt" +msgstr "{0} hat gerade ihr*sein Konto bei Passwort aktiviert" + +msgid "{0} commented on {1}" +msgstr "{0} kommentierte {1}" + +msgid "{0} deleted the group {1}" +msgstr "{0} hat die Gruppe {1} gelöscht" + +msgid "{0} updated the group {1}" +msgstr "{0} hat die Gruppe {1} aktualisiert" + +msgid "{0} added you to the group {1}" +msgstr "{0} hat Sie zur Gruppe {1} hinzugefügt" + +msgid "{0} requested you to add members to {1}" +msgstr "{0} hat Sie gebeten, Mitglieder zu {1} hinzuzufügen" + +msgid "{0} removed you from the group {1}" +msgstr "{0} hat Sie aus der Gruppe {1} entfernt" + +msgid "{0} updated your membership in the group {1}" +msgstr "{0} hat Ihre Mitgliedschaft in der Gruppe {1} aktualisiert" + +msgid "Your account recovery, {0}!" +msgstr "Ihre Kontowiederherstellung, {0}!" + +msgid "You added the password {0}" +msgstr "Sie haben das Passwort {0} hinzugefügt" + +msgid "{0} deleted the password {1}" +msgstr "{0} hat das Passwort {1} gelöscht" + +msgid "{0} edited the password {1}" +msgstr "{0} hat das Passwort {1} bearbeitet" + +msgid "{0} shared the password {1}" +msgstr "{0} hat das Passwort {1} geteilt" + +msgid "{0} deleted user {1}" +msgstr "{0} hat Benutzer*in {1} gelöscht" + +msgid "Welcome to passbolt, {0}!" +msgstr "Willkommen bei passbolt, {0}!" + +msgid "{0} updated your memberships in several groups" +msgstr "{0} hat Ihre Mitgliedschaften in mehreren Gruppen aktualisiert" + +msgid "Your membership in several groups changed in passbolt" +msgstr "Ihre Mitgliedschaft in mehreren Gruppen hat sich in Passbolt geändert" + +msgid "{0} deleted several groups" +msgstr "{0} hat mehrere Gruppen gelöscht" + +msgid "Several groups were deleted in passbolt" +msgstr "Mehrere Gruppen wurden in Passbolt gelöscht" + +msgid "{0} has made changes on several resources" +msgstr "{0} hat an mehreren Ressourcen Änderungen vorgenommen" + +msgid "Multiple passwords have been changed in passbolt" +msgstr "Mehrere Passwörter wurden in Passbolt geändert" + +msgid "{0} shared several items with you" +msgstr "{0} hat mehrere Elemente mit Ihnen geteilt" + +msgid "Multiple passwords have been shared with you in passbolt" +msgstr "Mehrere Passwörter wurden mit Ihnen in Passbolt geteilt" + +msgid "The purify subject setting should be a boolean." +msgstr "Die Einstellung Purify-Subject sollte ein Boolean sein." + +msgid "The show comment setting should be a boolean." +msgstr "Die Einstellung \"Kommentar anzeigen\" sollte ein Boolean sein." + +msgid "The show description setting should be a boolean." +msgstr "Die Einstellung \"Beschreibung anzeigen\" sollte ein Boolean sein." + +msgid "The show secret setting should be a boolean." +msgstr "Die Einstellung \"Secret anzeigen\" sollte ein Boolean sein." + +msgid "The show uri setting should be a boolean." +msgstr "Die Einstellung \"URI anzeigen\" sollte ein Boolean sein." + +msgid "The show username setting should be a boolean." +msgstr "Die Einstellung \"Benutzer*in anzeigen\" sollte ein Boolean sein." + +msgid "The send on user setup completed setting should be a boolean." +msgstr "Die Einstellung \"Senden bei erfolgreicher Benutzer*innen-Registrierung\" sollte ein Boolean sein." + +msgid "The send on comment added setting should be a boolean." +msgstr "Die Einstellung \"Senden bei neuem Kommentar\" sollte ein Boolean sein." + +msgid "The send on group deleted setting should be a boolean." +msgstr "Die Einstellung \"Senden beim Löschen einer Gruppe\" sollte ein Boolean sein." + +msgid "The send on group user added setting should be a boolean." +msgstr "Die Einstellung \"Senden beim Hinzufügen einer Person zu einer Gruppe\" sollte ein Boolean sein." + +msgid "The send on group user deleted setting should be a boolean." +msgstr "Die Einstellung \"Senden beim Entfernen einer Person aus einer Gruppe\" sollte ein Boolean sein." + +msgid "The send on group user updated setting should be a boolean." +msgstr "Die Einstellung \"Senden bei Aktualisierung der Gruppen-Benutzer*innen\" sollte ein Boolean sein." + +msgid "The send on group manager updated setting should be a boolean." +msgstr "Die Einstellung \"Senden bei Aktualisierung der Gruppenmanager*in\" sollte ein Boolean sein." + +msgid "The send on password created setting should be a boolean." +msgstr "Die Einstellung \"Senden beim Anlegen eines neuen Passworts\" sollte ein Boolean sein." + +msgid "The send on password shared setting should be a boolean." +msgstr "Die Einstellung \"Senden wenn ein Passwort geteilt wird\" sollte ein Boolean sein." + +msgid "The send on password updated setting should be a boolean." +msgstr "Die Einstellung \"Senden beim Aktualisieren eines Passworts\" sollte ein Boolean sein." + +msgid "The send on password deleted setting should be a boolean." +msgstr "Die Einstellung \"Senden, wenn ein Passwort gelöscht wird\" sollte ein Boolean sein." + +msgid "The send on user created setting should be a boolean." +msgstr "Die Einstellung \"Senden wenn ein*e Benutzer*in angelegt wird\" sollte ein Boolean sein." + +msgid "The send on user recovered setting should be a boolean." +msgstr "Die Einstellung \"Senden, wenn ein*e Benutzer*in wiederhergestellt wird\" sollte ein Boolean sein." + +msgid "Validation failed for authenticationToken {0}. {1}" +msgstr "Validierung fehlgeschlagen für Authentifizierungs-Token {0}. {1}" + +msgid "Validation success for authenticationToken {0}" +msgstr "Validierung erfolgreich für Authentifizierungs-Token {0}" + +msgid "Could not save the avatar in the {0} file." +msgstr "Der Avatar konnte nicht in der Datei {0} gespeichert werden." + +msgid "The avatar id is not valid." +msgstr "Die Avatar-Id ist nicht gültig." + +msgid "Validation failed for comment {0}. {1}" +msgstr "Validierung fehlgeschlagen für Kommentar {0}. {1}" + +msgid "Validation success for comment {0}" +msgstr "Validierung erfolgreich für Kommentar {0}" + +msgid "Validation failed for favorite {0}. {1}" +msgstr "Validierung fehlgeschlagen für Favoriten {0}. {1}" + +msgid "Validation success for favorite {0}" +msgstr "Validierung erfolgreich für Favoriten {0}" + +msgid "Validation success for key {0}" +msgstr "Validierung erfolgreich für Schlüssel {0}" + +msgid "Validation failed for key {0}. {1}" +msgstr "Validierung fehlgeschlagen für Schlüssel {0}. {1}" + +msgid "Encryption success for key {0}" +msgstr "Verschlüsselung erfolgreich für Schlüssel {0}" + +msgid "Failed to encrypt with key {0}. {1}" +msgstr "Fehler beim Verschlüsseln mit Schlüssel {0}. {1}" + +msgid "Validation failed for group {0}. {1}" +msgstr "Validierung fehlgeschlagen für Gruppe {0}. {1}" + +msgid "Validation success for group {0}" +msgstr "Validierung erfolgreich für Gruppe {0}" + +msgid "Could not validate group user data." +msgstr "Konnte die Gruppenbenutzer*innen-Daten nicht validieren." + +msgid "The group user does not exist." +msgstr "Gruppenbenutzer*in existiert nicht." + +msgid "At least one group manager should be provided." +msgstr "Mindestens ein*e Gruppenmanager*in muss angegeben werden." + +msgid "Could not validate permission data." +msgstr "Berechtigungsdaten konnten nicht validiert werden." + +msgid "Validation failed for permission {0}. {1}" +msgstr "Validierung fehlgeschlagen für Berechtigung {0}. {1}" + +msgid "Validation success for permission {0}" +msgstr "Validierung erfolgreich für Berechtigung {0}" + +msgid "Could not validate permissions data." +msgstr "Berechtigungsdaten konnten nicht validiert werden." + +msgid "Validation failed for profile {0}. {1}" +msgstr "Validierung fehlgeschlagen für Profil {0}. {1}" + +msgid "Validation success for profile {0}" +msgstr "Validierung erfolgreich für Profil {0}" + +msgid "Could not validate resource data." +msgstr "Ressourcendaten konnten nicht überprüft werden." + +msgid "An unexpected error occured, please contact an administrator." +msgstr "Ein unerwarteter Fehler ist aufgetreten, bitte kontaktieren Sie einen Administrator." + +msgid "The server returned the following error:" +msgstr "Der Server hat folgenden Fehler zurückgegeben:" + +msgid "Validation failed for resource {0}. {1}" +msgstr "Überprüfung fehlgeschlagen für Ressource {0}. {1}" + +msgid "Validation success for resource {0}" +msgstr "Validierung erfolgreich für Ressource {0}" + +msgid "You are not allowed to update this resource." +msgstr "Sie sind nicht berechtigt diese Ressource zu aktualisieren." + +msgid "Additional resource types are not enabled on this server." +msgstr "Zusätzliche Ressourcentypen sind auf diesem Server nicht aktiviert." + +msgid "There should be at least 4 roles" +msgstr "Es sollte mindestens 4 Rollen geben" + +msgid "Validation failed for role {0}. {1}" +msgstr "Validierung fehlgeschlagen für Rolle {0}. {1}" + +msgid "Validation success for role {0}" +msgstr "Validierung erfolgreich für Rolle {0}" + +msgid "Could not validate secret data." +msgstr "Secret Daten konnten nicht validiert werden." + +msgid "Validation failed for secret {0}. {1}" +msgstr "Validierung fehlgeschlagen für Secret {0}. {1}" + +msgid "Validation success for secret {0}" +msgstr "Validierung erfolgreich für Secret {0}" + +msgid "Could not validate secrets data." +msgstr "Secret Daten konnten nicht validiert werden." + +msgid "The secrets of all the users having access to the resource are required." +msgstr "Es werden die Secrets aller Benutzer*innen, welche Zugriff auf diese Ressource haben, benötigt." + +msgid "Validation failed for user {0}. {1}" +msgstr "Validierung fehlgeschlagen für Benutzer*in {0}. {1}" + +msgid "Validation success for user {0}" +msgstr "Validierung erfolgreich für Benutzer*in {0}" + +msgid "Could not connect to github repository" +msgstr "Verbindung zum Github Repository fehlgeschlagen" + +msgid "Could not read tag information on github repository" +msgstr "Tag-Informationen im GitHub Repository konnten nicht gelesen werden" + +msgid "The key {0} cannot be used to encrypt." +msgstr "Der Schlüssel {0} kann nicht zum Verschlüsseln verwendet werden." + +msgid "The key {0} cannot be used to decrypt." +msgstr "Der Schlüssel {0} kann nicht zum Entschlüsseln verwendet werden." + +msgid "Could not use key {0} for signing." +msgstr "Schlüssel {0} konnte nicht zum Signieren verwendet werden." + +msgid "Could not parse the OpenPGP public key." +msgstr "Konnte den öffentlichen OpenPGP-Schlüssel nicht analysieren." + +msgid "Invalid key. No OpenPGP public key package found." +msgstr "Ungültiger Schlüssel. Kein OpenPGP öffentliches Schlüssel-Paket gefunden." + +msgid "Invalid key. No user ID found." +msgstr "Ungültiger Schlüssel. Keine Benutzer*innen-ID gefunden." + +msgid "Could not import the OpenPGP key." +msgstr "Konnte den OpenPGP-Schlüssel nicht importieren." + +msgid "Could not use the key to sign and encrypt." +msgstr "Der Schlüssel kann nicht zum Signieren und Verschlüsseln verwendet werden." + +msgid "Could not use the key to encrypt." +msgstr "Kann den Schlüssel nicht zum Verschlüsseln verwenden." + +msgid "Expected {0} and got {1}." +msgstr "{0} erwartet und {1} erhalten." + +msgid "Decryption failed. Invalid signature." +msgstr "Entschlüsselung fehlgeschlagen. Ungültige Signatur." + +msgid "The message cannot be verified." +msgstr "Die Nachricht kann nicht verifiziert werden." + +msgid "Could not sign the text. " +msgstr "Text konnte nicht signiert werden. " + +msgid "The OpenPGP server key defined in the config is not found in the file system." +msgstr "Der in der Konfiguration definierte OpenPGP-Server-Schlüssel wird nicht im Dateisystem gefunden." + +msgid "The OpenPGP server key defined in the config cannot be opened." +msgstr "Der in der Konfiguration definierte OpenPGP-Server-Schlüssel kann nicht geöffnet werden." + +msgid "The OpenPGP server key defined on file is not a valid private key." +msgstr "Der im Datei definierte OpenPGP-Server-Schlüssel ist kein gültiger privater Schlüssel." + +msgid "There is an issue with the OpenPGP server key." +msgstr "Es gibt ein Problem mit dem OpenPGP-Serverschlüssel." + +msgid "The fingerprint does not match the one associated with the key on file." +msgstr "Der Fingerabdruck stimmt nicht mit dem in der Datei enthaltenen Schlüssel überein." + +msgid "No OpenPGP marker found." +msgstr "Kein OpenPGP-Marker gefunden." + +msgid "This is not a valid OpenPGP armored marker." +msgstr "Dies ist kein gültiger OpenPGP armored Marker." + +msgid "The key {0} was not found in the keyring" +msgstr "Der Schlüssel {0} wurde nicht im Schlüsselring gefunden" + +msgid "A value for the theme should be provided." +msgstr "Ein Wert für das Theme sollte angegeben werden." + +msgid "This is not a valid theme." +msgstr "Dies ist keine gültige Benutzeroberfläche." + +msgid "The setting type should be one of the following: {0}." +msgstr "Der Einstellungs-Typ sollte einer der folgenden sein: {0}." + +msgid "A setting type is required." +msgstr "Ein Einstellungs-Typ ist erforderlich." + +msgid "The setting type should not be empty" +msgstr "Der Einstellungs-Typ darf nicht leer sein" + +msgid "The setting value should be a valid UTF8 string." +msgstr "Der Wert der Einstellung sollte ein gültiger UTF8-String sein." + +msgid "The setting value length should be maximum {0} characters." +msgstr "Die Einstellung sollte maximal {0} Zeichen lang sein." + +msgid "A value setting is required." +msgstr "Ein Wert ist erforderlich." + +msgid "The setting value should not be empty." +msgstr "Der Wert der Einstellung sollte nicht leer sein." + +msgid "The parameter {0} is not set." +msgstr "Der Parameter {0} ist nicht gesetzt." + +msgid "This theme is not supported." +msgstr "Diese Benutzeroberfläche wird nicht unterstützt." + +msgid "You are not allowed to access this location." +msgstr "Sie sind nicht berechtigt, auf diesen Standort zuzugreifen." + +msgid "The notification settings for the organization were updated." +msgstr "Die Benachrichtigungseinstellungen für die Organisation wurden aktualisiert." + +msgid "The challenge cannot be decrypted." +msgstr "Das Challenge kann nicht entschlüsselt werden." + +msgid "The challenge is invalid. Deserialization failed." +msgstr "Das Challenge ist ungültig. Deserialisierung fehlgeschlagen." + +msgid "The challenge is invalid. Validation Failed." +msgstr "Das Challenge ist ungültig. Validierung fehlgeschlagen." + +msgid "The config for the server private key fingerprint is not available or incomplete." +msgstr "Die Konfiguration für den privaten Schlüssel-Fingerabdruck des Servers ist nicht verfügbar oder unvollständig." + +msgid "The config for the server private key passphrase is invalid." +msgstr "Die Konfiguration für den privaten Schlüssel des Servers ist ungültig." + +msgid "The user id is missing or invalid." +msgstr "Die Benutzer-ID fehlt oder ist ungültig." + +msgid "The user OpenPGP key does not exist, or is invalid, or has been deleted." +msgstr "Der OpenPGP-Schlüssel existiert nicht, ist ungültig oder wurde gelöscht." + +msgid "The user signature is invalid." +msgstr "Die Signatur des Benutzers ist ungültig." + +msgid "The user challenge is missing or invalid." +msgstr "Das Benutzer-Challenge fehlt oder ist ungültig." + +msgid "The version is invalid." +msgstr "Die Version ist ungültig." + +msgid "The domain is invalid." +msgstr "Die Domain ist ungültig." + +msgid "The domain is invalid. Expected: {0} and got {1}" +msgstr "Die Domain ist ungültig. Erwartet: {0} und {1} erhalten" + +msgid "The authentication was a success." +msgstr "Die Authentifizierung war erfolgreich." + +msgid "The authentication failed." +msgstr "Die Authentifizierung ist fehlgeschlagen." + +msgid "The credentials are missing." +msgstr "Die Zugangsdaten fehlen." + +msgid "The user does not exist or is not active or has been deleted." +msgstr "Der Benutzer existiert nicht oder ist nicht aktiv oder wurde gelöscht." + +msgid "The credentials are invalid." +msgstr "Die Zugangsdaten sind ungültig." + +msgid "An internal error occurred." +msgstr "Ein interner Fehler ist aufgetreten." + +msgid "The verify token is invalid." +msgstr "Das Verify-Token ist ungültig." + +msgid "The route {0} is not permitted with JWT authentication." +msgstr "Die Route {0} ist mit JWT-Authentifizierung nicht zulässig." + +msgid "The key pair for JWT Authentication is not complete. The following file could not be read: {0}." +msgstr "Das Schlüsselpaar für JWT-Authentifizierung ist nicht vollständig. Die folgende Datei konnte nicht gelesen werden: {0}." + +msgid "The JWT public key could not be read or is not valid." +msgstr "Der JWT-öffentliche Schlüssel konnte nicht gelesen werden oder ist nicht gültig." + +msgid "The JWT private key should be at least {0} bytes long." +msgstr "Der JWT private Schlüssel sollte mindestens {0} Bytes lang sein." + +msgid "The configuration {0} is not correctly set." +msgstr "Die Konfiguration {0} ist nicht korrekt gesetzt." + +msgid "No active refresh token matching the request could be found." +msgstr "Es wurde kein aktiver Aktualisierungstoken mit der Anfrage gefunden." + +msgid "The refresh token provided was already used." +msgstr "Der Aktualisierungstoken wurde bereits verwendet." + +msgid "Expired refresh token provided." +msgstr "Abgelaufenes Aktualisierungstoken angegeben." + +msgid "The refresh token should be a valid UUID." +msgstr "Das Aktualisierungstoken sollte eine gültige UUID sein." + +msgid "This is not a valid user id." +msgstr "Dies ist keine gültige Benutzer-ID." + +msgid "Invalid verify token expiry." +msgstr "Ungültiges Verify-Token Ablauf." + +msgid "Attempt to access an expired verify token." +msgstr "Versuch, auf ein abgelaufenes Verify-Token zuzugreifen." + +msgid "Invalid verify token format." +msgstr "Ungültiges Verify-Token-Format." + +msgid "Invalid user ID format." +msgstr "Ungültiges User-ID-Format." + +msgid "Verify token has been already used in the past." +msgstr "Das Verify-Token wurde in der Vergangenheit bereits verwendet." + +msgid "Security warning!" +msgstr "Sicherheitswarnung!" + +msgid "An unknown user with IP: {0} attempted to identify as {1}." +msgstr "Ein unbekannter Nutzer mit IP: {0} versuchte sich als {1} zu identifizieren." + +msgid "This is a potential security issue." +msgstr "Dies ist ein potenzieller Sicherheitsproblem." + +msgid "Please investigate!" +msgstr "Bitte untersuchen Sie!" + +msgid "An unknown user with IP: {0} attempted to steal your login data." +msgstr "Ein unbekannter Nutzer mit IP: {0} hat versucht, Ihre Login-Daten zu stehlen." + +msgid "Please get in tough with one of your administrators." +msgstr "Bitte setzen Sie sich mit einem Ihrer Administratoren in Verbindung." + +msgid "This is not a valid locale." +msgstr "Dies ist kein gültiges Locale." + +msgid "This is not a valid {0}." +msgstr "Dies ist kein gültiger {0}." + +msgid "The action log identifier should be a valid UUID." +msgstr "Der Action-Log-Identifikator sollte eine gültige UUID sein." + +msgid "The action log identifier should not be empty." +msgstr "Der Action-Log-Identifikator darf nicht leer sein." + +msgid "The action identifier should be a valid UUID." +msgstr "Der Action-Identifikator sollte eine gültige UUID sein." + +msgid "An action identifier is required." +msgstr "Ein Aktions-Identifikator ist erforderlich." + +msgid "The context should be a valid ASCII string." +msgstr "Der Kontext sollte eine gültige ASCII-Zeichenkette sein." + +msgid "The context length should be maximum {0} characters." +msgstr "Die Kontextlänge sollte maximal {0} Zeichen lang sein." + +msgid "A context is required." +msgstr "Ein Kontext ist erforderlich." + +msgid "The status should be a valid boolean." +msgstr "Der Status sollte ein gültiger Boolescher Wert sein." + +msgid "A status is required." +msgstr "Ein Status ist erforderlich." + +msgid "Could not validate action log data." +msgstr "Konnte die Logdaten der Aktion nicht überprüfen." + +msgid "The identifier should be not be empty." +msgstr "Der Identifikator sollte nicht leer sein." + +msgid "Could not validate action data." +msgstr "Konnte die Aktionsdaten nicht überprüfen." + +msgid "An action log identifier is required." +msgstr "Ein Action-Log-Identifikator ist erforderlich." + +msgid "The object type should be a valid ASCII string." +msgstr "Der Objekttyp sollte eine gültige ASCII-Zeichenkette sein." + +msgid "The object type length should be maximum {0} characters." +msgstr "Die Länge des Objekttyps sollte maximal {0} Zeichen lang sein." + +msgid "A object type is required." +msgstr "Ein Objekttyp ist erforderlich." + +msgid "The object type should not be empty." +msgstr "Der Objekttyp darf nicht leer sein." + +msgid "The object identifier should be a valid UUID." +msgstr "Der Objekt-Identifikator sollte eine gültige UUID sein." + +msgid "The object identifier should not be empty." +msgstr "Der Objekt-Identifikator sollte nicht leer sein." + +msgid "An object identifier is required." +msgstr "Ein Objekt-Identifikator ist erforderlich." + +msgid "The operation type should be one of the following: {0}." +msgstr "Der Operationstyp sollte einer der folgenden sein: {0}." + +msgid "An operation type is required." +msgstr "Ein Operationstyp ist erforderlich." + +msgid "The operation type should not be empty." +msgstr "Der Operationstyp darf nicht leer sein." + +msgid "Could not validate entity history data." +msgstr "Entitäts-Verlauf konnte nicht validiert werden." + +msgid "An identifier is required." +msgstr "Ein Identifikator ist erforderlich." + +msgid "The type must be one of the following: {0}." +msgstr "Der Typ muss einer der folgenden sein: {0}." + +msgid "The type is required." +msgstr "Der Typ ist erforderlich." + +msgid "Could not validate permission history data." +msgstr "Berechtigungsverlauf konnte nicht validiert werden." + +msgid "A resource identifier is required." +msgstr "Ein Ressourcen-Identifikator ist erforderlich." + +msgid "The secret identifier should be a valid UUID." +msgstr "Der geheime Identifikator sollte eine gültige UUID sein." + +msgid "A secret identifier is required." +msgstr "Ein geheimer Identifikator ist erforderlich." + +msgid "The secret identifier should not be empty." +msgstr "Der geheime Identifikator darf nicht leer sein." + +msgid "Could not validate secret access data." +msgstr "Geheime Zugriffsdaten konnten nicht validiert werden." + +msgid "Could not validate secret history data." +msgstr "Secret-Verlaufsdaten konnten nicht validiert werden." + +msgid "Information about the transfer is required." +msgstr "Informationen über die Übertragung sind erforderlich." + +msgid "The operation was successful" +msgstr "Der Vorgang war erfolgreich" + +msgid "The transfer id is not valid." +msgstr "Das Transfer-ID ist nicht gültig." + +msgid "The authentication token should be a valid uuid." +msgstr "Das Authentifizierungs-Token sollte ein gültiges uuid sein." + +msgid "The authentication token is invalid." +msgstr "Das Authentifizierungs-Token ist ungültig." + +msgid "The transfer id should be a uuid." +msgstr "Die Transfer-ID sollte eine Uuid sein." + +msgid "The user id should be a uuid." +msgstr "Die Benutzer-ID sollte eine Uuid sein." + +msgid "A user id is required" +msgstr "Eine Benutzer-Id ist erforderlich" + +msgid "The hash should be an ascii string." +msgstr "Der Hash sollte ein ascii String sein." + +msgid "The hash should be {0} characters in length." +msgstr "Der Hash muss {0} Zeichen lang sein." + +msgid "The data transfer hash is required." +msgstr "Der Datentransfer-Hash wird benötigt." + +msgid "The current page should be a non negative integer." +msgstr "Die aktuelle Seite sollte eine nicht negative Ganzzahl sein." + +msgid "The current page number is required" +msgstr "Die aktuelle Seitennummer ist erforderlich" + +msgid "The current page number should be equal or inferior to the total number of pages." +msgstr "Die aktuelle Seitenzahl sollte der Gesamtzahl der Seiten gleich oder kleiner sein." + +msgid "The current page cannot be greater than {0}" +msgstr "Die aktuelle Seite kann nicht größer als {0} sein" + +msgid "The total number of pages should a non negative integer." +msgstr "Die Gesamtzahl der Seiten sollte eine nicht negative Ganzzahl sein." + +msgid "The total number of pages is required." +msgstr "Die Gesamtzahl der Seiten ist erforderlich." + +msgid "The total number of pages should be greater than 0." +msgstr "Die Gesamtzahl der Seiten sollte größer als 0 sein." + +msgid "The total number of pages cannot be greater than {0}" +msgstr "Die Gesamtzahl der Seiten darf nicht größer als {0} sein" + +msgid "The status should not be empty." +msgstr "Der Status darf nicht leer sein." + +msgid "The status is required." +msgstr "Der Status ist erforderlich." + +msgid "The status must be one of the following: {0}." +msgstr "Der Status muss einer der folgenden sein: {0}." + +msgid "The user must be active." +msgstr "Der Benutzer muss aktiv sein." + +msgid "Could not validate the transfer data." +msgstr "Die Transferdaten konnten nicht validiert werden." + +msgid "The transfer could not be created." +msgstr "Der Transfer konnte nicht erstellt werden." + +msgid "Could not update the transfer data." +msgstr "Transferdaten konnten nicht aktualisiert werden." + +msgid "The transfer could not be updated." +msgstr "Der Transfer konnte nicht aktualisiert werden." + +msgid "This operation is not allowed." +msgstr "Dieser Vorgang ist nicht zulässig." + +msgid "Restarting a transfer is not allowed." +msgstr "Ein Transfer kann nicht neu gestartet werden." + +msgid "The operation is already over." +msgstr "Die Operation ist bereits beendet." + +msgid "The current page does not match the total number of pages." +msgstr "Die aktuelle Seite entspricht nicht der Gesamtzahl der Seiten." + +msgid "This operation is not allowed for this user." +msgstr "Dieser Vorgang ist für diesen Benutzer nicht erlaubt." + +msgid "The authentication token is missing." +msgstr "Der Authentifizierungstoken fehlt." + +msgid "The authentication token is not valid for this user." +msgstr "Der Authentifizierungstoken ist für diesen Benutzer nicht gültig." + +msgid "The authentication token type is invalid." +msgstr "Der Authentifizierungs-Token-Typ ist ungültig." + +msgid "The authentication token is not active." +msgstr "Das Authentifizierungstoken ist nicht aktiv." + +msgid "The authentication token is expired." +msgstr "Das Authentifizierungs-Token ist abgelaufen." + +msgid "The password generator value \"{0}\" is not valid." +msgstr "Der Passwortgeneratorwert \"{0}\" ist ungültig." + +msgid "5 minutes" +msgstr "5 Minuten" + +msgid "15 minutes" +msgstr "15 Minuten" + +msgid "30 minutes" +msgstr "30 Minuten" + +msgid "1 hour" +msgstr "1 Stunde" + +msgid "until I log out" +msgstr "bis ich mich abmelde" + +msgid "Only administrators can view reports." +msgstr "Nur Administratoren können Berichte ansehen." + +msgid "The requested report `{0}` does not exist." +msgstr "Der angefragte Bericht `{0}` existiert nicht." + +msgid "Description" +msgstr "Beschreibung" + +msgid "Passbolt report" +msgstr "Passbolt-Bericht" + +msgid "Report name" +msgstr "Name des Berichts" + +msgid "Generated by" +msgstr "Erzeugt von" + +msgid "Creation date" +msgstr "Erstellungsdatum" + +msgid "All users have completed the setup." +msgstr "Alle Benutzer haben die Einrichtung abgeschlossen." + +msgid "Name" +msgstr "Name" + +msgid "Username" +msgstr "Benutzername" + +msgid "Created since" +msgstr "Erstellt seit" + +msgid "Setup completed" +msgstr "Einrichtung abgeschlossen" + +msgid "Role" +msgstr "Rolle" + +msgid "No" +msgstr "Nein" + +msgid "The data entered are not correct" +msgstr "Die eingegebenen Daten sind nicht korrekt" + +msgid "A connection could not be established with the credentials provided." +msgstr "Es konnte keine Verbindung mit den angegebenen Zugangsdaten hergestellt werden." + +msgid "Please verify the settings." +msgstr "Bitte überprüfen Sie die Einstellungen." + +msgid "passbolt test email" +msgstr "Test-E-Mail von Passbolt" + +msgid "Congratulations!" +msgstr "Glückwunsch!" + +msgid "If you receive this email, it means that your passbolt smtp configuration is working fine." +msgstr "Wenn Sie diese E-Mail erhalten, funktionieren Ihre Passbolt SMTP-Konfigurationen fehlerfrei." + +msgid "The data entered are not correct: {0}" +msgstr "Die eingegebenen Daten sind nicht korrekt: {0}" + +msgid "The session has expired. Please start the configuration again." +msgstr "Die Sitzung ist abgelaufen. Bitte beginnen Sie die Konfiguration erneut." + +msgid "System check" +msgstr "Systemüberprüfung" + +msgid "Subscription key" +msgstr "Abo-Schlüssel" + +msgid "Database" +msgstr "Datenbank" + +msgid "Server keys" +msgstr "Serverschlüssel" + +msgid "Emails" +msgstr "E-Mails" + +msgid "Options" +msgstr "Optionen" + +msgid "First user" +msgstr "Erster Benutzer" + +msgid "Installation" +msgstr "Installation" + +msgid "That's it!" +msgstr "Das war's!" + +msgid "A host name is required." +msgstr "Ein Hostname ist erforderlich." + +msgid "The host name should not be empty." +msgstr "Der Hostname darf nicht leer sein." + +msgid "The host name should be a valid BMP-UTF8 string." +msgstr "Der Hostname sollte ein gültiger BMP-UTF8-String sein." + +msgid "A port number is required." +msgstr "Eine Portnummer ist erforderlich." + +msgid "The port number should be numeric." +msgstr "Die Portnummer muss numerisch sein." + +msgid "The port number should be between {0} and {1}." +msgstr "Die Portnummer muss zwischen {0} und {1} liegen." + +msgid "The username should be a valid BMP-UTF8 string." +msgstr "Der Benutzername sollte ein gültiger BMP-UTF8 String sein." + +msgid "The password should not contain quotes." +msgstr "Das Passwort darf keine Anführungszeichen enthalten." + +msgid "The password should be a valid BMP-UTF8 string." +msgstr "Das Passwort sollte ein gültiger BMP-UTF8 String sein." + +msgid "A database name is required." +msgstr "Ein Datenbankname ist erforderlich." + +msgid "The database name should not be empty." +msgstr "Der Datenbankname darf nicht leer sein." + +msgid "The database name should be a valid BMP-UTF8 string." +msgstr "Der Datenbankname sollte eine gültige BMP-UTF8-Zeichenkette sein." + +msgid "The database name should not contain dashes." +msgstr "Der Datenbankname darf keine Bindestriche enthalten." + +msgid "A sender name is required." +msgstr "Ein Absendername ist erforderlich." + +msgid "The sender name should not be empty." +msgstr "Der Absendername darf nicht leer sein." + +msgid "The sender name should be a valid BMP-UTF8 string." +msgstr "Der Absendername sollte eine gültige BMP-UTF8-Zeichenkette sein." + +msgid "A sender email is required." +msgstr "Eine Absender E-Mail ist erforderlich." + +msgid "The sender email should not be empty." +msgstr "Die Absender E-Mail darf nicht leer sein." + +msgid "The sender email should be a valid BMP-UTF8 string." +msgstr "Die Absender E-Mail sollte eine gültige BMP-UTF8-Zeichenkette sein." + +msgid "The sender email should be a valid email address." +msgstr "Die Absender E-Mail sollte eine gültige E-Mail-Adresse sein." + +msgid "A TLS setting is required." +msgstr "Eine TLS-Einstellung ist erforderlich." + +msgid "The TLS setting should be a valid boolean." +msgstr "Die TLS-Einstellung sollte ein gültiger Boolescher Wert sein." + +msgid "A password is required." +msgstr "Ein Passwort ist erforderlich." + +msgid "The test email should be a valid email address." +msgstr "Die Absender E-Mail sollte eine gültige E-Mail-Adresse sein." + +msgid "An OpenPGP public key is required." +msgstr "Ein öffentlicher OpenPGP Schlüssel ist erforderlich." + +msgid "The OpenPGP public key should not be empty." +msgstr "Der öffentliche OpenPGP-Schlüssel darf nicht leer sein." + +msgid "The OpenPGP public key should be a valid ASCII string." +msgstr "Der öffentliche OpenPGP-Schlüssel sollte eine gültige ASCII-Zeichenkette sein." + +msgid "The key is not a valid OpenPGP public key." +msgstr "Der Schlüssel ist kein gültiger öffentlicher OpenPGP-Schlüssel." + +msgid "The OpenPGP public key should not have an expiry date." +msgstr "Der öffentliche OpenPGP Schlüssel sollte kein Ablaufdatum haben." + +msgid "The OpenPGP public key cannot be used to encrypt." +msgstr "Der öffentliche OpenPGP-Schlüssel kann nicht zum Verschlüsseln verwendet werden." + +msgid "An OpenPGP private key is required." +msgstr "Ein privater OpenPGP Schlüssel ist erforderlich." + +msgid "The OpenPGP private key should not be empty." +msgstr "Der private OpenPGP-Schlüssel darf nicht leer sein." + +msgid "The OpenPGP private key should be a valid ASCII string." +msgstr "Der private OpenPGP Schlüssel sollte eine gültige ASCII Zeichenkette sein." + +msgid "The value is not a valid OpenPGP private key." +msgstr "Der Wert ist kein gültiger privater OpenPGP-Schlüssel." + +msgid "The OpenPGP private key should not have an expiry date." +msgstr "Der private OpenPGP-Schlüssel sollte kein Ablaufdatum haben." + +msgid "The OpenPGP private key cannot be used to decrypt." +msgstr "Der private OpenPGP-Schlüssel kann nicht zum Entschlüsseln verwendet werden." + +msgid "Please note that passbolt does not support OpenPGP key protected with a secret." +msgstr "Bitte beachten Sie, dass Passbolt keinen OpenPGP-Schlüssel unterstützt, der durch ein Geheimnis geschützt ist." + +msgid "A fingerprint is required." +msgstr "Ein Fingerabdruck ist erforderlich." + +msgid "The fingerprint should not be empty." +msgstr "Der Fingerabdruck darf nicht leer sein." + +msgid "The fingerprint should be a valid alphanumeric string." +msgstr "Der Fingerabdruck sollte eine gültige alphanumerische Zeichenkette sein." + +msgid "The fingerprint does not match the OpenPGP public and the OpenPGP private keys fingerprints." +msgstr "Der Fingerabdruck stimmt nicht mit dem öffentlichen OpenPGP und den privaten OpenPGP überein." + +msgid "A full base url is required." +msgstr "Eine vollständige Basis-URL ist erforderlich." + +msgid "The full base url should not be empty" +msgstr "Die vollständige Basis-URL darf nicht leer sein" + +msgid "The full base url should be a valid BMP-UTF8 string." +msgstr "Die vollständige Basis-URL sollte eine gültige BMP-UTF8-Zeichenkette sein." + +msgid "A public registration setting is required." +msgstr "Eine öffentliche Registrierungseinstellung ist erforderlich." + +msgid "The public registration setting should be a valid boolean." +msgstr "Die öffentliche Registrierungseinstellung sollte ein gültiger Boolescher Wert sein." + +msgid "A force ssl status is required." +msgstr "Ein Force SSL Status ist erforderlich." + +msgid "The force ssl setting should be a valid boolean." +msgstr "Die SSL-Erzwingen Einstellung sollte ein gültiger Boolescher Wert sein." + +msgid "The database schema does not match the one expected" +msgstr "Das Datenbankschema entspricht nicht dem erwarteten" + +msgid "There was a problem creating the first user" +msgstr "Beim Erstellen des ersten Benutzers ist ein Problem aufgetreten" + +msgid "There was a problem creating the registration token" +msgstr "Beim Erstellen des Registrierungstokens ist ein Problem aufgetreten" + +msgid "The passbolt config is writable." +msgstr "Die Passbolt Konfiguration ist beschreibbar." + +msgid "The passbolt config is not writable." +msgstr "Die Passbolt Konfiguration ist nicht beschreibbar." + +msgid "Ensure the file " +msgstr "Datei sicherstellen " + +msgid "you can try:" +msgstr "Sie können versuchen:" + +msgid "The server OpenPGP public key file is writable." +msgstr "Die öffentliche OpenPGP Schlüsseldatei des Servers ist beschreibbar." + +msgid "The server OpenPGP public key file is not writable." +msgstr "Die OpenPGP öffentliche Schlüsseldatei des Servers ist nicht schreibbar." + +msgid "The server OpenPGP private key file is writable." +msgstr "Die private OpenPGP Schlüsseldatei des Servers ist schreibbar." + +msgid "The server OpenPGP private key file is not writable." +msgstr "Die private OpenPGP Schlüsseldatei des Servers ist nicht schreibbar." + +msgid "Create your user account!" +msgstr "Erstellen Sie Ihr Benutzerkonto!" + +msgid "Admin user details" +msgstr "Admin-Benutzerdetails" + +msgid "First name" +msgstr "Vorname" + +msgid "Last name" +msgstr "Nachname" + +msgid "mail@yourdomain.com" +msgstr "mail@yourdomain.com" + +msgid "Next" +msgstr "Weiter" + +msgid "Enter your database details." +msgstr "Geben Sie Ihre Datenbankdetails ein." + +msgid "Database configuration" +msgstr "Datenbank-Konfiguration" + +msgid "An existing database has been detected on your server. The connection details are pre-filled below." +msgstr "Auf Ihrem Server wurde eine existierende Datenbank erkannt. Die Verbindungsdetails sind unten vorausgefüllt." + +msgid "You can keep it as it is (default), or replace it with the details of another database." +msgstr "Sie können es so behalten wie es ist (Standard), oder ersetzen Sie es durch die Details einer anderen Datenbank." + +msgid "Database connection url" +msgstr "Datenbankverbindungs-URL" + +msgid "host name or ip address" +msgstr "Hostname oder IP-Adresse" + +msgid "username" +msgstr "Benutzer*innenname" + +msgid "password" +msgstr "Passwort" + +msgid "Password" +msgstr "Passwort" + +msgid "database name" +msgstr "Datenbankname" + +msgid "Database name" +msgstr "Datenbankname" + +msgid "Cancel" +msgstr "Abbrechen" + +msgid "Enter your SMTP server settings." +msgstr "Geben Sie Ihre SMTP-Server-Einstellungen ein." + +msgid "Email configuration" +msgstr "E-mail Konfiguration" + +msgid "admin or company name" +msgstr "Admin oder Firmenname" + +msgid "Sender name" +msgstr "Absendername" + +msgid "email@company.com" +msgstr "email@firma.com" + +msgid "Sender email" +msgstr "Absender E-Mail" + +msgid "SMTP server configuration" +msgstr "SMTP-Server-Konfiguration" + +msgid "SMTP host" +msgstr "SMTP-Host" + +msgid "Use TLS?" +msgstr "TLS verwenden?" + +msgid "port" +msgstr "Port" + +msgid "Port" +msgstr "Anschluss" + +msgid "Passbolt is not configured." +msgstr "Passbolt ist nicht konfiguriert." + +msgid "Passbolt is not configured yet!" +msgstr "Passbolt ist noch nicht konfiguriert!" + +msgid "If you see this page, it means that passbolt is present on your server but not configured. Click on \"Get Started\" to launch the configuration wizard." +msgstr "Wenn Sie diese Seite sehen, bedeutet dies, dass passbolt auf Ihrem Server vorhanden, aber nicht konfiguriert ist. Klicken Sie auf \"Starten\", um den Konfigurations-Assistenten zu starten." + +msgid "Get Started" +msgstr "Loslegen" + +msgid "Create a new server OpenPGP key or {0} an existing one." +msgstr "Erstellen Sie einen neuen OpenPGP-Schlüssel des Servers oder {0} einen existierenden." + +msgid "import" +msgstr "importieren" + +msgid "Create a new OpenPGP key for your server" +msgstr "Erstellen Sie einen neuen OpenPGP-Schlüssel für Ihren Server" + +msgid "My company server name" +msgstr "Mein Firmen-Servername" + +msgid "Server Name" +msgstr "Servername" + +msgid "admin@your-server.com" +msgstr "admin@your-server.com" + +msgid "Server Email" +msgstr "Server E-Mail-Adresse" + +msgid "add a comment (optional)" +msgstr "einen Kommentar hinzufügen (optional)" + +msgid "Comment" +msgstr "Kommentar" + +msgid "Import an existing key or {0} a new one." +msgstr "Einen vorhandenen Schlüssel importieren oder {0} einen neuen." + +msgid "create" +msgstr "erstellen" + +msgid "Copy paste the OpenPGP private key below" +msgstr "Kopieren Sie den privaten OpenPGP-Schlüssel und fügen Sie ihn unten ein" + +msgid "Browse" +msgstr "Durchsuchen" + +msgid "Or select a file from your computer" +msgstr "Oder wählen Sie eine Datei von Ihrem Computer" + +msgid "Some binary things are happening..." +msgstr "Einige binäre Dinge passieren..." + +msgid "Passbolt Pro activation." +msgstr "Passbolt Pro Aktivierung." + +msgid "Copy paste your Passbolt Pro subscription key here" +msgstr "Kopieren Sie Ihren Passbolt Pro Abonnementschlüssel hier hinein" + +msgid "Choose your preferences." +msgstr "Wählen Sie Ihre Einstellungen." + +msgid "Full Base Url" +msgstr "Vollständige Basis-URL" + +msgid "Full base url" +msgstr "Vollständige Basis-URL" + +msgid "This is the url where passbolt will be accessible. This url will be used for places where the passbolt url cannot be guessed automatically, such as links in emails. No trailing slash." +msgstr "Dies ist die Url, wo passbolt zugänglich sein wird. Diese Url wird für Orte verwendet, an denen die Passbolt-Url nicht automatisch erraten werden kann, wie z. B. Links in E-Mails. Kein abschließender Schrägstrich." + +msgid "Allow public registration?" +msgstr "Öffentliche Registrierung erlauben?" + +msgid "Allowing public registration means that any visitor can create himself an account on your passbolt. Unless your instance of passbolt is not reachable by the outside world, it is usually a bad idea." +msgstr "Wenn Sie die öffentliche Registrierung zulassen, kann sich jeder Besucher ein Konto in Ihrem Passbolt erstellen. Außer wenn Ihre Instanz von Passbolt nicht von außen erreicht werden kann, ist es in der Regel eine schlechte Idee." + +msgid "Force SSL?" +msgstr "SSL erzwingen?" + +msgid "Forcing SSL means that passbolt will not accept connections coming from a non secure protocol. If SSL is forced, your server has to be configured for HTTPS. It is highly recommended that you do so." +msgstr "Das Erzwingen von SSL bedeutet, dass Passbolt keine Verbindungen akzeptiert, die von einem nicht sicheren Protokoll kommen. Wenn SSL erzwungen wird, muss Ihr Server für HTTPS konfiguriert werden. Dies wird dringend empfohlen." + +msgid "Welcome to Passbolt Pro! Let's get started with the configuration." +msgstr "Willkommen bei Passbolt Pro! Lassen Sie uns mit der Konfiguration beginnen." + +msgid "Nice one! Your environment is ready for passbolt." +msgstr "Sehr schön! Ihr System ist bereit für Passbolt." + +msgid "Oops!! Passbolt cannot run yet on your server." +msgstr "Ups!! Passbolt kann noch nicht auf Ihrem Server ausgeführt werden." + +msgid "Environment is configured correctly." +msgstr "Das System ist korrekt konfiguriert." + +msgid "URL rewriting is not properly configured on your server." +msgstr "URL-Rewriting ist auf Ihrem Server nicht korrekt konfiguriert." + +msgid "GPG is configured correctly." +msgstr "GPG ist korrekt konfiguriert." + +msgid "GPG Configuration" +msgstr "GPG-Konfiguration" + +msgid "SSL" +msgstr "SSL" + +msgid "SSL access is enabled." +msgstr "SSL-Zugriff ist aktiviert." + +msgid "SSL access is not enabled. You can still proceed, but it is highly recommended that you configure your web server to use HTTPS before you continue." +msgstr "SSL-Zugriff ist nicht aktiviert. Sie können trotzdem fortfahren, aber es wird dringend empfohlen, dass Sie Ihren Webserver so konfigurieren, dass er HTTPS verwendet, bevor Sie fortfahren." + +msgid "Start configuration" +msgstr "Konfiguration starten" + +msgid "The configuration is complete" +msgstr "Die Konfiguration ist abgeschlossen" + +msgid "Success!" +msgstr "Erfolgreich!" + +msgid "You have completed successfully the configuration procedure, congrats!" +msgstr "Sie haben die Konfiguration erfolgreich abgeschlossen, Glückwunsch!" + +msgid "You will soon be redirected to passbolt to complete your account setup." +msgstr "Sie werden in Kürze zu Passbolt weitergeleitet, um die Einrichtung Ihres Kontos abzuschließen." + +msgid "You will soon be redirected to the login page." +msgstr "Sie werden in Kürze zur Login-Seite weitergeleitet." + +msgid "next" +msgstr "weiter" + +msgid "Click here if you can't wait" +msgstr "Klicken Sie hier, wenn Sie nicht warten wollen" + +msgid "Errors details" +msgstr "Fehlerdetails" + +msgid "Installing" +msgstr "Installiere" + +msgid "Existing installation?" +msgstr "Bestehende Installation?" + +msgid "If you want to use an existing passbolt database, the installer will take care of updating it to the current passbolt version while keeping your data." +msgstr "Wenn Sie eine vorhandene Passbolt-Datenbank verwenden wollen, der Installer kümmert sich um die Aktualisierung auf die aktuelle Passbolt-Version, während er Ihre Daten behält." + +msgid "As a precaution, do not forget to backup your database before you continue." +msgstr "Vergessen Sie nicht, Ihre Datenbank zu sichern, bevor Sie fortfahren." + +msgid "Help" +msgstr "Hilfe" + +msgid "Need help? You can find more information on how to install passbolt in the official online help." +msgstr "Benötigen Sie Hilfe? Weitere Informationen zur Installation von Passbolt finden Sie in der offiziellen Online-Hilfe." + +msgid "What subscription key?" +msgstr "Welcher Abonnement-Schlüssel?" + +msgid "The subscription key is a file that was sent to you when you purchased Passbolt Pro. We need the subscription key in order to activate the Pro features of passbolt." +msgstr "Der Abonnement-Schlüssel ist eine Datei, die Ihnen beim Kauf von Passbolt Pro geschickt wurde. Wir benötigen den Abonnement-Schlüssel, um die Pro-Funktionen des Passbolts zu aktivieren." + +msgid "Send test email" +msgstr "Test-E-Mail senden" + +msgid "The test email has been sent successfully!" +msgstr "Die Test-E-Mail wurde erfolgreich versendet!" + +msgid "Email could not be sent:" +msgstr "Die E-Mail konnte nicht versendet werden:" + +msgid "See trace" +msgstr "Spur ansehen" + +msgid "Your email address" +msgstr "Ihre E-Mail-Adresse" + +msgid "Why do I need a SMTP server?" +msgstr "Warum brauche ich einen SMTP-Server?" + +msgid "Passbolt needs an smtp server in order to send invitation emails after an account creation and to send email notifications." +msgstr "Passbolt benötigt einen SMTP-Server, um Einladungs-E-Mails nach der Erstellung eines Kontos zu versenden und E-Mail-Benachrichtigungen zu senden." + +msgid "The requested address was not found on this server." +msgstr "Die angeforderte Adresse wurde auf diesem Server nicht gefunden." + +msgid "Please double check the url." +msgstr "Bitte überprüfen Sie die Url." + +msgid "Maybe the page was deleted or moved." +msgstr "Möglicherweise wurde die Seite gelöscht oder verschoben." + +msgid "Health checks" +msgstr "Gesundheitschecks" + +msgid "Passbolt API Status" +msgstr "Passbolt API Status" + +msgid "SSL access is not enabled." +msgstr "SSL-Zugriff ist nicht aktiviert." + +msgid "Group manager" +msgstr "Gruppenmanager" + +msgid "Member" +msgstr "Mitglied" + +msgid "Updated roles" +msgstr "Aktualisierte Rollen" + +msgid "is now group manager" +msgstr "ist jetzt Gruppen-Manager*in" + +msgid "is not anymore group manager" +msgstr "ist nicht mehr Gruppen-Manager*in" + +msgid "Terms" +msgstr "Bedingungen" + +msgid "Privacy" +msgstr "Privatsphäre" + +msgid "Credits" +msgstr "Credits" + +msgid "Versions" +msgstr "Versionen" + +msgid "home" +msgstr "Start" + +msgid "login" +msgstr "Anmelden" + +msgid "register" +msgstr "registrieren" + +msgid "You have initiated an account recovery!" +msgstr "Sie haben eine Kontowiederherstellung gestartet!" + +msgid "Welcome back!" +msgstr "Willkommen zurück!" + +msgid "You just requested to recover your passbolt account on this device." +msgstr "Sie gerade angefordert, Ihren Passbolt-Zugang auf diesem Gerät wiederherzustellen." + +msgid "Make sure you have a backup of your secret key handy." +msgstr "Stellen Sie unbedingt sicher, dass Sie ein Backup Ihres geheimen Schlüssels zur Hand haben." + +msgid "Click on the link below to proceed." +msgstr "Klicken Sie auf den Link unten, um fortzufahren." + +msgid "start recovery" +msgstr "starte Wiederherstellung" + +msgid "{0} just created an account for you on passbolt!" +msgstr "{0} hat gerade ein Konto in Passbolt für Sie erstellt!" + +msgid "Welcome {0}" +msgstr "Willkommen {0}" + +msgid "{0} just invited you to join passbolt at {1}" +msgstr "{0} hat Sie gerade zu Passbolt unter {1} eingeladen" + +msgid "Passbolt is an open source password manager." +msgstr "Passbolt ist ein Open-Source-Passwort-Manager." + +msgid "It is designed to allow sharing credentials securely with your team!" +msgstr "Es ist für das sichere Teilen von Passwörtern in Teams konzipiert!" + +msgid "Let's take the next five minutes to get you started!" +msgstr "Nehmen Sie sich die nächsten fünf Minuten Zeit, für den Einstieg in Passbolt!" + +msgid "get started" +msgstr "starten" + +msgid "You just created your account on passbolt!" +msgstr "Sie haben soeben Ihren Zugang in Passbolt erstellt!" + +msgid "You just opened an account on passbolt at {0}." +msgstr "Sie haben soeben einen Zugang in Passbolt unter {0} angelegt." + +msgid "{0} requested you to add members to a group" +msgstr "{0} bittet Sie, Mitglieder zu einer Gruppe hinzuzufügen" + +msgid "Group: {0}" +msgstr "Gruppe: {0}" + +msgid "The following members should be added:" +msgstr "Folgende Mitglieder sollen hinzugefügt werden:" + +msgid "Edit group members" +msgstr "Gruppen-Mitglieder bearbeiten" + +msgid "log in passbolt" +msgstr "in Passbolt einloggen" + +msgid "{0} deleted the user {1}" +msgstr "{0} hat Benutzer*in {1} gelöscht" + +msgid "The user {0} {1} ({2}) is now deleted from your organisation in passbolt." +msgstr "Benutzer*in {0} {1} ({2}) wurde in Passbolt aus Ihrer Organisation gelöscht." + +msgid "This user was a member of the following group(s) you manage:" +msgstr "Diese*r Benutzer* war Mitglied der folgenden Gruppe(n) die Sie verwalten:" + +msgid "view it in passbolt" +msgstr "in Passbolt anzeigen" + +msgid "{0} deleted a group" +msgstr "{0} hat eine Gruppe gelöscht" + +msgid "{0} deleted the group \"{1}\" you were a member of." +msgstr "{0} hat die Gruppe \"{1}\" gelöscht, in der Sie Mitglied waren." + +msgid "All passwords that were shared only with this group were also deleted." +msgstr "Alle Passwörter, die nur mit dieser Gruppe geteilt waren, wurden ebenfalls gelöscht." + +msgid "As member of the group you now have access to all the passwords that are shared with this group." +msgstr "Als Mitglied der Gruppe haben Sie nun Zugriff auf alle Passwörter, die mit dieser Gruppe geteilt sind." + +msgid "And as group manager you are also authorized to edit the members of the group." +msgstr "Und als Gruppen-Manager*in sind Sie auch berechtigt, die Mitglieder der Gruppe zu bearbeiten." + +msgid "You are no longer a member of this group." +msgstr "Sie sind nicht mehr Mitglied dieser Gruppe." + +msgid "You are no longer authorized to access the passwords shared with this group." +msgstr "Sie sind nicht mehr berechtigt, auf die mit dieser Gruppe geteilten Passwörter zuzugreifen." + +msgid "Please contact {0} or another group manager if this is a mistake." +msgstr "Bitte kontaktieren Sie {0} oder eine*n andere*n Gruppen-Manager*in, falls dies ein Fehler ist." + +msgid "You are now a group manager of this group." +msgstr "Sie sind jetzt ein*e Gruppen-Manager*in dieser Gruppe." + +msgid "As group manager you are now authorized to edit the members of this group." +msgstr "Als Gruppen-Manager*in sind Sie nun berechtigt, die Mitglieder dieser Gruppe zu bearbeiten." + +msgid "As member of the group you still have access to all the passwords that are shared with this group." +msgstr "Als Mitglied der Gruppe haben Sie weiterhin Zugriff auf alle Passwörter, die mit dieser Gruppe geteilt werden." + +msgid "You are no longer a group manager of this group." +msgstr "Sie sind kein*e Manager*in dieser Gruppe mehr." + +msgid "You are no longer authorized to edit the members of this group." +msgstr "Sie sind nicht mehr berechtigt, die Mitglieder dieser Gruppe zu bearbeiten." + +msgid "Edited your membership in several groups" +msgstr "Bearbeitete Deine Mitgliedschaft in mehreren Gruppen" + +msgid "{0} group memberships were affected." +msgstr "{0} Gruppenmitgliedschaften waren betroffen." + +msgid "It would be too much to list them here, but you can get more information on passbolt." +msgstr "Es wäre zu viel, sie hier aufzuzählen, aber Sie können mehr Informationen in Passbolt erhalten." + +msgid "go to passbolt" +msgstr "zu Passbolt gehen" + +msgid "{0} deleted several group" +msgstr "{0} hat mehrere Gruppen gelöscht" + +msgid "{0} deleted {1} groups you were a member of." +msgstr "{0} hat {1} Gruppen gelöscht, in denen Sie Mitglied waren." + +msgid "You have saved a new password" +msgstr "Sie haben ein neues Passwort gespeichert" + +msgid "Name: {0}" +msgstr "Name: {0}" + +msgid "Username: {0}" +msgstr "Benutzer*innennamen: {0}" + +msgid "URL: {0}" +msgstr "URL: {0}" + +msgid "Description: {0}" +msgstr "Beschreibung: {0}" + +msgid "{0} shared a password with you" +msgstr "{0} hat ein Passwort mit Ihnen geteilt" + +msgid "{0} updated the password {1}" +msgstr "{0} hat das Passwort {1} aktualisiert" + +msgid "Edited multiple resources" +msgstr "Hat mehrere Ressourcen bearbeitet" + +msgid "{0} resources were affected." +msgstr "{0} Ressourcen sind betroffen." + +msgid "It would be too much to list them here, but you can go check them on passbolt." +msgstr "Es wäre zu viel, sie hier aufzulisten, aber Sie können sie in Passbolt überprüfen." + +msgid "view them in passbolt" +msgstr "in Passbolt anzeigen" + +msgid "{0} shared passwords with you" +msgstr "{0} hat Passwörter mit Ihnen geteilt" + +msgid "{0} resources were shared with you." +msgstr "{0} Ressourcen wurden mit Ihnen geteilt." + +msgid "{0} just activated their account on passbolt!" +msgstr "{0} hat gerade ihr*sein Konto bei Passwort aktiviert!" + +msgid "The user is now active on passbolt and you can share passwords with them." +msgstr "Benutzer*in ist jetzt auf Passbolt aktiv und Sie können Passwörter mit ihr*ihm teilen." + +msgid "This user was invited by you {0}." +msgstr "Benutzer*in wurde durch Sie {0} eingeladen." + +msgid "This user signed up themselves, since the public registration is enabled." +msgstr "Diese*r Benutzer*in hat sich selber registriert, da die öffentliche Registrierung aktiv ist." + +msgid "This user was invited by {0} {1}." +msgstr "Benutzer*in wurde durch {0} {1} eingeladen." + +msgid "view users" +msgstr "Benutzer*innen anzeigen" + +msgid "The locale {0} is not valid or not supported." +msgstr "Das Gebietsschema {0} ist ungültig oder wird nicht unterstützt." + +msgid "An Internal Error Has Occurred" +msgstr "Ein interner Fehler ist aufgetreten" + diff --git a/resources/locales/en_UK/default.po b/resources/locales/en_UK/default.po index 579cc1dd80..f2d72abe22 100644 --- a/resources/locales/en_UK/default.po +++ b/resources/locales/en_UK/default.po @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" -"POT-Creation-Date: 2021-05-17 06:42+0000\n" +"POT-Creation-Date: 2021-10-06 14:32+0000\n" "PO-Revision-Date: YYYY-mm-DD HH:MM+ZZZZ\n" "Last-Translator: NAME \n" "Language-Team: LANGUAGE \n" @@ -263,9 +263,6 @@ msgstr "" msgid "The resource has been added successfully." msgstr "" -msgid "Could not validate resource data." -msgstr "" - msgid "The resource has been deleted successfully." msgstr "" @@ -1136,10 +1133,10 @@ msgstr "" msgid "Validation success for authenticationToken {0}" msgstr "" -msgid "Could not save the avatar in cache." +msgid "Could not save the avatar in the {0} file." msgstr "" -msgid "Could not read the avatar in cache." +msgid "The avatar id is not valid." msgstr "" msgid "Validation failed for comment {0}. {1}" @@ -1199,6 +1196,15 @@ msgstr "" msgid "Validation success for profile {0}" msgstr "" +msgid "Could not validate resource data." +msgstr "" + +msgid "An unexpected error occured, please contact an administrator." +msgstr "" + +msgid "The server returned the following error:" +msgstr "" + msgid "Validation failed for resource {0}. {1}" msgstr "" @@ -1349,6 +1355,126 @@ msgstr "" msgid "The notification settings for the organization were updated." msgstr "" +msgid "The challenge cannot be decrypted." +msgstr "" + +msgid "The challenge is invalid. Deserialization failed." +msgstr "" + +msgid "The challenge is invalid. Validation Failed." +msgstr "" + +msgid "The config for the server private key fingerprint is not available or incomplete." +msgstr "" + +msgid "The config for the server private key passphrase is invalid." +msgstr "" + +msgid "The user id is missing or invalid." +msgstr "" + +msgid "The user OpenPGP key does not exist, or is invalid, or has been deleted." +msgstr "" + +msgid "The user signature is invalid." +msgstr "" + +msgid "The user challenge is missing or invalid." +msgstr "" + +msgid "The version is invalid." +msgstr "" + +msgid "The domain is invalid." +msgstr "" + +msgid "The domain is invalid. Expected: {0} and got {1}" +msgstr "" + +msgid "The authentication was a success." +msgstr "" + +msgid "The authentication failed." +msgstr "" + +msgid "The credentials are missing." +msgstr "" + +msgid "The user does not exist or is not active or has been deleted." +msgstr "" + +msgid "The credentials are invalid." +msgstr "" + +msgid "An internal error occurred." +msgstr "" + +msgid "The verify token is invalid." +msgstr "" + +msgid "The route {0} is not permitted with JWT authentication." +msgstr "" + +msgid "The key pair for JWT Authentication is not complete. The following file could not be read: {0}." +msgstr "" + +msgid "The JWT public key could not be read or is not valid." +msgstr "" + +msgid "The JWT private key should be at least {0} bytes long." +msgstr "" + +msgid "The configuration {0} is not correctly set." +msgstr "" + +msgid "No active refresh token matching the request could be found." +msgstr "" + +msgid "The refresh token provided was already used." +msgstr "" + +msgid "Expired refresh token provided." +msgstr "" + +msgid "The refresh token should be a valid UUID." +msgstr "" + +msgid "This is not a valid user id." +msgstr "" + +msgid "Invalid verify token expiry." +msgstr "" + +msgid "Attempt to access an expired verify token." +msgstr "" + +msgid "Invalid verify token format." +msgstr "" + +msgid "Invalid user ID format." +msgstr "" + +msgid "Verify token has been already used in the past." +msgstr "" + +msgid "Security warning!" +msgstr "" + +msgid "An unknown user with IP: {0} attempted to identify as {1}." +msgstr "" + +msgid "This is a potential security issue." +msgstr "" + +msgid "Please investigate!" +msgstr "" + +msgid "An unknown user with IP: {0} attempted to steal your login data." +msgstr "" + +msgid "Please get in touch with one of your administrators." +msgstr "" + msgid "This is not a valid locale." msgstr "" @@ -1457,6 +1583,120 @@ msgstr "" msgid "Could not validate secret history data." msgstr "" +msgid "Information about the transfer is required." +msgstr "" + +msgid "The operation was successful" +msgstr "" + +msgid "The transfer id is not valid." +msgstr "" + +msgid "The authentication token should be a valid uuid." +msgstr "" + +msgid "The authentication token is invalid." +msgstr "" + +msgid "The transfer id should be a uuid." +msgstr "" + +msgid "The user id should be a uuid." +msgstr "" + +msgid "A user id is required" +msgstr "" + +msgid "The hash should be an ascii string." +msgstr "" + +msgid "The hash should be {0} characters in length." +msgstr "" + +msgid "The data transfer hash is required." +msgstr "" + +msgid "The current page should be a non negative integer." +msgstr "" + +msgid "The current page number is required" +msgstr "" + +msgid "The current page number should be equal or inferior to the total number of pages." +msgstr "" + +msgid "The current page cannot be greater than {0}" +msgstr "" + +msgid "The total number of pages should a non negative integer." +msgstr "" + +msgid "The total number of pages is required." +msgstr "" + +msgid "The total number of pages should be greater than 0." +msgstr "" + +msgid "The total number of pages cannot be greater than {0}" +msgstr "" + +msgid "The status should not be empty." +msgstr "" + +msgid "The status is required." +msgstr "" + +msgid "The status must be one of the following: {0}." +msgstr "" + +msgid "The user must be active." +msgstr "" + +msgid "Could not validate the transfer data." +msgstr "" + +msgid "The transfer could not be created." +msgstr "" + +msgid "Could not update the transfer data." +msgstr "" + +msgid "The transfer could not be updated." +msgstr "" + +msgid "This operation is not allowed." +msgstr "" + +msgid "Restarting a transfer is not allowed." +msgstr "" + +msgid "The operation is already over." +msgstr "" + +msgid "The current page does not match the total number of pages." +msgstr "" + +msgid "This operation is not allowed for this user." +msgstr "" + +msgid "The authentication token is missing." +msgstr "" + +msgid "The authentication token is not valid for this user." +msgstr "" + +msgid "The authentication token type is invalid." +msgstr "" + +msgid "The authentication token is not active." +msgstr "" + +msgid "The authentication token is expired." +msgstr "" + +msgid "The password generator value \"{0}\" is not valid." +msgstr "" + msgid "5 minutes" msgstr "" @@ -1829,21 +2069,6 @@ msgstr "" msgid "Port" msgstr "" -msgid "The test email has been sent successfully!" -msgstr "" - -msgid "Email could not be sent:" -msgstr "" - -msgid "See trace" -msgstr "" - -msgid "Your email address" -msgstr "" - -msgid "Send test email" -msgstr "" - msgid "Passbolt is not configured." msgstr "" @@ -1991,6 +2216,15 @@ msgstr "" msgid "Installing" msgstr "" +msgid "Existing installation?" +msgstr "" + +msgid "If you want to use an existing passbolt database, the installer will take care of updating it to the current passbolt version while keeping your data." +msgstr "" + +msgid "As a precaution, do not forget to backup your database before you continue." +msgstr "" + msgid "Help" msgstr "" @@ -2003,6 +2237,27 @@ msgstr "" msgid "The subscription key is a file that was sent to you when you purchased Passbolt Pro. We need the subscription key in order to activate the Pro features of passbolt." msgstr "" +msgid "Send test email" +msgstr "" + +msgid "The test email has been sent successfully!" +msgstr "" + +msgid "Email could not be sent:" +msgstr "" + +msgid "See trace" +msgstr "" + +msgid "Your email address" +msgstr "" + +msgid "Why do I need a SMTP server?" +msgstr "" + +msgid "Passbolt needs an smtp server in order to send invitation emails after an account creation and to send email notifications." +msgstr "" + msgid "The requested address was not found on this server." msgstr "" @@ -2027,6 +2282,9 @@ msgstr "" msgid "Member" msgstr "" +msgid "Updated roles" +msgstr "" + msgid "is now group manager" msgstr "" diff --git a/resources/locales/fr_FR/default.po b/resources/locales/fr_FR/default.po new file mode 100644 index 0000000000..7796083255 --- /dev/null +++ b/resources/locales/fr_FR/default.po @@ -0,0 +1,2508 @@ +msgid "" +msgstr "" +"Project-Id-Version: 41c2572bd9bd4cc908d3e09e0cbed6e5\n" +"POT-Creation-Date: 2021-10-06 14:32+0000\n" +"PO-Revision-Date: 2021-10-13 07:27\n" +"Last-Translator: NAME \n" +"Language-Team: French\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"X-Crowdin-Project: 41c2572bd9bd4cc908d3e09e0cbed6e5\n" +"X-Crowdin-Project-ID: 2\n" +"X-Crowdin-Language: fr\n" +"X-Crowdin-File: /[passbolt.passbolt-ce-api] release/resources/locales/en_UK/default.po\n" +"X-Crowdin-File-ID: 212\n" +"Language: fr_FR\n" + +msgid "You need to login to access this location." +msgstr "Vous devez vous connecter pour accéder à cet emplacement." + +msgid "Decryption failed." +msgstr "Le déchiffrement a échoué." + +msgid "Failed to create token." +msgstr "Impossible de créer le jeton." + +msgid "The user token result should be a valid UUID." +msgstr "Le résultat du jeton de l'utilisateur doit être un UUID valide." + +msgid "The user token result could not be found " +msgstr "Le résultat du jeton utilisateur est introuvable " + +msgid "The OpenPGP server key defined in the config cannot be used to decrypt." +msgstr "La clé de serveur OpenPGP définie dans la configuration ne peut pas être utilisée pour déchiffrer." + +msgid "Could not import the user OpenPGP key." +msgstr "Impossible d'importer la clé OpenPGP de l'utilisateur." + +msgid "Invalid verify token format, " +msgstr "Format de jeton de vérification invalide, " + +msgid "sections are missing or using wrong delimiters." +msgstr "sections manquantes ou mauvais délimiteurs utilisés." + +msgid "the version numbers do not match." +msgstr "les numéros de versions ne correspondent pas." + +msgid "wrong version number." +msgstr "mauvais numéro de version." + +msgid "it is not a UUID." +msgstr "ce n'est pas un UUID." + +msgid "wrong token data length." +msgstr "mauvaise longueur de données de jeton." + +msgid "There is no user associated with this key." +msgstr "Il n'y a aucun utilisateur associé à cette clé." + +msgid "Page not found." +msgstr "Page introuvable." + +msgid "This is not a valid Ajax/Json request." +msgstr "Ce n'est pas une requête Ajax/Json valide." + +msgid "You are successfully logged in." +msgstr "Vous avez été connecté avec succès." + +msgid "The OpenPGP public key information was not found in config." +msgstr "Les informations de la clé publique OpenPGP n'ont pas été trouvées dans la configuration." + +msgid "The operation was successful." +msgstr "L'opération a été effectuée avec succès." + +msgid "The resource identifier should be a valid UUID." +msgstr "L'identifiant de la ressource doit être un UUID valide." + +msgid "Could not save the comment, please try again later." +msgstr "Impossible d'enregistrer le commentaire, veuillez réessayer plus tard." + +msgid "The comment was successfully added." +msgstr "Le commentaire a été ajouté avec succès." + +msgid "The resource does not exist." +msgstr "La ressource n’existe pas." + +msgid "Could not validate comment data." +msgstr "Impossible de valider les données du commentaire." + +msgid "The comment id is not valid." +msgstr "L'identifiant du commentaire n'est pas valide." + +msgid "The comment does not exist." +msgstr "Le commentaire n'existe pas." + +msgid "The comment was deleted." +msgstr "Le commentaire a été supprimé." + +msgid "Could not delete comment." +msgstr "Impossible de supprimer le commentaire." + +msgid "The comment was successfully updated." +msgstr "Le commentaire a été mis à jour avec succès." + +msgid "You are not allowed to edit this comment." +msgstr "Vous n'êtes pas autorisé à modifier ce commentaire." + +msgid "Could not find comments for the requested model." +msgstr "Impossible de trouver les commentaires pour le modèle demandé." + +msgid "Invalid order. \"{0}\" is not in the list of allowed order." +msgstr "Tri incorrect \"{0}\" n'est pas dans la liste des tris autorisés." + +msgid "Invalid order. \"{0}\" is not a valid order." +msgstr "Tri incorrect. \"{0}\" n'est pas un ordre valide." + +msgid "Invalid order. \"{0}\" is not a valid field." +msgstr "Tri incorrect. \"{0}\" n'est pas une valeur correcte." + +msgid "Invalid pagination." +msgstr "Pagination invalide." + +msgid "Invalid query string. The filter parameter should be an array." +msgstr "Chaîne de requête invalide. Le paramètre de filtre doit être un tableau." + +msgid "Invalid query string. The contain parameter should be an array." +msgstr "Chaîne de requête invalide. Le paramètre permettant de gérer le contenu doit être un tableau." + +msgid "Invalid filter." +msgstr "Filtre invalide." + +msgid "Invalid order." +msgstr "Tri invalide." + +msgid "Invalid contain." +msgstr "Conteneur invalide." + +msgid "No validation rule for filter {0}. Please create one." +msgstr "Aucune règle de validation pour le filtre {0}. Veuillez en créer une." + +msgid "Filter {0} is not valid." +msgstr "Le filtre {0} n'est pas valide." + +msgid "\"{0}\" is not a valid value for filter {1}." +msgstr "\"{0}\" n'est pas une valeur valide pour le filtre {1}." + +msgid "\"{0}\" is not a valid search filter." +msgstr "\"{0}\" n'est pas un filtre de recherche valide." + +msgid "\"{0}\" is not a valid search filter. It is not a UTF8 string." +msgstr "\"{0}\" n'est pas un filtre de recherche valide. Ce n'est pas une chaîne UTF8." + +msgid "It should be between 1 and 64 char in length." +msgstr "Il doit être entre 1 à 64 caractères de longueur." + +msgid "\"{0}\" is not a valid user filter." +msgstr "\"{0}\" n'est pas un filtre utilisateur valide." + +msgid "\"{0}\" is not a valid user id for filter {1}." +msgstr "\"{0}\" n'est pas un identifiant d'utilisateur valide pour le filtre {1}." + +msgid "\"{0}\" is not a valid group filter." +msgstr "\"{0}\" n'est pas un filtre de groupe valide." + +msgid "\"{0}\" is not a valid resource id for filter {1}." +msgstr "\"{0}\" n'est pas un identifiant de ressource valide pour le filtre {1}." + +msgid "\"{0}\" is not a valid group id for filter {1}." +msgstr "\"{0}\" n'est pas un identifiant de groupe valide pour le filtre {1}." + +msgid "\"{0}\" is not a valid timestamp for filter {1}." +msgstr "\"{0}\" n'est pas un horodatage valide pour le filtre {1}." + +msgid "\"{0}\" is not a valid datetime for filter {1}." +msgstr "\"{0}\" n'est pas un format de date valide pour le filtre {1}." + +msgid "\"{0}\" is not a valid order." +msgstr "\"{0}\" n'est pas un tri valide." + +msgid "\"{0}\" is not in the list of allowed order." +msgstr "\"{0}\" n'est pas dans la liste des tris autorisés." + +msgid "\"{0}\" is not a valid contain value." +msgstr "\"{0}\" n'est pas une valeur contenue valide." + +msgid "undefined user agent" +msgstr "agent utilisateur indéfini" + +msgid "Access restricted to administrators." +msgstr "Accès limité aux administrateurs." + +msgid "This feature is not supported by your version of passbolt." +msgstr "Cette fonctionnalité n'est pas supportée par votre version de passbolt." + +msgid "The resource was marked as favorite." +msgstr "La ressource a été marquée comme favorite." + +msgid "This record is already marked as favorite." +msgstr "Cet enregistrement est déjà marqué comme favori." + +msgid "Could not validate favorite data." +msgstr "Impossible de valider les données du favori." + +msgid "The favorite id is not valid." +msgstr "L'identifiant du favori n'est pas valide." + +msgid "The favorite does not exist." +msgstr "Le favori n'existe pas." + +msgid "The favorite was deleted." +msgstr "Le favori a été supprimé." + +msgid "Could not delete favorite." +msgstr "Impossible de supprimer le favori." + +msgid "The OpenPGP key identifier should be a valid UUID." +msgstr "L'identifiant de clé OpenPGP doit être un UUID valide." + +msgid "The OpenPGP key does not exist." +msgstr "La clé OpenPGP n'existe pas." + +msgid "The group has been added successfully." +msgstr "Le groupe a été ajouté avec succès." + +msgid "The group can be deleted." +msgstr "Le groupe peut être supprimé." + +msgid "The group was deleted successfully." +msgstr "Le groupe a été supprimé avec succès." + +msgid "You are not authorized to access that location." +msgstr "Vous n'êtes pas autorisé à accéder à cet emplacement." + +msgid "The group identifier should be a valid UUID." +msgstr "L'identifiant du groupe doit être un UUID valide." + +msgid "The group does not exist or has been already deleted." +msgstr "Le groupe n'existe pas ou a déjà été supprimé." + +msgid "The group cannot be deleted." +msgstr "Le groupe ne peut pas être supprimé." + +msgid "The permissions identifiers must be valid UUID." +msgstr "Les identifiants de permissions doivent être des UUID valides." + +msgid "The group id is not valid." +msgstr "L'identifiant du groupe n'est pas valide." + +msgid "The group does not exist." +msgstr "Le groupe n'existe pas." + +msgid "All checks ran successfully!" +msgstr "Toutes les vérifications ont été exécutées avec succès !" + +msgid "OK" +msgstr "OK" + +msgid "The identifier should be a valid UUID." +msgstr "L'identifiant doit être un UUID valide." + +msgid "The resource type does not exist." +msgstr "Le type de ressource n'existe pas." + +msgid "The resource has been added successfully." +msgstr "La ressource a été ajoutée avec succès." + +msgid "The resource has been deleted successfully." +msgstr "La ressource a été supprimée avec succès." + +msgid "You do not have the permission to delete this resource." +msgstr "Vous n'avez pas la permission de supprimer cette ressource." + +msgid "Could not delete the resource." +msgstr "Impossible de supprimer la ressource." + +msgid "The resource has been updated successfully." +msgstr "La ressource a été mise à jour avec succès." + +msgid "The secret does not exist." +msgstr "Le secret n'existe pas." + +msgid "The key provided does not belong to given user." +msgstr "La clé fournie n'appartient pas à l'utilisateur donné." + +msgid "The recovery was completed successfully." +msgstr "La récupération a été terminée avec succès." + +msgid "The user identifier should be a valid UUID." +msgstr "L'identifiant de l'utilisateur doit être un UUID valide." + +msgid "The user does not exist, has not completed the setup or was deleted." +msgstr "L'utilisateur n'existe pas, n'a pas terminé la configuration ou a été supprimé." + +msgid "The user does not exist or is not active." +msgstr "L'utilisateur n'existe pas ou n'est pas actif." + +msgid "The authentication token is not valid." +msgstr "Le jeton d'authentification n'est pas valide." + +msgid "The token is expired." +msgstr "Le jeton a expiré." + +msgid "The OpenPGP key data is not valid." +msgstr "Les données de la clé OpenPGP ne sont pas valides." + +msgid "The setup was completed successfully." +msgstr "La configuration a été terminée avec succès." + +msgid "An authentication token should be provided." +msgstr "Un jeton d'authentification doit être fourni." + +msgid "The authentication token should be a valid UUID." +msgstr "Le jeton d'authentification doit être un UUID valide." + +msgid "The authentication token is not valid or has expired." +msgstr "Le jeton d'authentification n'est pas valide ou a expiré." + +msgid "The user does not exist, is already active or has been deleted." +msgstr "L'utilisateur n'existe pas, est déjà actif ou a été supprimé." + +msgid "An OpenPGP key must be provided." +msgstr "Une clé OpenPGP doit être fournie." + +msgid "A valid OpenPGP key must be provided." +msgstr "Une clé OpenPGP valide doit être fournie." + +msgid "The token should be a valid UUID." +msgstr "Le jeton doit être un UUID valide." + +msgid "The user does not exist or is active." +msgstr "L'utilisateur n'existe pas ou est actif." + +msgid "You are not authorized to share this resource." +msgstr "Vous n'êtes pas autorisé à partager cette ressource." + +msgid "Only administrators can add new users." +msgstr "Seulement les administrateurs peuvent ajouter de nouveaux utilisateurs." + +msgid "The user was successfully added. This user now need to complete the setup." +msgstr "L'utilisateur a été ajouté avec succès. Cet utilisateur doit maintenant terminer la configuration." + +msgid "The user can be deleted." +msgstr "L'utilisateur peut être supprimé." + +msgid "The user has been deleted successfully." +msgstr "L'utilisateur a été supprimé avec succès." + +msgid "You are not allowed to delete yourself." +msgstr "Vous n'êtes pas autorisé à vous supprimer vous-même." + +msgid "The user does not exist or has been already deleted." +msgstr "L'utilisateur n'existe pas ou a déjà été supprimé." + +msgid "The user cannot be deleted." +msgstr "L'utilisateur ne peut pas être supprimé." + +msgid "The groups users identifiers must be valid UUID." +msgstr "Les identifiants des utilisateurs de groupes doivent être des UUID valides." + +msgid "The user does not exist or has been deleted." +msgstr "L'utilisateur n'existe pas ou a été supprimé." + +msgid "Could not validate user data." +msgstr "Impossible de valider les données de l'utilisateur." + +msgid "Could not find the user data after save. Maybe it has been deleted in the meantime." +msgstr "Impossible de trouver les données de l'utilisateur après enregistrement. Peut-être a-t-il été supprimé entre-temps." + +msgid "The user has been updated successfully." +msgstr "L'utilisateur a été mis à jour avec succès." + +msgid "Some user data should be provided." +msgstr "Certaines données utilisateur doivent être fournies." + +msgid "Updating the OpenPGP key is not allowed." +msgstr "Mettre à jour la clé OpenPGP n'est pas autorisé." + +msgid "Updating the groups is not allowed." +msgstr "Mettre à jour les groupes n'est pas autorisé." + +msgid "You are not authorized to edit the role." +msgstr "Vous n'êtes pas autorisé à modifier le rôle." + +msgid "Only guest are allowed to recover an account. Please logout first." +msgstr "Seulement les invités sont autorisés à récupérer un compte. Veuillez d'abord vous déconnecter." + +msgid "Recovery process started, check your email." +msgstr "Le processus de récupération a commencé, vérifiez vos e-mails." + +msgid "Please provide a valid email address." +msgstr "Veuillez fournir une adresse e-mail valide." + +msgid "This user does not exist or has been deleted." +msgstr "Cet utilisateur n'existe pas ou a été supprimé." + +msgid "Please register and complete the setup first." +msgstr "Veuillez d'abord vous inscrire et terminer la configuration." + +msgid "Please contact your administrator." +msgstr "Veuillez contacter votre administrateur." + +msgid "Registration is not opened to public. Please contact your administrator." +msgstr "L'inscription n'est pas ouverte au public. Veuillez contacter votre administrateur." + +msgid "Only guest are allowed to register." +msgstr "Seuls les invités sont autorisés à s'inscrire." + +msgid "The user identifier should be a valid UUID or \"me\"." +msgstr "L'identifiant de l'utilisateur doit être un UUID valide ou \"me\"." + +msgid "The user does not exist." +msgstr "L'utilisateur n'existe pas." + +msgid "The identifier should not be empty." +msgstr "L'identifiant ne doit pas être vide." + +msgid "A token is required." +msgstr "Un jeton est requis." + +msgid "The token should not be empty." +msgstr "Le jeton ne doit pas être vide." + +msgid "The type should be one of the following: {0}." +msgstr "Le type doit être l'un des suivants : {0}." + +msgid "A type is required." +msgstr "Un type est requis." + +msgid "The type should not be empty." +msgstr "Le type ne doit pas être vide." + +msgid "A user identifier is required." +msgstr "Un identifiant d'utilisateur est requis." + +msgid "The user identifier should not be empty." +msgstr "L'identifiant d'utilisateur ne doit pas être vide." + +msgid "The active status should be a valid boolean." +msgstr "Le statut actif doit être un booléen valide." + +msgid "An active status is required" +msgstr "Un statut actif est requis" + +msgid "It is not possible to create an authentication token for this user." +msgstr "Il n'est pas possible de créer un jeton d'authentification pour cet utilisateur." + +msgid "A file is required." +msgstr "Un fichier est requis." + +msgid "The file should not be empty" +msgstr "Le fichier ne doit pas être vide" + +msgid "The file mime type should be one of the following: {0}." +msgstr "Le type de fichier Mime doit être l'un des suivants : {0}." + +msgid "The file extension should be one of the following: {0}." +msgstr "L'extension de fichier devrait être l'une des suivantes: {0}." + +msgid "The file is not valid, or exceeds max size of {0} bytes." +msgstr "Le fichier n'est pas valide ou dépasse la taille maximale de {0} octets." + +msgid "Could not save the data in {0} format." +msgstr "Impossible d'enregistrer les données au format {0}." + +msgid "The parent comment identifier should be a valid UUID." +msgstr "L'identifiant du commentaire parent doit être un UUID valide." + +msgid "The commented object type should be one of the following: {0}." +msgstr "Le type d'objet commenté devrait être l'un des suivants : {0}." + +msgid "The commented object type is required." +msgstr "Le type d'objet commenté est requis." + +msgid "The commented object type should not be empty." +msgstr "Le type d'objet commenté ne doit pas être vide." + +msgid "The commented object identifier should be a valid UUID." +msgstr "L'identifiant de l'objet commenté doit être un UUID valide." + +msgid "The commented object identifier is required." +msgstr "L'identifiant de l'objet commenté est requis." + +msgid "The commented object identifier should not be empty." +msgstr "L'identifiant de l'objet commenté ne doit pas être vide." + +msgid "The content should be a valid UTF8 string." +msgstr "Le contenu doit être une chaîne UTF8 valide." + +msgid "A content is required" +msgstr "Un contenu est requis" + +msgid "The content should not be empty" +msgstr "Le contenu ne doit pas être vide" + +msgid "The content length should be between {0} and {1} characters." +msgstr "La longueur du contenu doit être comprise entre {0} et {1} caractères." + +msgid "The identifier of the user who created the comment should be a valid UUID." +msgstr "L'identifiant de l'utilisateur qui a créé le commentaire doit être un UUID valide." + +msgid "The identifier of the user who created the comment is required." +msgstr "L'identifiant de l'utilisateur qui a créé le commentaire est requis." + +msgid "The identifier of the user who created the comment should not be empty." +msgstr "L'identifiant de l'utilisateur qui a créé le commentaire ne doit pas être vide." + +msgid "The identifier of the user who modified the comment should be a valid UUID." +msgstr "L'identifiant de l'utilisateur qui a modifié le commentaire doit être un UUID valide." + +msgid "The identifier of the user who modified the comment required." +msgstr "L'identifiant de l'utilisateur qui a modifié le commentaire requis." + +msgid "The identifier of the user who modified the comment should not be empty." +msgstr "L'identifiant de l'utilisateur qui a modifié le commentaire ne doit pas être vide." + +msgid "Access denied." +msgstr "Accès refusé." + +msgid "The parent comment does not exist." +msgstr "Le commentaire parent n'existe pas." + +msgid "The user cannot update this comment." +msgstr "L'utilisateur ne peut mettre à jour ce commentaire." + +msgid "The user cannot delete this comment." +msgstr "L'utilisateur ne peut pas supprimer ce commentaire." + +msgid "The commented object type does not exist." +msgstr "Le type d'objet commenté n'existe pas." + +msgid "The favorite object type should be one of the following: {0}." +msgstr "Le type d'objet favori devrait être l'un des éléments suivants : {0}." + +msgid "The favorite object type is required." +msgstr "Le type de l'objet favori est requis." + +msgid "The favorite object type should not be empty" +msgstr "Le type d'objet favori ne doit pas être vide" + +msgid "The favorite object identifier should be a valid UUID." +msgstr "L'identifiant de l'objet favori doit être un UUID valide." + +msgid "The favorite object identifier is required." +msgstr "L'identifiant de l'objet favori est requis." + +msgid "The favorite object identifier should not be empty." +msgstr "L'identifiant de l'objet favori ne doit pas être vide." + +msgid "The resource is already marked as favorite." +msgstr "La ressource est déjà marquée comme favorite." + +msgid "The user cannot delete this favorite." +msgstr "L'utilisateur ne peut pas supprimer ce favori." + +msgid "The armored key should be a valid ASCII string." +msgstr "La clé blindée doit être une chaîne ASCII valide." + +msgid "An armored key is required." +msgstr "Une clé blindée est requise." + +msgid "The armored key should not be empty." +msgstr "La clé blindée ne doit pas être vide." + +msgid "The armored key should be a valid ASCII-armored OpenPGP key." +msgstr "La clé blindée doit être une clé gpg valide à blindage ASCII." + +msgid "A length is required." +msgstr "Une longueur est requise." + +msgid "The identifier should be a valid BMP-UTF8 string." +msgstr "L'identifiant doit être une chaîne BMP-UTF8 valide." + +msgid "The key identifier should be a valid ASCII string." +msgstr "L'identifiant de la clé doit être une chaîne ASCII valide." + +msgid "A key identifier is required." +msgstr "Un identifiant de clé est requis." + +msgid "The key identifier should not be empty." +msgstr "L'identifiant de la clé ne doit pas être vide." + +msgid "The key identifier should be a string of 8 hexadecimal characters." +msgstr "L'identifiant de la clé doit être une chaîne de 8 caractères hexadécimaux." + +msgid "The fingerprint should be a valid ASCII string." +msgstr "L'empreinte digitale doit être une chaîne ASCII valide." + +msgid "A fingerprint is required" +msgstr "Une empreinte digitale est requise" + +msgid "The fingerprint should not be empty" +msgstr "L'empreinte digitale ne doit pas être vide" + +msgid "The fingerprint should be a string of 40 hexadecimal characters." +msgstr "L'empreinte doit être une chaîne de 40 caractères hexadécimaux." + +msgid "The type should be a valid ASCII string." +msgstr "Le type doit être une chaîne ASCII valide." + +msgid "A type is required" +msgstr "Un type est requis" + +msgid "The type should not be empty" +msgstr "Le type ne doit pas être vide" + +msgid "The type should be one of the following: RSA, DSA, ECC, ELGAMAL, ECDSA, DH." +msgstr "Le type doit être l'un des suivants : RSA, DSA, ECC, ELGAMAL, ECDSA, DH." + +msgid "The expiry should be a valid date." +msgstr "La date d'expiration doit être une date valide." + +msgid "The key should not already be expired." +msgstr "La clé ne doit pas déjà être expirée." + +msgid "The creation date should be a valid date." +msgstr "La date de création doit être une date valide." + +msgid "A creation date is required." +msgstr "Une date de création est requise." + +msgid "The creation date should not be empty." +msgstr "La date de création ne doit pas être vide." + +msgid "The creation date should be set in the past." +msgstr "La date de création doit être définie dans le passé." + +msgid "The deleted status should be a valid boolean." +msgstr "Le statut supprimé doit être un booléen valide." + +msgid "A deleted status is required" +msgstr "Un statut supprimé est requis" + +msgid "A user identifier is required" +msgstr "Un identifiant d'utilisateur est requis" + +msgid "The user identifier should not be empty" +msgstr "L'identifiant de l'utilisateur ne doit pas être vide" + +msgid "Could not parse the key info." +msgstr "Impossible d'analyser les informations de la clé." + +msgid "The name should be a valid UTF8 string." +msgstr "Le nom doit être une chaîne UTF8 valide." + +msgid "The name length should be maximum {0} characters." +msgstr "La longueur du nom doit être de {0} caractères au maximum." + +msgid "A name is required." +msgstr "Un nom est requis." + +msgid "The name should not be empty." +msgstr "Le nom ne doit pas être vide." + +msgid "The identifier of the user who created the group should be a valid UUID." +msgstr "L'identifiant de l'utilisateur qui a créé le groupe doit être un UUID valide." + +msgid "The identifier of the user who created the group is required." +msgstr "L'identifiant de l'utilisateur qui a créé le groupe est requis." + +msgid "The identifier of the user who created the group should not be empty." +msgstr "L'identifiant de l'utilisateur qui a créé le groupe ne doit pas être vide." + +msgid "The identifier of the user who modified the group should be a valid UUID." +msgstr "L'identifiant de l'utilisateur qui a modifié le groupe doit être un UUID valide." + +msgid "The identifier of the user who modified the group is required." +msgstr "L'identifiant de l'utilisateur qui a modifié le groupe est requis." + +msgid "The identifier of the user who modified the group should not be empty." +msgstr "L'identifiant de l'utilisateur qui a modifié le groupe ne doit pas être vide." + +msgid "The name is already used by another group." +msgstr "Le nom est déjà utilisé par un autre groupe." + +msgid "A group manager should be provided." +msgstr "Un gestionnaire de groupe doit être fourni." + +msgid "The group should not be soft deleted." +msgstr "Le groupe ne doit pas être supprimé de manière soft." + +msgid "The group should not be sole owner of shared content, transfer the ownership to other users." +msgstr "Le groupe ne doit pas être le seul propriétaire de contenu partagé, transférer la propriété à d'autres utilisateurs." + +msgid "Could not validate group data." +msgstr "Impossible de valider les données du groupe." + +msgid "Could not delete the group {0}, please try again later." +msgstr "Impossible de supprimer le groupe {0}, veuillez réessayer plus tard." + +msgid "A group identifier is required." +msgstr "Un identifiant de groupe est requis." + +msgid "The group identifier should not be empty." +msgstr "L'identifiant du groupe ne doit pas être vide." + +msgid "The group manager status should be a valid boolean." +msgstr "Le statut de gestionnaire de groupe doit être un booléen valide." + +msgid "A group manager status is required." +msgstr "Un statut de gestionnaire de groupe est requis." + +msgid "The group manager status should not be empty." +msgstr "Le statut de gestionnaire de groupe ne doit pas être vide." + +msgid "The user is already member of this group." +msgstr "L'utilisateur est déjà membre de ce groupe." + +msgid "The property length should be maximum {0} characters." +msgstr "La longueur de la propriété doit être de {0} caractères maximum." + +msgid "A property is required." +msgstr "Une propriété est requise." + +msgid "The property should not be empty." +msgstr "La propriété ne doit pas être vide." + +msgid "The property should be an alphabetical string and only point is accepted as special characters." +msgstr "La propriété doit être une chaîne alphabétique et seul le point est accepté comme caractère spécial." + +msgid "The property identifier should be a valid UUID." +msgstr "L'identifiant de la propriété doit être un UUID valide." + +msgid "A property identifier is required." +msgstr "Un identifiant de propriété est requis." + +msgid "The property identifier should not be empty." +msgstr "L'identifiant de la propriété ne doit pas être vide." + +msgid "The value should be a valid UTF8 string." +msgstr "La valeur doit être une chaîne UTF8 valide." + +msgid "The value length should be maximum {0} characters." +msgstr "La longueur de la valeur doit être de {0} caractères maximum." + +msgid "A value is required." +msgstr "Une valeur est requise." + +msgid "The value should not be empty." +msgstr "La valeur ne doit pas être vide." + +msgid "This property id is already in use." +msgstr "Cet identifiant de propriété est déjà utilisé." + +msgid "Only admin can create or update organization settings." +msgstr "Seul l'administrateur peut créer ou mettre à jour les paramètres de l'organisation." + +msgid "This is not a valid setting." +msgstr "Ce n'est pas un paramètre valide." + +msgid "The type of the access control object should be one of the following: {0}." +msgstr "Le type de l'objet à accès contrôlé doit être l'un des suivants : {0}." + +msgid "The type of the access control object is required." +msgstr "Le type de l'objet à accès contrôlé est requis." + +msgid "The type of the access control object should not be empty." +msgstr "Le type de l'objet à accès contrôlé ne doit pas être vide." + +msgid "The identifier of the access control object should be a valid UUID." +msgstr "L'identifiant de l'objet à accès contrôlé doit être un UUID valide." + +msgid "The identifier of the access control object is required." +msgstr "L'identifiant de l'objet à accès contrôlé est requis." + +msgid "The identifier of the access control object should not be empty." +msgstr "L'identifiant de l'objet à accès contrôlé ne doit pas être vide." + +msgid "The access request object type should be one of the following: {0}." +msgstr "Le type de l'objet de demande d'accès doit être l'un des suivants : {0}." + +msgid "The type of the access request object is required." +msgstr "Le type de l'objet requérant un accès est requis." + +msgid "The access request object type should not be empty." +msgstr "Le type de l'objet de demande d'accès ne doit pas être vide." + +msgid "The identifier of the access request object should be a valid UUID." +msgstr "L'identifiant de l'objet requérant un accès doit être un UUID valide." + +msgid "The identifier of the access request object is required." +msgstr "L'identifiant de l'objet requérant un accès est requis." + +msgid "The identifier of the access request object should not be empty." +msgstr "L'identifiant de l'objet requérant un accès ne doit pas être vide." + +msgid "A permission already exists for the given access control object and access request object." +msgstr "Une permission existe déjà pour l'objet à accès contrôlé et l'objet requérant un accès." + +msgid "The access control object does not exist." +msgstr "L'objet à accès contrôlé n'existe pas." + +msgid "The access request object does not exist." +msgstr "L'objet requérant un accès n'existe pas." + +msgid "A first name is required." +msgstr "Un prénom est requis." + +msgid "The first name should not be empty." +msgstr "Le prénom ne doit pas être vide." + +msgid "The first name should be a valid BMP-UTF8 string." +msgstr "Le prénom doit être une chaîne BMP-UTF8 valide." + +msgid "The first name length should be maximum {0} characters." +msgstr "La longueur du prénom doit être de {0} caractères au maximum." + +msgid "A last name is required." +msgstr "Un nom est requis." + +msgid "The last name should not be empty." +msgstr "Le nom ne doit pas être vide." + +msgid "The last name should be a valid BMP-UTF8 string." +msgstr "Le nom doit être une chaîne BMP-UTF8 valide." + +msgid "The last name length should be maximum {0} characters." +msgstr "La longueur du nom de famille doit être de {0} caractères au maximum." + +msgid "The name should be a valid BMP-UTF8 string." +msgstr "Le nom doit être une chaîne BMP-UTF8 valide." + +msgid "The slug should be a valid BMP-UTF8 string." +msgstr "Le slug doit être une chaîne BMP-UTF8 valide." + +msgid "A slug is required." +msgstr "Un slug est requis." + +msgid "The slug length should be maximum {0} characters." +msgstr "La longueur du slug doit être de {0} caractères maximum." + +msgid "The slug should not be empty" +msgstr "Le slug ne doit pas être vide" + +msgid "The description should be a valid BMP-UTF8 string." +msgstr "La description doit être une chaîne BMP-UTF8 valide." + +msgid "The description length should be maximum {0} characters." +msgstr "La longueur de la description doit être de {0} caractères au maximum." + +msgid "The definition should be a valid BMP-UTF8 string." +msgstr "La définition doit être une chaîne BMP-UTF8 valide." + +msgid "A definition is required." +msgstr "Une définition est requise." + +msgid "The definition should not be empty." +msgstr "La définition ne doit pas être vide." + +msgid "The message should be valid JSON message." +msgstr "Le message doit être un message JSON valide." + +msgid "A resource type already exists with this slug." +msgstr "Un type de ressource existe déjà avec ce slug." + +msgid "A resource type already exists with this definition." +msgstr "Un type de ressource existe déjà avec cette définition." + +msgid "The username should be a valid UTF8 string." +msgstr "Le nom d'utilisateur doit être une chaîne UTF8 valide." + +msgid "The username length should be maximum {0} characters." +msgstr "La longueur du nom d'utilisateur doit contenir au maximum {0} caractères." + +msgid "The uri should be a valid BMP-UTF8 string." +msgstr "L'uri doit être une chaîne BMP-UTF8 valide." + +msgid "The uri length should be maximum {0} characters." +msgstr "La longueur de l'uri doit être de {0} caractères au maximum." + +msgid "The description should be a valid UTF8 string." +msgstr "La description doit être une chaîne UTF8 valide." + +msgid "The deleted status should not be empty" +msgstr "Le statut supprimé ne doit pas être vide" + +msgid "The identifier of the user who created the resource should be a valid UUID." +msgstr "L'identifiant de l'utilisateur qui a créé la ressource doit être un UUID valide." + +msgid "The identifier of the user who created the resource is required." +msgstr "L'identifiant de l'utilisateur qui a créé la ressource est requis." + +msgid "The identifier of the user who created the resource should not be empty." +msgstr "L'identifiant de l'utilisateur qui a créé la ressource ne doit pas être vide." + +msgid "The identifier of the user who last modified the resource should be a valid UUID." +msgstr "L'identifiant de l'utilisateur qui a modifié la dernière ressource doit être un UUID valide." + +msgid "The identifier of the user who last modified the resource required." +msgstr "L'identifiant de l'utilisateur qui a modifié la dernière ressource requise." + +msgid "The identifier of the user who last modified the resource should not be empty." +msgstr "L'identifiant de l'utilisateur qui a modifié la dernière ressource ne doit pas être vide." + +msgid "The resource type identifier should be a valid UUID." +msgstr "L'identifiant du type de ressource doit être un UUID valide." + +msgid "A resource type identifier is required." +msgstr "Un identifiant de type de ressource est requis." + +msgid "The permissions are required." +msgstr "Les permissions sont requises." + +msgid "The permissions should not be empty." +msgstr "Les permissions ne doivent pas être vides." + +msgid "The permissions should contain only the permission of the owner." +msgstr "Les permissions ne doivent contenir que la permission du propriétaire." + +msgid "The owner secret is required." +msgstr "Le secret du propriétaire est requis." + +msgid "The secrets should not be empty." +msgstr "Les secrets ne doivent pas être vides." + +msgid "The secrets should contain only the secret of the owner." +msgstr "Les secrets ne doivent contenir que le secret du propriétaire." + +msgid "The permissions should contain the owner permission." +msgstr "Les permissions doivent contenir la permission du propriétaire." + +msgid "The secrets should contain the owner secret." +msgstr "Les secrets doivent contenir le secret du propriétaire." + +msgid "The secrets should contain the secrets of all the users having access to the resource." +msgstr "Les secrets doivent contenir les secrets de tous les utilisateurs ayant accès à la ressource." + +msgid "The permissions should contain at least the owner permission." +msgstr "Les permissions doivent contenir au moins la permission du propriétaire." + +msgid "The resource should not be already soft deleted." +msgstr "La ressource ne doit pas être déjà supprimée en mode soft." + +msgid "The user cannot delete this resource." +msgstr "L'utilisateur ne peut pas supprimer cette ressource." + +msgid "The name should be a valid ASCII string." +msgstr "Le nom doit être une chaîne ASCII valide." + +msgid "A role already exists for the given name." +msgstr "Un rôle existe déjà pour le nom donné." + +msgid "The role name should be from the list of allowed role names." +msgstr "Le nom du rôle doit être issu de la liste des noms de rôles autorisés." + +msgid "The resource identifier is required." +msgstr "L'identifiant de la ressource est requis." + +msgid "The resource identifier should not be empty." +msgstr "L'identifiant de la ressource ne doit pas être vide." + +msgid "The message should be a valid ASCII string." +msgstr "Le message doit être une chaîne ASCII valide." + +msgid "The message should be a valid ASCII-armored gpg message." +msgstr "Le message doit être un message gpg à blindage ASCII valide." + +msgid "A message is required." +msgstr "Un message est requis." + +msgid "The message should not be empty." +msgstr "Le message ne doit pas être vide." + +msgid "A secret already exists for the given user and resource." +msgstr "Un secret existe déjà pour l'utilisateur et la ressource donnés." + +msgid "The user identifier by should be a valid UUID." +msgstr "L'identifiant d'utilisateur doit être un UUID valide." + +msgid "A username is required." +msgstr "Un nom d'utilisateur est requis." + +msgid "The username should be a valid email address." +msgstr "Le nom d'utilisateur doit être une adresse e-mail valide." + +msgid "The role identifier should be a valid UUID." +msgstr "L'identifiant du rôle doit être un UUID valide." + +msgid "A role identifier is required." +msgstr "Un identifiant de rôle est requis." + +msgid "The profile should not be empty." +msgstr "Le profil ne doit pas être vide." + +msgid "The username should not be empty." +msgstr "Le nom d'utilisateur ne doit pas être vide." + +msgid "The username length should be maximum 255 characters." +msgstr "La longueur du nom d'utilisateur doit être de 255 caractères au maximum." + +msgid "The username is already in use." +msgstr "Le nom d'utilisateur est déjà utilisé." + +msgid "The role identifier does not exist." +msgstr "L'identifiant du rôle n'existe pas." + +msgid "The user should not be sole owner of shared content, transfer the ownership to other users." +msgstr "L'utilisateur ne doit pas être le seul propriétaire de contenu partagé, transférer la propriété à d'autres utilisateurs." + +msgid "The user should not be sole group manager of group(s), transfer the management to other users." +msgstr "L'utilisateur ne doit pas être le seul gestionnaire de groupe(s), transférer la gestion à d'autres utilisateurs." + +msgid "Could not delete the user {0}, please try again later." +msgstr "Impossible de supprimer l'utilisateur {0}, veuillez réessayer plus tard." + +msgid "{0} just activated their account on passbolt" +msgstr "{0} vient d'activer son compte sur passbolt" + +msgid "{0} commented on {1}" +msgstr "{0} a commenté {1}" + +msgid "{0} deleted the group {1}" +msgstr "{0} a supprimé le groupe {1}" + +msgid "{0} updated the group {1}" +msgstr "{0} a mis à jour le groupe {1}" + +msgid "{0} added you to the group {1}" +msgstr "{0} vous a ajouté au groupe {1}" + +msgid "{0} requested you to add members to {1}" +msgstr "{0} vous a demandé d'ajouter des membres à {1}" + +msgid "{0} removed you from the group {1}" +msgstr "{0} vous a retiré du groupe {1}" + +msgid "{0} updated your membership in the group {1}" +msgstr "{0} a mis à jour votre adhésion au groupe {1}" + +msgid "Your account recovery, {0}!" +msgstr "Récupération de votre compte, {0}!" + +msgid "You added the password {0}" +msgstr "Vous avez ajouté le mot de passe {0}" + +msgid "{0} deleted the password {1}" +msgstr "{0} a supprimé le mot de passe {1}" + +msgid "{0} edited the password {1}" +msgstr "{0} a édité le mot de passe {1}" + +msgid "{0} shared the password {1}" +msgstr "{0} a partagé le mot de passe {1}" + +msgid "{0} deleted user {1}" +msgstr "{0} a supprimé l'utilisateur {1}" + +msgid "Welcome to passbolt, {0}!" +msgstr "Bienvenue sur passbolt, {0}!" + +msgid "{0} updated your memberships in several groups" +msgstr "{0} a mis à jour vos adhésions à plusieurs groupes" + +msgid "Your membership in several groups changed in passbolt" +msgstr "Votre adhésion à plusieurs groupes a été modifiée dans passbolt" + +msgid "{0} deleted several groups" +msgstr "{0} a supprimé plusieurs groupes" + +msgid "Several groups were deleted in passbolt" +msgstr "Plusieurs groupes ont été supprimés de passbolt" + +msgid "{0} has made changes on several resources" +msgstr "{0} a apporté des modifications à plusieurs ressources" + +msgid "Multiple passwords have been changed in passbolt" +msgstr "Plusieurs mots de passe ont été modifiés dans passbolt" + +msgid "{0} shared several items with you" +msgstr "{0} a partagé plusieurs éléments avec vous" + +msgid "Multiple passwords have been shared with you in passbolt" +msgstr "Plusieurs mots de passe ont été partagés avec vous dans passbolt" + +msgid "The purify subject setting should be a boolean." +msgstr "Le réglage de la purification du sujet doit être un booléen." + +msgid "The show comment setting should be a boolean." +msgstr "Le réglage de la visualisation du commentaire doit être un booléen." + +msgid "The show description setting should be a boolean." +msgstr "Le réglage de la visualisation de la description doit être un booléen." + +msgid "The show secret setting should be a boolean." +msgstr "Le réglage de la visualisation du secret doit être un booléen." + +msgid "The show uri setting should be a boolean." +msgstr "Le réglage de la visualisation de l'uri doit être un booléen." + +msgid "The show username setting should be a boolean." +msgstr "Le réglage de la visualisation du nom d'utilisateur doit être un booléen." + +msgid "The send on user setup completed setting should be a boolean." +msgstr "Le réglage de l'envoi lorsque la configuration de l'utilisateur est terminée doit être un booléen." + +msgid "The send on comment added setting should be a boolean." +msgstr "Le réglage de l'envoi lorsqu'un commentaire est ajouté doit être un booléen." + +msgid "The send on group deleted setting should be a boolean." +msgstr "Le réglage de l'envoi lorsqu'un groupe est supprimé doit être un booléen." + +msgid "The send on group user added setting should be a boolean." +msgstr "Le réglage de l'envoi lorsqu'un utilisateur est ajouté doit être un booléen." + +msgid "The send on group user deleted setting should be a boolean." +msgstr "Le réglage de l'envoi lorsqu'un utilisateur est supprimé doit être un booléen." + +msgid "The send on group user updated setting should be a boolean." +msgstr "Le réglage de l'envoi lorsqu'un utilisateur est mis à jour doit être un booléen." + +msgid "The send on group manager updated setting should be a boolean." +msgstr "Le réglage de l'envoi lorsqu'un gestionnaire de groupe est mis à jour doit être un booléen." + +msgid "The send on password created setting should be a boolean." +msgstr "Le réglage de l'envoi lorsqu'un mot de passe est créé doit être un booléen." + +msgid "The send on password shared setting should be a boolean." +msgstr "Le réglage de l'envoi lorsqu'un mot de passe est partagé doit être un booléen." + +msgid "The send on password updated setting should be a boolean." +msgstr "Le réglage de l'envoi lorsqu'un mot de passe est partagé doit être un booléen." + +msgid "The send on password deleted setting should be a boolean." +msgstr "Le réglage de l'envoi lorsqu'un mot de passe est supprimé doit être un booléen." + +msgid "The send on user created setting should be a boolean." +msgstr "Le réglage de l'envoi lorsqu'un utilisateur est créé doit être un booléen." + +msgid "The send on user recovered setting should be a boolean." +msgstr "Le réglage de l'envoi lorsqu'un utilisateur a récupéré son compte doit être un booléen." + +msgid "Validation failed for authenticationToken {0}. {1}" +msgstr "Échec de la validation pour le jeton d'authentification {0}. {1}" + +msgid "Validation success for authenticationToken {0}" +msgstr "Validation du jeton d'authentification {0} réussie" + +msgid "Could not save the avatar in the {0} file." +msgstr "Impossible d'enregistrer l'avatar dans le fichier {0}." + +msgid "The avatar id is not valid." +msgstr "L'identifiant de l'avatar n'est pas valide." + +msgid "Validation failed for comment {0}. {1}" +msgstr "Échec de validation du commentaire {0}. {1}" + +msgid "Validation success for comment {0}" +msgstr "Validation du commentaire {0} réussie" + +msgid "Validation failed for favorite {0}. {1}" +msgstr "Échec de la validation pour le favori {0}. {1}" + +msgid "Validation success for favorite {0}" +msgstr "Validation réussie pour le favori {0}" + +msgid "Validation success for key {0}" +msgstr "Validation de la clé {0} réussie" + +msgid "Validation failed for key {0}. {1}" +msgstr "Échec de validation de la clé {0}. {1}" + +msgid "Encryption success for key {0}" +msgstr "Chiffrement réussi pour la clé {0}" + +msgid "Failed to encrypt with key {0}. {1}" +msgstr "Impossible de chiffrer avec la clé {0}. {1}" + +msgid "Validation failed for group {0}. {1}" +msgstr "Échec la validation du groupe {0}. {1}" + +msgid "Validation success for group {0}" +msgstr "Validation du groupe {0} réussie" + +msgid "Could not validate group user data." +msgstr "Impossible de valider les données d'utilisateur de groupe." + +msgid "The group user does not exist." +msgstr "L'utilisateur de groupe n'existe pas." + +msgid "At least one group manager should be provided." +msgstr "Au moins un gestionnaire de groupe doit être fourni." + +msgid "Could not validate permission data." +msgstr "Impossible de valider les données de permission." + +msgid "Validation failed for permission {0}. {1}" +msgstr "Échec de validation de la permission {0}. {1}" + +msgid "Validation success for permission {0}" +msgstr "Validation de la permission {0} réussie" + +msgid "Could not validate permissions data." +msgstr "Impossible de valider les données des permissions." + +msgid "Validation failed for profile {0}. {1}" +msgstr "Échec de validation du profil {0}. {1}" + +msgid "Validation success for profile {0}" +msgstr "Validation du profil {0} réussie" + +msgid "Could not validate resource data." +msgstr "Impossible de valider les données de la ressource." + +msgid "An unexpected error occured, please contact an administrator." +msgstr "Une erreur inattendue s'est produite, veuillez contacter un administrateur." + +msgid "The server returned the following error:" +msgstr "Le serveur a retourné l'erreur suivante:" + +msgid "Validation failed for resource {0}. {1}" +msgstr "Échec de validation de la ressource {0}. {1}" + +msgid "Validation success for resource {0}" +msgstr "Validation de la ressource {0} réussie" + +msgid "You are not allowed to update this resource." +msgstr "Vous n'êtes pas autorisé à mettre à jour cette ressource." + +msgid "Additional resource types are not enabled on this server." +msgstr "Les types de ressources additionnelles ne sont pas activés sur ce serveur." + +msgid "There should be at least 4 roles" +msgstr "Il devrait y avoir au moins 4 rôles" + +msgid "Validation failed for role {0}. {1}" +msgstr "Échec de la validation du rôle {0}. {1}" + +msgid "Validation success for role {0}" +msgstr "Validation du rôle {0} réussie" + +msgid "Could not validate secret data." +msgstr "Impossible de valider les données de secret." + +msgid "Validation failed for secret {0}. {1}" +msgstr "Échec de validation du secret {0}. {1}" + +msgid "Validation success for secret {0}" +msgstr "Validation du secret {0} réussie" + +msgid "Could not validate secrets data." +msgstr "Impossible de valider les données des secrets." + +msgid "The secrets of all the users having access to the resource are required." +msgstr "Les secrets de tous les utilisateurs ayant accès à la ressource sont requis." + +msgid "Validation failed for user {0}. {1}" +msgstr "Échec de validation de l'utilisateur {0}. {1}" + +msgid "Validation success for user {0}" +msgstr "Validation de l'utilisateur {0} réussie" + +msgid "Could not connect to github repository" +msgstr "Impossible de se connecter au dépôt github" + +msgid "Could not read tag information on github repository" +msgstr "Impossible de lire les informations de tag sur le dépôt github" + +msgid "The key {0} cannot be used to encrypt." +msgstr "La clé {0} ne peut pas être utilisée pour chiffrer." + +msgid "The key {0} cannot be used to decrypt." +msgstr "La clé {0} ne peut pas être utilisée pour déchiffrer." + +msgid "Could not use key {0} for signing." +msgstr "Impossible d'utiliser la clé {0} pour signer." + +msgid "Could not parse the OpenPGP public key." +msgstr "Impossible d'analyser la clé publique OpenPGP." + +msgid "Invalid key. No OpenPGP public key package found." +msgstr "Clé invalide. Aucun paquet de clé publique OpenPGP trouvé." + +msgid "Invalid key. No user ID found." +msgstr "Clé invalide. Aucun identifiant utilisateur trouvé." + +msgid "Could not import the OpenPGP key." +msgstr "Impossible d'importer la clé OpenPGP." + +msgid "Could not use the key to sign and encrypt." +msgstr "Impossible d'utiliser la clé pour signer et chiffrer." + +msgid "Could not use the key to encrypt." +msgstr "Impossible d'utiliser la clé pour chiffrer." + +msgid "Expected {0} and got {1}." +msgstr "{0} attendu et obtenu {1}." + +msgid "Decryption failed. Invalid signature." +msgstr "Le déchiffrement a échoué. Signature invalide." + +msgid "The message cannot be verified." +msgstr "Le message ne peut pas être vérifié." + +msgid "Could not sign the text. " +msgstr "Impossible de signer le texte. " + +msgid "The OpenPGP server key defined in the config is not found in the file system." +msgstr "La clé de serveur OpenPGP définie dans la configuration est introuvable sur le système de fichiers." + +msgid "The OpenPGP server key defined in the config cannot be opened." +msgstr "La clé de serveur OpenPGP définie dans la configuration ne peut pas être ouverte." + +msgid "The OpenPGP server key defined on file is not a valid private key." +msgstr "La clé de serveur OpenPGP définie sur le fichier n'est pas une clé privée valide." + +msgid "There is an issue with the OpenPGP server key." +msgstr "Il y a un problème avec la clé de serveur OpenPGP." + +msgid "The fingerprint does not match the one associated with the key on file." +msgstr "L'empreinte ne correspond pas à celle associée à la clé du fichier." + +msgid "No OpenPGP marker found." +msgstr "Aucun marqueur OpenPGP trouvé." + +msgid "This is not a valid OpenPGP armored marker." +msgstr "Ce n'est pas un marqueur OpenPGP valide." + +msgid "The key {0} was not found in the keyring" +msgstr "La clé {0} n'a pas été trouvée dans le trousseau" + +msgid "A value for the theme should be provided." +msgstr "Une valeur pour le thème doit être fournie." + +msgid "This is not a valid theme." +msgstr "Ce n'est pas un thème valide." + +msgid "The setting type should be one of the following: {0}." +msgstr "Le type de configuration doit être l'un des suivants : {0}." + +msgid "A setting type is required." +msgstr "Un type de paramètre est requis." + +msgid "The setting type should not be empty" +msgstr "Le type de configuration ne doit pas être vide" + +msgid "The setting value should be a valid UTF8 string." +msgstr "La valeur de la configuration doit être une chaîne UTF8 valide." + +msgid "The setting value length should be maximum {0} characters." +msgstr "La longueur de la valeur la configuration doit être de {0} caractères maximum." + +msgid "A value setting is required." +msgstr "Une valeur de paramètre est requise." + +msgid "The setting value should not be empty." +msgstr "La valeur de la configuration ne doit pas être vide." + +msgid "The parameter {0} is not set." +msgstr "Le paramètre {0} n'est pas défini." + +msgid "This theme is not supported." +msgstr "Ce thème n'est pas supporté." + +msgid "You are not allowed to access this location." +msgstr "Vous n'êtes pas autorisé à accéder à cet emplacement." + +msgid "The notification settings for the organization were updated." +msgstr "Les paramètres de notification de l'organisation ont été mis à jour." + +msgid "The challenge cannot be decrypted." +msgstr "Le challenge ne peut pas être déchiffré." + +msgid "The challenge is invalid. Deserialization failed." +msgstr "Le challenge est invalide. La désérialisation a échoué." + +msgid "The challenge is invalid. Validation Failed." +msgstr "Le challenge est invalide. La validation a échoué." + +msgid "The config for the server private key fingerprint is not available or incomplete." +msgstr "La configuration de l'empreinte de la clé privée du serveur n'est pas disponible ou incomplète." + +msgid "The config for the server private key passphrase is invalid." +msgstr "La configuration du mot de passe de la clé privée du serveur est invalide." + +msgid "The user id is missing or invalid." +msgstr "L'identifiant de l'utilisateur est manquant ou invalide." + +msgid "The user OpenPGP key does not exist, or is invalid, or has been deleted." +msgstr "La clé OpenPGP de l'utilisateur n'existe pas, est invalide ou a été supprimée." + +msgid "The user signature is invalid." +msgstr "La signature de l'utilisateur est invalide." + +msgid "The user challenge is missing or invalid." +msgstr "Le challenge utilisateur est manquant ou invalide." + +msgid "The version is invalid." +msgstr "La version est invalide." + +msgid "The domain is invalid." +msgstr "Le domaine est invalide." + +msgid "The domain is invalid. Expected: {0} and got {1}" +msgstr "Le domaine n'est pas valide. Attendu : {0} et obtenu {1}" + +msgid "The authentication was a success." +msgstr "L'authentification a été un succès." + +msgid "The authentication failed." +msgstr "L’authentification a échoué." + +msgid "The credentials are missing." +msgstr "Les identifiants sont manquants." + +msgid "The user does not exist or is not active or has been deleted." +msgstr "L'utilisateur n'existe pas ou n'est pas actif ou a été supprimé." + +msgid "The credentials are invalid." +msgstr "Les identifiants sont invalides." + +msgid "An internal error occurred." +msgstr "Une erreur interne est survenue." + +msgid "The verify token is invalid." +msgstr "Le jeton de vérification est invalide." + +msgid "The route {0} is not permitted with JWT authentication." +msgstr "La route {0} n'est pas autorisée avec l'authentification JWT." + +msgid "The key pair for JWT Authentication is not complete. The following file could not be read: {0}." +msgstr "La paire de clés d'authentification JWT n'est pas complète. Le fichier suivant n'a pas pu être lu : {0}." + +msgid "The JWT public key could not be read or is not valid." +msgstr "La clé publique JWT n'a pas pu être lue ou n'est pas valide." + +msgid "The JWT private key should be at least {0} bytes long." +msgstr "La clé privée JWT doit faire au moins {0} octets." + +msgid "The configuration {0} is not correctly set." +msgstr "La configuration {0} n'est pas correctement définie." + +msgid "No active refresh token matching the request could be found." +msgstr "Aucun jeton de rafraîchissement actif correspondant à la requête n'a pu être trouvé." + +msgid "The refresh token provided was already used." +msgstr "Le jeton de rafraîchissement fourni était déjà utilisé." + +msgid "Expired refresh token provided." +msgstr "Jeton de rafraîchissement fourni expiré." + +msgid "The refresh token should be a valid UUID." +msgstr "Le jeton de rafraîchissement doit être un UUID valide." + +msgid "This is not a valid user id." +msgstr "Ce n'est pas un identifiant d'utilisateur valide." + +msgid "Invalid verify token expiry." +msgstr "Expiration de jeton de vérification invalide." + +msgid "Attempt to access an expired verify token." +msgstr "Tentative d'accès à un jeton de vérification expiré." + +msgid "Invalid verify token format." +msgstr "Format de jeton de vérification invalide." + +msgid "Invalid user ID format." +msgstr "Format d'identifiant d'utilisateur invalide." + +msgid "Verify token has been already used in the past." +msgstr "Le jeton de vérification a déjà été utilisé dans le passé." + +msgid "Security warning!" +msgstr "Avertissement de sécurité !" + +msgid "An unknown user with IP: {0} attempted to identify as {1}." +msgstr "Un utilisateur inconnu avec l'IP : {0} a tenté de s'identifier en tant que {1}." + +msgid "This is a potential security issue." +msgstr "Il s'agit d'un problème de sécurité potentiel." + +msgid "Please investigate!" +msgstr "Veuillez vérifier !" + +msgid "An unknown user with IP: {0} attempted to steal your login data." +msgstr "Un utilisateur inconnu avec l'IP: {0} a tenté de voler vos données de connexion." + +msgid "Please get in tough with one of your administrators." +msgstr "Merci de prendre contact avec l'un de vos administrateurs." + +msgid "This is not a valid locale." +msgstr "Ce n'est pas une locale valide." + +msgid "This is not a valid {0}." +msgstr "Ce n'est pas un {0} valide." + +msgid "The action log identifier should be a valid UUID." +msgstr "L'identifiant du journal d'action doit être un UUID valide." + +msgid "The action log identifier should not be empty." +msgstr "L'identifiant du journal d'action ne doit pas être vide." + +msgid "The action identifier should be a valid UUID." +msgstr "L'identifiant de l'action doit être un UUID valide." + +msgid "An action identifier is required." +msgstr "Un identifiant d'action est requis." + +msgid "The context should be a valid ASCII string." +msgstr "Le contexte doit être une chaîne ASCII valide." + +msgid "The context length should be maximum {0} characters." +msgstr "La longueur du contexte doit être de {0} caractères maximum." + +msgid "A context is required." +msgstr "Un contexte est requis." + +msgid "The status should be a valid boolean." +msgstr "Le statut doit être un booléen valide." + +msgid "A status is required." +msgstr "Un statut est requis." + +msgid "Could not validate action log data." +msgstr "Impossible de valider les données du journal d'action." + +msgid "The identifier should be not be empty." +msgstr "L'identifiant ne doit pas être vide." + +msgid "Could not validate action data." +msgstr "Impossible de valider les données de l'action." + +msgid "An action log identifier is required." +msgstr "Un identifiant de journal d'action est requis." + +msgid "The object type should be a valid ASCII string." +msgstr "Le type d'objet doit être une chaîne ASCII valide." + +msgid "The object type length should be maximum {0} characters." +msgstr "La longueur du type d'objet doit être de {0} caractères au maximum." + +msgid "A object type is required." +msgstr "Un type d'objet est requis." + +msgid "The object type should not be empty." +msgstr "Le type d'objet ne doit pas être vide." + +msgid "The object identifier should be a valid UUID." +msgstr "L'identifiant de l'objet doit être un UUID valide." + +msgid "The object identifier should not be empty." +msgstr "L'identifiant de l'objet ne doit pas être vide." + +msgid "An object identifier is required." +msgstr "Un identifiant d'objet est requis." + +msgid "The operation type should be one of the following: {0}." +msgstr "Le type d'opération doit être l'un des suivants : {0}." + +msgid "An operation type is required." +msgstr "Un type d'opération est requis." + +msgid "The operation type should not be empty." +msgstr "Le type d'opération ne doit pas être vide." + +msgid "Could not validate entity history data." +msgstr "Impossible de valider les données d'historique d'entité." + +msgid "An identifier is required." +msgstr "Un identifiant est requis." + +msgid "The type must be one of the following: {0}." +msgstr "Le type doit être l'un des suivants : {0}." + +msgid "The type is required." +msgstr "Le type est requis." + +msgid "Could not validate permission history data." +msgstr "Impossible de valider les données de l'historique des permissions." + +msgid "A resource identifier is required." +msgstr "Un identifiant de ressource est requis." + +msgid "The secret identifier should be a valid UUID." +msgstr "L'identifiant de secret doit être un UUID valide." + +msgid "A secret identifier is required." +msgstr "Un identifiant de secret est requis." + +msgid "The secret identifier should not be empty." +msgstr "L'identifiant de secret ne doit pas être vide." + +msgid "Could not validate secret access data." +msgstr "Impossible de valider les données d'accès au secret." + +msgid "Could not validate secret history data." +msgstr "Impossible de valider les données de l'historique de secret." + +msgid "Information about the transfer is required." +msgstr "Les informations sur le transfert sont requises." + +msgid "The operation was successful" +msgstr "L'opération a réussi" + +msgid "The transfer id is not valid." +msgstr "L'identifiant de transfert n'est pas valide." + +msgid "The authentication token should be a valid uuid." +msgstr "Le jeton d'authentification doit être un uuid valide." + +msgid "The authentication token is invalid." +msgstr "Le jeton d'authentification est invalide." + +msgid "The transfer id should be a uuid." +msgstr "L'id de transfert doit être un uuid." + +msgid "The user id should be a uuid." +msgstr "L'identifiant de l'utilisateur doit être un uuid." + +msgid "A user id is required" +msgstr "Un identifiant d'utilisateur est requis" + +msgid "The hash should be an ascii string." +msgstr "Le hash doit être une chaîne ascii." + +msgid "The hash should be {0} characters in length." +msgstr "Le hash doit être de {0} caractères." + +msgid "The data transfer hash is required." +msgstr "Le hash de transfert de données est requis." + +msgid "The current page should be a non negative integer." +msgstr "La page courante doit être un entier non négatif." + +msgid "The current page number is required" +msgstr "Le numéro de page courant est requis" + +msgid "The current page number should be equal or inferior to the total number of pages." +msgstr "Le numéro de page courant doit être égal ou inférieur au nombre total de pages." + +msgid "The current page cannot be greater than {0}" +msgstr "La page courante ne peut pas être supérieure à {0}" + +msgid "The total number of pages should a non negative integer." +msgstr "Le nombre total de pages doit être un entier non négatif." + +msgid "The total number of pages is required." +msgstr "Le nombre total de pages est requis." + +msgid "The total number of pages should be greater than 0." +msgstr "Le nombre total de pages doit être supérieur à 0." + +msgid "The total number of pages cannot be greater than {0}" +msgstr "Le nombre total de pages ne peut pas être supérieur à {0}" + +msgid "The status should not be empty." +msgstr "Le statut ne doit pas être vide." + +msgid "The status is required." +msgstr "Le statut est requis." + +msgid "The status must be one of the following: {0}." +msgstr "Le statut doit être l'un des suivants : {0}." + +msgid "The user must be active." +msgstr "L'utilisateur doit être actif." + +msgid "Could not validate the transfer data." +msgstr "Impossible de valider les données de transfert." + +msgid "The transfer could not be created." +msgstr "Le transfert n'a pas pu être créé." + +msgid "Could not update the transfer data." +msgstr "Impossible de mettre à jour les données de transfert." + +msgid "The transfer could not be updated." +msgstr "Le transfert n'a pas pu être mis à jour." + +msgid "This operation is not allowed." +msgstr "Cette opération n'est pas autorisée." + +msgid "Restarting a transfer is not allowed." +msgstr "Redémarrer un transfert n'est pas autorisé." + +msgid "The operation is already over." +msgstr "L'opération est déjà terminée." + +msgid "The current page does not match the total number of pages." +msgstr "La page courante ne correspond pas au nombre total de pages." + +msgid "This operation is not allowed for this user." +msgstr "Cette opération n’est pas autorisée pour cet utilisateur." + +msgid "The authentication token is missing." +msgstr "Le jeton d'authentification est manquant." + +msgid "The authentication token is not valid for this user." +msgstr "Le jeton d'authentification n'est pas valide pour cet utilisateur." + +msgid "The authentication token type is invalid." +msgstr "Le type de jeton d'authentification n'est pas valide." + +msgid "The authentication token is not active." +msgstr "Le jeton d'authentification n'est pas actif." + +msgid "The authentication token is expired." +msgstr "Le jeton d'authentification a expiré." + +msgid "The password generator value \"{0}\" is not valid." +msgstr "La valeur du générateur de mot de passe \"{0}\" n'est pas valide." + +msgid "5 minutes" +msgstr "5 minutes" + +msgid "15 minutes" +msgstr "15 minutes" + +msgid "30 minutes" +msgstr "30 minutes" + +msgid "1 hour" +msgstr "1 heure" + +msgid "until I log out" +msgstr "jusqu'à ce que je me déconnecte" + +msgid "Only administrators can view reports." +msgstr "Seuls les administrateurs peuvent voir les rapports." + +msgid "The requested report `{0}` does not exist." +msgstr "Le rapport demandé `{0}` n'existe pas." + +msgid "Description" +msgstr "Description" + +msgid "Passbolt report" +msgstr "Rapport Passbolt" + +msgid "Report name" +msgstr "Nom du rapport" + +msgid "Generated by" +msgstr "Généré par" + +msgid "Creation date" +msgstr "Date de création" + +msgid "All users have completed the setup." +msgstr "Tous les utilisateurs ont terminé l'installation." + +msgid "Name" +msgstr "Nom" + +msgid "Username" +msgstr "Nom d'utilisateur" + +msgid "Created since" +msgstr "Créé depuis le" + +msgid "Setup completed" +msgstr "Installation terminée" + +msgid "Role" +msgstr "Rôle" + +msgid "No" +msgstr "Non" + +msgid "The data entered are not correct" +msgstr "Les données saisies sont incorrectes" + +msgid "A connection could not be established with the credentials provided." +msgstr "Impossible d'établir une connexion avec les identifiants fournis." + +msgid "Please verify the settings." +msgstr "Veuillez vérifier les paramètres." + +msgid "passbolt test email" +msgstr "email de test passbolt" + +msgid "Congratulations!" +msgstr "Félicitations!" + +msgid "If you receive this email, it means that your passbolt smtp configuration is working fine." +msgstr "Si vous recevez cet e-mail, cela signifie que votre configuration smtp de de passbolt fonctionne correctement." + +msgid "The data entered are not correct: {0}" +msgstr "Les données saisies ne sont pas correctes : {0}" + +msgid "The session has expired. Please start the configuration again." +msgstr "La session a expiré. Veuillez recommencer la configuration." + +msgid "System check" +msgstr "Vérification du système" + +msgid "Subscription key" +msgstr "Clé de souscription" + +msgid "Database" +msgstr "Base de données" + +msgid "Server keys" +msgstr "Clés du serveur" + +msgid "Emails" +msgstr "E-mails" + +msgid "Options" +msgstr "Options" + +msgid "First user" +msgstr "Premier utilisateur" + +msgid "Installation" +msgstr "Installation" + +msgid "That's it!" +msgstr "Voilà!" + +msgid "A host name is required." +msgstr "Un nom d'hôte est requis." + +msgid "The host name should not be empty." +msgstr "Le nom d'hôte ne doit pas être vide." + +msgid "The host name should be a valid BMP-UTF8 string." +msgstr "Le nom d'hôte doit être une chaîne BMP-UTF8 valide." + +msgid "A port number is required." +msgstr "Un numéro de port est requis." + +msgid "The port number should be numeric." +msgstr "Le numéro de port doit être numérique." + +msgid "The port number should be between {0} and {1}." +msgstr "Le numéro de port doit être compris entre {0} et {1}." + +msgid "The username should be a valid BMP-UTF8 string." +msgstr "Le nom d'utilisateur doit être une chaîne BMP-UTF8 valide." + +msgid "The password should not contain quotes." +msgstr "Le mot de passe ne doit pas contenir de guillemets." + +msgid "The password should be a valid BMP-UTF8 string." +msgstr "Le mot de passe doit être une chaîne BMP-UTF8 valide." + +msgid "A database name is required." +msgstr "Un nom de base de données est requis." + +msgid "The database name should not be empty." +msgstr "Le nom de la base de données ne doit pas être vide." + +msgid "The database name should be a valid BMP-UTF8 string." +msgstr "Le nom de la base de données doit être une chaîne BMP-UTF8 valide." + +msgid "The database name should not contain dashes." +msgstr "Le nom de la base de données ne doit pas contenir de tirets." + +msgid "A sender name is required." +msgstr "Un nom d'expéditeur est requis." + +msgid "The sender name should not be empty." +msgstr "Le nom de l'expéditeur ne doit pas être vide." + +msgid "The sender name should be a valid BMP-UTF8 string." +msgstr "Le nom de l'expéditeur doit être une chaîne BMP-UTF8 valide." + +msgid "A sender email is required." +msgstr "Un e-mail d'expéditeur est requis." + +msgid "The sender email should not be empty." +msgstr "L'email de l'expéditeur ne doit pas être vide." + +msgid "The sender email should be a valid BMP-UTF8 string." +msgstr "L'email de l'expéditeur doit être une chaîne BMP-UTF8 valide." + +msgid "The sender email should be a valid email address." +msgstr "L'email de l'expéditeur doit être une adresse e-mail valide." + +msgid "A TLS setting is required." +msgstr "Un réglage TLS est requis." + +msgid "The TLS setting should be a valid boolean." +msgstr "Le réglage TLS doit être un booléen valide." + +msgid "A password is required." +msgstr "Un mot de passe est requis." + +msgid "The test email should be a valid email address." +msgstr "L'email de test doit être une adresse email valide." + +msgid "An OpenPGP public key is required." +msgstr "Une clé publique OpenPGP est requise." + +msgid "The OpenPGP public key should not be empty." +msgstr "La clé publique OpenPGP ne doit pas être vide." + +msgid "The OpenPGP public key should be a valid ASCII string." +msgstr "La clé publique OpenPGP doit être une chaîne ASCII valide." + +msgid "The key is not a valid OpenPGP public key." +msgstr "La clé n'est pas une clé publique OpenPGP valide." + +msgid "The OpenPGP public key should not have an expiry date." +msgstr "La clé publique OpenPGP ne doit pas avoir de date d'expiration." + +msgid "The OpenPGP public key cannot be used to encrypt." +msgstr "La clé publique OpenPGP ne peut pas être utilisée pour chiffrer." + +msgid "An OpenPGP private key is required." +msgstr "Une clé privée OpenPGP est requise." + +msgid "The OpenPGP private key should not be empty." +msgstr "La clé privée OpenPGP ne doit pas être vide." + +msgid "The OpenPGP private key should be a valid ASCII string." +msgstr "La clé privée OpenPGP doit être une chaîne ASCII valide." + +msgid "The value is not a valid OpenPGP private key." +msgstr "La valeur n'est pas une clé privée OpenPGP valide." + +msgid "The OpenPGP private key should not have an expiry date." +msgstr "La clé privée OpenPGP ne doit pas avoir de date d'expiration." + +msgid "The OpenPGP private key cannot be used to decrypt." +msgstr "La clé privée OpenPGP ne peut pas être utilisée pour déchiffrer." + +msgid "Please note that passbolt does not support OpenPGP key protected with a secret." +msgstr "Veuillez noter que passbolt ne supporte pas les clés OpenPGP protégées par un secret." + +msgid "A fingerprint is required." +msgstr "Une empreinte digitale est requise." + +msgid "The fingerprint should not be empty." +msgstr "L'empreinte digitale ne doit pas être vide." + +msgid "The fingerprint should be a valid alphanumeric string." +msgstr "L'empreinte digitale doit être une chaîne alphanumérique valide." + +msgid "The fingerprint does not match the OpenPGP public and the OpenPGP private keys fingerprints." +msgstr "L'empreinte ne correspond pas aux empreintes des clés OpenPGP publique et privée." + +msgid "A full base url is required." +msgstr "Une url de base complète est requise." + +msgid "The full base url should not be empty" +msgstr "L'url de base complète ne doit pas être vide" + +msgid "The full base url should be a valid BMP-UTF8 string." +msgstr "L'url de base complète doit être une chaîne BMP-UTF8 valide." + +msgid "A public registration setting is required." +msgstr "Un paramètre d'enregistrement public est requis." + +msgid "The public registration setting should be a valid boolean." +msgstr "Le réglage d'inscription publique doit être un booléen valide." + +msgid "A force ssl status is required." +msgstr "Un statut pour forcer SSL est requis." + +msgid "The force ssl setting should be a valid boolean." +msgstr "Le réglage Force ssl doit être un booléen valide." + +msgid "The database schema does not match the one expected" +msgstr "Le schéma de base de données ne correspond pas à celui attendu" + +msgid "There was a problem creating the first user" +msgstr "Un problème est survenu lors de la création du premier utilisateur" + +msgid "There was a problem creating the registration token" +msgstr "Un problème est survenu lors de la création du jeton d'enregistrement" + +msgid "The passbolt config is writable." +msgstr "La configuration de passbolt est accessible en écriture." + +msgid "The passbolt config is not writable." +msgstr "La configuration de passbolt n'est pas accessible en écriture." + +msgid "Ensure the file " +msgstr "Assurez-vous que le fichier " + +msgid "you can try:" +msgstr "vous pouvez essayer :" + +msgid "The server OpenPGP public key file is writable." +msgstr "Le fichier de clé publique OpenPGP du serveur est accessible en écriture." + +msgid "The server OpenPGP public key file is not writable." +msgstr "Le fichier de clé publique OpenPGP du serveur n'est pas accessible en écriture." + +msgid "The server OpenPGP private key file is writable." +msgstr "Le fichier de clé privée OpenPGP du serveur est accessible en écriture." + +msgid "The server OpenPGP private key file is not writable." +msgstr "Le fichier de clé privée OpenPGP du serveur n'est pas accessible en écriture." + +msgid "Create your user account!" +msgstr "Créez votre compte utilisateur!" + +msgid "Admin user details" +msgstr "Détails de l'utilisateur admin" + +msgid "First name" +msgstr "Prénom" + +msgid "Last name" +msgstr "Nom" + +msgid "mail@yourdomain.com" +msgstr "mail@votredomaine.com" + +msgid "Next" +msgstr "Suivant" + +msgid "Enter your database details." +msgstr "Saisissez les détails de votre base de données." + +msgid "Database configuration" +msgstr "Configuration de la base de données" + +msgid "An existing database has been detected on your server. The connection details are pre-filled below." +msgstr "Une base de données existante a été détectée sur votre serveur. Les informations de connexion sont pré-remplies ci-dessous." + +msgid "You can keep it as it is (default), or replace it with the details of another database." +msgstr "Vous pouvez le conserver tel quel (par défaut), ou le remplacer par les détails d'une autre base de données." + +msgid "Database connection url" +msgstr "URL de connexion à la base de données" + +msgid "host name or ip address" +msgstr "nom d'hôte ou adresse IP" + +msgid "username" +msgstr "nom d'utilisateur" + +msgid "password" +msgstr "mot de passe" + +msgid "Password" +msgstr "Mot de passe" + +msgid "database name" +msgstr "nom de la base de données" + +msgid "Database name" +msgstr "Nom de la base de données" + +msgid "Cancel" +msgstr "Annuler" + +msgid "Enter your SMTP server settings." +msgstr "Saisissez les paramètres de votre serveur SMTP." + +msgid "Email configuration" +msgstr "Configuration email" + +msgid "admin or company name" +msgstr "nom de l'administrateur ou de la société" + +msgid "Sender name" +msgstr "Nom de l'expéditeur" + +msgid "email@company.com" +msgstr "email@company.com" + +msgid "Sender email" +msgstr "E-mail de l'expéditeur" + +msgid "SMTP server configuration" +msgstr "Configuration du serveur SMTP" + +msgid "SMTP host" +msgstr "Hôte SMTP" + +msgid "Use TLS?" +msgstr "Utiliser TLS ?" + +msgid "port" +msgstr "port" + +msgid "Port" +msgstr "Port" + +msgid "Passbolt is not configured." +msgstr "Passbolt n'est pas configuré." + +msgid "Passbolt is not configured yet!" +msgstr "Passbolt n'est pas encore configuré!" + +msgid "If you see this page, it means that passbolt is present on your server but not configured. Click on \"Get Started\" to launch the configuration wizard." +msgstr "Si vous voyez cette page, cela signifie que passbolt est présent sur votre serveur mais pas encore configuré. Cliquez sur \"Démarrer\" pour lancer l'assistant de configuration." + +msgid "Get Started" +msgstr "Démarrer" + +msgid "Create a new server OpenPGP key or {0} an existing one." +msgstr "Créez une nouvelle clé OpenPGP pour le serveur ou {0} une clé existante." + +msgid "import" +msgstr "importer" + +msgid "Create a new OpenPGP key for your server" +msgstr "Créer une nouvelle clé OpenPGP pour votre serveur" + +msgid "My company server name" +msgstr "Nom du serveur de ma société" + +msgid "Server Name" +msgstr "Nom du serveur" + +msgid "admin@your-server.com" +msgstr "admin@votre-serveur.com" + +msgid "Server Email" +msgstr "Adresse e-mail du serveur" + +msgid "add a comment (optional)" +msgstr "ajouter un commentaire (facultatif)" + +msgid "Comment" +msgstr "Commentaire" + +msgid "Import an existing key or {0} a new one." +msgstr "Importer une clé existante ou {0} une nouvelle clé." + +msgid "create" +msgstr "créer" + +msgid "Copy paste the OpenPGP private key below" +msgstr "Copiez coller la clé privée OpenPGP ci-dessous" + +msgid "Browse" +msgstr "Parcourir" + +msgid "Or select a file from your computer" +msgstr "Ou sélectionnez un fichier depuis votre ordinateur" + +msgid "Some binary things are happening..." +msgstr "Des choses binaires se produisent..." + +msgid "Passbolt Pro activation." +msgstr "Activation de Passbolt Pro." + +msgid "Copy paste your Passbolt Pro subscription key here" +msgstr "Copiez coller votre clé de souscription Passbolt Pro ici" + +msgid "Choose your preferences." +msgstr "Sélectionnez vos préférences." + +msgid "Full Base Url" +msgstr "Url de Base Complète" + +msgid "Full base url" +msgstr "Url de base complète" + +msgid "This is the url where passbolt will be accessible. This url will be used for places where the passbolt url cannot be guessed automatically, such as links in emails. No trailing slash." +msgstr "C'est l'url où passbolt sera accessible. Cette url sera utilisée aux endroits où l'url de passbolt ne peut pas être devinée automatiquement, comme les liens dans les e-mails. L'url ne doit pas se terminer par un slash." + +msgid "Allow public registration?" +msgstr "Autoriser l'inscription publique ?" + +msgid "Allowing public registration means that any visitor can create himself an account on your passbolt. Unless your instance of passbolt is not reachable by the outside world, it is usually a bad idea." +msgstr "Permettre l'inscription publique signifie que tout visiteur peut créer un compte sur votre passbolt. À moins que votre passbolt ne soit pas accessible par le monde extérieur, c'est généralement une mauvaise idée." + +msgid "Force SSL?" +msgstr "Forcer SSL ?" + +msgid "Forcing SSL means that passbolt will not accept connections coming from a non secure protocol. If SSL is forced, your server has to be configured for HTTPS. It is highly recommended that you do so." +msgstr "Forcer SSL signifie que passbolt n'acceptera pas les connexions provenant d'un protocole non sécurisé. Si SSL est forcé, votre serveur doit être configuré pour HTTPS. Il est fortement recommandé de le faire." + +msgid "Welcome to Passbolt Pro! Let's get started with the configuration." +msgstr "Bienvenue sur Passbolt Pro ! Commençons par la configuration." + +msgid "Nice one! Your environment is ready for passbolt." +msgstr "Génial! Votre environnement est prêt pour passbolt." + +msgid "Oops!! Passbolt cannot run yet on your server." +msgstr "Oups!! Passbolt ne peut pas encore être exécuté sur votre serveur." + +msgid "Environment is configured correctly." +msgstr "L'environnement est configuré correctement." + +msgid "URL rewriting is not properly configured on your server." +msgstr "La réécriture d'URL n'est pas correctement configurée sur votre serveur." + +msgid "GPG is configured correctly." +msgstr "GPG est configuré correctement." + +msgid "GPG Configuration" +msgstr "Configuration GPG" + +msgid "SSL" +msgstr "SSL" + +msgid "SSL access is enabled." +msgstr "L'accès SSL est activé." + +msgid "SSL access is not enabled. You can still proceed, but it is highly recommended that you configure your web server to use HTTPS before you continue." +msgstr "L'accès SSL n'est pas activé. Vous pouvez toujours continuer, mais il est fortement recommandé de configurer votre serveur web pour qu'il utilise HTTPS avant de continuer." + +msgid "Start configuration" +msgstr "Lancer la configuration" + +msgid "The configuration is complete" +msgstr "La configuration est terminée" + +msgid "Success!" +msgstr "Succès!" + +msgid "You have completed successfully the configuration procedure, congrats!" +msgstr "Vous avez terminé avec succès la procédure de configuration, félicitations !" + +msgid "You will soon be redirected to passbolt to complete your account setup." +msgstr "Vous allez bientôt être redirigé vers passbolt pour terminer la configuration de votre compte." + +msgid "You will soon be redirected to the login page." +msgstr "Vous allez bientôt être redirigé vers la page de connexion." + +msgid "next" +msgstr "suivant" + +msgid "Click here if you can't wait" +msgstr "Cliquez ici si vous ne pouvez pas attendre" + +msgid "Errors details" +msgstr "Détails des erreurs" + +msgid "Installing" +msgstr "Installation en cours" + +msgid "Existing installation?" +msgstr "Installation existante?" + +msgid "If you want to use an existing passbolt database, the installer will take care of updating it to the current passbolt version while keeping your data." +msgstr "Si vous voulez utiliser une base de données passbolt existante, l'installateur s'occupera de la mettre à jour vers la version actuelle de passbolt tout en conservant vos données." + +msgid "As a precaution, do not forget to backup your database before you continue." +msgstr "Par précaution, n'oubliez pas de sauvegarder votre base de données avant de continuer." + +msgid "Help" +msgstr "Aide" + +msgid "Need help? You can find more information on how to install passbolt in the official online help." +msgstr "Besoin d'aide ? Vous trouverez plus d'informations sur l'installation de passbolt dans l'aide en ligne officielle." + +msgid "What subscription key?" +msgstr "Quelle clé de souscription ?" + +msgid "The subscription key is a file that was sent to you when you purchased Passbolt Pro. We need the subscription key in order to activate the Pro features of passbolt." +msgstr "La clé d'abonnement est un fichier qui vous a été envoyé lorsque vous avez acheté Passbolt Pro. Nous avons besoin de la clé de souscription pour activer les fonctionnalités Pro de passbolt." + +msgid "Send test email" +msgstr "Envoyer un e-mail de test" + +msgid "The test email has been sent successfully!" +msgstr "L'e-mail de test a été envoyé avec succès !" + +msgid "Email could not be sent:" +msgstr "L'e-mail n'a pas pu être envoyé:" + +msgid "See trace" +msgstr "Voir la trace" + +msgid "Your email address" +msgstr "Votre adresse e-mail" + +msgid "Why do I need a SMTP server?" +msgstr "Pourquoi ai-je besoin d'un serveur SMTP ?" + +msgid "Passbolt needs an smtp server in order to send invitation emails after an account creation and to send email notifications." +msgstr "Passbolt a besoin d'un serveur smtp pour envoyer des e-mails d'invitation après la création d'un compte et pour envoyer des notifications par e-mail." + +msgid "The requested address was not found on this server." +msgstr "L'adresse demandée est introuvable sur ce serveur." + +msgid "Please double check the url." +msgstr "Veuillez vérifier l'url." + +msgid "Maybe the page was deleted or moved." +msgstr "La page a peut-être été supprimée ou déplacée." + +msgid "Health checks" +msgstr "Bilan de santé" + +msgid "Passbolt API Status" +msgstr "Passbolt API Status" + +msgid "SSL access is not enabled." +msgstr "L'accès SSL n'est pas activé." + +msgid "Group manager" +msgstr "Responsable du groupe" + +msgid "Member" +msgstr "Membre" + +msgid "Updated roles" +msgstr "Rôles mis à jour" + +msgid "is now group manager" +msgstr "est maintenant gestionnaire de groupe" + +msgid "is not anymore group manager" +msgstr "n'est plus gestionnaire de groupe" + +msgid "Terms" +msgstr "Termes" + +msgid "Privacy" +msgstr "Vie privée" + +msgid "Credits" +msgstr "Crédits" + +msgid "Versions" +msgstr "Versions" + +msgid "home" +msgstr "accueil" + +msgid "login" +msgstr "connexion" + +msgid "register" +msgstr "s'inscrire" + +msgid "You have initiated an account recovery!" +msgstr "Vous avez initié une récupération de compte !" + +msgid "Welcome back!" +msgstr "Bienvenue à nouveau!" + +msgid "You just requested to recover your passbolt account on this device." +msgstr "Vous venez juste de demander de récupérer votre compte passbolt sur cet appareil." + +msgid "Make sure you have a backup of your secret key handy." +msgstr "Assurez-vous d'avoir une sauvegarde de votre clé secrète à portée de main." + +msgid "Click on the link below to proceed." +msgstr "Cliquez sur le lien ci-dessous pour continuer." + +msgid "start recovery" +msgstr "démarrer la récupération" + +msgid "{0} just created an account for you on passbolt!" +msgstr "{0} vient de créer un compte pour vous sur passbolt!" + +msgid "Welcome {0}" +msgstr "Bienvenue {0}" + +msgid "{0} just invited you to join passbolt at {1}" +msgstr "{0} vous a invité à rejoindre passbolt à l'adresse {1}" + +msgid "Passbolt is an open source password manager." +msgstr "Passbolt est un gestionnaire de mots de passe open source." + +msgid "It is designed to allow sharing credentials securely with your team!" +msgstr "Il est conçu pour permettre le partage d'identifiants avec votre équipe en toute sécurité!" + +msgid "Let's take the next five minutes to get you started!" +msgstr "Nous allons prendre les cinq prochaines minutes pour vous aider à démarrer!" + +msgid "get started" +msgstr "démarrer" + +msgid "You just created your account on passbolt!" +msgstr "Vous venez de créer votre compte sur passbolt!" + +msgid "You just opened an account on passbolt at {0}." +msgstr "Vous venez d'ouvrir un compte sur passbolt à l'adresse {0}." + +msgid "{0} requested you to add members to a group" +msgstr "{0} vous a demandé d'ajouter des membres à un groupe" + +msgid "Group: {0}" +msgstr "Groupe: {0}" + +msgid "The following members should be added:" +msgstr "Les membres suivants doivent être ajoutés :" + +msgid "Edit group members" +msgstr "Editer les membres du groupe" + +msgid "log in passbolt" +msgstr "connectez-vous à passbolt" + +msgid "{0} deleted the user {1}" +msgstr "{0} a supprimé l'utilisateur {1}" + +msgid "The user {0} {1} ({2}) is now deleted from your organisation in passbolt." +msgstr "L'utilisateur {0} {1} ({2}) est maintenant supprimé de votre organisation dans passbolt." + +msgid "This user was a member of the following group(s) you manage:" +msgstr "Cet utilisateur était membre du ou des groupes suivants que vous gérez:" + +msgid "view it in passbolt" +msgstr "le voir dans Passbolt" + +msgid "{0} deleted a group" +msgstr "{0} a supprimé un groupe" + +msgid "{0} deleted the group \"{1}\" you were a member of." +msgstr "{0} a supprimé le groupe \"{1}\" dont vous étiez membre." + +msgid "All passwords that were shared only with this group were also deleted." +msgstr "Tous les mots de passe qui ont été partagés uniquement avec ce groupe ont également été supprimés." + +msgid "As member of the group you now have access to all the passwords that are shared with this group." +msgstr "En tant que membre du groupe, vous avez maintenant accès à tous les mots de passe qui sont partagés avec ce groupe." + +msgid "And as group manager you are also authorized to edit the members of the group." +msgstr "Et en tant que gestionnaire de groupe, vous êtes également autorisé à modifier les membres du groupe." + +msgid "You are no longer a member of this group." +msgstr "Vous n'êtes plus membre de ce groupe." + +msgid "You are no longer authorized to access the passwords shared with this group." +msgstr "Vous n'êtes plus autorisé à accéder aux mots de passe partagés avec ce groupe." + +msgid "Please contact {0} or another group manager if this is a mistake." +msgstr "Veuillez contacter {0} ou un autre gestionnaire de groupe s'il s'agit d'une erreur." + +msgid "You are now a group manager of this group." +msgstr "Vous êtes maintenant un gestionnaire de groupe de ce groupe." + +msgid "As group manager you are now authorized to edit the members of this group." +msgstr "En tant que gestionnaire de groupe, vous êtes maintenant autorisé à modifier les membres de ce groupe." + +msgid "As member of the group you still have access to all the passwords that are shared with this group." +msgstr "En tant que membre du groupe, vous avez toujours accès à tous les mots de passe qui sont partagés avec ce groupe." + +msgid "You are no longer a group manager of this group." +msgstr "Vous n'êtes plus un gestionnaire de groupe de ce groupe." + +msgid "You are no longer authorized to edit the members of this group." +msgstr "Vous n'êtes plus autorisé à modifier les membres de ce groupe." + +msgid "Edited your membership in several groups" +msgstr "A édité votre adhésion à plusieurs groupes" + +msgid "{0} group memberships were affected." +msgstr "{0} adhésions de groupe ont été affectées." + +msgid "It would be too much to list them here, but you can get more information on passbolt." +msgstr "Ce serait trop de les énumérer ici, mais vous pouvez obtenir plus d'informations sur passbolt." + +msgid "go to passbolt" +msgstr "accéder à passbolt" + +msgid "{0} deleted several group" +msgstr "{0} a supprimé plusieurs groupes" + +msgid "{0} deleted {1} groups you were a member of." +msgstr "{0} a supprimé {1} groupes dont vous étiez membre." + +msgid "You have saved a new password" +msgstr "Vous avez enregistré un nouveau mot de passe" + +msgid "Name: {0}" +msgstr "Nom : {0}" + +msgid "Username: {0}" +msgstr "Nom d'utilisateur: {0}" + +msgid "URL: {0}" +msgstr "URL: {0}" + +msgid "Description: {0}" +msgstr "Description: {0}" + +msgid "{0} shared a password with you" +msgstr "{0} a partagé un mot de passe avec vous" + +msgid "{0} updated the password {1}" +msgstr "{0} a mis à jour le mot de passe {1}" + +msgid "Edited multiple resources" +msgstr "A édité plusieurs ressources" + +msgid "{0} resources were affected." +msgstr "{0} ressources ont été affectées." + +msgid "It would be too much to list them here, but you can go check them on passbolt." +msgstr "Ce serait trop de les énumérer ici, mais vous pouvez aller les vérifier sur passbolt." + +msgid "view them in passbolt" +msgstr "les voir dans passbolt" + +msgid "{0} shared passwords with you" +msgstr "{0} a partagé des mots de passe avec vous" + +msgid "{0} resources were shared with you." +msgstr "{0} ressources ont été partagées avec vous." + +msgid "{0} just activated their account on passbolt!" +msgstr "{0} vient d'activer son compte sur passbolt!" + +msgid "The user is now active on passbolt and you can share passwords with them." +msgstr "L'utilisateur est maintenant actif sur passbolt et vous pouvez partager des mots de passe avec lui." + +msgid "This user was invited by you {0}." +msgstr "Cet utilisateur a été invité par vous {0}." + +msgid "This user signed up themselves, since the public registration is enabled." +msgstr "Cet utilisateur s'est inscrit lui-même, puisque l'inscription publique est activée." + +msgid "This user was invited by {0} {1}." +msgstr "Cet utilisateur a été invité par {0} {1}." + +msgid "view users" +msgstr "voir les utilisateurs" + +msgid "The locale {0} is not valid or not supported." +msgstr "La locale {0} n'est pas valide ou n'est pas supportée." + +msgid "An Internal Error Has Occurred" +msgstr "Une erreur interne est survenue" + diff --git a/resources/locales/sv_SE/default.po b/resources/locales/sv_SE/default.po new file mode 100644 index 0000000000..0402bbcfcc --- /dev/null +++ b/resources/locales/sv_SE/default.po @@ -0,0 +1,2508 @@ +msgid "" +msgstr "" +"Project-Id-Version: 41c2572bd9bd4cc908d3e09e0cbed6e5\n" +"POT-Creation-Date: 2021-10-06 14:32+0000\n" +"PO-Revision-Date: 2021-10-13 14:14\n" +"Last-Translator: NAME \n" +"Language-Team: Swedish\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Crowdin-Project: 41c2572bd9bd4cc908d3e09e0cbed6e5\n" +"X-Crowdin-Project-ID: 2\n" +"X-Crowdin-Language: sv\n" +"X-Crowdin-File: /[passbolt.passbolt-ce-api] release/resources/locales/en_UK/default.po\n" +"X-Crowdin-File-ID: 212\n" +"Language: sv_SE\n" + +msgid "You need to login to access this location." +msgstr "Du måste logga in för att få tillgång till denna plats." + +msgid "Decryption failed." +msgstr "Dekrypteringen misslyckades." + +msgid "Failed to create token." +msgstr "Kunde inte skapa token." + +msgid "The user token result should be a valid UUID." +msgstr "Användarens tokenresultat bör vara ett giltigt UUID." + +msgid "The user token result could not be found " +msgstr "Resultatet för användartoken kunde inte hittas " + +msgid "The OpenPGP server key defined in the config cannot be used to decrypt." +msgstr "OpenPGP-servernyckeln som definieras i konfigurationen kan inte användas för att dekryptera." + +msgid "Could not import the user OpenPGP key." +msgstr "Kunde inte importera användarens OpenPGP-nyckel." + +msgid "Invalid verify token format, " +msgstr "Ogiltigt format för tokenverifiering, " + +msgid "sections are missing or using wrong delimiters." +msgstr "sektioner saknas eller använder fel avgränsare." + +msgid "the version numbers do not match." +msgstr "versionsnumren matchar inte." + +msgid "wrong version number." +msgstr "fel versionsnummer." + +msgid "it is not a UUID." +msgstr "det är inte ett UUID." + +msgid "wrong token data length." +msgstr "fel datalängd för token." + +msgid "There is no user associated with this key." +msgstr "Det finns ingen användare kopplad till denna nyckel." + +msgid "Page not found." +msgstr "Sidan hittades inte." + +msgid "This is not a valid Ajax/Json request." +msgstr "Detta är inte en giltig Ajax/Json-förfrågan." + +msgid "You are successfully logged in." +msgstr "Du är inloggad." + +msgid "The OpenPGP public key information was not found in config." +msgstr "Informationen om OpenPGPs publika nyckel hittades inte i konfigurationen." + +msgid "The operation was successful." +msgstr "Operationen lyckades." + +msgid "The resource identifier should be a valid UUID." +msgstr "Resursidentifieraren ska vara ett giltigt UUID." + +msgid "Could not save the comment, please try again later." +msgstr "Kunde inte spara kommentaren, vänligen försök igen senare." + +msgid "The comment was successfully added." +msgstr "Kommentaren har lagts till." + +msgid "The resource does not exist." +msgstr "Resursen finns inte." + +msgid "Could not validate comment data." +msgstr "Kunde inte validera kommentarsdata." + +msgid "The comment id is not valid." +msgstr "Kommentarens id är inte giltigt." + +msgid "The comment does not exist." +msgstr "Kommentaren finns inte." + +msgid "The comment was deleted." +msgstr "Kommentaren har tagits bort." + +msgid "Could not delete comment." +msgstr "Kunde inte ta bort kommentaren." + +msgid "The comment was successfully updated." +msgstr "Kommentaren har lagts till." + +msgid "You are not allowed to edit this comment." +msgstr "Du har inte behörighet att redigera den här kommentaren." + +msgid "Could not find comments for the requested model." +msgstr "Kunde inte hitta kommentarer för den önskade modellen." + +msgid "Invalid order. \"{0}\" is not in the list of allowed order." +msgstr "Ogiltig order. \"{0}\" finns inte i listan över tillåtna ordrar." + +msgid "Invalid order. \"{0}\" is not a valid order." +msgstr "Ogiltig order. \"{0}\" är inte en giltig order." + +msgid "Invalid order. \"{0}\" is not a valid field." +msgstr "Ogiltig order. \"{0}\" är inte ett giltigt fält." + +msgid "Invalid pagination." +msgstr "Ogiltig sidnumrering." + +msgid "Invalid query string. The filter parameter should be an array." +msgstr "Ogiltig frågesträng. Filterparametern bör vara en matris." + +msgid "Invalid query string. The contain parameter should be an array." +msgstr "Ogiltig frågesträng. Innehållsparametern bör vara en matris." + +msgid "Invalid filter." +msgstr "Ogiltigt filter." + +msgid "Invalid order." +msgstr "Ogiltig order." + +msgid "Invalid contain." +msgstr "Ogiltigt innehåll." + +msgid "No validation rule for filter {0}. Please create one." +msgstr "Ingen valideringsregel för filtret {0}. Skapa en." + +msgid "Filter {0} is not valid." +msgstr "Filtret {0} är inte giltigt." + +msgid "\"{0}\" is not a valid value for filter {1}." +msgstr "\"{0}\" är inte ett giltigt värde för filter {1}." + +msgid "\"{0}\" is not a valid search filter." +msgstr "\"{0}\" är inte ett giltigt sökfilter." + +msgid "\"{0}\" is not a valid search filter. It is not a UTF8 string." +msgstr "\"{0}\" är inte ett giltigt sökfilter. Det är inte en UTF8-sträng." + +msgid "It should be between 1 and 64 char in length." +msgstr "Det bör vara mellan 1 och 64 tecken i längd." + +msgid "\"{0}\" is not a valid user filter." +msgstr "\"{0}\" är inte ett giltigt sökfilter." + +msgid "\"{0}\" is not a valid user id for filter {1}." +msgstr "\"{0}\" är inte ett giltigt användar-id för filter {1}." + +msgid "\"{0}\" is not a valid group filter." +msgstr "\"{0}\" är inte ett giltigt gruppfilter." + +msgid "\"{0}\" is not a valid resource id for filter {1}." +msgstr "\"{0}\" är inte ett giltigt resurs-id för filter {1}." + +msgid "\"{0}\" is not a valid group id for filter {1}." +msgstr "\"{0}\" är inte ett giltigt resurs-id för filter {1}." + +msgid "\"{0}\" is not a valid timestamp for filter {1}." +msgstr "\"{0}\" är inte en giltig tidsstämpel för filter {1}." + +msgid "\"{0}\" is not a valid datetime for filter {1}." +msgstr "\"{0}\" är inte ett giltigt datum/tid för filter {1}." + +msgid "\"{0}\" is not a valid order." +msgstr "\"{0}\" är inte en giltig order." + +msgid "\"{0}\" is not in the list of allowed order." +msgstr "\"{0}\" finns inte i listan över tillåtna ordrar." + +msgid "\"{0}\" is not a valid contain value." +msgstr "\"{0}\" är inte ett giltigt innehållsvärde." + +msgid "undefined user agent" +msgstr "odefinierad användaragent" + +msgid "Access restricted to administrators." +msgstr "Åtkomst begränsad till administratörer." + +msgid "This feature is not supported by your version of passbolt." +msgstr "Denna funktion stöds inte av din version av passbolt." + +msgid "The resource was marked as favorite." +msgstr "Resursen har markerats som favorit." + +msgid "This record is already marked as favorite." +msgstr "Denna post är redan markerad som favorit." + +msgid "Could not validate favorite data." +msgstr "Kunde inte validera favoritdata." + +msgid "The favorite id is not valid." +msgstr "Favorit-id är inte giltigt." + +msgid "The favorite does not exist." +msgstr "Favoriten finns inte." + +msgid "The favorite was deleted." +msgstr "Favoriten har tagits bort." + +msgid "Could not delete favorite." +msgstr "Kunde inte ta bort favorit." + +msgid "The OpenPGP key identifier should be a valid UUID." +msgstr "OpenPGP-nyckel-ID bör vara ett giltigt UUID." + +msgid "The OpenPGP key does not exist." +msgstr "OpenPGP-nyckeln finns inte." + +msgid "The group has been added successfully." +msgstr "Gruppen lades till." + +msgid "The group can be deleted." +msgstr "Gruppen kan tas bort." + +msgid "The group was deleted successfully." +msgstr "Gruppen har tagits bort." + +msgid "You are not authorized to access that location." +msgstr "Du har inte behörighet att komma åt platsen." + +msgid "The group identifier should be a valid UUID." +msgstr "Gruppidentifieraren ska vara ett giltigt UUID." + +msgid "The group does not exist or has been already deleted." +msgstr "Gruppen finns inte eller har redan tagits bort." + +msgid "The group cannot be deleted." +msgstr "Gruppen kan inte tas bort." + +msgid "The permissions identifiers must be valid UUID." +msgstr "Behörighetsidentifierare måste vara giltiga UUID." + +msgid "The group id is not valid." +msgstr "Grupp-id är inte giltigt." + +msgid "The group does not exist." +msgstr "Gruppen finns inte." + +msgid "All checks ran successfully!" +msgstr "Alla kontroller genomfördes framgångsrikt!" + +msgid "OK" +msgstr "OK" + +msgid "The identifier should be a valid UUID." +msgstr "Identifieraren ska vara ett giltigt UUID." + +msgid "The resource type does not exist." +msgstr "Resurstypen finns inte." + +msgid "The resource has been added successfully." +msgstr "Resursen har lagts till." + +msgid "The resource has been deleted successfully." +msgstr "Resursen har tagits bort." + +msgid "You do not have the permission to delete this resource." +msgstr "Du har inte behörighet att ta bort denna resurs." + +msgid "Could not delete the resource." +msgstr "Kunde inte radera resursen." + +msgid "The resource has been updated successfully." +msgstr "Resursen har uppdaterats." + +msgid "The secret does not exist." +msgstr "Hemligheten finns inte." + +msgid "The key provided does not belong to given user." +msgstr "Nyckeln som anges tillhör inte användaren." + +msgid "The recovery was completed successfully." +msgstr "Återställningen slutfördes framgångsrikt." + +msgid "The user identifier should be a valid UUID." +msgstr "Användarmarkören ska vara ett giltigt UUID." + +msgid "The user does not exist, has not completed the setup or was deleted." +msgstr "Användaren existerar inte, har inte slutfört installationen eller har tagits bort." + +msgid "The user does not exist or is not active." +msgstr "Användaren existerar inte eller är inte aktiv." + +msgid "The authentication token is not valid." +msgstr "Autentiserings-token är inte giltig." + +msgid "The token is expired." +msgstr "Token har löpt ut." + +msgid "The OpenPGP key data is not valid." +msgstr "OpenPGP-nyckeldatan är inte giltig." + +msgid "The setup was completed successfully." +msgstr "Konfigurationen har slutförts." + +msgid "An authentication token should be provided." +msgstr "En autentiseringstoken bör anges." + +msgid "The authentication token should be a valid UUID." +msgstr "Autentiseringstoken bör vara ett giltigt UUID." + +msgid "The authentication token is not valid or has expired." +msgstr "Autentiseringstoken är inte giltig eller har gått ut." + +msgid "The user does not exist, is already active or has been deleted." +msgstr "Användaren finns inte, är redan aktiv eller har tagits bort." + +msgid "An OpenPGP key must be provided." +msgstr "En OpenPGP-nyckel måste anges." + +msgid "A valid OpenPGP key must be provided." +msgstr "En giltig OpenPGP-nyckel måste anges." + +msgid "The token should be a valid UUID." +msgstr "Token ska vara ett giltigt UUID." + +msgid "The user does not exist or is active." +msgstr "Användaren finns inte eller är aktiv." + +msgid "You are not authorized to share this resource." +msgstr "Du har inte behörighet att dela denna resurs." + +msgid "Only administrators can add new users." +msgstr "Endast administratörer kan lägga till nya användare." + +msgid "The user was successfully added. This user now need to complete the setup." +msgstr "Användaren har lagts till. Denna användare måste nu slutföra konfigurationen." + +msgid "The user can be deleted." +msgstr "Användaren kan tas bort." + +msgid "The user has been deleted successfully." +msgstr "Användaren har tagits bort." + +msgid "You are not allowed to delete yourself." +msgstr "Du får inte ta bort dig själv." + +msgid "The user does not exist or has been already deleted." +msgstr "Användaren finns inte eller har redan tagits bort." + +msgid "The user cannot be deleted." +msgstr "Användaren kan inte tas bort." + +msgid "The groups users identifiers must be valid UUID." +msgstr "Gruppanvändarnas identifierare måste vara giltiga UUID." + +msgid "The user does not exist or has been deleted." +msgstr "Användaren finns inte eller har tagits bort." + +msgid "Could not validate user data." +msgstr "Kunde inte validera användardata." + +msgid "Could not find the user data after save. Maybe it has been deleted in the meantime." +msgstr "Kunde inte hitta användardata efter sparandet. Kanske har den tagits bort under tiden." + +msgid "The user has been updated successfully." +msgstr "Användaren har uppdaterats." + +msgid "Some user data should be provided." +msgstr "Viss användardata bör tillhandahållas." + +msgid "Updating the OpenPGP key is not allowed." +msgstr "Det är inte tillåtet att uppdatera OpenPGP-nyckeln." + +msgid "Updating the groups is not allowed." +msgstr "Det är inte tillåtet att uppdatera grupperna." + +msgid "You are not authorized to edit the role." +msgstr "Du har inte behörighet att redigera rollen." + +msgid "Only guest are allowed to recover an account. Please logout first." +msgstr "Endast gäster tillåts återställa ett konto. Vänligen logga ut först." + +msgid "Recovery process started, check your email." +msgstr "Återställningsprocessen har startats, kolla din e-post." + +msgid "Please provide a valid email address." +msgstr "Vänligen ange en giltig e-postadress." + +msgid "This user does not exist or has been deleted." +msgstr "Den här användaren finns inte eller har tagits bort." + +msgid "Please register and complete the setup first." +msgstr "Vänligen registrera dig och slutför installationen först." + +msgid "Please contact your administrator." +msgstr "Kontakta din administratör." + +msgid "Registration is not opened to public. Please contact your administrator." +msgstr "Registreringen är inte öppen för allmänheten. Kontakta din administratör." + +msgid "Only guest are allowed to register." +msgstr "Endast gäster får registrera sig." + +msgid "The user identifier should be a valid UUID or \"me\"." +msgstr "Användarmarkören ska vara ett giltigt UUID eller \"jag\"." + +msgid "The user does not exist." +msgstr "Användaren finns inte." + +msgid "The identifier should not be empty." +msgstr "Identifieraren får inte vara tom." + +msgid "A token is required." +msgstr "En token måste anges." + +msgid "The token should not be empty." +msgstr "Token får inte vara tom." + +msgid "The type should be one of the following: {0}." +msgstr "Typen bör vara en av följande: {0}." + +msgid "A type is required." +msgstr "Typ måste fyllas i." + +msgid "The type should not be empty." +msgstr "Typen får inte vara tom." + +msgid "A user identifier is required." +msgstr "En användaridentifiering krävs." + +msgid "The user identifier should not be empty." +msgstr "Användaridentifieraren får inte vara tom." + +msgid "The active status should be a valid boolean." +msgstr "Den aktiva statusen bör vara en giltig boolean." + +msgid "An active status is required" +msgstr "En aktiv status måste anges" + +msgid "It is not possible to create an authentication token for this user." +msgstr "Det går inte att skapa en autentiseringstoken för den här användaren." + +msgid "A file is required." +msgstr "En fil krävs." + +msgid "The file should not be empty" +msgstr "Filen får inte vara tom" + +msgid "The file mime type should be one of the following: {0}." +msgstr "Filen mime typ bör vara en av följande: {0}." + +msgid "The file extension should be one of the following: {0}." +msgstr "Filtillägget bör vara ett av följande: {0}." + +msgid "The file is not valid, or exceeds max size of {0} bytes." +msgstr "Filen är inte giltig eller överskrider maxstorleken {0} bytes." + +msgid "Could not save the data in {0} format." +msgstr "Kunde inte spara data i {0} format." + +msgid "The parent comment identifier should be a valid UUID." +msgstr "Överordnade kommentarsidentifierare bör vara ett giltigt UUID." + +msgid "The commented object type should be one of the following: {0}." +msgstr "Den kommenterade objekttypen bör vara en av följande: {0}." + +msgid "The commented object type is required." +msgstr "Den kommenterade objekttypen krävs." + +msgid "The commented object type should not be empty." +msgstr "Den kommenterade objekttypen får inte vara tom." + +msgid "The commented object identifier should be a valid UUID." +msgstr "Det kommenterade objekt-identifieraren bör vara ett giltigt UUID." + +msgid "The commented object identifier is required." +msgstr "Den kommenterade objektidentifieraren krävs." + +msgid "The commented object identifier should not be empty." +msgstr "Det kommenterade objekt-identifierarfältet får inte vara tomt." + +msgid "The content should be a valid UTF8 string." +msgstr "Innehållet bör vara en giltig UTF8-sträng." + +msgid "A content is required" +msgstr "Ett innehåll krävs" + +msgid "The content should not be empty" +msgstr "Innehållet får inte vara tomt" + +msgid "The content length should be between {0} and {1} characters." +msgstr "Innehållslängden bör vara mellan {0} och {1} tecken." + +msgid "The identifier of the user who created the comment should be a valid UUID." +msgstr "Identifieraren för användaren som skapade kommentaren bör vara ett giltigt UUID." + +msgid "The identifier of the user who created the comment is required." +msgstr "Identifieraren för användaren som skapade kommentaren krävs." + +msgid "The identifier of the user who created the comment should not be empty." +msgstr "Identifieraren för användaren som skapade kommentaren får inte vara tom." + +msgid "The identifier of the user who modified the comment should be a valid UUID." +msgstr "Identifieraren för användaren som ändrade kommentaren bör vara ett giltigt UUID." + +msgid "The identifier of the user who modified the comment required." +msgstr "Identifieraren av användaren som ändrade kommentaren krävs." + +msgid "The identifier of the user who modified the comment should not be empty." +msgstr "Identifieraren för användaren som ändrade kommentaren får inte vara tom." + +msgid "Access denied." +msgstr "Åtkomst nekad." + +msgid "The parent comment does not exist." +msgstr "Den överordnade kommentaren finns inte." + +msgid "The user cannot update this comment." +msgstr "Användaren kan inte uppdatera denna kommentar." + +msgid "The user cannot delete this comment." +msgstr "Användaren kan inte ta bort denna kommentar." + +msgid "The commented object type does not exist." +msgstr "Den kommenterade objekttypen finns inte." + +msgid "The favorite object type should be one of the following: {0}." +msgstr "Favoritobjekttypen bör vara en av följande: {0}." + +msgid "The favorite object type is required." +msgstr "Det krävs en favoritobjekttyp." + +msgid "The favorite object type should not be empty" +msgstr "Favoritobjekttypen får inte vara tom" + +msgid "The favorite object identifier should be a valid UUID." +msgstr "Favoritobjektsidentifieraren bör vara ett giltigt UUID." + +msgid "The favorite object identifier is required." +msgstr "Det krävs en favoritobjekt-identifierare." + +msgid "The favorite object identifier should not be empty." +msgstr "Favoritobjekt-identifieraren får inte vara tom." + +msgid "The resource is already marked as favorite." +msgstr "Resursen är redan markerad som favorit." + +msgid "The user cannot delete this favorite." +msgstr "Användaren kan inte ta bort denna favorit." + +msgid "The armored key should be a valid ASCII string." +msgstr "Den bepansrade nyckeln bör vara en giltig ASCII-sträng." + +msgid "An armored key is required." +msgstr "En bepansrad nyckel krävs." + +msgid "The armored key should not be empty." +msgstr "Den bepansrade nyckeln får inte vara tom." + +msgid "The armored key should be a valid ASCII-armored OpenPGP key." +msgstr "Den bepansrade nyckeln bör vara en giltig ASCII-bepansrad OpenPGP-nyckel." + +msgid "A length is required." +msgstr "Det krävs en längd." + +msgid "The identifier should be a valid BMP-UTF8 string." +msgstr "Identifieraren bör vara en giltig BMP-UTF8-sträng." + +msgid "The key identifier should be a valid ASCII string." +msgstr "Nyckelidentifieraren bör vara en giltig ASCII-sträng." + +msgid "A key identifier is required." +msgstr "En nyckel-identifierare krävs." + +msgid "The key identifier should not be empty." +msgstr "Nyckelidentifieraren får inte vara tom." + +msgid "The key identifier should be a string of 8 hexadecimal characters." +msgstr "Nyckeln identifierare bör vara en sträng av 8 hexadecimala tecken." + +msgid "The fingerprint should be a valid ASCII string." +msgstr "Fingeravtrycket bör vara en giltig ASCII-sträng." + +msgid "A fingerprint is required" +msgstr "Ett fingeravtryck krävs" + +msgid "The fingerprint should not be empty" +msgstr "Fingeravtrycket får inte vara tomt" + +msgid "The fingerprint should be a string of 40 hexadecimal characters." +msgstr "Fingeravtrycket bör vara en sträng av 40 hexadecimala tecken." + +msgid "The type should be a valid ASCII string." +msgstr "Typen ska vara en giltig ASCII-sträng." + +msgid "A type is required" +msgstr "En typ är obligatorisk" + +msgid "The type should not be empty" +msgstr "Typen får inte vara tom" + +msgid "The type should be one of the following: RSA, DSA, ECC, ELGAMAL, ECDSA, DH." +msgstr "Typen ska vara en av följande: RSA, DSA, ECC, ELGAMAL, ECDSA, DH." + +msgid "The expiry should be a valid date." +msgstr "Förfallodatum ska vara ett giltigt datum." + +msgid "The key should not already be expired." +msgstr "Nyckeln bör inte redan ha gått ut." + +msgid "The creation date should be a valid date." +msgstr "Skapandedatum måste vara ett giltigt datum." + +msgid "A creation date is required." +msgstr "Ett skapandedatum måste fyllas i." + +msgid "The creation date should not be empty." +msgstr "Skapandedatumet får inte vara tomt." + +msgid "The creation date should be set in the past." +msgstr "Skapandedatum måste fastställas i det förflutna." + +msgid "The deleted status should be a valid boolean." +msgstr "Den borttagna statusen ska vara en giltig boolean." + +msgid "A deleted status is required" +msgstr "En borttagen status krävs" + +msgid "A user identifier is required" +msgstr "En användaridentifiering krävs" + +msgid "The user identifier should not be empty" +msgstr "Användaridentifieraren får inte vara tom" + +msgid "Could not parse the key info." +msgstr "Kan inte tolka nyckelinformationen." + +msgid "The name should be a valid UTF8 string." +msgstr "Namnet bör vara en giltig UTF8-sträng." + +msgid "The name length should be maximum {0} characters." +msgstr "Längden på namnet bör vara högst {0} tecken." + +msgid "A name is required." +msgstr "Ett namn måste anges." + +msgid "The name should not be empty." +msgstr "Namnet får inte vara tomt." + +msgid "The identifier of the user who created the group should be a valid UUID." +msgstr "Identifieraren för den användare som skapade gruppen bör vara ett giltigt UUID." + +msgid "The identifier of the user who created the group is required." +msgstr "Identifieraren för den användare som skapade gruppen krävs." + +msgid "The identifier of the user who created the group should not be empty." +msgstr "Identifieraren för användaren som skapade gruppen får inte vara tom." + +msgid "The identifier of the user who modified the group should be a valid UUID." +msgstr "Identifieraren för den användare som ändrat gruppen bör vara ett giltigt UUID." + +msgid "The identifier of the user who modified the group is required." +msgstr "Identifieraren för den användare som ändrade gruppen krävs." + +msgid "The identifier of the user who modified the group should not be empty." +msgstr "Identifieraren för den användare som ändrat gruppen bör inte vara tom." + +msgid "The name is already used by another group." +msgstr "Namnet används redan av en annan grupp." + +msgid "A group manager should be provided." +msgstr "En gruppchef bör tillhandahållas." + +msgid "The group should not be soft deleted." +msgstr "Gruppen bör inte bli mjukraderad." + +msgid "The group should not be sole owner of shared content, transfer the ownership to other users." +msgstr "Gruppen bör inte vara ensam ägare av delat innehåll, överföra ägandet till andra användare." + +msgid "Could not validate group data." +msgstr "Kunde inte validera gruppdata." + +msgid "Could not delete the group {0}, please try again later." +msgstr "Kunde inte ta bort gruppen {0}, försök igen senare." + +msgid "A group identifier is required." +msgstr "En grupp-identifierare krävs." + +msgid "The group identifier should not be empty." +msgstr "Gruppidentifieraren får inte vara tom." + +msgid "The group manager status should be a valid boolean." +msgstr "Gruppchefens status ska vara en giltig boolean." + +msgid "A group manager status is required." +msgstr "En gruppchefsstatus krävs." + +msgid "The group manager status should not be empty." +msgstr "Gruppchefens status får inte vara tom." + +msgid "The user is already member of this group." +msgstr "Användaren är redan medlem i denna grupp." + +msgid "The property length should be maximum {0} characters." +msgstr "Egenskapens längd bör vara högst {0} tecken." + +msgid "A property is required." +msgstr "En egenskap krävs." + +msgid "The property should not be empty." +msgstr "Egenskapen får inte vara tom." + +msgid "The property should be an alphabetical string and only point is accepted as special characters." +msgstr "Egenskapen bör vara en alfabetisk sträng och endast punkt accepteras som specialtecken." + +msgid "The property identifier should be a valid UUID." +msgstr "Egenskapsidentifieraren bör vara ett giltigt UUID." + +msgid "A property identifier is required." +msgstr "En egenskapsidentifierare krävs." + +msgid "The property identifier should not be empty." +msgstr "Egenskapsidentifieraren får inte vara tom." + +msgid "The value should be a valid UTF8 string." +msgstr "Värdet bör vara en giltig UTF8-sträng." + +msgid "The value length should be maximum {0} characters." +msgstr "Värdets längd bör vara maximalt {0} tecken." + +msgid "A value is required." +msgstr "Ett värde måste anges." + +msgid "The value should not be empty." +msgstr "Värdet får inte vara tomt." + +msgid "This property id is already in use." +msgstr "Detta egenskaps-id används redan." + +msgid "Only admin can create or update organization settings." +msgstr "Endast administratören kan skapa eller uppdatera organisationsinställningar." + +msgid "This is not a valid setting." +msgstr "Detta är inte en giltig inställning." + +msgid "The type of the access control object should be one of the following: {0}." +msgstr "Typen av åtkomstkontrollens objekt bör vara en av följande: {0}." + +msgid "The type of the access control object is required." +msgstr "Typ av objekt för åtkomstkontroll krävs." + +msgid "The type of the access control object should not be empty." +msgstr "Typ av objekt för åtkomstkontroll får inte vara tomt." + +msgid "The identifier of the access control object should be a valid UUID." +msgstr "Identifieraren för åtkomstkontrollens objekt bör vara ett giltigt UUID." + +msgid "The identifier of the access control object is required." +msgstr "Identifierare för åtkomstkontrollens objekt krävs." + +msgid "The identifier of the access control object should not be empty." +msgstr "Identifieraren av objekt för åtkomstkontroll får inte vara tomt." + +msgid "The access request object type should be one of the following: {0}." +msgstr "Objekttypen för åtkomstbegäran bör vara en av följande: {0}." + +msgid "The type of the access request object is required." +msgstr "Typ av objekt för åtkomstbegäran krävs." + +msgid "The access request object type should not be empty." +msgstr "Objekttypen för åtkomstförfrågan får inte vara tom." + +msgid "The identifier of the access request object should be a valid UUID." +msgstr "Identifierare för objekt för åtkomstbegäran bör vara ett giltigt UUID." + +msgid "The identifier of the access request object is required." +msgstr "Identifierare för objekt för åtkomstbegäran krävs." + +msgid "The identifier of the access request object should not be empty." +msgstr "Identifieraren för objekt för åtkomstbegäran får inte vara tom." + +msgid "A permission already exists for the given access control object and access request object." +msgstr "Det finns redan en behörighet för objekten åtkomstkontroll och åtkomstbegäran." + +msgid "The access control object does not exist." +msgstr "Objekt för åtkomstkontroll finns inte." + +msgid "The access request object does not exist." +msgstr "Objekt för åtkomstbegäran finns inte." + +msgid "A first name is required." +msgstr "Ett förnamn måste anges." + +msgid "The first name should not be empty." +msgstr "Förnamnet får inte vara tomt." + +msgid "The first name should be a valid BMP-UTF8 string." +msgstr "Förnamnet bör vara en giltig BMP-UTF8-sträng." + +msgid "The first name length should be maximum {0} characters." +msgstr "Förnamnets längd bör vara högst {0} tecken." + +msgid "A last name is required." +msgstr "Ett efternamn måste anges." + +msgid "The last name should not be empty." +msgstr "Efternamnet får inte vara tomt." + +msgid "The last name should be a valid BMP-UTF8 string." +msgstr "Efternamnet bör vara en giltig BMP-UTF8-sträng." + +msgid "The last name length should be maximum {0} characters." +msgstr "Efternamnets längd bör vara högst {0} tecken." + +msgid "The name should be a valid BMP-UTF8 string." +msgstr "Namnet bör vara en giltig BMP-UTF8-sträng." + +msgid "The slug should be a valid BMP-UTF8 string." +msgstr "Sluggen bör vara en giltig BMP-UTF8-sträng." + +msgid "A slug is required." +msgstr "En slug måste fyllas." + +msgid "The slug length should be maximum {0} characters." +msgstr "Sluggens längd bör vara högst {0} tecken." + +msgid "The slug should not be empty" +msgstr "Sluggen får inte vara tom" + +msgid "The description should be a valid BMP-UTF8 string." +msgstr "Beskrivningen bör vara en giltig BMP-UTF8-sträng." + +msgid "The description length should be maximum {0} characters." +msgstr "Beskrivningens längd bör vara högst {0} tecken." + +msgid "The definition should be a valid BMP-UTF8 string." +msgstr "Definitionen bör vara en giltig BMP-UTF8-sträng." + +msgid "A definition is required." +msgstr "En definition krävs." + +msgid "The definition should not be empty." +msgstr "Definitionen får inte vara tom." + +msgid "The message should be valid JSON message." +msgstr "Meddelandet bör vara ett giltigt JSON-meddelande." + +msgid "A resource type already exists with this slug." +msgstr "Det finns redan en resurstyp med denna slug." + +msgid "A resource type already exists with this definition." +msgstr "En resurstyp finns redan med denna definition." + +msgid "The username should be a valid UTF8 string." +msgstr "Användarnamnet bör vara en giltig UTF8-sträng." + +msgid "The username length should be maximum {0} characters." +msgstr "Användarnamnet bör vara högst {0} tecken." + +msgid "The uri should be a valid BMP-UTF8 string." +msgstr "URI bör vara en giltig BMP-UTF8-sträng." + +msgid "The uri length should be maximum {0} characters." +msgstr "URI-längden bör vara maximalt {0} tecken." + +msgid "The description should be a valid UTF8 string." +msgstr "Beskrivningen bör vara en giltig UTF8-sträng." + +msgid "The deleted status should not be empty" +msgstr "Den raderade statusen får inte vara tom" + +msgid "The identifier of the user who created the resource should be a valid UUID." +msgstr "Identifieraren för användaren som skapade resursen bör vara ett giltigt UUID." + +msgid "The identifier of the user who created the resource is required." +msgstr "Identifierare för användaren som skapade resursen krävs." + +msgid "The identifier of the user who created the resource should not be empty." +msgstr "Identifieraren för användaren som skapade resursen får inte vara tom." + +msgid "The identifier of the user who last modified the resource should be a valid UUID." +msgstr "Identifierare för användaren som senast ändrade resursen bör vara ett giltigt UUID." + +msgid "The identifier of the user who last modified the resource required." +msgstr "Identifieraren av användaren som senast ändrade resursen krävs." + +msgid "The identifier of the user who last modified the resource should not be empty." +msgstr "Identifieraren för användaren som senast ändrade resursen får inte vara tom." + +msgid "The resource type identifier should be a valid UUID." +msgstr "Resurstypsidentifieraren bör vara ett giltigt UUID." + +msgid "A resource type identifier is required." +msgstr "En resurstyp-identifierare krävs." + +msgid "The permissions are required." +msgstr "Behörigheterna krävs." + +msgid "The permissions should not be empty." +msgstr "Behörigheterna får inte vara tomma." + +msgid "The permissions should contain only the permission of the owner." +msgstr "Behörigheterna får endast innehålla ägarens behörighet." + +msgid "The owner secret is required." +msgstr "Ägarens hemlighet krävs." + +msgid "The secrets should not be empty." +msgstr "Hemligheterna bör inte vara tomma." + +msgid "The secrets should contain only the secret of the owner." +msgstr "Hemligheterna bör endast innehålla ägarens hemligheter." + +msgid "The permissions should contain the owner permission." +msgstr "Behörigheterna bör innehålla behörigheten för ägaren." + +msgid "The secrets should contain the owner secret." +msgstr "Hemligheterna ska innehålla ägarens hemlighet." + +msgid "The secrets should contain the secrets of all the users having access to the resource." +msgstr "Hemligheterna ska innehålla hemligheter för alla användare som har tillgång till resursen." + +msgid "The permissions should contain at least the owner permission." +msgstr "Behörigheterna bör innehålla minst ägarens behörighet." + +msgid "The resource should not be already soft deleted." +msgstr "Resursen bör inte redan vara mjukraderad." + +msgid "The user cannot delete this resource." +msgstr "Användaren kan inte ta bort denna resurs." + +msgid "The name should be a valid ASCII string." +msgstr "Namnet bör vara en giltig ASCII-sträng." + +msgid "A role already exists for the given name." +msgstr "Det finns redan en roll för det angivna namnet." + +msgid "The role name should be from the list of allowed role names." +msgstr "Rollnamnet ska vara från listan över tillåtna rollnamn." + +msgid "The resource identifier is required." +msgstr "Resursidentifieraren krävs." + +msgid "The resource identifier should not be empty." +msgstr "Resursidentifieraren får inte vara tom." + +msgid "The message should be a valid ASCII string." +msgstr "Meddelandet ska vara en giltig ASCII-sträng." + +msgid "The message should be a valid ASCII-armored gpg message." +msgstr "Meddelandet bör vara ett giltigt ASCII-pansarat gpg-meddelande." + +msgid "A message is required." +msgstr "Ett meddelande måste anges." + +msgid "The message should not be empty." +msgstr "Meddelandet bör inte vara tomt." + +msgid "A secret already exists for the given user and resource." +msgstr "Det finns redan en hemlighet för den givna användaren och resursen." + +msgid "The user identifier by should be a valid UUID." +msgstr "Användaridentifieraren av bör vara ett giltigt UUID." + +msgid "A username is required." +msgstr "Ett användarnamn krävs." + +msgid "The username should be a valid email address." +msgstr "Användarnamnet bör vara en giltig e-postadress." + +msgid "The role identifier should be a valid UUID." +msgstr "Rollidentifieraren bör vara ett giltigt UUID." + +msgid "A role identifier is required." +msgstr "En rollidentifierare krävs." + +msgid "The profile should not be empty." +msgstr "Profilen bör inte vara tom." + +msgid "The username should not be empty." +msgstr "Användarnamnet får inte vara tomt." + +msgid "The username length should be maximum 255 characters." +msgstr "Användarnamnets längd bör vara högst 255 tecken." + +msgid "The username is already in use." +msgstr "Användarnamnet används redan." + +msgid "The role identifier does not exist." +msgstr "Rollidentifieraren finns inte." + +msgid "The user should not be sole owner of shared content, transfer the ownership to other users." +msgstr "Användaren bör inte vara ensam ägare av delat innehåll, överför ägandet till andra användare." + +msgid "The user should not be sole group manager of group(s), transfer the management to other users." +msgstr "Användaren bör inte vara ensam gruppchef för grupp(er), överför hanteringen till andra användare." + +msgid "Could not delete the user {0}, please try again later." +msgstr "Kunde inte ta bort användaren {0}, försök igen senare." + +msgid "{0} just activated their account on passbolt" +msgstr "{0} aktiverade precis sitt konto på passbolt" + +msgid "{0} commented on {1}" +msgstr "{0} kommenterade {1}" + +msgid "{0} deleted the group {1}" +msgstr "{0} tog bort gruppen {1}" + +msgid "{0} updated the group {1}" +msgstr "{0} uppdaterade gruppen {1}" + +msgid "{0} added you to the group {1}" +msgstr "{0} lade till dig i gruppen {1}" + +msgid "{0} requested you to add members to {1}" +msgstr "{0} bad dig att lägga till medlemmar i {1}" + +msgid "{0} removed you from the group {1}" +msgstr "{0} tog bort dig från gruppen {1}" + +msgid "{0} updated your membership in the group {1}" +msgstr "{0} uppdaterade ditt medlemskap i gruppen {1}" + +msgid "Your account recovery, {0}!" +msgstr "Återställning av ditt konto, {0}!" + +msgid "You added the password {0}" +msgstr "Du lade till lösenordet {0}" + +msgid "{0} deleted the password {1}" +msgstr "{0} tog bort lösenordet {1}" + +msgid "{0} edited the password {1}" +msgstr "{0} redigerade lösenordet {1}" + +msgid "{0} shared the password {1}" +msgstr "{0} delade lösenordet {1}" + +msgid "{0} deleted user {1}" +msgstr "{0} tog bort användare {1}" + +msgid "Welcome to passbolt, {0}!" +msgstr "Välkommen till passbolt, {0}!" + +msgid "{0} updated your memberships in several groups" +msgstr "{0} uppdaterade ditt medlemskap i flera grupper" + +msgid "Your membership in several groups changed in passbolt" +msgstr "Ditt medlemskap i flera grupper ändrades i passbolt" + +msgid "{0} deleted several groups" +msgstr "{0} tog bort flera grupper" + +msgid "Several groups were deleted in passbolt" +msgstr "Flera grupper togs bort i passbolt" + +msgid "{0} has made changes on several resources" +msgstr "{0} har gjort ändringar på flera resurser" + +msgid "Multiple passwords have been changed in passbolt" +msgstr "Flera lösenord har ändrats i passbolt" + +msgid "{0} shared several items with you" +msgstr "{0} delade flera objekt med dig" + +msgid "Multiple passwords have been shared with you in passbolt" +msgstr "Flera lösenord har delats med dig i passbolt" + +msgid "The purify subject setting should be a boolean." +msgstr "Den renade subjektinställningen bör vara en boolean." + +msgid "The show comment setting should be a boolean." +msgstr "Inställningen visa kommentar bör vara en boolean." + +msgid "The show description setting should be a boolean." +msgstr "Inställningen visa beskrivning bör vara en boolean." + +msgid "The show secret setting should be a boolean." +msgstr "Inställningen visa hemlighet bör vara en boolean." + +msgid "The show uri setting should be a boolean." +msgstr "Inställningen för visa URI bör vara en boolean." + +msgid "The show username setting should be a boolean." +msgstr "Inställningen för visa användarnamn bör vara en boolean." + +msgid "The send on user setup completed setting should be a boolean." +msgstr "Inställningen att skicka när användarkonfigurationen är klar måste vara en boolean." + +msgid "The send on comment added setting should be a boolean." +msgstr "Inställningen skicka när kommentar lagts till bör vara en boolean." + +msgid "The send on group deleted setting should be a boolean." +msgstr "Inställningen skicka när en grupp tagits bort bör vara en boolean." + +msgid "The send on group user added setting should be a boolean." +msgstr "Inställningen skicka när gruppanvändare lagts till bör vara en boolean." + +msgid "The send on group user deleted setting should be a boolean." +msgstr "Inställningen skicka när gruppanvändare tagits bort bör vara en boolean." + +msgid "The send on group user updated setting should be a boolean." +msgstr "Inställningen skicka när gruppanvändare uppdaterats bör vara en boolean." + +msgid "The send on group manager updated setting should be a boolean." +msgstr "Inställningen skicka när gruppchefsinställningarna uppdaterats bör vara en boolean." + +msgid "The send on password created setting should be a boolean." +msgstr "Inställningen skicka när lösenord skapats bör vara en boolean." + +msgid "The send on password shared setting should be a boolean." +msgstr "Inställningen skicka när lösenord delas bör vara en boolean." + +msgid "The send on password updated setting should be a boolean." +msgstr "Inställningen skicka när lösenord uppdaterats bör vara en boolean." + +msgid "The send on password deleted setting should be a boolean." +msgstr "Inställningen skicka när lösenord raderats bör vara en boolean." + +msgid "The send on user created setting should be a boolean." +msgstr "Inställningen skicka när användare skapats bör vara en boolean." + +msgid "The send on user recovered setting should be a boolean." +msgstr "Inställningen skicka när användare återställts bör vara en boolean." + +msgid "Validation failed for authenticationToken {0}. {1}" +msgstr "Validering misslyckades för autentiseringtoken {0}. {1}" + +msgid "Validation success for authenticationToken {0}" +msgstr "Validering lyckades för autentiseringtoken {0}" + +msgid "Could not save the avatar in the {0} file." +msgstr "" + +msgid "The avatar id is not valid." +msgstr "" + +msgid "Validation failed for comment {0}. {1}" +msgstr "Validering misslyckades för kommentar {0}. {1}" + +msgid "Validation success for comment {0}" +msgstr "Validering lyckades för kommentar {0}" + +msgid "Validation failed for favorite {0}. {1}" +msgstr "Validering misslyckades för favorit {0}. {1}" + +msgid "Validation success for favorite {0}" +msgstr "Validering lyckades för favorit {0}" + +msgid "Validation success for key {0}" +msgstr "Validering lyckades för nyckel {0}" + +msgid "Validation failed for key {0}. {1}" +msgstr "Validering misslyckades för nyckel {0}. {1}" + +msgid "Encryption success for key {0}" +msgstr "Kryptering lyckades för nyckel {0}" + +msgid "Failed to encrypt with key {0}. {1}" +msgstr "Misslyckades att kryptera med nyckeln {0}. {1}" + +msgid "Validation failed for group {0}. {1}" +msgstr "Validering misslyckades för gruppen {0}. {1}" + +msgid "Validation success for group {0}" +msgstr "Validering lyckades för grupp {0}" + +msgid "Could not validate group user data." +msgstr "Kunde inte validera användardata för grupp." + +msgid "The group user does not exist." +msgstr "Gruppanvändaren finns inte." + +msgid "At least one group manager should be provided." +msgstr "Minst en gruppchef bör tillhandahållas." + +msgid "Could not validate permission data." +msgstr "Kunde inte validera behörighetsdata." + +msgid "Validation failed for permission {0}. {1}" +msgstr "Validering misslyckades för behörighet {0}. {1}" + +msgid "Validation success for permission {0}" +msgstr "Validering lyckades för behörighet {0}" + +msgid "Could not validate permissions data." +msgstr "Kunde inte validera behörighetsdata." + +msgid "Validation failed for profile {0}. {1}" +msgstr "Validering misslyckades för profil {0}. {1}" + +msgid "Validation success for profile {0}" +msgstr "Validering lyckades för profil {0}" + +msgid "Could not validate resource data." +msgstr "Kunde inte validera resursdata." + +msgid "An unexpected error occured, please contact an administrator." +msgstr "" + +msgid "The server returned the following error:" +msgstr "" + +msgid "Validation failed for resource {0}. {1}" +msgstr "Validering misslyckades för resursen {0}. {1}" + +msgid "Validation success for resource {0}" +msgstr "Valideringen lyckades för resursen {0}" + +msgid "You are not allowed to update this resource." +msgstr "Du har inte behörighet att uppdatera denna resurs." + +msgid "Additional resource types are not enabled on this server." +msgstr "Ytterligare resurstyper är inte aktiverade på denna server." + +msgid "There should be at least 4 roles" +msgstr "Det bör finnas minst 4 roller" + +msgid "Validation failed for role {0}. {1}" +msgstr "Validering misslyckades för rollen {0}. {1}" + +msgid "Validation success for role {0}" +msgstr "Validering lyckades för rollen {0}" + +msgid "Could not validate secret data." +msgstr "Kunde inte validera hemlig data." + +msgid "Validation failed for secret {0}. {1}" +msgstr "Validering misslyckades för hemlighet {0}. {1}" + +msgid "Validation success for secret {0}" +msgstr "Valideringen lyckades för hemlighet {0}" + +msgid "Could not validate secrets data." +msgstr "Kunde inte validera hemlighetsdata." + +msgid "The secrets of all the users having access to the resource are required." +msgstr "Hemligheterna för alla användare som har tillgång till resursen krävs." + +msgid "Validation failed for user {0}. {1}" +msgstr "Validering misslyckades för användare {0}. {1}" + +msgid "Validation success for user {0}" +msgstr "Validering lyckades för användare {0}" + +msgid "Could not connect to github repository" +msgstr "Kunde inte ansluta till github repository" + +msgid "Could not read tag information on github repository" +msgstr "Kunde inte läsa tagginformation på github repository" + +msgid "The key {0} cannot be used to encrypt." +msgstr "Nyckeln {0} kan inte användas för att kryptera." + +msgid "The key {0} cannot be used to decrypt." +msgstr "Nyckeln {0} kan inte användas för att dekryptera." + +msgid "Could not use key {0} for signing." +msgstr "Kunde inte använda nyckel {0} för att signera." + +msgid "Could not parse the OpenPGP public key." +msgstr "Kunde inte tolka OpenPGP publika nyckel." + +msgid "Invalid key. No OpenPGP public key package found." +msgstr "Ogiltig nyckel. Inget OpenPGP-paket för publik nyckel hittades." + +msgid "Invalid key. No user ID found." +msgstr "Ogiltig nyckel. Inget användar-ID hittades." + +msgid "Could not import the OpenPGP key." +msgstr "Kunde inte importera OpenPGP-nyckeln." + +msgid "Could not use the key to sign and encrypt." +msgstr "Kunde inte använda nyckeln för att signera och kryptera." + +msgid "Could not use the key to encrypt." +msgstr "Kunde inte använda nyckeln för att kryptera." + +msgid "Expected {0} and got {1}." +msgstr "Förväntade {0} och fick {1}." + +msgid "Decryption failed. Invalid signature." +msgstr "Dekryptering misslyckades. Ogiltig signatur." + +msgid "The message cannot be verified." +msgstr "Meddelandet kan inte verifieras." + +msgid "Could not sign the text. " +msgstr "Kunde inte signera texten. " + +msgid "The OpenPGP server key defined in the config is not found in the file system." +msgstr "OpenPGP-serverns nyckel definierad i konfigurationen finns inte i filsystemet." + +msgid "The OpenPGP server key defined in the config cannot be opened." +msgstr "OpenPGP-serverns nyckel definierad i konfigurationen kan inte öppnas." + +msgid "The OpenPGP server key defined on file is not a valid private key." +msgstr "OpenPGP-servernyckeln definierad på filen är inte en giltig privat nyckel." + +msgid "There is an issue with the OpenPGP server key." +msgstr "Det finns ett problem med OpenPGP-serverns nyckel." + +msgid "The fingerprint does not match the one associated with the key on file." +msgstr "Fingeravtrycket matchar inte den som är associerad med nyckeln i filen." + +msgid "No OpenPGP marker found." +msgstr "Ingen OpenPGP-markör hittades." + +msgid "This is not a valid OpenPGP armored marker." +msgstr "Detta är inte en giltig OpenPGP pansarmarkör." + +msgid "The key {0} was not found in the keyring" +msgstr "Nyckeln {0} hittades inte i nyckelringen" + +msgid "A value for the theme should be provided." +msgstr "Ett värde för temat bör tillhandahållas." + +msgid "This is not a valid theme." +msgstr "Detta är inte ett giltigt tema." + +msgid "The setting type should be one of the following: {0}." +msgstr "Inställningstypen bör vara en av följande: {0}." + +msgid "A setting type is required." +msgstr "En inställningstyp krävs." + +msgid "The setting type should not be empty" +msgstr "Inställningstypen får inte vara tom" + +msgid "The setting value should be a valid UTF8 string." +msgstr "Inställningsvärdet bör vara en giltig UTF8-sträng." + +msgid "The setting value length should be maximum {0} characters." +msgstr "Inställningens värdelängd bör vara maximalt {0} tecken." + +msgid "A value setting is required." +msgstr "En värdeinställning krävs." + +msgid "The setting value should not be empty." +msgstr "Inställningsvärdet får inte vara tomt." + +msgid "The parameter {0} is not set." +msgstr "Parametern {0} är inte inställd." + +msgid "This theme is not supported." +msgstr "Detta tema stöds inte." + +msgid "You are not allowed to access this location." +msgstr "Du har inte behörighet att komma åt den här platsen." + +msgid "The notification settings for the organization were updated." +msgstr "Aviseringsinställningarna för organisationen uppdaterades." + +msgid "The challenge cannot be decrypted." +msgstr "" + +msgid "The challenge is invalid. Deserialization failed." +msgstr "" + +msgid "The challenge is invalid. Validation Failed." +msgstr "" + +msgid "The config for the server private key fingerprint is not available or incomplete." +msgstr "" + +msgid "The config for the server private key passphrase is invalid." +msgstr "" + +msgid "The user id is missing or invalid." +msgstr "" + +msgid "The user OpenPGP key does not exist, or is invalid, or has been deleted." +msgstr "" + +msgid "The user signature is invalid." +msgstr "" + +msgid "The user challenge is missing or invalid." +msgstr "" + +msgid "The version is invalid." +msgstr "" + +msgid "The domain is invalid." +msgstr "" + +msgid "The domain is invalid. Expected: {0} and got {1}" +msgstr "" + +msgid "The authentication was a success." +msgstr "" + +msgid "The authentication failed." +msgstr "" + +msgid "The credentials are missing." +msgstr "" + +msgid "The user does not exist or is not active or has been deleted." +msgstr "" + +msgid "The credentials are invalid." +msgstr "" + +msgid "An internal error occurred." +msgstr "" + +msgid "The verify token is invalid." +msgstr "" + +msgid "The route {0} is not permitted with JWT authentication." +msgstr "" + +msgid "The key pair for JWT Authentication is not complete. The following file could not be read: {0}." +msgstr "" + +msgid "The JWT public key could not be read or is not valid." +msgstr "" + +msgid "The JWT private key should be at least {0} bytes long." +msgstr "" + +msgid "The configuration {0} is not correctly set." +msgstr "" + +msgid "No active refresh token matching the request could be found." +msgstr "" + +msgid "The refresh token provided was already used." +msgstr "" + +msgid "Expired refresh token provided." +msgstr "" + +msgid "The refresh token should be a valid UUID." +msgstr "" + +msgid "This is not a valid user id." +msgstr "" + +msgid "Invalid verify token expiry." +msgstr "" + +msgid "Attempt to access an expired verify token." +msgstr "" + +msgid "Invalid verify token format." +msgstr "" + +msgid "Invalid user ID format." +msgstr "" + +msgid "Verify token has been already used in the past." +msgstr "" + +msgid "Security warning!" +msgstr "" + +msgid "An unknown user with IP: {0} attempted to identify as {1}." +msgstr "" + +msgid "This is a potential security issue." +msgstr "" + +msgid "Please investigate!" +msgstr "" + +msgid "An unknown user with IP: {0} attempted to steal your login data." +msgstr "" + +msgid "Please get in tough with one of your administrators." +msgstr "" + +msgid "This is not a valid locale." +msgstr "Detta är inte ett giltigt språk." + +msgid "This is not a valid {0}." +msgstr "Detta är inte en giltig {0}." + +msgid "The action log identifier should be a valid UUID." +msgstr "Åtgärdslogg-identifieraren bör vara ett giltigt UUID." + +msgid "The action log identifier should not be empty." +msgstr "Åtgärdslogg-identifieraren får inte vara tom." + +msgid "The action identifier should be a valid UUID." +msgstr "Åtgärds-identifieraren bör vara ett giltigt UUID." + +msgid "An action identifier is required." +msgstr "En åtgärdsidentifierare krävs." + +msgid "The context should be a valid ASCII string." +msgstr "Kontexten bör vara en giltig ASCII-sträng." + +msgid "The context length should be maximum {0} characters." +msgstr "Kontextens längd bör vara högst {0} tecken." + +msgid "A context is required." +msgstr "En kontext krävs." + +msgid "The status should be a valid boolean." +msgstr "Statusen bör vara en giltig boolean." + +msgid "A status is required." +msgstr "En status måste anges." + +msgid "Could not validate action log data." +msgstr "Kunde inte validera data för åtgärdslogg." + +msgid "The identifier should be not be empty." +msgstr "Identifieraren får inte vara tom." + +msgid "Could not validate action data." +msgstr "Kunde inte validera åtgärdsdata." + +msgid "An action log identifier is required." +msgstr "En åtgärdslogg-identifierare krävs." + +msgid "The object type should be a valid ASCII string." +msgstr "Objekttypen bör vara en giltig ASCII-sträng." + +msgid "The object type length should be maximum {0} characters." +msgstr "Objekttypens längd bör vara högst {0} tecken." + +msgid "A object type is required." +msgstr "En objekttyp krävs." + +msgid "The object type should not be empty." +msgstr "Objekttypen får inte vara tom." + +msgid "The object identifier should be a valid UUID." +msgstr "Objektidentifieraren ska vara ett giltigt UUID." + +msgid "The object identifier should not be empty." +msgstr "Objektidentifieraren får inte vara tom." + +msgid "An object identifier is required." +msgstr "En objektidentifierare krävs." + +msgid "The operation type should be one of the following: {0}." +msgstr "Operationstypen bör vara en av följande: {0}." + +msgid "An operation type is required." +msgstr "En operationstyp krävs." + +msgid "The operation type should not be empty." +msgstr "Operationstypen får inte vara tom." + +msgid "Could not validate entity history data." +msgstr "Kunde inte validera enhetshistorikdata." + +msgid "An identifier is required." +msgstr "En identifierare krävs." + +msgid "The type must be one of the following: {0}." +msgstr "Typen måste vara en av följande: {0}." + +msgid "The type is required." +msgstr "Typ måste fyllas i." + +msgid "Could not validate permission history data." +msgstr "Kunde inte validera behörighetshistorik." + +msgid "A resource identifier is required." +msgstr "En resursidentifierare krävs." + +msgid "The secret identifier should be a valid UUID." +msgstr "Hemlighetsidentifieraren bör vara ett giltigt UUID." + +msgid "A secret identifier is required." +msgstr "En hemlighetsidentifierare krävs." + +msgid "The secret identifier should not be empty." +msgstr "Hemlighetsidentifieraren får inte vara tom." + +msgid "Could not validate secret access data." +msgstr "Kunde inte validera hemlig åtkomstdata." + +msgid "Could not validate secret history data." +msgstr "Kunde inte validera hemlig historikdata." + +msgid "Information about the transfer is required." +msgstr "" + +msgid "The operation was successful" +msgstr "" + +msgid "The transfer id is not valid." +msgstr "" + +msgid "The authentication token should be a valid uuid." +msgstr "" + +msgid "The authentication token is invalid." +msgstr "" + +msgid "The transfer id should be a uuid." +msgstr "" + +msgid "The user id should be a uuid." +msgstr "" + +msgid "A user id is required" +msgstr "" + +msgid "The hash should be an ascii string." +msgstr "" + +msgid "The hash should be {0} characters in length." +msgstr "" + +msgid "The data transfer hash is required." +msgstr "" + +msgid "The current page should be a non negative integer." +msgstr "" + +msgid "The current page number is required" +msgstr "" + +msgid "The current page number should be equal or inferior to the total number of pages." +msgstr "" + +msgid "The current page cannot be greater than {0}" +msgstr "" + +msgid "The total number of pages should a non negative integer." +msgstr "" + +msgid "The total number of pages is required." +msgstr "" + +msgid "The total number of pages should be greater than 0." +msgstr "" + +msgid "The total number of pages cannot be greater than {0}" +msgstr "" + +msgid "The status should not be empty." +msgstr "" + +msgid "The status is required." +msgstr "" + +msgid "The status must be one of the following: {0}." +msgstr "" + +msgid "The user must be active." +msgstr "" + +msgid "Could not validate the transfer data." +msgstr "" + +msgid "The transfer could not be created." +msgstr "" + +msgid "Could not update the transfer data." +msgstr "" + +msgid "The transfer could not be updated." +msgstr "" + +msgid "This operation is not allowed." +msgstr "" + +msgid "Restarting a transfer is not allowed." +msgstr "" + +msgid "The operation is already over." +msgstr "" + +msgid "The current page does not match the total number of pages." +msgstr "" + +msgid "This operation is not allowed for this user." +msgstr "" + +msgid "The authentication token is missing." +msgstr "" + +msgid "The authentication token is not valid for this user." +msgstr "" + +msgid "The authentication token type is invalid." +msgstr "" + +msgid "The authentication token is not active." +msgstr "" + +msgid "The authentication token is expired." +msgstr "" + +msgid "The password generator value \"{0}\" is not valid." +msgstr "" + +msgid "5 minutes" +msgstr "5 minuter" + +msgid "15 minutes" +msgstr "15 minuter" + +msgid "30 minutes" +msgstr "30 minuter" + +msgid "1 hour" +msgstr "1 timme" + +msgid "until I log out" +msgstr "tills jag loggar ut" + +msgid "Only administrators can view reports." +msgstr "Endast administratörer kan se rapporter." + +msgid "The requested report `{0}` does not exist." +msgstr "Den begärda rapporten `{0}` finns inte." + +msgid "Description" +msgstr "Beskrivning" + +msgid "Passbolt report" +msgstr "Passbolt-rapport" + +msgid "Report name" +msgstr "Rapportera namn" + +msgid "Generated by" +msgstr "Genererad av" + +msgid "Creation date" +msgstr "Skapad den" + +msgid "All users have completed the setup." +msgstr "Alla användare har slutfört installationen." + +msgid "Name" +msgstr "Namn" + +msgid "Username" +msgstr "Användarnamn" + +msgid "Created since" +msgstr "Skapad sedan" + +msgid "Setup completed" +msgstr "Installationen slutförd" + +msgid "Role" +msgstr "Roll" + +msgid "No" +msgstr "Nej" + +msgid "The data entered are not correct" +msgstr "De angivna uppgifterna är felaktiga" + +msgid "A connection could not be established with the credentials provided." +msgstr "En anslutning kunde inte upprättas med de angivna uppgifterna." + +msgid "Please verify the settings." +msgstr "Vänligen kontrollera inställningarna." + +msgid "passbolt test email" +msgstr "passbolt test epost" + +msgid "Congratulations!" +msgstr "Grattis!" + +msgid "If you receive this email, it means that your passbolt smtp configuration is working fine." +msgstr "Om du får detta e-postmeddelande innebär det att din konfiguration för passbolt smtp fungerar bra." + +msgid "The data entered are not correct: {0}" +msgstr "De angivna uppgifterna är felaktiga: {0}" + +msgid "The session has expired. Please start the configuration again." +msgstr "Sessionen har löpt ut. Starta konfigurationen igen." + +msgid "System check" +msgstr "Systemkontroll" + +msgid "Subscription key" +msgstr "Prenumerationsnyckel" + +msgid "Database" +msgstr "Databas" + +msgid "Server keys" +msgstr "Servernycklar" + +msgid "Emails" +msgstr "E-post" + +msgid "Options" +msgstr "Alternativ" + +msgid "First user" +msgstr "Första användaren" + +msgid "Installation" +msgstr "Installation" + +msgid "That's it!" +msgstr "Det var det!" + +msgid "A host name is required." +msgstr "Ett värdnamn måste anges." + +msgid "The host name should not be empty." +msgstr "Värdnamnet får inte vara tomt." + +msgid "The host name should be a valid BMP-UTF8 string." +msgstr "Värdnamnet bör vara en giltig BMP-UTF8-sträng." + +msgid "A port number is required." +msgstr "Ett portnummer krävs." + +msgid "The port number should be numeric." +msgstr "Portnumret bör vara numeriskt." + +msgid "The port number should be between {0} and {1}." +msgstr "Portnumret bör vara mellan {0} och {1}." + +msgid "The username should be a valid BMP-UTF8 string." +msgstr "Användarnamnet bör vara en giltig BMP-UTF8-sträng." + +msgid "The password should not contain quotes." +msgstr "Lösenordet bör ej innehålla citat." + +msgid "The password should be a valid BMP-UTF8 string." +msgstr "Lösenordet bör vara en giltig BMP-UTF8-sträng." + +msgid "A database name is required." +msgstr "Ett databasnamn måste anges." + +msgid "The database name should not be empty." +msgstr "Databasnamnet får inte vara tomt." + +msgid "The database name should be a valid BMP-UTF8 string." +msgstr "Databasnamnet bör vara en giltig BMP-UTF8-sträng." + +msgid "The database name should not contain dashes." +msgstr "Databasnamnet får inte innehålla bindestreck." + +msgid "A sender name is required." +msgstr "Ett avsändarnamn måste anges." + +msgid "The sender name should not be empty." +msgstr "Avsändarnamnet får inte vara tomt." + +msgid "The sender name should be a valid BMP-UTF8 string." +msgstr "Avsändarnamnet bör vara en giltig BMP-UTF8-sträng." + +msgid "A sender email is required." +msgstr "En e-postadress för avsändaren måste anges." + +msgid "The sender email should not be empty." +msgstr "Avsändarens e-post får inte vara tom." + +msgid "The sender email should be a valid BMP-UTF8 string." +msgstr "Avsändarens e-post bör vara en giltig BMP-UTF8-sträng." + +msgid "The sender email should be a valid email address." +msgstr "Avsändarens e-postadress bör vara en giltig e-postadress." + +msgid "A TLS setting is required." +msgstr "En TLS-inställning måste anges." + +msgid "The TLS setting should be a valid boolean." +msgstr "TLS-inställningen bör vara en giltig boolean." + +msgid "A password is required." +msgstr "Lösenordet måste fyllas i." + +msgid "The test email should be a valid email address." +msgstr "Testmeddelandet bör vara en giltig e-postadress." + +msgid "An OpenPGP public key is required." +msgstr "En publik OpenPGP-nyckel krävs." + +msgid "The OpenPGP public key should not be empty." +msgstr "Den publika nyckeln för OpenPGP får inte vara tom." + +msgid "The OpenPGP public key should be a valid ASCII string." +msgstr "Den publika nyckeln för OpenPGP bör vara en giltig ASCII-sträng." + +msgid "The key is not a valid OpenPGP public key." +msgstr "Nyckeln är inte en giltig publik nyckel för OpenPGP." + +msgid "The OpenPGP public key should not have an expiry date." +msgstr "Den publika nyckeln för OpenPGP bör ej ha ett utgångsdatum." + +msgid "The OpenPGP public key cannot be used to encrypt." +msgstr "Den publika nyckeln för OpenPGP kan inte användas för att kryptera." + +msgid "An OpenPGP private key is required." +msgstr "En privat nyckel för OpenPGP krävs." + +msgid "The OpenPGP private key should not be empty." +msgstr "Den privata nyckeln för OpenPGP får ej vara tom." + +msgid "The OpenPGP private key should be a valid ASCII string." +msgstr "Den privata nyckeln för OpenPGP bör vara en giltig ASCII-sträng." + +msgid "The value is not a valid OpenPGP private key." +msgstr "Värdet är inte en giltig privat nyckel för OpenPGP." + +msgid "The OpenPGP private key should not have an expiry date." +msgstr "Den privata nyckeln för OpenPGP bör inte ha ett utgångsdatum." + +msgid "The OpenPGP private key cannot be used to decrypt." +msgstr "Den privata nyckeln för OpenPGP kan inte användas för att dekryptera." + +msgid "Please note that passbolt does not support OpenPGP key protected with a secret." +msgstr "Observera att passbolt inte har stöd för OpenPGP-nyckel som skyddas med en hemlighet." + +msgid "A fingerprint is required." +msgstr "Ett fingeravtryck krävs." + +msgid "The fingerprint should not be empty." +msgstr "Fingeravtrycket får inte vara tomt." + +msgid "The fingerprint should be a valid alphanumeric string." +msgstr "Fingeravtrycket bör vara en giltig alfanumerisk sträng." + +msgid "The fingerprint does not match the OpenPGP public and the OpenPGP private keys fingerprints." +msgstr "Fingeravtrycket matchar inte fingeravtrycken för publika eller privata nycklar för OpenPGP." + +msgid "A full base url is required." +msgstr "En fullständig bas-URL krävs." + +msgid "The full base url should not be empty" +msgstr "Den fullständiga bas-URL: en får inte vara tom" + +msgid "The full base url should be a valid BMP-UTF8 string." +msgstr "Den fullständiga bas-URL: en bör vara en giltig BMP-UTF8-sträng." + +msgid "A public registration setting is required." +msgstr "En offentlig registreringsinställning krävs." + +msgid "The public registration setting should be a valid boolean." +msgstr "Inställningen för offentlig registrering ska vara en giltig boolean." + +msgid "A force ssl status is required." +msgstr "En tvingande ssl status krävs." + +msgid "The force ssl setting should be a valid boolean." +msgstr "Den tvingande ssl inställningen bör vara en giltig boolean." + +msgid "The database schema does not match the one expected" +msgstr "Databasschemat matchar inte det förväntade" + +msgid "There was a problem creating the first user" +msgstr "Ett problem uppstod när den första användaren skulle skapas" + +msgid "There was a problem creating the registration token" +msgstr "Det gick inte att skapa registreringstoken" + +msgid "The passbolt config is writable." +msgstr "Konfigurationen för passbolt är skrivbar." + +msgid "The passbolt config is not writable." +msgstr "Konfigurationen för passbolt är inte skrivbar." + +msgid "Ensure the file " +msgstr "Se till att filen " + +msgid "you can try:" +msgstr "du kan prova:" + +msgid "The server OpenPGP public key file is writable." +msgstr "Serverns publika OpenPGP nyckel-fil är skrivbar." + +msgid "The server OpenPGP public key file is not writable." +msgstr "Serverns publika OpenPGP nyckel-fil är inte skrivbar." + +msgid "The server OpenPGP private key file is writable." +msgstr "Serverns privata OpenPGP nyckel-fil är skrivbar." + +msgid "The server OpenPGP private key file is not writable." +msgstr "Serverns privata OpenPGP nyckel-fil är inte skrivbar." + +msgid "Create your user account!" +msgstr "Skapa ditt användarkonto!" + +msgid "Admin user details" +msgstr "Admin-användaruppgifter" + +msgid "First name" +msgstr "Förnamn" + +msgid "Last name" +msgstr "Efternamn" + +msgid "mail@yourdomain.com" +msgstr "mail@dindomän.com" + +msgid "Next" +msgstr "Nästa" + +msgid "Enter your database details." +msgstr "Ange din databasinformation." + +msgid "Database configuration" +msgstr "Databaskonfiguration" + +msgid "An existing database has been detected on your server. The connection details are pre-filled below." +msgstr "En befintlig databas har upptäckts på din server. Anslutningsdetaljerna är ifyllda nedan." + +msgid "You can keep it as it is (default), or replace it with the details of another database." +msgstr "Du kan behålla den som den är (förvalt) eller ersätta det med detaljer från en annan databas." + +msgid "Database connection url" +msgstr "Anslutnings-URL för databas" + +msgid "host name or ip address" +msgstr "värdnamn eller IP-adress" + +msgid "username" +msgstr "användarnamn" + +msgid "password" +msgstr "lösenord" + +msgid "Password" +msgstr "Lösenord" + +msgid "database name" +msgstr "databasens namn" + +msgid "Database name" +msgstr "Databasens namn" + +msgid "Cancel" +msgstr "Avbryt" + +msgid "Enter your SMTP server settings." +msgstr "Ange inställningarna för din SMTP-server." + +msgid "Email configuration" +msgstr "E-postkonfiguration" + +msgid "admin or company name" +msgstr "administratör eller företagsnamn" + +msgid "Sender name" +msgstr "Avsändarnamn" + +msgid "email@company.com" +msgstr "epost@företag.com" + +msgid "Sender email" +msgstr "Avsändarens e-postadress" + +msgid "SMTP server configuration" +msgstr "Konfiguration av SMTP-server" + +msgid "SMTP host" +msgstr "SMTP-värd" + +msgid "Use TLS?" +msgstr "Använd TLS?" + +msgid "port" +msgstr "port" + +msgid "Port" +msgstr "Port" + +msgid "Passbolt is not configured." +msgstr "Passbolt har inte konfigurerats." + +msgid "Passbolt is not configured yet!" +msgstr "Passbolt har inte konfigurerats ännu!" + +msgid "If you see this page, it means that passbolt is present on your server but not configured. Click on \"Get Started\" to launch the configuration wizard." +msgstr "Om du ser den här sidan betyder det att passbolt finns på din server men inte konfigurerad. Klicka på \"Kom igång\" för att öppna konfigurationsguiden." + +msgid "Get Started" +msgstr "Kom igång" + +msgid "Create a new server OpenPGP key or {0} an existing one." +msgstr "Skapa en ny server OpenPGP-nyckel eller {0} en befintlig." + +msgid "import" +msgstr "importera" + +msgid "Create a new OpenPGP key for your server" +msgstr "Skapa en ny OpenPGP-nyckel för din server" + +msgid "My company server name" +msgstr "Mitt namn på företagsservern" + +msgid "Server Name" +msgstr "Servernamn" + +msgid "admin@your-server.com" +msgstr "admin@din-server.com" + +msgid "Server Email" +msgstr "E-postadress till server" + +msgid "add a comment (optional)" +msgstr "lägg till en kommentar (valfritt)" + +msgid "Comment" +msgstr "Kommentar" + +msgid "Import an existing key or {0} a new one." +msgstr "Importera en befintlig nyckel eller {0} en ny." + +msgid "create" +msgstr "skapa" + +msgid "Copy paste the OpenPGP private key below" +msgstr "Kopiera och klistra in den privata OpenPGP-nyckeln nedan" + +msgid "Browse" +msgstr "Utforska" + +msgid "Or select a file from your computer" +msgstr "Eller välj en fil från din dator" + +msgid "Some binary things are happening..." +msgstr "Några binära saker händer..." + +msgid "Passbolt Pro activation." +msgstr "Aktivering av Passbolt Pro." + +msgid "Copy paste your Passbolt Pro subscription key here" +msgstr "Kopiera och klistra in din Passbolt Pro-prenumerationsnyckel här" + +msgid "Choose your preferences." +msgstr "Välj dina preferenser." + +msgid "Full Base Url" +msgstr "Fullständig Bas URL" + +msgid "Full base url" +msgstr "Fullständig bas-URL" + +msgid "This is the url where passbolt will be accessible. This url will be used for places where the passbolt url cannot be guessed automatically, such as links in emails. No trailing slash." +msgstr "Detta är URL: en där passbolt kommer att vara tillgänglig. Denna url kommer att användas för platser där passbolts URL inte kan gissas automatiskt, till exempel länkar i e-postmeddelanden. Inget avslutande snedstreck." + +msgid "Allow public registration?" +msgstr "Tillåt offentlig registrering?" + +msgid "Allowing public registration means that any visitor can create himself an account on your passbolt. Unless your instance of passbolt is not reachable by the outside world, it is usually a bad idea." +msgstr "Att tillåta offentlig registrering innebär att alla besökare kan skapa sig ett konto på din passbolt. Om inte din instans av passbolt inte kan nås av omvärlden, är det oftast en dålig idé." + +msgid "Force SSL?" +msgstr "Tvinga SSL?" + +msgid "Forcing SSL means that passbolt will not accept connections coming from a non secure protocol. If SSL is forced, your server has to be configured for HTTPS. It is highly recommended that you do so." +msgstr "Att tvinga SSL innebär att passbolt inte kommer att acceptera anslutningar som kommer från ett icke säkert protokoll. Om SSL är tvingat måste din server konfigureras för HTTPS. Det rekommenderas starkt att du gör det." + +msgid "Welcome to Passbolt Pro! Let's get started with the configuration." +msgstr "Välkommen till Passbolt Pro! Kom igång med konfigurationen." + +msgid "Nice one! Your environment is ready for passbolt." +msgstr "Bra jobbat! Din miljö är redo för passbolt." + +msgid "Oops!! Passbolt cannot run yet on your server." +msgstr "Hoppsan! Passbolt kan inte köras ännu på din server." + +msgid "Environment is configured correctly." +msgstr "Miljön är korrekt konfigurerad." + +msgid "URL rewriting is not properly configured on your server." +msgstr "URL-omskrivningen är inte korrekt konfigurerad på din server." + +msgid "GPG is configured correctly." +msgstr "GPG är korrekt konfigurerad." + +msgid "GPG Configuration" +msgstr "Konfiguration av GPG" + +msgid "SSL" +msgstr "SSL" + +msgid "SSL access is enabled." +msgstr "SSL-åtkomst är aktiverad." + +msgid "SSL access is not enabled. You can still proceed, but it is highly recommended that you configure your web server to use HTTPS before you continue." +msgstr "SSL-åtkomst är inte aktiverad. Du kan fortfarande gå vidare men det rekommenderas starkt att du konfigurerar din webbserver för att använda HTTPS innan du fortsätter." + +msgid "Start configuration" +msgstr "Börja konfigurationen" + +msgid "The configuration is complete" +msgstr "Konfigurationen är slutförd" + +msgid "Success!" +msgstr "Klart!" + +msgid "You have completed successfully the configuration procedure, congrats!" +msgstr "Du har slutfört konfigurationsproceduren! Grattis!" + +msgid "You will soon be redirected to passbolt to complete your account setup." +msgstr "Du kommer snart att omdirigeras till passbolt för att slutföra dina kontoinställningar." + +msgid "You will soon be redirected to the login page." +msgstr "Du kommer snart att omdirigeras till inloggningssidan." + +msgid "next" +msgstr "nästa" + +msgid "Click here if you can't wait" +msgstr "Klicka här om du inte kan vänta" + +msgid "Errors details" +msgstr "Detaljerad felinformation" + +msgid "Installing" +msgstr "Installerar" + +msgid "Existing installation?" +msgstr "" + +msgid "If you want to use an existing passbolt database, the installer will take care of updating it to the current passbolt version while keeping your data." +msgstr "" + +msgid "As a precaution, do not forget to backup your database before you continue." +msgstr "" + +msgid "Help" +msgstr "Hjälp" + +msgid "Need help? You can find more information on how to install passbolt in the official online help." +msgstr "Behöver du hjälp? Du hittar mer information om hur du installerar passbolt i den officiella online-hjälpen." + +msgid "What subscription key?" +msgstr "Vilken prenumerationsnyckel?" + +msgid "The subscription key is a file that was sent to you when you purchased Passbolt Pro. We need the subscription key in order to activate the Pro features of passbolt." +msgstr "Prenumerationsnyckeln är en fil som skickades till dig när du köpte Passbolt Pro. Vi behöver en prenumerationsnyckeln för att kunna aktivera Pro-funktionerna i passbolt." + +msgid "Send test email" +msgstr "Skicka testmeddelande" + +msgid "The test email has been sent successfully!" +msgstr "Testmeddelandet har skickats!" + +msgid "Email could not be sent:" +msgstr "Det gick inte att skicka e-post:" + +msgid "See trace" +msgstr "Se spårning" + +msgid "Your email address" +msgstr "Din e-postadress" + +msgid "Why do I need a SMTP server?" +msgstr "" + +msgid "Passbolt needs an smtp server in order to send invitation emails after an account creation and to send email notifications." +msgstr "" + +msgid "The requested address was not found on this server." +msgstr "Den begärda adressen hittades inte på denna server." + +msgid "Please double check the url." +msgstr "Vänligen dubbelkolla URL: en." + +msgid "Maybe the page was deleted or moved." +msgstr "Sidan har kanske tagits bort eller flyttats." + +msgid "Health checks" +msgstr "Hälsokontroller" + +msgid "Passbolt API Status" +msgstr "Passbolt API Status" + +msgid "SSL access is not enabled." +msgstr "SSL-åtkomst är inte aktiverad." + +msgid "Group manager" +msgstr "Gruppchef" + +msgid "Member" +msgstr "Medlem" + +msgid "Updated roles" +msgstr "" + +msgid "is now group manager" +msgstr "är nu gruppchef" + +msgid "is not anymore group manager" +msgstr "är inte längre gruppchef" + +msgid "Terms" +msgstr "Villkor" + +msgid "Privacy" +msgstr "Sekretess" + +msgid "Credits" +msgstr "Tack till" + +msgid "Versions" +msgstr "Versioner" + +msgid "home" +msgstr "hem" + +msgid "login" +msgstr "logga in" + +msgid "register" +msgstr "registrera" + +msgid "You have initiated an account recovery!" +msgstr "Du har startat en kontoåterställning!" + +msgid "Welcome back!" +msgstr "Välkommen tillbaka!" + +msgid "You just requested to recover your passbolt account on this device." +msgstr "Du har just begärt återställning av ditt passboltkonto på den här enheten." + +msgid "Make sure you have a backup of your secret key handy." +msgstr "Se till att du har en säkerhetskopia av din hemliga nyckel till handa." + +msgid "Click on the link below to proceed." +msgstr "Klicka på länken nedan för att fortsätta." + +msgid "start recovery" +msgstr "påbörja återställning" + +msgid "{0} just created an account for you on passbolt!" +msgstr "{0} har precis skapat ett konto åt dig på passbolt!" + +msgid "Welcome {0}" +msgstr "Välkommen {0}" + +msgid "{0} just invited you to join passbolt at {1}" +msgstr "{0} har precis bjudit in dig att gå med i passbolt på {1}" + +msgid "Passbolt is an open source password manager." +msgstr "Passbolt är en lösenordshanterare med öppen källkod." + +msgid "It is designed to allow sharing credentials securely with your team!" +msgstr "Den är utformad för att tillåta säker delning av uppgifter med ditt team!" + +msgid "Let's take the next five minutes to get you started!" +msgstr "Ta fem minuter för att komma igång!" + +msgid "get started" +msgstr "kom igång" + +msgid "You just created your account on passbolt!" +msgstr "Du har precis skapat ditt konto på passbolt!" + +msgid "You just opened an account on passbolt at {0}." +msgstr "Du har precis öppnat ett konto på passbolt på {0}." + +msgid "{0} requested you to add members to a group" +msgstr "{0} bad dig att lägga till medlemmar i en grupp" + +msgid "Group: {0}" +msgstr "Grupp: {0}" + +msgid "The following members should be added:" +msgstr "Följande medlemmar bör läggas till:" + +msgid "Edit group members" +msgstr "Redigera gruppmedlemmar" + +msgid "log in passbolt" +msgstr "logga in passbolt" + +msgid "{0} deleted the user {1}" +msgstr "{0} tog bort användaren {1}" + +msgid "The user {0} {1} ({2}) is now deleted from your organisation in passbolt." +msgstr "Användaren {0} {1} ({2}) tas nu bort från din organisation i passbolt." + +msgid "This user was a member of the following group(s) you manage:" +msgstr "Den här användaren var medlem i följande grupp(er) som du hanterar:" + +msgid "view it in passbolt" +msgstr "visa i passbolt" + +msgid "{0} deleted a group" +msgstr "{0} tog bort en grupp" + +msgid "{0} deleted the group \"{1}\" you were a member of." +msgstr "{0} tog bort gruppen \"{1}\" som du var medlem i." + +msgid "All passwords that were shared only with this group were also deleted." +msgstr "Alla lösenord som endast delades med denna grupp togs också bort." + +msgid "As member of the group you now have access to all the passwords that are shared with this group." +msgstr "Som medlem i gruppen har du nu tillgång till alla lösenord som delas med denna grupp." + +msgid "And as group manager you are also authorized to edit the members of the group." +msgstr "Som gruppchef har du också behörighet att redigera medlemmarna i denna grupp." + +msgid "You are no longer a member of this group." +msgstr "Du är inte längre medlem i denna grupp." + +msgid "You are no longer authorized to access the passwords shared with this group." +msgstr "Du har inte längre behörighet att komma åt lösenorden som delas med denna grupp." + +msgid "Please contact {0} or another group manager if this is a mistake." +msgstr "Vänligen kontakta {0} eller en annan gruppchef om detta är ett misstag." + +msgid "You are now a group manager of this group." +msgstr "Du är nu gruppchef i den här gruppen." + +msgid "As group manager you are now authorized to edit the members of this group." +msgstr "Som gruppchef har du nu behörighet att redigera medlemmarna i denna grupp." + +msgid "As member of the group you still have access to all the passwords that are shared with this group." +msgstr "Som medlem i gruppen har du fortfarande tillgång till alla lösenord som delas med denna grupp." + +msgid "You are no longer a group manager of this group." +msgstr "Du är inte längre gruppchef i denna grupp." + +msgid "You are no longer authorized to edit the members of this group." +msgstr "Du har inte längre behörighet att redigera medlemmarna i denna grupp." + +msgid "Edited your membership in several groups" +msgstr "Redigerade ditt medlemskap i flera grupper" + +msgid "{0} group memberships were affected." +msgstr "{0} gruppmedlemskap påverkades." + +msgid "It would be too much to list them here, but you can get more information on passbolt." +msgstr "Det skulle vara för mycket att lista dem här men du kan få mer information på passbolt." + +msgid "go to passbolt" +msgstr "gå till passbolt" + +msgid "{0} deleted several group" +msgstr "{0} raderade flera grupper" + +msgid "{0} deleted {1} groups you were a member of." +msgstr "{0} tog bort {1} grupper som du var medlem av." + +msgid "You have saved a new password" +msgstr "Du har sparat ett nytt lösenord" + +msgid "Name: {0}" +msgstr "Namn: {0}" + +msgid "Username: {0}" +msgstr "Användarnamn: {0}" + +msgid "URL: {0}" +msgstr "URL: {0}" + +msgid "Description: {0}" +msgstr "Beskrivning: {0}" + +msgid "{0} shared a password with you" +msgstr "{0} har delat ett lösenord med dig" + +msgid "{0} updated the password {1}" +msgstr "{0} uppdaterade lösenordet {1}" + +msgid "Edited multiple resources" +msgstr "Redigerat flera resurser" + +msgid "{0} resources were affected." +msgstr "{0} resurser påverkades." + +msgid "It would be too much to list them here, but you can go check them on passbolt." +msgstr "Det skulle vara för mycket att lista dem här men du kan gå och kontrollera dem på passbolt." + +msgid "view them in passbolt" +msgstr "visa dem i passbolt" + +msgid "{0} shared passwords with you" +msgstr "{0} delade lösenord med dig" + +msgid "{0} resources were shared with you." +msgstr "{0} resurser har delats med dig." + +msgid "{0} just activated their account on passbolt!" +msgstr "{0} aktiverade precis sitt konto på passbolt!" + +msgid "The user is now active on passbolt and you can share passwords with them." +msgstr "Användaren är nu aktiv på passbolt och du kan dela lösenord med dem." + +msgid "This user was invited by you {0}." +msgstr "Den här användaren bjöds in av dig {0}." + +msgid "This user signed up themselves, since the public registration is enabled." +msgstr "Denna användare registrerade sig själv, eftersom den offentliga registreringen är aktiverad." + +msgid "This user was invited by {0} {1}." +msgstr "Den här användaren blev inbjuden av {0} {1}." + +msgid "view users" +msgstr "visa användare" + +msgid "The locale {0} is not valid or not supported." +msgstr "Språket {0} är inte giltigt eller stöds inte." + +msgid "An Internal Error Has Occurred" +msgstr "Ett internt fel har uppstått" + diff --git a/rpm/CHANGELOG.md b/rpm/CHANGELOG.md new file mode 100644 index 0000000000..c30c78dccd --- /dev/null +++ b/rpm/CHANGELOG.md @@ -0,0 +1,27 @@ +# Change Log +All notable changes to this project will be documented in this file. +This project adheres to [Semantic Versioning](http://semver.org/). + +* [3.4.0] Tue Dec 07 2021 Gerold Mougenel +- PB-9826 As a user I want to use passbolt natively on Edge +- PB-8371 As LU I want to see the login/MFA/recover/register screens in dark mode +- PB-8522 As LU I should see the MFA verify field having focus +- PB-9730 As AD I should be able to check avatars read issues from the healthcheck +- PB-8932 Fix as LU I should see an animation when I successfully configured MFA +- PB-9286 Fix as LU I should see the locale dropdown field of the setup/recover screen well positioned +- PB-9397 Fix as AD I shouldn't see an error on the healthcheck if the JWT auth is disabled and I never configured it +- PB-9114 Fix as lu I should be able to upload a transparent avatar in .png format. +- PB-9750 Fix spelling mistakes reported by the community +- PB-9762 Fix requesting /auth/login.json should not trigger an unexpected error +- PB-9888 Fix MFA & JWT refresh token issue, remove Bearer from the hashed session identifier +- PB-12817 Fix as LU I should be able to update jpeg avatar +- PB-7374 As soft deleted but logged in user I should be forbidden to request the API +- PB-9340 Fix email queue data should be stored and deserialized as json and not php +- PB-9311 Refactor JWT and MFA plugins for better code maintainability. +- PB-8320 Implement the tests that are marked as incomplete for cleaner continuous integration test reports +- PB-8211 Psalm set to level 4 +- PB-9726 Fix do not load cleanup tasks unless in CLI mode +- PB-9753 Improve table fields validation tests, do not save entity when testing the validation of properties +- PB-9310 Move avatar file_storage logic into AvatarsTable +- PB-9785 Update JWT healthcheck help messages +- PB-9656 Migrate fields from utf8mb4 to a more performant encoding when possible diff --git a/rpm/Dockerfile b/rpm/Dockerfile new file mode 100644 index 0000000000..56cc403ed9 --- /dev/null +++ b/rpm/Dockerfile @@ -0,0 +1,5 @@ +# docker build --build-arg FROM=dokken/fedora-34 -t anatomicjc/fedora:34 . +ARG FROM +FROM ${FROM} +RUN dnf install systemd libxcrypt-compat -y \ + && dnf clean all \ No newline at end of file diff --git a/rpm/Dockerfile-opensuse b/rpm/Dockerfile-opensuse new file mode 100644 index 0000000000..df0a44b3a2 --- /dev/null +++ b/rpm/Dockerfile-opensuse @@ -0,0 +1,4 @@ +# docker build --build-arg FROM=opensuse/leap:15 -t anatomicjc/opensuse-leap:15 -f Dockerfile-opensuse . +ARG FROM=opensuse/leap:15 +FROM ${FROM} +RUN zypper install -y systemd \ No newline at end of file diff --git a/rpm/_passbolt-configure/conf/nginx/passbolt.conf b/rpm/_passbolt-configure/conf/nginx/passbolt.conf new file mode 100644 index 0000000000..ed5e370555 --- /dev/null +++ b/rpm/_passbolt-configure/conf/nginx/passbolt.conf @@ -0,0 +1,33 @@ +server { + listen [::]:80; + listen 80; + server_name _SERVER_NAME_; + client_body_buffer_size 100K; + client_header_buffer_size 1K; + client_max_body_size 5M; + + client_body_timeout 10; + client_header_timeout 10; + keepalive_timeout 5 5; + send_timeout 10; + + root /usr/share/php/passbolt/webroot; + index index.php; + + location / { + try_files $uri $uri/ /index.php?$args; + } + + location ~ \.php$ { + try_files $uri =404; + include fastcgi_params; + fastcgi_pass php-fpm; + fastcgi_index index.php; + fastcgi_intercept_errors on; + fastcgi_split_path_info ^(.+\.php)(.+)$; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param SERVER_NAME $http_host; + fastcgi_param PHP_VALUE "upload_max_filesize=5M \n post_max_size=5M"; + } + +} diff --git a/rpm/_passbolt-configure/conf/nginx/passbolt_ssl.conf b/rpm/_passbolt-configure/conf/nginx/passbolt_ssl.conf new file mode 100644 index 0000000000..6957e91e54 --- /dev/null +++ b/rpm/_passbolt-configure/conf/nginx/passbolt_ssl.conf @@ -0,0 +1,39 @@ +server { + listen [::]:443 ssl http2; + listen 443 ssl http2; + server_name _SERVER_NAME_; + client_body_buffer_size 100K; + client_header_buffer_size 1k; + client_max_body_size 5M; + + client_body_timeout 10; + client_header_timeout 10; + keepalive_timeout 5 5; + send_timeout 10; + ssl_certificate _NGINX_CERT_FILE_; + ssl_certificate_key _NGINX_KEY_FILE_; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; + ssl_prefer_server_ciphers off; + ssl_session_tickets off; + + root /usr/share/php/passbolt/webroot; + index index.php; + + location / { + try_files $uri $uri/ /index.php?$args; + } + + location ~ \.php$ { + try_files $uri =404; + include fastcgi_params; + fastcgi_pass php-fpm; + fastcgi_index index.php; + fastcgi_intercept_errors on; + fastcgi_split_path_info ^(.+\.php)(.+)$; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param SERVER_NAME $http_host; + fastcgi_param PHP_VALUE "upload_max_filesize=5M \n post_max_size=5M"; + } + +} diff --git a/rpm/_passbolt-configure/conf/packages.txt b/rpm/_passbolt-configure/conf/packages.txt new file mode 100644 index 0000000000..8d1109992f --- /dev/null +++ b/rpm/_passbolt-configure/conf/packages.txt @@ -0,0 +1,17 @@ +php-intl +php-gd +php-mysql +php-pear +php-devel +php-mbstring +php-fpm +php-ldap +gcc +gpgme-devel +git +policycoreutils-python +nginx +unzip +wget +certbot +python2-certbot-nginx diff --git a/rpm/_passbolt-configure/conf/php/www.conf b/rpm/_passbolt-configure/conf/php/www.conf new file mode 100644 index 0000000000..5692227d8d --- /dev/null +++ b/rpm/_passbolt-configure/conf/php/www.conf @@ -0,0 +1,430 @@ +; Start a new pool named 'www'. +; the variable $pool can be used in any directive and will be replaced by the +; pool name ('www' here) +[www] + +; Per pool prefix +; It only applies on the following directives: +; - 'access.log' +; - 'slowlog' +; - 'listen' (unixsocket) +; - 'chroot' +; - 'chdir' +; - 'php_values' +; - 'php_admin_values' +; When not set, the global prefix (or /usr) applies instead. +; Note: This directive can also be relative to the global prefix. +; Default Value: none +;prefix = /path/to/pools/$pool + +; Unix user/group of processes +; Note: The user is mandatory. If the group is not set, the default user's group +; will be used. +user = _WWW_USER_ +group = _WWW_USER_ + +; The address on which to accept FastCGI requests. +; Valid syntaxes are: +; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific IPv4 address on +; a specific port; +; '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on +; a specific port; +; 'port' - to listen on a TCP socket to all addresses +; (IPv6 and IPv4-mapped) on a specific port; +; '/path/to/unix/socket' - to listen on a unix socket. +; Note: This value is mandatory. +listen = /run/php-fpm/www.sock + +; Set listen(2) backlog. +; Default Value: 511 (-1 on FreeBSD and OpenBSD) +;listen.backlog = 511 + +; Set permissions for unix socket, if one is used. In Linux, read/write +; permissions must be set in order to allow connections from a web server. Many +; BSD-derived systems allow connections regardless of permissions. +; Default Values: user and group are set as the running user +; mode is set to 0660 +listen.owner = _WWW_USER_ +listen.group = _WWW_GROUP_ +;listen.mode = 0660 +; When POSIX Access Control Lists are supported you can set them using +; these options, value is a comma separated list of user/group names. +; When set, listen.owner and listen.group are ignored +;listen.acl_users = +;listen.acl_groups = + +; List of addresses (IPv4/IPv6) of FastCGI clients which are allowed to connect. +; Equivalent to the FCGI_WEB_SERVER_ADDRS environment variable in the original +; PHP FCGI (5.2.2+). Makes sense only with a tcp listening socket. Each address +; must be separated by a comma. If this value is left blank, connections will be +; accepted from any ip address. +; Default Value: any +;listen.allowed_clients = 127.0.0.1 + +; Specify the nice(2) priority to apply to the pool processes (only if set) +; The value can vary from -19 (highest priority) to 20 (lower priority) +; Note: - It will only work if the FPM master process is launched as root +; - The pool processes will inherit the master process priority +; unless it specified otherwise +; Default Value: no set +; process.priority = -19 + +; Set the process dumpable flag (PR_SET_DUMPABLE prctl) even if the process user +; or group is differrent than the master process user. It allows to create process +; core dump and ptrace the process for the pool user. +; Default Value: no +; process.dumpable = yes + +; Choose how the process manager will control the number of child processes. +; Possible Values: +; static - a fixed number (pm.max_children) of child processes; +; dynamic - the number of child processes are set dynamically based on the +; following directives. With this process management, there will be +; always at least 1 children. +; pm.max_children - the maximum number of children that can +; be alive at the same time. +; pm.start_servers - the number of children created on startup. +; pm.min_spare_servers - the minimum number of children in 'idle' +; state (waiting to process). If the number +; of 'idle' processes is less than this +; number then some children will be created. +; pm.max_spare_servers - the maximum number of children in 'idle' +; state (waiting to process). If the number +; of 'idle' processes is greater than this +; number then some children will be killed. +; ondemand - no children are created at startup. Children will be forked when +; new requests will connect. The following parameter are used: +; pm.max_children - the maximum number of children that +; can be alive at the same time. +; pm.process_idle_timeout - The number of seconds after which +; an idle process will be killed. +; Note: This value is mandatory. +pm = dynamic + +; The number of child processes to be created when pm is set to 'static' and the +; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'. +; This value sets the limit on the number of simultaneous requests that will be +; served. Equivalent to the ApacheMaxClients directive with mpm_prefork. +; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP +; CGI. The below defaults are based on a server without much resources. Don't +; forget to tweak pm.* to fit your needs. +; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand' +; Note: This value is mandatory. +pm.max_children = 5 + +; The number of child processes created on startup. +; Note: Used only when pm is set to 'dynamic' +; Default Value: min_spare_servers + (max_spare_servers - min_spare_servers) / 2 +pm.start_servers = 2 + +; The desired minimum number of idle server processes. +; Note: Used only when pm is set to 'dynamic' +; Note: Mandatory when pm is set to 'dynamic' +pm.min_spare_servers = 1 + +; The desired maximum number of idle server processes. +; Note: Used only when pm is set to 'dynamic' +; Note: Mandatory when pm is set to 'dynamic' +pm.max_spare_servers = 3 + +; The number of seconds after which an idle process will be killed. +; Note: Used only when pm is set to 'ondemand' +; Default Value: 10s +;pm.process_idle_timeout = 10s; + +; The number of requests each child process should execute before respawning. +; This can be useful to work around memory leaks in 3rd party libraries. For +; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS. +; Default Value: 0 +;pm.max_requests = 500 + +; The URI to view the FPM status page. If this value is not set, no URI will be +; recognized as a status page. It shows the following informations: +; pool - the name of the pool; +; process manager - static, dynamic or ondemand; +; start time - the date and time FPM has started; +; start since - number of seconds since FPM has started; +; accepted conn - the number of request accepted by the pool; +; listen queue - the number of request in the queue of pending +; connections (see backlog in listen(2)); +; max listen queue - the maximum number of requests in the queue +; of pending connections since FPM has started; +; listen queue len - the size of the socket queue of pending connections; +; idle processes - the number of idle processes; +; active processes - the number of active processes; +; total processes - the number of idle + active processes; +; max active processes - the maximum number of active processes since FPM +; has started; +; max children reached - number of times, the process limit has been reached, +; when pm tries to start more children (works only for +; pm 'dynamic' and 'ondemand'); +; Value are updated in real time. +; Example output: +; pool: www +; process manager: static +; start time: 01/Jul/2011:17:53:49 +0200 +; start since: 62636 +; accepted conn: 190460 +; listen queue: 0 +; max listen queue: 1 +; listen queue len: 42 +; idle processes: 4 +; active processes: 11 +; total processes: 15 +; max active processes: 12 +; max children reached: 0 +; +; By default the status page output is formatted as text/plain. Passing either +; 'html', 'xml' or 'json' in the query string will return the corresponding +; output syntax. Example: +; http://www.foo.bar/status +; http://www.foo.bar/status?json +; http://www.foo.bar/status?html +; http://www.foo.bar/status?xml +; +; By default the status page only outputs short status. Passing 'full' in the +; query string will also return status for each pool process. +; Example: +; http://www.foo.bar/status?full +; http://www.foo.bar/status?json&full +; http://www.foo.bar/status?html&full +; http://www.foo.bar/status?xml&full +; The Full status returns for each process: +; pid - the PID of the process; +; state - the state of the process (Idle, Running, ...); +; start time - the date and time the process has started; +; start since - the number of seconds since the process has started; +; requests - the number of requests the process has served; +; request duration - the duration in µs of the requests; +; request method - the request method (GET, POST, ...); +; request URI - the request URI with the query string; +; content length - the content length of the request (only with POST); +; user - the user (PHP_AUTH_USER) (or '-' if not set); +; script - the main script called (or '-' if not set); +; last request cpu - the %cpu the last request consumed +; it's always 0 if the process is not in Idle state +; because CPU calculation is done when the request +; processing has terminated; +; last request memory - the max amount of memory the last request consumed +; it's always 0 if the process is not in Idle state +; because memory calculation is done when the request +; processing has terminated; +; If the process is in Idle state, then informations are related to the +; last request the process has served. Otherwise informations are related to +; the current request being served. +; Example output: +; ************************ +; pid: 31330 +; state: Running +; start time: 01/Jul/2011:17:53:49 +0200 +; start since: 63087 +; requests: 12808 +; request duration: 1250261 +; request method: GET +; request URI: /test_mem.php?N=10000 +; content length: 0 +; user: - +; script: /home/fat/web/docs/php/test_mem.php +; last request cpu: 0.00 +; last request memory: 0 +; +; Note: There is a real-time FPM status monitoring sample web page available +; It's available in: /usr/share/php/7.3/fpm/status.html +; +; Note: The value must start with a leading slash (/). The value can be +; anything, but it may not be a good idea to use the .php extension or it +; may conflict with a real PHP file. +; Default Value: not set +;pm.status_path = /status + +; The ping URI to call the monitoring page of FPM. If this value is not set, no +; URI will be recognized as a ping page. This could be used to test from outside +; that FPM is alive and responding, or to +; - create a graph of FPM availability (rrd or such); +; - remove a server from a group if it is not responding (load balancing); +; - trigger alerts for the operating team (24/7). +; Note: The value must start with a leading slash (/). The value can be +; anything, but it may not be a good idea to use the .php extension or it +; may conflict with a real PHP file. +; Default Value: not set +;ping.path = /ping + +; This directive may be used to customize the response of a ping request. The +; response is formatted as text/plain with a 200 response code. +; Default Value: pong +;ping.response = pong + +; The access log file +; Default: not set +;access.log = log/$pool.access.log + +; The access log format. +; The following syntax is allowed +; %%: the '%' character +; %C: %CPU used by the request +; it can accept the following format: +; - %{user}C for user CPU only +; - %{system}C for system CPU only +; - %{total}C for user + system CPU (default) +; %d: time taken to serve the request +; it can accept the following format: +; - %{seconds}d (default) +; - %{miliseconds}d +; - %{mili}d +; - %{microseconds}d +; - %{micro}d +; %e: an environment variable (same as $_ENV or $_SERVER) +; it must be associated with embraces to specify the name of the env +; variable. Some exemples: +; - server specifics like: %{REQUEST_METHOD}e or %{SERVER_PROTOCOL}e +; - HTTP headers like: %{HTTP_HOST}e or %{HTTP_USER_AGENT}e +; %f: script filename +; %l: content-length of the request (for POST request only) +; %m: request method +; %M: peak of memory allocated by PHP +; it can accept the following format: +; - %{bytes}M (default) +; - %{kilobytes}M +; - %{kilo}M +; - %{megabytes}M +; - %{mega}M +; %n: pool name +; %o: output header +; it must be associated with embraces to specify the name of the header: +; - %{Content-Type}o +; - %{X-Powered-By}o +; - %{Transfert-Encoding}o +; - .... +; %p: PID of the child that serviced the request +; %P: PID of the parent of the child that serviced the request +; %q: the query string +; %Q: the '?' character if query string exists +; %r: the request URI (without the query string, see %q and %Q) +; %R: remote IP address +; %s: status (response code) +; %t: server time the request was received +; it can accept a strftime(3) format: +; %d/%b/%Y:%H:%M:%S %z (default) +; The strftime(3) format must be encapsuled in a %{}t tag +; e.g. for a ISO8601 formatted timestring, use: %{%Y-%m-%dT%H:%M:%S%z}t +; %T: time the log has been written (the request has finished) +; it can accept a strftime(3) format: +; %d/%b/%Y:%H:%M:%S %z (default) +; The strftime(3) format must be encapsuled in a %{}t tag +; e.g. for a ISO8601 formatted timestring, use: %{%Y-%m-%dT%H:%M:%S%z}t +; %u: remote user +; +; Default: "%R - %u %t \"%m %r\" %s" +;access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%" + +; The log file for slow requests +; Default Value: not set +; Note: slowlog is mandatory if request_slowlog_timeout is set +;slowlog = log/$pool.log.slow + +; The timeout for serving a single request after which a PHP backtrace will be +; dumped to the 'slowlog' file. A value of '0s' means 'off'. +; Available units: s(econds)(default), m(inutes), h(ours), or d(ays) +; Default Value: 0 +;request_slowlog_timeout = 0 + +; Depth of slow log stack trace. +; Default Value: 20 +;request_slowlog_trace_depth = 20 + +; The timeout for serving a single request after which the worker process will +; be killed. This option should be used when the 'max_execution_time' ini option +; does not stop script execution for some reason. A value of '0' means 'off'. +; Available units: s(econds)(default), m(inutes), h(ours), or d(ays) +; Default Value: 0 +;request_terminate_timeout = 0 + +; Set open file descriptor rlimit. +; Default Value: system defined value +;rlimit_files = 1024 + +; Set max core size rlimit. +; Possible Values: 'unlimited' or an integer greater or equal to 0 +; Default Value: system defined value +;rlimit_core = 0 + +; Chroot to this directory at the start. This value must be defined as an +; absolute path. When this value is not set, chroot is not used. +; Note: you can prefix with '$prefix' to chroot to the pool prefix or one +; of its subdirectories. If the pool prefix is not set, the global prefix +; will be used instead. +; Note: chrooting is a great security feature and should be used whenever +; possible. However, all PHP paths will be relative to the chroot +; (error_log, sessions.save_path, ...). +; Default Value: not set +;chroot = + +; Chdir to this directory at the start. +; Note: relative path can be used. +; Default Value: current directory or / when chroot +;chdir = /var/www + +; Redirect worker stdout and stderr into main error log. If not set, stdout and +; stderr will be redirected to /dev/null according to FastCGI specs. +; Note: on highloaded environement, this can cause some delay in the page +; process time (several ms). +; Default Value: no +;catch_workers_output = yes + +; Decorate worker output with prefix and suffix containing information about +; the child that writes to the log and if stdout or stderr is used as well as +; log level and time. This options is used only if catch_workers_output is yes. +; Settings to "no" will output data as written to the stdout or stderr. +; Default value: yes +;decorate_workers_output = no + +; Clear environment in FPM workers +; Prevents arbitrary environment variables from reaching FPM worker processes +; by clearing the environment in workers before env vars specified in this +; pool configuration are added. +; Setting to "no" will make all environment variables available to PHP code +; via getenv(), $_ENV and $_SERVER. +; Default Value: yes +;clear_env = no + +; Limits the extensions of the main script FPM will allow to parse. This can +; prevent configuration mistakes on the web server side. You should only limit +; FPM to .php extensions to prevent malicious users to use other extensions to +; execute php code. +; Note: set an empty value to allow all extensions. +; Default Value: .php +;security.limit_extensions = .php .php3 .php4 .php5 .php7 + +; Pass environment variables like LD_LIBRARY_PATH. All $VARIABLEs are taken from +; the current environment. +; Default Value: clean env +;env[HOSTNAME] = $HOSTNAME +;env[PATH] = /usr/local/bin:/usr/bin:/bin +;env[TMP] = /tmp +;env[TMPDIR] = /tmp +;env[TEMP] = /tmp + +; Additional php.ini defines, specific to this pool of workers. These settings +; overwrite the values previously defined in the php.ini. The directives are the +; same as the PHP SAPI: +; php_value/php_flag - you can set classic ini defines which can +; be overwritten from PHP call 'ini_set'. +; php_admin_value/php_admin_flag - these directives won't be overwritten by +; PHP call 'ini_set' +; For php_*flag, valid values are on, off, 1, 0, true, false, yes or no. + +; Defining 'extension' will load the corresponding shared extension from +; extension_dir. Defining 'disable_functions' or 'disable_classes' will not +; overwrite previously defined php.ini values, but will append the new value +; instead. + +; Note: path INI options can be relative and will be expanded with the prefix +; (pool, global or /usr) + +; Default Value: nothing is defined by default except the values in php.ini and +; specified at startup with the -d argument +;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f www@my.domain.com +;php_flag[display_errors] = off +;php_admin_value[error_log] = /var/log/fpm-php.www.log +;php_admin_flag[log_errors] = on +php_admin_value[memory_limit] = 128M diff --git a/rpm/_passbolt-configure/passbolt-configure b/rpm/_passbolt-configure/passbolt-configure new file mode 100755 index 0000000000..1de5f7cc88 --- /dev/null +++ b/rpm/_passbolt-configure/passbolt-configure @@ -0,0 +1,747 @@ +#!/usr/bin/env bash + +set -euo pipefail + +############################################################# +# # +# Passbolt PRO installation script # +# # +# Requirements: # +# This script must be executed with root permissions # +# # +# Passbolt, the open source password manager for teams # +# (c) 2020 Passbolt SA # +# https://www.passbolt.com # +# # +############################################################# +script_path="$(realpath "$0")" +script_directory="$(dirname "$script_path")" +readonly UNDEFINED="_UNDEF_" +readonly PROGNAME="$0" +readonly PASSBOLT_BASE_DIR="/usr/share/php/passbolt" +readonly NGINX_BASE='/etc/nginx' +readonly NGINX_SITE_DIR="$NGINX_BASE/conf.d" +readonly SSL_CERT_PATH='/etc/ssl/certs/passbolt_certificate.crt' +readonly SSL_KEY_PATH='/etc/ssl/certs/passbolt_private.key' +readonly LETSENCRYPT_LIVE_DIR='/etc/letsencrypt/live' +readonly FPM_WWW_POOL="/etc/php-fpm.d/www.conf" +readonly FPM_SERVICE="php-fpm" +readonly WWW_USER="nginx" +readonly WWW_GROUP="nginx" + +die(){ + echo "$*" 1>&2 + exit 1 +} + +banner(){ + local message=$1 + local len_message=${#message} + local len=$((len_message < 80 ? len_message : 80 )) + + printf "%0.s=" $(seq 1 "$len") + printf "\\n" + printf "%b" "$message" |fold + printf "\\n" + printf "%0.s=" $(seq 1 "$len") + printf "\\n" +} + +installation_complete() { + local protocol='https' + + if [[ "$(__config_get 'ssl_none')" ]]; then + protocol='http' + fi + + banner "Installation is almost complete. Please point your browser to + $protocol://$(__config_get 'passbolt_hostname') to complete the process" +} + +disclaimer() { + cat <<-'EOF' +================================================================ + ____ __ ____ + / __ \____ _____ ____/ /_ ____ / / /_ + / /_/ / __ `/ ___/ ___/ __ \/ __ \/ / __/ + / ____/ /_/ (__ |__ ) /_/ / /_/ / / /_ + /_/ \__,_/____/____/_,___/\____/_/\__/ + + The open source password manager for teams + (c) 2020 Passbolt SA + https://www.passbolt.com +================================================================ +EOF +} + +usage() { + cat <<-EOF + usage: $PROGNAME [OPTION] [ARGUMENTS] + + OPTIONS: + -h This help message + -e Enable fast entropy generation with haveged + Use with caution as it might lead to unsecure gpg keys + + SSL SETUP OPTIONS: + ------------------ + -a Enable automatic SSL setup using Letsencrypt and certbot. + Conflicts with -[ck] + -c CERT_FILE_PATH SSL certificate system path. Enables SSL + -k KEY_FILE_PATH SSL certificate key path. Enables SSL + -m EMAIL Email address to use for letsencrypt registration + -n Disable SSL setup + -H HOSTNAME Specifies the hostname nginx will use as server_name + and if -a specified it will be used as letsencrypt + domain name + + FRESH MARIADB INSTALLATION OPTIONS: + ----------------------------------- + -d DB_NAME Specifies the name of the database passbolt will use + Conflicts with -r + -p PASSWORD Specifies the password of the passbolt database user + Conflicts with -r + -u DB_USERNAME Specifies the name of the mariadb passbolt database user + Conflicts with -r + -P PASSWORD Specifies the root database password + Conflicts with -r + + PREINSTALLED DATABASE SERVER OR REMOTE DATABASE SERVER: + ------------------------------------------------------- + -r Do not install a local mysql server. Useful for the following scenarios: + - Database is in a different server + - Manual installation for custom database scenarios + Conflicts with: -[dupP] + + EXAMPLES: + --------- + Interactive mode. Script will prompt user for details: + $ $PROGNAME + + Non interactive options: + - Install local mysql server and use user uploaded certificates: + $ $PROGNAME -P mysql_root_pass \\ + -p mysql_passbolt_pass \\ + -d database_name \\ + -u database_username \\ + -H domain_name \\ + -c path_to_certificate \\ + -k path_to_certificate_key + + - Install passbolt using remote or preinstalled db: + $ $PROGNAME -r \\ + -H domain_name \\ + -c path_to_certificate \\ + -k path_to_certificate_key + + - Install with letsencrypt support and remote/preinstalled database: + $ $PROGNAME -r -H domain_name -a -m my@email.com +EOF +} + +__validate_cli_options() { + local message_mysql="Wrong parameters. Check your database parameters" + local message_ssl="Wrong parameters. Check your SSL parameters" + + if [[ "$(__config_get 'mariadb_local_installation')" && \ + "$(__config_get 'mariadb_remote_installation')" ]]; then + die "$message_mysql" + fi + + if [[ ( "$(__config_get 'ssl_manual')" && "$(__config_get 'ssl_none')" ) || \ + ( "$(__config_get 'ssl_auto')" && "$(__config_get 'ssl_none')" ) || \ + ( "$(__config_get 'ssl_auto')" && "$(__config_get 'ssl_manual')" ) ]]; then + die "$message_ssl" + fi + + if [[ ( -n "$(__config_get 'ssl_certificate')" || \ + -n "$(__config_get 'ssl_privkey')" ) && "$(__config_get 'ssl_method')" == 'none' ]]; then + die "$message_ssl" + fi + + if [[ "$(__config_get 'ssl_auto')" == true && "$(__config_get 'ssl_method')" == 'none' ]]; then + die "$message_ssl" + fi + + + if [[ ( -n "$(__config_get 'ssl_certificate')" || \ + -n "$(__config_get 'ssl_privkey')" ) && "$(__config_get 'ssl_auto')" == true ]]; then + die "$message_ssl" + fi + + if [[ (-z "$(__config_get 'ssl_certificate')" && -n "$(__config_get 'ssl_privkey')") || \ + (-n "$(__config_get 'ssl_certificate')" && -z "$(__config_get 'ssl_privkey')") ]]; then + die "$message_ssl" + fi +} + +__validate_hostname() { + local _passbolt_hostname="$1" + + if ! [[ "$_passbolt_hostname" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ || \ + "$_passbolt_hostname" =~ ^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$ ]]; then + echo "false" + else + echo "true" + fi +} + +__validate_ssl_paths() { + local _cert_path="$1" + + if [[ -f "$_cert_path" ]]; then + echo "true" + else + echo "false" + fi +} + +__validate_cli_options() { + local message_mysql="Wrong parameters. Check your database parameters" + local message_ssl="Wrong parameters. Check your SSL parameters" + + if [[ "$(__config_get 'mariadb_local_installation')" && \ + "$(__config_get 'mariadb_remote_installation')" ]]; then + die "$message_mysql" + fi + + if [[ ( "$(__config_get 'ssl_manual')" && "$(__config_get 'ssl_none')" ) || \ + ( "$(__config_get 'ssl_auto')" && "$(__config_get 'ssl_none')" ) || \ + ( "$(__config_get 'ssl_auto')" && "$(__config_get 'ssl_manual')" ) ]]; then + die "$message_ssl" + fi + + if [[ ( -n "$(__config_get 'ssl_certificate')" || \ + -n "$(__config_get 'ssl_privkey')" ) && "$(__config_get 'ssl_method')" == 'none' ]]; then + die "$message_ssl" + fi + + if [[ "$(__config_get 'ssl_auto')" == true && "$(__config_get 'ssl_method')" == 'none' ]]; then + die "$message_ssl" + fi + + + if [[ ( -n "$(__config_get 'ssl_certificate')" || \ + -n "$(__config_get 'ssl_privkey')" ) && "$(__config_get 'ssl_auto')" == true ]]; then + die "$message_ssl" + fi + + if [[ (-z "$(__config_get 'ssl_certificate')" && -n "$(__config_get 'ssl_privkey')") || \ + (-n "$(__config_get 'ssl_certificate')" && -z "$(__config_get 'ssl_privkey')") ]]; then + die "$message_ssl" + fi +} + +get_options(){ + local OPTIND=$OPTIND + + while getopts "ahnrec:d:k:m:u:p:P:H:" opt; do + case $opt in + a) + __config_set 'ssl_auto' true + ;; + c) + __config_set 'ssl_certificate' "$OPTARG" + __config_set 'ssl_manual' true + ;; + d) + __config_set 'mariadb_name' "$OPTARG" + __config_set 'mariadb_local_installation' true + ;; + e) + __config_set 'haveged_install' true + ;; + h) + disclaimer + usage + exit 0 + ;; + k) + __config_set 'ssl_privkey' "$OPTARG" + __config_set 'ssl_manual' true + ;; + m) + __config_set 'letsencrypt_email' "$OPTARG" + __config_set 'ssl_auto' true + ;; + n) + __config_set 'ssl_none' true + ;; + p) + __config_set 'mariadb_passbolt_password' "$OPTARG" + __config_set 'mariadb_local_installation' true + ;; + r) + __config_set 'mariadb_remote_installation' true + ;; + u) + __config_set 'mariadb_user' "$OPTARG" + __config_set 'mariadb_local_installation' true + ;; + P) + __config_set 'mariadb_root_password' "$OPTARG" + __config_set 'mariadb_local_installation' true + ;; + H) + __config_set 'passbolt_hostname' "$OPTARG" + ;; + *) + die "Invalid option" + ;; + esac + done + + __validate_cli_options +} + +init_config() { + __config_data +} + +__prompt_entropy_check(){ + local options=("yes" "no") + local _config_key_haveged + + _config_key_haveged="$1" + if [[ -z "$(__config_get "$_config_key_haveged")" ]]; then + banner "On virtualized environments GnuPG happen to find not enough entropy + to generate a key. Therefore, Passbolt will not run properly. + Do you want to install Haveged to speed up the entropy generation on + your system? Please check https://help.passbolt.com/faq/hosting/why-haveged-virtual-env" + + select opt in "${options[@]}"; do + case $opt in + "yes") + __config_set haveged_install true + break + ;; + "no") + __config_set haveged_install false + break + ;; + *) + echo "Please choose (1) or (2) to continue" + ;; + esac + done + fi +} + +interactive_prompter() { + __prompt_mariadb_credentials 'mariadb_root_password' \ + 'mariadb_user' \ + 'mariadb_passbolt_password' \ + 'mariadb_name' \ + 'mariadb_local_installation' \ + 'mariadb_remote_installation' + __prompt_entropy_check 'haveged_install' + __prompt_passbolt_hostname 'passbolt_hostname' + __prompt_ssl 'ssl_auto' \ + 'ssl_manual' \ + 'ssl_none' \ + 'ssl_certificate' \ + 'ssl_privkey' \ + 'letsencrypt_email' +} + +__prompt_passbolt_hostname() { + local _passbolt_hostname + local _host_config_key="$1" + + _host_config_key="$1" + if [[ -z "$(__config_get "$_host_config_key")" ]]; then + banner "Setting hostname... + Please enter the domain name under which passbolt will run. + Note this hostname will be used as server_name for nginx + and as the domain name to register a SSL certificate with + let's encrypt. + If you don't have a domain name and you do not plan to use + let's encrypt please enter the ip address to access this machine" + read -r -p "Hostname:" _passbolt_hostname + while [[ "$(__validate_hostname "$_passbolt_hostname")" == 'false' ]]; do + banner "Please introduce a valid hostname. Valid hostnames are either + IPv4 addresses or fully qualified domain names" + read -r -p "Hostname:" _passbolt_hostname + done + __config_set "$_host_config_key" "$_passbolt_hostname" + fi +} + +__prompt_mariadb_credentials(){ + local _config_root_pw="$1" + local _config_user="$2" + local _config_pw="$3" + local _config_db="$4" + local _config_local_db="$5" + local _config_remote_db="$6" + local _mariadb_user + local _mariadb_name + local options=("yes" "no") + + if [[ -z "$(__config_get "$_config_local_db")" && \ + -z "$(__config_get "$_config_remote_db")" ]]; then + banner "Do you want to install a local mariadb server on this machine?" + select opt in "${options[@]}"; do + case $opt in + "yes") + __config_set "$_config_local_db" true + break + ;; + "no") + __config_set "$_config_local_db" false + return + ;; + *) + echo "Please select (1) or (2) to continue" + ;; + esac + done + fi + + if [[ "$(__config_get "$_config_local_db")" == 'true' ]]; then + if [[ -z "$(__config_get "$_config_root_pw")" ]]; then + banner "Please enter a new password for the root database user:" + __password_validation "MariaDB Root Password" "$_config_root_pw" + fi + + if [[ -z "$(__config_get "$_config_user")" ]]; then + banner "Please enter a name for the passbolt database username" + read -r -p "Passbolt database user name:" _mariadb_user + __config_set "$_config_user" "$_mariadb_user" + fi + + if [[ -z "$(__config_get "$_config_pw")" ]]; then + banner "Please enter a new password for the mysql passbolt user" + __password_validation "MariaDB passbolt user password" "$_config_pw" + fi + + if [[ -z "$(__config_get "$_config_db")" ]]; then + banner "Please enter a name for the passbolt database:" + read -r -p "Passbolt database name:" _mariadb_name + __config_set "$_config_db" "$_mariadb_name" + fi + fi +} + +__password_validation() { + local message="$1" + local pw_config="$2" + local _pw="$UNDEFINED" + local _pw_verify='' + + while [[ "$_pw" != "$_pw_verify" ]]; do + read -rs -p "$message:" _pw + if [[ -z "$_pw" ]]; then + echo "Dont use blank passwords" + _pw="$UNDEFINED" + continue + fi + echo "" + read -rs -p "$message (verify):" _pw_verify + echo "" + if [[ "$_pw" != "$_pw_verify" ]]; then + echo "Passwords mismatch, please try again." + fi + done + __config_set "$pw_config" "$_pw" +} + +__prompt_ssl_paths(){ + local _config_ssl_cert="$1" + local _config_ssl_key="$2" + local _ssl_cert + local _ssl_key + + if [[ -z "$(__config_get "$_config_ssl_cert")" ]]; then + read -rp "Enter the path to the SSL certificate: " _ssl_cert + while [[ "$(__validate_ssl_paths "$_ssl_cert")" == 'false' ]]; do + banner "Please introduce a valid path to your ssl certificate" + read -rp "Enter the path to the SSL certificate: " _ssl_cert + done + __config_set "$_config_ssl_cert" "$_ssl_cert" + fi + + if [[ -z "$(__config_get "$_config_ssl_key")" ]]; then + read -rp "Enter the path to the SSL privkey: " _ssl_key + while [[ "$(__validate_ssl_paths "$_ssl_key")" == 'false' ]]; do + banner "Please introduce a valid path to your ssl key file" + read -rp "Enter the path to the SSL key: " _ssl_key + done + __config_set "$_config_ssl_key" "$_ssl_key" + fi +} + +__prompt_lets_encrypt_details() { + local _config_ssl_email="$1" + local _ssl_email + + if [[ -z "$(__config_get "$_config_ssl_email")" ]]; then + read -rp "Enter a email address to register with Let's Encrypt: " _ssl_email + __config_set "$_config_ssl_email" "$_ssl_email" + fi +} + +__prompt_ssl(){ + local _options=("manual" "auto" "none") + local _config_ssl_auto="$1" + local _config_ssl_manual="$2" + local _config_ssl_none="$3" + local _config_ssl_cert="$4" + local _config_ssl_key="$5" + local _config_ssl_email="$6" + + if [[ -z "$(__config_get "$_config_ssl_auto")" && \ + -z "$(__config_get "$_config_ssl_manual")" && \ + -z "$(__config_get "$_config_ssl_none")" ]]; then + banner "Setting up SSL... + Do you want to setup a SSL certificate and enable HTTPS now? + - manual: Prompts for the path of user uploaded ssl certificates and set up nginx + - auto: Will issue a free SSL certificate with https://www.letsencrypt.org and set up nginx + - none: Do not setup HTTPS at all" + select opt in "${_options[@]}"; do + case $opt in + "manual") + __config_set "$_config_ssl_manual" true + break + ;; + "auto") + __config_set "$_config_ssl_auto" true + break + ;; + "none") + __config_set "$_config_ssl_none" true + return + break + ;; + *) + echo "Wrong option, please choose (1) manual, (2) auto or (3) none" + ;; + esac + done + fi + + if [[ "$(__config_get "$_config_ssl_manual")" == 'true' ]]; then + __prompt_ssl_paths "$_config_ssl_cert" "$_config_ssl_key" + fi + + if [[ "$(__config_get "$_config_ssl_auto")" == 'true' ]]; then + __prompt_lets_encrypt_details "$_config_ssl_email" + fi +} +__config_data() { + declare -gA config +} +__config_set() { + config[$1]="$2" + return $? +} + +__config_get() { + if [[ -z "${config[$1]+'test'}" ]]; then + echo "" + else + echo "${config[$1]}" + fi +} +setup_firewall() { + local zone=public + local services=(http https) + banner "Opening ports 80 and 443 on firewall" + + enable_service firewalld + + for i in "${services[@]}"; do + firewall-cmd --permanent --zone="$zone" --add-service="$i" + done + + enable_service firewalld +} + +__install_db() { + local _config_root_pw="$1" + local _config_user="$2" + local _config_pw="$3" + local _config_db="$4" + local mysql_commands + mysql_commands=$(cat < '\).*\(',.*$\)#\1${protocol}://$(__config_get "$_config_passbolt_host")\2#g" /etc/passbolt/passbolt.php + fi + cp "$source_template" "$nginx_config_file" + sed -i s:_SERVER_NAME_:"$(__config_get "$_config_passbolt_host")": "$nginx_config_file" +} + +__ssl_substitutions(){ + sed -i s:_NGINX_CERT_FILE_:"$SSL_CERT_PATH": "$NGINX_SITE_DIR/passbolt_ssl.conf" + sed -i s:_NGINX_KEY_FILE_:"$SSL_KEY_PATH": "$NGINX_SITE_DIR/passbolt_ssl.conf" +} + +setup_nginx(){ + local passbolt_domain + local nginx_service="nginx" + + if [ ! -f /etc/nginx/conf.d/php-fpm.conf ] + then + cat << EOF > /etc/nginx/conf.d/php-fpm.conf +# PHP-FPM FastCGI server +# network or unix domain socket configuration + +upstream php-fpm { + server unix:/run/php-fpm/www.sock; +} +EOF + fi + + passbolt_domain=$(__config_get 'passbolt_hostname') + banner "Setting up nginx..." + + __nginx_config "$script_directory/conf/nginx/passbolt.conf" "$NGINX_SITE_DIR/passbolt.conf" 'passbolt_hostname' + enable_service "$nginx_service" + + if [[ "$(__config_get 'ssl_auto')" == 'true' ]]; then + if __setup_letsencrypt 'passbolt_hostname' 'letsencrypt_email'; then + __nginx_config "$script_directory/conf/nginx/passbolt_ssl.conf" "$NGINX_SITE_DIR/passbolt_ssl.conf" 'passbolt_hostname' + ln -s "$LETSENCRYPT_LIVE_DIR/$passbolt_domain/cert.pem" "$SSL_CERT_PATH" + ln -s "$LETSENCRYPT_LIVE_DIR/$passbolt_domain/privkey.pem" "$SSL_KEY_PATH" + __ssl_substitutions + enable_service "$nginx_service" + else + banner "WARNING: Unable to setup SSL using lets encrypt. Please check the install.log" + fi + fi + + if [[ "$(__config_get 'ssl_manual')" == 'true' ]]; then + __nginx_config "$script_directory/conf/nginx/passbolt_ssl.conf" "$NGINX_SITE_DIR/passbolt_ssl.conf" 'passbolt_hostname' + __copy_ssl_certs 'ssl_certificate' 'ssl_privkey' + __ssl_substitutions + enable_service "$nginx_service" + fi +} +__copy_ssl_certs() { + local _config_ssl_cert="$1" + local _config_ssl_key="$2" + + if [[ -e "$SSL_CERT_PATH" ]]; then + mv "$SSL_CERT_PATH"{,.orig} + fi + + if [ -e "$SSL_KEY_PATH" ]; then + mv "$SSL_KEY_PATH"{,.orig} + fi + + if [[ -f "$(__config_get "$_config_ssl_cert")" && -f "$(__config_get "$_config_ssl_key")" ]]; then + cp "$(__config_get "$_config_ssl_cert")" "$SSL_CERT_PATH" + cp "$(__config_get "$_config_ssl_key")" "$SSL_KEY_PATH" + else + mv "$NGINX_SITE_DIR"/passbolt_ssl.conf{,.orig} + banner "Unable to locate SSL certificate files." + fi +} + +__setup_letsencrypt() { + local _config_passbolt_host="$1" + local _config_email="$2" + + certbot certonly --authenticator webroot \ + -n \ + -w "$PASSBOLT_BASE_DIR"/webroot \ + -d "$(__config_get "$_config_passbolt_host")" \ + -m "$(__config_get "$_config_email")" \ + --agree-tos +} + +main(){ + init_config + get_options "$@" + disclaimer + interactive_prompter + banner 'Installing os dependencies...' + setup_nginx + setup_fpm + mysql_setup + setup_firewall + setup_entropy + installation_complete +} + +main "$@" 2>&1 | tee -a install.log diff --git a/rpm/cron.d/passbolt-server b/rpm/cron.d/passbolt-server new file mode 100644 index 0000000000..4d9d732ad4 --- /dev/null +++ b/rpm/cron.d/passbolt-server @@ -0,0 +1,12 @@ +# +# +# Cronjob to process emails for the Passbolt Web Service every minute. +# +# This crontab script is part of the Passbolt Debian package, +# see dh_installcron debhelper program for more details. +# + +PATH=/bin:/usr/local/bin:/usr/bin +PASSBOLT_BASE_DIR=/usr/share/php/passbolt + +* * * * * nginx $PASSBOLT_BASE_DIR/bin/cron diff --git a/rpm/docker b/rpm/docker new file mode 100644 index 0000000000..47026c46b1 --- /dev/null +++ b/rpm/docker @@ -0,0 +1,5 @@ +# docker build --build-arg VERSION=34 -t anatomicjc/fedora:34 . +ARG VERSION=34 +FROM dokken/fedora-${VERSION} +RUN dnf install systemd libxcrypt-compat -y \ + && dnf clean all \ No newline at end of file diff --git a/rpm/logrotate.d/passbolt-server b/rpm/logrotate.d/passbolt-server new file mode 100644 index 0000000000..84af1847f5 --- /dev/null +++ b/rpm/logrotate.d/passbolt-server @@ -0,0 +1,9 @@ +/var/log/passbolt/error.log { + daily + missingok + rotate 14 + compress + delaycompress + notifempty + create 0640 nginx nginx +} diff --git a/rpm/passbolt-server-selinux-0.1/Makefile b/rpm/passbolt-server-selinux-0.1/Makefile new file mode 100644 index 0000000000..e4065dd267 --- /dev/null +++ b/rpm/passbolt-server-selinux-0.1/Makefile @@ -0,0 +1,15 @@ +TARGETS?= passbolt-server +MODULES?=${TARGETS:=.pp.bz2} + +all: ${TARGETS:=.pp.bz2} + +%.pp.bz2: %.pp + @echo Compressing $^ -\ $@ + bzip2 -9 $^ + +%.pp: %.te + make -f /usr/share/selinux/devel/Makefile $@ + +clean: + rm -f *~ *.tc *.pp *.pp.bz2 + rm -rf tmp diff --git a/rpm/passbolt-server-selinux-0.1/passbolt-server.fc b/rpm/passbolt-server-selinux-0.1/passbolt-server.fc new file mode 100644 index 0000000000..e69de29bb2 diff --git a/rpm/passbolt-server-selinux-0.1/passbolt-server.if b/rpm/passbolt-server-selinux-0.1/passbolt-server.if new file mode 100644 index 0000000000..3eb6a3057b --- /dev/null +++ b/rpm/passbolt-server-selinux-0.1/passbolt-server.if @@ -0,0 +1 @@ +## diff --git a/rpm/passbolt-server-selinux-0.1/passbolt-server.te b/rpm/passbolt-server-selinux-0.1/passbolt-server.te new file mode 100644 index 0000000000..1326c16ebf --- /dev/null +++ b/rpm/passbolt-server-selinux-0.1/passbolt-server.te @@ -0,0 +1,34 @@ + +module passbolt-server 1.0; + +require { + type etc_t; + type unreserved_port_t; + type var_lib_t; + type httpd_t; + type httpd_var_lib_t; + type var_log_t; + type cron_var_lib_t; + type postfix_local_t; + class sock_file { getattr setattr unlink write create }; + class dir { add_name setattr write }; + class file { create rename write unlink setattr open getattr read }; + class tcp_socket name_connect; + class process setrlimit; + class dir search; +} + +#============= httpd_t ============== +allow httpd_t etc_t:dir { add_name setattr write }; +allow httpd_t etc_t:file { create write setattr }; +allow httpd_t var_lib_t:dir setattr; +allow httpd_t var_lib_t:file { write rename unlink }; +allow httpd_t var_lib_t:sock_file { create getattr unlink write setattr }; +allow httpd_t var_log_t:file { write open }; +allow httpd_t unreserved_port_t:tcp_socket name_connect; +allow httpd_t self:process setrlimit; +allow httpd_t cron_var_lib_t:file { getattr read write }; + +#============= postfix_local_t ============== +allow postfix_local_t httpd_var_lib_t:dir search; + diff --git a/rpm/patches/01_paths_setup.diff b/rpm/patches/01_paths_setup.diff new file mode 100644 index 0000000000..76cd2812d5 --- /dev/null +++ b/rpm/patches/01_paths_setup.diff @@ -0,0 +1,79 @@ +Index: passbolt-@PASSBOLT_FLAVOUR@-server/bin/cake.php +=================================================================== +--- passbolt-@PASSBOLT_FLAVOUR@-server.orig/bin/cake.php ++++ passbolt-@PASSBOLT_FLAVOUR@-server/bin/cake.php +@@ -3,6 +3,7 @@ + // Check platform requirements + require dirname(__DIR__) . '/config/requirements.php'; + require dirname(__DIR__) . '/vendor/autoload.php'; ++include '/etc/passbolt/bootstrap.php'; + + use App\Application; + use Cake\Console\CommandRunner; +Index: passbolt-@PASSBOLT_FLAVOUR@-server/config/default.php +=================================================================== +--- passbolt-@PASSBOLT_FLAVOUR@-server.orig/config/default.php ++++ passbolt-@PASSBOLT_FLAVOUR@-server/config/default.php +@@ -151,10 +151,10 @@ return [ + // - Apache on Debian it would be in '/var/www/.gnupg' + // - Nginx on Centos it would be in '/var/lib/nginx/.gnupg' + // - etc. +- 'keyring' => getenv("HOME") . DS . '.gnupg', ++ 'keyring' => '/var/lib/passbolt' . DS . '.gnupg', + + // Replace GNUPGHOME with above value even if it is set. +- 'putenv' => false, ++ 'putenv' => true, + + // Main server key. + 'serverKey' => [ +Index: passbolt-@PASSBOLT_FLAVOUR@-server/config/paths.php +=================================================================== +--- passbolt-@PASSBOLT_FLAVOUR@-server.orig/config/paths.php ++++ passbolt-@PASSBOLT_FLAVOUR@-server/config/paths.php +@@ -28,7 +28,7 @@ if (!defined('DS')) { + /* + * The full path to the directory which holds "src", WITHOUT a trailing DS. + */ +-define('ROOT', dirname(__DIR__)); ++define('ROOT', '/usr/share/php/passbolt'); + + /* + * The actual directory name for the application directory. Normally +@@ -44,7 +44,7 @@ define('APP', ROOT . DS . APP_DIR . DS); + /* + * Path to the config directory. + */ +-define('CONFIG', ROOT . DS . 'config' . DS); ++define('CONFIG', '/etc/passbolt' . DS); + + /** + * Path to the plugins directory. +@@ -78,12 +78,12 @@ define('FIXTURES', TESTS . 'Fixture' . D + /* + * Path to the temporary files directory. + */ +-define('TMP', ROOT . DS . 'tmp' . DS); ++define('TMP', '/var/lib/passbolt/tmp' . DS); + + /* + * Path to the logs directory. + */ +-define('LOGS', ROOT . DS . 'logs' . DS); ++define('LOGS', '/var/log/passbolt' . DS); + + /* + * Path to the cache files directory. It can be shared between hosts in a multi-server setup. +Index: passbolt-@PASSBOLT_FLAVOUR@-server/webroot/index.php +=================================================================== +--- passbolt-@PASSBOLT_FLAVOUR@-server.orig/webroot/index.php ++++ passbolt-@PASSBOLT_FLAVOUR@-server/webroot/index.php +@@ -34,7 +34,7 @@ use App\Application; + use Cake\Http\Server; + + // Bind your application to the server. +-$server = new Server(new Application(dirname(__DIR__) . '/config')); ++$server = new Server(new Application('/etc/passbolt')); + + // Run the request/response through the application and emit the response. + $server->emit($server->run()); diff --git a/rpm/patches/02_webpaths_setup.diff b/rpm/patches/02_webpaths_setup.diff new file mode 100644 index 0000000000..bd3fb6f01c --- /dev/null +++ b/rpm/patches/02_webpaths_setup.diff @@ -0,0 +1,13 @@ +Index: passbolt-@PASSBOLT_FLAVOUR@-server/webroot/index.php +=================================================================== +--- passbolt-@PASSBOLT_FLAVOUR@-server.orig/webroot/index.php ++++ passbolt-@PASSBOLT_FLAVOUR@-server/webroot/index.php +@@ -16,7 +16,7 @@ + */ + + // Check platform requirements +-require dirname(__DIR__) . '/config/requirements.php'; ++include '/etc/passbolt/requirements.php'; + + // For built-in server + if (PHP_SAPI === 'cli-server') { diff --git a/rpm/patches/03_cake_import_paths.diff b/rpm/patches/03_cake_import_paths.diff new file mode 100644 index 0000000000..d8706467f1 --- /dev/null +++ b/rpm/patches/03_cake_import_paths.diff @@ -0,0 +1,20 @@ +Index: passbolt-@PASSBOLT_FLAVOUR@-server/bin/cake.php +=================================================================== +--- passbolt-@PASSBOLT_FLAVOUR@-server.orig/bin/cake.php ++++ passbolt-@PASSBOLT_FLAVOUR@-server/bin/cake.php +@@ -1,7 +1,7 @@ + #!/usr/bin/php -q + run($argv)); diff --git a/rpm/patches/series b/rpm/patches/series new file mode 100644 index 0000000000..34ec1eddb8 --- /dev/null +++ b/rpm/patches/series @@ -0,0 +1,3 @@ +02_webpaths_setup.diff +01_paths_setup.diff +03_cake_import_paths.diff diff --git a/rpm/scripts/build-passbolt-selinux.sh b/rpm/scripts/build-passbolt-selinux.sh new file mode 100644 index 0000000000..f8390292bc --- /dev/null +++ b/rpm/scripts/build-passbolt-selinux.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +set -exu + +SCRIPT_DIR="$( cd "$( dirname "${0}" )" && pwd )" +cd ${SCRIPT_DIR}/../.. + +yum install -y rpmdevtools rpmlint rsync selinux-policy-devel rpm-build bc +echo "setuptree" +rpmdev-setuptree + +OS_VERSION=$(grep -E '^VERSION_ID=' /etc/os-release | awk -F= '{print $2}' | sed 's/\"//g') +OS_VERSION_MAJOR=$(echo ${OS_VERSION:0:1} | bc) +_POLICYCOREUTILS_PYTHON=policycoreutils-python +if [ ${OS_VERSION_MAJOR} -gt 7 ] +then + _POLICYCOREUTILS_PYTHON=policycoreutils-python-utils +fi + +cp rpm/specs/passbolt-server-selinux.spec ~/rpmbuild/SPECS/ +cd rpm/passbolt-server-selinux-${PKG_VERSION} +make clean +echo "make" +make +cd .. +tar --create \ + --gzip \ + --file ~/rpmbuild/SOURCES/passbolt-server-selinux-${PKG_VERSION}.tar.gz passbolt-server-selinux-${PKG_VERSION} +cd - +make clean +rpmbuild -ba \ + --define "_passbolt_selinux_version ${PKG_VERSION}" \ + --define "_policycoreutils_python ${_POLICYCOREUTILS_PYTHON}" \ + ~/rpmbuild/SPECS/passbolt-server-selinux.spec diff --git a/rpm/scripts/build-passbolt-server.sh b/rpm/scripts/build-passbolt-server.sh new file mode 100644 index 0000000000..cd83ef7bf7 --- /dev/null +++ b/rpm/scripts/build-passbolt-server.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +set -eu +SCRIPT_DIR="$( cd "$( dirname "${0}" )" && pwd )" +PKG_VERSION=$(cat $SCRIPT_DIR/../CHANGELOG.md | awk 'match($0, /\[([0-9]+\.[0-9]+\.[0-9]+)\]?/) {print substr($0, RSTART, RLENGTH);exit}' | tr -d "[]") +cd ${SCRIPT_DIR}/../.. +PASSBOLT_DIR=$(basename $PWD) + +yum install -y rpmdevtools rpmlint rsync selinux-policy-devel rpm-build bc +rpmdev-setuptree + +cp -r rpm/_passbolt-configure . +cp -r rpm/cron.d . +cp -r rpm/logrotate.d . +cd .. +rsync -a --delete --delete-excluded \ + --exclude debian \ + --exclude rpm \ + --exclude tests \ + --exclude .git \ + --exclude *deb \ + $PASSBOLT_DIR/ passbolt-${PASSBOLT_FLAVOUR}-server-${PKG_VERSION} +tar \ + --exclude-vcs \ + --exclude debian \ + --exclude rpm \ + --exclude tests \ + --gzip \ + --create \ + --file ~/rpmbuild/SOURCES/passbolt-${PASSBOLT_FLAVOUR}-server-${PKG_VERSION}.tar.gz \ + passbolt-${PASSBOLT_FLAVOUR}-server-${PKG_VERSION} +cd - +cp rpm/patches/*.diff ~/rpmbuild/SOURCES +cp rpm/specs/passbolt-server.spec ~/rpmbuild/SPECS/ +rpmbuild -ba \ + --define "_passbolt_flavour ${PASSBOLT_FLAVOUR}" \ + --define "_passbolt_version ${PKG_VERSION}" \ + ~/rpmbuild/SPECS/passbolt-server.spec diff --git a/rpm/scripts/clean-debian.sh b/rpm/scripts/clean-debian.sh new file mode 100644 index 0000000000..ed25a58734 --- /dev/null +++ b/rpm/scripts/clean-debian.sh @@ -0,0 +1,4 @@ +#!/bin/sh +rm -rf debian +rm -rf passbolt-*-server* +git checkout debian \ No newline at end of file diff --git a/rpm/scripts/setup-composer.sh b/rpm/scripts/setup-composer.sh new file mode 100644 index 0000000000..671c672812 --- /dev/null +++ b/rpm/scripts/setup-composer.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +set -eu + +EXPECTED_CHECKSUM="$(php -r 'copy("https://composer.github.io/installer.sig", "php://stdout");')" +php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" +ACTUAL_CHECKSUM="$(php -r "echo hash_file('sha384', 'composer-setup.php');")" + +if [ "$EXPECTED_CHECKSUM" != "$ACTUAL_CHECKSUM" ] +then + >&2 echo 'ERROR: Invalid installer checksum' + sudo rm composer-setup.php + exit 1 +fi + +php composer-setup.php +rm composer-setup.php + +mv composer.phar /usr/local/bin/composer \ No newline at end of file diff --git a/rpm/scripts/setup-local-repository.sh b/rpm/scripts/setup-local-repository.sh new file mode 100644 index 0000000000..8d031a973d --- /dev/null +++ b/rpm/scripts/setup-local-repository.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +set -eu + +if [ ! -d ~/rpmbuild/RPMS ] +then + yum install -y rpmdevtools + rpmdev-setuptree + mkdir ~/rpmbuild/RPMS/noarch + cp passbolt-*.rpm ~/rpmbuild/RPMS/noarch +fi + +createrepo --update ~/rpmbuild/RPMS + +cat << EOF | tee /etc/yum.repos.d/local.repo +[local] +name=Local Repository Demo +baseurl=file:///root/rpmbuild/RPMS/ +enabled=1 +gpgcheck=0 +protect=1 +EOF \ No newline at end of file diff --git a/rpm/scripts/setup-remirepo.sh b/rpm/scripts/setup-remirepo.sh new file mode 100644 index 0000000000..f7ddb250c8 --- /dev/null +++ b/rpm/scripts/setup-remirepo.sh @@ -0,0 +1,33 @@ +#!/bin/sh + +# https://rpms.remirepo.net/wizard/ + +# Add explanations about how to setup php-gnupg with PECL for future checks + +set -eu + +OS_VERSION=$(grep -E '^VERSION_ID=' /etc/os-release | awk -F= '{print $2}' | sed 's/\"//g') +OS_VERSION_MAJOR=$(echo ${OS_VERSION:0:1} | bc) + +if [ ${OS_VERSION_MAJOR} -eq 7 ] +then + rpm -qa | grep epel-release || yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm -y + if ! rpm -qa | grep -q remi-release + then + yum install https://rpms.remirepo.net/enterprise/remi-release-7.rpm -y + fi + yum install yum-utils -y + yum-config-manager --disable 'remi-php*' -y + yum-config-manager --enable remi-php74 -y +elif [ ${OS_VERSION_MAJOR} -eq 8 ] +then + dnf install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm -y + if ! rpm -qa | grep -q remi-release + then + yum install https://rpms.remirepo.net/enterprise/remi-release-8.rpm -y + fi + dnf module reset php -y + dnf module install php:remi-7.4 -y +else + exit 1 +fi diff --git a/rpm/specs/passbolt-server-opensuse.spec b/rpm/specs/passbolt-server-opensuse.spec new file mode 100644 index 0000000000..4f11a35777 --- /dev/null +++ b/rpm/specs/passbolt-server-opensuse.spec @@ -0,0 +1,96 @@ +Name: passbolt-%{_passbolt_flavour}-server +Version: %{_passbolt_version} +Release: 1%{?dist} +Summary: Passbolt +BuildArch: noarch + +License: WTFPL | http://www.wtfpl.net/ +URL: https://www.passbolt.com +Source0: %{name}-%{version}.tar.gz +Patch0: 02_webpaths_setup.diff +Patch1: 01_paths_setup.diff +Patch2: 03_cake_import_paths.diff + +Requires: php7 >= 7.3 +Requires: php7-cli >= 7.3 +Requires: php7-mbstring >= 7.3 +Requires: php7-intl >= 7.3 +Requires: php7-mysql >= 7.3 +Requires: php7-fpm >= 7.3 +Requires: php7-xmlreader >= 7.3 +Requires: php7-xmlwriter >= 7.3 +Requires: php7-gd >= 7.3 +Requires: php7-json >= 7.3 +Requires: php7-curl >= 7.3 +Requires: php7-posix >= 7.3 +Requires: php7-fileinfo >= 7.3 +Requires: php7-pecl +%if "%{_passbolt_flavour}" == "pro" +Requires: php7-ldap >= 7.3 +%endif +Requires: cronie +Requires: nginx +Requires: mariadb + +%description +Passbolt on RPM POC + +%prep +%setup -q +%patch0 -p1 +%patch1 -p1 +%patch2 -p1 + +%install +rm -rf $RPM_BUILD_ROOT +mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/cron.d +mkdir -p $RPM_BUILD_ROOT/%{_datadir}/php/passbolt/logs +mkdir -p $RPM_BUILD_ROOT/usr/local/bin +cp -r config $RPM_BUILD_ROOT/%{_sysconfdir}/passbolt/ +cp -r cron.d/passbolt-server $RPM_BUILD_ROOT/%{_sysconfdir}/cron.d/passbolt-%{_passbolt_flavour}-server +cp -r index.php $RPM_BUILD_ROOT/%{_datadir}/php/passbolt +cp -r bin $RPM_BUILD_ROOT/%{_datadir}/php/passbolt +cp -r plugins $RPM_BUILD_ROOT/%{_datadir}/php/passbolt +cp -r resources $RPM_BUILD_ROOT/%{_datadir}/php/passbolt +cp -r src $RPM_BUILD_ROOT/%{_datadir}/php/passbolt +cp -r templates $RPM_BUILD_ROOT/%{_datadir}/php/passbolt +cp -r vendor $RPM_BUILD_ROOT/%{_datadir}/php/passbolt +cp -r webroot $RPM_BUILD_ROOT/%{_datadir}/php/passbolt +cp -r _passbolt-configure/* $RPM_BUILD_ROOT/usr/local/bin + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%{_datadir}/php/passbolt/index.php +%{_datadir}/php/passbolt/bin +%{_datadir}/php/passbolt/logs +%{_datadir}/php/passbolt/plugins +%{_datadir}/php/passbolt/resources +%{_datadir}/php/passbolt/src +%{_datadir}/php/passbolt/templates +%{_datadir}/php/passbolt/vendor +%{_datadir}/php/passbolt/webroot +%{_sysconfdir}/passbolt +%{_sysconfdir}/cron.d/passbolt-%{_passbolt_flavour}-server +/usr/local/bin/conf +/usr/local/bin/passbolt-configure + +%post +chown -R nginx:nginx %{_datadir}/php/passbolt +chown -R nginx:nginx %{_sysconfdir}/passbolt/ +chmod +x /usr/local/bin/passbolt-configure +mkdir -p /var/lib/passbolt/.gnupg +chown -R nginx:nginx /var/lib/passbolt +su - nginx -s /bin/bash -c "gpg --list-keys --home /var/lib/passbolt/.gnupg" +mkdir -p /var/log/passbolt +chown -R nginx:nginx /var/log/passbolt +# https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/ +if [ $1 -gt 1 ] +then + su -c '/usr/share/php/passbolt/bin/cake passbolt migrate' -s /bin/bash nginx >> /var/log/passbolt/upgrade.log + su -c '/usr/share/php/passbolt/bin/cake cache clear_all' -s /bin/bash nginx >> /var/log/passbolt/upgrade.log +fi + +%changelog + diff --git a/rpm/specs/passbolt-server-selinux.spec b/rpm/specs/passbolt-server-selinux.spec new file mode 100644 index 0000000000..911509bbec --- /dev/null +++ b/rpm/specs/passbolt-server-selinux.spec @@ -0,0 +1,82 @@ +%global selinuxtype targeted +%global moduletype services +%global modulenames passbolt-server + +# Usage: _format var format +# Expand 'modulenames' into various formats as needed +# Format must contain '$x' somewhere to do anything useful +%global _format() export %1=""; for x in %{modulenames}; do %1+=%2; %1+=" "; done; + +# Relabel files +%global relabel_files() \ # ADD files in *.fc file + + +# Version of distribution SELinux policy package +%global selinux_policyver 3.13.1-128.6.fc22 + +# Package information +Name: passbolt-server-selinux +Version: %{_passbolt_selinux_version} +Release: 1%{?dist} +License: GPLv2 +Group: System Environment/Base +Summary: SELinux Policies for Passbolt +BuildArch: noarch +URL: https://www.passbolt.com +Requires(post): selinux-policy-base >= %{selinux_policyver}, selinux-policy-targeted >= %{selinux_policyver}, policycoreutils, %{_policycoreutils_python}, libselinux-utils +BuildRequires: selinux-policy selinux-policy-devel + +Source: %{name}-%{version}.tar.gz + +%description +SELinux policy modules for use with Passbolt + +%prep +%setup -q + +%build +make SHARE="%{_datadir}" TARGETS="%{modulenames}" + +%install + +# Install SELinux interfaces +%_format INTERFACES $x.if +install -d %{buildroot}%{_datadir}/selinux/devel/include/%{moduletype} +install -p -m 644 $INTERFACES \ + %{buildroot}%{_datadir}/selinux/devel/include/%{moduletype} + +# Install policy modules +%_format MODULES $x.pp.bz2 +install -d %{buildroot}%{_datadir}/selinux/packages +install -m 0644 $MODULES \ + %{buildroot}%{_datadir}/selinux/packages + +%post +# +# Install all modules in a single transaction +# +%_format MODULES %{_datadir}/selinux/packages/$x.pp.bz2 +%{_sbindir}/semodule -n -s %{selinuxtype} -i $MODULES +if %{_sbindir}/selinuxenabled ; then + %{_sbindir}/load_policy + %relabel_files +fi + + +%postun +if [ $1 -eq 0 ]; then + %{_sbindir}/semodule -n -r %{modulenames} &> /dev/null || : + if %{_sbindir}/selinuxenabled ; then + %{_sbindir}/load_policy + %relabel_files + fi +fi + +%files +%defattr(-,root,root,0755) +%attr(0644,root,root) %{_datadir}/selinux/packages/*.pp.bz2 +%attr(0644,root,root) %{_datadir}/selinux/devel/include/%{moduletype}/*.if + +%changelog +* Fri Mar 06 2015 Lukas Vrabec - 0.1.0-1 +- First Build diff --git a/rpm/specs/passbolt-server.spec b/rpm/specs/passbolt-server.spec new file mode 100644 index 0000000000..20c06f36d2 --- /dev/null +++ b/rpm/specs/passbolt-server.spec @@ -0,0 +1,127 @@ +Name: passbolt-%{_passbolt_flavour}-server +Version: %{_passbolt_version} +Release: 1%{?dist} +Summary: Passbolt +BuildArch: noarch + +License: WTFPL | http://www.wtfpl.net/ +URL: https://www.passbolt.com +Source0: %{name}-%{version}.tar.gz +Patch0: 02_webpaths_setup.diff +Patch1: 01_paths_setup.diff +Patch2: 03_cake_import_paths.diff + +Requires: php >= 7.3 +Requires: php-cli >= 7.3 +Requires: php-mbstring >= 7.3 +Requires: php-intl >= 7.3 +Requires: php-mysqlnd >= 7.3 +Requires: php-fpm >= 7.3 +Requires: php-xml >= 7.3 +Requires: php-gd >= 7.3 +Requires: php-process >= 7.3 +Requires: php-json >= 7.3 +Requires: php-pecl-gnupg +%if "%{_passbolt_flavour}" == "pro" +Requires: php-ldap >= 7.3 +%endif +Requires: cronie +Requires: nginx +Requires: mariadb-server +Requires: passbolt-server-selinux +Requires: haveged + +%description +Passbolt on RPM POC + +%prep +%setup -q +%patch0 -p1 +%patch1 -p1 +%patch2 -p1 + +%install +rm -rf $RPM_BUILD_ROOT +mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/cron.d +mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/logrotate.d +mkdir -p $RPM_BUILD_ROOT/%{_datadir}/php/passbolt/logs +mkdir -p $RPM_BUILD_ROOT/%{_datadir}/passbolt/examples +mkdir -p $RPM_BUILD_ROOT/%{_datadir}/doc/passbolt-%{_passbolt_flavour}-server +mkdir -p $RPM_BUILD_ROOT/usr/local/bin +cp -r config $RPM_BUILD_ROOT/%{_sysconfdir}/passbolt/ +cp -r config/app.default.php $RPM_BUILD_ROOT/%{_sysconfdir}/passbolt/app.php +rm -f $RPM_BUILD_ROOT/%{_sysconfdir}/passbolt/gpg/* +cp -r cron.d/passbolt-server $RPM_BUILD_ROOT/%{_sysconfdir}/cron.d/passbolt-%{_passbolt_flavour}-server +cp -r logrotate.d/passbolt-server $RPM_BUILD_ROOT/%{_sysconfdir}/logrotate.d/passbolt-%{_passbolt_flavour}-server +cp -r index.php $RPM_BUILD_ROOT/%{_datadir}/php/passbolt +cp -r bin $RPM_BUILD_ROOT/%{_datadir}/php/passbolt +rm -f $RPM_BUILD_ROOT/%{_datadir}/php/passbolt/bin/{test,ci-test} +cp -r plugins $RPM_BUILD_ROOT/%{_datadir}/php/passbolt +cp -r resources $RPM_BUILD_ROOT/%{_datadir}/php/passbolt +cp -r src $RPM_BUILD_ROOT/%{_datadir}/php/passbolt +cp -r templates $RPM_BUILD_ROOT/%{_datadir}/php/passbolt +cp -r vendor $RPM_BUILD_ROOT/%{_datadir}/php/passbolt +cp -r webroot $RPM_BUILD_ROOT/%{_datadir}/php/passbolt +cp -r _passbolt-configure/* $RPM_BUILD_ROOT/usr/local/bin + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%{_datadir}/php/passbolt +%{_datadir}/php/passbolt/index.php +%{_datadir}/php/passbolt/bin +%{_datadir}/php/passbolt/logs +%{_datadir}/passbolt/examples +%{_datadir}/doc/passbolt-%{_passbolt_flavour}-server +%{_datadir}/php/passbolt/plugins +%{_datadir}/php/passbolt/resources +%{_datadir}/php/passbolt/src +%{_datadir}/php/passbolt/templates +%{_datadir}/php/passbolt/vendor +%{_datadir}/php/passbolt/webroot +%{_sysconfdir}/passbolt +%{_sysconfdir}/cron.d/passbolt-%{_passbolt_flavour}-server +%{_sysconfdir}/logrotate.d/passbolt-%{_passbolt_flavour}-server +/usr/local/bin/conf +/usr/local/bin/passbolt-configure + +%post +chmod +x /usr/local/bin/passbolt-configure +mkdir -p /var/lib/passbolt/{.gnupg,tmp} +# Adjust some system directory permissions +chown -R nginx:nginx /var/lib/passbolt +chown -R nginx:nginx /var/log/passbolt +# Img public folder should be writeable by nginx +chown -R nginx:nginx %{_datadir}/php/passbolt/webroot/img/public +chmod 0644 %{_datadir}/php/passbolt/webroot/img/public/empty +chown -R root:nginx %{_sysconfdir}/passbolt +# No file should be executable except bin/cake +chmod -R -x+X %{_sysconfdir}/passbolt +chmod +x %{_datadir}/php/passbolt/bin/cake +chmod +x %{_datadir}/php/passbolt/bin/cake.php +chmod +x %{_datadir}/php/passbolt/bin/cron +chmod +x %{_datadir}/php/passbolt/bin/healthcheck +chmod +x %{_datadir}/php/passbolt/bin/status-report +chmod +x %{_datadir}/php/passbolt/bin/versions +# Configuration files not readable by others +chmod -R o-rw %{_sysconfdir}/passbolt/* +# nginx needs to write on /etc/passbolt for webinstaller +chmod 0770 %{_sysconfdir}/passbolt +chmod 0770 %{_sysconfdir}/passbolt/gpg +chmod -R o-rwx %{_sysconfdir}/passbolt/* +# Strict permissions for gnupg server keyring +chmod 0700 /var/lib/passbolt/.gnupg/ + +#su - nginx -s /bin/bash -c "gpg --list-keys --home /var/lib/passbolt/.gnupg" +mkdir -p /var/log/passbolt +chown -R nginx:nginx /var/log/passbolt +# https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/ +if [ $1 -gt 1 ] +then + su -c '%{_datadir}/php/passbolt/bin/cake passbolt migrate' -s /bin/bash nginx >> /var/log/passbolt/upgrade.log + su -c '%{_datadir}/php/passbolt/bin/cake cache clear_all' -s /bin/bash nginx >> /var/log/passbolt/upgrade.log +fi + +%changelog + diff --git a/rpm/specs/php-pecl-gnupg.spec b/rpm/specs/php-pecl-gnupg.spec new file mode 100644 index 0000000000..cd2eed0117 --- /dev/null +++ b/rpm/specs/php-pecl-gnupg.spec @@ -0,0 +1,341 @@ +# spec file for php-pecl-gnupg +# +# Copyright (c) 2012-2021 Remi Collet +# License: CC-BY-SA +# http://creativecommons.org/licenses/by-sa/4.0/ +# +# Please, preserve the changelog entries +# +%if 0%{?scl:1} +%global sub_prefix %{scl_prefix} +%scl_package php-pecl-gnupg +%endif +%global with_tests 0%{!?_without_tests:1} + +%global pecl_name gnupg +%global with_zts 0%{!?_without_zts:%{?__ztsphp:1}} +%if "%{php_version}" < "5.6" +%global ini_name %{pecl_name}.ini +%else +%global ini_name 40-%{pecl_name}.ini +%endif +%global upstream_version 1.5.0 +#global upstream_prever RC2 + +Summary: Wrapper around the gpgme library +Name: %{?sub_prefix}php-pecl-gnupg +Version: %{upstream_version}%{?upstream_prever:~%{upstream_prever}} +Release: 1%{?dist}%{!?nophptag:%(%{__php} -r 'echo ".".PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;')} + +License: BSD +URL: https://pecl.php.net/package/gnupg +Source0: https://pecl.php.net/get/%{pecl_name}-%{upstream_version}%{?upstream_prever}.tgz + +BuildRequires: make +BuildRequires: %{?dtsprefix}gcc +BuildRequires: %{?scl_prefix}php-devel +BuildRequires: %{?scl_prefix}php-pear +BuildRequires: gpgme-devel +%if 0%{?rhel} == 7 +BuildRequires: gnupg1 +%else +BuildRequires: gnupg +%endif + +Requires: %{?scl_prefix}php(zend-abi) = %{php_zend_api} +Requires: %{?scl_prefix}php(api) = %{php_core_api} +# We force use of /usr/bin/gpg as gpg2 is unusable in non-interactive mode +%if 0%{?rhel} == 7 +Requires: gnupg1 +%else +Requires: gnupg +%endif +%{?_sclreq:Requires: %{?scl_prefix}runtime%{?_sclreq}%{?_isa}} + +Provides: %{?scl_prefix}php-%{pecl_name} = %{version} +Provides: %{?scl_prefix}php-%{pecl_name}%{?_isa} = %{version} +Provides: %{?scl_prefix}php-pecl(%{pecl_name}) = %{version} +Provides: %{?scl_prefix}php-pecl(%{pecl_name})%{?_isa} = %{version} +%if "%{?scl_prefix}" != "%{?sub_prefix}" +Provides: %{?scl_prefix}php-pecl-%{pecl_name} = %{version}-%{release} +Provides: %{?scl_prefix}php-pecl-%{pecl_name}%{?_isa} = %{version}-%{release} +%endif + +%if "%{?packager}" == "Remi Collet" && 0%{!?scl:1} && 0%{?rhel} +# Other third party repo stuff +%if "%{php_version}" > "7.3" +Obsoletes: php73-pecl-%{pecl_name} <= %{version} +%endif +%if "%{php_version}" > "7.4" +Obsoletes: php74-pecl-%{pecl_name} <= %{version} +%endif +%if "%{php_version}" > "8.0" +Obsoletes: php80-pecl-%{pecl_name} <= %{version} +%endif +%endif + + +%description +This module allows you to interact with gnupg. + +Documentation : http://www.php.net/gnupg + +Package built for PHP %(%{__php} -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;')%{?scl: as Software Collection (%{scl} by %{?scl_vendor}%{!?scl_vendor:rh})}. + + +%prep +%setup -c -q + +# Don't install/register tests +sed -e 's/role="test"/role="src"/' \ + %{?_licensedir:-e '/LICENSE/s/role="doc"/role="src"/' } \ + -i package.xml + +# Create configuration file +cat >%{ini_name} << 'EOF' +; Enable %{pecl_name} extension module +extension=%{pecl_name}.so +EOF + +mv %{pecl_name}-%{upstream_version}%{?upstream_prever} NTS +cd NTS +# Check extension version +extver=$(sed -n '/#define PHP_GNUPG_VERSION/{s/.* "//;s/".*$//;p}' php_gnupg.h) +if test "x${extver}" != "x%{upstream_version}%{?upstream_prever}"; then + : Error: Upstream extension version is ${extver}, expecting %{upstream_version}%{?upstream_prever}. + exit 1 +fi +cd .. + +%if %{with_zts} +# Build ZTS extension if ZTS devel available (fedora >= 17) +cp -r NTS ZTS +%endif + + +%build +%{?dtsenable} + +export PHP_RPATH=no +export CFLAGS="$RPM_OPT_FLAGS -D_FILE_OFFSET_BITS=64" +%if 0%{?rhel} == 7 +export GPG1=$(which gpg1 2>/dev/null || which gpg) +$GPG1 --version +%else +gpg --version +%endif + +peclbuild() { +%{_bindir}/${1}ize +%configure \ +%if 0%{?rhel} == 7 + --with-gpg=$GPG1 \ +%endif + --with-libdir=%{_lib} \ + --with-php-config=%{_bindir}/${1}-config + +make %{?_smp_mflags} +} + +cd NTS +peclbuild php + +%if %{with_zts} +cd ../ZTS +peclbuild zts-php +%endif + + +%install +%{?dtsenable} + +make install -C NTS INSTALL_ROOT=%{buildroot} + +# Drop in the bit of configuration +install -D -m 644 %{ini_name} %{buildroot}%{php_inidir}/%{ini_name} + +# Install XML package description +install -D -m 644 package.xml %{buildroot}%{pecl_xmldir}/%{name}.xml + +%if %{with_zts} +make install -C ZTS INSTALL_ROOT=%{buildroot} +install -D -m 644 %{ini_name} %{buildroot}%{php_ztsinidir}/%{ini_name} +%endif + +# Documentation +for i in $(grep 'role="doc"' package.xml | sed -e 's/^.*name="//;s/".*$//') +do install -Dpm 644 NTS/$i %{buildroot}%{pecl_docdir}/%{pecl_name}/$i +done + + +%if 0%{?fedora} < 24 && 0%{?rhel} < 8 +# when pear installed alone, after us +%triggerin -- %{?scl_prefix}php-pear +if [ -x %{__pecl} ] ; then + %{pecl_install} %{pecl_xmldir}/%{name}.xml >/dev/null || : +fi + +# posttrans as pear can be installed after us +%posttrans +if [ -x %{__pecl} ] ; then + %{pecl_install} %{pecl_xmldir}/%{name}.xml >/dev/null || : +fi + +%postun +if [ $1 -eq 0 -a -x %{__pecl} ] ; then + %{pecl_uninstall} %{pecl_name} >/dev/null || : +fi +%endif + + +%check +sed -e '/GnuPG v1/d' \ + -i ?TS/tests/gnupg_*_export.phpt + +unset GPG_AGENT_INFO + +cd NTS +: Check if build NTS extension can be loaded +%{__php} -n -q \ + -d extension=%{buildroot}%{php_extdir}/%{pecl_name}.so \ + --modules | grep %{pecl_name} + +%if %{with_tests} +: Run upstream test suite for NTS extension +TEST_PHP_EXECUTABLE=%{_bindir}/php \ +REPORT_EXIT_STATUS=1 \ +NO_INTERACTION=1 \ +%{__php} -n run-tests.php \ + -n -q \ + -d extension=%{buildroot}%{php_extdir}/%{pecl_name}.so \ + --show-diff +%endif + +%if %{with_zts} +cd ../ZTS +: Check if build ZTS extension can be loaded +%{__ztsphp} -n -q \ + -d extension=%{buildroot}%{php_ztsextdir}/%{pecl_name}.so \ + --modules | grep %{pecl_name} +%endif + + +%files +%{?_licensedir:%license NTS/LICENSE} +%doc %{pecl_docdir}/%{pecl_name} +%{pecl_xmldir}/%{name}.xml + +%config(noreplace) %{php_inidir}/%{ini_name} +%{php_extdir}/%{pecl_name}.so + +%if %{with_zts} +%config(noreplace) %{php_ztsinidir}/%{ini_name} +%{php_ztsextdir}/%{pecl_name}.so +%endif + + +%changelog +* Mon Apr 12 2021 Remi Collet - 1.5.0-1 +- Update to 1.5.0 + +* Mon Mar 8 2021 Remi Collet - 1.5.0~RC2-1 +- Update to 1.5.0RC2 +- drop patch merged upstream + +* Mon Feb 15 2021 Remi Collet - 1.5.0~RC1-1 +- Update to 1.5.0RC1 +- open https://github.com/php-gnupg/php-gnupg/pull/25 + missing files in pecl archive +- open https://github.com/php-gnupg/php-gnupg/pull/26 + also cleanup socket files +- open https://github.com/php-gnupg/php-gnupg/pull/27 + fix build with old libgpgme 1.3 +- use gnupg version 2 (Fedora and EL-8) + +* Tue Feb 2 2021 Remi Collet - 1.4.0-10 +- EL-7 rebuild + +* Tue Sep 03 2019 Remi Collet - 1.4.0-9 +- rebuild for 7.4.0RC1 + +* Tue Jul 23 2019 Remi Collet - 1.4.0-8 +- rebuild for 7.4.0beta1 + +* Wed Dec 12 2018 Remi Collet - 1.4.0-7 +- cleanup for EL-8 + +* Thu Aug 16 2018 Remi Collet - 1.4.0-6 +- rebuild for 7.3.0beta2 new ABI + +* Wed Jul 18 2018 Remi Collet - 1.4.0-5 +- rebuld for 7.3.0alpha4 new ABI + +* Tue Jul 18 2017 Remi Collet - 1.4.0-4 +- rebuild for PHP 7.2.0beta1 new API + +* Thu Dec 1 2016 Remi Collet - 1.4.0-3 +- rebuild with PHP 7.1.0 GA + +* Wed Sep 14 2016 Remi Collet - 1.4.0-2 +- rebuild for PHP 7.1 new API version + +* Thu Sep 1 2016 Remi Collet - 1.4.0-1 +- update to 1.4.0 (stable) + +* Mon Jul 25 2016 Remi Collet - 1.4.0-0.3.RC2 +- update to 1.4.0RC2 (beta) + +* Fri Jun 17 2016 Remi Collet - 1.4.0-0.2.RC1 +- always use gnupg < 2 + +* Thu Jun 16 2016 Remi Collet - 1.4.0-0.1.RC1 +- update to 1.4.0RC1 (beta) + +* Tue Mar 8 2016 Remi Collet - 1.3.6-2 +- adapt for F24 + +* Thu Feb 12 2015 Remi Collet - 1.3.6-1 +- Update to 1.3.6 +- don't install test suite +- drop runtime dependency on pear, new scriptlets + +* Wed Dec 24 2014 Remi Collet - 1.3.3-5.1 +- Fedora 21 SCL mass rebuild + +* Tue Aug 26 2014 Remi Collet - 1.3.3-5 +- improve SCL build + +* Wed Apr 16 2014 Remi Collet - 1.3.3-4 +- add numerical prefix to extension configuration file + +* Wed Mar 26 2014 Remi Collet - 1.3.3-3 +- allow SCL build + +* Mon Mar 17 2014 Remi Collet - 1.3.3-2 +- cleanups +- make ZTS build optional +- install doc in pecl_docdir +- install tests in pecl_testdir + +* Wed Jul 17 2013 Remi Collet - 1.3.3-1 +- update to 1.3.3 + +* Sun Jun 30 2013 Remi Collet - 1.3.2-4 +- ignore test result + +* Fri Nov 30 2012 Remi Collet - 1.3.2-3.1 +- also provides php-gnupg + cleanups + +* Sun May 06 2012 Remi Collet - 1.3.2-3 +- improve patch + +* Sat Jan 28 2012 Remi Collet - 1.3.2-2 +- build against PHP 5.4 + +* Sat Jan 28 2012 Remi Collet - 1.3.2-1 +- Initial RPM +- open upstream bugs + https://bugs.php.net/60913 - test suite fails + https://bugs.php.net/60914 - bad version + https://bugs.php.net/60915 - php 5.4 build fails + https://bugs.php.net/60916 - force use of /usr/bin/gpg diff --git a/rpm/specs/rhel-requires.inc b/rpm/specs/rhel-requires.inc new file mode 100644 index 0000000000..68cb5d0cf6 --- /dev/null +++ b/rpm/specs/rhel-requires.inc @@ -0,0 +1,19 @@ +Requires: php >= 7.3 +Requires: php-cli >= 7.3 +Requires: php-mbstring >= 7.3 +Requires: php-intl >= 7.3 +Requires: php-mysqlnd >= 7.3 +Requires: php-fpm >= 7.3 +Requires: php-xml >= 7.3 +Requires: php-gd >= 7.3 +Requires: php-process >= 7.3 +Requires: php-json >= 7.3 +Requires: php-pecl-gnupg +%if "%{_passbolt_flavour}" == "pro" +Requires: php-ldap >= 7.3 +%endif +Requires: cronie +Requires: nginx +Requires: mariadb-server +Requires: passbolt-server-selinux +Requires: haveged diff --git a/src/Application.php b/src/Application.php index f3dd09addf..725d0f4c9d 100644 --- a/src/Application.php +++ b/src/Application.php @@ -16,33 +16,47 @@ */ namespace App; +use App\Authenticator\SessionAuthenticationService; +use App\Authenticator\SessionIdentificationService; +use App\Authenticator\SessionIdentificationServiceInterface; +use App\Middleware\ContainerInjectorMiddleware; use App\Middleware\ContentSecurityPolicyMiddleware; use App\Middleware\CsrfProtectionMiddleware; use App\Middleware\GpgAuthHeadersMiddleware; +use App\Middleware\ServerRequestInterfaceInjectionMiddleware; +use App\Middleware\SessionAuthPreventDeletedUsersMiddleware; use App\Middleware\SessionPreventExtensionMiddleware; use App\Notification\Email\EmailSubscriptionDispatcher; use App\Notification\Email\Redactor\CoreEmailRedactorPool; use App\Notification\EmailDigest\DigestRegister\GroupDigests; use App\Notification\EmailDigest\DigestRegister\ResourceDigests; use App\Notification\NotificationSettings\CoreNotificationSettingsDefinition; -use Authentication\AuthenticationService; +use App\Service\Avatars\AvatarsConfigurationService; +use App\ServiceProvider\SetupServiceProvider; +use App\ServiceProvider\UserServiceProvider; +use App\Utility\Application\FeaturePluginAwareTrait; use Authentication\AuthenticationServiceInterface; use Authentication\AuthenticationServiceProviderInterface; use Authentication\Middleware\AuthenticationMiddleware; use Cake\Core\Configure; +use Cake\Core\ContainerInterface; use Cake\Core\Exception\MissingPluginException; use Cake\Error\Middleware\ErrorHandlerMiddleware; use Cake\Http\BaseApplication; use Cake\Http\Middleware\BodyParserMiddleware; use Cake\Http\Middleware\SecurityHeadersMiddleware; use Cake\Http\MiddlewareQueue; +use Cake\Http\ServerRequest; use Cake\Routing\Middleware\AssetMiddleware; use Cake\Routing\Middleware\RoutingMiddleware; +use Cake\Routing\Router; use Passbolt\WebInstaller\Middleware\WebInstallerMiddleware; use Psr\Http\Message\ServerRequestInterface; class Application extends BaseApplication implements AuthenticationServiceProviderInterface { + use FeaturePluginAwareTrait; + /** * Setup the PSR-7 middleware passbolt application will use. * @@ -53,7 +67,7 @@ public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue { $csrf = new CsrfProtectionMiddleware(); // Token check will be skipped when callback returns `true`. - $csrf->skipCheckCallback(function ($request) use ($csrf) { + $csrf->skipCheckCallback(function (ServerRequest $request) use ($csrf) { return $csrf->skipCsrfProtection($request); }); @@ -68,17 +82,18 @@ public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue * - Apply CSRF protection */ $middlewareQueue - ->add(ContentSecurityPolicyMiddleware::class) + ->prepend(new ContainerInjectorMiddleware($this->getContainer())) + ->add(new ContentSecurityPolicyMiddleware()) ->add(new ErrorHandlerMiddleware(Configure::read('Error'))) - ->add(new AssetMiddleware([ - 'cacheTime' => Configure::read('Asset.cacheTime'), - ])) + ->add(new AssetMiddleware(['cacheTime' => Configure::read('Asset.cacheTime')])) ->add(new RoutingMiddleware($this)) ->add(new SessionPreventExtensionMiddleware()) ->add(new BodyParserMiddleware()) - ->add(new AuthenticationMiddleware($this)) - ->add(GpgAuthHeadersMiddleware::class) - ->add($csrf); + ->add(SessionAuthPreventDeletedUsersMiddleware::class) + ->insertAfter(SessionAuthPreventDeletedUsersMiddleware::class, new AuthenticationMiddleware($this)) + ->add(new GpgAuthHeadersMiddleware()) + ->add($csrf) + ->insertAt(1000, ServerRequestInterfaceInjectionMiddleware::class); // Injects the server request at the end of the middleware queue /* * Additional security headers @@ -124,6 +139,7 @@ public function bootstrap(): void } $this->initEmails(); + (new AvatarsConfigurationService())->loadConfiguration(); } /** @@ -213,6 +229,7 @@ protected function addPassboltPlugins() // Add Common plugins. $this->addPlugin('Passbolt/AccountSettings', ['bootstrap' => true, 'routes' => true]); $this->addPlugin('Passbolt/Import', ['bootstrap' => true, 'routes' => true]); + $this->addPlugin('Passbolt/InFormIntegration', ['bootstrap' => true, 'routes' => false]); $this->addPlugin('Passbolt/Locale', ['bootstrap' => true, 'routes' => true]); $this->addPlugin('Passbolt/Export', ['bootstrap' => true, 'routes' => false]); $this->addPlugin('Passbolt/ResourceTypes', ['bootstrap' => true, 'routes' => false]); @@ -220,6 +237,9 @@ protected function addPassboltPlugins() $this->addPlugin('Passbolt/EmailNotificationSettings', ['bootstrap' => true, 'routes' => true]); $this->addPlugin('Passbolt/EmailDigest', ['bootstrap' => true, 'routes' => true]); $this->addPlugin('Passbolt/Reports', ['bootstrap' => true, 'routes' => true]); + $this->addFeaturePluginIfEnabled($this, 'Mobile'); + $this->addFeaturePluginIfEnabled($this, 'JwtAuthentication'); + $this->addPlugin('Passbolt/PasswordGenerator', ['routes' => true]); if (!WebInstallerMiddleware::isConfigured()) { $this->addPlugin('Passbolt/WebInstaller', ['bootstrap' => true, 'routes' => true]); @@ -254,6 +274,17 @@ protected function addCliPlugins() return $this; } + /** + * @inheritDoc + */ + public function services(ContainerInterface $container): void + { + $container->add(AuthenticationServiceInterface::class, SessionAuthenticationService::class); + $container->add(SessionIdentificationServiceInterface::class, SessionIdentificationService::class); + $container->addServiceProvider(new SetupServiceProvider()); + $container->addServiceProvider(new UserServiceProvider()); + } + /** * Returns a service provider instance. * @@ -262,24 +293,26 @@ protected function addCliPlugins() */ public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface { - $service = new AuthenticationService(); - - // Define where users should be redirected to when they are not authenticated - // The login url is provided in string format because the routes are not loaded yet /** @var \Cake\Http\ServerRequest $request */ + $loginUrl = Router::url([ + 'prefix' => 'Auth', + 'plugin' => null, + 'controller' => 'AuthLogin', + 'action' => 'loginGet', + '_method' => 'GET', + '_ext' => $request->is('json') ? 'json' : null, + ]); + + /** @var \Authentication\AuthenticationService $auth */ + $auth = $this->getContainer()->get(AuthenticationServiceInterface::class); if (!$request->is('json')) { - $loginUrl = '/auth/login'; - $service->setConfig([ + $auth->setConfig([ 'unauthenticatedRedirect' => $loginUrl, 'logoutRedirect' => $loginUrl, 'queryParam' => 'redirect', ]); } - // Load the authenticators. Session should be first. - $service->loadAuthenticator('Authentication.Session'); - $service->loadAuthenticator('Gpg'); - - return $service; + return $auth; } } diff --git a/src/Authenticator/AbstractSessionIdentificationService.php b/src/Authenticator/AbstractSessionIdentificationService.php new file mode 100644 index 0000000000..810370d097 --- /dev/null +++ b/src/Authenticator/AbstractSessionIdentificationService.php @@ -0,0 +1,52 @@ +getAttribute('authentication'); + if ($authService instanceof AuthenticationService) { + return $authService->getResult()->isValid(); + } + + return false; + } + + /** + * @inheritDoc + */ + public function checkAuthenticationToken(ServerRequest $request, AuthenticationToken $tokenToValidate): bool + { + return (new AuthenticationTokensSessionService())->checkSession( + $tokenToValidate, + $this->getSessionIdentifier($request) + ); + } +} diff --git a/src/Authenticator/GpgAuthenticator.php b/src/Authenticator/GpgAuthenticator.php index f58a2605f5..b4e66e2708 100644 --- a/src/Authenticator/GpgAuthenticator.php +++ b/src/Authenticator/GpgAuthenticator.php @@ -87,7 +87,7 @@ public function unauthenticated(ServerRequest $request, Response $response) if ($request->is('json')) { throw new ForbiddenException(__('You need to login to access this location.')); } - // Otherwise we let the controller handle it + // Otherwise we let the controller handle the redirections } /** @@ -114,19 +114,20 @@ private function authenticationFailedResult(string $status): ResultInterface */ private function authenticationSuccessResult(): ResultInterface { - return new Result($this->_user->toArray(), Result::SUCCESS, $this->headers); + return new Result(['user' => $this->_user->toArray()], Result::SUCCESS, $this->headers); } /** * Authenticate * See. https://www.passbolt.com/help/tech/auth * - * @param \Cake\Http\ServerRequest $request interface for accessing request parameters + * @param \Psr\Http\Message\ServerRequestInterface $request interface for accessing request parameters * @return \Authentication\Authenticator\ResultInterface User|false the user or false if authentication failed */ public function authenticate(ServerRequestInterface $request): ResultInterface { try { + /** @var \Cake\Http\ServerRequest $request */ // Init keyring and try to pre-identify the user using fingerprint if (!$this->_initForAllSteps($request)) { return $this->authenticationFailedResult(Result::FAILURE_IDENTITY_NOT_FOUND); diff --git a/src/Authenticator/Identifier/SessionIdentifier.php b/src/Authenticator/Identifier/SessionIdentifier.php new file mode 100644 index 0000000000..37afc47692 --- /dev/null +++ b/src/Authenticator/Identifier/SessionIdentifier.php @@ -0,0 +1,40 @@ +getResolver()->find([self::CREDENTIAL_USERNAME => $credentials[self::CREDENTIAL_USERNAME],]); + } +} diff --git a/src/Authenticator/SessionAuthenticationService.php b/src/Authenticator/SessionAuthenticationService.php new file mode 100644 index 0000000000..ebf5c20ceb --- /dev/null +++ b/src/Authenticator/SessionAuthenticationService.php @@ -0,0 +1,34 @@ +loadAuthenticator('Authentication.Session'); + $this->loadAuthenticator('Gpg'); + } +} diff --git a/tests/TestCase/Controller/Errors/ErrorNotFoundControllerTest.php b/src/Authenticator/SessionIdentificationService.php similarity index 58% rename from tests/TestCase/Controller/Errors/ErrorNotFoundControllerTest.php rename to src/Authenticator/SessionIdentificationService.php index 93066b3965..6a05566ab9 100644 --- a/tests/TestCase/Controller/Errors/ErrorNotFoundControllerTest.php +++ b/src/Authenticator/SessionIdentificationService.php @@ -12,21 +12,23 @@ * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) * @license https://opensource.org/licenses/AGPL-3.0 AGPL License * @link https://www.passbolt.com Passbolt(tm) - * @since 2.0.0 + * @since 3.3.0 */ -namespace App\Test\TestCase\Controller\Errors; +namespace App\Authenticator; -use App\Test\Lib\AppIntegrationTestCase; +use Cake\Http\ServerRequest; -class ErrorNotFoundControllerTest extends AppIntegrationTestCase +class SessionIdentificationService extends AbstractSessionIdentificationService { - public function testErrorNotSupported() + /** + * @inheritDoc + */ + public function getSessionIdentifier(ServerRequest $request): ?string { - if (!file_exists(PLUGINS . 'Passbolt' . DS . 'AccountSettings')) { - $this->get('/account/settings.json'); - $this->assertResponseError(); - } else { - $this->markTestSkipped(); + if (!$this->isAuthenticated($request)) { + return null; } + + return $request->getSession()->id(); } } diff --git a/src/Authenticator/SessionIdentificationServiceInterface.php b/src/Authenticator/SessionIdentificationServiceInterface.php new file mode 100644 index 0000000000..c41c836c56 --- /dev/null +++ b/src/Authenticator/SessionIdentificationServiceInterface.php @@ -0,0 +1,50 @@ +get('FileStorage') ->setTable('file_storage'); + /** @phpstan-ignore-next-line */ $AvatarsTable->setConnection(ConnectionManager::get($datasource)); + /** @phpstan-ignore-next-line */ $FileStorageTable->setConnection(ConnectionManager::get($datasource)); $results = (new AvatarsTransferService($AvatarsTable, $FileStorageTable, true))->transfer(); diff --git a/src/Command/CleanupCommand.php b/src/Command/CleanupCommand.php index 059a837faa..edc7aaaecd 100644 --- a/src/Command/CleanupCommand.php +++ b/src/Command/CleanupCommand.php @@ -25,9 +25,9 @@ class CleanupCommand extends PassboltCommand { /** - * @var array The list of cleanup jobs to perform. + * @var array|null The list of cleanup jobs to perform. */ - private static $cleanups; + private static $cleanups = null; /** * @var array The list of default cleanup jobs to perform. @@ -69,6 +69,11 @@ class CleanupCommand extends PassboltCommand 'Resources' => [ 'Missing ResourceType Id', ], + 'Avatars' => [ + 'Soft Deleted Users', + 'Hard Deleted Users', + 'Hard Deleted Profiles', + ], ]; /** @@ -125,7 +130,9 @@ public function initialize(): void $this->loadModel('Permissions'); $this->loadModel('AuthenticationTokens'); - self::resetCleanups(); + if (!isset(self::$cleanups)) { + self::resetCleanups(); + } } /** diff --git a/src/Command/CommandBootstrap.php b/src/Command/CommandBootstrap.php index 78af2b1f8f..d3039b25cc 100644 --- a/src/Command/CommandBootstrap.php +++ b/src/Command/CommandBootstrap.php @@ -33,9 +33,9 @@ class CommandBootstrap /** * Instance of class used for singleton. * - * @var \App\Command\CommandBootstrap + * @var \App\Command\CommandBootstrap|null */ - private static $instance; + private static $instance = null; /** * Init function. diff --git a/src/Command/HealthcheckCommand.php b/src/Command/HealthcheckCommand.php index 9292d7b5a9..0e685351ef 100644 --- a/src/Command/HealthcheckCommand.php +++ b/src/Command/HealthcheckCommand.php @@ -16,15 +16,19 @@ */ namespace App\Command; +use App\Utility\Application\FeaturePluginAwareTrait; use App\Utility\Healthchecks; use Cake\Console\Arguments; use Cake\Console\ConsoleIo; use Cake\Console\ConsoleOptionParser; use Cake\Core\Configure; +use Passbolt\JwtAuthentication\Service\AccessToken\JwtAbstractService; +use Passbolt\JwtAuthentication\Service\AccessToken\JwtKeyPairService; class HealthcheckCommand extends PassboltCommand { use DatabaseAwareCommandTrait; + use FeaturePluginAwareTrait; /** * Total number of errors for that check @@ -109,6 +113,10 @@ public function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionPar ->addOption('application', [ 'help' => __d('cake_console', 'Run passbolt app tests only.'), 'boolean' => true, + ]) + ->addOption('jwt', [ + 'help' => __d('cake_console', 'Run passbolt JWT tests only.'), + 'boolean' => true, ]); $this->addDatasourceOption($parser); @@ -142,7 +150,7 @@ public function execute(Arguments $args, ConsoleIo $io): ?int // if user only want to run one check $paramChecks = []; $checks = [ - 'environment', 'configFiles', 'core', 'ssl', 'database', 'gpg', 'application', + 'environment', 'configFiles', 'core', 'ssl', 'database', 'gpg', 'application', 'jwt', ]; foreach ($checks as $check) { if ($args->getOption($check)) { @@ -200,26 +208,14 @@ public function assertEnvironment($checks = null) ); $this->assert( $checks['environment']['tmpWritable'], - __('The temporary directory and its content are writable.'), - __('The temporary directory and its content are not writable.'), + __('The temporary directory and its content are writable and not executable.'), + __('The temporary directory and its content are not writable, or are executable.'), [ __('Ensure the temporary directory and its content are writable by the webserver user.'), __('you can try:'), 'sudo chown -R ' . PROCESS_USER . ':' . PROCESS_USER . ' ' . TMP, - 'sudo chmod 775 $(find ' . TMP . ' -type d)', - 'sudo chmod 664 $(find ' . TMP . ' -type f)', - ] - ); - $this->assert( - $checks['environment']['imgPublicWritable'], - __('The public image directory and its content are writable.'), - __('The public image directory and its content are not writable.'), - [ - __('Ensure the public image directory and its content are writable by the webserver user.'), - __('you can try:'), - 'sudo chown -R ' . PROCESS_USER . ':' . PROCESS_USER . ' ' . IMAGES . 'public', - 'sudo chmod 775 $(find ' . IMAGES . 'public -type d)', - 'sudo chmod 664 $(find ' . IMAGES . 'public -type f)', + 'sudo chmod -R 775 $(find ' . TMP . ' -type d)', + 'sudo chmod -R 664 $(find ' . TMP . ' -type f)', ] ); $this->assert( @@ -259,6 +255,14 @@ public function assertEnvironment($checks = null) __('See. https://secure.php.net/manual/en/book.mbstring.php'), ] ); +// $this->assert( +// $checks['environment']['allow_url_fopen'], +// __('The allow_url_fopen setting is activated in php.ini.'), +// __('You must activate the allow_url_fopen setting in php.ini to use Passbolt.'), +// [ +// __('See. https://www.php.net/manual/en/filesystem.configuration.php'), +// ] +// ); } /** @@ -678,6 +682,56 @@ public function assertGpg($checks = null) } } + /** + * Assert config files exist + * + * @param array $checks existing results + * @return void + */ + public function assertJWT($checks = null) + { + if (!isset($checks)) { + $checks = Healthchecks::jwt(); + } + + $this->title(__('JWT Authentication')); + + $this->warning( + $checks['jwt']['isEnabled'], + __('The {0} plugin is enabled', 'JWT Authentication'), + __('The {0} plugin is disabled', 'JWT Authentication'), + __('Set the environment variable {0} to true', 'PASSBOLT_PLUGINS_JWT_AUTHENTICATION_ENABLED'), + ); + + if (!$this->isFeaturePluginEnabled('JwtAuthentication')) { + return; + } + + $directory = JwtAbstractService::JWT_CONFIG_DIR; + $this->assert( + $checks['jwt']['jwtWritable'], + "The {$directory} directory is not writable.", + "The {$directory} directory should not be writable.", + [ + 'You can try: ', + 'sudo chown -R ' . PROCESS_USER . ':' . PROCESS_USER . ' ' . $directory, + 'sudo chmod 550 ' . $directory, + 'sudo chmod 440 $(find ' . $directory . ' -type f)', + ] + ); + + $fixCmd = (new JwtKeyPairService())->getCreateJwtKeysCommand(); + $this->assert( + $checks['jwt']['keyPairValid'], + __('A valid JWT key pair was found'), + __('A valid JWT key pair is missing'), + [ + __('Run the create JWT keys script to create a valid JWT secret and public key pair:'), + 'sudo su -s /bin/bash -c "' . $fixCmd . '" ' . PROCESS_USER, + ] + ); + } + /** * Display a success or error message depending on given condition * diff --git a/src/Command/InstallCommand.php b/src/Command/InstallCommand.php index ae07c625d7..5d5e78eae8 100644 --- a/src/Command/InstallCommand.php +++ b/src/Command/InstallCommand.php @@ -17,17 +17,22 @@ namespace App\Command; use App\Model\Entity\Role; +use App\Utility\Application\FeaturePluginAwareTrait; use App\Utility\Healthchecks; use Cake\Console\Arguments; use Cake\Console\ConsoleIo; use Cake\Console\ConsoleOptionParser; use Cake\Core\Configure; use Cake\Core\Exception\Exception; +use Passbolt\JwtAuthentication\Error\Exception\AccessToken\InvalidJwtKeyPairException; +use Passbolt\JwtAuthentication\Service\AccessToken\JwtAbstractService; +use Passbolt\JwtAuthentication\Service\AccessToken\JwtKeyPairService; use PassboltTestData\Command\InsertCommand; class InstallCommand extends PassboltCommand { use DatabaseAwareCommandTrait; + use FeaturePluginAwareTrait; /** * @inheritDoc @@ -94,6 +99,17 @@ public function execute(Arguments $args, ConsoleIo $io): ?int return $this->errorCode(); } + // Create a JWT key pair + if ($this->isFeaturePluginEnabled('JwtAuthentication')) { + $jwtService = new JwtKeyPairService(); + try { + $jwtService->createKeyPair(); + $jwtService->validateKeyPair(); + } catch (InvalidJwtKeyPairException $e) { + $io->abort($e->getMessage()); + } + } + // Quick mode - exit on success if ($args->getOption('quick')) { return $this->quickInstall($args, $io); @@ -381,6 +397,28 @@ protected function healthchecks(Arguments $args, ConsoleIo $io): bool } } + // JWT checks + if ($this->isFeaturePluginEnabled('JwtAuthentication')) { + $checks = Healthchecks::jwt(); + if ($checks['jwt']['keyPairValid'] !== true) { + $fixCmd = (new JwtKeyPairService())->getCreateJwtKeysCommand(); + + $this->error('The JWT key pair is not valid, or cannot be found.', $io); + $this->error('Please run ' . $fixCmd . ' to create a valid pair.', $io); + + return false; + } + + if ($checks['jwt']['jwtWritable'] !== true) { + $folder = JwtAbstractService::JWT_CONFIG_DIR; + $fixCmd = "sudo chmod 775 $(find $folder -type d)"; + $this->error("The directory {$folder} is not writable.", $io); + $this->error('You can try ' . $fixCmd, $io); + + return false; + } + } + $this->success(__('Critical healthchecks are OK'), $io); return true; diff --git a/src/Command/MigratePostgresCommand.php b/src/Command/MigratePostgresCommand.php new file mode 100644 index 0000000000..115acaed00 --- /dev/null +++ b/src/Command/MigratePostgresCommand.php @@ -0,0 +1,90 @@ +setDescription(__('Re-runs the migrations required by Postgres.')); + + $this->addDatasourceOption($parser); + + return $parser; + } + + /** + * @inheritDoc + */ + public function execute(Arguments $args, ConsoleIo $io): ?int + { + parent::execute($args, $io); + + // Root user is not allowed to execute this command. + // This command needs to be executed with the same user as the webserver. + if (!$this->assertNotRoot($io)) { + return $this->errorCode(); + } + + /** @var \Cake\Database\Connection $connection */ + $connection = ConnectionManager::get($args->getOption('datasource')); + if (!($connection->getDriver() instanceof Postgres)) { + $this->error('This command is available with a Postgres connection only.', $io); + $this->abort(); + } + + $this->deletePostgresRelevantMigrations($connection); + + $this->runMigrationsMigrateCommand($args, $io); + + $io->info('Passbolt can now be used with Postgres.'); + + return $this->successCode(); + } + + /** + * Marks as non-migrated the Postgres relevant migrations. + * + * @param \Cake\Database\Connection $connection Connection + * @return void + */ + public function deletePostgresRelevantMigrations(Connection $connection): void + { + $connection->newQuery() + ->delete('phinxlog') + ->where(['phinxlog.migration_name IN' => self::POSTGRES_RELEVANT_MIGRATIONS,]) + ->execute(); + } +} diff --git a/src/Command/MysqlExportCommand.php b/src/Command/MysqlExportCommand.php index 646be6cc94..c60909d3c0 100644 --- a/src/Command/MysqlExportCommand.php +++ b/src/Command/MysqlExportCommand.php @@ -230,7 +230,7 @@ protected function clearPrevious($dir, $newFile, ConsoleIo $io): void protected function getFile($dir, Arguments $args, ConsoleIo $io): ?string { $file = $args->getOption('file'); - if (empty($file)) { + if (!is_string($file) || empty($file)) { $file = 'backup_' . time() . '.sql'; } if (file_exists($dir . $file)) { @@ -261,7 +261,7 @@ protected function getDir(Arguments $args, ConsoleIo $io): ?string mkdir($dir); } } - if (!file_exists($dir)) { + if (!is_string($dir) || !file_exists($dir)) { $this->error(__('Could not access the backup directory: ' . $dir), $io); return null; diff --git a/src/Command/MysqlImportCommand.php b/src/Command/MysqlImportCommand.php index b9bc0b0994..9b99246ddd 100644 --- a/src/Command/MysqlImportCommand.php +++ b/src/Command/MysqlImportCommand.php @@ -92,7 +92,7 @@ public function execute(Arguments $args, ConsoleIo $io): ?int protected function getDir(Arguments $args, ConsoleIo $io): ?string { $dir = $args->getOption('dir'); - if (isset($dir)) { + if (isset($dir) && is_string($dir)) { if (!file_exists($dir)) { $this->error('Error: the directory does not exist' . $dir, $io); @@ -123,7 +123,7 @@ protected function getDir(Arguments $args, ConsoleIo $io): ?string protected function getFile(string $dir, Arguments $args, ConsoleIo $io): ?string { $file = $args->getOption('file'); - if (isset($file)) { + if (isset($file) && is_string($file)) { if (!file_exists($dir . $file)) { $this->error('Error: could not find the SQL file ' . $file, $io); diff --git a/src/Command/PassboltCommand.php b/src/Command/PassboltCommand.php index d7b4f22212..35f6350ecd 100644 --- a/src/Command/PassboltCommand.php +++ b/src/Command/PassboltCommand.php @@ -27,7 +27,10 @@ */ class PassboltCommand extends Command { - public static $userIsRoot = null; + /** + * @var bool + */ + public static $isUserRoot = false; /** * The Passbolt welcome banner should be shown only once. @@ -46,8 +49,8 @@ public function initialize(): void CommandBootstrap::init(); - if (!isset(self::$userIsRoot)) { - self::$userIsRoot = (PROCESS_USER === 'root'); + if (!isset(self::$isUserRoot)) { + self::$isUserRoot = (PROCESS_USER === 'root'); } } @@ -221,7 +224,7 @@ protected function success(string $msg, ConsoleIo $io): void */ protected function assertNotRoot(ConsoleIo $io): bool { - if (self::$userIsRoot) { + if (self::$isUserRoot) { $io->out(); $this->error('Passbolt commands cannot be executed as root.', $io); $io->out(); @@ -287,4 +290,18 @@ private function showHelp(Arguments $args, ConsoleIo $io): void $this->displayHelp($this->getOptionParser(), $args, $io); } } + + /** + * Abort command if not in debug mode. + * + * @param \Cake\Console\ConsoleIo $io Console IO + * @return void + */ + protected function abortIfNotInDebugMode(ConsoleIo $io): void + { + if (Configure::read('debug') !== true) { + $io->error('This command is available in debug mode only.'); + $this->abort(); + } + } } diff --git a/src/Command/SendTestEmailCommand.php b/src/Command/SendTestEmailCommand.php index e644289cc4..5b1b032622 100644 --- a/src/Command/SendTestEmailCommand.php +++ b/src/Command/SendTestEmailCommand.php @@ -98,11 +98,12 @@ public function execute(Arguments $args, ConsoleIo $io): ?int * The Email from parameter in the config can take either a string or an array. The purpose * of this function is to provided a standardized way to display the from field. * - * @return array|string + * @return string */ - protected static function getEmailFromAsString() + protected static function getEmailFromAsString(): string { $config = Mailer::getConfig('default'); + /** @var array|string $from */ $from = $config['from'] ?? ''; if (is_array($from)) { $emailFrom = key($from); @@ -148,7 +149,7 @@ protected function displayConfiguration(Arguments $args, ConsoleIo $io): void protected function getRecipient(Arguments $args): string { $recipient = $args->getOption('recipient'); - if (empty($recipient)) { + if (!is_string($recipient) || empty($recipient)) { $recipient = 'doesnotexist@passboltdummydomain.com'; } diff --git a/src/Console/Installer.php b/src/Console/Installer.php index 2ceb29e0fa..7d7fafdd1f 100644 --- a/src/Console/Installer.php +++ b/src/Console/Installer.php @@ -67,6 +67,7 @@ public static function postInstall(Event $event) static::setSecuritySalt($rootDir, $io); if (class_exists('\Cake\Codeception\Console\Installer')) { + /** @psalm-suppress UndefinedClass this code is not executed if the class doesn't exist */ \Cake\Codeception\Console\Installer::customizeCodeceptionBinary($event); } } diff --git a/src/Controller/AppController.php b/src/Controller/AppController.php index 328981352b..55067a201c 100644 --- a/src/Controller/AppController.php +++ b/src/Controller/AppController.php @@ -33,7 +33,6 @@ * * @property \App\Controller\Component\UserComponent $User * @property \App\Controller\Component\QueryStringComponent $QueryString - * @property \Cake\Controller\Component\AuthComponent $Auth * @property \Authentication\Controller\Component\AuthenticationComponent $Authentication * @link http://book.cakephp.org/3.0/en/controllers.html#the-app-controller */ @@ -54,24 +53,7 @@ public function initialize(): void ]); $this->loadComponent('User'); $this->loadComponent('QueryString'); - - /* - * Authentication Component - */ - $loginUrl = Router::url([ - 'prefix' => 'Auth', - 'plugin' => null, - 'controller' => 'AuthLogin', - 'action' => 'loginGet', - '_method' => 'GET', - '_ext' => $this->getRequest()->is('json') ? 'json' : null, - ]); - - $this->loadComponent('Authentication.Authentication', [ - 'unauthenticatedRedirect' => $loginUrl, - 'logoutRedirect' => $loginUrl, - 'queryParam' => 'redirect', - ]); + $this->loadAuthenticationComponent(); // Init user action. UserAction::initFromRequest($this->User->getAccessControl(), $this->request); @@ -99,18 +81,18 @@ public function beforeFilter(EventInterface $event) * Success renders set the variables used to render the json view * All passbolt response contains an header (metadata like status) an a body (data) * - * @param string $message message in the header section - * @param array $body data for the body section + * @param string|null $message message in the header section + * @param mixed $body data for the body section * @return void */ - protected function success($message = null, $body = null) + protected function success(?string $message = null, $body = null): void { $header = [ 'id' => UserAction::getInstance()->getUserActionId(), 'status' => 'success', 'servertime' => time(), 'action' => UserAction::getInstance()->getActionId(), - 'message' => $message, + 'message' => $message ?? 'The operation was successful.', 'url' => Router::url(), 'code' => 200, ]; @@ -123,12 +105,12 @@ protected function success($message = null, $body = null) /** * Render an error response * - * @param string $message optional message - * @param mixed $body optional json reponse body - * @param int $errorCode optional http error code + * @param string|null $message optional message + * @param mixed $body optional json reponse body + * @param int|null $errorCode optional http error code * @return void */ - protected function error($message = null, $body = null, $errorCode = 200) + protected function error(?string $message = null, $body = null, ?int $errorCode = 200): void { if ($errorCode !== 200) { $this->response = $this->response->withStatus($errorCode); @@ -139,7 +121,7 @@ protected function error($message = null, $body = null, $errorCode = 200) 'status' => 'error', 'servertime' => time(), 'action' => UserAction::getInstance()->getActionId(), - 'message' => $message, + 'message' => $message ?? 'The operation failed.', 'url' => Router::url(), 'code' => $errorCode, ]; @@ -185,7 +167,7 @@ public function getApiVersion() { $apiVersion = $this->request->getQuery('api-version'); // Default to v2 in v3 - if (!isset($apiVersion)) { + if (!isset($apiVersion) || !is_string($apiVersion)) { return 'v2'; } @@ -200,4 +182,26 @@ public function getApiVersion() // Return what is given return $apiVersion; } + + /** + * Loads the authentication component and sets up the + * Authentication logout redirection. + * + * @return void + * @throws \Exception + */ + private function loadAuthenticationComponent(): void + { + $loginUrl = Router::url([ + 'prefix' => 'Auth', + 'plugin' => null, + 'controller' => 'AuthLogin', + 'action' => 'loginGet', + '_method' => 'GET', + ]); + + $this->loadComponent('Authentication.Authentication', [ + 'logoutRedirect' => $loginUrl, + ]); + } } diff --git a/src/Controller/Auth/AuthLoginController.php b/src/Controller/Auth/AuthLoginController.php index 5e198c9a75..8d68403e6b 100644 --- a/src/Controller/Auth/AuthLoginController.php +++ b/src/Controller/Auth/AuthLoginController.php @@ -24,6 +24,7 @@ use Cake\Core\Configure; use Cake\Http\Exception\BadRequestException; use Cake\Http\Exception\InternalErrorException; +use Cake\Http\Exception\NotFoundException; class AuthLoginController extends AppController { @@ -48,6 +49,10 @@ public function beforeFilter(\Cake\Event\EventInterface $event) */ public function loginGet() { + if ($this->request->is('json')) { + throw new NotFoundException(__('Page not found.')); + } + // Do not allow logged in user to login again if ($this->User->role() !== Role::GUEST) { $this->redirect('/'); // user is already logged in @@ -76,7 +81,8 @@ public function loginPost() // They are translated into actual http headers as part of GpgAuthHeadersMiddleware::process $result = $this->Authentication->getResult(); if ($result->isValid()) { - $user = $result->getData(); + $data = $result->getData(); + $user = $data['user']; $uac = new UserAccessControl($user['role']['name'], $user['id']); UserAction::getInstance()->setUserAccessControl($uac); $this->success(__('You are successfully logged in.'), $user); diff --git a/src/Controller/Auth/AuthLogoutController.php b/src/Controller/Auth/AuthLogoutController.php index 63467febae..8caf0788f3 100644 --- a/src/Controller/Auth/AuthLogoutController.php +++ b/src/Controller/Auth/AuthLogoutController.php @@ -17,6 +17,7 @@ namespace App\Controller\Auth; use App\Controller\AppController; +use Cake\Http\Response; class AuthLogoutController extends AppController { @@ -25,17 +26,17 @@ class AuthLogoutController extends AppController */ public function beforeFilter(\Cake\Event\EventInterface $event) { - $this->Authentication->allowUnauthenticated(['logoutGet']); + $this->Authentication->allowUnauthenticated(['logout']); return parent::beforeFilter($event); } /** - * User Index action + * User logout action * * @return \Cake\Http\Response|null */ - public function logoutGet() + public function logout(): ?Response { return $this->redirect($this->Authentication->logout()); } diff --git a/src/Controller/Component/ApiPaginationComponent.php b/src/Controller/Component/ApiPaginationComponent.php index 0632026a0f..95d958340b 100644 --- a/src/Controller/Component/ApiPaginationComponent.php +++ b/src/Controller/Component/ApiPaginationComponent.php @@ -174,7 +174,7 @@ private function parseQueryPagination(ServerRequest $request): array if (is_string($sort)) { return [$sort => $direction]; - } else { + } elseif (is_array($sort)) { foreach ($sort as &$v) { if ($v === '') { $v = 'asc'; @@ -183,6 +183,8 @@ private function parseQueryPagination(ServerRequest $request): array return $sort; } + + return []; } /** diff --git a/src/Controller/Component/QueryStringComponent.php b/src/Controller/Component/QueryStringComponent.php index a2eec35250..47444527c9 100644 --- a/src/Controller/Component/QueryStringComponent.php +++ b/src/Controller/Component/QueryStringComponent.php @@ -196,7 +196,7 @@ public static function extractQueryArrayItems(array $query): array } if (is_array($items)) { foreach ($items as $subKey => $subItems) { - if (is_string($subKey) && substr($subKey, -1) === 's' && is_scalar($query[$key][$subKey])) { + if (is_string($subKey) && substr($subKey, -1) === 's' && is_string($query[$key][$subKey])) { $query[$key][$subKey] = explode(',', $query[$key][$subKey]); } } @@ -328,6 +328,23 @@ public static function validateFilters(?array $filters = null, array $filterVali return true; } + /** + * Check if the filter is a valid string + * + * @param mixed $value to check + * @param string $filtername for error message display + * @throw Exception if the filter is not valid + * @return bool true if the filter is valid + */ + public static function validateFilterString($value, string $filtername) + { + if (empty($value) || !is_string($value)) { + throw new Exception(__('"{0}" is not a valid value for filter {1}.', $value, $filtername)); + } + + return true; + } + /** * Check if the filter is a valid boolean * @@ -442,7 +459,7 @@ public static function validateFilterGroups(array $values, string $filterName): if (!is_int($i)) { throw new Exception(__('"{0}" is not a valid group filter.', $i, $filterName)); } - if (!is_scalar($groupId) || empty($groupId)) { + if (!is_string($groupId) || empty($groupId)) { throw new Exception(__('"{0}" is not a valid group filter.', $i)); } self::validateFilterGroup($groupId, $filterName); @@ -599,16 +616,16 @@ public static function normalizeBoolean($str) if (is_bool($str)) { return $str; } - if (!is_scalar($str)) { + if (!is_string($str)) { return false; } if ((strtolower($str) === 'true' || $str === '1')) { return true; } elseif ((strtolower($str) === 'false' || $str === '0')) { return false; - } else { - return $str; } + + return $str; } /** @@ -679,11 +696,11 @@ public static function isTimestamp($timestamp): bool */ public static function isOrder($orderName): bool { - if (!is_scalar($orderName)) { + if (!is_string($orderName)) { return false; } $orderRegex = '/^[a-zA-Z]+(\.){1}([a-z_]+){1}(( ){1}(ASC|DESC){1}){0,1}$/'; - return (bool)(preg_match($orderRegex, $orderName) === 1); + return preg_match($orderRegex, $orderName) === 1; } } diff --git a/src/Controller/Component/UserComponent.php b/src/Controller/Component/UserComponent.php index b514f5f651..a743a52959 100644 --- a/src/Controller/Component/UserComponent.php +++ b/src/Controller/Component/UserComponent.php @@ -36,7 +36,7 @@ class UserComponent extends Component /** * User agent cache to avoid parsing multiple times per request * - * @var null + * @var array|null */ protected $_userAgent = null; @@ -63,9 +63,9 @@ public function username(): ?string /** * Return the current user role or GUEST if the user is not identified * - * @return string + * @return string|null */ - public function role(): string + public function role(): ?string { return $this->getAuthenticatedUserProperty('role.name', Role::GUEST); } @@ -99,7 +99,14 @@ protected function getAuthenticatedUserProperty(string $property, ?string $defau { try { // Get the user delivered by the authentication result. - $user = $this->Authentication->getResult()->getData() ?? []; + $data = $this->Authentication->getResult()->getData() ?? null; + if (!isset($data)) { + $user = []; + } elseif (!isset($data['user'])) { + $user = $data; + } else { + $user = $data['user']; + } } catch (Exception $e) { $user = []; } finally { @@ -118,6 +125,7 @@ public function agent() if (!isset($this->_userAgent)) { // Parse the user agent string. try { + /** @var string|null $agent */ $agent = env('HTTP_USER_AGENT'); if ($agent === null) { throw new Exception(__('undefined user agent')); @@ -139,23 +147,20 @@ public function agent() /** * Get the user theme * - * @return string + * @return string|null */ public function theme() { - $defaultTheme = 'default'; if (Configure::read('passbolt.plugins.accountSettings')) { /** @var \Passbolt\AccountSettings\Model\Table\AccountSettingsTable $AccountSettings */ $AccountSettings = TableRegistry::getTableLocator()->get('Passbolt/AccountSettings.AccountSettings'); $theme = $AccountSettings->getTheme($this->id()); - if (!$theme) { - $theme = $defaultTheme; + if ($theme) { + return $theme; } - - return $theme; } - return $defaultTheme; + return null; } /** diff --git a/src/Controller/Events/ControllerFindIndexOptionsBeforeMarshal.php b/src/Controller/Events/ControllerFindIndexOptionsBeforeMarshal.php index 0dc8d85676..bd1f3cfe81 100644 --- a/src/Controller/Events/ControllerFindIndexOptionsBeforeMarshal.php +++ b/src/Controller/Events/ControllerFindIndexOptionsBeforeMarshal.php @@ -37,13 +37,13 @@ class ControllerFindIndexOptionsBeforeMarshal extends Event /** * @param string $name Name - * @param null $subject Subject must be an instance of Table - * @param null $data Data + * @param \Cake\Controller\Controller $subject Subject must be an instance of Table + * @param array $data Data */ - final public function __construct($name, $subject = null, $data = null) + final public function __construct($name, Controller $subject, $data = null) { $this->setController($subject); - $this->setOptions($data['options'] ?? null); + $this->setOptions($data['options']); parent::__construct($name, $subject, $data); } diff --git a/src/Controller/Resources/ResourcesAddController.php b/src/Controller/Resources/ResourcesAddController.php index 0ead3753f8..0a70b71ff7 100644 --- a/src/Controller/Resources/ResourcesAddController.php +++ b/src/Controller/Resources/ResourcesAddController.php @@ -18,41 +18,47 @@ namespace App\Controller\Resources; use App\Controller\AppController; -use App\Error\Exception\ValidationException; -use App\Model\Entity\Permission; -use App\Model\Entity\Resource; -use App\Model\Table\ResourceTypesTable; -use Cake\Core\Configure; -use Cake\Event\Event; +use App\Service\Resources\ResourcesAddService; +use Cake\Event\EventInterface; /** * @property \App\Model\Table\ResourcesTable $Resources + * @property \App\Model\Table\UsersTable $Users */ class ResourcesAddController extends AppController { - public const ADD_SUCCESS_EVENT_NAME = 'ResourcesAddController.addPost.success'; + /** + * @var \App\Service\Resources\ResourcesAddService + */ + protected $resourcesAddService; + + /** + * @inheritDoc + */ + public function initialize(): void + { + parent::initialize(); + $this->loadModel('Resources'); + + $this->resourcesAddService = new ResourcesAddService(); + + $this->createAfterSaveEvent(); + } /** * Resource Add action * * @return void * @throws \Exception + * @throws \App\Error\Exception\ValidationException if the resource is not valid. + * @throws \Cake\Http\Exception\ServiceUnavailableException if parallel requests lead to a table lock albeit multiple attempts. */ public function add() { - $this->loadModel('Resources'); - - $data = $this->request->getData(); - $resource = $this->_buildAndValidateEntity($data); - - $result = $this->Resources->getConnection()->transactional(function () use ($resource, $data) { - $result = $this->Resources->save($resource, ['atomic' => false]); - $this->_handleValidationError($resource); - $this->afterCreate($resource, $data); - $this->_handleValidationError($resource); - - return $result; - }); + $resource = $this->resourcesAddService->add( + $this->User->id(), + $this->getRequest()->getData() + ); // Retrieve the saved resource. $options = [ @@ -61,106 +67,28 @@ public function add() 'secret' => true, 'permission' => true, ], ]; - - $resource = $this->Resources->findView($this->User->id(), $result->id, $options)->first(); + $resource = $this->Resources->findView($this->User->id(), $resource->id, $options)->first(); $this->success(__('The resource has been added successfully.'), $resource); } /** - * Build the resource entity from user input - * - * @param array $data Array of data - * @return Resource - */ - protected function _buildAndValidateEntity(array $data) - { - // Enforce data. - $data['created_by'] = $this->User->id(); - $data['modified_by'] = $this->User->id(); - $data['permissions'] = [[ - 'aro' => 'User', - 'aro_foreign_key' => $this->User->id(), - 'aco' => 'Resource', - 'type' => Permission::OWNER, - ]]; - - // If no secrets given, the model will throw a validation error, no need to take care of it here. - if (isset($data['secrets'])) { - $data['secrets'][0]['user_id'] = $this->User->id(); - } - - if (!isset($data['resource_type_id']) || !Configure::read('passbolt.plugins.resourceTypes.enabled')) { - $data['resource_type_id'] = ResourceTypesTable::getDefaultTypeId(); - } - - // Build entity and perform basic check - /** @var Resource $resource */ - $resource = $this->Resources->newEntity($data, [ - 'accessibleFields' => [ - 'name' => true, - 'username' => true, - 'uri' => true, - 'description' => true, - 'created_by' => true, - 'modified_by' => true, - 'secrets' => true, - 'permissions' => true, - 'resource_type_id' => true, - ], - 'associated' => [ - 'Permissions' => [ - 'validate' => 'saveResource', - 'accessibleFields' => [ - 'aco' => true, - 'aro' => true, - 'aro_foreign_key' => true, - 'type' => true, - ], - ], - 'Secrets' => [ - 'validate' => 'saveResource', - 'accessibleFields' => [ - 'user_id' => true, - 'data' => true, - ], - ], - ], - ]); - - // Handle validation errors if any at this stage. - $this->_handleValidationError($resource); - - return $resource; - } - - /** - * Manage validation errors. - * - * @param Resource $resource resource - * @throws \App\Error\Exception\ValidationException if the resource validation failed - * @return void - */ - protected function _handleValidationError(Resource $resource) - { - $errors = $resource->getErrors(); - if (!empty($errors)) { - throw new ValidationException(__('Could not validate resource data.'), $resource, $this->Resources); - } - } - - /** - * Trigger the after resource create event. + * Create the after save events on the Resources table. * - * @param Resource $resource The created resource - * @param array $data The request data. * @return void */ - protected function afterCreate(Resource $resource, array $data = []) + protected function createAfterSaveEvent(): void { - $uac = $this->User->getAccessControl(); - $eventData = ['resource' => $resource, 'accessControl' => $uac, 'data' => $data]; - $event = new Event(static::ADD_SUCCESS_EVENT_NAME, $this, $eventData); - $this->getEventManager()->dispatch($event); + $this->Resources->getEventManager()->on( + 'Model.afterSave', + ['priority' => 1], + function (EventInterface $event, $resource) { + $this->resourcesAddService->afterSave( + $resource, + $this->User->getAccessControl(), + $this->getRequest()->getData() + ); + } + ); } } diff --git a/src/Controller/Resources/ResourcesDeleteController.php b/src/Controller/Resources/ResourcesDeleteController.php index 0cd2799299..7da46a4854 100644 --- a/src/Controller/Resources/ResourcesDeleteController.php +++ b/src/Controller/Resources/ResourcesDeleteController.php @@ -71,8 +71,13 @@ public function delete(string $id): void // Get the list of users who have access to the resource // useful to do now to notify users later, since it wont be possible to after delete + // The logged in user will not be notified. $options = ['contain' => ['role'], 'filter' => ['has-access' => [$resource->id]]]; - $users = $this->Users->findIndex(Role::USER, $options)->all(); + $users = $this->Users + ->findIndex(Role::USER, $options) + ->find('locale') + ->where(['Users.id !=' => $this->User->id()]) + ->all(); // Update the entity to delete=1, clear uri/desc/username and drop associated permissions if (!$this->Resources->softDelete($this->User->id(), $resource)) { @@ -116,7 +121,7 @@ protected function _handleDeleteError(Resource $resource): void * Send email notification * * @param Resource $resource Resource - * @param \Cake\Datasource\ResultSetInterface $users list of User entity who had access to the resource + * @param \Cake\Datasource\ResultSetInterface $users Users who had access to the resource, deleter excluded * @return void */ protected function _notifyUser(Resource $resource, ResultSetInterface $users): void diff --git a/src/Controller/Settings/SettingsIndexController.php b/src/Controller/Settings/SettingsIndexController.php index f28d975eaf..28b5948233 100644 --- a/src/Controller/Settings/SettingsIndexController.php +++ b/src/Controller/Settings/SettingsIndexController.php @@ -115,7 +115,10 @@ protected function _getSettings(string $role): array 'debug' => Configure::read('debug') ? 1 : 0, 'server_timezone' => date_default_timezone_get(), // session timeout info in minutes - 'session_timeout' => Configure::read('Session.timeout', ini_get('session.gc_maxlifetime') / 60), + 'session_timeout' => Configure::read( + 'Session.timeout', + (int)ini_get('session.gc_maxlifetime') / 60 + ), 'image_storage' => [ 'public_path' => Configure::read('ImageStorage.publicPath'), ], @@ -177,7 +180,7 @@ protected function _getWhiteListedPluginConfig(array $whiteList) $pluginsConfig = []; // Add white listed plugin options. foreach ($whiteList as $path) { - if (!empty(Configure::read('passbolt.plugins.' . $path))) { + if (Configure::check('passbolt.plugins.' . $path)) { $pluginsConfig = Hash::insert( $pluginsConfig, $path, diff --git a/src/Controller/Setup/RecoverCompleteController.php b/src/Controller/Setup/RecoverCompleteController.php index ac5038cdc9..53c68f713e 100644 --- a/src/Controller/Setup/RecoverCompleteController.php +++ b/src/Controller/Setup/RecoverCompleteController.php @@ -16,15 +16,24 @@ */ namespace App\Controller\Setup; -use App\Model\Entity\AuthenticationToken; -use Cake\Http\Exception\BadRequestException; -use Cake\Http\Exception\InternalErrorException; -use Cake\Validation\Validation; +use App\Controller\AppController; +use App\Service\Setup\RecoverCompleteServiceInterface; +use Cake\Event\EventInterface; -class RecoverCompleteController extends SetupCompleteController +class RecoverCompleteController extends AppController { public const COMPLETE_SUCCESS_EVENT_NAME = 'RecoverCompleteController.complete.success'; + /** + * @inheritDoc + */ + public function beforeFilter(EventInterface $event) + { + $this->Authentication->allowUnauthenticated(['complete']); + + return parent::beforeFilter($event); + } + /** * Recovery completion * Check if the public key matches the currently stored fingerprint @@ -38,55 +47,14 @@ class RecoverCompleteController extends SetupCompleteController * @throws \Cake\Http\Exception\BadRequestException if the OpenPGP key is not provided or not a valid OpenPGP key * @throws \Cake\Http\Exception\BadRequestException if the OpenPGP key does not belong to the user * @throws \Cake\Http\Exception\InternalErrorException if something went wrong when updating the data + * @param \App\Service\Setup\RecoverCompleteServiceInterface $recoverCompleteService Setup complete service * @param string $userId uuid of the user * @return void */ - public function complete(string $userId) + public function complete(RecoverCompleteServiceInterface $recoverCompleteService, string $userId) { - // Check request sanity - $user = $this->_getAndAssertUser($userId); - $token = $this->_getAndAssertToken($userId, AuthenticationToken::TYPE_RECOVER); - $gpgkey = $this->_getAndAssertGpgkey($userId); - - // Check that the "new" gpg key match the old one - $userKey = $this->Gpgkeys->getByFingerprintAndUserId($gpgkey->fingerprint, $userId); - if (empty($userKey)) { - throw new BadRequestException(__('The key provided does not belong to given user.')); - } - - // Deactivate the authentication token - $token->active = false; - if (!$this->AuthenticationTokens->save($token, ['checkRules' => false])) { - throw new InternalErrorException('Could not update the authentication token data.'); - } - - $this->dispatchEvent(static::COMPLETE_SUCCESS_EVENT_NAME, [ - 'user' => $user, - 'data' => $this->getRequest()->getData(), - ]); + $recoverCompleteService->complete($userId); $this->success(__('The recovery was completed successfully.')); } - - /** - * Return the user for matching the requesting id - * - * @param string $userId the user uuid - * @throws \Cake\Http\Exception\BadRequestException if the user id is not a valid uuid - * @throws \Cake\Http\Exception\BadRequestException if the user was deleted or has not completed the setup - * @return \App\Model\Entity\User - */ - protected function _getAndAssertUser(string $userId) - { - if (!Validation::uuid($userId)) { - throw new BadRequestException(__('The user identifier should be a valid UUID.')); - } - $user = $this->Users->findSetupRecover($userId); - if (empty($user)) { - $msg = __('The user does not exist, has not completed the setup or was deleted.'); - throw new BadRequestException($msg); - } - - return $user; - } } diff --git a/src/Controller/Setup/RecoverStartController.php b/src/Controller/Setup/RecoverStartController.php index fb34517c26..16cc346d95 100644 --- a/src/Controller/Setup/RecoverStartController.php +++ b/src/Controller/Setup/RecoverStartController.php @@ -17,11 +17,9 @@ namespace App\Controller\Setup; use App\Controller\AppController; -use App\Error\Exception\CustomValidationException; -use App\Model\Entity\AuthenticationToken; -use App\Model\Entity\User; +use App\Service\Setup\RecoverStartServiceInterface; use Cake\Core\Configure; -use Cake\Http\Exception\BadRequestException; +use Cake\Event\EventInterface; /** * @property \App\Model\Table\AuthenticationTokensTable $AuthenticationTokens @@ -29,16 +27,12 @@ */ class RecoverStartController extends AppController { - use SetupControllerTrait; - /** * @inheritDoc */ - public function beforeFilter(\Cake\Event\EventInterface $event) + public function beforeFilter(EventInterface $event) { $this->Authentication->allowUnauthenticated(['start']); - $this->loadModel('AuthenticationTokens'); - $this->loadModel('Users'); return parent::beforeFilter($event); } @@ -46,106 +40,21 @@ public function beforeFilter(\Cake\Event\EventInterface $event) /** * Recover start * + * @param \App\Service\Setup\RecoverStartServiceInterface $infoService info service * @param string $userId uuid of the user * @param string $token uuid of the token * @return void * @throws \Cake\Http\Exception\BadRequestException if the token is missing or not a uuid * @throws \Cake\Http\Exception\BadRequestException if the user id is missing or not a uuid */ - public function start(string $userId, string $token): void + public function start(RecoverStartServiceInterface $infoService, string $userId, string $token): void { if ($this->request->is('json')) { - $this->retrieveRecoverInfo($userId, $token); + $data = $infoService->getInfo($userId, $token); + $this->success(__('The operation was successful.'), $data); } else { - $this->renderRecoverApplication(); - } - } - - /** - * Retrieve the recover info - * - * @param string $userId uuid of the user - * @param string $token uuid of the token - * @return void - */ - private function retrieveRecoverInfo(string $userId, string $token): void - { - $this->_assertRequestSanity($userId, $token); - $user = $this->findUser($userId); - $token = $this->findToken($user, $token); - $this->assertTokenExpiry($user, $token); - $this->success(__('The operation was successful.'), ['user' => $user]); - } - - /** - * Find the user requesting the recover - * - * @param string $userId uuid of the user - * @return \App\Model\Entity\User - * @throw BadRequestException if the user cannot be found, is deleted or is inactive. - */ - private function findUser(string $userId): User - { - $user = $this->Users->findSetupRecover($userId); - if (empty($user)) { - throw new BadRequestException(__('The user does not exist or is not active.')); + $this->set('title', Configure::read('passbolt.meta.description')); + $infoService->setTemplate($this->viewBuilder()); } - - return $user; - } - - /** - * Find the recover token - * - * @param \App\Model\Entity\User $user user attempting to recover - * @param string $token uuid of the token - * @return \App\Model\Entity\AuthenticationToken - * @throw BadRequestException if the token is not valid - */ - private function findToken(User $user, string $token): AuthenticationToken - { - $finderOptions = ['userId' => $user->id, 'token' => $token]; - /** @var \App\Model\Entity\AuthenticationToken $token */ - $token = $this->AuthenticationTokens->find('activeUserRecoveryToken', $finderOptions)->first(); - if (empty($token)) { - throw new BadRequestException(__('The authentication token is not valid.')); - } - - return $token; - } - - /** - * Assert the token expiry. If the token is expired, regenerate a new one and throw an notify the client with an error. - * - * @param \App\Model\Entity\User $user user attempting to recover - * @param \App\Model\Entity\AuthenticationToken $token the recovery token - * @return void - * @throw CustomValidationException if the token is expired - */ - private function assertTokenExpiry(User $user, AuthenticationToken $token): void - { - $isExpired = $this->AuthenticationTokens->isExpired($token); - if ($isExpired) { - $error = [ - 'token' => [ - 'expired' => 'The token is expired.', - ], - ]; - throw new CustomValidationException(__('The token is expired.'), $error); - } - } - - /** - * Render the recover application - * - * @return void - */ - private function renderRecoverApplication(): void - { - $this->set('title', Configure::read('passbolt.meta.description')); - $this->viewBuilder() - ->setTemplatePath('/Setup') - ->setLayout('default') - ->setTemplate('recoverStart'); } } diff --git a/src/Controller/Setup/SetupCompleteController.php b/src/Controller/Setup/SetupCompleteController.php index 727b0cb7c9..15e03a0d2c 100644 --- a/src/Controller/Setup/SetupCompleteController.php +++ b/src/Controller/Setup/SetupCompleteController.php @@ -17,18 +17,9 @@ namespace App\Controller\Setup; use App\Controller\AppController; -use App\Error\Exception\CustomValidationException; -use App\Error\Exception\ValidationException; -use App\Model\Entity\AuthenticationToken; -use Cake\Http\Exception\BadRequestException; -use Cake\Http\Exception\InternalErrorException; -use Cake\Validation\Validation; +use App\Service\Setup\SetupCompleteServiceInterface; +use Cake\Event\EventInterface; -/** - * @property \App\Model\Table\AuthenticationTokensTable $AuthenticationTokens - * @property \App\Model\Table\GpgkeysTable $Gpgkeys - * @property \App\Model\Table\UsersTable $Users - */ class SetupCompleteController extends AppController { public const COMPLETE_SUCCESS_EVENT_NAME = 'SetupCompleteController.complete.success'; @@ -36,14 +27,10 @@ class SetupCompleteController extends AppController /** * @inheritDoc */ - public function beforeFilter(\Cake\Event\EventInterface $event) + public function beforeFilter(EventInterface $event) { $this->Authentication->allowUnauthenticated(['complete']); - $this->loadModel('Gpgkeys'); - $this->loadModel('AuthenticationTokens'); - $this->loadModel('Users'); - return parent::beforeFilter($event); } @@ -58,124 +45,14 @@ public function beforeFilter(\Cake\Event\EventInterface $event) * @throws \Cake\Http\Exception\BadRequestException if the authentication token is expired or invalid * @throws \Cake\Http\Exception\BadRequestException if the OpenPGP key is not provided or not a valid OpenPGP key * @throws \Cake\Http\Exception\InternalErrorException if something went wrong when updating the data + * @param \App\Service\Setup\SetupCompleteServiceInterface $setupCompleteService Setup complete service * @param string $userId uuid of the user * @return void */ - public function complete(string $userId) + public function complete(SetupCompleteServiceInterface $setupCompleteService, string $userId): void { - // Check request sanity - $user = $this->_getAndAssertUser($userId); - $token = $this->_getAndAssertToken($userId, AuthenticationToken::TYPE_REGISTER); - $gpgkey = $this->_getAndAssertGpgkey($userId); - - // Check business rules before saving - $this->Gpgkeys->checkRules($gpgkey); - if ($gpgkey->getErrors()) { - throw new ValidationException(__('The OpenPGP key data is not valid.'), $gpgkey, $this->Gpgkeys); - } - - // Deactivate the authentication token - $token->active = false; - if (!$this->AuthenticationTokens->save($token, ['checkRules' => false])) { - throw new InternalErrorException('Could not update the authentication token data.'); - } - - // Save user GPG key, rules were already checked - if (!$this->Gpgkeys->save($gpgkey, ['checkRules' => false])) { - throw new InternalErrorException('Could not save the OpenPGP key data.'); - } - - // Update the user - $user->active = true; - if (!$this->Users->save($user, ['checkRules' => false])) { - throw new InternalErrorException('Could not save the user data.'); - } - - $this->dispatchEvent(static::COMPLETE_SUCCESS_EVENT_NAME, [ - 'user' => $user, - 'data' => $this->getRequest()->getData(), - ]); + $setupCompleteService->complete($userId); $this->success(__('The setup was completed successfully.')); } - - /** - * Return the authentication from data if any - * - * @param string $userId the user uuid the token belongs to - * @param string $tokenType AuthenticationToken::TYPE_* - * @throws \Cake\Http\Exception\BadRequestException if no authentication token was provided - * @throws \Cake\Http\Exception\BadRequestException if the authentication token is not a uuid - * @throws \Cake\Http\Exception\BadRequestException if the authentication token is expired or invalid - * @return \App\Model\Entity\AuthenticationToken - */ - protected function _getAndAssertToken(string $userId, string $tokenType) - { - $data = $this->request->getData(); - if (!isset($data['authenticationtoken']) || !isset($data['authenticationtoken']['token'])) { - throw new BadRequestException(__('An authentication token should be provided.')); - } - $tokenId = $data['authenticationtoken']['token']; - if (!Validation::uuid($tokenId)) { - throw new BadRequestException(__('The authentication token should be a valid UUID.')); - } - if (!$this->AuthenticationTokens->isValid($tokenId, $userId, $tokenType)) { - throw new BadRequestException(__('The authentication token is not valid or has expired.')); - } - - /** @var \App\Model\Entity\AuthenticationToken $token */ - $token = $this->AuthenticationTokens->getByToken($tokenId); - - return $token; - } - - /** - * Return the user for matching the requesting id - * - * @param string $userId the user uuid - * @throws \Cake\Http\Exception\BadRequestException if the user id is not a valid uuid - * @throws \Cake\Http\Exception\BadRequestException if the user was deleted, is already active or does not exist - * @return \App\Model\Entity\User user entity - */ - protected function _getAndAssertUser(string $userId) - { - if (!Validation::uuid($userId)) { - throw new BadRequestException(__('The user identifier should be a valid UUID.')); - } - $user = $this->Users->findSetup($userId); - if (empty($user)) { - $msg = __('The user does not exist, is already active or has been deleted.'); - throw new BadRequestException($msg); - } - - return $user; - } - - /** - * Return the gpg key entity for matching the requesting id - * - * @param string $userId the user uuid - * @throws \Cake\Http\Exception\BadRequestException if the gpg key is not provided or not a valid OpenPGP key - * @return \App\Model\Entity\Gpgkey entity - */ - protected function _getAndAssertGpgkey(string $userId) - { - $data = $this->request->getData(); - $armoredKey = $data['gpgkey']['armored_key']; - - if (empty($armoredKey)) { - throw new BadRequestException(__('An OpenPGP key must be provided.')); - } - - if (!$this->Gpgkeys->isParsableArmoredPublicKey($armoredKey)) { - throw new BadRequestException(__('A valid OpenPGP key must be provided.')); - } - try { - $gpgkey = $this->Gpgkeys->buildEntityFromArmoredKey($armoredKey, $userId); - } catch (CustomValidationException $e) { - throw new BadRequestException(__('A valid OpenPGP key must be provided.')); - } - - return $gpgkey; - } } diff --git a/src/Controller/Setup/SetupControllerTrait.php b/src/Controller/Setup/SetupControllerTrait.php deleted file mode 100644 index c31fb074f0..0000000000 --- a/src/Controller/Setup/SetupControllerTrait.php +++ /dev/null @@ -1,62 +0,0 @@ -UserAgents->browserName()); - if (!in_array($browserName, $supportedBrowsers)) { - return false; - } - - return true; - } -} diff --git a/src/Controller/Setup/SetupStartController.php b/src/Controller/Setup/SetupStartController.php index 0dd1c4435f..62f40f4f0e 100644 --- a/src/Controller/Setup/SetupStartController.php +++ b/src/Controller/Setup/SetupStartController.php @@ -17,11 +17,9 @@ namespace App\Controller\Setup; use App\Controller\AppController; -use App\Error\Exception\CustomValidationException; -use App\Model\Entity\AuthenticationToken; -use App\Model\Entity\User; +use App\Service\Setup\SetupStartServiceInterface; use Cake\Core\Configure; -use Cake\Http\Exception\BadRequestException; +use Cake\Event\EventInterface; /** * @property \App\Model\Table\AuthenticationTokensTable $AuthenticationTokens @@ -29,123 +27,34 @@ */ class SetupStartController extends AppController { - use SetupControllerTrait; - /** * @inheritDoc */ - public function beforeFilter(\Cake\Event\EventInterface $event) + public function beforeFilter(EventInterface $event) { $this->Authentication->allowUnauthenticated(['start']); - $this->loadModel('AuthenticationTokens'); - $this->loadModel('Users'); return parent::beforeFilter($event); } /** - * Recover start + * Setup start * + * @param \App\Service\Setup\SetupStartServiceInterface $infoService info service * @param string $userId uuid of the user * @param string $token uuid of the token * @return void * @throws \Cake\Http\Exception\BadRequestException if the token is missing or not a uuid * @throws \Cake\Http\Exception\BadRequestException if the user id is missing or not a uuid */ - public function start(string $userId, string $token): void + public function start(SetupStartServiceInterface $infoService, string $userId, string $token): void { if ($this->request->is('json')) { - $this->retrieveSetupInfo($userId, $token); + $data = $infoService->getInfo($userId, $token); + $this->success(__('The operation was successful.'), $data); } else { - $this->renderSetupApplication(); - } - } - - /** - * Retrieve the recover info - * - * @param string $userId uuid of the user - * @param string $token uuid of the token - * @return void - */ - private function retrieveSetupInfo(string $userId, string $token): void - { - $this->_assertRequestSanity($userId, $token); - $user = $this->findUser($userId); - $token = $this->findToken($user, $token); - $this->assertTokenExpiry($user, $token); - $this->success(__('The operation was successful.'), ['user' => $user]); - } - - /** - * Find the user requesting the setup - * - * @param string $userId uuid of the user - * @return \App\Model\Entity\User - * @throw BadRequestException if the user cannot be found, is deleted or is active. - */ - private function findUser(string $userId): User - { - $user = $this->Users->findSetup($userId); - if (empty($user)) { - throw new BadRequestException(__('The user does not exist or is active.')); + $this->set('title', Configure::read('passbolt.meta.description')); + $infoService->setTemplate($this->viewBuilder()); } - - return $user; - } - - /** - * Find the setup token - * - * @param \App\Model\Entity\User $user user attempting to setup - * @param string $token uuid of the token - * @return \App\Model\Entity\AuthenticationToken - * @throw BadRequestException if the token is not valid - */ - private function findToken(User $user, string $token): AuthenticationToken - { - $finderOptions = ['userId' => $user->id, 'token' => $token]; - /** @var \App\Model\Entity\AuthenticationToken $token */ - $token = $this->AuthenticationTokens->find('activeUserRegistrationToken', $finderOptions)->first(); - if (empty($token)) { - throw new BadRequestException(__('The authentication token is not valid.')); - } - - return $token; - } - - /** - * Assert the token expiry. If the token is expired, regenerate a new one and throw an notify the client with an error. - * - * @param \App\Model\Entity\User $user user attempting to recover - * @param \App\Model\Entity\AuthenticationToken $token the recovery token - * @return void - * @throw CustomValidationException if the token is expired - */ - private function assertTokenExpiry(User $user, AuthenticationToken $token): void - { - $isExpired = $this->AuthenticationTokens->isExpired($token); - if ($isExpired) { - $error = [ - 'token' => [ - 'expired' => 'The token is expired.', - ], - ]; - throw new CustomValidationException(__('The token is expired.'), $error); - } - } - - /** - * Render the setup application - * - * @return void - */ - private function renderSetupApplication(): void - { - $this->set('title', Configure::read('passbolt.meta.description')); - $this->viewBuilder() - ->setTemplatePath('/Setup') - ->setLayout('default') - ->setTemplate('start'); } } diff --git a/src/Controller/Share/ShareSearchController.php b/src/Controller/Share/ShareSearchController.php index 1fb6b0d7e0..fb268b9a5e 100644 --- a/src/Controller/Share/ShareSearchController.php +++ b/src/Controller/Share/ShareSearchController.php @@ -20,7 +20,6 @@ use App\Controller\AppController; use App\Model\Entity\Group; use App\Model\Entity\User; -use Cake\Collection\Collection; use Cake\Collection\CollectionInterface; use Cake\ORM\Query; @@ -84,10 +83,10 @@ private function _searchUsers(?array $options = []): Query /** * Format the result alphabetically. * - * @param \Cake\Collection\Collection $aros The collection of groups and users to sort. + * @param \Cake\Collection\CollectionInterface $aros The collection of groups and users to sort. * @return \Cake\Collection\CollectionInterface */ - private function _formatResult(Collection $aros): CollectionInterface + private function _formatResult(CollectionInterface $aros): CollectionInterface { $sortIterator = $aros->sortBy(function ($item) { if ($item instanceof Group) { diff --git a/src/Controller/Users/UsersRecoverController.php b/src/Controller/Users/UsersRecoverController.php index 0f6105fd4a..d2d7f107f2 100644 --- a/src/Controller/Users/UsersRecoverController.php +++ b/src/Controller/Users/UsersRecoverController.php @@ -17,14 +17,12 @@ namespace App\Controller\Users; use App\Controller\AppController; -use App\Model\Entity\AuthenticationToken; use App\Model\Entity\Role; -use App\Model\Table\UsersTable; +use App\Service\Users\UserRecoverServiceInterface; use Cake\Core\Configure; -use Cake\Event\Event; +use Cake\Event\EventInterface; use Cake\Http\Exception\BadRequestException; use Cake\Http\Exception\ForbiddenException; -use Cake\Http\Exception\NotFoundException; /** * @property \App\Model\Table\UsersTable $Users @@ -37,11 +35,9 @@ class UsersRecoverController extends AppController /** * @inheritDoc */ - public function beforeFilter(\Cake\Event\EventInterface $event) + public function beforeFilter(EventInterface $event) { $this->Authentication->allowUnauthenticated(['recoverGet', 'recoverPost']); - $this->loadModel('Users'); - $this->loadModel('AuthenticationTokens'); return parent::beforeFilter($event); } @@ -50,9 +46,10 @@ public function beforeFilter(\Cake\Event\EventInterface $event) * Recover user action GET * Display a recovery form * + * @param \App\Service\Users\UserRecoverServiceInterface $userRecoverService User recover service * @return void */ - public function recoverGet() + public function recoverGet(UserRecoverServiceInterface $userRecoverService) { // Do not allow logged in user to recover if ($this->User->role() !== Role::GUEST) { @@ -60,21 +57,19 @@ public function recoverGet() } $this->set('title', Configure::read('passbolt.meta.description')); - $this->viewBuilder() - ->setTemplatePath('Auth') - ->setLayout('default') - ->setTemplate('triage'); + $userRecoverService->setTemplate($this->viewBuilder()); $this->success(); } /** * Register user action POST * + * @param \App\Service\Users\UserRecoverServiceInterface $userRecoverService User recover service * @return void * @throws \Cake\Http\Exception\BadRequestException if the username is not valid * @throws \Cake\Http\Exception\BadRequestException if the username is not provided */ - public function recoverPost() + public function recoverPost(UserRecoverServiceInterface $userRecoverService) { if (!$this->request->is('json')) { throw new BadRequestException(__('This is not a valid Ajax/Json request.')); @@ -85,80 +80,7 @@ public function recoverPost() throw new ForbiddenException(__('Only guest are allowed to recover an account. Please logout first.')); } - $this->_assertValidation(); - $user = $this->_assertRules(); - $token = null; - $adminId = null; - if ($user->active) { - $token = $this->AuthenticationTokens->generate($user->id, AuthenticationToken::TYPE_RECOVER); - $event = static::RECOVER_SUCCESS_EVENT_NAME; - } else { - // The user has not completed the setup, restart setup - // Fixes https://github.com/passbolt/passbolt_api/issues/73 - $token = $this->AuthenticationTokens->generate($user->id, AuthenticationToken::TYPE_REGISTER); - $event = UsersTable::AFTER_REGISTER_SUCCESS_EVENT_NAME; - if ($this->User->role() === Role::ADMIN) { - $adminId = $this->User->id(); - } - } - - // Create an event to build email with token - $options = ['user' => $user, 'token' => $token]; - if (isset($adminId)) { - $options['adminId'] = $adminId; - } - $event = new Event($event, $this, $options); - $this->getEventManager()->dispatch($event); - - $this->success(__('Recovery process started, check your email.'), $user); - } - - /** - * Assert some username data is provided - * - * @return \App\Model\Entity\User user entity - * @throws \Cake\Http\Exception\BadRequestException if the username is not valid - * @throws \Cake\Http\Exception\BadRequestException if the username is not provided - */ - protected function _assertValidation() - { - $data = $this->request->getData(); - if (!isset($data['username']) || empty($data['username'])) { - throw new BadRequestException(__('Please provide a valid email address.')); - } - - $user = $this->Users->newEntity( - $data, - ['validate' => 'recover', 'accessibleFields' => ['username' => true]] - ); - if ($user->getErrors()) { - throw new BadRequestException(__('Please provide a valid email address.')); - } - - return $user; - } - - /** - * Assert the user can actually perform a recovery on their account - * - * @return mixed - * @throws \Cake\Http\Exception\BadRequestException if the user does not exist or has been deleted - */ - protected function _assertRules() - { - $data = $this->request->getData(); - $user = $this->Users->findRecover($data['username'])->first(); - - if (empty($user)) { - $msg = __('This user does not exist or has been deleted.') . ' '; - if (Configure::read('passbolt.registration.public')) { - $msg .= __('Please register and complete the setup first.'); - } else { - $msg .= __('Please contact your administrator.'); - } - throw new NotFoundException($msg); - } - - return $user; + $userRecoverService->recover($this->User->getAccessControl()); + $this->success(__('Recovery process started, check your email.')); } } diff --git a/src/Controller/Users/UsersRegisterController.php b/src/Controller/Users/UsersRegisterController.php index c4496fb218..09935d6f2b 100644 --- a/src/Controller/Users/UsersRegisterController.php +++ b/src/Controller/Users/UsersRegisterController.php @@ -18,7 +18,9 @@ use App\Controller\AppController; use App\Model\Entity\Role; +use App\Service\Users\UserRegisterServiceInterface; use Cake\Core\Configure; +use Cake\Event\EventInterface; use Cake\Http\Exception\BadRequestException; use Cake\Http\Exception\ForbiddenException; use Cake\Http\Exception\NotFoundException; @@ -33,7 +35,7 @@ class UsersRegisterController extends AppController /** * @inheritDoc */ - public function beforeFilter(\Cake\Event\EventInterface $event) + public function beforeFilter(EventInterface $event) { if (Configure::read('passbolt.registration.public') === true) { $this->Authentication->allowUnauthenticated(['registerGet', 'registerPost']); @@ -42,8 +44,6 @@ public function beforeFilter(\Cake\Event\EventInterface $event) throw new NotFoundException($msg); } - $this->loadModel('Users'); - return parent::beforeFilter($event); } @@ -52,9 +52,10 @@ public function beforeFilter(\Cake\Event\EventInterface $event) * Display a registration form * * @throws \Cake\Http\Exception\ForbiddenException if the current user is logged in + * @param \App\Service\Users\UserRegisterServiceInterface $userRegisterService Service * @return void */ - public function registerGet() + public function registerGet(UserRegisterServiceInterface $userRegisterService) { // Do not allow logged in user to register if ($this->User->role() !== Role::GUEST) { @@ -62,19 +63,17 @@ public function registerGet() } $this->set('title', Configure::read('passbolt.meta.description')); - $this->viewBuilder() - ->setTemplatePath('Auth') - ->setLayout('default') - ->setTemplate('triage'); + $userRegisterService->setTemplate($this->viewBuilder()); } /** * Register user action POST * * @throws \Cake\Http\Exception\ForbiddenException if the current user is logged in + * @param \App\Service\Users\UserRegisterServiceInterface $userRegisterService Service * @return void */ - public function registerPost() + public function registerPost(UserRegisterServiceInterface $userRegisterService) { if (!$this->request->is('json')) { throw new BadRequestException(__('This is not a valid Ajax/Json request.')); @@ -85,13 +84,7 @@ public function registerPost() throw new ForbiddenException(__('Only guest are allowed to register.')); } - $data = $this->request->getData(); - $user = $this->Users->register($data); - - $this->dispatchEvent(static::USERS_REGISTER_EVENT_NAME, [ - 'user' => $user, - 'data' => $this->getRequest()->getData(), - ]); + $user = $userRegisterService->register(); $this->success(__('The operation was successful.'), $user); } diff --git a/src/Error/Exception/AbstractExceptionWithEmailEvent.php b/src/Error/Exception/AbstractExceptionWithEmailEvent.php new file mode 100644 index 0000000000..ee0bd65c07 --- /dev/null +++ b/src/Error/Exception/AbstractExceptionWithEmailEvent.php @@ -0,0 +1,94 @@ +dispatchEmailEvent(); + } + + /** + * Dispatch the exception's event. + * The name of the event is the fully qualified exception class. + * Register these events in a EmailRedactorPool, e.g. in JwtAuthenticationAttackEmailRedactor + * + * @return void + */ + protected function dispatchEmailEvent(): void + { + EventManager::instance()->on('Controller.beforeRender', function (EventInterface $event) { + $this->controller = $event->getSubject(); + $event = new Event($this->getEventName(), $this); + $this->getController()->getEventManager()->dispatch($event); + }); + } + + /** + * The controller calling the exception. + * + * @return \App\Controller\AppController + */ + public function getController(): AppController + { + return $this->controller; + } + + /** + * @param \App\Controller\AppController $controller Controller of the request + * @return void + */ + public function setController(AppController $controller): void + { + $this->controller = $controller; + } +} diff --git a/src/Error/Exception/AdminsEmailNotificationExceptionTrait.php b/src/Error/Exception/AdminsEmailNotificationExceptionTrait.php new file mode 100644 index 0000000000..ad5fefbbac --- /dev/null +++ b/src/Error/Exception/AdminsEmailNotificationExceptionTrait.php @@ -0,0 +1,35 @@ +_errors; } @@ -84,7 +84,7 @@ public function getErrors() * * @return \Cake\ORM\Table|null */ - public function getTable() + public function getTable(): ?Table { return $this->_table; } diff --git a/src/Error/Exception/ExceptionWithErrorsDetailInterface.php b/src/Error/Exception/ExceptionWithErrorsDetailInterface.php index 8c5a20d923..83bbec612c 100644 --- a/src/Error/Exception/ExceptionWithErrorsDetailInterface.php +++ b/src/Error/Exception/ExceptionWithErrorsDetailInterface.php @@ -22,7 +22,7 @@ interface ExceptionWithErrorsDetailInterface /** * Get the validation errors * - * @return array + * @return array|null */ - public function getErrors(); + public function getErrors(): ?array; } diff --git a/src/Error/Exception/ExceptionWithTableDetailInterface.php b/src/Error/Exception/ExceptionWithTableDetailInterface.php index f18bb74dd3..202ed58e72 100644 --- a/src/Error/Exception/ExceptionWithTableDetailInterface.php +++ b/src/Error/Exception/ExceptionWithTableDetailInterface.php @@ -17,12 +17,14 @@ namespace App\Error\Exception; +use Cake\ORM\Table; + interface ExceptionWithTableDetailInterface { /** * Get the table associated to the exception * - * @return \Cake\ORM\Table + * @return \Cake\ORM\Table|null */ - public function getTable(); + public function getTable(): ?Table; } diff --git a/src/Error/Exception/UserEmailNotificationExceptionTrait.php b/src/Error/Exception/UserEmailNotificationExceptionTrait.php new file mode 100644 index 0000000000..c664a8b3c8 --- /dev/null +++ b/src/Error/Exception/UserEmailNotificationExceptionTrait.php @@ -0,0 +1,35 @@ +getAttribute('container'); + } +} diff --git a/src/Middleware/ContainerInjectorMiddleware.php b/src/Middleware/ContainerInjectorMiddleware.php new file mode 100644 index 0000000000..f62ea259a4 --- /dev/null +++ b/src/Middleware/ContainerInjectorMiddleware.php @@ -0,0 +1,56 @@ +container = $container; + } + + /** + * Sets the container in the request as attribute. + * This makes the container accessible to other middlewares. + * + * @param \Psr\Http\Message\ServerRequestInterface $request The request. + * @param \Psr\Http\Server\RequestHandlerInterface $handler The request handler. + * @return \Psr\Http\Message\ResponseInterface A response. + */ + public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface + { + return $handler->handle($request->withAttribute(self::CONTAINER_ATTRIBUTE, $this->container)); + } +} diff --git a/src/Middleware/CsrfProtectionMiddleware.php b/src/Middleware/CsrfProtectionMiddleware.php index 754a10b7c1..e8ffe25aea 100644 --- a/src/Middleware/CsrfProtectionMiddleware.php +++ b/src/Middleware/CsrfProtectionMiddleware.php @@ -17,12 +17,14 @@ namespace App\Middleware; use Cake\Core\Configure; +use Cake\Http\ServerRequest; use Cake\Utility\Hash; use Cake\Utility\Security; -use Psr\Http\Message\RequestInterface; class CsrfProtectionMiddleware extends \Cake\Http\Middleware\CsrfProtectionMiddleware { + public const PASSBOLT_SECURITY_CSRF_PROTECTION_ACTIVE_CONFIG = 'passbolt.security.csrfProtection.active'; + /** * @inheritDoc */ @@ -50,13 +52,12 @@ public function createToken(): string /** * Skip Csrf protection. * - * @param \Psr\Http\Message\RequestInterface $request The request + * @param \Cake\Http\ServerRequest $request The request * @return bool result */ - public function skipCsrfProtection(RequestInterface $request): bool + public function skipCsrfProtection(ServerRequest $request): bool { $plugins = Configure::read('passbolt.plugins'); - /** @var \Cake\Http\ServerRequest $request */ $controller = $request->getParam('controller', 'Error'); $unlockedActions = Configure::read("passbolt.security.csrfProtection.unlockedActions.$controller", []); @@ -67,7 +68,7 @@ public function skipCsrfProtection(RequestInterface $request): bool } } - if (!Configure::read('passbolt.security.csrfProtection.active')) { + if (!Configure::read(self::PASSBOLT_SECURITY_CSRF_PROTECTION_ACTIVE_CONFIG)) { return true; } if (in_array($request->getParam('action'), $unlockedActions)) { diff --git a/src/Middleware/GpgAuthHeadersMiddleware.php b/src/Middleware/GpgAuthHeadersMiddleware.php index 0b115da529..52b3c2ed1e 100644 --- a/src/Middleware/GpgAuthHeadersMiddleware.php +++ b/src/Middleware/GpgAuthHeadersMiddleware.php @@ -17,6 +17,7 @@ namespace App\Middleware; use App\Authenticator\GpgAuthenticator; +use Passbolt\JwtAuthentication\Service\Middleware\JwtRequestDetectionService; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; @@ -39,6 +40,10 @@ public function process( $response = $handler->handle($request); $allowedHeaders = GpgAuthenticator::HTTP_HEADERS_WHITELIST; + if ($request->getAttribute(JwtRequestDetectionService::IS_JWT_AUTH_REQUEST)) { + return $response; + } + $response = $response ->withHeader('X-GPGAuth-Version', '1.3.0') ->withHeader('X-GPGAuth-Login-URL', '/auth/login') diff --git a/src/Middleware/ServerRequestInterfaceInjectionMiddleware.php b/src/Middleware/ServerRequestInterfaceInjectionMiddleware.php new file mode 100644 index 0000000000..7aeb0c59b7 --- /dev/null +++ b/src/Middleware/ServerRequestInterfaceInjectionMiddleware.php @@ -0,0 +1,43 @@ +getContainer($request)->add(ServerRequestInterface::class)->setConcrete($request)->setShared(true); + + return $handler->handle($request); + } +} diff --git a/src/Middleware/SessionAuthPreventDeletedUsersMiddleware.php b/src/Middleware/SessionAuthPreventDeletedUsersMiddleware.php new file mode 100644 index 0000000000..7354d0c2b5 --- /dev/null +++ b/src/Middleware/SessionAuthPreventDeletedUsersMiddleware.php @@ -0,0 +1,66 @@ +getSession()->read('Auth.user.id'); + if (is_string($userId) && $this->isUserSoftDeleted($userId)) { + $request->getSession()->destroy(); + } + + return $handler->handle($request); + } + + /** + * Returns true if the a user with the provided userId + * is soft deleted. + * + * @param string $userId user ID + * @return bool + */ + public function isUserSoftDeleted(string $userId): bool + { + return TableRegistry::getTableLocator()->get('Users') + ->find() + ->where([ + 'Users.id' => $userId, + 'Users.deleted' => true, + ]) + ->count() > 0; + } +} diff --git a/src/Middleware/UacAwareMiddlewareTrait.php b/src/Middleware/UacAwareMiddlewareTrait.php new file mode 100644 index 0000000000..e64142a56c --- /dev/null +++ b/src/Middleware/UacAwareMiddlewareTrait.php @@ -0,0 +1,49 @@ +getAttribute('identity', null); + if (empty($identity)) { + return null; + } + + // User might be stored in the field "user" of the + // identity (JWT Login endpoint) or can directly be + // the identity itself (general way) + $user = $identity->get('user') ?? $identity; + + return new UserAccessControl($user['role']['name'], $user['id'], $user['username']); + } +} diff --git a/src/Model/Entity/AuthenticationToken.php b/src/Model/Entity/AuthenticationToken.php index 90d1144573..f4ace6637c 100644 --- a/src/Model/Entity/AuthenticationToken.php +++ b/src/Model/Entity/AuthenticationToken.php @@ -16,6 +16,8 @@ */ namespace App\Model\Entity; +use App\Service\AuthenticationTokens\AuthenticationTokensSessionService; +use App\Utility\AuthToken\AuthTokenExpiry; use Cake\ORM\Entity; /** @@ -38,6 +40,11 @@ class AuthenticationToken extends Entity public const TYPE_REGISTER = 'register'; public const TYPE_MFA = 'mfa'; public const TYPE_LOGIN = 'login'; + public const TYPE_MOBILE_TRANSFER = 'mobile_transfer'; + public const TYPE_REFRESH_TOKEN = 'refresh_token'; + public const TYPE_VERIFY_TOKEN = 'verify_token'; + + public const SESSION_ID_KEY = 'session_id'; /** * Fields that can be mass assigned using newEntity() or patchEntity(). @@ -56,4 +63,98 @@ class AuthenticationToken extends Entity 'type' => false, 'data' => false, ]; + + /** + * @return bool + */ + public function isActive(): bool + { + return $this->active; + } + + /** + * @return bool + */ + public function isNotActive(): bool + { + return !$this->active; + } + + /** + * @param string|null $expiry Expiry in word format. + * @return bool + */ + public function isExpired(?string $expiry = null): bool + { + if (empty($expiry)) { + $expiry = (new AuthTokenExpiry())->getExpiryForTokenType($this->type); + } + $isNotExpired = $this->created->wasWithinLast($expiry); + + return !$isNotExpired; + } + + /** + * Json decode the token data. + * Will return an empty array if the data is not unserializable. + * + * @return array + */ + public function getJsonDecodedData(): array + { + return json_decode($this->data ?? '', true) ?? []; + } + + /** + * Reads the session ID in the token data. + * The session ID is stored hashed. + * + * @see SessionIdentificationServiceInterface + * @return string|null + */ + public function getHashedSessionId(): ?string + { + $data = $this->getJsonDecodedData(); + + return $data[self::SESSION_ID_KEY] ?? null; + } + + /** + * Writes the session ID in the token data + * + * The password hasher has a limit of 71 characters. + * Therefore two sessions starting with the same 71 characters will + * be tested as identical, although they are not. + * Nullable bytes should also not be placed in the password + * + * Therefore we hash with sha256 the session ID before hashing it with password_hash. + * + * @see SessionIdentificationServiceInterface + * @param string $sessionId Session ID + * @return void + */ + public function hashAndSetSessionId(string $sessionId): void + { + $hashedSessionId = (new AuthenticationTokensSessionService())->hash($sessionId); + $data = array_merge( + $this->getJsonDecodedData(), + [self::SESSION_ID_KEY => $hashedSessionId] + ); + + $this->set('data', json_encode($data)); + } + + /** + * Checks that the session ID provided + * matches the hashed session ID in data->session_id. + * + * The session ID can be a string or another authentication token. + * + * @param \App\Model\Entity\AuthenticationToken|string|null $sessionIdentifier Session ID to check + * @return bool + */ + public function checkSessionId($sessionIdentifier): bool + { + return (new AuthenticationTokensSessionService())->checkSession($this, $sessionIdentifier); + } } diff --git a/src/Model/Entity/Avatar.php b/src/Model/Entity/Avatar.php index e3ac077b3b..87aad7194d 100644 --- a/src/Model/Entity/Avatar.php +++ b/src/Model/Entity/Avatar.php @@ -19,10 +19,11 @@ use App\View\Helper\AvatarHelper; use Cake\Core\Configure; use Cake\ORM\Entity; +use Psr\Http\Message\StreamInterface; /** * @property string $id - * @property string|resource|null $data + * @property mixed $data * @property string $profile_id * @property \Cake\I18n\FrozenTime $created_at * @property \Cake\I18n\FrozenTime|null $updated_at @@ -33,6 +34,15 @@ class Avatar extends Entity { protected $_virtual = ['url']; + /** + * The avatar data never needs to be served. it is stored in cache. + * + * @var string[] + */ + protected $_hidden = [ + 'data', + ]; + /** * Fields that can be mass assigned using newEntity() or patchEntity(). * @@ -58,10 +68,31 @@ protected function _getUrl() $avatarsPath = []; // Add path for each available size. foreach ($sizes as $size => $filters) { - $avatarsPath[$size] = AvatarHelper::getAvatarUrl($this, $size); + $avatarsPath[$size] = AvatarHelper::getAvatarUrl([ + 'id' => $this->id, + 'data' => $this->data, + ], $size); } // Transform original model to add paths. return $avatarsPath; } + + /** + * Get data in string format. + * + * @return string + */ + public function getDataInStringFormat(): string + { + $data = $this->data ?? ''; + + if (is_resource($data)) { + $data = stream_get_contents($data); + } elseif ($data instanceof StreamInterface) { + $data = $data->getContents(); + } + + return $data; + } } diff --git a/src/Model/Entity/OrganizationSetting.php b/src/Model/Entity/OrganizationSetting.php index 90094963f0..4a5eda517d 100644 --- a/src/Model/Entity/OrganizationSetting.php +++ b/src/Model/Entity/OrganizationSetting.php @@ -27,7 +27,7 @@ * @property string $property * @property string $value * - * @property \Passbolt\OrganizationSettings\Model\Entity\User $user + * @property \App\Model\Entity\User $user * @property string $property_id * @property \Cake\I18n\FrozenTime $created * @property \Cake\I18n\FrozenTime $modified diff --git a/src/Model/Entity/Resource.php b/src/Model/Entity/Resource.php index 6074119725..44a37e0028 100644 --- a/src/Model/Entity/Resource.php +++ b/src/Model/Entity/Resource.php @@ -45,6 +45,16 @@ */ class Resource extends Entity { + /** + * List of property names that should **not** be included in JSON or Array + * representations of this Entity. + * + * @var string[] + */ + protected $_hidden = [ + '_joinData', + ]; + /** * Fields that can be mass assigned using newEntity() or patchEntity(). * diff --git a/src/Model/Entity/User.php b/src/Model/Entity/User.php index c03ba07cb7..939f045cb3 100644 --- a/src/Model/Entity/User.php +++ b/src/Model/Entity/User.php @@ -16,6 +16,7 @@ */ namespace App\Model\Entity; +use Authentication\IdentityInterface; use Cake\ORM\Entity; /** @@ -41,9 +42,9 @@ * @property \App\Model\Entity\Group[] $groups * @property \App\Model\Entity\Permission[] $permissions * @property \Passbolt\Log\Model\Entity\ActionLog[] $action_logs - * @property \Passbolt\AccountSettings\Model\Entity\AccountSetting|null $locale + * @property \Passbolt\AccountSettings\Model\Entity\AccountSetting|string|null $locale */ -class User extends Entity +class User extends Entity implements IdentityInterface { /** * last_logged_in virtual field. @@ -71,4 +72,24 @@ class User extends Entity // associated data 'profile' => false, ]; + + /** + * Authentication\IdentityInterface method + * + * @return string|null + */ + public function getIdentifier(): ?string + { + return $this->id; + } + + /** + * Authentication\IdentityInterface method + * + * @return self + */ + public function getOriginalData(): self + { + return $this; + } } diff --git a/src/Model/Event/TableFindIndexBefore.php b/src/Model/Event/TableFindIndexBefore.php index 646b4c2163..26274ea13c 100644 --- a/src/Model/Event/TableFindIndexBefore.php +++ b/src/Model/Event/TableFindIndexBefore.php @@ -43,14 +43,14 @@ class TableFindIndexBefore extends Event /** * @param string $name Name - * @param null $subject Subject - * @param null $data Data + * @param \Cake\ORM\Table $subject Subject + * @param array $data Data */ - final public function __construct($name, $subject = null, $data = null) + final public function __construct(string $name, Table $subject, array $data) { $this->setTable($subject); - $this->setOptions($data['options'] ?? null); - $this->setQuery($data['query'] ?? null); + $this->setOptions($data['options']); + $this->setQuery($data['query']); parent::__construct($name, $subject, $data); } diff --git a/src/Model/Rule/IsNotSoleManagerOfNonEmptyGroupRule.php b/src/Model/Rule/IsNotSoleManagerOfNonEmptyGroupRule.php index dc7e3c8904..ce509b5af2 100644 --- a/src/Model/Rule/IsNotSoleManagerOfNonEmptyGroupRule.php +++ b/src/Model/Rule/IsNotSoleManagerOfNonEmptyGroupRule.php @@ -33,7 +33,10 @@ public function __invoke(EntityInterface $entity, array $options) { /** @var \App\Model\Table\GroupsUsersTable $GroupsUsers */ $GroupsUsers = TableRegistry::getTableLocator()->get('GroupsUsers'); - $groups = $GroupsUsers->findNonEmptyGroupsWhereUserIsSoleManager($entity->id)->extract('group_id')->toArray(); + $groups = $GroupsUsers + ->findNonEmptyGroupsWhereUserIsSoleManager($entity->get('id')) + ->extract('group_id') + ->toArray(); return empty($groups); } diff --git a/src/Model/Rule/IsNotSoleOwnerOfSharedResourcesRule.php b/src/Model/Rule/IsNotSoleOwnerOfSharedResourcesRule.php index a46763a32f..ed7e326ace 100644 --- a/src/Model/Rule/IsNotSoleOwnerOfSharedResourcesRule.php +++ b/src/Model/Rule/IsNotSoleOwnerOfSharedResourcesRule.php @@ -42,7 +42,7 @@ public function __invoke(EntityInterface $entity, array $options): bool } $check = $Permissions - ->findSharedAcosByAroIsSoleOwner(PermissionsTable::RESOURCE_ACO, $entity->id, [ + ->findSharedAcosByAroIsSoleOwner(PermissionsTable::RESOURCE_ACO, $entity->get('id'), [ 'checkGroupsUsers' => $checkGroupsUsers, ]) ->count(); diff --git a/src/Model/Table/AuthenticationTokensTable.php b/src/Model/Table/AuthenticationTokensTable.php index dfe026176d..6d43eba65c 100644 --- a/src/Model/Table/AuthenticationTokensTable.php +++ b/src/Model/Table/AuthenticationTokensTable.php @@ -22,6 +22,9 @@ use App\Model\Traits\AuthenticationTokens\AuthenticationTokensFindersTrait; use App\Utility\AuthToken\AuthTokenExpiry; use App\Utility\UuidFactory; +use Cake\Http\Exception\InternalErrorException; +use Cake\I18n\FrozenTime; +use Cake\ORM\Query; use Cake\ORM\RulesChecker; use Cake\ORM\Table; use Cake\Validation\Validation; @@ -55,6 +58,9 @@ class AuthenticationTokensTable extends Table AuthenticationToken::TYPE_RECOVER, AuthenticationToken::TYPE_LOGIN, AuthenticationToken::TYPE_MFA, + AuthenticationToken::TYPE_MOBILE_TRANSFER, + AuthenticationToken::TYPE_REFRESH_TOKEN, + AuthenticationToken::TYPE_VERIFY_TOKEN, ]; /** @@ -135,11 +141,7 @@ public function validationDefault(Validator $validator): Validator */ public function isValidAuthenticationTokenType($check, array $context) { - return is_string($check) && ( - $check === AuthenticationToken::TYPE_REGISTER || - $check === AuthenticationToken::TYPE_RECOVER || - $check === AuthenticationToken::TYPE_LOGIN || - $check === AuthenticationToken::TYPE_MFA); + return is_string($check) && (in_array($check, self::ALLOWED_TYPES)); } /** @@ -178,25 +180,33 @@ public function buildRules(RulesChecker $rules): RulesChecker * * @param string $userId uuid * @param string $type AuthenticationToken::TYPE_* + * @param ?string $token token value (optional) + * @param ?array $data data value (optional) * @throws \App\Error\Exception\ValidationException is the user is not a valid uuid * @throws \App\Error\Exception\ValidationException is the user is not found * @throws \App\Error\Exception\ValidationException is the user is deleted * @return \App\Model\Entity\AuthenticationToken $token */ - public function generate(string $userId, string $type) - { + public function generate( + string $userId, + string $type, + ?string $token = null, + ?array $data = [] + ): AuthenticationToken { $token = $this->newEntity( [ 'user_id' => $userId, - 'token' => UuidFactory::uuid(), + 'token' => $token ?? UuidFactory::uuid(), 'active' => true, 'type' => $type, + 'data' => empty($data) ? null : json_encode($data), ], ['accessibleFields' => [ 'user_id' => true, 'token' => true, 'active' => true, 'type' => true, + 'data' => true, ]] ); $errors = $token->getErrors(); @@ -220,25 +230,26 @@ public function generate(string $userId, string $type) * - is active && * - is not expired ; * - * @param string $tokenId uuid of the token to check + * @param string $token uuid of the token to check * @param string $userId uuid of the user - * @param string $type token type + * @param string|null $type token type * @param string|int $expiry the numeric value with space then time type. * Example of valid types: 6 hours, 2 days, 1 minute. * @return bool true if it is valid */ - public function isValid(string $tokenId, string $userId, ?string $type = null, $expiry = null) + public function isValid(string $token, string $userId, ?string $type = null, $expiry = null): bool { // Are ids valid uuid? - if (!Validation::uuid($tokenId) || !Validation::uuid($userId)) { + if (!Validation::uuid($token) || !Validation::uuid($userId)) { return false; } // Does token exist? - $where = ['token' => $tokenId, 'user_id' => $userId, 'active' => true]; + $where = ['token' => $token, 'user_id' => $userId, 'active' => true]; if ($type) { $where['type'] = $type; } + /** @var \App\Model\Entity\AuthenticationToken|null $token */ $token = $this->find() ->where($where) @@ -269,28 +280,23 @@ public function isValid(string $tokenId, string $userId, ?string $type = null, $ */ public function isExpired(AuthenticationToken $token, $expiry = null): bool { - if ($expiry === null) { - $expiry = $this->authTokenExpiry->getExpirationForTokenType($token->type); - } - $isNotExpired = $token->created->wasWithinLast($expiry); - - return !$isNotExpired; + return $token->isExpired($expiry); } /** * Set a token as inactive * - * @param string $tokenId uuid - * @throws \InvalidArgumentException is the token is not a valid uuid + * @param string $tokenValue uuid * @return bool save result + * @throws \InvalidArgumentException is the token is not a valid uuid */ - public function setInactive(string $tokenId): bool + public function setInactive(string $tokenValue): bool { - if (!Validation::uuid($tokenId)) { + if (!Validation::uuid($tokenValue)) { throw new \InvalidArgumentException('The token should be a valid UUID.'); } $token = $this->find('all') - ->where(['token' => $tokenId, 'active' => true ]) + ->where(['token' => $tokenValue, 'active' => true ]) ->first(); if (empty($token)) { @@ -306,21 +312,22 @@ public function setInactive(string $tokenId): bool } /** - * Get a token entity using the token id + * Get a token entity using the token value * (e.g. get using token->token, not token->id ) * - * @param string $tokenId uuid + * @param string $tokenValue uuid + * @return \App\Model\Entity\AuthenticationToken * @throws \InvalidArgumentException is the token is not a valid uuid - * @return array|\Cake\Datasource\EntityInterface|null + * @throws \Cake\Datasource\Exception\RecordNotFoundException is the token is not found */ - public function getByToken(string $tokenId) + public function getByToken(string $tokenValue): AuthenticationToken { - if (!Validation::uuid($tokenId)) { - throw new \InvalidArgumentException('The token should be a valid UUID.'); + if (!Validation::uuid($tokenValue)) { + throw new \InvalidArgumentException(__('The token should be a valid UUID.')); } - $token = $this->find('all') - ->where(['token' => $tokenId, 'active' => true ]) - ->first(); + + /** @var \App\Model\Entity\AuthenticationToken $token */ + $token = $this->find()->where(['token' => $tokenValue, 'active' => true ])->firstOrFail(); return $token; } @@ -349,4 +356,77 @@ public function getByUserId(string $userId, $type = null) return $token; } + + /** + * getExpiryDate for a given type + * + * @param string $type type + * @return \Cake\I18n\FrozenTime + */ + private function getExpiryDate(string $type) + { + $expiryPeriod = $this->authTokenExpiry->getExpiryForTokenType($type); + if (!isset($expiryPeriod) || empty($expiryPeriod)) { + $msg = 'AuthenticationTokensTable::findExpiredByType no expiry in config for token type ' . $type; + throw new InternalErrorException($msg); + } + + return new FrozenTime($expiryPeriod . ' ago'); + } + + /** + * Finder to return authentication token that have expired + * Requires a type to be provided + * + * @param \Cake\ORM\Query $query query + * @param array $options options + * @throws \InvalidArgumentException if type is missing in options + * @throws \Cake\Http\Exception\InternalErrorException if token type does not have expiry in config + * @return \Cake\ORM\Query + */ + public function findExpiredByType(Query $query, array $options): Query + { + if (count($options) === 0 || !isset($options['type'])) { + $msg = 'AuthenticationTokensTable::findExpiredByType error, a token type is required'; + throw new \InvalidArgumentException($msg); + } + $type = $options['type']; + + return $query->where([ + 'type' => $type, + 'created <' => $this->getExpiryDate($type), + ]); + } + + /** + * Set all expired tokens to inactive + * + * @return void + */ + public function setAllActiveExpiredTokenToInactive(): void + { + $types = self::ALLOWED_TYPES; + foreach ($types as $type) { + $this->setActiveExpiredTokenToInactive($type); + } + } + + /** + * Set all expired tokens to inactive + * + * @param string $type type + * @return void + */ + public function setActiveExpiredTokenToInactive(string $type): void + { + $this->query() + ->update() + ->set(['active' => false]) + ->where([ + 'active' => true, + 'type' => $type, + 'created <' => $this->getExpiryDate($type), + ]) + ->execute(); + } } diff --git a/src/Model/Table/AvatarsTable.php b/src/Model/Table/AvatarsTable.php index 0569d9b631..a693c8a095 100644 --- a/src/Model/Table/AvatarsTable.php +++ b/src/Model/Table/AvatarsTable.php @@ -17,13 +17,15 @@ namespace App\Model\Table; use App\Model\Entity\Avatar; +use App\Model\Traits\Cleanup\AvatarsCleanupTrait; use App\Service\Avatars\AvatarsCacheService; +use App\Service\Avatars\AvatarsConfigurationService; use App\Utility\AvatarProcessing; use App\View\Helper\AvatarHelper; use Cake\Collection\CollectionInterface; use Cake\Core\Configure; -use Cake\Event\Event; use Cake\Database\Expression\IdentifierExpression; +use Cake\Event\Event; use Cake\Log\Log; use Cake\ORM\Query; use Cake\ORM\RulesChecker; @@ -31,6 +33,7 @@ use Cake\Validation\Validator; use League\Flysystem\Filesystem; use League\Flysystem\FilesystemAdapter; +use League\Flysystem\Local\LocalFilesystemAdapter; use Psr\Http\Message\UploadedFileInterface; /** @@ -53,11 +56,11 @@ */ class AvatarsTable extends Table { - public const FORMAT_SMALL = 'small'; - public const FORMAT_MEDIUM = 'medium'; + use AvatarsCleanupTrait; + public const MAX_SIZE = '5MB'; - public const ALLOWED_MIME_TYPES = ['image/jpeg', 'image/png', 'image/gif']; - public const ALLOWED_EXTENSIONS = ['png', 'jpg', 'gif']; + public const ALLOWED_MIME_TYPES = ['image/jpg', 'image/jpeg', 'image/png', 'image/gif']; + public const ALLOWED_EXTENSIONS = ['png', 'jpg', 'jpeg', 'gif']; /** * Initialize method @@ -69,6 +72,7 @@ public function initialize(array $config): void { parent::initialize($config); + $this->initializeConfiguration(); $this->addBehavior('Timestamp'); $this->belongsTo('Profiles'); } @@ -206,18 +210,21 @@ public static function formatResults(CollectionInterface $avatars) } /** - * Generate an Avatar contain clause to be inserted in a contain table. + * addContainAvatar + * Helper to add avatar contains options in a query * - * @return array + * @return array contain clause */ public static function addContainAvatar(): array { return [ 'Avatars' => function (Query $q) { - // Formatter for empty avatars. - return $q->formatResults(function (CollectionInterface $avatars) { - return AvatarsTable::formatResults($avatars); - }); + // Formatter for empty avatars. + return $q + ->select(['Avatars.id', 'Avatars.profile_id', 'Avatars.created', 'Avatars.modified']) + ->formatResults(function (CollectionInterface $avatars) { + return AvatarsTable::formatResults($avatars); + }); }, ]; } @@ -258,7 +265,9 @@ public function setData(Avatar $avatar): bool Configure::readOrFail('FileStorage.imageSizes.Avatar.medium.thumbnail.height') ); $avatar->set('data', $img); - } catch (\Exception $exception) { + } catch (\Throwable $e) { + Log::error($e->getMessage()); + return false; } @@ -283,4 +292,23 @@ public function setFilesystem(FilesystemAdapter $adapter): void { Configure::write('AvatarFilesystem', new Filesystem($adapter)); } + + /** + * Set the default file system adapter and configurations on initialize. + * + * @return void + */ + protected function initializeConfiguration(): void + { + // Per default, use the local file system adapter. This may be overwritten on the fly if needed + // e.g. if storing the avatar on a cloud bucket. + $this->setFilesystem(new LocalFilesystemAdapter(TMP . 'avatars')); + + // These configurations should be set in the Application::bootstrap() method. + // However, has a backup, we ensure that on AvatarTable's initialization these + // configurations are well set. + if (!Configure::check('FileStorage')) { + (new AvatarsConfigurationService())->loadConfiguration(); + } + } } diff --git a/src/Model/Table/OrganizationSettingsTable.php b/src/Model/Table/OrganizationSettingsTable.php index 0b43f0e72a..51c52b94ca 100644 --- a/src/Model/Table/OrganizationSettingsTable.php +++ b/src/Model/Table/OrganizationSettingsTable.php @@ -85,7 +85,7 @@ public function validationDefault(Validator $validator): Validator ->regex( 'property', '/^[a-zA-Z][a-zA-Z.]+[a-z]$/', - __('The property should be an alphabetical string and only point is accepted as special characters.') + __('The property should be an alphabetical string and only dot is accepted as special characters.') ); $validator diff --git a/src/Model/Table/PermissionsTable.php b/src/Model/Table/PermissionsTable.php index d38a8db8b4..9c11242335 100644 --- a/src/Model/Table/PermissionsTable.php +++ b/src/Model/Table/PermissionsTable.php @@ -50,7 +50,7 @@ * @method \App\Model\Entity\Permission[]|\Cake\Datasource\ResultSetInterface saveManyOrFail(iterable $entities, $options = []) * @method \App\Model\Entity\Permission[]|\Cake\Datasource\ResultSetInterface|false deleteMany(iterable $entities, $options = []) * @method \App\Model\Entity\Permission[]|\Cake\Datasource\ResultSetInterface deleteManyOrFail(iterable $entities, $options = []) - * @method \Cake\ORM\Query findByAcoForeignKeyAndType(string $acoForeignKey, string $type) + * @method \Cake\ORM\Query findByAcoForeignKeyAndType(string $acoForeignKey, int $type) * @method \Cake\ORM\Query findByAroAndAcoForeignKey(string $aro, string $acoForeignKey) * @method \Cake\ORM\Query findByIdAndAcoForeignKey(string $id, string $acoForeignKey) */ @@ -205,7 +205,7 @@ public function validationSaveResource(Validator $validator) */ public function isValidPermissionType(int $value) { - return is_int($value) && in_array($value, self::ALLOWED_TYPES); + return in_array($value, self::ALLOWED_TYPES); } /** diff --git a/src/Model/Table/ResourcesTable.php b/src/Model/Table/ResourcesTable.php index 33503e3c49..038fb71546 100644 --- a/src/Model/Table/ResourcesTable.php +++ b/src/Model/Table/ResourcesTable.php @@ -231,7 +231,7 @@ public function validationDefault(Validator $validator): Validator public function buildRules(RulesChecker $rules): RulesChecker { // Create and Update rules - $rules->add($rules->existsIn(['resource_type_id'], 'ResourceTypes'), 'resource_type_exists', [ + $rules->add($rules->existsIn('resource_type_id', 'ResourceTypes'), 'resource_type_exists', [ 'message' => __('The resource type does not exist.'), ]); diff --git a/src/Model/Table/RolesTable.php b/src/Model/Table/RolesTable.php index d2b6eaa988..3b514540b6 100644 --- a/src/Model/Table/RolesTable.php +++ b/src/Model/Table/RolesTable.php @@ -139,6 +139,6 @@ public function getIdByName(string $roleName) return null; } - return $role->id; + return $role->get('id'); } } diff --git a/src/Model/Table/UserAgentsTable.php b/src/Model/Table/UserAgentsTable.php index 53e55d3a2a..0c3cfe388b 100644 --- a/src/Model/Table/UserAgentsTable.php +++ b/src/Model/Table/UserAgentsTable.php @@ -77,10 +77,10 @@ public function validationDefault(Validator $validator): Validator /** * Sanitize and parse the user agent string * - * @param string $ua user agent (optional) - * @return string + * @param string|null $ua user agent (optional) + * @return string|null */ - public function browserName(?string $ua = null) + public function browserName(?string $ua = null): ?string { if ($ua == null) { $ua = Purifier::clean(env('HTTP_USER_AGENT')); diff --git a/src/Model/Table/UsersTable.php b/src/Model/Table/UsersTable.php index b53237ac42..bc48a66045 100644 --- a/src/Model/Table/UsersTable.php +++ b/src/Model/Table/UsersTable.php @@ -26,7 +26,6 @@ use App\Model\Traits\Users\UsersFindersTrait; use App\Utility\UserAccessControl; use Cake\Core\Configure; -use Cake\Event\Event; use Cake\Http\Exception\InternalErrorException; use Cake\ORM\RulesChecker; use Cake\ORM\Table; @@ -81,6 +80,7 @@ public function initialize(array $config): void $this->setDisplayField('id'); $this->setPrimaryKey('id'); + $this->addBehavior('Passbolt/Locale.Locale'); $this->addBehavior('Timestamp'); $this->belongsTo('Roles', [ @@ -409,7 +409,7 @@ public function softDelete(User $user, ?array $options = null) * @throws \App\Error\Exception\ValidationException if the user data do not validate * @return \App\Model\Entity\User entity */ - public function register(array $data, ?UserAccessControl $control = null) + public function register(array $data, ?UserAccessControl $control = null): User { // if role id is empty make it a user // Only admins are allowed to set the role @@ -449,8 +449,7 @@ public function register(array $data, ?UserAccessControl $control = null) if (isset($control) && !empty($control->getId())) { $eventData['adminId'] = $control->getId(); } - $event = new Event(static::AFTER_REGISTER_SUCCESS_EVENT_NAME, $this, $eventData); - $this->getEventManager()->dispatch($event); + $this->dispatchEvent(static::AFTER_REGISTER_SUCCESS_EVENT_NAME, $eventData, $this); return $user; } diff --git a/src/Model/Traits/AuthenticationTokens/AuthenticationTokensFindersTrait.php b/src/Model/Traits/AuthenticationTokens/AuthenticationTokensFindersTrait.php index 2140afffd4..be25ffc384 100644 --- a/src/Model/Traits/AuthenticationTokens/AuthenticationTokensFindersTrait.php +++ b/src/Model/Traits/AuthenticationTokens/AuthenticationTokensFindersTrait.php @@ -33,14 +33,7 @@ trait AuthenticationTokensFindersTrait */ public function findActiveUserRegistrationToken(Query $query, array $options): Query { - $where = [ - 'type' => AuthenticationToken::TYPE_REGISTER, - 'token' => Hash::get($options, 'token', ''), - 'user_id' => Hash::get($options, 'userId', ''), - 'active' => true, - ]; - - return $query->where($where); + return $this->findActiveByType($query, AuthenticationToken::TYPE_REGISTER, $options); } /** @@ -52,9 +45,21 @@ public function findActiveUserRegistrationToken(Query $query, array $options): Q * @return \Cake\ORM\Query */ public function findActiveUserRecoveryToken(Query $query, array $options): Query + { + return $this->findActiveByType($query, AuthenticationToken::TYPE_RECOVER, $options); + } + + /** + * @param \Cake\ORM\Query $query The query to decorate + * @param string $type The token type + * @param array $options The finder options + * [userId: string, token: string] + * @return \Cake\ORM\Query + */ + public function findActiveByType(Query $query, string $type, array $options): Query { $where = [ - 'type' => AuthenticationToken::TYPE_RECOVER, + 'type' => $type, 'token' => Hash::get($options, 'token', ''), 'user_id' => Hash::get($options, 'userId', ''), 'active' => true, diff --git a/src/Model/Traits/Cleanup/AvatarsCleanupTrait.php b/src/Model/Traits/Cleanup/AvatarsCleanupTrait.php new file mode 100644 index 0000000000..1e5609788a --- /dev/null +++ b/src/Model/Traits/Cleanup/AvatarsCleanupTrait.php @@ -0,0 +1,55 @@ +cleanupSoftDeleted('Profiles.Users', $dryRun); + } + + /** + * Delete all records where associated users are deleted + * + * @param bool|null $dryRun false + * @return int number of affected records + */ + public function cleanupHardDeletedUsers(?bool $dryRun = false): int + { + return $this->cleanupHardDeleted('Profiles.Users', $dryRun); + } + + /** + * Delete all records where associated users are deleted + * + * @param bool|null $dryRun false + * @return int number of affected records + */ + public function cleanupHardDeletedProfiles(?bool $dryRun = false): int + { + return $this->cleanupHardDeleted('Profiles', $dryRun); + } +} diff --git a/src/Model/Traits/Cleanup/PermissionsCleanupTrait.php b/src/Model/Traits/Cleanup/PermissionsCleanupTrait.php index 2510e67e8b..554161eed9 100644 --- a/src/Model/Traits/Cleanup/PermissionsCleanupTrait.php +++ b/src/Model/Traits/Cleanup/PermissionsCleanupTrait.php @@ -29,7 +29,7 @@ trait PermissionsCleanupTrait public function cleanupHardDeletedPermissions(?bool $dryRun = false): int { $secretsToDelete = []; - $secrets = $this->find('all'); + $secrets = $this->find('all')->select(['id', 'resource_id', 'user_id']); $acoType = PermissionsTable::RESOURCE_ACO; foreach ($secrets as $secret) { diff --git a/src/Model/Traits/Cleanup/TableCleanupTrait.php b/src/Model/Traits/Cleanup/TableCleanupTrait.php index 8e04cae6ee..1b0d245653 100644 --- a/src/Model/Traits/Cleanup/TableCleanupTrait.php +++ b/src/Model/Traits/Cleanup/TableCleanupTrait.php @@ -24,18 +24,18 @@ trait TableCleanupTrait /** * Delete all association records where associated model entities are soft deleted * - * @param string $modelName model + * @param string $association association * @param bool|null $dryRun false * @param \Cake\ORM\Query|null $query custom query to replace the default find if any * @return int Number of affected records */ - public function cleanupSoftDeleted(string $modelName, ?bool $dryRun = false, ?Query $query = null): int + public function cleanupSoftDeleted(string $association, ?bool $dryRun = false, ?Query $query = null): int { if (!isset($query)) { $query = $this->query() ->select(['id']) - ->leftJoinWith($modelName) - ->where([$modelName . '.deleted' => true]); + ->leftJoinWith($association) + ->where([$this->getModelNameFromAssociation($association) . '.deleted' => true]); } $records = Hash::extract($query->toArray(), '{n}.id'); if ($dryRun) { @@ -51,20 +51,18 @@ public function cleanupSoftDeleted(string $modelName, ?bool $dryRun = false, ?Qu /** * Delete all association records where associated model entities are deleted * - * @param string $modelName model + * @param string $association association * @param bool|null $dryRun false * @param \Cake\ORM\Query|null $query custom query to replace the default find if any * @return int Number of affected records */ - public function cleanupHardDeleted(string $modelName, ?bool $dryRun = false, ?Query $query = null): int + public function cleanupHardDeleted(string $association, ?bool $dryRun = false, ?Query $query = null): int { if (!isset($query)) { $query = $this->query() ->select(['id']) - ->leftJoinWith($modelName) - ->where(function ($exp, $q) use ($modelName) { - return $exp->isNull($modelName . '.id'); - }); + ->leftJoinWith($association) + ->whereNull($this->getModelNameFromAssociation($association) . '.id'); } $records = Hash::extract($query->toArray(), '{n}.id'); if ($dryRun) { @@ -76,4 +74,17 @@ public function cleanupHardDeleted(string $modelName, ?bool $dryRun = false, ?Qu return 0; } + + /** + * Extracts the string after the last dot. + * + * @param string $association Association path + * @return string + */ + protected function getModelNameFromAssociation(string $association): string + { + $pos = strrpos($association, '.'); + + return $pos === false ? $association : substr($association, $pos + 1); + } } diff --git a/src/Model/Traits/Resources/ResourcesFindersTrait.php b/src/Model/Traits/Resources/ResourcesFindersTrait.php index aad2fa148d..bfa3b7fe49 100644 --- a/src/Model/Traits/Resources/ResourcesFindersTrait.php +++ b/src/Model/Traits/Resources/ResourcesFindersTrait.php @@ -216,7 +216,7 @@ public function findView(string $userId, string $resourceId, ?array $options = [ * @param string $groupId uuid The group to fetch the resources for * @return \Cake\ORM\Query */ - public function findAllByGroupAccess(string $groupId) + public function findAllByGroupAccess(string $groupId): Query { if (!Validation::uuid($groupId)) { throw new \InvalidArgumentException('The group identifier should be a valid UUID.'); diff --git a/src/Model/Traits/Users/UsersFindersTrait.php b/src/Model/Traits/Users/UsersFindersTrait.php index 1134fa3fc7..c4ae465a80 100644 --- a/src/Model/Traits/Users/UsersFindersTrait.php +++ b/src/Model/Traits/Users/UsersFindersTrait.php @@ -32,7 +32,7 @@ use InvalidArgumentException; /** - * @method \Composer\EventDispatcher\EventDispatcher getEventManager() + * @method \Cake\Event\EventManager getEventManager() * @property \Passbolt\Log\Model\Table\ActionLogsTable $ActionLogs */ trait UsersFindersTrait @@ -99,7 +99,7 @@ private function _filterQueryByGroupsUsers(Query $query, array $groupsIds, bool private function _filterQueryByResourceAccess(\Cake\ORM\Query $query, string $resourceId) { if (!Validation::uuid($resourceId)) { - throw new InvalidArgumentException('The resource identifier should be a valid UUID.'); + throw new InvalidArgumentException(__('The resource identifier should be a valid UUID.')); } // The query requires a join with Permissions not constraint with the default condition added by the HasMany @@ -416,16 +416,16 @@ public function findSetup(string $userId) * * @param string $userId uuid * @throws \InvalidArgumentException if the user id is not a uuid - * @return \App\Model\Entity\User $user entity + * @return \App\Model\Entity\User|null $user entity */ - public function findSetupRecover(string $userId) + public function findSetupRecover(string $userId): ?User { if (!Validation::uuid($userId)) { throw new InvalidArgumentException('The user identifier should be a valid UUID.'); } // show active first and do not count deleted ones - /** @var \App\Model\Entity\User $user */ + /** @var \App\Model\Entity\User|null $user */ $user = $this->find('locale') ->contain([ 'Roles', @@ -446,7 +446,7 @@ public function findSetupRecover(string $userId) * * @param string $userId uuid * @throws \InvalidArgumentException if the user id is not a valid uuid - * @return \App\Model\Entity\User + * @return \App\Model\Entity\User|null */ public function findFirstForEmail(string $userId) { @@ -455,7 +455,7 @@ public function findFirstForEmail(string $userId) } /** @var \App\Model\Entity\User $user */ - $user = $this->find() + $user = $this->find('locale') ->where(['Users.id' => $userId]) ->contain([ 'Profiles' => AvatarsTable::addContainAvatar(), @@ -492,7 +492,7 @@ public function findFirstAdmin(): ?User * * @return \Cake\ORM\Query */ - public function findAdmins() + public function findAdmins(): Query { return $this->find() ->where( @@ -565,4 +565,18 @@ public function findlastLoggedIn(Query $query) return $query; } + + /** + * Active and non deleted users only. + * + * @param \Cake\ORM\Query $query Query to carve. + * @return \Cake\ORM\Query + */ + public function findActiveNotDeletedContainRole(Query $query): Query + { + return $query->where([ + $this->aliasField('active') => true, + $this->aliasField('deleted') => false, + ])->contain('Roles'); + } } diff --git a/src/Notification/Email/CollectSubscribedEmailRedactorEvent.php b/src/Notification/Email/CollectSubscribedEmailRedactorEvent.php index a7768147d3..7fbdd1c244 100644 --- a/src/Notification/Email/CollectSubscribedEmailRedactorEvent.php +++ b/src/Notification/Email/CollectSubscribedEmailRedactorEvent.php @@ -31,8 +31,8 @@ class CollectSubscribedEmailRedactorEvent extends Event /** * @param string $name Name of the event - * @param null $subject Subject of the dispatched event - * @param null $data Data for the event + * @param \App\Notification\Email\EmailSubscriptionManager|null $subject Subject of the dispatched event + * @param array|null $data Data for the event */ final public function __construct($name, $subject = null, $data = null) { diff --git a/src/Notification/Email/EmailSender.php b/src/Notification/Email/EmailSender.php index 5aa85daa01..41579fbc91 100644 --- a/src/Notification/Email/EmailSender.php +++ b/src/Notification/Email/EmailSender.php @@ -34,7 +34,7 @@ class EmailSender { /** - * @var \EmailQueue\EmailQueue + * @var \EmailQueue\Model\Table\EmailQueueTable */ private $emailQueue; diff --git a/src/Notification/Email/Redactor/AdminUserSetupCompleteEmailRedactor.php b/src/Notification/Email/Redactor/AdminUserSetupCompleteEmailRedactor.php index 3c80d760af..a647014e57 100644 --- a/src/Notification/Email/Redactor/AdminUserSetupCompleteEmailRedactor.php +++ b/src/Notification/Email/Redactor/AdminUserSetupCompleteEmailRedactor.php @@ -29,6 +29,7 @@ use Cake\I18n\FrozenTime; use Cake\ORM\Query; use Cake\ORM\TableRegistry; +use Passbolt\Locale\Service\LocaleService; use Passbolt\Log\Model\Entity\EntityHistory; use RuntimeException; @@ -117,7 +118,7 @@ private function createEmailCollection(User $userWhoCompletedSetup) } /** @var \App\Model\Entity\User[] $admins */ - $admins = $this->usersTable->findAdmins(); + $admins = $this->usersTable->findAdmins()->find('locale'); // Create an email for every admin foreach ($admins as $admin) { $emailCollection->addEmail( @@ -140,7 +141,12 @@ private function createEmail(User $admin, User $userCompletedSetup, User $invite /** @var \App\Model\Entity\Profile $profile */ $profile = $userCompletedSetup->profile; - $subject = __('{0} just activated their account on passbolt', $profile->first_name); + $subject = (new LocaleService())->translateString( + $admin->locale, + function () use ($profile) { + return __('{0} just activated their account on passbolt', $profile->first_name); + } + ); return new Email( $admin->username, diff --git a/src/Notification/Email/Redactor/Comment/CommentAddEmailRedactor.php b/src/Notification/Email/Redactor/Comment/CommentAddEmailRedactor.php index d44cabfc6b..582218a40c 100644 --- a/src/Notification/Email/Redactor/Comment/CommentAddEmailRedactor.php +++ b/src/Notification/Email/Redactor/Comment/CommentAddEmailRedactor.php @@ -30,6 +30,7 @@ use App\Notification\Email\SubscribedEmailRedactorTrait; use Cake\Event\Event; use Cake\ORM\TableRegistry; +use Passbolt\Locale\Service\LocaleService; class CommentAddEmailRedactor implements SubscribedEmailRedactorInterface { @@ -84,12 +85,13 @@ public function onSubscribedEvent(Event $event): EmailCollection { $emailCollection = new EmailCollection(); + /** @var \App\Model\Entity\Comment $comment */ $comment = $event->getData('comment'); // Find the users that have access to the resource (including via their groups) $options = ['contain' => ['role'], 'filter' => ['has-access' => [$comment->foreign_key]]]; - $users = $this->usersTable->findIndex(Role::USER, $options)->all(); - if (count($users) < 2) { + $users = $this->usersTable->findIndex(Role::USER, $options)->find('locale'); + if ($users->count() < 2) { // if there is nobody or just one user, give it up return $emailCollection; } @@ -110,15 +112,20 @@ public function onSubscribedEvent(Event $event): EmailCollection } /** - * @param \App\Model\Entity\User $user User to notify + * @param \App\Model\Entity\User $recipient User to notify * @param \App\Model\Entity\User $creator Creator of the comment * @param Resource $resource Resource on which a comment was added * @param \App\Model\Entity\Comment $comment Comment added * @return \App\Notification\Email\Email */ - private function createCommentAddEmail(User $user, User $creator, Resource $resource, Comment $comment): Email + private function createCommentAddEmail(User $recipient, User $creator, Resource $resource, Comment $comment): Email { - $subject = __('{0} commented on {1}', $creator->profile->first_name, $resource->name); + $subject = (new LocaleService())->translateString( + $recipient->locale, + function () use ($creator, $resource) { + return __('{0} commented on {1}', $creator->profile->first_name, $resource->name); + } + ); $body = [ 'creator' => $creator, 'comment' => $comment, @@ -130,6 +137,6 @@ private function createCommentAddEmail(User $user, User $creator, Resource $reso 'title' => $subject, ]; - return new Email($user->username, $subject, $data, self::TEMPLATE); + return new Email($recipient->username, $subject, $data, self::TEMPLATE); } } diff --git a/src/Notification/Email/Redactor/Group/GroupDeleteEmailRedactor.php b/src/Notification/Email/Redactor/Group/GroupDeleteEmailRedactor.php index a2b7b90703..5807c247c2 100644 --- a/src/Notification/Email/Redactor/Group/GroupDeleteEmailRedactor.php +++ b/src/Notification/Email/Redactor/Group/GroupDeleteEmailRedactor.php @@ -28,6 +28,7 @@ use Cake\Event\Event; use Cake\ORM\TableRegistry; use Cake\Utility\Hash; +use Passbolt\Locale\Service\LocaleService; class GroupDeleteEmailRedactor implements SubscribedEmailRedactorInterface { @@ -75,31 +76,35 @@ public function onSubscribedEvent(Event $event): EmailCollection $admin = $this->usersTable->findFirstForEmail($deletedBy); $usersIds = Hash::extract($group->groups_users, '{n}.user_id'); - $userNames = $this->usersTable->find()->select(['id', 'username'])->where(['id IN' => $usersIds])->all(); - $userNames = Hash::combine($userNames->toArray(), '{n}.id', '{n}.username'); + // Don't send notification if user is the one who deleted the group + $users = $this->usersTable->find('locale') + ->where(['Users.id IN' => $usersIds]) + ->where(['Users.id !=' => $deletedBy]); - foreach ($usersIds as $userId) { - // Don't send notification if user is the one who deleted the group - if ($userId === $deletedBy) { - continue; - } - $emailCollection->addEmail($this->createGroupDeleteEmail($userNames[$userId], $admin, $group)); + foreach ($users as $user) { + $email = $this->createGroupDeleteEmail($user, $admin, $group); + $emailCollection->addEmail($email); } return $emailCollection; } /** - * @param string $emailRecipient Email recipient + * @param \App\Model\Entity\User $recipient Email recipient * @param \App\Model\Entity\User $admin Admin * @param \App\Model\Entity\Group $group Group * @return \App\Notification\Email\Email */ - private function createGroupDeleteEmail(string $emailRecipient, User $admin, Group $group): Email + private function createGroupDeleteEmail(User $recipient, User $admin, Group $group): Email { - $subject = __('{0} deleted the group {1}', $admin->profile->first_name, $group->name); + $subject = (new LocaleService())->translateString( + $recipient->locale, + function () use ($admin, $group) { + return __('{0} deleted the group {1}', $admin->profile->first_name, $group->name); + } + ); $data = ['body' => ['admin' => $admin, 'group' => $group], 'title' => $subject]; - return new Email($emailRecipient, $subject, $data, self::TEMPLATE); + return new Email($recipient->username, $subject, $data, self::TEMPLATE); } } diff --git a/src/Notification/Email/Redactor/Group/GroupUpdateAdminSummaryEmailRedactor.php b/src/Notification/Email/Redactor/Group/GroupUpdateAdminSummaryEmailRedactor.php index af610825f6..f71ad1d5f3 100644 --- a/src/Notification/Email/Redactor/Group/GroupUpdateAdminSummaryEmailRedactor.php +++ b/src/Notification/Email/Redactor/Group/GroupUpdateAdminSummaryEmailRedactor.php @@ -28,6 +28,7 @@ use Cake\Event\Event; use Cake\ORM\TableRegistry; use Cake\Utility\Hash; +use Passbolt\Locale\Service\LocaleService; class GroupUpdateAdminSummaryEmailRedactor implements SubscribedEmailRedactorInterface { @@ -113,7 +114,7 @@ public function onSubscribedEvent(Event $event): EmailCollection // Send the email to all the group managers. foreach ($groupManagers as $groupManager) { $emailCollection->addEmail($this->createSummaryEmail( - $groupManager->username, + $groupManager, $group, $this->_getSummaryUser($addedUsersIds), // Retrieve the user information corresponding to the users impacted by the changes. @@ -128,7 +129,7 @@ public function onSubscribedEvent(Event $event): EmailCollection } /** - * @param string $emailRecipient Email recipient + * @param \App\Model\Entity\User $recipient User recipient * @param \App\Model\Entity\Group $group Group * @param array $addedUsers List of added users * @param array $updatedUsers List of updated users @@ -138,7 +139,7 @@ public function onSubscribedEvent(Event $event): EmailCollection * @return \App\Notification\Email\Email */ private function createSummaryEmail( - string $emailRecipient, + User $recipient, Group $group, array $addedUsers, array $updatedUsers, @@ -146,7 +147,12 @@ private function createSummaryEmail( array $whoIsAdmin, User $modifiedBy ): Email { - $subject = __('{0} updated the group {1}', $modifiedBy->profile->first_name, $group->name); + $subject = (new LocaleService())->translateString( + $recipient->locale, + function () use ($modifiedBy, $group) { + return __('{0} updated the group {1}', $modifiedBy->profile->first_name, $group->name); + } + ); $data = [ 'body' => [ 'admin' => $modifiedBy, @@ -159,7 +165,7 @@ private function createSummaryEmail( 'title' => $subject, ]; - return new Email($emailRecipient, $subject, $data, self::TEMPLATE); + return new Email($recipient->username, $subject, $data, self::TEMPLATE); } /** @@ -190,7 +196,7 @@ private function _getSummaryUser(array $usersIds = []): array */ private function getGroupManagers(Group $group, array $excludeUsersIds): array { - return $this->usersTable->find() + return $this->usersTable->find('locale') ->select(['Users.username']) ->innerJoinWith('GroupsUsers') ->where( diff --git a/src/Notification/Email/Redactor/Group/GroupUserAddEmailRedactor.php b/src/Notification/Email/Redactor/Group/GroupUserAddEmailRedactor.php index 4cadffed91..e059d16133 100644 --- a/src/Notification/Email/Redactor/Group/GroupUserAddEmailRedactor.php +++ b/src/Notification/Email/Redactor/Group/GroupUserAddEmailRedactor.php @@ -27,8 +27,10 @@ use App\Notification\Email\SubscribedEmailRedactorTrait; use App\Service\Groups\GroupsUpdateService; use Cake\Event\Event; +use Cake\ORM\Query; use Cake\ORM\TableRegistry; use Cake\Utility\Hash; +use Passbolt\Locale\Service\LocaleService; class GroupUserAddEmailRedactor implements SubscribedEmailRedactorInterface { @@ -94,16 +96,14 @@ public function onSubscribedEvent(Event $event): EmailCollection } /** - * Return a list of user ids + * Return a list of recipients * * @param array $userIds List of user ids - * @return \Cake\Datasource\ResultSetInterface + * @return \Cake\ORM\Query */ - private function getUserNames(array $userIds) + private function getRecipients(array $userIds): Query { - return $this->usersTable->find() - ->select(['id', 'username']) - ->where(['id IN' => $userIds])->all(); + return $this->usersTable->find('locale')->where(['Users.id IN' => $userIds]); } /** @@ -115,16 +115,17 @@ private function createGroupCreatedEmail(Group $group) $emails = []; $admin = $this->usersTable->findFirstForEmail($group->created_by); $userIds = Hash::extract($group->groups_users, '{n}.user_id'); - $userNames = $this->getUserNames($userIds); - $userNames = Hash::combine($userNames->toArray(), '{n}.id', '{n}.username'); + // Don't send notification if the user added themselves + $recipients = $this->getRecipients($userIds)->where([ + 'Users.id !=' => $group->created_by, + ])->all(); foreach ($group->groups_users as $group_user) { - // Don't send notification if the user added added themselves if ($group_user->user_id === $group->created_by) { continue; } - $recipient = $userNames[$group_user->user_id]; + $recipient = $recipients->firstMatch(['id' => $group_user->user_id]); $emails[] = $this->createGroupUserAddEmail($recipient, $admin, $group, $group_user->is_admin); } @@ -149,29 +150,34 @@ public function createGroupUserAddedUpdateEmails(Group $group, array $addedGroup // Retrieve the users to send an email to. $usersIds = Hash::extract($addedGroupsUsers, '{n}.user_id'); - $users = $this->getUserNames($usersIds)->combine('id', 'username'); + $users = $this->getRecipients($usersIds); $whoIsAdmin = Hash::combine($addedGroupsUsers, '{n}.user_id', '{n}.is_admin'); - foreach ($users as $userId => $userName) { - $isAdmin = isset($whoIsAdmin[$userId]) && $whoIsAdmin[$userId]; - $emails[] = $this->createGroupUserAddEmail($userName, $modifiedBy, $group, $isAdmin); + foreach ($users as $user) { + $isAdmin = isset($whoIsAdmin[$user->id]) && $whoIsAdmin[$user->id]; + $emails[] = $this->createGroupUserAddEmail($user, $modifiedBy, $group, $isAdmin); } return $emails; } /** - * @param string $recipient Email recipient + * @param \App\Model\Entity\User $recipient User recipient * @param \App\Model\Entity\User $admin Admin * @param \App\Model\Entity\Group $group Group * @param bool $isAdmin Is user admin * @return \App\Notification\Email\Email */ - private function createGroupUserAddEmail(string $recipient, User $admin, Group $group, bool $isAdmin): Email + private function createGroupUserAddEmail(User $recipient, User $admin, Group $group, bool $isAdmin): Email { - $subject = __('{0} added you to the group {1}', $admin->profile->first_name, $group->name); + $subject = (new LocaleService())->translateString( + $recipient->locale, + function () use ($admin, $group) { + return __('{0} added you to the group {1}', $admin->profile->first_name, $group->name); + } + ); $data = ['body' => ['isAdmin' => $isAdmin, 'admin' => $admin, 'group' => $group], 'title' => $subject]; - return new Email($recipient, $subject, $data, self::TEMPLATE); + return new Email($recipient->username, $subject, $data, self::TEMPLATE); } } diff --git a/src/Notification/Email/Redactor/Group/GroupUserAddRequestEmailRedactor.php b/src/Notification/Email/Redactor/Group/GroupUserAddRequestEmailRedactor.php index d9f20c142e..95fe81fbd6 100644 --- a/src/Notification/Email/Redactor/Group/GroupUserAddRequestEmailRedactor.php +++ b/src/Notification/Email/Redactor/Group/GroupUserAddRequestEmailRedactor.php @@ -25,9 +25,10 @@ use App\Notification\Email\EmailCollection; use App\Notification\Email\SubscribedEmailRedactorInterface; use App\Notification\Email\SubscribedEmailRedactorTrait; -use Cake\Datasource\ResultSetInterface; use Cake\Event\Event; +use Cake\ORM\Query; use Cake\ORM\TableRegistry; +use Passbolt\Locale\Service\LocaleService; class GroupUserAddRequestEmailRedactor implements SubscribedEmailRedactorInterface { @@ -93,7 +94,7 @@ public function onSubscribedEvent(Event $event): EmailCollection // Send to all group managers. foreach ($groupManagers as $groupManager) { $emailCollection->addEmail( - $this->createGroupUserAddEmail($groupManager->user->username, $admin, $group, $requestedGroupUsers) + $this->createGroupUserAddEmail($groupManager->user, $admin, $group, $requestedGroupUsers) ); } @@ -102,33 +103,42 @@ public function onSubscribedEvent(Event $event): EmailCollection /** * @param string $groupId Group for which to get group managers - * @return \Cake\Datasource\ResultSetInterface + * @return \Cake\ORM\Query */ - private function getGroupManagers(string $groupId): ResultSetInterface + private function getGroupManagers(string $groupId): Query { return $this->groupsUsersTable->find() - ->where(['group_id' => $groupId, 'is_admin' => true]) - ->contain(['Users']) - ->all(); + ->where([ + $this->groupsUsersTable->aliasField('group_id') => $groupId, + $this->groupsUsersTable->aliasField('is_admin') => true, + ]) + ->contain('Users', function (Query $q) { + return $q->find('locale'); + }); } /** - * @param string $recipient Email of the group manager to send the notification to + * @param \App\Model\Entity\User $recipient User of the group manager to send the notification to * @param \App\Model\Entity\User $admin the admin that requested the action * @param \App\Model\Entity\Group $group the group on which to add groupUsers * @param array $groupUsers the list of groupUsers entity to request to add * @return \App\Notification\Email\Email */ - private function createGroupUserAddEmail(string $recipient, User $admin, Group $group, array $groupUsers): Email + private function createGroupUserAddEmail(User $recipient, User $admin, Group $group, array $groupUsers): Email { - $subject = __('{0} requested you to add members to {1}', $admin->profile->first_name, $group->name); + $subject = (new LocaleService())->translateString( + $recipient->locale, + function () use ($admin, $group) { + return __('{0} requested you to add members to {1}', $admin->profile->first_name, $group->name); + } + ); $data = ['body' => [ 'admin' => $admin, 'group' => $group, 'groupUsers' => $groupUsers, ], 'title' => $subject]; - return new Email($recipient, $subject, $data, self::TEMPLATE); + return new Email($recipient->username, $subject, $data, self::TEMPLATE); } /** diff --git a/src/Notification/Email/Redactor/Group/GroupUserDeleteEmailRedactor.php b/src/Notification/Email/Redactor/Group/GroupUserDeleteEmailRedactor.php index db3a272143..dc4f8dd0d8 100644 --- a/src/Notification/Email/Redactor/Group/GroupUserDeleteEmailRedactor.php +++ b/src/Notification/Email/Redactor/Group/GroupUserDeleteEmailRedactor.php @@ -28,6 +28,7 @@ use Cake\Event\Event; use Cake\ORM\TableRegistry; use Cake\Utility\Hash; +use Passbolt\Locale\Service\LocaleService; class GroupUserDeleteEmailRedactor implements SubscribedEmailRedactorInterface { @@ -101,29 +102,31 @@ public function createGroupUserAddedUpdateEmails(Group $group, array $removedGro // Retrieve the users to send an email to. $usersIds = Hash::extract($removedGroupsUsers, '{n}.user_id'); - $users = $this->usersTable->find() - ->select(['id', 'username']) - ->where(['id IN' => $usersIds]) - ->combine('id', 'username'); + $users = $this->usersTable->find('locale')->where(['Users.id IN' => $usersIds]); - foreach ($users as $userId => $userName) { - $emails[] = $this->createGroupUserDeleteEmail($userName, $modifiedBy, $group); + foreach ($users as $user) { + $emails[] = $this->createGroupUserDeleteEmail($user, $modifiedBy, $group); } return $emails; } /** - * @param string $emailRecipient Email recipient + * @param \App\Model\Entity\User $recipient User recipient * @param \App\Model\Entity\User $admin Admin * @param \App\Model\Entity\Group $group Group * @return \App\Notification\Email\Email */ - private function createGroupUserDeleteEmail(string $emailRecipient, User $admin, Group $group) + private function createGroupUserDeleteEmail(User $recipient, User $admin, Group $group): Email { - $subject = __('{0} removed you from the group {1}', $admin->profile->first_name, $group->name); + $subject = (new LocaleService())->translateString( + $recipient->locale, + function () use ($admin, $group) { + return __('{0} removed you from the group {1}', $admin->profile->first_name, $group->name); + } + ); $data = ['body' => ['admin' => $admin, 'group' => $group], 'title' => $subject]; - return new Email($emailRecipient, $subject, $data, self::TEMPLATE); + return new Email($recipient->username, $subject, $data, self::TEMPLATE); } } diff --git a/src/Notification/Email/Redactor/Group/GroupUserUpdateEmailRedactor.php b/src/Notification/Email/Redactor/Group/GroupUserUpdateEmailRedactor.php index a1d595a3d8..fe18f4f8f7 100644 --- a/src/Notification/Email/Redactor/Group/GroupUserUpdateEmailRedactor.php +++ b/src/Notification/Email/Redactor/Group/GroupUserUpdateEmailRedactor.php @@ -28,6 +28,7 @@ use Cake\Event\Event; use Cake\ORM\TableRegistry; use Cake\Utility\Hash; +use Passbolt\Locale\Service\LocaleService; class GroupUserUpdateEmailRedactor implements SubscribedEmailRedactorInterface { @@ -97,16 +98,13 @@ public function createUpdateMembershipGroupUpdateEmails(array $updatedGroupsUser { // Retrieve the users to send an email to. $usersIds = Hash::extract($updatedGroupsUsers, '{n}.user_id'); - $users = $this->usersTable->find() - ->select(['id', 'username']) - ->where(['id IN' => $usersIds]) - ->combine('id', 'username'); + $users = $this->usersTable->find('locale')->where(['Users.id IN' => $usersIds]); $whoIsAdmin = Hash::combine($updatedGroupsUsers, '{n}.user_id', '{n}.is_admin'); $emails = []; - foreach ($users as $userId => $name) { - $isAdmin = isset($whoIsAdmin[$userId]) && $whoIsAdmin[$userId]; - $emails[] = $this->createUpdateMembershipGroupUpdateEmail($name, $isAdmin, $modifiedBy, $group); + foreach ($users as $user) { + $isAdmin = isset($whoIsAdmin[$user->id]) && $whoIsAdmin[$user->id]; + $emails[] = $this->createUpdateMembershipGroupUpdateEmail($user, $isAdmin, $modifiedBy, $group); } return $emails; @@ -115,21 +113,30 @@ public function createUpdateMembershipGroupUpdateEmails(array $updatedGroupsUser /** * Create group update email for the user whom the membership has changed * - * @param string $recipient Email recipient + * @param \App\Model\Entity\User $recipient User recipient * @param bool $isAdmin Is user admin * @param \App\Model\Entity\User $modifiedBy person who did the change * @param \App\Model\Entity\Group $group Group the affected group * @return \App\Notification\Email\Email */ public function createUpdateMembershipGroupUpdateEmail( - string $recipient, + User $recipient, bool $isAdmin, User $modifiedBy, Group $group ): Email { - $subject = __('{0} updated your membership in the group {1}', $modifiedBy->profile->first_name, $group->name); + $subject = (new LocaleService())->translateString( + $recipient->locale, + function () use ($modifiedBy, $group) { + return __( + '{0} updated your membership in the group {1}', + $modifiedBy->profile->first_name, + $group->name + ); + } + ); $data = ['body' => ['admin' => $modifiedBy, 'group' => $group, 'isAdmin' => $isAdmin], 'title' => $subject]; - return new Email($recipient, $subject, $data, self::TEMPLATE); + return new Email($recipient->username, $subject, $data, self::TEMPLATE); } } diff --git a/src/Notification/Email/Redactor/Recovery/AccountRecoveryEmailRedactor.php b/src/Notification/Email/Redactor/Recovery/AccountRecoveryEmailRedactor.php index 572e568f15..5a75817df1 100644 --- a/src/Notification/Email/Redactor/Recovery/AccountRecoveryEmailRedactor.php +++ b/src/Notification/Email/Redactor/Recovery/AccountRecoveryEmailRedactor.php @@ -25,6 +25,8 @@ use App\Notification\Email\SubscribedEmailRedactorInterface; use App\Notification\Email\SubscribedEmailRedactorTrait; use Cake\Event\Event; +use Passbolt\Locale\Service\GetUserLocaleService; +use Passbolt\Locale\Service\LocaleService; class AccountRecoveryEmailRedactor implements SubscribedEmailRedactorInterface { @@ -67,9 +69,16 @@ public function onSubscribedEvent(Event $event): EmailCollection * @param \App\Model\Entity\AuthenticationToken $token Token for recovery * @return \App\Notification\Email\Email */ - private function createAccountRecoveryEmail(User $user, AuthenticationToken $token) + private function createAccountRecoveryEmail(User $user, AuthenticationToken $token): Email { - $subject = __('Your account recovery, {0}!', $user->profile->first_name); + $locale = (new GetUserLocaleService())->getLocale($user->username); + $subject = (new LocaleService())->translateString( + $locale, + function () use ($user) { + return __('Your account recovery, {0}!', $user->profile->first_name); + } + ); + $data = ['body' => ['user' => $user, 'token' => $token], 'title' => $subject]; return new Email($user->username, $subject, $data, self::TEMPLATE); diff --git a/src/Notification/Email/Redactor/Resource/ResourceCreateEmailRedactor.php b/src/Notification/Email/Redactor/Resource/ResourceCreateEmailRedactor.php index 5350585a3b..db506f17be 100644 --- a/src/Notification/Email/Redactor/Resource/ResourceCreateEmailRedactor.php +++ b/src/Notification/Email/Redactor/Resource/ResourceCreateEmailRedactor.php @@ -17,15 +17,16 @@ namespace App\Notification\Email\Redactor\Resource; -use App\Controller\Resources\ResourcesAddController; use App\Model\Entity\Resource; -use App\Model\Table\UsersTable; +use App\Model\Entity\User; use App\Notification\Email\Email; use App\Notification\Email\EmailCollection; use App\Notification\Email\SubscribedEmailRedactorInterface; use App\Notification\Email\SubscribedEmailRedactorTrait; +use App\Service\Resources\ResourcesAddService; use Cake\Event\Event; -use Cake\ORM\TableRegistry; +use Passbolt\Locale\Service\GetUserLocaleService; +use Passbolt\Locale\Service\LocaleService; class ResourceCreateEmailRedactor implements SubscribedEmailRedactorInterface { @@ -33,20 +34,12 @@ class ResourceCreateEmailRedactor implements SubscribedEmailRedactorInterface public const TEMPLATE = 'LU/resource_create'; - /** - * @var \App\Model\Table\UsersTable - */ - private $usersTable; - /** * @param array|null $config Configuration for the redactor - * @param \App\Model\Table\UsersTable $usersTable Users Table */ - public function __construct(?array $config = [], ?UsersTable $usersTable = null) + public function __construct(?array $config = []) { $this->setConfig($config); - /** @phpstan-ignore-next-line */ - $this->usersTable = $usersTable ?? TableRegistry::getTableLocator()->get('Users'); } /** @@ -57,7 +50,7 @@ public function __construct(?array $config = [], ?UsersTable $usersTable = null) public function getSubscribedEvents(): array { return [ - ResourcesAddController::ADD_SUCCESS_EVENT_NAME, + ResourcesAddService::ADD_SUCCESS_EVENT_NAME, ]; } @@ -71,20 +64,28 @@ public function onSubscribedEvent(Event $event): EmailCollection /** @var Resource $resource */ $resource = $event->getData('resource'); + $user = $event->getData('user'); - $emailCollection->addEmail($this->createResourceCreateEmail($resource)); + $emailCollection->addEmail($this->createResourceCreateEmail($resource, $user)); return $emailCollection; } /** - * @param Resource $resource Resource + * @param Resource $resource Resource created. + * @param \App\Model\Entity\User $user User creating the resource. * @return \App\Notification\Email\Email */ - private function createResourceCreateEmail(Resource $resource) + private function createResourceCreateEmail(Resource $resource, User $user): Email { - $user = $this->usersTable->findFirstForEmail($resource->created_by); - $subject = __('You added the password {0}', $resource->name); + $locale = (new GetUserLocaleService())->getLocale($user->username); + $subject = (new LocaleService())->translateString( + $locale, + function () use ($resource) { + return __('You added the password {0}', $resource->name); + } + ); + $data = [ 'body' => [ 'user' => $user, diff --git a/src/Notification/Email/Redactor/Resource/ResourceDeleteEmailRedactor.php b/src/Notification/Email/Redactor/Resource/ResourceDeleteEmailRedactor.php index 43b837729e..0b20fc848d 100644 --- a/src/Notification/Email/Redactor/Resource/ResourceDeleteEmailRedactor.php +++ b/src/Notification/Email/Redactor/Resource/ResourceDeleteEmailRedactor.php @@ -27,6 +27,7 @@ use App\Notification\Email\SubscribedEmailRedactorTrait; use Cake\Event\Event; use Cake\ORM\TableRegistry; +use Passbolt\Locale\Service\LocaleService; class ResourceDeleteEmailRedactor implements SubscribedEmailRedactorInterface { @@ -74,35 +75,37 @@ public function onSubscribedEvent(Event $event): EmailCollection $resource = $event->getData('resource'); /** @var string $deletedBy */ $deletedBy = $event->getData('deletedBy'); - /** @var \Cake\Datasource\ResultSetInterface $users */ + /** @var \Cake\ORM\Query $users */ $users = $event->getData('users'); - // if there is nobody or just one user, give it up - if (count($users) < 2) { + // if there is nobody, give it up. The deleter has already been removed from $users. + if ($users->count() < 1) { return $emailCollection; } $owner = $this->usersTable->findFirstForEmail($deletedBy); foreach ($users as $user) { - if ($user->id === $deletedBy) { - continue; - } - $emailCollection->addEmail($this->createDeleteEmail($user->username, $owner, $resource)); + $emailCollection->addEmail($this->createDeleteEmail($user, $owner, $resource)); } return $emailCollection; } /** - * @param string $emailRecipient Email of the recipient user + * @param \App\Model\Entity\User $recipient Email of the recipient user * @param \App\Model\Entity\User $owner User who executed the action * @param Resource $resource Resource * @return \App\Notification\Email\Email */ - private function createDeleteEmail(string $emailRecipient, User $owner, Resource $resource) + private function createDeleteEmail(User $recipient, User $owner, Resource $resource): Email { - $subject = __('{0} deleted the password {1}', $owner->profile->first_name, $resource->name); + $subject = (new LocaleService())->translateString( + $recipient->locale, + function () use ($owner, $resource) { + return __('{0} deleted the password {1}', $owner->profile->first_name, $resource->name); + } + ); $data = [ 'body' => [ 'user' => $owner, @@ -114,6 +117,6 @@ private function createDeleteEmail(string $emailRecipient, User $owner, Resource 'title' => $subject, ]; - return new Email($emailRecipient, $subject, $data, self::TEMPLATE); + return new Email($recipient->username, $subject, $data, self::TEMPLATE); } } diff --git a/src/Notification/Email/Redactor/Resource/ResourceUpdateEmailRedactor.php b/src/Notification/Email/Redactor/Resource/ResourceUpdateEmailRedactor.php index 6c74cffe68..79a9dc9cbc 100644 --- a/src/Notification/Email/Redactor/Resource/ResourceUpdateEmailRedactor.php +++ b/src/Notification/Email/Redactor/Resource/ResourceUpdateEmailRedactor.php @@ -28,6 +28,7 @@ use App\Service\Resources\ResourcesUpdateService; use Cake\Event\Event; use Cake\ORM\TableRegistry; +use Passbolt\Locale\Service\LocaleService; class ResourceUpdateEmailRedactor implements SubscribedEmailRedactorInterface { @@ -76,26 +77,31 @@ public function onSubscribedEvent(Event $event): EmailCollection // Get the users that can access this resource $options = ['contain' => ['role'], 'filter' => ['has-access' => [$resource->id]]]; - $users = $this->usersTable->findIndex(Role::USER, $options)->all(); + $users = $this->usersTable->findIndex(Role::USER, $options)->find('locale'); $owner = $this->usersTable->findFirstForEmail($resource->modified_by); // Send emails to everybody that can see the resource foreach ($users as $user) { - $emailCollection->addEmail($this->createUpdateEmail($user->username, $owner, $resource)); + $emailCollection->addEmail($this->createUpdateEmail($user, $owner, $resource)); } return $emailCollection; } /** - * @param string $emailRecipient Email of the recipient user + * @param \App\Model\Entity\User $recipient Email of the recipient user * @param \App\Model\Entity\User $owner User who executed the action * @param Resource $resource Resource * @return \App\Notification\Email\Email */ - private function createUpdateEmail(string $emailRecipient, User $owner, Resource $resource) + private function createUpdateEmail(User $recipient, User $owner, Resource $resource): Email { - $subject = __('{0} edited the password {1}', $owner->profile->first_name, $resource->name); + $subject = (new LocaleService())->translateString( + $recipient->locale, + function () use ($owner, $resource) { + return __('{0} edited the password {1}', $owner->profile->first_name, $resource->name); + } + ); $data = [ 'body' => [ 'user' => $owner, @@ -107,6 +113,6 @@ private function createUpdateEmail(string $emailRecipient, User $owner, Resource ], 'title' => $subject, ]; - return new Email($emailRecipient, $subject, $data, self::TEMPLATE); + return new Email($recipient->username, $subject, $data, self::TEMPLATE); } } diff --git a/src/Notification/Email/Redactor/Share/ShareEmailRedactor.php b/src/Notification/Email/Redactor/Share/ShareEmailRedactor.php index 443b0d0d14..60d1163c03 100644 --- a/src/Notification/Email/Redactor/Share/ShareEmailRedactor.php +++ b/src/Notification/Email/Redactor/Share/ShareEmailRedactor.php @@ -28,6 +28,7 @@ use Cake\Event\Event; use Cake\ORM\TableRegistry; use Cake\Utility\Hash; +use Passbolt\Locale\Service\LocaleService; class ShareEmailRedactor implements SubscribedEmailRedactorInterface { @@ -82,12 +83,12 @@ public function onSubscribedEvent(Event $event): EmailCollection if (!empty($userIds)) { // Get the details of whoever did the changes $owner = $this->usersTable->findFirstForEmail($ownerId); - $users = Hash::combine($this->getUserFromIds($userIds), '{n}.id', '{n}.username'); + $users = $this->getUserFromIds($userIds); $secrets = Hash::combine($changes['secrets'], '{n}.user_id', '{n}.data'); - foreach ($users as $userId => $userName) { + foreach ($users as $user) { $emailCollection->addEmail( - $this->createShareEmail($userName, $owner, $resource, $secrets[$userId]) + $this->createShareEmail($user, $owner, $resource, $secrets[$user->id]) ); } } @@ -99,27 +100,28 @@ public function onSubscribedEvent(Event $event): EmailCollection * Return a collection of users from a list of user ids * * @param array $userIds A list of user ids - * @return array + * @return \Cake\ORM\Query */ private function getUserFromIds(array $userIds) { - return $this->usersTable->find() - ->select(['id', 'username']) - ->where(['id IN' => $userIds]) - ->all() - ->toArray(); + return $this->usersTable->find('locale')->where(['Users.id IN' => $userIds]); } /** - * @param string $emailRecipient Email of the user to send email to + * @param \App\Model\Entity\User $recipient User to send email to * @param \App\Model\Entity\User $owner Owner * @param Resource $resource Resource * @param string $secret Secret * @return \App\Notification\Email\Email */ - private function createShareEmail(string $emailRecipient, User $owner, Resource $resource, string $secret) + private function createShareEmail(User $recipient, User $owner, Resource $resource, string $secret): Email { - $subject = __('{0} shared the password {1}', $owner->profile->first_name, $resource->name); + $subject = (new LocaleService())->translateString( + $recipient->locale, + function () use ($owner, $resource) { + return __('{0} shared the password {1}', $owner->profile->first_name, $resource->name); + } + ); $data = [ 'body' => [ @@ -134,6 +136,6 @@ private function createShareEmail(string $emailRecipient, User $owner, Resource 'title' => $subject, ]; - return new Email($emailRecipient, $subject, $data, self::TEMPLATE); + return new Email($recipient->username, $subject, $data, self::TEMPLATE); } } diff --git a/src/Notification/Email/Redactor/User/UserDeleteEmailRedactor.php b/src/Notification/Email/Redactor/User/UserDeleteEmailRedactor.php index f795fd0336..5c44fc6a24 100644 --- a/src/Notification/Email/Redactor/User/UserDeleteEmailRedactor.php +++ b/src/Notification/Email/Redactor/User/UserDeleteEmailRedactor.php @@ -19,47 +19,32 @@ use App\Controller\Users\UsersDeleteController; use App\Model\Entity\User; -use App\Model\Table\GroupsUsersTable; -use App\Model\Table\UsersTable; use App\Notification\Email\Email; use App\Notification\Email\EmailCollection; use App\Notification\Email\SubscribedEmailRedactorInterface; use App\Notification\Email\SubscribedEmailRedactorTrait; +use Cake\Datasource\ModelAwareTrait; use Cake\Event\Event; -use Cake\ORM\TableRegistry; +use Cake\ORM\Query; +use Passbolt\Locale\Service\LocaleService; +/** + * Class UserDeleteEmailRedactor + * + * @property \App\Model\Table\UsersTable $Users + */ class UserDeleteEmailRedactor implements SubscribedEmailRedactorInterface { + use ModelAwareTrait; use SubscribedEmailRedactorTrait; - /** - * @var \App\Model\Table\UsersTable - */ - private $usersTable; - - /** - * @var \App\Model\Table\GroupsUsersTable - */ - private $groupsUsersTable; - - /** - * @param \App\Model\Table\UsersTable|null $usersTable UsersTable - * @param \App\Model\Table\GroupsUsersTable|null $groupsUsersTable GroupsUsersTable - */ - public function __construct(?UsersTable $usersTable = null, ?GroupsUsersTable $groupsUsersTable = null) - { - /** @phpstan-ignore-next-line */ - $this->usersTable = $usersTable ?? TableRegistry::getTableLocator()->get('Users'); - /** @phpstan-ignore-next-line */ - $this->groupsUsersTable = $groupsUsersTable ?? TableRegistry::getTableLocator()->get('GroupsUsers'); - } - /** * @param \Cake\Event\Event $event User delete event * @return \App\Notification\Email\EmailCollection */ public function onSubscribedEvent(Event $event): EmailCollection { + $this->loadModel('Users'); $emailCollection = new EmailCollection(); $user = $event->getData('user'); @@ -70,16 +55,18 @@ public function onSubscribedEvent(Event $event): EmailCollection return $emailCollection; } - $deletedBy = $this->usersTable->findFirstForEmail($deletedById); - $groupManagers = $this->getGroupManagers($groupsIds); - - $usersToNotify = []; - foreach ($groupManagers as $groupManager) { - $usersToNotify[$groupManager->user->username][] = $groupManager->group; - } - - foreach ($usersToNotify as $username => $groups) { - $emailCollection->addEmail($this->createDeleteUserEmail($username, $user, $groups, $deletedBy)); + $deletedBy = $this->Users->findFirstForEmail($deletedById); + $recipients = $this->getRecipientsWithGroups($groupsIds); + + foreach ($recipients as $recipient) { + $groups = []; + foreach ($recipient->groups_users as $gu) { + if (isset($gu->group)) { + $groups[] = $gu->group; + } + } + $email = $this->createDeleteUserEmail($recipient, $user, $groups, $deletedBy); + $emailCollection->addEmail($email); } return $emailCollection; @@ -87,30 +74,45 @@ public function onSubscribedEvent(Event $event): EmailCollection /** * @param array $groupsIds Groups IDs - * @return \Cake\Datasource\ResultSetInterface + * @return \Cake\ORM\Query */ - private function getGroupManagers(array $groupsIds) + private function getRecipientsWithGroups(array $groupsIds): Query { - return $this->groupsUsersTable->find() - ->select() - ->contain(['Users', 'Groups']) - ->where(['group_id IN' => $groupsIds, 'is_admin' => 1]) - ->all(); + $filter = [ + 'Groups.id IN' => $groupsIds, + 'GroupsUsers.is_admin' => 1, + ]; + + // This is ugly CakePHP https://github.com/cakephp/cakephp/issues/15689 + return $this->Users->find('locale') + ->group($this->Users->aliasField('id')) + ->select($this->Users) + ->contain('GroupsUsers.Groups', function (Query $q) use ($filter) { + return $q->where($filter); + }) + ->innerJoinWith('GroupsUsers.Groups', function (Query $q) use ($filter) { + return $q->where($filter); + }); } /** - * @param string $recipient Email recipient + * @param \App\Model\Entity\User $recipient User recipient * @param \App\Model\Entity\User $user User * @param \App\Model\Entity\Group[] $groups Groups * @param \App\Model\Entity\User $deletedBy User admin who deleted the user * @return \App\Notification\Email\Email */ - private function createDeleteUserEmail(string $recipient, User $user, array $groups, User $deletedBy) + private function createDeleteUserEmail(User $recipient, User $user, array $groups, User $deletedBy): Email { - $subject = __('{0} deleted user {1}', $deletedBy->profile->first_name, $user->profile->first_name); + $subject = (new LocaleService())->translateString( + $recipient->locale, + function () use ($deletedBy, $user) { + return __('{0} deleted user {1}', $deletedBy->profile->first_name, $user->profile->first_name); + } + ); return new Email( - $recipient, + $recipient->username, $subject, ['body' => ['user' => $user, 'groups' => $groups, 'admin' => $deletedBy], 'title' => $subject], 'GM/user_delete' diff --git a/src/Notification/Email/Redactor/User/UserRegisterEmailRedactor.php b/src/Notification/Email/Redactor/User/UserRegisterEmailRedactor.php index 5a1ccaf65a..7d1bf96cee 100644 --- a/src/Notification/Email/Redactor/User/UserRegisterEmailRedactor.php +++ b/src/Notification/Email/Redactor/User/UserRegisterEmailRedactor.php @@ -27,6 +27,7 @@ use App\Notification\Email\SubscribedEmailRedactorTrait; use Cake\Event\Event; use Cake\ORM\TableRegistry; +use Passbolt\Locale\Service\LocaleService; class UserRegisterEmailRedactor implements SubscribedEmailRedactorInterface { @@ -76,7 +77,12 @@ public function onSubscribedEvent(Event $event): EmailCollection */ private function getSubject(User $user): string { - return __('Welcome to passbolt, {0}!', $user->profile->first_name); + return (new LocaleService())->translateString( + $user->locale, + function () use ($user) { + return __('Welcome to passbolt, {0}!', $user->profile->first_name); + } + ); } /** @@ -110,6 +116,7 @@ private function createEmailSelfRegister(User $user, AuthenticationToken $uac): private function createEmailAdminRegister(User $user, AuthenticationToken $uac, string $adminId): Email { $admin = $this->usersTable->findFirstForEmail($adminId); + $user = $this->usersTable->findFirstForEmail($user->id); $this->usersTable->loadInto($user, [ 'Profiles' => AvatarsTable::addContainAvatar(), diff --git a/src/Notification/Email/SubscribedEmailRedactorTrait.php b/src/Notification/Email/SubscribedEmailRedactorTrait.php index e1c0d998b0..b2720a0119 100644 --- a/src/Notification/Email/SubscribedEmailRedactorTrait.php +++ b/src/Notification/Email/SubscribedEmailRedactorTrait.php @@ -40,11 +40,11 @@ trait SubscribedEmailRedactorTrait private $_defaultConfig = []; /** - * @param null $key Configuration key to retrieve - * @param null $default Default value + * @param string|null $key Configuration key to retrieve + * @param mixed $default Default value * @return mixed */ - public function getConfig($key = null, $default = null) + public function getConfig(?string $key = null, $default = null) { return $this->parentGetConfig($key) ?? EmailNotificationSettings::get($key); } diff --git a/src/Notification/EmailDigest/DigestRegister/ResourceDigests.php b/src/Notification/EmailDigest/DigestRegister/ResourceDigests.php index 92a5445ea9..222a5992a4 100644 --- a/src/Notification/EmailDigest/DigestRegister/ResourceDigests.php +++ b/src/Notification/EmailDigest/DigestRegister/ResourceDigests.php @@ -41,13 +41,13 @@ class ResourceDigests implements EventListenerInterface public const RESOURCE_SHARE_MULTIPLE_TEMPLATE = 'LU/resources_share'; /** - * @param \Passbolt\EmailDigest\Utility\Digest\DigestsPool $digestsCollection Instance of the marshaller + * @param \Passbolt\EmailDigest\Utility\Digest\DigestsPool $digestsPool Instance of the marshaller * @return void */ - public function addDigestsPool(DigestsPool $digestsCollection) + public function addDigestsPool(DigestsPool $digestsPool) { - $digestsCollection->addDigest($this->createResourceShareDigest()); - $digestsCollection->addDigest($this->createResourceChangesDigest()); + $digestsPool->addDigest($this->createResourceShareDigest()); + $digestsPool->addDigest($this->createResourceChangesDigest()); } /** diff --git a/src/Service/AuthenticationTokens/AuthenticationTokensHealthcheckService.php b/src/Service/AuthenticationTokens/AuthenticationTokensHealthcheckService.php index 3ed1a60525..7f42615c44 100644 --- a/src/Service/AuthenticationTokens/AuthenticationTokensHealthcheckService.php +++ b/src/Service/AuthenticationTokens/AuthenticationTokensHealthcheckService.php @@ -31,18 +31,18 @@ class AuthenticationTokensHealthcheckService extends AbstractHealthcheckService /** * @var \App\Model\Table\AuthenticationTokensTable */ - private $table; + private $AuthenticationTokens; /** * AuthenticationTokens Healthcheck constructor. * - * @param \App\Model\Table\AuthenticationTokensTable|null $table secret table + * @param \App\Model\Table\AuthenticationTokensTable|null $table table */ public function __construct(?AuthenticationTokensTable $table = null) { parent::__construct(self::NAME, self::CATEGORY); /** @phpstan-ignore-next-line */ - $this->table = $table ?? TableRegistry::getTableLocator()->get('AuthenticationTokens'); + $this->AuthenticationTokens = $table ?? TableRegistry::getTableLocator()->get('AuthenticationTokens'); $this->checks[self::CHECK_VALIDATES] = $this->healthcheckFactory(self::CHECK_VALIDATES, true); } @@ -51,7 +51,7 @@ public function __construct(?AuthenticationTokensTable $table = null) */ public function check(): array { - $records = $this->table->find()->all(); + $records = $this->AuthenticationTokens->find()->all(); foreach ($records as $i => $record) { $this->canValidate($record); @@ -68,7 +68,7 @@ public function check(): array */ private function canValidate(AuthenticationToken $authenticationToken): void { - $copy = $this->table->newEntity($authenticationToken->toArray()); + $copy = $this->AuthenticationTokens->newEntity($authenticationToken->toArray()); $error = $copy->getErrors(); if (count($error)) { diff --git a/src/Service/AuthenticationTokens/AuthenticationTokensSessionService.php b/src/Service/AuthenticationTokens/AuthenticationTokensSessionService.php new file mode 100644 index 0000000000..d6d241cdb9 --- /dev/null +++ b/src/Service/AuthenticationTokens/AuthenticationTokensSessionService.php @@ -0,0 +1,58 @@ +getHashedSessionId(); + + if (is_string($sessionIdentifier)) { + $hashedSessionId = $this->hash($sessionIdentifier); + } elseif ($sessionIdentifier instanceof AuthenticationToken) { + $hashedSessionId = $sessionIdentifier->getHashedSessionId(); + } else { + return false; + } + + if (empty($hashedSessionIdToCheck) || empty($hashedSessionId)) { + return false; + } + + return $hashedSessionIdToCheck === $hashedSessionId; + } + + /** + * @param string $string String to hash + * @return false|string + */ + public function hash(string $string) + { + return hash(self::HASH_ALGO, $string); + } +} diff --git a/src/Service/Avatars/AvatarsCacheService.php b/src/Service/Avatars/AvatarsCacheService.php index c8c45f11fc..386ed0061a 100644 --- a/src/Service/Avatars/AvatarsCacheService.php +++ b/src/Service/Avatars/AvatarsCacheService.php @@ -47,7 +47,7 @@ public function __construct(AvatarsTable $AvatarsTable) /** * @param string|null $id Avatar id - * @param string $format Avaar format + * @param string $format Avatar format * @return \Laminas\Diactoros\Stream */ public function readSteamFromId(?string $id, string $format): Stream @@ -73,7 +73,7 @@ public function getAvatarFileName(Avatar $avatar, ?string $format = null): strin { if (empty($avatar->data)) { return $this->getFallBackFileName($format); - } elseif ($format === AvatarsTable::FORMAT_SMALL) { + } elseif ($format === AvatarsConfigurationService::FORMAT_SMALL) { return $this->getSmallAvatarFileName($avatar); } else { return $this->getMediumAvatarFileName($avatar); @@ -88,12 +88,12 @@ public function getAvatarFileName(Avatar $avatar, ?string $format = null): strin */ public function storeInCache(Avatar $avatar): void { - if (empty($avatar->data)) { + $data = $avatar->getDataInStringFormat(); + + if (empty($data)) { return; } - $data = $avatar->data; - try { $smallImage = AvatarProcessing::resizeAndCrop( $data, @@ -107,9 +107,22 @@ public function storeInCache(Avatar $avatar): void return; } + $this->writeAvatarDataInFilesystem($this->getMediumAvatarFileName($avatar), $data, $avatar); + $this->writeAvatarDataInFilesystem($this->getSmallAvatarFileName($avatar), $smallImage, $avatar); + } + + /** + * Write avatar on file system as non-executable. + * + * @param string $filename Name of the target file + * @param string $data Image data + * @param \App\Model\Entity\Avatar $avatar Avatar + * @return void + */ + protected function writeAvatarDataInFilesystem(string $filename, string $data, Avatar $avatar): void + { try { - $this->Avatars->getFilesystem()->write($this->getMediumAvatarFileName($avatar), $data); - $this->Avatars->getFilesystem()->write($this->getSmallAvatarFileName($avatar), $smallImage); + $this->Avatars->getFilesystem()->write($filename, $data); } catch (\Throwable $e) { Log::error('Error while saving cache avatar with ID ' . $avatar->id . '.'); Log::error($e->getMessage()); @@ -126,23 +139,23 @@ public function storeInCache(Avatar $avatar): void * @param string $format The format to recover. * @return \Laminas\Diactoros\Stream The full path to the filename. */ - protected function readStreamInCache(Avatar $avatar, string $format = AvatarsTable::FORMAT_SMALL): Stream - { + protected function readStreamInCache( + Avatar $avatar, + string $format = AvatarsConfigurationService::FORMAT_SMALL + ): Stream { $fileName = $this->getAvatarFileName($avatar, $format); + try { + $stream = $this->Avatars->getFilesystem()->readStream($fileName); + } catch (\Throwable $e) { + $stream = null; + } - if (!$this->Avatars->getFilesystem()->fileExists($fileName)) { + if (empty($stream)) { try { $this->storeInCache($avatar); $stream = $this->Avatars->getFilesystem()->readStream($fileName); } catch (\Throwable $exception) { - Log::warning(__('Could not save the avatar in cache.')); - $stream = $this->getFallBackFileName($format); - } - } else { - try { - $stream = $this->Avatars->getFilesystem()->readStream($fileName); - } catch (\Throwable $exception) { - Log::warning(__('Could not read the avatar in cache.')); + Log::warning(__('Could not save the avatar in the {0} file.', $fileName)); $stream = $this->getFallBackFileName($format); } } @@ -177,7 +190,9 @@ public function getFallBackFileName(?string $format = null): string */ protected function getSmallAvatarFileName(Avatar $avatar): string { - return $this->getOrCreateAvatarDirectory($avatar) . AvatarsTable::FORMAT_SMALL . AvatarHelper::IMAGE_EXTENSION; + return $this->getOrCreateAvatarDirectory($avatar) + . AvatarsConfigurationService::FORMAT_SMALL + . AvatarHelper::IMAGE_EXTENSION; } /** @@ -186,7 +201,9 @@ protected function getSmallAvatarFileName(Avatar $avatar): string */ protected function getMediumAvatarFileName(Avatar $avatar): string { - return $this->getOrCreateAvatarDirectory($avatar) . AvatarsTable::FORMAT_MEDIUM . AvatarHelper::IMAGE_EXTENSION; + return $this->getOrCreateAvatarDirectory($avatar) + . AvatarsConfigurationService::FORMAT_MEDIUM + . AvatarHelper::IMAGE_EXTENSION; } /** @@ -216,6 +233,6 @@ protected function getOrCreateAvatarDirectory(Avatar $avatar): string */ protected function getDefaultFormat(): string { - return AvatarsTable::FORMAT_MEDIUM; + return AvatarsConfigurationService::FORMAT_MEDIUM; } } diff --git a/src/Service/Avatars/AvatarsConfigurationService.php b/src/Service/Avatars/AvatarsConfigurationService.php new file mode 100644 index 0000000000..c708d25973 --- /dev/null +++ b/src/Service/Avatars/AvatarsConfigurationService.php @@ -0,0 +1,73 @@ + [ + 'Avatar' => [ + self::FORMAT_MEDIUM => 'img' . DS . 'avatar' . DS . 'user_medium.png', + self::FORMAT_SMALL => 'img' . DS . 'avatar' . DS . 'user.png', + ], + ], + // Configure image versions on a per model base + 'imageSizes' => [ + 'Avatar' => [ + self::FORMAT_MEDIUM => [ + 'thumbnail' => [ + 'mode' => 'outbound', + 'width' => 200, + 'height' => 200, + ], + ], + self::FORMAT_SMALL => [ + 'thumbnail' => [ + 'mode' => 'outbound', + 'width' => 80, + 'height' => 80, + ], + 'crop' => [ + 'width' => 80, + 'height' => 80, + ], + ], + ], + ], + ]); + } +} diff --git a/src/Service/Avatars/AvatarsTransferService.php b/src/Service/Avatars/AvatarsTransferService.php index 9ceb9341a6..79751ae181 100644 --- a/src/Service/Avatars/AvatarsTransferService.php +++ b/src/Service/Avatars/AvatarsTransferService.php @@ -198,7 +198,8 @@ private function getCleanFilePath(string $path): string private function deleteFileStorage(EntityInterface $fileStorage, string $filePath): void { if ($this->FileStorage->delete($fileStorage) === false) { - $this->logError("The file storage with id {$fileStorage->id} could not be deleted."); + $id = $fileStorage->get('id'); + $this->logError("The file storage with id {$id} could not be deleted."); } try { diff --git a/src/Service/Groups/GroupsUpdateGroupUsersService.php b/src/Service/Groups/GroupsUpdateGroupUsersService.php index 764b3c0de4..d3f6395118 100644 --- a/src/Service/Groups/GroupsUpdateGroupUsersService.php +++ b/src/Service/Groups/GroupsUpdateGroupUsersService.php @@ -172,9 +172,9 @@ private function handleValidationErrors(array $errors = []): void * @param int $rowIndexRef The row index in the request data * @param string $groupId The target group * @param string $groupUserId The target group user - * @return \App\Model\Entity\GroupsUser + * @return \App\Model\Entity\GroupsUser|null */ - private function getGroupUser(int $rowIndexRef, string $groupId, string $groupUserId): GroupsUser + private function getGroupUser(int $rowIndexRef, string $groupId, string $groupUserId): ?GroupsUser { /** @var \App\Model\Entity\GroupsUser|null $groupUser */ $groupUser = $this->groupsUsersTable->findByIdAndGroupId($groupUserId, $groupId)->first(); diff --git a/src/Service/Permissions/PermissionsUpdatePermissionsService.php b/src/Service/Permissions/PermissionsUpdatePermissionsService.php index 0a208a55a7..cfe051f6e3 100644 --- a/src/Service/Permissions/PermissionsUpdatePermissionsService.php +++ b/src/Service/Permissions/PermissionsUpdatePermissionsService.php @@ -73,7 +73,7 @@ public function updatePermissions( string $aco, string $acoForeignkey, ?array $data = [] - ): ?array { + ): array { $addedPermissions = []; $updatedPermissions = []; $deletedPermissions = []; @@ -150,7 +150,7 @@ private function addPermission( * * @param array $errors The list of errors * @return void - * @throws \App\Error\Exception\ValidationException If the provided data does not validate. + * @throws \App\Error\Exception\CustomValidationException If the provided data does not validate. */ private function handleValidationErrors(array $errors = []): void { @@ -166,10 +166,10 @@ private function handleValidationErrors(array $errors = []): void * @param int $rowIndexRef The row index in the request data * @param string $acoForeignkey The target entity id * @param string $permissionId The permission identifier to retrieve. - * @return \App\Model\Entity\Permission + * @return \App\Model\Entity\Permission|null * @throws \App\Error\Exception\ValidationException If the permission does not exist. */ - private function getPermission(int $rowIndexRef, string $acoForeignkey, string $permissionId): Permission + private function getPermission(int $rowIndexRef, string $acoForeignkey, string $permissionId): ?Permission { /** @var \App\Model\Entity\Permission|null $permission */ $permission = $this->permissionsTable->findByIdAndAcoForeignKey($permissionId, $acoForeignkey)->first(); diff --git a/src/Service/Resources/ResourcesAddService.php b/src/Service/Resources/ResourcesAddService.php new file mode 100644 index 0000000000..532bd162b9 --- /dev/null +++ b/src/Service/Resources/ResourcesAddService.php @@ -0,0 +1,209 @@ +loadModel('Resources'); + $this->loadModel('Users'); + } + + /** + * Add the resource. + * This transaction is prone to deadlock when an import + * of resources is performed on parallel processes on a relatively + * fresh database. To counter this issue, the saving process is + * performed several times in case a PDO Exception is encountered. + * + * @param string $userId User ID creating the resource. + * @param array $data Payload to create the resource. + * @return Resource + * @throws \App\Error\Exception\ValidationException + * @throws \Cake\Http\Exception\ServiceUnavailableException + * @see ResourcesAddServiceStressTest + */ + public function add(string $userId, array $data): Resource + { + $attempts = 1; + do { + $resource = $this->buildEntity($userId, $data); + $this->handleValidationError($resource); + try { + $this->Resources->save($resource); + $this->handleValidationError($resource); + break; + } catch (\PDOException $e) { + Log::error(get_class($e) . ' --- attempt #' . $attempts . ' --- ' . $e->getMessage()); + $attempts++; + } + usleep(self::SLEEP_DURATION_WHEN_LOCKED); + } while ($attempts <= self::ATTEMPTS_ALLOWED); + + $this->handleAttemptsExceededError($attempts); + + return $resource; + } + + /** + * Build the resource entity from user input + * + * @param string $userId User ID creating the resource. + * @param array $data Array of data. + * @return Resource + * @throws \App\Error\Exception\ValidationException + */ + protected function buildEntity(string $userId, array $data): Resource + { + // Enforce data. + $data['created_by'] = $userId; + $data['modified_by'] = $userId; + $data['permissions'] = [[ + 'aro' => 'User', + 'aro_foreign_key' => $userId, + 'aco' => 'Resource', + 'type' => Permission::OWNER, + ]]; + + // If no secrets given, the model will throw a validation error, no need to take care of it here. + if (isset($data['secrets'])) { + $data['secrets'][0]['user_id'] = $userId; + } + + if (!isset($data['resource_type_id']) || !Configure::read('passbolt.plugins.resourceTypes.enabled')) { + $data['resource_type_id'] = ResourceTypesTable::getDefaultTypeId(); + } + + // Build entity and perform basic check + return $this->Resources->newEntity($data, [ + 'accessibleFields' => [ + 'name' => true, + 'username' => true, + 'uri' => true, + 'description' => true, + 'created_by' => true, + 'modified_by' => true, + 'secrets' => true, + 'permissions' => true, + 'resource_type_id' => true, + ], + 'associated' => [ + 'Permissions' => [ + 'validate' => 'saveResource', + 'accessibleFields' => [ + 'aco' => true, + 'aro' => true, + 'aro_foreign_key' => true, + 'type' => true, + ], + ], + 'Secrets' => [ + 'validate' => 'saveResource', + 'accessibleFields' => [ + 'user_id' => true, + 'data' => true, + ], + ], + ], + ]); + } + + /** + * Manage validation errors. + * + * @param Resource $resource resource + * @throws \App\Error\Exception\ValidationException if the resource validation failed + * @return void + */ + protected function handleValidationError(Resource $resource): void + { + $errors = $resource->getErrors(); + if (!empty($errors)) { + throw new ValidationException(__('Could not validate resource data.'), $resource, $this->Resources); + } + } + + /** + * @param int $attempts Number of save attempts stopped by a table lock. + * @return void + * @throws \Cake\Http\Exception\ServiceUnavailableException if the table lock blocked the saving process more than the allowed attempts. + */ + protected function handleAttemptsExceededError(int $attempts): void + { + if ($attempts > self::ATTEMPTS_ALLOWED) { + $msg = __('An unexpected error occured, please contact an administrator.') . ' '; + $msg .= __('The server returned the following error:') . ' '; + $msg .= 'Deadlock error while saving resource: attempts limit exceeded.'; + throw new ServiceUnavailableException($msg); + } + } + + /** + * Triggered by the after save resource event. + * + * @param Resource $resource The created resource + * @param \App\Utility\UserAccessControl $uac The user creating the resource. + * @param array $data Payload to create the resource. + * @return void + * @throws \App\Error\Exception\ValidationException + */ + public function afterSave( + Resource $resource, + UserAccessControl $uac, + array $data + ): void { + $this->handleValidationError($resource); + $user = $this->Users->findFirstForEmail($uac->getId()); + $eventData = compact('resource', 'uac', 'user', 'data'); + $event = new Event(static::ADD_SUCCESS_EVENT_NAME, $this, $eventData); + $this->Resources->getEventManager()->dispatch($event); + $this->handleValidationError($resource); + } +} diff --git a/src/Service/Secrets/SecretsUpdateSecretsService.php b/src/Service/Secrets/SecretsUpdateSecretsService.php index 6d4868d06e..94eb6cc9cb 100644 --- a/src/Service/Secrets/SecretsUpdateSecretsService.php +++ b/src/Service/Secrets/SecretsUpdateSecretsService.php @@ -73,6 +73,7 @@ public function updateSecrets(UserAccessControl $uac, string $resourceId, array { foreach ($data as $rowIndex => $row) { $userId = Hash::get($row, 'user_id', null); + /** @var \App\Model\Entity\Secret|null $secret */ $secret = $this->secretsTable->findByResourceIdAndUserId($resourceId, $userId)->first(); if ($secret) { $this->updateSecret($secret, $rowIndex, $row); @@ -135,7 +136,7 @@ private function handleValidationErrors(array $errors): void * @param int $rowIndexRef The row index in the request data * @param string $resourceId The target resource * @param array $data The secrets data - * @return \App\Model\Entity\Secret + * @return \App\Model\Entity\Secret|null * @throws \Exception */ private function addSecret(UserAccessControl $uac, int $rowIndexRef, string $resourceId, array $data) diff --git a/src/Service/Setup/AbstractCompleteService.php b/src/Service/Setup/AbstractCompleteService.php new file mode 100644 index 0000000000..50c5f19a11 --- /dev/null +++ b/src/Service/Setup/AbstractCompleteService.php @@ -0,0 +1,114 @@ +request = $request; + $this->loadModel('AuthenticationTokens'); + $this->loadModel('Gpgkeys'); + $this->loadModel('Users'); + } + + /** + * Return the authentication from data if any + * + * @param string $userId the user uuid the token belongs to + * @param string $tokenType AuthenticationToken::TYPE_* + * @throws \Cake\Http\Exception\BadRequestException if no authentication token was provided + * @throws \Cake\Http\Exception\BadRequestException if the authentication token is not a uuid + * @throws \Cake\Http\Exception\BadRequestException if the authentication token is expired or invalid + * @return \App\Model\Entity\AuthenticationToken + */ + protected function getAndAssertToken(string $userId, string $tokenType): AuthenticationToken + { + $data = $this->request->getData(); + if (!isset($data['authenticationtoken']) || !isset($data['authenticationtoken']['token'])) { + throw new BadRequestException(__('An authentication token should be provided.')); + } + $tokenValue = $data['authenticationtoken']['token']; + if (!Validation::uuid($tokenValue)) { + throw new BadRequestException(__('The authentication token should be a valid UUID.')); + } + if (!$this->AuthenticationTokens->isValid($tokenValue, $userId, $tokenType)) { + throw new BadRequestException(__('The authentication token is not valid or has expired.')); + } + try { + return $this->AuthenticationTokens->getByToken($tokenValue); + } catch (RecordNotFoundException $e) { + throw new BadRequestException(__('The authentication token does not exist or is inactive.')); + } + } + + /** + * Return the gpg key entity for matching the requesting id + * + * @param string $userId the user uuid + * @throws \Cake\Http\Exception\BadRequestException if the gpg key is not provided or not a valid OpenPGP key + * @return \App\Model\Entity\Gpgkey entity + */ + protected function getAndAssertGpgkey(string $userId) + { + $data = $this->request->getData(); + $armoredKey = $data['gpgkey']['armored_key']; + + if (empty($armoredKey)) { + throw new BadRequestException(__('An OpenPGP key must be provided.')); + } + + if (!$this->Gpgkeys->isParsableArmoredPublicKey($armoredKey)) { + throw new BadRequestException(__('A valid OpenPGP key must be provided.')); + } + try { + $gpgkey = $this->Gpgkeys->buildEntityFromArmoredKey($armoredKey, $userId); + } catch (CustomValidationException $e) { + throw new BadRequestException(__('A valid OpenPGP key must be provided.')); + } + + return $gpgkey; + } +} diff --git a/src/Service/Setup/AbstractStartService.php b/src/Service/Setup/AbstractStartService.php new file mode 100644 index 0000000000..2b4feddc62 --- /dev/null +++ b/src/Service/Setup/AbstractStartService.php @@ -0,0 +1,82 @@ +loadModel('AuthenticationTokens'); + $this->loadModel('Users'); + } + + /** + * Assert that the setup start request is valid + * + * @param string $userId uuid + * @param string $tokenId uuid + * @return void + * @throws \Cake\Http\Exception\BadRequestException if the token is missing or not a uuid + * @throws \Cake\Http\Exception\BadRequestException if the user id is missing or not a uuid + */ + protected function assertRequestSanity(string $userId, string $tokenId): void + { + if (!Validation::uuid($userId)) { + throw new BadRequestException(__('The user identifier should be a valid UUID.')); + } + if (!Validation::uuid($tokenId)) { + throw new BadRequestException(__('The token should be a valid UUID.')); + } + } + + /** + * Assert the token expiry. If the token is expired, regenerate a new one and throw an notify the client with an error. + * + * @param \App\Model\Entity\User $user user attempting to recover + * @param \App\Model\Entity\AuthenticationToken $token the recovery token + * @return void + * @throw CustomValidationException if the token is expired + */ + protected function assertTokenExpiry(User $user, AuthenticationToken $token): void + { + if ($this->AuthenticationTokens->isExpired($token)) { + $error = [ + 'token' => [ + 'expired' => 'The token is expired.', + ], + ]; + throw new CustomValidationException(__('The token is expired.'), $error); + } + } +} diff --git a/src/Service/Setup/RecoverCompleteService.php b/src/Service/Setup/RecoverCompleteService.php new file mode 100644 index 0000000000..5d6d3d9884 --- /dev/null +++ b/src/Service/Setup/RecoverCompleteService.php @@ -0,0 +1,91 @@ +getAndAssertUser($userId); + $token = $this->getAndAssertToken($userId, AuthenticationToken::TYPE_RECOVER); + $gpgkey = $this->getAndAssertGpgkey($userId); + + // Check that the "new" gpg key match the old one + $userKey = $this->Gpgkeys->getByFingerprintAndUserId($gpgkey->fingerprint, $userId); + if (empty($userKey)) { + throw new BadRequestException(__('The key provided does not belong to given user.')); + } + + // Deactivate the authentication token + $token->active = false; + if (!$this->AuthenticationTokens->save($token, ['checkRules' => false])) { + throw new InternalErrorException('Could not update the authentication token data.'); + } + + $this->dispatchEvent(RecoverCompleteController::COMPLETE_SUCCESS_EVENT_NAME, [ + 'user' => $user, + 'data' => $this->request->getData(), + ]); + } + + /** + * Return the user for matching the requesting id + * + * @param string $userId the user uuid + * @throws \Cake\Http\Exception\BadRequestException if the user id is not a valid uuid + * @throws \Cake\Http\Exception\BadRequestException if the user was deleted or has not completed the setup + * @return \App\Model\Entity\User + */ + protected function getAndAssertUser(string $userId): User + { + if (!Validation::uuid($userId)) { + throw new BadRequestException(__('The user identifier should be a valid UUID.')); + } + $user = $this->Users->findSetupRecover($userId); + if (empty($user)) { + $msg = __('The user does not exist, has not completed the setup or was deleted.'); + throw new BadRequestException($msg); + } + + return $user; + } +} diff --git a/src/Service/Setup/RecoverCompleteServiceInterface.php b/src/Service/Setup/RecoverCompleteServiceInterface.php new file mode 100644 index 0000000000..0b1a7eeb86 --- /dev/null +++ b/src/Service/Setup/RecoverCompleteServiceInterface.php @@ -0,0 +1,29 @@ +assertRequestSanity($userId, $token); + $user = $this->findUser($userId); + $token = $this->findToken($user, $token); + $this->assertTokenExpiry($user, $token); + + return compact('user'); + } + + /** + * @inheritDoc + */ + public function setTemplate(ViewBuilder $viewBuilder): void + { + $viewBuilder + ->setTemplatePath('/Setup') + ->setLayout('default') + ->setTemplate('recoverStart'); + } + + /** + * Find the user requesting the recover + * + * @param string $userId uuid of the user + * @return \App\Model\Entity\User + * @throw BadRequestException if the user cannot be found, is deleted or is inactive. + */ + private function findUser(string $userId): User + { + $user = $this->Users->findSetupRecover($userId); + if (empty($user)) { + throw new BadRequestException(__('The user does not exist or is not active.')); + } + + return $user; + } + + /** + * Find the recover token + * + * @param \App\Model\Entity\User $user user attempting to recover + * @param string $token uuid of the token + * @return \App\Model\Entity\AuthenticationToken + * @throw BadRequestException if the token is not valid + */ + private function findToken(User $user, string $token): AuthenticationToken + { + $finderOptions = ['userId' => $user->id, 'token' => $token]; + /** @var \App\Model\Entity\AuthenticationToken $token */ + $token = $this->AuthenticationTokens->find('activeUserRecoveryToken', $finderOptions)->first(); + if (empty($token)) { + throw new BadRequestException(__('The authentication token is not valid.')); + } + + return $token; + } +} diff --git a/src/Service/Setup/RecoverStartServiceInterface.php b/src/Service/Setup/RecoverStartServiceInterface.php new file mode 100644 index 0000000000..df1d9271d6 --- /dev/null +++ b/src/Service/Setup/RecoverStartServiceInterface.php @@ -0,0 +1,36 @@ +getAndAssertUser($userId); + $token = $this->getAndAssertToken($userId, AuthenticationToken::TYPE_REGISTER); + $gpgkey = $this->getAndAssertGpgkey($userId); + + // Check business rules before saving + $this->Gpgkeys->checkRules($gpgkey); + if ($gpgkey->getErrors()) { + throw new ValidationException(__('The OpenPGP key data is not valid.'), $gpgkey, $this->Gpgkeys); + } + + // Deactivate the authentication token + $token->active = false; + if (!$this->AuthenticationTokens->save($token, ['checkRules' => false])) { + throw new InternalErrorException('Could not update the authentication token data.'); + } + + // Save user GPG key, rules were already checked + if (!$this->Gpgkeys->save($gpgkey, ['checkRules' => false])) { + throw new InternalErrorException('Could not save the OpenPGP key data.'); + } + + // Update the user + $user->active = true; + if (!$this->Users->save($user, ['checkRules' => false])) { + throw new InternalErrorException('Could not save the user data.'); + } + + $this->dispatchEvent(SetupCompleteController::COMPLETE_SUCCESS_EVENT_NAME, [ + 'user' => $user, + 'data' => $this->request->getData(), + ]); + } + + /** + * Return the user for matching the requesting id + * + * @param string $userId the user uuid + * @throws \Cake\Http\Exception\BadRequestException if the user id is not a valid uuid + * @throws \Cake\Http\Exception\BadRequestException if the user was deleted, is already active or does not exist + * @return \App\Model\Entity\User user entity + */ + protected function getAndAssertUser(string $userId) + { + if (!Validation::uuid($userId)) { + throw new BadRequestException(__('The user identifier should be a valid UUID.')); + } + $user = $this->Users->findSetup($userId); + if (empty($user)) { + $msg = __('The user does not exist, is already active or has been deleted.'); + throw new BadRequestException($msg); + } + + return $user; + } +} diff --git a/src/Service/Setup/SetupCompleteServiceInterface.php b/src/Service/Setup/SetupCompleteServiceInterface.php new file mode 100644 index 0000000000..6436eb861c --- /dev/null +++ b/src/Service/Setup/SetupCompleteServiceInterface.php @@ -0,0 +1,29 @@ +assertRequestSanity($userId, $token); + $user = $this->findUser($userId); + $token = $this->findToken($user, $token); + $this->assertTokenExpiry($user, $token); + + return compact('user'); + } + + /** + * @inheritDoc + */ + public function setTemplate(ViewBuilder $viewBuilder): void + { + $viewBuilder + ->setTemplatePath('/Setup') + ->setLayout('default') + ->setTemplate('start'); + } + + /** + * Find the user requesting the setup + * + * @param string $userId uuid of the user + * @return \App\Model\Entity\User + * @throw BadRequestException if the user cannot be found, is deleted or is active. + */ + private function findUser(string $userId): User + { + $user = $this->Users->findSetup($userId); + if (empty($user)) { + throw new BadRequestException(__('The user does not exist or is already active.')); + } + + return $user; + } + + /** + * Find the setup token + * + * @param \App\Model\Entity\User $user user attempting to setup + * @param string $token uuid of the token + * @return \App\Model\Entity\AuthenticationToken + * @throw BadRequestException if the token is not valid + */ + private function findToken(User $user, string $token): AuthenticationToken + { + $finderOptions = ['userId' => $user->id, 'token' => $token]; + /** @var \App\Model\Entity\AuthenticationToken $token */ + $token = $this->AuthenticationTokens->find('activeUserRegistrationToken', $finderOptions)->first(); + if (empty($token)) { + throw new BadRequestException(__('The authentication token is not valid.')); + } + + return $token; + } +} diff --git a/src/Service/Users/UserRecoverService.php b/src/Service/Users/UserRecoverService.php new file mode 100644 index 0000000000..48087ce52a --- /dev/null +++ b/src/Service/Users/UserRecoverService.php @@ -0,0 +1,149 @@ +request = $serverRequest; + $this->loadModel('AuthenticationTokens'); + $this->loadModel('Users'); + } + + /** + * @inheritDoc + */ + public function recover(UserAccessControl $uac): void + { + $this->assertValidation(); + $user = $this->assertRules(); + $token = null; + $adminId = null; + if ($user->active) { + $token = $this->AuthenticationTokens->generate($user->id, AuthenticationToken::TYPE_RECOVER); + $event = UsersRecoverController::RECOVER_SUCCESS_EVENT_NAME; + } else { + // The user has not completed the setup, restart setup + // Fixes https://github.com/passbolt/passbolt_api/issues/73 + $token = $this->AuthenticationTokens->generate($user->id, AuthenticationToken::TYPE_REGISTER); + $event = UsersTable::AFTER_REGISTER_SUCCESS_EVENT_NAME; + if ($uac->isAdmin()) { + $adminId = $uac->getId(); + } + } + + // Create an event to build email with token + $options = ['user' => $user, 'token' => $token]; + if (isset($adminId)) { + $options['adminId'] = $adminId; + } + $event = new Event($event, $this, $options); + $this->getEventManager()->dispatch($event); + } + + /** + * @inheritDoc + */ + public function setTemplate(ViewBuilder $viewBuilder): void + { + $viewBuilder + ->setTemplatePath('Auth') + ->setLayout('default') + ->setTemplate('triage'); + } + + /** + * Assert some username data is provided + * + * @return \App\Model\Entity\User user entity + * @throws \Cake\Http\Exception\BadRequestException if the username is not valid + * @throws \Cake\Http\Exception\BadRequestException if the username is not provided + */ + protected function assertValidation(): User + { + $data = $this->request->getData(); + if (!isset($data['username']) || empty($data['username'])) { + throw new BadRequestException(__('Please provide a valid email address.')); + } + + $user = $this->Users->newEntity( + $data, + ['validate' => 'recover', 'accessibleFields' => ['username' => true]] + ); + if ($user->getErrors()) { + throw new BadRequestException(__('Please provide a valid email address.')); + } + + return $user; + } + + /** + * Assert the user can actually perform a recovery on their account + * + * @return \App\Model\Entity\User + * @throws \Cake\Http\Exception\BadRequestException if the user does not exist or has been deleted + */ + protected function assertRules(): User + { + $data = $this->request->getData(); + /** @var \App\Model\Entity\User|null $user */ + $user = $this->Users->findRecover($data['username'])->first(); + + if (empty($user)) { + $msg = __('This user does not exist or has been deleted.') . ' '; + if (Configure::read('passbolt.registration.public')) { + $msg .= __('Please register and complete the setup first.'); + } else { + $msg .= __('Please contact your administrator.'); + } + throw new NotFoundException($msg); + } + + return $user; + } +} diff --git a/src/Service/Users/UserRecoverServiceInterface.php b/src/Service/Users/UserRecoverServiceInterface.php new file mode 100644 index 0000000000..8f0e0eddd0 --- /dev/null +++ b/src/Service/Users/UserRecoverServiceInterface.php @@ -0,0 +1,36 @@ +request = $serverRequest; + $this->loadModel('Users'); + } + + /** + * @inheritDoc + */ + public function register(): User + { + $data = $this->request->getData(); + $user = $this->Users->register($data); + + $this->dispatchEvent(UsersRegisterController::USERS_REGISTER_EVENT_NAME, [ + 'user' => $user, + 'data' => $this->request->getData(), + ]); + + return $user; + } + + /** + * @inheritDoc + */ + public function setTemplate(ViewBuilder $viewBuilder): void + { + $viewBuilder + ->setTemplatePath('Auth') + ->setLayout('default') + ->setTemplate('triage'); + } +} diff --git a/src/Service/Users/UserRegisterServiceInterface.php b/src/Service/Users/UserRegisterServiceInterface.php new file mode 100644 index 0000000000..d3cd9ad37f --- /dev/null +++ b/src/Service/Users/UserRegisterServiceInterface.php @@ -0,0 +1,35 @@ +add(RecoverStartServiceInterface::class, RecoverStartService::class); + $container->add(SetupStartServiceInterface::class, StartStartService::class); + $container + ->add(SetupCompleteServiceInterface::class, SetupCompleteService::class) + ->addArgument(ServerRequestInterface::class); + $container + ->add(RecoverCompleteServiceInterface::class, RecoverCompleteService::class) + ->addArgument(ServerRequestInterface::class); + } +} diff --git a/src/ServiceProvider/UserServiceProvider.php b/src/ServiceProvider/UserServiceProvider.php new file mode 100644 index 0000000000..13c69dfa25 --- /dev/null +++ b/src/ServiceProvider/UserServiceProvider.php @@ -0,0 +1,47 @@ +add(UserRegisterServiceInterface::class, UserRegisterService::class) + ->addArgument(ServerRequestInterface::class); + $container + ->add(UserRecoverServiceInterface::class, UserRecoverService::class) + ->addArgument(ServerRequestInterface::class); + } +} diff --git a/src/Utility/Application/FeaturePluginAwareTrait.php b/src/Utility/Application/FeaturePluginAwareTrait.php new file mode 100644 index 0000000000..1d45dd24aa --- /dev/null +++ b/src/Utility/Application/FeaturePluginAwareTrait.php @@ -0,0 +1,99 @@ + true, 'routes' => true] + * @param bool|callable $isEnabledByDefault Boolean or callback indicating if the plugin should be loaded by default, if not priorly enabled in configurations. False by default. + * @return self + * @throws \TypeError if the callable $isEnabledByDefault does not return a boolean. + */ + public function addFeaturePluginIfEnabled( + PluginApplicationInterface $app, + string $name, + array $config = [], + $isEnabledByDefault = false + ): self { + $config = array_merge(['bootstrap' => true, 'routes' => true], $config); + + if (is_callable($isEnabledByDefault)) { + $isEnabledByDefault = $isEnabledByDefault(); + } + + if ($this->isFeaturePluginEnabled($name, $isEnabledByDefault)) { + $fullPluginName = 'Passbolt/' . ucfirst($name); + $app->addPlugin($fullPluginName, $config); + } + + return $this; + } + + /** + * @param string $name Plugin name, either upper case or lower case first (without the "Passbolt/" prefix) + * @param bool $isEnabledByDefault Should be loaded by default, if not priorly configured. False by default. + * @return bool + */ + public function isFeaturePluginEnabled(string $name, bool $isEnabledByDefault = false): bool + { + return Configure::read($this->getPluginEnabledConfigurationKey($name), $isEnabledByDefault); + } + + /** + * @param string $name Plugin name, either upper case or lower case first (without the "Passbolt/" prefix) + * @return void + */ + public function enableFeaturePlugin(string $name): void + { + Configure::write($this->getPluginEnabledConfigurationKey($name), true); + } + + /** + * @param string $name Plugin name, either upper case or lower case first (without the "Passbolt/" prefix) + * @return void + */ + public function disableFeaturePlugin(string $name): void + { + Configure::write($this->getPluginEnabledConfigurationKey($name), false); + } + + /** + * @param string $name Plugin name, either upper case or lower case first (without the "Passbolt/" prefix) + * @return string + */ + protected function getPluginEnabledConfigurationKey(string $name): string + { + $name = lcfirst($name); + + return "passbolt.plugins.{$name}.enabled"; + } +} diff --git a/src/Utility/AuthToken/AuthTokenExpiry.php b/src/Utility/AuthToken/AuthTokenExpiry.php index cfae065b59..e5a4c53929 100644 --- a/src/Utility/AuthToken/AuthTokenExpiry.php +++ b/src/Utility/AuthToken/AuthTokenExpiry.php @@ -1,32 +1,39 @@ getHeight() / $cropHeight; // < 1 if the image height is smaller than the crop $minRatio = min($widthRatio, $heightRatio); // Resize along the smallest ratio - $newWidth = $size->getWidth() / $minRatio; - $newHeight = $size->getHeight() / $minRatio; + $newWidth = (int)($size->getWidth() / $minRatio); + $newHeight = (int)($size->getHeight() / $minRatio); $image->resize(new Box($newWidth, $newHeight)); // Crop that resized image $size = $image->getSize(); - $x = round(($size->getWidth() - $cropWidth) / 2); - $y = round(($size->getHeight() - $cropHeight) / 2); + $x = max(0, (int)round(($size->getWidth() - $cropWidth) / 2)); + $y = max(0, (int)round(($size->getHeight() - $cropHeight) / 2)); - return $image - ->crop(new Point($x, $y), new Box($cropWidth, $cropHeight)) - ->get('jpeg', [ + $image = $image->crop(new Point($x, $y), new Box($cropWidth, $cropHeight)); + + // Create a non transparent canvas wth white background + $color = (new RGB())->color(self::BACKGROUND_COLOR); + $topLeft = new Point(0, 0); + $canvas = $imagine->create($image->getSize(), $color); + + // Paste the image in the canvas and return the collage + return $canvas + ->paste($image, $topLeft) + ->get('jpg', [ 'quality' => self::JPEG_QUALITY, ]); } diff --git a/src/Utility/Filesystem/DirectoryUtility.php b/src/Utility/Filesystem/DirectoryUtility.php index 324d03ddcc..8f07ef4c34 100644 --- a/src/Utility/Filesystem/DirectoryUtility.php +++ b/src/Utility/Filesystem/DirectoryUtility.php @@ -46,4 +46,28 @@ public static function removeRecursively(string $directoryName): bool return rmdir($directoryName); } + + /** + * The is_executable() PHP method is not reliable to check permissions, + * as it will return true on non executable files. The present method + * checks bitwise the permission of a given file. + * + * @param string $path File or directory path + * @return bool + * @throws \RuntimeException if the provided file/directory does not exist + */ + public static function isExecutable(string $path): bool + { + if (!file_exists($path)) { + throw new \RuntimeException("The file $path could not be found."); + } + $code = str_split(decoct(fileperms($path) & 0777)); + foreach ($code as $perm) { + if ((int)$perm % 2 !== 0) { + return true; + } + } + + return false; + } } diff --git a/src/Utility/Healthchecks.php b/src/Utility/Healthchecks.php index 5c34e1fb83..b53fbb23bd 100644 --- a/src/Utility/Healthchecks.php +++ b/src/Utility/Healthchecks.php @@ -17,6 +17,8 @@ namespace App\Utility; use App\Model\Entity\Role; +use App\Utility\Application\FeaturePluginAwareTrait; +use App\Utility\Filesystem\DirectoryUtility; use App\Utility\Healthchecks\DatabaseHealthchecks; use App\Utility\Healthchecks\GpgHealthchecks; use App\Utility\Healthchecks\SslHealthchecks; @@ -25,9 +27,13 @@ use Cake\Core\Exception\Exception; use Cake\ORM\TableRegistry; use Cake\Validation\Validation; +use Passbolt\JwtAuthentication\Service\AccessToken\JwtAbstractService; +use Passbolt\JwtAuthentication\Service\AccessToken\JwtKeyPairService; class Healthchecks { + use FeaturePluginAwareTrait; + /** * Run all healthchecks * @@ -221,8 +227,32 @@ public static function environment(?array $checks = []): array $checks['environment']['intl'] = extension_loaded('intl'); $checks['environment']['image'] = (extension_loaded('gd') || extension_loaded('imagick')); $checks['environment']['tmpWritable'] = self::_checkRecursiveDirectoryWritable(TMP); - $checks['environment']['imgPublicWritable'] = self::_checkRecursiveDirectoryWritable(IMAGES . 'public/'); $checks['environment']['logWritable'] = is_writable(LOGS); + //$checks['environment']['allow_url_fopen'] = ini_get('allow_url_fopen') === '1'; + + return $checks; + } + + /** + * Returns JWT related checks: + * - is the JWT Authentication enabled + * - if true, are the JWT key files correctly set and valid. + * + * @param array|null $checks List of checks + * @return array + */ + public static function jwt(?array $checks = []): array + { + try { + (new JwtKeyPairService())->validateKeyPair(); + $keyPairIsValid = true; + } catch (\Throwable $e) { + $keyPairIsValid = false; + } + + $checks['jwt']['isEnabled'] = (Configure::read('passbolt.plugins.jwtAuthentication.enabled') === true); + $checks['jwt']['keyPairValid'] = $keyPairIsValid; + $checks['jwt']['jwtWritable'] = is_writable(JwtAbstractService::JWT_CONFIG_DIR); return $checks; } @@ -260,6 +290,9 @@ public static function ssl(?array $checks = []): array */ private static function _checkRecursiveDirectoryWritable(string $path): bool { + clearstatcache(); + + /** @var \SplFileInfo[] $iterator */ $iterator = new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator($path), \RecursiveIteratorIterator::SELF_FIRST @@ -268,7 +301,11 @@ private static function _checkRecursiveDirectoryWritable(string $path): bool if (in_array($fileInfo->getFilename(), ['.', '..', 'empty'])) { continue; } - if (!is_writable($name)) { + // No file should be executable in tmp + if ($fileInfo->isFile() && DirectoryUtility::isExecutable($name)) { + return false; + } + if (!$fileInfo->isWritable()) { return false; } } diff --git a/src/Utility/Healthchecks/DatabaseHealthchecks.php b/src/Utility/Healthchecks/DatabaseHealthchecks.php index f7f1c49162..be79063ef2 100644 --- a/src/Utility/Healthchecks/DatabaseHealthchecks.php +++ b/src/Utility/Healthchecks/DatabaseHealthchecks.php @@ -50,7 +50,15 @@ public static function all(string $datasource, ?array $checks = []): array */ public static function canConnect(string $datasource, ?array $checks = []): array { - $checks['database']['connect'] = false; + $checks = array_replace_recursive( + [ + 'database' => [ + 'connect' => false, + 'info' => [], + ], + ], + $checks + ); try { /** @var \Cake\Database\Connection $connection */ $connection = ConnectionManager::get($datasource); @@ -61,7 +69,7 @@ public static function canConnect(string $datasource, ?array $checks = []): arra if (method_exists($connectionError, 'getAttributes')) { $attributes = $connectionError->getAttributes(); if (!empty($errorMsg)) { - $checks['database']['info'] .= ' ' . $attributes['message']; + $checks['database']['info']['connection'] = $attributes['message']; } } } @@ -97,13 +105,22 @@ public static function supportedBackend(string $datasource, ?array $checks = []) */ public static function tableCount(string $datasource, ?array $checks = []): array { - $checks['database']['info']['tablesCount'] = 0; - $checks['database']['tablesCount'] = false; + $checks = array_replace_recursive( + [ + 'database' => [ + 'tablesCount' => false, + 'info' => [ + 'tablesCount' => 0, + ], + ], + ], + $checks + ); try { $connection = ConnectionManager::get('default'); $tables = $connection->getSchemaCollection()->listTables(); - if ($tables !== false && count($tables)) { + if (count($tables) > 0) { $checks['database']['tablesCount'] = true; $checks['database']['info']['tablesCount'] = count($tables); } @@ -124,12 +141,10 @@ public static function defaultContent(?array $checks = []): array { $checks['database']['defaultContent'] = false; try { - $Roles = TableRegistry::getTableLocator()->get('Roles'); - if (!empty($Roles)) { - $i = $Roles->find('all')->count(); - $checks['database']['defaultContent'] = ($i > 3); - } - } catch (DatabaseException $e) { + $roles = TableRegistry::getTableLocator()->get('Roles'); + $i = $roles->find('all')->count(); + $checks['database']['defaultContent'] = ($i > 3); + } catch (DatabaseException | \PDOException $e) { } return $checks; diff --git a/src/Utility/Healthchecks/GpgHealthchecks.php b/src/Utility/Healthchecks/GpgHealthchecks.php index 1f568c4d05..0fb380a3d8 100644 --- a/src/Utility/Healthchecks/GpgHealthchecks.php +++ b/src/Utility/Healthchecks/GpgHealthchecks.php @@ -153,9 +153,19 @@ public static function gpgKeyFile(?array $checks = []): array */ public static function gpgFingerprint(?array $checks = []): array { - $checks['gpg']['gpgKeyPrivateFingerprint'] = false; - $checks['gpg']['gpgKeyPublicFingerprint'] = false; - $checks['gpg']['gpgKeyPublicEmail'] = false; + $checks = array_replace_recursive( + [ + 'gpg' => [ + 'gpgKeyPrivateFingerprint' => false, + 'gpgKeyPublicFingerprint' => false, + 'gpgKeyPublicEmail' => false, + 'gpgKeyPublicReadable' => false, + 'gpgKeyPrivateReadable' => false, + 'gpgKey' => false, + ], + ], + $checks + ); $areKeysReadable = $checks['gpg']['gpgKeyPublicReadable'] && $checks['gpg']['gpgKeyPrivateReadable']; if ($areKeysReadable && $checks['gpg']['gpgKey']) { $gpg = OpenPGPBackendFactory::get(); @@ -186,7 +196,15 @@ public static function gpgFingerprint(?array $checks = []): array */ public static function gpgKeyInKeyring(?array $checks = []): array { - $checks['gpg']['gpgKeyPublicInKeyring'] = false; + $checks = array_replace_recursive( + [ + 'gpg' => [ + 'gpgHome' => false, + 'gpgKeyPublicInKeyring' => false, + ], + ], + $checks + ); $fingerprint = Configure::read('passbolt.gpg.serverKey.fingerprint'); if (!$checks['gpg']['gpgHome'] || $fingerprint === null) { return $checks; @@ -208,17 +226,24 @@ public static function gpgKeyInKeyring(?array $checks = []): array */ public static function gpgCanEncrypt(?array $checks = []): array { - $checks['gpg']['canEncrypt'] = false; + $checks = array_replace_recursive( + [ + 'gpg' => [ + 'canEncrypt' => false, + 'gpgKeyPublicInKeyring' => false, + ], + ], + $checks + ); if ($checks['gpg']['gpgKeyPublicInKeyring']) { $_gpg = OpenPGPBackendFactory::get(); $messageToEncrypt = 'test message'; try { $_gpg->setEncryptKeyFromFingerprint(Configure::read('passbolt.gpg.serverKey.fingerprint')); - $encryptedMessage = $_gpg->encrypt($messageToEncrypt); - if ($encryptedMessage !== false) { - $checks['gpg']['canEncrypt'] = true; - } + $_gpg->encrypt($messageToEncrypt); + $checks['gpg']['canEncrypt'] = true; } catch (Exception $e) { + $checks['gpg']['canEncrypt'] = false; } } @@ -233,7 +258,15 @@ public static function gpgCanEncrypt(?array $checks = []): array */ public static function gpgCanEncryptSign(?array $checks = []): array { - $checks['gpg']['canEncryptSign'] = false; + $checks = array_replace_recursive( + [ + 'gpg' => [ + 'canEncryptSign' => false, + 'gpgKeyPublicInKeyring' => false, + ], + ], + $checks + ); if ($checks['gpg']['gpgKeyPublicInKeyring']) { $_gpg = OpenPGPBackendFactory::get(); $messageToEncrypt = 'test message'; @@ -242,11 +275,10 @@ public static function gpgCanEncryptSign(?array $checks = []): array $passphrase = Configure::read('passbolt.gpg.serverKey.passphrase'); $_gpg->setEncryptKeyFromFingerprint($fingerprint); $_gpg->setSignKeyFromFingerprint($fingerprint, $passphrase); - $encryptedMessage2 = $_gpg->encrypt($messageToEncrypt, true); - if ($encryptedMessage2 !== false) { - $checks['gpg']['canEncryptSign'] = true; - } + $_gpg->encrypt($messageToEncrypt, true); + $checks['gpg']['canEncryptSign'] = true; } catch (Exception $e) { + $checks['gpg']['canEncryptSign'] = false; } } @@ -261,7 +293,16 @@ public static function gpgCanEncryptSign(?array $checks = []): array */ public static function gpgCanDecrypt(?array $checks = []): array { - $checks['gpg']['canDecrypt'] = false; + $checks = array_replace_recursive( + [ + 'gpg' => [ + 'canEncrypt' => false, + 'canDecrypt' => false, + 'gpgKeyPublicInKeyring' => false, + ], + ], + $checks + ); if ($checks['gpg']['gpgKeyPublicInKeyring']) { if ($checks['gpg']['canEncrypt']) { $_gpg = OpenPGPBackendFactory::get(); @@ -292,7 +333,15 @@ public static function gpgCanDecrypt(?array $checks = []): array */ public static function gpgCanDecryptVerify(?array $checks = []): array { - $checks['gpg']['canDecryptVerify'] = false; + $checks = array_replace_recursive( + [ + 'gpg' => [ + 'canDecryptVerify' => false, + 'gpgKeyPublicInKeyring' => false, + ], + ], + $checks + ); if ($checks['gpg']['gpgKeyPublicInKeyring']) { $_gpg = OpenPGPBackendFactory::get(); $messageToEncrypt = 'test message'; @@ -323,7 +372,15 @@ public static function gpgCanDecryptVerify(?array $checks = []): array */ public static function gpgCanSign(?array $checks = []): array { - $checks['gpg']['canSign'] = false; + $checks = array_replace_recursive( + [ + 'gpg' => [ + 'gpgKeyPublicInKeyring' => false, + 'canSign' => false, + ], + ], + $checks + ); if ($checks['gpg']['gpgKeyPublicInKeyring']) { $_gpg = OpenPGPBackendFactory::get(); $_gpg->setSignKeyFromFingerprint( @@ -332,11 +389,10 @@ public static function gpgCanSign(?array $checks = []): array ); $messageToEncrypt = 'test message'; try { - $signature = $_gpg->sign($messageToEncrypt); - if ($signature !== false) { - $checks['gpg']['canSign'] = true; - } + $_gpg->sign($messageToEncrypt); + $checks['gpg']['canSign'] = true; } catch (Exception $e) { + $checks['gpg']['canSign'] = false; } } @@ -351,7 +407,15 @@ public static function gpgCanSign(?array $checks = []): array */ public static function gpgCanVerify(?array $checks = []): array { - $checks['gpg']['canVerify'] = false; + $checks = array_replace_recursive( + [ + 'gpg' => [ + 'canDecryptVerify' => false, + 'canVerify' => false, + ], + ], + $checks + ); if ($checks['gpg']['canDecryptVerify']) { $_gpg = OpenPGPBackendFactory::get(); $messageToEncrypt = 'test message'; @@ -364,7 +428,7 @@ public static function gpgCanVerify(?array $checks = []): array try { $_gpg->setVerifyKeyFromFingerprint($fingerprint); - $_gpg->verify($signedMessage, Configure::read('passbolt.gpg.serverKey.fingerprint')); + $_gpg->verify($signedMessage); $checks['gpg']['canVerify'] = true; } catch (Exception $e) { } diff --git a/src/Utility/Healthchecks/SslHealthchecks.php b/src/Utility/Healthchecks/SslHealthchecks.php index 519d265fe1..373b7cca19 100644 --- a/src/Utility/Healthchecks/SslHealthchecks.php +++ b/src/Utility/Healthchecks/SslHealthchecks.php @@ -100,6 +100,7 @@ public static function hostValid(?array $checks = []): array * Check that the certificate is not self signed * * @param array|null $checks List of checks + * @psalm-suppress InvalidNullableReturnType false positive * @return array */ public static function notSelfSigned(?array $checks = []): array @@ -116,6 +117,7 @@ public static function notSelfSigned(?array $checks = []): array } catch (\Exception $e) { } + /** @psalm-suppress NullableReturnStatement false positive */ return $checks; } } diff --git a/src/Utility/Migration.php b/src/Utility/Migration.php index 7af5079067..aa16170f5d 100644 --- a/src/Utility/Migration.php +++ b/src/Utility/Migration.php @@ -23,8 +23,6 @@ class Migration { - protected $_remoteTagName; - /** * Check if the app or plugins need a database migration * diff --git a/src/Utility/OpenPGP/Backends/Gnupg.php b/src/Utility/OpenPGP/Backends/Gnupg.php index 0054f0a993..adb6f954f8 100644 --- a/src/Utility/OpenPGP/Backends/Gnupg.php +++ b/src/Utility/OpenPGP/Backends/Gnupg.php @@ -44,8 +44,10 @@ class Gnupg extends OpenPGPBackend { /** * Gpg object. + * + * @var \gnupg */ - protected $_gpg = null; + protected $_gpg; /** * Constructor. @@ -63,8 +65,7 @@ public function __construct() } $this->_gpg = new \gnupg(); - /** @phpstan-ignore-next-line */ - $this->_gpg->seterrormode(\gnupg::ERROR_EXCEPTION); + $this->_gpg->seterrormode(GNUPG_ERROR_EXCEPTION); } /** @@ -74,7 +75,7 @@ public function __construct() * @throws \Cake\Core\Exception\Exception if the key cannot be used to encrypt * @return bool true if success */ - public function setEncryptKey(string $armoredKey) + public function setEncryptKey(string $armoredKey): bool { $this->_encryptKeyFingerprint = null; // Get the key info. @@ -107,7 +108,7 @@ public function setEncryptKey(string $armoredKey) * @throws \Cake\Core\Exception\Exception if there was an issue to use the key to encrypt * @return bool true if success */ - public function setEncryptKeyFromFingerprint(string $fingerprint) + public function setEncryptKeyFromFingerprint(string $fingerprint): bool { $this->_encryptKeyFingerprint = null; $this->assertKeyInKeyring($fingerprint); @@ -131,7 +132,7 @@ public function setEncryptKeyFromFingerprint(string $fingerprint) * @throws \Cake\Core\Exception\Exception if the key cannot be used to decrypt * @return bool true if success */ - public function setDecryptKey(string $armoredKey, string $passphrase) + public function setDecryptKey(string $armoredKey, string $passphrase): bool { $this->_decryptKeyFingerprint = null; @@ -168,7 +169,7 @@ public function setDecryptKey(string $armoredKey, string $passphrase) * @throws \Cake\Core\Exception\Exception if the key cannot be used to decrypt * @return bool true if success */ - public function setDecryptKeyFromFingerprint(string $fingerprint, string $passphrase) + public function setDecryptKeyFromFingerprint(string $fingerprint, string $passphrase): bool { $this->_decryptKeyFingerprint = null; @@ -193,7 +194,7 @@ public function setDecryptKeyFromFingerprint(string $fingerprint, string $passph * @return bool * @throws \Cake\Core\Exception\Exception */ - public function setSignKey(string $armoredKey, string $passphrase) + public function setSignKey(string $armoredKey, string $passphrase): bool { $this->_signKeyFingerprint = null; @@ -230,7 +231,7 @@ public function setSignKey(string $armoredKey, string $passphrase) * @param string $passphrase passphrase * @return true if success */ - public function setSignKeyFromFingerprint(string $fingerprint, string $passphrase) + public function setSignKeyFromFingerprint(string $fingerprint, string $passphrase): bool { $this->_signKeyFingerprint = null; @@ -288,7 +289,7 @@ public function isParsableArmoredPublicKey(string $armoredKey): bool * @param string $armoredKey ASCII armored key data * @return bool true if parsable false otherwise */ - public function isParsableArmoredPrivateKey(string $armoredKey) + public function isParsableArmoredPrivateKey(string $armoredKey): bool { try { $this->assertGpgMarker($armoredKey, self::PRIVATE_KEY_MARKER); @@ -319,7 +320,7 @@ public function isParsableArmoredPrivateKey(string $armoredKey) * @param string $armored ASCII armored signed message * @return bool */ - public function isParsableArmoredSignedMessage($armored) + public function isParsableArmoredSignedMessage($armored): bool { try { $marker = $this->getGpgMarker($armored); @@ -342,7 +343,7 @@ public function isParsableArmoredSignedMessage($armored) * @param string $armored ASCII armored message data * @return bool true if valid, false otherwise */ - public function isValidMessage(string $armored) + public function isValidMessage(string $armored): bool { try { $this->assertGpgMarker($armored, self::MESSAGE_MARKER); @@ -362,7 +363,7 @@ public function isValidMessage(string $armored) * @throws \Cake\Core\Exception\Exception if the armored key cannot be parsed * @return array key information (see getKeyInfo) */ - public function getPublicKeyInfo(string $armoredKey) + public function getPublicKeyInfo(string $armoredKey): array { if ( $this->isParsableArmoredPublicKey($armoredKey) === false @@ -391,7 +392,7 @@ public function getPublicKeyInfo(string $armoredKey) * @param string $armoredKey the ASCII armored key block * @return array as described above */ - public function getKeyInfo(string $armoredKey) + public function getKeyInfo(string $armoredKey): array { $keyUnarmored = $this->unarmor($armoredKey, $this->getGpgMarker($armoredKey)); if ($keyUnarmored === false) { @@ -424,7 +425,7 @@ public function getKeyInfo(string $armoredKey) foreach ($msg->signatures() as $signatures) { foreach ($signatures as $signature) { if ($signature instanceof \OpenPGP_UserIDPacket) { - $userIds[] = sprintf('%s', $signature); + $userIds[] = sprintf('%s', (string)$signature); } } } @@ -462,7 +463,7 @@ public function getKeyInfo(string $armoredKey) * @param string $fingerprint fingerprint * @return bool true if in keyring false otherwise */ - public function isKeyInKeyring(string $fingerprint) + public function isKeyInKeyring(string $fingerprint): bool { try { $results = $this->_gpg->keyinfo($fingerprint); @@ -483,7 +484,7 @@ public function isKeyInKeyring(string $fingerprint) * @throws \Cake\Core\Exception\Exception if the key could not be imported * @return string key fingerprint */ - public function importKeyIntoKeyring(string $armoredKey) + public function importKeyIntoKeyring(string $armoredKey): string { $msg = __('Could not import the OpenPGP key.'); try { @@ -511,13 +512,14 @@ public function importKeyIntoKeyring(string $armoredKey) * @throws \Cake\Core\Exception\Exception if there is an issue with the key to encrypt and optionally to sign * @return string encrypted text */ - public function encrypt(string $text, bool $sign = false) + public function encrypt(string $text, bool $sign = false): string { $this->assertEncryptKey(); if ($sign === true) { $msg = __('Could not use the key to sign and encrypt.'); $this->assertSignKey(); try { + /** @var string|false $encryptedText */ $encryptedText = $this->_gpg->encryptsign($text); } catch (\Exception $e) { throw new Exception($msg . $e->getMessage()); @@ -530,6 +532,7 @@ public function encrypt(string $text, bool $sign = false) $msg = __('Could not use the key to encrypt.'); $this->assertEncryptKey(); try { + /** @var string|false $encryptedText */ $encryptedText = $this->_gpg->encrypt($text); } catch (\Exception $e) { throw new Exception($msg . ' ' . $e->getMessage()); @@ -543,6 +546,20 @@ public function encrypt(string $text, bool $sign = false) return $encryptedText; } + /** + * Encrypt a text and sign it too + * Do not forget to add a key to encrypt and sign + * + * @param string $text plain text to be encrypted. + * @throws \Cake\Core\Exception\Exception if no key was set to encrypt and optionally to sign + * @throws \Cake\Core\Exception\Exception if there is an issue with the key to encrypt and optionally to sign + * @return string encrypted text + */ + public function encryptSign(string $text): string + { + return $this->encrypt($text, true); + } + /** * Decrypt a text * @@ -551,9 +568,11 @@ public function encrypt(string $text, bool $sign = false) * @throws \Cake\Core\Exception\Exception if decryption fails * @return string decrypted text */ - public function decrypt(string $text, bool $verifySignature = false) + public function decrypt(string $text, bool $verifySignature = false): string { $decrypted = false; + $fingerprint = null; + $signatureInfo = null; $this->assertDecryptKey(); if ($verifySignature) { $this->assertVerifyKey(); @@ -564,6 +583,7 @@ public function decrypt(string $text, bool $verifySignature = false) if ($verifySignature === false) { $decrypted = $this->_gpg->decrypt($text); } else { + /** @psalm-suppress InvalidArgument @phpstan-ignore-next-line */ $signatureInfo = $this->_gpg->decryptverify($text, $decrypted); } } catch (\Exception $e) { @@ -589,20 +609,23 @@ public function decrypt(string $text, bool $verifySignature = false) /** * Verify a signed message. * - * @param string $armored The armored signed message to verify. - * @param string $fingerprint The fingerprint of the key to verify for. - * @param mixed $plainText (optional) if this parameter is passed, it will be filled with the plain text. - * @return void + * @param string $signedText The signed message to verify. + * @param string|null $plainText (optional) if this parameter is passed, it will be filled with the plain text. + * @return array signature information * @throws \Cake\Core\Exception\Exception If the armored signed message cannot be verified. */ - public function verify($armored, $fingerprint, &$plainText = null) + public function verify(string $signedText, ?string &$plainText = null): array { + $this->assertVerifyKey(); $msg = __('The message cannot be verified.'); try { - $signature = $this->_gpg->verify($armored, false, $plainText); - if (empty($signature) || $signature[0]['fingerprint'] !== $fingerprint) { + /** @psalm-suppress InvalidArgument @phpstan-ignore-next-line */ + $signature = $this->_gpg->verify($signedText, false, $plainText); + if (empty($signature) || $signature[0]['fingerprint'] !== $this->_verifyKeyFingerprint) { throw new Exception($msg); } + + return $signature; } catch (\Exception $e) { throw new Exception($msg); } @@ -616,11 +639,12 @@ public function verify($armored, $fingerprint, &$plainText = null) * @throws \Cake\Core\Exception\Exception if there is an issue with the key to sign * @return string signed text */ - public function sign(string $text) + public function sign(string $text): string { $msg = __('Could not sign the text. '); $this->assertSignKey(); try { + /** @var string|false $signedText */ $signedText = $this->_gpg->sign($text); $this->clearSignKeys(); } catch (\Exception $e) { @@ -639,7 +663,7 @@ public function sign(string $text) * * @return void */ - public function clearDecryptKeys() + public function clearDecryptKeys(): void { $this->_decryptKeyFingerprint = null; $this->_gpg->cleardecryptkeys(); @@ -650,7 +674,7 @@ public function clearDecryptKeys() * * @return void */ - public function clearSignKeys() + public function clearSignKeys(): void { $this->_signKeyFingerprint = null; $this->_gpg->clearsignkeys(); @@ -661,7 +685,7 @@ public function clearSignKeys() * * @return void */ - public function clearEncryptKeys() + public function clearEncryptKeys(): void { $this->_encryptKeyFingerprint = null; $this->_gpg->clearencryptkeys(); diff --git a/src/Utility/OpenPGP/OpenPGPBackend.php b/src/Utility/OpenPGP/OpenPGPBackend.php index 7d3bda16a8..49f646992b 100644 --- a/src/Utility/OpenPGP/OpenPGPBackend.php +++ b/src/Utility/OpenPGP/OpenPGPBackend.php @@ -49,7 +49,7 @@ public function __construct() * @throws \Cake\Http\Exception\InternalErrorException if server key is undefined or invalid * @return void */ - public function importServerKeyInKeyring() + public function importServerKeyInKeyring(): void { $fingerprint = Configure::read('passbolt.gpg.serverKey.fingerprint'); $keyFilePath = Configure::read('passbolt.gpg.serverKey.private'); @@ -104,7 +104,7 @@ protected function getGpgMarker(string $armored) * * @return void */ - public function clearDecryptKeys() + public function clearDecryptKeys(): void { $this->_decryptKeyFingerprint = null; } @@ -114,7 +114,7 @@ public function clearDecryptKeys() * * @return void */ - public function clearSignKeys() + public function clearSignKeys(): void { $this->_signKeyFingerprint = null; } @@ -124,7 +124,7 @@ public function clearSignKeys() * * @return void */ - public function clearEncryptKeys() + public function clearEncryptKeys(): void { $this->_encryptKeyFingerprint = null; } @@ -134,7 +134,7 @@ public function clearEncryptKeys() * * @return void */ - public function clearVerifyKeys() + public function clearVerifyKeys(): void { $this->_verifyKeyFingerprint = null; } @@ -144,7 +144,7 @@ public function clearVerifyKeys() * * @return void */ - public function clearKeys() + public function clearKeys(): void { $this->clearDecryptKeys(); $this->clearEncryptKeys(); diff --git a/src/Utility/OpenPGP/OpenPGPBackendInterface.php b/src/Utility/OpenPGP/OpenPGPBackendInterface.php index ee75e284aa..8d2fbb18a8 100644 --- a/src/Utility/OpenPGP/OpenPGPBackendInterface.php +++ b/src/Utility/OpenPGP/OpenPGPBackendInterface.php @@ -1,4 +1,17 @@ loadHelper('Html');` - * - * @return void - */ - public function initialize(): void - { - $myTemplates = [ - 'error' => '
{{content}}
', - ]; - $this->Form->setTemplates($myTemplates); - } } diff --git a/src/View/Helper/AvatarHelper.php b/src/View/Helper/AvatarHelper.php index 5c5ced8138..f9f48c3447 100644 --- a/src/View/Helper/AvatarHelper.php +++ b/src/View/Helper/AvatarHelper.php @@ -3,8 +3,7 @@ namespace App\View\Helper; -use App\Model\Entity\Avatar; -use App\Model\Table\AvatarsTable; +use App\Service\Avatars\AvatarsConfigurationService; use Cake\Core\Configure; use Cake\Routing\Router; use Cake\View\Helper; @@ -14,13 +13,15 @@ class AvatarHelper extends Helper public const IMAGE_EXTENSION = '.jpg'; /** - * @param \App\Model\Entity\Avatar|null $avatar Avatar instance + * @param array|null $avatar Avatar instance * @param string|null $format Format of the avatar * @return string */ - public static function getAvatarUrl(?Avatar $avatar = null, ?string $format = AvatarsTable::FORMAT_SMALL): string - { - if (empty($avatar) || empty($avatar->get('data')) || empty($avatar->get('id'))) { + public static function getAvatarUrl( + ?array $avatar = null, + ?string $format = AvatarsConfigurationService::FORMAT_SMALL + ): string { + if (empty($avatar) || empty($avatar['id'])) { return self::getAvatarFallBackUrl($format); } else { return Router::url([ @@ -28,7 +29,7 @@ public static function getAvatarUrl(?Avatar $avatar = null, ?string $format = Av 'prefix' => 'Avatars', 'controller' => 'AvatarsView', 'action' => 'view', - 'id' => $avatar->get('id'), + 'id' => $avatar['id'], 'format' => $format, '_ext' => trim(self::IMAGE_EXTENSION, '.'), ], true); @@ -39,7 +40,7 @@ public static function getAvatarUrl(?Avatar $avatar = null, ?string $format = Av * @param string|null $format Image format. * @return string */ - public static function getAvatarFallBackUrl(?string $format = AvatarsTable::FORMAT_SMALL): string + public static function getAvatarFallBackUrl(?string $format = AvatarsConfigurationService::FORMAT_SMALL): string { return Router::url(Configure::readOrFail('FileStorage.imageDefaults.Avatar.' . $format), true); } @@ -48,6 +49,7 @@ public static function getAvatarFallBackUrl(?string $format = AvatarsTable::FORM * Checks if the format provided is medium or small * * @param bool $withExtension Append the image file extension. + * @psalm-suppress InvalidReturnType false positive * @return array * @throws \RuntimeException if the avatar config is not set in config/file_storage.php */ @@ -60,6 +62,7 @@ public static function getValidImageFormats(?bool $withExtension = true): array }); } + /** @psalm-suppress InvalidReturnStatement false positive */ return $formats; } } diff --git a/src/View/Helper/HealthcheckHtmlHelper.php b/src/View/Helper/HealthcheckHtmlHelper.php index 9a42876a60..d916d1d6eb 100644 --- a/src/View/Helper/HealthcheckHtmlHelper.php +++ b/src/View/Helper/HealthcheckHtmlHelper.php @@ -76,6 +76,6 @@ protected function warning(bool $condition, $success, $warning, $help = null) */ protected function title(string $title) { - echo '

' . $title . '

' . PHP_EOL; + echo '

' . $title . '

' . PHP_EOL; } } diff --git a/templates/Auth/triage.php b/templates/Auth/triage.php index beba1f0d1b..a4d9d88750 100644 --- a/templates/Auth/triage.php +++ b/templates/Auth/triage.php @@ -15,13 +15,21 @@ $this->assign('title', $title); $version = Configure::read('passbolt.version'); -$themePath = "themes/default/api_authentication.min.css?v=$version"; -$this->Html->css($themePath, ['block' => 'css', 'fullBase' => true, 'id' => 'js_css_theme']); // See. fetch('scriptBottom') $this->start('scriptBottom'); // Load the javascript application. -echo $this->Html->script('/js/app/api-vendors.js?v=' . Configure::read('passbolt.version'), ['fullBase' => true, 'cache-version' => Configure::read('passbolt.version')]); -echo $this->Html->script('/js/app/api-triage.js?v=' . Configure::read('passbolt.version'), ['fullBase' => true, 'cache-version' => Configure::read('passbolt.version')]); +echo $this->Html->script('/js/app/api-vendors.js?v=' . $version, ['fullBase' => true, 'cache-version' => $version]); +echo $this->Html->script('/js/app/api-triage.js?v=' . $version, ['fullBase' => true, 'cache-version' => $version]); +$this->end(); + +$this->start('scriptTop'); + +echo $this->Html->script('/js/app/stylesheet.js?v=' . $version, [ + 'id' => 'stylesheet-manager', + 'fullBase' => true, + 'data-file' => 'api_authentication.min.css', + 'cache-version' => $version]); + $this->end(); ?> diff --git a/templates/Healthcheck/index.php b/templates/Healthcheck/index.php index 8b2600a142..42120dd76d 100644 --- a/templates/Healthcheck/index.php +++ b/templates/Healthcheck/index.php @@ -21,10 +21,11 @@ $healthcheck = new HealthcheckHtmlHelper(); ?> +
-

+

assertEnvironment($body); ?> @@ -49,26 +50,32 @@ ?>
-

What is this page?

-

- This page is available to help administrators diagnose if something is wrong - with a passbolt installation and help keeping it secure. -

-

- It is also possible to perform a healt check using the command line tools as follow: -

- + + +
diff --git a/templates/Home/api_app.php b/templates/Home/api_app.php index 54db9ba80f..dee3ecb2ea 100644 --- a/templates/Home/api_app.php +++ b/templates/Home/api_app.php @@ -15,8 +15,6 @@ $this->assign('title', $title); $version = Configure::read('passbolt.version'); -$themePath = "themes/$theme/api_main.min.css?v=$version"; -$this->Html->css($themePath, ['block' => 'css', 'fullBase' => true, 'id' => 'js_css_theme']); // See. fetch('scriptBottom') $this->start('scriptBottom'); @@ -25,4 +23,15 @@ echo $this->Html->script('/js/app/api-app.js?v=' . Configure::read('passbolt.version'), ['fullBase' => true, 'cache-version' => Configure::read('passbolt.version')]); $this->end(); echo $this->element('Loader/skeleton'); + +$this->start('scriptTop'); + +echo $this->Html->script('/js/app/stylesheet.js?v=' . $version, [ + 'id' => 'stylesheet-manager', + 'fullBase' => true, + 'data-file' => 'api_main.min.css', + 'data-theme' => isset($theme) ? $theme : null, + 'cache-version' => $version]); + +$this->end(); ?> diff --git a/templates/Home/api_ext_app.php b/templates/Home/api_ext_app.php index 84de10caaa..18a109f078 100644 --- a/templates/Home/api_ext_app.php +++ b/templates/Home/api_ext_app.php @@ -15,7 +15,16 @@ $this->assign('title', $title); $version = Configure::read('passbolt.version'); -$themePath = "themes/$theme/api_main.min.css?v=$version"; -$this->Html->css($themePath, ['block' => 'css', 'fullBase' => true, 'id' => 'js_css_theme']); echo $this->element('Loader/skeleton'); + +$this->start('scriptTop'); + +echo $this->Html->script('/js/app/stylesheet.js?v=' . $version, [ + 'id' => 'stylesheet-manager', + 'fullBase' => true, + 'data-file' => 'api_main.min.css', + 'data-theme' => isset($theme) ? $theme : null, + 'cache-version' => $version]); + +$this->end(); ?> diff --git a/templates/Setup/recover_start.php b/templates/Setup/recover_start.php index 34df1c4b7a..6aa8ff3f81 100644 --- a/templates/Setup/recover_start.php +++ b/templates/Setup/recover_start.php @@ -15,13 +15,21 @@ $this->assign('title', $title); $version = Configure::read('passbolt.version'); -$themePath = "themes/default/api_authentication.min.css?v=$version"; -$this->Html->css($themePath, ['block' => 'css', 'fullBase' => true, 'id' => 'js_css_theme']); // See. fetch('scriptBottom') $this->start('scriptBottom'); // Load the javascript application. -echo $this->Html->script('/js/app/api-vendors.js?v=' . Configure::read('passbolt.version'), ['fullBase' => true, 'cache-version' => Configure::read('passbolt.version')]); -echo $this->Html->script('/js/app/api-recover.js?v=' . Configure::read('passbolt.version'), ['fullBase' => true, 'cache-version' => Configure::read('passbolt.version')]); +echo $this->Html->script('/js/app/api-vendors.js?v=' . $version, ['fullBase' => true, 'cache-version' => $version]); +echo $this->Html->script('/js/app/api-recover.js?v=' . $version, ['fullBase' => true, 'cache-version' => $version]); +$this->end(); + +$this->start('scriptTop'); + +echo $this->Html->script('/js/app/stylesheet.js?v=' . $version, [ + 'id' => 'stylesheet-manager', + 'fullBase' => true, + 'data-file' => 'api_authentication.min.css', + 'cache-version' => $version]); + $this->end(); ?> diff --git a/templates/Setup/start.php b/templates/Setup/start.php index 9f08a1d5f2..7e687936ec 100644 --- a/templates/Setup/start.php +++ b/templates/Setup/start.php @@ -15,13 +15,21 @@ $this->assign('title', $title); $version = Configure::read('passbolt.version'); -$themePath = "themes/default/api_authentication.min.css?v=$version"; -$this->Html->css($themePath, ['block' => 'css', 'fullBase' => true, 'id' => 'js_css_theme']); // See. fetch('scriptBottom') $this->start('scriptBottom'); // Load the javascript application. -echo $this->Html->script('/js/app/api-vendors.js?v=' . Configure::read('passbolt.version'), ['fullBase' => true, 'cache-version' => Configure::read('passbolt.version')]); -echo $this->Html->script('/js/app/api-setup.js?v=' . Configure::read('passbolt.version'), ['fullBase' => true, 'cache-version' => Configure::read('passbolt.version')]); +echo $this->Html->script('/js/app/api-vendors.js?v=' . $version, ['fullBase' => true, 'cache-version' => $version]); +echo $this->Html->script('/js/app/api-setup.js?v=' . $version, ['fullBase' => true, 'cache-version' => $version]); +$this->end(); + +$this->start('scriptTop'); + +echo $this->Html->script('/js/app/stylesheet.js?v=' . $version, [ + 'id' => 'stylesheet-manager', + 'fullBase' => true, + 'data-file' => 'api_authentication.min.css', + 'cache-version' => $version]); + $this->end(); ?> diff --git a/templates/element/Email/content/group_changes_summary.php b/templates/element/Email/content/group_changes_summary.php index 99b7c1f46b..ac93905b24 100644 --- a/templates/element/Email/content/group_changes_summary.php +++ b/templates/element/Email/content/group_changes_summary.php @@ -52,8 +52,8 @@ • - profile->first_name); ?> profile->last_name); ?> - (id]) && $whoIsAdmin[$addedUser->id] ? __('Group manager') : __('Member'); ?>) + + () @@ -66,8 +66,8 @@ • - profile->first_name); ?> profile->last_name); ?> - (id]) && $whoIsAdmin[$removedUser->id] ? __('Group manager') : __('Member'); ?>) + + () @@ -75,14 +75,14 @@
- Updated roles + diff --git a/templates/element/Email/content/group_users_summary.php b/templates/element/Email/content/group_users_summary.php index 2901ea5ad6..65fed5d655 100644 --- a/templates/element/Email/content/group_users_summary.php +++ b/templates/element/Email/content/group_users_summary.php @@ -18,7 +18,7 @@ - +
- profile->first_name); ?> profile->last_name); ?> - id]) && $whoIsAdmin[$updatedUser->id] ? __('is now group manager') : __('is not anymore group manager'); ?> + +
user->profile->first_name); ?> user->profile->last_name); ?> (is_admin ? __('Group manager') : __('Member'); ?>) ()
diff --git a/templates/element/Email/content/user_delete_groups_summary.php b/templates/element/Email/content/user_delete_groups_summary.php index f231640b7a..dd8ed7f4fe 100644 --- a/templates/element/Email/content/user_delete_groups_summary.php +++ b/templates/element/Email/content/user_delete_groups_summary.php @@ -18,7 +18,7 @@ • - name); ?> + diff --git a/templates/element/Email/module/avatar_text.php b/templates/element/Email/module/avatar_text.php index 8dd60819cc..5905fd22fc 100644 --- a/templates/element/Email/module/avatar_text.php +++ b/templates/element/Email/module/avatar_text.php @@ -13,7 +13,9 @@ * @since 2.0.0 */ use App\Utility\Purifier; -?> - ()
+use Cake\I18n\FrozenTime; + +?> + ()

-nice(); ?>
+nice(); ?>
diff --git a/templates/email/html/AN/user_recover.php b/templates/email/html/AN/user_recover.php index 4e1790dbab..bc41a8d3bb 100644 --- a/templates/email/html/AN/user_recover.php +++ b/templates/email/html/AN/user_recover.php @@ -22,12 +22,10 @@ $token = $body['token']; echo $this->element('Email/module/avatar',[ - 'url' => AvatarHelper::getAvatarUrl($user->profile->avatar), + 'url' => AvatarHelper::getAvatarUrl($user['profile']['avatar']), 'text' => $this->element('Email/module/avatar_text', [ - 'username' => Purifier::clean($user->username), - 'first_name' => Purifier::clean($user->profile->first_name), - 'last_name' => Purifier::clean($user->profile->last_name), - 'datetime' => $user->created, + 'user' => $user, + 'datetime' => $user['created'], 'text' => __('You have initiated an account recovery!') ]) ]); diff --git a/templates/email/html/AN/user_register_admin.php b/templates/email/html/AN/user_register_admin.php index 6a9c3d2b20..a1fd9d10f8 100644 --- a/templates/email/html/AN/user_register_admin.php +++ b/templates/email/html/AN/user_register_admin.php @@ -23,19 +23,17 @@ $token = $body['token']; echo $this->element('Email/module/avatar',[ - 'url' => AvatarHelper::getAvatarUrl($user->profile->avatar), + 'url' => AvatarHelper::getAvatarUrl($user['profile']['avatar']), 'text' => $this->element('Email/module/avatar_text', [ - 'username' => Purifier::clean($admin->username), - 'first_name' => Purifier::clean($admin->profile->first_name), - 'last_name' => Purifier::clean($admin->profile->last_name), - 'datetime' => $user->created, - 'text' => __('{0} just created an account for you on passbolt!', Purifier::clean($admin->profile->first_name)) + 'user' => $admin, + 'datetime' => $user['created'], + 'text' => __('{0} just created an account for you on passbolt!', Purifier::clean($admin['profile']['first_name'])) ]) ]); -$text = '

' . __('Welcome {0}', Purifier::clean($user->profile->first_name)) . ',


'; +$text = '

' . __('Welcome {0}', Purifier::clean($user['profile']['first_name'])) . ',


'; $text .= __('{0} just invited you to join passbolt at {1}', - ucfirst(Purifier::clean($admin->profile->first_name)), + ucfirst(Purifier::clean($admin['profile']['first_name'])), '' . Router::url('/',true) . '' ); $text .= ' ' . __('Passbolt is an open source password manager.'); diff --git a/templates/email/html/AN/user_register_self.php b/templates/email/html/AN/user_register_self.php index a748624acd..3f30b42cae 100644 --- a/templates/email/html/AN/user_register_self.php +++ b/templates/email/html/AN/user_register_self.php @@ -22,17 +22,15 @@ $token = $body['token']; echo $this->element('Email/module/avatar',[ - 'url' => AvatarHelper::getAvatarUrl($user->profile->avatar), + 'url' => AvatarHelper::getAvatarUrl($user['profile']['avatar']), 'text' => $this->element('Email/module/avatar_text', [ - 'username' => Purifier::clean($user->username), - 'first_name' => Purifier::clean($user->profile->first_name), - 'last_name' => Purifier::clean($user->profile->last_name), - 'datetime' => $user->created, + 'user' => $user, + 'datetime' => $user['created'], 'text' => __('You just created your account on passbolt!') ]) ]); -$text = '

' . __('Welcome {0}', Purifier::clean($user->profile->first_name)) . ',


'; +$text = '

' . __('Welcome {0}', Purifier::clean($user['profile']['first_name'])) . ',


'; $text .= __('You just opened an account on passbolt at {0}.', '' . Router::url('/',true) . '' ); diff --git a/templates/email/html/GM/group_user_request.php b/templates/email/html/GM/group_user_request.php index f8ae66f33e..8ee627e9c8 100644 --- a/templates/email/html/GM/group_user_request.php +++ b/templates/email/html/GM/group_user_request.php @@ -24,17 +24,15 @@ $groupUsers = $body['groupUsers']; echo $this->element('Email/module/avatar',[ - 'url' => AvatarHelper::getAvatarUrl($admin->profile->avatar), + 'url' => AvatarHelper::getAvatarUrl($admin['profile']['avatar']), 'text' => $this->element('Email/module/avatar_text', [ - 'username' => Purifier::clean($admin->username), - 'first_name' => Purifier::clean($admin->profile->first_name), - 'last_name' => Purifier::clean($admin->profile->last_name), + 'user' => $admin, 'datetime' => FrozenTime::now(), 'text' => __('{0} requested you to add members to a group', null) ]) ]); -$text = __('Group: {0}', Purifier::clean($group->name)) . '

'; +$text = __('Group: {0}', Purifier::clean($group['name'])) . '

'; $text .= ' ' . __('The following members should be added:') . '
'; $text .= $this->element('Email/content/group_users_summary', [ @@ -46,6 +44,6 @@ ]); echo $this->element('Email/module/button', [ - 'url' => Router::url('/app/groups/edit/'. $group->id, true), + 'url' => Router::url('/app/groups/edit/'. $group['id'], true), 'text' => __('Edit group members') ]); diff --git a/templates/email/html/GM/group_user_update.php b/templates/email/html/GM/group_user_update.php index f61d907747..0a00b8118e 100644 --- a/templates/email/html/GM/group_user_update.php +++ b/templates/email/html/GM/group_user_update.php @@ -27,13 +27,11 @@ $whoIsAdmin = $body['whoIsAdmin']; echo $this->element('Email/module/avatar',[ - 'url' => AvatarHelper::getAvatarUrl($admin->profile->avatar), + 'url' => AvatarHelper::getAvatarUrl($admin['profile']['avatar']), 'text' => $this->element('Email/module/avatar_text', [ - 'username' => Purifier::clean($admin->username), - 'first_name' => Purifier::clean($admin->profile->first_name), - 'last_name' => Purifier::clean($admin->profile->last_name), + 'user' => $admin, 'datetime' => FrozenTime::now(), - 'text' => __('{0} updated the group {1}', Purifier::clean($admin->profile->first_name), Purifier::clean($group->name)) + 'text' => __('{0} updated the group {1}', Purifier::clean($admin['profile']['first_name']), Purifier::clean($group['name'])) ]) ]); diff --git a/templates/email/html/GM/user_delete.php b/templates/email/html/GM/user_delete.php index 4e4c3b009e..ac9ba0ec9f 100644 --- a/templates/email/html/GM/user_delete.php +++ b/templates/email/html/GM/user_delete.php @@ -24,20 +24,18 @@ $groups = $body['groups']; echo $this->element('Email/module/avatar',[ - 'url' => AvatarHelper::getAvatarUrl($admin->profile->avatar), + 'url' => AvatarHelper::getAvatarUrl($admin['profile']['avatar']), 'text' => $this->element('Email/module/avatar_text', [ - 'username' => Purifier::clean($admin->username), - 'first_name' => Purifier::clean($admin->profile->first_name), - 'last_name' => Purifier::clean($admin->profile->last_name), + 'user' => $admin, 'datetime' => FrozenTime::now(), - 'text' => __('{0} deleted the user {1}', null, Purifier::clean($user->profile->first_name)) + 'text' => __('{0} deleted the user {1}', null, Purifier::clean($user['profile']['first_name'])) ]) ]); $text = __('The user {0} {1} ({2}) is now deleted from your organisation in passbolt.', - Purifier::clean($user->profile->first_name), - Purifier::clean($user->profile->last_name), - Purifier::clean($user->username) + Purifier::clean($user['profile']['first_name']), + Purifier::clean($user['profile']['last_name']), + Purifier::clean($user['username']) ); $text .= ' ' . __('This user was a member of the following group(s) you manage:') . '
'; diff --git a/templates/email/html/LU/comment_add.php b/templates/email/html/LU/comment_add.php index 2732c72b10..6f7c92f1ef 100644 --- a/templates/email/html/LU/comment_add.php +++ b/templates/email/html/LU/comment_add.php @@ -24,23 +24,21 @@ $showComment = $body['showComment']; echo $this->element('Email/module/avatar',[ - 'url' => AvatarHelper::getAvatarUrl($creator->profile->avatar), + 'url' => AvatarHelper::getAvatarUrl($creator['profile']['avatar']), 'text' => $this->element('Email/module/avatar_text', [ - 'username' => Purifier::clean($creator->username), - 'first_name' => Purifier::clean($creator->profile->first_name), - 'last_name' => Purifier::clean($creator->profile->last_name), - 'datetime' => $comment->created, - 'text' => __('{0} commented on {1}', Purifier::clean($creator->profile->first_name), Purifier::clean($resource->name)) + 'user' => $creator, + 'datetime' => $comment['created'], + 'text' => __('{0} commented on {1}', Purifier::clean($creator['profile']['first_name']), Purifier::clean($resource['name'])) ]) ]); if ($showComment) { echo $this->element('Email/module/text', [ - 'text' => Purifier::clean($comment->content) + 'text' => Purifier::clean($comment['content']) ]); } echo $this->element('Email/module/button', [ - 'url' => Router::url("/app/passwords/view/{$resource->id}", true), + 'url' => Router::url("/app/passwords/view/{$resource['id']}", true), 'text' => __('view it in passbolt') ]); diff --git a/templates/email/html/LU/group_delete.php b/templates/email/html/LU/group_delete.php index fd12d73afa..3ab3c34136 100644 --- a/templates/email/html/LU/group_delete.php +++ b/templates/email/html/LU/group_delete.php @@ -22,18 +22,16 @@ $group = $body['group']; echo $this->element('Email/module/avatar',[ - 'url' => AvatarHelper::getAvatarUrl($admin->profile->avatar), + 'url' => AvatarHelper::getAvatarUrl($admin['profile']['avatar']), 'text' => $this->element('Email/module/avatar_text', [ - 'username' => Purifier::clean($admin->username), - 'first_name' => Purifier::clean($admin->profile->first_name), - 'last_name' => Purifier::clean($admin->profile->last_name), - 'datetime' => $group->modified, - 'text' => __('{0} deleted a group', Purifier::clean($admin->profile->first_name)) + 'user' => $admin, + 'datetime' => $group['modified'], + 'text' => __('{0} deleted a group', Purifier::clean($admin['profile']['first_name'])) ]) ]); $text = __('{0} deleted the group "{1}" you were a member of.', - Purifier::clean($admin->profile->first_name), Purifier::clean($group->name) + Purifier::clean($admin['profile']['first_name']), Purifier::clean($group['name']) ); $text .= ' ' . __('All passwords that were shared only with this group were also deleted.'); diff --git a/templates/email/html/LU/group_user_add.php b/templates/email/html/LU/group_user_add.php index 2ba0294b82..7f170c17f9 100644 --- a/templates/email/html/LU/group_user_add.php +++ b/templates/email/html/LU/group_user_add.php @@ -24,13 +24,11 @@ $isAdmin = $body['isAdmin']; echo $this->element('Email/module/avatar',[ - 'url' => AvatarHelper::getAvatarUrl($admin->profile->avatar), + 'url' => AvatarHelper::getAvatarUrl($admin['profile']['avatar']), 'text' => $this->element('Email/module/avatar_text', [ - 'username' => Purifier::clean($admin->username), - 'first_name' => Purifier::clean($admin->profile->first_name), - 'last_name' => Purifier::clean($admin->profile->last_name), + 'user' => $admin, 'datetime' => FrozenTime::now(), - 'text' => __('{0} added you to the group {1}', Purifier::clean($admin->profile->first_name), Purifier::clean($group->name)) + 'text' => __('{0} added you to the group {1}', Purifier::clean($admin['profile']['first_name']), Purifier::clean($group['name'])) ]) ]); diff --git a/templates/email/html/LU/group_user_delete.php b/templates/email/html/LU/group_user_delete.php index 8334586f69..0fb5550766 100644 --- a/templates/email/html/LU/group_user_delete.php +++ b/templates/email/html/LU/group_user_delete.php @@ -23,20 +23,18 @@ $group = $body['group']; echo $this->element('Email/module/avatar',[ - 'url' => AvatarHelper::getAvatarUrl($admin->profile->avatar), + 'url' => AvatarHelper::getAvatarUrl($admin['profile']['avatar']), 'text' => $this->element('Email/module/avatar_text', [ - 'username' => Purifier::clean($admin->username), - 'first_name' => Purifier::clean($admin->profile->first_name), - 'last_name' => Purifier::clean($admin->profile->last_name), + 'user' => $admin, 'datetime' => FrozenTime::now(), - 'text' => __('{0} removed you from the group {1}', Purifier::clean($admin->profile->first_name), Purifier::clean($group->name)) + 'text' => __('{0} removed you from the group {1}', Purifier::clean($admin['profile']['first_name']), Purifier::clean($group['name'])) ]) ]); $text = __('You are no longer a member of this group.'); $text .= ' ' . __('You are no longer authorized to access the passwords shared with this group.'); $text .= ' ' . __('Please contact {0} or another group manager if this is a mistake.', - Purifier::clean($admin->profile->first_name) + Purifier::clean($admin['profile']['first_name']) ); echo $this->element('Email/module/text', [ diff --git a/templates/email/html/LU/group_user_update.php b/templates/email/html/LU/group_user_update.php index bd30ba59e6..1a650677c0 100644 --- a/templates/email/html/LU/group_user_update.php +++ b/templates/email/html/LU/group_user_update.php @@ -24,15 +24,13 @@ $isAdmin = $body['isAdmin']; echo $this->element('Email/module/avatar',[ - 'url' => AvatarHelper::getAvatarUrl($admin->profile->avatar), + 'url' => AvatarHelper::getAvatarUrl($admin['profile']['avatar']), 'text' => $this->element('Email/module/avatar_text', [ - 'username' => Purifier::clean($admin->username), - 'first_name' => Purifier::clean($admin->profile->first_name), - 'last_name' => Purifier::clean($admin->profile->last_name), + 'user' => $admin, 'datetime' => FrozenTime::now(), 'text' => __('{0} updated your membership in the group {1}', - Purifier::clean($admin->profile->first_name), - Purifier::clean($group->name) + Purifier::clean($admin['profile']['first_name']), + Purifier::clean($group['name']) ) ]) ]); diff --git a/templates/email/html/LU/group_users_change.php b/templates/email/html/LU/group_users_change.php index 37edaf85d5..8675c82b84 100644 --- a/templates/email/html/LU/group_users_change.php +++ b/templates/email/html/LU/group_users_change.php @@ -24,11 +24,9 @@ $count = $body['count']; echo $this->element('Email/module/avatar',[ - 'url' => AvatarHelper::getAvatarUrl($user->profile->avatar), + 'url' => AvatarHelper::getAvatarUrl($user['profile']['avatar']), 'text' => $this->element('Email/module/avatar_text', [ - 'username' => Purifier::clean($user->username), - 'first_name' => Purifier::clean($user->profile->first_name), - 'last_name' => Purifier::clean($user->profile->last_name), + 'user' => $user, 'datetime' => new FrozenTime(), 'text' => __('Edited your membership in several groups') ]) diff --git a/templates/email/html/LU/groups_delete.php b/templates/email/html/LU/groups_delete.php index e3f3182b82..c9e9f176d3 100644 --- a/templates/email/html/LU/groups_delete.php +++ b/templates/email/html/LU/groups_delete.php @@ -23,17 +23,15 @@ $count = $body['count']; echo $this->element('Email/module/avatar',[ - 'url' => AvatarHelper::getAvatarUrl($admin->profile->avatar), + 'url' => AvatarHelper::getAvatarUrl($admin['profile']['avatar']), 'text' => $this->element('Email/module/avatar_text', [ - 'username' => Purifier::clean($admin->username), - 'first_name' => Purifier::clean($admin->profile->first_name), - 'last_name' => Purifier::clean($admin->profile->last_name), - 'datetime' => $group->modified, - 'text' => __('{0} deleted several group', Purifier::clean($admin->profile->first_name)) + 'user' => $admin, + 'datetime' => $group['modified'], + 'text' => __('{0} deleted several group', Purifier::clean($admin['profile']['first_name'])) ]) ]); -$text = __('{0} deleted {1} groups you were a member of.', Purifier::clean($admin->profile->first_name), $count) . ' '; +$text = __('{0} deleted {1} groups you were a member of.', Purifier::clean($admin['profile']['first_name']), $count) . ' '; $text .= __('All passwords that were shared only with this group were also deleted.') . ' '; $text .= __('It would be too much to list them here, but you can get more information on passbolt.') ; diff --git a/templates/email/html/LU/resource_create.php b/templates/email/html/LU/resource_create.php index bbf597363a..40b9c85fcf 100644 --- a/templates/email/html/LU/resource_create.php +++ b/templates/email/html/LU/resource_create.php @@ -26,36 +26,34 @@ $showSecret = $body['showSecret']; echo $this->element('Email/module/avatar',[ - 'url' => AvatarHelper::getAvatarUrl($user->profile->avatar), + 'url' => AvatarHelper::getAvatarUrl($user['profile']['avatar']), 'text' => $this->element('Email/module/avatar_text', [ - 'username' => Purifier::clean($user->username), - 'first_name' => Purifier::clean($user->profile->first_name), - 'last_name' => Purifier::clean($user->profile->last_name), - 'datetime' => $resource->created, + 'user' => $user, + 'datetime' => $resource['created'], 'text' => __('You have saved a new password') ]) ]); -$text = __('Name: {0}', Purifier::clean($resource->name)) . '
'; +$text = __('Name: {0}', Purifier::clean($resource['name'])) . '
'; if ($showUsername) { - $text .= __('Username: {0}', Purifier::clean($resource->username)) . '
'; + $text .= __('Username: {0}', Purifier::clean($resource['username'])) . '
'; } if ($showUri) { - $text .= __('URL: {0}', Purifier::clean($resource->uri)) . '
'; + $text .= __('URL: {0}', Purifier::clean($resource['uri'])) . '
'; } -if ($showDescription) { - $text .= __('Description: {0}', Purifier::clean($resource->description)) . '
'; +if ($showDescription && isset($resource['description'])) { + $text .= __('Description: {0}', Purifier::clean($resource['description'])) . '
'; } echo $this->element('Email/module/text', [ 'text' => $text ]); if ($showSecret) { echo $this->element('Email/module/code', [ - 'text' => $resource->secrets[0]->data + 'text' => $resource['secrets'][0]['data'] ]); } echo $this->element('Email/module/button', [ - 'url' => Router::url("/app/passwords/view/{$resource->id}", true), + 'url' => Router::url("/app/passwords/view/{$resource['id']}", true), 'text' => __('view it in passbolt') ]); diff --git a/templates/email/html/LU/resource_delete.php b/templates/email/html/LU/resource_delete.php index 30948af3da..c2119e7aff 100644 --- a/templates/email/html/LU/resource_delete.php +++ b/templates/email/html/LU/resource_delete.php @@ -25,26 +25,24 @@ $showDescription = $body['showDescription']; echo $this->element('Email/module/avatar',[ - 'url' => AvatarHelper::getAvatarUrl($user->profile->avatar), + 'url' => AvatarHelper::getAvatarUrl($user['profile']['avatar']), 'text' => $this->element('Email/module/avatar_text', [ - 'username' => Purifier::clean($user->username), - 'first_name' => Purifier::clean($user->profile->first_name), - 'last_name' => Purifier::clean($user->profile->last_name), - 'datetime' => $resource->modified, - 'text' => __('{0} deleted the password {1}', Purifier::clean($user->profile->first_name), Purifier::clean($resource->name)) + 'user' => $user, + 'datetime' => $resource['modified'], + 'text' => __('{0} deleted the password {1}', Purifier::clean($user['profile']['first_name']), Purifier::clean($resource['name'])) ]) ]); -$text = __('Name: {0}', Purifier::clean($resource->name)) . '
'; +$text = __('Name: {0}', Purifier::clean($resource['name'])) . '
'; if ($showUsername) { - $text .= __('Username: {0}', Purifier::clean($resource->username)) . '
'; + $text .= __('Username: {0}', Purifier::clean($resource['username'])) . '
'; } if ($showUri) { - $text .= __('URL: {0}', Purifier::clean($resource->uri)) . '
'; + $text .= __('URL: {0}', Purifier::clean($resource['uri'])) . '
'; } -if ($showDescription) { - $text .= __('Description: {0}', Purifier::clean($resource->description)) . '
'; +if ($showDescription && isset($resource['description'])) { + $text .= __('Description: {0}', Purifier::clean($resource['description'])) . '
'; } echo $this->element('Email/module/text', [ 'text' => $text diff --git a/templates/email/html/LU/resource_share.php b/templates/email/html/LU/resource_share.php index 3198fde0ff..40d67678f0 100644 --- a/templates/email/html/LU/resource_share.php +++ b/templates/email/html/LU/resource_share.php @@ -28,26 +28,24 @@ $showSecret = $body['showSecret']; echo $this->element('Email/module/avatar',[ - 'url' => AvatarHelper::getAvatarUrl($owner->profile->avatar), + 'url' => AvatarHelper::getAvatarUrl($owner['profile']['avatar']), 'text' => $this->element('Email/module/avatar_text', [ - 'username' => Purifier::clean($owner->username), - 'first_name' => Purifier::clean($owner->profile->first_name), - 'last_name' => Purifier::clean($owner->profile->last_name), + 'user' => $owner, 'datetime' => FrozenTime::now(), - 'text' => __('{0} shared a password with you', Purifier::clean($owner->profile->first_name)) + 'text' => __('{0} shared a password with you', Purifier::clean($owner['profile']['first_name'])) ]) ]); -$text = __('Name: {0}', Purifier::clean($resource->name)) . '
'; +$text = __('Name: {0}', Purifier::clean($resource['name'])) . '
'; if ($showUsername) { - $text .= __('Username: {0}', Purifier::clean($resource->username)) . '
'; + $text .= __('Username: {0}', Purifier::clean($resource['username'])) . '
'; } if ($showUri) { - $text .= __('URL: {0}', Purifier::clean($resource->uri)) . '
'; + $text .= __('URL: {0}', Purifier::clean($resource['uri'])) . '
'; } -if ($showDescription) { - $text .= __('Description: {0}', Purifier::clean($resource->description)) . '
'; +if ($showDescription && isset($resource['description'])) { + $text .= __('Description: {0}', Purifier::clean($resource['description'])) . '
'; } echo $this->element('Email/module/text', [ 'text' => $text @@ -58,6 +56,6 @@ ]); } echo $this->element('Email/module/button', [ - 'url' => Router::url("/app/passwords/view/{$resource->id}", true), + 'url' => Router::url("/app/passwords/view/{$resource['id']}", true), 'text' => __('view it in passbolt') ]); diff --git a/templates/email/html/LU/resource_update.php b/templates/email/html/LU/resource_update.php index 827fd21b8c..aa5622fe8b 100644 --- a/templates/email/html/LU/resource_update.php +++ b/templates/email/html/LU/resource_update.php @@ -26,36 +26,34 @@ $showSecret = $body['showSecret']; echo $this->element('Email/module/avatar',[ - 'url' => AvatarHelper::getAvatarUrl($user->profile->avatar), + 'url' => AvatarHelper::getAvatarUrl($user['profile']['avatar']), 'text' => $this->element('Email/module/avatar_text', [ - 'username' => Purifier::clean($user->username), - 'first_name' => Purifier::clean($user->profile->first_name), - 'last_name' => Purifier::clean($user->profile->last_name), - 'datetime' => $resource->modified, - 'text' => __('{0} updated the password {1}', Purifier::clean($user->profile->first_name), Purifier::clean($resource->name)) + 'user' => $user, + 'datetime' => $resource['modified'], + 'text' => __('{0} updated the password {1}', Purifier::clean($user['profile']['first_name']), Purifier::clean($resource['name'])) ]) ]); -$text = __('Name: {0}', Purifier::clean($resource->name)) . '
'; +$text = __('Name: {0}', Purifier::clean($resource['name'])) . '
'; if ($showUsername) { - $text .= __('Username: {0}', Purifier::clean($resource->username)) . '
'; + $text .= __('Username: {0}', Purifier::clean($resource['username'])) . '
'; } if ($showUri) { - $text .= __('URL: {0}', Purifier::clean($resource->uri)) . '
'; + $text .= __('URL: {0}', Purifier::clean($resource['uri'])) . '
'; } -if ($showDescription) { - $text .= __('Description: {0}', Purifier::clean($resource->description)) . '
'; +if ($showDescription && isset($resource['description'])) { + $text .= __('Description: {0}', Purifier::clean($resource['description'])) . '
'; } echo $this->element('Email/module/text', [ 'text' => $text ]); -if ($showSecret && isset($resource->secrets[0]->data)) { +if ($showSecret && isset($resource['secrets'][0]['data'])) { echo $this->element('Email/module/code', [ - 'text' => $resource->secrets[0]->data + 'text' => $resource['secrets'][0]['data'] ]); } echo $this->element('Email/module/button', [ - 'url' => Router::url("/app/passwords/view/{$resource->id}", true), + 'url' => Router::url("/app/passwords/view/{$resource['id']}", true), 'text' => __('view it in passbolt') ]); diff --git a/templates/email/html/LU/resources_change.php b/templates/email/html/LU/resources_change.php index 6c2a5e2666..a4c504d323 100644 --- a/templates/email/html/LU/resources_change.php +++ b/templates/email/html/LU/resources_change.php @@ -24,11 +24,9 @@ $count = $body['count']; echo $this->element('Email/module/avatar',[ - 'url' => AvatarHelper::getAvatarUrl($user->profile->avatar), + 'url' => AvatarHelper::getAvatarUrl($user['profile']['avatar']), 'text' => $this->element('Email/module/avatar_text', [ - 'username' => Purifier::clean($user->username), - 'first_name' => Purifier::clean($user->profile->first_name), - 'last_name' => Purifier::clean($user->profile->last_name), + 'user' => $user, 'datetime' => new FrozenTime(), 'text' => __('Edited multiple resources') ]) diff --git a/templates/email/html/LU/resources_share.php b/templates/email/html/LU/resources_share.php index c007e0fd78..b9f20eacf9 100644 --- a/templates/email/html/LU/resources_share.php +++ b/templates/email/html/LU/resources_share.php @@ -27,13 +27,11 @@ $count = $body['count']; echo $this->element('Email/module/avatar',[ - 'url' => AvatarHelper::getAvatarUrl($user->profile->avatar), + 'url' => AvatarHelper::getAvatarUrl($owner['profile']['avatar']), 'text' => $this->element('Email/module/avatar_text', [ - 'username' => Purifier::clean($owner->username), - 'first_name' => Purifier::clean($owner->profile->first_name), - 'last_name' => Purifier::clean($owner->profile->last_name), + 'user' => $owner, 'datetime' => FrozenTime::now(), - 'text' => __('{0} shared passwords with you', $owner->profile->first_name) + 'text' => __('{0} shared passwords with you', $owner['profile']['first_name']) ]) ]); diff --git a/templates/email/html/LU/user_setup_complete.php b/templates/email/html/LU/user_setup_complete.php index ce7c40c642..8658385087 100644 --- a/templates/email/html/LU/user_setup_complete.php +++ b/templates/email/html/LU/user_setup_complete.php @@ -38,13 +38,11 @@ echo $this->element('Email/module/avatar',[ 'url' => Router::url($avatar, true), 'text' => $this->element('Email/module/avatar_text', [ - 'username' => Purifier::clean($user->username), - 'first_name' => Purifier::clean($user->profile->first_name), - 'last_name' => Purifier::clean($user->profile->last_name), - 'datetime' => $user->modified, + 'user' => $user, + 'datetime' => $user['modified'], 'text' => __( '{0} just activated their account on passbolt!', - $user->profile->first_name + $user['profile']['first_name'] ) ]) ]); @@ -53,10 +51,10 @@ $text .= ' '; if ($invitedByYou) { $text .= __('This user was invited by you {0}.', $invitedWhen); -} else if ($user->username === $invitedBy->username) { +} else if ($user['username'] === $invitedBy['username']) { $text .= __('This user signed up themselves, since the public registration is enabled.'); } else { - $text .= __('This user was invited by {0} {1}.', $invitedBy->profile->first_name, $invitedWhen); + $text .= __('This user was invited by {0} {1}.', $invitedBy['profile']['first_name'], $invitedWhen); } $text .= '
'; echo $this->element('Email/module/text', [ diff --git a/templates/layout/default.php b/templates/layout/default.php index d47e3eadaf..c35466cdf9 100644 --- a/templates/layout/default.php +++ b/templates/layout/default.php @@ -22,6 +22,7 @@ <?= Configure::read('passbolt.meta.title'); ?> | <?= $this->fetch('title') ?> element('Header/meta') ?> fetch('css') ?> + fetch('scriptTop') ?> diff --git a/templates/layout/email/html/default.php b/templates/layout/email/html/default.php index 65ea45e8be..bb1dea996e 100644 --- a/templates/layout/email/html/default.php +++ b/templates/layout/email/html/default.php @@ -249,8 +249,8 @@
- This email is an automatic notification sent by . - You can disable these notifications by requesting an administrator to delete your account. + . +
diff --git a/tests/Factory/AuthenticationTokenFactory.php b/tests/Factory/AuthenticationTokenFactory.php new file mode 100644 index 0000000000..26bf194d62 --- /dev/null +++ b/tests/Factory/AuthenticationTokenFactory.php @@ -0,0 +1,137 @@ +setDefaultData(function (Generator $faker) { + return [ + 'token' => $faker->uuid(), + 'user_id' => $faker->uuid(), + 'type' => AuthenticationToken::TYPE_LOGIN, + 'data' => null, + 'active' => $faker->boolean(), + 'created' => Chronos::now(), + 'modified' => Chronos::now(), + ]; + }); + } + + /** + * @param ChronosInterface $modified token type + * @return $this + */ + public function modified(ChronosInterface $modified) + { + return $this->patchData(compact('modified')); + } + + /** + * @param ChronosInterface $created token type + * @return $this + */ + public function created(ChronosInterface $created) + { + return $this->patchData(compact('created')); + } + + /** + * @return $this + */ + public function expired() + { + return $this->created(new FrozenDate('5 years ago')); + } + + /** + * @param string $type token type + * @return $this + */ + public function type(string $type) + { + return $this->patchData(compact('type')); + } + + /** + * @param array $data token type + * @return $this + */ + public function data(array $data) + { + $data = empty($data) ? null : json_encode($data); + + return $this->patchData(compact('data')); + } + + /** + * @return $this + */ + public function inactive() + { + return $this->patchData(['active' => false]); + } + + /** + * @return $this + */ + public function active() + { + return $this->patchData(['active' => true]); + } + + /** + * @param string $userId user ID + * @return $this + */ + public function userId(string $userId) + { + return $this->patchData(['user_id' => $userId]); + } +} diff --git a/tests/Factory/AvatarFactory.php b/tests/Factory/AvatarFactory.php index 8ad5254163..a7ec9c5fcc 100644 --- a/tests/Factory/AvatarFactory.php +++ b/tests/Factory/AvatarFactory.php @@ -1,6 +1,19 @@ without('Avatars'); + } + + return $this->with('Profiles', $profileFactory); + } + + /** + * @param UserFactory|null $userFactory Associated user + * @return $this + */ + public function withUser(?UserFactory $userFactory = null) + { + if (!isset($userFactory)) { + $userFactory = UserFactory::make()->with( + 'Profiles', + ProfileFactory::make()->without('Avatars') + ); + } + + return $this->with('Profiles.Users', $userFactory); + } } diff --git a/tests/Factory/FactoryHelperTrait.php b/tests/Factory/FactoryHelperTrait.php new file mode 100644 index 0000000000..c5f494391a --- /dev/null +++ b/tests/Factory/FactoryHelperTrait.php @@ -0,0 +1,28 @@ +patchData(['deleted' => true]); + } +} diff --git a/tests/Factory/FavoriteFactory.php b/tests/Factory/FavoriteFactory.php index f0454b2be4..1410bdcf83 100644 --- a/tests/Factory/FavoriteFactory.php +++ b/tests/Factory/FavoriteFactory.php @@ -1,6 +1,19 @@ setDefaultData(function (Generator $faker) { return [ - 'user_id' => $faker->uuid, - 'foreign_key' => $faker->uuid, - 'foreign_model' => $faker->uuid, + 'user_id' => $faker->uuid(), + 'foreign_key' => $faker->uuid(), + 'foreign_model' => $faker->uuid(), 'created' => Chronos::now()->subDay($faker->randomNumber(4)), 'modified' => Chronos::now()->subDay($faker->randomNumber(4)), ]; diff --git a/tests/Factory/GpgkeyFactory.php b/tests/Factory/GpgkeyFactory.php index a0a0cb13b2..14ea60cba2 100644 --- a/tests/Factory/GpgkeyFactory.php +++ b/tests/Factory/GpgkeyFactory.php @@ -1,8 +1,22 @@ $faker->text(), 'uid' => $faker->text(128), 'key_id' => $faker->text(16), - 'fingerprint' => $faker->text(51), + 'fingerprint' => $faker->shuffle('ABCDE12345ABCDE12345ABCDE12345ABCDE12345'), // 40 character random upper case ]; }); } + + /** + * Set the armored key and fingerprint as found in config + * + * @return $this + */ + public function validFingerprint() + { + return $this->patchData([ + 'armored_key' => file_get_contents(Configure::read('passbolt.gpg.serverKey.private')), + 'fingerprint' => Configure::read('passbolt.gpg.serverKey.fingerprint'), + ]); + } } diff --git a/tests/Factory/GroupsUserFactory.php b/tests/Factory/GroupsUserFactory.php index ece43dc2c4..01a5c992b1 100644 --- a/tests/Factory/GroupsUserFactory.php +++ b/tests/Factory/GroupsUserFactory.php @@ -1,6 +1,19 @@ setDefaultData(function (Generator $faker) { return [ - 'created_by' => $faker->uuid, - 'modified_by' => $faker->uuid, + 'created_by' => $faker->uuid(), + 'modified_by' => $faker->uuid(), ]; }); } @@ -59,4 +59,13 @@ public function locale(string $value) { return $this->setPropertyValue('locale', $value); } + + /** + * @param $value + * @return $this + */ + public function value($value) + { + return $this->patchData(['value' => json_encode($value)]); + } } diff --git a/tests/Factory/PermissionFactory.php b/tests/Factory/PermissionFactory.php index 4a443dc2d3..1994d463cb 100644 --- a/tests/Factory/PermissionFactory.php +++ b/tests/Factory/PermissionFactory.php @@ -1,6 +1,19 @@ $faker->text(30), 'type' => rand(), - 'aco_foreign_key' => $faker->uuid, + 'aco_foreign_key' => $faker->uuid(), 'created' => Chronos::now(), 'modified' => Chronos::now(), ]; diff --git a/tests/Factory/ProfileFactory.php b/tests/Factory/ProfileFactory.php index 4f3ca7134c..019002080b 100644 --- a/tests/Factory/ProfileFactory.php +++ b/tests/Factory/ProfileFactory.php @@ -1,6 +1,19 @@ setDefaultData(function (Generator $faker) { return [ - 'first_name' => $faker->firstNameFemale, - 'last_name' => $faker->lastName, - 'user_id' => $faker->uuid, + 'first_name' => $faker->firstNameFemale(), + 'last_name' => $faker->lastName(), + 'user_id' => $faker->uuid(), 'created' => Chronos::now()->subDay($faker->randomNumber(4)), 'modified' => Chronos::now()->subDay($faker->randomNumber(4)), ]; diff --git a/tests/Factory/ResourceFactory.php b/tests/Factory/ResourceFactory.php index 2234a63af6..0783e23c95 100644 --- a/tests/Factory/ResourceFactory.php +++ b/tests/Factory/ResourceFactory.php @@ -1,16 +1,33 @@ setDefaultData(function (Generator $faker) { return [ 'name' => $faker->text(64), - 'username' => $faker->email, - 'uri' => $faker->url, - 'created_by' => $faker->uuid, - 'modified_by' => $faker->uuid, + 'username' => $faker->email(), + 'uri' => $faker->url(), + 'created_by' => $faker->uuid(), + 'modified_by' => $faker->uuid(), 'created' => Chronos::now()->subDay($faker->randomNumber(4)), 'modified' => Chronos::now()->subDay($faker->randomNumber(4)), ]; }); } - /** - * @param UserFactory $factory - * @return ResourceFactory - */ - public function withCreator(UserFactory $factory): self - { - return $this->with('Creator', $factory); - } - /** * Associates a previously persisted user with ACO permission. * - * @param User $creator Persisted creator + * @param \App\Model\Entity\User $creator Persisted creator * @return $this */ - public function withCreatorAndPermission(EntityInterface $creator) + public function withCreatorAndPermission(User $creator) { $aco = PermissionsTable::RESOURCE_ACO; $aro_foreign_key = $creator->id; diff --git a/tests/Factory/ResourceTypeFactory.php b/tests/Factory/ResourceTypeFactory.php new file mode 100644 index 0000000000..2b73d3a862 --- /dev/null +++ b/tests/Factory/ResourceTypeFactory.php @@ -0,0 +1,45 @@ +setDefaultData(function (Generator $faker) { + return [ + 'name' => $faker->text(64), + 'slug' => $faker->text(64), + ]; + }); + } + + public function default(): self + { + return $this->patchData(['id' => ResourceTypesTable::getDefaultTypeId()]); + } +} diff --git a/tests/Factory/RoleFactory.php b/tests/Factory/RoleFactory.php index 0a3cb0192c..c8022f9953 100644 --- a/tests/Factory/RoleFactory.php +++ b/tests/Factory/RoleFactory.php @@ -1,9 +1,23 @@ setDefaultData(function (Generator $faker) { return [ - 'name' => $faker->name, + 'name' => $faker->name(), + 'created' => Chronos::now()->subDay($faker->randomNumber(4)), + 'modified' => Chronos::now()->subDay($faker->randomNumber(4)), ]; }); } diff --git a/tests/Factory/SecretFactory.php b/tests/Factory/SecretFactory.php new file mode 100644 index 0000000000..1db3a8c831 --- /dev/null +++ b/tests/Factory/SecretFactory.php @@ -0,0 +1,82 @@ +setDefaultData(function (Generator $faker) { + return [ + 'data' => $this->getValidSecret(), + ]; + }); + + $this + ->with('Users') + ->with('Resources'); + } + + /** + * Produces a valid secret + * + * @return string + */ + protected function getValidSecret(): string + { + return "-----BEGIN PGP MESSAGE----- +Version: GnuPG v1.4.12 (GNU/Linux) + +hQEMAwvNmZMMcWZiAQf9HpfcNeuC5W/VAzEtAe8mTBUk1vcJENtGpMyRkVTC8KbQ +xaEr3+UG6h0ZVzfrMFYrYLolS3fie83cj4FnC3gg1uijo7zTf9QhJMdi7p/ASB6N +y7//8AriVqUAOJ2WCxAVseQx8qt2KqkQvS7F7iNUdHfhEhiHkczTlehyel7PEeas +SdM/kKEsYKk6i4KLPBrbWsflFOkfQGcPL07uRK3laFz8z4LNzvNQOoU7P/C1L0X3 +tlK3vuq+r01zRwmflCaFXaHVifj3X74ljhlk5i/JKLoPRvbxlPTevMNag5e6QhPQ +kpj+TJD2frfGlLhyM50hQMdJ7YVypDllOBmnTRwZ0tJFAXm+F987ovAVLMXGJtGO +P+b3c493CfF0fQ1MBYFluVK/Wka8usg/b0pNkRGVWzBcZ1BOONYlOe/JmUyMutL5 +hcciUFw5 +=TcQF +-----END PGP MESSAGE-----"; + } +} diff --git a/tests/Factory/UserFactory.php b/tests/Factory/UserFactory.php index 4bf47e0740..d5ff74fef0 100644 --- a/tests/Factory/UserFactory.php +++ b/tests/Factory/UserFactory.php @@ -1,10 +1,25 @@ setDefaultData(function (Generator $faker) { return [ - 'username' => $faker->userName . '@passbolt.com', + 'username' => $faker->userName() . '@passbolt.com', 'active' => true, 'deleted' => false, - 'created' => Chronos::now()->subDay($faker->randomNumber(4)), - 'modified' => Chronos::now()->subDay($faker->randomNumber(4)), + 'created' => Time::now()->subDay($faker->randomNumber(4)), + 'modified' => Time::now()->subDay($faker->randomNumber(4)), ]; }); @@ -121,4 +142,53 @@ public function withLocale(string $locale) AccountSettingFactory::make()->locale($locale) ); } + + /** + * Return a non persisted UAC + * + * @return UserAccessControl + */ + public function nonPersistedUAC(): UserAccessControl + { + return $this->makeUserAccessControl($this->getEntity()); + } + + /** + * Persist and return UAC + * + * @return UserAccessControl + */ + public function persistedUAC(): UserAccessControl + { + return $this->makeUserAccessControl($this->persist()); + } + + /** + * @param User $user User + * @return UserAccessControl UAC + */ + private function makeUserAccessControl(User $user): UserAccessControl + { + return new UserAccessControl($user->role->name, $user->get('id'), $user->get('username')); + } + + /** + * @param AuthenticationTokenFactory $factory Authentication token + * @return UserFactory this + */ + public function withAuthenticationTokens(AuthenticationTokenFactory $factory): self + { + return $this->with('AuthenticationTokens', $factory); + } + + /** + * @param string $filename File to import + * @return UserFactory this + */ + public function withAvatar(string $filename = FIXTURES . 'Avatar' . DS . 'ada.jpg'): self + { + return $this->with('Profiles.Avatars', [ + 'data' => file_get_contents($filename), + ]); + } } diff --git a/tests/Fixture/Avatar/ada.gif b/tests/Fixture/Avatar/ada.gif new file mode 100644 index 0000000000..7c20c1d404 Binary files /dev/null and b/tests/Fixture/Avatar/ada.gif differ diff --git a/tests/Fixture/Avatar/ada.jpeg b/tests/Fixture/Avatar/ada.jpeg new file mode 100644 index 0000000000..18ff130c1a Binary files /dev/null and b/tests/Fixture/Avatar/ada.jpeg differ diff --git a/tests/Fixture/Avatar/ada.jpg b/tests/Fixture/Avatar/ada.jpg new file mode 100644 index 0000000000..18ff130c1a Binary files /dev/null and b/tests/Fixture/Avatar/ada.jpg differ diff --git a/tests/Lib/AppIntegrationTestCase.php b/tests/Lib/AppIntegrationTestCase.php index 465a165001..6dd6188a73 100644 --- a/tests/Lib/AppIntegrationTestCase.php +++ b/tests/Lib/AppIntegrationTestCase.php @@ -16,10 +16,12 @@ */ namespace App\Test\Lib; -use App\Model\Entity\Role; +use App\Authenticator\AbstractSessionIdentificationService; +use App\Authenticator\SessionIdentificationServiceInterface; +use App\Middleware\CsrfProtectionMiddleware; use App\Model\Entity\User; use App\Test\Factory\UserFactory; -use App\Test\Lib\Model\AvatarsModelTestTrait; +use App\Test\Lib\Model\AvatarsModelTrait; use App\Test\Lib\Model\GpgkeysModelTrait; use App\Test\Lib\Model\PermissionsModelTrait; use App\Test\Lib\Model\ProfilesModelTrait; @@ -32,17 +34,24 @@ use App\Test\Lib\Utility\ErrorTrait; use App\Test\Lib\Utility\JsonRequestTrait; use App\Test\Lib\Utility\ObjectTrait; +use App\Utility\Application\FeaturePluginAwareTrait; +use App\Utility\OpenPGP\OpenPGPBackendFactory; +use App\Utility\UserAction; use App\Utility\UuidFactory; use Cake\Core\Configure; +use Cake\ORM\TableRegistry; use Cake\TestSuite\IntegrationTestTrait; use Cake\TestSuite\TestCase; +use CakephpFixtureFactories\Scenario\ScenarioAwareTrait; +use Passbolt\EmailDigest\Utility\Digest\DigestsPool; abstract class AppIntegrationTestCase extends TestCase { use ArrayTrait; - use AvatarsModelTestTrait; + use AvatarsModelTrait; use EntityTrait; use ErrorTrait; + use FeaturePluginAwareTrait; use GpgkeysModelTrait; use IntegrationTestTrait; use JsonRequestTrait; @@ -51,6 +60,7 @@ abstract class AppIntegrationTestCase extends TestCase use ProfilesModelTrait; use ResourcesModelTrait; use RolesModelTrait; + use ScenarioAwareTrait; use SecretsModelTrait; use UsersModelTrait; @@ -60,9 +70,14 @@ abstract class AppIntegrationTestCase extends TestCase public function setUp(): void { parent::setUp(); + $this->cleanup(); $this->enableCsrfToken(); $this->loadRoutes(); Configure::write('passbolt.plugins.log.enabled', false); + Configure::write(CsrfProtectionMiddleware::PASSBOLT_SECURITY_CSRF_PROTECTION_ACTIVE_CONFIG, true); + OpenPGPBackendFactory::reset(); + UserAction::destroy(); + DigestsPool::clearInstance(); } /** @@ -79,24 +94,36 @@ public function tearDown(): void * * @param string $userFirstName The user first name. * @return void + * @deprecated use logInAs. */ public function authenticateAs($userFirstName) { - $data = [ - 'id' => UuidFactory::uuid('user.id.' . $userFirstName), - 'username' => $userFirstName . '@passbolt.com', - 'profile' => [ - 'first_name' => $userFirstName, - 'last_name' => 'testing', - ], - 'role' => [ - 'name' => Role::USER, - ], - ]; - if ($userFirstName === 'admin') { - $data['role']['name'] = Role::ADMIN; + $userId = UuidFactory::uuid('user.id.' . $userFirstName); + $Users = TableRegistry::getTableLocator()->get('Users'); + $user = $Users->find() + ->where(['Users.id' => $userId]) + ->contain(['Profiles', 'Roles']) + ->first(); + + if ($user === null) { + $user = UserFactory::make([ + 'id' => $userId, + 'username' => $userFirstName . '@passbolt.com', + 'profile' => [ + 'first_name' => $userFirstName, + 'last_name' => 'testing', + ], + ]); + if ($userFirstName === 'admin') { + $user->admin(); + } else { + $user->user(); + } + + $user = $user->persist(); } - $this->session(['Auth' => $data]); + + $this->logInAs($user); } /** @@ -104,7 +131,7 @@ public function authenticateAs($userFirstName) */ public function logInAs(User $user) { - $this->session(['Auth' => $user->toArray()]); + $this->session(['Auth' => compact('user')]); } /** @@ -140,4 +167,37 @@ public function disableCsrfToken() { $this->_csrfToken = false; } + + /** + * Injects in the DIC a session identification Interface with the provided ID. + * In Session, will return the session ID + * In JWT, will return the access token + * In JWT refresh token, will return the hashed access token associated to the refresh token + * + * @param string $sessionId Session Id to mock + * @return void + */ + public function mockSessionId(string $sessionId) + { + $this->mockService(SessionIdentificationServiceInterface::class, function () use ($sessionId) { + $stubSessionIdentifier = $this->getMockForAbstractClass(AbstractSessionIdentificationService::class); + $stubSessionIdentifier->method('getSessionIdentifier')->willReturn($sessionId); + + return $stubSessionIdentifier; + }); + } + + /** + * @param mixed $expected Expected value + * @param string $name Cookie name + */ + public function assertCookieIsSecure($expected, string $name): void + { + $this->assertCookie($expected, $name); + /** @var Response $response */ + $response = $this->_response; + $cookie = $response->getCookieCollection()->get($name); + $this->assertTrue($cookie->isSecure()); + $this->assertTrue($cookie->isHttpOnly()); + } } diff --git a/tests/Lib/Model/AvatarsModelTestTrait.php b/tests/Lib/Model/AvatarsModelTrait.php similarity index 68% rename from tests/Lib/Model/AvatarsModelTestTrait.php rename to tests/Lib/Model/AvatarsModelTrait.php index db81d358ac..f1f4050a8f 100644 --- a/tests/Lib/Model/AvatarsModelTestTrait.php +++ b/tests/Lib/Model/AvatarsModelTrait.php @@ -18,17 +18,19 @@ namespace App\Test\Lib\Model; use App\Model\Entity\Avatar; -use App\Model\Table\AvatarsTable; use App\Service\Avatars\AvatarsCacheService; +use App\Service\Avatars\AvatarsConfigurationService; +use App\Test\Factory\AvatarFactory; use App\Test\Factory\ProfileFactory; use Cake\ORM\TableRegistry; use Laminas\Diactoros\Stream; use Laminas\Diactoros\UploadedFile; +use League\Flysystem\Local\LocalFilesystemAdapter; /** * @property \App\Model\Table\AvatarsTable $Avatars */ -trait AvatarsModelTestTrait +trait AvatarsModelTrait { /** * Asserts that an object has all the attributes an avatar should have. @@ -37,7 +39,8 @@ trait AvatarsModelTestTrait */ protected function assertAvatarAttributes($avatar) { - $this->assertObjectHasAttributes(['data'], $avatar); + $this->assertObjectHasAttributes(['url'], $avatar); + $this->assertObjectHasAttributes(['small', 'medium'], $avatar->url); } /** @@ -64,6 +67,7 @@ public function createAvatar(?Avatar $avatar = null): Avatar 'profile_id' => $profileId, ]; + /** @var AvatarsTable $AvatarsTable */ $AvatarsTable = TableRegistry::getTableLocator()->get('Avatars'); if ($avatar) { $avatar = $AvatarsTable->patchEntity($avatar, $data); @@ -77,28 +81,41 @@ public function createAvatar(?Avatar $avatar = null): Avatar /** * Create a dummy upload file * + * @param string $format Format * @return UploadedFile */ - public function createUploadFile() + public function createUploadFile(string $format = 'png') { - $adaAvatar = FIXTURES . 'Avatar' . DS . 'ada.png'; + $uploadFile = FIXTURES . 'Avatar' . DS . 'ada' . '.' . $format; return new UploadedFile( - $adaAvatar, - filesize($adaAvatar), + $uploadFile, + filesize($uploadFile), \UPLOAD_ERR_OK, - $adaAvatar, - 'image/png' + $uploadFile, + 'image/' . $format ); } private function assertAvatarCachedFilesExist(Avatar $avatar) { $service = new AvatarsCacheService($this->Avatars); - $this->assertInstanceOf(Stream::class, $service->readSteamFromId($avatar->id, AvatarsTable::FORMAT_SMALL)); - $this->assertInstanceOf(Stream::class, $service->readSteamFromId($avatar->id, AvatarsTable::FORMAT_MEDIUM)); + $this->assertInstanceOf(Stream::class, $service->readSteamFromId($avatar->id, AvatarsConfigurationService::FORMAT_SMALL)); + $this->assertInstanceOf(Stream::class, $service->readSteamFromId($avatar->id, AvatarsConfigurationService::FORMAT_MEDIUM)); $this->assertInstanceOf(Stream::class, $service->readSteamFromId($avatar->id, 'whateverFormatWillReturnSmall')); $this->assertTextEndsWith('.jpg', $service->getAvatarFileName($avatar)); $this->assertTextEndsWith('.jpg', $service->getAvatarFileName($avatar, 'medium')); } + + /** + * Set the avatar directory to tmp/tests/avatars + * The file system of both the regular table and avatar factory + * table need to be set + */ + protected function setTestLocalFilesystemAdapter(): void + { + $testFileSystem = new LocalFilesystemAdapter(TMP . 'tests' . DS . 'avatars'); + TableRegistry::getTableLocator()->get('Avatars')->setFilesystem($testFileSystem); + AvatarFactory::make()->getTable()->setFilesystem($testFileSystem); + } } diff --git a/tests/Lib/Model/EmailQueueTrait.php b/tests/Lib/Model/EmailQueueTrait.php new file mode 100644 index 0000000000..d40bafeb75 --- /dev/null +++ b/tests/Lib/Model/EmailQueueTrait.php @@ -0,0 +1,154 @@ +assertTrue(EmailQueueFactory::find()->where($properties)->count() === 1, 'The email is not in the email queue.'); + } + + /** + * Asserts that an email with given recipient is in the email queue. + */ + protected function assertEmailWithRecipientIsInQueue(string $email) + { + $this->assertEmailIsInQueue(compact('email')); + } + + /** + * Asserts that n emails are in the email queue. + */ + protected function assertEmailQueueCount(int $n) + { + $this->assertSame($n, EmailQueueFactory::find()->count()); + } + + /** + * Asserts that no email is in the email queue. + */ + protected function assertEmailQueueIsEmpty() + { + $this->assertEmailQueueCount(0); + } + + /** + * Not all email notifications are activated per default. + * Particularly on the cloud. + * This method enables the activation of the provided email notification setting + * + * @param string $notificationSettingPath Notification path + * @param bool $value Value to assign + * @return void + */ + protected function setEmailNotificationsSetting(string $notificationSettingPath, bool $value): void + { + EmailNotificationSettings::flushCache(); + $this->backupEmailNotificationSettings = array_merge_recursive( + Configure::read('passbolt.email.send'), + $this->backupEmailNotificationSettings + ); + Configure::write('passbolt.email.send.' . $notificationSettingPath, $value); + } + + /** + * Whenever an email notification setting is set with setEmailNotificationsSetting, this method will restore the + * settings to settings value prior to the test. This is necessary to ensute test independence + */ + protected function restoreEmailNotificationsSettings(): void + { + Configure::write('passbolt.email.send', $this->backupEmailNotificationSettings); + EmailNotificationSettings::flushCache(); + } + + /** + * Asserts that all emails of a given recipient have a locale set and equal to the expectation. + * + * @param string $email Recipient + * @param string $expectedLocale Expected locale + */ + protected function assetEmailLocale(string $email, string $expectedLocale) + { + $emails = EmailQueueFactory::find()->where(compact('email')); + $this->assertTrue($emails->count() > 0); + foreach ($emails as $email) { + $this->assertTextEquals($expectedLocale, $email->get('template_vars')['locale']); + } + } + + /** + * Asserts that all emails of a given recipient have expected subject. + * + * @param string $email Recipient + * @param string $expectedSubject Expected subject + */ + protected function assetEmailSubject(string $email, string $expectedSubject) + { + $emails = EmailQueueFactory::find()->where(compact('email')); + $this->assertTrue($emails->count() > 0); + foreach ($emails as $email) { + $this->assertTextEquals($expectedSubject, $email->get('subject')); + } + } + + /** + * Asserts that a string is found in an email of the email queue. + * + * @param string $string String to search for + * @param int $i Email position in the queue (start with 0), default 0 + * @param string $message Error message + * @throws \Exception If the test fails. + */ + protected function assertEmailInBatchContains(string $string, int $i = 0, string $message = ''): void + { + $EmailQueue = TableRegistry::getTableLocator()->get('EmailQueue.EmailQueue'); + $email = $EmailQueue->find()->order('id')->offset($i)->first(); + if (empty($email)) { + $this->fail("The email queue does not have an email at index $i"); + } + + // Get template, template vars, subject and format + $format = $email->get('format'); + $viewBuilder = new ViewBuilder(); + $viewBuilder->setVar('title', $email->get('subject')); + $viewBuilder->setVar('body', $email->get('template_vars')['body']); + + $viewBuilder + ->setLayout('default') + ->setLayoutPath("email/$format") + ->setTemplate($email->get('template')) + ->setTemplatePath("email/$format"); + + $renderedEmail = $viewBuilder->build()->render(); + $this->assertStringContainsString($string, $renderedEmail, $message); + } +} diff --git a/tests/Lib/Model/FormatValidationTrait.php b/tests/Lib/Model/FormatValidationTrait.php index ebe7c2d924..ed575c9ab4 100644 --- a/tests/Lib/Model/FormatValidationTrait.php +++ b/tests/Lib/Model/FormatValidationTrait.php @@ -106,19 +106,19 @@ public function assertFieldFormatValidation($entityTable, $fieldName, $entityDat $entityData = array_merge($entityData, [$fieldName => $testCaseData]); $entityData = $this->_adjustEntityData($entityData); $entity = $entityTable->newEntity($entityData, $entityOptions); - } elseif ($context == 'update') { + } else { $entity = $entityTable->get($entityData['id']); $entityData = array_merge($entityData, [$fieldName => $testCaseData]); $entityData = $this->_adjustEntityData($entityData); $entity = $entityTable->patchEntity($entity, $entityData, $entityOptions); } - $save = $entityTable->save($entity, ['checkRules' => false]); + $result = count($entity->getErrors()) === 0; if ($expectedResult == true) { - $this->assertEquals(true, (bool)$save, __('The test for {0}:{1} = {2} is expected to save data', $fieldName, $testCaseName, $testCaseData)); + $this->assertEquals(true, $result, __('The test for {0}:{1} = {2} is expected to save data', $fieldName, $testCaseName, $testCaseData)); } else { - $this->assertEquals(false, (bool)$save, __('The test for {0}:{1} = {2} is not expected to save data', $fieldName, $testCaseName, $testCaseData)); + $this->assertEquals(false, $result, __('The test for {0}:{1} = {2} is not expected to save data', $fieldName, $testCaseName, $testCaseData)); $errors = $entity->getErrors(); $this->assertNotEmpty($errors, __('The test {0}:{1} = {2} should have returned an error.', $fieldName, $testCaseName, $testCaseData)); $this->assertNotEmpty( @@ -188,12 +188,13 @@ public function assertPatchEntityValidation($entityTable, $fieldName, $entity, $ $entityData = [$fieldName => $testCaseData]; $entityData = $this->_adjustEntityData($entityData); $entity = $entityTable->patchEntity($entity, $entityData, $entityOptions); - $save = $entityTable->save($entity, ['checkRules' => false]); + + $result = count($entity->getErrors()) === 0; if ($expectedResult == true) { - $this->assertEquals(true, (bool)$save, __('The test for {0}:{1} = {2} is expected to save data', $fieldName, $testCaseName, $testCaseData)); + $this->assertEquals(true, $result, __('The test for {0}:{1} = {2} is expected to save data', $fieldName, $testCaseName, $testCaseData)); } else { - $this->assertEquals(false, (bool)$save, __('The test for {0}:{1} = {2} is not expected to save data', $fieldName, $testCaseName, $testCaseData)); + $this->assertEquals(false, $result, __('The test for {0}:{1} = {2} is not expected to save data', $fieldName, $testCaseName, $testCaseData)); $errors = $entity->getErrors(); $this->assertNotEmpty($errors, __('The test {0}:{1} = {2} should have returned an error.', $fieldName, $testCaseName, $testCaseData)); $this->assertNotEmpty( diff --git a/tests/Lib/Model/ResourcesModelTrait.php b/tests/Lib/Model/ResourcesModelTrait.php index 4ba316279d..9c389cb42d 100644 --- a/tests/Lib/Model/ResourcesModelTrait.php +++ b/tests/Lib/Model/ResourcesModelTrait.php @@ -229,18 +229,42 @@ protected function assertResourceIsNotSoftDeleted($id) $this->assertFalse($resource->deleted); } - /** - * Assert than a resource does not exist - * - * @param mixed $selector Either the resource id or a find options array - */ - protected function assertResourceNotExist($selector) + protected function getDummyGpgMessage(): string { - if (is_string($selector)) { - $resource = $this->Resources->get($selector); - } else { - $resource = $this->Resources->find()->where($selector)->first(); - } - $this->assertEmpty($resource); + return '-----BEGIN PGP MESSAGE----- + +hQIMA1P90Qk1JHA+ARAAu3oaLzv/BfeukST6tYAkAID+xbt5dhsv4lxL3oSbo8Nm +qmJQSVe6wmh8nZJjeHN4L7iCq8FEZpdCwrDbX1qIuqBFFO3vx6BJFOURG0JbI/E/ +nXtvck00RvxTB1Y30OUbGp21jjEILyuELhWpf11+AQelybY4XKyM8UxGjSncDqaS +X7/yXspCByywci1VfzK7D6+zfcyLy29wQm9Ci5j6I4QqhvlKQPTxl6tWrJh+EyLP +SLZjO8ofc00fbc7mUIH5taDg6Br2VLG/x29HhKCPYdOVzSz3BpUCcUcPgn98mCV0 +Qh7ZPE1NNmCWXID5hryuSF71IiAYhxae9u77pOAbVe0PwFgMY6kke/hJQkO6IYJ/ +/Q3aL/xHTlY2XtPbpV1in6soc0wJBuoROrwN0AdtvEJOnomclNEH5BPwLjZ1shCr +vuk0zJjj9WcqQiVNEuErs4d7rLc+dB7md+97S8Gtcf8lrlZMH9ooI2UnvxC8HRqX +KzcgW17YF44VtD2TLMymvpnjPV9gruYnmpkQG/1ihnDOWe6xWlFH6jZf5eE4IEVn +osx/D6inZHHMXWbZu9hMiQloKKZ0s8yxTFw9C1wFwaIxRtvJ84qc17rJs7mfcC2n +sG7jLzQBV/GVWtR4hVebstP+q05Sib+sKwLOTZhzWNPKruBsdaBCUTxcmI6qwDHS +QQFgGx0K1xQj2rKiP2j0cDHyGsWIlOITN+4r6Ohx23qRhVo0txPWVOYLpC8JnlfQ +W3AI8+rWjK8MGH2T88hCYI/6 +=uahb +-----END PGP MESSAGE-----'; + } + + protected function getDummyResourcesPostData($data = []): array + { + $defaultData = [ + 'name' => 'new resource name', + 'username' => 'username@domain.com', + 'uri' => 'https://www.domain.com', + 'description' => 'new resource description', + 'secrets' => [ + [ + 'data' => $this->getDummyGpgMessage(), + ], + ], + ]; + $data = array_merge($defaultData, $data); + + return $data; } } diff --git a/tests/Lib/Utility/AuthToken/AuthTokenExpiryTest.php b/tests/Lib/Utility/AuthToken/AuthTokenExpiryTest.php index 768562200b..d98e8b01f0 100644 --- a/tests/Lib/Utility/AuthToken/AuthTokenExpiryTest.php +++ b/tests/Lib/Utility/AuthToken/AuthTokenExpiryTest.php @@ -26,7 +26,7 @@ public function setUp(): void public function testThatGetExpirationForInvalidTokenTypeThrowAnException() { $this->expectException(InvalidArgumentException::class); - $this->sut->getExpirationForTokenType('type'); + $this->sut->getExpiryForTokenType('type'); } public function testThatGetExpirationForTokenRetrieveConfigurationForTokenType() @@ -37,7 +37,7 @@ public function testThatGetExpirationForTokenRetrieveConfigurationForTokenType() Configure::write('passbolt.auth.token.' . $tokenType . '.expiry', $expectedExpiry); Configure::write('passbolt.auth.tokenExpiry', '10 days'); - $this->assertEquals($expectedExpiry, $this->sut->getExpirationForTokenType($tokenType)); + $this->assertEquals($expectedExpiry, $this->sut->getExpiryForTokenType($tokenType)); } public function testThatGetExpirationForTokenFallbackToDefaultExpiryConfigurationIfExpiryNotDefinedForTokenType() @@ -47,7 +47,7 @@ public function testThatGetExpirationForTokenFallbackToDefaultExpiryConfiguratio Configure::clear(); Configure::write('passbolt.auth.tokenExpiry', $expectedExpiry); - $this->assertEquals($expectedExpiry, $this->sut->getExpirationForTokenType($tokenType)); + $this->assertEquals($expectedExpiry, $this->sut->getExpiryForTokenType($tokenType)); } public function testThatGetExpirationForTokenFallbackToDefaultExpiryConfigurationIfExpiryInvalidForTokenType() @@ -58,6 +58,6 @@ public function testThatGetExpirationForTokenFallbackToDefaultExpiryConfiguratio Configure::write('passbolt.auth.token.' . $tokenType . '.expiry', null); Configure::write('passbolt.auth.tokenExpiry', $expectedExpiry); - $this->assertEquals($expectedExpiry, $this->sut->getExpirationForTokenType($tokenType)); + $this->assertEquals($expectedExpiry, $this->sut->getExpiryForTokenType($tokenType)); } } diff --git a/tests/Lib/Utility/ErrorTrait.php b/tests/Lib/Utility/ErrorTrait.php index 9ccf961ae6..60bb205d37 100644 --- a/tests/Lib/Utility/ErrorTrait.php +++ b/tests/Lib/Utility/ErrorTrait.php @@ -21,7 +21,7 @@ trait ErrorTrait /** * Asserts that the latest json request failed. * - * @param null $code (optional) Expected response code + * @param int|null $code (optional) Expected response code * @param string $message (optional) Expected response message. * @param string $errorMessage (optional) Test case error message to be displayed * @return void @@ -66,13 +66,64 @@ public function assertForbiddenError($msg = 'Forbidden') $this->assertError(403, $msg); } + /** + * Asserts that the json response is relative to a payment required error. + * + * @return void + */ + public function assertPaymentRequiredError($msg = 'Payment Required') + { + $this->assertError(402, $msg); + } + /** * Asserts that the json response is relative to a forbidden error. * + * @param string $msg * @return void */ public function assertBadRequestError($msg = 'Bad Request') { $this->assertError(400, $msg); } + + /** + * Asserts that the json response is relative to a forbidden error. + * + * @param string $msg + * @return void + */ + public function assertNotFoundError($msg = 'Not Found') + { + $this->assertError(404, $msg); + } + + /** + * Asserts that the json response is relative to a forbidden error. + * + * @param string $msg + * @return void + */ + public function assertInternalError($msg = 'Internal Error') + { + $this->assertError(500, $msg); + } + + /** + * Read a cookie in the response, assert that the cookie is found and expired. + * + * @param string $cookie Cookie name + * @param string $msg Error message + * @return void + */ + public function assertCookieExpired(string $cookie, string $msg = 'Expired cookie not found.') + { + /** @var \Cake\Http\Cookie\CookieCollection $cookies */ + $cookies = $this->_response->getCookieCollection(); + if (!$cookies->has($cookie)) { + $this->fail($msg); + } + $mfaCookie = $cookies->get($cookie); + $this->assertTrue($mfaCookie->isExpired(), $msg); + } } diff --git a/tests/Lib/Utility/Gpg/GpgAdaSetupTrait.php b/tests/Lib/Utility/Gpg/GpgAdaSetupTrait.php new file mode 100644 index 0000000000..eaef5fdca8 --- /dev/null +++ b/tests/Lib/Utility/Gpg/GpgAdaSetupTrait.php @@ -0,0 +1,54 @@ +gpg = OpenPGPBackendFactory::get(); + $this->gpg->clearKeys(); + + // Import the server key. + $this->serverKeyId = $this->gpg->importKeyIntoKeyring(file_get_contents(Configure::read('passbolt.gpg.serverKey.private'))); + $this->gpg->importKeyIntoKeyring(file_get_contents(Configure::read('passbolt.gpg.serverKey.public'))); + + // Import the key of ada. + $this->adaKeyId = $this->gpg->importKeyIntoKeyring(file_get_contents(FIXTURES . DS . 'Gpgkeys' . DS . 'ada_private_nopassphrase.key')); + } +} diff --git a/tests/Lib/Utility/JsonRequestTrait.php b/tests/Lib/Utility/JsonRequestTrait.php index 09328aa404..13942070b4 100644 --- a/tests/Lib/Utility/JsonRequestTrait.php +++ b/tests/Lib/Utility/JsonRequestTrait.php @@ -128,7 +128,6 @@ private function setJsonHeaderAndBody() { $this->_responseJson = json_decode($this->_getBodyAsString()); if (empty($this->_responseJson)) { - pr($this->_getBodyAsString()); Assert::fail('The result of the request is not a valid json.'); } $this->_responseJsonHeader = $this->_responseJson->header; diff --git a/tests/Lib/Utility/PassboltCommandTestTrait.php b/tests/Lib/Utility/PassboltCommandTestTrait.php index 69889cbe0f..bad49e9202 100644 --- a/tests/Lib/Utility/PassboltCommandTestTrait.php +++ b/tests/Lib/Utility/PassboltCommandTestTrait.php @@ -20,14 +20,14 @@ trait PassboltCommandTestTrait { public function assertCommandCannotBeRunAsRootUser(string $commandClassName) { - /** @var PassboltCommand $cmd */ + /** @var \App\Command\PassboltCommand $cmd */ $cmd = new $commandClassName(); - $cmd::$userIsRoot = true; + $cmd::$isUserRoot = true; $this->exec($cmd::defaultName()); $this->assertOutputContains('Passbolt commands cannot be executed as root.'); $this->assertExitError(); - $cmd::$userIsRoot = false; + $cmd::$isUserRoot = false; } /** diff --git a/tests/Lib/Utility/UserAccessControlTrait.php b/tests/Lib/Utility/UserAccessControlTrait.php index 2a3252c621..ebf2afa64c 100644 --- a/tests/Lib/Utility/UserAccessControlTrait.php +++ b/tests/Lib/Utility/UserAccessControlTrait.php @@ -17,6 +17,7 @@ namespace App\Test\Lib\Utility; use App\Model\Entity\Role; +use App\Model\Entity\User; use App\Utility\UserAccessControl; use App\Utility\UuidFactory; @@ -33,4 +34,14 @@ public function mockUserAccessControl($user, $role = Role::GUEST) { return new UserAccessControl($role, UuidFactory::uuid('user.id.' . $user), $user . '@passbolt.com'); } + + public function mockAdminAccessControl() + { + return new UserAccessControl(Role::ADMIN, UuidFactory::uuid('user.id.admin'), 'admin@passbolt.com'); + } + + public function makeUac(User $user) + { + return new UserAccessControl($user->role->name, $user->id, $user->username); + } } diff --git a/tests/TestCase/Command/DropTablesCommandTest.php b/tests/TestCase/Command/DropTablesCommandTest.php index e1096e1141..2bc125c67d 100644 --- a/tests/TestCase/Command/DropTablesCommandTest.php +++ b/tests/TestCase/Command/DropTablesCommandTest.php @@ -20,7 +20,6 @@ use Cake\Datasource\Exception\MissingDatasourceConfigException; use Cake\TestSuite\ConsoleIntegrationTestTrait; use Cake\TestSuite\TestCase; -use CakephpTestSuiteLight\Sniffer\SnifferRegistry; class DropTablesCommandTest extends TestCase { @@ -62,8 +61,6 @@ public function testDropTablesCommand() // Run migrations to recreate the lost tables. $this->exec('migrations migrate -c test -q --no-lock'); - SnifferRegistry::get('test')->restart(); - SnifferRegistry::get('test')->markAllTablesAsDirty(); } /** diff --git a/tests/TestCase/Command/HealthcheckCommandTest.php b/tests/TestCase/Command/HealthcheckCommandTest.php index beee6a6dc4..4eea0aed68 100644 --- a/tests/TestCase/Command/HealthcheckCommandTest.php +++ b/tests/TestCase/Command/HealthcheckCommandTest.php @@ -35,7 +35,7 @@ public function setUp(): void { parent::setUp(); $this->useCommandRunner(); - HealthcheckCommand::$userIsRoot = false; + HealthcheckCommand::$isUserRoot = false; } /** @@ -67,4 +67,11 @@ public function testHealthcheckCommand() // Since the tests run with debug on, here will always be at least one error in the healthcheck. $this->assertOutputContains('error(s) found. Hang in there!'); } + + public function testHealthcheckCommand_Environment() + { + $this->exec('passbolt healthcheck -d test --environment'); + $this->assertExitSuccess(); + $this->assertOutputContains('No error found. Nice one sparky!'); + } } diff --git a/tests/TestCase/Command/InstallCommandTest.php b/tests/TestCase/Command/InstallCommandTest.php index a9caaf1229..254dd2f4d0 100644 --- a/tests/TestCase/Command/InstallCommandTest.php +++ b/tests/TestCase/Command/InstallCommandTest.php @@ -19,16 +19,17 @@ use App\Command\InstallCommand; use App\Model\Entity\Role; use App\Test\Lib\Utility\PassboltCommandTestTrait; +use App\Utility\Application\FeaturePluginAwareTrait; use Cake\ORM\Query; use Cake\ORM\TableRegistry; use Cake\TestSuite\ConsoleIntegrationTestTrait; use Cake\TestSuite\TestCase; -use CakephpTestSuiteLight\Sniffer\SnifferRegistry; use Faker\Factory; class InstallCommandTest extends TestCase { use ConsoleIntegrationTestTrait; + use FeaturePluginAwareTrait; use PassboltCommandTestTrait; /** @@ -40,16 +41,15 @@ public function setUp(): void { parent::setUp(); $this->useCommandRunner(); - InstallCommand::$userIsRoot = false; + InstallCommand::$isUserRoot = false; $this->emptyDirectory(CACHE . 'database' . DS); + $this->enableFeaturePlugin('JwtAuthentication'); } public function tearDown(): void { parent::tearDown(); - - SnifferRegistry::get('test')->restart(); - SnifferRegistry::get('test')->markAllTablesAsDirty(); + $this->disableFeaturePlugin('JwtAuthentication'); } /** @@ -135,8 +135,7 @@ public function testInstallCommandNormalForceWithoutAdmin() */ public function testInstallCommandNormalForceWithDataImport() { - $this->markTestSkipped(); // output gpg key in console - $this->exec('passbolt install --force --no-admin --backup -q --data alt0 -d test'); + $this->exec('passbolt install --force --no-admin --backup -q -d test'); $this->assertExitSuccess(); } @@ -148,9 +147,9 @@ public function testInstallCommandNormalForceWithDataImport() public function testInstallCommandNormalForceWithAdminData() { $faker = Factory::create(); - $userName = $faker->email; - $firstName = $faker->firstNameFemale; - $lastName = $faker->lastName; + $userName = $faker->email(); + $firstName = $faker->firstNameFemale(); + $lastName = $faker->lastName(); $cmd = 'passbolt install --force --backup -q '; $cmd .= ' --admin-first-name ' . $firstName; $cmd .= ' --admin-last-name ' . $lastName; diff --git a/tests/TestCase/Command/KeyringInitCommandTest.php b/tests/TestCase/Command/KeyringInitCommandTest.php index de0c5d441f..46ac4c3ada 100644 --- a/tests/TestCase/Command/KeyringInitCommandTest.php +++ b/tests/TestCase/Command/KeyringInitCommandTest.php @@ -41,7 +41,7 @@ public function setUp(): void { parent::setUp(); $this->useCommandRunner(); - KeyringInitCommand::$userIsRoot = false; + KeyringInitCommand::$isUserRoot = false; $this->key = Configure::read('passbolt.gpg.serverKey.private'); } diff --git a/tests/TestCase/Command/MigrateCommandTest.php b/tests/TestCase/Command/MigrateCommandTest.php index 53ca0eddd3..c68b950068 100644 --- a/tests/TestCase/Command/MigrateCommandTest.php +++ b/tests/TestCase/Command/MigrateCommandTest.php @@ -40,7 +40,7 @@ public function setUp(): void { parent::setUp(); $this->useCommandRunner(); - MigrateCommand::$userIsRoot = false; + MigrateCommand::$isUserRoot = false; } /** diff --git a/tests/TestCase/Command/MigratePostgresCommandTest.php b/tests/TestCase/Command/MigratePostgresCommandTest.php new file mode 100644 index 0000000000..334c27c46c --- /dev/null +++ b/tests/TestCase/Command/MigratePostgresCommandTest.php @@ -0,0 +1,111 @@ +useCommandRunner(); + MigratePostgresCommand::$isUserRoot = false; + } + + protected function countMigrations(): int + { + return (int)ConnectionManager::get('test')->newQuery() + ->select('COUNT(*)') + ->from('phinxlog') + ->execute() + ->fetch()[0]; + } + + /** + * Basic help test + */ + public function testPostgresMigrateCommandHelp() + { + $this->exec('passbolt migrate_postgres -h'); + $this->assertExitSuccess(); + $this->assertOutputContains('Re-runs the migrations required by Postgres.'); + $this->assertOutputContains('cake passbolt migrate_postgres'); + } + + public function testPostgresMigrateCommandAsRoot() + { + $this->assertCommandCannotBeRunAsRootUser(MigratePostgresCommand::class); + } + + /** + * Run the command and ensure that all migrations are still in place. + */ + public function testPostgresMigrateCommandAsNonRoot() + { + $nMigrations = $this->countMigrations(); + // Ensure that the count migration is returning some integer, and not a fake 1 or something. + $this->assertGreaterThan(50, $nMigrations); + + $this->exec('passbolt migrate_postgres -d test -q'); + if (ConnectionManager::get('test')->getDriver() instanceof Postgres) { + $this->assertExitSuccess('Passbolt can now be used with Postgres.'); + } else { + $this->assertExitError('This command is available with a Postgres connection only.'); + } + + $this->assertSame($nMigrations, $this->countMigrations()); + } + + /** + * Ensures that a limited amount of migrations is impacted by the command. + */ + public function testPostgresMigrateCommand_DeletePostgresRelevantMigrations() + { + $cmd = new MigratePostgresCommand(); + $connection = ConnectionManager::get('test'); + $count = $this->countMigrations(); + $cmd->deletePostgresRelevantMigrations($connection); + $newCount = $this->countMigrations(); + $diff = $count - $newCount; + + $this->assertLessThanOrEqual(count($cmd::POSTGRES_RELEVANT_MIGRATIONS), $diff); + $this->assertGreaterThanOrEqual(2, $diff); + + // Re-run the migrations to complete the schema again + $this->exec('passbolt migrate -q -d test'); + $this->assertSame($count, $this->countMigrations()); + } +} diff --git a/tests/TestCase/Command/RegisterUserCommandTest.php b/tests/TestCase/Command/RegisterUserCommandTest.php index a2aa25c643..269cb43315 100644 --- a/tests/TestCase/Command/RegisterUserCommandTest.php +++ b/tests/TestCase/Command/RegisterUserCommandTest.php @@ -46,7 +46,7 @@ public function setUp(): void { parent::setUp(); $this->useCommandRunner(); - RegisterUserCommand::$userIsRoot = false; + RegisterUserCommand::$isUserRoot = false; $this->Users = TableRegistry::getTableLocator()->get('Users'); } @@ -97,9 +97,9 @@ public function testRegisterUserCommandWithOrWithoutExistingAdmin(bool $withAdmi $faker = Factory::create(); $role = Role::USER; - $username = $faker->email; - $firstName = $faker->firstNameFemale; - $lastName = $faker->lastName; + $username = $faker->email(); + $firstName = $faker->firstNameFemale(); + $lastName = $faker->lastName(); $options = " -r $role -u $username -f $firstName -l $lastName"; @@ -119,7 +119,7 @@ public function testRegisterUserCommandInteractively() // Prepare the interaction inputs $faker = Factory::create(); - $input = [$faker->email, $faker->firstNameFemale, $faker->lastName, Role::USER]; + $input = [$faker->email(), $faker->firstNameFemale(), $faker->lastName(), Role::USER]; // Run the register command $this->exec('passbolt register_user -i', $input); diff --git a/tests/TestCase/Controller/Auth/AuthIsAuthenticatedControllerTest.php b/tests/TestCase/Controller/Auth/AuthIsAuthenticatedControllerTest.php index 0d2b304772..dcec95bb8c 100644 --- a/tests/TestCase/Controller/Auth/AuthIsAuthenticatedControllerTest.php +++ b/tests/TestCase/Controller/Auth/AuthIsAuthenticatedControllerTest.php @@ -16,25 +16,39 @@ */ namespace App\Test\TestCase\Controller\Auth; +use App\Model\Entity\User; +use App\Test\Factory\UserFactory; use App\Test\Lib\AppIntegrationTestCase; class AuthIsAuthenticatedControllerTest extends AppIntegrationTestCase { public function testIsAuthenticatedNotLoggedIn() { - $this->get('/auth/is-authenticated.json'); + $this->getJson('/auth/is-authenticated.json'); $this->assertResponseError(); - $response = json_decode($this->_getBodyAsString()); - $this->assertTextContains('error', $response->header->status); - $this->assertTextContains('Authentication is required to continue', $response->header->message); + $this->assertTextContains('error', $this->_responseJsonHeader->status); + $this->assertTextContains('Authentication is required to continue', $this->_responseJsonHeader->message); } public function testIsAuthenticatedLoggedIn() { $this->authenticateAs('ada'); - $this->get('/auth/is-authenticated.json'); + $this->getJson('/auth/is-authenticated.json'); $this->assertResponseOk(); - $response = json_decode($this->_getBodyAsString()); - $this->assertTextContains('success', $response->header->status); + $this->assertInstanceOf(User::class, $this->getSession()->read('Auth.user')); + $this->assertTextContains('success', $this->_responseJsonHeader->status); + } + + /** + * @covers \App\Middleware\SessionAuthPreventDeletedUsersMiddleware::process + */ + public function testIsAuthenticatedSoftDeletedLoggedUserShouldBeForbiddenToRequestTheApi() + { + $user = UserFactory::make()->user()->deleted()->persist(); + + $this->loginAs($user); + $this->getJson('/auth/is-authenticated.json'); + $this->assertEmpty($this->getSession()->read()); + $this->assertAuthenticationError(); } } diff --git a/tests/TestCase/Controller/Auth/AuthLoginControllerTest.php b/tests/TestCase/Controller/Auth/AuthLoginControllerTest.php index fbf4f062d6..9309866419 100644 --- a/tests/TestCase/Controller/Auth/AuthLoginControllerTest.php +++ b/tests/TestCase/Controller/Auth/AuthLoginControllerTest.php @@ -22,6 +22,7 @@ use Cake\Core\Configure; use Cake\ORM\TableRegistry; use Cake\Validation\Validation; +use Passbolt\JwtAuthentication\Service\AccessToken\JwtKeyPairService; class AuthLoginControllerTest extends AppIntegrationTestCase { @@ -32,7 +33,7 @@ class AuthLoginControllerTest extends AppIntegrationTestCase public $keyid; /** - * @var OpenPGPBackend $gpg + * @var \App\Utility\OpenPGP\OpenPGPBackend $gpg */ public $gpg; @@ -40,6 +41,20 @@ class AuthLoginControllerTest extends AppIntegrationTestCase protected $adaKeyId; protected $serverKeyId; + public function setUp(): void + { + parent::setUp(); + $jwtKeyPairService = new JwtKeyPairService(); + $jwtKeyPairService->createKeyPair(); + $this->enableFeaturePlugin('JwtAuthentication'); + } + + public function tearDown(): void + { + parent::tearDown(); + $this->disableFeaturePlugin('JwtAuthentication'); + } + /** * Test getting login started with deleted account */ @@ -62,6 +77,9 @@ public function testAuthLoginControllerUserLoginAsDeletedUserError() */ public function testAuthLoginControllerLoginServerKeyFingerprintMissing() { + // Disable this plugin in this test as long as the cookie pepper is the fingerprint. + $this->disableFeaturePlugin('JwtAuthentication'); + Configure::delete('passbolt.gpg.serverKey.fingerprint'); $this->postJson('/auth/login.json'); $this->assertResponseCode(500); @@ -94,6 +112,16 @@ public function testAuthLoginControllerGetHeaders() $this->assertHeader('X-GPGAuth-Logout-URL', '/auth/logout'); } + /** + * Check that GET /auth/login.json triggers a not found error. + */ + public function testAuthLoginControllerGetJson() + { + $this->getJson('/auth/login.json'); + $this->assertResponseError('Page not found.'); + $this->assertResponseCode(404); + } + /** * Check that GPGAuth headers are set everywhere */ @@ -294,7 +322,7 @@ public function testAuthLoginControllerStage1UserToken() $this->assertTrue($isValid, 'There should a valid auth token'); // Send it back! - $this->post('/auth/login.json', [ + $this->postJson('/auth/login.json', [ 'data' => [ 'gpg_auth' => [ 'keyid' => $this->adaKeyId, // Ada's key diff --git a/tests/TestCase/Controller/Auth/AuthLogoutControllerTest.php b/tests/TestCase/Controller/Auth/AuthLogoutControllerTest.php index 798e6d5eff..2316053088 100644 --- a/tests/TestCase/Controller/Auth/AuthLogoutControllerTest.php +++ b/tests/TestCase/Controller/Auth/AuthLogoutControllerTest.php @@ -22,8 +22,6 @@ class AuthLogoutControllerTest extends AppIntegrationTestCase { - public $fixtures = ['app.Base/Users', 'app.Base/Roles', 'app.Base/Profiles']; - /** * Check if a redirection is of type ZendRedirect * Usefull for high level routes redirections / route alias testing @@ -37,7 +35,7 @@ public function assertZendRedirect(string $url) $this->assertEquals($url, $location[0]); } - public function testAuthLogoutLoggedIn() + public function testAuthLogoutGetLoggedIn() { $this->logInAsUser(); @@ -45,6 +43,23 @@ public function testAuthLogoutLoggedIn() $this->assertRedirect('/auth/login'); } + public function testAuthLogoutPostLoggedIn() + { + $this->logInAsUser(); + + $this->post('/auth/logout'); + $this->assertRedirect('/auth/login'); + } + + public function testAuthLogoutPostLoggedInWithouthCSRF() + { + $this->logInAsUser(); + $this->disableCsrfToken(); + + $this->post('/auth/logout'); + $this->assertResponseError('Missing or incorrect CSRF cookie type.'); + } + public function testAuthLogoutNotLoggedIn() { $this->get('/auth/logout'); diff --git a/tests/TestCase/Controller/Avatars/AvatarsViewControllerTest.php b/tests/TestCase/Controller/Avatars/AvatarsViewControllerTest.php index 8f036e9012..9fb03411e5 100644 --- a/tests/TestCase/Controller/Avatars/AvatarsViewControllerTest.php +++ b/tests/TestCase/Controller/Avatars/AvatarsViewControllerTest.php @@ -16,11 +16,12 @@ */ namespace App\Test\TestCase\Controller\Avatars; -use App\Utility\UuidFactory; -use App\Model\Table\AvatarsTable; + use App\Service\Avatars\AvatarsCacheService; +use App\Service\Avatars\AvatarsConfigurationService; use App\Test\Lib\AppIntegrationTestCase; -use App\Test\Lib\Model\AvatarsModelTestTrait; +use App\Test\Lib\Model\AvatarsModelTrait; +use App\Utility\UuidFactory; use App\View\Helper\AvatarHelper; use Cake\ORM\TableRegistry; use Cake\TestSuite\IntegrationTestTrait; @@ -33,7 +34,7 @@ */ class AvatarsViewControllerTest extends AppIntegrationTestCase { - use AvatarsModelTestTrait; + use AvatarsModelTrait; use IntegrationTestTrait; /** @@ -65,8 +66,8 @@ public function tearDown(): void public function validFormatDataProvider() { return [ - [AvatarsTable::FORMAT_SMALL], - [AvatarsTable::FORMAT_MEDIUM], + [AvatarsConfigurationService::FORMAT_SMALL], + [AvatarsConfigurationService::FORMAT_MEDIUM], ]; } @@ -78,10 +79,9 @@ public function validFormatDataProvider() * @return void * @throws \PHPUnit\Exception */ - public function testViewNonExistent(string $format) + public function testAvatarsViewController_ViewNonExistentAvatar(string $format) { - - $this->get('avatars/view/' .UuidFactory::Uuid(). '/' .$format . AvatarHelper::IMAGE_EXTENSION); + $this->get('avatars/view/' . UuidFactory::Uuid() . '/' . $format . AvatarHelper::IMAGE_EXTENSION); $defaultAvatarFileName = $this->avatarsCacheService->getFallBackFileName(); $this->assertResponseEquals(file_get_contents($defaultAvatarFileName)); $this->assertContentType('jpg'); @@ -95,7 +95,7 @@ public function testViewNonExistent(string $format) * @return void * @throws \PHPUnit\Exception */ - public function testViewOnExistent(string $format) + public function testViewAvatarsViewController_ViewExistentAvatar(string $format) { $avatar = $this->createAvatar(); @@ -109,8 +109,39 @@ public function testViewOnExistent(string $format) // Ensure that the virtual field is correctly constructed. $virtualField = [ - AvatarsTable::FORMAT_MEDIUM => AvatarHelper::getAvatarUrl($avatar, AvatarsTable::FORMAT_MEDIUM), - AvatarsTable::FORMAT_SMALL => AvatarHelper::getAvatarUrl($avatar), + AvatarsConfigurationService::FORMAT_MEDIUM => AvatarHelper::getAvatarUrl($avatar->toArray(), AvatarsConfigurationService::FORMAT_MEDIUM), + AvatarsConfigurationService::FORMAT_SMALL => AvatarHelper::getAvatarUrl($avatar->toArray()), + ]; + $this->assertSame($virtualField, $avatar->url); + } + + /** + * Test view method on existent Avatar, which local storage has been deleted. + * + * @dataProvider validFormatDataProvider + * @param string $format + * @return void + * @throws \PHPUnit\Exception + */ + public function testAvatarsViewController_ViewExistentAvatarWithDeletedFile(string $format) + { + $avatar = $this->createAvatar(); + + $fileName = TMP . 'tests' . DS . 'avatars' . DS . + $this->avatarsCacheService->getAvatarFileName($avatar, $format); + + $expectedFileContent = file_get_contents($fileName); + + // Delete the file previously saved on disk + unlink($fileName); + + $this->get('avatars/view/' . $avatar->id . '/' . $format . AvatarHelper::IMAGE_EXTENSION); + $this->assertResponseEquals($expectedFileContent); + + // Ensure that the virtual field is correctly constructed. + $virtualField = [ + AvatarsConfigurationService::FORMAT_MEDIUM => AvatarHelper::getAvatarUrl($avatar->toArray(), AvatarsConfigurationService::FORMAT_MEDIUM), + AvatarsConfigurationService::FORMAT_SMALL => AvatarHelper::getAvatarUrl($avatar->toArray()), ]; $this->assertSame($virtualField, $avatar->url); } @@ -120,7 +151,7 @@ public function testViewOnExistent(string $format) * * @dataProvider validFormatDataProvider */ - public function testViewOnWrongExtension(string $format) + public function testAvatarsViewController_ViewOnWrongExtension(string $format) { $avatar = $this->createAvatar(); $expectedFileContent = file_get_contents($this->avatarsCacheService->getFallBackFileName()); diff --git a/tests/TestCase/Controller/Comments/CommentsDeleteControllerTest.php b/tests/TestCase/Controller/Comments/CommentsDeleteControllerTest.php index 0c05d881b2..9b0b41f97b 100644 --- a/tests/TestCase/Controller/Comments/CommentsDeleteControllerTest.php +++ b/tests/TestCase/Controller/Comments/CommentsDeleteControllerTest.php @@ -23,7 +23,7 @@ class CommentsDeleteControllerTest extends AppIntegrationTestCase { - public $fixtures = ['app.Base/Users', 'app.Base/Groups', 'app.Base/GroupsUsers', 'app.Base/Resources', 'app.Base/Secrets', 'app.Base/Comments']; + public $fixtures = ['app.Base/Users', 'app.Base/Roles', 'app.Base/Profiles', 'app.Base/Groups', 'app.Base/GroupsUsers', 'app.Base/Resources', 'app.Base/Secrets', 'app.Base/Comments']; public function testCommentsDeleteSuccess() { diff --git a/tests/TestCase/Controller/Comments/CommentsUpdateControllerTest.php b/tests/TestCase/Controller/Comments/CommentsUpdateControllerTest.php index 0ca75206c2..c44c529124 100644 --- a/tests/TestCase/Controller/Comments/CommentsUpdateControllerTest.php +++ b/tests/TestCase/Controller/Comments/CommentsUpdateControllerTest.php @@ -27,7 +27,7 @@ class CommentsUpdateControllerTest extends AppIntegrationTestCase { public $Comments; - public $fixtures = ['app.Base/Users', 'app.Base/Groups', 'app.Base/GroupsUsers', 'app.Base/Resources', 'app.Base/Comments', 'app.Base/Permissions']; + public $fixtures = ['app.Base/Users', 'app.Base/Roles', 'app.Base/Profiles', 'app.Base/Groups', 'app.Base/GroupsUsers', 'app.Base/Resources', 'app.Base/Comments', 'app.Base/Permissions']; public function setUp(): void { diff --git a/tests/TestCase/Controller/Comments/CommentsViewControllerTest.php b/tests/TestCase/Controller/Comments/CommentsViewControllerTest.php index bd3426d437..0ad0392dd3 100644 --- a/tests/TestCase/Controller/Comments/CommentsViewControllerTest.php +++ b/tests/TestCase/Controller/Comments/CommentsViewControllerTest.php @@ -26,7 +26,7 @@ class CommentsViewControllerTest extends AppIntegrationTestCase use CommentsModelTrait; public $fixtures = [ - 'app.Base/Users', 'app.Base/Profiles', 'app.Base/Groups', 'app.Base/GroupsUsers', + 'app.Base/Users', 'app.Base/Profiles', 'app.Base/Roles', 'app.Base/Groups', 'app.Base/GroupsUsers', 'app.Base/Permissions', 'app.Base/Resources', 'app.Base/Comments', ]; diff --git a/tests/TestCase/Controller/Favorites/FavoritesAddControllerTest.php b/tests/TestCase/Controller/Favorites/FavoritesAddControllerTest.php index 65cfde763d..c931834b15 100644 --- a/tests/TestCase/Controller/Favorites/FavoritesAddControllerTest.php +++ b/tests/TestCase/Controller/Favorites/FavoritesAddControllerTest.php @@ -33,7 +33,7 @@ class FavoritesAddControllerTest extends AppIntegrationTestCase private $Favorites; public $fixtures = [ - 'app.Base/Users', 'app.Base/Groups', 'app.Base/GroupsUsers', 'app.Base/Resources', + 'app.Base/Users', 'app.Base/Profiles', 'app.Base/Roles', 'app.Base/Groups', 'app.Base/GroupsUsers', 'app.Base/Resources', 'app.Base/Favorites', 'app.Base/Permissions', ]; diff --git a/tests/TestCase/Controller/Favorites/FavoritesDeleteControllerTest.php b/tests/TestCase/Controller/Favorites/FavoritesDeleteControllerTest.php index 8dcaf73c74..2751eaa689 100644 --- a/tests/TestCase/Controller/Favorites/FavoritesDeleteControllerTest.php +++ b/tests/TestCase/Controller/Favorites/FavoritesDeleteControllerTest.php @@ -23,7 +23,7 @@ class FavoritesDeleteControllerTest extends AppIntegrationTestCase { - public $fixtures = ['app.Base/Users', 'app.Base/Resources', 'app.Base/Secrets', 'app.Base/Favorites']; + public $fixtures = ['app.Base/Users', 'app.Base/Profiles', 'app.Base/Roles', 'app.Base/Resources', 'app.Base/Secrets', 'app.Base/Favorites']; public function testFavoritesDeleteSuccess() { diff --git a/tests/TestCase/Controller/Groups/GroupsAddControllerTest.php b/tests/TestCase/Controller/Groups/GroupsAddControllerTest.php index b48ceaad62..b8b2fdc18c 100644 --- a/tests/TestCase/Controller/Groups/GroupsAddControllerTest.php +++ b/tests/TestCase/Controller/Groups/GroupsAddControllerTest.php @@ -196,11 +196,6 @@ public function testGroupsAddValidationErrors() } } - public function testGroupsAddCannotModifyNotAccessibleFields() - { - $this->markTestIncomplete(); - } - public function testGroupsAddErrorNotAdmin() { $this->authenticateAs('dame'); diff --git a/tests/TestCase/Controller/Groups/GroupsIndexControllerTest.php b/tests/TestCase/Controller/Groups/GroupsIndexControllerTest.php index d1448f438a..237c5f56f4 100644 --- a/tests/TestCase/Controller/Groups/GroupsIndexControllerTest.php +++ b/tests/TestCase/Controller/Groups/GroupsIndexControllerTest.php @@ -28,7 +28,7 @@ class GroupsIndexControllerTest extends AppIntegrationTestCase use GroupsModelTrait; use GroupsUsersModelTrait; - public $fixtures = ['app.Base/Users', 'app.Base/Profiles', 'app.Base/Groups', + public $fixtures = ['app.Base/Users', 'app.Base/Profiles', 'app.Base/Roles', 'app.Base/Profiles', 'app.Base/Groups', 'app.Base/GroupsUsers', 'app.Base/Permissions']; public function testGroupsIndexSuccess() diff --git a/tests/TestCase/Controller/Groups/GroupsUpdateControllerTest.php b/tests/TestCase/Controller/Groups/GroupsUpdateControllerTest.php index b4ac042fac..321d060834 100644 --- a/tests/TestCase/Controller/Groups/GroupsUpdateControllerTest.php +++ b/tests/TestCase/Controller/Groups/GroupsUpdateControllerTest.php @@ -577,11 +577,6 @@ public function testGroupsUpdateAsAdminCannotDeleteGroupUserError() $this->assertnotEmpty($groupUser); } - public function testGroupsUpdateCannotModifyNotAccessibleFields() - { - $this->markTestIncomplete(); - } - public function testGroupsUpdateErrorNotValidId() { $this->authenticateAs('ada'); diff --git a/tests/TestCase/Controller/Groups/GroupsUpdateDryRunControllerTest.php b/tests/TestCase/Controller/Groups/GroupsUpdateDryRunControllerTest.php index d6ec06b3a4..4db15016e8 100644 --- a/tests/TestCase/Controller/Groups/GroupsUpdateDryRunControllerTest.php +++ b/tests/TestCase/Controller/Groups/GroupsUpdateDryRunControllerTest.php @@ -26,7 +26,7 @@ class GroupsUpdateDryRunControllerTest extends AppIntegrationTestCase { public $fixtures = [ 'app.Base/Groups', 'app.Base/GroupsUsers', 'app.Base/Resources', 'app.Base/Permissions', - 'app.Base/Users', 'app.Base/Secrets', + 'app.Base/Users', 'app.Base/Profiles', 'app.Base/Roles', 'app.Base/Secrets', ]; public function setUp(): void @@ -181,11 +181,6 @@ public function testGroupsUpdateDryRunAsAdminSuccess() $this->assertEmpty($result['dry-run']['Secrets']); } - public function testGroupsUpdateDryRunCannotModifyNotAccessibleFields() - { - $this->markTestIncomplete(); - } - public function testGroupsUpdateDryRunErrorNotValidId() { $this->authenticateAs('ada'); diff --git a/tests/TestCase/Controller/Groups/GroupsViewControllerTest.php b/tests/TestCase/Controller/Groups/GroupsViewControllerTest.php index fe9a836cc3..f0b75de375 100644 --- a/tests/TestCase/Controller/Groups/GroupsViewControllerTest.php +++ b/tests/TestCase/Controller/Groups/GroupsViewControllerTest.php @@ -28,7 +28,7 @@ class GroupsViewControllerTest extends AppIntegrationTestCase use GroupsUsersModelTrait; public $fixtures = [ - 'app.Base/Users', 'app.Base/Profiles', 'app.Base/Groups', + 'app.Base/Users', 'app.Base/Profiles', 'app.Base/Roles', 'app.Base/Groups', 'app.Base/GroupsUsers', 'app.Base/Gpgkeys', 'app.Base/Permissions', ]; diff --git a/tests/TestCase/Controller/Notifications/ResourcesAddNotificationTest.php b/tests/TestCase/Controller/Notifications/ResourcesAddNotificationTest.php index 6b3625a0e5..60a3267f20 100644 --- a/tests/TestCase/Controller/Notifications/ResourcesAddNotificationTest.php +++ b/tests/TestCase/Controller/Notifications/ResourcesAddNotificationTest.php @@ -112,10 +112,4 @@ public function testResourcesAddNotificationSuccess() $this->assertResponseCode(200); $this->assertResponseContains('You have saved a new password'); } - - public function testResourcesAddNotificationSettings() - { - // Test the configuration flags like passbolt.show.resource.url - $this->markTestIncomplete(); - } } diff --git a/tests/TestCase/Controller/Notifications/ResourcesUpdateNotificationTest.php b/tests/TestCase/Controller/Notifications/ResourcesUpdateNotificationTest.php index 906ec89878..8ef53b52f7 100644 --- a/tests/TestCase/Controller/Notifications/ResourcesUpdateNotificationTest.php +++ b/tests/TestCase/Controller/Notifications/ResourcesUpdateNotificationTest.php @@ -136,10 +136,4 @@ public function testResourcesUpdateNotificationSuccess() $this->assertResponseCode(200); $this->assertResponseContains('updated the password'); } - - public function testResourcesUpdateNotificationSettings() - { - // Test the configuration flags like passbolt.show.resource.url - $this->markTestIncomplete(); - } } diff --git a/tests/TestCase/Controller/Notifications/UsersDeleteNotificationTest.php b/tests/TestCase/Controller/Notifications/UsersDeleteNotificationTest.php index 1e34eea428..85a7694a5c 100644 --- a/tests/TestCase/Controller/Notifications/UsersDeleteNotificationTest.php +++ b/tests/TestCase/Controller/Notifications/UsersDeleteNotificationTest.php @@ -17,13 +17,12 @@ namespace App\Test\TestCase\Controller\Notifications; use App\Test\Lib\AppIntegrationTestCase; +use App\Test\Lib\Model\EmailQueueTrait; use App\Utility\UuidFactory; class UsersDeleteNotificationTest extends AppIntegrationTestCase { - public $Users; - public $GroupsUsers; - public $Permissions; + use EmailQueueTrait; public $fixtures = [ 'app.Base/Users', 'app.Base/Groups', 'app.Base/Profiles', 'app.Base/Gpgkeys', 'app.Base/Roles', @@ -53,5 +52,8 @@ public function testUsersDeleteNotificationSuccess() $this->get('/seleniumtests/showlastemail/wang@passbolt.com'); $this->assertResponseCode(500); $this->assertResponseContains('No email was sent to this user.'); + + // Two mails should be in the queue + $this->assertEmailQueueCount(2); } } diff --git a/tests/TestCase/Controller/Permissions/PermissionsViewAcoPermissionsControllerTest.php b/tests/TestCase/Controller/Permissions/PermissionsViewAcoPermissionsControllerTest.php index a54add0f05..c2ab2b8cdc 100644 --- a/tests/TestCase/Controller/Permissions/PermissionsViewAcoPermissionsControllerTest.php +++ b/tests/TestCase/Controller/Permissions/PermissionsViewAcoPermissionsControllerTest.php @@ -28,7 +28,7 @@ class PermissionsViewAcoPermissionsControllerTest extends AppIntegrationTestCase public $fixtures = [ 'app.Base/Groups', 'app.Base/GroupsUsers', 'app.Base/Permissions', - 'app.Base/Profiles', 'app.Base/Resources', 'app.Base/Users', + 'app.Base/Profiles', 'app.Base/Resources', 'app.Base/Users', 'app.Base/Roles', ]; public function testPermissionsViewSuccess() @@ -72,10 +72,6 @@ public function testPermissionsViewContainSuccess() // Contain group. $this->assertObjectHasAttribute('group', $permission); $this->assertGroupAttributes($permission->group); - - $this->markTestIncomplete('TODO: create an Avatar for the user relevant to the test.'); - $this->assertObjectHasAttribute('avatar', $permission->user->profile); - $this->assertAvatarAttributes($permission->user->profile->avatar); } public function testPermissionsViewErrorNotAuthenticated() diff --git a/tests/TestCase/Controller/Resources/ResourcesAddControllerTest.php b/tests/TestCase/Controller/Resources/ResourcesAddControllerTest.php index bed6276568..868d8b0cb4 100644 --- a/tests/TestCase/Controller/Resources/ResourcesAddControllerTest.php +++ b/tests/TestCase/Controller/Resources/ResourcesAddControllerTest.php @@ -18,145 +18,199 @@ namespace App\Test\TestCase\Controller\Resources; use App\Model\Entity\Permission; +use App\Notification\Email\Redactor\Resource\ResourceCreateEmailRedactor; +use App\Service\Resources\ResourcesAddService; +use App\Test\Factory\ResourceTypeFactory; +use App\Test\Factory\UserFactory; use App\Test\Lib\AppIntegrationTestCase; +use App\Test\Lib\Model\EmailQueueTrait; +use App\Test\Lib\Model\ResourcesModelTrait; use App\Utility\UuidFactory; +use Cake\Event\EventList; use Cake\ORM\TableRegistry; use Cake\Utility\Hash; +use Passbolt\JwtAuthentication\Service\AccessToken\JwtKeyPairService; +use Passbolt\JwtAuthentication\Test\Utility\JwtAuthTestTrait; class ResourcesAddControllerTest extends AppIntegrationTestCase { - public $fixtures = [ - 'app.Base/Users', 'app.Base/Groups', 'app.Base/GroupsUsers', 'app.Base/Resources', 'app.Base/Profiles', - 'app.Base/Secrets', 'app.Base/Permissions', 'app.Base/Roles', 'app.Base/Favorites', - 'app.Base/ResourceTypes', - ]; + use EmailQueueTrait; + use JwtAuthTestTrait; + use ResourcesModelTrait; + + /** + * @var \App\Model\Table\ResourcesTable + */ + public $Resources; + + /** + * @var \App\Model\Table\SecretsTable + */ + public $Secrets; + + /** + * @var \App\Model\Table\PermissionsTable + */ + public $Permissions; public function setUp(): void { $this->Resources = TableRegistry::getTableLocator()->get('Resources'); + $this->Secrets = TableRegistry::getTableLocator()->get('Secrets'); + $this->Permissions = TableRegistry::getTableLocator()->get('Permissions'); + $this->Resources->getEventManager()->setEventList(new EventList()); + ResourceTypeFactory::make()->default()->persist(); + (new JwtKeyPairService())->createKeyPair(); + $this->enableFeaturePlugin('JwtAuthentication'); + $this->setEmailNotificationsSetting('password.create', true); parent::setUp(); } - protected function _getGpgMessage() + public function tearDown(): void { - return '-----BEGIN PGP MESSAGE----- - -hQIMA1P90Qk1JHA+ARAAu3oaLzv/BfeukST6tYAkAID+xbt5dhsv4lxL3oSbo8Nm -qmJQSVe6wmh8nZJjeHN4L7iCq8FEZpdCwrDbX1qIuqBFFO3vx6BJFOURG0JbI/E/ -nXtvck00RvxTB1Y30OUbGp21jjEILyuELhWpf11+AQelybY4XKyM8UxGjSncDqaS -X7/yXspCByywci1VfzK7D6+zfcyLy29wQm9Ci5j6I4QqhvlKQPTxl6tWrJh+EyLP -SLZjO8ofc00fbc7mUIH5taDg6Br2VLG/x29HhKCPYdOVzSz3BpUCcUcPgn98mCV0 -Qh7ZPE1NNmCWXID5hryuSF71IiAYhxae9u77pOAbVe0PwFgMY6kke/hJQkO6IYJ/ -/Q3aL/xHTlY2XtPbpV1in6soc0wJBuoROrwN0AdtvEJOnomclNEH5BPwLjZ1shCr -vuk0zJjj9WcqQiVNEuErs4d7rLc+dB7md+97S8Gtcf8lrlZMH9ooI2UnvxC8HRqX -KzcgW17YF44VtD2TLMymvpnjPV9gruYnmpkQG/1ihnDOWe6xWlFH6jZf5eE4IEVn -osx/D6inZHHMXWbZu9hMiQloKKZ0s8yxTFw9C1wFwaIxRtvJ84qc17rJs7mfcC2n -sG7jLzQBV/GVWtR4hVebstP+q05Sib+sKwLOTZhzWNPKruBsdaBCUTxcmI6qwDHS -QQFgGx0K1xQj2rKiP2j0cDHyGsWIlOITN+4r6Ohx23qRhVo0txPWVOYLpC8JnlfQ -W3AI8+rWjK8MGH2T88hCYI/6 -=uahb ------END PGP MESSAGE-----'; + parent::tearDown(); + $this->disableFeaturePlugin('JwtAuthentication'); + $this->restoreEmailNotificationsSettings(); + unset($this->Resources); + unset($this->Permissions); + unset($this->Resources); } - protected function _getDummyPostData($data = []) + public function testResourcesAddSuccess() { - $defaultData = [ - 'name' => 'new resource name', + $user = UserFactory::make()->user()->persist(); + $this->logInAs($user); + + $data = $this->getDummyResourcesPostData([ + 'name' => '新的專用資源名稱', 'username' => 'username@domain.com', - 'uri' => 'https://www.domain.com', - 'description' => 'new resource description', - 'secrets' => [ - [ - 'data' => $this->_getGpgMessage(), - ], - ], - ]; - $data = array_merge($defaultData, $data); + 'uri' => 'https://www.域.com', + 'description' => '新的資源描述', + ]); + + $this->postJson('/resources.json?api-version=2', $data); + $this->assertSuccess(); + + // Check the server response. + $resource = $this->_responseJsonBody; + + // Check the resource attributes. + $this->assertResourceAttributes($resource); + $this->assertEquals($data['name'], $resource->name); + $this->assertEquals($data['username'], $resource->username); + $this->assertEquals($data['uri'], $resource->uri); + $this->assertEquals($data['description'], $resource->description); + $this->assertEquals($user->id, $resource->created_by); + $this->assertEquals($user->id, $resource->modified_by); - return $data; + // Check the creator attribute + $this->assertNotNull($resource->creator); + $this->assertUserAttributes($resource->creator); + $this->assertEquals($user->id, $resource->creator->id); + + // Check the modifier attribute + $this->assertNotNull($resource->modifier); + $this->assertUserAttributes($resource->modifier); + $this->assertEquals($user->id, $resource->modifier->id); + + // Check the permission attribute + $this->assertNotNull($resource->permission); + $this->assertPermissionAttributes($resource->permission); + $this->assertEquals('Resource', $resource->permission->aco); + $this->assertEquals($resource->id, $resource->permission->aco_foreign_key); + $this->assertEquals('User', $resource->permission->aro); + $this->assertEquals($user->id, $resource->permission->aro_foreign_key); + $this->assertEquals(Permission::OWNER, $resource->permission->type); + + // Check the secret attribute + $this->assertNotEmpty($resource->secrets); + $this->assertSecretAttributes($resource->secrets[0]); + $this->assertCount(1, $resource->secrets); + $this->assertEquals($user->id, $resource->secrets[0]->user_id); + $this->assertEquals($resource->id, $resource->secrets[0]->resource_id); + $this->assertEquals($data['secrets'][0]['data'], $resource->secrets[0]->data); + + // Ensure that an email was sent + $this->assertEmailIsInQueue([ + 'email' => $user->username, + 'subject' => 'You added the password ' . $data['name'], + 'template' => ResourceCreateEmailRedactor::TEMPLATE, + ]); + $this->assertEmailQueueCount(1); + + $this->assertEventFired(ResourcesAddService::ADD_SUCCESS_EVENT_NAME, $this->Resources->getEventManager()); } - public function testResourcesAddSuccess() + public function testResourcesAddSuccessWithJWT() { - $success = [ - 'chinese' => $this->_getDummyPostData([ - 'name' => '新的專用資源名稱', - 'username' => 'username@domain.com', - 'uri' => 'https://www.域.com', - 'description' => '新的資源描述', - ]), - 'slavic' => $this->_getDummyPostData([ - 'name' => 'Новое имя частного ресурса', - 'username' => 'username@domain.com', - 'uri' => 'https://www.домен.com', - 'description' => 'Новое описание частного ресурса', - ]), - 'french' => $this->_getDummyPostData([ - 'name' => 'Nouveau nom de resource privée', - 'username' => 'username@domain.com', - 'uri' => 'https://www.mon-domain.com', - 'description' => 'Nouvelle description de resource privée', - ]), - 'emoticon' => $this->_getDummyPostData([ - 'name' => "\u{1F61C}\u{1F61C}\u{1F61C}\u{1F61C}\u{1F61C}\u{1F61C}\u{1F61C}", - 'username' => 'username@domain.com', - 'uri' => 'https://www.domain.com', - 'description' => "\u{1F61C}\u{1F61C}\u{1F61C}\u{1F61C}\u{1F61C}\u{1F61C}\u{1F61C}", - ]), - ]; + $user = UserFactory::make()->user()->persist(); + $this->createJwtTokenAndSetInHeader($user->id); + + $data = $this->getDummyResourcesPostData([ + 'name' => '新的專用資源名稱', + 'username' => 'username@domain.com', + 'uri' => 'https://www.域.com', + 'description' => '新的資源描述', + ]); - foreach ($success as $case => $data) { - $userId = UuidFactory::uuid('user.id.ada'); - $this->authenticateAs('ada'); - $this->postJson('/resources.json?api-version=2', $data); - $this->assertSuccess(); - - // Check the server response. - $resource = $this->_responseJsonBody; - - // Check the resource attributes. - $this->assertResourceAttributes($resource); - $this->assertEquals($data['name'], $resource->name); - $this->assertEquals($data['username'], $resource->username); - $this->assertEquals($data['uri'], $resource->uri); - $this->assertEquals($data['description'], $resource->description); - $this->assertEquals($userId, $resource->created_by); - $this->assertEquals($userId, $resource->modified_by); - - // Check the creator attribute - $this->assertNotNull($resource->creator); - $this->assertUserAttributes($resource->creator); - $this->assertEquals($userId, $resource->creator->id); - - // Check the modifier attribute - $this->assertNotNull($resource->modifier); - $this->assertUserAttributes($resource->modifier); - $this->assertEquals($userId, $resource->modifier->id); - - // Check the permission attribute - $this->assertNotNull($resource->permission); - $this->assertPermissionAttributes($resource->permission); - $this->assertEquals('Resource', $resource->permission->aco); - $this->assertEquals($resource->id, $resource->permission->aco_foreign_key); - $this->assertEquals('User', $resource->permission->aro); - $this->assertEquals($userId, $resource->permission->aro_foreign_key); - $this->assertEquals(Permission::OWNER, $resource->permission->type); - - // Check the secret attribute - $this->assertNotEmpty($resource->secrets); - $this->assertSecretAttributes($resource->secrets[0]); - $this->assertCount(1, $resource->secrets); - $this->assertEquals($userId, $resource->secrets[0]->user_id); - $this->assertEquals($resource->id, $resource->secrets[0]->resource_id); - $this->assertEquals($data['secrets'][0]['data'], $resource->secrets[0]->data); - } + $this->postJson('/resources.json?api-version=2', $data); + $this->assertSuccess(); + + // Check the server response. + $resource = $this->_responseJsonBody; + + // Check the resource attributes. + $this->assertResourceAttributes($resource); + $this->assertEquals($data['name'], $resource->name); + $this->assertEquals($data['username'], $resource->username); + $this->assertEquals($data['uri'], $resource->uri); + $this->assertEquals($data['description'], $resource->description); + $this->assertEquals($user->id, $resource->created_by); + $this->assertEquals($user->id, $resource->modified_by); + + // Check the creator attribute + $this->assertNotNull($resource->creator); + $this->assertUserAttributes($resource->creator); + $this->assertEquals($user->id, $resource->creator->id); + + // Check the modifier attribute + $this->assertNotNull($resource->modifier); + $this->assertUserAttributes($resource->modifier); + $this->assertEquals($user->id, $resource->modifier->id); + + // Check the permission attribute + $this->assertNotNull($resource->permission); + $this->assertPermissionAttributes($resource->permission); + $this->assertEquals('Resource', $resource->permission->aco); + $this->assertEquals($resource->id, $resource->permission->aco_foreign_key); + $this->assertEquals('User', $resource->permission->aro); + $this->assertEquals($user->id, $resource->permission->aro_foreign_key); + $this->assertEquals(Permission::OWNER, $resource->permission->type); + + // Check the secret attribute + $this->assertNotEmpty($resource->secrets); + $this->assertSecretAttributes($resource->secrets[0]); + $this->assertCount(1, $resource->secrets); + $this->assertEquals($user->id, $resource->secrets[0]->user_id); + $this->assertEquals($resource->id, $resource->secrets[0]->resource_id); + $this->assertEquals($data['secrets'][0]['data'], $resource->secrets[0]->data); + + // Ensure that an email was sent + $this->assertEmailIsInQueue([ + 'email' => $user->username, + 'subject' => 'You added the password ' . $data['name'], + 'template' => ResourceCreateEmailRedactor::TEMPLATE, + ]); + $this->assertEmailQueueCount(1); + + $this->assertEventFired(ResourcesAddService::ADD_SUCCESS_EVENT_NAME, $this->Resources->getEventManager()); } public function testResourcesAddCsrfTokenError() { $this->disableCsrfToken(); - $this->authenticateAs('ada'); - $data = $this->_getDummyPostData(); + $this->logInAsUser(); + $data = $this->getDummyResourcesPostData(); $this->post('/resources.json', $data); $this->assertResponseCode(403); // This will throw a route not found exeption @@ -165,73 +219,92 @@ public function testResourcesAddCsrfTokenError() $this->assertStringContainsString($expect, $data); } - public function testResourcesAddValidationErrors() + /** + * @dataProvider dataForTestResourcesAddValidationErrors + * @dataProvider dataForTestResourcesAddBuildRulesErrors + */ + public function testResourcesAddValidationErrors(string $caseLabel, array $case) { - $responseCode = 400; - $responseMessage = 'Could not validate resource data'; - $errors = [ - 'resource name is missing' => [ + $this->logInAsUser(); + $this->postJson('/resources.json?api-version=v2', $case['data']); + $this->assertError(400, 'Could not validate resource data'); + $arr = json_decode(json_encode($this->_responseJsonBody), true); + $error = Hash::get($arr, $case['errorField']); + $this->assertNotNull($error, "The case \"$caseLabel\" should fail"); + $this->assertSame(0, $this->Resources->find()->count()); + $this->assertSame(0, $this->Secrets->find()->count()); + $this->assertSame(0, $this->Permissions->find()->count()); + $this->assertEmailQueueIsEmpty(); + } + + public function dataForTestResourcesAddValidationErrors(): array + { + return [ + ['resource name is missing', [ 'errorField' => 'name._empty', - 'data' => $this->_getDummyPostData(['name' => null]), - ], - 'secret must be provided' => [ + 'data' => $this->getDummyResourcesPostData(['name' => null]), + ]], + ['secret must be provided', [ 'errorField' => 'secrets._empty', - 'data' => $this->_getDummyPostData(['secrets' => null]), - ], - 'secret data must be provided' => [ + 'data' => $this->getDummyResourcesPostData(['secrets' => null]), + ]], + ['secret data must be provided', [ 'errorField' => 'secrets.0.data._required', - 'data' => $this->_getDummyPostData(['secrets' => []]), - ], - 'secret is invalid' => [ + 'data' => $this->getDummyResourcesPostData(['secrets' => []]), + ]], + ['secret is invalid', [ 'errorField' => 'secrets.0.data.isValidGpgMessage', - 'data' => $this->_getDummyPostData(['secrets' => [ + 'data' => $this->getDummyResourcesPostData(['secrets' => [ 0 => ['data' => 'Invalid secret'], ]]), - ], - 'too many secrets provided' => [ + ]], + ['too many secrets provided', [ 'errorField' => 'secrets.hasAtMost', - 'data' => $this->_getDummyPostData(['secrets' => [ - 0 => ['data' => $this->_getGpgMessage()], - 1 => ['user_id' => UuidFactory::uuid('user.id.betty'), 'data' => $this->_getGpgMessage()], + 'data' => $this->getDummyResourcesPostData(['secrets' => [ + 0 => ['data' => $this->getDummyGpgMessage()], + 1 => ['user_id' => UuidFactory::uuid('user.id.betty'), 'data' => $this->getDummyGpgMessage()], ]]), - ], - 'invalid resource type' => [ + ]], + ['invalid resource type', [ 'errorField' => 'resource_type_id', - 'data' => $this->_getDummyPostData([ + 'data' => $this->getDummyResourcesPostData([ 'name' => 'new resource name', 'username' => 'username@domain.com', 'uri' => 'https://www.domain.com', 'description' => 'new resource description', 'resource_type_id' => 'invalid', ]), - ], - 'non-existing resource type' => [ + ]], + ]; + } + + public function dataForTestResourcesAddBuildRulesErrors(): array + { + return [ + ['non-existing resource type', [ 'errorField' => 'resource_type_id', - 'data' => $this->_getDummyPostData([ + 'data' => $this->getDummyResourcesPostData([ 'name' => 'new resource name', 'username' => 'username@domain.com', 'uri' => 'https://www.domain.com', 'description' => 'new resource description', 'resource_type_id' => UuidFactory::uuid(), ]), - ], + ]], ]; + } - foreach ($errors as $caseLabel => $case) { - $this->authenticateAs('ada'); - $this->postJson('/resources.json?api-version=v2', $case['data']); - $this->assertError($responseCode, $responseMessage); - $arr = json_decode(json_encode($this->_responseJsonBody), true); - $error = Hash::get($arr, $case['errorField']); - $this->assertNotNull($error, "The case \"$caseLabel\" should fail"); - $this->assertResourceNotExist(['name' => $case['data']['name'] ?? ' ']); - } + public function testResourcesAddNonValidUserUuid() + { + $user = UserFactory::make(['id' => 'Not a valid UUID'])->getEntity(); + $this->logInAs($user); + $this->postJson('/resources.json?api-version=v2'); + $this->assertResponseFailure('The user identifier should be a valid UUID.'); } public function testResourcesAddErrorNotAuthenticated() { - $data = $this->_getDummyPostData(); - $this->postJson('/resources.json?api-version=v2', $data); + $this->postJson('/resources.json?api-version=v2'); $this->assertAuthenticationError(); } } diff --git a/tests/TestCase/Controller/Resources/ResourcesIndexControllerTest.php b/tests/TestCase/Controller/Resources/ResourcesIndexControllerTest.php index 3a14fd0219..23fb7868ee 100644 --- a/tests/TestCase/Controller/Resources/ResourcesIndexControllerTest.php +++ b/tests/TestCase/Controller/Resources/ResourcesIndexControllerTest.php @@ -29,7 +29,7 @@ class ResourcesIndexControllerTest extends AppIntegrationTestCase use FavoritesModelTrait; public $fixtures = [ - 'app.Base/Users', 'app.Base/Profiles', 'app.Base/Groups', 'app.Base/GroupsUsers', 'app.Base/Resources', + 'app.Base/Users', 'app.Base/Profiles', 'app.Base/Roles', 'app.Base/Groups', 'app.Base/GroupsUsers', 'app.Base/Resources', 'app.Base/Secrets', 'app.Base/Favorites', 'app.Base/Permissions', ]; diff --git a/tests/TestCase/Controller/Resources/ResourcesViewControllerTest.php b/tests/TestCase/Controller/Resources/ResourcesViewControllerTest.php index a7166a51a5..ec33090db2 100644 --- a/tests/TestCase/Controller/Resources/ResourcesViewControllerTest.php +++ b/tests/TestCase/Controller/Resources/ResourcesViewControllerTest.php @@ -29,7 +29,7 @@ class ResourcesViewControllerTest extends AppIntegrationTestCase use GroupsModelTrait; public $fixtures = [ - 'app.Base/Users', 'app.Base/Profiles', 'app.Base/Groups', 'app.Base/GroupsUsers', 'app.Base/Resources', + 'app.Base/Users', 'app.Base/Profiles', 'app.Base/Roles', 'app.Base/Groups', 'app.Base/GroupsUsers', 'app.Base/Resources', 'app.Base/Secrets', 'app.Base/Favorites', 'app.Base/Permissions', ]; diff --git a/tests/TestCase/Controller/Secrets/SecretsViewControllerTest.php b/tests/TestCase/Controller/Secrets/SecretsViewControllerTest.php index 02d3d5b927..09d59ff515 100644 --- a/tests/TestCase/Controller/Secrets/SecretsViewControllerTest.php +++ b/tests/TestCase/Controller/Secrets/SecretsViewControllerTest.php @@ -23,7 +23,7 @@ class SecretsViewControllerTest extends AppIntegrationTestCase { public $fixtures = [ - 'app.Base/Users', 'app.Base/Secrets', + 'app.Base/Users', 'app.Base/Profiles', 'app.Base/Roles', 'app.Base/Secrets', ]; public function testSuccess() diff --git a/tests/TestCase/Controller/Setup/RecoverCompleteControllerTest.php b/tests/TestCase/Controller/Setup/RecoverCompleteControllerTest.php index f308f22643..6dd6b6223e 100644 --- a/tests/TestCase/Controller/Setup/RecoverCompleteControllerTest.php +++ b/tests/TestCase/Controller/Setup/RecoverCompleteControllerTest.php @@ -19,12 +19,15 @@ use App\Model\Entity\AuthenticationToken; use App\Test\Lib\AppIntegrationTestCase; use App\Test\Lib\Model\AuthenticationTokenModelTrait; +use App\Test\Lib\Model\EmailQueueTrait; use App\Utility\UuidFactory; +use Cake\Core\Configure; use Cake\ORM\TableRegistry; class RecoverCompleteControllerTest extends AppIntegrationTestCase { use AuthenticationTokenModelTrait; + use EmailQueueTrait; public $fixtures = ['app.Base/Users', 'app.Base/Profiles', 'app.Base/Gpgkeys', 'app.Base/Roles',]; public $AuthenticationTokens; @@ -44,6 +47,8 @@ public function setUp(): void */ public function testRecoverCompleteSuccess() { + $logEnabled = Configure::read('passbolt.plugins.log.enabled'); + Configure::write('passbolt.plugins.log.enabled', true); $t = $this->AuthenticationTokens->generate(UuidFactory::uuid('user.id.ada'), AuthenticationToken::TYPE_RECOVER); $url = '/setup/recover/complete/' . UuidFactory::uuid('user.id.ada') . '.json'; $armoredKey = file_get_contents(FIXTURES . DS . 'Gpgkeys' . DS . 'ada_public.key'); @@ -61,6 +66,8 @@ public function testRecoverCompleteSuccess() // Check that token is now inactive $t2 = $this->AuthenticationTokens->get($t->id); $this->assertFalse($t2->active); + $this->assertEmailQueueIsEmpty(); + Configure::write('passbolt.plugins.log.enabled', $logEnabled); } /** diff --git a/tests/TestCase/Controller/Setup/SetupCompleteControllerTest.php b/tests/TestCase/Controller/Setup/SetupCompleteControllerTest.php index d198e59744..a88604c8b8 100644 --- a/tests/TestCase/Controller/Setup/SetupCompleteControllerTest.php +++ b/tests/TestCase/Controller/Setup/SetupCompleteControllerTest.php @@ -19,13 +19,16 @@ use App\Model\Entity\AuthenticationToken; use App\Test\Lib\AppIntegrationTestCase; use App\Test\Lib\Model\AuthenticationTokenModelTrait; +use App\Test\Lib\Model\EmailQueueTrait; use App\Utility\UuidFactory; +use Cake\Core\Configure; use Cake\ORM\TableRegistry; use Passbolt\Locale\Service\LocaleService; class SetupCompleteControllerTest extends AppIntegrationTestCase { use AuthenticationTokenModelTrait; + use EmailQueueTrait; public $fixtures = [ 'app.Base/Users', 'app.Base/Profiles', 'app.Base/Gpgkeys', 'app.Base/Roles', @@ -47,6 +50,8 @@ public function setUp(): void */ public function testSetupCompleteSuccess() { + $logEnabled = Configure::read('passbolt.plugins.log.enabled'); + Configure::write('passbolt.plugins.log.enabled', true); $t = $this->AuthenticationTokens->generate(UuidFactory::uuid('user.id.ruth'), AuthenticationToken::TYPE_REGISTER); $url = '/setup/complete/' . UuidFactory::uuid('user.id.ruth') . '.json'; $armoredKey = file_get_contents(FIXTURES . DS . 'Gpgkeys' . DS . 'ruth_public.key'); @@ -69,6 +74,12 @@ public function testSetupCompleteSuccess() ->getFirstPropertyOrFail(UuidFactory::uuid('user.id.ruth'), LocaleService::SETTING_PROPERTY) ->value; $this->assertSame('fr-FR', $userLocale); + $this->assertEmailIsInQueue([ + 'email' => 'admin@passbolt.com', + 'template' => 'LU/user_setup_complete', + 'subject' => 'Ruth just activated their account on passbolt', + ]); + Configure::write('passbolt.plugins.log.enabled', $logEnabled); } /** diff --git a/tests/TestCase/Controller/Users/UsersEditAvatarControllerTest.php b/tests/TestCase/Controller/Users/UsersEditAvatarControllerTest.php index d6bb1105ba..2c34f8f23f 100644 --- a/tests/TestCase/Controller/Users/UsersEditAvatarControllerTest.php +++ b/tests/TestCase/Controller/Users/UsersEditAvatarControllerTest.php @@ -16,17 +16,17 @@ */ namespace App\Test\TestCase\Controller\Users; +use App\Test\Factory\AvatarFactory; use App\Test\Factory\RoleFactory; use App\Test\Factory\UserFactory; use App\Test\Lib\AppIntegrationTestCase; -use App\Test\Lib\Model\AvatarsModelTestTrait; +use App\Test\Lib\Model\AvatarsModelTrait; use App\Utility\UuidFactory; use Cake\ORM\TableRegistry; -use League\Flysystem\Local\LocalFilesystemAdapter; class UsersEditAvatarControllerTest extends AppIntegrationTestCase { - use AvatarsModelTestTrait; + use AvatarsModelTrait; public $localFileStorageListener = null; public $imageProcessingListener = null; @@ -40,8 +40,7 @@ public function setUp(): void { parent::setUp(); $this->Avatars = TableRegistry::getTableLocator()->get('Avatars'); - $this->Avatars->setFilesystem(new LocalFilesystemAdapter(TMP . 'tests' . DS . 'avatars')); - + $this->setTestLocalFilesystemAdapter(); RoleFactory::make()->guest()->persist(); } @@ -134,18 +133,14 @@ public function testUsersEditAvatarNoDataProvided() public function testUsersEditAvatarCantOverrideData() { - $this->markTestSkipped('Not possible to fake file upload, issue with validation'); - $adaAvatar = FIXTURES . 'Avatar' . DS . 'ada.png'; + $irene = UserFactory::make()->user()->persist(); - $this->authenticateAs('irene'); + $this->logInAs($irene); $data = [ - 'id' => UuidFactory::uuid('user.id.irene'), + 'id' => $irene->id, 'profile' => [ 'avatar' => [ - 'file' => [ - 'tmp_name' => $adaAvatar, - 'name' => 'irene.png', - ], + 'file' => $this->createUploadFile(), 'user_id' => UuidFactory::uuid('user.id.whatever'), 'foreign_key' => UuidFactory::uuid('profile.id.whatever'), 'model' => 'Test', @@ -159,25 +154,27 @@ public function testUsersEditAvatarCantOverrideData() ], ], ]; - $this->postJson('/users/' . UuidFactory::uuid('user.id.irene') . '.json', $data); + $this->postJson('/users/' . $irene->id . '.json', $data); $this->assertSuccess(); - $ireneAvatar = $this->Avatars - ->find() - ->orderDesc('created') - ->first(); + /** @var \App\Model\Entity\Avatar $ireneAvatar */ + $ireneAvatar = AvatarFactory::find() + ->contain('Profiles') + ->orderDesc('Avatars.created') + ->firstOrFail(); $data = $data['profile']['avatar']; - $this->assertNotEquals($data['user_id'], $ireneAvatar->user_id); + + $this->assertNotEquals($data['user_id'], $ireneAvatar->profile->user_id); $this->assertNotEquals($data['foreign_key'], $ireneAvatar->foreign_key); $this->assertNotEquals($data['model'], $ireneAvatar->model); $this->assertNotEquals($data['filename'], $ireneAvatar->filename); - $this->assertEquals('irene.png', $ireneAvatar->filename); $this->assertNotEquals($data['filesize'], $ireneAvatar->filesize); $this->assertNotEquals($data['mime_type'], $ireneAvatar->mime_type); $this->assertNotEquals($data['extension'], $ireneAvatar->extension); $this->assertNotEquals($data['hash'], $ireneAvatar->hash); $this->assertNotEquals($data['path'], $ireneAvatar->path); $this->assertNotEquals($data['adapter'], $ireneAvatar->adapter); + $this->assertSame(1, AvatarFactory::count()); } } diff --git a/tests/TestCase/Controller/Users/UsersIndexControllerTest.php b/tests/TestCase/Controller/Users/UsersIndexControllerTest.php index 6ac14543c2..8e84f27ee7 100644 --- a/tests/TestCase/Controller/Users/UsersIndexControllerTest.php +++ b/tests/TestCase/Controller/Users/UsersIndexControllerTest.php @@ -17,6 +17,8 @@ namespace App\Test\TestCase\Controller\Users; +use App\Test\Factory\ProfileFactory; +use App\Test\Factory\ResourceFactory; use App\Test\Factory\RoleFactory; use App\Test\Factory\UserFactory; use App\Test\Lib\AppIntegrationTestCase; @@ -25,6 +27,7 @@ use App\Utility\UuidFactory; use Cake\Chronos\Date; use Cake\Utility\Hash; +use Faker\Generator; class UsersIndexControllerTest extends AppIntegrationTestCase { @@ -105,7 +108,13 @@ public function testUsersIndexOrderByUsername() public function testUsersIndexOrderByFirstName() { RoleFactory::make()->guest()->persist(); - UserFactory::make(5)->user()->with('Profiles')->persist(); + UserFactory::make(5)->user()->with( + 'Profiles', + function (ProfileFactory $factory, Generator $faker) { + // Makes sure that all first name are distinct + return ['first_name' => $faker->unique()->firstName()]; + } + )->persist(); $this->logInAsUser(); @@ -121,7 +130,13 @@ public function testUsersIndexOrderByFirstName() public function testUsersIndexOrderByLastName() { RoleFactory::make()->guest()->persist(); - UserFactory::make(5)->user()->with('Profiles')->persist(); + UserFactory::make(5)->user()->with( + 'Profiles', + function (ProfileFactory $factory, Generator $faker) { + // Makes sure that all last name are distinct + return ['last_name' => $faker->unique()->lastName(),]; + } + )->persist(); $this->logInAsUser(); @@ -274,7 +289,17 @@ public function testUsersIndexFilterByInvalidSearchError() public function testUsersIndexFilterByHasAccessSuccess() { - $this->markTestIncomplete(); + RoleFactory::make()->guest()->persist(); + $user = UserFactory::make(2)->user()->persist()[0]; + $resourceFactory = ResourceFactory::make(); + $resource = $resourceFactory->withCreatorAndPermission($user)->persist(); + $resourceFactory->persist(); + + $this->logInAs($user); + $this->getJson('/users.json?api-version=v2&filter[has-access]=' . $resource->id); + $this->assertResponseOk(); + $this->assertCount(1, $this->_responseJsonBody); + $this->assertSame($user->id, $this->_responseJsonBody[0]->id); } public function testUsersIndexFilterActiveAsAdminSuccess() diff --git a/tests/TestCase/Controller/Users/UsersRecoverControllerTest.php b/tests/TestCase/Controller/Users/UsersRecoverControllerTest.php index f8165d064f..f866f51b8b 100644 --- a/tests/TestCase/Controller/Users/UsersRecoverControllerTest.php +++ b/tests/TestCase/Controller/Users/UsersRecoverControllerTest.php @@ -16,13 +16,18 @@ */ namespace App\Test\TestCase\Controller\Users; +use App\Test\Factory\UserFactory; use App\Test\Lib\AppIntegrationTestCase; +use App\Test\Lib\Model\EmailQueueTrait; class UsersRecoverControllerTest extends AppIntegrationTestCase { + use EmailQueueTrait; + + public $autoFixtures = false; + public $fixtures = [ 'app.Base/Users', 'app.Base/Roles', 'app.Base/Profiles', - ]; public $fails = [ @@ -36,14 +41,13 @@ class UsersRecoverControllerTest extends AppIntegrationTestCase ], ]; - public $successes = [ - 'can recover an active user' => [ - 'form-data' => ['username' => 'ada@passbolt.com'], - ], - 'can recover a user that has not completed setup' => [ - 'form-data' => ['username' => 'ruth@passbolt.com'], - ], - ]; + public function dataProviderForPostSuccess(): array + { + return [ + ['can recover an active user' => 'ada@passbolt.com', 'email template' => 'AN/user_recover'], + ['can recover a user that has not completed setup' => 'ruth@passbolt.com', 'email template' => 'AN/user_register_self'], + ]; + } public function testRecoverGetRedirect() { @@ -65,6 +69,8 @@ public function testRecoverGetJsonSuccess() public function testRecoverPostErrors() { + $this->loadFixtures(); + foreach ($this->fails as $case => $data) { $this->postJson('/users/recover.json', $data['form-data']); $result = $this->_getBodyAsString(); @@ -74,6 +80,8 @@ public function testRecoverPostErrors() public function testRecoverPostError_UserDeleted() { + $this->loadFixtures(); + $data = ['username' => 'sofia@passbolt.com']; $error = 'This user does not exist or has been deleted.'; $this->postJson('/users/recover.json', $data); @@ -84,6 +92,8 @@ public function testRecoverPostError_UserDeleted() public function testRecoverPostError_UserNotExist() { + $this->loadFixtures(); + $data = ['username' => 'notauser@passbolt.com']; $error = 'This user does not exist or has been deleted.'; $this->postJson('/users/recover.json', $data); @@ -92,13 +102,20 @@ public function testRecoverPostError_UserNotExist() $this->assertStringContainsString($error, $result); } - public function testRecoverPostSuccess() + /** + * @dataProvider dataProviderForPostSuccess + */ + public function testRecoverPostSuccess(string $username, string $emailTemplate) { - foreach ($this->successes as $case => $data) { - $this->postJson('/users/recover.json', $data['form-data']); - $result = $this->_getBodyAsString(); - $this->assertResponseSuccess('Recovery process started, check your email.'); - } + $this->loadFixtures(); + + $this->postJson('/users/recover.json', compact('username')); + $result = $this->_getBodyAsString(); + $this->assertResponseSuccess('Recovery process started, check your email.'); + $this->assertSuccess(); + + $this->assertEmailIsInQueue(['email' => $username, 'template' => $emailTemplate]); + $this->assertEmailQueueCount(1); } public function testRecoverPostJsonError() @@ -109,18 +126,24 @@ public function testRecoverPostJsonError() } } - public function testRecoverPostJsonSuccess() - { - foreach ($this->successes as $case => $data) { - $this->postJson('/users/recover.json', $data['form-data']); - $this->assertSuccess(); - } - } - public function testRecoverPostJsonError_MissingCsrfTokenError() { $this->disableCsrfToken(); $this->post('/users/recover.json?api-version=v2'); $this->assertResponseCode(403); } + + public function testRecoverPostJsonSuccess_For_User_With_Avatar() + { + $user = UserFactory::make()->withAvatar()->user()->persist(); + + $this->postJson('/users/recover.json?api-version=v2', ['username' => $user->username]); + $this->assertSuccess(); + + $this->assertEmailIsInQueue([ + 'email' => $user->username, + 'subject' => "Your account recovery, {$user->profile->first_name}!", + 'template' => 'AN/user_recover', + ]); + } } diff --git a/tests/TestCase/Controller/Users/UsersRegisterControllerTest.php b/tests/TestCase/Controller/Users/UsersRegisterControllerTest.php index 5ddaee479d..599caa2646 100644 --- a/tests/TestCase/Controller/Users/UsersRegisterControllerTest.php +++ b/tests/TestCase/Controller/Users/UsersRegisterControllerTest.php @@ -18,6 +18,7 @@ use App\Model\Entity\Role; use App\Test\Lib\AppIntegrationTestCase; +use App\Test\Lib\Model\EmailQueueTrait; use App\Utility\UuidFactory; use Cake\I18n\FrozenTime; use Cake\ORM\TableRegistry; @@ -26,6 +27,8 @@ class UsersRegisterControllerTest extends AppIntegrationTestCase { + use EmailQueueTrait; + public $fixtures = [ 'app.Base/Users', 'app.Base/Gpgkeys', 'app.Base/Roles', 'app.Base/Profiles', 'app.Base/Permissions', 'app.Base/GroupsUsers', 'app.Base/Groups', 'app.Base/Favorites', 'app.Base/Secrets', @@ -37,61 +40,72 @@ public function testUsersRegisterGetSuccess() $this->assertResponseOk(); } - public function testUsersRegisterPostSuccess() + public function dataProviderFortTestUsersRegisterPostSuccess(): array { - $success = [ - 'chinese_name' => [ + return [ + ['chinese_name' => [ 'username' => 'ping.fu@passbolt.com', 'profile' => [ 'first_name' => '傅', 'last_name' => '苹', ], - ], - 'slavic_name' => [ + ]], + ['slavic_name' => [ 'username' => 'borka@passbolt.com', 'profile' => [ 'first_name' => 'Borka', 'last_name' => 'Jerman Blažič', ], - ], - 'french_name' => [ + ]], + ['french_name' => [ 'username' => 'aurore@passbolt.com', 'profile' => [ 'first_name' => 'Aurore', 'last_name' => 'Avarguès-Weber', ], 'locale' => 'fr-FR', - ], + ]], ]; + } - foreach ($success as $case => $data) { - $this->postJson('/users/register.json', $data); - $this->assertResponseSuccess(); - - // Check user was saved - $users = TableRegistry::getTableLocator()->get('Users'); - $query = $users->find()->where(['username' => $data['username']]); - $this->assertEquals(1, $query->count()); - $user = $query->first(); - $this->assertFalse($user->active); - $this->assertFalse($user->deleted); - - // Check profile exist - $profiles = TableRegistry::getTableLocator()->get('Profiles'); - $query = $profiles->find()->where(['first_name' => $data['profile']['first_name']]); - $this->assertEquals(1, $query->count()); - - // Check role exist - $roles = TableRegistry::getTableLocator()->get('Roles'); - $role = $roles->get($user->get('role_id')); - $this->assertEquals(Role::USER, $role->name); - - // Check locale was stored - GetOrgLocaleService::clearOrganisationLocale(); - $expectedLocale = $data['locale'] ?? GetOrgLocaleService::getLocale(); - $locale = (new GetUserLocaleService())->getLocale($user->username); - $this->assertSame($expectedLocale, $locale); - } + /** + * @dataProvider dataProviderFortTestUsersRegisterPostSuccess + */ + public function testUsersRegisterPostSuccess(array $data) + { + $this->postJson('/users/register.json', $data); + $this->assertResponseSuccess(); + + // Check user was saved + $users = TableRegistry::getTableLocator()->get('Users'); + $query = $users->find()->where(['username' => $data['username']]); + $this->assertEquals(1, $query->count()); + $user = $query->first(); + $this->assertFalse($user->active); + $this->assertFalse($user->deleted); + + // Check profile exist + $profiles = TableRegistry::getTableLocator()->get('Profiles'); + $query = $profiles->find()->where(['first_name' => $data['profile']['first_name']]); + $this->assertEquals(1, $query->count()); + + // Check role exist + $roles = TableRegistry::getTableLocator()->get('Roles'); + $role = $roles->get($user->get('role_id')); + $this->assertEquals(Role::USER, $role->name); + + // Check locale was stored + GetOrgLocaleService::clearOrganisationLocale(); + $expectedLocale = $data['locale'] ?? GetOrgLocaleService::getLocale(); + $locale = (new GetUserLocaleService())->getLocale($user->username); + $this->assertSame($expectedLocale, $locale); + + // Check that an email was sent + $this->assertEmailIsInQueue([ + 'email' => $data['username'], + 'subject' => "Welcome to passbolt, {$data['profile']['first_name']}!", + 'template' => 'AN/user_register_self', + ]); } public function testUsersRegisterPostFailValidation() diff --git a/tests/TestCase/Middleware/UacAwareMiddlewareTraitTest.php b/tests/TestCase/Middleware/UacAwareMiddlewareTraitTest.php new file mode 100644 index 0000000000..f254d0272a --- /dev/null +++ b/tests/TestCase/Middleware/UacAwareMiddlewareTraitTest.php @@ -0,0 +1,45 @@ + UuidFactory::uuid()])->user()->getEntity(); + $request = (new ServerRequest())->withAttribute('identity', $user); + + $uac = $this->getUacInRequest($request); + + $this->assertSame($user->id, $uac->getId()); + $this->assertSame(Role::USER, $uac->roleName()); + $this->assertSame($user->username, $uac->getUsername()); + } +} diff --git a/tests/TestCase/Migrations/V340MigrateASCIIFieldsEncodingTest.php b/tests/TestCase/Migrations/V340MigrateASCIIFieldsEncodingTest.php new file mode 100644 index 0000000000..7c3143cf62 --- /dev/null +++ b/tests/TestCase/Migrations/V340MigrateASCIIFieldsEncodingTest.php @@ -0,0 +1,180 @@ +getDriver() instanceof Mysql)) { + $this->expectNotToPerformAssertions(); + + return; + } + + $schemaCollection = $connection->getSchemaCollection(); + $tables = $schemaCollection->listTables(); + + $errors = []; + foreach ($tables as $table) { + $columns = $schemaCollection->describe($table, ['forceRefresh' => true])->columns(); + foreach ($columns as $columnName) { + $col = ConnectionManager::get('test') + ->execute("SHOW FULL COLUMNS FROM `$table` WHERE Field = '$columnName';") + ->fetch('assoc'); + + $colType = $col['Type']; + $collation = $col['Collation']; + $fullName = $table . '.' . $columnName; + // Skip if it is an integer + if (substr($colType, 0, 3) === 'int') { + continue; + } + if (in_array($fullName, $this->getWhiteList())) { + continue; + } + $isSuspiciousType = in_array($colType, $this->getSuspiciousColumnTypes()); + $isSuspiciousName = in_array($columnName, $this->getSuspiciousAsciiColumnNames()); + $isId = $columnName === 'id' || strpos($columnName, '_id') !== false; + + if ($isId || $isSuspiciousType || $isSuspiciousName) { + if ($collation != 'ascii_general_ci') { + $errors[] = "Column '$columnName' in table '$table' is no ascii_general_ci but '$collation'"; + } + } + } + } + + $this->assertSame([], $errors); + } + + /** + * Check that fields susceptible to be UUID are of UUID type + */ + public function testV340MigrateUuidFields_Postgres() + { + $connection = ConnectionManager::get('test'); + if (!($connection->getDriver() instanceof Postgres)) { + $this->expectNotToPerformAssertions(); + + return; + } + $schemaCollection = ConnectionManager::get('test')->getSchemaCollection(); + $tables = $schemaCollection->listTables(); + + $errors = []; + foreach ($tables as $table) { + $columns = $schemaCollection->describe($table, ['forceRefresh' => true])->columns(); + foreach ($columns as $columnName) { + $col = ConnectionManager::get('test') + ->execute("SELECT table_name, column_name, data_type + FROM information_schema.columns WHERE + table_name = '$table' AND column_name = '$columnName'; + ") + ->fetch('assoc'); + + $dataType = $col['data_type']; + + if ($this->isUuid($table, $columnName) && $dataType !== 'uuid') { + $errors[] = "Column '$columnName' data type in table '$table' is not UUID but $dataType"; + } + } + } + + $this->assertSame([], $errors); + } + + /** + * Column names that should probably Be ASCII encoded + * + * @return string[] + */ + public function getSuspiciousAsciiColumnNames(): array + { + return [ + 'aco', + 'aco_foreign_key', + 'aro', + 'aro_foreign_key', + 'foreign_key', + 'foreign_model', + 'hash', + 'status', + 'token', + 'type', + 'created_by', + 'modified_by', + ]; + } + + /** + * Indicate if this id is most probably an uuid + * + * @param string $tableName table name + * @param string $columnName column name + * @return bool + */ + public function isUuid(string $tableName, string $columnName): bool + { + // Some columns do not follow the general pattern and are not UUIDs + $exceptions = ['email_queue.id', 'gpgkeys.key_id']; + + if (in_array("$tableName.$columnName", $exceptions)) { + return false; + } + + return $columnName === 'id' + || strpos($columnName, '_id') !== false + || strpos($columnName, 'foreign_key') !== false + || strpos($columnName, '_by') !== false; + } + + /** + * Column types that should most probably be ascii_general_ci + * + * @return string[] + */ + public function getSuspiciousColumnTypes(): array + { + return [ + 'char(1)', + 'char(36)', + ]; + } + + /** + * Columns that are OK, skip the check there. + * + * @return string[] + */ + public function getWhiteList(): array + { + return [ + 'gpgkeys.key_id', + 'gpgkeys.type', + ]; + } +} diff --git a/tests/TestCase/Model/Entity/AuthenticationTokenTest.php b/tests/TestCase/Model/Entity/AuthenticationTokenTest.php new file mode 100644 index 0000000000..61435dfb86 --- /dev/null +++ b/tests/TestCase/Model/Entity/AuthenticationTokenTest.php @@ -0,0 +1,106 @@ + 'Foo'], 'Foo',], + [[AuthenticationToken::SESSION_ID_KEY => ''], '',], + [[], null,], + ]; + } + + /** + * @dataProvider dataProviderForSessionId + * @param $data + * @param $expectedSessionId + */ + public function testAuthenticationToken_GetSessionId($data, $expectedSessionId) + { + $entity = AuthenticationTokenFactory::make() + ->data($data) + ->getEntity(); + + $this->assertEquals($expectedSessionId, $entity->getHashedSessionId()); + } + + /** + * @dataProvider dataProviderForSessionId + * @param $data + */ + public function testAuthenticationToken_hashAndSetSessionId($data) + { + $entity = AuthenticationTokenFactory::make() + ->data($data) + ->getEntity(); + + $newSession = 'Bar'; + $entity->hashAndSetSessionId($newSession); + + $this->assertTrue($entity->checkSessionId($newSession)); + } + + /** + * @see \App\Model\Entity\AuthenticationToken::checkSessionId() + */ + public function testAuthenticationToken_hashAndSetSessionId_LongId() + { + $accessToken1 = (new JwtTokenCreateService())->createToken(UuidFactory::uuid()); + $accessToken2 = (new JwtTokenCreateService())->createToken(UuidFactory::uuid()); + $entity = AuthenticationTokenFactory::make()->getEntity(); + $entity->hashAndSetSessionId($accessToken1); + $this->assertTextNotEquals($accessToken1, $accessToken2); + $this->assertTrue($entity->checkSessionId($accessToken1)); + $this->assertFalse($entity->checkSessionId($accessToken2)); + } + + public function testAuthenticationToken_getJsonDecodedData() + { + $data = ['foo' => 'bar']; + $entity = AuthenticationTokenFactory::make() + ->data($data) + ->getEntity(); + + $this->assertSame($data, $entity->getJsonDecodedData()); + + // Empty data + $entity = AuthenticationTokenFactory::make()->getEntity(); + $this->assertSame([], $entity->getJsonDecodedData()); + } + + public function testAuthenticationToken_getInvalidJsonData() + { + $entity = AuthenticationTokenFactory::make() + ->patchData(['data' => 'blah']) + ->getEntity(); + + $this->assertSame([], $entity->getJsonDecodedData()); + } +} diff --git a/tests/TestCase/Model/Table/AuthenticationTokens/FindExpiredByTypeTest.php b/tests/TestCase/Model/Table/AuthenticationTokens/FindExpiredByTypeTest.php new file mode 100644 index 0000000000..c1ac225b39 --- /dev/null +++ b/tests/TestCase/Model/Table/AuthenticationTokens/FindExpiredByTypeTest.php @@ -0,0 +1,63 @@ +exists('AuthenticationTokens') ? + [] : ['className' => AuthenticationTokensTable::class]; + $this->AuthenticationTokens = TableRegistry::getTableLocator()->get('AuthenticationTokens', $config); + } + + public function testAuthenticationTokensTableFindExpiredByTypeSuccess(): void + { + foreach (AuthenticationTokensTable::ALLOWED_TYPES as $type) { + AuthenticationTokenFactory::make()->type($type)->active()->expired()->persist(); + AuthenticationTokenFactory::make()->type($type)->inactive()->expired()->persist(); + AuthenticationTokenFactory::make()->type($type)->inactive()->persist(); + AuthenticationTokenFactory::make()->type($type)->active()->persist(); + } + + $found = $this->AuthenticationTokens + ->find('expiredByType', ['type' => AuthenticationToken::TYPE_RECOVER]) + ->count(); + $this->assertTrue($found === 2); + + $found = $this->AuthenticationTokens + ->find('expiredByType', ['type' => AuthenticationToken::TYPE_MFA]) + ->where(['active' => true]) + ->count(); + $this->assertTrue($found === 1); + } +} diff --git a/tests/TestCase/Model/Table/AuthenticationTokens/GetByTokenTest.php b/tests/TestCase/Model/Table/AuthenticationTokens/GetByTokenTest.php index 6946ef9ce3..3349838625 100644 --- a/tests/TestCase/Model/Table/AuthenticationTokens/GetByTokenTest.php +++ b/tests/TestCase/Model/Table/AuthenticationTokens/GetByTokenTest.php @@ -21,6 +21,7 @@ use App\Test\Lib\AppTestCase; use App\Test\Lib\Model\AuthenticationTokenModelTrait; use App\Utility\UuidFactory; +use Cake\Datasource\Exception\RecordNotFoundException; use Cake\ORM\TableRegistry; class GetByTokenTest extends AppTestCase @@ -57,8 +58,8 @@ public function testAuthenticationTokensGetByTokenInactive() { $userId = UuidFactory::uuid('user.id.ruth'); $tokenInactive = $this->quickDummyAuthToken($userId, AuthenticationToken::TYPE_REGISTER, 'inactive'); - $t = $this->AuthenticationTokens->getByToken($tokenInactive); - $this->assertEmpty($t); + $this->expectException(RecordNotFoundException::class); + $this->AuthenticationTokens->getByToken($tokenInactive); } /** diff --git a/tests/TestCase/Model/Table/AuthenticationTokens/IsExpiredTest.php b/tests/TestCase/Model/Table/AuthenticationTokens/IsExpiredTest.php new file mode 100644 index 0000000000..18ccfffcf4 --- /dev/null +++ b/tests/TestCase/Model/Table/AuthenticationTokens/IsExpiredTest.php @@ -0,0 +1,63 @@ +loadModel('AuthenticationTokens'); + } + + public function expiryData(): array + { + return [ + [null, false], + ['', false], + ['1 hour', true], + ['1 week', false], + ]; + } + + /** + * @dataProvider expiryData + */ + public function testAuthenticationTokensIsExpired($expiry, bool $isExpired) + { + $token = AuthenticationTokenFactory::make() + ->type(AuthenticationToken::TYPE_REGISTER) + ->created(Date::yesterday()) + ->getEntity(); + + $result = $this->AuthenticationTokens->isExpired($token, $expiry); + $this->assertSame($isExpired, $result); + } +} diff --git a/tests/TestCase/Model/Table/AuthenticationTokens/SetExpiredActiveTokenToInactiveTest.php b/tests/TestCase/Model/Table/AuthenticationTokens/SetExpiredActiveTokenToInactiveTest.php new file mode 100644 index 0000000000..efeacf8a78 --- /dev/null +++ b/tests/TestCase/Model/Table/AuthenticationTokens/SetExpiredActiveTokenToInactiveTest.php @@ -0,0 +1,85 @@ +exists('AuthenticationTokens') ? + [] : ['className' => AuthenticationTokensTable::class]; + $this->AuthenticationTokens = TableRegistry::getTableLocator()->get('AuthenticationTokens', $config); + } + + public function testAuthenticationTokensTableSetActiveExpiredTokenToInactive(): void + { + $type = AuthenticationToken::TYPE_RECOVER; + AuthenticationTokenFactory::make()->type($type)->active()->expired()->persist(); + AuthenticationTokenFactory::make()->type($type)->inactive()->expired()->persist(); + AuthenticationTokenFactory::make()->type($type)->inactive()->persist(); + AuthenticationTokenFactory::make()->type($type)->active()->persist(); + + $this->AuthenticationTokens->setActiveExpiredTokenToInactive($type); + + $found = $this->AuthenticationTokens->find()->where(['type' => $type])->toArray(); + $this->assertTrue(count($found) === 4); + + $found = Hash::extract($found, '{n}[active=false]'); + $this->assertTrue(count($found) === 3); + } + + public function testAuthenticationTokensTableSetAllActiveExpiredTokenToInactive(): void + { + foreach (AuthenticationTokensTable::ALLOWED_TYPES as $type) { + AuthenticationTokenFactory::make()->type($type)->active()->expired()->persist(); + AuthenticationTokenFactory::make()->type($type)->inactive()->expired()->persist(); + AuthenticationTokenFactory::make()->type($type)->inactive()->persist(); + AuthenticationTokenFactory::make()->type($type)->active()->persist(); + } + + $this->AuthenticationTokens->setAllActiveExpiredTokenToInactive(); + + $found = $this->AuthenticationTokens->find()->all()->toArray(); + $this->assertTrue(count($found) === count(AuthenticationTokensTable::ALLOWED_TYPES) * 4); + + $found = Hash::extract($found, '{n}[active=false]'); + $this->assertTrue(count($found) === count(AuthenticationTokensTable::ALLOWED_TYPES) * 3); + } + + public function testAuthenticationTokensTableSetAllActiveExpiredTokenToInactiveEmpty(): void + { + $this->AuthenticationTokens->setAllActiveExpiredTokenToInactive(); + $found = $this->AuthenticationTokens->find()->all()->toArray(); + $this->assertTrue(count($found) === 0); + } +} diff --git a/tests/TestCase/Model/Table/Avatars/AddContainAvatarTest.php b/tests/TestCase/Model/Table/Avatars/AddContainAvatarTest.php new file mode 100644 index 0000000000..d5447db41b --- /dev/null +++ b/tests/TestCase/Model/Table/Avatars/AddContainAvatarTest.php @@ -0,0 +1,72 @@ +loadModel('Users'); + } + + public function testAvatarsTableAddContainAvatar_Should_Not_Retrieve_Avatar_Data() + { + $user = UserFactory::make()->withAvatar()->user()->persist(); + + /** @var \App\Model\Entity\User $retrievedUser */ + $retrievedUser = $this->Users->find() + ->where(['Users.id' => $user->id]) + ->contain([ + 'Profiles' => AvatarsTable::addContainAvatar(), + ]) + ->firstOrFail(); + + $this->assertSame($user->profile->avatar->id, $retrievedUser->profile->avatar->id); + $this->assertNotNull($user->profile->avatar->data); + $this->assertNull($retrievedUser->profile->avatar->data ?? null); + } + + public function testAvatarsTableAddContainAvatar_On_Empty_Avatar() + { + $user = UserFactory::make()->user()->persist(); + + /** @var \App\Model\Entity\User $retrievedUser */ + $retrievedUser = $this->Users->find() + ->where(['Users.id' => $user->id]) + ->contain([ + 'Profiles' => AvatarsTable::addContainAvatar(), + ]) + ->firstOrFail(); + + $this->assertNull($user->profile->avatar->id ?? null); + $this->assertNull($user->profile->avatar->data ?? null); + $this->assertNull($retrievedUser->profile->avatar->id ?? null); + $this->assertNull($retrievedUser->profile->avatar->data ?? null); + $this->assertIsArray($retrievedUser->profile->avatar->url); + } +} diff --git a/tests/TestCase/Model/Table/Avatars/AvatarsCleanupTest.php b/tests/TestCase/Model/Table/Avatars/AvatarsCleanupTest.php new file mode 100644 index 0000000000..e28bdc5d58 --- /dev/null +++ b/tests/TestCase/Model/Table/Avatars/AvatarsCleanupTest.php @@ -0,0 +1,103 @@ +exists('Avatars') ? [] : ['className' => AvatarsTable::class]; + $this->Avatars = TableRegistry::getTableLocator()->get('Avatars', $config); + $this->loadRoutes(); + } + + /** + * tearDown method + * + * @return void + */ + public function tearDown(): void + { + unset($this->Avatars); + + parent::tearDown(); + } + + public function hardDelete(): array + { + return [[false], [true]]; + } + + /** + * @dataProvider hardDelete + */ + public function testAvatarsCleanupDeletedUsers(bool $hardDelete) + { + // Create avatar with non deleted user + UserFactory::make(1)->with('Profiles.Avatars')->persist(); + // Create avatar with deleted user - Soft + UserFactory::make(2)->with('Profiles.Avatars')->deleted()->persist(); + // Create avatar with no user - Hard + AvatarFactory::make(3)->withProfile()->persist(); + + if ($hardDelete) { + $output = $this->Avatars->cleanupHardDeletedUsers(); + $expectedOutput = 3; + } else { + $output = $this->Avatars->cleanupSoftDeletedUsers(); + $expectedOutput = 2; + } + $this->assertSame($expectedOutput, $output); + $this->assertSame(6 - $expectedOutput, $this->Avatars->find()->count()); + } + + public function testAvatarsCleanupDeletedFavorites() + { + // Create avatar with profile + AvatarFactory::make(1)->withProfile()->persist(); + // Create avatar with no profile + AvatarFactory::make(2)->patchData(['profile_id' => UuidFactory::uuid('foo')])->persist(); + + $output = $this->Avatars->cleanupHardDeletedProfiles(); + $this->assertSame(2, $output); + $this->assertSame(1, $this->Avatars->find()->count()); + } +} diff --git a/tests/TestCase/Model/Table/Avatars/CreateTest.php b/tests/TestCase/Model/Table/Avatars/AvatarsCreateTest.php similarity index 84% rename from tests/TestCase/Model/Table/Avatars/CreateTest.php rename to tests/TestCase/Model/Table/Avatars/AvatarsCreateTest.php index c88d7de692..8a3aed96de 100644 --- a/tests/TestCase/Model/Table/Avatars/CreateTest.php +++ b/tests/TestCase/Model/Table/Avatars/AvatarsCreateTest.php @@ -18,13 +18,12 @@ namespace App\Test\TestCase\Model\Table\Avatars; use App\Test\Lib\AppTestCase; -use App\Test\Lib\Model\AvatarsModelTestTrait; +use App\Test\Lib\Model\AvatarsModelTrait; use Cake\ORM\TableRegistry; -use League\Flysystem\Local\LocalFilesystemAdapter; -class CreateTest extends AppTestCase +class AvatarsCreateTest extends AppTestCase { - use AvatarsModelTestTrait; + use AvatarsModelTrait; /** * @var \App\Model\Table\AvatarsTable @@ -36,7 +35,7 @@ public function setUp(): void parent::setUp(); TableRegistry::getTableLocator()->clear(); $this->Avatars = TableRegistry::getTableLocator()->get('Avatars'); - $this->Avatars->setFilesystem(new LocalFilesystemAdapter(TMP . 'tests' . DS . 'avatars')); + $this->setTestLocalFilesystemAdapter(); } public function tearDown(): void @@ -49,8 +48,14 @@ public function tearDown(): void public function dataProviderForCreateAvatarFile() { return [ - [false,], - [true,], + [false, 'png'], + [true, 'png'], + [false, 'jpg'], + [true, 'jpg'], + [false, 'jpeg'], + [true, 'jpeg'], + [false, 'gif'], + [true, 'gif'], ]; } diff --git a/tests/TestCase/Model/Table/Avatars/ValidationTest.php b/tests/TestCase/Model/Table/Avatars/AvatarsValidationTest.php similarity index 76% rename from tests/TestCase/Model/Table/Avatars/ValidationTest.php rename to tests/TestCase/Model/Table/Avatars/AvatarsValidationTest.php index 2d746556b5..552f9e1f21 100644 --- a/tests/TestCase/Model/Table/Avatars/ValidationTest.php +++ b/tests/TestCase/Model/Table/Avatars/AvatarsValidationTest.php @@ -18,8 +18,8 @@ namespace App\Test\TestCase\Model\Table\Avatars; use App\Model\Table\AvatarsTable; +use App\Test\Lib\Model\AvatarsModelTrait; use App\Utility\UuidFactory; -use App\Test\Lib\Model\AvatarsModelTestTrait; use Cake\ORM\Exception\PersistenceFailedException; use Cake\ORM\TableRegistry; use Cake\TestSuite\TestCase; @@ -28,9 +28,9 @@ /** * App\Model\Table\FileStorageTable Test Case */ -class ValidationTest extends TestCase +class AvatarsValidationTest extends TestCase { - use AvatarsModelTestTrait; + use AvatarsModelTrait; /** * Test subject @@ -73,26 +73,33 @@ public function testInitialize() $this->assertSame('avatars', $this->Avatars->getTable()); } + public function dataProviderForValidationDefaultSuccess(): array + { + return [ + ['png'], + ['jpg'], + ['jpeg'], + ['gif'], + ]; + } + /** * Test validationDefault method * + * @dataProvider dataProviderForValidationDefaultSuccess * @return void */ - public function testValidationDefaultSuccess() + public function testValidationDefaultSuccess(string $extension) { - $adaAvatar = FIXTURES . 'Avatar' . DS . 'ada.png'; - $file = new UploadedFile( - $adaAvatar, + FIXTURES . 'Avatar' . DS . 'ada.' . $extension, 170049, \UPLOAD_ERR_OK, - 'ada.png', - 'image/png' + 'ada.' . $extension, + 'image/' . $extension ); - $data = compact('file'); - - $file = $this->Avatars->newEntity($data); + $file = $this->Avatars->newEntity(compact('file')); $this->assertEmpty($file->getErrors()); } @@ -103,21 +110,17 @@ public function testValidationDefaultSuccess() */ public function testValidationDefaultError() { - $adaAvatar = FIXTURES . 'Avatar' . DS . 'ada.png'; - - for ($i = 1; $i <= 8; $i++) { + for ($errorStatus = 1; $errorStatus <= 8; $errorStatus++) { $file = new UploadedFile( - $adaAvatar, + FIXTURES . 'Avatar' . DS . 'ada.png', 170049, - $i, + $errorStatus, 'ada.png', 'image/png' ); } - $data = compact('file'); - - $file = $this->Avatars->newEntity($data); + $file = $this->Avatars->newEntity(compact('file')); $this->assertNotEmpty($file->getErrors()); } diff --git a/tests/TestCase/Model/Table/Permissions/FindViewAcoPermissionsTest.php b/tests/TestCase/Model/Table/Permissions/FindViewAcoPermissionsTest.php index 1767f0d8ed..f77a3ce571 100644 --- a/tests/TestCase/Model/Table/Permissions/FindViewAcoPermissionsTest.php +++ b/tests/TestCase/Model/Table/Permissions/FindViewAcoPermissionsTest.php @@ -73,7 +73,20 @@ public function testContainUserProfile() public function testContainUserProfileAvatar() { - $this->markTestIncomplete(); + $resourceId = UuidFactory::uuid('resource.id.debian'); + $options['contain']['user.profile'] = true; + $permissions = $this->Permissions->findViewAcoPermissions($resourceId, $options)->all(); + + // retrieve a direct user permission + $permission = Hash::extract($permissions->toArray(), '{n}[aro=User]')[0]; + $this->assertPermissionAttributes($permission); + $this->assertProfileAttributes($permission->user->profile); + $this->assertObjectHasAttribute('avatar', $permission->user->profile); + $this->assertArrayHasKey('url', $permission->user->profile->avatar); + $this->assertArrayHasKey('medium', $permission->user->profile->avatar->url); + $this->assertArrayHasKey('small', $permission->user->profile->avatar->url); + $this->assertStringContainsString('/img/avatar/user_medium.png', $permission->user->profile->avatar->url['medium']); + $this->assertStringContainsString('/img/avatar/user.png', $permission->user->profile->avatar->url['small']); } public function testPermissions() diff --git a/tests/TestCase/Model/Table/Resources/FindIndexTest.php b/tests/TestCase/Model/Table/Resources/FindIndexTest.php index 2da1ef2386..6ee515504f 100644 --- a/tests/TestCase/Model/Table/Resources/FindIndexTest.php +++ b/tests/TestCase/Model/Table/Resources/FindIndexTest.php @@ -66,7 +66,16 @@ public function testSuccess() public function testExcludeSoftDeletedResources() { - $this->markTestIncomplete('Not implemented yet.'); + $user = UserFactory::make()->persist(); + $factory = ResourceFactory::make()->withCreatorAndPermission($user); + + $notDeletedResource = $factory->persist(); + $factory->setField('deleted', true)->persist(); + + $resources = $this->Resources->findIndex($user->id); + + $this->assertSame(1, $resources->count()); + $this->assertSame($notDeletedResource->id, $resources->firstOrFail()->id); } public function testContainSecrets() @@ -297,7 +306,6 @@ public function testPermissions() public function testErrorInvalidUserIdParameter() { - $this->loadFixtures(); try { $this->Resources->findIndex('not-valid'); } catch (\InvalidArgumentException $e) { diff --git a/tests/TestCase/Model/Table/Resources/SoftDeleteAllTest.php b/tests/TestCase/Model/Table/Resources/SoftDeleteAllTest.php index c59720329c..0218608a6e 100644 --- a/tests/TestCase/Model/Table/Resources/SoftDeleteAllTest.php +++ b/tests/TestCase/Model/Table/Resources/SoftDeleteAllTest.php @@ -21,7 +21,6 @@ use App\Test\Lib\AppTestCase; use App\Test\Lib\Model\FormatValidationTrait; use Cake\ORM\TableRegistry; -use Migrations\Migrations; class SoftDeleteAllTest extends AppTestCase { @@ -139,52 +138,4 @@ public function testSoftDeleteAllSuccessWithAssociation(bool $cascade) $this->assertSame($expect, $count[$association]); } } - - /** - * @see \V300DeleteMetadataOfSoftDeletedResources::up() - */ - public function testSoftDeleteCleanupWithMigration() - { - $this->markTestSkipped('Migrations cannot be run in the test environment for the moment'); - - // Fetch the non deleted resources with populated fields - $resourcesId = $this->Resources - ->find('list', ['valueField' => 'id']) - ->where([ - 'username IS NOT NULL', - 'uri IS NOT NULL', - 'description IS NOT NULL', - 'deleted IS FALSE', - ]); - - $associations = ['Favorites', 'Secrets', 'Permissions']; - - // Count associated entities - $count = []; - foreach ($associations as $association) { - $count[$association] = $this->Resources->{$association}->find()->count(); - $this->assertTrue($count[$association] > 0, "No $association were found"); - } - - // Soft delete the resources found - $this->Resources->updateAll(['deleted' => true], ['id IN' => $resourcesId]); - - // Mark as non migrated - $this->Resources->getConnection()->execute('DELETE FROM phinxlog WHERE version = 20201221093528'); - - (new Migrations())->migrate(); - - // Fetch soft deleted resources - $deletedResources = $this->Resources - ->find() - ->where([ - 'username IS NULL', - 'uri IS NULL', - 'description IS NULL', - 'deleted IS TRUE', - ]); - - $resources = $this->Resources->find(); - $this->assertSame($resources->count(), $deletedResources->count()); - } } diff --git a/tests/TestCase/Notification/Email/Redactor/AdminUserSetupEmailRedactorTest.php b/tests/TestCase/Notification/Email/Redactor/AdminUserSetupEmailRedactorTest.php index a63cbeedd0..c8da10bbed 100644 --- a/tests/TestCase/Notification/Email/Redactor/AdminUserSetupEmailRedactorTest.php +++ b/tests/TestCase/Notification/Email/Redactor/AdminUserSetupEmailRedactorTest.php @@ -18,40 +18,17 @@ namespace App\Test\TestCase\Notification\Email\Redactor; use App\Controller\Setup\SetupCompleteController; -use App\Model\Entity\Profile; -use App\Model\Entity\User; -use App\Model\Table\UsersTable; -use App\Notification\Email\Email; use App\Notification\Email\Redactor\AdminUserSetupCompleteEmailRedactor; -use App\Utility\UuidFactory; use Cake\Core\Configure; -use Cake\Event\Event; -use Cake\I18n\FrozenTime; use Cake\TestSuite\TestCase; -use Passbolt\Log\Model\Entity\ActionLog; -use Passbolt\Log\Model\Entity\EntityHistory; class AdminUserSetupEmailRedactorTest extends TestCase { - /** - * @var AdminUserSetupCompleteEmailRedactor - */ - private $sut; - - /** - * @var MockObject|UsersTable - */ - private $usersTableMock; - public function setUp(): void { - $this->usersTableMock = $this->createMock(UsersTable::class); + parent::setUp(); Configure::write('passbolt.plugins.log.enabled', true); - - $this->sut = new AdminUserSetupCompleteEmailRedactor($this->usersTableMock); - - parent::setUp(); } public function testThatRedactorIsSubscribedToEvents() @@ -60,134 +37,7 @@ public function testThatRedactorIsSubscribedToEvents() [ SetupCompleteController::COMPLETE_SUCCESS_EVENT_NAME, ], - $this->sut->getSubscribedEvents() - ); - } - - public function createUserMock(string $username, string $firstName, string $lastName) - { - $user = new User(); - $user->id = UuidFactory::uuid(); - $user->username = $username; - - $profile = new Profile(); - $profile->first_name = $firstName; - $profile->last_name = $lastName; - - $user->profile = $profile; - - return $user; - } - - public function testThatEmailsAreCreatedForAllAdmins() - { - $expectedInvitedWhen = new FrozenTime(); - $expectedInvitedBy = $this->createUserMock('carol@passbolt.com', 'Carol', 'Noway'); - $expectedAdmins = [$this->createUserMock('ada@passbolt.com', 'Ada', 'Lovelace')]; - - $userCompletedSetup = $this->createUserMock('betty@passbolt.com', 'Betty', 'What'); - - $userCompletedSetup->entities_history = [ - $this->createEntityHistoryForUser($expectedInvitedBy, $expectedInvitedWhen), - ]; - - $this->assertThatUsersTableReturnSpecifiedAdminList($expectedAdmins); - $this->assertThatUsersTableReturnUserWhoCompletedSetupWithEntityHIstory($userCompletedSetup); - - $result = $this->sut->onSubscribedEvent( - new Event('some_event', null, [ - 'user' => $userCompletedSetup, - ]) - ); - - $emailRecipients = []; - /** @var Email $email */ - foreach ($result->getEmails() as $email) { - $emailRecipients[] = $email->getRecipient(); - } - - $this->assertThatAllAdminHaveAnEmailCreated($emailRecipients, $expectedAdmins); - - $expectedSubject = sprintf('%s just activated their account on passbolt', $userCompletedSetup->profile->first_name); - - $email = $result->getEmails()[0]; - $this->assertEmailSubject($expectedSubject, $email, $userCompletedSetup, $expectedAdmins[0]); - $this->assertEmailData( - $email, - $userCompletedSetup, - $expectedAdmins[0], - $expectedSubject, - $expectedInvitedBy, - $expectedInvitedWhen + (new AdminUserSetupCompleteEmailRedactor())->getSubscribedEvents() ); } - - public function assertEmailData( - Email $email, - User $userCompletedSetup, - User $admin, - string $expectedSubject, - User $invitedBy, - FrozenTime $expectedInvitedWhen - ) { - $emailData = $email->getData(); - - $expectedEmailData = [ - 'title' => $expectedSubject, - 'body' => [ - 'user' => $userCompletedSetup, - 'admin' => $admin, - 'invitedBy' => $invitedBy, - 'invitedWhen' => $expectedInvitedWhen->timeAgoInWords(['accuracy' => 'day']), - 'invitedByYou' => $invitedBy->id === $admin->id, - ], - ]; - - $this->assertSame($expectedEmailData, $emailData); - } - - public function assertEmailSubject(string $expectedSubject, Email $email, User $userCompletedSetup, User $admin) - { - $this->assertSame( - $expectedSubject, - $email->getSubject() - ); - } - - private function assertThatAllAdminHaveAnEmailCreated(array $emailRecipients, array $admins) - { - foreach ($admins as $admin) { - $this->assertContains( - $admin->username, - $emailRecipients, - sprintf('`%s` is an admin and should have an email created', $admin->username) - ); - } - } - - private function createEntityHistoryForUser(User $invitedBy, FrozenTime $invitedWhen) - { - $actionLog = new ActionLog(); - $actionLog->created = $invitedWhen; - $actionLog->user = $invitedBy; - - $entityHistory = new EntityHistory(); - $entityHistory->action_log = $actionLog; - - return $entityHistory; - } - - private function assertThatUsersTableReturnSpecifiedAdminList(array $admins) - { - $this->usersTableMock->expects($this->once()) - ->method('findAdmins') - ->willReturn($admins); - } - - private function assertThatUsersTableReturnUserWhoCompletedSetupWithEntityHIstory(User $userWhoCompletedSetup) - { - $this->usersTableMock->expects($this->once()) - ->method('loadInto') - ->willReturn($userWhoCompletedSetup); - } } diff --git a/tests/TestCase/Notification/Email/Redactor/Recovery/AccountRecoveryEmailRedactorTest.php b/tests/TestCase/Notification/Email/Redactor/Recovery/AccountRecoveryEmailRedactorTest.php new file mode 100644 index 0000000000..6841b29695 --- /dev/null +++ b/tests/TestCase/Notification/Email/Redactor/Recovery/AccountRecoveryEmailRedactorTest.php @@ -0,0 +1,72 @@ +getJson('/auth/is-authenticated.json'); + + $user = UserFactory::make()->withAvatar()->user()->persist(); + + /** @var UsersTable $Users */ + $Users = TableRegistry::getTableLocator()->get('Users'); + /** @var User $user */ + $user = $Users->findRecover($user->username)->first(); + $token = AuthenticationTokenFactory::make()->persist(); + $event = new Event(UsersRecoverController::RECOVER_SUCCESS_EVENT_NAME, null, compact('user', 'token')); + EventManager::instance()->dispatch($event); + + $this->assertSame(1, EmailQueueFactory::count()); + $this->assertEmailIsInQueue([ + 'email' => $user->username, + 'subject' => "Your account recovery, {$user->profile->first_name}!", + 'template' => 'AN/user_recover', + ]); + $emailVars = EmailQueueFactory::find()->firstOrFail()->get('template_vars'); + $this->assertSame($user->username, $emailVars['body']['user']['username']); + $this->assertSame($user->profile->first_name, $emailVars['body']['user']['profile']['first_name']); + $this->assertSame($token->token, $emailVars['body']['token']['token']); + } +} diff --git a/tests/TestCase/Service/Avatars/AvatarsCacheServiceTest.php b/tests/TestCase/Service/Avatars/AvatarsCacheServiceTest.php index 24db528df3..e01a193eac 100644 --- a/tests/TestCase/Service/Avatars/AvatarsCacheServiceTest.php +++ b/tests/TestCase/Service/Avatars/AvatarsCacheServiceTest.php @@ -66,15 +66,16 @@ public function dataForTestAvatarsCacheServiceStore(): array return [ [file_get_contents(FIXTURES . 'Avatar' . DS . 'ada.png')], [(new Stream(FIXTURES . 'Avatar' . DS . 'ada.png'))->getContents()], + [(new Stream(FIXTURES . 'Avatar' . DS . 'ada.png'))], ]; } public function dataForTestAvatarsCacheServiceStoreFail(): array { return [ + [null], ['1234'], [FIXTURES . 'Avatar' . DS . 'ada.png'], - [(new Stream(FIXTURES . 'Avatar' . DS . 'ada.png'))], ]; } @@ -83,19 +84,28 @@ public function dataForTestAvatarsCacheServiceStoreFail(): array */ public function testAvatarsCacheServiceStore($data) { - $id = UuidFactory::uuid('foo'); + $id = UuidFactory::uuid(); $avatar = new Avatar(compact('id', 'data')); + $mediumFileName = $this->cachedFileLocation . $id . DS . 'medium.jpg'; + $smallFileName = $this->cachedFileLocation . $id . DS . 'small.jpg'; $this->avatarsCacheService->storeInCache($avatar); - $this->assertFileExists($this->cachedFileLocation . $id . DS . 'medium.jpg'); - $this->assertFileExists($this->cachedFileLocation . $id . DS . 'small.jpg'); + $this->assertFileExists($mediumFileName); + $this->assertFileExists($smallFileName); // Perform the action twice to ensure that no overwriting issues occur $this->avatarsCacheService->storeInCache($avatar); - $this->assertFileExists($this->cachedFileLocation . $id . DS . 'medium.jpg'); - $this->assertFileExists($this->cachedFileLocation . $id . DS . 'small.jpg'); + $this->assertFileExists($mediumFileName); + $this->assertFileExists($smallFileName); + + // Ensure that both files are not executable + $getRights = function (string $filepath) { + return substr(decoct(fileperms($filepath)), -4); + }; + $this->assertSame('0644', $getRights($mediumFileName)); + $this->assertSame('0644', $getRights($smallFileName)); $this->assertSame( file_get_contents(FIXTURES . 'Avatar' . DS . 'ada.png'), @@ -108,7 +118,7 @@ public function testAvatarsCacheServiceStore($data) */ public function testAvatarsCacheServiceStoreFail($data) { - $id = UuidFactory::uuid('foo'); + $id = UuidFactory::uuid(); $avatar = new Avatar(compact('id', 'data')); $this->avatarsCacheService->storeInCache($avatar); @@ -116,4 +126,24 @@ public function testAvatarsCacheServiceStoreFail($data) $this->assertFileDoesNotExist($this->cachedFileLocation . $id . DS . 'medium.jpg'); $this->assertFileDoesNotExist($this->cachedFileLocation . $id . DS . 'small.jpg'); } + + public function testAvatarsCacheServiceStore_Fail_After_File_Deleted() + { + $data = file_get_contents(FIXTURES . 'Avatar' . DS . 'ada.png'); + $id = UuidFactory::uuid(); + $avatar = new Avatar(compact('id', 'data')); + + $this->avatarsCacheService->storeInCache($avatar); + + $this->assertFileExists($this->cachedFileLocation . $id . DS . 'medium.jpg'); + $this->assertFileExists($this->cachedFileLocation . $id . DS . 'small.jpg'); + + unlink($this->cachedFileLocation . $id . DS . 'medium.jpg'); + unlink($this->cachedFileLocation . $id . DS . 'small.jpg'); + + $this->avatarsCacheService->storeInCache($avatar); + + $this->assertFileExists($this->cachedFileLocation . $id . DS . 'medium.jpg'); + $this->assertFileExists($this->cachedFileLocation . $id . DS . 'small.jpg'); + } } diff --git a/tests/TestCase/Service/Resources/ResourcesAddServiceStressTest.php b/tests/TestCase/Service/Resources/ResourcesAddServiceStressTest.php new file mode 100644 index 0000000000..0b665f0d9c --- /dev/null +++ b/tests/TestCase/Service/Resources/ResourcesAddServiceStressTest.php @@ -0,0 +1,105 @@ +get('Users')->find()->contain('Roles')->first(); + } + + public function setUp(): void + { + $this->skipIf(self::$NIterations < 1); + parent::setUp(); + } + + /** + * Run this test once to clean the DB and create the required default resource type. + * Deadlocks are more likely to happen in a DB of reduced size. + */ + public function testTruncateDirtyTables() + { + SnifferRegistry::get('test')->truncateDirtyTables(); + $this->assertTrue(true); + ResourceTypeFactory::make()->default()->persist(); + UserFactory::make()->user()->persist(); + } + + public function dataForStressTest(): array + { + $iter = []; + foreach (range(1, self::$NIterations) as $i) { + $iter[] = [$i]; + } + + return $iter; + } + + /** + * Run this test in 4 or more different consoles to + * parallelize the creation of resources. + * Check the logs. + * + * @dataProvider dataForStressTest + */ + public function testResourceAddServiceSuccessStressTest(int $iter) + { + $data = $this->getDummyResourcesPostData(); + $service = new ResourcesAddService(); + + $resource = $service->add(self::$user->id, $data); + + $this->assertInstanceOf(Resource::class, $resource); + $this->assertInstanceOf(Secret::class, $resource->secrets[0]); + $this->assertFalse($resource->hasErrors()); + $this->assertFalse($resource->secrets[0]->hasErrors()); + } +} diff --git a/tests/TestCase/Service/Resources/ResourcesAddServiceTest.php b/tests/TestCase/Service/Resources/ResourcesAddServiceTest.php new file mode 100644 index 0000000000..01786b280c --- /dev/null +++ b/tests/TestCase/Service/Resources/ResourcesAddServiceTest.php @@ -0,0 +1,167 @@ +Resources = TableRegistry::getTableLocator()->get('Resources'); + $this->Secrets = TableRegistry::getTableLocator()->get('Secrets'); + ResourceTypeFactory::make()->default()->persist(); + } + + public function dataForTestResourceAddSuccess(): array + { + return [ + ['chinese' => $this->getDummyResourcesPostData([ + 'name' => '新的專用資源名稱', + 'username' => 'username@domain.com', + 'uri' => 'https://www.域.com', + 'description' => '新的資源描述', + ])], + ['slavic' => $this->getDummyResourcesPostData([ + 'name' => 'Новое имя частного ресурса', + 'username' => 'username@domain.com', + 'uri' => 'https://www.домен.com', + 'description' => 'Новое описание частного ресурса', + ])], + ['french' => $this->getDummyResourcesPostData([ + 'name' => 'Nouveau nom de resource privée', + 'username' => 'username@domain.com', + 'uri' => 'https://www.mon-domain.com', + 'description' => 'Nouvelle description de resource privée', + ])], + ['emoticon' => $this->getDummyResourcesPostData([ + 'name' => "\u{1F61C}\u{1F61C}\u{1F61C}\u{1F61C}\u{1F61C}\u{1F61C}\u{1F61C}", + 'username' => 'username@domain.com', + 'uri' => 'https://www.domain.com', + 'description' => "\u{1F61C}\u{1F61C}\u{1F61C}\u{1F61C}\u{1F61C}\u{1F61C}\u{1F61C}", + ])], + ]; + } + + /** + * @dataProvider dataForTestResourceAddSuccess + * @param array $data Data passed + */ + public function testResourceAddServiceSuccess(array $data) + { + $user = UserFactory::make()->user()->persist(); + $service = new ResourcesAddService(); + + $resource = $service->add($user->id, $data); + + $this->assertInstanceOf(Resource::class, $resource); + $this->assertInstanceOf(Secret::class, $resource->secrets[0]); + + $this->assertSame(1, $this->Resources->find()->count()); + $this->assertSame(1, $this->Secrets->find()->count()); + } + + public function testResourceAddServiceNoIdUser() + { + $this->expectException(ValidationException::class); + // This UAC does not have an id + (new ResourcesAddService())->add('', []); + } + + public function testResourceAddServiceNoUser() + { + $user = UserFactory::make(['id' => UuidFactory::uuid()])->user()->getEntity(); + $this->expectException(ValidationException::class); + // This UAC is not persisted + (new ResourcesAddService())->add($user->id, []); + } + + public function testResourceAddServiceInvalidResourceType() + { + $data = $this->getDummyResourcesPostData(); + $data['resource_type_id'] = 'invalid'; + $user = UserFactory::make()->user()->persist(); + $service = new ResourcesAddService(); + + $this->expectException(ValidationException::class); + $service->add($user->id, $data); + + $this->assertSame(0, $this->Resources->find()->count()); + $this->assertSame(0, $this->Secrets->find()->count()); + } + + public function testResourceAddServiceTooManySecrets() + { + $data = $this->getDummyResourcesPostData(['secrets' => [ + 0 => ['data' => $this->getDummyGpgMessage()], + 1 => ['user_id' => UuidFactory::uuid(), 'data' => $this->getDummyGpgMessage()], + ]]); + $user = UserFactory::make()->user()->persist(); + $service = new ResourcesAddService(); + + $this->expectException(ValidationException::class); + $service->add($user->id, $data); + + $this->assertSame(0, $this->Resources->find()->count()); + $this->assertSame(0, $this->Secrets->find()->count()); + } + + public function testResourceAddServiceSoftDeletedUser() + { + $data = $this->getDummyResourcesPostData(); + $user = UserFactory::make()->user()->patchData(['deleted' => true])->persist(); + $service = new ResourcesAddService(); + + $this->expectException(ValidationException::class); + $resource = $service->add($user->id, $data); + + $this->assertInstanceOf(Resource::class, $resource); + $this->assertInstanceOf(Secret::class, $resource->secrets[0]); + + $this->assertSame(0, $this->Resources->find()->count()); + $this->assertSame(0, $this->Secrets->find()->count()); + } +} diff --git a/tests/TestCase/Utility/Application/FeaturePluginAwareTraitTest.php b/tests/TestCase/Utility/Application/FeaturePluginAwareTraitTest.php new file mode 100644 index 0000000000..767289739a --- /dev/null +++ b/tests/TestCase/Utility/Application/FeaturePluginAwareTraitTest.php @@ -0,0 +1,147 @@ +app = $this->createApp(); + $this->clearPlugins(); + } + + public function tearDown(): void + { + $this->app->disableFeaturePlugin('mobile'); + unset($this->app); + parent::tearDown(); + } + + public function testFeaturePluginAwareTraitAddFeaturePlugin_On_Enabled_Plugin() + { + $this->app->enableFeaturePlugin('mobile'); + + $this->app->addFeaturePluginIfEnabled($this->app, 'Mobile'); + $this->assertTrue($this->app->getPlugins()->has('Passbolt/Mobile')); + + $plugin = $this->app->getPlugins()->get('Passbolt/Mobile'); + $this->assertSame('Passbolt/Mobile', $this->app->getPlugins()->get('Passbolt/Mobile')->getName()); + $this->assertTrue($plugin->isEnabled('bootstrap')); + $this->assertTrue($plugin->isEnabled('routes')); + } + + public function testFeaturePluginAwareTraitAddFeaturePlugin_On_Enabled_Plugin_With_Config() + { + $this->app->enableFeaturePlugin('mobile'); + + $this->app->addFeaturePluginIfEnabled($this->app, 'Mobile', ['routes' => false, 'bootstrap' => false]); + $this->assertTrue($this->app->getPlugins()->has('Passbolt/Mobile')); + + $plugin = $this->app->getPlugins()->get('Passbolt/Mobile'); + $this->assertSame('Passbolt/Mobile', $this->app->getPlugins()->get('Passbolt/Mobile')->getName()); + $this->assertFalse($plugin->isEnabled('bootstrap')); + $this->assertFalse($plugin->isEnabled('routes')); + } + + public function dataFortestFeaturePluginAwareTraitAddFeaturePlugin_On_Disabled_Plugin() + { + return [ + [], + [false], + [function () { + + return false; + }], + ]; + } + + /** + * @dataProvider dataFortestFeaturePluginAwareTraitAddFeaturePlugin_On_Disabled_Plugin + * @param callable|bool $isEnabledByDefault + */ + public function testFeaturePluginAwareTraitAddFeaturePlugin_On_Disabled_Plugin($isEnabledByDefault = null) + { + // If not defined, the feature flag is considered as false by default. + Configure::delete('passbolt.plugins.mobile.enabled'); + $this->assertFalse($this->app->isFeaturePluginEnabled('mobile')); + + if ($isEnabledByDefault === null) { + $this->app->addFeaturePluginIfEnabled($this->app, 'mobile'); + } else { + $this->app->addFeaturePluginIfEnabled($this->app, 'mobile', [], $isEnabledByDefault); + } + $this->assertFalse($this->app->getPlugins()->has('Passbolt/Mobile')); + } + + public function dataFortestFeaturePluginAwareTraitAddFeaturePlugin_On_Enabled_Plugin_By_Default() + { + return [ + [true], + [function () { + + return true; + }], + ]; + } + + /** + * @dataProvider dataFortestFeaturePluginAwareTraitAddFeaturePlugin_On_Enabled_Plugin_By_Default + * @param callable|bool $isEnabledByDefault + */ + public function testFeaturePluginAwareTraitAddFeaturePlugin_On_Enabled_Plugin_By_Default($isEnabledByDefault) + { + // If not defined, the feature flag is considered as false by default. + Configure::delete('passbolt.plugins.mobile.enabled'); + $this->assertFalse($this->app->isFeaturePluginEnabled('mobile')); + + $this->app->addFeaturePluginIfEnabled($this->app, 'mobile', [], $isEnabledByDefault); + $this->assertTrue($this->app->getPlugins()->has('Passbolt/Mobile')); + $this->assertSame('Passbolt/Mobile', $this->app->getPlugins()->get('Passbolt/Mobile')->getName()); + $plugin = $this->app->getPlugins()->get('Passbolt/Mobile'); + $this->assertTrue($plugin->isEnabled('bootstrap')); + $this->assertTrue($plugin->isEnabled('routes')); + } + + public function testFeaturePluginAwareTraitAddFeaturePlugin_On_Non_Existing_Plugin() + { + $this->expectNotToPerformAssertions(); + $this->app->addFeaturePluginIfEnabled($this->app, 'foo'); + } + + public function testFeaturePluginAwareTraitAddFeaturePlugin_On_Callable_Not_Returning_A_Boolean() + { + // Callable not returning a boolean makes the code crash. + $callable = function () { + return 'Foo'; + }; + + $this->expectException(\TypeError::class); + $this->app->addFeaturePluginIfEnabled($this->app, 'Bar', [], $callable); + } +} diff --git a/tests/TestCase/Utility/AvatarProcessingTest.php b/tests/TestCase/Utility/AvatarProcessingTest.php index 44ecc416f5..c1eb24dced 100644 --- a/tests/TestCase/Utility/AvatarProcessingTest.php +++ b/tests/TestCase/Utility/AvatarProcessingTest.php @@ -42,6 +42,8 @@ public function imagesToProcess() { return [ ['ada.png'], + ['ada.jpg'], + ['ada.gif'], ['50_80.png'], ['50_60.png'], ['100_50.gif'], diff --git a/tests/TestCase/Utility/Filesystem/DirectoryUtilityTest.php b/tests/TestCase/Utility/Filesystem/DirectoryUtilityTest.php index 360ffbeb62..29dc767145 100644 --- a/tests/TestCase/Utility/Filesystem/DirectoryUtilityTest.php +++ b/tests/TestCase/Utility/Filesystem/DirectoryUtilityTest.php @@ -16,15 +16,15 @@ */ namespace App\Test\TestCase\Utility\Filesystem; -use App\Test\Lib\AppIntegrationTestCase; use App\Utility\Filesystem\DirectoryUtility; +use Cake\TestSuite\TestCase; -class DirectoryUtilityTest extends AppIntegrationTestCase +class DirectoryUtilityTest extends TestCase { /** * @see DirectoryUtility::removeRecursively() */ - public function testRemoveRecursively() + public function testDirectoryUtilityRemoveRecursively() { $folderToDelete = TMP . 'test_folder'; $subFolder = $folderToDelete . DS . 'sub_folder'; @@ -45,4 +45,41 @@ public function testRemoveRecursively() // The parent folder should have disappeared $this->assertSame(false, is_dir($folderToDelete)); } + + public function testDirectoryUtilityIsFileExecutable_OnNonExistingFile() + { + $this->expectException(\RuntimeException::class); + $file = 'Foo'; + DirectoryUtility::isExecutable($file); + } + + public function testDirectoryUtilityIsFileExecutable_OnNonExecutableFile() + { + $file = TMP . 'tests' . DS . 'directory.test'; + file_put_contents($file, 'foo'); + + $perms = [0666, 0662, 0422, 626, 0242]; + foreach ($perms as $perm) { + chmod($file, $perm); + $res = DirectoryUtility::isExecutable($file); + $this->assertFalse($res); + } + + unlink($file); + } + + public function testDirectoryUtilityIsFileExecutable_OnExecutableFile() + { + $file = TMP . 'tests' . DS . 'directory.test'; + file_put_contents($file, 'foo'); + + $perms = [0755, 0535, 0661, 677, 0777]; + foreach ($perms as $perm) { + chmod($file, $perm); + $res = DirectoryUtility::isExecutable($file); + $this->assertTrue($res); + } + + unlink($file); + } } diff --git a/tests/TestCase/Utility/HealthchecksTest.php b/tests/TestCase/Utility/HealthchecksTest.php index 4440cc9442..1ed6172701 100644 --- a/tests/TestCase/Utility/HealthchecksTest.php +++ b/tests/TestCase/Utility/HealthchecksTest.php @@ -17,10 +17,21 @@ namespace App\Test\TestCase\Utility; use App\Test\Lib\AppIntegrationTestCase; +use App\Utility\Filesystem\DirectoryUtility; use App\Utility\Healthchecks; +use Cake\Core\Configure; +use Passbolt\JwtAuthentication\Service\AccessToken\JwksGetService; +use Passbolt\JwtAuthentication\Service\AccessToken\JwtKeyPairService; +use Passbolt\JwtAuthentication\Service\AccessToken\JwtTokenCreateService; class HealthchecksTest extends AppIntegrationTestCase { + public function tearDown(): void + { + parent::tearDown(); + $this->disableFeaturePlugin('JwtAuthentication'); + } + public function testHealthcheckApplication() { $check = Healthchecks::application(); @@ -62,8 +73,18 @@ public function testDatabase() public function testHealthcheckEnvironment() { $check = Healthchecks::environment(); - $attributes = ['phpVersion', 'pcre', 'tmpWritable', 'imgPublicWritable']; - $this->assertArrayHasAttributes($attributes, $check['environment']); + $expectedCheck = [ 'environment' => [ + 'phpVersion' => true, + 'pcre' => true, + 'mbstring' => true, + 'gnupg' => true, + 'intl' => true, + 'image' => true, + 'tmpWritable' => true, + 'logWritable' => true, + //'allow_url_fopen' => true, + ]]; + $this->assertSame($expectedCheck, $check); } public function testHealthcheckGpg() @@ -83,4 +104,20 @@ public function testSsl() $attributes = ['peerValid', 'hostValid', 'notSelfSigned']; $this->assertArrayHasAttributes($attributes, $check['ssl']); } + + public function testJwt() + { + DirectoryUtility::removeRecursively(JwtTokenCreateService::JWT_SECRET_KEY_PATH); + DirectoryUtility::removeRecursively(JwksGetService::PUBLIC_KEY_PATH); + Configure::delete('passbolt.plugins.jwtAuthentication.enabled'); + $check = Healthchecks::jwt(); + $attributes = ['isEnabled',]; + $this->assertArrayHasAttributes($attributes, $check['jwt']); + + (new JwtKeyPairService())->createKeyPair(); + $this->enableFeaturePlugin('JwtAuthentication'); + $check = Healthchecks::jwt(); + $attributes = ['isEnabled', 'keyPairValid',]; + $this->assertArrayHasAttributes($attributes, $check['jwt']); + } } diff --git a/tests/TestCase/Utility/OpenPGP/Backends/GnupgTest.php b/tests/TestCase/Utility/OpenPGP/Backends/GnupgTest.php index 253230b28d..3129607e16 100644 --- a/tests/TestCase/Utility/OpenPGP/Backends/GnupgTest.php +++ b/tests/TestCase/Utility/OpenPGP/Backends/GnupgTest.php @@ -156,7 +156,25 @@ public function testGnupgAssertDecryptKeyError() public function testGnupgAssertDecryptKeySuccess() { - $this->markTestIncomplete(); + $keys = $this->getDummyGpgkey(); + $this->gnupg->setDecryptKey($keys['private_key_armored'], ''); + $encrypted = '-----BEGIN PGP MESSAGE----- + hQIMAxWrZ+ffF0kbAQ/7Bbn3FDqVhUygbt2GuT/zZYJWbHLpxzHKS0Thn5sZeusp + W46co6ehOTTUOelK/8ODSAZo/7VHjqEhYdtonwBxTVqAfk9as3ffNlr2CTyUdlRD + 1Rr7zj8zHKDGFaeA6M8oKR+gnIIweiCL9xhpSXZJdad+lC9862Ws0XekhqdMmckn + PZTJFyEOG6KUSlOgsHWDr4iDcGLSf6/6+R+/apTEFV8m6eAQLZW1pmFPQfMwdjdI + 52I9aNoW7Eafn581ER/WeJkyX6VGUQBkEgph13tB3JB4V9NNxxllqBzdY5cH2xO/ + 6kRnIz722NZ7lgGjJz5zIUmV6aFwH5jgZWhLN6gwKRJuGdqb7ncMxxMqNTvv4Hkk + HFrl7m9XjAR9I4+mXTEqbD1w9JjBws4lXdschLHOKZjUrziAmSChBsegxPTm8mpI + YXhFPsLCkC3jaPj9TeTIgSemuxmmQMzjeHj+RpPVciNcFFv3tfMF9WM6JiEsuVeR + 0io2rqHzEMBLhbmDIPQ4nsTWyVxWswqbzleMGcUUfgUwXyRJDD56M6kpXY7BI5lh + Z1pKGSbzO72RX9jSynanDhhv/BeIPklmSLKfqBZtG/y7x8b/HQJ1ugA2F7vymW3k + zi/cSx3JwgsAplFPGUTdGxX9Ht+EtP6GXfb2rCAmMNDUTc4kqP6LbObk3ib1PLrS + PwGOkDyjWQT0cvmL+P9lWaGwNwtqtxYtTiEoYS4fYK0sRjkFSrKsserkND3Ad/ol + p7hokpGnpTQXl9C5Oi/+uQ== + =lMs6 + -----END PGP MESSAGE-----'; + $this->assertEquals('test', $this->gnupg->decrypt($encrypted)); } public function testGnupgAssertEncryptKeyError() @@ -167,7 +185,11 @@ public function testGnupgAssertEncryptKeyError() public function testGnupgAssertEncryptKeySuccess() { - $this->markTestIncomplete(); + $keys = $this->getDummyGpgkey(); + $this->gnupg->setEncryptKey($keys['public_key_armored']); + $encrypted = $this->gnupg->encrypt('test'); + + $this->assertStringNotContainsString('test', $encrypted); } public function testGnupgAssertSignKeyError() @@ -178,7 +200,11 @@ public function testGnupgAssertSignKeyError() public function testGnupgAssertSignKeySuccess() { - $this->markTestIncomplete(); + $keys = $this->getDummyGpgkey(); + $this->gnupg->setSignKey($keys['private_key_armored'], ''); + $signed = $this->gnupg->sign('test'); + $this->assertNotEquals('test', $signed); + $this->assertStringContainsString('test', $signed); } public function testGnupgIsValidMessageError() @@ -195,8 +221,9 @@ public function testGnupgVerifySuccess() $armoredSignedMessage = $this->getDummySignedMessage('betty'); $armoredKey = file_get_contents(FIXTURES . DS . 'Gpgkeys' . DS . 'ada_public.key'); $fingerprint = $this->gnupg->importKeyIntoKeyring($armoredKey); - $message = null; - $this->gnupg->verify($armoredSignedMessage, $fingerprint, $message); + $message = ''; + $this->gnupg->setVerifyKeyFromFingerprint($fingerprint); + $this->gnupg->verify($armoredSignedMessage, $message); $this->assertMatchesRegularExpression('/^Signed message/', $message); } @@ -206,7 +233,8 @@ public function testGnupgVerifyError() $armoredKey = file_get_contents(FIXTURES . DS . 'Gpgkeys' . DS . 'betty_public.key'); $fingerprint = $this->gnupg->importKeyIntoKeyring($armoredKey); $this->expectException(Exception::class); - $this->gnupg->verify($armoredSignedMessage, $fingerprint); + $this->gnupg->setVerifyKeyFromFingerprint($fingerprint); + $this->gnupg->verify($armoredSignedMessage); } public function testIsParsableArmoredSignedMessageSuccess() @@ -264,7 +292,8 @@ public function testGnupgSignSuccess() $signedMessage = $this->gnupg->sign($messageToSign); $messageUnsigned = null; - $this->gnupg->verify($signedMessage, $keyInfo['fingerprint'], $messageUnsigned); + $this->gnupg->setVerifyKeyFromFingerprint($keyInfo['fingerprint']); + $this->gnupg->verify($signedMessage, $messageUnsigned); $this->assertEquals($messageToSign . "\n", $messageUnsigned); } @@ -280,21 +309,35 @@ public function testGnupgParsePublicECCKey() { // ECDH (Encrypt only) - curve25519 // Algorithm 22? - $this->markTestSkipped(); $pkey = '-----BEGIN PGP PUBLIC KEY BLOCK----- -Version: Mailvelope v4.4.0 -Comment: https://www.mailvelope.com - -xjMEX/6JgRYJKwYBBAHaRw8BAQdAvRfrNkvv+hzj95mnAKKb572W8Fw5tSsu -ypTpEJn2HWHNJEVDQyBUZXN0IEtleSA8ZWNjLXRlc3RAcGFzc2JvbHQuY29t -PsJ3BBAWCgAfBQJf/omBBgsJBwgDAgQVCAoCAxYCAQIZAQIbAwIeAQAKCRCP -jtZ79Q2PWxz5AQDOnJ6Ux9lYC9RqN/Xxf6yM+PD0WczVZ2PI/6ICsnVEugD/ -RO44leGb/at6kXdMi+yGb8fDbP3QN/YbGTJe0H3XIQ3OOARf/omBEgorBgEE -AZdVAQUBAQdABJrhHd7yhXF2x/UAJ1f7incWdMLpbaQdNLFK/VvNOTQDAQgH -wmEEGBYIAAkFAl/+iYECGwwACgkQj47We/UNj1vKUAD9Gc9XF0mHXDn8E4TW -VUbjxe+FtEFWymo5j0tGExtgBasA/182NTNv+mjJUjH4+l7WzdZzv7icliZ8 -kr4WH0WH9ycL -=nkep + +mQENBFv/eJgBCADDkW8IYwHmaQXWw5Dce9OzPH4N5NMuhdgli286ADBH3/Tkfi96 +2SBtcf3sOfw0yNXlFU9F2yv9c+pAsjL+TUveTotCcKp3GflT4qCKbTTj2Vt09m8z +8nrZUwe05szcWqnCKCh7sBGQlFG3GkiH42QQ7kqE0vuEa08eSYUhBWYsK28hBtUJ +sXC2iP4sNymC+0DmzpdJ6DjZJUpoHnk77B1IvPAPTDo/jFH4/PwAMoi4khPvFjMJ +gKq40exIL/a60osYZN1D2KrawEjPRo3jJslrr6F2OwBJ77bTLCScHLxRmE3LOULp +YhkHx1A6GmVzZoF0BIBTKfWk21lM9BhI7wXxABEBAAG0I1Bhc3Nib2x0IGR1bW15 +IDxkdW1teUBwYXNzYm9sdC5jb20+iQFUBBMBCAA+FiEENlfUAuY5Y5ZX4xTR7Hu+ +/5sJExsFAlv/eJgCGwMFCQeGH4AFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQ +7Hu+/5sJExsefQgAkW+m4AAE1skaUol2StivuwDaO5ncpo25YC9+jg8TTRlUq7qq +cM1Dfys+7G5leOLNrIA98e+Rv3gtlLy3UevGVRN4R3iRhV7A9bgb3o/rQR2dVI3P +XEkB2iKGY/YsmayebzaMwY2rWhYrqJC4VDkAiLP7nC1xFDkBvzGvIxg/fJWi0eiv +NbQ/ztZla1ZctxttNRejDyLWzDgvR0aruv2+rRbO++QzrLEXv/NThD4Iy8diHM48 +QoVWKwKOgHNorNYi4zeBOycP6KJ3No0oOOvnQ1dMa8EUee7FEgDp9pZ7TKpcC2P0 +FEkjd4HDiLYZ0ppci0VAd4eLKddUbtEoseEYKrkBDQRb/3iYAQgA1SxFmNm4Byys +7MFXebJsh9TfYDcS0wnAXKy6frABFS1O/e35djH5Emr9xKTFVQn9VouJ6jd5WDCg +oplssKLC1izItQePe2p6JLP4p+Zv23MfsluyEEjlHviT/VOwGCYXuYjKgqrHd/Uj +XPKijsrLKH2BIXWB1Zt8gHxS8StL+632SXT3ZONETf7nKKnHosIxa8ATBm9Ncr1Z +aqahQmuOFqqyVw1U34vznBz8Xx009h39oKkJTymUXEzb/rYCdo6iKLSO6NqpG2Gz +0H8wl2q6KiG84hcSEFiJ6t9m8U9iq08PxSyV8AUaY950Pa0yI/8AkX+yxLEXkHNF +tbptB0fKPQARAQABiQE8BBgBCAAmFiEENlfUAuY5Y5ZX4xTR7Hu+/5sJExsFAlv/ +eJgCGwwFCQeGH4AACgkQ7Hu+/5sJExvluggApQcvGqkfyD4Eb115LUmi549IKKWq +8FFf85MWoZJ0BLNpIiWLBZFIs8dC4GJYSc1TaBlqlPtaHVh4kxlMvmAWGvDJ0AiE +GVhwE8B5T7pMkFZBIzKPpOPMxBSIue//K2XzxN0yXz+Rae7wpdQlgbcHByZZPnp3 +/9E46AOwf5WDWu9J3081jIspeoAl4XOOncVi4azCNX8iwPcJVERQnInnpqBEV9qf +H7sFPO+a9XpBJWjB8mMJzoA3ICWzb0u5YyUpBU6LmHHCGWY+gBDaNKMbRoRUUYyK +eZOICKSe4NoPeN03QbqyJsSV1vynpafS+G+AFfbCGnj0dy6DvWldiSR6kA== +=OtIW -----END PGP PUBLIC KEY BLOCK----- '; $parsable = $this->gnupg->isParsableArmoredPublicKey($pkey); diff --git a/tests/TestCase/View/Helper/AvatarHelperTest.php b/tests/TestCase/View/Helper/AvatarHelperTest.php index 803f91f348..1ba514aa9b 100644 --- a/tests/TestCase/View/Helper/AvatarHelperTest.php +++ b/tests/TestCase/View/Helper/AvatarHelperTest.php @@ -17,18 +17,19 @@ namespace App\Test\TestCase\View\Helper; -use App\Model\Table\AvatarsTable; +use App\Service\Avatars\AvatarsConfigurationService; use App\Test\Lib\AppIntegrationTestCase; -use App\Test\Lib\Model\AvatarsModelTestTrait; +use App\Test\Lib\Model\AvatarsModelTrait; use App\View\Helper\AvatarHelper; use Cake\Core\Configure; +use Cake\ORM\TableRegistry; /** * @covers \App\View\Helper\AvatarHelper */ class AvatarHelperTest extends AppIntegrationTestCase { - use AvatarsModelTestTrait; + use AvatarsModelTrait; /** * @var string @@ -47,6 +48,8 @@ public function tearDown(): void public function testGetDefaultAvatarUrl() { + TableRegistry::getTableLocator()->get('Avatars'); + $this->assertSame( $this->fullBaseUrl . '/img/avatar/user.png', AvatarHelper::getAvatarUrl() @@ -54,12 +57,12 @@ public function testGetDefaultAvatarUrl() $this->assertSame( $this->fullBaseUrl . '/img/avatar/user.png', - AvatarHelper::getAvatarUrl(null, AvatarsTable::FORMAT_SMALL) + AvatarHelper::getAvatarUrl(null, AvatarsConfigurationService::FORMAT_SMALL) ); $this->assertSame( $this->fullBaseUrl . '/img/avatar/user_medium.png', - AvatarHelper::getAvatarUrl(null, AvatarsTable::FORMAT_MEDIUM) + AvatarHelper::getAvatarUrl(null, AvatarsConfigurationService::FORMAT_MEDIUM) ); $this->expectException(\RuntimeException::class); @@ -70,7 +73,7 @@ public function testGetExistingAvatarUrl() { $this->loadRoutes(); $avatar = $this->createAvatar(); - $expectedUrl = $this->fullBaseUrl . '/avatars/view/' . $avatar->get('id') . '/' . AvatarsTable::FORMAT_SMALL . AvatarHelper::IMAGE_EXTENSION; + $expectedUrl = $this->fullBaseUrl . '/avatars/view/' . $avatar->get('id') . '/' . AvatarsConfigurationService::FORMAT_SMALL . AvatarHelper::IMAGE_EXTENSION; // // We are performing a unit test here. But the routes are loaded in the Middleware in CakePHP4 // // Therefore an application needs to be build, which is here made using a call to a dummy url (an avatar one) // $this->get($expectedUrl); @@ -79,7 +82,10 @@ public function testGetExistingAvatarUrl() // We now test the AvatarHelper as such. $this->assertSame( $expectedUrl, - AvatarHelper::getAvatarUrl($avatar) + AvatarHelper::getAvatarUrl([ + 'id' => $avatar['id'], + 'data' => $avatar['data'], + ]) ); } diff --git a/tests/cookbooks/passbolt_package/recipes/passbolt_install.rb b/tests/cookbooks/passbolt_package/recipes/passbolt_install.rb index ca6283c31c..9589ca0aeb 100644 --- a/tests/cookbooks/passbolt_package/recipes/passbolt_install.rb +++ b/tests/cookbooks/passbolt_package/recipes/passbolt_install.rb @@ -5,12 +5,52 @@ # Copyright:: 2020, The Authors, All Rights Reserved. # -apt_update +if platform_family?('debian') + apt_update -# TODO: Change this command when the repo is available + # TODO: Change this command when the repo is available -execute "Install passbolt" do - command "DEBIAN_FRONTEND=noninteractive apt-get install -y #{node['parameters']} /tmp/passbolt/passbolt*.deb \ - && service php$(php -r 'echo PHP_VERSION;' | sed 's:\\(7\\.[2-4]\\).*:\\1:')-fpm start #{node.has_key?(:parameters) ? '' : '&& service nginx start'}" - action :run + execute "Install passbolt" do + command "DEBIAN_FRONTEND=noninteractive apt-get install -y #{node['parameters']} /tmp/passbolt/passbolt*.deb \ + && service php$(php -r 'echo PHP_VERSION;' | sed 's:\\(7\\.[2-4]\\).*:\\1:')-fpm start #{node.has_key?(:parameters) ? '' : '&& service nginx start'}" + action :run + end +elsif platform_family?('rhel') + package 'RHEL: Install dependencies' do + package_name ['rpmdevtools', 'bc', 'createrepo', 'firewalld'] + action :install + end + + execute "Setup remirepo" do + cwd "#{node['dest_dir']}" + command "/bin/sh rpm/scripts/setup-remirepo.sh" + action :run + end + + execute "Setup local repository" do + cwd "#{node['dest_dir']}" + command "/bin/sh rpm/scripts/setup-local-repository.sh" + action :run + end + + package "Install Passbolt" do + flush_cache [ :before ] + package_name "passbolt-#{node['passbolt_flavour']}-server" + action :install + end + + service 'firewalld' do + action :restart + end + + execute "Configure passbolt" do + command "/usr/local/bin/passbolt-configure \ + -P passbolt \ + -p hawhawhaw \ + -d passboltdb \ + -u passboltadmin \ + -H 127.0.0.1 \ + -e -n" + action :run + end end diff --git a/tests/cookbooks/passbolt_package/recipes/passbolt_install_and_purge.rb b/tests/cookbooks/passbolt_package/recipes/passbolt_install_and_purge.rb index 12c0c158b3..fd5fc6c36c 100644 --- a/tests/cookbooks/passbolt_package/recipes/passbolt_install_and_purge.rb +++ b/tests/cookbooks/passbolt_package/recipes/passbolt_install_and_purge.rb @@ -5,7 +5,9 @@ # Copyright:: 2020, The Authors, All Rights Reserved. # -apt_update +if platform_family?('debian') + apt_update +end include_recipe '::passbolt_install' diff --git a/tests/cookbooks/passbolt_package/recipes/passbolt_install_break_and_recover.rb b/tests/cookbooks/passbolt_package/recipes/passbolt_install_break_and_recover.rb index 33eab92667..823f45175e 100644 --- a/tests/cookbooks/passbolt_package/recipes/passbolt_install_break_and_recover.rb +++ b/tests/cookbooks/passbolt_package/recipes/passbolt_install_break_and_recover.rb @@ -4,11 +4,21 @@ # # Copyright:: 2020, The Authors, All Rights Reserved. # -apt_update - -package 'Install mariadb and nginx' do - package_name ['debconf-utils', 'curl', 'nginx', 'default-mysql-server'] - action :install +if platform_family?('debian') + apt_update + package 'Debian: Install mariadb and nginx' do + package_name ['debconf-utils', 'curl', 'nginx', 'default-mysql-server'] + action :install + end +elsif platform_family?('rhel') + package 'RHEL: Install epel-release repository' do + package_name ['epel-release'] + action :install + end + package 'RHEL: Install mariadb and nginx' do + package_name ['curl', 'nginx', 'mariadb-server', 'createrepo', 'firewalld'] + action :install + end end execute "Break nginx and mysql" do diff --git a/tests/cookbooks/passbolt_package/recipes/passbolt_install_breaks.rb b/tests/cookbooks/passbolt_package/recipes/passbolt_install_breaks.rb index ba104958bf..4d55ddb696 100644 --- a/tests/cookbooks/passbolt_package/recipes/passbolt_install_breaks.rb +++ b/tests/cookbooks/passbolt_package/recipes/passbolt_install_breaks.rb @@ -5,11 +5,47 @@ # Copyright:: 2020, The Authors, All Rights Reserved. # -apt_update +if platform_family?('debian') + apt_update -execute "Install passbolt, expect to break, display output" do - command "VERBOSE=1 DEBIAN_FRONTEND=noninteractive apt-get install -y #{node['parameters']} /tmp/passbolt/passbolt*.deb" - ignore_failure true - live_stream true - action :run + execute "Install passbolt, expect to break, display output" do + command "VERBOSE=1 DEBIAN_FRONTEND=noninteractive apt-get install -y #{node['parameters']} /tmp/passbolt/passbolt*.deb" + ignore_failure true + action :run + end +elsif platform_family?('rhel') + package 'RHEL: Install dependencies' do + package_name ['rpmdevtools', 'bc', 'createrepo', 'firewalld'] + action :install + end + + execute "Setup remirepo" do + cwd "#{node['dest_dir']}" + command "/bin/sh rpm/scripts/setup-remirepo.sh" + action :run + end + + execute "Setup local repository" do + cwd "#{node['dest_dir']}" + command "/bin/sh rpm/scripts/setup-local-repository.sh" + action :run + end + + package "Install Passbolt" do + flush_cache [ :before ] + package_name "passbolt-#{node['passbolt_flavour']}-server" + action :install + end + + execute "Configure passbolt, expect to break, display output" do + command "/usr/local/bin/passbolt-configure \ + -P passbolt \ + -p passbolt \ + -d passbolt \ + -u passbolt \ + -H pro.rockylinux8.jc \ + -e -n" + ignore_failure true + action :run + end end diff --git a/tests/cookbooks/passbolt_package/recipes/passbolt_install_mariadb_nginx.rb b/tests/cookbooks/passbolt_package/recipes/passbolt_install_mariadb_nginx.rb index d0d5862d83..0a2b0693bb 100644 --- a/tests/cookbooks/passbolt_package/recipes/passbolt_install_mariadb_nginx.rb +++ b/tests/cookbooks/passbolt_package/recipes/passbolt_install_mariadb_nginx.rb @@ -4,16 +4,35 @@ # # Copyright:: 2020, The Authors, All Rights Reserved. # -apt_update -package 'mariadb-server' do - package_name [ 'debconf-utils', 'curl', 'mariadb-server', 'nginx' ] - action :install -end +# Defaul database engine +database_engine = 'mysql' + +if platform_family?('debian') + apt_update + package 'Debian: Install mariadb and nginx' do + package_name ['debconf-utils', 'curl', 'nginx', 'default-mysql-server'] + action :install + end -execute "Start mysql" do - command "service mysql start" - action :run + # Use different database service depending on the OS + if (node['platform'] == 'debian' && node['platform_version'] =='11') + database_engine = 'mariadb' + end + + execute "Start mysql" do + command "service #{database_engine} start" + action :run + end +elsif platform_family?('rhel') + package 'RHEL: Install epel-release repository' do + package_name ['epel-release'] + action :install + end + package 'RHEL: Install mariadb and nginx' do + package_name ['curl', 'nginx', 'mariadb-server', 'createrepo', 'firewalld'] + action :install + end end include_recipe '::passbolt_responses_nginx_mysql' diff --git a/tests/cookbooks/passbolt_package/recipes/passbolt_package_build.rb b/tests/cookbooks/passbolt_package/recipes/passbolt_package_build.rb index e59cb7973b..7e2a19f2c7 100644 --- a/tests/cookbooks/passbolt_package/recipes/passbolt_package_build.rb +++ b/tests/cookbooks/passbolt_package/recipes/passbolt_package_build.rb @@ -5,19 +5,41 @@ # Copyright:: 2020, The Authors, All Rights Reserved. # -if (Dir.glob("#{node['dest_dir']}/passbolt-*.deb").empty? or - Dir.glob("#{node['dest_dir']}/vendor/*").empty?) then - apt_update 'all platforms' do - action :update +if platform_family?('rhel') && node['platform_version'].to_f < 8.0 + file '/etc/resolv.conf' do + content 'nameserver 1.1.1.1' + mode '0644' + owner 'root' + group 'root' end end -if Dir.glob("#{node['dest_dir']}/vendor/*").empty? then - package 'Install composer' do - package_name 'composer' - action :install +if platform_family?('debian') + if (Dir.glob("#{node['dest_dir']}/passbolt-*.deb").empty? or + Dir.glob("#{node['dest_dir']}/vendor/*").empty?) then + apt_update 'all platforms' do + action :update + end end +end +if Dir.glob("#{node['dest_dir']}/vendor/*").empty? then + if platform_family?('debian') then + package 'Debian: Install composer' do + package_name 'composer' + action :install + end + elsif platform_family?('rhel') then + package 'RHEL: Install composer dependencies' do + package_name ['php-cli', 'php-zip', 'php-json', 'wget', 'unzip'] + action :install + end + execute 'RHEL: Install composer' do + cwd "#{node['dest_dir']}" + command "/bin/sh rpm/scripts/setup-composer.sh" + action :run + end + end execute 'Download vendors' do cwd "#{node['dest_dir']}" command 'composer install -o --prefer-dist --no-dev --ignore-platform-reqs --no-interaction' @@ -25,23 +47,65 @@ end end -if Dir.glob("#{node['dest_dir']}/passbolt-*.deb").empty? then - package 'Install dev dependencies' do - package_name ['devscripts', 'build-essential', 'apt-utils', 'fakeroot', 'equivs', 'cdbs', 'git-buildpackage'] - action :install +if platform_family?('debian') + if Dir.glob("#{node['dest_dir']}/passbolt-*.deb").empty? then + package 'Install dev dependencies' do + package_name ['devscripts', 'build-essential', 'apt-utils', 'fakeroot', 'equivs', 'cdbs', 'git-buildpackage'] + action :install + end + + execute 'Build debian package' do + cwd "#{node['dest_dir']}" + command "export PASSBOLT_FLAVOUR=#{node['passbolt_flavour']} \ + && make -f debian/rules debian/control \ + && gbp dch --snapshot --snapshot-number=$(date +%s) --ignore-branch \ + && mk-build-deps -irt'apt-get --no-install-recommends -yV' debian/control && dpkg-checkbuilddeps \ + && debuild --preserve-envvar PASSBOLT_FLAVOUR -us -uc -b -i -I \ + && cp ../*.deb . \ + && cp ../*.build . \ + && cp ../*.buildinfo . \ + && cp ../*.changes ." + action :run + end end +elsif platform_family?('rhel') + if Dir.glob("#{node['dest_dir']}/passbolt-*.rpm").empty? then + package 'RHEL: Install dev dependencies' do + package_name ['rpmdevtools', 'rpmlint', 'rsync', 'selinux-policy-devel', 'rpm-build'] + action :install + end - execute 'Build debian package' do - cwd "#{node['dest_dir']}" - command "export PASSBOLT_FLAVOUR=#{node['passbolt_flavour']} \ - && make -f debian/rules debian/control \ - && gbp dch --snapshot --snapshot-number=$(date +%s) --ignore-branch \ - && mk-build-deps -irt'apt-get --no-install-recommends -yV' debian/control && dpkg-checkbuilddeps \ - && debuild --preserve-envvar PASSBOLT_FLAVOUR -us -uc -b -i -I \ - && cp ../*.deb . \ - && cp ../*.build . \ - && cp ../*.buildinfo . \ - && cp ../*.changes ." - action :run + execute 'RHEL: List' do + cwd "#{node['dest_dir']}" + command "ls -las" + live_stream true + action :run + end + + execute 'RHEL: Setup RPM devtree' do + cwd "#{node['dest_dir']}" + command "rpmdev-setuptree" + action :run + end + + execute 'RHEL: Build Passbolt RPM package' do + cwd "#{node['dest_dir']}" + command "PASSBOLT_FLAVOUR=#{node['passbolt_flavour']} \ + PKG_VERSION=#{node['passbolt_version']} \ + /bin/sh rpm/scripts/build-passbolt-server.sh" + action :run + end + + execute 'RHEL: Build passbolt-selinux RPM package' do + cwd "#{node['dest_dir']}" + command "PKG_VERSION=0.1 /bin/sh rpm/scripts/build-passbolt-selinux.sh" + action :run + end + + execute 'RHEL: Copy packages in repository' do + cwd "#{node['dest_dir']}" + command "cp -rf ~/rpmbuild/RPMS/noarch/passbolt-* ." + action :run + end end end diff --git a/tests/cookbooks/passbolt_package/recipes/passbolt_responses_nginx_mysql.rb b/tests/cookbooks/passbolt_package/recipes/passbolt_responses_nginx_mysql.rb index 55b179c95b..d31650b064 100644 --- a/tests/cookbooks/passbolt_package/recipes/passbolt_responses_nginx_mysql.rb +++ b/tests/cookbooks/passbolt_package/recipes/passbolt_responses_nginx_mysql.rb @@ -16,11 +16,13 @@ # nginx-ssl: none # nginx-domain: empty (postinst sets _) -execute "Provide responses to customize nginx and mysql" do - command "printf 'passbolt-#{node['passbolt_flavour']}-server passbolt/mysql-configuration select true' | debconf-set-selections ; \ - printf 'passbolt-#{node['passbolt_flavour']}-server passbolt/mysql-passbolt-password password hawhawhaw' | debconf-set-selections ; \ - printf 'passbolt-#{node['passbolt_flavour']}-server passbolt/nginx-configuration select true' | debconf-set-selections ; \ - printf 'passbolt-#{node['passbolt_flavour']}-server passbolt/nginx-configuration-two-choices select none' | debconf-set-selections ; \ - printf 'passbolt-#{node['passbolt_flavour']}-server passbolt/nginx-domain string 127.0.0.1' | debconf-set-selections" - action :run -end +if platform_family?('debian') + execute "Provide responses to customize nginx and mysql" do + command "printf 'passbolt-#{node['passbolt_flavour']}-server passbolt/mysql-configuration select true' | debconf-set-selections ; \ + printf 'passbolt-#{node['passbolt_flavour']}-server passbolt/mysql-passbolt-password password hawhawhaw' | debconf-set-selections ; \ + printf 'passbolt-#{node['passbolt_flavour']}-server passbolt/nginx-configuration select true' | debconf-set-selections ; \ + printf 'passbolt-#{node['passbolt_flavour']}-server passbolt/nginx-configuration-two-choices select none' | debconf-set-selections ; \ + printf 'passbolt-#{node['passbolt_flavour']}-server passbolt/nginx-domain string 127.0.0.1' | debconf-set-selections" + action :run + end +end \ No newline at end of file diff --git a/tests/integration/filesystem-benchmarks/controls/configuration.rb b/tests/integration/filesystem-benchmarks/controls/configuration.rb index 3da727808c..d7b058e3b6 100644 --- a/tests/integration/filesystem-benchmarks/controls/configuration.rb +++ b/tests/integration/filesystem-benchmarks/controls/configuration.rb @@ -6,6 +6,14 @@ config_dir = '/etc/passbolt' gnupg_dir = '/var/lib/passbolt/.gnupg' +webserver_owner = 'www-data' +webserver_group = 'www-data' + +if os.family == 'redhat' + webserver_owner = 'nginx' + webserver_group = 'nginx' +end + # you add controls here control 'passbolt-config-01' do # A unique ID for this control impact 1 # The criticality, if this control fails. @@ -14,7 +22,7 @@ describe file("#{config_dir}/app.php") do # The actual test it { should exist } its('owner') { should eq 'root' } - its('group') { should eq 'www-data' } + its('group') { should eq webserver_group } its('mode') { should cmp '00640' } end end @@ -27,7 +35,7 @@ describe file("#{config_dir}/bootstrap.php") do # The actual test it { should exist } its('owner') { should eq 'root' } - its('group') { should eq 'www-data' } + its('group') { should eq webserver_group } its('mode') { should cmp '00640' } end end @@ -40,7 +48,7 @@ describe file("#{config_dir}/bootstrap_cli.php") do # The actual test it { should exist } its('owner') { should eq 'root' } - its('group') { should eq 'www-data' } + its('group') { should eq webserver_group } its('mode') { should cmp '00640' } end end @@ -53,20 +61,7 @@ describe file("#{config_dir}/bootstrap_plugins.php") do # The actual test it { should exist } its('owner') { should eq 'root' } - its('group') { should eq 'www-data' } - its('mode') { should cmp '00640' } - end -end - -# you add controls here -control 'passbolt-config-05' do # A unique ID for this control - impact 1 # The criticality, if this control fails. - title 'file_storage.php file is installed' # A human-readable title - desc 'file_storage.php file is present with correct permissions' - describe file("#{config_dir}/file_storage.php") do # The actual test - it { should exist } - its('owner') { should eq 'root' } - its('group') { should eq 'www-data' } + its('group') { should eq webserver_group } its('mode') { should cmp '00640' } end end @@ -79,7 +74,7 @@ describe file("#{config_dir}/requirements.php") do # The actual test it { should exist } its('owner') { should eq 'root' } - its('group') { should eq 'www-data' } + its('group') { should eq webserver_group } its('mode') { should cmp '00640' } end end @@ -92,7 +87,7 @@ describe file("#{config_dir}/routes.php") do # The actual test it { should exist } its('owner') { should eq 'root' } - its('group') { should eq 'www-data' } + its('group') { should eq webserver_group } its('mode') { should cmp '00640' } end end @@ -105,7 +100,7 @@ describe file("#{config_dir}/version.php") do # The actual test it { should exist } its('owner') { should eq 'root' } - its('group') { should eq 'www-data' } + its('group') { should eq webserver_group } its('mode') { should cmp '00640' } end end @@ -118,7 +113,7 @@ describe directory("#{config_dir}/gpg") do # The actual test it { should exist } its('owner') { should eq 'root' } - its('group') { should eq 'www-data' } + its('group') { should eq webserver_group } its('mode') { should cmp '00770' } it 'should be empty' do expect(command("ls #{config_dir}/gpg | wc -l").stdout).to eq "0\n" @@ -133,8 +128,8 @@ desc 'gnugpg directory is present with correct permissions for web server user' describe directory("#{gnupg_dir}") do # The actual test it { should exist } - its('owner') { should eq 'www-data' } - its('group') { should eq 'www-data' } + its('owner') { should eq webserver_owner } + its('group') { should eq webserver_group } its('mode') { should cmp '00700' } it 'should be empty' do expect(command("ls #{gnupg_dir} | wc -l").stdout).to eq "0\n" diff --git a/tests/integration/filesystem-benchmarks/controls/systemfiles.rb b/tests/integration/filesystem-benchmarks/controls/systemfiles.rb index 010262695e..34b5bf892b 100644 --- a/tests/integration/filesystem-benchmarks/controls/systemfiles.rb +++ b/tests/integration/filesystem-benchmarks/controls/systemfiles.rb @@ -9,14 +9,22 @@ logrotate_dir = '/etc/logrotate.d' cron_script = '/usr/share/php/passbolt/bin/cron' +webserver_owner = 'www-data' +webserver_group = 'www-data' + +if os.family == 'redhat' + webserver_owner = 'nginx' + webserver_group = 'nginx' +end + control 'passbolt-logs-01' do impact 1 title 'passbolt logs directory' desc 'passbolt logs directory is present and has write permissions for www user' describe directory("#{logs_dir}") do it { should exist } - its('owner') { should eq 'www-data' } - its('group') { should eq 'www-data' } + its('owner') { should eq webserver_owner } + its('group') { should eq webserver_group } its('mode') { should cmp '0755' } end end @@ -27,8 +35,8 @@ desc 'passbolt temporary directory is present and has write permissions for www user' describe directory("#{tmp_dir}") do it { should exist } - its('owner') { should eq 'www-data' } - its('group') { should eq 'www-data' } + its('owner') { should eq webserver_owner } + its('group') { should eq webserver_group } its('mode') { should cmp '0755' } end end @@ -40,7 +48,7 @@ describe directory("#{config_dir}") do it { should exist } its('owner') { should eq 'root' } - its('group') { should eq 'www-data' } + its('group') { should eq webserver_group } its('mode') { should cmp '00770' } end end diff --git a/tests/integration/filesystem-benchmarks/controls/webroot.rb b/tests/integration/filesystem-benchmarks/controls/webroot.rb index 02871c23fe..c72c375e07 100644 --- a/tests/integration/filesystem-benchmarks/controls/webroot.rb +++ b/tests/integration/filesystem-benchmarks/controls/webroot.rb @@ -2,6 +2,14 @@ source_dir = '/usr/share/php/passbolt/webroot' +webserver_owner = 'www-data' +webserver_group = 'www-data' + +if os.family == 'redhat' + webserver_owner = 'nginx' + webserver_group = 'nginx' +end + control 'passbolt-webroot-01' do # A unique ID for this control impact 1 # The criticality, if this control fails. title 'webroot directory' # A human-readable title @@ -23,8 +31,8 @@ owner = 'root' group = 'root' if item.include? "public" - owner = 'www-data' - group = 'www-data' + owner = webserver_owner + group = webserver_group end its('owner') { should eq owner } its('group') { should eq group } @@ -42,8 +50,8 @@ owner = 'root' group = 'root' if item.include? "public" - owner = 'www-data' - group = 'www-data' + owner = webserver_owner + group = webserver_group end its('owner') { should eq owner } its('group') { should eq group } diff --git a/tests/integration/runtime-benchmarks/controls/cache.rb b/tests/integration/runtime-benchmarks/controls/cache.rb index c8d3ddcc41..eda0e0a4f6 100644 --- a/tests/integration/runtime-benchmarks/controls/cache.rb +++ b/tests/integration/runtime-benchmarks/controls/cache.rb @@ -1,10 +1,12 @@ title 'Check passbolt has cleared debconf cache' -control 'passbolt-cache-0' do - impact 0.5 - title 'debconf cache data' - desc 'debconf cached parameters for passbolt should not exist' - describe command("debconf-get-selections | grep passbolt") do - its('stdout') { should eq '' } +if os.family == 'debian' + control 'passbolt-cache-0' do + impact 0.5 + title 'debconf cache data' + desc 'debconf cached parameters for passbolt should not exist' + describe command("debconf-get-selections | grep passbolt") do + its('stdout') { should eq '' } + end end end diff --git a/tests/integration/runtime-benchmarks/controls/mysql.rb b/tests/integration/runtime-benchmarks/controls/mysql.rb index f5c5115b9e..98c3316aad 100644 --- a/tests/integration/runtime-benchmarks/controls/mysql.rb +++ b/tests/integration/runtime-benchmarks/controls/mysql.rb @@ -1,6 +1,9 @@ title 'Check passbolt database' -sql=mysql_session('root','') +sql_username = 'passboltadmin' +sql_user_password = 'hawhawhaw' + +sql=mysql_session(sql_username, sql_user_password) control 'passbolt-mysql-0' do impact 1 diff --git a/tmp/avatars/empty b/tmp/avatars/empty new file mode 100755 index 0000000000..e69de29bb2 diff --git a/webroot/css/themes/default/api_authentication.min.css b/webroot/css/themes/default/api_authentication.min.css index c95a8541d8..aa665e5643 100644 --- a/webroot/css/themes/default/api_authentication.min.css +++ b/webroot/css/themes/default/api_authentication.min.css @@ -1,9 +1,9 @@ /**! * @name passbolt-styleguide - * @version v3.2.1 - * @date 2021-05-19 + * @version v3.4.0 + * @date 2021-12-01 * @copyright Copyright 2021 Passbolt SA * @source https://github.com/passbolt/passbolt_styleguide * @license AGPL-3.0 */ -/*! normalize.css v8.0.0 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;src:local('Open Sans'),local('OpenSans'),url(../../../fonts/opensans-regular.woff) format('woff')}@font-face{font-family:'Open Sans';font-style:normal;font-weight:700;src:local('Open Sans Bold'),local('OpenSans-Bold'),url(../../../fonts/opensans-bold.woff) format('woff')}body{font-family:'Open Sans',Verdana,sans-serif}.code{font-family:'Courier New',Courier,monospace}html{font-size:62.5%}body{font-size:1.4rem}h1{font-size:2.2rem;line-height:3.2rem}h2{font-size:1.6rem;line-height:2.4rem}p{font-size:1.4rem;line-height:2.4rem}code{font-size:1.1rem}html body .hidden{display:none}.visually-hidden,.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visually-hidden .focusable:active,.visually-hidden .focusable:focus,.visuallyhidden .focusable:active,.visuallyhidden .focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.clearfix:after,.clearfix:before{content:"";display:table}.clearfix:after{clear:both}.rounded{-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em}.ellipsis{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.rotate-right{transform:rotate(-90deg)}html{line-height:1.6rem}body{color:#333;background:#fff}body.iframe{background:0 0}a{line-height:1.5rem;border-bottom:1px solid #ddd;text-decoration:none}a:link,a:visited{color:#333}a:hover{color:#2894df;border-bottom:1px solid #2894df}a:active,a:focus{outline:0;color:#2894df;border:0}a.no-border,a:hover.no-border{border-bottom:0}a.disabled{outline:0;text-decoration:none;cursor:default;color:#888;pointer-events:none}.button{font-size:1.4rem;padding:.4rem 1.6rem;line-height:1.5rem;position:relative;display:inline-block;vertical-align:baseline;margin-right:1.6rem;outline:0;cursor:pointer;text-align:center;text-decoration:none;border:1px solid #ccc;color:#333;font-weight:400;text-shadow:1px 1px 0 rgba(255,255,255,.5);background:#eee;background-image:linear-gradient(top,#f3f3f3,#eee);-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em;box-sizing:border-box}.button.small{font-size:1.4rem;padding:.4rem 1.6rem}.button.medium{font-size:1.6rem;padding:.8rem 1.6rem}.button.big{font-size:1.8rem;padding:1.6rem 3.2rem}.button.full-width{width:100%}.button.primary{background:#2894df;border:1px solid #2894df;color:#fff;text-shadow:1px 1px 0 rgba(0,0,0,.2)}.button.primary:hover{background:#2a9ceb;border:1px solid #2a9ceb;color:#fff}.button.primary:active,.button.primary:focus{color:#fff;border:1px solid #4271b7;background:#2a9ceb}.button.warning,.button.warning:active,.button.warning:focus,.button.warning:hover{color:#fff;background:#d40101;border:1px solid #d40101;text-shadow:none}.button.warning:active,.button.warning:focus{border:1px solid #92000c}.button.cancel,.button.cancel:active,.button.cancel:focus,.button.cancel:hover,.button.dim,.button.dim:active,.button.dim:focus,.button.dim:hover{background:#fff;font-weight:400}.button:hover{color:#333;text-decoration:none;background:#f3f3f3;background-image:linear-gradient(top,#f3f3f3,#f3f3f3);border:1px solid #bbb}.button:focus{color:#2894df;border:1px solid #2894df}.button:active{color:#2894df;border:1px solid #2894df;background:#eee;position:relative;box-shadow:inset 0 1px 2px rgba(0,0,0,.2)}.button.disabled{cursor:default;color:#888;background:#fff;border:1px solid #ddd;text-shadow:none;font-weight:400}.button.disabled:active,.button.disabled:focus,.button.disabled:hover{box-shadow:0 0 0;top:0;color:#888;background:#fff;border:1px solid #ddd}.button.processing{background:#fff;border:1px solid #fff}.button.processing:after{width:100%;height:100%;position:absolute;content:" ";top:-1px;left:-1px;background:#fff url(../../../img/controls/loading_light.svg) center center no-repeat;background-size:auto 50%;border:1px solid #ddd;-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em}input[type=submit].button.processing{font-size:0;background:#fff url(../../../img/controls/loading_light.svg) center center no-repeat;background-size:auto 50%;border:1px solid #ddd;border-radius:5px}.button-toggle.selected{background:#eee;box-shadow:inset 0 1px 2px rgba(0,0,0,.2);border:1px solid #bbb}label{font-weight:700;font-size:1.6rem;line-height:2.4rem;padding:.8rem 0;display:block}.required label:after{content:" \002A";color:#d40101;font-weight:700}.input.error label{color:#d40101}input[type=email],input[type=password],input[type=search],input[type=text],textarea{box-sizing:border-box;font-size:1.6rem;line-height:2.4rem;color:#333;background:#fff;width:100%;max-width:64rem;padding:.6rem 1.2rem;display:inline-block;margin-bottom:.8rem;border:1px solid #ccc;border-top:1px solid #bbb;vertical-align:middle}input[type=file]{box-sizing:border-box;font-size:1.6rem;line-height:2.4rem;color:#333;background:#fff;width:100%;max-width:64rem;display:inline-block;margin-bottom:.8rem;vertical-align:middle}input[type=email]:hover,input[type=password]:hover,input[type=search]:hover,input[type=text]:hover,textarea:hover{border:1px solid #bbb}input[type=email]:focus,input[type=password]:focus,input[type=search]:focus,input[type=text]:focus,textarea:focus{outline:0;box-shadow:inset 1px 1px 2px rgba(0,0,0,.2);border:1px solid #2894df}input[type=email]:disabled,input[type=password]:disabled,input[type=search]:disabled,input[type=text]:disabled,textarea:disabled{color:#333;border:1px solid #ddd;box-shadow:0 0;cursor:default;background:#f3f3f3}textarea{height:3.6rem}.textarea.large textarea{width:30rem;height:7rem}.checkbox input[type=checkbox]{position:absolute;opacity:0;cursor:pointer}.checkbox input[type=checkbox].error{color:#d40101}.checkbox input[type=checkbox]+label{font-weight:400;position:relative;cursor:pointer;padding:0;margin-bottom:.8rem}.checkbox input[type=checkbox]+label:before{content:'';margin-top:.39rem;margin-right:1rem;display:inline-block;vertical-align:text-top;width:1.4rem;height:1.4rem;background:#fff;border:1px solid #ddd}.checkbox input[type=checkbox]:hover+label:before{border:1px solid #bbb}.checkbox input[type=checkbox]:focus+label:before{box-shadow:0 0 0 .3rem rgba(0,0,0,.12)}.checkbox input[type=checkbox]:checked+label:before{background:#fff;border:1px solid #bbb}.checkbox input[type=checkbox]:disabled+label{color:#ddd;cursor:auto}.checkbox input[type=checkbox]:disabled+label:before{box-shadow:none;background:#f3f3f3}.checkbox input[type=checkbox]:checked+label:after{content:'';position:absolute;left:.3rem;top:.79rem;width:1rem;height:1rem;background-size:1rem 1rem;background-color:#333;-webkit-mask-image:url(../../../img/controls/check_black.svg);mask-image:url(../../../img/controls/check_black.svg)}.checkbox.small input[type=checkbox]{cursor:pointer}.checkbox.small input[type=checkbox]+label{font-size:1.4rem}.checkbox.small input[type=checkbox]+label:before{margin-top:.2rem;margin-right:1rem;width:1.4rem;height:1.4rem}.checkbox.small input[type=checkbox]:checked+label:after{left:.3rem;top:.7rem;width:1.1rem;height:1.1rem;background-size:1.1rem 1.1rem}.input .error-message{padding:0;font-size:1.4rem;margin-top:.2rem;margin-bottom:1.6rem;background-color:transparent;border:0;font-weight:400;color:#d40101;background:#fff;clear:both}.input .help-message{padding:0;font-size:1.4rem;margin-top:.2rem;margin-bottom:1.6rem;background-color:transparent;border:0;color:#888;font-weight:400;background:#fff}.logo{background:transparent url(../../../img/logo/logo.svg) 0 0 no-repeat;background-size:20rem auto;width:20rem;height:4.5rem}ul{padding:0;margin:0}ul li{list-style:none;padding:0;margin:0}[data-simplebar]{position:relative;flex-direction:column;flex-wrap:wrap;justify-content:flex-start;align-content:flex-start;align-items:flex-start;width:inherit;height:inherit;max-width:inherit;max-height:inherit}.simplebar-wrapper{overflow:hidden;width:inherit;height:inherit;max-width:inherit;max-height:inherit}.simplebar-mask{direction:inherit;position:absolute;overflow:hidden;padding:0;margin:0;left:0;top:0;bottom:0;right:0;width:auto!important;height:auto!important;z-index:0}.simplebar-offset{direction:inherit!important;box-sizing:inherit!important;resize:none!important;position:absolute;top:0;left:0;bottom:0;right:0;padding:0;margin:0;-webkit-overflow-scrolling:touch}.simplebar-content-wrapper{direction:inherit;box-sizing:border-box!important;position:relative;display:block;height:100%;width:auto;max-width:100%;max-height:100%;scrollbar-width:none;-ms-overflow-style:none}.simplebar-content-wrapper::-webkit-scrollbar,.simplebar-hide-scrollbar::-webkit-scrollbar{width:0;height:0}.simplebar-content:after,.simplebar-content:before{content:' ';display:table}.simplebar-placeholder{max-height:100%;max-width:100%;width:100%;pointer-events:none}.simplebar-height-auto-observer-wrapper{box-sizing:inherit!important;height:100%;width:inherit;max-width:1px;position:relative;float:left;max-height:1px;overflow:hidden;z-index:-1;padding:0;margin:0;pointer-events:none;flex-grow:inherit;flex-shrink:0;flex-basis:0}.simplebar-height-auto-observer{box-sizing:inherit;display:block;opacity:0;position:absolute;top:0;left:0;height:1000%;width:1000%;min-height:1px;min-width:1px;overflow:hidden;pointer-events:none;z-index:-1}.simplebar-track{z-index:1;position:absolute;right:0;bottom:0;pointer-events:none;overflow:hidden}[data-simplebar].simplebar-dragging .simplebar-content{pointer-events:none;user-select:none;-webkit-user-select:none}[data-simplebar].simplebar-dragging .simplebar-track{pointer-events:all}.simplebar-scrollbar{position:absolute;right:2px;width:7px;min-height:10px}.simplebar-scrollbar:before{position:absolute;content:'';background:#000;border-radius:7px;left:0;right:0;opacity:0;transition:opacity .2s linear}.simplebar-scrollbar.simplebar-visible:before{opacity:.5;transition:opacity 0s linear}.simplebar-track.simplebar-vertical{top:0;width:11px}.simplebar-track.simplebar-vertical .simplebar-scrollbar:before{top:2px;bottom:2px}.simplebar-track.simplebar-horizontal{left:0;height:11px}.simplebar-track.simplebar-horizontal .simplebar-scrollbar:before{height:100%;left:2px;right:2px}.simplebar-track.simplebar-horizontal .simplebar-scrollbar{right:auto;left:0;top:2px;height:7px;min-height:0;min-width:10px;width:auto}[data-simplebar-direction=rtl] .simplebar-track.simplebar-vertical{right:auto;left:0}.hs-dummy-scrollbar-size{direction:rtl;position:fixed;opacity:0;visibility:hidden;height:500px;width:500px;overflow-y:hidden;overflow-x:scroll}.simplebar-hide-scrollbar{position:fixed;left:0;visibility:hidden;overflow-y:scroll;scrollbar-width:none;-ms-overflow-style:none}.scroll-shadow{overflow:auto;background:linear-gradient(#fff 30%,rgba(255,255,255,0)),linear-gradient(rgba(255,255,255,0),#fff 70%) 0 100%,radial-gradient(farthest-side at 50% 0,rgba(0,0,0,.08),rgba(0,0,0,0)),radial-gradient(farthest-side at 50% 100%,rgba(0,0,0,.08),rgba(0,0,0,0)) 0 100%;background-repeat:no-repeat;background-color:#fff;background-size:100% 40px,100% 40px,100% 14px,100% 14px;background-attachment:local,local,scroll,scroll}.simplebar-content{overflow:auto;background:linear-gradient(#fff 30%,rgba(255,255,255,0)),linear-gradient(rgba(255,255,255,0),#fff 70%) 0 100%,radial-gradient(farthest-side at 50% 0,rgba(0,0,0,.08),rgba(0,0,0,0)),radial-gradient(farthest-side at 50% 100%,rgba(0,0,0,.08),rgba(0,0,0,0)) 0 100%;background-repeat:no-repeat;background-color:#fff;background-size:100% 40px,100% 40px,100% 14px,100% 14px;background-attachment:local,local,scroll,scroll}.svg-icon{display:inline-flex;align-self:center}.svg-icon svg{fill:#333;height:1em;width:1em}.svg-icon.baseline svg{top:.125em;position:relative}.svg-icon.dim svg,.svg-icon.light svg{fill:#888}.svg-icon.icon-only svg{position:relative;top:.25em;width:1.1rem;height:1.1rem}a:hover .svg-icon svg{fill:#2894df}a.disabled .svg-icon svg{fill:#888;pointer-events:none;cursor:default}a.disabled:hover .svg-icon svg{fill:#888}a.fav .svg-icon svg{fill:#d40101}a.unfav .svg-icon svg{fill:#ddd}.button .svg-icon svg{top:.125em;position:relative}.button .svg-icon+span:not(.visuallyhidden){margin-left:2px;display:inline-block}.button .svg-icon svg:hover,.button:hover .svg-icon svg{fill:#333}.button.primary:active .svg-icon svg,.button.primary:focus .svg-icon svg,.button.warning .svg-icon svg,.button.warning:active .svg-icon svg,.button.warning:focus .svg-icon svg,.button.warning:hover .svg-icon svg{fill:#fff}.button:active .svg-icon svg,.button:focus .svg-icon svg{fill:#2894df}.button.disabled .svg-icon svg{fill:#888}.button.disabled .svg-icon svg:hover{fill:#888}.tooltip,[data-tooltip]{position:relative;cursor:pointer}.tooltip:after,.tooltip:before,[data-tooltip]:after,[data-tooltip]:before{position:absolute;visibility:hidden;opacity:0;transition:opacity .2s ease-in-out,visibility .2s ease-in-out,transform .2s cubic-bezier(.71,1.7,.77,1.24);transform:translate3d(0,0,0);pointer-events:none;text-align:center}.always-show:after,.always-show:before,.tooltip:focus:after,.tooltip:focus:before,.tooltip:hover:after,.tooltip:hover:before,[data-tooltip]:focus:after,[data-tooltip]:focus:before,[data-tooltip]:hover:after,[data-tooltip]:hover:before{visibility:visible;opacity:1}.tooltip:before,[data-tooltip]:before{z-index:990;border:.6rem solid transparent;background:0 0;content:""}.tooltip:after,[data-tooltip]:after{z-index:990;padding:.4rem;width:16rem;background-color:hsla(0,0%,20%,.9);color:#fff;content:attr(data-tooltip);font-size:1.4rem;line-height:2.4rem;font-weight:400;text-shadow:none}.tooltip-top:after,.tooltip-top:before,.tooltip:after,.tooltip:before,[data-tooltip]:after,[data-tooltip]:before{bottom:100%;left:50%}.tooltip-top:before,.tooltip:before,[data-tooltip]:before{margin-left:-.6rem;margin-bottom:-1.2rem;border-top-color:hsla(0,0%,20%,.9)}.tooltip-top:after,.tooltip:after,[data-tooltip]:after{margin-left:-8rem}.tooltip-top.always-show:after,.tooltip-top.always-show:before,.tooltip-top:focus:after,.tooltip-top:focus:before,.tooltip-top:hover:after,.tooltip-top:hover:before,.tooltip.always-show:after,.tooltip.always-show:before,.tooltip:focus:after,.tooltip:focus:before,.tooltip:hover:after,.tooltip:hover:before,[data-tooltip]:focus:after,[data-tooltip]:focus:before,[data-tooltip]:hover:after,[data-tooltip]:hover:before{transform:translateY(-1.2rem)}.tooltip-left:after,.tooltip-left:before{right:100%;bottom:50%;left:auto}.tooltip-left:before{margin-left:0;margin-right:-1.2rem;margin-bottom:0;border-top-color:transparent;border-left-color:hsla(0,0%,20%,.9)}.tooltip-left.always-show:after,.tooltip-left.always-show:before,.tooltip-left:focus:after,.tooltip-left:focus:before,.tooltip-left:hover:after,.tooltip-left:hover:before{transform:translateX(-1.2rem)}.tooltip-bottom:after,.tooltip-bottom:before{top:100%;bottom:auto;left:50%}.tooltip-bottom:before{margin-top:-1.2rem;margin-bottom:0;border-top-color:transparent;border-bottom-color:hsla(0,0%,20%,.9)}.tooltip-bottom.always-show:after,.tooltip-bottom.always-show:before,.tooltip-bottom:focus:after,.tooltip-bottom:focus:before,.tooltip-bottom:hover:after,.tooltip-bottom:hover:before{transform:translateY(1.2rem)}.tooltip-right:after,.tooltip-right:before{bottom:50%;left:100%}.tooltip-right:before{margin-bottom:0;margin-left:-1.2rem;border-top-color:transparent;border-right-color:#333}.tooltip-right.always-show:after,.tooltip-right.always-show:before,.tooltip-right:focus:after,.tooltip-right:focus:before,.tooltip-right:hover:after,.tooltip-right:hover:before{transform:translateX(1.2rem)}.tooltip-left:before,.tooltip-right:before{top:.3rem}.tooltip-left:after,.tooltip-right:after{margin-left:0;margin-bottom:-1.6rem}.tooltip.large:after,[data-tooltip].large:after{width:240rem}.tooltip-left.large:after,.tooltip-left.large:before,.tooltip-right.large:after,.tooltip-right.large:before{margin-top:.6rem}.openpgp-key textarea{height:12rem}.enter-passphrase .password:after,.enter-passphrase .password:before{content:"";display:table}.enter-passphrase .password:after{clear:both}.enter-passphrase .password:after,.enter-passphrase .password:before{content:"";display:table}.enter-passphrase .password:after{clear:both}.enter-passphrase .password input[type=password],.enter-passphrase .password input[type=text]{float:left;box-sizing:border-box;width:calc(100% - 4.1rem)}.enter-passphrase .password .password-view{width:3em;float:left;margin-top:0;margin-right:0;margin-left:-.1rem;height:2.714em;border-radius:0;padding:.6rem .8rem .6rem 1rem;background:#fff;border-left:0;border-top:1px solid #bbb}.enter-passphrase .password .password-view.selected{background:#eee;box-shadow:inset 0 1px 2px rgba(0,0,0,.2)}.enter-passphrase .password .password-view svg{margin-top:3px}.enter-passphrase .password input[type=password]:focus~.password-view,.enter-passphrase .password input[type=text]:focus~.password-view{border:1px solid #2894df;border-left:0}.enter-passphrase .password.with-token input[type=password],.enter-passphrase .password.with-token input[type=text]{width:calc(100% - 11rem)}.enter-passphrase .password.with-token .password-view{margin-right:1rem}.enter-passphrase .password.with-token .security-token{width:4em;float:left;margin-top:0;margin-right:0;border-radius:0;padding:1.05rem 0;text-align:center;background:#fff;border:1px solid #bbb}.password-complexity .complexity-text{float:right;clear:right;width:30%;font-size:11px;text-align:left;padding-left:0}.password-complexity.not_available .complexity-text{color:#ddd}.password-complexity .progress{width:100%;box-sizing:border-box;border:1px solid #ddd;height:10px;display:block;clear:both;margin:.25em 0 .5em 0;float:left}.password-complexity .progress-bar{background:#000;width:0;height:6px;display:block;float:left;margin:1px}.password-complexity .progress-bar.very-weak{background:#000;width:5%}.password-complexity .progress-bar.weak{background:#d40101;width:10%}.password-complexity .progress-bar.fair{background:#ffbd2e;width:60%}.password-complexity .progress-bar.strong{background:#6c0;width:80%}.password-complexity .progress-bar.very-strong{background:#090;width:99.5%}.password-hints{margin:.5em 0 1em 0}.password-hints li{font-size:1.5rem;line-height:2.4rem}.password-hints li:before{content:"\25CF";color:#ddd;padding-right:.5em}.password-hints li.success:before{color:#090}.password-hints li.error:before{color:#d40101}.dialog-wrapper{position:absolute;width:100%;height:100%;z-index:800;background:rgba(255,255,255,.9);overflow:auto}.dialog-wrapper .placeholder{position:relative;width:1em;margin:auto;height:100%}.dialog-wrapper .placeholder .loading{position:absolute;top:30%;height:2em;width:2em;background:url(../../../img/controls/loading_light.svg);background-size:2em 2em}.dialog{position:relative;max-width:30em;border:1px solid #ddd;background:#f3f3f3;margin:auto;margin-top:1%;margin-bottom:3em;box-shadow:0 0 10px 0 #ddd}.dialog.medium{max-width:36em}.dialog .dialog-header{padding:.5em 1em 0 1em;height:3em}.dialog .dialog-header h2{margin:.25em 2em .5em .2em;font-size:1.2em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.dialog .dialog-header h2 svg{position:relative;top:.0625em}.dialog .dialog-header .tooltip-alt{margin-left:.3em;font-size:.7em;font-weight:400;color:#333}.dialog .dialog-header .tooltip-alt .tooltip-text{white-space:normal}.dialog .dialog-header .dialog-header-subtitle{padding-top:.3em;padding-left:1em;font-size:.7em;color:#333}.dialog .dialog-header .dialog-close{margin-top:-2.5em}.dialog .form-content{background:#fff;padding:.813em 1.5em 1.5em 1.5em}.dialog p{margin-top:.5em;font-size:1em}.dialog p+.checkbox{padding-bottom:.5em}.dialog p+.checkbox label{font-size:1em}.dialog label{clear:both;font-size:.9375em;line-height:1.6em}.dialog input[type=text],.dialog select.large,.dialog textarea{width:100%;box-sizing:border-box}.dialog input[type=text]:after,.dialog input[type=text]:before,.dialog select.large:after,.dialog select.large:before,.dialog textarea:after,.dialog textarea:before{content:"";display:table}.dialog input[type=text]:after,.dialog select.large:after,.dialog textarea:after{clear:both}.dialog input[type=text]:after,.dialog input[type=text]:before,.dialog select.large:after,.dialog select.large:before,.dialog textarea:after,.dialog textarea:before{content:"";display:table}.dialog input[type=text]:after,.dialog select.large:after,.dialog textarea:after{clear:both}.dialog input+.message,.dialog textarea+.message{display:none}.dialog input+.message.error,.dialog textarea+.message.error{display:block;clear:both;width:100%}.dialog .input .message.warning,.dialog input+.message.warning,.dialog textarea+.message.warning{display:block;clear:both;width:100%;color:#9f6000;background-color:transparent}.dialog .inline-error{color:#d40101;font-weight:700}.dialog .accordion-header a{display:inline}.dialog .submit-wrapper{margin:0;clear:both;width:100%;padding:.75em 0}.dialog .submit-wrapper .button,.dialog .submit-wrapper .cancel{float:right;margin-right:1.25em;font-size:1.125em}.dialog .submit-wrapper .button{box-sizing:border-box;min-width:5em}.dialog .submit-wrapper .secondary{float:left;margin-left:1.5em;margin-top:.7em;font-size:1em}.dialog .submit-wrapper .cancel{margin-top:.7em;font-size:1em}.dialog-close,.dialog-close:hover{display:block;float:right;border:0}.dialog-close:active{-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em;box-shadow:inset 0 1px 2px rgba(0,0,0,.2)}.dialog-close .fa-close{padding-top:15px;width:30px;height:15px;text-align:center;display:block;vertical-align:baseline;line-height:0;border:1px solid #eee;-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em}.dialog-close .svg-icon{padding:7px 0 7px 0;width:30px;height:15px;text-align:center;display:block;vertical-align:baseline;line-height:15px;border:1px solid #eee;-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em}.dialog-close:hover .fa-close,.dialog-close:hover .svg-icon{border:1px solid #ddd;-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em}@media all and (max-width:480px){.dialog{margin:0;border:0;box-shadow:none;width:100%;max-width:100%;margin-bottom:2.5em}}.footer{margin-top:0;position:fixed;bottom:0;padding:1.167em 1.5% 0 .5%;text-align:right;font-size:.857em;width:98%;height:2em;background:#fff;border-top:1px solid #ddd;z-index:890}.footer .footer-links{width:100%;margin-top:-.25em}.footer .footer-links li{display:inline;margin-right:1.5em}.footer .footer-links li.error-message a{background-color:#fff;color:#d40101}.footer .footer-links li .github-star{display:inline;position:absolute;margin-left:-8em;margin-top:-1px}.footer .footer-links a:not(.gh-btn):not(.gh-count){border:0}.avatar img{width:36px;height:36px;border-radius:50%}.big.avatar img{width:72px;height:72px;border-radius:50%}iframe.full-screen{position:absolute;width:100%;height:100%;z-index:999;border:0;top:0;left:0}iframe.cachette{position:absolute;width:1px;height:1px;z-index:999;border:0;bottom:0;right:0}#user-locale-input{border:none;background:transparent url(../../../img/controls/chevron-down_black.svg) right center/1rem no-repeat;appearance:none;margin-top:.4rem;padding-right:1rem}body,html{height:100%}.login.page{padding:2.4rem}.login.page h1{margin-top:0;font-size:2.4rem;color:#666}.login.page p{font-size:1.6rem}.login.page .processing-wrapper{display:block;width:10em;height:10em;margin:auto}.login.page .processing-wrapper .processing{display:block;text-align:center}.login.page .processing-wrapper .processing:after{display:block;width:10em;height:10em;content:" ";background:transparent url(../../../img/controls/loading_light.svg) center center no-repeat;background-size:auto 40%}.login.page .login-form{min-height:16rem}.login.page .login-form .form-actions{text-align:center;padding-top:1.2em}.login.page .login-form .button+a{font-size:1.6rem;line-height:2.4rem;text-align:center;display:inline-block;margin-top:1.6rem;cursor:pointer}.login.page .email-sent-instructions{text-align:center}.login.page .email-sent-instructions .email-sent-bg{background:transparent url(../../../img/illustrations/email.png) top center no-repeat;background-size:auto 90%;height:16rem}.login.page .email-sent-instructions h1{margin-top:1.5em;font-size:2.4rem}.login.page .email-sent-instructions p{padding:.5em .5em 0 .5em;margin-bottom:0}.login.page .choose-security-token .input-security-token{margin:1em 0 1.5em 0}.login.page .choose-security-token .input-security-token:after,.login.page .choose-security-token .input-security-token:before{content:"";display:table}.login.page .choose-security-token .input-security-token:after{clear:both}.login.page .choose-security-token .input-security-token:after,.login.page .choose-security-token .input-security-token:before{content:"";display:table}.login.page .choose-security-token .input-security-token:after{clear:both}.login.page .choose-security-token .input-security-token label{margin-bottom:.5em}.login.page .choose-security-token .input-security-token .input.text{-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em;font-size:3rem;max-width:10rem;float:left;text-align:center;margin-right:1em}.login.page .choose-security-token .input-security-token .circle-picker{float:left}.login.page .choose-security-token .input-security-token .randomize-button-wrapper{float:left;width:10rem;text-align:center;clear:both;cursor:pointer}.login.page .choose-security-token .input-security-token .svg-icon svg{height:.85em;width:.85em}.login.page .install-extension a.browser-webstore{border:0}.login.page .install-extension a.browser-webstore img{display:block;margin-left:auto;margin-right:auto;max-width:26rem}.login.page .install-extension a.browser-webstore.firefox img{padding:1em 0}.login.page .browser-not-supported a.browser{border:0}.login.page .browser-not-supported a.browser img{max-width:26rem;display:block;margin-left:auto;margin-right:auto}.login.page .login .login-user{width:100%;margin:auto}.login.page .login .login-user>*{text-align:center}.login.page .login .login-user .login-user-name{font-weight:700;font-size:1.6rem;line-height:1rem}.login.page .login .login-user .login-user-email{font-size:1.6rem;line-height:1rem}.login.page .login-processing{display:flex;flex-direction:column;align-items:center;justify-content:center}@media only screen and (min-width:42rem){body{background:#f3f3f3}.login.page{display:grid;height:100%;grid-template-columns:1fr 1.5fr 1fr;grid-template-rows:0 1fr .05fr;grid-gap:1px;grid-template-areas:". . ." ". login-form ." "footer footer footer"}.login.page .content{grid-area:login-form}.login.page .content .loading-bar{display:block}.login.page .content .logo{margin:1.6em auto;width:20rem;background-size:20rem auto}.login.page .content .login-form{box-shadow:0 0 10px 0 #ddd;border-radius:.3rem;max-width:37.2rem;margin:auto;padding:4.8rem 4rem 5.6rem 4rem;background:#fff}.login.page .content .input.select.locale{max-width:45.2rem;margin:auto}} \ No newline at end of file +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}div{outline:0}html body .hidden{display:none}.visually-hidden,.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visually-hidden .focusable:active,.visually-hidden .focusable:focus,.visuallyhidden .focusable:active,.visuallyhidden .focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.clearfix:after,.clearfix:before{content:"";display:table}.clearfix:after{clear:both}.rounded{-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem}.ellipsis{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.rotate-right{transform:rotate(-90deg)}html{line-height:normal}body{color:#333;background:#fff}body.iframe{background:0 0}@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;src:local('Open Sans'),local('OpenSans'),url('../../../fonts/opensans-regular.woff') format('woff')}@font-face{font-family:'Open Sans';font-style:normal;font-weight:700;src:local('Open Sans Bold'),local('OpenSans-Bold'),url('../../../fonts/opensans-bold.woff') format('woff')}body{font-family:'Open Sans',Verdana,sans-serif}.code{font-family:'Courier New',Courier,monospace}html{font-size:62.5%}body{font-size:1.6rem}h1{font-size:2.2rem;line-height:3.2rem}h2{font-size:1.8rem;line-height:2.4rem}h3{font-size:1.6rem;line-height:2.4rem;border-bottom:1px dotted #ddd}p{font-size:1.5rem;line-height:2.2rem;margin-top:0}code{font-size:1.1rem}.font-dim{color:#666}a{outline:0;text-decoration:none;cursor:pointer;border-bottom:1px solid #ddd}a:link,a:visited{color:#333}a:hover{text-decoration:none;cursor:pointer;color:#2894df;border-bottom:1px solid #2894df}a:active,a:focus{outline:0;color:#2894df;border:0}a.no-border,a:hover.no-border{border-bottom:0}a.disabled{outline:0;text-decoration:none;cursor:default;color:#888;pointer-events:none}ul{padding:0;margin:0}ul li{list-style:none;padding:0;margin:0}.button,button{font-size:1.4rem;padding:.8rem 1.6rem;line-height:1.6rem;margin-right:.8rem;position:relative;display:inline-block;vertical-align:baseline;outline:0;cursor:pointer;text-align:center;text-decoration:none;border-top:1px solid #ccc;border-right:1px solid #ccc;border-bottom:1px solid #ccc;border-left:1px solid #ccc;color:#333;font-weight:400;text-shadow:1px 1px 0 rgba(255,255,255,.5);background:#f3f3f3;-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem;box-sizing:border-box}.button.medium{font-size:1.6rem;line-height:2.4rem;padding:.8rem 2.4rem}.button.big{font-size:1.8rem;line-height:3.2rem;padding:.8rem 3.2rem}.button.full-width{width:100%}.button.primary{background:#2894df;border:1px solid #2894df;color:#fff;text-shadow:1px 1px 0 rgba(0,0,0,.2)}.button.primary:hover{background:#2a9ceb;border:1px solid #2a9ceb;color:#fff}.button.primary:active,.button.primary:focus{color:#fff;border:1px solid #4271b7;background:#2a9ceb}.button.warning,.button.warning:active,.button.warning:focus,.button.warning:hover{color:#fff;background:#d40101;border:1px solid #d40101;text-shadow:none}.button.warning:active,.button.warning:focus{border:1px solid #92000c}.button.cancel,.button.cancel:active,.button.cancel:focus,.button.cancel:hover,.button.dim,.button.dim:active,.button.dim:focus,.button.dim:hover{background:#fff;font-weight:400}.button:hover{color:#333;text-decoration:none;background:#eee;border:1px solid #bbb}.button:focus{color:#2894df;border:1px solid #2894df}.button:active{color:#2894df;border:1px solid #2894df;background:#eee;position:relative;box-shadow:inset 0 1px 2px rgba(0,0,0,.2)}.button.disabled{cursor:default;color:#888;background:#fff;border:1px solid #ddd;text-shadow:none;font-weight:400}.button.disabled:active,.button.disabled:focus,.button.disabled:hover{box-shadow:0 0 0;top:0;color:#888;background:#fff;border:1px solid #ddd}.button.processing{background:#fff;border:1px solid #fff}.button.processing:after{width:100%;height:100%;position:absolute;content:" ";top:-1px;left:-1px;background:#fff url('../../../img/controls/loading_light.svg') center center no-repeat;background-size:auto 50%;border:1px solid #ddd;-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem}input[type=submit].button.processing{font-size:0;background:#fff url('../../../img/controls/loading_light.svg') center center no-repeat;background-size:auto 50%;border:1px solid #ddd;border-radius:5px}.button-toggle.selected{background:#eee;box-shadow:inset 0 1px 2px rgba(0,0,0,.2);border:1px solid #bbb}.button-group{display:flex;flex-wrap:wrap;justify-content:flex-start}.button-group--nowrap>.button{white-space:nowrap}.button-group>*{flex-grow:1;margin-bottom:.5em}label{font-weight:700;font-size:1.6rem;line-height:2.4rem;padding:.8rem 0;display:block}.required label:after{content:" \002A";color:#d40101;font-weight:700}.input.error label{color:#d40101}input[type=email],input[type=number],input[type=password],input[type=search],input[type=text],textarea{box-sizing:border-box;font-size:1.6rem;line-height:2.4rem;color:#333;background:#fff;width:100%;max-width:64rem;padding:.6rem 1.2rem;display:inline-block;margin-bottom:.8rem;border:1px solid #ccc;border-top:1px solid #bbb;vertical-align:middle}::-ms-reveal{display:none}input[type=number]{box-sizing:border-box}input[type=file]{box-sizing:border-box;font-size:1.6rem;line-height:2.4rem;color:#333;background:#fff;width:100%;max-width:64rem;display:inline-block;margin-bottom:.8rem;vertical-align:middle}input[type=email]:hover,input[type=number]:hover,input[type=password]:hover,input[type=search]:hover,input[type=text]:hover,textarea:hover{border:1px solid #bbb}input[type=email]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=text]:focus,textarea:focus{outline:0;box-shadow:inset 1px 1px 2px rgba(0,0,0,.2);border:1px solid #2894df}input[type=email]:disabled,input[type=number]:disabled,input[type=password]:disabled,input[type=search]:disabled,input[type=text]:disabled,textarea:disabled{color:#333;border:1px solid #ddd;box-shadow:0 0;cursor:default;background:#f3f3f3}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{opacity:1}textarea{height:9rem}.textarea.large textarea{width:30rem;height:9rem}.checkbox input[type=checkbox]{position:absolute;opacity:0;cursor:pointer}.checkbox input[type=checkbox].error{color:#d40101}.checkbox input[type=checkbox]+label{font-weight:400;position:relative;cursor:pointer;padding:0;margin-bottom:.8rem}.checkbox input[type=checkbox]+label:before{content:'';margin-top:.4rem;margin-right:1rem;display:inline-block;vertical-align:text-top;width:1.4rem;height:1.4rem;background:#fff;border:1px solid #ddd;border-radius:.3rem;box-sizing:border-box}.checkbox input[type=checkbox]:hover+label:before{box-shadow:0 .1rem 0 #ddd,inset 0 .1rem 0 rgba(255,255,255,.25)}.checkbox input[type=checkbox]:focus+label:before{box-shadow:0 0 .4rem #2a9ceb;border:1px solid #2a9ceb}.checkbox input[type=checkbox]:active+label:before{box-shadow:inset 0 -.1rem 0 rgba(255,255,255,.25),inset 0 .1rem 0 #ddd}.checkbox input[type=checkbox]:disabled+label{color:#ddd;cursor:auto}.checkbox input[type=checkbox]:disabled+label:before{box-shadow:none;background:#ccc;border:none}.checkbox input[type=checkbox]:checked+label:after{content:'';position:absolute;left:.2rem;top:.75rem;width:1rem;height:1rem;background-size:1rem 1rem;background-image:url('../../../img/controls/check_black.svg');-webkit-mask-image:url('../../../img/controls/check_black.svg');mask-image:url('../../../img/controls/check_black.svg')}.checkbox input[type=checkbox]:disabled+label:after{background:#969696}.checkbox.medium input[type=checkbox]{cursor:pointer}.checkbox.medium input[type=checkbox]+label{font-size:1.5rem}.checkbox.medium input[type=checkbox]+label:before{margin-top:.3rem;margin-right:1rem;width:1.4rem;height:1.4rem}.checkbox.medium input[type=checkbox]:checked+label:after{left:.2rem;top:.7rem;width:1rem;height:1rem;background-size:1rem 1rem}.checkbox.small input[type=checkbox]{cursor:pointer}.checkbox.small input[type=checkbox]+label{font-size:1.4rem}.checkbox.small input[type=checkbox]+label:before{margin-top:.3rem;margin-right:1rem;width:1.4rem;height:1.4rem}.checkbox.small input[type=checkbox]:checked+label:after{left:.2rem;top:.7rem;width:1rem;height:1rem;background-size:1rem 1rem}.radiolist{margin-bottom:.8rem}.radiolist:after,.radiolist:before{content:"";display:table}.radiolist:after{clear:both}.radiolist:after,.radiolist:before{content:"";display:table}.radiolist:after{clear:both}.radiolist .input.radio{float:left}.radiolist .input.radio input{float:left;margin-top:.8rem}.radiolist .input.radio input+label{float:left;font-weight:400;display:inline-block;margin-left:1rem;font-size:1.6rem;margin-right:3.2rem;line-height:1.4rem}.radiolist .input.radio input+label:after{content:initial}.input.toggle-switch{display:flex;padding-top:1.6rem;padding-bottom:.8rem}.input.toggle-switch label{padding-top:0;padding-left:1.6rem;order:2;flex:1;font-weight:400;font-size:1.6rem}.input.toggle-switch.disabled label{color:#969696}.input.toggle-switch .toggle-switch-checkbox{order:-1;flex:0 0 3em;display:none}.input.toggle-switch .toggle-switch-checkbox,.input.toggle-switch .toggle-switch-checkbox *,.input.toggle-switch .toggle-switch-checkbox :after,.input.toggle-switch .toggle-switch-checkbox :before,.input.toggle-switch .toggle-switch-checkbox+.toggle-switch-button,.input.toggle-switch .toggle-switch-checkbox:after,.input.toggle-switch .toggle-switch-checkbox:before{box-sizing:border-box}.input.toggle-switch .toggle-switch-checkbox ::selection,.input.toggle-switch .toggle-switch-checkbox :after::selection,.input.toggle-switch .toggle-switch-checkbox :before::selection,.input.toggle-switch .toggle-switch-checkbox+.toggle-switch-button::selection,.input.toggle-switch .toggle-switch-checkbox::selection,.input.toggle-switch .toggle-switch-checkbox:after::selection,.input.toggle-switch .toggle-switch-checkbox:before::selection{background:0 0}.input.toggle-switch .toggle-switch-button{order:-1;flex:none;outline:0;display:block;width:3em;height:1.5em;position:relative;cursor:pointer;user-select:none;background:#f3f3f3;border-radius:2em;padding:2px;transition:all .4s ease;border:1px solid #ddd}.input.toggle-switch .toggle-switch-button span{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.input.toggle-switch .toggle-switch-button span .focusable:active,.input.toggle-switch .toggle-switch-button span .focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.input.toggle-switch .toggle-switch-button span .focusable:active,.input.toggle-switch .toggle-switch-button span .focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.input.toggle-switch .toggle-switch-button:hover:after{will-change:padding}.input.toggle-switch .toggle-switch-button:active{box-shadow:inset 0 0 0 2em #e8eae9}.input.toggle-switch .toggle-switch-button:active:after{padding-right:.8em}.input.toggle-switch .toggle-switch-button:after,.input.toggle-switch .toggle-switch-button:before{position:relative;display:block;content:"";width:50%;height:100%}.input.toggle-switch .toggle-switch-button:after{left:0;border-radius:2em;background:#fff;transition:left .3s cubic-bezier(.175, .885, .32, 1.275),padding .3s ease,margin .3s ease;box-shadow:0 0 0 1px rgba(0,0,0,.1),0 4px 0 rgba(0,0,0,.08)}.input.toggle-switch .toggle-switch-button:before{display:none}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button:after{left:50%}.input.toggle-switch .toggle-switch-checkbox:disabled+.toggle-switch-button{background:#fff;cursor:not-allowed}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button{background:#6c0}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button:active{box-shadow:none}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button:active:after{margin-left:-.8em}.input.toggle-switch .toggle-switch-checkbox:disabled:checked+.toggle-switch-button{background:#e6ffcc}h1 .input.toggle-switch,h2 .input.toggle-switch,h3 .input.toggle-switch,h4 .input.toggle-switch,h5 .input.toggle-switch,h6 .input.toggle-switch{float:left;padding:.4em 1em 0 0}.input.select select{display:inline-block;font-size:1.6rem;line-height:2.4rem;color:#333;padding:.6rem 1.2rem;width:100%;max-width:64rem;box-sizing:border-box;margin:0 0 .8rem 0;border:1px solid #ccc;-moz-appearance:none;-webkit-appearance:none;appearance:none;background-color:#fff;background-image:url('../../../img/controls/chevron-down_black.svg'),linear-gradient(to bottom,#fff 0,#fff 100%);background-repeat:no-repeat,repeat;background-position:right .6rem top 50%,0 0;background-size:1rem auto,100%}.input.select select.medium{width:50%}.input.select select:focus{outline:0;border:1px solid #2894df;border-radius:0;background-image:url('../../../img/controls/chevron-down_blue.svg'),linear-gradient(to bottom,#fff 0,#fff 100%)}.input.select select:disabled{background-image:url('../../../img/controls/chevron-down_black.svg'),linear-gradient(to bottom,#f3f3f3 0,#f3f3f3 100%);color:#888}.input .error-message{padding:0;font-size:1.4rem;margin-top:.2rem;margin-bottom:1.6rem;background-color:transparent;border:0;font-weight:400;color:#d40101;background:#fff;clear:both}.input .help-message{padding:0;font-size:1.4rem;margin-top:.2rem;margin-bottom:1.6rem;background-color:transparent;border:0;color:#888;font-weight:400;background:#fff;clear:both}.singleline{display:flex;max-width:64rem}.singleline:after,.singleline:before{content:"";display:table}.singleline:after{clear:both}.singleline:after,.singleline:before{content:"";display:table}.singleline:after{clear:both}.singleline .input{flex:1}.singleline .input.first-field,.singleline .input:first-child{margin-right:2%}.slider{display:flex;align-items:center}.slider input[type=range]{-webkit-appearance:none;width:100%;height:1px;border-radius:5px;background:#ddd;outline:0;opacity:.7;-webkit-transition:.2s;transition:opacity .2s;flex-grow:1}.slider input[type=range]:hover{opacity:1}.slider input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:15px;height:15px;border-radius:50%;border-width:1px;border-style:solid;border-color:#969696;background:#ddd;cursor:pointer}.slider input[type=range]::-moz-range-thumb{width:15px;height:15px;border-radius:50%;background:#ddd;cursor:pointer}.slider input[type=number]{width:6.5rem;margin-left:1.6rem;padding:.8rem}.svg-icon{display:inline-flex;align-self:center}.svg-icon svg{fill:#333;height:1em;width:1em}.svg-icon.baseline svg{top:.125em;position:relative}.svg-icon.dim svg,.svg-icon.light svg{fill:#888888}.svg-icon.icon-only svg{padding:.1rem;height:1.6rem;width:1.6rem}a:hover .svg-icon svg{fill:#2894DF}a.disabled .svg-icon svg{fill:#888888;pointer-events:none;cursor:default}a.disabled:hover .svg-icon svg{fill:#888888}a.fav .svg-icon svg{fill:#D40101}a.unfav .svg-icon svg{fill:#DDD}.button .svg-icon svg{top:.2rem;position:relative}.button .svg-icon+span:not(.visuallyhidden){margin-left:.8rem;display:inline-block}.button .svg-icon svg:hover,.button:hover .svg-icon svg{fill:#333}.button.primary:active .svg-icon svg,.button.primary:focus .svg-icon svg,.button.warning .svg-icon svg,.button.warning:active .svg-icon svg,.button.warning:focus .svg-icon svg,.button.warning:hover .svg-icon svg{fill:#FFF}.button:active .svg-icon svg,.button:focus .svg-icon svg{fill:#2894DF}.button.disabled .svg-icon svg{fill:#888888}.button.disabled .svg-icon svg:hover{fill:#888888}@keyframes drawCircle{0%{stroke-dashoffset:180px}100%{stroke-dashoffset:0}}@keyframes drawCheck{0%{stroke-dashoffset:50px}100%{stroke-dashoffset:0}}@keyframes drawCross{0%{stroke-dashoffset:50px}100%{stroke-dashoffset:0}}@keyframes drawWarning{0%{stroke-dashoffset:230px}100%{stroke-dashoffset:0}}.icon-feedback .success-animation-circle{stroke-dasharray:180px 180px;stroke:#009900}.icon-feedback .success-animation-line{stroke-dasharray:50px 50px;stroke:#009900}.icon-feedback .success-animation-line2{stroke-dasharray:50px 50px;stroke:#009900}.icon-feedback .error-animation-circle{stroke-dasharray:180px 180px;stroke:#D40101}.icon-feedback .error-animation-line{stroke-dasharray:50px 50px;stroke:#D40101}.icon-feedback .warning-animation-line{stroke-dasharray:230px 230px;stroke:#9F6000;stroke-linecap:round;stroke-linejoin:round}.icon-feedback .warning-animation-circle{fill:#9F6000}.icon-feedback .animated{animation:.75s ease-out 0s 1 both pop}.icon-feedback .animated .error-animation-circle,.icon-feedback .animated .success-animation-circle,.icon-feedback .animated .warning-animation-circle{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawCircle}.icon-feedback .animated .success-animation-line{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawCheck}.icon-feedback .animated .error-animation-line{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawCross}.icon-feedback .animated .warning-animation-line{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawWarning}table{border-collapse:collapse;border-spacing:0}table td,table th{text-align:left;font-weight:400}.logo{background:transparent url('../../../img/logo/logo.svg') 0 0 no-repeat;background-size:20rem auto;width:20rem;height:4.5rem}.scroll{overflow-y:scroll;background:linear-gradient(#fff 30%,rgba(255,255,255,0)),linear-gradient(rgba(255,255,255,0),#fff 70%) 0 100%,radial-gradient(farthest-side at 75% 0,rgba(0,0,0,.2),rgba(0,0,0,0)),radial-gradient(farthest-side at 75% 100%,rgba(0,0,0,.2),rgba(0,0,0,0)) 0 100%;background-repeat:no-repeat;background-color:#fff;background-size:100% 10px,100% 10px,100% 5px,100% 5px}::-webkit-scrollbar{width:1em}::-webkit-scrollbar-track{background:#f9f9f9;border-top:0;border-bottom:0}::-webkit-scrollbar-thumb{background:#c1c1c1;border-radius:1em;border:4px solid #f9f9f9}.scroll-shadow{overflow:auto;background:linear-gradient(#fff 30%,rgba(255,255,255,0)),linear-gradient(rgba(255,255,255,0),#fff 70%) 0 100%,radial-gradient(farthest-side at 50% 0,rgba(0,0,0,.08),rgba(0,0,0,0)),radial-gradient(farthest-side at 50% 100%,rgba(0,0,0,.08),rgba(0,0,0,0)) 0 100%;background-repeat:no-repeat;background-color:#fff;background-size:100% 40px,100% 40px,100% 14px,100% 14px;background-attachment:local,local,scroll,scroll}.simplebar-content{overflow:auto;background:linear-gradient(#fff 30%,rgba(255,255,255,0)),linear-gradient(rgba(255,255,255,0),#fff 70%) 0 100%,radial-gradient(farthest-side at 50% 0,rgba(0,0,0,.08),rgba(0,0,0,0)),radial-gradient(farthest-side at 50% 100%,rgba(0,0,0,.08),rgba(0,0,0,0)) 0 100%;background-repeat:no-repeat;background-color:#fff;background-size:100% 40px,100% 40px,100% 14px,100% 14px;background-attachment:local,local,scroll,scroll}[data-simplebar]{position:relative;flex-direction:column;flex-wrap:wrap;justify-content:flex-start;align-content:flex-start;align-items:flex-start;width:inherit;height:inherit;max-width:inherit;max-height:inherit}.simplebar-wrapper{overflow:hidden;width:inherit;height:inherit;max-width:inherit;max-height:inherit}.simplebar-mask{direction:inherit;position:absolute;overflow:hidden;padding:0;margin:0;left:0;top:0;bottom:0;right:0;width:auto!important;height:auto!important;z-index:0}.simplebar-offset{direction:inherit!important;box-sizing:inherit!important;resize:none!important;position:absolute;top:0;left:0;bottom:0;right:0;padding:0;margin:0;-webkit-overflow-scrolling:touch}.simplebar-content-wrapper{direction:inherit;box-sizing:border-box!important;position:relative;display:block;height:100%;width:auto;max-width:100%;max-height:100%;scrollbar-width:none;-ms-overflow-style:none}.simplebar-content-wrapper::-webkit-scrollbar,.simplebar-hide-scrollbar::-webkit-scrollbar{width:0;height:0}.simplebar-content:after,.simplebar-content:before{content:' ';display:table}.simplebar-placeholder{max-height:100%;max-width:100%;width:100%;pointer-events:none}.simplebar-height-auto-observer-wrapper{box-sizing:inherit!important;height:100%;width:inherit;max-width:1px;position:relative;float:left;max-height:1px;overflow:hidden;z-index:-1;padding:0;margin:0;pointer-events:none;flex-grow:inherit;flex-shrink:0;flex-basis:0}.simplebar-height-auto-observer{box-sizing:inherit;display:block;opacity:0;position:absolute;top:0;left:0;height:1000%;width:1000%;min-height:1px;min-width:1px;overflow:hidden;pointer-events:none;z-index:-1}.simplebar-track{z-index:1;position:absolute;right:0;bottom:0;pointer-events:none;overflow:hidden}[data-simplebar].simplebar-dragging .simplebar-content{pointer-events:none;user-select:none;-webkit-user-select:none}[data-simplebar].simplebar-dragging .simplebar-track{pointer-events:all}.simplebar-scrollbar{position:absolute;right:2px;width:7px;min-height:10px}.simplebar-scrollbar:before{position:absolute;content:'';background:#000;border-radius:7px;left:0;right:0;opacity:0;transition:opacity .2s linear}.simplebar-scrollbar.simplebar-visible:before{opacity:.5;transition:opacity 0s linear}.simplebar-track.simplebar-vertical{top:0;width:11px}.simplebar-track.simplebar-vertical .simplebar-scrollbar:before{top:2px;bottom:2px}.simplebar-track.simplebar-horizontal{left:0;height:11px}.simplebar-track.simplebar-horizontal .simplebar-scrollbar:before{height:100%;left:2px;right:2px}.simplebar-track.simplebar-horizontal .simplebar-scrollbar{right:auto;left:0;top:2px;height:7px;min-height:0;min-width:10px;width:auto}[data-simplebar-direction=rtl] .simplebar-track.simplebar-vertical{right:auto;left:0}.hs-dummy-scrollbar-size{direction:rtl;position:fixed;opacity:0;visibility:hidden;height:500px;width:500px;overflow-y:hidden;overflow-x:scroll}.simplebar-hide-scrollbar{position:fixed;left:0;visibility:hidden;overflow-y:scroll;scrollbar-width:none;-ms-overflow-style:none}.shimmer{display:block;position:relative;width:100%;height:100%;content:'';animation:shimmer 2s infinite;background:linear-gradient(45deg,rgba(255,255,255,0) 0,rgba(255,255,255,.1) 30%,rgba(255,255,255,.5) 50%,rgba(255,255,255,0))}@keyframes shimmer{0%{background-position:0 0}100%{background-position:1000px 0}}html{scroll-behavior:smooth}.animated{-webkit-animation-duration:.5s;animation-duration:.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both}@keyframes fadeInUp{0%{opacity:0;-webkit-transform:translateY(20px);-ms-transform:translateY(20px);transform:translateY(20px)}100%{opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}}@keyframes fadeOutUp{from{opacity:1}to{opacity:0;transform:translate3d(0,-100%,0)}}@keyframes fadeInDown{from{opacity:0;transform:translate3d(0,-100%,0)}to{opacity:1;transform:none}}@keyframes pop{0%{opacity:0;transform:scale(1)}55%{opacity:1;transform:scale(1)}65%{opacity:1;transform:scale(1.25)}75%{opacity:1;transform:scale(1)}100%{opacity:1;transform:scale(1)}}.fadeInDown{animation-name:fadeInDown}.fadeOutUp{animation-name:fadeOutUp}.fadeInUp{animation-name:fadeInUp}.pop{animation-name:pop}.page{width:100%;min-width:32rem;top:0;left:0;right:0;bottom:0;position:absolute;overflow:auto}.page .panel{height:100%;width:100%;position:absolute;overflow:auto}.page .panel.main{bottom:3.8rem;height:auto;padding:0}.page .panel.main .grid{padding-bottom:1.6rem}.page .panel.main .row{padding-left:0}.page .panel.main .panel.left{background:#fff}.page .header.second+.panel.main{top:6.875em}.page .header.third+.panel.main{top:10em;border-top:1px solid #ddd}.page .header{width:100%;overflow:hidden}.page .header.first{min-height:3.2rem}.page .header.second{height:7rem}.page .header.third{height:4.8rem}.page .col1,.page .col2,.page .col2_3,.page .col3{position:absolute}.page .col1,.page .panel.left{width:17.25%;box-sizing:border-box}.page .panel.middle{width:82%;left:18%;overflow:hidden}.page .panel.middle.scroll{overflow-y:scroll}.page .col2{width:70%;left:18%}.page .col3,.page .panel.right{width:24%;left:75%}.page .col2_3{width:81.5%;left:18%}.page .panel.main .grid-responsive-12{float:left;width:100%;max-width:none;padding-left:.25em;box-sizing:border-box}@media all and (max-width:1024px){.page .panel.left{display:none}.page .header.second .col2{display:none}.page .header.second .col2_3{display:none}.page .header.third{height:4.5em}.page .header.third .col1,.page .header.third .col2,.page .header.third .col2_3{position:relative;float:left;left:auto;width:auto}.page .header.third .col3{display:none}.page .panel.main .panel.left{display:none}.page .panel.main .panel.middle{width:100%;left:0}.page .panel.main .row{padding-left:.5em}.page .panel.main .panel.aside{min-width:auto}}.grid,.grid-responsive-12{margin:0 auto;padding:0;max-width:1220px}.grid .row,.grid-responsive-12 .row{padding:0 .5em 0 .5em}.grid .row:after,.grid .row:before,.grid-responsive-12 .row:after,.grid-responsive-12 .row:before{content:"";display:table}.grid .row:after,.grid-responsive-12 .row:after{clear:both}.grid .row:after,.grid .row:before,.grid-responsive-12 .row:after,.grid-responsive-12 .row:before{content:"";display:table}.grid .row:after,.grid-responsive-12 .row:after{clear:both}.grid .row .col1,.grid .row .col10,.grid .row .col11,.grid .row .col12,.grid .row .col2,.grid .row .col3,.grid .row .col4,.grid .row .col5,.grid .row .col6,.grid .row .col7,.grid .row .col8,.grid .row .col9,.grid-responsive-12 .row .col1,.grid-responsive-12 .row .col10,.grid-responsive-12 .row .col11,.grid-responsive-12 .row .col12,.grid-responsive-12 .row .col2,.grid-responsive-12 .row .col3,.grid-responsive-12 .row .col4,.grid-responsive-12 .row .col5,.grid-responsive-12 .row .col6,.grid-responsive-12 .row .col7,.grid-responsive-12 .row .col8,.grid-responsive-12 .row .col9{position:relative;left:auto;float:none;width:99%;box-sizing:border-box}.grid .row .col10:after,.grid .row .col10:before,.grid .row .col11:after,.grid .row .col11:before,.grid .row .col12:after,.grid .row .col12:before,.grid .row .col1:after,.grid .row .col1:before,.grid .row .col2:after,.grid .row .col2:before,.grid .row .col3:after,.grid .row .col3:before,.grid .row .col4:after,.grid .row .col4:before,.grid .row .col5:after,.grid .row .col5:before,.grid .row .col6:after,.grid .row .col6:before,.grid .row .col7:after,.grid .row .col7:before,.grid .row .col8:after,.grid .row .col8:before,.grid .row .col9:after,.grid .row .col9:before,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col10:before,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col11:before,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col12:before,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col1:before,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col2:before,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col3:before,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col4:before,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col5:before,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col6:before,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col7:before,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col8:before,.grid-responsive-12 .row .col9:after,.grid-responsive-12 .row .col9:before{content:"";display:table}.grid .row .col10:after,.grid .row .col11:after,.grid .row .col12:after,.grid .row .col1:after,.grid .row .col2:after,.grid .row .col3:after,.grid .row .col4:after,.grid .row .col5:after,.grid .row .col6:after,.grid .row .col7:after,.grid .row .col8:after,.grid .row .col9:after,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col9:after{clear:both}.grid .row .col10:after,.grid .row .col10:before,.grid .row .col11:after,.grid .row .col11:before,.grid .row .col12:after,.grid .row .col12:before,.grid .row .col1:after,.grid .row .col1:before,.grid .row .col2:after,.grid .row .col2:before,.grid .row .col3:after,.grid .row .col3:before,.grid .row .col4:after,.grid .row .col4:before,.grid .row .col5:after,.grid .row .col5:before,.grid .row .col6:after,.grid .row .col6:before,.grid .row .col7:after,.grid .row .col7:before,.grid .row .col8:after,.grid .row .col8:before,.grid .row .col9:after,.grid .row .col9:before,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col10:before,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col11:before,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col12:before,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col1:before,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col2:before,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col3:before,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col4:before,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col5:before,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col6:before,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col7:before,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col8:before,.grid-responsive-12 .row .col9:after,.grid-responsive-12 .row .col9:before{content:"";display:table}.grid .row .col10:after,.grid .row .col11:after,.grid .row .col12:after,.grid .row .col1:after,.grid .row .col2:after,.grid .row .col3:after,.grid .row .col4:after,.grid .row .col5:after,.grid .row .col6:after,.grid .row .col7:after,.grid .row .col8:after,.grid .row .col9:after,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col9:after{clear:both}.grid .row .col1 img,.grid .row .col10 img,.grid .row .col11 img,.grid .row .col12 img,.grid .row .col2 img,.grid .row .col3 img,.grid .row .col4 img,.grid .row .col5 img,.grid .row .col6 img,.grid .row .col7 img,.grid .row .col8 img,.grid .row .col9 img,.grid-responsive-12 .row .col1 img,.grid-responsive-12 .row .col10 img,.grid-responsive-12 .row .col11 img,.grid-responsive-12 .row .col12 img,.grid-responsive-12 .row .col2 img,.grid-responsive-12 .row .col3 img,.grid-responsive-12 .row .col4 img,.grid-responsive-12 .row .col5 img,.grid-responsive-12 .row .col6 img,.grid-responsive-12 .row .col7 img,.grid-responsive-12 .row .col8 img,.grid-responsive-12 .row .col9 img{width:100%;height:auto;display:block}@media all and (min-width:780px){.grid .row{padding:0 .5em 0 .5em}.grid .row .col1,.grid .row .col10,.grid .row .col11,.grid .row .col12,.grid .row .col2,.grid .row .col3,.grid .row .col4,.grid .row .col5,.grid .row .col6,.grid .row .col7,.grid .row .col8,.grid .row .col9{float:left;position:relative;margin:0 3% 0 0}.grid .row .last{margin-right:0}.grid .row .col1{width:5.5%}.grid .row .col2{width:14%}.grid .row .col3{width:22.5%}.grid .row .col4{width:31%}.grid .row .col5{width:39.5%}.grid .row .col6{width:48%}.grid .row .col7{width:56.5%}.grid .row .col8{width:65%}.grid .row .col9{width:73.5%}.grid .row .col10{width:82%}.grid .row .col11{width:90.5%}.grid .row .col12{width:99%;margin:0}.grid .row .push1{margin-left:5.5%}.grid .row .push2{margin-left:14%}.grid .row .push3{margin-left:22.5%}.grid .row .push4{margin-left:31%}}@media all and (max-width:768px){.hidden-xs{display:none}}.tooltip,[data-tooltip]{position:relative;cursor:pointer}.tooltip:after,.tooltip:before,[data-tooltip]:after,[data-tooltip]:before{position:absolute;visibility:hidden;opacity:0;transition:opacity .2s ease-in-out,visibility .2s ease-in-out,transform .2s cubic-bezier(.71, 1.7, .77, 1.24);transform:translate3d(0,0,0);pointer-events:none;text-align:center}.always-show:after,.always-show:before,.tooltip:focus:after,.tooltip:focus:before,.tooltip:hover:after,.tooltip:hover:before,[data-tooltip]:focus:after,[data-tooltip]:focus:before,[data-tooltip]:hover:after,[data-tooltip]:hover:before{visibility:visible;opacity:1}.tooltip:before,[data-tooltip]:before{z-index:990;border:.6rem solid transparent;background:0 0;content:""}.tooltip:after,[data-tooltip]:after{z-index:990;padding:.4rem;width:16rem;background-color:hsla(0,0%,0%,.9);color:#fff;content:attr(data-tooltip);font-size:1.4rem;line-height:2.4rem;font-weight:400;text-shadow:none}.tooltip-top:after,.tooltip-top:before,.tooltip:after,.tooltip:before,[data-tooltip]:after,[data-tooltip]:before{bottom:100%;left:50%}.tooltip-top:before,.tooltip:before,[data-tooltip]:before{margin-left:-.6rem;margin-bottom:-1.2rem;border-top-color:hsla(0,0%,20%,.9)}.tooltip-top:after,.tooltip:after,[data-tooltip]:after{margin-left:-8rem}.tooltip-top.always-show:after,.tooltip-top.always-show:before,.tooltip-top:focus:after,.tooltip-top:focus:before,.tooltip-top:hover:after,.tooltip-top:hover:before,.tooltip.always-show:after,.tooltip.always-show:before,.tooltip:focus:after,.tooltip:focus:before,.tooltip:hover:after,.tooltip:hover:before,[data-tooltip]:focus:after,[data-tooltip]:focus:before,[data-tooltip]:hover:after,[data-tooltip]:hover:before{transform:translateY(-1.2rem)}.tooltip-left:after,.tooltip-left:before{right:100%;bottom:50%;left:auto}.tooltip-left:before{margin-left:0;margin-right:-1.2rem;margin-bottom:0;border-top-color:transparent;border-left-color:hsla(0,0%,20%,.9)}.tooltip-left.always-show:after,.tooltip-left.always-show:before,.tooltip-left:focus:after,.tooltip-left:focus:before,.tooltip-left:hover:after,.tooltip-left:hover:before{transform:translateX(-1.2rem)}.tooltip-bottom:after,.tooltip-bottom:before{top:100%;bottom:auto;left:50%}.tooltip-bottom:before{margin-top:-1.2rem;margin-bottom:0;border-top-color:transparent;border-bottom-color:hsla(0,0%,20%,.9)}.tooltip-bottom.always-show:after,.tooltip-bottom.always-show:before,.tooltip-bottom:focus:after,.tooltip-bottom:focus:before,.tooltip-bottom:hover:after,.tooltip-bottom:hover:before{transform:translateY(1.2rem)}.tooltip-right:after,.tooltip-right:before{bottom:50%;left:100%}.tooltip-right:before{margin-bottom:0;margin-left:-1.2rem;border-top-color:transparent;border-right-color:#333}.tooltip-right.always-show:after,.tooltip-right.always-show:before,.tooltip-right:focus:after,.tooltip-right:focus:before,.tooltip-right:hover:after,.tooltip-right:hover:before{transform:translateX(1.2rem)}.tooltip-left:before,.tooltip-right:before{top:.3rem}.tooltip-left:after,.tooltip-right:after{margin-left:0;margin-bottom:-1.6rem}.tooltip.large:after,[data-tooltip].large:after{width:240rem}.tooltip-left.large:after,.tooltip-left.large:before,.tooltip-right.large:after,.tooltip-right.large:before{margin-top:.6rem}.tooltip-alt{cursor:pointer;display:inline-block;border-bottom:0}.tooltip-alt .tooltip-text{position:absolute;visibility:hidden;width:180px;background-color:#333;color:#fff;padding:.5em 1em;transition:opacity .2s ease-in-out,visibility .2s ease-in-out,transform .2s cubic-bezier(.71, 1.7, .77, 1.24);transform:translate3d(0,0,0);z-index:99999}.tooltip-alt .tooltip-text.right{margin-left:1.2rem;margin-top:-2.65rem}.tooltip-alt .tooltip-text.right::after{content:" ";position:absolute;top:1.125em;right:100%;margin-top:-5px;border:5px #333 solid;border-color:transparent #333 transparent transparent}.tooltip-alt:hover .tooltip-text{transform:translateX(12px);visibility:visible;opacity:1}.tooltip svg,.tooltip-alt svg{fill:#666;top:.125em;position:relative}.openpgp-key textarea{height:12rem}.enter-passphrase .password:after,.enter-passphrase .password:before{content:"";display:table}.enter-passphrase .password:after{clear:both}.enter-passphrase .password:after,.enter-passphrase .password:before{content:"";display:table}.enter-passphrase .password:after{clear:both}.enter-passphrase .password input[type=password],.enter-passphrase .password input[type=text]{float:left;box-sizing:border-box;width:calc(100% - 4.2rem)}.enter-passphrase .password .password-view{width:4.2rem;float:left;margin-top:0;margin-right:0;margin-left:-.1rem;height:3.8rem;border-radius:0;padding:.6rem .8rem .6rem 1rem;background:#fff;border-left:0;border-top:1px solid #bbb}.enter-passphrase .password .password-view.selected{background:#eee;box-shadow:inset 0 1px 2px rgba(0,0,0,.2)}.enter-passphrase .password .password-view svg{margin-top:.2rem}.enter-passphrase .password input[type=password]:focus~.password-view,.enter-passphrase .password input[type=text]:focus~.password-view{border:1px solid #2894df;border-left:0}.enter-passphrase .password.with-token input[type=password],.enter-passphrase .password.with-token input[type=text]{width:calc(100% - 11.7rem)}.enter-passphrase .password.with-token .password-view{margin-right:.8rem}.enter-passphrase .password.with-token .security-token{width:6rem;float:left;margin-top:0;margin-right:0;border-radius:0;padding:.7rem 0;text-align:center;background:#fff;border:1px solid #bbb}.password-complexity .complexity-text{float:right;clear:right;width:30%;font-size:1rem;text-align:left;padding-left:0}.password-complexity.not_available .complexity-text{color:#ddd}.password-complexity .progress{width:100%;box-sizing:border-box;border:1px solid #ddd;height:10px;display:block;clear:both;margin:.25em 0 .5em 0;float:left}.password-complexity .progress-bar{background:#000;width:0;height:6px;display:block;float:left;margin:1px}.password-complexity .progress-bar.very-weak{background:#000;width:5%}.password-complexity .progress-bar.weak{background:#d40101;width:10%}.password-complexity .progress-bar.fair{background:#ffbd2e;width:60%}.password-complexity .progress-bar.strong{background:#6c0;width:80%}.password-complexity .progress-bar.very-strong{background:#090;width:99.5%}.password-hints{margin:.5em 0 1em 0}.password-hints li{font-size:1.5rem;line-height:2.4rem}.password-hints li:before{content:"\25CF";color:#ddd;padding-right:.5em}.password-hints li.success:before{color:#090}.password-hints li.error:before{color:#d40101}.dialog-wrapper{position:absolute;width:100%;height:100%;z-index:800;background:rgba(255,255,255,.9);overflow:auto}.dialog{position:relative;max-width:48rem;border:1px solid #ddd;background:#f3f3f3;margin:auto;margin-top:1%;margin-bottom:4.8rem;box-shadow:0 0 10px 0 #ddd}.dialog .dialog-header{padding:.8rem 1.6rem 0 1.6rem;height:4.8rem}.dialog .dialog-header h2{margin:.8rem 0 1rem .4rem;font-size:1.8rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.dialog .dialog-header .dialog-header-subtitle{padding-top:.4rem;padding-left:.8rem;font-size:1.4rem;color:#333}.dialog .dialog-header .tooltip-alt{margin-left:.8rem;font-size:1.2rem;font-weight:400;color:#333;line-height:1em}.dialog .dialog-header .tooltip-alt .tooltip-text{white-space:normal}.dialog .dialog-header .dialog-close{margin-top:-3.6rem}.dialog .form-content{background:#fff;padding:1rem 2rem 2.4rem 2rem}.dialog .error-message{margin-bottom:1rem}.dialog p{margin-top:.8rem;font-size:1.5rem}.dialog p+.checkbox{padding-bottom:.8rem}.dialog p+.checkbox label{font-size:1.6rem}.dialog label{clear:both;font-size:1.5rem}.dialog input[type=text],.dialog textarea{width:100%;box-sizing:border-box}.dialog input[type=text]:after,.dialog input[type=text]:before,.dialog textarea:after,.dialog textarea:before{content:"";display:table}.dialog input[type=text]:after,.dialog textarea:after{clear:both}.dialog input[type=text]:after,.dialog input[type=text]:before,.dialog textarea:after,.dialog textarea:before{content:"";display:table}.dialog input[type=text]:after,.dialog textarea:after{clear:both}.dialog input+.message,.dialog textarea+.message{display:none}.dialog input+.message.error,.dialog textarea+.message.error{display:block;clear:both;width:100%}.dialog .inline-error{color:#d40101;font-weight:700}.dialog .accordion-header a{display:inline}.dialog .submit-wrapper{margin:0;clear:both;width:100%;padding:1.2rem 0}.dialog .submit-wrapper .button,.dialog .submit-wrapper .cancel{float:right;margin-right:1.6rem}.dialog .submit-wrapper .button{box-sizing:border-box;min-width:8rem}.dialog .submit-wrapper .primary{font-size:1.8rem;padding:1.2rem 2.4rem}.dialog .submit-wrapper .cancel{margin-top:.7em;font-size:1.6rem}.dialog-close,.dialog-close:hover{display:block;float:right;border:0}.dialog-close:active{-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem;box-shadow:inset 0 1px 2px rgba(0,0,0,.2)}.dialog-close .fa-close{padding-top:15px;width:30px;height:15px;text-align:center;display:block;vertical-align:baseline;line-height:0;border:1px solid #eee;-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem}.dialog-close .svg-icon{padding:7px 0 7px 0;width:30px;height:15px;text-align:center;display:block;vertical-align:baseline;line-height:15px;border:1px solid #eee;-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem}.dialog-close:hover .fa-close,.dialog-close:hover .svg-icon{border:1px solid #ddd;-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem}@media all and (max-width:480px){.dialog{margin:0;border:0;box-shadow:none;width:100%;max-width:100%;margin-bottom:2.5em}}.footer{margin-top:0;position:fixed;bottom:0;text-align:right;font-size:1.2rem;width:100%;height:3.8rem;background:#fff;border-top:1px solid #ddd;z-index:890}.footer .footer-links{padding-top:1rem;width:100%}.footer .footer-links li{display:inline;margin-right:1.5em}.footer .footer-links li.error-message a{background-color:#fff;color:#d40101}.footer .footer-links li .github-star{display:inline;position:absolute;margin-left:-8em;margin-top:-1px}.footer .footer-links a:not(.gh-btn):not(.gh-count){border:0}.avatar img{width:36px;height:36px;border-radius:50%}.big.avatar img{width:72px;height:72px;border-radius:50%}iframe{margin:0;padding:0;border:0;outline:0;font-size:100%;vertical-align:baseline;background:0 0}iframe.full-screen{position:absolute;width:100%;height:100%;z-index:999;border:0;top:0;left:0}iframe.cachette{position:absolute;width:1px;height:1px;z-index:999;border:0;bottom:0;right:0}#user-locale-input{border:none;background:#f3f3f3 url('../../../img/controls/chevron-down_black.svg') 92% center/1rem no-repeat;appearance:none;margin-top:.8rem;padding-left:.4rem;padding-right:2rem;line-height:2rem;width:auto}#user-locale-input:focus{outline:0;border:1px solid #2894df;border-radius:0}#user-locale-input option{background-color:#f3f3f3}body,html{height:100%}.login.page h1{margin-top:0;font-size:2.4rem;color:#666}.login.page p{font-size:1.6rem;line-height:2.4rem}.login.page .processing-wrapper{display:block;width:16rem;height:16rem;margin:auto}.login.page .processing-wrapper .processing{display:block;text-align:center}.login.page .processing-wrapper .processing:after{display:block;width:16rem;height:16rem;content:" ";background:transparent url('../../../img/controls/loading_light.svg') center center no-repeat;background-size:auto 40%}.login.page .login-form{min-height:16rem}.login.page .login-form .form-actions{text-align:center;padding-top:1.2em}.login.page .login-form .button+a{font-size:1.6rem;line-height:2.4rem;text-align:center;display:inline-block;margin-top:1.6rem;cursor:pointer}.login.page .email-sent-instructions{text-align:center}.login.page .email-sent-instructions .email-sent-bg{background:transparent url('../../../img/illustrations/email.png') top center no-repeat;background-size:auto 90%;height:16rem}.login.page .email-sent-instructions h1{margin-top:2.4rem}.login.page .email-sent-instructions p{padding:.8rem .8rem 0 .8rem;margin-bottom:0}.login.page .choose-security-token .input-security-token{margin:1em 0 1.5em 0}.login.page .choose-security-token .input-security-token:after,.login.page .choose-security-token .input-security-token:before{content:"";display:table}.login.page .choose-security-token .input-security-token:after{clear:both}.login.page .choose-security-token .input-security-token:after,.login.page .choose-security-token .input-security-token:before{content:"";display:table}.login.page .choose-security-token .input-security-token:after{clear:both}.login.page .choose-security-token .input-security-token label{margin-bottom:.8rem}.login.page .choose-security-token .input-security-token .input.text{-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem;font-size:3rem;max-width:10rem;float:left;text-align:center;margin-right:3rem}.login.page .choose-security-token .input-security-token .circle-picker{float:left}.login.page .choose-security-token .input-security-token .randomize-button-wrapper{float:left;width:10rem;text-align:center;clear:both;cursor:pointer}.login.page .install-extension a.browser-webstore{border:0}.login.page .install-extension a.browser-webstore img{display:block;margin-left:auto;margin-right:auto;max-width:26rem}.login.page .install-extension a.browser-webstore.edge img,.login.page .install-extension a.browser-webstore.firefox img{padding:1.6rem 0}.login.page .introduce-setup-extension .animated-setup-introduction.chrome{background:transparent url('../../../img/illustrations/pin_passbolt.gif') center center no-repeat;background-size:contain;height:25rem}.login.page .introduce-setup-extension .arrow{background:transparent url('../../../img/illustrations/wave-pin_my_extension.svg') center top no-repeat;width:10rem;height:10rem;position:absolute;top:0;right:calc(3.75rem - calc(100vw - 100%))}.login.page .browser-not-supported a.browser{border:0}.login.page .browser-not-supported a.browser img{max-width:26rem;display:block;margin-left:auto;margin-right:auto}.login.page .login .login-user{width:100%;margin:auto}.login.page .login .login-user>*{text-align:center}.login.page .login .login-user .login-user-name{font-weight:700;font-size:1.6rem;line-height:1rem;margin-top:1.6rem}.login.page .login .login-user .login-user-email{font-size:1.6rem;line-height:1rem}.login.page .login-processing{display:flex;flex-direction:column;align-items:center;justify-content:center}@media only screen and (min-width:42rem){body{background:#f3f3f3}.login.page{display:grid;height:calc(100% - 4rem);grid-template-columns:1fr 1.5fr 1fr;grid-template-rows:0 1fr 0.05fr;grid-gap:1px;grid-template-areas:". . ." ". login-form ." "footer footer footer"}.login.page .content{grid-area:login-form}.login.page .content .loading-bar{display:block}.login.page .content .logo{margin:1.6em auto;width:20rem;background-size:20rem auto}.login.page .content .login-form{box-shadow:0 0 10px 0 #ddd;border-radius:.3rem;max-width:37.2rem;margin:auto;padding:4.8rem 4rem 5.6rem 4rem;background:#fff}.login.page .content .input.select.locale{max-width:45.2rem;margin:auto}} \ No newline at end of file diff --git a/webroot/css/themes/default/api_main.min.css b/webroot/css/themes/default/api_main.min.css index ce77fc0efe..20a777d263 100644 --- a/webroot/css/themes/default/api_main.min.css +++ b/webroot/css/themes/default/api_main.min.css @@ -1,16 +1,12 @@ /**! * @name passbolt-styleguide - * @version v3.2.1 - * @date 2021-05-19 + * @version v3.4.0 + * @date 2021-12-01 * @copyright Copyright 2021 Passbolt SA * @source https://github.com/passbolt/passbolt_styleguide * @license AGPL-3.0 */ -a,abbr,acronym,address,applet,b,big,blockquote,body,caption,center,cite,code,dd,del,dfn,div,dl,dt,em,fieldset,font,form,h1,h2,h3,h4,h5,h6,html,i,iframe,img,ins,kbd,label,legend,li,object,ol,p,pre,q,s,samp,small,span,strike,strong,sub,sup,table,tbody,td,tfoot,th,thead,tr,tt,u,ul,var{margin:0;padding:0;border:0;outline:0;font-size:100%;vertical-align:baseline;background:0 0}body{line-height:1}:focus{outline:0}ins{text-decoration:none}del{text-decoration:line-through}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}[hidden]{display:none}html{font-size:100%;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}button,html,input,select,textarea{font-family:"Open Sans",Verdana,sans-serif}a{outline:0;text-decoration:none;cursor:pointer}a:hover{text-decoration:none;cursor:pointer}h1{font-size:2em;margin:.67em 0}h2{font-size:1.5em;margin:.83em 0}h3{font-size:1.17em;margin:1em 0}h4{font-size:1em;margin:1.33em 0}h5{font-size:.83em;margin:1.67em 0}h6{font-size:.67em;margin:2.33em 0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}blockquote,q{quotes:none}blockquote:after,blockquote:before,q:after,q:before{content:'';content:none}dfn{font-style:italic}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}p,pre{margin:1em 0}code,kbd,pre,samp{font-family:monospace,serif;font-size:1em}pre{white-space:pre;white-space:pre-wrap;word-wrap:break-word}q{quotes:none}q:after,q:before{content:'';content:none}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}nav ol,nav ul,ol,ul{list-style:none;list-style-image:none}img{border:0;-ms-interpolation-mode:bicubic}svg:not(:root){overflow:hidden}figure{margin:0}form{margin:0}legend{border:0;padding:0;white-space:normal}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline}button,input{line-height:normal}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default;pointer-events:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top}table{border-collapse:collapse;border-spacing:0}table td,table th{text-align:left;font-weight:400}.ir{background-color:transparent;border:0;overflow:hidden}.ir:before{content:"";display:block;width:0;height:150%}html body .hidden{display:none}.visually-hidden,.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visually-hidden .focusable:active,.visually-hidden .focusable:focus,.visuallyhidden .focusable:active,.visuallyhidden .focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.clearfix{zoom:1}.clearfix:after,.clearfix:before{content:"";display:table}.clearfix:after{clear:both}.rounded{-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em}.left{float:left}.right{float:right}.ellipsis{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.inline{display:inline-block}@-webkit-keyframes rotate{from{-webkit-transform:rotate(0)}to{-webkit-transform:rotate(-360deg)}}@-moz-keyframes rotate{from{-moz-transform:rotate(0)}to{-moz-transform:rotate(-360deg)}}.rotate{-webkit-animation-name:rotate;-webkit-animation-duration:4s;-webkit-animation-iteration-count:infinite;-webkit-animation-timing-function:linear;-moz-animation-name:rotate;-moz-animation-duration:4s;-moz-animation-iteration-count:infinite;-moz-animation-timing-function:linear}.rotate-right{transform:rotate(-90deg)}.blink{animation:blinker 1s linear infinite}.blink-fast{animation:blinker .25s linear infinite}@keyframes blinker{50%{opacity:0}}body{color:#333;background:#fff}body.iframe{background:0 0}a:link,a:visited{color:#333}a:hover{color:#2894df}a:active,a:focus{outline:0;color:#2894df;border:0}a{border-bottom:1px solid #ddd}a:hover{border-bottom:1px solid #2894df}a.disabled{outline:0;text-decoration:none;cursor:default;color:#888;pointer-events:none}html{scroll-behavior:smooth}.animated{-webkit-animation-duration:.5s;animation-duration:.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both}@keyframes fadeInUp{0%{opacity:0;-webkit-transform:translateY(20px);-ms-transform:translateY(20px);transform:translateY(20px)}100%{opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}}@keyframes fadeOutUp{from{opacity:1}to{opacity:0;transform:translate3d(0,-100%,0)}}@keyframes fadeInDown{from{opacity:0;transform:translate3d(0,-100%,0)}to{opacity:1;transform:none}}@keyframes pop{0%{opacity:0;transform:scale(1)}55%{opacity:1;transform:scale(1)}65%{opacity:1;transform:scale(1.25)}75%{opacity:1;transform:scale(1)}100%{opacity:1;transform:scale(1)}}.fadeInDown{animation-name:fadeInDown}.fadeOutUp{animation-name:fadeOutUp}.fadeInUp{animation-name:fadeInUp}.pop{animation-name:pop}@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;src:local('Open Sans'),local('OpenSans'),url(../../../fonts/opensans-regular.woff) format('woff')}@font-face{font-family:'Open Sans';font-style:normal;font-weight:700;src:local('Open Sans Bold'),local('OpenSans-Bold'),url(../../../fonts/opensans-bold.woff) format('woff')}body{font-family:'Open Sans',Verdana,sans-serif;font-size:100%;line-height:1.42;font-weight:400}h1{font-size:1.5em}h2{font-size:1.25em}h3{font-size:1.125em;display:block;padding-bottom:.333em;border-bottom:1px dotted #ddd}p{margin:0 0 1em 0;line-height:1.5em}.code{font-family:"Courier New",Courier,monospace;font-size:.688em}.capitalize{text-transform:capitalize}/*! - * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome - * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - * @licstart - */@font-face{font-family:FontAwesome;src:url(../../../fonts/fontawesome-webfont.eot?v=4.7.0);src:url(../../../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0) format('embedded-opentype'),url(../../../fonts/fontawesome-webfont.woff2?v=4.7.0) format('woff2'),url(../../../fonts/fontawesome-webfont.woff?v=4.7.0) format('woff'),url(../../../fonts/fontawesome-webfont.ttf?v=4.7.0) format('truetype'),url(../../../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular) format('svg');font-weight:400;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-webkit-transform:scale(-1,1);-ms-transform:scale(-1,1);transform:scale(-1,1)}.fa-flip-vertical{-webkit-transform:scale(1,-1);-ms-transform:scale(1,-1);transform:scale(1,-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-rotate-90{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-close:before,.fa-remove:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-cog:before,.fa-gear:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-repeat:before,.fa-rotate-right:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-image:before,.fa-photo:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-exclamation-triangle:before,.fa-warning:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-cogs:before,.fa-gears:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-floppy-o:before,.fa-save:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-bars:before,.fa-navicon:before,.fa-reorder:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-sort:before,.fa-unsorted:before{content:"\f0dc"}.fa-sort-desc:before,.fa-sort-down:before{content:"\f0dd"}.fa-sort-asc:before,.fa-sort-up:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-gavel:before,.fa-legal:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-bolt:before,.fa-flash:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-clipboard:before,.fa-paste:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-chain-broken:before,.fa-unlink:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-caret-square-o-down:before,.fa-toggle-down:before{content:"\f150"}.fa-caret-square-o-up:before,.fa-toggle-up:before{content:"\f151"}.fa-caret-square-o-right:before,.fa-toggle-right:before{content:"\f152"}.fa-eur:before,.fa-euro:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-inr:before,.fa-rupee:before{content:"\f156"}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen:before{content:"\f157"}.fa-rouble:before,.fa-rub:before,.fa-ruble:before{content:"\f158"}.fa-krw:before,.fa-won:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-caret-square-o-left:before,.fa-toggle-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-try:before,.fa-turkish-lira:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-bank:before,.fa-institution:before,.fa-university:before{content:"\f19c"}.fa-graduation-cap:before,.fa-mortar-board:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-image-o:before,.fa-file-photo-o:before,.fa-file-picture-o:before{content:"\f1c5"}.fa-file-archive-o:before,.fa-file-zip-o:before{content:"\f1c6"}.fa-file-audio-o:before,.fa-file-sound-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-ring:before,.fa-life-saver:before,.fa-support:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before,.fa-resistance:before{content:"\f1d0"}.fa-empire:before,.fa-ge:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-hacker-news:before,.fa-y-combinator-square:before,.fa-yc-square:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-paper-plane:before,.fa-send:before{content:"\f1d8"}.fa-paper-plane-o:before,.fa-send-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-futbol-o:before,.fa-soccer-ball-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-ils:before,.fa-shekel:before,.fa-sheqel:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-bed:before,.fa-hotel:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-y-combinator:before,.fa-yc:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before,.fa-battery:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-paper-o:before,.fa-hand-stop-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-television:before,.fa-tv:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before{content:"\f2a3"}.fa-deaf:before,.fa-deafness:before,.fa-hard-of-hearing:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-sign-language:before,.fa-signing:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-address-card:before,.fa-vcard:before{content:"\f2bb"}.fa-address-card-o:before,.fa-vcard-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer-full:before,.fa-thermometer:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bath:before,.fa-bathtub:before,.fa-s15:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}/*! @licend */.icon{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.icon.fav:before{content:"\f005";color:#ddd}.icon.unfav:before{content:"\f005";color:#d40101}.svg-icon{display:inline-flex;align-self:center}.svg-icon svg{fill:#333;height:1em;width:1em}.svg-icon.baseline svg{top:.125em;position:relative}.svg-icon.dim svg,.svg-icon.light svg{fill:#888}.svg-icon.icon-only svg{position:relative;top:.25em;width:1.1rem;height:1.1rem}.button .svg-icon svg{top:.125em;position:relative}.button .svg-icon+span:not(.visuallyhidden){margin-left:.5em;display:inline-block}.button .svg-icon svg:hover,.button:hover .svg-icon svg{fill:#333}.button.primary:active .svg-icon svg,.button.primary:focus .svg-icon svg,.button.warning .svg-icon svg,.button.warning:active .svg-icon svg,.button.warning:focus .svg-icon svg,.button.warning:hover .svg-icon svg{fill:#fff}.button:active .svg-icon svg,.button:focus .svg-icon svg{fill:#2894df}.button.disabled .svg-icon svg{fill:#888}.button.disabled .svg-icon svg:hover{fill:#888}a:hover .svg-icon svg{fill:#2894df}a.disabled .svg-icon svg{fill:#888;pointer-events:none;cursor:default}a.disabled:hover .svg-icon svg{fill:#888}a.fav .svg-icon svg{fill:#d40101}a.unfav{display:inline-flex}a.unfav .svg-icon.star svg{fill:#ddd}.button,button,input[type=submit]{position:relative;font-size:.875em;min-height:1.428571em;padding:.4285714em 1.225em .4285714em 1.225em;display:inline-block;vertical-align:baseline;margin-right:.5em;outline:0;cursor:pointer;text-align:center;text-decoration:none;border:1px solid #ccc;color:#333;font-weight:400;text-shadow:1px 1px 0 rgba(255,255,255,.5);background:#eee;background-image:linear-gradient(top,#f3f3f3,#eee);-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em}.submit{display:inline-block}.button.big{font-size:1.125em}.button:hover,button:hover{color:#333;text-decoration:none;background:#f3f3f3;background-image:linear-gradient(top,#f3f3f3,#f3f3f3);border:1px solid #bbb}.button:focus,button:focus{color:#2894df;border:1px solid #2894df}.button:active,button:active{color:#2894df;border:1px solid #2894df;background:#eee;position:relative;box-shadow:inset 0 1px 2px rgba(0,0,0,.2)}.button.cancel,.button.cancel:active,.button.cancel:focus,.button.cancel:hover,.button.dim,.button.dim:active,.button.dim:focus,.button.dim:hover{background:#fff;font-weight:400}.button.primary{background:#2894df;border:1px solid #2894df;color:#fff;text-shadow:1px 1px 0 rgba(0,0,0,.2)}.button.primary:hover{background:#2a9ceb;border:1px solid #2a9ceb;color:#fff}.button.primary:active,.button.primary:focus{color:#fff;border:1px solid #4271b7;background:#2a9ceb}.button.warning,.button.warning:active,.button.warning:focus,.button.warning:hover{color:#fff;background:#d40101;border:1px solid #d40101;text-shadow:none}.button.warning:active,.button.warning:focus{border:1px solid #92000c}.button.disabled{cursor:default;color:#888;background:#fff;border:1px solid #ddd;text-shadow:none;font-weight:400}.button.disabled:active,.button.disabled:focus,.button.disabled:hover{box-shadow:0 0 0;top:0;color:#888;background:#fff;border:1px solid #ddd}.duo-wrapper{zoom:1}.duo-wrapper:after,.duo-wrapper:before{content:"";display:table}.duo-wrapper:after{clear:both}.duo-wrapper:after,.duo-wrapper:before{content:"";display:table}.duo-wrapper:after{clear:both}.duo-wrapper .button{float:left}.duo-wrapper .button.duo:nth-child(1){margin-right:0;border-top-right-radius:0;border-bottom-right-radius:0}.duo-wrapper .button.duo:nth-child(2){margin-left:0;border-left:0;border-top-left-radius:0;border-bottom-left-radius:0}.button-spacer,.buttons-spacer{margin-right:.5em}html body .button.processing{background:#fff;border:1px solid #fff;cursor:default}.button.processing:active,.button.processing:hover{border:1px solid #fff}.button.processing:after{line-height:2.25em;width:100%;height:100%;position:absolute;content:" ";top:-1px;left:-1px;background:#fff url(../../../img/controls/loading_light.svg) center center no-repeat;background-size:auto 50%;border:1px solid #ddd;border-radius:5px}input[type=submit].button.processing{color:transparent;text-shadow:none;background:#fff url(../../../img/controls/loading_light.svg) center center no-repeat;background-size:auto 50%;border:1px solid #ddd;border-radius:5px}.button-toggle.button.selected,.toggle.button.selected{background:#eee;box-shadow:inset 0 1px 2px rgba(0,0,0,.2);border:1px solid #bbb}label{font-weight:700;font-size:1em;line-height:1.333333em;padding:.5em 0;display:block}.required label:after{content:" \002A";color:#d40101;font-weight:700}.placeholder{color:#969696}::-webkit-input-placeholder{color:#969696}:-moz-placeholder,::-moz-placeholder{color:#969696}input[type=email],input[type=number],input[type=password],input[type=search],input[type=text],textarea{font-size:16px;line-height:24px;color:#333;background:#fff;width:14em;padding:.375em .625em .375em .625em;display:inline-block;margin-bottom:.5em;-webkit-appearance:none;border:1px solid #ccc;border-top:1px solid #bbb;vertical-align:middle;box-sizing:content-box}input[type=email]:hover,input[type=number]:hover,input[type=password]:hover,input[type=search]:hover,input[type=text]:hover,textarea:hover{border:1px solid #bbb}input[type=email]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=text]:focus,textarea:focus{box-shadow:inset 1px 1px 2px rgba(0,0,0,.2);border:1px solid #2894df}.input-focus{box-shadow:inset 1px 1px 2px rgba(0,0,0,.2);border:1px solid #2894df}input[type=email]:disabled,input[type=number]:disabled,input[type=password]:disabled,input[type=search]:disabled,input[type=text]:disabled,textarea:disabled{border:1px solid #ddd;box-shadow:0 0;cursor:pointer;background:#f3f3f3}textarea{height:4.8em;line-height:1.5em}.textarea.large textarea{width:30em;height:7em}textarea.message_notice{margin-bottom:0}textarea.full_report{height:12em;line-height:1.5em}.date.input .input-addon,.text.input .input-addon{position:absolute;margin-left:-2.625em}.date.input .input-addon i,.text.input .input-addon i{font-size:16px;width:1.25em;border:1px solid #ccc;border-top:1px solid #bbb;line-height:24px;color:#333;background:#fff;padding:.375em .625em .375em .625em}.date.input input:hover+.input-addon i,.text.input input:hover+.input-addon i{border:1px solid #bbb}.date.input input:focus+.input-addon i,.text.input input:focus+.input-addon i{border:1px solid #2894df}.date.input input:disabled+.input-addon i,.text.input input:disabled+.input-addon i{background:#f3f3f3;border:1px solid #ddd}.checkboxlist .input.checkbox label,.radiolist .input.radio label,input[type=checkbox]+label{font-weight:400;display:inline;margin-left:.25em;margin-bottom:.25em;cursor:pointer}.radiolist{zoom:1}.radiolist:after,.radiolist:before{content:"";display:table}.radiolist:after{clear:both}.radiolist:after,.radiolist:before{content:"";display:table}.radiolist:after{clear:both}.radiolist .input.radio{float:left}.radiolist .input.radio input{float:left;margin-top:.55em}.radiolist .input.radio input+label{font-weight:400;display:inline-block;margin-left:.65em;font-size:1em;float:left;margin-right:2em}.input .message{padding:0;padding-left:.25em;padding-bottom:.5em;font-size:.813em;margin-top:.125em;margin-bottom:.25em;background-color:transparent;border:0}.input .message.error{font-weight:400;color:#d40101}.input .message.helptext{color:#888;font-weight:400}.input .message:empty{display:none}.form-content .input .message,form .input .message{background-color:transparent}.input.error label,textarea.error label{color:#d40101}form .input .inline.message{display:inline-block}select{font-size:16px;margin:.55em 0}select.large{line-height:24px;color:#333;background:#fff;width:14em;padding:.375em .425em .375em .425em;display:inline-block;margin-bottom:.5em;border:1px solid #ccc;border-top:1px solid #bbb;vertical-align:middle;box-sizing:content-box}input[type=file]{width:100%;color:#333}input[type=email].fluid,input[type=password].fluid,input[type=text].fluid,select.fluid,textarea.fluid{width:80%}.submit-input-wrapper,.submit-wrapper{margin-top:.5em}.singleline{zoom:1}.singleline:after,.singleline:before{content:"";display:table}.singleline:after{clear:both}.singleline:after,.singleline:before{content:"";display:table}.singleline:after{clear:both}.singleline .input.text{float:left;width:40.2%}.singleline .input.text.first-field,.singleline .input.text:first-child{margin-right:2.5%}.singleline .input.text input[type=email],.singleline .input.text input[type=password],.singleline .input.text input[type=search],.singleline .input.text input[type=text]{width:93%}.form-content .accordion h3.accordion-header a{border:0}.form-content .accordion h3.accordion-header a:hover{border:0}.form-content .accordion h3.accordion-header a:before{font:normal normal normal 1em FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;width:1em;height:1em;position:absolute;text-align:center}.form-content table{width:100%;margin-bottom:1em}.form-content table thead{background-color:transparent!important}.form-content table thead th{background-color:transparent;font-weight:700;border:1px solid #ddd;padding:.7em .5em}.form-content table tbody tr{font-size:.8em;text-align:left}.form-content table tbody tr:nth-child(even){background-color:transparent}.form-content table tbody tr:nth-child(odd){background-color:#f3f3f3;color:#666}.form-content table tbody tr td{text-align:left;padding:.5em .5em;border:1px solid #ddd}.form-content table tbody tr td code{padding:.2em .3em;background-color:#ddd}.form-content table tbody tr td pre{margin:0;padding:0;background-color:transparent}.form-content table tbody tr td code,.form-content table tbody tr td pre{font-family:monospace,serif;font-size:1em}.form-content table tbody tr td em{color:#888;font-size:.8em;font-style:normal}.page{width:100%;min-width:320px;top:0;left:0;right:0;bottom:0;position:absolute;overflow:auto}.page .panel{height:100%;width:100%;position:absolute;overflow:auto}.page .panel.main{top:10em;bottom:2.45em;height:auto;padding:0}.page .panel.main .row{padding-left:0}.page .panel.main .panel.left{background:#fff}.page .header.second+.panel.main{top:6.875em}.page .header.third+.panel.main{top:10em;border-top:1px solid #ddd}.page .header{width:100%;overflow:hidden}.page .header.first{min-height:2.4em}.page .header.second{top:2.375em;height:4.5em}.page .header.third{top:6.875em;height:3em}.page .col1,.page .col2,.page .col2_3,.page .col3{position:absolute}.page .col1,.page .panel.left{width:17.25%;box-sizing:border-box}.page .panel.middle{width:82%;left:18%;overflow:hidden}.page .panel.middle.scroll{overflow-y:scroll}.page .col2{width:70%;left:18%}.page .col3,.page .panel.right{width:24%;left:75%}.page .col2_3{width:81.5%;left:18%}.page .panel.main .grid-responsive-12{float:left;width:100%;max-width:none;padding-left:.25em;box-sizing:border-box}@media all and (max-width:1024px){.page .panel.left{display:none}.page .header.second .col2{display:none}.page .header.second .col2_3{display:none}.page .header.third{height:4.5em}.page .header.third .col1,.page .header.third .col2,.page .header.third .col2_3{position:relative;float:left;left:auto;width:auto}.page .header.third .col3{display:none}.page .panel.main .panel.left{display:none}.page .panel.main .panel.middle{width:100%;left:0}.page .panel.main .row{padding-left:.5em}.page .panel.main .panel.aside{min-width:auto}}.grid,.grid-responsive-12{margin:0 auto;padding:0;max-width:1220px}.grid .row,.grid-responsive-12 .row{zoom:1;padding:0 .5em 0 .5em}.grid .row:after,.grid .row:before,.grid-responsive-12 .row:after,.grid-responsive-12 .row:before{content:"";display:table}.grid .row:after,.grid-responsive-12 .row:after{clear:both}.grid .row:after,.grid .row:before,.grid-responsive-12 .row:after,.grid-responsive-12 .row:before{content:"";display:table}.grid .row:after,.grid-responsive-12 .row:after{clear:both}.grid .row .col1,.grid .row .col10,.grid .row .col11,.grid .row .col12,.grid .row .col2,.grid .row .col3,.grid .row .col4,.grid .row .col5,.grid .row .col6,.grid .row .col7,.grid .row .col8,.grid .row .col9,.grid-responsive-12 .row .col1,.grid-responsive-12 .row .col10,.grid-responsive-12 .row .col11,.grid-responsive-12 .row .col12,.grid-responsive-12 .row .col2,.grid-responsive-12 .row .col3,.grid-responsive-12 .row .col4,.grid-responsive-12 .row .col5,.grid-responsive-12 .row .col6,.grid-responsive-12 .row .col7,.grid-responsive-12 .row .col8,.grid-responsive-12 .row .col9{zoom:1;position:relative;left:auto;float:none;width:99%;box-sizing:border-box}.grid .row .col10:after,.grid .row .col10:before,.grid .row .col11:after,.grid .row .col11:before,.grid .row .col12:after,.grid .row .col12:before,.grid .row .col1:after,.grid .row .col1:before,.grid .row .col2:after,.grid .row .col2:before,.grid .row .col3:after,.grid .row .col3:before,.grid .row .col4:after,.grid .row .col4:before,.grid .row .col5:after,.grid .row .col5:before,.grid .row .col6:after,.grid .row .col6:before,.grid .row .col7:after,.grid .row .col7:before,.grid .row .col8:after,.grid .row .col8:before,.grid .row .col9:after,.grid .row .col9:before,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col10:before,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col11:before,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col12:before,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col1:before,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col2:before,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col3:before,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col4:before,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col5:before,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col6:before,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col7:before,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col8:before,.grid-responsive-12 .row .col9:after,.grid-responsive-12 .row .col9:before{content:"";display:table}.grid .row .col10:after,.grid .row .col11:after,.grid .row .col12:after,.grid .row .col1:after,.grid .row .col2:after,.grid .row .col3:after,.grid .row .col4:after,.grid .row .col5:after,.grid .row .col6:after,.grid .row .col7:after,.grid .row .col8:after,.grid .row .col9:after,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col9:after{clear:both}.grid .row .col10:after,.grid .row .col10:before,.grid .row .col11:after,.grid .row .col11:before,.grid .row .col12:after,.grid .row .col12:before,.grid .row .col1:after,.grid .row .col1:before,.grid .row .col2:after,.grid .row .col2:before,.grid .row .col3:after,.grid .row .col3:before,.grid .row .col4:after,.grid .row .col4:before,.grid .row .col5:after,.grid .row .col5:before,.grid .row .col6:after,.grid .row .col6:before,.grid .row .col7:after,.grid .row .col7:before,.grid .row .col8:after,.grid .row .col8:before,.grid .row .col9:after,.grid .row .col9:before,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col10:before,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col11:before,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col12:before,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col1:before,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col2:before,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col3:before,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col4:before,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col5:before,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col6:before,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col7:before,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col8:before,.grid-responsive-12 .row .col9:after,.grid-responsive-12 .row .col9:before{content:"";display:table}.grid .row .col10:after,.grid .row .col11:after,.grid .row .col12:after,.grid .row .col1:after,.grid .row .col2:after,.grid .row .col3:after,.grid .row .col4:after,.grid .row .col5:after,.grid .row .col6:after,.grid .row .col7:after,.grid .row .col8:after,.grid .row .col9:after,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col9:after{clear:both}.grid .row .col1 img,.grid .row .col10 img,.grid .row .col11 img,.grid .row .col12 img,.grid .row .col2 img,.grid .row .col3 img,.grid .row .col4 img,.grid .row .col5 img,.grid .row .col6 img,.grid .row .col7 img,.grid .row .col8 img,.grid .row .col9 img,.grid-responsive-12 .row .col1 img,.grid-responsive-12 .row .col10 img,.grid-responsive-12 .row .col11 img,.grid-responsive-12 .row .col12 img,.grid-responsive-12 .row .col2 img,.grid-responsive-12 .row .col3 img,.grid-responsive-12 .row .col4 img,.grid-responsive-12 .row .col5 img,.grid-responsive-12 .row .col6 img,.grid-responsive-12 .row .col7 img,.grid-responsive-12 .row .col8 img,.grid-responsive-12 .row .col9 img{width:100%;height:auto;display:block}@media all and (min-width:780px){.grid .row{padding:0 .5em 0 .5em}.grid .row .col1,.grid .row .col10,.grid .row .col11,.grid .row .col12,.grid .row .col2,.grid .row .col3,.grid .row .col4,.grid .row .col5,.grid .row .col6,.grid .row .col7,.grid .row .col8,.grid .row .col9{float:left;position:relative;margin:0 3% 0 0}.grid .row .last{margin-right:0}.grid .row .col1{width:5.5%}.grid .row .col2{width:14%}.grid .row .col3{width:22.5%}.grid .row .col4{width:31%}.grid .row .col5{width:39.5%}.grid .row .col6{width:48%}.grid .row .col7{width:56.5%}.grid .row .col8{width:65%}.grid .row .col9{width:73.5%}.grid .row .col10{width:82%}.grid .row .col11{width:90.5%}.grid .row .col12{width:99%;margin:0}.grid .row .push1{margin-left:5.5%}.grid .row .push2{margin-left:14%}.grid .row .push3{margin-left:22.5%}.grid .row .push4{margin-left:31%}}@media all and (max-width:768px){.hidden-xs{display:none}}.scroll{overflow-y:scroll;background:linear-gradient(#fff 30%,rgba(255,255,255,0)),linear-gradient(rgba(255,255,255,0),#fff 70%) 0 100%,radial-gradient(farthest-side at 75% 0,rgba(0,0,0,.2),rgba(0,0,0,0)),radial-gradient(farthest-side at 75% 100%,rgba(0,0,0,.2),rgba(0,0,0,0)) 0 100%;background-repeat:no-repeat;background-color:#fff;background-size:100% 10px,100% 10px,100% 5px,100% 5px}::-webkit-scrollbar{width:1em}::-webkit-scrollbar-track{background:#f9f9f9;border-top:0;border-bottom:0}::-webkit-scrollbar-thumb{background:#c1c1c1;border-radius:1em;border:4px solid #f9f9f9}.input.toggle-switch{display:flex;padding-top:1em;padding-bottom:.5em}.input.toggle-switch label{padding-top:.15em;padding-left:1em;order:2;flex:1;font-weight:400;font-size:1em}.input.toggle-switch.disabled label{color:#969696}.input.toggle-switch .toggle-switch-checkbox{order:-1;flex:0 0 3em;display:none}.input.toggle-switch .toggle-switch-checkbox,.input.toggle-switch .toggle-switch-checkbox *,.input.toggle-switch .toggle-switch-checkbox :after,.input.toggle-switch .toggle-switch-checkbox :before,.input.toggle-switch .toggle-switch-checkbox+.toggle-switch-button,.input.toggle-switch .toggle-switch-checkbox:after,.input.toggle-switch .toggle-switch-checkbox:before{box-sizing:border-box}.input.toggle-switch .toggle-switch-checkbox ::selection,.input.toggle-switch .toggle-switch-checkbox :after::selection,.input.toggle-switch .toggle-switch-checkbox :before::selection,.input.toggle-switch .toggle-switch-checkbox+.toggle-switch-button::selection,.input.toggle-switch .toggle-switch-checkbox::selection,.input.toggle-switch .toggle-switch-checkbox:after::selection,.input.toggle-switch .toggle-switch-checkbox:before::selection{background:0 0}.input.toggle-switch .toggle-switch-button{order:-1;flex:none;outline:0;display:block;width:3em;height:1.5em;position:relative;cursor:pointer;user-select:none;background:#f3f3f3;border-radius:2em;padding:2px;transition:all .4s ease;border:1px solid #ddd}.input.toggle-switch .toggle-switch-button span{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.input.toggle-switch .toggle-switch-button span .focusable:active,.input.toggle-switch .toggle-switch-button span .focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.input.toggle-switch .toggle-switch-button span .focusable:active,.input.toggle-switch .toggle-switch-button span .focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.input.toggle-switch .toggle-switch-button:hover:after{will-change:padding}.input.toggle-switch .toggle-switch-button:active{box-shadow:inset 0 0 0 2em #e8eae9}.input.toggle-switch .toggle-switch-button:active:after{padding-right:.8em}.input.toggle-switch .toggle-switch-button:after,.input.toggle-switch .toggle-switch-button:before{position:relative;display:block;content:"";width:50%;height:100%}.input.toggle-switch .toggle-switch-button:after{left:0;border-radius:2em;background:#fff;transition:left .3s cubic-bezier(.175,.885,.32,1.275),padding .3s ease,margin .3s ease;box-shadow:0 0 0 1px rgba(0,0,0,.1),0 4px 0 rgba(0,0,0,.08)}.input.toggle-switch .toggle-switch-button:before{display:none}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button:after{left:50%}.input.toggle-switch .toggle-switch-checkbox:disabled+.toggle-switch-button{background:#fff;cursor:not-allowed}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button{background:#6c0}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button:active{box-shadow:none}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button:active:after{margin-left:-.8em}.input.toggle-switch .toggle-switch-checkbox:disabled:checked+.toggle-switch-button{background:#e6ffcc}h1 .input.toggle-switch,h2 .input.toggle-switch,h3 .input.toggle-switch,h4 .input.toggle-switch,h5 .input.toggle-switch,h6 .input.toggle-switch{float:left;padding:.4em 1em 0 0}.header{overflow:visible!important}.header.first{background:#333}.header.second{background:#eee}.header .navigation.primary{zoom:1}.header .navigation.primary:after,.header .navigation.primary:before{content:"";display:table}.header .navigation.primary:after{clear:both}.header .navigation.primary:after,.header .navigation.primary:before{content:"";display:table}.header .navigation.primary:after{clear:both}.header .navigation.primary li{padding:.5em;float:left}.header .navigation.primary li:first-child{padding-left:1em}.header .navigation.primary li.right{float:right;margin-right:1em}.header .navigation.primary li a{color:#ddd;text-decoration:none;border:0;display:inline-block}.header .navigation.primary li a:hover{color:#fff}.header .navigation.primary li a:active,.header .navigation.primary li a:focus{color:#2894df}.header .navigation.primary li a.highlighted{background-color:#2894df;padding:0 .5em 0 .5em;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.header .navigation.primary li a.highlighted:active,.header .navigation.primary li a.highlighted:focus{color:#ddd}.header .navigation.primary li .row.selected a{color:#fff}.header .navigation.primary li .row.selected a:focus{color:#2894df}.header .navigation.primary .github-star{display:none;position:absolute;right:1em;top:4px}@media all and (min-width:600px){.header .navigation.primary .github-star{display:block}}.header .logo{margin:1.25em 0 0 1em;max-width:80%}.logo.no-img{background:transparent url(../../../img/logo/logo.svg) 0 0 no-repeat;background-size:150px auto;width:150px;height:30px}.logo h1{display:none}.logo.bigger{background:transparent url(../../../img/logo/logo.svg) 0 0 no-repeat;background-size:200px auto;width:200px;height:45px}.header.second .col1{min-width:200px}@media only screen and (-moz-min-device-pixel-ratio:1.5),only screen and (-o-min-device-pixel-ratio:1.5),only screen and (-webkit-min-device-pixel-ratio:1.5),only screen and (min-devicepixel-ratio:1.5),only screen and (min-resolution:1.5dppx){.logo.no-img{background:transparent url(../../../img/logo/logo.svg) 0 0 no-repeat;background-size:150px auto}.logo.bigger{background:transparent url(../../../img/logo/logo.svg) 0 0 no-repeat;background-size:200px auto}}form.search{margin-top:1em}form.search label,form.search legend{display:none;width:10em}form.search input[type=search]{float:left;width:65%;margin-bottom:0;border-right:1px solid #fff;padding:.313em .625em}form.search input[type=search]:active,form.search input[type=search]:focus{border-right:1px solid #2894df}form.search button{height:2.571em;width:4.5em;float:left;margin-left:0;border-radius:0 2px 2px 0}form.search button .svg-icon.icon-only svg{top:.125em}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{display:none}@media all and (max-width:480px){form.search{display:none}}.avatar img{width:36px;height:36px;border-radius:50%}.big.avatar img{width:72px;height:72px;border-radius:50%}.user.profile{max-width:16em;float:right;border:1px solid #ddd;margin-top:.5em;overflow:hidden;cursor:pointer;border-radius:3px;background:#eee;background-image:linear-gradient(top,#f3f3f3,#eee)}.user.profile .more{background:#eee;background-image:linear-gradient(top,#f3f3f3,#eee);border-left:1px dotted #ddd}.user.profile .center-cell-wrapper,.user.profile .left-cell,.user.profile .right-cell{float:left;height:3.125em}.user.profile .center-cell-wrapper{width:100%}.user.profile .center-cell{margin:0 0 0 3.125em;overflow:hidden}.user.profile .left-cell{width:3.125em;margin-left:-100%}.user.profile .right-cell{width:1.6875em;margin-left:-1.75em}.user.profile .picture img{width:2.6em;padding:.28em .35em .35em .35em;border-radius:50%}.user.profile .details{float:left;font-size:.875em;padding:.357em 1em .5em 1em}.user.profile .details .email,.user.profile .details .name{width:11em;float:left;clear:both;text-overflow:clip;word-wrap:break-word;display:inline-block;line-height:normal;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.user.profile .details .name{font-weight:700;padding-top:0}.user.profile .details .email{padding-bottom:0}.user.profile .more a{position:absolute;display:block;border:0;color:#666}.user.profile .more a .svg-icon{margin:16px 0 0 7px;display:inline-block}.user.profile .more a .svg-icon svg{fill:#333}.user.profile .more a:hover{color:#000}.user.profile .more a:hover svg{fill:#333}.user.profile .more a span:last-child{visibility:hidden}.user.profile .dropdown-content{top:59px;max-width:16em;width:100%;background:#fff}.user.profile .dropdown-content.visible{display:block}@media all and (max-width:1024px){.user.profile{display:block;width:auto}.user.profile .center-cell{display:none}.user.profile .right-cell{display:none}}.usercard-col-2{zoom:1;margin:0 0 1em 0}.usercard-col-2:after,.usercard-col-2:before{content:"";display:table}.usercard-col-2:after{clear:both}.usercard-col-2:after,.usercard-col-2:before{content:"";display:table}.usercard-col-2:after{clear:both}.usercard-col-2:last-of-type{margin-bottom:0}.usercard-col-2 .content-wrapper{float:left;width:100%}.usercard-col-2 .content-wrapper .content{margin-left:36px;padding:0 1em 0 1em}.usercard-col-2 .content-wrapper .content .name{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.usercard-col-2 .content-wrapper .content .subinfo{color:#333;font-size:.929em}.usercard-col-2 .avatar{float:left;width:36px;margin-left:-100%}.usercard-col-2 .avatar img{width:36px;height:36px}.usercard-col-2 ul.permissions-list{margin-top:1em;font-size:.8em}.usercard-col-2 ul.permissions-list li .avatar{margin-left:0;float:left;width:18px}.usercard-col-2 ul.permissions-list li .avatar img{width:18px;height:18px}.usercard-col-2 ul.permissions-list li .name{display:inline-block;padding-left:7%;line-height:18px}.usercard-col-2 ul.permissions-list li .name.crossed{text-decoration:line-through}.usercard-col-2 ul.permissions-list li .type{float:right;text-align:right;line-height:18px;color:#888}.usercard-col-2 ul.permissions-list li .type span{background-color:#fff;font-size:.8em;color:#fff;padding:.1em .5em;border-radius:.5em}.usercard-col-2 ul.permissions-list li .type span.created{background-color:#6c0}.usercard-col-2 ul.permissions-list li .type span.updated{background-color:#2a9ceb}.usercard-col-2 ul.permissions-list li .type span.removed{background-color:#db5454}.usercard-detailed-col-2{zoom:1;margin:0 0 1em 0;padding:.5em 0;margin:0}.usercard-detailed-col-2:after,.usercard-detailed-col-2:before{content:"";display:table}.usercard-detailed-col-2:after{clear:both}.usercard-detailed-col-2:after,.usercard-detailed-col-2:before{content:"";display:table}.usercard-detailed-col-2:after{clear:both}.usercard-detailed-col-2:last-of-type{margin-bottom:0}.usercard-detailed-col-2 .content-wrapper{float:left;width:100%}.usercard-detailed-col-2 .content-wrapper .content{margin-left:36px;padding:0 1em 0 1em}.usercard-detailed-col-2 .content-wrapper .content .name{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.usercard-detailed-col-2 .content-wrapper .content .subinfo{color:#333;font-size:.929em}.usercard-detailed-col-2 .avatar{float:left;width:36px;margin-left:-100%}.usercard-detailed-col-2 .avatar img{width:36px;height:36px}.usercard-detailed-col-2 ul.permissions-list{margin-top:1em;font-size:.8em}.usercard-detailed-col-2 ul.permissions-list li .avatar{margin-left:0;float:left;width:18px}.usercard-detailed-col-2 ul.permissions-list li .avatar img{width:18px;height:18px}.usercard-detailed-col-2 ul.permissions-list li .name{display:inline-block;padding-left:7%;line-height:18px}.usercard-detailed-col-2 ul.permissions-list li .name.crossed{text-decoration:line-through}.usercard-detailed-col-2 ul.permissions-list li .type{float:right;text-align:right;line-height:18px;color:#888}.usercard-detailed-col-2 ul.permissions-list li .type span{background-color:#fff;font-size:.8em;color:#fff;padding:.1em .5em;border-radius:.5em}.usercard-detailed-col-2 ul.permissions-list li .type span.created{background-color:#6c0}.usercard-detailed-col-2 ul.permissions-list li .type span.updated{background-color:#2a9ceb}.usercard-detailed-col-2 ul.permissions-list li .type span.removed{background-color:#db5454}@keyframes highlight{from{background-color:#fffae7}to{background-color:transparent}}.usercard-detailed-col-2.highlight{background-color:#fffae7;animation-delay:2s;animation-duration:5s;animation-name:highlight;animation-fill-mode:forwards}.usercard-detailed-col-2 .content-wrapper .content .name,.usercard-detailed-col-2 .content-wrapper .content .subinfo{color:#888}.usercard-detailed-col-2 .content-wrapper .content .name{white-space:normal}.usercard-detailed-col-2 .content-wrapper .content .name .creator{color:#333}.breadcrumbs{height:2em;padding:.1875em 0 .1875em .1875em;margin-bottom:0;background:#fff}.breadcrumbs ul{padding-top:.45em;margin-left:.1875em}.breadcrumbs ul li{display:inline-block;font-size:.8em;margin-left:.25em;max-width:25%;float:left}.breadcrumbs ul li:before{content:"\203A";margin-right:.5em}.breadcrumbs ul li:first-child{margin-left:0;padding-left:0}.breadcrumbs ul li:first-child:before{content:""}.breadcrumbs ul a{border:0}.breadcrumbs div.main-cell{display:inline}.panel.middle .breadcrumbs{border-bottom:1px solid #ddd}.header.third .main-action-wrapper{margin-top:.5em;padding-left:.625em}.header.third .main-action-wrapper .button{float:left;font-size:.875em;min-width:.75em}.header.third .actions-wrapper{margin-top:.5em}.header.third .actions-wrapper .button{font-size:.875em;min-width:4em;float:left}.header.third .actions-wrapper li{display:inline}.header.third .actions-wrapper .secondary{float:right}.header.third .actions-wrapper .secondary .button{font-size:.875em;min-width:1em}.header.third .actions-wrapper .secondary .button.info{padding:.3285714em 1.025em .3285714em 1.025em}.header.third .actions-wrapper .secondary .button.info .svg-icon{font-size:1.33333333em}@media all and (max-width:1024px){.header.third .actions-wrapper .actions.secondary,.header.third .actions-wrapper .dropdown{display:none}}@media all and (max-width:1024px){.header.third .actions-wrapper i,.header.third .main-action-wrapper i{display:none}.header.third .actions-wrapper i+span,.header.third .main-action-wrapper i+span{margin-left:0}.header.third .actions-wrapper .disabled,.header.third .main-action-wrapper .disabled{display:none}}@media all and (max-width:540px){.header.third .actions-wrapper a i,.header.third .main-action-wrapper a i{display:block}.header.third .actions-wrapper a.button,.header.third .main-action-wrapper a.button{min-width:1em;font-size:1em}.header.third .actions-wrapper a i+span,.header.third .main-action-wrapper a i+span{margin-left:0;display:none}.header.third .actions-wrapper .disabled,.header.third .main-action-wrapper .disabled{display:none}}.progress-bar{background:0 0;width:100%;height:2px;display:block}.progress-bar span{background:#d40101;height:2px;display:block}.progress-bar-wrapper{border:1px solid #ddd;height:10px;display:block;margin:2em 0 1em 0;padding:2px}.progress-bar-wrapper .progress-bar.big{width:100%;height:10px;display:block;clear:both}.progress-bar-wrapper .progress-bar.big .progress{background:#d40101;width:5%;display:block;height:10px}.progress-bar-wrapper .progress-bar.big.infinite{background:#d40101}.progress-bar-wrapper .progress-bar.big.infinite .progress{width:100%;overflow:hidden;background:url(../../../img/controls/infinite-bar.gif) repeat-x;-moz-opacity:.5;-khtml-opacity:.5;opacity:.5}.progress-details{color:#888;margin:.5em 0 .5em 0}.progress-details .progress-percent{float:right}.update-loading-bar{position:fixed;display:block;width:100%;bottom:2.35em;z-index:991}.update-loading-bar .progress-bar span{transition:width 2s;transition-timing-function:cubic-bezier(.45,1.27,.76,.9)}.notification-container{font-size:.85em;top:0;position:absolute;z-index:991;height:2em;padding-top:1em;width:60%;margin-left:20%}.notification-container .notification{position:relative;left:50%;float:left;clear:both;margin-bottom:1em}.notification-container .notification .message{box-shadow:0 0 10px 0 #333;border-radius:2px;padding:.5em 1em;position:relative;left:-50%;float:left;color:#000;font-weight:400;width:auto}.notification-container .notification .message.warning{color:#333;background:#fef0bf}.notification-container .notification .message .content{margin-right:1em}.notification-container .notification .message .content strong{text-transform:capitalize}.notification-container .notification .message .action{border-bottom:none;margin-left:.4em}.notification-container .notification .message .action:hover .svg-icon svg{fill:#000}.js .message.no-js{display:none}.cookies .message.no-cookies{display:none}.message{padding:1em}.message a{border-bottom:1px solid #888}.message a:hover{border-bottom:1px solid #2894df}.message.error{color:#333;background:#ffe4e4}.message.error a:link,.message.error a:visited{color:#333;border-bottom:1px dotted #888}.message.error a:hover{color:#333;border-bottom:1px solid #888}.message.success{color:#333;background:#edf7eb}.message.notice{color:#333;background:#daecf9}.message.warning{color:#333;background:#fef0bf}.message p:last-child{margin-bottom:0}.message.side-message{margin-left:1em;font-size:1em;margin-right:2em}.message.side-message p,.message.side-message ul{padding-bottom:1em}.message.animated{background:#fff;color:#333;display:flex;border:1px solid #ddd;border-radius:3px}.message.animated .illustration{flex:0 0 180px}.message.animated .additional-information{flex:1;line-height:180px;margin-top:1.5em;padding-left:1em}@keyframes drawCircle{0%{stroke-dashoffset:180px}100%{stroke-dashoffset:0}}@keyframes drawCheck{0%{stroke-dashoffset:50px}100%{stroke-dashoffset:0}}@keyframes drawCross{0%{stroke-dashoffset:50px}100%{stroke-dashoffset:0}}@keyframes drawWarning{0%{stroke-dashoffset:180px}100%{stroke-dashoffset:0}}.message.animated #successAnimationCircle{stroke-dasharray:180px 180px;stroke:#090}.message.animated #successAnimationCheck{stroke-dasharray:50px 50px;stroke:#090}.message.animated #errorAnimationCircle{stroke-dasharray:180px 180px;stroke:#d40101}.message.animated #errorAnimationCross{stroke-dasharray:50px 50px;stroke:#d40101}.message.animated #warningAnimation{stroke-dasharray:180px 180px;stroke:#9f6000;stroke-linecap:square;stroke-linejoin:round}.message.animated #warningAnimation.round{stroke-linecap:round}.message.animated .animated{animation:.75s ease-out 0s 1 both pop}.message.animated .animated #errorAnimationCircle,.message.animated .animated #successAnimationCircle{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawCircle}.message.animated .animated #successAnimationCheck{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawCheck}.message.animated .animated #errorAnimationCross{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawCross}.message.animated .animated #warningAnimation{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawWarning}@media only screen and (max-width:767px){.message.animated{flex-direction:column}}.secret-copy{overflow:visible;display:table-cell;vertical-align:middle;zoom:1}.secret-copy:after,.secret-copy:before{content:"";display:table}.secret-copy:after{clear:both}.secret-copy:after,.secret-copy:before{content:"";display:table}.secret-copy:after{clear:both}.secret-copy>a{background:transparent url(../../../img/controls/dot_black.svg) repeat-x left 3px;display:inline-block;width:108px;height:20px;float:left;border:0}.secret-copy>a:hover{background:transparent url(../../../img/controls/dot_red.svg) repeat-x left 3px}.secret-copy>a>span{display:none}.secret-copy>pre{display:none}.dialog-wrapper{position:absolute;width:100%;height:100%;z-index:800;background:rgba(255,255,255,.9);overflow:auto}.dialog-wrapper .placeholder{position:relative;width:1em;margin:auto;height:100%}.dialog-wrapper .placeholder .loading{position:absolute;top:30%;height:2em;width:2em;background:url(../../../img/controls/loading_light.svg);background-size:2em 2em}.dialog{position:relative;max-width:30em;border:1px solid #ddd;background:#f3f3f3;margin:auto;margin-top:1%;margin-bottom:3em;box-shadow:0 0 10px 0 #ddd}.dialog.medium{max-width:36em}.dialog .dialog-header{padding:.5em 1em 0 1em;height:3em}.dialog .dialog-header h2{margin:.25em 2em .5em .2em;font-size:1.2em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.dialog .dialog-header h2 svg{position:relative;top:.0625em}.dialog .dialog-header .tooltip-alt{margin-left:.3em;font-size:.7em;font-weight:400;color:#333}.dialog .dialog-header .tooltip-alt .tooltip-text{white-space:normal}.dialog .dialog-header .dialog-header-subtitle{padding-top:.3em;padding-left:1em;font-size:.7em;color:#333}.dialog .dialog-header .dialog-close{margin-top:-2.5em}.dialog .form-content{background:#fff;padding:.813em 1.5em 1.5em 1.5em}.dialog p{margin-top:.5em;font-size:1em}.dialog p+.checkbox{padding-bottom:.5em}.dialog p+.checkbox label{font-size:1em}.dialog label{clear:both;font-size:.9375em;line-height:1.6em}.dialog input[type=text],.dialog select.large,.dialog textarea{width:100%;box-sizing:border-box;zoom:1}.dialog input[type=text]:after,.dialog input[type=text]:before,.dialog select.large:after,.dialog select.large:before,.dialog textarea:after,.dialog textarea:before{content:"";display:table}.dialog input[type=text]:after,.dialog select.large:after,.dialog textarea:after{clear:both}.dialog input[type=text]:after,.dialog input[type=text]:before,.dialog select.large:after,.dialog select.large:before,.dialog textarea:after,.dialog textarea:before{content:"";display:table}.dialog input[type=text]:after,.dialog select.large:after,.dialog textarea:after{clear:both}.dialog input+.message,.dialog textarea+.message{display:none}.dialog input+.message.error,.dialog textarea+.message.error{display:block;clear:both;width:100%}.dialog .input .message.warning,.dialog input+.message.warning,.dialog textarea+.message.warning{display:block;clear:both;width:100%;color:#9f6000;background-color:transparent}.dialog .inline-error{color:#d40101;font-weight:700}.dialog .accordion-header a{display:inline}.dialog .submit-wrapper{margin:0;clear:both;width:100%;padding:.75em 0}.dialog .submit-wrapper .button,.dialog .submit-wrapper .cancel{float:right;margin-right:1.25em;font-size:1.125em}.dialog .submit-wrapper .button{box-sizing:border-box;min-width:5em}.dialog .submit-wrapper .secondary{float:left;margin-left:1.5em;margin-top:.7em;font-size:1em}.dialog .submit-wrapper .cancel{margin-top:.7em;font-size:1em}.dialog-close,.dialog-close:hover{display:block;float:right;border:0}.dialog-close:active{-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em;box-shadow:inset 0 1px 2px rgba(0,0,0,.2)}.dialog-close .fa-close{padding-top:15px;width:30px;height:15px;text-align:center;display:block;vertical-align:baseline;line-height:0;border:1px solid #eee;-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em}.dialog-close .svg-icon{padding:7px 0 7px 0;width:30px;height:15px;text-align:center;display:block;vertical-align:baseline;line-height:15px;border:1px solid #eee;-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em}.dialog-close:hover .fa-close,.dialog-close:hover .svg-icon{border:1px solid #ddd;-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em}@media all and (max-width:480px){.dialog{margin:0;border:0;box-shadow:none;width:100%;max-width:100%;margin-bottom:2.5em}}.dropdown{float:left}.dropdown .button.create:focus,.dropdown .button:focus{border:1px solid #ddd;color:#333;background:#fff;z-index:801;border-bottom:0;-webkit-border-bottom-right-radius:0;-webkit-border-bottom-left-radius:0;-moz-border-radius-bottomright:0;-moz-border-radius-bottomleft:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.dropdown .button.create:focus .svg-icon svg,.dropdown .button:focus .svg-icon svg{fill:#333}.dropdown .button.more .svg-icon svg{top:.15em;margin-left:.5em}.dropdown .button.create .svg-icon svg{fill:#fff}.dropdown .button.create.disabled .svg-icon svg{fill:#888}.dropdown .dropdown-content{display:none;border:1px solid #ddd;background:#fff;float:left;position:absolute;min-width:13em;z-index:800;padding:.25em 0}.dropdown .dropdown-content.visible{display:block}.dropdown .dropdown-content.right{right:0}.dropdown .dropdown-content li a{display:block;min-width:9em;font-size:.9375em;border:0;padding:.313em .626em}.dropdown .dropdown-content li a:hover{background:#eee;text-decoration:none;color:#333}.dropdown .dropdown-content li.disabled a,.dropdown .dropdown-content li.disabled a:hover{color:#969696}.dropdown .dropdown-content li .separator-before{border-top:1px solid #ddd}.dropdown .dropdown-content li .separator-after{border-bottom:1px solid #ddd}.dropdown .button+.dropdown-content{margin-top:2em;margin-bottom:2em}.profile .section.detailed-information h4{margin-bottom:.626em;padding-bottom:.313em;border-bottom:1px dotted #ddd}.profile .section.detailed-information li .label{float:left;width:6em;color:#969696}.profile .section.detailed-information li .value{display:inline-block}.profile .section.detailed-information .avatar img{width:2.875em;padding:.125em}.profile .section .section-action{padding-left:1em}.profile .edit-avatar-action .fa{margin-top:.25em}.tags-list{zoom:1}.tags-list:after,.tags-list:before{content:"";display:table}.tags-list:after{clear:both}.tags-list:after,.tags-list:before{content:"";display:table}.tags-list:after{clear:both}.tags-list .tag-list-item{float:left;max-width:95%}.tag-editor{margin-bottom:1em}.tag-editor .message{font-size:1em;margin-top:.5em}.tag-editor .message span.svg-icon{display:initial;margin-left:-.15em;padding-right:.15em}.tag-editor .tag{padding-right:0}.tag-editor .tag-content{padding-right:.5em;padding-top:.25em;display:inline-block;max-width:90%}.tag-editor .tag-delete{border-left:1px dotted #2894df;margin-left:-.25em;padding-left:.5em;font-size:.75em;vertical-align:30%;cursor:pointer}.tag-editor .tag:hover:after{border-color:#bbb}.tag-editor .tag-editor-input-wrapper{zoom:1;min-height:5em;padding:.5em;border:1px solid #ccc;border-top:1px solid #bbb;position:relative}.tag-editor .tag-editor-input-wrapper:after,.tag-editor .tag-editor-input-wrapper:before{content:"";display:table}.tag-editor .tag-editor-input-wrapper:after{clear:both}.tag-editor .tag-editor-input-wrapper:after,.tag-editor .tag-editor-input-wrapper:before{content:"";display:table}.tag-editor .tag-editor-input-wrapper:after{clear:both}.tag-editor .tag-editor-input-wrapper.input-focus{border-color:#2894df}.tag-editor .tag-editor-input{min-height:1.5em;padding:.2em .4em 0 .4em;box-shadow:none;min-width:6em;max-width:24em;float:left;word-break:break-word}.tag-editor .autocomplete-suggestions{text-align:left;cursor:default;border:1px solid #ddd;border-top:0;background:#fff;box-shadow:0 0 10px 0 #ddd;position:absolute;display:inline-flex;z-index:9999;max-height:120px;overflow:hidden;overflow-y:auto;box-sizing:border-box}.tag-editor .autocomplete-suggestions .autocomplete-content.scroll{overflow-y:auto}.tag-editor .autocomplete-suggestions .autocomplete-content.scroll .name.ellipsis{display:block}.tag-editor .autocomplete-suggestions .autocomplete-suggestion.row{position:relative;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:#333;display:block;padding:.35em .43em;border:0}.tag-editor .autocomplete-suggestions .autocomplete-suggestion.row.selected{background:#eee}.tag-editor .tag,.tags-list .tag{padding:0 .25em;float:left;position:relative;margin:0 .5em .5em 0;text-decoration:none;border-radius:3px;max-width:95%;border:1px solid #2894df;background:rgba(42,156,235,.05);color:#2894df}.tag-editor .tag:active,.tag-editor .tag:hover,.tags-list .tag:active,.tags-list .tag:hover{border:1px solid #2894df;background:#2894df;color:#fff}.announcement{margin:0;top:0;position:absolute;height:2em;padding-top:.425em;font-size:.85em;text-align:center;background:#fef0bf;color:#000;width:100%}.announcement a{color:#000;display:inline-block;margin-left:.5em;border-bottom-color:#666}.announcement .announcement-close{float:right;border:0;margin-right:.5em;margin-top:.15em}.announcement+#container.page{top:2em}.jfilestyle{display:inline-block;margin:0 0 10px 0;padding:0;position:relative;border-collapse:separate}.jfilestyle input,.jfilestyle label{font-family:"Open Sans",Verdana,sans-serif}.jfilestyle input{border:1px solid #ddd;background:#fff;margin:0 -5px 0 0;vertical-align:middle;padding:10px 15px;font-size:14px;border-radius:0;color:#8d8d8d;cursor:default;line-height:normal}.jfilestyle input[type=email]:disabled,.jfilestyle input[type=password]:disabled,.jfilestyle input[type=search]:disabled,.jfilestyle input[type=text]:disabled{background-color:#f3f3f3;padding:.375em .625em .375em .625em;font-size:1em;line-height:24px;border:1px solid #ccc}.jfilestyle label{display:inline-block;border:1px solid #ccc;border-left:0;background:#ddd;padding:.65em 1.225em .65em 1.225em;color:#0662ba;vertical-align:middle;line-height:normal;text-align:center;margin:0;font-size:.875em;width:auto;font-weight:700;border-radius:0 .214em .214em 0;max-height:18px}.jfilestyle label[disabled]{pointer-events:none;opacity:.6;cursor:not-allowed}.jfilestyle label:hover{cursor:pointer;opacity:.9;border:1px solid #bbb;border-left:0}.jfilestyle label.primary{background:#2894df;border:1px solid #2894df}.jfilestyle label.primary span{color:#fff}.jfilestyle label.primary span:before{color:#fff}.jfilestyle label span{color:#333}.jfilestyle label span:before{font-family:FontAwesome;content:"\f093";display:block;width:1em;height:1.3em;line-height:1.3em;background-size:1em 1em;float:left;color:#333;margin-right:.5em}.jfilestyle.primary label{background:#2894df}.jfilestyle.primary label span{color:#fff}.jfilestyle.primary label span:before{color:#fff}.jfilestyle .jfilestyle-corner input:last-child,.jfilestyle .jfilestyle-corner label:last-child{margin-left:-1px}.jfilestyle .count-jfilestyle{background:#303030;color:#fff;border-radius:50%;padding:1px 5px;font-size:12px;vertical-align:middle}.required .jfilestyle label:after{content:none}.panel.aside .comments{width:100%}.panel.aside .comments .wrap-right-column{float:left;width:100%}.panel.aside .comments .right-column{margin-left:2.5em}.panel.aside .comments .left-column{float:left;width:2em;margin-left:-100%}.panel.aside .comments .comment{padding-bottom:.5em;zoom:1}.panel.aside .comments .comment:after,.panel.aside .comments .comment:before{content:"";display:table}.panel.aside .comments .comment:after{clear:both}.panel.aside .comments .comment:after,.panel.aside .comments .comment:before{content:"";display:table}.panel.aside .comments .comment:after{clear:both}.panel.aside .comments .comment p{background:#eee;padding:1em;margin-left:10px}.panel.aside .comments .comment .author.picture{width:2em;height:2em;margin-top:.25em}.panel.aside .comments .comment .author.picture img{width:2em;height:2em}.panel.aside .comments .comment .author.picture a{border:0}.panel.aside .comments .comment .metadata{font-style:italic;margin:.5em 0 1em 1em}.panel.aside .comments .comment .metadata .author.username{max-width:48%;float:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.panel.aside .comments .comment .metadata .author.username:after{content:', '}.panel.aside .comments .comment .metadata .modified{max-width:48%;padding-right:2px;padding-left:4px;color:#969696;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.panel.aside .comments .comment .form-content{margin-left:.5em}.panel.aside .comments .comment .actions{float:right;margin-top:-2.25em;margin-right:0}.panel.aside .comments .comment p:before{content:' ';position:absolute;width:1px;height:1px;border-top:10px solid transparent;border-bottom:10px solid transparent;border-right:10px solid #eee;margin-left:-1.8em;margin-top:-.5em}.panel.aside .comments .comment a.delete-comment{border:0}.panel.aside .activity div.actions{margin-top:1em;text-align:center}.panel .navigation{font-size:.9375em}.panel .navigation li{zoom:1}.panel .navigation li:after,.panel .navigation li:before{content:"";display:table}.panel .navigation li:after{clear:both}.panel .navigation li:after,.panel .navigation li:before{content:"";display:table}.panel .navigation li:after{clear:both}.panel .navigation.first{border-bottom:1px dotted #ddd;padding:.625em 0;margin-bottom:.625em}.panel .navigation .row{float:left;width:100%;box-sizing:border-box}.panel .navigation .row:hover{background:#f3f3f3}.panel .navigation .row.no-hover:hover{background:0 0}.panel .navigation .row.selected{font-weight:700}.panel .navigation .row .main-cell-wrapper{float:left;width:100%}.panel .navigation .row .main-cell{margin:0 1.5em 0 0}.panel .navigation .row .main-cell h3{border:0;font-size:1em;margin:0 .25em 0 1em;padding:.25em 0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.panel .navigation .row .main-cell h3 a{padding-top:0;padding-bottom:0}.panel .navigation .row .main-cell span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis;display:block}.panel .navigation .row .main-cell a{border:0;padding:.2em .313em;padding-left:1em;color:#333;display:block}.panel .navigation .row .right-cell{float:right;width:1em;height:1em;margin-right:1em;margin-top:-1.5em}.panel .navigation .row .right-cell a{display:none;width:1em;height:1em;padding:.125em;color:#888;border:1px solid transparent}.panel .navigation .row .right-cell a .svg-icon{position:absolute;width:16px;height:16px;line-height:16px;text-align:center;vertical-align:center}.panel .navigation .row .right-cell a .svg-icon svg{fill:#888}.panel .navigation .row .right-cell a .svg-icon svg:hover{fill:#2894df}.panel .navigation .row .right-cell a:hover{color:#2894df;background:#fff}.panel .navigation .row.title .right-cell{margin-top:-1.825em}.panel .navigation .row:hover{background:#f3f3f3}.panel .navigation .row:hover .right-cell a{display:block}.contextual-menu{position:absolute;background:#fff;border:1px solid #ddd;width:12em;box-shadow:0 0 10px 0 #ddd;z-index:993;left:11.25em;display:none;padding:.25em 0}.contextual-menu a{font-size:.875em;display:block;padding:.357em .714em;border:0}.contextual-menu a:hover{color:#333;background:#eee}.contextual-menu li.disabled a{color:#ddd}.contextual-menu li.disabled a:hover{color:#ddd;background:#eee}.contextual-menu .separator-before{border-top:1px solid #ddd}.contextual-menu .separator-after{border-bottom:1px solid #ddd}.panel.aside{position:absolute;right:2em;min-width:25em;width:30%;bottom:0;height:100%;background:#fff;box-shadow:0 0 10px 0 #ddd}.panel.aside .sidebar-header{zoom:1}.panel.aside .sidebar-header:after,.panel.aside .sidebar-header:before{content:"";display:table}.panel.aside .sidebar-header:after{clear:both}.panel.aside .sidebar-header:after,.panel.aside .sidebar-header:before{content:"";display:table}.panel.aside .sidebar-header:after{clear:both}.panel.aside .sidebar-header h3{float:left;width:67%;margin-top:.88em;padding:0 0 0 .6em;display:table-cell;vertical-align:middle;border:0}.panel.aside .sidebar-header h3 .title-wrapper{width:100%;line-height:1.2em;font-size:1.2em}.panel.aside .sidebar-header h3 .title-wrapper .name{max-width:85%;float:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.panel.aside .sidebar-header h3 .title-wrapper .title-link{border:0;color:#666;font-size:.875em;margin-left:.429em;vertical-align:bottom}.panel.aside .sidebar-header h3 .title-wrapper .title-link:hover{color:#2894df}.panel.aside .sidebar-header h3 .title-wrapper .title-link .svg-icon{margin-top:.25em}.panel.aside .sidebar-header h3 .subtitle{font-size:.8em;color:#969696;font-weight:400;display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.panel.aside .sidebar-header .teaser-image{margin:1em 0 1em 1em;float:left;width:3em;height:3em;border:1px solid #ddd;line-height:3em;text-align:center;border-radius:50%}.panel.aside .sidebar-header .teaser-image .svg-icon svg{width:1.5rem;height:1.5rem;margin-top:.75em}.panel.aside .sidebar-header .teaser-image img{width:3em;height:3em}.panel.aside .sidebar-header .dialog-close{position:absolute;right:1em;top:1em}.panel.aside .sidebar-section{margin-top:-1px;border-bottom:1px solid #f3f3f3;border-top:1px solid #f3f3f3;font-size:.875em;background:#fff}.panel.aside .sidebar-section h4{font-size:1.125em;padding:1em 1.125em;margin:0;display:block}.panel.aside .sidebar-section .accordion-content{padding:1.25em 1.25em 1.5em 1.25em;background:#fff}.panel.aside .sidebar-section .accordion-content .section-action{font-size:1.125em;position:absolute;margin-top:-3.1em;right:3.25em;width:1.125em;height:1.125em;z-index:401;border:0}.panel.aside .detailed-information li{zoom:1;padding-bottom:.25em}.panel.aside .detailed-information li:after,.panel.aside .detailed-information li:before{content:"";display:table}.panel.aside .detailed-information li:after{clear:both}.panel.aside .detailed-information li:after,.panel.aside .detailed-information li:before{content:"";display:table}.panel.aside .detailed-information li:after{clear:both}.panel.aside .detailed-information li span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.panel.aside .detailed-information li .label{width:30%;display:inline-block;margin-right:10%;float:left;height:1.8em}.panel.aside .detailed-information li .value{width:60%;display:inline-block;float:left;height:1.8em}.panel.aside .description-content{white-space:pre-line}.panel.aside .description-editor .actions{margin:.5em 0 1em 0}.panel.aside .description-editor .actions .description-lock{float:left}.panel.aside .description-editor .actions .description-lock span.tooltip.tooltip-right{white-space:normal}.panel.aside .description-editor .actions .description-lock span.tooltip.tooltip-right .svg-icon{overflow:initial}.panel.aside .key-information .fingerprint .value{height:3.2em}.panel.aside .key-information textarea{min-height:30em;line-height:normal;margin-top:.8em;width:100%;box-sizing:border-box}.panel.aside .key-information li{zoom:1;padding-bottom:.25em}.panel.aside .key-information li:after,.panel.aside .key-information li:before{content:"";display:table}.panel.aside .key-information li:after{clear:both}.panel.aside .key-information li:after,.panel.aside .key-information li:before{content:"";display:table}.panel.aside .key-information li:after{clear:both}.panel.aside .key-information li span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.panel.aside .key-information li .label{width:30%;display:inline-block;margin-right:10%;float:left;height:1.8em}.panel.aside .key-information li .value{width:60%;display:inline-block;float:left;height:1.8em}.panel.aside .form-content{margin:0 0 .5em 0;zoom:1}.panel.aside .form-content:after,.panel.aside .form-content:before{content:"";display:table}.panel.aside .form-content:after{clear:both}.panel.aside .form-content:after,.panel.aside .form-content:before{content:"";display:table}.panel.aside .form-content:after{clear:both}.panel.aside .form-content label{display:none}.panel.aside .form-content textarea{font-size:1em;width:100%;box-sizing:border-box;height:5em;margin-top:0}.panel.aside .form-content .button,.panel.aside .form-content .cancel{float:right;margin-top:-.25em;margin-right:0}.panel.aside .form-content .cancel{margin-right:.5em}.panel.aside .form-content .message.notice{padding:.5em;margin:0;font-size:.929em;background:#daecf9}.panel.aside .form-content .message.notice span:last-child{display:inline-block}@media all and (max-width:1024px){.panel.aside{position:absolute;right:0;width:100%;bottom:0;top:0;height:100%}}.tabs{border-bottom:1px solid #ddd;width:100%}.tabs-nav{border-bottom:1px solid #ddd}.tabs-nav li{display:inline}.tabs-nav li div{display:inline-block}.tabs-nav li a{display:inline-block;padding:.4em 1.75em;margin-left:1.25em;font-size:15px;border:0}.tabs-nav li a.selected{margin-bottom:-1px;border:1px solid #ddd;border-bottom:1px solid #fff;background:#fff;-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em;border-bottom-left-radius:0;border-bottom-right-radius:0}.tab-content{display:none}.tab-content.selected{display:block}.tableview{background:#fff;position:relative;font-size:.9375em;height:100%}.tableview .tableview-header{background:#fff;height:2.125em;overflow-y:scroll;padding-top:.125em}.tableview .tableview-header table{width:100%}.tableview .tableview-header table th.l-cell.sortable .svg-icon svg,.tableview .tableview-header table th.m-cell.sortable .svg-icon svg{margin-left:.55em}.tableview .tableview-content{top:2.125em;bottom:2.45em;padding:0;position:absolute;width:100%;border-top:1px solid #ddd;overflow-y:scroll;background:#fff}.tableview .tableview-content table{width:100%}.tableview table a{border:0}.tableview table td,.tableview table th{text-align:left;padding:.2em .2em;vertical-align:middle}.tableview table th{font-weight:700}.tableview table td div{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.tableview table .s-cell{width:1em;max-width:1em;min-width:1em}.tableview table .m-cell{width:9em;max-width:9em;vertical-align:middle}.tableview table .l-cell{width:14em;max-width:14em}.tableview table .xl-cell{width:19em;max-width:19em}.tableview table .cell-header{display:flex}.tableview table .cell-header-text{flex:0 1 auto;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.tableview table .cell-header-icon-sort .svg-icon{display:inherit}.tableview table tbody tr{height:2em}.tableview table tbody tr.selected{background:#fffae7}.tableview table tbody tr:hover{background:#f8f8f8}.tableview table tbody tr.selected:hover{background:#fffae7}.tableview table tbody tr.inactive{color:#969696}.tableview table td.cell-multiple-select,.tableview table th.cell-multiple-select{text-align:center}.tableview table td.cell-multiple-select .input.checkbox label,.tableview table th.cell-multiple-select .input.checkbox label{display:none}.tableview table td.password .secret{float:left;max-width:calc(100% - 1.8rem)}.tableview table td.password .password-view{display:none;position:relative;box-sizing:content-box;margin:0 0 0 .3rem;padding:0 .3rem 0 .3rem;background:0 0;border:0;float:left}.tableview table td.password .password-view.selected{margin-top:.1rem;display:block;background:#eee}.tableview table td.password:hover .password-view{display:block}@supports (-webkit-appearance:none) and (not (overflow:-webkit-marquee)) and (not (-ms-ime-align:auto)) and (not (-moz-appearance:none)){.tableview table td.cell-multiple-select .input.checkbox,.tableview table th.cell-multiple-select .input.checkbox{margin-top:1px}}@media all and (max-width:600px){.tableview table tbody{background:#fffae7}.tableview table tbody td{display:block}.tableview table tbody td.l-cell,.tableview table tbody td.m-cell,.tableview table tbody td.s-cell,.tableview table tbody td.s2-cell{max-width:90%;width:90%;padding:.5em}.tableview table tbody td.s-cell{display:none}.tableview thead{display:none}.tableview .tableview-content{top:0;border:0;bottom:0}}table.table-info,table.table-info.vertical{border-collapse:separate}table.table-info td,table.table-info.vertical td{padding:.625em;padding-right:1.25em;color:#666}table.table-info td.warning,table.table-info.vertical td.warning{background:#fef0bf}table.table-info td.error,table.table-info.vertical td.error{background:#ffe4e4}table.table-info td .alt.side,table.table-info.vertical td .alt.side{color:#666;float:right}table.table-info td .alt.side:before,table.table-info.vertical td .alt.side:before{content:"\0028"}table.table-info td .alt.side:after,table.table-info.vertical td .alt.side:after{content:"\0029"}table.table-info td+td,table.table-info.vertical td+td{color:#333;padding-right:.625em}table.table-info.horizontal{width:100%;border-collapse:collapse;padding:0;border:1px solid #ddd;margin-bottom:1em}table.table-info.horizontal.with-borders td,table.table-info.horizontal.with-borders th{border-right:1px solid #ddd}table.table-info.horizontal td,table.table-info.horizontal th{padding:.5em .5em .5em .75em;margin:0}table.table-info.horizontal th{border-bottom:1px solid #ddd}table.table-info.horizontal th a{display:block;border:0}table.table-info.horizontal th a.sortable .svg-icon svg{top:.15em;position:relative;margin-left:.5em}table.table-info.horizontal thead tr{background:#fff}table.table-info.horizontal tbody{background:#fff}table.table-info.horizontal tbody tr:nth-child(odd){background:#fff}table.table-info.horizontal tbody tr:hover{background:#f8f8f8}.table-info-pagination .pagination-limit{margin-top:-.5em;float:left}.table-info-pagination .pagination-limit label{float:left;font-weight:400}.table-info-pagination .pagination-limit select{float:left;margin:.35em}.table-info-pagination .pagination-pages{float:right}.table-info-pagination .pagination-pages .page-location{float:left}.table-info-pagination .pagination-pages .page-buttons{float:right}.table-info-pagination .pagination-pages .button{float:left;margin:-.5em 0 0 .75em;padding:.35em .75em}.tooltip,[data-tooltip]{position:relative;cursor:pointer}.tooltip:after,.tooltip:before,[data-tooltip]:after,[data-tooltip]:before{position:absolute;visibility:hidden;opacity:0;transition:opacity .2s ease-in-out,visibility .2s ease-in-out,transform .2s cubic-bezier(.71,1.7,.77,1.24);transform:translate3d(0,0,0);pointer-events:none;text-align:center}.always-show:after,.always-show:before,.tooltip:focus:after,.tooltip:focus:before,.tooltip:hover:after,.tooltip:hover:before,[data-tooltip]:focus:after,[data-tooltip]:focus:before,[data-tooltip]:hover:after,[data-tooltip]:hover:before{visibility:visible;opacity:1}.tooltip:before,[data-tooltip]:before{z-index:990;border:6px solid transparent;background:0 0;content:""}.tooltip:after,[data-tooltip]:after{z-index:990;padding:8px;width:160px;background-color:#333;color:#fff;content:attr(data-tooltip);font-size:14px;line-height:1.2em;font-weight:400;text-shadow:none}.tooltip-top:after,.tooltip-top:before,.tooltip:after,.tooltip:before,[data-tooltip]:after,[data-tooltip]:before{bottom:100%;left:50%}.tooltip-top:before,.tooltip:before,[data-tooltip]:before{margin-left:-6px;margin-bottom:-12px;border-top-color:#333}.tooltip-top:after,.tooltip:after,[data-tooltip]:after{margin-left:-80px}.tooltip-top.always-show:after,.tooltip-top.always-show:before,.tooltip-top:focus:after,.tooltip-top:focus:before,.tooltip-top:hover:after,.tooltip-top:hover:before,.tooltip.always-show:after,.tooltip.always-show:before,.tooltip:focus:after,.tooltip:focus:before,.tooltip:hover:after,.tooltip:hover:before,[data-tooltip]:focus:after,[data-tooltip]:focus:before,[data-tooltip]:hover:after,[data-tooltip]:hover:before{-webkit-transform:translateY(-12px);-moz-transform:translateY(-12px);transform:translateY(-12px)}.tooltip-left:after,.tooltip-left:before{right:100%;bottom:50%;left:auto}.tooltip-left:before{margin-left:0;margin-right:-12px;margin-bottom:0;border-top-color:transparent;border-left-color:#333;border-left-color:hsla(0,0%,20%,.9)}.tooltip-left.always-show:after,.tooltip-left.always-show:before,.tooltip-left:focus:after,.tooltip-left:focus:before,.tooltip-left:hover:after,.tooltip-left:hover:before{-webkit-transform:translateX(-12px);-moz-transform:translateX(-12px);transform:translateX(-12px)}.tooltip-bottom:after,.tooltip-bottom:before{top:100%;bottom:auto;left:50%}.tooltip-bottom:before{margin-top:-12px;margin-bottom:0;border-top-color:transparent;border-bottom-color:#333;border-bottom-color:hsla(0,0%,20%,.9)}.tooltip-bottom.always-show:after,.tooltip-bottom.always-show:before,.tooltip-bottom:focus:after,.tooltip-bottom:focus:before,.tooltip-bottom:hover:after,.tooltip-bottom:hover:before{-webkit-transform:translateY(12px);-moz-transform:translateY(12px);transform:translateY(12px)}.tooltip-right:after,.tooltip-right:before{bottom:50%;left:100%}.tooltip-right:before{margin-bottom:0;margin-left:-12px;border-top-color:transparent;border-right-color:#333}.tooltip-right.always-show:after,.tooltip-right.always-show:before,.tooltip-right:focus:after,.tooltip-right:focus:before,.tooltip-right:hover:after,.tooltip-right:hover:before{-webkit-transform:translateX(12px);-moz-transform:translateX(12px);transform:translateX(12px)}.tooltip-left:before,.tooltip-right:before{top:3px}.tooltip-left:after,.tooltip-right:after{margin-left:0;margin-bottom:-16px}.tooltip.large:after,[data-tooltip].large:after{width:240px}.tooltip-left.large:after,.tooltip-left.large:before,.tooltip-right.large:after,.tooltip-right.large:before{margin-top:6px}.tooltip-alt{cursor:pointer;display:inline-block;border-bottom:0}.tooltip-alt .tooltip-text{position:absolute;visibility:hidden;width:180px;background-color:#333;color:#fff;padding:.5em 1em;transition:opacity .2s ease-in-out,visibility .2s ease-in-out,transform .2s cubic-bezier(.71,1.7,.77,1.24);transform:translate3d(0,0,0);z-index:99999}.tooltip-alt .tooltip-text.right{margin-left:1em;margin-top:-1.75em}.tooltip-alt .tooltip-text.right::after{content:" ";position:absolute;top:1.125em;right:100%;margin-top:-5px;border:5px #333 solid;border-color:transparent #333 transparent transparent}.tooltip-alt:hover .tooltip-text{transform:translateX(12px);visibility:visible;opacity:1}.tooltip svg,.tooltip-alt svg{fill:#666;top:.125em;position:relative}.footer{margin-top:0;position:fixed;bottom:0;padding:1.167em 1.5% 0 .5%;text-align:right;font-size:.75em;width:98%;height:2em;background:#fff;border-top:1px solid #ddd;z-index:890}.footer .footer-links{width:100%;margin-top:-.25em}.footer .footer-links li{display:inline;margin-right:1.5em}.footer .footer-links li.error-message a{background-color:#fff;color:#d40101}.footer .footer-links li .github-star{display:inline;position:absolute;margin-left:-8em;margin-top:-1px}.footer .footer-links a:not(.gh-btn):not(.gh-count){border:0}.input-password-wrapper{width:100%;box-sizing:border-box;zoom:1}.input-password-wrapper:after,.input-password-wrapper:before{content:"";display:table}.input-password-wrapper:after{clear:both}.input-password-wrapper:after,.input-password-wrapper:before{content:"";display:table}.input-password-wrapper:after{clear:both}.input-password-wrapper .input.password{float:left;width:65.5%}.input-password-wrapper .input.password input[type=password],.input-password-wrapper .input.password input[type=text]{width:calc(100% - 3.5em);box-sizing:border-box}.input-password-wrapper .input.password input[type=password].decrypting{background:transparent url(../../../img/controls/loading_light.svg) no-repeat 90% center}.input-password-wrapper .actions.inline{float:right;width:30%}.input-password-wrapper .actions.inline .button{float:left;width:3.75em;padding-left:0;padding-right:0}.input-password-wrapper .actions.inline .button+.button{margin-left:.5em}.input-password-wrapper .password-complexity .complexity-text{float:right;clear:right;width:30%;font-size:11px;text-align:left;padding-left:0}.input-password-wrapper .password-complexity.not_available .complexity-text{color:#ddd}.input-password-wrapper .password-complexity .progress{width:65.5%;box-sizing:border-box;border:1px solid #ddd;height:10px;display:block;clear:both;margin:.25em 0 .5em 0;float:left}.input-password-wrapper .password-complexity .progress-bar{background:#000;width:0;height:6px;display:block;float:left;margin:1px}.input-password-wrapper .password-complexity .progress-bar.very-weak{background:#000;width:5%}.input-password-wrapper .password-complexity .progress-bar.weak{background:#d40101;width:10%}.input-password-wrapper .password-complexity .progress-bar.fair{background:#ffbd2e;width:60%}.input-password-wrapper .password-complexity .progress-bar.strong{background:#6c0;width:80%}.input-password-wrapper .password-complexity .progress-bar.very-strong{background:#090;width:99.5%}@media all and (max-width:400px){.input-password-wrapper .input.password{float:left;width:100%}.input-password-wrapper .actions.inline{width:50%;float:left;margin-bottom:.5em}.input-password-wrapper .password-complexity .progress{display:none}.input-password-wrapper .password-complexity .complexity-text{float:left;width:50%;font-size:.831em;margin-top:.5em}.fa.fa-lg{line-height:1}}.colorpicker{margin-top:1.5em}.colorpicker .input.color{float:left;margin-top:70px;margin-left:240px}.colorpicker .input input[type=text].token{width:4em;font-size:1.25em;height:auto;line-height:2em;border:none;-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em;text-align:center}.colorpicker .farbtastic{position:relative}.colorpicker .farbtastic *{position:absolute;cursor:crosshair}.colorpicker .farbtastic,.colorpicker .farbtastic .wheel{width:195px;height:195px}.colorpicker .farbtastic .color,.colorpicker .farbtastic .overlay{top:47px;left:47px;width:101px;height:101px}.colorpicker .farbtastic .wheel{background:url(../../../img/controls/colorpicker/wheel.png) no-repeat;width:195px;height:195px}.colorpicker .farbtastic .overlay{background:url(../../../img/controls/colorpicker/mask.png) no-repeat}.colorpicker .farbtastic .marker{width:17px;height:17px;margin:-8px 0 0 -8px;overflow:hidden;background:url(../../../img/controls/colorpicker/marker.png) no-repeat}.gpgkey.input.textarea textarea{height:24em;width:95%}.dialog .permission-add{background-color:#fff;padding:0 1em 1em 1em;zoom:1}.dialog .permission-add:after,.dialog .permission-add:before{content:"";display:table}.dialog .permission-add:after{clear:both}.dialog .permission-add:after,.dialog .permission-add:before{content:"";display:table}.dialog .permission-add:after{clear:both}.dialog .permission-edit{padding:0;border-bottom:1px solid #f3f3f3}.permissions.scroll{max-height:19em;margin:0}.permissions .row{zoom:1;font-size:.813em;padding:1em .5em .875em 1em;border-bottom:1px dotted #ddd}.permissions .row:after,.permissions .row:before{content:"";display:table}.permissions .row:after{clear:both}.permissions .row:after,.permissions .row:before{content:"";display:table}.permissions .row:after{clear:both}.permissions .row:last-child{border:0}.permissions .row .avatar img{float:left}.permissions .row .group,.permissions .row .user{width:48%;float:left;padding-left:1em;padding-right:1em}.permissions .row .details{zoom:1}.permissions .row .details:after,.permissions .row .details:before{content:"";display:table}.permissions .row .details:after{clear:both}.permissions .row .details:after,.permissions .row .details:before{content:"";display:table}.permissions .row .details:after{clear:both}.permissions .row .details .name{float:left;max-width:190px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.permissions .row .details .more_details{margin-left:.5em;margin-top:0}.permissions .row .details .email{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis;font-weight:700;padding-bottom:.125em}.permissions .row .permission_changes{color:#969696;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis;display:block;border:0}.permissions .row .rights{float:left;margin-top:.5em;width:27%}.permissions .row .rights select{margin:0;font-size:1em}.permissions .row .actions{float:right;width:3em;margin-top:.55em}.permissions .row .actions a{font-size:1.125em;padding:.25em .5em .2em .5em;border:1px solid transparent}.permissions .row .actions a:hover{border:1px solid #ddd}.permissions .row.permission-updated{background:#fffae7}.dialog .message.empty-permission{font-size:.875em;padding-left:1em}@media all and (max-width:480px){.dialog .permissions.scroll{height:auto;margin:0}.dialog .permissions .row .group,.dialog .permissions .row .user{width:45%}}.accordion .accordion-header{zoom:1}.accordion .accordion-header:after,.accordion .accordion-header:before{content:"";display:table}.accordion .accordion-header:after{clear:both}.accordion .accordion-header:after,.accordion .accordion-header:before{content:"";display:table}.accordion .accordion-header:after{clear:both}.accordion .accordion-header a{color:#333;display:block}.accordion .accordion-header a:hover{color:#2894df}.accordion .accordion-content{zoom:1}.accordion .accordion-content:after,.accordion .accordion-content:before{content:"";display:table}.accordion .accordion-content:after{clear:both}.accordion .accordion-content:after,.accordion .accordion-content:before{content:"";display:table}.accordion .accordion-content:after{clear:both}.accordion .accordion-content .processing-wrapper{background-color:#fff;padding:.5em 1.5em}.accordion .accordion-content .processing-wrapper .processing-text{position:relative;padding-left:1.5em}.accordion .accordion-content .processing-wrapper .processing-text:before{width:100%;height:100%;position:absolute;content:" ";top:0;left:0;background:transparent url(../../../img/controls/loading_light.svg) left center no-repeat;background-size:auto 75%}.accordion.closed .accordion-content{display:none}.dialog .accordion .accordion-header{margin-bottom:.5em}.dialog .accordion .accordion-header a{display:inline;border-bottom:0}.accordion.sidebar-section .accordion-header a{border:0}.accordion.sidebar-section .accordion-header a .svg-icon{position:absolute;right:1.5em;margin-top:.25em}.accordion.navigation .accordion-header .main-cell a:before{margin-left:-1.25em;margin-top:.25em;content:"\f0d7";font:normal normal normal 1em FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;width:1em;height:1em;position:absolute;text-align:center}.accordion.navigation .accordion-content>.empty-content{padding:0 0 0 2.5em}.autocomplete-suggestions{text-align:left;cursor:default;border:1px solid #ddd;border-top:0;background:#fff;box-shadow:0 0 10px 0 #ddd;position:absolute;display:none;z-index:9999;max-height:120px;overflow:hidden;overflow-y:auto;box-sizing:border-box;width:350px}.autocomplete-suggestions .autocomplete-suggestion{position:relative;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:#333;font-size:.875em;display:block;padding:.357em .714em;border:0}.autocomplete-suggestions .autocomplete-suggestion b{font-weight:400;color:#333}.autocomplete-suggestions .autocomplete-suggestion.selected{background:#eee}/*! +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}div{outline:0}html body .hidden{display:none}.visually-hidden,.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visually-hidden .focusable:active,.visually-hidden .focusable:focus,.visuallyhidden .focusable:active,.visuallyhidden .focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.clearfix:after,.clearfix:before{content:"";display:table}.clearfix:after{clear:both}.rounded{-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem}.ellipsis{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.rotate-right{transform:rotate(-90deg)}html{line-height:normal}body{color:#333;background:#fff}body.iframe{background:0 0}@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;src:local('Open Sans'),local('OpenSans'),url('../../../fonts/opensans-regular.woff') format('woff')}@font-face{font-family:'Open Sans';font-style:normal;font-weight:700;src:local('Open Sans Bold'),local('OpenSans-Bold'),url('../../../fonts/opensans-bold.woff') format('woff')}body{font-family:'Open Sans',Verdana,sans-serif}.code{font-family:'Courier New',Courier,monospace}html{font-size:62.5%}body{font-size:1.6rem}h1{font-size:2.2rem;line-height:3.2rem}h2{font-size:1.8rem;line-height:2.4rem}h3{font-size:1.6rem;line-height:2.4rem;border-bottom:1px dotted #ddd}p{font-size:1.5rem;line-height:2.2rem;margin-top:0}code{font-size:1.1rem}.font-dim{color:#666}a{outline:0;text-decoration:none;cursor:pointer;border-bottom:1px solid #ddd}a:link,a:visited{color:#333}a:hover{text-decoration:none;cursor:pointer;color:#2894df;border-bottom:1px solid #2894df}a:active,a:focus{outline:0;color:#2894df;border:0}a.no-border,a:hover.no-border{border-bottom:0}a.disabled{outline:0;text-decoration:none;cursor:default;color:#888;pointer-events:none}ul{padding:0;margin:0}ul li{list-style:none;padding:0;margin:0}.button,button{font-size:1.4rem;padding:.8rem 1.6rem;line-height:1.6rem;margin-right:.8rem;position:relative;display:inline-block;vertical-align:baseline;outline:0;cursor:pointer;text-align:center;text-decoration:none;border-top:1px solid #ccc;border-right:1px solid #ccc;border-bottom:1px solid #ccc;border-left:1px solid #ccc;color:#333;font-weight:400;text-shadow:1px 1px 0 rgba(255,255,255,.5);background:#f3f3f3;-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem;box-sizing:border-box}.button.medium{font-size:1.6rem;line-height:2.4rem;padding:.8rem 2.4rem}.button.big{font-size:1.8rem;line-height:3.2rem;padding:.8rem 3.2rem}.button.full-width{width:100%}.button.primary{background:#2894df;border:1px solid #2894df;color:#fff;text-shadow:1px 1px 0 rgba(0,0,0,.2)}.button.primary:hover{background:#2a9ceb;border:1px solid #2a9ceb;color:#fff}.button.primary:active,.button.primary:focus{color:#fff;border:1px solid #4271b7;background:#2a9ceb}.button.warning,.button.warning:active,.button.warning:focus,.button.warning:hover{color:#fff;background:#d40101;border:1px solid #d40101;text-shadow:none}.button.warning:active,.button.warning:focus{border:1px solid #92000c}.button.cancel,.button.cancel:active,.button.cancel:focus,.button.cancel:hover,.button.dim,.button.dim:active,.button.dim:focus,.button.dim:hover{background:#fff;font-weight:400}.button:hover{color:#333;text-decoration:none;background:#eee;border:1px solid #bbb}.button:focus{color:#2894df;border:1px solid #2894df}.button:active{color:#2894df;border:1px solid #2894df;background:#eee;position:relative;box-shadow:inset 0 1px 2px rgba(0,0,0,.2)}.button.disabled{cursor:default;color:#888;background:#fff;border:1px solid #ddd;text-shadow:none;font-weight:400}.button.disabled:active,.button.disabled:focus,.button.disabled:hover{box-shadow:0 0 0;top:0;color:#888;background:#fff;border:1px solid #ddd}.button.processing{background:#fff;border:1px solid #fff}.button.processing:after{width:100%;height:100%;position:absolute;content:" ";top:-1px;left:-1px;background:#fff url('../../../img/controls/loading_light.svg') center center no-repeat;background-size:auto 50%;border:1px solid #ddd;-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem}input[type=submit].button.processing{font-size:0;background:#fff url('../../../img/controls/loading_light.svg') center center no-repeat;background-size:auto 50%;border:1px solid #ddd;border-radius:5px}.button-toggle.selected{background:#eee;box-shadow:inset 0 1px 2px rgba(0,0,0,.2);border:1px solid #bbb}.button-group{display:flex;flex-wrap:wrap;justify-content:flex-start}.button-group--nowrap>.button{white-space:nowrap}.button-group>*{flex-grow:1;margin-bottom:.5em}label{font-weight:700;font-size:1.6rem;line-height:2.4rem;padding:.8rem 0;display:block}.required label:after{content:" \002A";color:#d40101;font-weight:700}.input.error label{color:#d40101}input[type=email],input[type=number],input[type=password],input[type=search],input[type=text],textarea{box-sizing:border-box;font-size:1.6rem;line-height:2.4rem;color:#333;background:#fff;width:100%;max-width:64rem;padding:.6rem 1.2rem;display:inline-block;margin-bottom:.8rem;border:1px solid #ccc;border-top:1px solid #bbb;vertical-align:middle}::-ms-reveal{display:none}input[type=number]{box-sizing:border-box}input[type=file]{box-sizing:border-box;font-size:1.6rem;line-height:2.4rem;color:#333;background:#fff;width:100%;max-width:64rem;display:inline-block;margin-bottom:.8rem;vertical-align:middle}input[type=email]:hover,input[type=number]:hover,input[type=password]:hover,input[type=search]:hover,input[type=text]:hover,textarea:hover{border:1px solid #bbb}input[type=email]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=text]:focus,textarea:focus{outline:0;box-shadow:inset 1px 1px 2px rgba(0,0,0,.2);border:1px solid #2894df}input[type=email]:disabled,input[type=number]:disabled,input[type=password]:disabled,input[type=search]:disabled,input[type=text]:disabled,textarea:disabled{color:#333;border:1px solid #ddd;box-shadow:0 0;cursor:default;background:#f3f3f3}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{opacity:1}textarea{height:9rem}.textarea.large textarea{width:30rem;height:9rem}.checkbox input[type=checkbox]{position:absolute;opacity:0;cursor:pointer}.checkbox input[type=checkbox].error{color:#d40101}.checkbox input[type=checkbox]+label{font-weight:400;position:relative;cursor:pointer;padding:0;margin-bottom:.8rem}.checkbox input[type=checkbox]+label:before{content:'';margin-top:.4rem;margin-right:1rem;display:inline-block;vertical-align:text-top;width:1.4rem;height:1.4rem;background:#fff;border:1px solid #ddd;border-radius:.3rem;box-sizing:border-box}.checkbox input[type=checkbox]:hover+label:before{box-shadow:0 .1rem 0 #ddd,inset 0 .1rem 0 rgba(255,255,255,.25)}.checkbox input[type=checkbox]:focus+label:before{box-shadow:0 0 .4rem #2a9ceb;border:1px solid #2a9ceb}.checkbox input[type=checkbox]:active+label:before{box-shadow:inset 0 -.1rem 0 rgba(255,255,255,.25),inset 0 .1rem 0 #ddd}.checkbox input[type=checkbox]:disabled+label{color:#ddd;cursor:auto}.checkbox input[type=checkbox]:disabled+label:before{box-shadow:none;background:#ccc;border:none}.checkbox input[type=checkbox]:checked+label:after{content:'';position:absolute;left:.2rem;top:.75rem;width:1rem;height:1rem;background-size:1rem 1rem;background-image:url('../../../img/controls/check_black.svg');-webkit-mask-image:url('../../../img/controls/check_black.svg');mask-image:url('../../../img/controls/check_black.svg')}.checkbox input[type=checkbox]:disabled+label:after{background:#969696}.checkbox.medium input[type=checkbox]{cursor:pointer}.checkbox.medium input[type=checkbox]+label{font-size:1.5rem}.checkbox.medium input[type=checkbox]+label:before{margin-top:.3rem;margin-right:1rem;width:1.4rem;height:1.4rem}.checkbox.medium input[type=checkbox]:checked+label:after{left:.2rem;top:.7rem;width:1rem;height:1rem;background-size:1rem 1rem}.checkbox.small input[type=checkbox]{cursor:pointer}.checkbox.small input[type=checkbox]+label{font-size:1.4rem}.checkbox.small input[type=checkbox]+label:before{margin-top:.3rem;margin-right:1rem;width:1.4rem;height:1.4rem}.checkbox.small input[type=checkbox]:checked+label:after{left:.2rem;top:.7rem;width:1rem;height:1rem;background-size:1rem 1rem}.radiolist{margin-bottom:.8rem}.radiolist:after,.radiolist:before{content:"";display:table}.radiolist:after{clear:both}.radiolist:after,.radiolist:before{content:"";display:table}.radiolist:after{clear:both}.radiolist .input.radio{float:left}.radiolist .input.radio input{float:left;margin-top:.8rem}.radiolist .input.radio input+label{float:left;font-weight:400;display:inline-block;margin-left:1rem;font-size:1.6rem;margin-right:3.2rem;line-height:1.4rem}.radiolist .input.radio input+label:after{content:initial}.input.toggle-switch{display:flex;padding-top:1.6rem;padding-bottom:.8rem}.input.toggle-switch label{padding-top:0;padding-left:1.6rem;order:2;flex:1;font-weight:400;font-size:1.6rem}.input.toggle-switch.disabled label{color:#969696}.input.toggle-switch .toggle-switch-checkbox{order:-1;flex:0 0 3em;display:none}.input.toggle-switch .toggle-switch-checkbox,.input.toggle-switch .toggle-switch-checkbox *,.input.toggle-switch .toggle-switch-checkbox :after,.input.toggle-switch .toggle-switch-checkbox :before,.input.toggle-switch .toggle-switch-checkbox+.toggle-switch-button,.input.toggle-switch .toggle-switch-checkbox:after,.input.toggle-switch .toggle-switch-checkbox:before{box-sizing:border-box}.input.toggle-switch .toggle-switch-checkbox ::selection,.input.toggle-switch .toggle-switch-checkbox :after::selection,.input.toggle-switch .toggle-switch-checkbox :before::selection,.input.toggle-switch .toggle-switch-checkbox+.toggle-switch-button::selection,.input.toggle-switch .toggle-switch-checkbox::selection,.input.toggle-switch .toggle-switch-checkbox:after::selection,.input.toggle-switch .toggle-switch-checkbox:before::selection{background:0 0}.input.toggle-switch .toggle-switch-button{order:-1;flex:none;outline:0;display:block;width:3em;height:1.5em;position:relative;cursor:pointer;user-select:none;background:#f3f3f3;border-radius:2em;padding:2px;transition:all .4s ease;border:1px solid #ddd}.input.toggle-switch .toggle-switch-button span{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.input.toggle-switch .toggle-switch-button span .focusable:active,.input.toggle-switch .toggle-switch-button span .focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.input.toggle-switch .toggle-switch-button span .focusable:active,.input.toggle-switch .toggle-switch-button span .focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.input.toggle-switch .toggle-switch-button:hover:after{will-change:padding}.input.toggle-switch .toggle-switch-button:active{box-shadow:inset 0 0 0 2em #e8eae9}.input.toggle-switch .toggle-switch-button:active:after{padding-right:.8em}.input.toggle-switch .toggle-switch-button:after,.input.toggle-switch .toggle-switch-button:before{position:relative;display:block;content:"";width:50%;height:100%}.input.toggle-switch .toggle-switch-button:after{left:0;border-radius:2em;background:#fff;transition:left .3s cubic-bezier(.175, .885, .32, 1.275),padding .3s ease,margin .3s ease;box-shadow:0 0 0 1px rgba(0,0,0,.1),0 4px 0 rgba(0,0,0,.08)}.input.toggle-switch .toggle-switch-button:before{display:none}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button:after{left:50%}.input.toggle-switch .toggle-switch-checkbox:disabled+.toggle-switch-button{background:#fff;cursor:not-allowed}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button{background:#6c0}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button:active{box-shadow:none}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button:active:after{margin-left:-.8em}.input.toggle-switch .toggle-switch-checkbox:disabled:checked+.toggle-switch-button{background:#e6ffcc}h1 .input.toggle-switch,h2 .input.toggle-switch,h3 .input.toggle-switch,h4 .input.toggle-switch,h5 .input.toggle-switch,h6 .input.toggle-switch{float:left;padding:.4em 1em 0 0}.input.select select{display:inline-block;font-size:1.6rem;line-height:2.4rem;color:#333;padding:.6rem 1.2rem;width:100%;max-width:64rem;box-sizing:border-box;margin:0 0 .8rem 0;border:1px solid #ccc;-moz-appearance:none;-webkit-appearance:none;appearance:none;background-color:#fff;background-image:url('../../../img/controls/chevron-down_black.svg'),linear-gradient(to bottom,#fff 0,#fff 100%);background-repeat:no-repeat,repeat;background-position:right .6rem top 50%,0 0;background-size:1rem auto,100%}.input.select select.medium{width:50%}.input.select select:focus{outline:0;border:1px solid #2894df;border-radius:0;background-image:url('../../../img/controls/chevron-down_blue.svg'),linear-gradient(to bottom,#fff 0,#fff 100%)}.input.select select:disabled{background-image:url('../../../img/controls/chevron-down_black.svg'),linear-gradient(to bottom,#f3f3f3 0,#f3f3f3 100%);color:#888}.input .error-message{padding:0;font-size:1.4rem;margin-top:.2rem;margin-bottom:1.6rem;background-color:transparent;border:0;font-weight:400;color:#d40101;background:#fff;clear:both}.input .help-message{padding:0;font-size:1.4rem;margin-top:.2rem;margin-bottom:1.6rem;background-color:transparent;border:0;color:#888;font-weight:400;background:#fff;clear:both}.singleline{display:flex;max-width:64rem}.singleline:after,.singleline:before{content:"";display:table}.singleline:after{clear:both}.singleline:after,.singleline:before{content:"";display:table}.singleline:after{clear:both}.singleline .input{flex:1}.singleline .input.first-field,.singleline .input:first-child{margin-right:2%}.slider{display:flex;align-items:center}.slider input[type=range]{-webkit-appearance:none;width:100%;height:1px;border-radius:5px;background:#ddd;outline:0;opacity:.7;-webkit-transition:.2s;transition:opacity .2s;flex-grow:1}.slider input[type=range]:hover{opacity:1}.slider input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:15px;height:15px;border-radius:50%;border-width:1px;border-style:solid;border-color:#969696;background:#ddd;cursor:pointer}.slider input[type=range]::-moz-range-thumb{width:15px;height:15px;border-radius:50%;background:#ddd;cursor:pointer}.slider input[type=number]{width:6.5rem;margin-left:1.6rem;padding:.8rem}.svg-icon{display:inline-flex;align-self:center}.svg-icon svg{fill:#333;height:1em;width:1em}.svg-icon.baseline svg{top:.125em;position:relative}.svg-icon.dim svg,.svg-icon.light svg{fill:#888888}.svg-icon.icon-only svg{padding:.1rem;height:1.6rem;width:1.6rem}a:hover .svg-icon svg{fill:#2894DF}a.disabled .svg-icon svg{fill:#888888;pointer-events:none;cursor:default}a.disabled:hover .svg-icon svg{fill:#888888}a.fav .svg-icon svg{fill:#D40101}a.unfav .svg-icon svg{fill:#DDD}.button .svg-icon svg{top:.2rem;position:relative}.button .svg-icon+span:not(.visuallyhidden){margin-left:.8rem;display:inline-block}.button .svg-icon svg:hover,.button:hover .svg-icon svg{fill:#333}.button.primary:active .svg-icon svg,.button.primary:focus .svg-icon svg,.button.warning .svg-icon svg,.button.warning:active .svg-icon svg,.button.warning:focus .svg-icon svg,.button.warning:hover .svg-icon svg{fill:#FFF}.button:active .svg-icon svg,.button:focus .svg-icon svg{fill:#2894DF}.button.disabled .svg-icon svg{fill:#888888}.button.disabled .svg-icon svg:hover{fill:#888888}@keyframes drawCircle{0%{stroke-dashoffset:180px}100%{stroke-dashoffset:0}}@keyframes drawCheck{0%{stroke-dashoffset:50px}100%{stroke-dashoffset:0}}@keyframes drawCross{0%{stroke-dashoffset:50px}100%{stroke-dashoffset:0}}@keyframes drawWarning{0%{stroke-dashoffset:230px}100%{stroke-dashoffset:0}}.icon-feedback .success-animation-circle{stroke-dasharray:180px 180px;stroke:#009900}.icon-feedback .success-animation-line{stroke-dasharray:50px 50px;stroke:#009900}.icon-feedback .success-animation-line2{stroke-dasharray:50px 50px;stroke:#009900}.icon-feedback .error-animation-circle{stroke-dasharray:180px 180px;stroke:#D40101}.icon-feedback .error-animation-line{stroke-dasharray:50px 50px;stroke:#D40101}.icon-feedback .warning-animation-line{stroke-dasharray:230px 230px;stroke:#9F6000;stroke-linecap:round;stroke-linejoin:round}.icon-feedback .warning-animation-circle{fill:#9F6000}.icon-feedback .animated{animation:.75s ease-out 0s 1 both pop}.icon-feedback .animated .error-animation-circle,.icon-feedback .animated .success-animation-circle,.icon-feedback .animated .warning-animation-circle{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawCircle}.icon-feedback .animated .success-animation-line{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawCheck}.icon-feedback .animated .error-animation-line{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawCross}.icon-feedback .animated .warning-animation-line{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawWarning}table{border-collapse:collapse;border-spacing:0}table td,table th{text-align:left;font-weight:400}.logo{background:transparent url('../../../img/logo/logo.svg') 0 0 no-repeat;background-size:20rem auto;width:20rem;height:4.5rem}.scroll{overflow-y:scroll;background:linear-gradient(#fff 30%,rgba(255,255,255,0)),linear-gradient(rgba(255,255,255,0),#fff 70%) 0 100%,radial-gradient(farthest-side at 75% 0,rgba(0,0,0,.2),rgba(0,0,0,0)),radial-gradient(farthest-side at 75% 100%,rgba(0,0,0,.2),rgba(0,0,0,0)) 0 100%;background-repeat:no-repeat;background-color:#fff;background-size:100% 10px,100% 10px,100% 5px,100% 5px}::-webkit-scrollbar{width:1em}::-webkit-scrollbar-track{background:#f9f9f9;border-top:0;border-bottom:0}::-webkit-scrollbar-thumb{background:#c1c1c1;border-radius:1em;border:4px solid #f9f9f9}.scroll-shadow{overflow:auto;background:linear-gradient(#fff 30%,rgba(255,255,255,0)),linear-gradient(rgba(255,255,255,0),#fff 70%) 0 100%,radial-gradient(farthest-side at 50% 0,rgba(0,0,0,.08),rgba(0,0,0,0)),radial-gradient(farthest-side at 50% 100%,rgba(0,0,0,.08),rgba(0,0,0,0)) 0 100%;background-repeat:no-repeat;background-color:#fff;background-size:100% 40px,100% 40px,100% 14px,100% 14px;background-attachment:local,local,scroll,scroll}.simplebar-content{overflow:auto;background:linear-gradient(#fff 30%,rgba(255,255,255,0)),linear-gradient(rgba(255,255,255,0),#fff 70%) 0 100%,radial-gradient(farthest-side at 50% 0,rgba(0,0,0,.08),rgba(0,0,0,0)),radial-gradient(farthest-side at 50% 100%,rgba(0,0,0,.08),rgba(0,0,0,0)) 0 100%;background-repeat:no-repeat;background-color:#fff;background-size:100% 40px,100% 40px,100% 14px,100% 14px;background-attachment:local,local,scroll,scroll}[data-simplebar]{position:relative;flex-direction:column;flex-wrap:wrap;justify-content:flex-start;align-content:flex-start;align-items:flex-start;width:inherit;height:inherit;max-width:inherit;max-height:inherit}.simplebar-wrapper{overflow:hidden;width:inherit;height:inherit;max-width:inherit;max-height:inherit}.simplebar-mask{direction:inherit;position:absolute;overflow:hidden;padding:0;margin:0;left:0;top:0;bottom:0;right:0;width:auto!important;height:auto!important;z-index:0}.simplebar-offset{direction:inherit!important;box-sizing:inherit!important;resize:none!important;position:absolute;top:0;left:0;bottom:0;right:0;padding:0;margin:0;-webkit-overflow-scrolling:touch}.simplebar-content-wrapper{direction:inherit;box-sizing:border-box!important;position:relative;display:block;height:100%;width:auto;max-width:100%;max-height:100%;scrollbar-width:none;-ms-overflow-style:none}.simplebar-content-wrapper::-webkit-scrollbar,.simplebar-hide-scrollbar::-webkit-scrollbar{width:0;height:0}.simplebar-content:after,.simplebar-content:before{content:' ';display:table}.simplebar-placeholder{max-height:100%;max-width:100%;width:100%;pointer-events:none}.simplebar-height-auto-observer-wrapper{box-sizing:inherit!important;height:100%;width:inherit;max-width:1px;position:relative;float:left;max-height:1px;overflow:hidden;z-index:-1;padding:0;margin:0;pointer-events:none;flex-grow:inherit;flex-shrink:0;flex-basis:0}.simplebar-height-auto-observer{box-sizing:inherit;display:block;opacity:0;position:absolute;top:0;left:0;height:1000%;width:1000%;min-height:1px;min-width:1px;overflow:hidden;pointer-events:none;z-index:-1}.simplebar-track{z-index:1;position:absolute;right:0;bottom:0;pointer-events:none;overflow:hidden}[data-simplebar].simplebar-dragging .simplebar-content{pointer-events:none;user-select:none;-webkit-user-select:none}[data-simplebar].simplebar-dragging .simplebar-track{pointer-events:all}.simplebar-scrollbar{position:absolute;right:2px;width:7px;min-height:10px}.simplebar-scrollbar:before{position:absolute;content:'';background:#000;border-radius:7px;left:0;right:0;opacity:0;transition:opacity .2s linear}.simplebar-scrollbar.simplebar-visible:before{opacity:.5;transition:opacity 0s linear}.simplebar-track.simplebar-vertical{top:0;width:11px}.simplebar-track.simplebar-vertical .simplebar-scrollbar:before{top:2px;bottom:2px}.simplebar-track.simplebar-horizontal{left:0;height:11px}.simplebar-track.simplebar-horizontal .simplebar-scrollbar:before{height:100%;left:2px;right:2px}.simplebar-track.simplebar-horizontal .simplebar-scrollbar{right:auto;left:0;top:2px;height:7px;min-height:0;min-width:10px;width:auto}[data-simplebar-direction=rtl] .simplebar-track.simplebar-vertical{right:auto;left:0}.hs-dummy-scrollbar-size{direction:rtl;position:fixed;opacity:0;visibility:hidden;height:500px;width:500px;overflow-y:hidden;overflow-x:scroll}.simplebar-hide-scrollbar{position:fixed;left:0;visibility:hidden;overflow-y:scroll;scrollbar-width:none;-ms-overflow-style:none}.shimmer{display:block;position:relative;width:100%;height:100%;content:'';animation:shimmer 2s infinite;background:linear-gradient(45deg,rgba(255,255,255,0) 0,rgba(255,255,255,.1) 30%,rgba(255,255,255,.5) 50%,rgba(255,255,255,0))}@keyframes shimmer{0%{background-position:0 0}100%{background-position:1000px 0}}html{scroll-behavior:smooth}.animated{-webkit-animation-duration:.5s;animation-duration:.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both}@keyframes fadeInUp{0%{opacity:0;-webkit-transform:translateY(20px);-ms-transform:translateY(20px);transform:translateY(20px)}100%{opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}}@keyframes fadeOutUp{from{opacity:1}to{opacity:0;transform:translate3d(0,-100%,0)}}@keyframes fadeInDown{from{opacity:0;transform:translate3d(0,-100%,0)}to{opacity:1;transform:none}}@keyframes pop{0%{opacity:0;transform:scale(1)}55%{opacity:1;transform:scale(1)}65%{opacity:1;transform:scale(1.25)}75%{opacity:1;transform:scale(1)}100%{opacity:1;transform:scale(1)}}.fadeInDown{animation-name:fadeInDown}.fadeOutUp{animation-name:fadeOutUp}.fadeInUp{animation-name:fadeInUp}.pop{animation-name:pop}.page{width:100%;min-width:32rem;top:0;left:0;right:0;bottom:0;position:absolute;overflow:auto}.page .panel{height:100%;width:100%;position:absolute;overflow:auto}.page .panel.main{bottom:3.8rem;height:auto;padding:0}.page .panel.main .grid{padding-bottom:1.6rem}.page .panel.main .row{padding-left:0}.page .panel.main .panel.left{background:#fff}.page .header.second+.panel.main{top:6.875em}.page .header.third+.panel.main{top:10em;border-top:1px solid #ddd}.page .header{width:100%;overflow:hidden}.page .header.first{min-height:3.2rem}.page .header.second{height:7rem}.page .header.third{height:4.8rem}.page .col1,.page .col2,.page .col2_3,.page .col3{position:absolute}.page .col1,.page .panel.left{width:17.25%;box-sizing:border-box}.page .panel.middle{width:82%;left:18%;overflow:hidden}.page .panel.middle.scroll{overflow-y:scroll}.page .col2{width:70%;left:18%}.page .col3,.page .panel.right{width:24%;left:75%}.page .col2_3{width:81.5%;left:18%}.page .panel.main .grid-responsive-12{float:left;width:100%;max-width:none;padding-left:.25em;box-sizing:border-box}@media all and (max-width:1024px){.page .panel.left{display:none}.page .header.second .col2{display:none}.page .header.second .col2_3{display:none}.page .header.third{height:4.5em}.page .header.third .col1,.page .header.third .col2,.page .header.third .col2_3{position:relative;float:left;left:auto;width:auto}.page .header.third .col3{display:none}.page .panel.main .panel.left{display:none}.page .panel.main .panel.middle{width:100%;left:0}.page .panel.main .row{padding-left:.5em}.page .panel.main .panel.aside{min-width:auto}}.grid,.grid-responsive-12{margin:0 auto;padding:0;max-width:1220px}.grid .row,.grid-responsive-12 .row{padding:0 .5em 0 .5em}.grid .row:after,.grid .row:before,.grid-responsive-12 .row:after,.grid-responsive-12 .row:before{content:"";display:table}.grid .row:after,.grid-responsive-12 .row:after{clear:both}.grid .row:after,.grid .row:before,.grid-responsive-12 .row:after,.grid-responsive-12 .row:before{content:"";display:table}.grid .row:after,.grid-responsive-12 .row:after{clear:both}.grid .row .col1,.grid .row .col10,.grid .row .col11,.grid .row .col12,.grid .row .col2,.grid .row .col3,.grid .row .col4,.grid .row .col5,.grid .row .col6,.grid .row .col7,.grid .row .col8,.grid .row .col9,.grid-responsive-12 .row .col1,.grid-responsive-12 .row .col10,.grid-responsive-12 .row .col11,.grid-responsive-12 .row .col12,.grid-responsive-12 .row .col2,.grid-responsive-12 .row .col3,.grid-responsive-12 .row .col4,.grid-responsive-12 .row .col5,.grid-responsive-12 .row .col6,.grid-responsive-12 .row .col7,.grid-responsive-12 .row .col8,.grid-responsive-12 .row .col9{position:relative;left:auto;float:none;width:99%;box-sizing:border-box}.grid .row .col10:after,.grid .row .col10:before,.grid .row .col11:after,.grid .row .col11:before,.grid .row .col12:after,.grid .row .col12:before,.grid .row .col1:after,.grid .row .col1:before,.grid .row .col2:after,.grid .row .col2:before,.grid .row .col3:after,.grid .row .col3:before,.grid .row .col4:after,.grid .row .col4:before,.grid .row .col5:after,.grid .row .col5:before,.grid .row .col6:after,.grid .row .col6:before,.grid .row .col7:after,.grid .row .col7:before,.grid .row .col8:after,.grid .row .col8:before,.grid .row .col9:after,.grid .row .col9:before,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col10:before,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col11:before,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col12:before,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col1:before,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col2:before,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col3:before,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col4:before,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col5:before,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col6:before,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col7:before,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col8:before,.grid-responsive-12 .row .col9:after,.grid-responsive-12 .row .col9:before{content:"";display:table}.grid .row .col10:after,.grid .row .col11:after,.grid .row .col12:after,.grid .row .col1:after,.grid .row .col2:after,.grid .row .col3:after,.grid .row .col4:after,.grid .row .col5:after,.grid .row .col6:after,.grid .row .col7:after,.grid .row .col8:after,.grid .row .col9:after,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col9:after{clear:both}.grid .row .col10:after,.grid .row .col10:before,.grid .row .col11:after,.grid .row .col11:before,.grid .row .col12:after,.grid .row .col12:before,.grid .row .col1:after,.grid .row .col1:before,.grid .row .col2:after,.grid .row .col2:before,.grid .row .col3:after,.grid .row .col3:before,.grid .row .col4:after,.grid .row .col4:before,.grid .row .col5:after,.grid .row .col5:before,.grid .row .col6:after,.grid .row .col6:before,.grid .row .col7:after,.grid .row .col7:before,.grid .row .col8:after,.grid .row .col8:before,.grid .row .col9:after,.grid .row .col9:before,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col10:before,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col11:before,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col12:before,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col1:before,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col2:before,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col3:before,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col4:before,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col5:before,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col6:before,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col7:before,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col8:before,.grid-responsive-12 .row .col9:after,.grid-responsive-12 .row .col9:before{content:"";display:table}.grid .row .col10:after,.grid .row .col11:after,.grid .row .col12:after,.grid .row .col1:after,.grid .row .col2:after,.grid .row .col3:after,.grid .row .col4:after,.grid .row .col5:after,.grid .row .col6:after,.grid .row .col7:after,.grid .row .col8:after,.grid .row .col9:after,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col9:after{clear:both}.grid .row .col1 img,.grid .row .col10 img,.grid .row .col11 img,.grid .row .col12 img,.grid .row .col2 img,.grid .row .col3 img,.grid .row .col4 img,.grid .row .col5 img,.grid .row .col6 img,.grid .row .col7 img,.grid .row .col8 img,.grid .row .col9 img,.grid-responsive-12 .row .col1 img,.grid-responsive-12 .row .col10 img,.grid-responsive-12 .row .col11 img,.grid-responsive-12 .row .col12 img,.grid-responsive-12 .row .col2 img,.grid-responsive-12 .row .col3 img,.grid-responsive-12 .row .col4 img,.grid-responsive-12 .row .col5 img,.grid-responsive-12 .row .col6 img,.grid-responsive-12 .row .col7 img,.grid-responsive-12 .row .col8 img,.grid-responsive-12 .row .col9 img{width:100%;height:auto;display:block}@media all and (min-width:780px){.grid .row{padding:0 .5em 0 .5em}.grid .row .col1,.grid .row .col10,.grid .row .col11,.grid .row .col12,.grid .row .col2,.grid .row .col3,.grid .row .col4,.grid .row .col5,.grid .row .col6,.grid .row .col7,.grid .row .col8,.grid .row .col9{float:left;position:relative;margin:0 3% 0 0}.grid .row .last{margin-right:0}.grid .row .col1{width:5.5%}.grid .row .col2{width:14%}.grid .row .col3{width:22.5%}.grid .row .col4{width:31%}.grid .row .col5{width:39.5%}.grid .row .col6{width:48%}.grid .row .col7{width:56.5%}.grid .row .col8{width:65%}.grid .row .col9{width:73.5%}.grid .row .col10{width:82%}.grid .row .col11{width:90.5%}.grid .row .col12{width:99%;margin:0}.grid .row .push1{margin-left:5.5%}.grid .row .push2{margin-left:14%}.grid .row .push3{margin-left:22.5%}.grid .row .push4{margin-left:31%}}@media all and (max-width:768px){.hidden-xs{display:none}}.header.third .main-action-wrapper{margin-top:.8rem;padding-left:.8rem}.header.third .main-action-wrapper .button{float:left;font-size:1.4rem;min-width:1rem}.header.third .actions-wrapper{margin-top:.8rem}.header.third .actions-wrapper .button{font-size:1.4rem;min-width:9rem;float:left}.header.third .actions-wrapper li{display:inline}.header.third .actions-wrapper .secondary{float:right}.header.third .actions-wrapper .secondary .button{min-width:1.6rem;padding:.6rem 1.4rem}@media all and (max-width:1024px){.header.third .actions-wrapper .actions.secondary,.header.third .actions-wrapper .dropdown{display:none}}@media all and (max-width:1024px){.header.third .actions-wrapper i,.header.third .main-action-wrapper i{display:none}.header.third .actions-wrapper i+span,.header.third .main-action-wrapper i+span{margin-left:0}.header.third .actions-wrapper .disabled,.header.third .main-action-wrapper .disabled{display:none}}@media all and (max-width:540px){.header.third .actions-wrapper a i,.header.third .main-action-wrapper a i{display:block}.header.third .actions-wrapper a.button,.header.third .main-action-wrapper a.button{min-width:1em;font-size:1em}.header.third .actions-wrapper a i+span,.header.third .main-action-wrapper a i+span{margin-left:0;display:none}.header.third .actions-wrapper .disabled,.header.third .main-action-wrapper .disabled{display:none}}.accordion .accordion-header:after,.accordion .accordion-header:before{content:"";display:table}.accordion .accordion-header:after{clear:both}.accordion .accordion-header:after,.accordion .accordion-header:before{content:"";display:table}.accordion .accordion-header:after{clear:both}.accordion .accordion-header a{color:#333;display:block}.accordion .accordion-header a:hover{color:#2894df}.accordion h3.accordion-header a{line-height:3.2rem;border-bottom:1px dotted #ddd}.accordion h3.accordion-header a .svg-icon{margin-right:.4rem}.accordion .accordion-content:after,.accordion .accordion-content:before{content:"";display:table}.accordion .accordion-content:after{clear:both}.accordion .accordion-content:after,.accordion .accordion-content:before{content:"";display:table}.accordion .accordion-content:after{clear:both}.accordion .accordion-content .processing-wrapper{background-color:#fff;padding:.5em 1.5em}.accordion .accordion-content .processing-wrapper .processing-text{position:relative;padding-left:1.5em}.accordion .accordion-content .processing-wrapper .processing-text:before{width:100%;height:100%;position:absolute;content:" ";top:0;left:0;background:transparent url('../../../img/controls/loading_light.svg') left center no-repeat;background-size:auto 75%}.accordion.closed .accordion-content{display:none}.dialog .accordion .accordion-header{margin-bottom:.5em}.dialog .accordion .accordion-header a{display:inline;border-bottom:0}.accordion.sidebar-section .accordion-header a{border:0}.accordion.sidebar-section .accordion-header a .svg-icon{position:absolute;right:1.5em;margin-top:.25em}.accordion.navigation-secondary .accordion-header .main-cell a:before{margin-left:-1.25em;margin-top:.25em;content:"\f0d7";font:normal normal normal 1em FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;width:1em;height:1em;position:absolute;text-align:center}.accordion.navigation-secondary .accordion-content>.empty-content{padding:0 0 0 2.5em}.announcement{margin:0;top:0;position:absolute;height:3.8rem;font-size:1.4rem;text-align:center;background:#fef0bf;color:#000;width:100%}.announcement p{padding:0;margin:.8rem}.announcement a{color:#333;border-bottom:1px solid #666;display:inline-block;padding-bottom:0;line-height:1.6rem;margin-left:.8rem}.announcement a:link,.announcement a:visited{color:#333}.announcement a:hover{text-decoration:none;cursor:pointer;color:#2894df;border-bottom:1px solid #2894df}.announcement a:active,.announcement a:focus{outline:0;color:#2894df;border:0}.announcement .announcement-close{float:right;border:0;margin-right:1.6rem;margin-top:.4rem}.announcement+#container.page{top:3.8rem}.avatar img{width:36px;height:36px;border-radius:50%}.big.avatar img{width:72px;height:72px;border-radius:50%}.breadcrumbs{height:3.2rem;padding:0;margin-bottom:0;background:#fff}.breadcrumbs ul{padding-top:.45em;margin-left:.1875em}.breadcrumbs ul li{display:inline-block;font-size:.8em;margin-left:.25em;max-width:25%;float:left}.breadcrumbs ul li:before{content:"\203A";margin-right:.5em}.breadcrumbs ul li:first-child{margin-left:0;padding-left:0}.breadcrumbs ul li:first-child:before{content:""}.breadcrumbs ul a{border:0}.breadcrumbs div.main-cell{display:inline}.panel.middle .breadcrumbs{border-bottom:1px solid #ddd}.dialog-wrapper{position:absolute;width:100%;height:100%;z-index:800;background:rgba(255,255,255,.9);overflow:auto}.dialog{position:relative;max-width:48rem;border:1px solid #ddd;background:#f3f3f3;margin:auto;margin-top:1%;margin-bottom:4.8rem;box-shadow:0 0 10px 0 #ddd}.dialog .dialog-header{padding:.8rem 1.6rem 0 1.6rem;height:4.8rem}.dialog .dialog-header h2{margin:.8rem 0 1rem .4rem;font-size:1.8rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.dialog .dialog-header .dialog-header-subtitle{padding-top:.4rem;padding-left:.8rem;font-size:1.4rem;color:#333}.dialog .dialog-header .tooltip-alt{margin-left:.8rem;font-size:1.2rem;font-weight:400;color:#333;line-height:1em}.dialog .dialog-header .tooltip-alt .tooltip-text{white-space:normal}.dialog .dialog-header .dialog-close{margin-top:-3.6rem}.dialog .form-content{background:#fff;padding:1rem 2rem 2.4rem 2rem}.dialog .error-message{margin-bottom:1rem}.dialog p{margin-top:.8rem;font-size:1.5rem}.dialog p+.checkbox{padding-bottom:.8rem}.dialog p+.checkbox label{font-size:1.6rem}.dialog label{clear:both;font-size:1.5rem}.dialog input[type=text],.dialog textarea{width:100%;box-sizing:border-box}.dialog input[type=text]:after,.dialog input[type=text]:before,.dialog textarea:after,.dialog textarea:before{content:"";display:table}.dialog input[type=text]:after,.dialog textarea:after{clear:both}.dialog input[type=text]:after,.dialog input[type=text]:before,.dialog textarea:after,.dialog textarea:before{content:"";display:table}.dialog input[type=text]:after,.dialog textarea:after{clear:both}.dialog input+.message,.dialog textarea+.message{display:none}.dialog input+.message.error,.dialog textarea+.message.error{display:block;clear:both;width:100%}.dialog .inline-error{color:#d40101;font-weight:700}.dialog .accordion-header a{display:inline}.dialog .submit-wrapper{margin:0;clear:both;width:100%;padding:1.2rem 0}.dialog .submit-wrapper .button,.dialog .submit-wrapper .cancel{float:right;margin-right:1.6rem}.dialog .submit-wrapper .button{box-sizing:border-box;min-width:8rem}.dialog .submit-wrapper .primary{font-size:1.8rem;padding:1.2rem 2.4rem}.dialog .submit-wrapper .cancel{margin-top:.7em;font-size:1.6rem}.dialog-close,.dialog-close:hover{display:block;float:right;border:0}.dialog-close:active{-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem;box-shadow:inset 0 1px 2px rgba(0,0,0,.2)}.dialog-close .fa-close{padding-top:15px;width:30px;height:15px;text-align:center;display:block;vertical-align:baseline;line-height:0;border:1px solid #eee;-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem}.dialog-close .svg-icon{padding:7px 0 7px 0;width:30px;height:15px;text-align:center;display:block;vertical-align:baseline;line-height:15px;border:1px solid #eee;-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem}.dialog-close:hover .fa-close,.dialog-close:hover .svg-icon{border:1px solid #ddd;-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem}@media all and (max-width:480px){.dialog{margin:0;border:0;box-shadow:none;width:100%;max-width:100%;margin-bottom:2.5em}}.drag-and-drop,.drag-and-drop-multiple{position:absolute;top:-1000px;background:#666;border:1px solid #969696;z-index:9999999;padding:.65em;line-height:1em;color:#fff;border-radius:5px}.drag-and-drop svg,.drag-and-drop-multiple svg{position:absolute;margin-top:-.85em;margin-left:.4em}.drag-and-drop svg path,.drag-and-drop-multiple svg path{fill:#FFF}.drag-and-drop span.message,.drag-and-drop-multiple span.message{display:inline;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.drag-and-drop span.message.not-allowed,.drag-and-drop-multiple span.message.not-allowed{padding-left:1.85em}.drag-and-drop-multiple{box-shadow:.25em .25em 0 -1px #969696}.drag-and-drop-multiple .count{position:absolute;top:-9px;right:-3px;font-size:.7em;background:#d40101;color:#fff;width:1.5em;height:1.5em;text-align:center;line-height:1.5em;border-radius:50%;box-shadow:0 0 1px #333}.drop-focus{background-color:#fffae7}.dropdown{float:left}.dropdown .button.create:focus,.dropdown .button:focus{border:1px solid #ddd;color:#333;background:#fff;z-index:801;border-bottom:0;-webkit-border-bottom-right-radius:0;-webkit-border-bottom-left-radius:0;-moz-border-radius-bottomright:0;-moz-border-radius-bottomleft:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.dropdown .button.create:focus .svg-icon svg,.dropdown .button:focus .svg-icon svg{fill:#333}.dropdown .button.more .svg-icon svg{top:.15em;margin-left:.5em}.dropdown .button.create .svg-icon svg{fill:#FFF}.dropdown .button.create.disabled .svg-icon svg{fill:#888888}.dropdown .dropdown-content{display:none;border:1px solid #ddd;background:#fff;float:left;position:absolute;min-width:13em;z-index:800;padding:.25em 0}.dropdown .dropdown-content.visible{display:block}.dropdown .dropdown-content.right{right:0}.dropdown .dropdown-content li a{display:block;min-width:9em;font-size:.9375em;border:0;padding:.313em .626em}.dropdown .dropdown-content li a:hover{background:#eee;text-decoration:none;color:#333}.dropdown .dropdown-content li.disabled a,.dropdown .dropdown-content li.disabled a:hover{color:#969696}.dropdown .dropdown-content li .separator-before{border-top:1px solid #ddd}.dropdown .dropdown-content li .separator-after{border-bottom:1px solid #ddd}.dropdown .button+.dropdown-content{margin-top:2em;margin-bottom:2em}.footer{margin-top:0;position:fixed;bottom:0;text-align:right;font-size:1.2rem;width:100%;height:3.8rem;background:#fff;border-top:1px solid #ddd;z-index:890}.footer .footer-links{padding-top:1rem;width:100%}.footer .footer-links li{display:inline;margin-right:1.5em}.footer .footer-links li.error-message a{background-color:#fff;color:#d40101}.footer .footer-links li .github-star{display:inline;position:absolute;margin-left:-8em;margin-top:-1px}.footer .footer-links a:not(.gh-btn):not(.gh-count){border:0}.header{overflow:visible!important}.header.first{background:#333}.header.second{background:#eee}.header .navigation.primary:after,.header .navigation.primary:before{content:"";display:table}.header .navigation.primary:after{clear:both}.header .navigation.primary:after,.header .navigation.primary:before{content:"";display:table}.header .navigation.primary:after{clear:both}.header .navigation.primary li{padding:.5em;float:left}.header .navigation.primary li:first-child{padding-left:1em}.header .navigation.primary li.right{float:right;margin-right:1em}.header .navigation.primary li a{color:#ddd;text-decoration:none;border:0;display:inline-block}.header .navigation.primary li a:hover{color:#fff}.header .navigation.primary li a:active,.header .navigation.primary li a:focus{color:#2894df}.header .navigation.primary li a.highlighted{background-color:#2894df;padding:0 .5em 0 .5em;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.header .navigation.primary li a.highlighted:active,.header .navigation.primary li a.highlighted:focus{color:#ddd}.header .navigation.primary li .row.selected a{color:#fff}.header .navigation.primary li .row.selected a:focus{color:#2894df}.header .navigation.primary .github-star{display:none;position:absolute;right:1em;top:4px}@media all and (min-width:600px){.header .navigation.primary .github-star{display:block}}.header .logo{margin:1.25em 0 0 1em;max-width:80%}.progress-bar{background:0 0;width:100%;height:2px;display:block}.progress-bar span{background:#d40101;height:2px;display:block}.progress-bar-wrapper{border:1px solid #ddd;height:10px;display:block;margin:2em 0 1em 0;padding:2px}.progress-bar-wrapper .progress-bar.big{width:100%;height:10px;display:block;clear:both}.progress-bar-wrapper .progress-bar.big .progress{background:#d40101;width:5%;display:block;height:10px}.progress-bar-wrapper .progress-bar.big.infinite{background:#d40101}.progress-bar-wrapper .progress-bar.big.infinite .progress{width:100%;overflow:hidden;background:url('../../../img/controls/infinite-bar.gif') repeat-x;-moz-opacity:0.5;-khtml-opacity:0.5;opacity:.5}.progress-details{color:#888;margin:.5em 0 .5em 0}.progress-details .progress-percent{float:right}.update-loading-bar{position:fixed;display:block;width:100%;bottom:2.35em;z-index:991}.update-loading-bar .progress-bar span{transition:width 2s;transition-timing-function:cubic-bezier(0.45,1.27,0.76,0.9)}.logo.no-img{background:transparent url('../../../img/logo/logo.svg') 0 0 no-repeat;background-size:150px auto;width:150px;height:30px}.logo h1{display:none}.logo.bigger{background:transparent url('../../../img/logo/logo.svg') 0 0 no-repeat;background-size:200px auto;width:200px;height:45px}.header.second .col1{min-width:200px}@media only screen and (-moz-min-device-pixel-ratio:1.5),only screen and (-o-min-device-pixel-ratio:1.5),only screen and (-webkit-min-device-pixel-ratio:1.5),only screen and (min-devicepixel-ratio:1.5),only screen and (min-resolution:1.5dppx){.logo.no-img{background:transparent url('../../../img/logo/logo.svg') 0 0 no-repeat;background-size:150px auto}.logo.bigger{background:transparent url('../../../img/logo/logo.svg') 0 0 no-repeat;background-size:200px auto}}.js .message.no-js{display:none}.cookies .message.no-cookies{display:none}.message{padding:1.6rem}.message a{border-bottom:1px solid #888}.message a:hover{border-bottom:1px solid #2894df}.message.error{color:#333;background:#ffe4e4}.message.error a:link,.message.error a:visited{color:#333;border-bottom:1px dotted #888}.message.error a:hover{color:#333;border-bottom:1px solid #888}.message.success{color:#333;background:#edf7eb}.message.notice{color:#333;background:#daecf9}.message.warning{color:#333;background:#fef0bf}.message p:last-child{margin-bottom:0}.message.side-message{margin-left:1.6rem;font-size:1.6rem;margin-right:3.2rem}.message.side-message p,.message.side-message ul{padding-bottom:1.6rem}.feedback-card,.message.animated{background:#fff;color:#333;display:flex;border:1px solid #ddd;border-radius:3px}.feedback-card .illustration,.message.animated .illustration{flex:0 0 180px}.feedback-card .additional-information,.message.animated .additional-information{flex:1;line-height:180px;margin-top:1.5em;padding-left:1em;padding-right:1em}@media only screen and (max-width:767px){.feedback-card,.message.animated{flex-direction:column}}.notification-container{font-size:.85em;top:0;position:absolute;z-index:991;height:2em;padding-top:1em;width:60%;margin-left:20%}.notification-container .notification{position:relative;left:50%;float:left;clear:both;margin-bottom:1em}.notification-container .notification .message{box-shadow:0 0 10px 0 #333;border-radius:2px;padding:.5em 1em;position:relative;left:-50%;float:left;color:#000;font-weight:400;width:auto}.notification-container .notification .message.warning{color:#333;background:#fef0bf}.notification-container .notification .message .content{margin-right:1em}.notification-container .notification .message .content strong{text-transform:capitalize}form.search{margin-top:1.6rem}form.search label,form.search legend{display:none;width:16rem}form.search input[type=search]{float:left;width:65%;margin-bottom:0;border-right:1px solid #fff;padding:.5rem 1rem}form.search input[type=search]:active,form.search input[type=search]:focus{border-right:1px solid #2894df}form.search button{height:3.6rem;width:6.5rem;float:left;margin-left:0;border-radius:0 2px 2px 0}form.search button .svg-icon svg{top:.1rem}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{display:none}@media all and (max-width:480px){form.search{display:none}}.tooltip,[data-tooltip]{position:relative;cursor:pointer}.tooltip:after,.tooltip:before,[data-tooltip]:after,[data-tooltip]:before{position:absolute;visibility:hidden;opacity:0;transition:opacity .2s ease-in-out,visibility .2s ease-in-out,transform .2s cubic-bezier(.71, 1.7, .77, 1.24);transform:translate3d(0,0,0);pointer-events:none;text-align:center}.always-show:after,.always-show:before,.tooltip:focus:after,.tooltip:focus:before,.tooltip:hover:after,.tooltip:hover:before,[data-tooltip]:focus:after,[data-tooltip]:focus:before,[data-tooltip]:hover:after,[data-tooltip]:hover:before{visibility:visible;opacity:1}.tooltip:before,[data-tooltip]:before{z-index:990;border:.6rem solid transparent;background:0 0;content:""}.tooltip:after,[data-tooltip]:after{z-index:990;padding:.4rem;width:16rem;background-color:hsla(0,0%,0%,.9);color:#fff;content:attr(data-tooltip);font-size:1.4rem;line-height:2.4rem;font-weight:400;text-shadow:none}.tooltip-top:after,.tooltip-top:before,.tooltip:after,.tooltip:before,[data-tooltip]:after,[data-tooltip]:before{bottom:100%;left:50%}.tooltip-top:before,.tooltip:before,[data-tooltip]:before{margin-left:-.6rem;margin-bottom:-1.2rem;border-top-color:hsla(0,0%,20%,.9)}.tooltip-top:after,.tooltip:after,[data-tooltip]:after{margin-left:-8rem}.tooltip-top.always-show:after,.tooltip-top.always-show:before,.tooltip-top:focus:after,.tooltip-top:focus:before,.tooltip-top:hover:after,.tooltip-top:hover:before,.tooltip.always-show:after,.tooltip.always-show:before,.tooltip:focus:after,.tooltip:focus:before,.tooltip:hover:after,.tooltip:hover:before,[data-tooltip]:focus:after,[data-tooltip]:focus:before,[data-tooltip]:hover:after,[data-tooltip]:hover:before{transform:translateY(-1.2rem)}.tooltip-left:after,.tooltip-left:before{right:100%;bottom:50%;left:auto}.tooltip-left:before{margin-left:0;margin-right:-1.2rem;margin-bottom:0;border-top-color:transparent;border-left-color:hsla(0,0%,20%,.9)}.tooltip-left.always-show:after,.tooltip-left.always-show:before,.tooltip-left:focus:after,.tooltip-left:focus:before,.tooltip-left:hover:after,.tooltip-left:hover:before{transform:translateX(-1.2rem)}.tooltip-bottom:after,.tooltip-bottom:before{top:100%;bottom:auto;left:50%}.tooltip-bottom:before{margin-top:-1.2rem;margin-bottom:0;border-top-color:transparent;border-bottom-color:hsla(0,0%,20%,.9)}.tooltip-bottom.always-show:after,.tooltip-bottom.always-show:before,.tooltip-bottom:focus:after,.tooltip-bottom:focus:before,.tooltip-bottom:hover:after,.tooltip-bottom:hover:before{transform:translateY(1.2rem)}.tooltip-right:after,.tooltip-right:before{bottom:50%;left:100%}.tooltip-right:before{margin-bottom:0;margin-left:-1.2rem;border-top-color:transparent;border-right-color:#333}.tooltip-right.always-show:after,.tooltip-right.always-show:before,.tooltip-right:focus:after,.tooltip-right:focus:before,.tooltip-right:hover:after,.tooltip-right:hover:before{transform:translateX(1.2rem)}.tooltip-left:before,.tooltip-right:before{top:.3rem}.tooltip-left:after,.tooltip-right:after{margin-left:0;margin-bottom:-1.6rem}.tooltip.large:after,[data-tooltip].large:after{width:240rem}.tooltip-left.large:after,.tooltip-left.large:before,.tooltip-right.large:after,.tooltip-right.large:before{margin-top:.6rem}.tooltip-alt{cursor:pointer;display:inline-block;border-bottom:0}.tooltip-alt .tooltip-text{position:absolute;visibility:hidden;width:180px;background-color:#333;color:#fff;padding:.5em 1em;transition:opacity .2s ease-in-out,visibility .2s ease-in-out,transform .2s cubic-bezier(.71, 1.7, .77, 1.24);transform:translate3d(0,0,0);z-index:99999}.tooltip-alt .tooltip-text.right{margin-left:1.2rem;margin-top:-2.65rem}.tooltip-alt .tooltip-text.right::after{content:" ";position:absolute;top:1.125em;right:100%;margin-top:-5px;border:5px #333 solid;border-color:transparent #333 transparent transparent}.tooltip-alt:hover .tooltip-text{transform:translateX(12px);visibility:visible;opacity:1}.tooltip svg,.tooltip-alt svg{fill:#666;top:.125em;position:relative}.user.profile{max-width:16em;float:right;border:1px solid #ddd;margin-top:.5em;background:#eee;overflow:hidden;cursor:pointer;border-radius:3px;background:#f3f3f3}.user.profile .more{background:#f3f3f3;border-left:1px dotted #ddd}.user.profile .center-cell-wrapper,.user.profile .left-cell,.user.profile .right-cell{float:left;height:3.125em}.user.profile .center-cell-wrapper{width:100%}.user.profile .center-cell{margin:0 0 0 3.125em;overflow:hidden}.user.profile .left-cell{width:3.125em;margin-left:-100%}.user.profile .right-cell{width:1.6875em;margin-left:-1.75em}.user.profile .picture img{width:2.6em;padding:.28em .35em .35em .35em;border-radius:50%}.user.profile .details{float:left;font-size:.875em;padding:.357em 1em .5em 1em}.user.profile .details .email,.user.profile .details .name{width:11em;float:left;clear:both;text-overflow:clip;word-wrap:break-word;display:inline-block;line-height:normal;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.user.profile .details .name{font-weight:700;padding-top:0}.user.profile .details .email{padding-bottom:0}.user.profile .more a{position:absolute;display:block;border:0;color:#666}.user.profile .more a .svg-icon{margin:16px 0 0 7px;display:inline-block}.user.profile .more a .svg-icon svg{fill:#333}.user.profile .more a:hover{color:#000}.user.profile .more a:hover svg{fill:#333}.user.profile .more a span:last-child{visibility:hidden}.user.profile .dropdown-content{top:59px;max-width:16em;width:100%;background:#fff}.user.profile .dropdown-content.visible{display:block}@media all and (max-width:1024px){.user.profile{display:block;width:auto}.user.profile .center-cell{display:none}.user.profile .right-cell{display:none}}.contextual-menu{position:absolute;background:#fff;border:1px solid #ddd;width:12em;box-shadow:0 0 10px 0 #ddd;z-index:993;left:11.25em;display:none;padding:.25em 0}.contextual-menu a{font-size:.875em;display:block;padding:.357em .714em;border:0}.contextual-menu a:hover{color:#333;background:#eee}.contextual-menu li.disabled a{color:#ddd}.contextual-menu li.disabled a:hover{color:#ddd;background:#eee}.contextual-menu .separator-before{border-top:1px solid #ddd}.contextual-menu .separator-after{border-bottom:1px solid #ddd}.navigation-secondary{font-size:.9375em;border-bottom:1px dotted #ddd;padding:.625em 0}.navigation-secondary ul{list-style:none;padding:0}.navigation-secondary li:after,.navigation-secondary li:before{content:"";display:table}.navigation-secondary li:after{clear:both}.navigation-secondary li:after,.navigation-secondary li:before{content:"";display:table}.navigation-secondary li:after{clear:both}.navigation-secondary .row{float:left;width:100%;box-sizing:border-box}.navigation-secondary .row:hover{background:#f3f3f3}.navigation-secondary .row.no-hover:hover{background:0 0}.navigation-secondary .row.selected{font-weight:700}.navigation-secondary .row .main-cell-wrapper{float:left;width:100%}.navigation-secondary .row .main-cell{margin:0 1.5em 0 0}.navigation-secondary .row .main-cell h3{border:0;font-size:1em;margin:0 .25em 0 1em;padding:.25em 0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.navigation-secondary .row .main-cell h3 a{padding-top:0;padding-bottom:0}.navigation-secondary .row .main-cell span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis;display:block}.navigation-secondary .row .main-cell a{border:0;padding:.2em .313em;padding-left:1em;color:#333;display:block}.navigation-secondary .row .right-cell{float:right;width:1em;height:1em;margin-right:1em;margin-top:-1.5em}.navigation-secondary .row .right-cell a{display:none;width:1em;height:1em;padding:.125em;color:#888;border:1px solid transparent}.navigation-secondary .row .right-cell a .svg-icon{position:absolute;width:16px;height:16px;line-height:16px;text-align:center;vertical-align:center}.navigation-secondary .row .right-cell a .svg-icon svg{fill:#888888}.navigation-secondary .row .right-cell a .svg-icon svg:hover{fill:#2894DF}.navigation-secondary .row .right-cell a:hover{color:#2894df;background:#fff}.navigation-secondary .row.title .right-cell{margin-top:-1.825em}.navigation-secondary .row:hover{background:#f3f3f3}.navigation-secondary .row:hover .right-cell a{display:block}iframe{margin:0;padding:0;border:0;outline:0;font-size:100%;vertical-align:baseline;background:0 0}iframe.full-screen{position:absolute;width:100%;height:100%;z-index:999;border:0;top:0;left:0}iframe.cachette{position:absolute;width:1px;height:1px;z-index:999;border:0;bottom:0;right:0}.sidebar-help{margin-top:3.2rem;padding:2.4rem;background-color:#daecf9}.sidebar-help.transparent{background-color:transparent;border:1px solid #f3f3f3}.sidebar-help h3{margin:0 0 1.6rem 0;border-bottom:none}.input-password-wrapper{width:100%;box-sizing:border-box}.input-password-wrapper:after,.input-password-wrapper:before{content:"";display:table}.input-password-wrapper:after{clear:both}.input-password-wrapper:after,.input-password-wrapper:before{content:"";display:table}.input-password-wrapper:after{clear:both}.input-password-wrapper .input.password{display:inline-flex;width:65.5%}.input-password-wrapper .input.password input[type=password],.input-password-wrapper .input.password input[type=text]{flex:1;box-sizing:border-box}.input-password-wrapper .input.password input[type=password].decrypting{background:transparent url('../../../img/controls/loading_light.svg') no-repeat 90% center}.input-password-wrapper .password-view{padding:.8rem 1.4rem;margin-top:0;margin-right:0;margin-left:-.1rem;margin-bottom:.8rem;border-radius:0;background:#fff;border-left:0;border-top:1px solid #bbb}.input-password-wrapper .password-view.selected{background:#eee}.input-password-wrapper input[type=password]:focus~.button.password-view,.input-password-wrapper input[type=text]:focus~.button.password-view{box-shadow:inset -1px 1px 1px rgba(0,0,0,.2);border:1px solid #2894df;border-left:0}.input-password-wrapper .actions.inline{float:right;width:30%}.input-password-wrapper .actions.inline .button{float:left;width:4.8rem;padding-left:0;padding-right:0}.input-password-wrapper .actions.inline .button+.button{margin-left:.8rem}.input-password-wrapper .password-complexity .complexity-text{float:right;clear:right;width:30%;font-size:11px;text-align:left;padding-left:0}.input-password-wrapper .password-complexity.not_available .complexity-text{color:#ddd}.input-password-wrapper .password-complexity .progress{width:65.5%;box-sizing:border-box;border:1px solid #ddd;height:10px;display:block;clear:both;margin:.25em 0 .5em 0;float:left}.input-password-wrapper .password-complexity .progress-bar{background:#000;width:0;height:6px;display:block;float:left;margin:1px}.input-password-wrapper .password-complexity .progress-bar.very-weak{background:#000;width:5%}.input-password-wrapper .password-complexity .progress-bar.weak{background:#d40101;width:10%}.input-password-wrapper .password-complexity .progress-bar.fair{background:#ffbd2e;width:60%}.input-password-wrapper .password-complexity .progress-bar.strong{background:#6c0;width:80%}.input-password-wrapper .password-complexity .progress-bar.very-strong{background:#090;width:99.5%}@media all and (max-width:400px){.input-password-wrapper .input.password{float:left;width:100%}.input-password-wrapper .actions.inline{width:50%;float:left;margin-bottom:.5em}.input-password-wrapper .password-complexity .progress{display:none}.input-password-wrapper .password-complexity .complexity-text{float:left;width:50%;font-size:.831em;margin-top:.5em}.fa.fa-lg{line-height:1}}.gpgkey.input.textarea textarea{height:24em;width:95%}/*! Chosen, a Select Box Enhancer for jQuery and Prototype by Patrick Filler for Harvest, http://getharvest.com @@ -19,5 +15,4 @@ Full source at https://github.com/harvesthq/chosen Copyright (c) 2011-2018 Harvest http://getharvest.com MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md -This file is generated by `grunt build`, do not edit it by hand. -*/.chosen-container{position:relative;display:inline-block;vertical-align:middle;font-size:13px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.chosen-container *{-webkit-box-sizing:border-box;box-sizing:border-box}.chosen-container .chosen-drop{position:absolute;top:100%;z-index:1010;width:100%;border:1px solid #aaa;border-top:0;background:#fff;-webkit-box-shadow:0 4px 5px rgba(0,0,0,.15);box-shadow:0 4px 5px rgba(0,0,0,.15);display:none}.chosen-container.chosen-container-single{padding:0;width:83%}.chosen-container.chosen-container-single.connection-type{float:left;width:23%}.chosen-container.chosen-with-drop .chosen-drop{display:block}.chosen-container a{cursor:pointer}.chosen-container .chosen-single .group-name,.chosen-container .search-choice .group-name{margin-right:4px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-weight:400;color:#969696}.chosen-container .chosen-single .group-name:after,.chosen-container .search-choice .group-name:after{content:"\003A";padding-left:2px;vertical-align:top}.chosen-container-single .chosen-single{position:relative;display:block;overflow:hidden;padding:0 0 0 8px;height:25px;border:1px solid #bbb;border-radius:5px;background-color:#fff;background:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#fff),color-stop(50%,#ddd),color-stop(52%,#eee),to(#f3f3f3));background:linear-gradient(#fff 20%,#ddd 50%,#eee 52%,#f3f3f3 100%);background-clip:padding-box;-webkit-box-shadow:0 0 3px #fff inset,0 1px 1px rgba(0,0,0,.1);box-shadow:0 0 3px #fff inset,0 1px 1px rgba(0,0,0,.1);color:#333;text-decoration:none;white-space:nowrap;line-height:24px}.chosen-container-single .chosen-single input[type=text]{cursor:pointer;opacity:0;position:absolute}.chosen-container-single .chosen-default{color:#888}.chosen-container-single .chosen-single span{display:block;overflow:hidden;margin-right:26px;text-overflow:ellipsis;white-space:nowrap}.chosen-container-single .chosen-single-with-deselect span{margin-right:38px}.chosen-container-single .chosen-single abbr{position:absolute;top:6px;right:26px;display:block;width:12px;height:12px;background:url(../../../img/third_party/chosen-sprite.png) -42px 1px no-repeat;font-size:1px}.chosen-container-single .chosen-single abbr:hover{background-position:-42px -10px}.chosen-container-single.chosen-disabled .chosen-single abbr:hover{background-position:-42px -10px}.chosen-container-single .chosen-single div{position:absolute;top:0;right:0;display:block;width:18px;height:100%}.chosen-container-single .chosen-single div b{display:block;width:100%;height:100%;background:url(../../../img/third_party/chosen-sprite.png) no-repeat 0 2px}.chosen-container-single .chosen-search{position:relative;z-index:1010;margin:0;padding:3px 4px;white-space:nowrap}.chosen-container-single .chosen-search span.svg-icon{position:absolute;margin-left:-1.5em;margin-top:.35em}.chosen-container-single .chosen-search input[type=text]{margin:1px 0;padding:4px 20px 4px 5px;width:100%;height:auto;outline:0;border:1px solid #bbb;background:url(../../../img/third_party/chosen-sprite.png) no-repeat 100% -20px;font-size:1em;font-family:sans-serif;line-height:normal;border-radius:0}.chosen-container-single .chosen-drop{margin-top:-.5em;border-radius:0 0 4px 4px;background-clip:padding-box}.chosen-container-single.chosen-container-single-nosearch .chosen-search{position:absolute;clip:rect(0,0,0,0)}.chosen-container .chosen-results{color:#333;position:relative;overflow-x:hidden;overflow-y:auto;margin:0 4px 4px 0;padding:0 0 0 4px;max-height:240px;-webkit-overflow-scrolling:touch}.chosen-container .chosen-results li{display:none;margin:0;padding:5px 6px;list-style:none;line-height:15px;word-wrap:break-word;-webkit-touch-callout:none}.chosen-container .chosen-results li.active-result{display:list-item;cursor:pointer}.chosen-container .chosen-results li.disabled-result{display:list-item;color:#ddd;cursor:default}.chosen-container .chosen-results li:hover{background-color:#daecf9;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#3875d7),color-stop(90%,#2a62bc));background-image:linear-gradient(#3875d7 20%,#2a62bc 90%);color:#fff}.chosen-container .chosen-results li.no-results{color:#666;display:list-item;background:#f3f3f3}.chosen-container .chosen-results li.group-result{display:list-item;font-weight:700;cursor:default}.chosen-container .chosen-results li.group-option{padding-left:15px}.chosen-container .chosen-results li em{font-style:normal;text-decoration:underline}.chosen-container-multi .chosen-choices{position:relative;overflow:hidden;margin:0;padding:0 5px;width:100%;height:auto;border:1px solid #bbb;background-color:#fff;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(1%,#eee),color-stop(15%,#fff));background-image:linear-gradient(#eee 1%,#fff 15%);cursor:text}.chosen-container-multi .chosen-choices li{float:left;list-style:none}.chosen-container-multi .chosen-choices li.search-field{margin:0;padding:0;white-space:nowrap}.chosen-container-multi .chosen-choices li.search-field input[type=text]{margin:1px 0;padding:0;height:25px;outline:0;border:0!important;background:0 0!important;-webkit-box-shadow:none;box-shadow:none;color:#888;font-size:100%;font-family:sans-serif;line-height:normal;border-radius:0;width:25px}.chosen-container-multi .chosen-choices li.search-choice{position:relative;margin:3px 5px 3px 0;padding:3px 20px 3px 5px;border:1px solid #bbb;max-width:100%;border-radius:3px;background-color:#fff;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#fff),color-stop(50%,#ddd),color-stop(52%,#eee),to(#f3f3f3));background-image:linear-gradient(#fff 20%,#ddd 50%,#eee 52%,#f3f3f3 100%);background-size:100% 19px;background-repeat:repeat-x;background-clip:padding-box;-webkit-box-shadow:0 0 2px #fff inset,0 1px 0 rgba(0,0,0,.05);box-shadow:0 0 2px #fff inset,0 1px 0 rgba(0,0,0,.05);color:#ddd;line-height:13px;cursor:default}.chosen-container-multi .chosen-choices li.search-choice span{word-wrap:break-word}.chosen-container-multi .chosen-choices li.search-choice .search-choice-close{position:absolute;top:4px;right:3px;display:block;width:12px;height:12px;background:url(../../../img/third_party/chosen-sprite.png) -42px 1px no-repeat;font-size:1px}.chosen-container-multi .chosen-choices li.search-choice .search-choice-close:hover{background-position:-42px -10px}.chosen-container-multi .chosen-choices li.search-choice-disabled{padding-right:5px;border:1px solid #ccc;background-color:#f3f3f3;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#fff),color-stop(50%,#ddd),color-stop(52%,#eee),to(#f3f3f3));background-image:linear-gradient(#fff 20%,#ddd 50%,#eee 52%,#f3f3f3 100%);color:#666}.chosen-container-multi .chosen-choices li.search-choice-focus{background:#ddd}.chosen-container-multi .chosen-choices li.search-choice-focus .search-choice-close{background-position:-42px -10px}.chosen-container-multi .chosen-results{margin:0;padding:0}.chosen-container-multi .chosen-drop .result-selected{display:list-item;color:#eee;cursor:default}.chosen-container-active .chosen-single{border:1px solid #2894df;-webkit-box-shadow:0 0 5px rgba(0,0,0,.3);box-shadow:0 0 5px rgba(0,0,0,.3)}.chosen-container-active.chosen-with-drop .chosen-single{border:1px solid #bbb;border-bottom-right-radius:0;border-bottom-left-radius:0;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#eee),color-stop(80%,#fff));background-image:linear-gradient(#eee 20%,#fff 80%);-webkit-box-shadow:0 1px 0 #fff inset;box-shadow:0 1px 0 #fff inset}.chosen-container-active.chosen-with-drop .chosen-single div{border-left:none;background:0 0}.chosen-container-active.chosen-with-drop .chosen-single div b{background-position:-18px 2px}.chosen-container-active .chosen-choices{border:1px solid #2894df;-webkit-box-shadow:0 0 5px rgba(0,0,0,.3);box-shadow:0 0 5px rgba(0,0,0,.3)}.chosen-container-active .chosen-choices li.search-field input[type=text]{color:#333!important}.chosen-disabled{opacity:.5!important;cursor:default}.chosen-disabled .chosen-single{cursor:default}.chosen-disabled .chosen-choices .search-choice .search-choice-close{cursor:default}.chosen-rtl{text-align:right}.chosen-rtl .chosen-single{overflow:visible;padding:0 8px 0 0}.chosen-rtl .chosen-single span{margin-right:0;margin-left:26px;direction:rtl}.chosen-rtl .chosen-single-with-deselect span{margin-left:38px}.chosen-rtl .chosen-single div{right:auto;left:3px}.chosen-rtl .chosen-single abbr{right:auto;left:26px}.chosen-rtl .chosen-choices li{float:right}.chosen-rtl .chosen-choices li.search-field input[type=text]{direction:rtl}.chosen-rtl .chosen-choices li.search-choice{margin:3px 5px 3px 0;padding:3px 5px 3px 19px}.chosen-rtl .chosen-choices li.search-choice .search-choice-close{right:auto;left:4px}.chosen-rtl.chosen-container-single .chosen-results{margin:0 0 4px 4px;padding:0 4px 0 0}.chosen-rtl .chosen-results li.group-option{padding-right:15px;padding-left:0}.chosen-rtl.chosen-container-active.chosen-with-drop .chosen-single div{border-right:none}.chosen-rtl .chosen-search input[type=text]{padding:4px 5px 4px 20px;background:url(../../../img/third_party/chosen-sprite.png) no-repeat -30px -20px;direction:rtl}.chosen-rtl.chosen-container-single .chosen-single div b{background-position:6px 2px}.chosen-rtl.chosen-container-single.chosen-with-drop .chosen-single div b{background-position:-12px 2px}@media only screen and (-webkit-min-device-pixel-ratio:1.5),only screen and (min-resolution:144dpi),only screen and (min-resolution:1.5dppx){.chosen-container .chosen-results-scroll-down span,.chosen-container .chosen-results-scroll-up span,.chosen-container-multi .chosen-choices .search-choice .search-choice-close,.chosen-container-single .chosen-search input[type=text],.chosen-container-single .chosen-single abbr,.chosen-container-single .chosen-single div b,.chosen-rtl .chosen-search input[type=text]{background-image:url(../../../img/third_party/chosen-sprite@2x.png)!important;background-size:52px 37px!important;background-repeat:no-repeat!important}}.chosen-container{margin-bottom:.5em}.chosen-container.chosen-disabled a.chosen-single{border:1px solid #ccc}.chosen-container a.chosen-single{color:#333;display:block;text-overflow:ellipsis;white-space:nowrap;height:38px;width:100%;background:#f3f3f3;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;border:1px solid #ccc;border-top:1px solid #bbb;box-shadow:none;margin:0 0 .5em 0}.chosen-container a.chosen-single span{font-size:16px;padding-top:3px;margin-top:2px;color:#333}.chosen-container a.chosen-single span.svg-icon{float:left}.chosen-container a.chosen-single div b{background-position:0 6px}.chosen-container .chosen-search input.chosen-search-input[type=text]{width:95%!important;color:#666}.chosen-container .chosen-drop{border:1px solid #ccc!important;border-top:0!important;-webkit-border-radius:0!important;-moz-border-radius:0!important;border-radius:0!important;background:#fff!important}.chosen-container .chosen-drop .chosen-results{color:#333}.chosen-container.chosen-container-active.chosen-with-drop a.chosen-single{background:#fff;border:1px solid #ccc;border-top:1px solid #bbb;box-shadow:none}.chosen-container.chosen-container-active.chosen-with-drop a.chosen-single div b{background-position:-18px 8px}.singleline.connection_info.protocol_host_port .input.text.field_protocol_host{border:0;padding:0;width:70%}.singleline.connection_info.protocol_host_port .input.text.field_protocol_host .input.text{padding:0;margin:0}.singleline.connection_info.protocol_host_port .input.text.field_protocol_host .input.text input{margin:0;border-left:none}.singleline.connection_info.protocol_host_port .input.text.field_protocol_host .input.text.host{width:75%}.singleline.connection_info.protocol_host_port .input.text.field_protocol_host .input.text.host input[type=text]{width:95%}.singleline.connection_info.protocol_host_port .input.text.field_protocol_host .input.text.protocol{width:25%;position:relative}.singleline.connection_info.protocol_host_port .input.text.field_protocol_host .input.text.protocol .chosen-container{display:block}.singleline.connection_info.protocol_host_port .input.text.field_protocol_host .input.text.protocol .chosen-container a.chosen-single{height:38px}.singleline.connection_info.protocol_host_port .input.text.field_protocol_host .input.text.protocol .chosen-container a.chosen-single div b{background-position:0 10px}.singleline.connection_info.protocol_host_port .input.text.field_protocol_host .input.text.protocol .chosen-container a.chosen-single span{font-size:16px;padding:6px 0 0 0}.singleline.connection_info.protocol_host_port .input.text.field_protocol_host .input.text.protocol .chosen-container-active.chosen-with-drop a.chosen-single div b{background-position:-18px 10px}.singleline.connection_info.protocol_host_port .input.text.port{width:9%;margin-left:1%}.singleline.connection_info.protocol_host_port .input.text.port input[type=number]{width:100%}.panel .folders.navigation{border:0}.panel .folders.navigation ul{list-style:none;padding:0}.panel .folders.navigation .folders-label-selected{background:#f3f3f3}.panel .folders.navigation .folders-label-height{height:1.8em}.panel .folders.navigation.first{padding-top:0}.panel .folders.navigation .row .main-cell span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis;display:inline-block;margin-top:.2em;width:calc(100% - 2.5em)}.panel .folders.navigation .row .main-cell .svg-icon{display:inline}.panel .folders.navigation .row .main-cell .svg-icon.hidden{display:none}.panel .folders.navigation .row .main-cell .svg-icon svg{width:.83em;height:.83em;position:absolute;margin-top:.5em;fill:#333}.panel .folders.navigation .row .main-cell a{padding:0 0 0 2.2em}.panel .folders.navigation .row .main-cell a .svg-icon.caret-down svg,.panel .folders.navigation .row .main-cell a .svg-icon.caret-right svg{margin-left:-1.2em;margin-top:.4em;width:1em;height:1em;position:absolute}.panel .folders.navigation .row .main-cell a .folder-name{margin-left:1.3em}.panel .folders.navigation .row .right-cell{margin-top:-1.66666667em}.panel .folders.navigation .row.title .main-cell h3{padding-top:.26666667em;padding-bottom:0;border:0;margin-top:0;line-height:1.26666667em}.panel .folders.navigation .row.title .main-cell h3 span{width:100%;margin-top:0}.panel .folders.navigation .row.title .main-cell h3 a{padding-left:0}.panel .folders.navigation .row.title .main-cell h3 a::after,.panel .folders.navigation .row.title .main-cell h3 a::before{display:none}.panel .folders.navigation .row.title .main-cell h3 .folders-label{width:100%;margin-left:1em;cursor:pointer}.panel .folders.navigation .row.title .main-cell h3 .folders-label span{width:calc(100% - 1.5em)}.panel .folders.navigation .row.title .main-cell h3 .folders-label .svg-icon.caret-down svg,.panel .folders.navigation .row.title .main-cell h3 .folders-label .svg-icon.caret-right svg{margin-left:-1.2em;margin-top:.2em;width:1em;height:1em;position:absolute}.panel .folders.navigation .row.title .main-cell h3 .folders-label .svg-icon.spinner svg{margin-left:-1.1em;margin-top:.3em;width:.83em;height:.83em;position:absolute}.panel .folders.navigation .row.title .right-cell{margin-top:-1.66666667em}.panel .folders.navigation .row.disabled{cursor:default;pointer-events:none}.panel .folders.navigation .row.disabled .main-cell span.folder-name{color:#888;text-shadow:none;font-weight:400}.panel .folders.navigation .row.disabled .main-cell span.svg-icon svg{fill:#888}.panel .folders.navigation .row.disabled .main-cell a:before{color:#888}.panel .folders.navigation .row.disabled.is-dragged{pointer-events:auto}.panel .folders.navigation li li{padding-left:1.15em}.drag-and-drop,.drag-and-drop-multiple{position:absolute;top:-1000px;background:#666;border:1px solid #969696;z-index:9999999;padding:.65em;line-height:1em;color:#fff;border-radius:5px}.drag-and-drop svg,.drag-and-drop-multiple svg{position:absolute;margin-top:-.85em;margin-left:.4em}.drag-and-drop svg path,.drag-and-drop-multiple svg path{fill:#fff}.drag-and-drop span.message,.drag-and-drop-multiple span.message{display:inline;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.drag-and-drop span.message.not-allowed,.drag-and-drop-multiple span.message.not-allowed{padding-left:1.85em}.drag-and-drop-multiple{box-shadow:.25em .25em 0 -1px #969696}.drag-and-drop-multiple .count{position:absolute;top:-9px;right:-3px;font-size:.7em;background:#d40101;color:#fff;width:1.5em;height:1.5em;text-align:center;line-height:1.5em;border-radius:50%;box-shadow:0 0 1px #333}.drop-focus{background-color:#fffae7}iframe.full-screen{position:absolute;width:100%;height:100%;z-index:999;border:0;top:0;left:0}iframe.cachette{position:absolute;width:1px;height:1px;z-index:999;border:0;bottom:0;right:0}#user-locale-input{border:none;background:transparent url(../../../img/controls/chevron-down_black.svg) right center/1rem no-repeat;appearance:none;margin-top:.4rem;padding-right:1rem}.input-password-wrapper{overflow:hidden}.input-password-wrapper .message.error{display:block;clear:both;width:100%;padding-bottom:.5em;font-size:.813em}.dialog .share-tab .message{font-size:.875em;padding-left:1em}.share-password-dialog .processing-wrapper{background-color:#fff;text-align:center;padding:1em .5em 3em 1em}.share-password-dialog .processing-wrapper .processing-text{position:relative;padding-left:1.5em}.share-password-dialog .processing-wrapper .processing-text:before{width:100%;height:100%;position:absolute;content:" ";top:0;left:0;background:transparent url(../../../img/controls/loading_light.svg) left center no-repeat;background-size:auto 75%}.dialog-wrapper.session-expired-dialog{z-index:994}.dialog-wrapper.session-expired-dialog .dialog-close{display:none}.dialog .group .form-content.permission-edit{border-top:1px solid #f3f3f3;margin:0}.edit-group-dialog .group_edit_form .form-content{padding-bottom:0}.edit-group-dialog .group_edit_form .form-content .input.text .message{padding-left:0}.edit-group-dialog .group_members{margin-top:-.5em}.edit-group-dialog .group_members .message.warning.feedback{margin-top:0}.edit-group-dialog .group_members .message.warning.feedback span{display:block}.edit-group-dialog .group_members ul.permissions{display:block}.edit-group-dialog .group_members.empty ul.permissions{display:none}.dialog.import .input-password-wrapper .input.password,.dialog.kdbx-credentials .input-password-wrapper .input.password{width:90%}.dialog.import .actions.inline,.dialog.kdbx-credentials .actions.inline{width:14%}.dialog.import .actions.inline .button,.dialog.kdbx-credentials .actions.inline .button{height:1.5em;padding-top:.6em}.dialog.import div.jfilestyle,.dialog.kdbx-credentials div.jfilestyle{width:100%!important}.dialog.import div.jfilestyle input[type=text]:disabled,.dialog.kdbx-credentials div.jfilestyle input[type=text]:disabled{width:57%!important}.delete-group-dialog .intro p+p,.delete-user-dialog .intro p+p{margin-bottom:0}.delete-group-dialog .ownership-transfer,.delete-user-dialog .ownership-transfer{max-height:20em;overflow:auto;background:#fff}.delete-group-dialog .ownership-transfer h3,.delete-user-dialog .ownership-transfer h3{border:0;padding:.5em 1.25em;margin:0;background:#f3f3f3}.delete-group-dialog .ownership-transfer li,.delete-user-dialog .ownership-transfer li{border-bottom:1px dotted #ddd;padding:.5em 1.25em}.delete-group-dialog .ownership-transfer li select,.delete-user-dialog .ownership-transfer li select{width:100%}.ldap-test-settings-report div.directory-structure{border:1px solid #ddd;padding:.5em 0 .5em .5em}.ldap-test-settings-report div.directory-structure>ul>li{margin-left:.5em}.ldap-test-settings-report div.directory-structure ul{font-size:.8rem;list-style-type:square;list-style-position:inside}.ldap-test-settings-report div.directory-structure ul li{margin-left:1em}.ldap-test-settings-report div.directory-structure ul li em{color:#888;font-size:.8em}.ldap-test-settings-report div.directory-structure ul li.user{font-weight:400;list-style-type:circle}.ldap-test-settings-report div.directory-structure ul li.group{font-weight:700}.panel.aside .group .sidebar-header .logo img,.panel.aside .user .sidebar-header .logo img{margin:0;width:3em;height:3em}.panel.aside li.key{margin-bottom:.5em}.panel.aside li.key a.button.copy-public-key{padding-top:.2em;padding-bottom:.2em;font-size:.75em}.panel.aside li textarea{width:100%}.edit-group-dialog .permission-edit{margin-top:-1em;border-top:1px solid #f3f3f3}.page.people .tableview-header th.cell-avatar,.page.people .tableview-header th.cell-icon{width:45px}.page.people .tableview-content td.cell-avatar,.page.people .tableview-content th.cell-icon{margin:0;text-align:left;width:45px}.page.people .tableview-content td.cell-avatar img,.page.people .tableview-content th.cell-icon img{margin-top:3px;width:1.7em;height:1.7em;border-radius:50%}.login-history td:first-child{text-align:center}.page.user .tableview.empty .tableview-header{display:none}.page.user .tableview.empty .tableview-content{border:0;top:0}.page.user .tableview.empty .empty-content{text-align:center;margin:3em .5em .67em .5em}.page.user .tableview.empty .empty-content h1{font-size:1.5em}.page.user .tableview.empty .empty-content p{font-size:1em;line-height:1.5em}@media all and (min-width:780px){.page.user .tableview.empty .empty-content h1{font-size:2.2em}.page.user .tableview.empty .empty-content p{font-size:1.2em;line-height:1.8em}}.panel.aside .resource .detailed-information li.password .value{height:1.6em;padding-top:.2em}.panel.aside .resource .detailed-information li.password .value .secret{float:left;max-width:calc(100% - 1.8rem);overflow:hidden;text-overflow:ellipsis}.panel.aside .resource .detailed-information li.password .password-view{display:block;position:relative;box-sizing:content-box;margin:0 0 0 .3rem;padding:.05rem .3rem 0 .3rem;background:0 0;border:0;float:left}.panel.aside .resource .detailed-information li.password .password-view.selected{background:#eee}.page.password .tableview.empty .tableview-header{display:none}.page.password .tableview.empty .tableview-content{border:0;top:0}@media all and (min-width:780px){.page.password .tableview.empty.all_items .tableview-content{background-position:center 60%;background-size:10%}}@media all and (min-width:1024px){.page.password .tableview.empty.all_items .tableview-content{background-position:center 80%;background-size:20%}}.page.password .tableview.empty .empty-content{text-align:center;margin:auto;width:50%;padding-top:2em}.page.password .tableview.empty .empty-content h1{font-size:1.5em}.page.password .tableview.empty .empty-content p{font-size:1em;line-height:1.5em}@media all and (min-width:780px){.page.password .tableview.empty .empty-content h1{font-size:2.2em}.page.password .tableview.empty .empty-content p{font-size:1.2em;line-height:1.8em}}.page.settings .main.panel .middle{overflow-y:auto}.page.settings .profile-detailed-information{zoom:1}.page.settings .profile-detailed-information:after,.page.settings .profile-detailed-information:before{content:"";display:table}.page.settings .profile-detailed-information:after{clear:both}.page.settings .profile-detailed-information:after,.page.settings .profile-detailed-information:before{content:"";display:table}.page.settings .profile-detailed-information:after{clear:both}.page.settings .profile-detailed-information .avatar{float:left}.page.settings .profile-detailed-information .avatar img{border:1px solid #ddd;padding:0;width:12.5em;height:12.5em;margin:.5em 1em 0 0}.page.settings .profile-detailed-information .avatar .edit{width:12.5em;height:3.125em;background-color:rgba(0,0,0,.7);border:1px solid transparent;color:#fff;margin:-3.25em 0 2em 0;position:relative}.page.settings .profile-detailed-information .avatar .edit a{color:#fff;display:block;height:3.125em;zoom:1}.page.settings .profile-detailed-information .avatar .edit a:after,.page.settings .profile-detailed-information .avatar .edit a:before{content:"";display:table}.page.settings .profile-detailed-information .avatar .edit a:after{clear:both}.page.settings .profile-detailed-information .avatar .edit a:after,.page.settings .profile-detailed-information .avatar .edit a:before{content:"";display:table}.page.settings .profile-detailed-information .avatar .edit a:after{clear:both}.page.settings .profile-detailed-information .avatar .edit a .svg-icon{float:left}.page.settings .profile-detailed-information .avatar .edit a .svg-icon svg{float:left;fill:#fff;width:1.6rem;height:1.6rem;margin:.8em}.page.settings .profile-detailed-information .avatar .edit a .help-text{float:left;display:block;font-size:.813em;margin-left:0;padding:.5em;width:9em}.page.settings .profile-key-inspector-information .input.select.tooltip-top{display:inline-block}.page.settings .key-export .actions{margin:1em 0}.page.settings .key-export .input.textarea.gpgkey textarea.fluid.code{height:27em}.page.settings .profile-passphrase .password-management-bg{background:transparent url(../../../img/illustrations/passphrase_intro.png) center center no-repeat;background-size:contain;height:16em}.page.settings .profile-passphrase .input.checkbox{margin-bottom:1em}.page.settings .profile-passphrase .passphrase-help{margin-top:2em;padding:1.5em;background-color:#daecf9}.page.settings .profile-passphrase .passphrase-help h3{margin:0;border-bottom:none}.page.settings .profile-passphrase .password{zoom:1}.page.settings .profile-passphrase .password:after,.page.settings .profile-passphrase .password:before{content:"";display:table}.page.settings .profile-passphrase .password:after{clear:both}.page.settings .profile-passphrase .password:after,.page.settings .profile-passphrase .password:before{content:"";display:table}.page.settings .profile-passphrase .password:after{clear:both}.page.settings .profile-passphrase .password input{width:calc(100% - 16rem)}.page.settings .profile-passphrase .password .input .message.error{clear:both}.page.settings .profile-passphrase .password .password-view{width:2em;height:1.5em;padding:.44rem .8rem .5rem .9rem}.page.settings .profile-passphrase .password-complexity .progress{width:calc(100% - 12.6rem)}.page.settings .profile-passphrase .password-hints{margin:.5em 0 1em 0}.page.settings .profile-passphrase .password-hints li{font-size:1rem;line-height:1.5rem}.page.settings .profile-choose-security-token .input-security-token{zoom:1;margin:1em 0 1.5em 0}.page.settings .profile-choose-security-token .input-security-token:after,.page.settings .profile-choose-security-token .input-security-token:before{content:"";display:table}.page.settings .profile-choose-security-token .input-security-token:after{clear:both}.page.settings .profile-choose-security-token .input-security-token:after,.page.settings .profile-choose-security-token .input-security-token:before{content:"";display:table}.page.settings .profile-choose-security-token .input-security-token:after{clear:both}.page.settings .profile-choose-security-token .input-security-token label{margin-bottom:.5em}.page.settings .profile-choose-security-token .input-security-token .input.text{-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em;font-size:2rem;max-width:5rem;float:left;text-align:center;margin-right:1em}.page.settings .profile-choose-security-token .input-security-token .circle-picker{float:left}.page.settings .profile-choose-security-token .input-security-token .randomize-button-wrapper{float:left;text-align:center;clear:both;cursor:pointer}.page.settings .profile-choose-security-token .input-security-token .svg-icon svg{height:.85em;width:.85em}@media all and (max-width:950px){.page.settings .profile-detailed-information .avatar{float:none}}@media (max-width:1280px){.key-info .table-info{font-size:.875em}.key-info .table-info .select select{font-size:.929em}}.themes .theme{float:left;border-radius:2px}.themes .theme a{max-width:275px;display:block;margin:1em;border:1px solid #ddd;padding:1em;box-shadow:0 0 10px 0 #ddd;border-radius:3px}.themes .theme a:hover{border:1px solid #2a9ceb}.themes .theme .theme-desc{padding-top:1em;text-align:center}.themes .theme.selected{font-weight:700}.themes .theme.selected a{background:#eee;box-shadow:inset 0 1px 2px rgba(0,0,0,.2);border:1px solid #bbb}html.launching .launching-screen{display:block;width:100%;height:100%;position:absolute;z-index:999;background:#fff}html.launching .launching-screen .launching-screen-holder{width:20%;margin:auto;margin-top:7em}html.launching .launching-screen .progress-bar-wrapper{margin-bottom:0}html.launching .launching-screen p{margin:1em 0;font-size:.75em}.launching-screen{display:none}@media all and (min-width:460px){.page.error .grid{text-align:center;width:100%;margin-bottom:2.5em}.page.error.error-400 .row,.page.error.error-404 .row,.page.error.error-500 .row{max-width:400px;margin:auto}.page.error.error-400 .grid:before,.page.error.error-404 .grid:before,.page.error.error-500 .grid:before{font-size:15em;font-weight:700;color:#333}.page.error.error-404 .grid:before{content:"404"}.page.error.error-400 .grid:before{content:"400"}.page.error.error-500 .grid:before{content:"500"}}.page.setup,.page.status{margin-bottom:2.5em}.page.setup .grid,.page.status .grid{padding-bottom:2em}.page.setup #url-rewriting-warning,.page.status #url-rewriting-warning{display:none}.page.setup .grid .message,.page.status .grid .message{padding:.75em 1em;margin-bottom:.5em}.page.setup .grid .input .message,.page.status .grid .input .message{padding:0 0 .5em 0}.cake-error{display:none}#js_mfa_iframe{width:100%;height:calc(100% - 2.5em);position:absolute;box-sizing:border-box;padding:0;margin:0;background:#fff}.mfa.iframe .grid,.mfa.iframe .grid-responsive-12{max-width:none}.mfa.iframe .actions-wrapper{margin-top:3em}.mfa.iframe .totp-setup .input-verify{float:left;background:#f3f3f3;padding:2.5em;max-width:25em;height:262px;min-width:22em;box-sizing:border-box;border:3px solid #f3f3f3;border-left:0}.mfa.iframe .totp-setup .input-verify .helptext{max-width:18em}.mfa.iframe .totp-setup .qrcode{float:left;max-width:262px;box-sizing:border-box;max-height:262px;border:3px solid #f3f3f3}.mfa.iframe .how-it-works p{width:28%;float:left;color:#666}.mfa.iframe .how-it-works p+p{margin-left:5%}.mfa.iframe .how-it-works p+p+p{margin-left:8%}.mfa.iframe .mfa-providers{zoom:1}.mfa.iframe .mfa-providers:after,.mfa.iframe .mfa-providers:before{content:"";display:table}.mfa.iframe .mfa-providers:after{clear:both}.mfa.iframe .mfa-providers:after,.mfa.iframe .mfa-providers:before{content:"";display:table}.mfa.iframe .mfa-providers:after{clear:both}.mfa.iframe .mfa-providers li{float:left;-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em;width:12.5em;margin-bottom:2em;margin-right:2em;border:1px solid #ddd}.mfa.iframe .mfa-providers li:hover{border:1px solid #ddd;box-shadow:0 0 10px 0 #ddd}.mfa.iframe .mfa-providers a{border-bottom:1px solid #ddd;display:block;text-align:center}.mfa.iframe .mfa-providers a span{padding:1em 0 2em 0;display:block}.mfa.iframe .mfa-providers a img{display:block;padding:2em 0 .5em 0;height:5em}.mfa.iframe .mfa-providers .mfa-provider-status{padding:1em;background:#f3f3f3;text-align:center}.mfa.iframe .mfa-providers .mfa-provider-status.disabled{color:#888}.mfa.iframe .mfa-trusted-device{padding:1em;display:flex}.mfa.iframe .mfa-trusted-device:nth-child(even){background:#f3f3f3}.mfa.iframe .mfa-trusted-device .device{flex:1;font-size:2.5em;text-align:center;color:#666}.mfa.iframe .mfa-trusted-device .device.current:before{content:'\2022';color:#090;font-size:.75em;position:absolute;margin-left:-.5em}.mfa.iframe .mfa-trusted-device .session{flex:2 0 10em}.mfa.iframe .mfa-trusted-device .action{flex:1;padding-top:.5em}.mfa.iframe .mfa-trusted-device table td,.mfa.iframe .mfa-trusted-device table th{padding:.125em 1em}.mfa.iframe .mfa-trusted-device table th{font-weight:700}.page.administration .workspace-main .grid{position:absolute;bottom:0;top:2.125em;padding:0;overflow-y:scroll}.dialog .ldap-test-settings-report .directory-list span.error{color:#d40101}.dialog .ldap-test-settings-report p.directory-errors.error{padding:1em 0 0 0;color:#d40101}.dialog .ldap-test-settings-report .accordion-directory-structure .error{color:#d40101}.dialog .ldap-test-settings-report .accordion-directory-errors textarea{font-family:"Courier New",Courier,monospace;font-size:11px;overflow:auto;height:220px}.report-widget .colors.red{color:#d40101}.report-widget .colors.green{color:#090}.report-widget .colors.orange{color:#9f6000}.report-widget .colors.blue{color:#5bbbff}.report-widget.gauge div.widget-content{min-height:220.567px!important}.report-widget.gauge p.widget-description{color:#969696;text-align:center;margin-top:-20px}.report-widget.simple-number{text-align:center}.report-widget.simple-number .widget-content{width:145px;height:145px;background-color:#f3f3f3;border-radius:50%;display:inline-block;margin-top:20px;border:5px solid #f3f3f3}.report-widget.simple-number .widget-content span{font-size:27px;margin-top:51px;display:inline-block;color:#000}.report-widget.simple-number p.widget-description{color:#969696;text-align:center;margin-top:25px}.report-widget .plots{margin-top:2em}.report-widget .plots .plot-container{margin-top:2em}.report-widget .plots .plot-container .plot{display:inline-block;width:80%}.report-widget .plots .plot-container .plot span.label{font-size:1em}.report-widget .plots .plot-container .plot .bar-container{position:relative;border:1px solid #ddd;height:5px;margin-top:.3em}.report-widget .plots .plot-container .plot .bar-container .bar{position:absolute;top:-1px;left:-1px;height:5px;border:1px solid #5bbbff;background:#5bbbff}.report-widget .plots .plot-container .plot-value{width:15%;font-size:1.6em;padding:.35em 0 0 .5em;line-height:1em;float:right}.report-widget.widget-dashboard-count{position:relative}.report-widget.widget-dashboard-count.items .illustration{background:#ff6c70}.report-widget.widget-dashboard-count.items .illustration svg{margin-top:1.3em}.report-widget.widget-dashboard-count.groups .illustration{background:#00ba93}.report-widget.widget-dashboard-count.groups .illustration svg{margin-top:.9em;width:2.8em;height:2.8em}.report-widget.widget-dashboard-count.users .illustration{background:#5bbbff}.report-widget.widget-dashboard-count.users .illustration svg{margin-top:1.2em}.report-widget.widget-dashboard-count .count .number{display:block;font-size:2.5em;line-height:1em}.report-widget.widget-dashboard-count .count .label{display:block;font-size:1.25em;line-height:1em;margin-top:.6em}.report-widget.widget-dashboard-count .illustration{position:absolute;top:2.5em;right:2.75em;width:4.7em;height:4.7em;-webkit-border-radius:50%;-moz-border-radius:50%;border-radius:50%;background:#5bbbff;text-align:center}.report-widget.widget-dashboard-count .illustration svg{margin-top:1.1em;fill:#fff;color:#fff;height:2.2em;width:2.2em}.report-widget.widget-dashboard-count .since{font-size:.8em;margin-top:1.8em}.report-widget.widget-dashboard-count .since span{color:#090}.report-widget.widget-dashboard-plots{margin-top:2em;padding-bottom:4em}.report-widget.widget-dashboard-plots.items .plots .plot-container .bar-container .bar{background:#ff6c70!important;border:1px solid #ff6c70!important}.report-widget.widget-login-history{margin-top:2em;margin-bottom:3em}.report-widget.widget-login-history .chart{margin-top:2em}.page.printable-report{background-color:#f3f3f3}.page.printable-report .page-wrapper{width:900px;min-height:1536px;background-color:#fff;padding:3em;margin:3em auto;box-sizing:border-box;box-shadow:0 0 10px 0 #ddd}.page.printable-report .page-wrapper .report-header .creator-info .label{display:inline-block;width:150px;margin-bottom:.25em}.page.printable-report .page-wrapper .report-header .creator-info .value{display:inline-block;margin-bottom:.25em}.page.printable-report .page-wrapper .report-header .company-info{text-align:right}.page.printable-report .page-wrapper .report-header .company-info .logo{margin-top:65px;width:200px;float:right}.page.printable-report .page-wrapper .report-content{margin-top:2em}.page.printable-report .page-wrapper .report-content .row.charts{margin-top:2em}.page.printable-report .page-wrapper .report-content .row.list{margin-top:3em}.page.printable-report .page-wrapper .report-content .table-info{border-right:0;border-left:0;border-bottom:0}.page.printable-report .page-wrapper .report-content .table-info tr td:first-of-type,.page.printable-report .page-wrapper .report-content .table-info tr th:first-of-type{width:30%}.page.printable-report .page-wrapper .report-content .table-info tr td{word-wrap:break-word;color:#666;vertical-align:middle}.page.printable-report .page-wrapper .report-content .table-info tr td+td{color:#666}.page.printable-report .page-wrapper .report-content .table-info tr span.email,.page.printable-report .page-wrapper .report-content .table-info tr span.name{display:block;max-width:300px}.page.printable-report .page-wrapper .report-content .table-info tr span.email{font-size:.8em;color:#969696}@media print{@page{size:auto;margin:0}.page.printable-report{background-color:#fff}.page.printable-report .page-wrapper{min-height:0!important;box-shadow:none!important;color:#ddd}}.reports-workspace .accordion.navigation.first{border:none;padding:.625em 0 0 0;margin:0}.reports-workspace .workspace-reports-content{position:relative;height:100%}.reports-workspace .workspace-reports-content .report-wrapper{top:0;bottom:2.45em;padding:1em 2em;position:absolute;width:100%;overflow-y:scroll;background-color:#f3f3f3;box-sizing:border-box}.reports-workspace .workspace-reports-content .report-wrapper .report-page{margin-top:3em}.reports-workspace .workspace-reports-content .report-wrapper .report-page .report-loading{text-align:center;padding-top:10em}.reports-workspace .workspace-reports-content .report-wrapper .report-page .report-loading .spinner{background:transparent url(../../../img/controls/loading_light.svg) center center no-repeat;content:' ';height:50px}.reports-workspace .workspace-reports-content .report-widget{background-color:#fff;padding:2.6em;border:1px solid #ddd;box-shadow:0 0 10px 0 #ddd;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.reports-workspace .workspace-reports-content .report-widget h2{font-weight:400;line-height:1em;margin:0;font-size:1.45em}.reports-workspace .workspace-reports-content iframe{width:100%;height:100%} \ No newline at end of file +*/.chosen-container{position:relative;display:inline-block;vertical-align:middle;font-size:13px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.chosen-container *{-webkit-box-sizing:border-box;box-sizing:border-box}.chosen-container .chosen-drop{position:absolute;top:100%;z-index:1010;width:100%;border:1px solid #aaa;border-top:0;background:#fff;-webkit-box-shadow:0 4px 5px rgba(0,0,0,.15);box-shadow:0 4px 5px rgba(0,0,0,.15);display:none}.chosen-container.chosen-with-drop .chosen-drop{display:block}.chosen-container a{cursor:pointer}.chosen-container .chosen-single .group-name,.chosen-container .search-choice .group-name{margin-right:4px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-weight:400;color:#969696}.chosen-container .chosen-single .group-name:after,.chosen-container .search-choice .group-name:after{content:"\003A";padding-left:2px;vertical-align:top}.chosen-container-single .chosen-single{position:relative;display:block;overflow:hidden;padding:0 0 0 8px;height:25px;border:1px solid #bbb;border-radius:5px;background-color:#fff;background:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#fff),color-stop(50%,#ddd),color-stop(52%,#eee),to(#f3f3f3));background:linear-gradient(#fff 20%,#ddd 50%,#eee 52%,#f3f3f3 100%);background-clip:padding-box;-webkit-box-shadow:0 0 3px #fff inset,0 1px 1px rgba(0,0,0,.1);box-shadow:0 0 3px #fff inset,0 1px 1px rgba(0,0,0,.1);color:#333;text-decoration:none;white-space:nowrap;line-height:24px}.chosen-container-single .chosen-single input[type=text]{cursor:pointer;opacity:0;position:absolute}.chosen-container-single .chosen-default{color:#888}.chosen-container-single .chosen-single span{display:block;overflow:hidden;margin-right:32px;text-overflow:ellipsis;white-space:nowrap}.chosen-container-single .chosen-single-with-deselect span{margin-right:38px}.chosen-container-single .chosen-single abbr{position:absolute;top:6px;right:26px;display:block;width:12px;height:12px;background:url('../../../img/third_party/chosen-sprite.png') -42px 1px no-repeat;font-size:1px}.chosen-container-single .chosen-single abbr:hover{background-position:-42px -10px}.chosen-container-single.chosen-disabled .chosen-single abbr:hover{background-position:-42px -10px}.chosen-container-single .chosen-single div{position:absolute;top:0;right:.8rem;display:block;width:1.8rem;height:100%}.chosen-container-single .chosen-single div b{display:block;width:100%;height:100%;background:url("../../../img/third_party/chosen-sprite.png") no-repeat 0 2px}.chosen-container-single .chosen-search{position:relative;z-index:1010;margin:0;padding:3px 4px;white-space:nowrap}.chosen-container-single .chosen-search span.svg-icon{position:absolute;margin-left:-1.5em;margin-top:.35em}.chosen-container-single .chosen-search input[type=text]{margin:1px 0;padding:4px 20px 4px 5px;width:100%;height:auto;outline:0;border:1px solid #bbb;background:url("../../../img/third_party/chosen-sprite.png") no-repeat 100% -20px;font-size:1em;font-family:sans-serif;line-height:normal;border-radius:0}.chosen-container-single .chosen-drop{margin-top:-.5em;border-radius:0 0 4px 4px;background-clip:padding-box}.chosen-container-single.chosen-container-single-nosearch .chosen-search{position:absolute;clip:rect(0,0,0,0)}.chosen-container .chosen-results{color:#333;position:relative;overflow-x:hidden;overflow-y:auto;margin:0 4px 4px 0;padding:0 0 0 4px;max-height:240px;-webkit-overflow-scrolling:touch}.chosen-container .chosen-results li{display:none;margin:0;padding:5px 6px;list-style:none;line-height:15px;word-wrap:break-word;-webkit-touch-callout:none}.chosen-container .chosen-results li.active-result{display:list-item;cursor:pointer}.chosen-container .chosen-results li.disabled-result{display:list-item;color:#ddd;cursor:default}.chosen-container .chosen-results li:hover{background-color:#daecf9;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#3875d7),color-stop(90%,#2a62bc));background-image:linear-gradient(#3875d7 20%,#2a62bc 90%);color:#fff}.chosen-container .chosen-results li.no-results{color:#666;display:list-item;background:#f3f3f3}.chosen-container .chosen-results li.group-result{display:list-item;font-weight:700;cursor:default}.chosen-container .chosen-results li.group-option{padding-left:15px}.chosen-container .chosen-results li em{font-style:normal;text-decoration:underline}.chosen-container-multi .chosen-choices{position:relative;overflow:hidden;margin:0;padding:0 5px;width:100%;height:auto;border:1px solid #bbb;background-color:#fff;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(1%,#eee),color-stop(15%,#fff));background-image:linear-gradient(#eee 1%,#fff 15%);cursor:text}.chosen-container-multi .chosen-choices li{float:left;list-style:none}.chosen-container-multi .chosen-choices li.search-field{margin:0;padding:0;white-space:nowrap}.chosen-container-multi .chosen-choices li.search-field input[type=text]{margin:1px 0;padding:0;height:25px;outline:0;border:0!important;background:0 0!important;-webkit-box-shadow:none;box-shadow:none;color:#888;font-size:100%;font-family:sans-serif;line-height:normal;border-radius:0;width:25px}.chosen-container-multi .chosen-choices li.search-choice{position:relative;margin:3px 5px 3px 0;padding:3px 20px 3px 5px;border:1px solid #bbb;max-width:100%;border-radius:3px;background-color:#fff;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#fff),color-stop(50%,#ddd),color-stop(52%,#eee),to(#f3f3f3));background-image:linear-gradient(#fff 20%,#ddd 50%,#eee 52%,#f3f3f3 100%);background-size:100% 19px;background-repeat:repeat-x;background-clip:padding-box;-webkit-box-shadow:0 0 2px #fff inset,0 1px 0 rgba(0,0,0,.05);box-shadow:0 0 2px #fff inset,0 1px 0 rgba(0,0,0,.05);color:#ddd;line-height:13px;cursor:default}.chosen-container-multi .chosen-choices li.search-choice span{word-wrap:break-word}.chosen-container-multi .chosen-choices li.search-choice .search-choice-close{position:absolute;top:4px;right:3px;display:block;width:12px;height:12px;background:url("../../../img/third_party/chosen-sprite.png") -42px 1px no-repeat;font-size:1px}.chosen-container-multi .chosen-choices li.search-choice .search-choice-close:hover{background-position:-42px -10px}.chosen-container-multi .chosen-choices li.search-choice-disabled{padding-right:5px;border:1px solid #ccc;background-color:#f3f3f3;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#fff),color-stop(50%,#ddd),color-stop(52%,#eee),to(#f3f3f3));background-image:linear-gradient(#fff 20%,#ddd 50%,#eee 52%,#f3f3f3 100%);color:#666}.chosen-container-multi .chosen-choices li.search-choice-focus{background:#ddd}.chosen-container-multi .chosen-choices li.search-choice-focus .search-choice-close{background-position:-42px -10px}.chosen-container-multi .chosen-results{margin:0;padding:0}.chosen-container-multi .chosen-drop .result-selected{display:list-item;color:#eee;cursor:default}.chosen-container-active .chosen-single{border:1px solid #2894df;-webkit-box-shadow:0 0 5px rgba(0,0,0,.3);box-shadow:0 0 5px rgba(0,0,0,.3)}.chosen-container-active.chosen-with-drop .chosen-single{border:1px solid #bbb;border-bottom-right-radius:0;border-bottom-left-radius:0;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#eee),color-stop(80%,#fff));background-image:linear-gradient(#eee 20%,#fff 80%);-webkit-box-shadow:0 1px 0 #fff inset;box-shadow:0 1px 0 #fff inset}.chosen-container-active.chosen-with-drop .chosen-single div{border-left:none;background:0 0}.chosen-container-active.chosen-with-drop .chosen-single div b{background-position:-18px 2px}.chosen-container-active .chosen-choices{border:1px solid #2894df;-webkit-box-shadow:0 0 5px rgba(0,0,0,.3);box-shadow:0 0 5px rgba(0,0,0,.3)}.chosen-container-active .chosen-choices li.search-field input[type=text]{color:#333!important}.chosen-disabled{opacity:.5!important;cursor:default}.chosen-disabled .chosen-single{cursor:default}.chosen-disabled .chosen-choices .search-choice .search-choice-close{cursor:default}.chosen-rtl{text-align:right}.chosen-rtl .chosen-single{overflow:visible;padding:0 8px 0 0}.chosen-rtl .chosen-single span{margin-right:0;margin-left:26px;direction:rtl}.chosen-rtl .chosen-single-with-deselect span{margin-left:38px}.chosen-rtl .chosen-single div{right:auto;left:3px}.chosen-rtl .chosen-single abbr{right:auto;left:26px}.chosen-rtl .chosen-choices li{float:right}.chosen-rtl .chosen-choices li.search-field input[type=text]{direction:rtl}.chosen-rtl .chosen-choices li.search-choice{margin:3px 5px 3px 0;padding:3px 5px 3px 19px}.chosen-rtl .chosen-choices li.search-choice .search-choice-close{right:auto;left:4px}.chosen-rtl.chosen-container-single .chosen-results{margin:0 0 4px 4px;padding:0 4px 0 0}.chosen-rtl .chosen-results li.group-option{padding-right:15px;padding-left:0}.chosen-rtl.chosen-container-active.chosen-with-drop .chosen-single div{border-right:none}.chosen-rtl .chosen-search input[type=text]{padding:4px 5px 4px 20px;background:url("../../../img/third_party/chosen-sprite.png") no-repeat -30px -20px;direction:rtl}.chosen-rtl.chosen-container-single .chosen-single div b{background-position:6px 2px}.chosen-rtl.chosen-container-single.chosen-with-drop .chosen-single div b{background-position:-12px 2px}@media only screen and (-webkit-min-device-pixel-ratio:1.5),only screen and (min-resolution:144dpi),only screen and (min-resolution:1.5dppx){.chosen-container .chosen-results-scroll-down span,.chosen-container .chosen-results-scroll-up span,.chosen-container-multi .chosen-choices .search-choice .search-choice-close,.chosen-container-single .chosen-search input[type=text],.chosen-container-single .chosen-single abbr,.chosen-container-single .chosen-single div b,.chosen-rtl .chosen-search input[type=text]{background-image:url('../../../img/third_party/chosen-sprite@2x.png')!important;background-size:52px 37px!important;background-repeat:no-repeat!important}}.chosen-container{margin-bottom:.5em}.chosen-container.chosen-disabled a.chosen-single{border:1px solid #ccc}.chosen-container a.chosen-single{color:#333;display:block;text-overflow:ellipsis;white-space:nowrap;height:3.8rem;width:100%;background:#f3f3f3;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;border:1px solid #ccc;border-top:1px solid #bbb;box-shadow:none;margin:0 0 .8rem 0}.chosen-container a.chosen-single span{font-size:1.6rem;padding-left:.4rem;padding-top:.4rem;margin-top:.2rem;color:#333}.chosen-container a.chosen-single span.svg-icon{float:left}.chosen-container a.chosen-single div b{background-position:0 .6rem}.chosen-container .chosen-search input.chosen-search-input[type=text]{width:95%!important;color:#666}.chosen-container .chosen-drop{border:1px solid #ccc!important;border-top:0!important;-webkit-border-radius:0!important;-moz-border-radius:0!important;border-radius:0!important;background:#fff!important}.chosen-container .chosen-drop .chosen-results{color:#333}.chosen-container.chosen-container-active.chosen-with-drop a.chosen-single{background:#fff;border:1px solid #ccc;border-top:1px solid #bbb;box-shadow:none}.chosen-container.chosen-container-active.chosen-with-drop a.chosen-single div b{background-position:-1.8rem .8rem}.singleline.connection_info .input.text.first-field,.singleline.connection_info .input.text:first-child{margin-right:0}.singleline.connection_info .input.text.protocol{flex:0 0 2.5rem}.singleline.connection_info .input.text.protocol .chosen-container{display:block}.singleline.connection_info .input.text.protocol .chosen-container a.chosen-single div b{background-position:0 .8rem}.singleline.connection_info .input.text.protocol .chosen-container-active.chosen-with-drop a.chosen-single div b{background-position:-1.8rem .8rem}.singleline.connection_info .input.text.host{flex:1 0 auto}.singleline.connection_info .input.text.port{flex:0 0 9.5rem}.autocomplete-suggestions{text-align:left;cursor:default;border:1px solid #ddd;border-top:0;background:#fff;box-shadow:0 0 10px 0 #ddd;position:absolute;display:none;z-index:9999;max-height:120px;overflow:hidden;overflow-y:auto;box-sizing:border-box;width:350px}.autocomplete-suggestions .autocomplete-suggestion{position:relative;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:#333;font-size:.875em;display:block;padding:.357em .714em;border:0}.autocomplete-suggestions .autocomplete-suggestion b{font-weight:400;color:#333}.autocomplete-suggestions .autocomplete-suggestion.selected{background:#eee}#user-locale-input{border:none;background:#f3f3f3 url('../../../img/controls/chevron-down_black.svg') 92% center/1rem no-repeat;appearance:none;margin-top:.8rem;padding-left:.4rem;padding-right:2rem;line-height:2rem;width:auto}#user-locale-input:focus{outline:0;border:1px solid #2894df;border-radius:0}#user-locale-input option{background-color:#f3f3f3}.ldap-test-settings-report div.directory-structure{border:1px solid #ddd;padding:.5em 0 .5em .5em}.ldap-test-settings-report div.directory-structure>ul>li{margin-left:.5em}.ldap-test-settings-report div.directory-structure ul{font-size:.8rem;list-style-type:square;list-style-position:inside}.ldap-test-settings-report div.directory-structure ul li{margin-left:1em}.ldap-test-settings-report div.directory-structure ul li em{color:#888;font-size:.8em}.ldap-test-settings-report div.directory-structure ul li.user{font-weight:400;list-style-type:circle}.ldap-test-settings-report div.directory-structure ul li.group{font-weight:700}.page.settings .main.panel .middle{overflow-y:auto}.page.settings .profile-detailed-information:after,.page.settings .profile-detailed-information:before{content:"";display:table}.page.settings .profile-detailed-information:after{clear:both}.page.settings .profile-detailed-information:after,.page.settings .profile-detailed-information:before{content:"";display:table}.page.settings .profile-detailed-information:after{clear:both}.page.settings .profile-detailed-information .avatar{float:left}.page.settings .profile-detailed-information .avatar img{border:1px solid #ddd;padding:0;width:12.5em;height:12.5em;margin:.5em 1em 0 0}.page.settings .profile-detailed-information .avatar .edit{width:12.5em;height:3.125em;background-color:rgba(0,0,0,.7);border:1px solid transparent;color:#fff;margin:-3.25em 0 2em 0;position:relative}.page.settings .profile-detailed-information .avatar .edit a{color:#fff;display:block;height:3.125em}.page.settings .profile-detailed-information .avatar .edit a:after,.page.settings .profile-detailed-information .avatar .edit a:before{content:"";display:table}.page.settings .profile-detailed-information .avatar .edit a:after{clear:both}.page.settings .profile-detailed-information .avatar .edit a:after,.page.settings .profile-detailed-information .avatar .edit a:before{content:"";display:table}.page.settings .profile-detailed-information .avatar .edit a:after{clear:both}.page.settings .profile-detailed-information .avatar .edit a .svg-icon{float:left}.page.settings .profile-detailed-information .avatar .edit a .svg-icon svg{float:left;fill:white;width:1.6rem;height:1.6rem;margin:.8em}.page.settings .profile-detailed-information .avatar .edit a .help-text{float:left;display:block;font-size:.813em;margin-left:0;padding:.5em;width:9em}.page.settings .profile-key-inspector-information .input.select.tooltip-top{display:inline-block}.page.settings .profile-key-inspector-information .key-info .table-info .select select{margin:0;padding:.1rem 1.8rem .1rem .2rem;background-size:.8rem auto,100%}.page.settings .key-export .actions{margin:1em 0}.page.settings .key-export .input.textarea.gpgkey textarea.fluid.code{height:27em}.page.settings .profile-passphrase .password-management-bg{background:transparent url('../../../img/illustrations/passphrase_intro.png') center center no-repeat;background-size:contain;height:16rem}.page.settings .profile-passphrase .input.checkbox{margin-bottom:1em}.page.settings .profile-passphrase .password:after,.page.settings .profile-passphrase .password:before{content:"";display:table}.page.settings .profile-passphrase .password:after{clear:both}.page.settings .profile-passphrase .password:after,.page.settings .profile-passphrase .password:before{content:"";display:table}.page.settings .profile-passphrase .password:after{clear:both}.page.settings .profile-passphrase .password input{width:calc(100% - 30rem)}.page.settings .profile-passphrase .password .security-token{margin-top:0;margin-left:.8rem}.page.settings .profile-passphrase .password .input .message.error{clear:both}.page.settings .profile-passphrase .password-complexity .progress{width:calc(100% - 25.9rem)}.page.settings .profile-passphrase .enter-passphrase .submit-wrapper{display:flex;flex-direction:row}.page.settings .profile-passphrase .password-hints{margin:.8rem 0 1.6rem 0}.page.settings .profile-passphrase .password-hints li{font-size:1.6rem}.page.settings .profile-choose-security-token .input-security-token{margin:1em 0 1.5em 0}.page.settings .profile-choose-security-token .input-security-token:after,.page.settings .profile-choose-security-token .input-security-token:before{content:"";display:table}.page.settings .profile-choose-security-token .input-security-token:after{clear:both}.page.settings .profile-choose-security-token .input-security-token:after,.page.settings .profile-choose-security-token .input-security-token:before{content:"";display:table}.page.settings .profile-choose-security-token .input-security-token:after{clear:both}.page.settings .profile-choose-security-token .input-security-token label{margin-bottom:.5em}.page.settings .profile-choose-security-token .input-security-token .input.text{-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem;font-size:3.8rem;max-width:12rem;float:left;text-align:center;margin-right:2.4rem}.page.settings .profile-choose-security-token .input-security-token .circle-picker{float:left}.page.settings .profile-choose-security-token .input-security-token .randomize-button-wrapper{float:left;text-align:center;clear:both;cursor:pointer}.page.settings .profile-mobile-transfer .mobile-transfer-bg{background:transparent url('../../../img/diagrams/mobile-transfer.svg') left center no-repeat;background-size:contain;height:16em;margin:2em 0}@media all and (max-width:950px){.page.settings .profile-detailed-information .avatar{float:none}}@media (max-width:1280px){.key-info .table-info{font-size:.875em}.key-info .table-info .select select{font-size:.929em}}html.launching .launching-screen{display:block;width:100%;height:100%;position:absolute;z-index:999;background:#fff}html.launching .launching-screen .launching-screen-holder{width:20%;margin:auto;margin-top:7em}html.launching .launching-screen .progress-bar-wrapper{margin-bottom:0}html.launching .launching-screen p{margin:1em 0;font-size:.75em}.launching-screen{display:none}@media all and (min-width:460px){.page.error .grid{text-align:center;width:100%;margin-bottom:2.5em}.page.error.error-400 .row,.page.error.error-404 .row,.page.error.error-500 .row{max-width:400px;margin:auto}.page.error.error-400 .grid:before,.page.error.error-404 .grid:before,.page.error.error-500 .grid:before{font-size:15em;font-weight:700;color:#333}.page.error.error-404 .grid:before{content:"404"}.page.error.error-400 .grid:before{content:"400"}.page.error.error-500 .grid:before{content:"500"}}.page.setup,.page.status{margin-bottom:2.5em}.page.setup .grid,.page.status .grid{padding-bottom:2em}.page.setup #url-rewriting-warning,.page.status #url-rewriting-warning{display:none}.page.setup .grid .message,.page.status .grid .message{padding:.75em 1em;margin-bottom:.5em}.page.setup .grid .input .message,.page.status .grid .input .message{padding:0 0 .5em 0}.cake-error{display:none}.themes .theme{float:left;border-radius:2px}.themes .theme a{max-width:275px;display:block;margin:1em;border:1px solid #ddd;padding:1em;box-shadow:0 0 10px 0 #ddd;border-radius:3px}.themes .theme a:hover{border:1px solid #2a9ceb}.themes .theme .theme-desc{padding-top:1em;text-align:center}.themes .theme.selected{font-weight:700}.themes .theme.selected a{background:#eee;box-shadow:inset 0 1px 2px rgba(0,0,0,.2);border:1px solid #bbb}#js_mfa_iframe{width:100%;height:calc(100% - 2.5em);position:absolute;box-sizing:border-box;padding:0;margin:0;background:#fff}.mfa.iframe .grid,.mfa.iframe .grid-responsive-12{max-width:none}.mfa.iframe .actions-wrapper{margin-top:3em}.mfa.iframe .totp-setup .input-verify{float:left;background:#f3f3f3;padding:2.5em;max-width:25em;height:262px;min-width:22em;box-sizing:border-box;border:3px solid #f3f3f3;border-left:0}.mfa.iframe .totp-setup .input-verify .helptext{max-width:18em}.mfa.iframe .totp-setup .qrcode{float:left;max-width:262px;box-sizing:border-box;max-height:262px;border:3px solid #f3f3f3}.mfa.iframe .how-it-works p{width:28%;float:left;color:#666}.mfa.iframe .how-it-works p+p{margin-left:5%}.mfa.iframe .how-it-works p+p+p{margin-left:8%}.mfa.iframe .mfa-providers:after,.mfa.iframe .mfa-providers:before{content:"";display:table}.mfa.iframe .mfa-providers:after{clear:both}.mfa.iframe .mfa-providers:after,.mfa.iframe .mfa-providers:before{content:"";display:table}.mfa.iframe .mfa-providers:after{clear:both}.mfa.iframe .mfa-providers li{float:left;-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem;width:12.5em;margin-bottom:2em;margin-right:2em;border:1px solid #ddd}.mfa.iframe .mfa-providers li:hover{border:1px solid #ddd;box-shadow:0 0 10px 0 #ddd}.mfa.iframe .mfa-providers a{border-bottom:1px solid #ddd;display:block;text-align:center}.mfa.iframe .mfa-providers a span{padding:1em 0 2em 0;display:block}.mfa.iframe .mfa-providers a img{display:block;padding:2em 0 .5em 0;height:5em}.mfa.iframe .mfa-providers .mfa-provider-status{padding:1em;background:#f3f3f3;text-align:center}.mfa.iframe .mfa-providers .mfa-provider-status.disabled{color:#888}.mfa.iframe .mfa-trusted-device{padding:1em;display:flex}.mfa.iframe .mfa-trusted-device:nth-child(even){background:#f3f3f3}.mfa.iframe .mfa-trusted-device .device{flex:1;font-size:2.5em;text-align:center;color:#666}.mfa.iframe .mfa-trusted-device .device.current:before{content:'\2022';color:#090;font-size:.75em;position:absolute;margin-left:-.5em}.mfa.iframe .mfa-trusted-device .session{flex:2 0 10em}.mfa.iframe .mfa-trusted-device .action{flex:1;padding-top:.5em}.mfa.iframe .mfa-trusted-device table td,.mfa.iframe .mfa-trusted-device table th{padding:.125em 1em}.mfa.iframe .mfa-trusted-device table th{font-weight:700}.page.administration .workspace-main .grid{position:absolute;bottom:0;top:2.125em;padding:0;overflow-y:scroll}.ldap-settings input[type=text]{max-width:100%}.ldap-settings .singleline{max-width:100%}.dialog .ldap-test-settings-report .directory-list span.error{color:#d40101}.dialog .ldap-test-settings-report p.directory-errors.error{padding:1em 0 0 0;color:#d40101}.dialog .ldap-test-settings-report .accordion-directory-structure .error{color:#d40101}.dialog .ldap-test-settings-report .accordion-directory-errors textarea{font-family:"Courier New",Courier,monospace;font-size:11px;overflow:auto;height:220px}.page.administration .mfa-settings .provider-section .description.enabled{display:none}.page.administration .mfa-settings .provider-section.enabled .description.disabled{display:none}.page.administration .mfa-settings .provider-section.enabled .description.enabled{display:block}.report-widget .colors.red{color:#d40101}.report-widget .colors.green{color:#090}.report-widget .colors.orange{color:#9f6000}.report-widget .colors.blue{color:#5bbbff}.report-widget.gauge div.widget-content{min-height:220.567px!important}.report-widget.gauge p.widget-description{color:#969696;text-align:center;margin-top:-20px}.report-widget.simple-number{text-align:center}.report-widget.simple-number .widget-content{width:145px;height:145px;background-color:#f3f3f3;border-radius:50%;display:inline-block;margin-top:20px;border:5px solid #f3f3f3}.report-widget.simple-number .widget-content span{font-size:27px;margin-top:51px;display:inline-block;color:#000}.report-widget.simple-number p.widget-description{color:#969696;text-align:center;margin-top:25px}.report-widget .plots{margin-top:2em}.report-widget .plots .plot-container{margin-top:2em}.report-widget .plots .plot-container .plot{display:inline-block;width:80%}.report-widget .plots .plot-container .plot span.label{font-size:1em}.report-widget .plots .plot-container .plot .bar-container{position:relative;border:1px solid #ddd;height:5px;margin-top:.3em}.report-widget .plots .plot-container .plot .bar-container .bar{position:absolute;top:-1px;left:-1px;height:5px;border:1px solid #5bbbff;background:#5bbbff}.report-widget .plots .plot-container .plot-value{width:15%;font-size:1.6em;padding:.35em 0 0 .5em;line-height:1em;float:right}.report-widget.widget-dashboard-count{position:relative}.report-widget.widget-dashboard-count.items .illustration{background:#ff6c70}.report-widget.widget-dashboard-count.items .illustration svg{margin-top:1.3em}.report-widget.widget-dashboard-count.groups .illustration{background:#00ba93}.report-widget.widget-dashboard-count.groups .illustration svg{margin-top:.9em;width:2.8em;height:2.8em}.report-widget.widget-dashboard-count.users .illustration{background:#5bbbff}.report-widget.widget-dashboard-count.users .illustration svg{margin-top:1.2em}.report-widget.widget-dashboard-count .count .number{display:block;font-size:2.5em;line-height:1em}.report-widget.widget-dashboard-count .count .label{display:block;font-size:1.25em;line-height:1em;margin-top:.6em}.report-widget.widget-dashboard-count .illustration{position:absolute;top:2.5em;right:2.75em;width:4.7em;height:4.7em;-webkit-border-radius:50%;-moz-border-radius:50%;border-radius:50%;background:#5bbbff;text-align:center}.report-widget.widget-dashboard-count .illustration svg{margin-top:1.1em;fill:#FFF;color:#fff;height:2.2em;width:2.2em}.report-widget.widget-dashboard-count .since{font-size:.8em;margin-top:1.8em}.report-widget.widget-dashboard-count .since span{color:#090}.report-widget.widget-dashboard-plots{margin-top:2em;padding-bottom:4em}.report-widget.widget-dashboard-plots.items .plots .plot-container .bar-container .bar{background:#ff6c70!important;border:1px solid #ff6c70!important}.report-widget.widget-login-history{margin-top:2em;margin-bottom:3em}.report-widget.widget-login-history .chart{margin-top:2em}.page.printable-report{background-color:#f3f3f3}.page.printable-report .page-wrapper{width:900px;min-height:1536px;background-color:#fff;padding:3em;margin:3em auto;box-sizing:border-box;box-shadow:0 0 10px 0 #ddd}.page.printable-report .page-wrapper .report-header .creator-info .label{display:inline-block;width:150px;margin-bottom:.25em}.page.printable-report .page-wrapper .report-header .creator-info .value{display:inline-block;margin-bottom:.25em}.page.printable-report .page-wrapper .report-header .company-info{text-align:right}.page.printable-report .page-wrapper .report-header .company-info .logo{margin-top:65px;width:200px;float:right}.page.printable-report .page-wrapper .report-content{margin-top:2em}.page.printable-report .page-wrapper .report-content .row.charts{margin-top:2em}.page.printable-report .page-wrapper .report-content .row.list{margin-top:3em}.page.printable-report .page-wrapper .report-content .table-info{border-right:0;border-left:0;border-bottom:0}.page.printable-report .page-wrapper .report-content .table-info tr td:first-of-type,.page.printable-report .page-wrapper .report-content .table-info tr th:first-of-type{width:30%}.page.printable-report .page-wrapper .report-content .table-info tr td{word-wrap:break-word;color:#666;vertical-align:middle}.page.printable-report .page-wrapper .report-content .table-info tr td+td{color:#666}.page.printable-report .page-wrapper .report-content .table-info tr span.email,.page.printable-report .page-wrapper .report-content .table-info tr span.name{display:block;max-width:300px}.page.printable-report .page-wrapper .report-content .table-info tr span.email{font-size:.8em;color:#969696}@media print{@page{size:auto;margin:0}.page.printable-report{background-color:#fff}.page.printable-report .page-wrapper{min-height:0!important;box-shadow:none!important;color:#ddd}}.reports-workspace .accordion.navigation.first{border:none;padding:.625em 0 0 0;margin:0}.reports-workspace .workspace-reports-content{position:relative;height:100%}.reports-workspace .workspace-reports-content .report-wrapper{top:0;bottom:2.45em;padding:1em 2em;position:absolute;width:100%;overflow-y:scroll;background-color:#f3f3f3;box-sizing:border-box}.reports-workspace .workspace-reports-content .report-wrapper .report-page{margin-top:3em}.reports-workspace .workspace-reports-content .report-wrapper .report-page .report-loading{text-align:center;padding-top:10em}.reports-workspace .workspace-reports-content .report-wrapper .report-page .report-loading .spinner{background:transparent url('../../../img/controls/loading_light.svg') center center no-repeat;content:' ';height:50px}.reports-workspace .workspace-reports-content .report-widget{background-color:#fff;padding:2.6em;border:1px solid #ddd;box-shadow:0 0 10px 0 #ddd;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.reports-workspace .workspace-reports-content .report-widget h2{font-weight:400;line-height:1em;margin:0;font-size:1.45em}.reports-workspace .workspace-reports-content iframe{width:100%;height:100%} \ No newline at end of file diff --git a/webroot/css/themes/default/api_webinstaller.min.css b/webroot/css/themes/default/api_webinstaller.min.css index 811e60d74c..227db6b234 100644 --- a/webroot/css/themes/default/api_webinstaller.min.css +++ b/webroot/css/themes/default/api_webinstaller.min.css @@ -1,16 +1,12 @@ /**! * @name passbolt-styleguide - * @version v3.2.1 - * @date 2021-05-19 + * @version v3.4.0 + * @date 2021-12-01 * @copyright Copyright 2021 Passbolt SA * @source https://github.com/passbolt/passbolt_styleguide * @license AGPL-3.0 */ -a,abbr,acronym,address,applet,b,big,blockquote,body,caption,center,cite,code,dd,del,dfn,div,dl,dt,em,fieldset,font,form,h1,h2,h3,h4,h5,h6,html,i,iframe,img,ins,kbd,label,legend,li,object,ol,p,pre,q,s,samp,small,span,strike,strong,sub,sup,table,tbody,td,tfoot,th,thead,tr,tt,u,ul,var{margin:0;padding:0;border:0;outline:0;font-size:100%;vertical-align:baseline;background:0 0}body{line-height:1}:focus{outline:0}ins{text-decoration:none}del{text-decoration:line-through}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}[hidden]{display:none}html{font-size:100%;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}button,html,input,select,textarea{font-family:"Open Sans",Verdana,sans-serif}a{outline:0;text-decoration:none;cursor:pointer}a:hover{text-decoration:none;cursor:pointer}h1{font-size:2em;margin:.67em 0}h2{font-size:1.5em;margin:.83em 0}h3{font-size:1.17em;margin:1em 0}h4{font-size:1em;margin:1.33em 0}h5{font-size:.83em;margin:1.67em 0}h6{font-size:.67em;margin:2.33em 0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}blockquote,q{quotes:none}blockquote:after,blockquote:before,q:after,q:before{content:'';content:none}dfn{font-style:italic}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}p,pre{margin:1em 0}code,kbd,pre,samp{font-family:monospace,serif;font-size:1em}pre{white-space:pre;white-space:pre-wrap;word-wrap:break-word}q{quotes:none}q:after,q:before{content:'';content:none}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}nav ol,nav ul,ol,ul{list-style:none;list-style-image:none}img{border:0;-ms-interpolation-mode:bicubic}svg:not(:root){overflow:hidden}figure{margin:0}form{margin:0}legend{border:0;padding:0;white-space:normal}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline}button,input{line-height:normal}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default;pointer-events:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top}table{border-collapse:collapse;border-spacing:0}table td,table th{text-align:left;font-weight:400}.ir{background-color:transparent;border:0;overflow:hidden}.ir:before{content:"";display:block;width:0;height:150%}html body .hidden{display:none}.visually-hidden,.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visually-hidden .focusable:active,.visually-hidden .focusable:focus,.visuallyhidden .focusable:active,.visuallyhidden .focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.clearfix{zoom:1}.clearfix:after,.clearfix:before{content:"";display:table}.clearfix:after{clear:both}.rounded{-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em}.left{float:left}.right{float:right}.ellipsis{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.inline{display:inline-block}@-webkit-keyframes rotate{from{-webkit-transform:rotate(0)}to{-webkit-transform:rotate(-360deg)}}@-moz-keyframes rotate{from{-moz-transform:rotate(0)}to{-moz-transform:rotate(-360deg)}}.rotate{-webkit-animation-name:rotate;-webkit-animation-duration:4s;-webkit-animation-iteration-count:infinite;-webkit-animation-timing-function:linear;-moz-animation-name:rotate;-moz-animation-duration:4s;-moz-animation-iteration-count:infinite;-moz-animation-timing-function:linear}.rotate-right{transform:rotate(-90deg)}.blink{animation:blinker 1s linear infinite}.blink-fast{animation:blinker .25s linear infinite}@keyframes blinker{50%{opacity:0}}body{color:#333;background:#fff}body.iframe{background:0 0}a:link,a:visited{color:#333}a:hover{color:#2894df}a:active,a:focus{outline:0;color:#2894df;border:0}a{border-bottom:1px solid #ddd}a:hover{border-bottom:1px solid #2894df}a.disabled{outline:0;text-decoration:none;cursor:default;color:#888;pointer-events:none}html{scroll-behavior:smooth}.animated{-webkit-animation-duration:.5s;animation-duration:.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both}@keyframes fadeInUp{0%{opacity:0;-webkit-transform:translateY(20px);-ms-transform:translateY(20px);transform:translateY(20px)}100%{opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}}@keyframes fadeOutUp{from{opacity:1}to{opacity:0;transform:translate3d(0,-100%,0)}}@keyframes fadeInDown{from{opacity:0;transform:translate3d(0,-100%,0)}to{opacity:1;transform:none}}@keyframes pop{0%{opacity:0;transform:scale(1)}55%{opacity:1;transform:scale(1)}65%{opacity:1;transform:scale(1.25)}75%{opacity:1;transform:scale(1)}100%{opacity:1;transform:scale(1)}}.fadeInDown{animation-name:fadeInDown}.fadeOutUp{animation-name:fadeOutUp}.fadeInUp{animation-name:fadeInUp}.pop{animation-name:pop}@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;src:local('Open Sans'),local('OpenSans'),url(../../../fonts/opensans-regular.woff) format('woff')}@font-face{font-family:'Open Sans';font-style:normal;font-weight:700;src:local('Open Sans Bold'),local('OpenSans-Bold'),url(../../../fonts/opensans-bold.woff) format('woff')}body{font-family:'Open Sans',Verdana,sans-serif;font-size:100%;line-height:1.42;font-weight:400}h1{font-size:1.5em}h2{font-size:1.25em}h3{font-size:1.125em;display:block;padding-bottom:.333em;border-bottom:1px dotted #ddd}p{margin:0 0 1em 0;line-height:1.5em}.code{font-family:"Courier New",Courier,monospace;font-size:.688em}.capitalize{text-transform:capitalize}/*! - * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome - * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - * @licstart - */@font-face{font-family:FontAwesome;src:url(../../../fonts/fontawesome-webfont.eot?v=4.7.0);src:url(../../../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0) format('embedded-opentype'),url(../../../fonts/fontawesome-webfont.woff2?v=4.7.0) format('woff2'),url(../../../fonts/fontawesome-webfont.woff?v=4.7.0) format('woff'),url(../../../fonts/fontawesome-webfont.ttf?v=4.7.0) format('truetype'),url(../../../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular) format('svg');font-weight:400;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-webkit-transform:scale(-1,1);-ms-transform:scale(-1,1);transform:scale(-1,1)}.fa-flip-vertical{-webkit-transform:scale(1,-1);-ms-transform:scale(1,-1);transform:scale(1,-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-rotate-90{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-close:before,.fa-remove:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-cog:before,.fa-gear:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-repeat:before,.fa-rotate-right:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-image:before,.fa-photo:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-exclamation-triangle:before,.fa-warning:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-cogs:before,.fa-gears:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-floppy-o:before,.fa-save:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-bars:before,.fa-navicon:before,.fa-reorder:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-sort:before,.fa-unsorted:before{content:"\f0dc"}.fa-sort-desc:before,.fa-sort-down:before{content:"\f0dd"}.fa-sort-asc:before,.fa-sort-up:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-gavel:before,.fa-legal:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-bolt:before,.fa-flash:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-clipboard:before,.fa-paste:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-chain-broken:before,.fa-unlink:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-caret-square-o-down:before,.fa-toggle-down:before{content:"\f150"}.fa-caret-square-o-up:before,.fa-toggle-up:before{content:"\f151"}.fa-caret-square-o-right:before,.fa-toggle-right:before{content:"\f152"}.fa-eur:before,.fa-euro:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-inr:before,.fa-rupee:before{content:"\f156"}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen:before{content:"\f157"}.fa-rouble:before,.fa-rub:before,.fa-ruble:before{content:"\f158"}.fa-krw:before,.fa-won:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-caret-square-o-left:before,.fa-toggle-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-try:before,.fa-turkish-lira:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-bank:before,.fa-institution:before,.fa-university:before{content:"\f19c"}.fa-graduation-cap:before,.fa-mortar-board:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-image-o:before,.fa-file-photo-o:before,.fa-file-picture-o:before{content:"\f1c5"}.fa-file-archive-o:before,.fa-file-zip-o:before{content:"\f1c6"}.fa-file-audio-o:before,.fa-file-sound-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-ring:before,.fa-life-saver:before,.fa-support:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before,.fa-resistance:before{content:"\f1d0"}.fa-empire:before,.fa-ge:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-hacker-news:before,.fa-y-combinator-square:before,.fa-yc-square:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-paper-plane:before,.fa-send:before{content:"\f1d8"}.fa-paper-plane-o:before,.fa-send-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-futbol-o:before,.fa-soccer-ball-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-ils:before,.fa-shekel:before,.fa-sheqel:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-bed:before,.fa-hotel:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-y-combinator:before,.fa-yc:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before,.fa-battery:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-paper-o:before,.fa-hand-stop-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-television:before,.fa-tv:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before{content:"\f2a3"}.fa-deaf:before,.fa-deafness:before,.fa-hard-of-hearing:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-sign-language:before,.fa-signing:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-address-card:before,.fa-vcard:before{content:"\f2bb"}.fa-address-card-o:before,.fa-vcard-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer-full:before,.fa-thermometer:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bath:before,.fa-bathtub:before,.fa-s15:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}/*! @licend */.icon{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.icon.fav:before{content:"\f005";color:#ddd}.icon.unfav:before{content:"\f005";color:#d40101}.svg-icon{display:inline-flex;align-self:center}.svg-icon svg{fill:#333;height:1em;width:1em}.svg-icon.baseline svg{top:.125em;position:relative}.svg-icon.dim svg,.svg-icon.light svg{fill:#888}.svg-icon.icon-only svg{position:relative;top:.25em;width:1.1rem;height:1.1rem}.button .svg-icon svg{top:.125em;position:relative}.button .svg-icon+span:not(.visuallyhidden){margin-left:.5em;display:inline-block}.button .svg-icon svg:hover,.button:hover .svg-icon svg{fill:#333}.button.primary:active .svg-icon svg,.button.primary:focus .svg-icon svg,.button.warning .svg-icon svg,.button.warning:active .svg-icon svg,.button.warning:focus .svg-icon svg,.button.warning:hover .svg-icon svg{fill:#fff}.button:active .svg-icon svg,.button:focus .svg-icon svg{fill:#2894df}.button.disabled .svg-icon svg{fill:#888}.button.disabled .svg-icon svg:hover{fill:#888}a:hover .svg-icon svg{fill:#2894df}a.disabled .svg-icon svg{fill:#888;pointer-events:none;cursor:default}a.disabled:hover .svg-icon svg{fill:#888}a.fav .svg-icon svg{fill:#d40101}a.unfav{display:inline-flex}a.unfav .svg-icon.star svg{fill:#ddd}.button,button,input[type=submit]{position:relative;font-size:.875em;min-height:1.428571em;padding:.4285714em 1.225em .4285714em 1.225em;display:inline-block;vertical-align:baseline;margin-right:.5em;outline:0;cursor:pointer;text-align:center;text-decoration:none;border:1px solid #ccc;color:#333;font-weight:400;text-shadow:1px 1px 0 rgba(255,255,255,.5);background:#eee;background-image:linear-gradient(top,#f3f3f3,#eee);-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em}.submit{display:inline-block}.button.big{font-size:1.125em}.button:hover,button:hover{color:#333;text-decoration:none;background:#f3f3f3;background-image:linear-gradient(top,#f3f3f3,#f3f3f3);border:1px solid #bbb}.button:focus,button:focus{color:#2894df;border:1px solid #2894df}.button:active,button:active{color:#2894df;border:1px solid #2894df;background:#eee;position:relative;box-shadow:inset 0 1px 2px rgba(0,0,0,.2)}.button.cancel,.button.cancel:active,.button.cancel:focus,.button.cancel:hover,.button.dim,.button.dim:active,.button.dim:focus,.button.dim:hover{background:#fff;font-weight:400}.button.primary{background:#2894df;border:1px solid #2894df;color:#fff;text-shadow:1px 1px 0 rgba(0,0,0,.2)}.button.primary:hover{background:#2a9ceb;border:1px solid #2a9ceb;color:#fff}.button.primary:active,.button.primary:focus{color:#fff;border:1px solid #4271b7;background:#2a9ceb}.button.warning,.button.warning:active,.button.warning:focus,.button.warning:hover{color:#fff;background:#d40101;border:1px solid #d40101;text-shadow:none}.button.warning:active,.button.warning:focus{border:1px solid #92000c}.button.disabled{cursor:default;color:#888;background:#fff;border:1px solid #ddd;text-shadow:none;font-weight:400}.button.disabled:active,.button.disabled:focus,.button.disabled:hover{box-shadow:0 0 0;top:0;color:#888;background:#fff;border:1px solid #ddd}.duo-wrapper{zoom:1}.duo-wrapper:after,.duo-wrapper:before{content:"";display:table}.duo-wrapper:after{clear:both}.duo-wrapper:after,.duo-wrapper:before{content:"";display:table}.duo-wrapper:after{clear:both}.duo-wrapper .button{float:left}.duo-wrapper .button.duo:nth-child(1){margin-right:0;border-top-right-radius:0;border-bottom-right-radius:0}.duo-wrapper .button.duo:nth-child(2){margin-left:0;border-left:0;border-top-left-radius:0;border-bottom-left-radius:0}.button-spacer,.buttons-spacer{margin-right:.5em}html body .button.processing{background:#fff;border:1px solid #fff;cursor:default}.button.processing:active,.button.processing:hover{border:1px solid #fff}.button.processing:after{line-height:2.25em;width:100%;height:100%;position:absolute;content:" ";top:-1px;left:-1px;background:#fff url(../../../img/controls/loading_light.svg) center center no-repeat;background-size:auto 50%;border:1px solid #ddd;border-radius:5px}input[type=submit].button.processing{color:transparent;text-shadow:none;background:#fff url(../../../img/controls/loading_light.svg) center center no-repeat;background-size:auto 50%;border:1px solid #ddd;border-radius:5px}.button-toggle.button.selected,.toggle.button.selected{background:#eee;box-shadow:inset 0 1px 2px rgba(0,0,0,.2);border:1px solid #bbb}label{font-weight:700;font-size:1em;line-height:1.333333em;padding:.5em 0;display:block}.required label:after{content:" \002A";color:#d40101;font-weight:700}.placeholder{color:#969696}::-webkit-input-placeholder{color:#969696}:-moz-placeholder,::-moz-placeholder{color:#969696}input[type=email],input[type=number],input[type=password],input[type=search],input[type=text],textarea{font-size:16px;line-height:24px;color:#333;background:#fff;width:14em;padding:.375em .625em .375em .625em;display:inline-block;margin-bottom:.5em;-webkit-appearance:none;border:1px solid #ccc;border-top:1px solid #bbb;vertical-align:middle;box-sizing:content-box}input[type=email]:hover,input[type=number]:hover,input[type=password]:hover,input[type=search]:hover,input[type=text]:hover,textarea:hover{border:1px solid #bbb}input[type=email]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=text]:focus,textarea:focus{box-shadow:inset 1px 1px 2px rgba(0,0,0,.2);border:1px solid #2894df}.input-focus{box-shadow:inset 1px 1px 2px rgba(0,0,0,.2);border:1px solid #2894df}input[type=email]:disabled,input[type=number]:disabled,input[type=password]:disabled,input[type=search]:disabled,input[type=text]:disabled,textarea:disabled{border:1px solid #ddd;box-shadow:0 0;cursor:pointer;background:#f3f3f3}textarea{height:4.8em;line-height:1.5em}.textarea.large textarea{width:30em;height:7em}textarea.message_notice{margin-bottom:0}textarea.full_report{height:12em;line-height:1.5em}.date.input .input-addon,.text.input .input-addon{position:absolute;margin-left:-2.625em}.date.input .input-addon i,.text.input .input-addon i{font-size:16px;width:1.25em;border:1px solid #ccc;border-top:1px solid #bbb;line-height:24px;color:#333;background:#fff;padding:.375em .625em .375em .625em}.date.input input:hover+.input-addon i,.text.input input:hover+.input-addon i{border:1px solid #bbb}.date.input input:focus+.input-addon i,.text.input input:focus+.input-addon i{border:1px solid #2894df}.date.input input:disabled+.input-addon i,.text.input input:disabled+.input-addon i{background:#f3f3f3;border:1px solid #ddd}.checkboxlist .input.checkbox label,.radiolist .input.radio label,input[type=checkbox]+label{font-weight:400;display:inline;margin-left:.25em;margin-bottom:.25em;cursor:pointer}.radiolist{zoom:1}.radiolist:after,.radiolist:before{content:"";display:table}.radiolist:after{clear:both}.radiolist:after,.radiolist:before{content:"";display:table}.radiolist:after{clear:both}.radiolist .input.radio{float:left}.radiolist .input.radio input{float:left;margin-top:.55em}.radiolist .input.radio input+label{font-weight:400;display:inline-block;margin-left:.65em;font-size:1em;float:left;margin-right:2em}.input .message{padding:0;padding-left:.25em;padding-bottom:.5em;font-size:.813em;margin-top:.125em;margin-bottom:.25em;background-color:transparent;border:0}.input .message.error{font-weight:400;color:#d40101}.input .message.helptext{color:#888;font-weight:400}.input .message:empty{display:none}.form-content .input .message,form .input .message{background-color:transparent}.input.error label,textarea.error label{color:#d40101}form .input .inline.message{display:inline-block}select{font-size:16px;margin:.55em 0}select.large{line-height:24px;color:#333;background:#fff;width:14em;padding:.375em .425em .375em .425em;display:inline-block;margin-bottom:.5em;border:1px solid #ccc;border-top:1px solid #bbb;vertical-align:middle;box-sizing:content-box}input[type=file]{width:100%;color:#333}input[type=email].fluid,input[type=password].fluid,input[type=text].fluid,select.fluid,textarea.fluid{width:80%}.submit-input-wrapper,.submit-wrapper{margin-top:.5em}.singleline{zoom:1}.singleline:after,.singleline:before{content:"";display:table}.singleline:after{clear:both}.singleline:after,.singleline:before{content:"";display:table}.singleline:after{clear:both}.singleline .input.text{float:left;width:40.2%}.singleline .input.text.first-field,.singleline .input.text:first-child{margin-right:2.5%}.singleline .input.text input[type=email],.singleline .input.text input[type=password],.singleline .input.text input[type=search],.singleline .input.text input[type=text]{width:93%}.form-content .accordion h3.accordion-header a{border:0}.form-content .accordion h3.accordion-header a:hover{border:0}.form-content .accordion h3.accordion-header a:before{font:normal normal normal 1em FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;width:1em;height:1em;position:absolute;text-align:center}.form-content table{width:100%;margin-bottom:1em}.form-content table thead{background-color:transparent!important}.form-content table thead th{background-color:transparent;font-weight:700;border:1px solid #ddd;padding:.7em .5em}.form-content table tbody tr{font-size:.8em;text-align:left}.form-content table tbody tr:nth-child(even){background-color:transparent}.form-content table tbody tr:nth-child(odd){background-color:#f3f3f3;color:#666}.form-content table tbody tr td{text-align:left;padding:.5em .5em;border:1px solid #ddd}.form-content table tbody tr td code{padding:.2em .3em;background-color:#ddd}.form-content table tbody tr td pre{margin:0;padding:0;background-color:transparent}.form-content table tbody tr td code,.form-content table tbody tr td pre{font-family:monospace,serif;font-size:1em}.form-content table tbody tr td em{color:#888;font-size:.8em;font-style:normal}.page{width:100%;min-width:320px;top:0;left:0;right:0;bottom:0;position:absolute;overflow:auto}.page .panel{height:100%;width:100%;position:absolute;overflow:auto}.page .panel.main{top:10em;bottom:2.45em;height:auto;padding:0}.page .panel.main .row{padding-left:0}.page .panel.main .panel.left{background:#fff}.page .header.second+.panel.main{top:6.875em}.page .header.third+.panel.main{top:10em;border-top:1px solid #ddd}.page .header{width:100%;overflow:hidden}.page .header.first{min-height:2.4em}.page .header.second{top:2.375em;height:4.5em}.page .header.third{top:6.875em;height:3em}.page .col1,.page .col2,.page .col2_3,.page .col3{position:absolute}.page .col1,.page .panel.left{width:17.25%;box-sizing:border-box}.page .panel.middle{width:82%;left:18%;overflow:hidden}.page .panel.middle.scroll{overflow-y:scroll}.page .col2{width:70%;left:18%}.page .col3,.page .panel.right{width:24%;left:75%}.page .col2_3{width:81.5%;left:18%}.page .panel.main .grid-responsive-12{float:left;width:100%;max-width:none;padding-left:.25em;box-sizing:border-box}@media all and (max-width:1024px){.page .panel.left{display:none}.page .header.second .col2{display:none}.page .header.second .col2_3{display:none}.page .header.third{height:4.5em}.page .header.third .col1,.page .header.third .col2,.page .header.third .col2_3{position:relative;float:left;left:auto;width:auto}.page .header.third .col3{display:none}.page .panel.main .panel.left{display:none}.page .panel.main .panel.middle{width:100%;left:0}.page .panel.main .row{padding-left:.5em}.page .panel.main .panel.aside{min-width:auto}}.grid,.grid-responsive-12{margin:0 auto;padding:0;max-width:1220px}.grid .row,.grid-responsive-12 .row{zoom:1;padding:0 .5em 0 .5em}.grid .row:after,.grid .row:before,.grid-responsive-12 .row:after,.grid-responsive-12 .row:before{content:"";display:table}.grid .row:after,.grid-responsive-12 .row:after{clear:both}.grid .row:after,.grid .row:before,.grid-responsive-12 .row:after,.grid-responsive-12 .row:before{content:"";display:table}.grid .row:after,.grid-responsive-12 .row:after{clear:both}.grid .row .col1,.grid .row .col10,.grid .row .col11,.grid .row .col12,.grid .row .col2,.grid .row .col3,.grid .row .col4,.grid .row .col5,.grid .row .col6,.grid .row .col7,.grid .row .col8,.grid .row .col9,.grid-responsive-12 .row .col1,.grid-responsive-12 .row .col10,.grid-responsive-12 .row .col11,.grid-responsive-12 .row .col12,.grid-responsive-12 .row .col2,.grid-responsive-12 .row .col3,.grid-responsive-12 .row .col4,.grid-responsive-12 .row .col5,.grid-responsive-12 .row .col6,.grid-responsive-12 .row .col7,.grid-responsive-12 .row .col8,.grid-responsive-12 .row .col9{zoom:1;position:relative;left:auto;float:none;width:99%;box-sizing:border-box}.grid .row .col10:after,.grid .row .col10:before,.grid .row .col11:after,.grid .row .col11:before,.grid .row .col12:after,.grid .row .col12:before,.grid .row .col1:after,.grid .row .col1:before,.grid .row .col2:after,.grid .row .col2:before,.grid .row .col3:after,.grid .row .col3:before,.grid .row .col4:after,.grid .row .col4:before,.grid .row .col5:after,.grid .row .col5:before,.grid .row .col6:after,.grid .row .col6:before,.grid .row .col7:after,.grid .row .col7:before,.grid .row .col8:after,.grid .row .col8:before,.grid .row .col9:after,.grid .row .col9:before,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col10:before,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col11:before,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col12:before,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col1:before,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col2:before,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col3:before,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col4:before,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col5:before,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col6:before,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col7:before,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col8:before,.grid-responsive-12 .row .col9:after,.grid-responsive-12 .row .col9:before{content:"";display:table}.grid .row .col10:after,.grid .row .col11:after,.grid .row .col12:after,.grid .row .col1:after,.grid .row .col2:after,.grid .row .col3:after,.grid .row .col4:after,.grid .row .col5:after,.grid .row .col6:after,.grid .row .col7:after,.grid .row .col8:after,.grid .row .col9:after,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col9:after{clear:both}.grid .row .col10:after,.grid .row .col10:before,.grid .row .col11:after,.grid .row .col11:before,.grid .row .col12:after,.grid .row .col12:before,.grid .row .col1:after,.grid .row .col1:before,.grid .row .col2:after,.grid .row .col2:before,.grid .row .col3:after,.grid .row .col3:before,.grid .row .col4:after,.grid .row .col4:before,.grid .row .col5:after,.grid .row .col5:before,.grid .row .col6:after,.grid .row .col6:before,.grid .row .col7:after,.grid .row .col7:before,.grid .row .col8:after,.grid .row .col8:before,.grid .row .col9:after,.grid .row .col9:before,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col10:before,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col11:before,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col12:before,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col1:before,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col2:before,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col3:before,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col4:before,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col5:before,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col6:before,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col7:before,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col8:before,.grid-responsive-12 .row .col9:after,.grid-responsive-12 .row .col9:before{content:"";display:table}.grid .row .col10:after,.grid .row .col11:after,.grid .row .col12:after,.grid .row .col1:after,.grid .row .col2:after,.grid .row .col3:after,.grid .row .col4:after,.grid .row .col5:after,.grid .row .col6:after,.grid .row .col7:after,.grid .row .col8:after,.grid .row .col9:after,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col9:after{clear:both}.grid .row .col1 img,.grid .row .col10 img,.grid .row .col11 img,.grid .row .col12 img,.grid .row .col2 img,.grid .row .col3 img,.grid .row .col4 img,.grid .row .col5 img,.grid .row .col6 img,.grid .row .col7 img,.grid .row .col8 img,.grid .row .col9 img,.grid-responsive-12 .row .col1 img,.grid-responsive-12 .row .col10 img,.grid-responsive-12 .row .col11 img,.grid-responsive-12 .row .col12 img,.grid-responsive-12 .row .col2 img,.grid-responsive-12 .row .col3 img,.grid-responsive-12 .row .col4 img,.grid-responsive-12 .row .col5 img,.grid-responsive-12 .row .col6 img,.grid-responsive-12 .row .col7 img,.grid-responsive-12 .row .col8 img,.grid-responsive-12 .row .col9 img{width:100%;height:auto;display:block}@media all and (min-width:780px){.grid .row{padding:0 .5em 0 .5em}.grid .row .col1,.grid .row .col10,.grid .row .col11,.grid .row .col12,.grid .row .col2,.grid .row .col3,.grid .row .col4,.grid .row .col5,.grid .row .col6,.grid .row .col7,.grid .row .col8,.grid .row .col9{float:left;position:relative;margin:0 3% 0 0}.grid .row .last{margin-right:0}.grid .row .col1{width:5.5%}.grid .row .col2{width:14%}.grid .row .col3{width:22.5%}.grid .row .col4{width:31%}.grid .row .col5{width:39.5%}.grid .row .col6{width:48%}.grid .row .col7{width:56.5%}.grid .row .col8{width:65%}.grid .row .col9{width:73.5%}.grid .row .col10{width:82%}.grid .row .col11{width:90.5%}.grid .row .col12{width:99%;margin:0}.grid .row .push1{margin-left:5.5%}.grid .row .push2{margin-left:14%}.grid .row .push3{margin-left:22.5%}.grid .row .push4{margin-left:31%}}@media all and (max-width:768px){.hidden-xs{display:none}}.panel.middle .grid{padding-bottom:2em;max-width:100%}.panel.middle .grid .breadcrumbs{border-bottom:1px dotted #ddd}.header h2{margin:1em 0}.header h2 .button{margin-right:.3em;margin-top:-5px}@media all and (min-width:1024px){.panel.middle{overflow-y:scroll}}@media all and (max-width:1024px){.col1,.col2,.col2_3,.col3,.header.first,.header.second,.header.second+.panel.main,.page,.panel{position:relative;width:auto;display:block;margin:0;top:auto;overflow:auto}.col3{display:none}.header.second{text-align:center;margin-bottom:2.5em}}.scroll{overflow-y:scroll;background:linear-gradient(#fff 30%,rgba(255,255,255,0)),linear-gradient(rgba(255,255,255,0),#fff 70%) 0 100%,radial-gradient(farthest-side at 75% 0,rgba(0,0,0,.2),rgba(0,0,0,0)),radial-gradient(farthest-side at 75% 100%,rgba(0,0,0,.2),rgba(0,0,0,0)) 0 100%;background-repeat:no-repeat;background-color:#fff;background-size:100% 10px,100% 10px,100% 5px,100% 5px}::-webkit-scrollbar{width:1em}::-webkit-scrollbar-track{background:#f9f9f9;border-top:0;border-bottom:0}::-webkit-scrollbar-thumb{background:#c1c1c1;border-radius:1em;border:4px solid #f9f9f9}.header{overflow:visible!important}.header.first{background:#333}.header.second{background:#eee}.header .navigation.primary{zoom:1}.header .navigation.primary:after,.header .navigation.primary:before{content:"";display:table}.header .navigation.primary:after{clear:both}.header .navigation.primary:after,.header .navigation.primary:before{content:"";display:table}.header .navigation.primary:after{clear:both}.header .navigation.primary li{padding:.5em;float:left}.header .navigation.primary li:first-child{padding-left:1em}.header .navigation.primary li.right{float:right;margin-right:1em}.header .navigation.primary li a{color:#ddd;text-decoration:none;border:0;display:inline-block}.header .navigation.primary li a:hover{color:#fff}.header .navigation.primary li a:active,.header .navigation.primary li a:focus{color:#2894df}.header .navigation.primary li a.highlighted{background-color:#2894df;padding:0 .5em 0 .5em;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.header .navigation.primary li a.highlighted:active,.header .navigation.primary li a.highlighted:focus{color:#ddd}.header .navigation.primary li .row.selected a{color:#fff}.header .navigation.primary li .row.selected a:focus{color:#2894df}.header .navigation.primary .github-star{display:none;position:absolute;right:1em;top:4px}@media all and (min-width:600px){.header .navigation.primary .github-star{display:block}}.header .logo{margin:1.25em 0 0 1em;max-width:80%}.logo.no-img{background:transparent url(../../../img/logo/logo.svg) 0 0 no-repeat;background-size:150px auto;width:150px;height:30px}.logo h1{display:none}.logo.bigger{background:transparent url(../../../img/logo/logo.svg) 0 0 no-repeat;background-size:200px auto;width:200px;height:45px}.header.second .col1{min-width:200px}@media only screen and (-moz-min-device-pixel-ratio:1.5),only screen and (-o-min-device-pixel-ratio:1.5),only screen and (-webkit-min-device-pixel-ratio:1.5),only screen and (min-devicepixel-ratio:1.5),only screen and (min-resolution:1.5dppx){.logo.no-img{background:transparent url(../../../img/logo/logo.svg) 0 0 no-repeat;background-size:150px auto}.logo.bigger{background:transparent url(../../../img/logo/logo.svg) 0 0 no-repeat;background-size:200px auto}}.progress-bar{background:0 0;width:100%;height:2px;display:block}.progress-bar span{background:#d40101;height:2px;display:block}.progress-bar-wrapper{border:1px solid #ddd;height:10px;display:block;margin:2em 0 1em 0;padding:2px}.progress-bar-wrapper .progress-bar.big{width:100%;height:10px;display:block;clear:both}.progress-bar-wrapper .progress-bar.big .progress{background:#d40101;width:5%;display:block;height:10px}.progress-bar-wrapper .progress-bar.big.infinite{background:#d40101}.progress-bar-wrapper .progress-bar.big.infinite .progress{width:100%;overflow:hidden;background:url(../../../img/controls/infinite-bar.gif) repeat-x;-moz-opacity:.5;-khtml-opacity:.5;opacity:.5}.progress-details{color:#888;margin:.5em 0 .5em 0}.progress-details .progress-percent{float:right}.update-loading-bar{position:fixed;display:block;width:100%;bottom:2.35em;z-index:991}.update-loading-bar .progress-bar span{transition:width 2s;transition-timing-function:cubic-bezier(.45,1.27,.76,.9)}.notification-container{font-size:.85em;top:0;position:absolute;z-index:991;height:2em;padding-top:1em;width:60%;margin-left:20%}.notification-container .notification{position:relative;left:50%;float:left;clear:both;margin-bottom:1em}.notification-container .notification .message{box-shadow:0 0 10px 0 #333;border-radius:2px;padding:.5em 1em;position:relative;left:-50%;float:left;color:#000;font-weight:400;width:auto}.notification-container .notification .message.warning{color:#333;background:#fef0bf}.notification-container .notification .message .content{margin-right:1em}.notification-container .notification .message .content strong{text-transform:capitalize}.notification-container .notification .message .action{border-bottom:none;margin-left:.4em}.notification-container .notification .message .action:hover .svg-icon svg{fill:#000}.dialog-wrapper{position:absolute;width:100%;height:100%;z-index:800;background:rgba(255,255,255,.9);overflow:auto}.dialog-wrapper .placeholder{position:relative;width:1em;margin:auto;height:100%}.dialog-wrapper .placeholder .loading{position:absolute;top:30%;height:2em;width:2em;background:url(../../../img/controls/loading_light.svg);background-size:2em 2em}.dialog{position:relative;max-width:30em;border:1px solid #ddd;background:#f3f3f3;margin:auto;margin-top:1%;margin-bottom:3em;box-shadow:0 0 10px 0 #ddd}.dialog.medium{max-width:36em}.dialog .dialog-header{padding:.5em 1em 0 1em;height:3em}.dialog .dialog-header h2{margin:.25em 2em .5em .2em;font-size:1.2em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.dialog .dialog-header h2 svg{position:relative;top:.0625em}.dialog .dialog-header .tooltip-alt{margin-left:.3em;font-size:.7em;font-weight:400;color:#333}.dialog .dialog-header .tooltip-alt .tooltip-text{white-space:normal}.dialog .dialog-header .dialog-header-subtitle{padding-top:.3em;padding-left:1em;font-size:.7em;color:#333}.dialog .dialog-header .dialog-close{margin-top:-2.5em}.dialog .form-content{background:#fff;padding:.813em 1.5em 1.5em 1.5em}.dialog p{margin-top:.5em;font-size:1em}.dialog p+.checkbox{padding-bottom:.5em}.dialog p+.checkbox label{font-size:1em}.dialog label{clear:both;font-size:.9375em;line-height:1.6em}.dialog input[type=text],.dialog select.large,.dialog textarea{width:100%;box-sizing:border-box;zoom:1}.dialog input[type=text]:after,.dialog input[type=text]:before,.dialog select.large:after,.dialog select.large:before,.dialog textarea:after,.dialog textarea:before{content:"";display:table}.dialog input[type=text]:after,.dialog select.large:after,.dialog textarea:after{clear:both}.dialog input[type=text]:after,.dialog input[type=text]:before,.dialog select.large:after,.dialog select.large:before,.dialog textarea:after,.dialog textarea:before{content:"";display:table}.dialog input[type=text]:after,.dialog select.large:after,.dialog textarea:after{clear:both}.dialog input+.message,.dialog textarea+.message{display:none}.dialog input+.message.error,.dialog textarea+.message.error{display:block;clear:both;width:100%}.dialog .input .message.warning,.dialog input+.message.warning,.dialog textarea+.message.warning{display:block;clear:both;width:100%;color:#9f6000;background-color:transparent}.dialog .inline-error{color:#d40101;font-weight:700}.dialog .accordion-header a{display:inline}.dialog .submit-wrapper{margin:0;clear:both;width:100%;padding:.75em 0}.dialog .submit-wrapper .button,.dialog .submit-wrapper .cancel{float:right;margin-right:1.25em;font-size:1.125em}.dialog .submit-wrapper .button{box-sizing:border-box;min-width:5em}.dialog .submit-wrapper .secondary{float:left;margin-left:1.5em;margin-top:.7em;font-size:1em}.dialog .submit-wrapper .cancel{margin-top:.7em;font-size:1em}.dialog-close,.dialog-close:hover{display:block;float:right;border:0}.dialog-close:active{-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em;box-shadow:inset 0 1px 2px rgba(0,0,0,.2)}.dialog-close .fa-close{padding-top:15px;width:30px;height:15px;text-align:center;display:block;vertical-align:baseline;line-height:0;border:1px solid #eee;-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em}.dialog-close .svg-icon{padding:7px 0 7px 0;width:30px;height:15px;text-align:center;display:block;vertical-align:baseline;line-height:15px;border:1px solid #eee;-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em}.dialog-close:hover .fa-close,.dialog-close:hover .svg-icon{border:1px solid #ddd;-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em}@media all and (max-width:480px){.dialog{margin:0;border:0;box-shadow:none;width:100%;max-width:100%;margin-bottom:2.5em}}.footer{margin-top:0;position:fixed;bottom:0;padding:1.167em 1.5% 0 .5%;text-align:right;font-size:.75em;width:98%;height:2em;background:#fff;border-top:1px solid #ddd;z-index:890}.footer .footer-links{width:100%;margin-top:-.25em}.footer .footer-links li{display:inline;margin-right:1.5em}.footer .footer-links li.error-message a{background-color:#fff;color:#d40101}.footer .footer-links li .github-star{display:inline;position:absolute;margin-left:-8em;margin-top:-1px}.footer .footer-links a:not(.gh-btn):not(.gh-count){border:0}.plugin-check{padding:1em}.plugin-check.firefox{background:transparent url(../../../img/third_party/firefox_logo.png) 1.5em center no-repeat;background-size:150px auto}.plugin-check.chrome{background:transparent url(../../../img/third_party/ChromeWebStore.png) .5em center no-repeat;background-size:180px auto}.plugin-check.gpg{background:transparent url(../../../img/third_party/gnupg_logo_disabled.png) 2em center no-repeat;background-size:150px auto}.plugin-check.gpg.success{background:#edf7eb url(../../../img/third_party/gnupg_logo.png) 2em center no-repeat;background-size:150px auto}.plugin-check.success{background-color:#edf7eb}.plugin-check.error{background-color:#ffe4e4}.plugin-check.warning{background-color:#fef0bf}.plugin-check.notice{background-color:#daecf9}.plugin-check .message{font-weight:400;margin-left:180px;padding:.5em 1em .25em 1em}@media all and (max-width:1024px){.plugin-check.chrome,.plugin-check.chrome.error,.plugin-check.firefox,.plugin-check.gpg,.plugin-check.gpg.success{background-image:none}.plugin-check .message{margin-left:0;padding:.5em 1em}}.panel .navigation{margin-top:1em}.panel .navigation h2{padding:0;margin:0}.panel .navigation a{color:#333;border:0;display:block}.panel .navigation li{font-size:.9375em;padding:.469em 0 .469em 1.333em;border-left:5px solid #fff}.panel .navigation li:hover{color:#333;border-left:5px solid #ddd}.panel .navigation li.active,.panel .navigation li.selected{font-weight:700;border-left:5px solid #d40101}.js .message.no-js{display:none}.cookies .message.no-cookies{display:none}.message{padding:1em}.message a{border-bottom:1px solid #888}.message a:hover{border-bottom:1px solid #2894df}.message.error{color:#333;background:#ffe4e4}.message.error a:link,.message.error a:visited{color:#333;border-bottom:1px dotted #888}.message.error a:hover{color:#333;border-bottom:1px solid #888}.message.success{color:#333;background:#edf7eb}.message.notice{color:#333;background:#daecf9}.message.warning{color:#333;background:#fef0bf}.message p:last-child{margin-bottom:0}.message.side-message{margin-left:1em;font-size:1em;margin-right:2em}.message.side-message p,.message.side-message ul{padding-bottom:1em}.message.animated{background:#fff;color:#333;display:flex;border:1px solid #ddd;border-radius:3px}.message.animated .illustration{flex:0 0 180px}.message.animated .additional-information{flex:1;line-height:180px;margin-top:1.5em;padding-left:1em}@keyframes drawCircle{0%{stroke-dashoffset:180px}100%{stroke-dashoffset:0}}@keyframes drawCheck{0%{stroke-dashoffset:50px}100%{stroke-dashoffset:0}}@keyframes drawCross{0%{stroke-dashoffset:50px}100%{stroke-dashoffset:0}}@keyframes drawWarning{0%{stroke-dashoffset:180px}100%{stroke-dashoffset:0}}.message.animated #successAnimationCircle{stroke-dasharray:180px 180px;stroke:#090}.message.animated #successAnimationCheck{stroke-dasharray:50px 50px;stroke:#090}.message.animated #errorAnimationCircle{stroke-dasharray:180px 180px;stroke:#d40101}.message.animated #errorAnimationCross{stroke-dasharray:50px 50px;stroke:#d40101}.message.animated #warningAnimation{stroke-dasharray:180px 180px;stroke:#9f6000;stroke-linecap:square;stroke-linejoin:round}.message.animated #warningAnimation.round{stroke-linecap:round}.message.animated .animated{animation:.75s ease-out 0s 1 both pop}.message.animated .animated #errorAnimationCircle,.message.animated .animated #successAnimationCircle{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawCircle}.message.animated .animated #successAnimationCheck{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawCheck}.message.animated .animated #errorAnimationCross{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawCross}.message.animated .animated #warningAnimation{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawWarning}@media only screen and (max-width:767px){.message.animated{flex-direction:column}}table.table-info,table.table-info.vertical{border-collapse:separate}table.table-info td,table.table-info.vertical td{padding:.625em;padding-right:1.25em;color:#666}table.table-info td.warning,table.table-info.vertical td.warning{background:#fef0bf}table.table-info td.error,table.table-info.vertical td.error{background:#ffe4e4}table.table-info td .alt.side,table.table-info.vertical td .alt.side{color:#666;float:right}table.table-info td .alt.side:before,table.table-info.vertical td .alt.side:before{content:"\0028"}table.table-info td .alt.side:after,table.table-info.vertical td .alt.side:after{content:"\0029"}table.table-info td+td,table.table-info.vertical td+td{color:#333;padding-right:.625em}table.table-info.horizontal{width:100%;border-collapse:collapse;padding:0;border:1px solid #ddd;margin-bottom:1em}table.table-info.horizontal.with-borders td,table.table-info.horizontal.with-borders th{border-right:1px solid #ddd}table.table-info.horizontal td,table.table-info.horizontal th{padding:.5em .5em .5em .75em;margin:0}table.table-info.horizontal th{border-bottom:1px solid #ddd}table.table-info.horizontal th a{display:block;border:0}table.table-info.horizontal th a.sortable .svg-icon svg{top:.15em;position:relative;margin-left:.5em}table.table-info.horizontal thead tr{background:#fff}table.table-info.horizontal tbody{background:#fff}table.table-info.horizontal tbody tr:nth-child(odd){background:#fff}table.table-info.horizontal tbody tr:hover{background:#f8f8f8}.table-info-pagination .pagination-limit{margin-top:-.5em;float:left}.table-info-pagination .pagination-limit label{float:left;font-weight:400}.table-info-pagination .pagination-limit select{float:left;margin:.35em}.table-info-pagination .pagination-pages{float:right}.table-info-pagination .pagination-pages .page-location{float:left}.table-info-pagination .pagination-pages .page-buttons{float:right}.table-info-pagination .pagination-pages .button{float:left;margin:-.5em 0 0 .75em;padding:.35em .75em}.tooltip,[data-tooltip]{position:relative;cursor:pointer}.tooltip:after,.tooltip:before,[data-tooltip]:after,[data-tooltip]:before{position:absolute;visibility:hidden;opacity:0;transition:opacity .2s ease-in-out,visibility .2s ease-in-out,transform .2s cubic-bezier(.71,1.7,.77,1.24);transform:translate3d(0,0,0);pointer-events:none;text-align:center}.always-show:after,.always-show:before,.tooltip:focus:after,.tooltip:focus:before,.tooltip:hover:after,.tooltip:hover:before,[data-tooltip]:focus:after,[data-tooltip]:focus:before,[data-tooltip]:hover:after,[data-tooltip]:hover:before{visibility:visible;opacity:1}.tooltip:before,[data-tooltip]:before{z-index:990;border:6px solid transparent;background:0 0;content:""}.tooltip:after,[data-tooltip]:after{z-index:990;padding:8px;width:160px;background-color:#333;color:#fff;content:attr(data-tooltip);font-size:14px;line-height:1.2em;font-weight:400;text-shadow:none}.tooltip-top:after,.tooltip-top:before,.tooltip:after,.tooltip:before,[data-tooltip]:after,[data-tooltip]:before{bottom:100%;left:50%}.tooltip-top:before,.tooltip:before,[data-tooltip]:before{margin-left:-6px;margin-bottom:-12px;border-top-color:#333}.tooltip-top:after,.tooltip:after,[data-tooltip]:after{margin-left:-80px}.tooltip-top.always-show:after,.tooltip-top.always-show:before,.tooltip-top:focus:after,.tooltip-top:focus:before,.tooltip-top:hover:after,.tooltip-top:hover:before,.tooltip.always-show:after,.tooltip.always-show:before,.tooltip:focus:after,.tooltip:focus:before,.tooltip:hover:after,.tooltip:hover:before,[data-tooltip]:focus:after,[data-tooltip]:focus:before,[data-tooltip]:hover:after,[data-tooltip]:hover:before{-webkit-transform:translateY(-12px);-moz-transform:translateY(-12px);transform:translateY(-12px)}.tooltip-left:after,.tooltip-left:before{right:100%;bottom:50%;left:auto}.tooltip-left:before{margin-left:0;margin-right:-12px;margin-bottom:0;border-top-color:transparent;border-left-color:#333;border-left-color:hsla(0,0%,20%,.9)}.tooltip-left.always-show:after,.tooltip-left.always-show:before,.tooltip-left:focus:after,.tooltip-left:focus:before,.tooltip-left:hover:after,.tooltip-left:hover:before{-webkit-transform:translateX(-12px);-moz-transform:translateX(-12px);transform:translateX(-12px)}.tooltip-bottom:after,.tooltip-bottom:before{top:100%;bottom:auto;left:50%}.tooltip-bottom:before{margin-top:-12px;margin-bottom:0;border-top-color:transparent;border-bottom-color:#333;border-bottom-color:hsla(0,0%,20%,.9)}.tooltip-bottom.always-show:after,.tooltip-bottom.always-show:before,.tooltip-bottom:focus:after,.tooltip-bottom:focus:before,.tooltip-bottom:hover:after,.tooltip-bottom:hover:before{-webkit-transform:translateY(12px);-moz-transform:translateY(12px);transform:translateY(12px)}.tooltip-right:after,.tooltip-right:before{bottom:50%;left:100%}.tooltip-right:before{margin-bottom:0;margin-left:-12px;border-top-color:transparent;border-right-color:#333}.tooltip-right.always-show:after,.tooltip-right.always-show:before,.tooltip-right:focus:after,.tooltip-right:focus:before,.tooltip-right:hover:after,.tooltip-right:hover:before{-webkit-transform:translateX(12px);-moz-transform:translateX(12px);transform:translateX(12px)}.tooltip-left:before,.tooltip-right:before{top:3px}.tooltip-left:after,.tooltip-right:after{margin-left:0;margin-bottom:-16px}.tooltip.large:after,[data-tooltip].large:after{width:240px}.tooltip-left.large:after,.tooltip-left.large:before,.tooltip-right.large:after,.tooltip-right.large:before{margin-top:6px}.tooltip-alt{cursor:pointer;display:inline-block;border-bottom:0}.tooltip-alt .tooltip-text{position:absolute;visibility:hidden;width:180px;background-color:#333;color:#fff;padding:.5em 1em;transition:opacity .2s ease-in-out,visibility .2s ease-in-out,transform .2s cubic-bezier(.71,1.7,.77,1.24);transform:translate3d(0,0,0);z-index:99999}.tooltip-alt .tooltip-text.right{margin-left:1em;margin-top:-1.75em}.tooltip-alt .tooltip-text.right::after{content:" ";position:absolute;top:1.125em;right:100%;margin-top:-5px;border:5px #333 solid;border-color:transparent #333 transparent transparent}.tooltip-alt:hover .tooltip-text{transform:translateX(12px);visibility:visible;opacity:1}.tooltip svg,.tooltip-alt svg{fill:#666;top:.125em;position:relative}.colorpicker{margin-top:1.5em}.colorpicker .input.color{float:left;margin-top:70px;margin-left:240px}.colorpicker .input input[type=text].token{width:4em;font-size:1.25em;height:auto;line-height:2em;border:none;-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em;text-align:center}.colorpicker .farbtastic{position:relative}.colorpicker .farbtastic *{position:absolute;cursor:crosshair}.colorpicker .farbtastic,.colorpicker .farbtastic .wheel{width:195px;height:195px}.colorpicker .farbtastic .color,.colorpicker .farbtastic .overlay{top:47px;left:47px;width:101px;height:101px}.colorpicker .farbtastic .wheel{background:url(../../../img/controls/colorpicker/wheel.png) no-repeat;width:195px;height:195px}.colorpicker .farbtastic .overlay{background:url(../../../img/controls/colorpicker/mask.png) no-repeat}.colorpicker .farbtastic .marker{width:17px;height:17px;margin:-8px 0 0 -8px;overflow:hidden;background:url(../../../img/controls/colorpicker/marker.png) no-repeat}.input.file.image .image-wrapper.avatar{width:50px;float:left}.input.file.image .input-wrapper{float:right;width:100%;margin-left:-50px}.input.file.image .input-wrapper-2{margin-left:50px;padding-left:1em}.gpgkey.input.textarea textarea{height:24em;width:95%}.input-password-wrapper{width:100%;box-sizing:border-box;zoom:1}.input-password-wrapper:after,.input-password-wrapper:before{content:"";display:table}.input-password-wrapper:after{clear:both}.input-password-wrapper:after,.input-password-wrapper:before{content:"";display:table}.input-password-wrapper:after{clear:both}.input-password-wrapper .input.password{float:left;width:65.5%}.input-password-wrapper .input.password input[type=password],.input-password-wrapper .input.password input[type=text]{width:calc(100% - 3.5em);box-sizing:border-box}.input-password-wrapper .input.password input[type=password].decrypting{background:transparent url(../../../img/controls/loading_light.svg) no-repeat 90% center}.input-password-wrapper .actions.inline{float:right;width:30%}.input-password-wrapper .actions.inline .button{float:left;width:3.75em;padding-left:0;padding-right:0}.input-password-wrapper .actions.inline .button+.button{margin-left:.5em}.input-password-wrapper .password-complexity .complexity-text{float:right;clear:right;width:30%;font-size:11px;text-align:left;padding-left:0}.input-password-wrapper .password-complexity.not_available .complexity-text{color:#ddd}.input-password-wrapper .password-complexity .progress{width:65.5%;box-sizing:border-box;border:1px solid #ddd;height:10px;display:block;clear:both;margin:.25em 0 .5em 0;float:left}.input-password-wrapper .password-complexity .progress-bar{background:#000;width:0;height:6px;display:block;float:left;margin:1px}.input-password-wrapper .password-complexity .progress-bar.very-weak{background:#000;width:5%}.input-password-wrapper .password-complexity .progress-bar.weak{background:#d40101;width:10%}.input-password-wrapper .password-complexity .progress-bar.fair{background:#ffbd2e;width:60%}.input-password-wrapper .password-complexity .progress-bar.strong{background:#6c0;width:80%}.input-password-wrapper .password-complexity .progress-bar.very-strong{background:#090;width:99.5%}@media all and (max-width:400px){.input-password-wrapper .input.password{float:left;width:100%}.input-password-wrapper .actions.inline{width:50%;float:left;margin-bottom:.5em}.input-password-wrapper .password-complexity .progress{display:none}.input-password-wrapper .password-complexity .complexity-text{float:left;width:50%;font-size:.831em;margin-top:.5em}.fa.fa-lg{line-height:1}}.ribbon{position:absolute;right:-5px;top:-5px;z-index:1;overflow:hidden;width:75px;height:75px;text-align:right}.ribbon span{font-size:10px;font-weight:700;color:#fff;text-transform:uppercase;text-align:center;line-height:20px;transform:rotate(45deg);-webkit-transform:rotate(45deg);width:100px;display:block;background:#090;box-shadow:0 3px 10px -5px #000;position:absolute;top:19px;right:-21px}.ribbon span::before{content:"";position:absolute;left:0;top:100%;z-index:-1;border-left:3px solid #090;border-right:3px solid transparent;border-bottom:3px solid transparent;border-top:3px solid #090}.ribbon span::after{content:"";position:absolute;right:0;top:100%;z-index:-1;border-left:3px solid transparent;border-right:3px solid #090;border-bottom:3px solid transparent;border-top:3px solid #090}.ribbon.ribbon-warning span{background:#d40101}.ribbon.ribbon-warning span::before{border-left:3px solid #d40101;border-top:3px solid #d40101}.ribbon.ribbon-warning span::after{border-right:3px solid #d40101;border-top:3px solid #d40101}/*! +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}div{outline:0}html body .hidden{display:none}.visually-hidden,.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visually-hidden .focusable:active,.visually-hidden .focusable:focus,.visuallyhidden .focusable:active,.visuallyhidden .focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.clearfix:after,.clearfix:before{content:"";display:table}.clearfix:after{clear:both}.rounded{-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem}.ellipsis{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.rotate-right{transform:rotate(-90deg)}html{line-height:normal}body{color:#333;background:#fff}body.iframe{background:0 0}@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;src:local('Open Sans'),local('OpenSans'),url('../../../fonts/opensans-regular.woff') format('woff')}@font-face{font-family:'Open Sans';font-style:normal;font-weight:700;src:local('Open Sans Bold'),local('OpenSans-Bold'),url('../../../fonts/opensans-bold.woff') format('woff')}body{font-family:'Open Sans',Verdana,sans-serif}.code{font-family:'Courier New',Courier,monospace}html{font-size:62.5%}body{font-size:1.6rem}h1{font-size:2.2rem;line-height:3.2rem}h2{font-size:1.8rem;line-height:2.4rem}h3{font-size:1.6rem;line-height:2.4rem;border-bottom:1px dotted #ddd}p{font-size:1.5rem;line-height:2.2rem;margin-top:0}code{font-size:1.1rem}.font-dim{color:#666}a{outline:0;text-decoration:none;cursor:pointer;border-bottom:1px solid #ddd}a:link,a:visited{color:#333}a:hover{text-decoration:none;cursor:pointer;color:#2894df;border-bottom:1px solid #2894df}a:active,a:focus{outline:0;color:#2894df;border:0}a.no-border,a:hover.no-border{border-bottom:0}a.disabled{outline:0;text-decoration:none;cursor:default;color:#888;pointer-events:none}ul{padding:0;margin:0}ul li{list-style:none;padding:0;margin:0}.button,button{font-size:1.4rem;padding:.8rem 1.6rem;line-height:1.6rem;margin-right:.8rem;position:relative;display:inline-block;vertical-align:baseline;outline:0;cursor:pointer;text-align:center;text-decoration:none;border-top:1px solid #ccc;border-right:1px solid #ccc;border-bottom:1px solid #ccc;border-left:1px solid #ccc;color:#333;font-weight:400;text-shadow:1px 1px 0 rgba(255,255,255,.5);background:#f3f3f3;-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem;box-sizing:border-box}.button.medium{font-size:1.6rem;line-height:2.4rem;padding:.8rem 2.4rem}.button.big{font-size:1.8rem;line-height:3.2rem;padding:.8rem 3.2rem}.button.full-width{width:100%}.button.primary{background:#2894df;border:1px solid #2894df;color:#fff;text-shadow:1px 1px 0 rgba(0,0,0,.2)}.button.primary:hover{background:#2a9ceb;border:1px solid #2a9ceb;color:#fff}.button.primary:active,.button.primary:focus{color:#fff;border:1px solid #4271b7;background:#2a9ceb}.button.warning,.button.warning:active,.button.warning:focus,.button.warning:hover{color:#fff;background:#d40101;border:1px solid #d40101;text-shadow:none}.button.warning:active,.button.warning:focus{border:1px solid #92000c}.button.cancel,.button.cancel:active,.button.cancel:focus,.button.cancel:hover,.button.dim,.button.dim:active,.button.dim:focus,.button.dim:hover{background:#fff;font-weight:400}.button:hover{color:#333;text-decoration:none;background:#eee;border:1px solid #bbb}.button:focus{color:#2894df;border:1px solid #2894df}.button:active{color:#2894df;border:1px solid #2894df;background:#eee;position:relative;box-shadow:inset 0 1px 2px rgba(0,0,0,.2)}.button.disabled{cursor:default;color:#888;background:#fff;border:1px solid #ddd;text-shadow:none;font-weight:400}.button.disabled:active,.button.disabled:focus,.button.disabled:hover{box-shadow:0 0 0;top:0;color:#888;background:#fff;border:1px solid #ddd}.button.processing{background:#fff;border:1px solid #fff}.button.processing:after{width:100%;height:100%;position:absolute;content:" ";top:-1px;left:-1px;background:#fff url('../../../img/controls/loading_light.svg') center center no-repeat;background-size:auto 50%;border:1px solid #ddd;-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem}input[type=submit].button.processing{font-size:0;background:#fff url('../../../img/controls/loading_light.svg') center center no-repeat;background-size:auto 50%;border:1px solid #ddd;border-radius:5px}.button-toggle.selected{background:#eee;box-shadow:inset 0 1px 2px rgba(0,0,0,.2);border:1px solid #bbb}.button-group{display:flex;flex-wrap:wrap;justify-content:flex-start}.button-group--nowrap>.button{white-space:nowrap}.button-group>*{flex-grow:1;margin-bottom:.5em}label{font-weight:700;font-size:1.6rem;line-height:2.4rem;padding:.8rem 0;display:block}.required label:after{content:" \002A";color:#d40101;font-weight:700}.input.error label{color:#d40101}input[type=email],input[type=number],input[type=password],input[type=search],input[type=text],textarea{box-sizing:border-box;font-size:1.6rem;line-height:2.4rem;color:#333;background:#fff;width:100%;max-width:64rem;padding:.6rem 1.2rem;display:inline-block;margin-bottom:.8rem;border:1px solid #ccc;border-top:1px solid #bbb;vertical-align:middle}::-ms-reveal{display:none}input[type=number]{box-sizing:border-box}input[type=file]{box-sizing:border-box;font-size:1.6rem;line-height:2.4rem;color:#333;background:#fff;width:100%;max-width:64rem;display:inline-block;margin-bottom:.8rem;vertical-align:middle}input[type=email]:hover,input[type=number]:hover,input[type=password]:hover,input[type=search]:hover,input[type=text]:hover,textarea:hover{border:1px solid #bbb}input[type=email]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=text]:focus,textarea:focus{outline:0;box-shadow:inset 1px 1px 2px rgba(0,0,0,.2);border:1px solid #2894df}input[type=email]:disabled,input[type=number]:disabled,input[type=password]:disabled,input[type=search]:disabled,input[type=text]:disabled,textarea:disabled{color:#333;border:1px solid #ddd;box-shadow:0 0;cursor:default;background:#f3f3f3}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{opacity:1}textarea{height:9rem}.textarea.large textarea{width:30rem;height:9rem}.checkbox input[type=checkbox]{position:absolute;opacity:0;cursor:pointer}.checkbox input[type=checkbox].error{color:#d40101}.checkbox input[type=checkbox]+label{font-weight:400;position:relative;cursor:pointer;padding:0;margin-bottom:.8rem}.checkbox input[type=checkbox]+label:before{content:'';margin-top:.4rem;margin-right:1rem;display:inline-block;vertical-align:text-top;width:1.4rem;height:1.4rem;background:#fff;border:1px solid #ddd;border-radius:.3rem;box-sizing:border-box}.checkbox input[type=checkbox]:hover+label:before{box-shadow:0 .1rem 0 #ddd,inset 0 .1rem 0 rgba(255,255,255,.25)}.checkbox input[type=checkbox]:focus+label:before{box-shadow:0 0 .4rem #2a9ceb;border:1px solid #2a9ceb}.checkbox input[type=checkbox]:active+label:before{box-shadow:inset 0 -.1rem 0 rgba(255,255,255,.25),inset 0 .1rem 0 #ddd}.checkbox input[type=checkbox]:disabled+label{color:#ddd;cursor:auto}.checkbox input[type=checkbox]:disabled+label:before{box-shadow:none;background:#ccc;border:none}.checkbox input[type=checkbox]:checked+label:after{content:'';position:absolute;left:.2rem;top:.75rem;width:1rem;height:1rem;background-size:1rem 1rem;background-image:url('../../../img/controls/check_black.svg');-webkit-mask-image:url('../../../img/controls/check_black.svg');mask-image:url('../../../img/controls/check_black.svg')}.checkbox input[type=checkbox]:disabled+label:after{background:#969696}.checkbox.medium input[type=checkbox]{cursor:pointer}.checkbox.medium input[type=checkbox]+label{font-size:1.5rem}.checkbox.medium input[type=checkbox]+label:before{margin-top:.3rem;margin-right:1rem;width:1.4rem;height:1.4rem}.checkbox.medium input[type=checkbox]:checked+label:after{left:.2rem;top:.7rem;width:1rem;height:1rem;background-size:1rem 1rem}.checkbox.small input[type=checkbox]{cursor:pointer}.checkbox.small input[type=checkbox]+label{font-size:1.4rem}.checkbox.small input[type=checkbox]+label:before{margin-top:.3rem;margin-right:1rem;width:1.4rem;height:1.4rem}.checkbox.small input[type=checkbox]:checked+label:after{left:.2rem;top:.7rem;width:1rem;height:1rem;background-size:1rem 1rem}.radiolist{margin-bottom:.8rem}.radiolist:after,.radiolist:before{content:"";display:table}.radiolist:after{clear:both}.radiolist:after,.radiolist:before{content:"";display:table}.radiolist:after{clear:both}.radiolist .input.radio{float:left}.radiolist .input.radio input{float:left;margin-top:.8rem}.radiolist .input.radio input+label{float:left;font-weight:400;display:inline-block;margin-left:1rem;font-size:1.6rem;margin-right:3.2rem;line-height:1.4rem}.radiolist .input.radio input+label:after{content:initial}.input.toggle-switch{display:flex;padding-top:1.6rem;padding-bottom:.8rem}.input.toggle-switch label{padding-top:0;padding-left:1.6rem;order:2;flex:1;font-weight:400;font-size:1.6rem}.input.toggle-switch.disabled label{color:#969696}.input.toggle-switch .toggle-switch-checkbox{order:-1;flex:0 0 3em;display:none}.input.toggle-switch .toggle-switch-checkbox,.input.toggle-switch .toggle-switch-checkbox *,.input.toggle-switch .toggle-switch-checkbox :after,.input.toggle-switch .toggle-switch-checkbox :before,.input.toggle-switch .toggle-switch-checkbox+.toggle-switch-button,.input.toggle-switch .toggle-switch-checkbox:after,.input.toggle-switch .toggle-switch-checkbox:before{box-sizing:border-box}.input.toggle-switch .toggle-switch-checkbox ::selection,.input.toggle-switch .toggle-switch-checkbox :after::selection,.input.toggle-switch .toggle-switch-checkbox :before::selection,.input.toggle-switch .toggle-switch-checkbox+.toggle-switch-button::selection,.input.toggle-switch .toggle-switch-checkbox::selection,.input.toggle-switch .toggle-switch-checkbox:after::selection,.input.toggle-switch .toggle-switch-checkbox:before::selection{background:0 0}.input.toggle-switch .toggle-switch-button{order:-1;flex:none;outline:0;display:block;width:3em;height:1.5em;position:relative;cursor:pointer;user-select:none;background:#f3f3f3;border-radius:2em;padding:2px;transition:all .4s ease;border:1px solid #ddd}.input.toggle-switch .toggle-switch-button span{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.input.toggle-switch .toggle-switch-button span .focusable:active,.input.toggle-switch .toggle-switch-button span .focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.input.toggle-switch .toggle-switch-button span .focusable:active,.input.toggle-switch .toggle-switch-button span .focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.input.toggle-switch .toggle-switch-button:hover:after{will-change:padding}.input.toggle-switch .toggle-switch-button:active{box-shadow:inset 0 0 0 2em #e8eae9}.input.toggle-switch .toggle-switch-button:active:after{padding-right:.8em}.input.toggle-switch .toggle-switch-button:after,.input.toggle-switch .toggle-switch-button:before{position:relative;display:block;content:"";width:50%;height:100%}.input.toggle-switch .toggle-switch-button:after{left:0;border-radius:2em;background:#fff;transition:left .3s cubic-bezier(.175, .885, .32, 1.275),padding .3s ease,margin .3s ease;box-shadow:0 0 0 1px rgba(0,0,0,.1),0 4px 0 rgba(0,0,0,.08)}.input.toggle-switch .toggle-switch-button:before{display:none}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button:after{left:50%}.input.toggle-switch .toggle-switch-checkbox:disabled+.toggle-switch-button{background:#fff;cursor:not-allowed}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button{background:#6c0}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button:active{box-shadow:none}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button:active:after{margin-left:-.8em}.input.toggle-switch .toggle-switch-checkbox:disabled:checked+.toggle-switch-button{background:#e6ffcc}h1 .input.toggle-switch,h2 .input.toggle-switch,h3 .input.toggle-switch,h4 .input.toggle-switch,h5 .input.toggle-switch,h6 .input.toggle-switch{float:left;padding:.4em 1em 0 0}.input.select select{display:inline-block;font-size:1.6rem;line-height:2.4rem;color:#333;padding:.6rem 1.2rem;width:100%;max-width:64rem;box-sizing:border-box;margin:0 0 .8rem 0;border:1px solid #ccc;-moz-appearance:none;-webkit-appearance:none;appearance:none;background-color:#fff;background-image:url('../../../img/controls/chevron-down_black.svg'),linear-gradient(to bottom,#fff 0,#fff 100%);background-repeat:no-repeat,repeat;background-position:right .6rem top 50%,0 0;background-size:1rem auto,100%}.input.select select.medium{width:50%}.input.select select:focus{outline:0;border:1px solid #2894df;border-radius:0;background-image:url('../../../img/controls/chevron-down_blue.svg'),linear-gradient(to bottom,#fff 0,#fff 100%)}.input.select select:disabled{background-image:url('../../../img/controls/chevron-down_black.svg'),linear-gradient(to bottom,#f3f3f3 0,#f3f3f3 100%);color:#888}.input .error-message{padding:0;font-size:1.4rem;margin-top:.2rem;margin-bottom:1.6rem;background-color:transparent;border:0;font-weight:400;color:#d40101;background:#fff;clear:both}.input .help-message{padding:0;font-size:1.4rem;margin-top:.2rem;margin-bottom:1.6rem;background-color:transparent;border:0;color:#888;font-weight:400;background:#fff;clear:both}.singleline{display:flex;max-width:64rem}.singleline:after,.singleline:before{content:"";display:table}.singleline:after{clear:both}.singleline:after,.singleline:before{content:"";display:table}.singleline:after{clear:both}.singleline .input{flex:1}.singleline .input.first-field,.singleline .input:first-child{margin-right:2%}.slider{display:flex;align-items:center}.slider input[type=range]{-webkit-appearance:none;width:100%;height:1px;border-radius:5px;background:#ddd;outline:0;opacity:.7;-webkit-transition:.2s;transition:opacity .2s;flex-grow:1}.slider input[type=range]:hover{opacity:1}.slider input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:15px;height:15px;border-radius:50%;border-width:1px;border-style:solid;border-color:#969696;background:#ddd;cursor:pointer}.slider input[type=range]::-moz-range-thumb{width:15px;height:15px;border-radius:50%;background:#ddd;cursor:pointer}.slider input[type=number]{width:6.5rem;margin-left:1.6rem;padding:.8rem}.svg-icon{display:inline-flex;align-self:center}.svg-icon svg{fill:#333;height:1em;width:1em}.svg-icon.baseline svg{top:.125em;position:relative}.svg-icon.dim svg,.svg-icon.light svg{fill:#888888}.svg-icon.icon-only svg{padding:.1rem;height:1.6rem;width:1.6rem}a:hover .svg-icon svg{fill:#2894DF}a.disabled .svg-icon svg{fill:#888888;pointer-events:none;cursor:default}a.disabled:hover .svg-icon svg{fill:#888888}a.fav .svg-icon svg{fill:#D40101}a.unfav .svg-icon svg{fill:#DDD}.button .svg-icon svg{top:.2rem;position:relative}.button .svg-icon+span:not(.visuallyhidden){margin-left:.8rem;display:inline-block}.button .svg-icon svg:hover,.button:hover .svg-icon svg{fill:#333}.button.primary:active .svg-icon svg,.button.primary:focus .svg-icon svg,.button.warning .svg-icon svg,.button.warning:active .svg-icon svg,.button.warning:focus .svg-icon svg,.button.warning:hover .svg-icon svg{fill:#FFF}.button:active .svg-icon svg,.button:focus .svg-icon svg{fill:#2894DF}.button.disabled .svg-icon svg{fill:#888888}.button.disabled .svg-icon svg:hover{fill:#888888}@keyframes drawCircle{0%{stroke-dashoffset:180px}100%{stroke-dashoffset:0}}@keyframes drawCheck{0%{stroke-dashoffset:50px}100%{stroke-dashoffset:0}}@keyframes drawCross{0%{stroke-dashoffset:50px}100%{stroke-dashoffset:0}}@keyframes drawWarning{0%{stroke-dashoffset:230px}100%{stroke-dashoffset:0}}.icon-feedback .success-animation-circle{stroke-dasharray:180px 180px;stroke:#009900}.icon-feedback .success-animation-line{stroke-dasharray:50px 50px;stroke:#009900}.icon-feedback .success-animation-line2{stroke-dasharray:50px 50px;stroke:#009900}.icon-feedback .error-animation-circle{stroke-dasharray:180px 180px;stroke:#D40101}.icon-feedback .error-animation-line{stroke-dasharray:50px 50px;stroke:#D40101}.icon-feedback .warning-animation-line{stroke-dasharray:230px 230px;stroke:#9F6000;stroke-linecap:round;stroke-linejoin:round}.icon-feedback .warning-animation-circle{fill:#9F6000}.icon-feedback .animated{animation:.75s ease-out 0s 1 both pop}.icon-feedback .animated .error-animation-circle,.icon-feedback .animated .success-animation-circle,.icon-feedback .animated .warning-animation-circle{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawCircle}.icon-feedback .animated .success-animation-line{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawCheck}.icon-feedback .animated .error-animation-line{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawCross}.icon-feedback .animated .warning-animation-line{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawWarning}table{border-collapse:collapse;border-spacing:0}table td,table th{text-align:left;font-weight:400}.logo{background:transparent url('../../../img/logo/logo.svg') 0 0 no-repeat;background-size:20rem auto;width:20rem;height:4.5rem}.scroll{overflow-y:scroll;background:linear-gradient(#fff 30%,rgba(255,255,255,0)),linear-gradient(rgba(255,255,255,0),#fff 70%) 0 100%,radial-gradient(farthest-side at 75% 0,rgba(0,0,0,.2),rgba(0,0,0,0)),radial-gradient(farthest-side at 75% 100%,rgba(0,0,0,.2),rgba(0,0,0,0)) 0 100%;background-repeat:no-repeat;background-color:#fff;background-size:100% 10px,100% 10px,100% 5px,100% 5px}::-webkit-scrollbar{width:1em}::-webkit-scrollbar-track{background:#f9f9f9;border-top:0;border-bottom:0}::-webkit-scrollbar-thumb{background:#c1c1c1;border-radius:1em;border:4px solid #f9f9f9}.scroll-shadow{overflow:auto;background:linear-gradient(#fff 30%,rgba(255,255,255,0)),linear-gradient(rgba(255,255,255,0),#fff 70%) 0 100%,radial-gradient(farthest-side at 50% 0,rgba(0,0,0,.08),rgba(0,0,0,0)),radial-gradient(farthest-side at 50% 100%,rgba(0,0,0,.08),rgba(0,0,0,0)) 0 100%;background-repeat:no-repeat;background-color:#fff;background-size:100% 40px,100% 40px,100% 14px,100% 14px;background-attachment:local,local,scroll,scroll}.simplebar-content{overflow:auto;background:linear-gradient(#fff 30%,rgba(255,255,255,0)),linear-gradient(rgba(255,255,255,0),#fff 70%) 0 100%,radial-gradient(farthest-side at 50% 0,rgba(0,0,0,.08),rgba(0,0,0,0)),radial-gradient(farthest-side at 50% 100%,rgba(0,0,0,.08),rgba(0,0,0,0)) 0 100%;background-repeat:no-repeat;background-color:#fff;background-size:100% 40px,100% 40px,100% 14px,100% 14px;background-attachment:local,local,scroll,scroll}[data-simplebar]{position:relative;flex-direction:column;flex-wrap:wrap;justify-content:flex-start;align-content:flex-start;align-items:flex-start;width:inherit;height:inherit;max-width:inherit;max-height:inherit}.simplebar-wrapper{overflow:hidden;width:inherit;height:inherit;max-width:inherit;max-height:inherit}.simplebar-mask{direction:inherit;position:absolute;overflow:hidden;padding:0;margin:0;left:0;top:0;bottom:0;right:0;width:auto!important;height:auto!important;z-index:0}.simplebar-offset{direction:inherit!important;box-sizing:inherit!important;resize:none!important;position:absolute;top:0;left:0;bottom:0;right:0;padding:0;margin:0;-webkit-overflow-scrolling:touch}.simplebar-content-wrapper{direction:inherit;box-sizing:border-box!important;position:relative;display:block;height:100%;width:auto;max-width:100%;max-height:100%;scrollbar-width:none;-ms-overflow-style:none}.simplebar-content-wrapper::-webkit-scrollbar,.simplebar-hide-scrollbar::-webkit-scrollbar{width:0;height:0}.simplebar-content:after,.simplebar-content:before{content:' ';display:table}.simplebar-placeholder{max-height:100%;max-width:100%;width:100%;pointer-events:none}.simplebar-height-auto-observer-wrapper{box-sizing:inherit!important;height:100%;width:inherit;max-width:1px;position:relative;float:left;max-height:1px;overflow:hidden;z-index:-1;padding:0;margin:0;pointer-events:none;flex-grow:inherit;flex-shrink:0;flex-basis:0}.simplebar-height-auto-observer{box-sizing:inherit;display:block;opacity:0;position:absolute;top:0;left:0;height:1000%;width:1000%;min-height:1px;min-width:1px;overflow:hidden;pointer-events:none;z-index:-1}.simplebar-track{z-index:1;position:absolute;right:0;bottom:0;pointer-events:none;overflow:hidden}[data-simplebar].simplebar-dragging .simplebar-content{pointer-events:none;user-select:none;-webkit-user-select:none}[data-simplebar].simplebar-dragging .simplebar-track{pointer-events:all}.simplebar-scrollbar{position:absolute;right:2px;width:7px;min-height:10px}.simplebar-scrollbar:before{position:absolute;content:'';background:#000;border-radius:7px;left:0;right:0;opacity:0;transition:opacity .2s linear}.simplebar-scrollbar.simplebar-visible:before{opacity:.5;transition:opacity 0s linear}.simplebar-track.simplebar-vertical{top:0;width:11px}.simplebar-track.simplebar-vertical .simplebar-scrollbar:before{top:2px;bottom:2px}.simplebar-track.simplebar-horizontal{left:0;height:11px}.simplebar-track.simplebar-horizontal .simplebar-scrollbar:before{height:100%;left:2px;right:2px}.simplebar-track.simplebar-horizontal .simplebar-scrollbar{right:auto;left:0;top:2px;height:7px;min-height:0;min-width:10px;width:auto}[data-simplebar-direction=rtl] .simplebar-track.simplebar-vertical{right:auto;left:0}.hs-dummy-scrollbar-size{direction:rtl;position:fixed;opacity:0;visibility:hidden;height:500px;width:500px;overflow-y:hidden;overflow-x:scroll}.simplebar-hide-scrollbar{position:fixed;left:0;visibility:hidden;overflow-y:scroll;scrollbar-width:none;-ms-overflow-style:none}.shimmer{display:block;position:relative;width:100%;height:100%;content:'';animation:shimmer 2s infinite;background:linear-gradient(45deg,rgba(255,255,255,0) 0,rgba(255,255,255,.1) 30%,rgba(255,255,255,.5) 50%,rgba(255,255,255,0))}@keyframes shimmer{0%{background-position:0 0}100%{background-position:1000px 0}}html{scroll-behavior:smooth}.animated{-webkit-animation-duration:.5s;animation-duration:.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both}@keyframes fadeInUp{0%{opacity:0;-webkit-transform:translateY(20px);-ms-transform:translateY(20px);transform:translateY(20px)}100%{opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}}@keyframes fadeOutUp{from{opacity:1}to{opacity:0;transform:translate3d(0,-100%,0)}}@keyframes fadeInDown{from{opacity:0;transform:translate3d(0,-100%,0)}to{opacity:1;transform:none}}@keyframes pop{0%{opacity:0;transform:scale(1)}55%{opacity:1;transform:scale(1)}65%{opacity:1;transform:scale(1.25)}75%{opacity:1;transform:scale(1)}100%{opacity:1;transform:scale(1)}}.fadeInDown{animation-name:fadeInDown}.fadeOutUp{animation-name:fadeOutUp}.fadeInUp{animation-name:fadeInUp}.pop{animation-name:pop}.page{width:100%;min-width:32rem;top:0;left:0;right:0;bottom:0;position:absolute;overflow:auto}.page .panel{height:100%;width:100%;position:absolute;overflow:auto}.page .panel.main{bottom:3.8rem;height:auto;padding:0}.page .panel.main .grid{padding-bottom:1.6rem}.page .panel.main .row{padding-left:0}.page .panel.main .panel.left{background:#fff}.page .header.second+.panel.main{top:6.875em}.page .header.third+.panel.main{top:10em;border-top:1px solid #ddd}.page .header{width:100%;overflow:hidden}.page .header.first{min-height:3.2rem}.page .header.second{height:7rem}.page .header.third{height:4.8rem}.page .col1,.page .col2,.page .col2_3,.page .col3{position:absolute}.page .col1,.page .panel.left{width:17.25%;box-sizing:border-box}.page .panel.middle{width:82%;left:18%;overflow:hidden}.page .panel.middle.scroll{overflow-y:scroll}.page .col2{width:70%;left:18%}.page .col3,.page .panel.right{width:24%;left:75%}.page .col2_3{width:81.5%;left:18%}.page .panel.main .grid-responsive-12{float:left;width:100%;max-width:none;padding-left:.25em;box-sizing:border-box}@media all and (max-width:1024px){.page .panel.left{display:none}.page .header.second .col2{display:none}.page .header.second .col2_3{display:none}.page .header.third{height:4.5em}.page .header.third .col1,.page .header.third .col2,.page .header.third .col2_3{position:relative;float:left;left:auto;width:auto}.page .header.third .col3{display:none}.page .panel.main .panel.left{display:none}.page .panel.main .panel.middle{width:100%;left:0}.page .panel.main .row{padding-left:.5em}.page .panel.main .panel.aside{min-width:auto}}.grid,.grid-responsive-12{margin:0 auto;padding:0;max-width:1220px}.grid .row,.grid-responsive-12 .row{padding:0 .5em 0 .5em}.grid .row:after,.grid .row:before,.grid-responsive-12 .row:after,.grid-responsive-12 .row:before{content:"";display:table}.grid .row:after,.grid-responsive-12 .row:after{clear:both}.grid .row:after,.grid .row:before,.grid-responsive-12 .row:after,.grid-responsive-12 .row:before{content:"";display:table}.grid .row:after,.grid-responsive-12 .row:after{clear:both}.grid .row .col1,.grid .row .col10,.grid .row .col11,.grid .row .col12,.grid .row .col2,.grid .row .col3,.grid .row .col4,.grid .row .col5,.grid .row .col6,.grid .row .col7,.grid .row .col8,.grid .row .col9,.grid-responsive-12 .row .col1,.grid-responsive-12 .row .col10,.grid-responsive-12 .row .col11,.grid-responsive-12 .row .col12,.grid-responsive-12 .row .col2,.grid-responsive-12 .row .col3,.grid-responsive-12 .row .col4,.grid-responsive-12 .row .col5,.grid-responsive-12 .row .col6,.grid-responsive-12 .row .col7,.grid-responsive-12 .row .col8,.grid-responsive-12 .row .col9{position:relative;left:auto;float:none;width:99%;box-sizing:border-box}.grid .row .col10:after,.grid .row .col10:before,.grid .row .col11:after,.grid .row .col11:before,.grid .row .col12:after,.grid .row .col12:before,.grid .row .col1:after,.grid .row .col1:before,.grid .row .col2:after,.grid .row .col2:before,.grid .row .col3:after,.grid .row .col3:before,.grid .row .col4:after,.grid .row .col4:before,.grid .row .col5:after,.grid .row .col5:before,.grid .row .col6:after,.grid .row .col6:before,.grid .row .col7:after,.grid .row .col7:before,.grid .row .col8:after,.grid .row .col8:before,.grid .row .col9:after,.grid .row .col9:before,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col10:before,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col11:before,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col12:before,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col1:before,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col2:before,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col3:before,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col4:before,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col5:before,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col6:before,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col7:before,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col8:before,.grid-responsive-12 .row .col9:after,.grid-responsive-12 .row .col9:before{content:"";display:table}.grid .row .col10:after,.grid .row .col11:after,.grid .row .col12:after,.grid .row .col1:after,.grid .row .col2:after,.grid .row .col3:after,.grid .row .col4:after,.grid .row .col5:after,.grid .row .col6:after,.grid .row .col7:after,.grid .row .col8:after,.grid .row .col9:after,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col9:after{clear:both}.grid .row .col10:after,.grid .row .col10:before,.grid .row .col11:after,.grid .row .col11:before,.grid .row .col12:after,.grid .row .col12:before,.grid .row .col1:after,.grid .row .col1:before,.grid .row .col2:after,.grid .row .col2:before,.grid .row .col3:after,.grid .row .col3:before,.grid .row .col4:after,.grid .row .col4:before,.grid .row .col5:after,.grid .row .col5:before,.grid .row .col6:after,.grid .row .col6:before,.grid .row .col7:after,.grid .row .col7:before,.grid .row .col8:after,.grid .row .col8:before,.grid .row .col9:after,.grid .row .col9:before,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col10:before,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col11:before,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col12:before,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col1:before,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col2:before,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col3:before,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col4:before,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col5:before,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col6:before,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col7:before,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col8:before,.grid-responsive-12 .row .col9:after,.grid-responsive-12 .row .col9:before{content:"";display:table}.grid .row .col10:after,.grid .row .col11:after,.grid .row .col12:after,.grid .row .col1:after,.grid .row .col2:after,.grid .row .col3:after,.grid .row .col4:after,.grid .row .col5:after,.grid .row .col6:after,.grid .row .col7:after,.grid .row .col8:after,.grid .row .col9:after,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col9:after{clear:both}.grid .row .col1 img,.grid .row .col10 img,.grid .row .col11 img,.grid .row .col12 img,.grid .row .col2 img,.grid .row .col3 img,.grid .row .col4 img,.grid .row .col5 img,.grid .row .col6 img,.grid .row .col7 img,.grid .row .col8 img,.grid .row .col9 img,.grid-responsive-12 .row .col1 img,.grid-responsive-12 .row .col10 img,.grid-responsive-12 .row .col11 img,.grid-responsive-12 .row .col12 img,.grid-responsive-12 .row .col2 img,.grid-responsive-12 .row .col3 img,.grid-responsive-12 .row .col4 img,.grid-responsive-12 .row .col5 img,.grid-responsive-12 .row .col6 img,.grid-responsive-12 .row .col7 img,.grid-responsive-12 .row .col8 img,.grid-responsive-12 .row .col9 img{width:100%;height:auto;display:block}@media all and (min-width:780px){.grid .row{padding:0 .5em 0 .5em}.grid .row .col1,.grid .row .col10,.grid .row .col11,.grid .row .col12,.grid .row .col2,.grid .row .col3,.grid .row .col4,.grid .row .col5,.grid .row .col6,.grid .row .col7,.grid .row .col8,.grid .row .col9{float:left;position:relative;margin:0 3% 0 0}.grid .row .last{margin-right:0}.grid .row .col1{width:5.5%}.grid .row .col2{width:14%}.grid .row .col3{width:22.5%}.grid .row .col4{width:31%}.grid .row .col5{width:39.5%}.grid .row .col6{width:48%}.grid .row .col7{width:56.5%}.grid .row .col8{width:65%}.grid .row .col9{width:73.5%}.grid .row .col10{width:82%}.grid .row .col11{width:90.5%}.grid .row .col12{width:99%;margin:0}.grid .row .push1{margin-left:5.5%}.grid .row .push2{margin-left:14%}.grid .row .push3{margin-left:22.5%}.grid .row .push4{margin-left:31%}}@media all and (max-width:768px){.hidden-xs{display:none}}.footer{margin-top:0;position:fixed;bottom:0;text-align:right;font-size:1.2rem;width:100%;height:3.8rem;background:#fff;border-top:1px solid #ddd;z-index:890}.footer .footer-links{padding-top:1rem;width:100%}.footer .footer-links li{display:inline;margin-right:1.5em}.footer .footer-links li.error-message a{background-color:#fff;color:#d40101}.footer .footer-links li .github-star{display:inline;position:absolute;margin-left:-8em;margin-top:-1px}.footer .footer-links a:not(.gh-btn):not(.gh-count){border:0}.header{overflow:visible!important}.header.first{background:#333}.header.second{background:#eee}.header .navigation.primary:after,.header .navigation.primary:before{content:"";display:table}.header .navigation.primary:after{clear:both}.header .navigation.primary:after,.header .navigation.primary:before{content:"";display:table}.header .navigation.primary:after{clear:both}.header .navigation.primary li{padding:.5em;float:left}.header .navigation.primary li:first-child{padding-left:1em}.header .navigation.primary li.right{float:right;margin-right:1em}.header .navigation.primary li a{color:#ddd;text-decoration:none;border:0;display:inline-block}.header .navigation.primary li a:hover{color:#fff}.header .navigation.primary li a:active,.header .navigation.primary li a:focus{color:#2894df}.header .navigation.primary li a.highlighted{background-color:#2894df;padding:0 .5em 0 .5em;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.header .navigation.primary li a.highlighted:active,.header .navigation.primary li a.highlighted:focus{color:#ddd}.header .navigation.primary li .row.selected a{color:#fff}.header .navigation.primary li .row.selected a:focus{color:#2894df}.header .navigation.primary .github-star{display:none;position:absolute;right:1em;top:4px}@media all and (min-width:600px){.header .navigation.primary .github-star{display:block}}.header .logo{margin:1.25em 0 0 1em;max-width:80%}.logo.no-img{background:transparent url('../../../img/logo/logo.svg') 0 0 no-repeat;background-size:150px auto;width:150px;height:30px}.logo h1{display:none}.logo.bigger{background:transparent url('../../../img/logo/logo.svg') 0 0 no-repeat;background-size:200px auto;width:200px;height:45px}.header.second .col1{min-width:200px}@media only screen and (-moz-min-device-pixel-ratio:1.5),only screen and (-o-min-device-pixel-ratio:1.5),only screen and (-webkit-min-device-pixel-ratio:1.5),only screen and (min-devicepixel-ratio:1.5),only screen and (min-resolution:1.5dppx){.logo.no-img{background:transparent url('../../../img/logo/logo.svg') 0 0 no-repeat;background-size:150px auto}.logo.bigger{background:transparent url('../../../img/logo/logo.svg') 0 0 no-repeat;background-size:200px auto}}.js .message.no-js{display:none}.cookies .message.no-cookies{display:none}.message{padding:1.6rem}.message a{border-bottom:1px solid #888}.message a:hover{border-bottom:1px solid #2894df}.message.error{color:#333;background:#ffe4e4}.message.error a:link,.message.error a:visited{color:#333;border-bottom:1px dotted #888}.message.error a:hover{color:#333;border-bottom:1px solid #888}.message.success{color:#333;background:#edf7eb}.message.notice{color:#333;background:#daecf9}.message.warning{color:#333;background:#fef0bf}.message p:last-child{margin-bottom:0}.message.side-message{margin-left:1.6rem;font-size:1.6rem;margin-right:3.2rem}.message.side-message p,.message.side-message ul{padding-bottom:1.6rem}.sidebar-help{margin-top:3.2rem;padding:2.4rem;background-color:#daecf9}.sidebar-help.transparent{background-color:transparent;border:1px solid #f3f3f3}.sidebar-help h3{margin:0 0 1.6rem 0;border-bottom:none}.navigation-setup,.panel .navigation{margin-top:1em}.navigation-setup h2,.panel .navigation h2{padding:0;margin:0}.navigation-setup a,.panel .navigation a{color:#333;border:0;display:block}.navigation-setup li,.panel .navigation li{font-size:.9375em;padding:.469em 0 .469em 1.333em;border-left:5px solid #fff}.navigation-setup li:hover,.panel .navigation li:hover{color:#333;border-left:5px solid #ddd}.navigation-setup li.active,.navigation-setup li.selected,.panel .navigation li.active,.panel .navigation li.selected{font-weight:700;border-left:5px solid #d40101}.gpgkey.input.textarea textarea{height:24em;width:95%}/*! Chosen, a Select Box Enhancer for jQuery and Prototype by Patrick Filler for Harvest, http://getharvest.com @@ -19,5 +15,4 @@ Full source at https://github.com/harvesthq/chosen Copyright (c) 2011-2018 Harvest http://getharvest.com MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md -This file is generated by `grunt build`, do not edit it by hand. -*/.chosen-container{position:relative;display:inline-block;vertical-align:middle;font-size:13px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.chosen-container *{-webkit-box-sizing:border-box;box-sizing:border-box}.chosen-container .chosen-drop{position:absolute;top:100%;z-index:1010;width:100%;border:1px solid #aaa;border-top:0;background:#fff;-webkit-box-shadow:0 4px 5px rgba(0,0,0,.15);box-shadow:0 4px 5px rgba(0,0,0,.15);display:none}.chosen-container.chosen-container-single{padding:0;width:83%}.chosen-container.chosen-container-single.connection-type{float:left;width:23%}.chosen-container.chosen-with-drop .chosen-drop{display:block}.chosen-container a{cursor:pointer}.chosen-container .chosen-single .group-name,.chosen-container .search-choice .group-name{margin-right:4px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-weight:400;color:#969696}.chosen-container .chosen-single .group-name:after,.chosen-container .search-choice .group-name:after{content:"\003A";padding-left:2px;vertical-align:top}.chosen-container-single .chosen-single{position:relative;display:block;overflow:hidden;padding:0 0 0 8px;height:25px;border:1px solid #bbb;border-radius:5px;background-color:#fff;background:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#fff),color-stop(50%,#ddd),color-stop(52%,#eee),to(#f3f3f3));background:linear-gradient(#fff 20%,#ddd 50%,#eee 52%,#f3f3f3 100%);background-clip:padding-box;-webkit-box-shadow:0 0 3px #fff inset,0 1px 1px rgba(0,0,0,.1);box-shadow:0 0 3px #fff inset,0 1px 1px rgba(0,0,0,.1);color:#333;text-decoration:none;white-space:nowrap;line-height:24px}.chosen-container-single .chosen-single input[type=text]{cursor:pointer;opacity:0;position:absolute}.chosen-container-single .chosen-default{color:#888}.chosen-container-single .chosen-single span{display:block;overflow:hidden;margin-right:26px;text-overflow:ellipsis;white-space:nowrap}.chosen-container-single .chosen-single-with-deselect span{margin-right:38px}.chosen-container-single .chosen-single abbr{position:absolute;top:6px;right:26px;display:block;width:12px;height:12px;background:url(../../../img/third_party/chosen-sprite.png) -42px 1px no-repeat;font-size:1px}.chosen-container-single .chosen-single abbr:hover{background-position:-42px -10px}.chosen-container-single.chosen-disabled .chosen-single abbr:hover{background-position:-42px -10px}.chosen-container-single .chosen-single div{position:absolute;top:0;right:0;display:block;width:18px;height:100%}.chosen-container-single .chosen-single div b{display:block;width:100%;height:100%;background:url(../../../img/third_party/chosen-sprite.png) no-repeat 0 2px}.chosen-container-single .chosen-search{position:relative;z-index:1010;margin:0;padding:3px 4px;white-space:nowrap}.chosen-container-single .chosen-search span.svg-icon{position:absolute;margin-left:-1.5em;margin-top:.35em}.chosen-container-single .chosen-search input[type=text]{margin:1px 0;padding:4px 20px 4px 5px;width:100%;height:auto;outline:0;border:1px solid #bbb;background:url(../../../img/third_party/chosen-sprite.png) no-repeat 100% -20px;font-size:1em;font-family:sans-serif;line-height:normal;border-radius:0}.chosen-container-single .chosen-drop{margin-top:-.5em;border-radius:0 0 4px 4px;background-clip:padding-box}.chosen-container-single.chosen-container-single-nosearch .chosen-search{position:absolute;clip:rect(0,0,0,0)}.chosen-container .chosen-results{color:#333;position:relative;overflow-x:hidden;overflow-y:auto;margin:0 4px 4px 0;padding:0 0 0 4px;max-height:240px;-webkit-overflow-scrolling:touch}.chosen-container .chosen-results li{display:none;margin:0;padding:5px 6px;list-style:none;line-height:15px;word-wrap:break-word;-webkit-touch-callout:none}.chosen-container .chosen-results li.active-result{display:list-item;cursor:pointer}.chosen-container .chosen-results li.disabled-result{display:list-item;color:#ddd;cursor:default}.chosen-container .chosen-results li:hover{background-color:#daecf9;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#3875d7),color-stop(90%,#2a62bc));background-image:linear-gradient(#3875d7 20%,#2a62bc 90%);color:#fff}.chosen-container .chosen-results li.no-results{color:#666;display:list-item;background:#f3f3f3}.chosen-container .chosen-results li.group-result{display:list-item;font-weight:700;cursor:default}.chosen-container .chosen-results li.group-option{padding-left:15px}.chosen-container .chosen-results li em{font-style:normal;text-decoration:underline}.chosen-container-multi .chosen-choices{position:relative;overflow:hidden;margin:0;padding:0 5px;width:100%;height:auto;border:1px solid #bbb;background-color:#fff;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(1%,#eee),color-stop(15%,#fff));background-image:linear-gradient(#eee 1%,#fff 15%);cursor:text}.chosen-container-multi .chosen-choices li{float:left;list-style:none}.chosen-container-multi .chosen-choices li.search-field{margin:0;padding:0;white-space:nowrap}.chosen-container-multi .chosen-choices li.search-field input[type=text]{margin:1px 0;padding:0;height:25px;outline:0;border:0!important;background:0 0!important;-webkit-box-shadow:none;box-shadow:none;color:#888;font-size:100%;font-family:sans-serif;line-height:normal;border-radius:0;width:25px}.chosen-container-multi .chosen-choices li.search-choice{position:relative;margin:3px 5px 3px 0;padding:3px 20px 3px 5px;border:1px solid #bbb;max-width:100%;border-radius:3px;background-color:#fff;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#fff),color-stop(50%,#ddd),color-stop(52%,#eee),to(#f3f3f3));background-image:linear-gradient(#fff 20%,#ddd 50%,#eee 52%,#f3f3f3 100%);background-size:100% 19px;background-repeat:repeat-x;background-clip:padding-box;-webkit-box-shadow:0 0 2px #fff inset,0 1px 0 rgba(0,0,0,.05);box-shadow:0 0 2px #fff inset,0 1px 0 rgba(0,0,0,.05);color:#ddd;line-height:13px;cursor:default}.chosen-container-multi .chosen-choices li.search-choice span{word-wrap:break-word}.chosen-container-multi .chosen-choices li.search-choice .search-choice-close{position:absolute;top:4px;right:3px;display:block;width:12px;height:12px;background:url(../../../img/third_party/chosen-sprite.png) -42px 1px no-repeat;font-size:1px}.chosen-container-multi .chosen-choices li.search-choice .search-choice-close:hover{background-position:-42px -10px}.chosen-container-multi .chosen-choices li.search-choice-disabled{padding-right:5px;border:1px solid #ccc;background-color:#f3f3f3;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#fff),color-stop(50%,#ddd),color-stop(52%,#eee),to(#f3f3f3));background-image:linear-gradient(#fff 20%,#ddd 50%,#eee 52%,#f3f3f3 100%);color:#666}.chosen-container-multi .chosen-choices li.search-choice-focus{background:#ddd}.chosen-container-multi .chosen-choices li.search-choice-focus .search-choice-close{background-position:-42px -10px}.chosen-container-multi .chosen-results{margin:0;padding:0}.chosen-container-multi .chosen-drop .result-selected{display:list-item;color:#eee;cursor:default}.chosen-container-active .chosen-single{border:1px solid #2894df;-webkit-box-shadow:0 0 5px rgba(0,0,0,.3);box-shadow:0 0 5px rgba(0,0,0,.3)}.chosen-container-active.chosen-with-drop .chosen-single{border:1px solid #bbb;border-bottom-right-radius:0;border-bottom-left-radius:0;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#eee),color-stop(80%,#fff));background-image:linear-gradient(#eee 20%,#fff 80%);-webkit-box-shadow:0 1px 0 #fff inset;box-shadow:0 1px 0 #fff inset}.chosen-container-active.chosen-with-drop .chosen-single div{border-left:none;background:0 0}.chosen-container-active.chosen-with-drop .chosen-single div b{background-position:-18px 2px}.chosen-container-active .chosen-choices{border:1px solid #2894df;-webkit-box-shadow:0 0 5px rgba(0,0,0,.3);box-shadow:0 0 5px rgba(0,0,0,.3)}.chosen-container-active .chosen-choices li.search-field input[type=text]{color:#333!important}.chosen-disabled{opacity:.5!important;cursor:default}.chosen-disabled .chosen-single{cursor:default}.chosen-disabled .chosen-choices .search-choice .search-choice-close{cursor:default}.chosen-rtl{text-align:right}.chosen-rtl .chosen-single{overflow:visible;padding:0 8px 0 0}.chosen-rtl .chosen-single span{margin-right:0;margin-left:26px;direction:rtl}.chosen-rtl .chosen-single-with-deselect span{margin-left:38px}.chosen-rtl .chosen-single div{right:auto;left:3px}.chosen-rtl .chosen-single abbr{right:auto;left:26px}.chosen-rtl .chosen-choices li{float:right}.chosen-rtl .chosen-choices li.search-field input[type=text]{direction:rtl}.chosen-rtl .chosen-choices li.search-choice{margin:3px 5px 3px 0;padding:3px 5px 3px 19px}.chosen-rtl .chosen-choices li.search-choice .search-choice-close{right:auto;left:4px}.chosen-rtl.chosen-container-single .chosen-results{margin:0 0 4px 4px;padding:0 4px 0 0}.chosen-rtl .chosen-results li.group-option{padding-right:15px;padding-left:0}.chosen-rtl.chosen-container-active.chosen-with-drop .chosen-single div{border-right:none}.chosen-rtl .chosen-search input[type=text]{padding:4px 5px 4px 20px;background:url(../../../img/third_party/chosen-sprite.png) no-repeat -30px -20px;direction:rtl}.chosen-rtl.chosen-container-single .chosen-single div b{background-position:6px 2px}.chosen-rtl.chosen-container-single.chosen-with-drop .chosen-single div b{background-position:-12px 2px}@media only screen and (-webkit-min-device-pixel-ratio:1.5),only screen and (min-resolution:144dpi),only screen and (min-resolution:1.5dppx){.chosen-container .chosen-results-scroll-down span,.chosen-container .chosen-results-scroll-up span,.chosen-container-multi .chosen-choices .search-choice .search-choice-close,.chosen-container-single .chosen-search input[type=text],.chosen-container-single .chosen-single abbr,.chosen-container-single .chosen-single div b,.chosen-rtl .chosen-search input[type=text]{background-image:url(../../../img/third_party/chosen-sprite@2x.png)!important;background-size:52px 37px!important;background-repeat:no-repeat!important}}.chosen-container{margin-bottom:.5em}.chosen-container.chosen-disabled a.chosen-single{border:1px solid #ccc}.chosen-container a.chosen-single{color:#333;display:block;text-overflow:ellipsis;white-space:nowrap;height:38px;width:100%;background:#f3f3f3;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;border:1px solid #ccc;border-top:1px solid #bbb;box-shadow:none;margin:0 0 .5em 0}.chosen-container a.chosen-single span{font-size:16px;padding-top:3px;margin-top:2px;color:#333}.chosen-container a.chosen-single span.svg-icon{float:left}.chosen-container a.chosen-single div b{background-position:0 6px}.chosen-container .chosen-search input.chosen-search-input[type=text]{width:95%!important;color:#666}.chosen-container .chosen-drop{border:1px solid #ccc!important;border-top:0!important;-webkit-border-radius:0!important;-moz-border-radius:0!important;border-radius:0!important;background:#fff!important}.chosen-container .chosen-drop .chosen-results{color:#333}.chosen-container.chosen-container-active.chosen-with-drop a.chosen-single{background:#fff;border:1px solid #ccc;border-top:1px solid #bbb;box-shadow:none}.chosen-container.chosen-container-active.chosen-with-drop a.chosen-single div b{background-position:-18px 8px}.singleline.connection_info.protocol_host_port .input.text.field_protocol_host{border:0;padding:0;width:70%}.singleline.connection_info.protocol_host_port .input.text.field_protocol_host .input.text{padding:0;margin:0}.singleline.connection_info.protocol_host_port .input.text.field_protocol_host .input.text input{margin:0;border-left:none}.singleline.connection_info.protocol_host_port .input.text.field_protocol_host .input.text.host{width:75%}.singleline.connection_info.protocol_host_port .input.text.field_protocol_host .input.text.host input[type=text]{width:95%}.singleline.connection_info.protocol_host_port .input.text.field_protocol_host .input.text.protocol{width:25%;position:relative}.singleline.connection_info.protocol_host_port .input.text.field_protocol_host .input.text.protocol .chosen-container{display:block}.singleline.connection_info.protocol_host_port .input.text.field_protocol_host .input.text.protocol .chosen-container a.chosen-single{height:38px}.singleline.connection_info.protocol_host_port .input.text.field_protocol_host .input.text.protocol .chosen-container a.chosen-single div b{background-position:0 10px}.singleline.connection_info.protocol_host_port .input.text.field_protocol_host .input.text.protocol .chosen-container a.chosen-single span{font-size:16px;padding:6px 0 0 0}.singleline.connection_info.protocol_host_port .input.text.field_protocol_host .input.text.protocol .chosen-container-active.chosen-with-drop a.chosen-single div b{background-position:-18px 10px}.singleline.connection_info.protocol_host_port .input.text.port{width:9%;margin-left:1%}.singleline.connection_info.protocol_host_port .input.text.port input[type=number]{width:100%}.dialog.key-info table{margin-top:1em}.dialog.key-info table tr td{padding:.5em 0 .5em 0}.dialog.key-info table tr td.label{padding-right:4em;color:#333}.setup .panel.main{bottom:0}.setup .panel.middle{overflow:auto}.setup .video-wrapper{margin-top:2em}.setup .input-password-wrapper{margin-top:1.5em;width:90%}.setup .input-password-wrapper .input.password input[type=password],.setup .input-password-wrapper .input.password input[type=text]{width:100%;box-sizing:border-box}.setup .why-plugin-wrapper p{padding-bottom:1em}.setup .why-plugin-wrapper .input{zoom:1}.setup .why-plugin-wrapper .input:after,.setup .why-plugin-wrapper .input:before{content:"";display:table}.setup .why-plugin-wrapper .input:after{clear:both}.setup .why-plugin-wrapper .input:after,.setup .why-plugin-wrapper .input:before{content:"";display:table}.setup .why-plugin-wrapper .input:after{clear:both}.setup .why-plugin-wrapper .input label{font-size:1em}.setup .why-plugin-wrapper .input.text{padding-bottom:.5em}.setup .why-plugin-wrapper .input.text input[type=text]{width:65%;float:left}.setup .why-plugin-wrapper .input.text label{width:20%;float:left;padding:.475em .625em .475em 0}.setup .why-plugin-wrapper .input.text a.more{padding:.475em 0 .475em .625em;text-decoration:underline;float:left;border:0}.setup .why-plugin-wrapper .input.checkbox{padding:1em 0 2em 0}.setup .why-plugin-wrapper .input .message.error{zoom:1;width:65%;margin-left:20%;padding-left:.625em}.setup .why-plugin-wrapper .input .message.error:after,.setup .why-plugin-wrapper .input .message.error:before{content:"";display:table}.setup .why-plugin-wrapper .input .message.error:after{clear:both}.setup .why-plugin-wrapper .input .message.error:after,.setup .why-plugin-wrapper .input .message.error:before{content:"";display:table}.setup .why-plugin-wrapper .input .message.error:after{clear:both}.setup .first-password-wrapper{padding-top:1em}.setup .import-key-wrapper .input{margin-top:1em;margin-right:1em}.setup .import-key-wrapper .input.file .help-text{margin-left:1em}.setup .row.last{padding-top:1em}.setup .side-message{margin-top:3em}.setup .backup-prompt{zoom:1;margin:2em 0}.setup .backup-prompt:after,.setup .backup-prompt:before{content:"";display:table}.setup .backup-prompt:after{clear:both}.setup .backup-prompt:after,.setup .backup-prompt:before{content:"";display:table}.setup .backup-prompt:after{clear:both}.setup .backup-prompt .button{margin-top:.25em;float:left;min-width:110px;width:20%}.setup .backup-prompt .help-text{float:left;margin-left:5%;width:60%}.passboltplugin .setup .plugin-check.success{display:block}@media all and (max-width:1024px){.input-password-wrapper .actions.inline,.input-password-wrapper .input.password{position:relative;float:none;clear:both}.input-password-wrapper .actions.inline{width:100%}.input-password-wrapper .input.password input[type=password]{width:87%}}.page.setup,.page.status{margin-bottom:2.5em}.page.setup .grid,.page.status .grid{padding-bottom:2em}.page.setup #url-rewriting-warning,.page.status #url-rewriting-warning{display:none}.page.setup .grid .message,.page.status .grid .message{padding:.75em 1em;margin-bottom:.5em}.page.setup .grid .input .message,.page.status .grid .input .message{padding:0 0 .5em 0}.cake-error{display:none}.page.start h2{color:#666;font-weight:400}.page.start .grid{max-width:860px}.page.start .intro{padding-top:1em;text-align:center}.page.start .big-choice{margin:1em 0;border:1px solid #eaeaea;padding:1em;padding-bottom:1.5em;min-height:10em;border-radius:.25em;position:relative}.page.start .big-choice:hover{border:1px solid #ddd}.page.start h3{border:0;padding:.5em 0 1em 0;margin:0}.page.start .help{margin-bottom:5em;background:#f0f7fd;border:1px solid #f0f7fd}.page.start .help:hover{background:#f0f7fd;border:1px solid #f0f7fd} \ No newline at end of file +*/.chosen-container{position:relative;display:inline-block;vertical-align:middle;font-size:13px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.chosen-container *{-webkit-box-sizing:border-box;box-sizing:border-box}.chosen-container .chosen-drop{position:absolute;top:100%;z-index:1010;width:100%;border:1px solid #aaa;border-top:0;background:#fff;-webkit-box-shadow:0 4px 5px rgba(0,0,0,.15);box-shadow:0 4px 5px rgba(0,0,0,.15);display:none}.chosen-container.chosen-with-drop .chosen-drop{display:block}.chosen-container a{cursor:pointer}.chosen-container .chosen-single .group-name,.chosen-container .search-choice .group-name{margin-right:4px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-weight:400;color:#969696}.chosen-container .chosen-single .group-name:after,.chosen-container .search-choice .group-name:after{content:"\003A";padding-left:2px;vertical-align:top}.chosen-container-single .chosen-single{position:relative;display:block;overflow:hidden;padding:0 0 0 8px;height:25px;border:1px solid #bbb;border-radius:5px;background-color:#fff;background:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#fff),color-stop(50%,#ddd),color-stop(52%,#eee),to(#f3f3f3));background:linear-gradient(#fff 20%,#ddd 50%,#eee 52%,#f3f3f3 100%);background-clip:padding-box;-webkit-box-shadow:0 0 3px #fff inset,0 1px 1px rgba(0,0,0,.1);box-shadow:0 0 3px #fff inset,0 1px 1px rgba(0,0,0,.1);color:#333;text-decoration:none;white-space:nowrap;line-height:24px}.chosen-container-single .chosen-single input[type=text]{cursor:pointer;opacity:0;position:absolute}.chosen-container-single .chosen-default{color:#888}.chosen-container-single .chosen-single span{display:block;overflow:hidden;margin-right:32px;text-overflow:ellipsis;white-space:nowrap}.chosen-container-single .chosen-single-with-deselect span{margin-right:38px}.chosen-container-single .chosen-single abbr{position:absolute;top:6px;right:26px;display:block;width:12px;height:12px;background:url('../../../img/third_party/chosen-sprite.png') -42px 1px no-repeat;font-size:1px}.chosen-container-single .chosen-single abbr:hover{background-position:-42px -10px}.chosen-container-single.chosen-disabled .chosen-single abbr:hover{background-position:-42px -10px}.chosen-container-single .chosen-single div{position:absolute;top:0;right:.8rem;display:block;width:1.8rem;height:100%}.chosen-container-single .chosen-single div b{display:block;width:100%;height:100%;background:url("../../../img/third_party/chosen-sprite.png") no-repeat 0 2px}.chosen-container-single .chosen-search{position:relative;z-index:1010;margin:0;padding:3px 4px;white-space:nowrap}.chosen-container-single .chosen-search span.svg-icon{position:absolute;margin-left:-1.5em;margin-top:.35em}.chosen-container-single .chosen-search input[type=text]{margin:1px 0;padding:4px 20px 4px 5px;width:100%;height:auto;outline:0;border:1px solid #bbb;background:url("../../../img/third_party/chosen-sprite.png") no-repeat 100% -20px;font-size:1em;font-family:sans-serif;line-height:normal;border-radius:0}.chosen-container-single .chosen-drop{margin-top:-.5em;border-radius:0 0 4px 4px;background-clip:padding-box}.chosen-container-single.chosen-container-single-nosearch .chosen-search{position:absolute;clip:rect(0,0,0,0)}.chosen-container .chosen-results{color:#333;position:relative;overflow-x:hidden;overflow-y:auto;margin:0 4px 4px 0;padding:0 0 0 4px;max-height:240px;-webkit-overflow-scrolling:touch}.chosen-container .chosen-results li{display:none;margin:0;padding:5px 6px;list-style:none;line-height:15px;word-wrap:break-word;-webkit-touch-callout:none}.chosen-container .chosen-results li.active-result{display:list-item;cursor:pointer}.chosen-container .chosen-results li.disabled-result{display:list-item;color:#ddd;cursor:default}.chosen-container .chosen-results li:hover{background-color:#daecf9;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#3875d7),color-stop(90%,#2a62bc));background-image:linear-gradient(#3875d7 20%,#2a62bc 90%);color:#fff}.chosen-container .chosen-results li.no-results{color:#666;display:list-item;background:#f3f3f3}.chosen-container .chosen-results li.group-result{display:list-item;font-weight:700;cursor:default}.chosen-container .chosen-results li.group-option{padding-left:15px}.chosen-container .chosen-results li em{font-style:normal;text-decoration:underline}.chosen-container-multi .chosen-choices{position:relative;overflow:hidden;margin:0;padding:0 5px;width:100%;height:auto;border:1px solid #bbb;background-color:#fff;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(1%,#eee),color-stop(15%,#fff));background-image:linear-gradient(#eee 1%,#fff 15%);cursor:text}.chosen-container-multi .chosen-choices li{float:left;list-style:none}.chosen-container-multi .chosen-choices li.search-field{margin:0;padding:0;white-space:nowrap}.chosen-container-multi .chosen-choices li.search-field input[type=text]{margin:1px 0;padding:0;height:25px;outline:0;border:0!important;background:0 0!important;-webkit-box-shadow:none;box-shadow:none;color:#888;font-size:100%;font-family:sans-serif;line-height:normal;border-radius:0;width:25px}.chosen-container-multi .chosen-choices li.search-choice{position:relative;margin:3px 5px 3px 0;padding:3px 20px 3px 5px;border:1px solid #bbb;max-width:100%;border-radius:3px;background-color:#fff;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#fff),color-stop(50%,#ddd),color-stop(52%,#eee),to(#f3f3f3));background-image:linear-gradient(#fff 20%,#ddd 50%,#eee 52%,#f3f3f3 100%);background-size:100% 19px;background-repeat:repeat-x;background-clip:padding-box;-webkit-box-shadow:0 0 2px #fff inset,0 1px 0 rgba(0,0,0,.05);box-shadow:0 0 2px #fff inset,0 1px 0 rgba(0,0,0,.05);color:#ddd;line-height:13px;cursor:default}.chosen-container-multi .chosen-choices li.search-choice span{word-wrap:break-word}.chosen-container-multi .chosen-choices li.search-choice .search-choice-close{position:absolute;top:4px;right:3px;display:block;width:12px;height:12px;background:url("../../../img/third_party/chosen-sprite.png") -42px 1px no-repeat;font-size:1px}.chosen-container-multi .chosen-choices li.search-choice .search-choice-close:hover{background-position:-42px -10px}.chosen-container-multi .chosen-choices li.search-choice-disabled{padding-right:5px;border:1px solid #ccc;background-color:#f3f3f3;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#fff),color-stop(50%,#ddd),color-stop(52%,#eee),to(#f3f3f3));background-image:linear-gradient(#fff 20%,#ddd 50%,#eee 52%,#f3f3f3 100%);color:#666}.chosen-container-multi .chosen-choices li.search-choice-focus{background:#ddd}.chosen-container-multi .chosen-choices li.search-choice-focus .search-choice-close{background-position:-42px -10px}.chosen-container-multi .chosen-results{margin:0;padding:0}.chosen-container-multi .chosen-drop .result-selected{display:list-item;color:#eee;cursor:default}.chosen-container-active .chosen-single{border:1px solid #2894df;-webkit-box-shadow:0 0 5px rgba(0,0,0,.3);box-shadow:0 0 5px rgba(0,0,0,.3)}.chosen-container-active.chosen-with-drop .chosen-single{border:1px solid #bbb;border-bottom-right-radius:0;border-bottom-left-radius:0;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#eee),color-stop(80%,#fff));background-image:linear-gradient(#eee 20%,#fff 80%);-webkit-box-shadow:0 1px 0 #fff inset;box-shadow:0 1px 0 #fff inset}.chosen-container-active.chosen-with-drop .chosen-single div{border-left:none;background:0 0}.chosen-container-active.chosen-with-drop .chosen-single div b{background-position:-18px 2px}.chosen-container-active .chosen-choices{border:1px solid #2894df;-webkit-box-shadow:0 0 5px rgba(0,0,0,.3);box-shadow:0 0 5px rgba(0,0,0,.3)}.chosen-container-active .chosen-choices li.search-field input[type=text]{color:#333!important}.chosen-disabled{opacity:.5!important;cursor:default}.chosen-disabled .chosen-single{cursor:default}.chosen-disabled .chosen-choices .search-choice .search-choice-close{cursor:default}.chosen-rtl{text-align:right}.chosen-rtl .chosen-single{overflow:visible;padding:0 8px 0 0}.chosen-rtl .chosen-single span{margin-right:0;margin-left:26px;direction:rtl}.chosen-rtl .chosen-single-with-deselect span{margin-left:38px}.chosen-rtl .chosen-single div{right:auto;left:3px}.chosen-rtl .chosen-single abbr{right:auto;left:26px}.chosen-rtl .chosen-choices li{float:right}.chosen-rtl .chosen-choices li.search-field input[type=text]{direction:rtl}.chosen-rtl .chosen-choices li.search-choice{margin:3px 5px 3px 0;padding:3px 5px 3px 19px}.chosen-rtl .chosen-choices li.search-choice .search-choice-close{right:auto;left:4px}.chosen-rtl.chosen-container-single .chosen-results{margin:0 0 4px 4px;padding:0 4px 0 0}.chosen-rtl .chosen-results li.group-option{padding-right:15px;padding-left:0}.chosen-rtl.chosen-container-active.chosen-with-drop .chosen-single div{border-right:none}.chosen-rtl .chosen-search input[type=text]{padding:4px 5px 4px 20px;background:url("../../../img/third_party/chosen-sprite.png") no-repeat -30px -20px;direction:rtl}.chosen-rtl.chosen-container-single .chosen-single div b{background-position:6px 2px}.chosen-rtl.chosen-container-single.chosen-with-drop .chosen-single div b{background-position:-12px 2px}@media only screen and (-webkit-min-device-pixel-ratio:1.5),only screen and (min-resolution:144dpi),only screen and (min-resolution:1.5dppx){.chosen-container .chosen-results-scroll-down span,.chosen-container .chosen-results-scroll-up span,.chosen-container-multi .chosen-choices .search-choice .search-choice-close,.chosen-container-single .chosen-search input[type=text],.chosen-container-single .chosen-single abbr,.chosen-container-single .chosen-single div b,.chosen-rtl .chosen-search input[type=text]{background-image:url('../../../img/third_party/chosen-sprite@2x.png')!important;background-size:52px 37px!important;background-repeat:no-repeat!important}}.chosen-container{margin-bottom:.5em}.chosen-container.chosen-disabled a.chosen-single{border:1px solid #ccc}.chosen-container a.chosen-single{color:#333;display:block;text-overflow:ellipsis;white-space:nowrap;height:3.8rem;width:100%;background:#f3f3f3;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;border:1px solid #ccc;border-top:1px solid #bbb;box-shadow:none;margin:0 0 .8rem 0}.chosen-container a.chosen-single span{font-size:1.6rem;padding-left:.4rem;padding-top:.4rem;margin-top:.2rem;color:#333}.chosen-container a.chosen-single span.svg-icon{float:left}.chosen-container a.chosen-single div b{background-position:0 .6rem}.chosen-container .chosen-search input.chosen-search-input[type=text]{width:95%!important;color:#666}.chosen-container .chosen-drop{border:1px solid #ccc!important;border-top:0!important;-webkit-border-radius:0!important;-moz-border-radius:0!important;border-radius:0!important;background:#fff!important}.chosen-container .chosen-drop .chosen-results{color:#333}.chosen-container.chosen-container-active.chosen-with-drop a.chosen-single{background:#fff;border:1px solid #ccc;border-top:1px solid #bbb;box-shadow:none}.chosen-container.chosen-container-active.chosen-with-drop a.chosen-single div b{background-position:-1.8rem .8rem}.singleline.connection_info .input.text.first-field,.singleline.connection_info .input.text:first-child{margin-right:0}.singleline.connection_info .input.text.protocol{flex:0 0 2.5rem}.singleline.connection_info .input.text.protocol .chosen-container{display:block}.singleline.connection_info .input.text.protocol .chosen-container a.chosen-single div b{background-position:0 .8rem}.singleline.connection_info .input.text.protocol .chosen-container-active.chosen-with-drop a.chosen-single div b{background-position:-1.8rem .8rem}.singleline.connection_info .input.text.host{flex:1 0 auto}.singleline.connection_info .input.text.port{flex:0 0 9.5rem}.setup .panel.main{bottom:0}.setup .panel.middle{overflow:auto}.setup .row.last{padding-top:1.6rem}.setup h1 .button{margin-right:0;padding:0 .7rem;font-size:2.2rem;line-height:3.2rem}.setup .button.primary .svg-icon svg{fill:#FFF}.page.setup,.page.status{margin-bottom:2.5em}.page.setup .grid,.page.status .grid{padding-bottom:2em}.page.setup #url-rewriting-warning,.page.status #url-rewriting-warning{display:none}.page.setup .grid .message,.page.status .grid .message{padding:.75em 1em;margin-bottom:.5em}.page.setup .grid .input .message,.page.status .grid .input .message{padding:0 0 .5em 0}.cake-error{display:none}.page.start h2{color:#666;font-weight:400}.page.start .grid{max-width:860px}.page.start .intro{padding-top:1em;text-align:center}.page.start .big-choice{margin:1em 0;border:1px solid #eaeaea;padding:1em;padding-bottom:1.5em;min-height:10em;border-radius:.25em;position:relative}.page.start .big-choice:hover{border:1px solid #ddd}.page.start h3{border:0;padding:.5em 0 1em 0;margin:0}.page.start .help{margin-bottom:5em;background:#f0f7fd;border:1px solid #f0f7fd}.page.start .help:hover{background:#f0f7fd;border:1px solid #f0f7fd} \ No newline at end of file diff --git a/webroot/css/themes/default/ext_authentication.min.css b/webroot/css/themes/default/ext_authentication.min.css new file mode 100644 index 0000000000..aa665e5643 --- /dev/null +++ b/webroot/css/themes/default/ext_authentication.min.css @@ -0,0 +1,9 @@ +/**! + * @name passbolt-styleguide + * @version v3.4.0 + * @date 2021-12-01 + * @copyright Copyright 2021 Passbolt SA + * @source https://github.com/passbolt/passbolt_styleguide + * @license AGPL-3.0 + */ +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}div{outline:0}html body .hidden{display:none}.visually-hidden,.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visually-hidden .focusable:active,.visually-hidden .focusable:focus,.visuallyhidden .focusable:active,.visuallyhidden .focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.clearfix:after,.clearfix:before{content:"";display:table}.clearfix:after{clear:both}.rounded{-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem}.ellipsis{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.rotate-right{transform:rotate(-90deg)}html{line-height:normal}body{color:#333;background:#fff}body.iframe{background:0 0}@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;src:local('Open Sans'),local('OpenSans'),url('../../../fonts/opensans-regular.woff') format('woff')}@font-face{font-family:'Open Sans';font-style:normal;font-weight:700;src:local('Open Sans Bold'),local('OpenSans-Bold'),url('../../../fonts/opensans-bold.woff') format('woff')}body{font-family:'Open Sans',Verdana,sans-serif}.code{font-family:'Courier New',Courier,monospace}html{font-size:62.5%}body{font-size:1.6rem}h1{font-size:2.2rem;line-height:3.2rem}h2{font-size:1.8rem;line-height:2.4rem}h3{font-size:1.6rem;line-height:2.4rem;border-bottom:1px dotted #ddd}p{font-size:1.5rem;line-height:2.2rem;margin-top:0}code{font-size:1.1rem}.font-dim{color:#666}a{outline:0;text-decoration:none;cursor:pointer;border-bottom:1px solid #ddd}a:link,a:visited{color:#333}a:hover{text-decoration:none;cursor:pointer;color:#2894df;border-bottom:1px solid #2894df}a:active,a:focus{outline:0;color:#2894df;border:0}a.no-border,a:hover.no-border{border-bottom:0}a.disabled{outline:0;text-decoration:none;cursor:default;color:#888;pointer-events:none}ul{padding:0;margin:0}ul li{list-style:none;padding:0;margin:0}.button,button{font-size:1.4rem;padding:.8rem 1.6rem;line-height:1.6rem;margin-right:.8rem;position:relative;display:inline-block;vertical-align:baseline;outline:0;cursor:pointer;text-align:center;text-decoration:none;border-top:1px solid #ccc;border-right:1px solid #ccc;border-bottom:1px solid #ccc;border-left:1px solid #ccc;color:#333;font-weight:400;text-shadow:1px 1px 0 rgba(255,255,255,.5);background:#f3f3f3;-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem;box-sizing:border-box}.button.medium{font-size:1.6rem;line-height:2.4rem;padding:.8rem 2.4rem}.button.big{font-size:1.8rem;line-height:3.2rem;padding:.8rem 3.2rem}.button.full-width{width:100%}.button.primary{background:#2894df;border:1px solid #2894df;color:#fff;text-shadow:1px 1px 0 rgba(0,0,0,.2)}.button.primary:hover{background:#2a9ceb;border:1px solid #2a9ceb;color:#fff}.button.primary:active,.button.primary:focus{color:#fff;border:1px solid #4271b7;background:#2a9ceb}.button.warning,.button.warning:active,.button.warning:focus,.button.warning:hover{color:#fff;background:#d40101;border:1px solid #d40101;text-shadow:none}.button.warning:active,.button.warning:focus{border:1px solid #92000c}.button.cancel,.button.cancel:active,.button.cancel:focus,.button.cancel:hover,.button.dim,.button.dim:active,.button.dim:focus,.button.dim:hover{background:#fff;font-weight:400}.button:hover{color:#333;text-decoration:none;background:#eee;border:1px solid #bbb}.button:focus{color:#2894df;border:1px solid #2894df}.button:active{color:#2894df;border:1px solid #2894df;background:#eee;position:relative;box-shadow:inset 0 1px 2px rgba(0,0,0,.2)}.button.disabled{cursor:default;color:#888;background:#fff;border:1px solid #ddd;text-shadow:none;font-weight:400}.button.disabled:active,.button.disabled:focus,.button.disabled:hover{box-shadow:0 0 0;top:0;color:#888;background:#fff;border:1px solid #ddd}.button.processing{background:#fff;border:1px solid #fff}.button.processing:after{width:100%;height:100%;position:absolute;content:" ";top:-1px;left:-1px;background:#fff url('../../../img/controls/loading_light.svg') center center no-repeat;background-size:auto 50%;border:1px solid #ddd;-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem}input[type=submit].button.processing{font-size:0;background:#fff url('../../../img/controls/loading_light.svg') center center no-repeat;background-size:auto 50%;border:1px solid #ddd;border-radius:5px}.button-toggle.selected{background:#eee;box-shadow:inset 0 1px 2px rgba(0,0,0,.2);border:1px solid #bbb}.button-group{display:flex;flex-wrap:wrap;justify-content:flex-start}.button-group--nowrap>.button{white-space:nowrap}.button-group>*{flex-grow:1;margin-bottom:.5em}label{font-weight:700;font-size:1.6rem;line-height:2.4rem;padding:.8rem 0;display:block}.required label:after{content:" \002A";color:#d40101;font-weight:700}.input.error label{color:#d40101}input[type=email],input[type=number],input[type=password],input[type=search],input[type=text],textarea{box-sizing:border-box;font-size:1.6rem;line-height:2.4rem;color:#333;background:#fff;width:100%;max-width:64rem;padding:.6rem 1.2rem;display:inline-block;margin-bottom:.8rem;border:1px solid #ccc;border-top:1px solid #bbb;vertical-align:middle}::-ms-reveal{display:none}input[type=number]{box-sizing:border-box}input[type=file]{box-sizing:border-box;font-size:1.6rem;line-height:2.4rem;color:#333;background:#fff;width:100%;max-width:64rem;display:inline-block;margin-bottom:.8rem;vertical-align:middle}input[type=email]:hover,input[type=number]:hover,input[type=password]:hover,input[type=search]:hover,input[type=text]:hover,textarea:hover{border:1px solid #bbb}input[type=email]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=text]:focus,textarea:focus{outline:0;box-shadow:inset 1px 1px 2px rgba(0,0,0,.2);border:1px solid #2894df}input[type=email]:disabled,input[type=number]:disabled,input[type=password]:disabled,input[type=search]:disabled,input[type=text]:disabled,textarea:disabled{color:#333;border:1px solid #ddd;box-shadow:0 0;cursor:default;background:#f3f3f3}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{opacity:1}textarea{height:9rem}.textarea.large textarea{width:30rem;height:9rem}.checkbox input[type=checkbox]{position:absolute;opacity:0;cursor:pointer}.checkbox input[type=checkbox].error{color:#d40101}.checkbox input[type=checkbox]+label{font-weight:400;position:relative;cursor:pointer;padding:0;margin-bottom:.8rem}.checkbox input[type=checkbox]+label:before{content:'';margin-top:.4rem;margin-right:1rem;display:inline-block;vertical-align:text-top;width:1.4rem;height:1.4rem;background:#fff;border:1px solid #ddd;border-radius:.3rem;box-sizing:border-box}.checkbox input[type=checkbox]:hover+label:before{box-shadow:0 .1rem 0 #ddd,inset 0 .1rem 0 rgba(255,255,255,.25)}.checkbox input[type=checkbox]:focus+label:before{box-shadow:0 0 .4rem #2a9ceb;border:1px solid #2a9ceb}.checkbox input[type=checkbox]:active+label:before{box-shadow:inset 0 -.1rem 0 rgba(255,255,255,.25),inset 0 .1rem 0 #ddd}.checkbox input[type=checkbox]:disabled+label{color:#ddd;cursor:auto}.checkbox input[type=checkbox]:disabled+label:before{box-shadow:none;background:#ccc;border:none}.checkbox input[type=checkbox]:checked+label:after{content:'';position:absolute;left:.2rem;top:.75rem;width:1rem;height:1rem;background-size:1rem 1rem;background-image:url('../../../img/controls/check_black.svg');-webkit-mask-image:url('../../../img/controls/check_black.svg');mask-image:url('../../../img/controls/check_black.svg')}.checkbox input[type=checkbox]:disabled+label:after{background:#969696}.checkbox.medium input[type=checkbox]{cursor:pointer}.checkbox.medium input[type=checkbox]+label{font-size:1.5rem}.checkbox.medium input[type=checkbox]+label:before{margin-top:.3rem;margin-right:1rem;width:1.4rem;height:1.4rem}.checkbox.medium input[type=checkbox]:checked+label:after{left:.2rem;top:.7rem;width:1rem;height:1rem;background-size:1rem 1rem}.checkbox.small input[type=checkbox]{cursor:pointer}.checkbox.small input[type=checkbox]+label{font-size:1.4rem}.checkbox.small input[type=checkbox]+label:before{margin-top:.3rem;margin-right:1rem;width:1.4rem;height:1.4rem}.checkbox.small input[type=checkbox]:checked+label:after{left:.2rem;top:.7rem;width:1rem;height:1rem;background-size:1rem 1rem}.radiolist{margin-bottom:.8rem}.radiolist:after,.radiolist:before{content:"";display:table}.radiolist:after{clear:both}.radiolist:after,.radiolist:before{content:"";display:table}.radiolist:after{clear:both}.radiolist .input.radio{float:left}.radiolist .input.radio input{float:left;margin-top:.8rem}.radiolist .input.radio input+label{float:left;font-weight:400;display:inline-block;margin-left:1rem;font-size:1.6rem;margin-right:3.2rem;line-height:1.4rem}.radiolist .input.radio input+label:after{content:initial}.input.toggle-switch{display:flex;padding-top:1.6rem;padding-bottom:.8rem}.input.toggle-switch label{padding-top:0;padding-left:1.6rem;order:2;flex:1;font-weight:400;font-size:1.6rem}.input.toggle-switch.disabled label{color:#969696}.input.toggle-switch .toggle-switch-checkbox{order:-1;flex:0 0 3em;display:none}.input.toggle-switch .toggle-switch-checkbox,.input.toggle-switch .toggle-switch-checkbox *,.input.toggle-switch .toggle-switch-checkbox :after,.input.toggle-switch .toggle-switch-checkbox :before,.input.toggle-switch .toggle-switch-checkbox+.toggle-switch-button,.input.toggle-switch .toggle-switch-checkbox:after,.input.toggle-switch .toggle-switch-checkbox:before{box-sizing:border-box}.input.toggle-switch .toggle-switch-checkbox ::selection,.input.toggle-switch .toggle-switch-checkbox :after::selection,.input.toggle-switch .toggle-switch-checkbox :before::selection,.input.toggle-switch .toggle-switch-checkbox+.toggle-switch-button::selection,.input.toggle-switch .toggle-switch-checkbox::selection,.input.toggle-switch .toggle-switch-checkbox:after::selection,.input.toggle-switch .toggle-switch-checkbox:before::selection{background:0 0}.input.toggle-switch .toggle-switch-button{order:-1;flex:none;outline:0;display:block;width:3em;height:1.5em;position:relative;cursor:pointer;user-select:none;background:#f3f3f3;border-radius:2em;padding:2px;transition:all .4s ease;border:1px solid #ddd}.input.toggle-switch .toggle-switch-button span{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.input.toggle-switch .toggle-switch-button span .focusable:active,.input.toggle-switch .toggle-switch-button span .focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.input.toggle-switch .toggle-switch-button span .focusable:active,.input.toggle-switch .toggle-switch-button span .focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.input.toggle-switch .toggle-switch-button:hover:after{will-change:padding}.input.toggle-switch .toggle-switch-button:active{box-shadow:inset 0 0 0 2em #e8eae9}.input.toggle-switch .toggle-switch-button:active:after{padding-right:.8em}.input.toggle-switch .toggle-switch-button:after,.input.toggle-switch .toggle-switch-button:before{position:relative;display:block;content:"";width:50%;height:100%}.input.toggle-switch .toggle-switch-button:after{left:0;border-radius:2em;background:#fff;transition:left .3s cubic-bezier(.175, .885, .32, 1.275),padding .3s ease,margin .3s ease;box-shadow:0 0 0 1px rgba(0,0,0,.1),0 4px 0 rgba(0,0,0,.08)}.input.toggle-switch .toggle-switch-button:before{display:none}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button:after{left:50%}.input.toggle-switch .toggle-switch-checkbox:disabled+.toggle-switch-button{background:#fff;cursor:not-allowed}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button{background:#6c0}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button:active{box-shadow:none}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button:active:after{margin-left:-.8em}.input.toggle-switch .toggle-switch-checkbox:disabled:checked+.toggle-switch-button{background:#e6ffcc}h1 .input.toggle-switch,h2 .input.toggle-switch,h3 .input.toggle-switch,h4 .input.toggle-switch,h5 .input.toggle-switch,h6 .input.toggle-switch{float:left;padding:.4em 1em 0 0}.input.select select{display:inline-block;font-size:1.6rem;line-height:2.4rem;color:#333;padding:.6rem 1.2rem;width:100%;max-width:64rem;box-sizing:border-box;margin:0 0 .8rem 0;border:1px solid #ccc;-moz-appearance:none;-webkit-appearance:none;appearance:none;background-color:#fff;background-image:url('../../../img/controls/chevron-down_black.svg'),linear-gradient(to bottom,#fff 0,#fff 100%);background-repeat:no-repeat,repeat;background-position:right .6rem top 50%,0 0;background-size:1rem auto,100%}.input.select select.medium{width:50%}.input.select select:focus{outline:0;border:1px solid #2894df;border-radius:0;background-image:url('../../../img/controls/chevron-down_blue.svg'),linear-gradient(to bottom,#fff 0,#fff 100%)}.input.select select:disabled{background-image:url('../../../img/controls/chevron-down_black.svg'),linear-gradient(to bottom,#f3f3f3 0,#f3f3f3 100%);color:#888}.input .error-message{padding:0;font-size:1.4rem;margin-top:.2rem;margin-bottom:1.6rem;background-color:transparent;border:0;font-weight:400;color:#d40101;background:#fff;clear:both}.input .help-message{padding:0;font-size:1.4rem;margin-top:.2rem;margin-bottom:1.6rem;background-color:transparent;border:0;color:#888;font-weight:400;background:#fff;clear:both}.singleline{display:flex;max-width:64rem}.singleline:after,.singleline:before{content:"";display:table}.singleline:after{clear:both}.singleline:after,.singleline:before{content:"";display:table}.singleline:after{clear:both}.singleline .input{flex:1}.singleline .input.first-field,.singleline .input:first-child{margin-right:2%}.slider{display:flex;align-items:center}.slider input[type=range]{-webkit-appearance:none;width:100%;height:1px;border-radius:5px;background:#ddd;outline:0;opacity:.7;-webkit-transition:.2s;transition:opacity .2s;flex-grow:1}.slider input[type=range]:hover{opacity:1}.slider input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:15px;height:15px;border-radius:50%;border-width:1px;border-style:solid;border-color:#969696;background:#ddd;cursor:pointer}.slider input[type=range]::-moz-range-thumb{width:15px;height:15px;border-radius:50%;background:#ddd;cursor:pointer}.slider input[type=number]{width:6.5rem;margin-left:1.6rem;padding:.8rem}.svg-icon{display:inline-flex;align-self:center}.svg-icon svg{fill:#333;height:1em;width:1em}.svg-icon.baseline svg{top:.125em;position:relative}.svg-icon.dim svg,.svg-icon.light svg{fill:#888888}.svg-icon.icon-only svg{padding:.1rem;height:1.6rem;width:1.6rem}a:hover .svg-icon svg{fill:#2894DF}a.disabled .svg-icon svg{fill:#888888;pointer-events:none;cursor:default}a.disabled:hover .svg-icon svg{fill:#888888}a.fav .svg-icon svg{fill:#D40101}a.unfav .svg-icon svg{fill:#DDD}.button .svg-icon svg{top:.2rem;position:relative}.button .svg-icon+span:not(.visuallyhidden){margin-left:.8rem;display:inline-block}.button .svg-icon svg:hover,.button:hover .svg-icon svg{fill:#333}.button.primary:active .svg-icon svg,.button.primary:focus .svg-icon svg,.button.warning .svg-icon svg,.button.warning:active .svg-icon svg,.button.warning:focus .svg-icon svg,.button.warning:hover .svg-icon svg{fill:#FFF}.button:active .svg-icon svg,.button:focus .svg-icon svg{fill:#2894DF}.button.disabled .svg-icon svg{fill:#888888}.button.disabled .svg-icon svg:hover{fill:#888888}@keyframes drawCircle{0%{stroke-dashoffset:180px}100%{stroke-dashoffset:0}}@keyframes drawCheck{0%{stroke-dashoffset:50px}100%{stroke-dashoffset:0}}@keyframes drawCross{0%{stroke-dashoffset:50px}100%{stroke-dashoffset:0}}@keyframes drawWarning{0%{stroke-dashoffset:230px}100%{stroke-dashoffset:0}}.icon-feedback .success-animation-circle{stroke-dasharray:180px 180px;stroke:#009900}.icon-feedback .success-animation-line{stroke-dasharray:50px 50px;stroke:#009900}.icon-feedback .success-animation-line2{stroke-dasharray:50px 50px;stroke:#009900}.icon-feedback .error-animation-circle{stroke-dasharray:180px 180px;stroke:#D40101}.icon-feedback .error-animation-line{stroke-dasharray:50px 50px;stroke:#D40101}.icon-feedback .warning-animation-line{stroke-dasharray:230px 230px;stroke:#9F6000;stroke-linecap:round;stroke-linejoin:round}.icon-feedback .warning-animation-circle{fill:#9F6000}.icon-feedback .animated{animation:.75s ease-out 0s 1 both pop}.icon-feedback .animated .error-animation-circle,.icon-feedback .animated .success-animation-circle,.icon-feedback .animated .warning-animation-circle{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawCircle}.icon-feedback .animated .success-animation-line{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawCheck}.icon-feedback .animated .error-animation-line{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawCross}.icon-feedback .animated .warning-animation-line{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawWarning}table{border-collapse:collapse;border-spacing:0}table td,table th{text-align:left;font-weight:400}.logo{background:transparent url('../../../img/logo/logo.svg') 0 0 no-repeat;background-size:20rem auto;width:20rem;height:4.5rem}.scroll{overflow-y:scroll;background:linear-gradient(#fff 30%,rgba(255,255,255,0)),linear-gradient(rgba(255,255,255,0),#fff 70%) 0 100%,radial-gradient(farthest-side at 75% 0,rgba(0,0,0,.2),rgba(0,0,0,0)),radial-gradient(farthest-side at 75% 100%,rgba(0,0,0,.2),rgba(0,0,0,0)) 0 100%;background-repeat:no-repeat;background-color:#fff;background-size:100% 10px,100% 10px,100% 5px,100% 5px}::-webkit-scrollbar{width:1em}::-webkit-scrollbar-track{background:#f9f9f9;border-top:0;border-bottom:0}::-webkit-scrollbar-thumb{background:#c1c1c1;border-radius:1em;border:4px solid #f9f9f9}.scroll-shadow{overflow:auto;background:linear-gradient(#fff 30%,rgba(255,255,255,0)),linear-gradient(rgba(255,255,255,0),#fff 70%) 0 100%,radial-gradient(farthest-side at 50% 0,rgba(0,0,0,.08),rgba(0,0,0,0)),radial-gradient(farthest-side at 50% 100%,rgba(0,0,0,.08),rgba(0,0,0,0)) 0 100%;background-repeat:no-repeat;background-color:#fff;background-size:100% 40px,100% 40px,100% 14px,100% 14px;background-attachment:local,local,scroll,scroll}.simplebar-content{overflow:auto;background:linear-gradient(#fff 30%,rgba(255,255,255,0)),linear-gradient(rgba(255,255,255,0),#fff 70%) 0 100%,radial-gradient(farthest-side at 50% 0,rgba(0,0,0,.08),rgba(0,0,0,0)),radial-gradient(farthest-side at 50% 100%,rgba(0,0,0,.08),rgba(0,0,0,0)) 0 100%;background-repeat:no-repeat;background-color:#fff;background-size:100% 40px,100% 40px,100% 14px,100% 14px;background-attachment:local,local,scroll,scroll}[data-simplebar]{position:relative;flex-direction:column;flex-wrap:wrap;justify-content:flex-start;align-content:flex-start;align-items:flex-start;width:inherit;height:inherit;max-width:inherit;max-height:inherit}.simplebar-wrapper{overflow:hidden;width:inherit;height:inherit;max-width:inherit;max-height:inherit}.simplebar-mask{direction:inherit;position:absolute;overflow:hidden;padding:0;margin:0;left:0;top:0;bottom:0;right:0;width:auto!important;height:auto!important;z-index:0}.simplebar-offset{direction:inherit!important;box-sizing:inherit!important;resize:none!important;position:absolute;top:0;left:0;bottom:0;right:0;padding:0;margin:0;-webkit-overflow-scrolling:touch}.simplebar-content-wrapper{direction:inherit;box-sizing:border-box!important;position:relative;display:block;height:100%;width:auto;max-width:100%;max-height:100%;scrollbar-width:none;-ms-overflow-style:none}.simplebar-content-wrapper::-webkit-scrollbar,.simplebar-hide-scrollbar::-webkit-scrollbar{width:0;height:0}.simplebar-content:after,.simplebar-content:before{content:' ';display:table}.simplebar-placeholder{max-height:100%;max-width:100%;width:100%;pointer-events:none}.simplebar-height-auto-observer-wrapper{box-sizing:inherit!important;height:100%;width:inherit;max-width:1px;position:relative;float:left;max-height:1px;overflow:hidden;z-index:-1;padding:0;margin:0;pointer-events:none;flex-grow:inherit;flex-shrink:0;flex-basis:0}.simplebar-height-auto-observer{box-sizing:inherit;display:block;opacity:0;position:absolute;top:0;left:0;height:1000%;width:1000%;min-height:1px;min-width:1px;overflow:hidden;pointer-events:none;z-index:-1}.simplebar-track{z-index:1;position:absolute;right:0;bottom:0;pointer-events:none;overflow:hidden}[data-simplebar].simplebar-dragging .simplebar-content{pointer-events:none;user-select:none;-webkit-user-select:none}[data-simplebar].simplebar-dragging .simplebar-track{pointer-events:all}.simplebar-scrollbar{position:absolute;right:2px;width:7px;min-height:10px}.simplebar-scrollbar:before{position:absolute;content:'';background:#000;border-radius:7px;left:0;right:0;opacity:0;transition:opacity .2s linear}.simplebar-scrollbar.simplebar-visible:before{opacity:.5;transition:opacity 0s linear}.simplebar-track.simplebar-vertical{top:0;width:11px}.simplebar-track.simplebar-vertical .simplebar-scrollbar:before{top:2px;bottom:2px}.simplebar-track.simplebar-horizontal{left:0;height:11px}.simplebar-track.simplebar-horizontal .simplebar-scrollbar:before{height:100%;left:2px;right:2px}.simplebar-track.simplebar-horizontal .simplebar-scrollbar{right:auto;left:0;top:2px;height:7px;min-height:0;min-width:10px;width:auto}[data-simplebar-direction=rtl] .simplebar-track.simplebar-vertical{right:auto;left:0}.hs-dummy-scrollbar-size{direction:rtl;position:fixed;opacity:0;visibility:hidden;height:500px;width:500px;overflow-y:hidden;overflow-x:scroll}.simplebar-hide-scrollbar{position:fixed;left:0;visibility:hidden;overflow-y:scroll;scrollbar-width:none;-ms-overflow-style:none}.shimmer{display:block;position:relative;width:100%;height:100%;content:'';animation:shimmer 2s infinite;background:linear-gradient(45deg,rgba(255,255,255,0) 0,rgba(255,255,255,.1) 30%,rgba(255,255,255,.5) 50%,rgba(255,255,255,0))}@keyframes shimmer{0%{background-position:0 0}100%{background-position:1000px 0}}html{scroll-behavior:smooth}.animated{-webkit-animation-duration:.5s;animation-duration:.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both}@keyframes fadeInUp{0%{opacity:0;-webkit-transform:translateY(20px);-ms-transform:translateY(20px);transform:translateY(20px)}100%{opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}}@keyframes fadeOutUp{from{opacity:1}to{opacity:0;transform:translate3d(0,-100%,0)}}@keyframes fadeInDown{from{opacity:0;transform:translate3d(0,-100%,0)}to{opacity:1;transform:none}}@keyframes pop{0%{opacity:0;transform:scale(1)}55%{opacity:1;transform:scale(1)}65%{opacity:1;transform:scale(1.25)}75%{opacity:1;transform:scale(1)}100%{opacity:1;transform:scale(1)}}.fadeInDown{animation-name:fadeInDown}.fadeOutUp{animation-name:fadeOutUp}.fadeInUp{animation-name:fadeInUp}.pop{animation-name:pop}.page{width:100%;min-width:32rem;top:0;left:0;right:0;bottom:0;position:absolute;overflow:auto}.page .panel{height:100%;width:100%;position:absolute;overflow:auto}.page .panel.main{bottom:3.8rem;height:auto;padding:0}.page .panel.main .grid{padding-bottom:1.6rem}.page .panel.main .row{padding-left:0}.page .panel.main .panel.left{background:#fff}.page .header.second+.panel.main{top:6.875em}.page .header.third+.panel.main{top:10em;border-top:1px solid #ddd}.page .header{width:100%;overflow:hidden}.page .header.first{min-height:3.2rem}.page .header.second{height:7rem}.page .header.third{height:4.8rem}.page .col1,.page .col2,.page .col2_3,.page .col3{position:absolute}.page .col1,.page .panel.left{width:17.25%;box-sizing:border-box}.page .panel.middle{width:82%;left:18%;overflow:hidden}.page .panel.middle.scroll{overflow-y:scroll}.page .col2{width:70%;left:18%}.page .col3,.page .panel.right{width:24%;left:75%}.page .col2_3{width:81.5%;left:18%}.page .panel.main .grid-responsive-12{float:left;width:100%;max-width:none;padding-left:.25em;box-sizing:border-box}@media all and (max-width:1024px){.page .panel.left{display:none}.page .header.second .col2{display:none}.page .header.second .col2_3{display:none}.page .header.third{height:4.5em}.page .header.third .col1,.page .header.third .col2,.page .header.third .col2_3{position:relative;float:left;left:auto;width:auto}.page .header.third .col3{display:none}.page .panel.main .panel.left{display:none}.page .panel.main .panel.middle{width:100%;left:0}.page .panel.main .row{padding-left:.5em}.page .panel.main .panel.aside{min-width:auto}}.grid,.grid-responsive-12{margin:0 auto;padding:0;max-width:1220px}.grid .row,.grid-responsive-12 .row{padding:0 .5em 0 .5em}.grid .row:after,.grid .row:before,.grid-responsive-12 .row:after,.grid-responsive-12 .row:before{content:"";display:table}.grid .row:after,.grid-responsive-12 .row:after{clear:both}.grid .row:after,.grid .row:before,.grid-responsive-12 .row:after,.grid-responsive-12 .row:before{content:"";display:table}.grid .row:after,.grid-responsive-12 .row:after{clear:both}.grid .row .col1,.grid .row .col10,.grid .row .col11,.grid .row .col12,.grid .row .col2,.grid .row .col3,.grid .row .col4,.grid .row .col5,.grid .row .col6,.grid .row .col7,.grid .row .col8,.grid .row .col9,.grid-responsive-12 .row .col1,.grid-responsive-12 .row .col10,.grid-responsive-12 .row .col11,.grid-responsive-12 .row .col12,.grid-responsive-12 .row .col2,.grid-responsive-12 .row .col3,.grid-responsive-12 .row .col4,.grid-responsive-12 .row .col5,.grid-responsive-12 .row .col6,.grid-responsive-12 .row .col7,.grid-responsive-12 .row .col8,.grid-responsive-12 .row .col9{position:relative;left:auto;float:none;width:99%;box-sizing:border-box}.grid .row .col10:after,.grid .row .col10:before,.grid .row .col11:after,.grid .row .col11:before,.grid .row .col12:after,.grid .row .col12:before,.grid .row .col1:after,.grid .row .col1:before,.grid .row .col2:after,.grid .row .col2:before,.grid .row .col3:after,.grid .row .col3:before,.grid .row .col4:after,.grid .row .col4:before,.grid .row .col5:after,.grid .row .col5:before,.grid .row .col6:after,.grid .row .col6:before,.grid .row .col7:after,.grid .row .col7:before,.grid .row .col8:after,.grid .row .col8:before,.grid .row .col9:after,.grid .row .col9:before,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col10:before,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col11:before,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col12:before,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col1:before,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col2:before,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col3:before,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col4:before,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col5:before,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col6:before,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col7:before,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col8:before,.grid-responsive-12 .row .col9:after,.grid-responsive-12 .row .col9:before{content:"";display:table}.grid .row .col10:after,.grid .row .col11:after,.grid .row .col12:after,.grid .row .col1:after,.grid .row .col2:after,.grid .row .col3:after,.grid .row .col4:after,.grid .row .col5:after,.grid .row .col6:after,.grid .row .col7:after,.grid .row .col8:after,.grid .row .col9:after,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col9:after{clear:both}.grid .row .col10:after,.grid .row .col10:before,.grid .row .col11:after,.grid .row .col11:before,.grid .row .col12:after,.grid .row .col12:before,.grid .row .col1:after,.grid .row .col1:before,.grid .row .col2:after,.grid .row .col2:before,.grid .row .col3:after,.grid .row .col3:before,.grid .row .col4:after,.grid .row .col4:before,.grid .row .col5:after,.grid .row .col5:before,.grid .row .col6:after,.grid .row .col6:before,.grid .row .col7:after,.grid .row .col7:before,.grid .row .col8:after,.grid .row .col8:before,.grid .row .col9:after,.grid .row .col9:before,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col10:before,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col11:before,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col12:before,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col1:before,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col2:before,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col3:before,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col4:before,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col5:before,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col6:before,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col7:before,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col8:before,.grid-responsive-12 .row .col9:after,.grid-responsive-12 .row .col9:before{content:"";display:table}.grid .row .col10:after,.grid .row .col11:after,.grid .row .col12:after,.grid .row .col1:after,.grid .row .col2:after,.grid .row .col3:after,.grid .row .col4:after,.grid .row .col5:after,.grid .row .col6:after,.grid .row .col7:after,.grid .row .col8:after,.grid .row .col9:after,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col9:after{clear:both}.grid .row .col1 img,.grid .row .col10 img,.grid .row .col11 img,.grid .row .col12 img,.grid .row .col2 img,.grid .row .col3 img,.grid .row .col4 img,.grid .row .col5 img,.grid .row .col6 img,.grid .row .col7 img,.grid .row .col8 img,.grid .row .col9 img,.grid-responsive-12 .row .col1 img,.grid-responsive-12 .row .col10 img,.grid-responsive-12 .row .col11 img,.grid-responsive-12 .row .col12 img,.grid-responsive-12 .row .col2 img,.grid-responsive-12 .row .col3 img,.grid-responsive-12 .row .col4 img,.grid-responsive-12 .row .col5 img,.grid-responsive-12 .row .col6 img,.grid-responsive-12 .row .col7 img,.grid-responsive-12 .row .col8 img,.grid-responsive-12 .row .col9 img{width:100%;height:auto;display:block}@media all and (min-width:780px){.grid .row{padding:0 .5em 0 .5em}.grid .row .col1,.grid .row .col10,.grid .row .col11,.grid .row .col12,.grid .row .col2,.grid .row .col3,.grid .row .col4,.grid .row .col5,.grid .row .col6,.grid .row .col7,.grid .row .col8,.grid .row .col9{float:left;position:relative;margin:0 3% 0 0}.grid .row .last{margin-right:0}.grid .row .col1{width:5.5%}.grid .row .col2{width:14%}.grid .row .col3{width:22.5%}.grid .row .col4{width:31%}.grid .row .col5{width:39.5%}.grid .row .col6{width:48%}.grid .row .col7{width:56.5%}.grid .row .col8{width:65%}.grid .row .col9{width:73.5%}.grid .row .col10{width:82%}.grid .row .col11{width:90.5%}.grid .row .col12{width:99%;margin:0}.grid .row .push1{margin-left:5.5%}.grid .row .push2{margin-left:14%}.grid .row .push3{margin-left:22.5%}.grid .row .push4{margin-left:31%}}@media all and (max-width:768px){.hidden-xs{display:none}}.tooltip,[data-tooltip]{position:relative;cursor:pointer}.tooltip:after,.tooltip:before,[data-tooltip]:after,[data-tooltip]:before{position:absolute;visibility:hidden;opacity:0;transition:opacity .2s ease-in-out,visibility .2s ease-in-out,transform .2s cubic-bezier(.71, 1.7, .77, 1.24);transform:translate3d(0,0,0);pointer-events:none;text-align:center}.always-show:after,.always-show:before,.tooltip:focus:after,.tooltip:focus:before,.tooltip:hover:after,.tooltip:hover:before,[data-tooltip]:focus:after,[data-tooltip]:focus:before,[data-tooltip]:hover:after,[data-tooltip]:hover:before{visibility:visible;opacity:1}.tooltip:before,[data-tooltip]:before{z-index:990;border:.6rem solid transparent;background:0 0;content:""}.tooltip:after,[data-tooltip]:after{z-index:990;padding:.4rem;width:16rem;background-color:hsla(0,0%,0%,.9);color:#fff;content:attr(data-tooltip);font-size:1.4rem;line-height:2.4rem;font-weight:400;text-shadow:none}.tooltip-top:after,.tooltip-top:before,.tooltip:after,.tooltip:before,[data-tooltip]:after,[data-tooltip]:before{bottom:100%;left:50%}.tooltip-top:before,.tooltip:before,[data-tooltip]:before{margin-left:-.6rem;margin-bottom:-1.2rem;border-top-color:hsla(0,0%,20%,.9)}.tooltip-top:after,.tooltip:after,[data-tooltip]:after{margin-left:-8rem}.tooltip-top.always-show:after,.tooltip-top.always-show:before,.tooltip-top:focus:after,.tooltip-top:focus:before,.tooltip-top:hover:after,.tooltip-top:hover:before,.tooltip.always-show:after,.tooltip.always-show:before,.tooltip:focus:after,.tooltip:focus:before,.tooltip:hover:after,.tooltip:hover:before,[data-tooltip]:focus:after,[data-tooltip]:focus:before,[data-tooltip]:hover:after,[data-tooltip]:hover:before{transform:translateY(-1.2rem)}.tooltip-left:after,.tooltip-left:before{right:100%;bottom:50%;left:auto}.tooltip-left:before{margin-left:0;margin-right:-1.2rem;margin-bottom:0;border-top-color:transparent;border-left-color:hsla(0,0%,20%,.9)}.tooltip-left.always-show:after,.tooltip-left.always-show:before,.tooltip-left:focus:after,.tooltip-left:focus:before,.tooltip-left:hover:after,.tooltip-left:hover:before{transform:translateX(-1.2rem)}.tooltip-bottom:after,.tooltip-bottom:before{top:100%;bottom:auto;left:50%}.tooltip-bottom:before{margin-top:-1.2rem;margin-bottom:0;border-top-color:transparent;border-bottom-color:hsla(0,0%,20%,.9)}.tooltip-bottom.always-show:after,.tooltip-bottom.always-show:before,.tooltip-bottom:focus:after,.tooltip-bottom:focus:before,.tooltip-bottom:hover:after,.tooltip-bottom:hover:before{transform:translateY(1.2rem)}.tooltip-right:after,.tooltip-right:before{bottom:50%;left:100%}.tooltip-right:before{margin-bottom:0;margin-left:-1.2rem;border-top-color:transparent;border-right-color:#333}.tooltip-right.always-show:after,.tooltip-right.always-show:before,.tooltip-right:focus:after,.tooltip-right:focus:before,.tooltip-right:hover:after,.tooltip-right:hover:before{transform:translateX(1.2rem)}.tooltip-left:before,.tooltip-right:before{top:.3rem}.tooltip-left:after,.tooltip-right:after{margin-left:0;margin-bottom:-1.6rem}.tooltip.large:after,[data-tooltip].large:after{width:240rem}.tooltip-left.large:after,.tooltip-left.large:before,.tooltip-right.large:after,.tooltip-right.large:before{margin-top:.6rem}.tooltip-alt{cursor:pointer;display:inline-block;border-bottom:0}.tooltip-alt .tooltip-text{position:absolute;visibility:hidden;width:180px;background-color:#333;color:#fff;padding:.5em 1em;transition:opacity .2s ease-in-out,visibility .2s ease-in-out,transform .2s cubic-bezier(.71, 1.7, .77, 1.24);transform:translate3d(0,0,0);z-index:99999}.tooltip-alt .tooltip-text.right{margin-left:1.2rem;margin-top:-2.65rem}.tooltip-alt .tooltip-text.right::after{content:" ";position:absolute;top:1.125em;right:100%;margin-top:-5px;border:5px #333 solid;border-color:transparent #333 transparent transparent}.tooltip-alt:hover .tooltip-text{transform:translateX(12px);visibility:visible;opacity:1}.tooltip svg,.tooltip-alt svg{fill:#666;top:.125em;position:relative}.openpgp-key textarea{height:12rem}.enter-passphrase .password:after,.enter-passphrase .password:before{content:"";display:table}.enter-passphrase .password:after{clear:both}.enter-passphrase .password:after,.enter-passphrase .password:before{content:"";display:table}.enter-passphrase .password:after{clear:both}.enter-passphrase .password input[type=password],.enter-passphrase .password input[type=text]{float:left;box-sizing:border-box;width:calc(100% - 4.2rem)}.enter-passphrase .password .password-view{width:4.2rem;float:left;margin-top:0;margin-right:0;margin-left:-.1rem;height:3.8rem;border-radius:0;padding:.6rem .8rem .6rem 1rem;background:#fff;border-left:0;border-top:1px solid #bbb}.enter-passphrase .password .password-view.selected{background:#eee;box-shadow:inset 0 1px 2px rgba(0,0,0,.2)}.enter-passphrase .password .password-view svg{margin-top:.2rem}.enter-passphrase .password input[type=password]:focus~.password-view,.enter-passphrase .password input[type=text]:focus~.password-view{border:1px solid #2894df;border-left:0}.enter-passphrase .password.with-token input[type=password],.enter-passphrase .password.with-token input[type=text]{width:calc(100% - 11.7rem)}.enter-passphrase .password.with-token .password-view{margin-right:.8rem}.enter-passphrase .password.with-token .security-token{width:6rem;float:left;margin-top:0;margin-right:0;border-radius:0;padding:.7rem 0;text-align:center;background:#fff;border:1px solid #bbb}.password-complexity .complexity-text{float:right;clear:right;width:30%;font-size:1rem;text-align:left;padding-left:0}.password-complexity.not_available .complexity-text{color:#ddd}.password-complexity .progress{width:100%;box-sizing:border-box;border:1px solid #ddd;height:10px;display:block;clear:both;margin:.25em 0 .5em 0;float:left}.password-complexity .progress-bar{background:#000;width:0;height:6px;display:block;float:left;margin:1px}.password-complexity .progress-bar.very-weak{background:#000;width:5%}.password-complexity .progress-bar.weak{background:#d40101;width:10%}.password-complexity .progress-bar.fair{background:#ffbd2e;width:60%}.password-complexity .progress-bar.strong{background:#6c0;width:80%}.password-complexity .progress-bar.very-strong{background:#090;width:99.5%}.password-hints{margin:.5em 0 1em 0}.password-hints li{font-size:1.5rem;line-height:2.4rem}.password-hints li:before{content:"\25CF";color:#ddd;padding-right:.5em}.password-hints li.success:before{color:#090}.password-hints li.error:before{color:#d40101}.dialog-wrapper{position:absolute;width:100%;height:100%;z-index:800;background:rgba(255,255,255,.9);overflow:auto}.dialog{position:relative;max-width:48rem;border:1px solid #ddd;background:#f3f3f3;margin:auto;margin-top:1%;margin-bottom:4.8rem;box-shadow:0 0 10px 0 #ddd}.dialog .dialog-header{padding:.8rem 1.6rem 0 1.6rem;height:4.8rem}.dialog .dialog-header h2{margin:.8rem 0 1rem .4rem;font-size:1.8rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.dialog .dialog-header .dialog-header-subtitle{padding-top:.4rem;padding-left:.8rem;font-size:1.4rem;color:#333}.dialog .dialog-header .tooltip-alt{margin-left:.8rem;font-size:1.2rem;font-weight:400;color:#333;line-height:1em}.dialog .dialog-header .tooltip-alt .tooltip-text{white-space:normal}.dialog .dialog-header .dialog-close{margin-top:-3.6rem}.dialog .form-content{background:#fff;padding:1rem 2rem 2.4rem 2rem}.dialog .error-message{margin-bottom:1rem}.dialog p{margin-top:.8rem;font-size:1.5rem}.dialog p+.checkbox{padding-bottom:.8rem}.dialog p+.checkbox label{font-size:1.6rem}.dialog label{clear:both;font-size:1.5rem}.dialog input[type=text],.dialog textarea{width:100%;box-sizing:border-box}.dialog input[type=text]:after,.dialog input[type=text]:before,.dialog textarea:after,.dialog textarea:before{content:"";display:table}.dialog input[type=text]:after,.dialog textarea:after{clear:both}.dialog input[type=text]:after,.dialog input[type=text]:before,.dialog textarea:after,.dialog textarea:before{content:"";display:table}.dialog input[type=text]:after,.dialog textarea:after{clear:both}.dialog input+.message,.dialog textarea+.message{display:none}.dialog input+.message.error,.dialog textarea+.message.error{display:block;clear:both;width:100%}.dialog .inline-error{color:#d40101;font-weight:700}.dialog .accordion-header a{display:inline}.dialog .submit-wrapper{margin:0;clear:both;width:100%;padding:1.2rem 0}.dialog .submit-wrapper .button,.dialog .submit-wrapper .cancel{float:right;margin-right:1.6rem}.dialog .submit-wrapper .button{box-sizing:border-box;min-width:8rem}.dialog .submit-wrapper .primary{font-size:1.8rem;padding:1.2rem 2.4rem}.dialog .submit-wrapper .cancel{margin-top:.7em;font-size:1.6rem}.dialog-close,.dialog-close:hover{display:block;float:right;border:0}.dialog-close:active{-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem;box-shadow:inset 0 1px 2px rgba(0,0,0,.2)}.dialog-close .fa-close{padding-top:15px;width:30px;height:15px;text-align:center;display:block;vertical-align:baseline;line-height:0;border:1px solid #eee;-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem}.dialog-close .svg-icon{padding:7px 0 7px 0;width:30px;height:15px;text-align:center;display:block;vertical-align:baseline;line-height:15px;border:1px solid #eee;-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem}.dialog-close:hover .fa-close,.dialog-close:hover .svg-icon{border:1px solid #ddd;-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem}@media all and (max-width:480px){.dialog{margin:0;border:0;box-shadow:none;width:100%;max-width:100%;margin-bottom:2.5em}}.footer{margin-top:0;position:fixed;bottom:0;text-align:right;font-size:1.2rem;width:100%;height:3.8rem;background:#fff;border-top:1px solid #ddd;z-index:890}.footer .footer-links{padding-top:1rem;width:100%}.footer .footer-links li{display:inline;margin-right:1.5em}.footer .footer-links li.error-message a{background-color:#fff;color:#d40101}.footer .footer-links li .github-star{display:inline;position:absolute;margin-left:-8em;margin-top:-1px}.footer .footer-links a:not(.gh-btn):not(.gh-count){border:0}.avatar img{width:36px;height:36px;border-radius:50%}.big.avatar img{width:72px;height:72px;border-radius:50%}iframe{margin:0;padding:0;border:0;outline:0;font-size:100%;vertical-align:baseline;background:0 0}iframe.full-screen{position:absolute;width:100%;height:100%;z-index:999;border:0;top:0;left:0}iframe.cachette{position:absolute;width:1px;height:1px;z-index:999;border:0;bottom:0;right:0}#user-locale-input{border:none;background:#f3f3f3 url('../../../img/controls/chevron-down_black.svg') 92% center/1rem no-repeat;appearance:none;margin-top:.8rem;padding-left:.4rem;padding-right:2rem;line-height:2rem;width:auto}#user-locale-input:focus{outline:0;border:1px solid #2894df;border-radius:0}#user-locale-input option{background-color:#f3f3f3}body,html{height:100%}.login.page h1{margin-top:0;font-size:2.4rem;color:#666}.login.page p{font-size:1.6rem;line-height:2.4rem}.login.page .processing-wrapper{display:block;width:16rem;height:16rem;margin:auto}.login.page .processing-wrapper .processing{display:block;text-align:center}.login.page .processing-wrapper .processing:after{display:block;width:16rem;height:16rem;content:" ";background:transparent url('../../../img/controls/loading_light.svg') center center no-repeat;background-size:auto 40%}.login.page .login-form{min-height:16rem}.login.page .login-form .form-actions{text-align:center;padding-top:1.2em}.login.page .login-form .button+a{font-size:1.6rem;line-height:2.4rem;text-align:center;display:inline-block;margin-top:1.6rem;cursor:pointer}.login.page .email-sent-instructions{text-align:center}.login.page .email-sent-instructions .email-sent-bg{background:transparent url('../../../img/illustrations/email.png') top center no-repeat;background-size:auto 90%;height:16rem}.login.page .email-sent-instructions h1{margin-top:2.4rem}.login.page .email-sent-instructions p{padding:.8rem .8rem 0 .8rem;margin-bottom:0}.login.page .choose-security-token .input-security-token{margin:1em 0 1.5em 0}.login.page .choose-security-token .input-security-token:after,.login.page .choose-security-token .input-security-token:before{content:"";display:table}.login.page .choose-security-token .input-security-token:after{clear:both}.login.page .choose-security-token .input-security-token:after,.login.page .choose-security-token .input-security-token:before{content:"";display:table}.login.page .choose-security-token .input-security-token:after{clear:both}.login.page .choose-security-token .input-security-token label{margin-bottom:.8rem}.login.page .choose-security-token .input-security-token .input.text{-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem;font-size:3rem;max-width:10rem;float:left;text-align:center;margin-right:3rem}.login.page .choose-security-token .input-security-token .circle-picker{float:left}.login.page .choose-security-token .input-security-token .randomize-button-wrapper{float:left;width:10rem;text-align:center;clear:both;cursor:pointer}.login.page .install-extension a.browser-webstore{border:0}.login.page .install-extension a.browser-webstore img{display:block;margin-left:auto;margin-right:auto;max-width:26rem}.login.page .install-extension a.browser-webstore.edge img,.login.page .install-extension a.browser-webstore.firefox img{padding:1.6rem 0}.login.page .introduce-setup-extension .animated-setup-introduction.chrome{background:transparent url('../../../img/illustrations/pin_passbolt.gif') center center no-repeat;background-size:contain;height:25rem}.login.page .introduce-setup-extension .arrow{background:transparent url('../../../img/illustrations/wave-pin_my_extension.svg') center top no-repeat;width:10rem;height:10rem;position:absolute;top:0;right:calc(3.75rem - calc(100vw - 100%))}.login.page .browser-not-supported a.browser{border:0}.login.page .browser-not-supported a.browser img{max-width:26rem;display:block;margin-left:auto;margin-right:auto}.login.page .login .login-user{width:100%;margin:auto}.login.page .login .login-user>*{text-align:center}.login.page .login .login-user .login-user-name{font-weight:700;font-size:1.6rem;line-height:1rem;margin-top:1.6rem}.login.page .login .login-user .login-user-email{font-size:1.6rem;line-height:1rem}.login.page .login-processing{display:flex;flex-direction:column;align-items:center;justify-content:center}@media only screen and (min-width:42rem){body{background:#f3f3f3}.login.page{display:grid;height:calc(100% - 4rem);grid-template-columns:1fr 1.5fr 1fr;grid-template-rows:0 1fr 0.05fr;grid-gap:1px;grid-template-areas:". . ." ". login-form ." "footer footer footer"}.login.page .content{grid-area:login-form}.login.page .content .loading-bar{display:block}.login.page .content .logo{margin:1.6em auto;width:20rem;background-size:20rem auto}.login.page .content .login-form{box-shadow:0 0 10px 0 #ddd;border-radius:.3rem;max-width:37.2rem;margin:auto;padding:4.8rem 4rem 5.6rem 4rem;background:#fff}.login.page .content .input.select.locale{max-width:45.2rem;margin:auto}} \ No newline at end of file diff --git a/webroot/css/themes/midgar/api_authentication.min.css b/webroot/css/themes/midgar/api_authentication.min.css new file mode 100644 index 0000000000..45ed6a203d --- /dev/null +++ b/webroot/css/themes/midgar/api_authentication.min.css @@ -0,0 +1,9 @@ +/**! + * @name passbolt-styleguide + * @version v3.4.0 + * @date 2021-12-01 + * @copyright Copyright 2021 Passbolt SA + * @source https://github.com/passbolt/passbolt_styleguide + * @license AGPL-3.0 + */ +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}div{outline:0}html body .hidden{display:none}.visually-hidden,.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visually-hidden .focusable:active,.visually-hidden .focusable:focus,.visuallyhidden .focusable:active,.visuallyhidden .focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.clearfix:after,.clearfix:before{content:"";display:table}.clearfix:after{clear:both}.rounded{-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem}.ellipsis{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.rotate-right{transform:rotate(-90deg)}html{line-height:normal}body{color:#fffffc;background:#30302d}body.iframe{background:0 0}@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;src:local('Open Sans'),local('OpenSans'),url('../../../fonts/opensans-regular.woff') format('woff')}@font-face{font-family:'Open Sans';font-style:normal;font-weight:700;src:local('Open Sans Bold'),local('OpenSans-Bold'),url('../../../fonts/opensans-bold.woff') format('woff')}body{font-family:'Open Sans',Verdana,sans-serif}.code{font-family:'Courier New',Courier,monospace}html{font-size:62.5%}body{font-size:1.6rem}h1{font-size:2.2rem;line-height:3.2rem}h2{font-size:1.8rem;line-height:2.4rem}h3{font-size:1.6rem;line-height:2.4rem;border-bottom:1px dotted #0c0c0a}p{font-size:1.5rem;line-height:2.2rem;margin-top:0}code{font-size:1.1rem}.font-dim{color:#cacac9}a{outline:0;text-decoration:none;cursor:pointer;border-bottom:1px solid #0c0c0a}a:link,a:visited{color:#fffffc}a:hover{text-decoration:none;cursor:pointer;color:#2894df;border-bottom:1px solid #2894df}a:active,a:focus{outline:0;color:#2894df;border:0}a.no-border,a:hover.no-border{border-bottom:0}a.disabled{outline:0;text-decoration:none;cursor:default;color:#606060;pointer-events:none}ul{padding:0;margin:0}ul li{list-style:none;padding:0;margin:0}.button,button{font-size:1.4rem;padding:.8rem 1.6rem;line-height:1.6rem;margin-right:.8rem;position:relative;display:inline-block;vertical-align:baseline;outline:0;cursor:pointer;text-align:center;text-decoration:none;border-top:1px solid #222220;border-right:1px solid #222220;border-bottom:1px solid #222220;border-left:1px solid #222220;color:#fffffc;font-weight:400;text-shadow:0 1px 0 #000;background:#444442;background-image:linear-gradient(top,#444442,#444442);-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem;box-sizing:border-box}.button.medium{font-size:1.6rem;line-height:2.4rem;padding:.8rem 2.4rem}.button.big{font-size:1.8rem;line-height:3.2rem;padding:.8rem 3.2rem}.button.full-width{width:100%}.button.primary{background:#2894df;border:1px solid #2894df;color:#fff;text-shadow:0 1px 0 rgba(0,0,0,.2)}.button.primary:hover{background:#2a9ceb;border:1px solid #2a9ceb;color:#fff}.button.primary:active,.button.primary:focus{color:#eeeeec;border:1px solid #4271b7;background:#2a9ceb}.button.warning,.button.warning:active,.button.warning:focus,.button.warning:hover{color:#eeeeec;background:#d40101;border:1px solid #d40101;text-shadow:none}.button.warning:active,.button.warning:focus{border:1px solid #92000c}.button.cancel,.button.cancel:active,.button.cancel:focus,.button.cancel:hover,.button.dim,.button.dim:active,.button.dim:focus,.button.dim:hover{background:#30302d;font-weight:400}.button:hover{color:#fffffc;text-decoration:none;background:#444442;background-image:linear-gradient(top,#606060,#606060);border:1px solid #30302d}.button:focus{color:#2894df;border:1px solid #2894df}.button:active{color:#2894df;border:1px solid #2894df;background:#3b3b39;position:relative;box-shadow:inset 0 1px 2px rgba(0,0,0,.2)}.button.disabled{cursor:default;color:#8b8b89;background:#3b3b39;border:1px solid #0c0c0a;text-shadow:none;font-weight:400}.button.disabled:active,.button.disabled:focus,.button.disabled:hover{box-shadow:0 0 0;top:0;color:#8b8b89;background:#3b3b39;border:1px solid #0c0c0a}.button.processing{background:#30302d;border:1px solid #30302d}.button.processing:after{width:100%;height:100%;position:absolute;content:" ";top:-1px;left:-1px;background:#30302d url('../../../img/controls/loading_dark.svg') center center no-repeat;background-size:auto 50%;border:1px solid #0c0c0a;-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem}input[type=submit].button.processing{font-size:0;background:#30302d url('../../../img/controls/loading_dark.svg') center center no-repeat;background-size:auto 50%;border:1px solid #0c0c0a;border-radius:5px}.button-toggle.selected{background:#222220;box-shadow:inset 0 1px 2px rgba(0,0,0,.2);border:1px solid #0c0c0a}.button-group{display:flex;flex-wrap:wrap;justify-content:flex-start}.button-group--nowrap>.button{white-space:nowrap}.button-group>*{flex-grow:1;margin-bottom:.5em}label{font-weight:700;font-size:1.6rem;line-height:2.4rem;padding:.8rem 0;display:block}.required label:after{content:" \002A";color:#ff6b70;font-weight:700}.input.error label{color:#ff6b70}input[type=email],input[type=number],input[type=password],input[type=search],input[type=text],textarea{box-sizing:border-box;font-size:1.6rem;line-height:2.4rem;color:#fffffc;background:#0c0c0a;width:100%;max-width:64rem;padding:.6rem 1.2rem;display:inline-block;margin-bottom:.8rem;border:1px solid #222220;border-top:1px solid #30302d;vertical-align:middle}::-ms-reveal{display:none}input[type=number]{box-sizing:border-box}input[type=file]{box-sizing:border-box;font-size:1.6rem;line-height:2.4rem;color:#fffffc;background:#0c0c0a;width:100%;max-width:64rem;display:inline-block;margin-bottom:.8rem;vertical-align:middle}input[type=email]:hover,input[type=number]:hover,input[type=password]:hover,input[type=search]:hover,input[type=text]:hover,textarea:hover{border:1px solid #30302d}input[type=email]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=text]:focus,textarea:focus{outline:0;box-shadow:inset 1px 1px 2px rgba(0,0,0,.2);border:1px solid #2894df}input[type=email]:disabled,input[type=number]:disabled,input[type=password]:disabled,input[type=search]:disabled,input[type=text]:disabled,textarea:disabled{color:#fffffc;border:1px solid #0c0c0a;box-shadow:0 0;cursor:default;background:#444442}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{opacity:1}textarea{height:9rem}.textarea.large textarea{width:30rem;height:9rem}.checkbox input[type=checkbox]{position:absolute;opacity:0;cursor:pointer}.checkbox input[type=checkbox].error{color:#d40101}.checkbox input[type=checkbox]+label{font-weight:400;position:relative;cursor:pointer;padding:0;margin-bottom:.8rem}.checkbox input[type=checkbox]+label:before{content:'';margin-top:.4rem;margin-right:1rem;display:inline-block;vertical-align:text-top;width:1.4rem;height:1.4rem;background:#222220;border:1px solid #606060;border-radius:.3rem;box-sizing:border-box}.checkbox input[type=checkbox]:hover+label:before{box-shadow:0 .1rem 0 #000,inset 0 .1rem 0 rgba(255,255,255,.25)}.checkbox input[type=checkbox]:focus+label:before{box-shadow:0 0 .4rem #4271b7;border:1px solid #4271b7}.checkbox input[type=checkbox]:active+label:before{box-shadow:inset 0 -.1rem 0 rgba(255,255,255,.25),inset 0 .1rem 0 #000}.checkbox input[type=checkbox]:disabled+label{color:#0c0c0a;cursor:auto}.checkbox input[type=checkbox]:disabled+label:before{box-shadow:none;background:#000;border:none}.checkbox input[type=checkbox]:checked+label:after{content:'';position:absolute;left:.2rem;top:.75rem;width:1rem;height:1rem;background-size:1rem 1rem;background-image:url('../../../img/controls/check_white.svg');-webkit-mask-image:url('../../../img/controls/check_white.svg');mask-image:url('../../../img/controls/check_white.svg')}.checkbox input[type=checkbox]:disabled+label:after{background:#30302d}.checkbox.medium input[type=checkbox]{cursor:pointer}.checkbox.medium input[type=checkbox]+label{font-size:1.5rem}.checkbox.medium input[type=checkbox]+label:before{margin-top:.3rem;margin-right:1rem;width:1.4rem;height:1.4rem}.checkbox.medium input[type=checkbox]:checked+label:after{left:.2rem;top:.7rem;width:1rem;height:1rem;background-size:1rem 1rem}.checkbox.small input[type=checkbox]{cursor:pointer}.checkbox.small input[type=checkbox]+label{font-size:1.4rem}.checkbox.small input[type=checkbox]+label:before{margin-top:.3rem;margin-right:1rem;width:1.4rem;height:1.4rem}.checkbox.small input[type=checkbox]:checked+label:after{left:.2rem;top:.7rem;width:1rem;height:1rem;background-size:1rem 1rem}.radiolist{margin-bottom:.8rem}.radiolist:after,.radiolist:before{content:"";display:table}.radiolist:after{clear:both}.radiolist:after,.radiolist:before{content:"";display:table}.radiolist:after{clear:both}.radiolist .input.radio{float:left}.radiolist .input.radio input{float:left;margin-top:.8rem}.radiolist .input.radio input+label{float:left;font-weight:400;display:inline-block;margin-left:1rem;font-size:1.6rem;margin-right:3.2rem;line-height:1.4rem}.radiolist .input.radio input+label:after{content:initial}.input.toggle-switch{display:flex;padding-top:1.6rem;padding-bottom:.8rem}.input.toggle-switch label{padding-top:0;padding-left:1.6rem;order:2;flex:1;font-weight:400;font-size:1.6rem}.input.toggle-switch.disabled label{color:#8b8b89}.input.toggle-switch .toggle-switch-checkbox{order:-1;flex:0 0 3em;display:none}.input.toggle-switch .toggle-switch-checkbox,.input.toggle-switch .toggle-switch-checkbox *,.input.toggle-switch .toggle-switch-checkbox :after,.input.toggle-switch .toggle-switch-checkbox :before,.input.toggle-switch .toggle-switch-checkbox+.toggle-switch-button,.input.toggle-switch .toggle-switch-checkbox:after,.input.toggle-switch .toggle-switch-checkbox:before{box-sizing:border-box}.input.toggle-switch .toggle-switch-checkbox ::selection,.input.toggle-switch .toggle-switch-checkbox :after::selection,.input.toggle-switch .toggle-switch-checkbox :before::selection,.input.toggle-switch .toggle-switch-checkbox+.toggle-switch-button::selection,.input.toggle-switch .toggle-switch-checkbox::selection,.input.toggle-switch .toggle-switch-checkbox:after::selection,.input.toggle-switch .toggle-switch-checkbox:before::selection{background:0 0}.input.toggle-switch .toggle-switch-button{order:-1;flex:none;outline:0;display:block;width:3em;height:1.5em;position:relative;cursor:pointer;user-select:none;background:#444442;border-radius:2em;padding:2px;transition:all .4s ease;border:1px solid #0c0c0a}.input.toggle-switch .toggle-switch-button span{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.input.toggle-switch .toggle-switch-button span .focusable:active,.input.toggle-switch .toggle-switch-button span .focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.input.toggle-switch .toggle-switch-button span .focusable:active,.input.toggle-switch .toggle-switch-button span .focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.input.toggle-switch .toggle-switch-button:hover:after{will-change:padding}.input.toggle-switch .toggle-switch-button:active{box-shadow:inset 0 0 0 2em #e8eae9}.input.toggle-switch .toggle-switch-button:active:after{padding-right:.8em}.input.toggle-switch .toggle-switch-button:after,.input.toggle-switch .toggle-switch-button:before{position:relative;display:block;content:"";width:50%;height:100%}.input.toggle-switch .toggle-switch-button:after{left:0;border-radius:2em;background:#fff;transition:left .3s cubic-bezier(.175, .885, .32, 1.275),padding .3s ease,margin .3s ease;box-shadow:0 0 0 1px rgba(0,0,0,.1),0 4px 0 rgba(0,0,0,.08)}.input.toggle-switch .toggle-switch-button:before{display:none}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button:after{left:50%}.input.toggle-switch .toggle-switch-checkbox:disabled+.toggle-switch-button{background:#30302d;cursor:not-allowed}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button{background:#6c0}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button:active{box-shadow:none}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button:active:after{margin-left:-.8em}.input.toggle-switch .toggle-switch-checkbox:disabled:checked+.toggle-switch-button{background:#e6ffcc}h1 .input.toggle-switch,h2 .input.toggle-switch,h3 .input.toggle-switch,h4 .input.toggle-switch,h5 .input.toggle-switch,h6 .input.toggle-switch{float:left;padding:.4em 1em 0 0}.input.select select{display:inline-block;font-size:1.6rem;line-height:2.4rem;color:#fffffc;padding:.6rem 1.2rem;width:100%;max-width:64rem;box-sizing:border-box;margin:0 0 .8rem 0;border:1px solid #222220;-moz-appearance:none;-webkit-appearance:none;appearance:none;background-color:#30302d;background-image:url('../../../img/controls/chevron-down_white.svg'),linear-gradient(to bottom,#0c0c0a 0,#0c0c0a 100%);background-repeat:no-repeat,repeat;background-position:right .6rem top 50%,0 0;background-size:1rem auto,100%}.input.select select.medium{width:50%}.input.select select:focus{outline:0;border:1px solid #2894df;border-radius:0;background-image:url('../../../img/controls/chevron-down_blue.svg'),linear-gradient(to bottom,#0c0c0a 0,#0c0c0a 100%)}.input.select select:disabled{background-image:url('../../../img/controls/chevron-down_white.svg'),linear-gradient(to bottom,#444442 0,#444442 100%);color:#8b8b89}.input .error-message{padding:0;font-size:1.4rem;margin-top:.2rem;margin-bottom:1.6rem;background-color:transparent;border:0;font-weight:400;color:#ff6b70;background:#30302d;clear:both}.input .help-message{padding:0;font-size:1.4rem;margin-top:.2rem;margin-bottom:1.6rem;background-color:transparent;border:0;color:#8b8b89;font-weight:400;background:#30302d;clear:both}.singleline{display:flex;max-width:64rem}.singleline:after,.singleline:before{content:"";display:table}.singleline:after{clear:both}.singleline:after,.singleline:before{content:"";display:table}.singleline:after{clear:both}.singleline .input{flex:1}.singleline .input.first-field,.singleline .input:first-child{margin-right:2%}.slider{display:flex;align-items:center}.slider input[type=range]{-webkit-appearance:none;width:100%;height:1px;border-radius:5px;background:#8b8b89;outline:0;opacity:.7;-webkit-transition:.2s;transition:opacity .2s;flex-grow:1}.slider input[type=range]:hover{opacity:1}.slider input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:15px;height:15px;border-radius:50%;border-width:1px;border-style:solid;border-color:#3b3b39;background:#8b8b89;cursor:pointer}.slider input[type=range]::-moz-range-thumb{width:15px;height:15px;border-radius:50%;background:#8b8b89;cursor:pointer}.slider input[type=number]{width:6.5rem;margin-left:1.6rem;padding:.8rem}.svg-icon{display:inline-flex;align-self:center}.svg-icon svg{fill:#FFF;height:1em;width:1em}.svg-icon.baseline svg{top:.125em;position:relative}.svg-icon.dim svg,.svg-icon.light svg{fill:#EEEEEC}.svg-icon.icon-only svg{padding:.1rem;height:1.6rem;width:1.6rem}a:hover .svg-icon svg{fill:#2894DF}a.disabled .svg-icon svg{fill:#8B8B89;pointer-events:none;cursor:default}a.disabled:hover .svg-icon svg{fill:#8B8B89}a.fav .svg-icon svg{fill:#D40101}a.unfav .svg-icon svg{fill:#8B8B89}.button .svg-icon svg{top:.2rem;position:relative}.button .svg-icon+span:not(.visuallyhidden){margin-left:.8rem;display:inline-block}.button .svg-icon svg:hover,.button:hover .svg-icon svg{fill:#FFFFFC}.button.primary:active .svg-icon svg,.button.primary:focus .svg-icon svg,.button.warning .svg-icon svg,.button.warning:active .svg-icon svg,.button.warning:focus .svg-icon svg,.button.warning:hover .svg-icon svg{fill:#EEEEEC}.button:active .svg-icon svg,.button:focus .svg-icon svg{fill:#2894DF}.button.disabled .svg-icon svg{fill:#8B8B89}.button.disabled .svg-icon svg:hover{fill:#8B8B89}@keyframes drawCircle{0%{stroke-dashoffset:180px}100%{stroke-dashoffset:0}}@keyframes drawCheck{0%{stroke-dashoffset:50px}100%{stroke-dashoffset:0}}@keyframes drawCross{0%{stroke-dashoffset:50px}100%{stroke-dashoffset:0}}@keyframes drawWarning{0%{stroke-dashoffset:230px}100%{stroke-dashoffset:0}}.icon-feedback .success-animation-circle{stroke-dasharray:180px 180px;stroke:#009900}.icon-feedback .success-animation-line{stroke-dasharray:50px 50px;stroke:#009900}.icon-feedback .success-animation-line2{stroke-dasharray:50px 50px;stroke:#009900}.icon-feedback .error-animation-circle{stroke-dasharray:180px 180px;stroke:#D40101}.icon-feedback .error-animation-line{stroke-dasharray:50px 50px;stroke:#D40101}.icon-feedback .warning-animation-line{stroke-dasharray:230px 230px;stroke:#9F6000;stroke-linecap:round;stroke-linejoin:round}.icon-feedback .warning-animation-circle{fill:#9F6000}.icon-feedback .animated{animation:.75s ease-out 0s 1 both pop}.icon-feedback .animated .error-animation-circle,.icon-feedback .animated .success-animation-circle,.icon-feedback .animated .warning-animation-circle{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawCircle}.icon-feedback .animated .success-animation-line{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawCheck}.icon-feedback .animated .error-animation-line{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawCross}.icon-feedback .animated .warning-animation-line{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawWarning}table{border-collapse:collapse;border-spacing:0}table td,table th{text-align:left;font-weight:400}.logo{background:transparent url('../../../img/logo/logo_white.svg') 0 0 no-repeat;background-size:20rem auto;width:20rem;height:4.5rem}.scroll{overflow-y:scroll;background:linear-gradient(#30302d 30%,rgba(255,255,255,0)),linear-gradient(rgba(255,255,255,0),#30302d 70%) 0 100%,radial-gradient(farthest-side at 75% 0,rgba(0,0,0,.2),rgba(0,0,0,0)),radial-gradient(farthest-side at 75% 100%,rgba(0,0,0,.2),rgba(0,0,0,0)) 0 100%;background-repeat:no-repeat;background-color:#30302d;background-size:100% 10px,100% 10px,100% 5px,100% 5px}::-webkit-scrollbar{width:1em}::-webkit-scrollbar-track{background:#30302d;border-top:0;border-bottom:0}::-webkit-scrollbar-thumb{background:#606060;border-radius:1em;border:4px solid #30302d}.scroll-shadow{overflow:auto;background:linear-gradient(#30302d 30%,rgba(255,255,255,0)),linear-gradient(rgba(255,255,255,0),#30302d 70%) 0 100%,radial-gradient(farthest-side at 50% 0,rgba(0,0,0,.08),rgba(0,0,0,0)),radial-gradient(farthest-side at 50% 100%,rgba(0,0,0,.08),rgba(0,0,0,0)) 0 100%;background-repeat:no-repeat;background-color:#30302d;background-size:100% 40px,100% 40px,100% 14px,100% 14px;background-attachment:local,local,scroll,scroll}.simplebar-content{overflow:auto;background:linear-gradient(#30302d 30%,rgba(255,255,255,0)),linear-gradient(rgba(255,255,255,0),#30302d 70%) 0 100%,radial-gradient(farthest-side at 50% 0,rgba(0,0,0,.08),rgba(0,0,0,0)),radial-gradient(farthest-side at 50% 100%,rgba(0,0,0,.08),rgba(0,0,0,0)) 0 100%;background-repeat:no-repeat;background-color:#30302d;background-size:100% 40px,100% 40px,100% 14px,100% 14px;background-attachment:local,local,scroll,scroll}[data-simplebar]{position:relative;flex-direction:column;flex-wrap:wrap;justify-content:flex-start;align-content:flex-start;align-items:flex-start;width:inherit;height:inherit;max-width:inherit;max-height:inherit}.simplebar-wrapper{overflow:hidden;width:inherit;height:inherit;max-width:inherit;max-height:inherit}.simplebar-mask{direction:inherit;position:absolute;overflow:hidden;padding:0;margin:0;left:0;top:0;bottom:0;right:0;width:auto!important;height:auto!important;z-index:0}.simplebar-offset{direction:inherit!important;box-sizing:inherit!important;resize:none!important;position:absolute;top:0;left:0;bottom:0;right:0;padding:0;margin:0;-webkit-overflow-scrolling:touch}.simplebar-content-wrapper{direction:inherit;box-sizing:border-box!important;position:relative;display:block;height:100%;width:auto;max-width:100%;max-height:100%;scrollbar-width:none;-ms-overflow-style:none}.simplebar-content-wrapper::-webkit-scrollbar,.simplebar-hide-scrollbar::-webkit-scrollbar{width:0;height:0}.simplebar-content:after,.simplebar-content:before{content:' ';display:table}.simplebar-placeholder{max-height:100%;max-width:100%;width:100%;pointer-events:none}.simplebar-height-auto-observer-wrapper{box-sizing:inherit!important;height:100%;width:inherit;max-width:1px;position:relative;float:left;max-height:1px;overflow:hidden;z-index:-1;padding:0;margin:0;pointer-events:none;flex-grow:inherit;flex-shrink:0;flex-basis:0}.simplebar-height-auto-observer{box-sizing:inherit;display:block;opacity:0;position:absolute;top:0;left:0;height:1000%;width:1000%;min-height:1px;min-width:1px;overflow:hidden;pointer-events:none;z-index:-1}.simplebar-track{z-index:1;position:absolute;right:0;bottom:0;pointer-events:none;overflow:hidden}[data-simplebar].simplebar-dragging .simplebar-content{pointer-events:none;user-select:none;-webkit-user-select:none}[data-simplebar].simplebar-dragging .simplebar-track{pointer-events:all}.simplebar-scrollbar{position:absolute;right:2px;width:7px;min-height:10px}.simplebar-scrollbar:before{position:absolute;content:'';background:#000;border-radius:7px;left:0;right:0;opacity:0;transition:opacity .2s linear}.simplebar-scrollbar.simplebar-visible:before{opacity:.5;transition:opacity 0s linear}.simplebar-track.simplebar-vertical{top:0;width:11px}.simplebar-track.simplebar-vertical .simplebar-scrollbar:before{top:2px;bottom:2px}.simplebar-track.simplebar-horizontal{left:0;height:11px}.simplebar-track.simplebar-horizontal .simplebar-scrollbar:before{height:100%;left:2px;right:2px}.simplebar-track.simplebar-horizontal .simplebar-scrollbar{right:auto;left:0;top:2px;height:7px;min-height:0;min-width:10px;width:auto}[data-simplebar-direction=rtl] .simplebar-track.simplebar-vertical{right:auto;left:0}.hs-dummy-scrollbar-size{direction:rtl;position:fixed;opacity:0;visibility:hidden;height:500px;width:500px;overflow-y:hidden;overflow-x:scroll}.simplebar-hide-scrollbar{position:fixed;left:0;visibility:hidden;overflow-y:scroll;scrollbar-width:none;-ms-overflow-style:none}.shimmer{display:block;position:relative;width:100%;height:100%;content:'';animation:shimmer 2s infinite;background:linear-gradient(45deg,rgba(48,48,45,0) 0,rgba(48,48,45,.1) 30%,rgba(48,48,45,.5) 50%,rgba(48,48,45,0))}@keyframes shimmer{0%{background-position:0 0}100%{background-position:1000px 0}}html{scroll-behavior:smooth}.animated{-webkit-animation-duration:.5s;animation-duration:.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both}@keyframes fadeInUp{0%{opacity:0;-webkit-transform:translateY(20px);-ms-transform:translateY(20px);transform:translateY(20px)}100%{opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}}@keyframes fadeOutUp{from{opacity:1}to{opacity:0;transform:translate3d(0,-100%,0)}}@keyframes fadeInDown{from{opacity:0;transform:translate3d(0,-100%,0)}to{opacity:1;transform:none}}@keyframes pop{0%{opacity:0;transform:scale(1)}55%{opacity:1;transform:scale(1)}65%{opacity:1;transform:scale(1.25)}75%{opacity:1;transform:scale(1)}100%{opacity:1;transform:scale(1)}}.fadeInDown{animation-name:fadeInDown}.fadeOutUp{animation-name:fadeOutUp}.fadeInUp{animation-name:fadeInUp}.pop{animation-name:pop}.page{width:100%;min-width:32rem;top:0;left:0;right:0;bottom:0;position:absolute;overflow:auto}.page .panel{height:100%;width:100%;position:absolute;overflow:auto}.page .panel.main{bottom:3.8rem;height:auto;padding:0}.page .panel.main .grid{padding-bottom:1.6rem}.page .panel.main .row{padding-left:0}.page .panel.main .panel.left{background:#353532}.page .header.second+.panel.main{top:6.875em}.page .header.third+.panel.main{top:10em;border-top:1px solid #0c0c0a}.page .header{width:100%;overflow:hidden}.page .header.first{min-height:3.2rem}.page .header.second{height:7rem}.page .header.third{height:4.8rem}.page .col1,.page .col2,.page .col2_3,.page .col3{position:absolute}.page .col1,.page .panel.left{width:17.25%;box-sizing:border-box}.page .panel.middle{width:82%;left:18%;overflow:hidden}.page .panel.middle.scroll{overflow-y:scroll}.page .col2{width:70%;left:18%}.page .col3,.page .panel.right{width:24%;left:75%}.page .col2_3{width:81.5%;left:18%}.page .panel.main .grid-responsive-12{float:left;width:100%;max-width:none;padding-left:.25em;box-sizing:border-box}@media all and (max-width:1024px){.page .panel.left{display:none}.page .header.second .col2{display:none}.page .header.second .col2_3{display:none}.page .header.third{height:4.5em}.page .header.third .col1,.page .header.third .col2,.page .header.third .col2_3{position:relative;float:left;left:auto;width:auto}.page .header.third .col3{display:none}.page .panel.main .panel.left{display:none}.page .panel.main .panel.middle{width:100%;left:0}.page .panel.main .row{padding-left:.5em}.page .panel.main .panel.aside{min-width:auto}}.grid,.grid-responsive-12{margin:0 auto;padding:0;max-width:1220px}.grid .row,.grid-responsive-12 .row{padding:0 .5em 0 .5em}.grid .row:after,.grid .row:before,.grid-responsive-12 .row:after,.grid-responsive-12 .row:before{content:"";display:table}.grid .row:after,.grid-responsive-12 .row:after{clear:both}.grid .row:after,.grid .row:before,.grid-responsive-12 .row:after,.grid-responsive-12 .row:before{content:"";display:table}.grid .row:after,.grid-responsive-12 .row:after{clear:both}.grid .row .col1,.grid .row .col10,.grid .row .col11,.grid .row .col12,.grid .row .col2,.grid .row .col3,.grid .row .col4,.grid .row .col5,.grid .row .col6,.grid .row .col7,.grid .row .col8,.grid .row .col9,.grid-responsive-12 .row .col1,.grid-responsive-12 .row .col10,.grid-responsive-12 .row .col11,.grid-responsive-12 .row .col12,.grid-responsive-12 .row .col2,.grid-responsive-12 .row .col3,.grid-responsive-12 .row .col4,.grid-responsive-12 .row .col5,.grid-responsive-12 .row .col6,.grid-responsive-12 .row .col7,.grid-responsive-12 .row .col8,.grid-responsive-12 .row .col9{position:relative;left:auto;float:none;width:99%;box-sizing:border-box}.grid .row .col10:after,.grid .row .col10:before,.grid .row .col11:after,.grid .row .col11:before,.grid .row .col12:after,.grid .row .col12:before,.grid .row .col1:after,.grid .row .col1:before,.grid .row .col2:after,.grid .row .col2:before,.grid .row .col3:after,.grid .row .col3:before,.grid .row .col4:after,.grid .row .col4:before,.grid .row .col5:after,.grid .row .col5:before,.grid .row .col6:after,.grid .row .col6:before,.grid .row .col7:after,.grid .row .col7:before,.grid .row .col8:after,.grid .row .col8:before,.grid .row .col9:after,.grid .row .col9:before,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col10:before,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col11:before,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col12:before,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col1:before,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col2:before,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col3:before,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col4:before,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col5:before,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col6:before,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col7:before,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col8:before,.grid-responsive-12 .row .col9:after,.grid-responsive-12 .row .col9:before{content:"";display:table}.grid .row .col10:after,.grid .row .col11:after,.grid .row .col12:after,.grid .row .col1:after,.grid .row .col2:after,.grid .row .col3:after,.grid .row .col4:after,.grid .row .col5:after,.grid .row .col6:after,.grid .row .col7:after,.grid .row .col8:after,.grid .row .col9:after,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col9:after{clear:both}.grid .row .col10:after,.grid .row .col10:before,.grid .row .col11:after,.grid .row .col11:before,.grid .row .col12:after,.grid .row .col12:before,.grid .row .col1:after,.grid .row .col1:before,.grid .row .col2:after,.grid .row .col2:before,.grid .row .col3:after,.grid .row .col3:before,.grid .row .col4:after,.grid .row .col4:before,.grid .row .col5:after,.grid .row .col5:before,.grid .row .col6:after,.grid .row .col6:before,.grid .row .col7:after,.grid .row .col7:before,.grid .row .col8:after,.grid .row .col8:before,.grid .row .col9:after,.grid .row .col9:before,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col10:before,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col11:before,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col12:before,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col1:before,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col2:before,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col3:before,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col4:before,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col5:before,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col6:before,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col7:before,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col8:before,.grid-responsive-12 .row .col9:after,.grid-responsive-12 .row .col9:before{content:"";display:table}.grid .row .col10:after,.grid .row .col11:after,.grid .row .col12:after,.grid .row .col1:after,.grid .row .col2:after,.grid .row .col3:after,.grid .row .col4:after,.grid .row .col5:after,.grid .row .col6:after,.grid .row .col7:after,.grid .row .col8:after,.grid .row .col9:after,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col9:after{clear:both}.grid .row .col1 img,.grid .row .col10 img,.grid .row .col11 img,.grid .row .col12 img,.grid .row .col2 img,.grid .row .col3 img,.grid .row .col4 img,.grid .row .col5 img,.grid .row .col6 img,.grid .row .col7 img,.grid .row .col8 img,.grid .row .col9 img,.grid-responsive-12 .row .col1 img,.grid-responsive-12 .row .col10 img,.grid-responsive-12 .row .col11 img,.grid-responsive-12 .row .col12 img,.grid-responsive-12 .row .col2 img,.grid-responsive-12 .row .col3 img,.grid-responsive-12 .row .col4 img,.grid-responsive-12 .row .col5 img,.grid-responsive-12 .row .col6 img,.grid-responsive-12 .row .col7 img,.grid-responsive-12 .row .col8 img,.grid-responsive-12 .row .col9 img{width:100%;height:auto;display:block}@media all and (min-width:780px){.grid .row{padding:0 .5em 0 .5em}.grid .row .col1,.grid .row .col10,.grid .row .col11,.grid .row .col12,.grid .row .col2,.grid .row .col3,.grid .row .col4,.grid .row .col5,.grid .row .col6,.grid .row .col7,.grid .row .col8,.grid .row .col9{float:left;position:relative;margin:0 3% 0 0}.grid .row .last{margin-right:0}.grid .row .col1{width:5.5%}.grid .row .col2{width:14%}.grid .row .col3{width:22.5%}.grid .row .col4{width:31%}.grid .row .col5{width:39.5%}.grid .row .col6{width:48%}.grid .row .col7{width:56.5%}.grid .row .col8{width:65%}.grid .row .col9{width:73.5%}.grid .row .col10{width:82%}.grid .row .col11{width:90.5%}.grid .row .col12{width:99%;margin:0}.grid .row .push1{margin-left:5.5%}.grid .row .push2{margin-left:14%}.grid .row .push3{margin-left:22.5%}.grid .row .push4{margin-left:31%}}@media all and (max-width:768px){.hidden-xs{display:none}}.tooltip,[data-tooltip]{position:relative;cursor:pointer}.tooltip:after,.tooltip:before,[data-tooltip]:after,[data-tooltip]:before{position:absolute;visibility:hidden;opacity:0;transition:opacity .2s ease-in-out,visibility .2s ease-in-out,transform .2s cubic-bezier(.71, 1.7, .77, 1.24);transform:translate3d(0,0,0);pointer-events:none;text-align:center}.always-show:after,.always-show:before,.tooltip:focus:after,.tooltip:focus:before,.tooltip:hover:after,.tooltip:hover:before,[data-tooltip]:focus:after,[data-tooltip]:focus:before,[data-tooltip]:hover:after,[data-tooltip]:hover:before{visibility:visible;opacity:1}.tooltip:before,[data-tooltip]:before{z-index:990;border:.6rem solid transparent;background:0 0;content:""}.tooltip:after,[data-tooltip]:after{z-index:990;padding:.4rem;width:16rem;background-color:hsla(0,0%,0%,.9);color:#eeeeec;content:attr(data-tooltip);font-size:1.4rem;line-height:2.4rem;font-weight:400;text-shadow:none}.tooltip-top:after,.tooltip-top:before,.tooltip:after,.tooltip:before,[data-tooltip]:after,[data-tooltip]:before{bottom:100%;left:50%}.tooltip-top:before,.tooltip:before,[data-tooltip]:before{margin-left:-.6rem;margin-bottom:-1.2rem;border-top-color:hsla(0,0%,20%,.9)}.tooltip-top:after,.tooltip:after,[data-tooltip]:after{margin-left:-8rem}.tooltip-top.always-show:after,.tooltip-top.always-show:before,.tooltip-top:focus:after,.tooltip-top:focus:before,.tooltip-top:hover:after,.tooltip-top:hover:before,.tooltip.always-show:after,.tooltip.always-show:before,.tooltip:focus:after,.tooltip:focus:before,.tooltip:hover:after,.tooltip:hover:before,[data-tooltip]:focus:after,[data-tooltip]:focus:before,[data-tooltip]:hover:after,[data-tooltip]:hover:before{transform:translateY(-1.2rem)}.tooltip-left:after,.tooltip-left:before{right:100%;bottom:50%;left:auto}.tooltip-left:before{margin-left:0;margin-right:-1.2rem;margin-bottom:0;border-top-color:transparent;border-left-color:hsla(0,0%,20%,.9)}.tooltip-left.always-show:after,.tooltip-left.always-show:before,.tooltip-left:focus:after,.tooltip-left:focus:before,.tooltip-left:hover:after,.tooltip-left:hover:before{transform:translateX(-1.2rem)}.tooltip-bottom:after,.tooltip-bottom:before{top:100%;bottom:auto;left:50%}.tooltip-bottom:before{margin-top:-1.2rem;margin-bottom:0;border-top-color:transparent;border-bottom-color:hsla(0,0%,20%,.9)}.tooltip-bottom.always-show:after,.tooltip-bottom.always-show:before,.tooltip-bottom:focus:after,.tooltip-bottom:focus:before,.tooltip-bottom:hover:after,.tooltip-bottom:hover:before{transform:translateY(1.2rem)}.tooltip-right:after,.tooltip-right:before{bottom:50%;left:100%}.tooltip-right:before{margin-bottom:0;margin-left:-1.2rem;border-top-color:transparent;border-right-color:#222220}.tooltip-right.always-show:after,.tooltip-right.always-show:before,.tooltip-right:focus:after,.tooltip-right:focus:before,.tooltip-right:hover:after,.tooltip-right:hover:before{transform:translateX(1.2rem)}.tooltip-left:before,.tooltip-right:before{top:.3rem}.tooltip-left:after,.tooltip-right:after{margin-left:0;margin-bottom:-1.6rem}.tooltip.large:after,[data-tooltip].large:after{width:240rem}.tooltip-left.large:after,.tooltip-left.large:before,.tooltip-right.large:after,.tooltip-right.large:before{margin-top:.6rem}.tooltip-alt{cursor:pointer;display:inline-block;border-bottom:0}.tooltip-alt .tooltip-text{position:absolute;visibility:hidden;width:180px;background-color:#222220;color:#eeeeec;padding:.5em 1em;transition:opacity .2s ease-in-out,visibility .2s ease-in-out,transform .2s cubic-bezier(.71, 1.7, .77, 1.24);transform:translate3d(0,0,0);z-index:99999}.tooltip-alt .tooltip-text.right{margin-left:1.2rem;margin-top:-2.65rem}.tooltip-alt .tooltip-text.right::after{content:" ";position:absolute;top:1.125em;right:100%;margin-top:-5px;border:5px #333 solid;border-color:transparent #222220 transparent transparent}.tooltip-alt:hover .tooltip-text{transform:translateX(12px);visibility:visible;opacity:1}.tooltip svg,.tooltip-alt svg{fill:#cacac9;top:.125em;position:relative}.openpgp-key textarea{height:12rem}.enter-passphrase .password:after,.enter-passphrase .password:before{content:"";display:table}.enter-passphrase .password:after{clear:both}.enter-passphrase .password:after,.enter-passphrase .password:before{content:"";display:table}.enter-passphrase .password:after{clear:both}.enter-passphrase .password input[type=password],.enter-passphrase .password input[type=text]{float:left;box-sizing:border-box;width:calc(100% - 4.2rem)}.enter-passphrase .password .password-view{width:4.2rem;float:left;margin-top:0;margin-right:0;margin-left:-.1rem;height:3.8rem;border-radius:0;padding:.6rem .8rem .6rem 1rem;background:#0c0c0a;border-left:0;border-top:1px solid #30302d}.enter-passphrase .password .password-view.selected{background:#222220;box-shadow:inset 0 1px 2px rgba(0,0,0,.2)}.enter-passphrase .password .password-view svg{margin-top:.2rem}.enter-passphrase .password input[type=password]:focus~.password-view,.enter-passphrase .password input[type=text]:focus~.password-view{border:1px solid #2894df;border-left:0}.enter-passphrase .password.with-token input[type=password],.enter-passphrase .password.with-token input[type=text]{width:calc(100% - 11.7rem)}.enter-passphrase .password.with-token .password-view{margin-right:.8rem}.enter-passphrase .password.with-token .security-token{width:6rem;float:left;margin-top:0;margin-right:0;border-radius:0;padding:.7rem 0;text-align:center;background:#0c0c0a;border:1px solid #30302d}.password-complexity .complexity-text{float:right;clear:right;width:30%;font-size:1rem;text-align:left;padding-left:0}.password-complexity.not_available .complexity-text{color:#0c0c0a}.password-complexity .progress{width:100%;box-sizing:border-box;border:1px solid #0c0c0a;height:10px;display:block;clear:both;margin:.25em 0 .5em 0;float:left}.password-complexity .progress-bar{background:#000;width:0;height:6px;display:block;float:left;margin:1px}.password-complexity .progress-bar.very-weak{background:#000;width:5%}.password-complexity .progress-bar.weak{background:#d40101;width:10%}.password-complexity .progress-bar.fair{background:#ffbd2e;width:60%}.password-complexity .progress-bar.strong{background:#6c0;width:80%}.password-complexity .progress-bar.very-strong{background:#090;width:99.5%}.password-hints{margin:.5em 0 1em 0}.password-hints li{font-size:1.5rem;line-height:2.4rem}.password-hints li:before{content:"\25CF";color:#444442;padding-right:.5em}.password-hints li.success:before{color:#090}.password-hints li.error:before{color:#d40101}.dialog-wrapper{position:absolute;width:100%;height:100%;z-index:800;background:rgba(0,0,0,.8);overflow:auto}.dialog{position:relative;max-width:48rem;border:1px solid #0c0c0a;background:#444442;margin:auto;margin-top:1%;margin-bottom:4.8rem;box-shadow:0 0 10px 0 #000}.dialog .dialog-header{padding:.8rem 1.6rem 0 1.6rem;height:4.8rem}.dialog .dialog-header h2{margin:.8rem 0 1rem .4rem;font-size:1.8rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.dialog .dialog-header .dialog-header-subtitle{padding-top:.4rem;padding-left:.8rem;font-size:1.4rem;color:#fffffc}.dialog .dialog-header .tooltip-alt{margin-left:.8rem;font-size:1.2rem;font-weight:400;color:#fffffc;line-height:1em}.dialog .dialog-header .tooltip-alt .tooltip-text{white-space:normal}.dialog .dialog-header .dialog-close{margin-top:-3.6rem}.dialog .form-content{background:#30302d;padding:1rem 2rem 2.4rem 2rem}.dialog .error-message{margin-bottom:1rem}.dialog p{margin-top:.8rem;font-size:1.5rem}.dialog p+.checkbox{padding-bottom:.8rem}.dialog p+.checkbox label{font-size:1.6rem}.dialog label{clear:both;font-size:1.5rem}.dialog input[type=text],.dialog textarea{width:100%;box-sizing:border-box}.dialog input[type=text]:after,.dialog input[type=text]:before,.dialog textarea:after,.dialog textarea:before{content:"";display:table}.dialog input[type=text]:after,.dialog textarea:after{clear:both}.dialog input[type=text]:after,.dialog input[type=text]:before,.dialog textarea:after,.dialog textarea:before{content:"";display:table}.dialog input[type=text]:after,.dialog textarea:after{clear:both}.dialog input+.message,.dialog textarea+.message{display:none}.dialog input+.message.error,.dialog textarea+.message.error{display:block;clear:both;width:100%}.dialog .inline-error{color:#ff6b70;font-weight:700}.dialog .accordion-header a{display:inline}.dialog .submit-wrapper{margin:0;clear:both;width:100%;padding:1.2rem 0}.dialog .submit-wrapper .button,.dialog .submit-wrapper .cancel{float:right;margin-right:1.6rem}.dialog .submit-wrapper .button{box-sizing:border-box;min-width:8rem}.dialog .submit-wrapper .primary{font-size:1.8rem;padding:1.2rem 2.4rem}.dialog .submit-wrapper .cancel{margin-top:.7em;font-size:1.6rem}.dialog-close,.dialog-close:hover{display:block;float:right;border:0}.dialog-close:active{-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem;box-shadow:inset 0 1px 2px rgba(0,0,0,.2)}.dialog-close .fa-close{padding-top:15px;width:30px;height:15px;text-align:center;display:block;vertical-align:baseline;line-height:0;border:1px solid #3b3b39;-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem}.dialog-close .svg-icon{padding:7px 0 7px 0;width:30px;height:15px;text-align:center;display:block;vertical-align:baseline;line-height:15px;border:1px solid #3b3b39;-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem}.dialog-close:hover .fa-close,.dialog-close:hover .svg-icon{border:1px solid #3b3b39;-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem}@media all and (max-width:480px){.dialog{margin:0;border:0;box-shadow:none;width:100%;max-width:100%;margin-bottom:2.5em}}.footer{margin-top:0;position:fixed;bottom:0;text-align:right;font-size:1.2rem;width:100%;height:3.8rem;background:#30302d;border-top:1px solid #0c0c0a;z-index:890}.footer .footer-links{padding-top:1rem;width:100%}.footer .footer-links li{display:inline;margin-right:1.5em}.footer .footer-links li.error-message a{background-color:#30302d;color:#ff6b70}.footer .footer-links li .github-star{display:inline;position:absolute;margin-left:-8em;margin-top:-1px}.footer .footer-links a:not(.gh-btn):not(.gh-count){border:0}.avatar img{width:36px;height:36px;border-radius:50%}.big.avatar img{width:72px;height:72px;border-radius:50%}iframe{margin:0;padding:0;border:0;outline:0;font-size:100%;vertical-align:baseline;background:0 0}iframe.full-screen{position:absolute;width:100%;height:100%;z-index:999;border:0;top:0;left:0}iframe.cachette{position:absolute;width:1px;height:1px;z-index:999;border:0;bottom:0;right:0}#user-locale-input{border:none;background:#222220 url('../../../img/controls/chevron-down_white.svg') 92% center/1rem no-repeat;appearance:none;margin-top:.8rem;padding-left:.4rem;padding-right:2rem;line-height:2rem;width:auto}#user-locale-input:focus{outline:0;border:1px solid #2894df;border-radius:0}#user-locale-input option{background-color:#222220}body,html{height:100%}.login.page h1{margin-top:0;font-size:2.4rem;color:#cacac9}.login.page p{font-size:1.6rem;line-height:2.4rem}.login.page .processing-wrapper{display:block;width:16rem;height:16rem;margin:auto}.login.page .processing-wrapper .processing{display:block;text-align:center}.login.page .processing-wrapper .processing:after{display:block;width:16rem;height:16rem;content:" ";background:transparent url('../../../img/controls/loading_dark.svg') center center no-repeat;background-size:auto 40%}.login.page .login-form{min-height:16rem}.login.page .login-form .form-actions{text-align:center;padding-top:1.2em}.login.page .login-form .button+a{font-size:1.6rem;line-height:2.4rem;text-align:center;display:inline-block;margin-top:1.6rem;cursor:pointer}.login.page .email-sent-instructions{text-align:center}.login.page .email-sent-instructions .email-sent-bg{background:transparent url('../../../img/illustrations/email.png') top center no-repeat;background-size:auto 90%;height:16rem}.login.page .email-sent-instructions h1{margin-top:2.4rem}.login.page .email-sent-instructions p{padding:.8rem .8rem 0 .8rem;margin-bottom:0}.login.page .choose-security-token .input-security-token{margin:1em 0 1.5em 0}.login.page .choose-security-token .input-security-token:after,.login.page .choose-security-token .input-security-token:before{content:"";display:table}.login.page .choose-security-token .input-security-token:after{clear:both}.login.page .choose-security-token .input-security-token:after,.login.page .choose-security-token .input-security-token:before{content:"";display:table}.login.page .choose-security-token .input-security-token:after{clear:both}.login.page .choose-security-token .input-security-token label{margin-bottom:.8rem}.login.page .choose-security-token .input-security-token .input.text{-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem;font-size:3rem;max-width:10rem;float:left;text-align:center;margin-right:3rem}.login.page .choose-security-token .input-security-token .circle-picker{float:left}.login.page .choose-security-token .input-security-token .randomize-button-wrapper{float:left;width:10rem;text-align:center;clear:both;cursor:pointer}.login.page .install-extension a.browser-webstore{border:0}.login.page .install-extension a.browser-webstore img{display:block;margin-left:auto;margin-right:auto;max-width:26rem}.login.page .install-extension a.browser-webstore.edge img,.login.page .install-extension a.browser-webstore.firefox img{padding:1.6rem 0}.login.page .introduce-setup-extension .animated-setup-introduction.chrome{background:transparent url('../../../img/illustrations/pin_passbolt.gif') center center no-repeat;background-size:contain;height:25rem}.login.page .introduce-setup-extension .arrow{background:transparent url('../../../img/illustrations/wave-pin_my_extension.svg') center top no-repeat;width:10rem;height:10rem;position:absolute;top:0;right:calc(3.75rem - calc(100vw - 100%))}.login.page .browser-not-supported a.browser{border:0}.login.page .browser-not-supported a.browser img{max-width:26rem;display:block;margin-left:auto;margin-right:auto}.login.page .login .login-user{width:100%;margin:auto}.login.page .login .login-user>*{text-align:center}.login.page .login .login-user .login-user-name{font-weight:700;font-size:1.6rem;line-height:1rem;margin-top:1.6rem}.login.page .login .login-user .login-user-email{font-size:1.6rem;line-height:1rem}.login.page .login-processing{display:flex;flex-direction:column;align-items:center;justify-content:center}@media only screen and (min-width:42rem){body{background:#222220}.login.page{display:grid;height:calc(100% - 4rem);grid-template-columns:1fr 1.5fr 1fr;grid-template-rows:0 1fr 0.05fr;grid-gap:1px;grid-template-areas:". . ." ". login-form ." "footer footer footer"}.login.page .content{grid-area:login-form}.login.page .content .loading-bar{display:block}.login.page .content .logo{margin:1.6em auto;width:20rem;background-size:20rem auto}.login.page .content .login-form{box-shadow:0 0 10px 0 #000;border-radius:.3rem;max-width:37.2rem;margin:auto;padding:4.8rem 4rem 5.6rem 4rem;background:#30302d}.login.page .content .input.select.locale{max-width:45.2rem;margin:auto}} \ No newline at end of file diff --git a/webroot/css/themes/midgar/api_main.min.css b/webroot/css/themes/midgar/api_main.min.css index 8010c12500..acfa387f7a 100644 --- a/webroot/css/themes/midgar/api_main.min.css +++ b/webroot/css/themes/midgar/api_main.min.css @@ -1,16 +1,12 @@ /**! * @name passbolt-styleguide - * @version v3.2.1 - * @date 2021-05-19 + * @version v3.4.0 + * @date 2021-12-01 * @copyright Copyright 2021 Passbolt SA * @source https://github.com/passbolt/passbolt_styleguide * @license AGPL-3.0 */ -a,abbr,acronym,address,applet,b,big,blockquote,body,caption,center,cite,code,dd,del,dfn,div,dl,dt,em,fieldset,font,form,h1,h2,h3,h4,h5,h6,html,i,iframe,img,ins,kbd,label,legend,li,object,ol,p,pre,q,s,samp,small,span,strike,strong,sub,sup,table,tbody,td,tfoot,th,thead,tr,tt,u,ul,var{margin:0;padding:0;border:0;outline:0;font-size:100%;vertical-align:baseline;background:0 0}body{line-height:1}:focus{outline:0}ins{text-decoration:none}del{text-decoration:line-through}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}[hidden]{display:none}html{font-size:100%;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}button,html,input,select,textarea{font-family:"Open Sans",Verdana,sans-serif}a{outline:0;text-decoration:none;cursor:pointer}a:hover{text-decoration:none;cursor:pointer}h1{font-size:2em;margin:.67em 0}h2{font-size:1.5em;margin:.83em 0}h3{font-size:1.17em;margin:1em 0}h4{font-size:1em;margin:1.33em 0}h5{font-size:.83em;margin:1.67em 0}h6{font-size:.67em;margin:2.33em 0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}blockquote,q{quotes:none}blockquote:after,blockquote:before,q:after,q:before{content:'';content:none}dfn{font-style:italic}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}p,pre{margin:1em 0}code,kbd,pre,samp{font-family:monospace,serif;font-size:1em}pre{white-space:pre;white-space:pre-wrap;word-wrap:break-word}q{quotes:none}q:after,q:before{content:'';content:none}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}nav ol,nav ul,ol,ul{list-style:none;list-style-image:none}img{border:0;-ms-interpolation-mode:bicubic}svg:not(:root){overflow:hidden}figure{margin:0}form{margin:0}legend{border:0;padding:0;white-space:normal}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline}button,input{line-height:normal}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default;pointer-events:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top}table{border-collapse:collapse;border-spacing:0}table td,table th{text-align:left;font-weight:400}.ir{background-color:transparent;border:0;overflow:hidden}.ir:before{content:"";display:block;width:0;height:150%}html body .hidden{display:none}.visually-hidden,.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visually-hidden .focusable:active,.visually-hidden .focusable:focus,.visuallyhidden .focusable:active,.visuallyhidden .focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.clearfix{zoom:1}.clearfix:after,.clearfix:before{content:"";display:table}.clearfix:after{clear:both}.rounded{-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em}.left{float:left}.right{float:right}.ellipsis{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.inline{display:inline-block}@-webkit-keyframes rotate{from{-webkit-transform:rotate(0)}to{-webkit-transform:rotate(-360deg)}}@-moz-keyframes rotate{from{-moz-transform:rotate(0)}to{-moz-transform:rotate(-360deg)}}.rotate{-webkit-animation-name:rotate;-webkit-animation-duration:4s;-webkit-animation-iteration-count:infinite;-webkit-animation-timing-function:linear;-moz-animation-name:rotate;-moz-animation-duration:4s;-moz-animation-iteration-count:infinite;-moz-animation-timing-function:linear}.rotate-right{transform:rotate(-90deg)}.blink{animation:blinker 1s linear infinite}.blink-fast{animation:blinker .25s linear infinite}@keyframes blinker{50%{opacity:0}}body{color:#fffffc;background:#30302d}body.iframe{background:0 0}a:link,a:visited{color:#fffffc}a:hover{color:#2894df}a:active,a:focus{outline:0;color:#2894df;border:0}a{border-bottom:1px solid #0c0c0a}a:hover{border-bottom:1px solid #2894df}a.disabled{outline:0;text-decoration:none;cursor:default;color:#606060;pointer-events:none}html{scroll-behavior:smooth}.animated{-webkit-animation-duration:.5s;animation-duration:.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both}@keyframes fadeInUp{0%{opacity:0;-webkit-transform:translateY(20px);-ms-transform:translateY(20px);transform:translateY(20px)}100%{opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}}@keyframes fadeOutUp{from{opacity:1}to{opacity:0;transform:translate3d(0,-100%,0)}}@keyframes fadeInDown{from{opacity:0;transform:translate3d(0,-100%,0)}to{opacity:1;transform:none}}@keyframes pop{0%{opacity:0;transform:scale(1)}55%{opacity:1;transform:scale(1)}65%{opacity:1;transform:scale(1.25)}75%{opacity:1;transform:scale(1)}100%{opacity:1;transform:scale(1)}}.fadeInDown{animation-name:fadeInDown}.fadeOutUp{animation-name:fadeOutUp}.fadeInUp{animation-name:fadeInUp}.pop{animation-name:pop}@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;src:local('Open Sans'),local('OpenSans'),url(../../../fonts/opensans-regular.woff) format('woff')}@font-face{font-family:'Open Sans';font-style:normal;font-weight:700;src:local('Open Sans Bold'),local('OpenSans-Bold'),url(../../../fonts/opensans-bold.woff) format('woff')}body{font-family:'Open Sans',Verdana,sans-serif;font-size:100%;line-height:1.42;font-weight:400}h1{font-size:1.5em}h2{font-size:1.25em}h3{font-size:1.125em;display:block;padding-bottom:.333em;border-bottom:1px dotted #0c0c0a}p{margin:0 0 1em 0;line-height:1.5em}.code{font-family:"Courier New",Courier,monospace;font-size:.688em}.capitalize{text-transform:capitalize}/*! - * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome - * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - * @licstart - */@font-face{font-family:FontAwesome;src:url(../../../fonts/fontawesome-webfont.eot?v=4.7.0);src:url(../../../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0) format('embedded-opentype'),url(../../../fonts/fontawesome-webfont.woff2?v=4.7.0) format('woff2'),url(../../../fonts/fontawesome-webfont.woff?v=4.7.0) format('woff'),url(../../../fonts/fontawesome-webfont.ttf?v=4.7.0) format('truetype'),url(../../../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular) format('svg');font-weight:400;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #0c0c0a;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-webkit-transform:scale(-1,1);-ms-transform:scale(-1,1);transform:scale(-1,1)}.fa-flip-vertical{-webkit-transform:scale(1,-1);-ms-transform:scale(1,-1);transform:scale(1,-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-rotate-90{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-close:before,.fa-remove:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-cog:before,.fa-gear:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-repeat:before,.fa-rotate-right:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-image:before,.fa-photo:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-exclamation-triangle:before,.fa-warning:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-cogs:before,.fa-gears:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-floppy-o:before,.fa-save:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-bars:before,.fa-navicon:before,.fa-reorder:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-sort:before,.fa-unsorted:before{content:"\f0dc"}.fa-sort-desc:before,.fa-sort-down:before{content:"\f0dd"}.fa-sort-asc:before,.fa-sort-up:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-gavel:before,.fa-legal:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-bolt:before,.fa-flash:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-clipboard:before,.fa-paste:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-chain-broken:before,.fa-unlink:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-caret-square-o-down:before,.fa-toggle-down:before{content:"\f150"}.fa-caret-square-o-up:before,.fa-toggle-up:before{content:"\f151"}.fa-caret-square-o-right:before,.fa-toggle-right:before{content:"\f152"}.fa-eur:before,.fa-euro:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-inr:before,.fa-rupee:before{content:"\f156"}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen:before{content:"\f157"}.fa-rouble:before,.fa-rub:before,.fa-ruble:before{content:"\f158"}.fa-krw:before,.fa-won:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-caret-square-o-left:before,.fa-toggle-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-try:before,.fa-turkish-lira:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-bank:before,.fa-institution:before,.fa-university:before{content:"\f19c"}.fa-graduation-cap:before,.fa-mortar-board:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-image-o:before,.fa-file-photo-o:before,.fa-file-picture-o:before{content:"\f1c5"}.fa-file-archive-o:before,.fa-file-zip-o:before{content:"\f1c6"}.fa-file-audio-o:before,.fa-file-sound-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-ring:before,.fa-life-saver:before,.fa-support:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before,.fa-resistance:before{content:"\f1d0"}.fa-empire:before,.fa-ge:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-hacker-news:before,.fa-y-combinator-square:before,.fa-yc-square:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-paper-plane:before,.fa-send:before{content:"\f1d8"}.fa-paper-plane-o:before,.fa-send-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-futbol-o:before,.fa-soccer-ball-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-ils:before,.fa-shekel:before,.fa-sheqel:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-bed:before,.fa-hotel:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-y-combinator:before,.fa-yc:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before,.fa-battery:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-paper-o:before,.fa-hand-stop-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-television:before,.fa-tv:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before{content:"\f2a3"}.fa-deaf:before,.fa-deafness:before,.fa-hard-of-hearing:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-sign-language:before,.fa-signing:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-address-card:before,.fa-vcard:before{content:"\f2bb"}.fa-address-card-o:before,.fa-vcard-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer-full:before,.fa-thermometer:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bath:before,.fa-bathtub:before,.fa-s15:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}/*! @licend */.icon{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.icon.fav:before{content:"\f005";color:#444442}.icon.unfav:before{content:"\f005";color:#d40101}.svg-icon{display:inline-flex;align-self:center}.svg-icon svg{fill:#fff;height:1em;width:1em}.svg-icon.baseline svg{top:.125em;position:relative}.svg-icon.dim svg,.svg-icon.light svg{fill:#eeeeec}.svg-icon.icon-only svg{position:relative;top:.25em;width:1.1rem;height:1.1rem}.button .svg-icon svg{top:.125em;position:relative}.button .svg-icon+span:not(.visuallyhidden){margin-left:.5em;display:inline-block}.button .svg-icon svg:hover,.button:hover .svg-icon svg{fill:#fffffc}.button.primary:active .svg-icon svg,.button.primary:focus .svg-icon svg,.button.warning .svg-icon svg,.button.warning:active .svg-icon svg,.button.warning:focus .svg-icon svg,.button.warning:hover .svg-icon svg{fill:#eeeeec}.button:active .svg-icon svg,.button:focus .svg-icon svg{fill:#2894df}.button.disabled .svg-icon svg{fill:#8b8b89}.button.disabled .svg-icon svg:hover{fill:#8b8b89}a:hover .svg-icon svg{fill:#2894df}a.disabled .svg-icon svg{fill:#8b8b89;pointer-events:none;cursor:default}a.disabled:hover .svg-icon svg{fill:#8b8b89}a.fav .svg-icon svg{fill:#d40101}a.unfav{display:inline-flex}a.unfav .svg-icon.star svg{fill:#8b8b89}.button,button,input[type=submit]{position:relative;font-size:.875em;min-height:1.428571em;padding:.4285714em 1.225em .4285714em 1.225em;display:inline-block;vertical-align:baseline;margin-right:.5em;outline:0;cursor:pointer;text-align:center;text-decoration:none;border:1px solid #222220;color:#fffffc;font-weight:400;text-shadow:0 1px 0 #000;background:#444442;background-image:linear-gradient(top,#444442,#444442);-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em}.submit{display:inline-block}.button.big{font-size:1.125em}.button:hover,button:hover{color:#fffffc;text-decoration:none;background:#444442;background-image:linear-gradient(top,#606060,#606060);border:1px solid #30302d}.button:focus,button:focus{color:#2894df;border:1px solid #2894df}.button:active,button:active{color:#2894df;border:1px solid #2894df;background:#3b3b39;position:relative;box-shadow:inset 0 1px 2px rgba(0,0,0,.2)}.button.cancel,.button.cancel:active,.button.cancel:focus,.button.cancel:hover,.button.dim,.button.dim:active,.button.dim:focus,.button.dim:hover{background:#30302d;font-weight:400}.button.primary{background:#2894df;border:1px solid #2894df;color:#fff;text-shadow:0 1px 0 rgba(0,0,0,.2)}.button.primary:hover{background:#2a9ceb;border:1px solid #2a9ceb;color:#fff}.button.primary:active,.button.primary:focus{color:#eeeeec;border:1px solid #4271b7;background:#2a9ceb}.button.warning,.button.warning:active,.button.warning:focus,.button.warning:hover{color:#eeeeec;background:#d40101;border:1px solid #d40101;text-shadow:none}.button.warning:active,.button.warning:focus{border:1px solid #92000c}.button.disabled{cursor:default;color:#8b8b89;background:#3b3b39;border:1px solid #0c0c0a;text-shadow:none;font-weight:400}.button.disabled:active,.button.disabled:focus,.button.disabled:hover{box-shadow:0 0 0;top:0;color:#8b8b89;background:#3b3b39;border:1px solid #0c0c0a}.duo-wrapper{zoom:1}.duo-wrapper:after,.duo-wrapper:before{content:"";display:table}.duo-wrapper:after{clear:both}.duo-wrapper:after,.duo-wrapper:before{content:"";display:table}.duo-wrapper:after{clear:both}.duo-wrapper .button{float:left}.duo-wrapper .button.duo:nth-child(1){margin-right:0;border-top-right-radius:0;border-bottom-right-radius:0}.duo-wrapper .button.duo:nth-child(2){margin-left:0;border-left:0;border-top-left-radius:0;border-bottom-left-radius:0}.button-spacer,.buttons-spacer{margin-right:.5em}html body .button.processing{background:#30302d;border:1px solid #30302d;cursor:default}.button.processing:active,.button.processing:hover{border:1px solid #30302d}.button.processing:after{line-height:2.25em;width:100%;height:100%;position:absolute;content:" ";top:-1px;left:-1px;background:#30302d url(../../../img/controls/loading_dark.svg) center center no-repeat;background-size:auto 50%;border:1px solid #0c0c0a;border-radius:5px}input[type=submit].button.processing{color:transparent;text-shadow:none;background:#30302d url(../../../img/controls/loading_dark.svg) center center no-repeat;background-size:auto 50%;border:1px solid #0c0c0a;border-radius:5px}.button-toggle.button.selected,.toggle.button.selected{background:#222220;box-shadow:inset 0 1px 2px rgba(0,0,0,.2);border:1px solid #0c0c0a}label{font-weight:700;font-size:1em;line-height:1.333333em;padding:.5em 0;display:block}.required label:after{content:" \002A";color:#ff6b70;font-weight:700}.placeholder{color:#8b8b89}::-webkit-input-placeholder{color:#8b8b89}:-moz-placeholder,::-moz-placeholder{color:#8b8b89}input[type=email],input[type=number],input[type=password],input[type=search],input[type=text],textarea{font-size:16px;line-height:24px;color:#fffffc;background:#0c0c0a;width:14em;padding:.375em .625em .375em .625em;display:inline-block;margin-bottom:.5em;-webkit-appearance:none;border:1px solid #222220;border-top:1px solid #30302d;vertical-align:middle;box-sizing:content-box}input[type=email]:hover,input[type=number]:hover,input[type=password]:hover,input[type=search]:hover,input[type=text]:hover,textarea:hover{border:1px solid #30302d}input[type=email]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=text]:focus,textarea:focus{box-shadow:inset 1px 1px 2px rgba(0,0,0,.2);border:1px solid #2894df}.input-focus{box-shadow:inset 1px 1px 2px rgba(0,0,0,.2);border:1px solid #2894df}input[type=email]:disabled,input[type=number]:disabled,input[type=password]:disabled,input[type=search]:disabled,input[type=text]:disabled,textarea:disabled{border:1px solid #0c0c0a;box-shadow:0 0;cursor:pointer;background:#444442}textarea{height:4.8em;line-height:1.5em}.textarea.large textarea{width:30em;height:7em}textarea.message_notice{margin-bottom:0}textarea.full_report{height:12em;line-height:1.5em}.date.input .input-addon,.text.input .input-addon{position:absolute;margin-left:-2.625em}.date.input .input-addon i,.text.input .input-addon i{font-size:16px;width:1.25em;border:1px solid #222220;border-top:1px solid #30302d;line-height:24px;color:#fffffc;background:#0c0c0a;padding:.375em .625em .375em .625em}.date.input input:hover+.input-addon i,.text.input input:hover+.input-addon i{border:1px solid #30302d}.date.input input:focus+.input-addon i,.text.input input:focus+.input-addon i{border:1px solid #2894df}.date.input input:disabled+.input-addon i,.text.input input:disabled+.input-addon i{background:#444442;border:1px solid #0c0c0a}.checkboxlist .input.checkbox label,.radiolist .input.radio label,input[type=checkbox]+label{font-weight:400;display:inline;margin-left:.25em;margin-bottom:.25em;cursor:pointer}.radiolist{zoom:1}.radiolist:after,.radiolist:before{content:"";display:table}.radiolist:after{clear:both}.radiolist:after,.radiolist:before{content:"";display:table}.radiolist:after{clear:both}.radiolist .input.radio{float:left}.radiolist .input.radio input{float:left;margin-top:.55em}.radiolist .input.radio input+label{font-weight:400;display:inline-block;margin-left:.65em;font-size:1em;float:left;margin-right:2em}.input .message{padding:0;padding-left:.25em;padding-bottom:.5em;font-size:.813em;margin-top:.125em;margin-bottom:.25em;background-color:transparent;border:0}.input .message.error{font-weight:400;color:#ff6b70}.input .message.helptext{color:#8b8b89;font-weight:400}.input .message:empty{display:none}.form-content .input .message,form .input .message{background-color:transparent}.input.error label,textarea.error label{color:#ff6b70}form .input .inline.message{display:inline-block}select{font-size:16px;margin:.55em 0}select.large{line-height:24px;color:#fffffc;background:#0c0c0a;width:14em;padding:.375em .425em .375em .425em;display:inline-block;margin-bottom:.5em;border:1px solid #222220;border-top:1px solid #30302d;vertical-align:middle;box-sizing:content-box}input[type=file]{width:100%;color:#333}input[type=email].fluid,input[type=password].fluid,input[type=text].fluid,select.fluid,textarea.fluid{width:80%}.submit-input-wrapper,.submit-wrapper{margin-top:.5em}.singleline{zoom:1}.singleline:after,.singleline:before{content:"";display:table}.singleline:after{clear:both}.singleline:after,.singleline:before{content:"";display:table}.singleline:after{clear:both}.singleline .input.text{float:left;width:40.2%}.singleline .input.text.first-field,.singleline .input.text:first-child{margin-right:2.5%}.singleline .input.text input[type=email],.singleline .input.text input[type=password],.singleline .input.text input[type=search],.singleline .input.text input[type=text]{width:93%}.form-content .accordion h3.accordion-header a{border:0}.form-content .accordion h3.accordion-header a:hover{border:0}.form-content .accordion h3.accordion-header a:before{font:normal normal normal 1em FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;width:1em;height:1em;position:absolute;text-align:center}.form-content table{width:100%;margin-bottom:1em}.form-content table thead{background-color:transparent!important}.form-content table thead th{background-color:transparent;font-weight:700;border:1px solid #8b8b89;padding:.7em .5em}.form-content table tbody tr{font-size:.8em;text-align:left}.form-content table tbody tr:nth-child(even){background-color:transparent}.form-content table tbody tr:nth-child(odd){background-color:#f3f3f3;color:#0c0c0a}.form-content table tbody tr td{text-align:left;padding:.5em .5em;border:1px solid #8b8b89}.form-content table tbody tr td code{padding:.2em .3em;background-color:#8b8b89}.form-content table tbody tr td pre{margin:0;padding:0;background-color:transparent}.form-content table tbody tr td code,.form-content table tbody tr td pre{font-family:monospace,serif;font-size:1em}.form-content table tbody tr td em{color:#606060;font-size:.8em;font-style:normal}.page{width:100%;min-width:320px;top:0;left:0;right:0;bottom:0;position:absolute;overflow:auto}.page .panel{height:100%;width:100%;position:absolute;overflow:auto}.page .panel.main{top:10em;bottom:2.45em;height:auto;padding:0}.page .panel.main .row{padding-left:0}.page .panel.main .panel.left{background:#353532}.page .header.second+.panel.main{top:6.875em}.page .header.third+.panel.main{top:10em;border-top:1px solid #0c0c0a}.page .header{width:100%;overflow:hidden}.page .header.first{min-height:2.4em}.page .header.second{top:2.375em;height:4.5em}.page .header.third{top:6.875em;height:3em}.page .col1,.page .col2,.page .col2_3,.page .col3{position:absolute}.page .col1,.page .panel.left{width:17.25%;box-sizing:border-box}.page .panel.middle{width:82%;left:18%;overflow:hidden}.page .panel.middle.scroll{overflow-y:scroll}.page .col2{width:70%;left:18%}.page .col3,.page .panel.right{width:24%;left:75%}.page .col2_3{width:81.5%;left:18%}.page .panel.main .grid-responsive-12{float:left;width:100%;max-width:none;padding-left:.25em;box-sizing:border-box}@media all and (max-width:1024px){.page .panel.left{display:none}.page .header.second .col2{display:none}.page .header.second .col2_3{display:none}.page .header.third{height:4.5em}.page .header.third .col1,.page .header.third .col2,.page .header.third .col2_3{position:relative;float:left;left:auto;width:auto}.page .header.third .col3{display:none}.page .panel.main .panel.left{display:none}.page .panel.main .panel.middle{width:100%;left:0}.page .panel.main .row{padding-left:.5em}.page .panel.main .panel.aside{min-width:auto}}.grid,.grid-responsive-12{margin:0 auto;padding:0;max-width:1220px}.grid .row,.grid-responsive-12 .row{zoom:1;padding:0 .5em 0 .5em}.grid .row:after,.grid .row:before,.grid-responsive-12 .row:after,.grid-responsive-12 .row:before{content:"";display:table}.grid .row:after,.grid-responsive-12 .row:after{clear:both}.grid .row:after,.grid .row:before,.grid-responsive-12 .row:after,.grid-responsive-12 .row:before{content:"";display:table}.grid .row:after,.grid-responsive-12 .row:after{clear:both}.grid .row .col1,.grid .row .col10,.grid .row .col11,.grid .row .col12,.grid .row .col2,.grid .row .col3,.grid .row .col4,.grid .row .col5,.grid .row .col6,.grid .row .col7,.grid .row .col8,.grid .row .col9,.grid-responsive-12 .row .col1,.grid-responsive-12 .row .col10,.grid-responsive-12 .row .col11,.grid-responsive-12 .row .col12,.grid-responsive-12 .row .col2,.grid-responsive-12 .row .col3,.grid-responsive-12 .row .col4,.grid-responsive-12 .row .col5,.grid-responsive-12 .row .col6,.grid-responsive-12 .row .col7,.grid-responsive-12 .row .col8,.grid-responsive-12 .row .col9{zoom:1;position:relative;left:auto;float:none;width:99%;box-sizing:border-box}.grid .row .col10:after,.grid .row .col10:before,.grid .row .col11:after,.grid .row .col11:before,.grid .row .col12:after,.grid .row .col12:before,.grid .row .col1:after,.grid .row .col1:before,.grid .row .col2:after,.grid .row .col2:before,.grid .row .col3:after,.grid .row .col3:before,.grid .row .col4:after,.grid .row .col4:before,.grid .row .col5:after,.grid .row .col5:before,.grid .row .col6:after,.grid .row .col6:before,.grid .row .col7:after,.grid .row .col7:before,.grid .row .col8:after,.grid .row .col8:before,.grid .row .col9:after,.grid .row .col9:before,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col10:before,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col11:before,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col12:before,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col1:before,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col2:before,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col3:before,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col4:before,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col5:before,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col6:before,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col7:before,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col8:before,.grid-responsive-12 .row .col9:after,.grid-responsive-12 .row .col9:before{content:"";display:table}.grid .row .col10:after,.grid .row .col11:after,.grid .row .col12:after,.grid .row .col1:after,.grid .row .col2:after,.grid .row .col3:after,.grid .row .col4:after,.grid .row .col5:after,.grid .row .col6:after,.grid .row .col7:after,.grid .row .col8:after,.grid .row .col9:after,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col9:after{clear:both}.grid .row .col10:after,.grid .row .col10:before,.grid .row .col11:after,.grid .row .col11:before,.grid .row .col12:after,.grid .row .col12:before,.grid .row .col1:after,.grid .row .col1:before,.grid .row .col2:after,.grid .row .col2:before,.grid .row .col3:after,.grid .row .col3:before,.grid .row .col4:after,.grid .row .col4:before,.grid .row .col5:after,.grid .row .col5:before,.grid .row .col6:after,.grid .row .col6:before,.grid .row .col7:after,.grid .row .col7:before,.grid .row .col8:after,.grid .row .col8:before,.grid .row .col9:after,.grid .row .col9:before,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col10:before,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col11:before,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col12:before,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col1:before,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col2:before,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col3:before,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col4:before,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col5:before,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col6:before,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col7:before,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col8:before,.grid-responsive-12 .row .col9:after,.grid-responsive-12 .row .col9:before{content:"";display:table}.grid .row .col10:after,.grid .row .col11:after,.grid .row .col12:after,.grid .row .col1:after,.grid .row .col2:after,.grid .row .col3:after,.grid .row .col4:after,.grid .row .col5:after,.grid .row .col6:after,.grid .row .col7:after,.grid .row .col8:after,.grid .row .col9:after,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col9:after{clear:both}.grid .row .col1 img,.grid .row .col10 img,.grid .row .col11 img,.grid .row .col12 img,.grid .row .col2 img,.grid .row .col3 img,.grid .row .col4 img,.grid .row .col5 img,.grid .row .col6 img,.grid .row .col7 img,.grid .row .col8 img,.grid .row .col9 img,.grid-responsive-12 .row .col1 img,.grid-responsive-12 .row .col10 img,.grid-responsive-12 .row .col11 img,.grid-responsive-12 .row .col12 img,.grid-responsive-12 .row .col2 img,.grid-responsive-12 .row .col3 img,.grid-responsive-12 .row .col4 img,.grid-responsive-12 .row .col5 img,.grid-responsive-12 .row .col6 img,.grid-responsive-12 .row .col7 img,.grid-responsive-12 .row .col8 img,.grid-responsive-12 .row .col9 img{width:100%;height:auto;display:block}@media all and (min-width:780px){.grid .row{padding:0 .5em 0 .5em}.grid .row .col1,.grid .row .col10,.grid .row .col11,.grid .row .col12,.grid .row .col2,.grid .row .col3,.grid .row .col4,.grid .row .col5,.grid .row .col6,.grid .row .col7,.grid .row .col8,.grid .row .col9{float:left;position:relative;margin:0 3% 0 0}.grid .row .last{margin-right:0}.grid .row .col1{width:5.5%}.grid .row .col2{width:14%}.grid .row .col3{width:22.5%}.grid .row .col4{width:31%}.grid .row .col5{width:39.5%}.grid .row .col6{width:48%}.grid .row .col7{width:56.5%}.grid .row .col8{width:65%}.grid .row .col9{width:73.5%}.grid .row .col10{width:82%}.grid .row .col11{width:90.5%}.grid .row .col12{width:99%;margin:0}.grid .row .push1{margin-left:5.5%}.grid .row .push2{margin-left:14%}.grid .row .push3{margin-left:22.5%}.grid .row .push4{margin-left:31%}}@media all and (max-width:768px){.hidden-xs{display:none}}.scroll{overflow-y:scroll;background:linear-gradient(#30302d 30%,rgba(255,255,255,0)),linear-gradient(rgba(255,255,255,0),#30302d 70%) 0 100%,radial-gradient(farthest-side at 75% 0,rgba(0,0,0,.2),rgba(0,0,0,0)),radial-gradient(farthest-side at 75% 100%,rgba(0,0,0,.2),rgba(0,0,0,0)) 0 100%;background-repeat:no-repeat;background-color:#30302d;background-size:100% 10px,100% 10px,100% 5px,100% 5px}::-webkit-scrollbar{width:1em}::-webkit-scrollbar-track{background:#30302d;border-top:0;border-bottom:0}::-webkit-scrollbar-thumb{background:#606060;border-radius:1em;border:4px solid #30302d}.input.toggle-switch{display:flex;padding-top:1em;padding-bottom:.5em}.input.toggle-switch label{padding-top:.15em;padding-left:1em;order:2;flex:1;font-weight:400;font-size:1em}.input.toggle-switch.disabled label{color:#8b8b89}.input.toggle-switch .toggle-switch-checkbox{order:-1;flex:0 0 3em;display:none}.input.toggle-switch .toggle-switch-checkbox,.input.toggle-switch .toggle-switch-checkbox *,.input.toggle-switch .toggle-switch-checkbox :after,.input.toggle-switch .toggle-switch-checkbox :before,.input.toggle-switch .toggle-switch-checkbox+.toggle-switch-button,.input.toggle-switch .toggle-switch-checkbox:after,.input.toggle-switch .toggle-switch-checkbox:before{box-sizing:border-box}.input.toggle-switch .toggle-switch-checkbox ::selection,.input.toggle-switch .toggle-switch-checkbox :after::selection,.input.toggle-switch .toggle-switch-checkbox :before::selection,.input.toggle-switch .toggle-switch-checkbox+.toggle-switch-button::selection,.input.toggle-switch .toggle-switch-checkbox::selection,.input.toggle-switch .toggle-switch-checkbox:after::selection,.input.toggle-switch .toggle-switch-checkbox:before::selection{background:0 0}.input.toggle-switch .toggle-switch-button{order:-1;flex:none;outline:0;display:block;width:3em;height:1.5em;position:relative;cursor:pointer;user-select:none;background:#444442;border-radius:2em;padding:2px;transition:all .4s ease;border:1px solid #0c0c0a}.input.toggle-switch .toggle-switch-button span{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.input.toggle-switch .toggle-switch-button span .focusable:active,.input.toggle-switch .toggle-switch-button span .focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.input.toggle-switch .toggle-switch-button span .focusable:active,.input.toggle-switch .toggle-switch-button span .focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.input.toggle-switch .toggle-switch-button:hover:after{will-change:padding}.input.toggle-switch .toggle-switch-button:active{box-shadow:inset 0 0 0 2em #e8eae9}.input.toggle-switch .toggle-switch-button:active:after{padding-right:.8em}.input.toggle-switch .toggle-switch-button:after,.input.toggle-switch .toggle-switch-button:before{position:relative;display:block;content:"";width:50%;height:100%}.input.toggle-switch .toggle-switch-button:after{left:0;border-radius:2em;background:#fff;transition:left .3s cubic-bezier(.175,.885,.32,1.275),padding .3s ease,margin .3s ease;box-shadow:0 0 0 1px rgba(0,0,0,.1),0 4px 0 rgba(0,0,0,.08)}.input.toggle-switch .toggle-switch-button:before{display:none}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button:after{left:50%}.input.toggle-switch .toggle-switch-checkbox:disabled+.toggle-switch-button{background:#30302d;cursor:not-allowed}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button{background:#6c0}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button:active{box-shadow:none}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button:active:after{margin-left:-.8em}.input.toggle-switch .toggle-switch-checkbox:disabled:checked+.toggle-switch-button{background:#e6ffcc}h1 .input.toggle-switch,h2 .input.toggle-switch,h3 .input.toggle-switch,h4 .input.toggle-switch,h5 .input.toggle-switch,h6 .input.toggle-switch{float:left;padding:.4em 1em 0 0}.header{overflow:visible!important}.header.first{background:#222220}.header.second{background:#3b3b39}.header .navigation.primary{zoom:1}.header .navigation.primary:after,.header .navigation.primary:before{content:"";display:table}.header .navigation.primary:after{clear:both}.header .navigation.primary:after,.header .navigation.primary:before{content:"";display:table}.header .navigation.primary:after{clear:both}.header .navigation.primary li{padding:.5em;float:left}.header .navigation.primary li:first-child{padding-left:1em}.header .navigation.primary li.right{float:right;margin-right:1em}.header .navigation.primary li a{color:#f3f3f3;text-decoration:none;border:0;display:inline-block}.header .navigation.primary li a:hover{color:#eeeeec}.header .navigation.primary li a:active,.header .navigation.primary li a:focus{color:#2894df}.header .navigation.primary li a.highlighted{background-color:#2894df;padding:0 .5em 0 .5em;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.header .navigation.primary li a.highlighted:active,.header .navigation.primary li a.highlighted:focus{color:#f3f3f3}.header .navigation.primary li .row.selected a{color:#eeeeec}.header .navigation.primary li .row.selected a:focus{color:#2894df}.header .navigation.primary .github-star{display:none;position:absolute;right:1em;top:4px}@media all and (min-width:600px){.header .navigation.primary .github-star{display:block}}.header .logo{margin:1.25em 0 0 1em;max-width:80%}.logo.no-img{background:transparent url(../../../img/logo/logo_white.svg) 0 0 no-repeat;background-size:150px auto;width:150px;height:30px}.logo h1{display:none}.logo.bigger{background:transparent url(../../../img/logo/logo_white.svg) 0 0 no-repeat;background-size:200px auto;width:200px;height:45px}.header.second .col1{min-width:200px}@media only screen and (-moz-min-device-pixel-ratio:1.5),only screen and (-o-min-device-pixel-ratio:1.5),only screen and (-webkit-min-device-pixel-ratio:1.5),only screen and (min-devicepixel-ratio:1.5),only screen and (min-resolution:1.5dppx){.logo.no-img{background:transparent url(../../../img/logo/logo_white.svg) 0 0 no-repeat;background-size:150px auto}.logo.bigger{background:transparent url(../../../img/logo/logo_white.svg) 0 0 no-repeat;background-size:200px auto}}form.search{margin-top:1em}form.search label,form.search legend{display:none;width:10em}form.search input[type=search]{float:left;width:65%;margin-bottom:0;border-right:1px solid #30302d;padding:.313em .625em}form.search input[type=search]:active,form.search input[type=search]:focus{border-right:1px solid #2894df}form.search button{height:2.571em;width:4.5em;float:left;margin-left:0;border-radius:0 2px 2px 0}form.search button .svg-icon.icon-only svg{top:.125em}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{display:none}@media all and (max-width:480px){form.search{display:none}}.avatar img{width:36px;height:36px;border-radius:50%}.big.avatar img{width:72px;height:72px;border-radius:50%}.user.profile{max-width:16em;float:right;border:1px solid #0c0c0a;margin-top:.5em;background:#3b3b39;overflow:hidden;cursor:pointer;border-radius:3px;background:#444442;background-image:linear-gradient(top,#444442,#444442)}.user.profile .more{background:#444442;background-image:linear-gradient(top,#444442,#444442);border-left:1px dotted #0c0c0a}.user.profile .center-cell-wrapper,.user.profile .left-cell,.user.profile .right-cell{float:left;height:3.125em}.user.profile .center-cell-wrapper{width:100%}.user.profile .center-cell{margin:0 0 0 3.125em;overflow:hidden}.user.profile .left-cell{width:3.125em;margin-left:-100%}.user.profile .right-cell{width:1.6875em;margin-left:-1.75em}.user.profile .picture img{width:2.6em;padding:.28em .35em .35em .35em;border-radius:50%}.user.profile .details{float:left;font-size:.875em;padding:.357em 1em .5em 1em}.user.profile .details .email,.user.profile .details .name{width:11em;float:left;clear:both;text-overflow:clip;word-wrap:break-word;display:inline-block;line-height:normal;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.user.profile .details .name{font-weight:700;padding-top:0}.user.profile .details .email{padding-bottom:0}.user.profile .more a{position:absolute;display:block;border:0;color:#cacac9}.user.profile .more a .svg-icon{margin:16px 0 0 7px;display:inline-block}.user.profile .more a .svg-icon svg{fill:#fff}.user.profile .more a:hover{color:#000}.user.profile .more a:hover svg{fill:#8b8b89}.user.profile .more a span:last-child{visibility:hidden}.user.profile .dropdown-content{top:59px;max-width:16em;width:100%;background:#3b3b39}.user.profile .dropdown-content.visible{display:block}@media all and (max-width:1024px){.user.profile{display:block;width:auto}.user.profile .center-cell{display:none}.user.profile .right-cell{display:none}}.usercard-col-2{zoom:1;margin:0 0 1em 0}.usercard-col-2:after,.usercard-col-2:before{content:"";display:table}.usercard-col-2:after{clear:both}.usercard-col-2:after,.usercard-col-2:before{content:"";display:table}.usercard-col-2:after{clear:both}.usercard-col-2:last-of-type{margin-bottom:0}.usercard-col-2 .content-wrapper{float:left;width:100%}.usercard-col-2 .content-wrapper .content{margin-left:36px;padding:0 1em 0 1em}.usercard-col-2 .content-wrapper .content .name{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.usercard-col-2 .content-wrapper .content .subinfo{color:#fffffc;font-size:.929em}.usercard-col-2 .avatar{float:left;width:36px;margin-left:-100%}.usercard-col-2 .avatar img{width:36px;height:36px}.usercard-col-2 ul.permissions-list{margin-top:1em;font-size:.8em}.usercard-col-2 ul.permissions-list li .avatar{margin-left:0;float:left;width:18px}.usercard-col-2 ul.permissions-list li .avatar img{width:18px;height:18px}.usercard-col-2 ul.permissions-list li .name{display:inline-block;padding-left:7%;line-height:18px}.usercard-col-2 ul.permissions-list li .name.crossed{text-decoration:line-through}.usercard-col-2 ul.permissions-list li .type{float:right;text-align:right;line-height:18px;color:#606060}.usercard-col-2 ul.permissions-list li .type span{background-color:#fff;font-size:.8em;color:#fff;padding:.1em .5em;border-radius:.5em}.usercard-col-2 ul.permissions-list li .type span.created{background-color:#6c0}.usercard-col-2 ul.permissions-list li .type span.updated{background-color:#2a9ceb}.usercard-col-2 ul.permissions-list li .type span.removed{background-color:#db5454}.usercard-detailed-col-2{zoom:1;margin:0 0 1em 0;padding:.5em 0;margin:0}.usercard-detailed-col-2:after,.usercard-detailed-col-2:before{content:"";display:table}.usercard-detailed-col-2:after{clear:both}.usercard-detailed-col-2:after,.usercard-detailed-col-2:before{content:"";display:table}.usercard-detailed-col-2:after{clear:both}.usercard-detailed-col-2:last-of-type{margin-bottom:0}.usercard-detailed-col-2 .content-wrapper{float:left;width:100%}.usercard-detailed-col-2 .content-wrapper .content{margin-left:36px;padding:0 1em 0 1em}.usercard-detailed-col-2 .content-wrapper .content .name{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.usercard-detailed-col-2 .content-wrapper .content .subinfo{color:#fffffc;font-size:.929em}.usercard-detailed-col-2 .avatar{float:left;width:36px;margin-left:-100%}.usercard-detailed-col-2 .avatar img{width:36px;height:36px}.usercard-detailed-col-2 ul.permissions-list{margin-top:1em;font-size:.8em}.usercard-detailed-col-2 ul.permissions-list li .avatar{margin-left:0;float:left;width:18px}.usercard-detailed-col-2 ul.permissions-list li .avatar img{width:18px;height:18px}.usercard-detailed-col-2 ul.permissions-list li .name{display:inline-block;padding-left:7%;line-height:18px}.usercard-detailed-col-2 ul.permissions-list li .name.crossed{text-decoration:line-through}.usercard-detailed-col-2 ul.permissions-list li .type{float:right;text-align:right;line-height:18px;color:#606060}.usercard-detailed-col-2 ul.permissions-list li .type span{background-color:#fff;font-size:.8em;color:#fff;padding:.1em .5em;border-radius:.5em}.usercard-detailed-col-2 ul.permissions-list li .type span.created{background-color:#6c0}.usercard-detailed-col-2 ul.permissions-list li .type span.updated{background-color:#2a9ceb}.usercard-detailed-col-2 ul.permissions-list li .type span.removed{background-color:#db5454}@keyframes highlight{from{background-color:#0e3e60}to{background-color:transparent}}.usercard-detailed-col-2.highlight{background-color:#0e3e60;animation-delay:2s;animation-duration:5s;animation-name:highlight;animation-fill-mode:forwards}.usercard-detailed-col-2 .content-wrapper .content .name,.usercard-detailed-col-2 .content-wrapper .content .subinfo{color:#606060}.usercard-detailed-col-2 .content-wrapper .content .name{white-space:normal}.usercard-detailed-col-2 .content-wrapper .content .name .creator{color:#fffffc}.breadcrumbs{height:2em;padding:.1875em 0 .1875em .1875em;margin-bottom:0;background:#30302d}.breadcrumbs ul{padding-top:.45em;margin-left:.1875em}.breadcrumbs ul li{display:inline-block;font-size:.8em;margin-left:.25em;max-width:25%;float:left}.breadcrumbs ul li:before{content:"\203A";margin-right:.5em}.breadcrumbs ul li:first-child{margin-left:0;padding-left:0}.breadcrumbs ul li:first-child:before{content:""}.breadcrumbs ul a{border:0}.breadcrumbs div.main-cell{display:inline}.panel.middle .breadcrumbs{border-bottom:1px solid #0c0c0a}.header.third .main-action-wrapper{margin-top:.5em;padding-left:.625em}.header.third .main-action-wrapper .button{float:left;font-size:.875em;min-width:.75em}.header.third .actions-wrapper{margin-top:.5em}.header.third .actions-wrapper .button{font-size:.875em;min-width:4em;float:left}.header.third .actions-wrapper li{display:inline}.header.third .actions-wrapper .secondary{float:right}.header.third .actions-wrapper .secondary .button{font-size:.875em;min-width:1em}.header.third .actions-wrapper .secondary .button.info{padding:.3285714em 1.025em .3285714em 1.025em}.header.third .actions-wrapper .secondary .button.info .svg-icon{font-size:1.33333333em}@media all and (max-width:1024px){.header.third .actions-wrapper .actions.secondary,.header.third .actions-wrapper .dropdown{display:none}}@media all and (max-width:1024px){.header.third .actions-wrapper i,.header.third .main-action-wrapper i{display:none}.header.third .actions-wrapper i+span,.header.third .main-action-wrapper i+span{margin-left:0}.header.third .actions-wrapper .disabled,.header.third .main-action-wrapper .disabled{display:none}}@media all and (max-width:540px){.header.third .actions-wrapper a i,.header.third .main-action-wrapper a i{display:block}.header.third .actions-wrapper a.button,.header.third .main-action-wrapper a.button{min-width:1em;font-size:1em}.header.third .actions-wrapper a i+span,.header.third .main-action-wrapper a i+span{margin-left:0;display:none}.header.third .actions-wrapper .disabled,.header.third .main-action-wrapper .disabled{display:none}}.progress-bar{background:0 0;width:100%;height:2px;display:block}.progress-bar span{background:#d40101;height:2px;display:block}.progress-bar-wrapper{border:1px solid #0c0c0a;height:10px;display:block;margin:2em 0 1em 0;padding:2px}.progress-bar-wrapper .progress-bar.big{width:100%;height:10px;display:block;clear:both}.progress-bar-wrapper .progress-bar.big .progress{background:#d40101;width:5%;display:block;height:10px}.progress-bar-wrapper .progress-bar.big.infinite{background:#d40101}.progress-bar-wrapper .progress-bar.big.infinite .progress{width:100%;overflow:hidden;background:url(../../../img/controls/infinite-bar.gif) repeat-x;-moz-opacity:.5;-khtml-opacity:.5;opacity:.5}.progress-details{color:#8b8b89;margin:.5em 0 .5em 0}.progress-details .progress-percent{float:right}.update-loading-bar{position:fixed;display:block;width:100%;bottom:2.35em;z-index:991}.update-loading-bar .progress-bar span{transition:width 2s;transition-timing-function:cubic-bezier(.45,1.27,.76,.9)}.notification-container{font-size:.85em;top:0;position:absolute;z-index:991;height:2em;padding-top:1em;width:60%;margin-left:20%}.notification-container .notification{position:relative;left:50%;float:left;clear:both;margin-bottom:1em}.notification-container .notification .message{box-shadow:0 0 10px 0 #000;border-radius:2px;padding:.5em 1em;position:relative;left:-50%;float:left;color:#000;font-weight:400;width:auto}.notification-container .notification .message.warning{color:#333;background:#fef0bf}.notification-container .notification .message .content{margin-right:1em}.notification-container .notification .message .content strong{text-transform:capitalize}.notification-container .notification .message .action{border-bottom:none;margin-left:.4em}.notification-container .notification .message .action:hover .svg-icon svg{fill:#000}.js .message.no-js{display:none}.cookies .message.no-cookies{display:none}.message{padding:1em}.message a{border-bottom:1px solid #0c0c0a}.message a:hover{border-bottom:1px solid #2894df}.message.error{color:#333;background:#ffe4e4}.message.error a:link,.message.error a:visited{color:#333;border-bottom:1px dotted #0c0c0a}.message.error a:hover{color:#333;border-bottom:1px solid #0c0c0a}.message.success{color:#333;background:#f4f4d9}.message.notice{color:#333;background:#2894df}.message.warning{color:#333;background:#fef0bf}.message p:last-child{margin-bottom:0}.message.side-message{margin-left:1em;font-size:1em;margin-right:2em}.message.side-message p,.message.side-message ul{padding-bottom:1em}.message.animated{background:#30302d;color:#fffffc;display:flex;border:1px solid #0c0c0a;border-radius:3px}.message.animated .illustration{flex:0 0 180px}.message.animated .additional-information{flex:1;line-height:180px;margin-top:1.5em;padding-left:1em}@keyframes drawCircle{0%{stroke-dashoffset:180px}100%{stroke-dashoffset:0}}@keyframes drawCheck{0%{stroke-dashoffset:50px}100%{stroke-dashoffset:0}}@keyframes drawCross{0%{stroke-dashoffset:50px}100%{stroke-dashoffset:0}}@keyframes drawWarning{0%{stroke-dashoffset:180px}100%{stroke-dashoffset:0}}.message.animated #successAnimationCircle{stroke-dasharray:180px 180px;stroke:#090}.message.animated #successAnimationCheck{stroke-dasharray:50px 50px;stroke:#090}.message.animated #errorAnimationCircle{stroke-dasharray:180px 180px;stroke:#d40101}.message.animated #errorAnimationCross{stroke-dasharray:50px 50px;stroke:#d40101}.message.animated #warningAnimation{stroke-dasharray:180px 180px;stroke:#9f6000;stroke-linecap:square;stroke-linejoin:round}.message.animated #warningAnimation.round{stroke-linecap:round}.message.animated .animated{animation:.75s ease-out 0s 1 both pop}.message.animated .animated #errorAnimationCircle,.message.animated .animated #successAnimationCircle{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawCircle}.message.animated .animated #successAnimationCheck{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawCheck}.message.animated .animated #errorAnimationCross{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawCross}.message.animated .animated #warningAnimation{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawWarning}@media only screen and (max-width:767px){.message.animated{flex-direction:column}}.secret-copy{overflow:visible;display:table-cell;vertical-align:middle;zoom:1}.secret-copy:after,.secret-copy:before{content:"";display:table}.secret-copy:after{clear:both}.secret-copy:after,.secret-copy:before{content:"";display:table}.secret-copy:after{clear:both}.secret-copy>a{background:transparent url(../../../img/controls/dot_white.svg) repeat-x left 3px;display:inline-block;width:108px;height:20px;float:left;border:0}.secret-copy>a:hover{background:transparent url(../../../img/controls/dot_red.svg) repeat-x left 3px}.secret-copy>a>span{display:none}.secret-copy>pre{display:none}.dialog-wrapper{position:absolute;width:100%;height:100%;z-index:800;background:rgba(0,0,0,.8);overflow:auto}.dialog-wrapper .placeholder{position:relative;width:1em;margin:auto;height:100%}.dialog-wrapper .placeholder .loading{position:absolute;top:30%;height:2em;width:2em;background:url(../../../img/controls/loading_dark.svg);background-size:2em 2em}.dialog{position:relative;max-width:30em;border:1px solid #0c0c0a;background:#444442;margin:auto;margin-top:1%;margin-bottom:3em;box-shadow:0 0 10px 0 #000}.dialog.medium{max-width:36em}.dialog .dialog-header{padding:.5em 1em 0 1em;height:3em}.dialog .dialog-header h2{margin:.25em 2em .5em .2em;font-size:1.2em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.dialog .dialog-header h2 svg{position:relative;top:.0625em}.dialog .dialog-header .tooltip-alt{margin-left:.3em;font-size:.7em;font-weight:400;color:#fffffc}.dialog .dialog-header .tooltip-alt .tooltip-text{white-space:normal}.dialog .dialog-header .dialog-header-subtitle{padding-top:.3em;padding-left:1em;font-size:.7em;color:#fffffc}.dialog .dialog-header .dialog-close{margin-top:-2.5em}.dialog .form-content{background:#30302d;padding:.813em 1.5em 1.5em 1.5em}.dialog p{margin-top:.5em;font-size:1em}.dialog p+.checkbox{padding-bottom:.5em}.dialog p+.checkbox label{font-size:1em}.dialog label{clear:both;font-size:.9375em;line-height:1.6em}.dialog input[type=text],.dialog select.large,.dialog textarea{width:100%;box-sizing:border-box;zoom:1}.dialog input[type=text]:after,.dialog input[type=text]:before,.dialog select.large:after,.dialog select.large:before,.dialog textarea:after,.dialog textarea:before{content:"";display:table}.dialog input[type=text]:after,.dialog select.large:after,.dialog textarea:after{clear:both}.dialog input[type=text]:after,.dialog input[type=text]:before,.dialog select.large:after,.dialog select.large:before,.dialog textarea:after,.dialog textarea:before{content:"";display:table}.dialog input[type=text]:after,.dialog select.large:after,.dialog textarea:after{clear:both}.dialog input+.message,.dialog textarea+.message{display:none}.dialog input+.message.error,.dialog textarea+.message.error{display:block;clear:both;width:100%}.dialog .input .message.warning,.dialog input+.message.warning,.dialog textarea+.message.warning{display:block;clear:both;width:100%;color:#9f6000;background-color:transparent}.dialog .inline-error{color:#ff6b70;font-weight:700}.dialog .accordion-header a{display:inline}.dialog .submit-wrapper{margin:0;clear:both;width:100%;padding:.75em 0}.dialog .submit-wrapper .button,.dialog .submit-wrapper .cancel{float:right;margin-right:1.25em;font-size:1.125em}.dialog .submit-wrapper .button{box-sizing:border-box;min-width:5em}.dialog .submit-wrapper .secondary{float:left;margin-left:1.5em;margin-top:.7em;font-size:1em}.dialog .submit-wrapper .cancel{margin-top:.7em;font-size:1em}.dialog-close,.dialog-close:hover{display:block;float:right;border:0}.dialog-close:active{-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em;box-shadow:inset 0 1px 2px rgba(0,0,0,.2)}.dialog-close .fa-close{padding-top:15px;width:30px;height:15px;text-align:center;display:block;vertical-align:baseline;line-height:0;border:1px solid #3b3b39;-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em}.dialog-close .svg-icon{padding:7px 0 7px 0;width:30px;height:15px;text-align:center;display:block;vertical-align:baseline;line-height:15px;border:1px solid #3b3b39;-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em}.dialog-close:hover .fa-close,.dialog-close:hover .svg-icon{border:1px solid #3b3b39;-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em}@media all and (max-width:480px){.dialog{margin:0;border:0;box-shadow:none;width:100%;max-width:100%;margin-bottom:2.5em}}.dropdown{float:left}.dropdown .button.create:focus,.dropdown .button:focus{border:1px solid #0c0c0a;color:#fffffc;background:#30302d;z-index:801;border-bottom:0;-webkit-border-bottom-right-radius:0;-webkit-border-bottom-left-radius:0;-moz-border-radius-bottomright:0;-moz-border-radius-bottomleft:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.dropdown .button.create:focus .svg-icon svg,.dropdown .button:focus .svg-icon svg{fill:#fffffc}.dropdown .button.more .svg-icon svg{top:.15em;margin-left:.5em}.dropdown .button.create .svg-icon svg{fill:#fff}.dropdown .button.create.disabled .svg-icon svg{fill:#8b8b89}.dropdown .dropdown-content{display:none;border:1px solid #0c0c0a;background:#3b3b39;float:left;position:absolute;min-width:13em;z-index:800;padding:.25em 0}.dropdown .dropdown-content.visible{display:block}.dropdown .dropdown-content.right{right:0}.dropdown .dropdown-content li a{display:block;min-width:9em;font-size:.9375em;border:0;padding:.313em .626em}.dropdown .dropdown-content li a:hover{background:#444442;text-decoration:none;color:#fffffc}.dropdown .dropdown-content li.disabled a,.dropdown .dropdown-content li.disabled a:hover{color:#8b8b89}.dropdown .dropdown-content li .separator-before{border-top:1px solid #0c0c0a}.dropdown .dropdown-content li .separator-after{border-bottom:1px solid #0c0c0a}.dropdown .button+.dropdown-content{margin-top:2em;margin-bottom:2em}.profile .section.detailed-information h4{margin-bottom:.626em;padding-bottom:.313em;border-bottom:1px dotted #0c0c0a}.profile .section.detailed-information li .label{float:left;width:6em;color:#8b8b89}.profile .section.detailed-information li .value{display:inline-block}.profile .section.detailed-information .avatar img{width:2.875em;padding:.125em}.profile .section .section-action{padding-left:1em}.profile .edit-avatar-action .fa{margin-top:.25em}.tags-list{zoom:1}.tags-list:after,.tags-list:before{content:"";display:table}.tags-list:after{clear:both}.tags-list:after,.tags-list:before{content:"";display:table}.tags-list:after{clear:both}.tags-list .tag-list-item{float:left;max-width:95%}.tag-editor{margin-bottom:1em}.tag-editor .message{font-size:1em;margin-top:.5em}.tag-editor .message span.svg-icon{display:initial;margin-left:-.15em;padding-right:.15em}.tag-editor .tag{padding-right:0}.tag-editor .tag-content{padding-right:.5em;padding-top:.25em;display:inline-block;max-width:90%}.tag-editor .tag-delete{border-left:1px dotted #2894df;margin-left:-.25em;padding-left:.5em;font-size:.75em;vertical-align:30%;cursor:pointer}.tag-editor .tag:hover:after{border-color:#30302d}.tag-editor .tag-editor-input-wrapper{zoom:1;min-height:5em;padding:.5em;border:1px solid #222220;border-top:1px solid #30302d;position:relative}.tag-editor .tag-editor-input-wrapper:after,.tag-editor .tag-editor-input-wrapper:before{content:"";display:table}.tag-editor .tag-editor-input-wrapper:after{clear:both}.tag-editor .tag-editor-input-wrapper:after,.tag-editor .tag-editor-input-wrapper:before{content:"";display:table}.tag-editor .tag-editor-input-wrapper:after{clear:both}.tag-editor .tag-editor-input-wrapper.input-focus{border-color:#2894df}.tag-editor .tag-editor-input{min-height:1.5em;padding:.2em .4em 0 .4em;box-shadow:none;min-width:6em;max-width:24em;float:left;word-break:break-word}.tag-editor .autocomplete-suggestions{text-align:left;cursor:default;border:1px solid #0c0c0a;border-top:0;background:#30302d;box-shadow:0 0 10px 0 #000;position:absolute;display:inline-flex;z-index:9999;max-height:120px;overflow:hidden;overflow-y:auto;box-sizing:border-box}.tag-editor .autocomplete-suggestions .autocomplete-content.scroll{overflow-y:auto}.tag-editor .autocomplete-suggestions .autocomplete-content.scroll .name.ellipsis{display:block}.tag-editor .autocomplete-suggestions .autocomplete-suggestion.row{position:relative;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:#fffffc;display:block;padding:.35em .43em;border:0}.tag-editor .autocomplete-suggestions .autocomplete-suggestion.row.selected{background:#3b3b39}.tag-editor .tag,.tags-list .tag{padding:0 .25em;float:left;position:relative;margin:0 .5em .5em 0;text-decoration:none;border-radius:3px;max-width:95%;border:1px solid #b8d0fd;background:rgba(42,156,235,.1);color:#d4e4ff}.tag-editor .tag:active,.tag-editor .tag:hover,.tags-list .tag:active,.tags-list .tag:hover{border:1px solid #2a9ceb;background:#30302d;color:#2a9ceb}.announcement{margin:0;top:0;position:absolute;height:2em;padding-top:.425em;font-size:.85em;text-align:center;background:#fef0bf;color:#000;width:100%}.announcement a{color:#000;display:inline-block;margin-left:.5em;border-bottom-color:#444442}.announcement .announcement-close{float:right;border:0;margin-right:.5em;margin-top:.15em}.announcement+#container.page{top:2em}.jfilestyle{display:inline-block;margin:0 0 10px 0;padding:0;position:relative;border-collapse:separate}.jfilestyle input,.jfilestyle label{font-family:"Open Sans",Verdana,sans-serif}.jfilestyle input{border:1px solid #0c0c0a;background:#0c0c0a;margin:0 -5px 0 0;vertical-align:middle;padding:10px 15px;font-size:14px;border-radius:0;color:#8d8d8d;cursor:default;line-height:normal}.jfilestyle input[type=email]:disabled,.jfilestyle input[type=password]:disabled,.jfilestyle input[type=search]:disabled,.jfilestyle input[type=text]:disabled{background-color:#444442;padding:.375em .625em .375em .625em;font-size:1em;line-height:24px;border:1px solid #222220}.jfilestyle label{display:inline-block;border:1px solid #222220;border-left:0;background:#3b3b39;padding:.65em 1.225em .65em 1.225em;color:#0662ba;vertical-align:middle;line-height:normal;text-align:center;margin:0;font-size:.875em;width:auto;font-weight:700;border-radius:0 .214em .214em 0;max-height:18px}.jfilestyle label[disabled]{pointer-events:none;opacity:.6;cursor:not-allowed}.jfilestyle label:hover{cursor:pointer;opacity:.9;border:1px solid #30302d;border-left:0}.jfilestyle label.primary{background:#2894df;border:1px solid #2894df}.jfilestyle label.primary span{color:#fff}.jfilestyle label.primary span:before{color:#fff}.jfilestyle label span{color:#fffffc}.jfilestyle label span:before{font-family:FontAwesome;content:"\f093";display:block;width:1em;height:1.3em;line-height:1.3em;background-size:1em 1em;float:left;color:#333;margin-right:.5em}.jfilestyle.primary label{background:#2894df}.jfilestyle.primary label span{color:#fff}.jfilestyle.primary label span:before{color:#fff}.jfilestyle .jfilestyle-corner input:last-child,.jfilestyle .jfilestyle-corner label:last-child{margin-left:-1px}.jfilestyle .count-jfilestyle{background:#303030;color:#eeeeec;border-radius:50%;padding:1px 5px;font-size:12px;vertical-align:middle}.required .jfilestyle label:after{content:none}.panel.aside .comments{width:100%}.panel.aside .comments .wrap-right-column{float:left;width:100%}.panel.aside .comments .right-column{margin-left:2.5em}.panel.aside .comments .left-column{float:left;width:2em;margin-left:-100%}.panel.aside .comments .comment{padding-bottom:.5em;zoom:1}.panel.aside .comments .comment:after,.panel.aside .comments .comment:before{content:"";display:table}.panel.aside .comments .comment:after{clear:both}.panel.aside .comments .comment:after,.panel.aside .comments .comment:before{content:"";display:table}.panel.aside .comments .comment:after{clear:both}.panel.aside .comments .comment p{background:#3b3b39;padding:1em;margin-left:10px}.panel.aside .comments .comment .author.picture{width:2em;height:2em;margin-top:.25em}.panel.aside .comments .comment .author.picture img{width:2em;height:2em}.panel.aside .comments .comment .author.picture a{border:0}.panel.aside .comments .comment .metadata{font-style:italic;margin:.5em 0 1em 1em}.panel.aside .comments .comment .metadata .author.username{max-width:48%;float:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.panel.aside .comments .comment .metadata .author.username:after{content:', '}.panel.aside .comments .comment .metadata .modified{max-width:48%;padding-right:2px;padding-left:4px;color:#8b8b89;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.panel.aside .comments .comment .form-content{margin-left:.5em}.panel.aside .comments .comment .actions{float:right;margin-top:-2.25em;margin-right:0}.panel.aside .comments .comment p:before{content:' ';position:absolute;width:1px;height:1px;border-top:10px solid transparent;border-bottom:10px solid transparent;border-right:10px solid #3b3b39;margin-left:-1.8em;margin-top:-.5em}.panel.aside .comments .comment a.delete-comment{border:0}.panel.aside .activity div.actions{margin-top:1em;text-align:center}.panel .navigation{font-size:.9375em}.panel .navigation li{zoom:1}.panel .navigation li:after,.panel .navigation li:before{content:"";display:table}.panel .navigation li:after{clear:both}.panel .navigation li:after,.panel .navigation li:before{content:"";display:table}.panel .navigation li:after{clear:both}.panel .navigation.first{border-bottom:1px dotted #0c0c0a;padding:.625em 0;margin-bottom:.625em}.panel .navigation .row{float:left;width:100%;box-sizing:border-box}.panel .navigation .row:hover{background:#444442}.panel .navigation .row.no-hover:hover{background:0 0}.panel .navigation .row.selected{font-weight:700}.panel .navigation .row .main-cell-wrapper{float:left;width:100%}.panel .navigation .row .main-cell{margin:0 1.5em 0 0}.panel .navigation .row .main-cell h3{border:0;font-size:1em;margin:0 .25em 0 1em;padding:.25em 0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.panel .navigation .row .main-cell h3 a{padding-top:0;padding-bottom:0}.panel .navigation .row .main-cell span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis;display:block}.panel .navigation .row .main-cell a{border:0;padding:.2em .313em;padding-left:1em;color:#fffffc;display:block}.panel .navigation .row .right-cell{float:right;width:1em;height:1em;margin-right:1em;margin-top:-1.5em}.panel .navigation .row .right-cell a{display:none;width:1em;height:1em;padding:.125em;color:#8b8b89;border:1px solid transparent}.panel .navigation .row .right-cell a .svg-icon{position:absolute;width:16px;height:16px;line-height:16px;text-align:center;vertical-align:center}.panel .navigation .row .right-cell a .svg-icon svg{fill:#606060}.panel .navigation .row .right-cell a .svg-icon svg:hover{fill:#2894df}.panel .navigation .row .right-cell a:hover{color:#2894df;background:#30302d}.panel .navigation .row.title .right-cell{margin-top:-1.825em}.panel .navigation .row:hover{background:#444442}.panel .navigation .row:hover .right-cell a{display:block}.contextual-menu{position:absolute;background:#3b3b39;border:1px solid #0c0c0a;width:12em;box-shadow:0 0 10px 0 #000;z-index:993;left:11.25em;display:none;padding:.25em 0}.contextual-menu a{font-size:.875em;display:block;padding:.357em .714em;border:0}.contextual-menu a:hover{color:#fffffc;background:#444442}.contextual-menu li.disabled a{color:#444442}.contextual-menu li.disabled a:hover{color:#444442;background:#3b3b39}.contextual-menu .separator-before{border-top:1px solid #0c0c0a}.contextual-menu .separator-after{border-bottom:1px solid #0c0c0a}.panel.aside{position:absolute;right:2em;min-width:25em;width:30%;bottom:0;height:100%;background:#3b3b39;box-shadow:0 0 10px 0 #000}.panel.aside .sidebar-header{zoom:1}.panel.aside .sidebar-header:after,.panel.aside .sidebar-header:before{content:"";display:table}.panel.aside .sidebar-header:after{clear:both}.panel.aside .sidebar-header:after,.panel.aside .sidebar-header:before{content:"";display:table}.panel.aside .sidebar-header:after{clear:both}.panel.aside .sidebar-header h3{float:left;width:67%;margin-top:.88em;padding:0 0 0 .6em;display:table-cell;vertical-align:middle;border:0}.panel.aside .sidebar-header h3 .title-wrapper{width:100%;line-height:1.2em;font-size:1.2em}.panel.aside .sidebar-header h3 .title-wrapper .name{max-width:85%;float:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.panel.aside .sidebar-header h3 .title-wrapper .title-link{border:0;color:#cacac9;font-size:.875em;margin-left:.429em;vertical-align:bottom}.panel.aside .sidebar-header h3 .title-wrapper .title-link:hover{color:#2894df}.panel.aside .sidebar-header h3 .title-wrapper .title-link .svg-icon{margin-top:.25em}.panel.aside .sidebar-header h3 .subtitle{font-size:.8em;color:#8b8b89;font-weight:400;display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.panel.aside .sidebar-header .teaser-image{margin:1em 0 1em 1em;float:left;width:3em;height:3em;border:1px solid #8b8b89;line-height:3em;text-align:center;border-radius:50%}.panel.aside .sidebar-header .teaser-image .svg-icon svg{width:1.5rem;height:1.5rem;margin-top:.75em}.panel.aside .sidebar-header .teaser-image img{width:3em;height:3em}.panel.aside .sidebar-header .dialog-close{position:absolute;right:1em;top:1em}.panel.aside .sidebar-section{margin-top:-1px;border-bottom:1px solid #0c0c0a;border-top:1px solid #0c0c0a;font-size:.875em;background:#3b3b39}.panel.aside .sidebar-section h4{font-size:1.125em;padding:1em 1.125em;margin:0;display:block}.panel.aside .sidebar-section .accordion-content{padding:1.25em 1.25em 1.5em 1.25em;background:#30302d}.panel.aside .sidebar-section .accordion-content .section-action{font-size:1.125em;position:absolute;margin-top:-3.1em;right:3.25em;width:1.125em;height:1.125em;z-index:401;border:0}.panel.aside .detailed-information li{zoom:1;padding-bottom:.25em}.panel.aside .detailed-information li:after,.panel.aside .detailed-information li:before{content:"";display:table}.panel.aside .detailed-information li:after{clear:both}.panel.aside .detailed-information li:after,.panel.aside .detailed-information li:before{content:"";display:table}.panel.aside .detailed-information li:after{clear:both}.panel.aside .detailed-information li span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.panel.aside .detailed-information li .label{width:30%;display:inline-block;margin-right:10%;float:left;height:1.8em}.panel.aside .detailed-information li .value{width:60%;display:inline-block;float:left;height:1.8em}.panel.aside .description-content{white-space:pre-line}.panel.aside .description-editor .actions{margin:.5em 0 1em 0}.panel.aside .description-editor .actions .description-lock{float:left}.panel.aside .description-editor .actions .description-lock span.tooltip.tooltip-right{white-space:normal}.panel.aside .description-editor .actions .description-lock span.tooltip.tooltip-right .svg-icon{overflow:initial}.panel.aside .key-information .fingerprint .value{height:3.2em}.panel.aside .key-information textarea{min-height:30em;line-height:normal;margin-top:.8em;width:100%;box-sizing:border-box}.panel.aside .key-information li{zoom:1;padding-bottom:.25em}.panel.aside .key-information li:after,.panel.aside .key-information li:before{content:"";display:table}.panel.aside .key-information li:after{clear:both}.panel.aside .key-information li:after,.panel.aside .key-information li:before{content:"";display:table}.panel.aside .key-information li:after{clear:both}.panel.aside .key-information li span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.panel.aside .key-information li .label{width:30%;display:inline-block;margin-right:10%;float:left;height:1.8em}.panel.aside .key-information li .value{width:60%;display:inline-block;float:left;height:1.8em}.panel.aside .form-content{margin:0 0 .5em 0;zoom:1}.panel.aside .form-content:after,.panel.aside .form-content:before{content:"";display:table}.panel.aside .form-content:after{clear:both}.panel.aside .form-content:after,.panel.aside .form-content:before{content:"";display:table}.panel.aside .form-content:after{clear:both}.panel.aside .form-content label{display:none}.panel.aside .form-content textarea{font-size:1em;width:100%;box-sizing:border-box;height:5em;margin-top:0}.panel.aside .form-content .button,.panel.aside .form-content .cancel{float:right;margin-top:-.25em;margin-right:0}.panel.aside .form-content .cancel{margin-right:.5em}.panel.aside .form-content .message.notice{padding:.5em;margin:0;font-size:.929em;background:#2894df}.panel.aside .form-content .message.notice span:last-child{display:inline-block}@media all and (max-width:1024px){.panel.aside{position:absolute;right:0;width:100%;bottom:0;top:0;height:100%}}.tabs{border-bottom:1px solid #0c0c0a;width:100%}.tabs-nav{border-bottom:1px solid #0c0c0a}.tabs-nav li{display:inline}.tabs-nav li div{display:inline-block}.tabs-nav li a{display:inline-block;padding:.4em 1.75em;margin-left:1.25em;font-size:15px;border:0}.tabs-nav li a.selected{margin-bottom:-1px;border:1px solid #0c0c0a;border-bottom:1px solid #30302d;background:#30302d;-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em;border-bottom-left-radius:0;border-bottom-right-radius:0}.tab-content{display:none}.tab-content.selected{display:block}.tableview{background:#222220;position:relative;font-size:.9375em;height:100%}.tableview .tableview-header{background:#3b3b39;height:2.125em;overflow-y:scroll;padding-top:.125em}.tableview .tableview-header table{width:100%}.tableview .tableview-header table th.l-cell.sortable .svg-icon svg,.tableview .tableview-header table th.m-cell.sortable .svg-icon svg{margin-left:.55em}.tableview .tableview-content{top:2.125em;bottom:2.45em;padding:0;position:absolute;width:100%;border-top:1px solid #0c0c0a;overflow-y:scroll;background:#222220}.tableview .tableview-content table{width:100%}.tableview table a{border:0}.tableview table td,.tableview table th{text-align:left;padding:.2em .2em;vertical-align:middle}.tableview table th{font-weight:700}.tableview table td div{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.tableview table .s-cell{width:1em;max-width:1em;min-width:1em}.tableview table .m-cell{width:9em;max-width:9em;vertical-align:middle}.tableview table .l-cell{width:14em;max-width:14em}.tableview table .xl-cell{width:19em;max-width:19em}.tableview table .cell-header{display:flex}.tableview table .cell-header-text{flex:0 1 auto;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.tableview table .cell-header-icon-sort .svg-icon{display:inherit}.tableview table tbody tr{height:2em}.tableview table tbody tr.selected{background:#0e3e60}.tableview table tbody tr:hover{background:#3b3b39}.tableview table tbody tr.selected:hover{background:#0e3e60}.tableview table tbody tr.inactive{color:#8b8b89}.tableview table td.cell-multiple-select,.tableview table th.cell-multiple-select{text-align:center}.tableview table td.cell-multiple-select .input.checkbox label,.tableview table th.cell-multiple-select .input.checkbox label{display:none}.tableview table td.password .secret{float:left;max-width:calc(100% - 1.8rem)}.tableview table td.password .password-view{display:none;position:relative;box-sizing:content-box;margin:0 0 0 .3rem;padding:0 .3rem 0 .3rem;background:0 0;border:0;float:left}.tableview table td.password .password-view.selected{margin-top:.1rem;display:block;background:#222220}.tableview table td.password:hover .password-view{display:block}@supports (-webkit-appearance:none) and (not (overflow:-webkit-marquee)) and (not (-ms-ime-align:auto)) and (not (-moz-appearance:none)){.tableview table td.cell-multiple-select .input.checkbox,.tableview table th.cell-multiple-select .input.checkbox{margin-top:1px}}@media all and (max-width:600px){.tableview table tbody{background:#0e3e60}.tableview table tbody td{display:block}.tableview table tbody td.l-cell,.tableview table tbody td.m-cell,.tableview table tbody td.s-cell,.tableview table tbody td.s2-cell{max-width:90%;width:90%;padding:.5em}.tableview table tbody td.s-cell{display:none}.tableview thead{display:none}.tableview .tableview-content{top:0;border:0;bottom:0}}table.table-info,table.table-info.vertical{border-collapse:separate}table.table-info td,table.table-info.vertical td{padding:.625em;padding-right:1.25em;color:#cacac9}table.table-info td.warning,table.table-info.vertical td.warning{background:#fef0bf}table.table-info td.error,table.table-info.vertical td.error{background:#ffe4e4}table.table-info td .alt.side,table.table-info.vertical td .alt.side{color:#cacac9;float:right}table.table-info td .alt.side:before,table.table-info.vertical td .alt.side:before{content:"\0028"}table.table-info td .alt.side:after,table.table-info.vertical td .alt.side:after{content:"\0029"}table.table-info td+td,table.table-info.vertical td+td{color:#fffffc;padding-right:.625em}table.table-info.horizontal{width:100%;border-collapse:collapse;padding:0;border:1px solid #0c0c0a;margin-bottom:1em}table.table-info.horizontal.with-borders td,table.table-info.horizontal.with-borders th{border-right:1px solid #0c0c0a}table.table-info.horizontal td,table.table-info.horizontal th{padding:.5em .5em .5em .75em;margin:0}table.table-info.horizontal th{border-bottom:1px solid #0c0c0a}table.table-info.horizontal th a{display:block;border:0}table.table-info.horizontal th a.sortable .svg-icon svg{top:.15em;position:relative;margin-left:.5em}table.table-info.horizontal thead tr{background:#3b3b39}table.table-info.horizontal tbody{background:#222220}table.table-info.horizontal tbody tr:nth-child(odd){background:#272725}table.table-info.horizontal tbody tr:hover{background:#3b3b39}.table-info-pagination .pagination-limit{margin-top:-.5em;float:left}.table-info-pagination .pagination-limit label{float:left;font-weight:400}.table-info-pagination .pagination-limit select{float:left;margin:.35em}.table-info-pagination .pagination-pages{float:right}.table-info-pagination .pagination-pages .page-location{float:left}.table-info-pagination .pagination-pages .page-buttons{float:right}.table-info-pagination .pagination-pages .button{float:left;margin:-.5em 0 0 .75em;padding:.35em .75em}.tooltip,[data-tooltip]{position:relative;cursor:pointer}.tooltip:after,.tooltip:before,[data-tooltip]:after,[data-tooltip]:before{position:absolute;visibility:hidden;opacity:0;transition:opacity .2s ease-in-out,visibility .2s ease-in-out,transform .2s cubic-bezier(.71,1.7,.77,1.24);transform:translate3d(0,0,0);pointer-events:none;text-align:center}.always-show:after,.always-show:before,.tooltip:focus:after,.tooltip:focus:before,.tooltip:hover:after,.tooltip:hover:before,[data-tooltip]:focus:after,[data-tooltip]:focus:before,[data-tooltip]:hover:after,[data-tooltip]:hover:before{visibility:visible;opacity:1}.tooltip:before,[data-tooltip]:before{z-index:990;border:6px solid transparent;background:0 0;content:""}.tooltip:after,[data-tooltip]:after{z-index:990;padding:8px;width:160px;background-color:#444442;color:#eeeeec;content:attr(data-tooltip);font-size:14px;line-height:1.2em;font-weight:400;text-shadow:none}.tooltip-top:after,.tooltip-top:before,.tooltip:after,.tooltip:before,[data-tooltip]:after,[data-tooltip]:before{bottom:100%;left:50%}.tooltip-top:before,.tooltip:before,[data-tooltip]:before{margin-left:-6px;margin-bottom:-12px;border-top-color:#444442}.tooltip-top:after,.tooltip:after,[data-tooltip]:after{margin-left:-80px}.tooltip-top.always-show:after,.tooltip-top.always-show:before,.tooltip-top:focus:after,.tooltip-top:focus:before,.tooltip-top:hover:after,.tooltip-top:hover:before,.tooltip.always-show:after,.tooltip.always-show:before,.tooltip:focus:after,.tooltip:focus:before,.tooltip:hover:after,.tooltip:hover:before,[data-tooltip]:focus:after,[data-tooltip]:focus:before,[data-tooltip]:hover:after,[data-tooltip]:hover:before{-webkit-transform:translateY(-12px);-moz-transform:translateY(-12px);transform:translateY(-12px)}.tooltip-left:after,.tooltip-left:before{right:100%;bottom:50%;left:auto}.tooltip-left:before{margin-left:0;margin-right:-12px;margin-bottom:0;border-top-color:transparent;border-left-color:#222220;border-left-color:hsla(0,0%,20%,.9)}.tooltip-left.always-show:after,.tooltip-left.always-show:before,.tooltip-left:focus:after,.tooltip-left:focus:before,.tooltip-left:hover:after,.tooltip-left:hover:before{-webkit-transform:translateX(-12px);-moz-transform:translateX(-12px);transform:translateX(-12px)}.tooltip-bottom:after,.tooltip-bottom:before{top:100%;bottom:auto;left:50%}.tooltip-bottom:before{margin-top:-12px;margin-bottom:0;border-top-color:transparent;border-bottom-color:#222220;border-bottom-color:hsla(0,0%,20%,.9)}.tooltip-bottom.always-show:after,.tooltip-bottom.always-show:before,.tooltip-bottom:focus:after,.tooltip-bottom:focus:before,.tooltip-bottom:hover:after,.tooltip-bottom:hover:before{-webkit-transform:translateY(12px);-moz-transform:translateY(12px);transform:translateY(12px)}.tooltip-right:after,.tooltip-right:before{bottom:50%;left:100%}.tooltip-right:before{margin-bottom:0;margin-left:-12px;border-top-color:transparent;border-right-color:#222220}.tooltip-right.always-show:after,.tooltip-right.always-show:before,.tooltip-right:focus:after,.tooltip-right:focus:before,.tooltip-right:hover:after,.tooltip-right:hover:before{-webkit-transform:translateX(12px);-moz-transform:translateX(12px);transform:translateX(12px)}.tooltip-left:before,.tooltip-right:before{top:3px}.tooltip-left:after,.tooltip-right:after{margin-left:0;margin-bottom:-16px}.tooltip.large:after,[data-tooltip].large:after{width:240px}.tooltip-left.large:after,.tooltip-left.large:before,.tooltip-right.large:after,.tooltip-right.large:before{margin-top:6px}.tooltip-alt{cursor:pointer;display:inline-block;border-bottom:0}.tooltip-alt .tooltip-text{position:absolute;visibility:hidden;width:180px;background-color:#222220;color:#eeeeec;padding:.5em 1em;transition:opacity .2s ease-in-out,visibility .2s ease-in-out,transform .2s cubic-bezier(.71,1.7,.77,1.24);transform:translate3d(0,0,0);z-index:99999}.tooltip-alt .tooltip-text.right{margin-left:1em;margin-top:-1.75em}.tooltip-alt .tooltip-text.right::after{content:" ";position:absolute;top:1.125em;right:100%;margin-top:-5px;border:5px #333 solid;border-color:transparent #222220 transparent transparent}.tooltip-alt:hover .tooltip-text{transform:translateX(12px);visibility:visible;opacity:1}.tooltip svg,.tooltip-alt svg{fill:#cacac9;top:.125em;position:relative}.footer{margin-top:0;position:fixed;bottom:0;padding:1.167em 1.5% 0 .5%;text-align:right;font-size:.75em;width:98%;height:2em;background:#30302d;border-top:1px solid #0c0c0a;z-index:890}.footer .footer-links{width:100%;margin-top:-.25em}.footer .footer-links li{display:inline;margin-right:1.5em}.footer .footer-links li.error-message a{background-color:#30302d;color:#ff6b70}.footer .footer-links li .github-star{display:inline;position:absolute;margin-left:-8em;margin-top:-1px}.footer .footer-links a:not(.gh-btn):not(.gh-count){border:0}.input-password-wrapper{width:100%;box-sizing:border-box;zoom:1}.input-password-wrapper:after,.input-password-wrapper:before{content:"";display:table}.input-password-wrapper:after{clear:both}.input-password-wrapper:after,.input-password-wrapper:before{content:"";display:table}.input-password-wrapper:after{clear:both}.input-password-wrapper .input.password{float:left;width:65.5%}.input-password-wrapper .input.password input[type=password],.input-password-wrapper .input.password input[type=text]{width:calc(100% - 3.5em);box-sizing:border-box}.input-password-wrapper .input.password input[type=password].decrypting{background:transparent url(../../../img/controls/loading_dark.svg) no-repeat 90% center}.input-password-wrapper .actions.inline{float:right;width:30%}.input-password-wrapper .actions.inline .button{float:left;width:3.75em;padding-left:0;padding-right:0}.input-password-wrapper .actions.inline .button+.button{margin-left:.5em}.input-password-wrapper .password-complexity .complexity-text{float:right;clear:right;width:30%;font-size:11px;text-align:left;padding-left:0}.input-password-wrapper .password-complexity.not_available .complexity-text{color:#0c0c0a}.input-password-wrapper .password-complexity .progress{width:65.5%;box-sizing:border-box;border:1px solid #0c0c0a;height:10px;display:block;clear:both;margin:.25em 0 .5em 0;float:left}.input-password-wrapper .password-complexity .progress-bar{background:#000;width:0;height:6px;display:block;float:left;margin:1px}.input-password-wrapper .password-complexity .progress-bar.very-weak{background:#000;width:5%}.input-password-wrapper .password-complexity .progress-bar.weak{background:#d40101;width:10%}.input-password-wrapper .password-complexity .progress-bar.fair{background:#ffbd2e;width:60%}.input-password-wrapper .password-complexity .progress-bar.strong{background:#6c0;width:80%}.input-password-wrapper .password-complexity .progress-bar.very-strong{background:#090;width:99.5%}@media all and (max-width:400px){.input-password-wrapper .input.password{float:left;width:100%}.input-password-wrapper .actions.inline{width:50%;float:left;margin-bottom:.5em}.input-password-wrapper .password-complexity .progress{display:none}.input-password-wrapper .password-complexity .complexity-text{float:left;width:50%;font-size:.831em;margin-top:.5em}.fa.fa-lg{line-height:1}}.colorpicker{margin-top:1.5em}.colorpicker .input.color{float:left;margin-top:70px;margin-left:240px}.colorpicker .input input[type=text].token{width:4em;font-size:1.25em;height:auto;line-height:2em;border:none;-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em;text-align:center}.colorpicker .farbtastic{position:relative}.colorpicker .farbtastic *{position:absolute;cursor:crosshair}.colorpicker .farbtastic,.colorpicker .farbtastic .wheel{width:195px;height:195px}.colorpicker .farbtastic .color,.colorpicker .farbtastic .overlay{top:47px;left:47px;width:101px;height:101px}.colorpicker .farbtastic .wheel{background:url(../../../img/controls/colorpicker/wheel.png) no-repeat;width:195px;height:195px}.colorpicker .farbtastic .overlay{background:url(../../../img/controls/colorpicker/mask.png) no-repeat}.colorpicker .farbtastic .marker{width:17px;height:17px;margin:-8px 0 0 -8px;overflow:hidden;background:url(../../../img/controls/colorpicker/marker.png) no-repeat}.gpgkey.input.textarea textarea{height:24em;width:95%}.dialog .permission-add{background-color:#30302d;padding:0 1em 1em 1em;zoom:1}.dialog .permission-add:after,.dialog .permission-add:before{content:"";display:table}.dialog .permission-add:after{clear:both}.dialog .permission-add:after,.dialog .permission-add:before{content:"";display:table}.dialog .permission-add:after{clear:both}.dialog .permission-edit{padding:0;border-bottom:1px solid #0c0c0a}.permissions.scroll{max-height:19em;margin:0}.permissions .row{zoom:1;font-size:.813em;padding:1em .5em .875em 1em;border-bottom:1px dotted #0c0c0a}.permissions .row:after,.permissions .row:before{content:"";display:table}.permissions .row:after{clear:both}.permissions .row:after,.permissions .row:before{content:"";display:table}.permissions .row:after{clear:both}.permissions .row:last-child{border:0}.permissions .row .avatar img{float:left}.permissions .row .group,.permissions .row .user{width:48%;float:left;padding-left:1em;padding-right:1em}.permissions .row .details{zoom:1}.permissions .row .details:after,.permissions .row .details:before{content:"";display:table}.permissions .row .details:after{clear:both}.permissions .row .details:after,.permissions .row .details:before{content:"";display:table}.permissions .row .details:after{clear:both}.permissions .row .details .name{float:left;max-width:190px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.permissions .row .details .more_details{margin-left:.5em;margin-top:0}.permissions .row .details .email{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis;font-weight:700;padding-bottom:.125em}.permissions .row .permission_changes{color:#8b8b89;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis;display:block;border:0}.permissions .row .rights{float:left;margin-top:.5em;width:27%}.permissions .row .rights select{margin:0;font-size:1em}.permissions .row .actions{float:right;width:3em;margin-top:.55em}.permissions .row .actions a{font-size:1.125em;padding:.25em .5em .2em .5em;border:1px solid transparent}.permissions .row .actions a:hover{border:1px solid #0c0c0a}.permissions .row.permission-updated{background:#5c564c}.dialog .message.empty-permission{font-size:.875em;padding-left:1em}@media all and (max-width:480px){.dialog .permissions.scroll{height:auto;margin:0}.dialog .permissions .row .group,.dialog .permissions .row .user{width:45%}}.accordion .accordion-header{zoom:1}.accordion .accordion-header:after,.accordion .accordion-header:before{content:"";display:table}.accordion .accordion-header:after{clear:both}.accordion .accordion-header:after,.accordion .accordion-header:before{content:"";display:table}.accordion .accordion-header:after{clear:both}.accordion .accordion-header a{color:#fffffc;display:block}.accordion .accordion-header a:hover{color:#2894df}.accordion .accordion-content{zoom:1}.accordion .accordion-content:after,.accordion .accordion-content:before{content:"";display:table}.accordion .accordion-content:after{clear:both}.accordion .accordion-content:after,.accordion .accordion-content:before{content:"";display:table}.accordion .accordion-content:after{clear:both}.accordion .accordion-content .processing-wrapper{background-color:#30302d;padding:.5em 1.5em}.accordion .accordion-content .processing-wrapper .processing-text{position:relative;padding-left:1.5em}.accordion .accordion-content .processing-wrapper .processing-text:before{width:100%;height:100%;position:absolute;content:" ";top:0;left:0;background:transparent url(../../../img/controls/loading_dark.svg) left center no-repeat;background-size:auto 75%}.accordion.closed .accordion-content{display:none}.dialog .accordion .accordion-header{margin-bottom:.5em}.dialog .accordion .accordion-header a{display:inline;border-bottom:0}.accordion.sidebar-section .accordion-header a{border:0}.accordion.sidebar-section .accordion-header a .svg-icon{position:absolute;right:1.5em;margin-top:.25em}.accordion.navigation .accordion-header .main-cell a:before{margin-left:-1.25em;margin-top:.25em;content:"\f0d7";font:normal normal normal 1em FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;width:1em;height:1em;position:absolute;text-align:center}.accordion.navigation .accordion-content>.empty-content{padding:0 0 0 2.5em}.autocomplete-suggestions{text-align:left;cursor:default;border:1px solid #0c0c0a;border-top:0;background:#30302d;box-shadow:0 0 10px 0 #000;position:absolute;display:none;z-index:9999;max-height:120px;overflow:hidden;overflow-y:auto;box-sizing:border-box;width:350px}.autocomplete-suggestions .autocomplete-suggestion{position:relative;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:#fffffc;font-size:.875em;display:block;padding:.357em .714em;border:0}.autocomplete-suggestions .autocomplete-suggestion b{font-weight:400;color:#fffffc}.autocomplete-suggestions .autocomplete-suggestion.selected{background:#3b3b39}/*! +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}div{outline:0}html body .hidden{display:none}.visually-hidden,.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visually-hidden .focusable:active,.visually-hidden .focusable:focus,.visuallyhidden .focusable:active,.visuallyhidden .focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.clearfix:after,.clearfix:before{content:"";display:table}.clearfix:after{clear:both}.rounded{-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem}.ellipsis{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.rotate-right{transform:rotate(-90deg)}html{line-height:normal}body{color:#fffffc;background:#30302d}body.iframe{background:0 0}@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;src:local('Open Sans'),local('OpenSans'),url('../../../fonts/opensans-regular.woff') format('woff')}@font-face{font-family:'Open Sans';font-style:normal;font-weight:700;src:local('Open Sans Bold'),local('OpenSans-Bold'),url('../../../fonts/opensans-bold.woff') format('woff')}body{font-family:'Open Sans',Verdana,sans-serif}.code{font-family:'Courier New',Courier,monospace}html{font-size:62.5%}body{font-size:1.6rem}h1{font-size:2.2rem;line-height:3.2rem}h2{font-size:1.8rem;line-height:2.4rem}h3{font-size:1.6rem;line-height:2.4rem;border-bottom:1px dotted #0c0c0a}p{font-size:1.5rem;line-height:2.2rem;margin-top:0}code{font-size:1.1rem}.font-dim{color:#cacac9}a{outline:0;text-decoration:none;cursor:pointer;border-bottom:1px solid #0c0c0a}a:link,a:visited{color:#fffffc}a:hover{text-decoration:none;cursor:pointer;color:#2894df;border-bottom:1px solid #2894df}a:active,a:focus{outline:0;color:#2894df;border:0}a.no-border,a:hover.no-border{border-bottom:0}a.disabled{outline:0;text-decoration:none;cursor:default;color:#606060;pointer-events:none}ul{padding:0;margin:0}ul li{list-style:none;padding:0;margin:0}.button,button{font-size:1.4rem;padding:.8rem 1.6rem;line-height:1.6rem;margin-right:.8rem;position:relative;display:inline-block;vertical-align:baseline;outline:0;cursor:pointer;text-align:center;text-decoration:none;border-top:1px solid #222220;border-right:1px solid #222220;border-bottom:1px solid #222220;border-left:1px solid #222220;color:#fffffc;font-weight:400;text-shadow:0 1px 0 #000;background:#444442;background-image:linear-gradient(top,#444442,#444442);-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem;box-sizing:border-box}.button.medium{font-size:1.6rem;line-height:2.4rem;padding:.8rem 2.4rem}.button.big{font-size:1.8rem;line-height:3.2rem;padding:.8rem 3.2rem}.button.full-width{width:100%}.button.primary{background:#2894df;border:1px solid #2894df;color:#fff;text-shadow:0 1px 0 rgba(0,0,0,.2)}.button.primary:hover{background:#2a9ceb;border:1px solid #2a9ceb;color:#fff}.button.primary:active,.button.primary:focus{color:#eeeeec;border:1px solid #4271b7;background:#2a9ceb}.button.warning,.button.warning:active,.button.warning:focus,.button.warning:hover{color:#eeeeec;background:#d40101;border:1px solid #d40101;text-shadow:none}.button.warning:active,.button.warning:focus{border:1px solid #92000c}.button.cancel,.button.cancel:active,.button.cancel:focus,.button.cancel:hover,.button.dim,.button.dim:active,.button.dim:focus,.button.dim:hover{background:#30302d;font-weight:400}.button:hover{color:#fffffc;text-decoration:none;background:#444442;background-image:linear-gradient(top,#606060,#606060);border:1px solid #30302d}.button:focus{color:#2894df;border:1px solid #2894df}.button:active{color:#2894df;border:1px solid #2894df;background:#3b3b39;position:relative;box-shadow:inset 0 1px 2px rgba(0,0,0,.2)}.button.disabled{cursor:default;color:#8b8b89;background:#3b3b39;border:1px solid #0c0c0a;text-shadow:none;font-weight:400}.button.disabled:active,.button.disabled:focus,.button.disabled:hover{box-shadow:0 0 0;top:0;color:#8b8b89;background:#3b3b39;border:1px solid #0c0c0a}.button.processing{background:#30302d;border:1px solid #30302d}.button.processing:after{width:100%;height:100%;position:absolute;content:" ";top:-1px;left:-1px;background:#30302d url('../../../img/controls/loading_dark.svg') center center no-repeat;background-size:auto 50%;border:1px solid #0c0c0a;-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem}input[type=submit].button.processing{font-size:0;background:#30302d url('../../../img/controls/loading_dark.svg') center center no-repeat;background-size:auto 50%;border:1px solid #0c0c0a;border-radius:5px}.button-toggle.selected{background:#222220;box-shadow:inset 0 1px 2px rgba(0,0,0,.2);border:1px solid #0c0c0a}.button-group{display:flex;flex-wrap:wrap;justify-content:flex-start}.button-group--nowrap>.button{white-space:nowrap}.button-group>*{flex-grow:1;margin-bottom:.5em}label{font-weight:700;font-size:1.6rem;line-height:2.4rem;padding:.8rem 0;display:block}.required label:after{content:" \002A";color:#ff6b70;font-weight:700}.input.error label{color:#ff6b70}input[type=email],input[type=number],input[type=password],input[type=search],input[type=text],textarea{box-sizing:border-box;font-size:1.6rem;line-height:2.4rem;color:#fffffc;background:#0c0c0a;width:100%;max-width:64rem;padding:.6rem 1.2rem;display:inline-block;margin-bottom:.8rem;border:1px solid #222220;border-top:1px solid #30302d;vertical-align:middle}::-ms-reveal{display:none}input[type=number]{box-sizing:border-box}input[type=file]{box-sizing:border-box;font-size:1.6rem;line-height:2.4rem;color:#fffffc;background:#0c0c0a;width:100%;max-width:64rem;display:inline-block;margin-bottom:.8rem;vertical-align:middle}input[type=email]:hover,input[type=number]:hover,input[type=password]:hover,input[type=search]:hover,input[type=text]:hover,textarea:hover{border:1px solid #30302d}input[type=email]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=text]:focus,textarea:focus{outline:0;box-shadow:inset 1px 1px 2px rgba(0,0,0,.2);border:1px solid #2894df}input[type=email]:disabled,input[type=number]:disabled,input[type=password]:disabled,input[type=search]:disabled,input[type=text]:disabled,textarea:disabled{color:#fffffc;border:1px solid #0c0c0a;box-shadow:0 0;cursor:default;background:#444442}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{opacity:1}textarea{height:9rem}.textarea.large textarea{width:30rem;height:9rem}.checkbox input[type=checkbox]{position:absolute;opacity:0;cursor:pointer}.checkbox input[type=checkbox].error{color:#d40101}.checkbox input[type=checkbox]+label{font-weight:400;position:relative;cursor:pointer;padding:0;margin-bottom:.8rem}.checkbox input[type=checkbox]+label:before{content:'';margin-top:.4rem;margin-right:1rem;display:inline-block;vertical-align:text-top;width:1.4rem;height:1.4rem;background:#222220;border:1px solid #606060;border-radius:.3rem;box-sizing:border-box}.checkbox input[type=checkbox]:hover+label:before{box-shadow:0 .1rem 0 #000,inset 0 .1rem 0 rgba(255,255,255,.25)}.checkbox input[type=checkbox]:focus+label:before{box-shadow:0 0 .4rem #4271b7;border:1px solid #4271b7}.checkbox input[type=checkbox]:active+label:before{box-shadow:inset 0 -.1rem 0 rgba(255,255,255,.25),inset 0 .1rem 0 #000}.checkbox input[type=checkbox]:disabled+label{color:#0c0c0a;cursor:auto}.checkbox input[type=checkbox]:disabled+label:before{box-shadow:none;background:#000;border:none}.checkbox input[type=checkbox]:checked+label:after{content:'';position:absolute;left:.2rem;top:.75rem;width:1rem;height:1rem;background-size:1rem 1rem;background-image:url('../../../img/controls/check_white.svg');-webkit-mask-image:url('../../../img/controls/check_white.svg');mask-image:url('../../../img/controls/check_white.svg')}.checkbox input[type=checkbox]:disabled+label:after{background:#30302d}.checkbox.medium input[type=checkbox]{cursor:pointer}.checkbox.medium input[type=checkbox]+label{font-size:1.5rem}.checkbox.medium input[type=checkbox]+label:before{margin-top:.3rem;margin-right:1rem;width:1.4rem;height:1.4rem}.checkbox.medium input[type=checkbox]:checked+label:after{left:.2rem;top:.7rem;width:1rem;height:1rem;background-size:1rem 1rem}.checkbox.small input[type=checkbox]{cursor:pointer}.checkbox.small input[type=checkbox]+label{font-size:1.4rem}.checkbox.small input[type=checkbox]+label:before{margin-top:.3rem;margin-right:1rem;width:1.4rem;height:1.4rem}.checkbox.small input[type=checkbox]:checked+label:after{left:.2rem;top:.7rem;width:1rem;height:1rem;background-size:1rem 1rem}.radiolist{margin-bottom:.8rem}.radiolist:after,.radiolist:before{content:"";display:table}.radiolist:after{clear:both}.radiolist:after,.radiolist:before{content:"";display:table}.radiolist:after{clear:both}.radiolist .input.radio{float:left}.radiolist .input.radio input{float:left;margin-top:.8rem}.radiolist .input.radio input+label{float:left;font-weight:400;display:inline-block;margin-left:1rem;font-size:1.6rem;margin-right:3.2rem;line-height:1.4rem}.radiolist .input.radio input+label:after{content:initial}.input.toggle-switch{display:flex;padding-top:1.6rem;padding-bottom:.8rem}.input.toggle-switch label{padding-top:0;padding-left:1.6rem;order:2;flex:1;font-weight:400;font-size:1.6rem}.input.toggle-switch.disabled label{color:#8b8b89}.input.toggle-switch .toggle-switch-checkbox{order:-1;flex:0 0 3em;display:none}.input.toggle-switch .toggle-switch-checkbox,.input.toggle-switch .toggle-switch-checkbox *,.input.toggle-switch .toggle-switch-checkbox :after,.input.toggle-switch .toggle-switch-checkbox :before,.input.toggle-switch .toggle-switch-checkbox+.toggle-switch-button,.input.toggle-switch .toggle-switch-checkbox:after,.input.toggle-switch .toggle-switch-checkbox:before{box-sizing:border-box}.input.toggle-switch .toggle-switch-checkbox ::selection,.input.toggle-switch .toggle-switch-checkbox :after::selection,.input.toggle-switch .toggle-switch-checkbox :before::selection,.input.toggle-switch .toggle-switch-checkbox+.toggle-switch-button::selection,.input.toggle-switch .toggle-switch-checkbox::selection,.input.toggle-switch .toggle-switch-checkbox:after::selection,.input.toggle-switch .toggle-switch-checkbox:before::selection{background:0 0}.input.toggle-switch .toggle-switch-button{order:-1;flex:none;outline:0;display:block;width:3em;height:1.5em;position:relative;cursor:pointer;user-select:none;background:#444442;border-radius:2em;padding:2px;transition:all .4s ease;border:1px solid #0c0c0a}.input.toggle-switch .toggle-switch-button span{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.input.toggle-switch .toggle-switch-button span .focusable:active,.input.toggle-switch .toggle-switch-button span .focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.input.toggle-switch .toggle-switch-button span .focusable:active,.input.toggle-switch .toggle-switch-button span .focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.input.toggle-switch .toggle-switch-button:hover:after{will-change:padding}.input.toggle-switch .toggle-switch-button:active{box-shadow:inset 0 0 0 2em #e8eae9}.input.toggle-switch .toggle-switch-button:active:after{padding-right:.8em}.input.toggle-switch .toggle-switch-button:after,.input.toggle-switch .toggle-switch-button:before{position:relative;display:block;content:"";width:50%;height:100%}.input.toggle-switch .toggle-switch-button:after{left:0;border-radius:2em;background:#fff;transition:left .3s cubic-bezier(.175, .885, .32, 1.275),padding .3s ease,margin .3s ease;box-shadow:0 0 0 1px rgba(0,0,0,.1),0 4px 0 rgba(0,0,0,.08)}.input.toggle-switch .toggle-switch-button:before{display:none}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button:after{left:50%}.input.toggle-switch .toggle-switch-checkbox:disabled+.toggle-switch-button{background:#30302d;cursor:not-allowed}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button{background:#6c0}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button:active{box-shadow:none}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button:active:after{margin-left:-.8em}.input.toggle-switch .toggle-switch-checkbox:disabled:checked+.toggle-switch-button{background:#e6ffcc}h1 .input.toggle-switch,h2 .input.toggle-switch,h3 .input.toggle-switch,h4 .input.toggle-switch,h5 .input.toggle-switch,h6 .input.toggle-switch{float:left;padding:.4em 1em 0 0}.input.select select{display:inline-block;font-size:1.6rem;line-height:2.4rem;color:#fffffc;padding:.6rem 1.2rem;width:100%;max-width:64rem;box-sizing:border-box;margin:0 0 .8rem 0;border:1px solid #222220;-moz-appearance:none;-webkit-appearance:none;appearance:none;background-color:#30302d;background-image:url('../../../img/controls/chevron-down_white.svg'),linear-gradient(to bottom,#0c0c0a 0,#0c0c0a 100%);background-repeat:no-repeat,repeat;background-position:right .6rem top 50%,0 0;background-size:1rem auto,100%}.input.select select.medium{width:50%}.input.select select:focus{outline:0;border:1px solid #2894df;border-radius:0;background-image:url('../../../img/controls/chevron-down_blue.svg'),linear-gradient(to bottom,#0c0c0a 0,#0c0c0a 100%)}.input.select select:disabled{background-image:url('../../../img/controls/chevron-down_white.svg'),linear-gradient(to bottom,#444442 0,#444442 100%);color:#8b8b89}.input .error-message{padding:0;font-size:1.4rem;margin-top:.2rem;margin-bottom:1.6rem;background-color:transparent;border:0;font-weight:400;color:#ff6b70;background:#30302d;clear:both}.input .help-message{padding:0;font-size:1.4rem;margin-top:.2rem;margin-bottom:1.6rem;background-color:transparent;border:0;color:#8b8b89;font-weight:400;background:#30302d;clear:both}.singleline{display:flex;max-width:64rem}.singleline:after,.singleline:before{content:"";display:table}.singleline:after{clear:both}.singleline:after,.singleline:before{content:"";display:table}.singleline:after{clear:both}.singleline .input{flex:1}.singleline .input.first-field,.singleline .input:first-child{margin-right:2%}.slider{display:flex;align-items:center}.slider input[type=range]{-webkit-appearance:none;width:100%;height:1px;border-radius:5px;background:#8b8b89;outline:0;opacity:.7;-webkit-transition:.2s;transition:opacity .2s;flex-grow:1}.slider input[type=range]:hover{opacity:1}.slider input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:15px;height:15px;border-radius:50%;border-width:1px;border-style:solid;border-color:#3b3b39;background:#8b8b89;cursor:pointer}.slider input[type=range]::-moz-range-thumb{width:15px;height:15px;border-radius:50%;background:#8b8b89;cursor:pointer}.slider input[type=number]{width:6.5rem;margin-left:1.6rem;padding:.8rem}.svg-icon{display:inline-flex;align-self:center}.svg-icon svg{fill:#FFF;height:1em;width:1em}.svg-icon.baseline svg{top:.125em;position:relative}.svg-icon.dim svg,.svg-icon.light svg{fill:#EEEEEC}.svg-icon.icon-only svg{padding:.1rem;height:1.6rem;width:1.6rem}a:hover .svg-icon svg{fill:#2894DF}a.disabled .svg-icon svg{fill:#8B8B89;pointer-events:none;cursor:default}a.disabled:hover .svg-icon svg{fill:#8B8B89}a.fav .svg-icon svg{fill:#D40101}a.unfav .svg-icon svg{fill:#8B8B89}.button .svg-icon svg{top:.2rem;position:relative}.button .svg-icon+span:not(.visuallyhidden){margin-left:.8rem;display:inline-block}.button .svg-icon svg:hover,.button:hover .svg-icon svg{fill:#FFFFFC}.button.primary:active .svg-icon svg,.button.primary:focus .svg-icon svg,.button.warning .svg-icon svg,.button.warning:active .svg-icon svg,.button.warning:focus .svg-icon svg,.button.warning:hover .svg-icon svg{fill:#EEEEEC}.button:active .svg-icon svg,.button:focus .svg-icon svg{fill:#2894DF}.button.disabled .svg-icon svg{fill:#8B8B89}.button.disabled .svg-icon svg:hover{fill:#8B8B89}@keyframes drawCircle{0%{stroke-dashoffset:180px}100%{stroke-dashoffset:0}}@keyframes drawCheck{0%{stroke-dashoffset:50px}100%{stroke-dashoffset:0}}@keyframes drawCross{0%{stroke-dashoffset:50px}100%{stroke-dashoffset:0}}@keyframes drawWarning{0%{stroke-dashoffset:230px}100%{stroke-dashoffset:0}}.icon-feedback .success-animation-circle{stroke-dasharray:180px 180px;stroke:#009900}.icon-feedback .success-animation-line{stroke-dasharray:50px 50px;stroke:#009900}.icon-feedback .success-animation-line2{stroke-dasharray:50px 50px;stroke:#009900}.icon-feedback .error-animation-circle{stroke-dasharray:180px 180px;stroke:#D40101}.icon-feedback .error-animation-line{stroke-dasharray:50px 50px;stroke:#D40101}.icon-feedback .warning-animation-line{stroke-dasharray:230px 230px;stroke:#9F6000;stroke-linecap:round;stroke-linejoin:round}.icon-feedback .warning-animation-circle{fill:#9F6000}.icon-feedback .animated{animation:.75s ease-out 0s 1 both pop}.icon-feedback .animated .error-animation-circle,.icon-feedback .animated .success-animation-circle,.icon-feedback .animated .warning-animation-circle{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawCircle}.icon-feedback .animated .success-animation-line{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawCheck}.icon-feedback .animated .error-animation-line{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawCross}.icon-feedback .animated .warning-animation-line{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawWarning}table{border-collapse:collapse;border-spacing:0}table td,table th{text-align:left;font-weight:400}.logo{background:transparent url('../../../img/logo/logo_white.svg') 0 0 no-repeat;background-size:20rem auto;width:20rem;height:4.5rem}.scroll{overflow-y:scroll;background:linear-gradient(#30302d 30%,rgba(255,255,255,0)),linear-gradient(rgba(255,255,255,0),#30302d 70%) 0 100%,radial-gradient(farthest-side at 75% 0,rgba(0,0,0,.2),rgba(0,0,0,0)),radial-gradient(farthest-side at 75% 100%,rgba(0,0,0,.2),rgba(0,0,0,0)) 0 100%;background-repeat:no-repeat;background-color:#30302d;background-size:100% 10px,100% 10px,100% 5px,100% 5px}::-webkit-scrollbar{width:1em}::-webkit-scrollbar-track{background:#30302d;border-top:0;border-bottom:0}::-webkit-scrollbar-thumb{background:#606060;border-radius:1em;border:4px solid #30302d}.scroll-shadow{overflow:auto;background:linear-gradient(#30302d 30%,rgba(255,255,255,0)),linear-gradient(rgba(255,255,255,0),#30302d 70%) 0 100%,radial-gradient(farthest-side at 50% 0,rgba(0,0,0,.08),rgba(0,0,0,0)),radial-gradient(farthest-side at 50% 100%,rgba(0,0,0,.08),rgba(0,0,0,0)) 0 100%;background-repeat:no-repeat;background-color:#30302d;background-size:100% 40px,100% 40px,100% 14px,100% 14px;background-attachment:local,local,scroll,scroll}.simplebar-content{overflow:auto;background:linear-gradient(#30302d 30%,rgba(255,255,255,0)),linear-gradient(rgba(255,255,255,0),#30302d 70%) 0 100%,radial-gradient(farthest-side at 50% 0,rgba(0,0,0,.08),rgba(0,0,0,0)),radial-gradient(farthest-side at 50% 100%,rgba(0,0,0,.08),rgba(0,0,0,0)) 0 100%;background-repeat:no-repeat;background-color:#30302d;background-size:100% 40px,100% 40px,100% 14px,100% 14px;background-attachment:local,local,scroll,scroll}[data-simplebar]{position:relative;flex-direction:column;flex-wrap:wrap;justify-content:flex-start;align-content:flex-start;align-items:flex-start;width:inherit;height:inherit;max-width:inherit;max-height:inherit}.simplebar-wrapper{overflow:hidden;width:inherit;height:inherit;max-width:inherit;max-height:inherit}.simplebar-mask{direction:inherit;position:absolute;overflow:hidden;padding:0;margin:0;left:0;top:0;bottom:0;right:0;width:auto!important;height:auto!important;z-index:0}.simplebar-offset{direction:inherit!important;box-sizing:inherit!important;resize:none!important;position:absolute;top:0;left:0;bottom:0;right:0;padding:0;margin:0;-webkit-overflow-scrolling:touch}.simplebar-content-wrapper{direction:inherit;box-sizing:border-box!important;position:relative;display:block;height:100%;width:auto;max-width:100%;max-height:100%;scrollbar-width:none;-ms-overflow-style:none}.simplebar-content-wrapper::-webkit-scrollbar,.simplebar-hide-scrollbar::-webkit-scrollbar{width:0;height:0}.simplebar-content:after,.simplebar-content:before{content:' ';display:table}.simplebar-placeholder{max-height:100%;max-width:100%;width:100%;pointer-events:none}.simplebar-height-auto-observer-wrapper{box-sizing:inherit!important;height:100%;width:inherit;max-width:1px;position:relative;float:left;max-height:1px;overflow:hidden;z-index:-1;padding:0;margin:0;pointer-events:none;flex-grow:inherit;flex-shrink:0;flex-basis:0}.simplebar-height-auto-observer{box-sizing:inherit;display:block;opacity:0;position:absolute;top:0;left:0;height:1000%;width:1000%;min-height:1px;min-width:1px;overflow:hidden;pointer-events:none;z-index:-1}.simplebar-track{z-index:1;position:absolute;right:0;bottom:0;pointer-events:none;overflow:hidden}[data-simplebar].simplebar-dragging .simplebar-content{pointer-events:none;user-select:none;-webkit-user-select:none}[data-simplebar].simplebar-dragging .simplebar-track{pointer-events:all}.simplebar-scrollbar{position:absolute;right:2px;width:7px;min-height:10px}.simplebar-scrollbar:before{position:absolute;content:'';background:#000;border-radius:7px;left:0;right:0;opacity:0;transition:opacity .2s linear}.simplebar-scrollbar.simplebar-visible:before{opacity:.5;transition:opacity 0s linear}.simplebar-track.simplebar-vertical{top:0;width:11px}.simplebar-track.simplebar-vertical .simplebar-scrollbar:before{top:2px;bottom:2px}.simplebar-track.simplebar-horizontal{left:0;height:11px}.simplebar-track.simplebar-horizontal .simplebar-scrollbar:before{height:100%;left:2px;right:2px}.simplebar-track.simplebar-horizontal .simplebar-scrollbar{right:auto;left:0;top:2px;height:7px;min-height:0;min-width:10px;width:auto}[data-simplebar-direction=rtl] .simplebar-track.simplebar-vertical{right:auto;left:0}.hs-dummy-scrollbar-size{direction:rtl;position:fixed;opacity:0;visibility:hidden;height:500px;width:500px;overflow-y:hidden;overflow-x:scroll}.simplebar-hide-scrollbar{position:fixed;left:0;visibility:hidden;overflow-y:scroll;scrollbar-width:none;-ms-overflow-style:none}.shimmer{display:block;position:relative;width:100%;height:100%;content:'';animation:shimmer 2s infinite;background:linear-gradient(45deg,rgba(48,48,45,0) 0,rgba(48,48,45,.1) 30%,rgba(48,48,45,.5) 50%,rgba(48,48,45,0))}@keyframes shimmer{0%{background-position:0 0}100%{background-position:1000px 0}}html{scroll-behavior:smooth}.animated{-webkit-animation-duration:.5s;animation-duration:.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both}@keyframes fadeInUp{0%{opacity:0;-webkit-transform:translateY(20px);-ms-transform:translateY(20px);transform:translateY(20px)}100%{opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}}@keyframes fadeOutUp{from{opacity:1}to{opacity:0;transform:translate3d(0,-100%,0)}}@keyframes fadeInDown{from{opacity:0;transform:translate3d(0,-100%,0)}to{opacity:1;transform:none}}@keyframes pop{0%{opacity:0;transform:scale(1)}55%{opacity:1;transform:scale(1)}65%{opacity:1;transform:scale(1.25)}75%{opacity:1;transform:scale(1)}100%{opacity:1;transform:scale(1)}}.fadeInDown{animation-name:fadeInDown}.fadeOutUp{animation-name:fadeOutUp}.fadeInUp{animation-name:fadeInUp}.pop{animation-name:pop}.page{width:100%;min-width:32rem;top:0;left:0;right:0;bottom:0;position:absolute;overflow:auto}.page .panel{height:100%;width:100%;position:absolute;overflow:auto}.page .panel.main{bottom:3.8rem;height:auto;padding:0}.page .panel.main .grid{padding-bottom:1.6rem}.page .panel.main .row{padding-left:0}.page .panel.main .panel.left{background:#353532}.page .header.second+.panel.main{top:6.875em}.page .header.third+.panel.main{top:10em;border-top:1px solid #0c0c0a}.page .header{width:100%;overflow:hidden}.page .header.first{min-height:3.2rem}.page .header.second{height:7rem}.page .header.third{height:4.8rem}.page .col1,.page .col2,.page .col2_3,.page .col3{position:absolute}.page .col1,.page .panel.left{width:17.25%;box-sizing:border-box}.page .panel.middle{width:82%;left:18%;overflow:hidden}.page .panel.middle.scroll{overflow-y:scroll}.page .col2{width:70%;left:18%}.page .col3,.page .panel.right{width:24%;left:75%}.page .col2_3{width:81.5%;left:18%}.page .panel.main .grid-responsive-12{float:left;width:100%;max-width:none;padding-left:.25em;box-sizing:border-box}@media all and (max-width:1024px){.page .panel.left{display:none}.page .header.second .col2{display:none}.page .header.second .col2_3{display:none}.page .header.third{height:4.5em}.page .header.third .col1,.page .header.third .col2,.page .header.third .col2_3{position:relative;float:left;left:auto;width:auto}.page .header.third .col3{display:none}.page .panel.main .panel.left{display:none}.page .panel.main .panel.middle{width:100%;left:0}.page .panel.main .row{padding-left:.5em}.page .panel.main .panel.aside{min-width:auto}}.grid,.grid-responsive-12{margin:0 auto;padding:0;max-width:1220px}.grid .row,.grid-responsive-12 .row{padding:0 .5em 0 .5em}.grid .row:after,.grid .row:before,.grid-responsive-12 .row:after,.grid-responsive-12 .row:before{content:"";display:table}.grid .row:after,.grid-responsive-12 .row:after{clear:both}.grid .row:after,.grid .row:before,.grid-responsive-12 .row:after,.grid-responsive-12 .row:before{content:"";display:table}.grid .row:after,.grid-responsive-12 .row:after{clear:both}.grid .row .col1,.grid .row .col10,.grid .row .col11,.grid .row .col12,.grid .row .col2,.grid .row .col3,.grid .row .col4,.grid .row .col5,.grid .row .col6,.grid .row .col7,.grid .row .col8,.grid .row .col9,.grid-responsive-12 .row .col1,.grid-responsive-12 .row .col10,.grid-responsive-12 .row .col11,.grid-responsive-12 .row .col12,.grid-responsive-12 .row .col2,.grid-responsive-12 .row .col3,.grid-responsive-12 .row .col4,.grid-responsive-12 .row .col5,.grid-responsive-12 .row .col6,.grid-responsive-12 .row .col7,.grid-responsive-12 .row .col8,.grid-responsive-12 .row .col9{position:relative;left:auto;float:none;width:99%;box-sizing:border-box}.grid .row .col10:after,.grid .row .col10:before,.grid .row .col11:after,.grid .row .col11:before,.grid .row .col12:after,.grid .row .col12:before,.grid .row .col1:after,.grid .row .col1:before,.grid .row .col2:after,.grid .row .col2:before,.grid .row .col3:after,.grid .row .col3:before,.grid .row .col4:after,.grid .row .col4:before,.grid .row .col5:after,.grid .row .col5:before,.grid .row .col6:after,.grid .row .col6:before,.grid .row .col7:after,.grid .row .col7:before,.grid .row .col8:after,.grid .row .col8:before,.grid .row .col9:after,.grid .row .col9:before,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col10:before,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col11:before,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col12:before,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col1:before,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col2:before,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col3:before,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col4:before,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col5:before,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col6:before,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col7:before,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col8:before,.grid-responsive-12 .row .col9:after,.grid-responsive-12 .row .col9:before{content:"";display:table}.grid .row .col10:after,.grid .row .col11:after,.grid .row .col12:after,.grid .row .col1:after,.grid .row .col2:after,.grid .row .col3:after,.grid .row .col4:after,.grid .row .col5:after,.grid .row .col6:after,.grid .row .col7:after,.grid .row .col8:after,.grid .row .col9:after,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col9:after{clear:both}.grid .row .col10:after,.grid .row .col10:before,.grid .row .col11:after,.grid .row .col11:before,.grid .row .col12:after,.grid .row .col12:before,.grid .row .col1:after,.grid .row .col1:before,.grid .row .col2:after,.grid .row .col2:before,.grid .row .col3:after,.grid .row .col3:before,.grid .row .col4:after,.grid .row .col4:before,.grid .row .col5:after,.grid .row .col5:before,.grid .row .col6:after,.grid .row .col6:before,.grid .row .col7:after,.grid .row .col7:before,.grid .row .col8:after,.grid .row .col8:before,.grid .row .col9:after,.grid .row .col9:before,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col10:before,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col11:before,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col12:before,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col1:before,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col2:before,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col3:before,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col4:before,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col5:before,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col6:before,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col7:before,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col8:before,.grid-responsive-12 .row .col9:after,.grid-responsive-12 .row .col9:before{content:"";display:table}.grid .row .col10:after,.grid .row .col11:after,.grid .row .col12:after,.grid .row .col1:after,.grid .row .col2:after,.grid .row .col3:after,.grid .row .col4:after,.grid .row .col5:after,.grid .row .col6:after,.grid .row .col7:after,.grid .row .col8:after,.grid .row .col9:after,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col9:after{clear:both}.grid .row .col1 img,.grid .row .col10 img,.grid .row .col11 img,.grid .row .col12 img,.grid .row .col2 img,.grid .row .col3 img,.grid .row .col4 img,.grid .row .col5 img,.grid .row .col6 img,.grid .row .col7 img,.grid .row .col8 img,.grid .row .col9 img,.grid-responsive-12 .row .col1 img,.grid-responsive-12 .row .col10 img,.grid-responsive-12 .row .col11 img,.grid-responsive-12 .row .col12 img,.grid-responsive-12 .row .col2 img,.grid-responsive-12 .row .col3 img,.grid-responsive-12 .row .col4 img,.grid-responsive-12 .row .col5 img,.grid-responsive-12 .row .col6 img,.grid-responsive-12 .row .col7 img,.grid-responsive-12 .row .col8 img,.grid-responsive-12 .row .col9 img{width:100%;height:auto;display:block}@media all and (min-width:780px){.grid .row{padding:0 .5em 0 .5em}.grid .row .col1,.grid .row .col10,.grid .row .col11,.grid .row .col12,.grid .row .col2,.grid .row .col3,.grid .row .col4,.grid .row .col5,.grid .row .col6,.grid .row .col7,.grid .row .col8,.grid .row .col9{float:left;position:relative;margin:0 3% 0 0}.grid .row .last{margin-right:0}.grid .row .col1{width:5.5%}.grid .row .col2{width:14%}.grid .row .col3{width:22.5%}.grid .row .col4{width:31%}.grid .row .col5{width:39.5%}.grid .row .col6{width:48%}.grid .row .col7{width:56.5%}.grid .row .col8{width:65%}.grid .row .col9{width:73.5%}.grid .row .col10{width:82%}.grid .row .col11{width:90.5%}.grid .row .col12{width:99%;margin:0}.grid .row .push1{margin-left:5.5%}.grid .row .push2{margin-left:14%}.grid .row .push3{margin-left:22.5%}.grid .row .push4{margin-left:31%}}@media all and (max-width:768px){.hidden-xs{display:none}}.header.third .main-action-wrapper{margin-top:.8rem;padding-left:.8rem}.header.third .main-action-wrapper .button{float:left;font-size:1.4rem;min-width:1rem}.header.third .actions-wrapper{margin-top:.8rem}.header.third .actions-wrapper .button{font-size:1.4rem;min-width:9rem;float:left}.header.third .actions-wrapper li{display:inline}.header.third .actions-wrapper .secondary{float:right}.header.third .actions-wrapper .secondary .button{min-width:1.6rem;padding:.6rem 1.4rem}@media all and (max-width:1024px){.header.third .actions-wrapper .actions.secondary,.header.third .actions-wrapper .dropdown{display:none}}@media all and (max-width:1024px){.header.third .actions-wrapper i,.header.third .main-action-wrapper i{display:none}.header.third .actions-wrapper i+span,.header.third .main-action-wrapper i+span{margin-left:0}.header.third .actions-wrapper .disabled,.header.third .main-action-wrapper .disabled{display:none}}@media all and (max-width:540px){.header.third .actions-wrapper a i,.header.third .main-action-wrapper a i{display:block}.header.third .actions-wrapper a.button,.header.third .main-action-wrapper a.button{min-width:1em;font-size:1em}.header.third .actions-wrapper a i+span,.header.third .main-action-wrapper a i+span{margin-left:0;display:none}.header.third .actions-wrapper .disabled,.header.third .main-action-wrapper .disabled{display:none}}.accordion .accordion-header:after,.accordion .accordion-header:before{content:"";display:table}.accordion .accordion-header:after{clear:both}.accordion .accordion-header:after,.accordion .accordion-header:before{content:"";display:table}.accordion .accordion-header:after{clear:both}.accordion .accordion-header a{color:#fffffc;display:block}.accordion .accordion-header a:hover{color:#2894df}.accordion h3.accordion-header a{line-height:3.2rem;border-bottom:1px dotted #0c0c0a}.accordion h3.accordion-header a .svg-icon{margin-right:.4rem}.accordion .accordion-content:after,.accordion .accordion-content:before{content:"";display:table}.accordion .accordion-content:after{clear:both}.accordion .accordion-content:after,.accordion .accordion-content:before{content:"";display:table}.accordion .accordion-content:after{clear:both}.accordion .accordion-content .processing-wrapper{background-color:#30302d;padding:.5em 1.5em}.accordion .accordion-content .processing-wrapper .processing-text{position:relative;padding-left:1.5em}.accordion .accordion-content .processing-wrapper .processing-text:before{width:100%;height:100%;position:absolute;content:" ";top:0;left:0;background:transparent url('../../../img/controls/loading_dark.svg') left center no-repeat;background-size:auto 75%}.accordion.closed .accordion-content{display:none}.dialog .accordion .accordion-header{margin-bottom:.5em}.dialog .accordion .accordion-header a{display:inline;border-bottom:0}.accordion.sidebar-section .accordion-header a{border:0}.accordion.sidebar-section .accordion-header a .svg-icon{position:absolute;right:1.5em;margin-top:.25em}.accordion.navigation-secondary .accordion-header .main-cell a:before{margin-left:-1.25em;margin-top:.25em;content:"\f0d7";font:normal normal normal 1em FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;width:1em;height:1em;position:absolute;text-align:center}.accordion.navigation-secondary .accordion-content>.empty-content{padding:0 0 0 2.5em}.announcement{margin:0;top:0;position:absolute;height:3.8rem;font-size:1.4rem;text-align:center;background:#fef0bf;color:#000;width:100%}.announcement p{padding:0;margin:.8rem}.announcement a{color:#333;border-bottom:1px solid #444442;display:inline-block;padding-bottom:0;line-height:1.6rem;margin-left:.8rem}.announcement a:link,.announcement a:visited{color:#333}.announcement a:hover{text-decoration:none;cursor:pointer;color:#2894df;border-bottom:1px solid #2894df}.announcement a:active,.announcement a:focus{outline:0;color:#2894df;border:0}.announcement .announcement-close{float:right;border:0;margin-right:1.6rem;margin-top:.4rem}.announcement+#container.page{top:3.8rem}.avatar img{width:36px;height:36px;border-radius:50%}.big.avatar img{width:72px;height:72px;border-radius:50%}.breadcrumbs{height:3.2rem;padding:0;margin-bottom:0;background:#30302d}.breadcrumbs ul{padding-top:.45em;margin-left:.1875em}.breadcrumbs ul li{display:inline-block;font-size:.8em;margin-left:.25em;max-width:25%;float:left}.breadcrumbs ul li:before{content:"\203A";margin-right:.5em}.breadcrumbs ul li:first-child{margin-left:0;padding-left:0}.breadcrumbs ul li:first-child:before{content:""}.breadcrumbs ul a{border:0}.breadcrumbs div.main-cell{display:inline}.panel.middle .breadcrumbs{border-bottom:1px solid #0c0c0a}.dialog-wrapper{position:absolute;width:100%;height:100%;z-index:800;background:rgba(0,0,0,.8);overflow:auto}.dialog{position:relative;max-width:48rem;border:1px solid #0c0c0a;background:#444442;margin:auto;margin-top:1%;margin-bottom:4.8rem;box-shadow:0 0 10px 0 #000}.dialog .dialog-header{padding:.8rem 1.6rem 0 1.6rem;height:4.8rem}.dialog .dialog-header h2{margin:.8rem 0 1rem .4rem;font-size:1.8rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.dialog .dialog-header .dialog-header-subtitle{padding-top:.4rem;padding-left:.8rem;font-size:1.4rem;color:#fffffc}.dialog .dialog-header .tooltip-alt{margin-left:.8rem;font-size:1.2rem;font-weight:400;color:#fffffc;line-height:1em}.dialog .dialog-header .tooltip-alt .tooltip-text{white-space:normal}.dialog .dialog-header .dialog-close{margin-top:-3.6rem}.dialog .form-content{background:#30302d;padding:1rem 2rem 2.4rem 2rem}.dialog .error-message{margin-bottom:1rem}.dialog p{margin-top:.8rem;font-size:1.5rem}.dialog p+.checkbox{padding-bottom:.8rem}.dialog p+.checkbox label{font-size:1.6rem}.dialog label{clear:both;font-size:1.5rem}.dialog input[type=text],.dialog textarea{width:100%;box-sizing:border-box}.dialog input[type=text]:after,.dialog input[type=text]:before,.dialog textarea:after,.dialog textarea:before{content:"";display:table}.dialog input[type=text]:after,.dialog textarea:after{clear:both}.dialog input[type=text]:after,.dialog input[type=text]:before,.dialog textarea:after,.dialog textarea:before{content:"";display:table}.dialog input[type=text]:after,.dialog textarea:after{clear:both}.dialog input+.message,.dialog textarea+.message{display:none}.dialog input+.message.error,.dialog textarea+.message.error{display:block;clear:both;width:100%}.dialog .inline-error{color:#ff6b70;font-weight:700}.dialog .accordion-header a{display:inline}.dialog .submit-wrapper{margin:0;clear:both;width:100%;padding:1.2rem 0}.dialog .submit-wrapper .button,.dialog .submit-wrapper .cancel{float:right;margin-right:1.6rem}.dialog .submit-wrapper .button{box-sizing:border-box;min-width:8rem}.dialog .submit-wrapper .primary{font-size:1.8rem;padding:1.2rem 2.4rem}.dialog .submit-wrapper .cancel{margin-top:.7em;font-size:1.6rem}.dialog-close,.dialog-close:hover{display:block;float:right;border:0}.dialog-close:active{-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem;box-shadow:inset 0 1px 2px rgba(0,0,0,.2)}.dialog-close .fa-close{padding-top:15px;width:30px;height:15px;text-align:center;display:block;vertical-align:baseline;line-height:0;border:1px solid #3b3b39;-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem}.dialog-close .svg-icon{padding:7px 0 7px 0;width:30px;height:15px;text-align:center;display:block;vertical-align:baseline;line-height:15px;border:1px solid #3b3b39;-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem}.dialog-close:hover .fa-close,.dialog-close:hover .svg-icon{border:1px solid #3b3b39;-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem}@media all and (max-width:480px){.dialog{margin:0;border:0;box-shadow:none;width:100%;max-width:100%;margin-bottom:2.5em}}.drag-and-drop,.drag-and-drop-multiple{position:absolute;top:-1000px;background:#444442;border:1px solid #3b3b39;z-index:9999999;padding:.65em;line-height:1em;color:#fff;border-radius:5px}.drag-and-drop svg,.drag-and-drop-multiple svg{position:absolute;margin-top:-.85em;margin-left:.4em}.drag-and-drop svg path,.drag-and-drop-multiple svg path{fill:#FFF}.drag-and-drop span.message,.drag-and-drop-multiple span.message{display:inline;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.drag-and-drop span.message.not-allowed,.drag-and-drop-multiple span.message.not-allowed{padding-left:1.85em}.drag-and-drop-multiple{box-shadow:.25em .25em 0 -1px #3b3b39}.drag-and-drop-multiple .count{position:absolute;top:-9px;right:-3px;font-size:.7em;background:#d40101;color:#fff;width:1.5em;height:1.5em;text-align:center;line-height:1.5em;border-radius:50%;box-shadow:0 0 1px #333}.drop-focus{background-color:#0e3e60}.dropdown{float:left}.dropdown .button.create:focus,.dropdown .button:focus{border:1px solid #0c0c0a;color:#fffffc;background:#30302d;z-index:801;border-bottom:0;-webkit-border-bottom-right-radius:0;-webkit-border-bottom-left-radius:0;-moz-border-radius-bottomright:0;-moz-border-radius-bottomleft:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.dropdown .button.create:focus .svg-icon svg,.dropdown .button:focus .svg-icon svg{fill:#FFFFFC}.dropdown .button.more .svg-icon svg{top:.15em;margin-left:.5em}.dropdown .button.create .svg-icon svg{fill:#FFF}.dropdown .button.create.disabled .svg-icon svg{fill:#8B8B89}.dropdown .dropdown-content{display:none;border:1px solid #0c0c0a;background:#3b3b39;float:left;position:absolute;min-width:13em;z-index:800;padding:.25em 0}.dropdown .dropdown-content.visible{display:block}.dropdown .dropdown-content.right{right:0}.dropdown .dropdown-content li a{display:block;min-width:9em;font-size:.9375em;border:0;padding:.313em .626em}.dropdown .dropdown-content li a:hover{background:#444442;text-decoration:none;color:#fffffc}.dropdown .dropdown-content li.disabled a,.dropdown .dropdown-content li.disabled a:hover{color:#8b8b89}.dropdown .dropdown-content li .separator-before{border-top:1px solid #0c0c0a}.dropdown .dropdown-content li .separator-after{border-bottom:1px solid #0c0c0a}.dropdown .button+.dropdown-content{margin-top:2em;margin-bottom:2em}.footer{margin-top:0;position:fixed;bottom:0;text-align:right;font-size:1.2rem;width:100%;height:3.8rem;background:#30302d;border-top:1px solid #0c0c0a;z-index:890}.footer .footer-links{padding-top:1rem;width:100%}.footer .footer-links li{display:inline;margin-right:1.5em}.footer .footer-links li.error-message a{background-color:#30302d;color:#ff6b70}.footer .footer-links li .github-star{display:inline;position:absolute;margin-left:-8em;margin-top:-1px}.footer .footer-links a:not(.gh-btn):not(.gh-count){border:0}.header{overflow:visible!important}.header.first{background:#222220}.header.second{background:#3b3b39}.header .navigation.primary:after,.header .navigation.primary:before{content:"";display:table}.header .navigation.primary:after{clear:both}.header .navigation.primary:after,.header .navigation.primary:before{content:"";display:table}.header .navigation.primary:after{clear:both}.header .navigation.primary li{padding:.5em;float:left}.header .navigation.primary li:first-child{padding-left:1em}.header .navigation.primary li.right{float:right;margin-right:1em}.header .navigation.primary li a{color:#f3f3f3;text-decoration:none;border:0;display:inline-block}.header .navigation.primary li a:hover{color:#eeeeec}.header .navigation.primary li a:active,.header .navigation.primary li a:focus{color:#2894df}.header .navigation.primary li a.highlighted{background-color:#2894df;padding:0 .5em 0 .5em;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.header .navigation.primary li a.highlighted:active,.header .navigation.primary li a.highlighted:focus{color:#f3f3f3}.header .navigation.primary li .row.selected a{color:#eeeeec}.header .navigation.primary li .row.selected a:focus{color:#2894df}.header .navigation.primary .github-star{display:none;position:absolute;right:1em;top:4px}@media all and (min-width:600px){.header .navigation.primary .github-star{display:block}}.header .logo{margin:1.25em 0 0 1em;max-width:80%}.progress-bar{background:0 0;width:100%;height:2px;display:block}.progress-bar span{background:#d40101;height:2px;display:block}.progress-bar-wrapper{border:1px solid #0c0c0a;height:10px;display:block;margin:2em 0 1em 0;padding:2px}.progress-bar-wrapper .progress-bar.big{width:100%;height:10px;display:block;clear:both}.progress-bar-wrapper .progress-bar.big .progress{background:#d40101;width:5%;display:block;height:10px}.progress-bar-wrapper .progress-bar.big.infinite{background:#d40101}.progress-bar-wrapper .progress-bar.big.infinite .progress{width:100%;overflow:hidden;background:url('../../../img/controls/infinite-bar.gif') repeat-x;-moz-opacity:0.5;-khtml-opacity:0.5;opacity:.5}.progress-details{color:#8b8b89;margin:.5em 0 .5em 0}.progress-details .progress-percent{float:right}.update-loading-bar{position:fixed;display:block;width:100%;bottom:2.35em;z-index:991}.update-loading-bar .progress-bar span{transition:width 2s;transition-timing-function:cubic-bezier(0.45,1.27,0.76,0.9)}.logo.no-img{background:transparent url('../../../img/logo/logo_white.svg') 0 0 no-repeat;background-size:150px auto;width:150px;height:30px}.logo h1{display:none}.logo.bigger{background:transparent url('../../../img/logo/logo_white.svg') 0 0 no-repeat;background-size:200px auto;width:200px;height:45px}.header.second .col1{min-width:200px}@media only screen and (-moz-min-device-pixel-ratio:1.5),only screen and (-o-min-device-pixel-ratio:1.5),only screen and (-webkit-min-device-pixel-ratio:1.5),only screen and (min-devicepixel-ratio:1.5),only screen and (min-resolution:1.5dppx){.logo.no-img{background:transparent url('../../../img/logo/logo_white.svg') 0 0 no-repeat;background-size:150px auto}.logo.bigger{background:transparent url('../../../img/logo/logo_white.svg') 0 0 no-repeat;background-size:200px auto}}.js .message.no-js{display:none}.cookies .message.no-cookies{display:none}.message{padding:1.6rem}.message a{border-bottom:1px solid #0c0c0a}.message a:hover{border-bottom:1px solid #2894df}.message.error{color:#333;background:#ffe4e4}.message.error a:link,.message.error a:visited{color:#333;border-bottom:1px dotted #0c0c0a}.message.error a:hover{color:#333;border-bottom:1px solid #0c0c0a}.message.success{color:#333;background:#f4f4d9}.message.notice{color:#333;background:#2894df}.message.warning{color:#333;background:#fef0bf}.message p:last-child{margin-bottom:0}.message.side-message{margin-left:1.6rem;font-size:1.6rem;margin-right:3.2rem}.message.side-message p,.message.side-message ul{padding-bottom:1.6rem}.feedback-card,.message.animated{background:#30302d;color:#fffffc;display:flex;border:1px solid #0c0c0a;border-radius:3px}.feedback-card .illustration,.message.animated .illustration{flex:0 0 180px}.feedback-card .additional-information,.message.animated .additional-information{flex:1;line-height:180px;margin-top:1.5em;padding-left:1em;padding-right:1em}@media only screen and (max-width:767px){.feedback-card,.message.animated{flex-direction:column}}.notification-container{font-size:.85em;top:0;position:absolute;z-index:991;height:2em;padding-top:1em;width:60%;margin-left:20%}.notification-container .notification{position:relative;left:50%;float:left;clear:both;margin-bottom:1em}.notification-container .notification .message{box-shadow:0 0 10px 0 #000;border-radius:2px;padding:.5em 1em;position:relative;left:-50%;float:left;color:#000;font-weight:400;width:auto}.notification-container .notification .message.warning{color:#333;background:#fef0bf}.notification-container .notification .message .content{margin-right:1em}.notification-container .notification .message .content strong{text-transform:capitalize}form.search{margin-top:1.6rem}form.search label,form.search legend{display:none;width:16rem}form.search input[type=search]{float:left;width:65%;margin-bottom:0;border-right:1px solid #30302d;padding:.5rem 1rem}form.search input[type=search]:active,form.search input[type=search]:focus{border-right:1px solid #2894df}form.search button{height:3.6rem;width:6.5rem;float:left;margin-left:0;border-radius:0 2px 2px 0}form.search button .svg-icon svg{top:.1rem}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{display:none}@media all and (max-width:480px){form.search{display:none}}.tooltip,[data-tooltip]{position:relative;cursor:pointer}.tooltip:after,.tooltip:before,[data-tooltip]:after,[data-tooltip]:before{position:absolute;visibility:hidden;opacity:0;transition:opacity .2s ease-in-out,visibility .2s ease-in-out,transform .2s cubic-bezier(.71, 1.7, .77, 1.24);transform:translate3d(0,0,0);pointer-events:none;text-align:center}.always-show:after,.always-show:before,.tooltip:focus:after,.tooltip:focus:before,.tooltip:hover:after,.tooltip:hover:before,[data-tooltip]:focus:after,[data-tooltip]:focus:before,[data-tooltip]:hover:after,[data-tooltip]:hover:before{visibility:visible;opacity:1}.tooltip:before,[data-tooltip]:before{z-index:990;border:.6rem solid transparent;background:0 0;content:""}.tooltip:after,[data-tooltip]:after{z-index:990;padding:.4rem;width:16rem;background-color:hsla(0,0%,0%,.9);color:#eeeeec;content:attr(data-tooltip);font-size:1.4rem;line-height:2.4rem;font-weight:400;text-shadow:none}.tooltip-top:after,.tooltip-top:before,.tooltip:after,.tooltip:before,[data-tooltip]:after,[data-tooltip]:before{bottom:100%;left:50%}.tooltip-top:before,.tooltip:before,[data-tooltip]:before{margin-left:-.6rem;margin-bottom:-1.2rem;border-top-color:hsla(0,0%,20%,.9)}.tooltip-top:after,.tooltip:after,[data-tooltip]:after{margin-left:-8rem}.tooltip-top.always-show:after,.tooltip-top.always-show:before,.tooltip-top:focus:after,.tooltip-top:focus:before,.tooltip-top:hover:after,.tooltip-top:hover:before,.tooltip.always-show:after,.tooltip.always-show:before,.tooltip:focus:after,.tooltip:focus:before,.tooltip:hover:after,.tooltip:hover:before,[data-tooltip]:focus:after,[data-tooltip]:focus:before,[data-tooltip]:hover:after,[data-tooltip]:hover:before{transform:translateY(-1.2rem)}.tooltip-left:after,.tooltip-left:before{right:100%;bottom:50%;left:auto}.tooltip-left:before{margin-left:0;margin-right:-1.2rem;margin-bottom:0;border-top-color:transparent;border-left-color:hsla(0,0%,20%,.9)}.tooltip-left.always-show:after,.tooltip-left.always-show:before,.tooltip-left:focus:after,.tooltip-left:focus:before,.tooltip-left:hover:after,.tooltip-left:hover:before{transform:translateX(-1.2rem)}.tooltip-bottom:after,.tooltip-bottom:before{top:100%;bottom:auto;left:50%}.tooltip-bottom:before{margin-top:-1.2rem;margin-bottom:0;border-top-color:transparent;border-bottom-color:hsla(0,0%,20%,.9)}.tooltip-bottom.always-show:after,.tooltip-bottom.always-show:before,.tooltip-bottom:focus:after,.tooltip-bottom:focus:before,.tooltip-bottom:hover:after,.tooltip-bottom:hover:before{transform:translateY(1.2rem)}.tooltip-right:after,.tooltip-right:before{bottom:50%;left:100%}.tooltip-right:before{margin-bottom:0;margin-left:-1.2rem;border-top-color:transparent;border-right-color:#222220}.tooltip-right.always-show:after,.tooltip-right.always-show:before,.tooltip-right:focus:after,.tooltip-right:focus:before,.tooltip-right:hover:after,.tooltip-right:hover:before{transform:translateX(1.2rem)}.tooltip-left:before,.tooltip-right:before{top:.3rem}.tooltip-left:after,.tooltip-right:after{margin-left:0;margin-bottom:-1.6rem}.tooltip.large:after,[data-tooltip].large:after{width:240rem}.tooltip-left.large:after,.tooltip-left.large:before,.tooltip-right.large:after,.tooltip-right.large:before{margin-top:.6rem}.tooltip-alt{cursor:pointer;display:inline-block;border-bottom:0}.tooltip-alt .tooltip-text{position:absolute;visibility:hidden;width:180px;background-color:#222220;color:#eeeeec;padding:.5em 1em;transition:opacity .2s ease-in-out,visibility .2s ease-in-out,transform .2s cubic-bezier(.71, 1.7, .77, 1.24);transform:translate3d(0,0,0);z-index:99999}.tooltip-alt .tooltip-text.right{margin-left:1.2rem;margin-top:-2.65rem}.tooltip-alt .tooltip-text.right::after{content:" ";position:absolute;top:1.125em;right:100%;margin-top:-5px;border:5px #333 solid;border-color:transparent #222220 transparent transparent}.tooltip-alt:hover .tooltip-text{transform:translateX(12px);visibility:visible;opacity:1}.tooltip svg,.tooltip-alt svg{fill:#cacac9;top:.125em;position:relative}.user.profile{max-width:16em;float:right;border:1px solid #0c0c0a;margin-top:.5em;background:#3b3b39;overflow:hidden;cursor:pointer;border-radius:3px;background:#444442;background-image:linear-gradient(top,#444442,#444442)}.user.profile .more{background:#444442;background-image:linear-gradient(top,#444442,#444442);border-left:1px dotted #0c0c0a}.user.profile .center-cell-wrapper,.user.profile .left-cell,.user.profile .right-cell{float:left;height:3.125em}.user.profile .center-cell-wrapper{width:100%}.user.profile .center-cell{margin:0 0 0 3.125em;overflow:hidden}.user.profile .left-cell{width:3.125em;margin-left:-100%}.user.profile .right-cell{width:1.6875em;margin-left:-1.75em}.user.profile .picture img{width:2.6em;padding:.28em .35em .35em .35em;border-radius:50%}.user.profile .details{float:left;font-size:.875em;padding:.357em 1em .5em 1em}.user.profile .details .email,.user.profile .details .name{width:11em;float:left;clear:both;text-overflow:clip;word-wrap:break-word;display:inline-block;line-height:normal;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.user.profile .details .name{font-weight:700;padding-top:0}.user.profile .details .email{padding-bottom:0}.user.profile .more a{position:absolute;display:block;border:0;color:#cacac9}.user.profile .more a .svg-icon{margin:16px 0 0 7px;display:inline-block}.user.profile .more a .svg-icon svg{fill:#FFF}.user.profile .more a:hover{color:#000}.user.profile .more a:hover svg{fill:#8B8B89}.user.profile .more a span:last-child{visibility:hidden}.user.profile .dropdown-content{top:59px;max-width:16em;width:100%;background:#3b3b39}.user.profile .dropdown-content.visible{display:block}@media all and (max-width:1024px){.user.profile{display:block;width:auto}.user.profile .center-cell{display:none}.user.profile .right-cell{display:none}}.contextual-menu{position:absolute;background:#3b3b39;border:1px solid #0c0c0a;width:12em;box-shadow:0 0 10px 0 #000;z-index:993;left:11.25em;display:none;padding:.25em 0}.contextual-menu a{font-size:.875em;display:block;padding:.357em .714em;border:0}.contextual-menu a:hover{color:#fffffc;background:#444442}.contextual-menu li.disabled a{color:#444442}.contextual-menu li.disabled a:hover{color:#444442;background:#3b3b39}.contextual-menu .separator-before{border-top:1px solid #0c0c0a}.contextual-menu .separator-after{border-bottom:1px solid #0c0c0a}.navigation-secondary{font-size:.9375em;border-bottom:1px dotted #0c0c0a;padding:.625em 0}.navigation-secondary ul{list-style:none;padding:0}.navigation-secondary li:after,.navigation-secondary li:before{content:"";display:table}.navigation-secondary li:after{clear:both}.navigation-secondary li:after,.navigation-secondary li:before{content:"";display:table}.navigation-secondary li:after{clear:both}.navigation-secondary .row{float:left;width:100%;box-sizing:border-box}.navigation-secondary .row:hover{background:#444442}.navigation-secondary .row.no-hover:hover{background:0 0}.navigation-secondary .row.selected{font-weight:700}.navigation-secondary .row .main-cell-wrapper{float:left;width:100%}.navigation-secondary .row .main-cell{margin:0 1.5em 0 0}.navigation-secondary .row .main-cell h3{border:0;font-size:1em;margin:0 .25em 0 1em;padding:.25em 0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.navigation-secondary .row .main-cell h3 a{padding-top:0;padding-bottom:0}.navigation-secondary .row .main-cell span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis;display:block}.navigation-secondary .row .main-cell a{border:0;padding:.2em .313em;padding-left:1em;color:#fffffc;display:block}.navigation-secondary .row .right-cell{float:right;width:1em;height:1em;margin-right:1em;margin-top:-1.5em}.navigation-secondary .row .right-cell a{display:none;width:1em;height:1em;padding:.125em;color:#8b8b89;border:1px solid transparent}.navigation-secondary .row .right-cell a .svg-icon{position:absolute;width:16px;height:16px;line-height:16px;text-align:center;vertical-align:center}.navigation-secondary .row .right-cell a .svg-icon svg{fill:#606060}.navigation-secondary .row .right-cell a .svg-icon svg:hover{fill:#2894DF}.navigation-secondary .row .right-cell a:hover{color:#2894df;background:#30302d}.navigation-secondary .row.title .right-cell{margin-top:-1.825em}.navigation-secondary .row:hover{background:#444442}.navigation-secondary .row:hover .right-cell a{display:block}iframe{margin:0;padding:0;border:0;outline:0;font-size:100%;vertical-align:baseline;background:0 0}iframe.full-screen{position:absolute;width:100%;height:100%;z-index:999;border:0;top:0;left:0}iframe.cachette{position:absolute;width:1px;height:1px;z-index:999;border:0;bottom:0;right:0}.sidebar-help{margin-top:3.2rem;padding:2.4rem;background-color:#2894df}.sidebar-help.transparent{background-color:transparent;border:1px solid #0c0c0a}.sidebar-help h3{margin:0 0 1.6rem 0;border-bottom:none}.input-password-wrapper{width:100%;box-sizing:border-box}.input-password-wrapper:after,.input-password-wrapper:before{content:"";display:table}.input-password-wrapper:after{clear:both}.input-password-wrapper:after,.input-password-wrapper:before{content:"";display:table}.input-password-wrapper:after{clear:both}.input-password-wrapper .input.password{display:inline-flex;width:65.5%}.input-password-wrapper .input.password input[type=password],.input-password-wrapper .input.password input[type=text]{flex:1;box-sizing:border-box}.input-password-wrapper .input.password input[type=password].decrypting{background:transparent url('../../../img/controls/loading_dark.svg') no-repeat 90% center}.input-password-wrapper .password-view{padding:.8rem 1.4rem;margin-top:0;margin-right:0;margin-left:-.1rem;margin-bottom:.8rem;border-radius:0;background:#0c0c0a;border-left:0;border-top:1px solid #30302d}.input-password-wrapper .password-view.selected{background:#222220}.input-password-wrapper input[type=password]:focus~.button.password-view,.input-password-wrapper input[type=text]:focus~.button.password-view{box-shadow:inset -1px 1px 1px rgba(0,0,0,.2);border:1px solid #2894df;border-left:0}.input-password-wrapper .actions.inline{float:right;width:30%}.input-password-wrapper .actions.inline .button{float:left;width:4.8rem;padding-left:0;padding-right:0}.input-password-wrapper .actions.inline .button+.button{margin-left:.8rem}.input-password-wrapper .password-complexity .complexity-text{float:right;clear:right;width:30%;font-size:11px;text-align:left;padding-left:0}.input-password-wrapper .password-complexity.not_available .complexity-text{color:#0c0c0a}.input-password-wrapper .password-complexity .progress{width:65.5%;box-sizing:border-box;border:1px solid #0c0c0a;height:10px;display:block;clear:both;margin:.25em 0 .5em 0;float:left}.input-password-wrapper .password-complexity .progress-bar{background:#000;width:0;height:6px;display:block;float:left;margin:1px}.input-password-wrapper .password-complexity .progress-bar.very-weak{background:#000;width:5%}.input-password-wrapper .password-complexity .progress-bar.weak{background:#d40101;width:10%}.input-password-wrapper .password-complexity .progress-bar.fair{background:#ffbd2e;width:60%}.input-password-wrapper .password-complexity .progress-bar.strong{background:#6c0;width:80%}.input-password-wrapper .password-complexity .progress-bar.very-strong{background:#090;width:99.5%}@media all and (max-width:400px){.input-password-wrapper .input.password{float:left;width:100%}.input-password-wrapper .actions.inline{width:50%;float:left;margin-bottom:.5em}.input-password-wrapper .password-complexity .progress{display:none}.input-password-wrapper .password-complexity .complexity-text{float:left;width:50%;font-size:.831em;margin-top:.5em}.fa.fa-lg{line-height:1}}.gpgkey.input.textarea textarea{height:24em;width:95%}/*! Chosen, a Select Box Enhancer for jQuery and Prototype by Patrick Filler for Harvest, http://getharvest.com @@ -19,5 +15,4 @@ Full source at https://github.com/harvesthq/chosen Copyright (c) 2011-2018 Harvest http://getharvest.com MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md -This file is generated by `grunt build`, do not edit it by hand. -*/.chosen-container{position:relative;display:inline-block;vertical-align:middle;font-size:13px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.chosen-container *{-webkit-box-sizing:border-box;box-sizing:border-box}.chosen-container .chosen-drop{position:absolute;top:100%;z-index:1010;width:100%;border:1px solid #aaa;border-top:0;background:#30302d;-webkit-box-shadow:0 4px 5px rgba(0,0,0,.15);box-shadow:0 4px 5px rgba(0,0,0,.15);display:none}.chosen-container.chosen-container-single{padding:0;width:83%}.chosen-container.chosen-container-single.connection-type{float:left;width:23%}.chosen-container.chosen-with-drop .chosen-drop{display:block}.chosen-container a{cursor:pointer}.chosen-container .chosen-single .group-name,.chosen-container .search-choice .group-name{margin-right:4px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-weight:400;color:#8b8b89}.chosen-container .chosen-single .group-name:after,.chosen-container .search-choice .group-name:after{content:"\003A";padding-left:2px;vertical-align:top}.chosen-container-single .chosen-single{position:relative;display:block;overflow:hidden;padding:0 0 0 8px;height:25px;border:1px solid #30302d;border-radius:5px;background-color:#30302d;background:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#30302d),color-stop(50%,#3b3b39),color-stop(52%,#3b3b39),to(#444442));background:linear-gradient(#30302d 20%,#3b3b39 50%,#3b3b39 52%,#444442 100%);background-clip:padding-box;-webkit-box-shadow:0 0 3px #30302d inset,0 1px 1px rgba(0,0,0,.1);box-shadow:0 0 3px #30302d inset,0 1px 1px rgba(0,0,0,.1);color:#fffffc;text-decoration:none;white-space:nowrap;line-height:24px}.chosen-container-single .chosen-single input[type=text]{cursor:pointer;opacity:0;position:absolute}.chosen-container-single .chosen-default{color:#8b8b89}.chosen-container-single .chosen-single span{display:block;overflow:hidden;margin-right:26px;text-overflow:ellipsis;white-space:nowrap}.chosen-container-single .chosen-single-with-deselect span{margin-right:38px}.chosen-container-single .chosen-single abbr{position:absolute;top:6px;right:26px;display:block;width:12px;height:12px;background:url(../../../img/third_party/chosen-sprite.png) -42px 1px no-repeat;font-size:1px}.chosen-container-single .chosen-single abbr:hover{background-position:-42px -10px}.chosen-container-single.chosen-disabled .chosen-single abbr:hover{background-position:-42px -10px}.chosen-container-single .chosen-single div{position:absolute;top:0;right:0;display:block;width:18px;height:100%}.chosen-container-single .chosen-single div b{display:block;width:100%;height:100%;background:url(../../../img/third_party/chosen-sprite.png) no-repeat 0 2px}.chosen-container-single .chosen-search{position:relative;z-index:1010;margin:0;padding:3px 4px;white-space:nowrap}.chosen-container-single .chosen-search span.svg-icon{position:absolute;margin-left:-1.5em;margin-top:.35em}.chosen-container-single .chosen-search input[type=text]{margin:1px 0;padding:4px 20px 4px 5px;width:100%;height:auto;outline:0;border:1px solid #30302d;background:url(../../../img/third_party/chosen-sprite.png) no-repeat 100% -20px;font-size:1em;font-family:sans-serif;line-height:normal;border-radius:0}.chosen-container-single .chosen-drop{margin-top:-.5em;border-radius:0 0 4px 4px;background-clip:padding-box}.chosen-container-single.chosen-container-single-nosearch .chosen-search{position:absolute;clip:rect(0,0,0,0)}.chosen-container .chosen-results{color:#fffffc;position:relative;overflow-x:hidden;overflow-y:auto;margin:0 4px 4px 0;padding:0 0 0 4px;max-height:240px;-webkit-overflow-scrolling:touch}.chosen-container .chosen-results li{display:none;margin:0;padding:5px 6px;list-style:none;line-height:15px;word-wrap:break-word;-webkit-touch-callout:none}.chosen-container .chosen-results li.active-result{display:list-item;cursor:pointer}.chosen-container .chosen-results li.disabled-result{display:list-item;color:#444442;cursor:default}.chosen-container .chosen-results li:hover{background-color:#2894df;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#3875d7),color-stop(90%,#2a62bc));background-image:linear-gradient(#3875d7 20%,#2a62bc 90%);color:#eeeeec}.chosen-container .chosen-results li.no-results{color:#cacac9;display:list-item;background:#444442}.chosen-container .chosen-results li.group-result{display:list-item;font-weight:700;cursor:default}.chosen-container .chosen-results li.group-option{padding-left:15px}.chosen-container .chosen-results li em{font-style:normal;text-decoration:underline}.chosen-container-multi .chosen-choices{position:relative;overflow:hidden;margin:0;padding:0 5px;width:100%;height:auto;border:1px solid #30302d;background-color:#30302d;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(1%,#3b3b39),color-stop(15%,#30302d));background-image:linear-gradient(#3b3b39 1%,#30302d 15%);cursor:text}.chosen-container-multi .chosen-choices li{float:left;list-style:none}.chosen-container-multi .chosen-choices li.search-field{margin:0;padding:0;white-space:nowrap}.chosen-container-multi .chosen-choices li.search-field input[type=text]{margin:1px 0;padding:0;height:25px;outline:0;border:0!important;background:0 0!important;-webkit-box-shadow:none;box-shadow:none;color:#8b8b89;font-size:100%;font-family:sans-serif;line-height:normal;border-radius:0;width:25px}.chosen-container-multi .chosen-choices li.search-choice{position:relative;margin:3px 5px 3px 0;padding:3px 20px 3px 5px;border:1px solid #30302d;max-width:100%;border-radius:3px;background-color:#30302d;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#30302d),color-stop(50%,#3b3b39),color-stop(52%,#3b3b39),to(#444442));background-image:linear-gradient(#30302d 20%,#3b3b39 50%,#3b3b39 52%,#444442 100%);background-size:100% 19px;background-repeat:repeat-x;background-clip:padding-box;-webkit-box-shadow:0 0 2px #30302d inset,0 1px 0 rgba(0,0,0,.05);box-shadow:0 0 2px #30302d inset,0 1px 0 rgba(0,0,0,.05);color:#444442;line-height:13px;cursor:default}.chosen-container-multi .chosen-choices li.search-choice span{word-wrap:break-word}.chosen-container-multi .chosen-choices li.search-choice .search-choice-close{position:absolute;top:4px;right:3px;display:block;width:12px;height:12px;background:url(../../../img/third_party/chosen-sprite.png) -42px 1px no-repeat;font-size:1px}.chosen-container-multi .chosen-choices li.search-choice .search-choice-close:hover{background-position:-42px -10px}.chosen-container-multi .chosen-choices li.search-choice-disabled{padding-right:5px;border:1px solid #222220;background-color:#444442;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#30302d),color-stop(50%,#3b3b39),color-stop(52%,#3b3b39),to(#444442));background-image:linear-gradient(#30302d 20%,#3b3b39 50%,#3b3b39 52%,#444442 100%);color:#cacac9}.chosen-container-multi .chosen-choices li.search-choice-focus{background:#3b3b39}.chosen-container-multi .chosen-choices li.search-choice-focus .search-choice-close{background-position:-42px -10px}.chosen-container-multi .chosen-results{margin:0;padding:0}.chosen-container-multi .chosen-drop .result-selected{display:list-item;color:#3b3b39;cursor:default}.chosen-container-active .chosen-single{border:1px solid #2894df;-webkit-box-shadow:0 0 5px rgba(0,0,0,.3);box-shadow:0 0 5px rgba(0,0,0,.3)}.chosen-container-active.chosen-with-drop .chosen-single{border:1px solid #30302d;border-bottom-right-radius:0;border-bottom-left-radius:0;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#3b3b39),color-stop(80%,#30302d));background-image:linear-gradient(#3b3b39 20%,#30302d 80%);-webkit-box-shadow:0 1px 0 #30302d inset;box-shadow:0 1px 0 #30302d inset}.chosen-container-active.chosen-with-drop .chosen-single div{border-left:none;background:0 0}.chosen-container-active.chosen-with-drop .chosen-single div b{background-position:-18px 2px}.chosen-container-active .chosen-choices{border:1px solid #2894df;-webkit-box-shadow:0 0 5px rgba(0,0,0,.3);box-shadow:0 0 5px rgba(0,0,0,.3)}.chosen-container-active .chosen-choices li.search-field input[type=text]{color:#fffffc!important}.chosen-disabled{opacity:.5!important;cursor:default}.chosen-disabled .chosen-single{cursor:default}.chosen-disabled .chosen-choices .search-choice .search-choice-close{cursor:default}.chosen-rtl{text-align:right}.chosen-rtl .chosen-single{overflow:visible;padding:0 8px 0 0}.chosen-rtl .chosen-single span{margin-right:0;margin-left:26px;direction:rtl}.chosen-rtl .chosen-single-with-deselect span{margin-left:38px}.chosen-rtl .chosen-single div{right:auto;left:3px}.chosen-rtl .chosen-single abbr{right:auto;left:26px}.chosen-rtl .chosen-choices li{float:right}.chosen-rtl .chosen-choices li.search-field input[type=text]{direction:rtl}.chosen-rtl .chosen-choices li.search-choice{margin:3px 5px 3px 0;padding:3px 5px 3px 19px}.chosen-rtl .chosen-choices li.search-choice .search-choice-close{right:auto;left:4px}.chosen-rtl.chosen-container-single .chosen-results{margin:0 0 4px 4px;padding:0 4px 0 0}.chosen-rtl .chosen-results li.group-option{padding-right:15px;padding-left:0}.chosen-rtl.chosen-container-active.chosen-with-drop .chosen-single div{border-right:none}.chosen-rtl .chosen-search input[type=text]{padding:4px 5px 4px 20px;background:url(../../../img/third_party/chosen-sprite.png) no-repeat -30px -20px;direction:rtl}.chosen-rtl.chosen-container-single .chosen-single div b{background-position:6px 2px}.chosen-rtl.chosen-container-single.chosen-with-drop .chosen-single div b{background-position:-12px 2px}@media only screen and (-webkit-min-device-pixel-ratio:1.5),only screen and (min-resolution:144dpi),only screen and (min-resolution:1.5dppx){.chosen-container .chosen-results-scroll-down span,.chosen-container .chosen-results-scroll-up span,.chosen-container-multi .chosen-choices .search-choice .search-choice-close,.chosen-container-single .chosen-search input[type=text],.chosen-container-single .chosen-single abbr,.chosen-container-single .chosen-single div b,.chosen-rtl .chosen-search input[type=text]{background-image:url(../../../img/third_party/chosen-sprite@2x.png)!important;background-size:52px 37px!important;background-repeat:no-repeat!important}}.chosen-container{margin-bottom:.5em}.chosen-container.chosen-disabled a.chosen-single{border:1px solid #222220}.chosen-container a.chosen-single{color:#fffffc;display:block;text-overflow:ellipsis;white-space:nowrap;height:38px;width:100%;background:#444442;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;border:1px solid #222220;border-top:1px solid #30302d;box-shadow:none;margin:0 0 .5em 0}.chosen-container a.chosen-single span{font-size:16px;padding-top:3px;margin-top:2px;color:#fffffc}.chosen-container a.chosen-single span.svg-icon{float:left}.chosen-container a.chosen-single div b{background-position:0 6px}.chosen-container .chosen-search input.chosen-search-input[type=text]{width:95%!important;color:#cacac9}.chosen-container .chosen-drop{border:1px solid #222220!important;border-top:0!important;-webkit-border-radius:0!important;-moz-border-radius:0!important;border-radius:0!important;background:#0c0c0a!important}.chosen-container .chosen-drop .chosen-results{color:#fffffc}.chosen-container.chosen-container-active.chosen-with-drop a.chosen-single{background:#0c0c0a;border:1px solid #222220;border-top:1px solid #30302d;box-shadow:none}.chosen-container.chosen-container-active.chosen-with-drop a.chosen-single div b{background-position:-18px 8px}.singleline.connection_info.protocol_host_port .input.text.field_protocol_host{border:0;padding:0;width:70%}.singleline.connection_info.protocol_host_port .input.text.field_protocol_host .input.text{padding:0;margin:0}.singleline.connection_info.protocol_host_port .input.text.field_protocol_host .input.text input{margin:0;border-left:none}.singleline.connection_info.protocol_host_port .input.text.field_protocol_host .input.text.host{width:75%}.singleline.connection_info.protocol_host_port .input.text.field_protocol_host .input.text.host input[type=text]{width:95%}.singleline.connection_info.protocol_host_port .input.text.field_protocol_host .input.text.protocol{width:25%;position:relative}.singleline.connection_info.protocol_host_port .input.text.field_protocol_host .input.text.protocol .chosen-container{display:block}.singleline.connection_info.protocol_host_port .input.text.field_protocol_host .input.text.protocol .chosen-container a.chosen-single{height:38px}.singleline.connection_info.protocol_host_port .input.text.field_protocol_host .input.text.protocol .chosen-container a.chosen-single div b{background-position:0 10px}.singleline.connection_info.protocol_host_port .input.text.field_protocol_host .input.text.protocol .chosen-container a.chosen-single span{font-size:16px;padding:6px 0 0 0}.singleline.connection_info.protocol_host_port .input.text.field_protocol_host .input.text.protocol .chosen-container-active.chosen-with-drop a.chosen-single div b{background-position:-18px 10px}.singleline.connection_info.protocol_host_port .input.text.port{width:9%;margin-left:1%}.singleline.connection_info.protocol_host_port .input.text.port input[type=number]{width:100%}.panel .folders.navigation{border:0}.panel .folders.navigation ul{list-style:none;padding:0}.panel .folders.navigation .folders-label-selected{background:#f3f3f3}.panel .folders.navigation .folders-label-height{height:1.8em}.panel .folders.navigation.first{padding-top:0}.panel .folders.navigation .row .main-cell span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis;display:inline-block;margin-top:.2em;width:calc(100% - 2.5em)}.panel .folders.navigation .row .main-cell .svg-icon{display:inline}.panel .folders.navigation .row .main-cell .svg-icon.hidden{display:none}.panel .folders.navigation .row .main-cell .svg-icon svg{width:.83em;height:.83em;position:absolute;margin-top:.5em;fill:#fffffc}.panel .folders.navigation .row .main-cell a{padding:0 0 0 2.2em}.panel .folders.navigation .row .main-cell a .svg-icon.caret-down svg,.panel .folders.navigation .row .main-cell a .svg-icon.caret-right svg{margin-left:-1.2em;margin-top:.4em;width:1em;height:1em;position:absolute}.panel .folders.navigation .row .main-cell a .folder-name{margin-left:1.3em}.panel .folders.navigation .row .right-cell{margin-top:-1.66666667em}.panel .folders.navigation .row.title .main-cell h3{padding-top:.26666667em;padding-bottom:0;border:0;margin-top:0;line-height:1.26666667em}.panel .folders.navigation .row.title .main-cell h3 span{width:100%;margin-top:0}.panel .folders.navigation .row.title .main-cell h3 a{padding-left:0}.panel .folders.navigation .row.title .main-cell h3 a::after,.panel .folders.navigation .row.title .main-cell h3 a::before{display:none}.panel .folders.navigation .row.title .main-cell h3 .folders-label{width:100%;margin-left:1em;cursor:pointer}.panel .folders.navigation .row.title .main-cell h3 .folders-label span{width:calc(100% - 1.5em)}.panel .folders.navigation .row.title .main-cell h3 .folders-label .svg-icon.caret-down svg,.panel .folders.navigation .row.title .main-cell h3 .folders-label .svg-icon.caret-right svg{margin-left:-1.2em;margin-top:.2em;width:1em;height:1em;position:absolute}.panel .folders.navigation .row.title .main-cell h3 .folders-label .svg-icon.spinner svg{margin-left:-1.1em;margin-top:.3em;width:.83em;height:.83em;position:absolute}.panel .folders.navigation .row.title .right-cell{margin-top:-1.66666667em}.panel .folders.navigation .row.disabled{cursor:default;pointer-events:none}.panel .folders.navigation .row.disabled .main-cell span.folder-name{color:#8b8b89;text-shadow:none;font-weight:400}.panel .folders.navigation .row.disabled .main-cell span.svg-icon svg{fill:#8b8b89}.panel .folders.navigation .row.disabled .main-cell a:before{color:#8b8b89}.panel .folders.navigation .row.disabled.is-dragged{pointer-events:auto}.panel .folders.navigation li li{padding-left:1.15em}.drag-and-drop,.drag-and-drop-multiple{position:absolute;top:-1000px;background:#444442;border:1px solid #3b3b39;z-index:9999999;padding:.65em;line-height:1em;color:#fff;border-radius:5px}.drag-and-drop svg,.drag-and-drop-multiple svg{position:absolute;margin-top:-.85em;margin-left:.4em}.drag-and-drop svg path,.drag-and-drop-multiple svg path{fill:#fff}.drag-and-drop span.message,.drag-and-drop-multiple span.message{display:inline;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.drag-and-drop span.message.not-allowed,.drag-and-drop-multiple span.message.not-allowed{padding-left:1.85em}.drag-and-drop-multiple{box-shadow:.25em .25em 0 -1px #3b3b39}.drag-and-drop-multiple .count{position:absolute;top:-9px;right:-3px;font-size:.7em;background:#d40101;color:#fff;width:1.5em;height:1.5em;text-align:center;line-height:1.5em;border-radius:50%;box-shadow:0 0 1px #333}.drop-focus{background-color:#0e3e60}iframe.full-screen{position:absolute;width:100%;height:100%;z-index:999;border:0;top:0;left:0}iframe.cachette{position:absolute;width:1px;height:1px;z-index:999;border:0;bottom:0;right:0}#user-locale-input{border:none;background:transparent url(../../../img/controls/chevron-down_white.svg) right center/1rem no-repeat;appearance:none;margin-top:.4rem;padding-right:1rem}.input-password-wrapper{overflow:hidden}.input-password-wrapper .message.error{display:block;clear:both;width:100%;padding-bottom:.5em;font-size:.813em}.dialog .share-tab .message{font-size:.875em;padding-left:1em}.share-password-dialog .processing-wrapper{background-color:#30302d;text-align:center;padding:1em .5em 3em 1em}.share-password-dialog .processing-wrapper .processing-text{position:relative;padding-left:1.5em}.share-password-dialog .processing-wrapper .processing-text:before{width:100%;height:100%;position:absolute;content:" ";top:0;left:0;background:transparent url(../../../img/controls/loading_dark.svg) left center no-repeat;background-size:auto 75%}.dialog-wrapper.session-expired-dialog{z-index:994}.dialog-wrapper.session-expired-dialog .dialog-close{display:none}.dialog .group .form-content.permission-edit{border-top:1px solid #0c0c0a;margin:0}.edit-group-dialog .group_edit_form .form-content{padding-bottom:0}.edit-group-dialog .group_edit_form .form-content .input.text .message{padding-left:0}.edit-group-dialog .group_members{margin-top:-.5em}.edit-group-dialog .group_members .message.warning.feedback{margin-top:0}.edit-group-dialog .group_members .message.warning.feedback span{display:block}.edit-group-dialog .group_members ul.permissions{display:block}.edit-group-dialog .group_members.empty ul.permissions{display:none}.dialog.import .input-password-wrapper .input.password,.dialog.kdbx-credentials .input-password-wrapper .input.password{width:90%}.dialog.import .actions.inline,.dialog.kdbx-credentials .actions.inline{width:14%}.dialog.import .actions.inline .button,.dialog.kdbx-credentials .actions.inline .button{height:1.5em;padding-top:.6em}.dialog.import div.jfilestyle,.dialog.kdbx-credentials div.jfilestyle{width:100%!important}.dialog.import div.jfilestyle input[type=text]:disabled,.dialog.kdbx-credentials div.jfilestyle input[type=text]:disabled{width:57%!important}.delete-group-dialog .intro p+p,.delete-user-dialog .intro p+p{margin-bottom:0}.delete-group-dialog .ownership-transfer,.delete-user-dialog .ownership-transfer{max-height:20em;overflow:auto;background:#30302d}.delete-group-dialog .ownership-transfer h3,.delete-user-dialog .ownership-transfer h3{border:0;padding:.5em 1.25em;margin:0;background:#444442}.delete-group-dialog .ownership-transfer li,.delete-user-dialog .ownership-transfer li{border-bottom:1px dotted #0c0c0a;padding:.5em 1.25em}.delete-group-dialog .ownership-transfer li select,.delete-user-dialog .ownership-transfer li select{width:100%}.ldap-test-settings-report div.directory-structure{border:1px solid #8b8b89;padding:.5em 0 .5em .5em}.ldap-test-settings-report div.directory-structure>ul>li{margin-left:.5em}.ldap-test-settings-report div.directory-structure ul{font-size:.8rem;list-style-type:square;list-style-position:inside}.ldap-test-settings-report div.directory-structure ul li{margin-left:1em}.ldap-test-settings-report div.directory-structure ul li em{color:#606060;font-size:.8em}.ldap-test-settings-report div.directory-structure ul li.user{font-weight:400;list-style-type:circle}.ldap-test-settings-report div.directory-structure ul li.group{font-weight:700}.panel.aside .group .sidebar-header .logo img,.panel.aside .user .sidebar-header .logo img{margin:0;width:3em;height:3em}.panel.aside li.key{margin-bottom:.5em}.panel.aside li.key a.button.copy-public-key{padding-top:.2em;padding-bottom:.2em;font-size:.75em}.panel.aside li textarea{width:100%}.edit-group-dialog .permission-edit{margin-top:-1em;border-top:1px solid #0c0c0a}.page.people .tableview-header th.cell-avatar,.page.people .tableview-header th.cell-icon{width:45px}.page.people .tableview-content td.cell-avatar,.page.people .tableview-content th.cell-icon{margin:0;text-align:left;width:45px}.page.people .tableview-content td.cell-avatar img,.page.people .tableview-content th.cell-icon img{margin-top:3px;width:1.7em;height:1.7em;border-radius:50%}.login-history td:first-child{text-align:center}.page.user .tableview.empty .tableview-header{display:none}.page.user .tableview.empty .tableview-content{border:0;top:0}.page.user .tableview.empty .empty-content{text-align:center;margin:3em .5em .67em .5em}.page.user .tableview.empty .empty-content h1{font-size:1.5em}.page.user .tableview.empty .empty-content p{font-size:1em;line-height:1.5em}@media all and (min-width:780px){.page.user .tableview.empty .empty-content h1{font-size:2.2em}.page.user .tableview.empty .empty-content p{font-size:1.2em;line-height:1.8em}}.panel.aside .resource .detailed-information li.password .value{height:1.6em;padding-top:.2em}.panel.aside .resource .detailed-information li.password .value .secret{float:left;max-width:calc(100% - 1.8rem);overflow:hidden;text-overflow:ellipsis}.panel.aside .resource .detailed-information li.password .password-view{display:block;position:relative;box-sizing:content-box;margin:0 0 0 .3rem;padding:.05rem .3rem 0 .3rem;background:0 0;border:0;float:left}.panel.aside .resource .detailed-information li.password .password-view.selected{background:#222220}.page.password .tableview.empty .tableview-header{display:none}.page.password .tableview.empty .tableview-content{border:0;top:0}@media all and (min-width:780px){.page.password .tableview.empty.all_items .tableview-content{background-position:center 60%;background-size:10%}}@media all and (min-width:1024px){.page.password .tableview.empty.all_items .tableview-content{background-position:center 80%;background-size:20%}}.page.password .tableview.empty .empty-content{text-align:center;margin:auto;width:50%;padding-top:2em}.page.password .tableview.empty .empty-content h1{font-size:1.5em}.page.password .tableview.empty .empty-content p{font-size:1em;line-height:1.5em}@media all and (min-width:780px){.page.password .tableview.empty .empty-content h1{font-size:2.2em}.page.password .tableview.empty .empty-content p{font-size:1.2em;line-height:1.8em}}.page.settings .main.panel .middle{overflow-y:auto}.page.settings .profile-detailed-information{zoom:1}.page.settings .profile-detailed-information:after,.page.settings .profile-detailed-information:before{content:"";display:table}.page.settings .profile-detailed-information:after{clear:both}.page.settings .profile-detailed-information:after,.page.settings .profile-detailed-information:before{content:"";display:table}.page.settings .profile-detailed-information:after{clear:both}.page.settings .profile-detailed-information .avatar{float:left}.page.settings .profile-detailed-information .avatar img{border:1px solid #0c0c0a;padding:0;width:12.5em;height:12.5em;margin:.5em 1em 0 0}.page.settings .profile-detailed-information .avatar .edit{width:12.5em;height:3.125em;background-color:rgba(0,0,0,.7);border:1px solid transparent;color:#eeeeec;margin:-3.25em 0 2em 0;position:relative}.page.settings .profile-detailed-information .avatar .edit a{color:#eeeeec;display:block;height:3.125em;zoom:1}.page.settings .profile-detailed-information .avatar .edit a:after,.page.settings .profile-detailed-information .avatar .edit a:before{content:"";display:table}.page.settings .profile-detailed-information .avatar .edit a:after{clear:both}.page.settings .profile-detailed-information .avatar .edit a:after,.page.settings .profile-detailed-information .avatar .edit a:before{content:"";display:table}.page.settings .profile-detailed-information .avatar .edit a:after{clear:both}.page.settings .profile-detailed-information .avatar .edit a .svg-icon{float:left}.page.settings .profile-detailed-information .avatar .edit a .svg-icon svg{float:left;fill:#fff;width:1.6rem;height:1.6rem;margin:.8em}.page.settings .profile-detailed-information .avatar .edit a .help-text{float:left;display:block;font-size:.813em;margin-left:0;padding:.5em;width:9em}.page.settings .profile-key-inspector-information .input.select.tooltip-top{display:inline-block}.page.settings .key-export .actions{margin:1em 0}.page.settings .key-export .input.textarea.gpgkey textarea.fluid.code{height:27em}.page.settings .profile-passphrase .password-management-bg{background:transparent url(../../../img/illustrations/passphrase_intro.png) center center no-repeat;background-size:contain;height:16em}.page.settings .profile-passphrase .input.checkbox{margin-bottom:1em}.page.settings .profile-passphrase .passphrase-help{margin-top:2em;padding:1.5em;background-color:#2894df}.page.settings .profile-passphrase .passphrase-help h3{margin:0;border-bottom:none}.page.settings .profile-passphrase .password{zoom:1}.page.settings .profile-passphrase .password:after,.page.settings .profile-passphrase .password:before{content:"";display:table}.page.settings .profile-passphrase .password:after{clear:both}.page.settings .profile-passphrase .password:after,.page.settings .profile-passphrase .password:before{content:"";display:table}.page.settings .profile-passphrase .password:after{clear:both}.page.settings .profile-passphrase .password input{width:calc(100% - 16rem)}.page.settings .profile-passphrase .password .input .message.error{clear:both}.page.settings .profile-passphrase .password .password-view{width:2em;height:1.5em;padding:.44rem .8rem .5rem .9rem}.page.settings .profile-passphrase .password-complexity .progress{width:calc(100% - 12.6rem)}.page.settings .profile-passphrase .password-hints{margin:.5em 0 1em 0}.page.settings .profile-passphrase .password-hints li{font-size:1rem;line-height:1.5rem}.page.settings .profile-choose-security-token .input-security-token{zoom:1;margin:1em 0 1.5em 0}.page.settings .profile-choose-security-token .input-security-token:after,.page.settings .profile-choose-security-token .input-security-token:before{content:"";display:table}.page.settings .profile-choose-security-token .input-security-token:after{clear:both}.page.settings .profile-choose-security-token .input-security-token:after,.page.settings .profile-choose-security-token .input-security-token:before{content:"";display:table}.page.settings .profile-choose-security-token .input-security-token:after{clear:both}.page.settings .profile-choose-security-token .input-security-token label{margin-bottom:.5em}.page.settings .profile-choose-security-token .input-security-token .input.text{-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em;font-size:2rem;max-width:5rem;float:left;text-align:center;margin-right:1em}.page.settings .profile-choose-security-token .input-security-token .circle-picker{float:left}.page.settings .profile-choose-security-token .input-security-token .randomize-button-wrapper{float:left;text-align:center;clear:both;cursor:pointer}.page.settings .profile-choose-security-token .input-security-token .svg-icon svg{height:.85em;width:.85em}@media all and (max-width:950px){.page.settings .profile-detailed-information .avatar{float:none}}@media (max-width:1280px){.key-info .table-info{font-size:.875em}.key-info .table-info .select select{font-size:.929em}}.themes .theme{float:left;border-radius:2px}.themes .theme a{max-width:275px;display:block;margin:1em;border:1px solid #0c0c0a;padding:1em;box-shadow:0 0 10px 0 #000;border-radius:3px}.themes .theme a:hover{border:1px solid #2a9ceb}.themes .theme .theme-desc{padding-top:1em;text-align:center}.themes .theme.selected{font-weight:700}.themes .theme.selected a{background:#222220;box-shadow:inset 0 1px 2px rgba(0,0,0,.2);border:1px solid #0c0c0a}html.launching .launching-screen{display:block;width:100%;height:100%;position:absolute;z-index:999;background:#30302d}html.launching .launching-screen .launching-screen-holder{width:20%;margin:auto;margin-top:7em}html.launching .launching-screen .progress-bar-wrapper{margin-bottom:0}html.launching .launching-screen p{margin:1em 0;font-size:.75em}.launching-screen{display:none}@media all and (min-width:460px){.page.error .grid{text-align:center;width:100%;margin-bottom:2.5em}.page.error.error-400 .row,.page.error.error-404 .row,.page.error.error-500 .row{max-width:400px;margin:auto}.page.error.error-400 .grid:before,.page.error.error-404 .grid:before,.page.error.error-500 .grid:before{font-size:15em;font-weight:700;color:#222220}.page.error.error-404 .grid:before{content:"404"}.page.error.error-400 .grid:before{content:"400"}.page.error.error-500 .grid:before{content:"500"}}.page.setup,.page.status{margin-bottom:2.5em}.page.setup .grid,.page.status .grid{padding-bottom:2em}.page.setup #url-rewriting-warning,.page.status #url-rewriting-warning{display:none}.page.setup .grid .message,.page.status .grid .message{padding:.75em 1em;margin-bottom:.5em}.page.setup .grid .input .message,.page.status .grid .input .message{padding:0 0 .5em 0}.cake-error{display:none}#js_mfa_iframe{width:100%;height:calc(100% - 2.5em);position:absolute;box-sizing:border-box;padding:0;margin:0;background:#30302d}.mfa.iframe .grid,.mfa.iframe .grid-responsive-12{max-width:none}.mfa.iframe .actions-wrapper{margin-top:3em}.mfa.iframe .totp-setup .input-verify{float:left;background:#444442;padding:2.5em;max-width:25em;height:262px;min-width:22em;box-sizing:border-box;border:3px solid #444442;border-left:0}.mfa.iframe .totp-setup .input-verify .helptext{max-width:18em}.mfa.iframe .totp-setup .qrcode{float:left;max-width:262px;box-sizing:border-box;max-height:262px;border:3px solid #444442}.mfa.iframe .how-it-works p{width:28%;float:left;color:#cacac9}.mfa.iframe .how-it-works p+p{margin-left:5%}.mfa.iframe .how-it-works p+p+p{margin-left:8%}.mfa.iframe .mfa-providers{zoom:1}.mfa.iframe .mfa-providers:after,.mfa.iframe .mfa-providers:before{content:"";display:table}.mfa.iframe .mfa-providers:after{clear:both}.mfa.iframe .mfa-providers:after,.mfa.iframe .mfa-providers:before{content:"";display:table}.mfa.iframe .mfa-providers:after{clear:both}.mfa.iframe .mfa-providers li{float:left;-webkit-border-radius:.214em;-moz-border-radius:.214em;border-radius:.214em;width:12.5em;margin-bottom:2em;margin-right:2em;border:1px solid #0c0c0a}.mfa.iframe .mfa-providers li:hover{border:1px solid #0c0c0a;box-shadow:0 0 10px 0 #000}.mfa.iframe .mfa-providers a{border-bottom:1px solid #0c0c0a;display:block;text-align:center}.mfa.iframe .mfa-providers a span{padding:1em 0 2em 0;display:block}.mfa.iframe .mfa-providers a img{display:block;padding:2em 0 .5em 0;height:5em}.mfa.iframe .mfa-providers .mfa-provider-status{padding:1em;background:#444442;text-align:center}.mfa.iframe .mfa-providers .mfa-provider-status.disabled{color:#8b8b89}.mfa.iframe .mfa-trusted-device{padding:1em;display:flex}.mfa.iframe .mfa-trusted-device:nth-child(even){background:#444442}.mfa.iframe .mfa-trusted-device .device{flex:1;font-size:2.5em;text-align:center;color:#cacac9}.mfa.iframe .mfa-trusted-device .device.current:before{content:'\2022';color:#090;font-size:.75em;position:absolute;margin-left:-.5em}.mfa.iframe .mfa-trusted-device .session{flex:2 0 10em}.mfa.iframe .mfa-trusted-device .action{flex:1;padding-top:.5em}.mfa.iframe .mfa-trusted-device table td,.mfa.iframe .mfa-trusted-device table th{padding:.125em 1em}.mfa.iframe .mfa-trusted-device table th{font-weight:700}.page.administration .workspace-main .grid{position:absolute;bottom:0;top:2.125em;padding:0;overflow-y:scroll}.dialog .ldap-test-settings-report .directory-list span.error{color:#d40101}.dialog .ldap-test-settings-report p.directory-errors.error{padding:1em 0 0 0;color:#d40101}.dialog .ldap-test-settings-report .accordion-directory-structure .error{color:#d40101}.dialog .ldap-test-settings-report .accordion-directory-errors textarea{font-family:"Courier New",Courier,monospace;font-size:11px;overflow:auto;height:220px}.report-widget .colors.red{color:#d40101}.report-widget .colors.green{color:#090}.report-widget .colors.orange{color:#9f6000}.report-widget .colors.blue{color:#5bbbff}.report-widget.gauge div.widget-content{min-height:220.567px!important}.report-widget.gauge p.widget-description{color:#3b3b39;text-align:center;margin-top:-20px}.report-widget.simple-number{text-align:center}.report-widget.simple-number .widget-content{width:145px;height:145px;background-color:#f3f3f3;border-radius:50%;display:inline-block;margin-top:20px;border:5px solid #f3f3f3}.report-widget.simple-number .widget-content span{font-size:27px;margin-top:51px;display:inline-block;color:#000}.report-widget.simple-number p.widget-description{color:#3b3b39;text-align:center;margin-top:25px}.report-widget .plots{margin-top:2em}.report-widget .plots .plot-container{margin-top:2em}.report-widget .plots .plot-container .plot{display:inline-block;width:80%}.report-widget .plots .plot-container .plot span.label{font-size:1em}.report-widget .plots .plot-container .plot .bar-container{position:relative;border:1px solid #8b8b89;height:5px;margin-top:.3em}.report-widget .plots .plot-container .plot .bar-container .bar{position:absolute;top:-1px;left:-1px;height:5px;border:1px solid #5bbbff;background:#5bbbff}.report-widget .plots .plot-container .plot-value{width:15%;font-size:1.6em;padding:.35em 0 0 .5em;line-height:1em;float:right}.report-widget.widget-dashboard-count{position:relative}.report-widget.widget-dashboard-count.items .illustration{background:#ff6c70}.report-widget.widget-dashboard-count.items .illustration svg{margin-top:1.3em}.report-widget.widget-dashboard-count.groups .illustration{background:#00ba93}.report-widget.widget-dashboard-count.groups .illustration svg{margin-top:.9em;width:2.8em;height:2.8em}.report-widget.widget-dashboard-count.users .illustration{background:#5bbbff}.report-widget.widget-dashboard-count.users .illustration svg{margin-top:1.2em}.report-widget.widget-dashboard-count .count .number{display:block;font-size:2.5em;line-height:1em}.report-widget.widget-dashboard-count .count .label{display:block;font-size:1.25em;line-height:1em;margin-top:.6em}.report-widget.widget-dashboard-count .illustration{position:absolute;top:2.5em;right:2.75em;width:4.7em;height:4.7em;-webkit-border-radius:50%;-moz-border-radius:50%;border-radius:50%;background:#5bbbff;text-align:center}.report-widget.widget-dashboard-count .illustration svg{margin-top:1.1em;fill:#fff;color:#fff;height:2.2em;width:2.2em}.report-widget.widget-dashboard-count .since{font-size:.8em;margin-top:1.8em}.report-widget.widget-dashboard-count .since span{color:#090}.report-widget.widget-dashboard-plots{margin-top:2em;padding-bottom:4em}.report-widget.widget-dashboard-plots.items .plots .plot-container .bar-container .bar{background:#ff6c70!important;border:1px solid #ff6c70!important}.report-widget.widget-login-history{margin-top:2em;margin-bottom:3em}.report-widget.widget-login-history .chart{margin-top:2em}.page.printable-report{background-color:#f3f3f3}.page.printable-report .page-wrapper{width:900px;min-height:1536px;background-color:#fff;padding:3em;margin:3em auto;box-sizing:border-box;box-shadow:0 0 10px 0 #000}.page.printable-report .page-wrapper .report-header .creator-info .label{display:inline-block;width:150px;margin-bottom:.25em}.page.printable-report .page-wrapper .report-header .creator-info .value{display:inline-block;margin-bottom:.25em}.page.printable-report .page-wrapper .report-header .company-info{text-align:right}.page.printable-report .page-wrapper .report-header .company-info .logo{margin-top:65px;width:200px;float:right}.page.printable-report .page-wrapper .report-content{margin-top:2em}.page.printable-report .page-wrapper .report-content .row.charts{margin-top:2em}.page.printable-report .page-wrapper .report-content .row.list{margin-top:3em}.page.printable-report .page-wrapper .report-content .table-info{border-right:0;border-left:0;border-bottom:0}.page.printable-report .page-wrapper .report-content .table-info tr td:first-of-type,.page.printable-report .page-wrapper .report-content .table-info tr th:first-of-type{width:30%}.page.printable-report .page-wrapper .report-content .table-info tr td{word-wrap:break-word;color:#444442;vertical-align:middle}.page.printable-report .page-wrapper .report-content .table-info tr td+td{color:#444442}.page.printable-report .page-wrapper .report-content .table-info tr span.email,.page.printable-report .page-wrapper .report-content .table-info tr span.name{display:block;max-width:300px}.page.printable-report .page-wrapper .report-content .table-info tr span.email{font-size:.8em;color:#3b3b39}@media print{@page{size:auto;margin:0}.page.printable-report{background-color:#fff}.page.printable-report .page-wrapper{min-height:0!important;box-shadow:none!important;color:#8b8b89}}.reports-workspace .accordion.navigation.first{border:none;padding:.625em 0 0 0;margin:0}.reports-workspace .workspace-reports-content{position:relative;height:100%}.reports-workspace .workspace-reports-content .report-wrapper{top:0;bottom:2.45em;padding:1em 2em;position:absolute;width:100%;overflow-y:scroll;background-color:#f3f3f3;box-sizing:border-box}.reports-workspace .workspace-reports-content .report-wrapper .report-page{margin-top:3em}.reports-workspace .workspace-reports-content .report-wrapper .report-page .report-loading{text-align:center;padding-top:10em}.reports-workspace .workspace-reports-content .report-wrapper .report-page .report-loading .spinner{background:transparent url(../../../img/controls/loading_dark.svg) center center no-repeat;content:' ';height:50px}.reports-workspace .workspace-reports-content .report-widget{background-color:#fff;padding:2.6em;border:1px solid #8b8b89;box-shadow:0 0 10px 0 #000;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.reports-workspace .workspace-reports-content .report-widget h2{font-weight:400;line-height:1em;margin:0;font-size:1.45em}.reports-workspace .workspace-reports-content iframe{width:100%;height:100%} \ No newline at end of file +*/.chosen-container{position:relative;display:inline-block;vertical-align:middle;font-size:13px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.chosen-container *{-webkit-box-sizing:border-box;box-sizing:border-box}.chosen-container .chosen-drop{position:absolute;top:100%;z-index:1010;width:100%;border:1px solid #aaa;border-top:0;background:#30302d;-webkit-box-shadow:0 4px 5px rgba(0,0,0,.15);box-shadow:0 4px 5px rgba(0,0,0,.15);display:none}.chosen-container.chosen-with-drop .chosen-drop{display:block}.chosen-container a{cursor:pointer}.chosen-container .chosen-single .group-name,.chosen-container .search-choice .group-name{margin-right:4px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-weight:400;color:#8b8b89}.chosen-container .chosen-single .group-name:after,.chosen-container .search-choice .group-name:after{content:"\003A";padding-left:2px;vertical-align:top}.chosen-container-single .chosen-single{position:relative;display:block;overflow:hidden;padding:0 0 0 8px;height:25px;border:1px solid #30302d;border-radius:5px;background-color:#30302d;background:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#30302d),color-stop(50%,#3b3b39),color-stop(52%,#3b3b39),to(#444442));background:linear-gradient(#30302d 20%,#3b3b39 50%,#3b3b39 52%,#444442 100%);background-clip:padding-box;-webkit-box-shadow:0 0 3px #30302d inset,0 1px 1px rgba(0,0,0,.1);box-shadow:0 0 3px #30302d inset,0 1px 1px rgba(0,0,0,.1);color:#fffffc;text-decoration:none;white-space:nowrap;line-height:24px}.chosen-container-single .chosen-single input[type=text]{cursor:pointer;opacity:0;position:absolute}.chosen-container-single .chosen-default{color:#8b8b89}.chosen-container-single .chosen-single span{display:block;overflow:hidden;margin-right:32px;text-overflow:ellipsis;white-space:nowrap}.chosen-container-single .chosen-single-with-deselect span{margin-right:38px}.chosen-container-single .chosen-single abbr{position:absolute;top:6px;right:26px;display:block;width:12px;height:12px;background:url('../../../img/third_party/chosen-sprite.png') -42px 1px no-repeat;font-size:1px}.chosen-container-single .chosen-single abbr:hover{background-position:-42px -10px}.chosen-container-single.chosen-disabled .chosen-single abbr:hover{background-position:-42px -10px}.chosen-container-single .chosen-single div{position:absolute;top:0;right:.8rem;display:block;width:1.8rem;height:100%}.chosen-container-single .chosen-single div b{display:block;width:100%;height:100%;background:url("../../../img/third_party/chosen-sprite.png") no-repeat 0 2px}.chosen-container-single .chosen-search{position:relative;z-index:1010;margin:0;padding:3px 4px;white-space:nowrap}.chosen-container-single .chosen-search span.svg-icon{position:absolute;margin-left:-1.5em;margin-top:.35em}.chosen-container-single .chosen-search input[type=text]{margin:1px 0;padding:4px 20px 4px 5px;width:100%;height:auto;outline:0;border:1px solid #30302d;background:url("../../../img/third_party/chosen-sprite.png") no-repeat 100% -20px;font-size:1em;font-family:sans-serif;line-height:normal;border-radius:0}.chosen-container-single .chosen-drop{margin-top:-.5em;border-radius:0 0 4px 4px;background-clip:padding-box}.chosen-container-single.chosen-container-single-nosearch .chosen-search{position:absolute;clip:rect(0,0,0,0)}.chosen-container .chosen-results{color:#fffffc;position:relative;overflow-x:hidden;overflow-y:auto;margin:0 4px 4px 0;padding:0 0 0 4px;max-height:240px;-webkit-overflow-scrolling:touch}.chosen-container .chosen-results li{display:none;margin:0;padding:5px 6px;list-style:none;line-height:15px;word-wrap:break-word;-webkit-touch-callout:none}.chosen-container .chosen-results li.active-result{display:list-item;cursor:pointer}.chosen-container .chosen-results li.disabled-result{display:list-item;color:#444442;cursor:default}.chosen-container .chosen-results li:hover{background-color:#2894df;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#3875d7),color-stop(90%,#2a62bc));background-image:linear-gradient(#3875d7 20%,#2a62bc 90%);color:#eeeeec}.chosen-container .chosen-results li.no-results{color:#cacac9;display:list-item;background:#444442}.chosen-container .chosen-results li.group-result{display:list-item;font-weight:700;cursor:default}.chosen-container .chosen-results li.group-option{padding-left:15px}.chosen-container .chosen-results li em{font-style:normal;text-decoration:underline}.chosen-container-multi .chosen-choices{position:relative;overflow:hidden;margin:0;padding:0 5px;width:100%;height:auto;border:1px solid #30302d;background-color:#30302d;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(1%,#3b3b39),color-stop(15%,#30302d));background-image:linear-gradient(#3b3b39 1%,#30302d 15%);cursor:text}.chosen-container-multi .chosen-choices li{float:left;list-style:none}.chosen-container-multi .chosen-choices li.search-field{margin:0;padding:0;white-space:nowrap}.chosen-container-multi .chosen-choices li.search-field input[type=text]{margin:1px 0;padding:0;height:25px;outline:0;border:0!important;background:0 0!important;-webkit-box-shadow:none;box-shadow:none;color:#8b8b89;font-size:100%;font-family:sans-serif;line-height:normal;border-radius:0;width:25px}.chosen-container-multi .chosen-choices li.search-choice{position:relative;margin:3px 5px 3px 0;padding:3px 20px 3px 5px;border:1px solid #30302d;max-width:100%;border-radius:3px;background-color:#30302d;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#30302d),color-stop(50%,#3b3b39),color-stop(52%,#3b3b39),to(#444442));background-image:linear-gradient(#30302d 20%,#3b3b39 50%,#3b3b39 52%,#444442 100%);background-size:100% 19px;background-repeat:repeat-x;background-clip:padding-box;-webkit-box-shadow:0 0 2px #30302d inset,0 1px 0 rgba(0,0,0,.05);box-shadow:0 0 2px #30302d inset,0 1px 0 rgba(0,0,0,.05);color:#444442;line-height:13px;cursor:default}.chosen-container-multi .chosen-choices li.search-choice span{word-wrap:break-word}.chosen-container-multi .chosen-choices li.search-choice .search-choice-close{position:absolute;top:4px;right:3px;display:block;width:12px;height:12px;background:url("../../../img/third_party/chosen-sprite.png") -42px 1px no-repeat;font-size:1px}.chosen-container-multi .chosen-choices li.search-choice .search-choice-close:hover{background-position:-42px -10px}.chosen-container-multi .chosen-choices li.search-choice-disabled{padding-right:5px;border:1px solid #222220;background-color:#444442;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#30302d),color-stop(50%,#3b3b39),color-stop(52%,#3b3b39),to(#444442));background-image:linear-gradient(#30302d 20%,#3b3b39 50%,#3b3b39 52%,#444442 100%);color:#cacac9}.chosen-container-multi .chosen-choices li.search-choice-focus{background:#3b3b39}.chosen-container-multi .chosen-choices li.search-choice-focus .search-choice-close{background-position:-42px -10px}.chosen-container-multi .chosen-results{margin:0;padding:0}.chosen-container-multi .chosen-drop .result-selected{display:list-item;color:#3b3b39;cursor:default}.chosen-container-active .chosen-single{border:1px solid #2894df;-webkit-box-shadow:0 0 5px rgba(0,0,0,.3);box-shadow:0 0 5px rgba(0,0,0,.3)}.chosen-container-active.chosen-with-drop .chosen-single{border:1px solid #30302d;border-bottom-right-radius:0;border-bottom-left-radius:0;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#3b3b39),color-stop(80%,#30302d));background-image:linear-gradient(#3b3b39 20%,#30302d 80%);-webkit-box-shadow:0 1px 0 #30302d inset;box-shadow:0 1px 0 #30302d inset}.chosen-container-active.chosen-with-drop .chosen-single div{border-left:none;background:0 0}.chosen-container-active.chosen-with-drop .chosen-single div b{background-position:-18px 2px}.chosen-container-active .chosen-choices{border:1px solid #2894df;-webkit-box-shadow:0 0 5px rgba(0,0,0,.3);box-shadow:0 0 5px rgba(0,0,0,.3)}.chosen-container-active .chosen-choices li.search-field input[type=text]{color:#fffffc!important}.chosen-disabled{opacity:.5!important;cursor:default}.chosen-disabled .chosen-single{cursor:default}.chosen-disabled .chosen-choices .search-choice .search-choice-close{cursor:default}.chosen-rtl{text-align:right}.chosen-rtl .chosen-single{overflow:visible;padding:0 8px 0 0}.chosen-rtl .chosen-single span{margin-right:0;margin-left:26px;direction:rtl}.chosen-rtl .chosen-single-with-deselect span{margin-left:38px}.chosen-rtl .chosen-single div{right:auto;left:3px}.chosen-rtl .chosen-single abbr{right:auto;left:26px}.chosen-rtl .chosen-choices li{float:right}.chosen-rtl .chosen-choices li.search-field input[type=text]{direction:rtl}.chosen-rtl .chosen-choices li.search-choice{margin:3px 5px 3px 0;padding:3px 5px 3px 19px}.chosen-rtl .chosen-choices li.search-choice .search-choice-close{right:auto;left:4px}.chosen-rtl.chosen-container-single .chosen-results{margin:0 0 4px 4px;padding:0 4px 0 0}.chosen-rtl .chosen-results li.group-option{padding-right:15px;padding-left:0}.chosen-rtl.chosen-container-active.chosen-with-drop .chosen-single div{border-right:none}.chosen-rtl .chosen-search input[type=text]{padding:4px 5px 4px 20px;background:url("../../../img/third_party/chosen-sprite.png") no-repeat -30px -20px;direction:rtl}.chosen-rtl.chosen-container-single .chosen-single div b{background-position:6px 2px}.chosen-rtl.chosen-container-single.chosen-with-drop .chosen-single div b{background-position:-12px 2px}@media only screen and (-webkit-min-device-pixel-ratio:1.5),only screen and (min-resolution:144dpi),only screen and (min-resolution:1.5dppx){.chosen-container .chosen-results-scroll-down span,.chosen-container .chosen-results-scroll-up span,.chosen-container-multi .chosen-choices .search-choice .search-choice-close,.chosen-container-single .chosen-search input[type=text],.chosen-container-single .chosen-single abbr,.chosen-container-single .chosen-single div b,.chosen-rtl .chosen-search input[type=text]{background-image:url('../../../img/third_party/chosen-sprite@2x.png')!important;background-size:52px 37px!important;background-repeat:no-repeat!important}}.chosen-container{margin-bottom:.5em}.chosen-container.chosen-disabled a.chosen-single{border:1px solid #222220}.chosen-container a.chosen-single{color:#fffffc;display:block;text-overflow:ellipsis;white-space:nowrap;height:3.8rem;width:100%;background:#444442;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;border:1px solid #222220;border-top:1px solid #30302d;box-shadow:none;margin:0 0 .8rem 0}.chosen-container a.chosen-single span{font-size:1.6rem;padding-left:.4rem;padding-top:.4rem;margin-top:.2rem;color:#fffffc}.chosen-container a.chosen-single span.svg-icon{float:left}.chosen-container a.chosen-single div b{background-position:0 .6rem}.chosen-container .chosen-search input.chosen-search-input[type=text]{width:95%!important;color:#cacac9}.chosen-container .chosen-drop{border:1px solid #222220!important;border-top:0!important;-webkit-border-radius:0!important;-moz-border-radius:0!important;border-radius:0!important;background:#0c0c0a!important}.chosen-container .chosen-drop .chosen-results{color:#fffffc}.chosen-container.chosen-container-active.chosen-with-drop a.chosen-single{background:#0c0c0a;border:1px solid #222220;border-top:1px solid #30302d;box-shadow:none}.chosen-container.chosen-container-active.chosen-with-drop a.chosen-single div b{background-position:-1.8rem .8rem}.singleline.connection_info .input.text.first-field,.singleline.connection_info .input.text:first-child{margin-right:0}.singleline.connection_info .input.text.protocol{flex:0 0 2.5rem}.singleline.connection_info .input.text.protocol .chosen-container{display:block}.singleline.connection_info .input.text.protocol .chosen-container a.chosen-single div b{background-position:0 .8rem}.singleline.connection_info .input.text.protocol .chosen-container-active.chosen-with-drop a.chosen-single div b{background-position:-1.8rem .8rem}.singleline.connection_info .input.text.host{flex:1 0 auto}.singleline.connection_info .input.text.port{flex:0 0 9.5rem}.autocomplete-suggestions{text-align:left;cursor:default;border:1px solid #0c0c0a;border-top:0;background:#30302d;box-shadow:0 0 10px 0 #000;position:absolute;display:none;z-index:9999;max-height:120px;overflow:hidden;overflow-y:auto;box-sizing:border-box;width:350px}.autocomplete-suggestions .autocomplete-suggestion{position:relative;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:#fffffc;font-size:.875em;display:block;padding:.357em .714em;border:0}.autocomplete-suggestions .autocomplete-suggestion b{font-weight:400;color:#fffffc}.autocomplete-suggestions .autocomplete-suggestion.selected{background:#3b3b39}#user-locale-input{border:none;background:#222220 url('../../../img/controls/chevron-down_white.svg') 92% center/1rem no-repeat;appearance:none;margin-top:.8rem;padding-left:.4rem;padding-right:2rem;line-height:2rem;width:auto}#user-locale-input:focus{outline:0;border:1px solid #2894df;border-radius:0}#user-locale-input option{background-color:#222220}.ldap-test-settings-report div.directory-structure{border:1px solid #8b8b89;padding:.5em 0 .5em .5em}.ldap-test-settings-report div.directory-structure>ul>li{margin-left:.5em}.ldap-test-settings-report div.directory-structure ul{font-size:.8rem;list-style-type:square;list-style-position:inside}.ldap-test-settings-report div.directory-structure ul li{margin-left:1em}.ldap-test-settings-report div.directory-structure ul li em{color:#606060;font-size:.8em}.ldap-test-settings-report div.directory-structure ul li.user{font-weight:400;list-style-type:circle}.ldap-test-settings-report div.directory-structure ul li.group{font-weight:700}.page.settings .main.panel .middle{overflow-y:auto}.page.settings .profile-detailed-information:after,.page.settings .profile-detailed-information:before{content:"";display:table}.page.settings .profile-detailed-information:after{clear:both}.page.settings .profile-detailed-information:after,.page.settings .profile-detailed-information:before{content:"";display:table}.page.settings .profile-detailed-information:after{clear:both}.page.settings .profile-detailed-information .avatar{float:left}.page.settings .profile-detailed-information .avatar img{border:1px solid #0c0c0a;padding:0;width:12.5em;height:12.5em;margin:.5em 1em 0 0}.page.settings .profile-detailed-information .avatar .edit{width:12.5em;height:3.125em;background-color:rgba(0,0,0,.7);border:1px solid transparent;color:#eeeeec;margin:-3.25em 0 2em 0;position:relative}.page.settings .profile-detailed-information .avatar .edit a{color:#eeeeec;display:block;height:3.125em}.page.settings .profile-detailed-information .avatar .edit a:after,.page.settings .profile-detailed-information .avatar .edit a:before{content:"";display:table}.page.settings .profile-detailed-information .avatar .edit a:after{clear:both}.page.settings .profile-detailed-information .avatar .edit a:after,.page.settings .profile-detailed-information .avatar .edit a:before{content:"";display:table}.page.settings .profile-detailed-information .avatar .edit a:after{clear:both}.page.settings .profile-detailed-information .avatar .edit a .svg-icon{float:left}.page.settings .profile-detailed-information .avatar .edit a .svg-icon svg{float:left;fill:white;width:1.6rem;height:1.6rem;margin:.8em}.page.settings .profile-detailed-information .avatar .edit a .help-text{float:left;display:block;font-size:.813em;margin-left:0;padding:.5em;width:9em}.page.settings .profile-key-inspector-information .input.select.tooltip-top{display:inline-block}.page.settings .profile-key-inspector-information .key-info .table-info .select select{margin:0;padding:.1rem 1.8rem .1rem .2rem;background-size:.8rem auto,100%}.page.settings .key-export .actions{margin:1em 0}.page.settings .key-export .input.textarea.gpgkey textarea.fluid.code{height:27em}.page.settings .profile-passphrase .password-management-bg{background:transparent url('../../../img/illustrations/passphrase_intro.png') center center no-repeat;background-size:contain;height:16rem}.page.settings .profile-passphrase .input.checkbox{margin-bottom:1em}.page.settings .profile-passphrase .password:after,.page.settings .profile-passphrase .password:before{content:"";display:table}.page.settings .profile-passphrase .password:after{clear:both}.page.settings .profile-passphrase .password:after,.page.settings .profile-passphrase .password:before{content:"";display:table}.page.settings .profile-passphrase .password:after{clear:both}.page.settings .profile-passphrase .password input{width:calc(100% - 30rem)}.page.settings .profile-passphrase .password .security-token{margin-top:0;margin-left:.8rem}.page.settings .profile-passphrase .password .input .message.error{clear:both}.page.settings .profile-passphrase .password-complexity .progress{width:calc(100% - 25.9rem)}.page.settings .profile-passphrase .enter-passphrase .submit-wrapper{display:flex;flex-direction:row}.page.settings .profile-passphrase .password-hints{margin:.8rem 0 1.6rem 0}.page.settings .profile-passphrase .password-hints li{font-size:1.6rem}.page.settings .profile-choose-security-token .input-security-token{margin:1em 0 1.5em 0}.page.settings .profile-choose-security-token .input-security-token:after,.page.settings .profile-choose-security-token .input-security-token:before{content:"";display:table}.page.settings .profile-choose-security-token .input-security-token:after{clear:both}.page.settings .profile-choose-security-token .input-security-token:after,.page.settings .profile-choose-security-token .input-security-token:before{content:"";display:table}.page.settings .profile-choose-security-token .input-security-token:after{clear:both}.page.settings .profile-choose-security-token .input-security-token label{margin-bottom:.5em}.page.settings .profile-choose-security-token .input-security-token .input.text{-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem;font-size:3.8rem;max-width:12rem;float:left;text-align:center;margin-right:2.4rem}.page.settings .profile-choose-security-token .input-security-token .circle-picker{float:left}.page.settings .profile-choose-security-token .input-security-token .randomize-button-wrapper{float:left;text-align:center;clear:both;cursor:pointer}.page.settings .profile-mobile-transfer .mobile-transfer-bg{background:transparent url('../../../img/diagrams/mobile-transfer.svg') left center no-repeat;background-size:contain;height:16em;margin:2em 0}@media all and (max-width:950px){.page.settings .profile-detailed-information .avatar{float:none}}@media (max-width:1280px){.key-info .table-info{font-size:.875em}.key-info .table-info .select select{font-size:.929em}}html.launching .launching-screen{display:block;width:100%;height:100%;position:absolute;z-index:999;background:#30302d}html.launching .launching-screen .launching-screen-holder{width:20%;margin:auto;margin-top:7em}html.launching .launching-screen .progress-bar-wrapper{margin-bottom:0}html.launching .launching-screen p{margin:1em 0;font-size:.75em}.launching-screen{display:none}@media all and (min-width:460px){.page.error .grid{text-align:center;width:100%;margin-bottom:2.5em}.page.error.error-400 .row,.page.error.error-404 .row,.page.error.error-500 .row{max-width:400px;margin:auto}.page.error.error-400 .grid:before,.page.error.error-404 .grid:before,.page.error.error-500 .grid:before{font-size:15em;font-weight:700;color:#222220}.page.error.error-404 .grid:before{content:"404"}.page.error.error-400 .grid:before{content:"400"}.page.error.error-500 .grid:before{content:"500"}}.page.setup,.page.status{margin-bottom:2.5em}.page.setup .grid,.page.status .grid{padding-bottom:2em}.page.setup #url-rewriting-warning,.page.status #url-rewriting-warning{display:none}.page.setup .grid .message,.page.status .grid .message{padding:.75em 1em;margin-bottom:.5em}.page.setup .grid .input .message,.page.status .grid .input .message{padding:0 0 .5em 0}.cake-error{display:none}.themes .theme{float:left;border-radius:2px}.themes .theme a{max-width:275px;display:block;margin:1em;border:1px solid #0c0c0a;padding:1em;box-shadow:0 0 10px 0 #000;border-radius:3px}.themes .theme a:hover{border:1px solid #2a9ceb}.themes .theme .theme-desc{padding-top:1em;text-align:center}.themes .theme.selected{font-weight:700}.themes .theme.selected a{background:#222220;box-shadow:inset 0 1px 2px rgba(0,0,0,.2);border:1px solid #0c0c0a}#js_mfa_iframe{width:100%;height:calc(100% - 2.5em);position:absolute;box-sizing:border-box;padding:0;margin:0;background:#30302d}.mfa.iframe .grid,.mfa.iframe .grid-responsive-12{max-width:none}.mfa.iframe .actions-wrapper{margin-top:3em}.mfa.iframe .totp-setup .input-verify{float:left;background:#444442;padding:2.5em;max-width:25em;height:262px;min-width:22em;box-sizing:border-box;border:3px solid #444442;border-left:0}.mfa.iframe .totp-setup .input-verify .helptext{max-width:18em}.mfa.iframe .totp-setup .qrcode{float:left;max-width:262px;box-sizing:border-box;max-height:262px;border:3px solid #444442}.mfa.iframe .how-it-works p{width:28%;float:left;color:#cacac9}.mfa.iframe .how-it-works p+p{margin-left:5%}.mfa.iframe .how-it-works p+p+p{margin-left:8%}.mfa.iframe .mfa-providers:after,.mfa.iframe .mfa-providers:before{content:"";display:table}.mfa.iframe .mfa-providers:after{clear:both}.mfa.iframe .mfa-providers:after,.mfa.iframe .mfa-providers:before{content:"";display:table}.mfa.iframe .mfa-providers:after{clear:both}.mfa.iframe .mfa-providers li{float:left;-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem;width:12.5em;margin-bottom:2em;margin-right:2em;border:1px solid #0c0c0a}.mfa.iframe .mfa-providers li:hover{border:1px solid #0c0c0a;box-shadow:0 0 10px 0 #000}.mfa.iframe .mfa-providers a{border-bottom:1px solid #0c0c0a;display:block;text-align:center}.mfa.iframe .mfa-providers a span{padding:1em 0 2em 0;display:block}.mfa.iframe .mfa-providers a img{display:block;padding:2em 0 .5em 0;height:5em}.mfa.iframe .mfa-providers .mfa-provider-status{padding:1em;background:#444442;text-align:center}.mfa.iframe .mfa-providers .mfa-provider-status.disabled{color:#8b8b89}.mfa.iframe .mfa-trusted-device{padding:1em;display:flex}.mfa.iframe .mfa-trusted-device:nth-child(even){background:#444442}.mfa.iframe .mfa-trusted-device .device{flex:1;font-size:2.5em;text-align:center;color:#cacac9}.mfa.iframe .mfa-trusted-device .device.current:before{content:'\2022';color:#090;font-size:.75em;position:absolute;margin-left:-.5em}.mfa.iframe .mfa-trusted-device .session{flex:2 0 10em}.mfa.iframe .mfa-trusted-device .action{flex:1;padding-top:.5em}.mfa.iframe .mfa-trusted-device table td,.mfa.iframe .mfa-trusted-device table th{padding:.125em 1em}.mfa.iframe .mfa-trusted-device table th{font-weight:700}.page.administration .workspace-main .grid{position:absolute;bottom:0;top:2.125em;padding:0;overflow-y:scroll}.ldap-settings input[type=text]{max-width:100%}.ldap-settings .singleline{max-width:100%}.dialog .ldap-test-settings-report .directory-list span.error{color:#d40101}.dialog .ldap-test-settings-report p.directory-errors.error{padding:1em 0 0 0;color:#d40101}.dialog .ldap-test-settings-report .accordion-directory-structure .error{color:#d40101}.dialog .ldap-test-settings-report .accordion-directory-errors textarea{font-family:"Courier New",Courier,monospace;font-size:11px;overflow:auto;height:220px}.page.administration .mfa-settings .provider-section .description.enabled{display:none}.page.administration .mfa-settings .provider-section.enabled .description.disabled{display:none}.page.administration .mfa-settings .provider-section.enabled .description.enabled{display:block}.report-widget .colors.red{color:#d40101}.report-widget .colors.green{color:#090}.report-widget .colors.orange{color:#9f6000}.report-widget .colors.blue{color:#5bbbff}.report-widget.gauge div.widget-content{min-height:220.567px!important}.report-widget.gauge p.widget-description{color:#3b3b39;text-align:center;margin-top:-20px}.report-widget.simple-number{text-align:center}.report-widget.simple-number .widget-content{width:145px;height:145px;background-color:#f3f3f3;border-radius:50%;display:inline-block;margin-top:20px;border:5px solid #f3f3f3}.report-widget.simple-number .widget-content span{font-size:27px;margin-top:51px;display:inline-block;color:#000}.report-widget.simple-number p.widget-description{color:#3b3b39;text-align:center;margin-top:25px}.report-widget .plots{margin-top:2em}.report-widget .plots .plot-container{margin-top:2em}.report-widget .plots .plot-container .plot{display:inline-block;width:80%}.report-widget .plots .plot-container .plot span.label{font-size:1em}.report-widget .plots .plot-container .plot .bar-container{position:relative;border:1px solid #8b8b89;height:5px;margin-top:.3em}.report-widget .plots .plot-container .plot .bar-container .bar{position:absolute;top:-1px;left:-1px;height:5px;border:1px solid #5bbbff;background:#5bbbff}.report-widget .plots .plot-container .plot-value{width:15%;font-size:1.6em;padding:.35em 0 0 .5em;line-height:1em;float:right}.report-widget.widget-dashboard-count{position:relative}.report-widget.widget-dashboard-count.items .illustration{background:#ff6c70}.report-widget.widget-dashboard-count.items .illustration svg{margin-top:1.3em}.report-widget.widget-dashboard-count.groups .illustration{background:#00ba93}.report-widget.widget-dashboard-count.groups .illustration svg{margin-top:.9em;width:2.8em;height:2.8em}.report-widget.widget-dashboard-count.users .illustration{background:#5bbbff}.report-widget.widget-dashboard-count.users .illustration svg{margin-top:1.2em}.report-widget.widget-dashboard-count .count .number{display:block;font-size:2.5em;line-height:1em}.report-widget.widget-dashboard-count .count .label{display:block;font-size:1.25em;line-height:1em;margin-top:.6em}.report-widget.widget-dashboard-count .illustration{position:absolute;top:2.5em;right:2.75em;width:4.7em;height:4.7em;-webkit-border-radius:50%;-moz-border-radius:50%;border-radius:50%;background:#5bbbff;text-align:center}.report-widget.widget-dashboard-count .illustration svg{margin-top:1.1em;fill:#FFF;color:#fff;height:2.2em;width:2.2em}.report-widget.widget-dashboard-count .since{font-size:.8em;margin-top:1.8em}.report-widget.widget-dashboard-count .since span{color:#090}.report-widget.widget-dashboard-plots{margin-top:2em;padding-bottom:4em}.report-widget.widget-dashboard-plots.items .plots .plot-container .bar-container .bar{background:#ff6c70!important;border:1px solid #ff6c70!important}.report-widget.widget-login-history{margin-top:2em;margin-bottom:3em}.report-widget.widget-login-history .chart{margin-top:2em}.page.printable-report{background-color:#f3f3f3}.page.printable-report .page-wrapper{width:900px;min-height:1536px;background-color:#fff;padding:3em;margin:3em auto;box-sizing:border-box;box-shadow:0 0 10px 0 #000}.page.printable-report .page-wrapper .report-header .creator-info .label{display:inline-block;width:150px;margin-bottom:.25em}.page.printable-report .page-wrapper .report-header .creator-info .value{display:inline-block;margin-bottom:.25em}.page.printable-report .page-wrapper .report-header .company-info{text-align:right}.page.printable-report .page-wrapper .report-header .company-info .logo{margin-top:65px;width:200px;float:right}.page.printable-report .page-wrapper .report-content{margin-top:2em}.page.printable-report .page-wrapper .report-content .row.charts{margin-top:2em}.page.printable-report .page-wrapper .report-content .row.list{margin-top:3em}.page.printable-report .page-wrapper .report-content .table-info{border-right:0;border-left:0;border-bottom:0}.page.printable-report .page-wrapper .report-content .table-info tr td:first-of-type,.page.printable-report .page-wrapper .report-content .table-info tr th:first-of-type{width:30%}.page.printable-report .page-wrapper .report-content .table-info tr td{word-wrap:break-word;color:#444442;vertical-align:middle}.page.printable-report .page-wrapper .report-content .table-info tr td+td{color:#444442}.page.printable-report .page-wrapper .report-content .table-info tr span.email,.page.printable-report .page-wrapper .report-content .table-info tr span.name{display:block;max-width:300px}.page.printable-report .page-wrapper .report-content .table-info tr span.email{font-size:.8em;color:#3b3b39}@media print{@page{size:auto;margin:0}.page.printable-report{background-color:#fff}.page.printable-report .page-wrapper{min-height:0!important;box-shadow:none!important;color:#8b8b89}}.reports-workspace .accordion.navigation.first{border:none;padding:.625em 0 0 0;margin:0}.reports-workspace .workspace-reports-content{position:relative;height:100%}.reports-workspace .workspace-reports-content .report-wrapper{top:0;bottom:2.45em;padding:1em 2em;position:absolute;width:100%;overflow-y:scroll;background-color:#f3f3f3;box-sizing:border-box}.reports-workspace .workspace-reports-content .report-wrapper .report-page{margin-top:3em}.reports-workspace .workspace-reports-content .report-wrapper .report-page .report-loading{text-align:center;padding-top:10em}.reports-workspace .workspace-reports-content .report-wrapper .report-page .report-loading .spinner{background:transparent url('../../../img/controls/loading_dark.svg') center center no-repeat;content:' ';height:50px}.reports-workspace .workspace-reports-content .report-widget{background-color:#fff;padding:2.6em;border:1px solid #8b8b89;box-shadow:0 0 10px 0 #000;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.reports-workspace .workspace-reports-content .report-widget h2{font-weight:400;line-height:1em;margin:0;font-size:1.45em}.reports-workspace .workspace-reports-content iframe{width:100%;height:100%} \ No newline at end of file diff --git a/webroot/css/themes/midgar/ext_authentication.min.css b/webroot/css/themes/midgar/ext_authentication.min.css new file mode 100644 index 0000000000..45ed6a203d --- /dev/null +++ b/webroot/css/themes/midgar/ext_authentication.min.css @@ -0,0 +1,9 @@ +/**! + * @name passbolt-styleguide + * @version v3.4.0 + * @date 2021-12-01 + * @copyright Copyright 2021 Passbolt SA + * @source https://github.com/passbolt/passbolt_styleguide + * @license AGPL-3.0 + */ +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}div{outline:0}html body .hidden{display:none}.visually-hidden,.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visually-hidden .focusable:active,.visually-hidden .focusable:focus,.visuallyhidden .focusable:active,.visuallyhidden .focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.clearfix:after,.clearfix:before{content:"";display:table}.clearfix:after{clear:both}.rounded{-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem}.ellipsis{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.rotate-right{transform:rotate(-90deg)}html{line-height:normal}body{color:#fffffc;background:#30302d}body.iframe{background:0 0}@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;src:local('Open Sans'),local('OpenSans'),url('../../../fonts/opensans-regular.woff') format('woff')}@font-face{font-family:'Open Sans';font-style:normal;font-weight:700;src:local('Open Sans Bold'),local('OpenSans-Bold'),url('../../../fonts/opensans-bold.woff') format('woff')}body{font-family:'Open Sans',Verdana,sans-serif}.code{font-family:'Courier New',Courier,monospace}html{font-size:62.5%}body{font-size:1.6rem}h1{font-size:2.2rem;line-height:3.2rem}h2{font-size:1.8rem;line-height:2.4rem}h3{font-size:1.6rem;line-height:2.4rem;border-bottom:1px dotted #0c0c0a}p{font-size:1.5rem;line-height:2.2rem;margin-top:0}code{font-size:1.1rem}.font-dim{color:#cacac9}a{outline:0;text-decoration:none;cursor:pointer;border-bottom:1px solid #0c0c0a}a:link,a:visited{color:#fffffc}a:hover{text-decoration:none;cursor:pointer;color:#2894df;border-bottom:1px solid #2894df}a:active,a:focus{outline:0;color:#2894df;border:0}a.no-border,a:hover.no-border{border-bottom:0}a.disabled{outline:0;text-decoration:none;cursor:default;color:#606060;pointer-events:none}ul{padding:0;margin:0}ul li{list-style:none;padding:0;margin:0}.button,button{font-size:1.4rem;padding:.8rem 1.6rem;line-height:1.6rem;margin-right:.8rem;position:relative;display:inline-block;vertical-align:baseline;outline:0;cursor:pointer;text-align:center;text-decoration:none;border-top:1px solid #222220;border-right:1px solid #222220;border-bottom:1px solid #222220;border-left:1px solid #222220;color:#fffffc;font-weight:400;text-shadow:0 1px 0 #000;background:#444442;background-image:linear-gradient(top,#444442,#444442);-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem;box-sizing:border-box}.button.medium{font-size:1.6rem;line-height:2.4rem;padding:.8rem 2.4rem}.button.big{font-size:1.8rem;line-height:3.2rem;padding:.8rem 3.2rem}.button.full-width{width:100%}.button.primary{background:#2894df;border:1px solid #2894df;color:#fff;text-shadow:0 1px 0 rgba(0,0,0,.2)}.button.primary:hover{background:#2a9ceb;border:1px solid #2a9ceb;color:#fff}.button.primary:active,.button.primary:focus{color:#eeeeec;border:1px solid #4271b7;background:#2a9ceb}.button.warning,.button.warning:active,.button.warning:focus,.button.warning:hover{color:#eeeeec;background:#d40101;border:1px solid #d40101;text-shadow:none}.button.warning:active,.button.warning:focus{border:1px solid #92000c}.button.cancel,.button.cancel:active,.button.cancel:focus,.button.cancel:hover,.button.dim,.button.dim:active,.button.dim:focus,.button.dim:hover{background:#30302d;font-weight:400}.button:hover{color:#fffffc;text-decoration:none;background:#444442;background-image:linear-gradient(top,#606060,#606060);border:1px solid #30302d}.button:focus{color:#2894df;border:1px solid #2894df}.button:active{color:#2894df;border:1px solid #2894df;background:#3b3b39;position:relative;box-shadow:inset 0 1px 2px rgba(0,0,0,.2)}.button.disabled{cursor:default;color:#8b8b89;background:#3b3b39;border:1px solid #0c0c0a;text-shadow:none;font-weight:400}.button.disabled:active,.button.disabled:focus,.button.disabled:hover{box-shadow:0 0 0;top:0;color:#8b8b89;background:#3b3b39;border:1px solid #0c0c0a}.button.processing{background:#30302d;border:1px solid #30302d}.button.processing:after{width:100%;height:100%;position:absolute;content:" ";top:-1px;left:-1px;background:#30302d url('../../../img/controls/loading_dark.svg') center center no-repeat;background-size:auto 50%;border:1px solid #0c0c0a;-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem}input[type=submit].button.processing{font-size:0;background:#30302d url('../../../img/controls/loading_dark.svg') center center no-repeat;background-size:auto 50%;border:1px solid #0c0c0a;border-radius:5px}.button-toggle.selected{background:#222220;box-shadow:inset 0 1px 2px rgba(0,0,0,.2);border:1px solid #0c0c0a}.button-group{display:flex;flex-wrap:wrap;justify-content:flex-start}.button-group--nowrap>.button{white-space:nowrap}.button-group>*{flex-grow:1;margin-bottom:.5em}label{font-weight:700;font-size:1.6rem;line-height:2.4rem;padding:.8rem 0;display:block}.required label:after{content:" \002A";color:#ff6b70;font-weight:700}.input.error label{color:#ff6b70}input[type=email],input[type=number],input[type=password],input[type=search],input[type=text],textarea{box-sizing:border-box;font-size:1.6rem;line-height:2.4rem;color:#fffffc;background:#0c0c0a;width:100%;max-width:64rem;padding:.6rem 1.2rem;display:inline-block;margin-bottom:.8rem;border:1px solid #222220;border-top:1px solid #30302d;vertical-align:middle}::-ms-reveal{display:none}input[type=number]{box-sizing:border-box}input[type=file]{box-sizing:border-box;font-size:1.6rem;line-height:2.4rem;color:#fffffc;background:#0c0c0a;width:100%;max-width:64rem;display:inline-block;margin-bottom:.8rem;vertical-align:middle}input[type=email]:hover,input[type=number]:hover,input[type=password]:hover,input[type=search]:hover,input[type=text]:hover,textarea:hover{border:1px solid #30302d}input[type=email]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=text]:focus,textarea:focus{outline:0;box-shadow:inset 1px 1px 2px rgba(0,0,0,.2);border:1px solid #2894df}input[type=email]:disabled,input[type=number]:disabled,input[type=password]:disabled,input[type=search]:disabled,input[type=text]:disabled,textarea:disabled{color:#fffffc;border:1px solid #0c0c0a;box-shadow:0 0;cursor:default;background:#444442}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{opacity:1}textarea{height:9rem}.textarea.large textarea{width:30rem;height:9rem}.checkbox input[type=checkbox]{position:absolute;opacity:0;cursor:pointer}.checkbox input[type=checkbox].error{color:#d40101}.checkbox input[type=checkbox]+label{font-weight:400;position:relative;cursor:pointer;padding:0;margin-bottom:.8rem}.checkbox input[type=checkbox]+label:before{content:'';margin-top:.4rem;margin-right:1rem;display:inline-block;vertical-align:text-top;width:1.4rem;height:1.4rem;background:#222220;border:1px solid #606060;border-radius:.3rem;box-sizing:border-box}.checkbox input[type=checkbox]:hover+label:before{box-shadow:0 .1rem 0 #000,inset 0 .1rem 0 rgba(255,255,255,.25)}.checkbox input[type=checkbox]:focus+label:before{box-shadow:0 0 .4rem #4271b7;border:1px solid #4271b7}.checkbox input[type=checkbox]:active+label:before{box-shadow:inset 0 -.1rem 0 rgba(255,255,255,.25),inset 0 .1rem 0 #000}.checkbox input[type=checkbox]:disabled+label{color:#0c0c0a;cursor:auto}.checkbox input[type=checkbox]:disabled+label:before{box-shadow:none;background:#000;border:none}.checkbox input[type=checkbox]:checked+label:after{content:'';position:absolute;left:.2rem;top:.75rem;width:1rem;height:1rem;background-size:1rem 1rem;background-image:url('../../../img/controls/check_white.svg');-webkit-mask-image:url('../../../img/controls/check_white.svg');mask-image:url('../../../img/controls/check_white.svg')}.checkbox input[type=checkbox]:disabled+label:after{background:#30302d}.checkbox.medium input[type=checkbox]{cursor:pointer}.checkbox.medium input[type=checkbox]+label{font-size:1.5rem}.checkbox.medium input[type=checkbox]+label:before{margin-top:.3rem;margin-right:1rem;width:1.4rem;height:1.4rem}.checkbox.medium input[type=checkbox]:checked+label:after{left:.2rem;top:.7rem;width:1rem;height:1rem;background-size:1rem 1rem}.checkbox.small input[type=checkbox]{cursor:pointer}.checkbox.small input[type=checkbox]+label{font-size:1.4rem}.checkbox.small input[type=checkbox]+label:before{margin-top:.3rem;margin-right:1rem;width:1.4rem;height:1.4rem}.checkbox.small input[type=checkbox]:checked+label:after{left:.2rem;top:.7rem;width:1rem;height:1rem;background-size:1rem 1rem}.radiolist{margin-bottom:.8rem}.radiolist:after,.radiolist:before{content:"";display:table}.radiolist:after{clear:both}.radiolist:after,.radiolist:before{content:"";display:table}.radiolist:after{clear:both}.radiolist .input.radio{float:left}.radiolist .input.radio input{float:left;margin-top:.8rem}.radiolist .input.radio input+label{float:left;font-weight:400;display:inline-block;margin-left:1rem;font-size:1.6rem;margin-right:3.2rem;line-height:1.4rem}.radiolist .input.radio input+label:after{content:initial}.input.toggle-switch{display:flex;padding-top:1.6rem;padding-bottom:.8rem}.input.toggle-switch label{padding-top:0;padding-left:1.6rem;order:2;flex:1;font-weight:400;font-size:1.6rem}.input.toggle-switch.disabled label{color:#8b8b89}.input.toggle-switch .toggle-switch-checkbox{order:-1;flex:0 0 3em;display:none}.input.toggle-switch .toggle-switch-checkbox,.input.toggle-switch .toggle-switch-checkbox *,.input.toggle-switch .toggle-switch-checkbox :after,.input.toggle-switch .toggle-switch-checkbox :before,.input.toggle-switch .toggle-switch-checkbox+.toggle-switch-button,.input.toggle-switch .toggle-switch-checkbox:after,.input.toggle-switch .toggle-switch-checkbox:before{box-sizing:border-box}.input.toggle-switch .toggle-switch-checkbox ::selection,.input.toggle-switch .toggle-switch-checkbox :after::selection,.input.toggle-switch .toggle-switch-checkbox :before::selection,.input.toggle-switch .toggle-switch-checkbox+.toggle-switch-button::selection,.input.toggle-switch .toggle-switch-checkbox::selection,.input.toggle-switch .toggle-switch-checkbox:after::selection,.input.toggle-switch .toggle-switch-checkbox:before::selection{background:0 0}.input.toggle-switch .toggle-switch-button{order:-1;flex:none;outline:0;display:block;width:3em;height:1.5em;position:relative;cursor:pointer;user-select:none;background:#444442;border-radius:2em;padding:2px;transition:all .4s ease;border:1px solid #0c0c0a}.input.toggle-switch .toggle-switch-button span{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.input.toggle-switch .toggle-switch-button span .focusable:active,.input.toggle-switch .toggle-switch-button span .focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.input.toggle-switch .toggle-switch-button span .focusable:active,.input.toggle-switch .toggle-switch-button span .focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.input.toggle-switch .toggle-switch-button:hover:after{will-change:padding}.input.toggle-switch .toggle-switch-button:active{box-shadow:inset 0 0 0 2em #e8eae9}.input.toggle-switch .toggle-switch-button:active:after{padding-right:.8em}.input.toggle-switch .toggle-switch-button:after,.input.toggle-switch .toggle-switch-button:before{position:relative;display:block;content:"";width:50%;height:100%}.input.toggle-switch .toggle-switch-button:after{left:0;border-radius:2em;background:#fff;transition:left .3s cubic-bezier(.175, .885, .32, 1.275),padding .3s ease,margin .3s ease;box-shadow:0 0 0 1px rgba(0,0,0,.1),0 4px 0 rgba(0,0,0,.08)}.input.toggle-switch .toggle-switch-button:before{display:none}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button:after{left:50%}.input.toggle-switch .toggle-switch-checkbox:disabled+.toggle-switch-button{background:#30302d;cursor:not-allowed}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button{background:#6c0}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button:active{box-shadow:none}.input.toggle-switch .toggle-switch-checkbox:checked+.toggle-switch-button:active:after{margin-left:-.8em}.input.toggle-switch .toggle-switch-checkbox:disabled:checked+.toggle-switch-button{background:#e6ffcc}h1 .input.toggle-switch,h2 .input.toggle-switch,h3 .input.toggle-switch,h4 .input.toggle-switch,h5 .input.toggle-switch,h6 .input.toggle-switch{float:left;padding:.4em 1em 0 0}.input.select select{display:inline-block;font-size:1.6rem;line-height:2.4rem;color:#fffffc;padding:.6rem 1.2rem;width:100%;max-width:64rem;box-sizing:border-box;margin:0 0 .8rem 0;border:1px solid #222220;-moz-appearance:none;-webkit-appearance:none;appearance:none;background-color:#30302d;background-image:url('../../../img/controls/chevron-down_white.svg'),linear-gradient(to bottom,#0c0c0a 0,#0c0c0a 100%);background-repeat:no-repeat,repeat;background-position:right .6rem top 50%,0 0;background-size:1rem auto,100%}.input.select select.medium{width:50%}.input.select select:focus{outline:0;border:1px solid #2894df;border-radius:0;background-image:url('../../../img/controls/chevron-down_blue.svg'),linear-gradient(to bottom,#0c0c0a 0,#0c0c0a 100%)}.input.select select:disabled{background-image:url('../../../img/controls/chevron-down_white.svg'),linear-gradient(to bottom,#444442 0,#444442 100%);color:#8b8b89}.input .error-message{padding:0;font-size:1.4rem;margin-top:.2rem;margin-bottom:1.6rem;background-color:transparent;border:0;font-weight:400;color:#ff6b70;background:#30302d;clear:both}.input .help-message{padding:0;font-size:1.4rem;margin-top:.2rem;margin-bottom:1.6rem;background-color:transparent;border:0;color:#8b8b89;font-weight:400;background:#30302d;clear:both}.singleline{display:flex;max-width:64rem}.singleline:after,.singleline:before{content:"";display:table}.singleline:after{clear:both}.singleline:after,.singleline:before{content:"";display:table}.singleline:after{clear:both}.singleline .input{flex:1}.singleline .input.first-field,.singleline .input:first-child{margin-right:2%}.slider{display:flex;align-items:center}.slider input[type=range]{-webkit-appearance:none;width:100%;height:1px;border-radius:5px;background:#8b8b89;outline:0;opacity:.7;-webkit-transition:.2s;transition:opacity .2s;flex-grow:1}.slider input[type=range]:hover{opacity:1}.slider input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:15px;height:15px;border-radius:50%;border-width:1px;border-style:solid;border-color:#3b3b39;background:#8b8b89;cursor:pointer}.slider input[type=range]::-moz-range-thumb{width:15px;height:15px;border-radius:50%;background:#8b8b89;cursor:pointer}.slider input[type=number]{width:6.5rem;margin-left:1.6rem;padding:.8rem}.svg-icon{display:inline-flex;align-self:center}.svg-icon svg{fill:#FFF;height:1em;width:1em}.svg-icon.baseline svg{top:.125em;position:relative}.svg-icon.dim svg,.svg-icon.light svg{fill:#EEEEEC}.svg-icon.icon-only svg{padding:.1rem;height:1.6rem;width:1.6rem}a:hover .svg-icon svg{fill:#2894DF}a.disabled .svg-icon svg{fill:#8B8B89;pointer-events:none;cursor:default}a.disabled:hover .svg-icon svg{fill:#8B8B89}a.fav .svg-icon svg{fill:#D40101}a.unfav .svg-icon svg{fill:#8B8B89}.button .svg-icon svg{top:.2rem;position:relative}.button .svg-icon+span:not(.visuallyhidden){margin-left:.8rem;display:inline-block}.button .svg-icon svg:hover,.button:hover .svg-icon svg{fill:#FFFFFC}.button.primary:active .svg-icon svg,.button.primary:focus .svg-icon svg,.button.warning .svg-icon svg,.button.warning:active .svg-icon svg,.button.warning:focus .svg-icon svg,.button.warning:hover .svg-icon svg{fill:#EEEEEC}.button:active .svg-icon svg,.button:focus .svg-icon svg{fill:#2894DF}.button.disabled .svg-icon svg{fill:#8B8B89}.button.disabled .svg-icon svg:hover{fill:#8B8B89}@keyframes drawCircle{0%{stroke-dashoffset:180px}100%{stroke-dashoffset:0}}@keyframes drawCheck{0%{stroke-dashoffset:50px}100%{stroke-dashoffset:0}}@keyframes drawCross{0%{stroke-dashoffset:50px}100%{stroke-dashoffset:0}}@keyframes drawWarning{0%{stroke-dashoffset:230px}100%{stroke-dashoffset:0}}.icon-feedback .success-animation-circle{stroke-dasharray:180px 180px;stroke:#009900}.icon-feedback .success-animation-line{stroke-dasharray:50px 50px;stroke:#009900}.icon-feedback .success-animation-line2{stroke-dasharray:50px 50px;stroke:#009900}.icon-feedback .error-animation-circle{stroke-dasharray:180px 180px;stroke:#D40101}.icon-feedback .error-animation-line{stroke-dasharray:50px 50px;stroke:#D40101}.icon-feedback .warning-animation-line{stroke-dasharray:230px 230px;stroke:#9F6000;stroke-linecap:round;stroke-linejoin:round}.icon-feedback .warning-animation-circle{fill:#9F6000}.icon-feedback .animated{animation:.75s ease-out 0s 1 both pop}.icon-feedback .animated .error-animation-circle,.icon-feedback .animated .success-animation-circle,.icon-feedback .animated .warning-animation-circle{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawCircle}.icon-feedback .animated .success-animation-line{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawCheck}.icon-feedback .animated .error-animation-line{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawCross}.icon-feedback .animated .warning-animation-line{animation:.75s cubic-bezier(.77,0,.175,1) 0s 1 both drawWarning}table{border-collapse:collapse;border-spacing:0}table td,table th{text-align:left;font-weight:400}.logo{background:transparent url('../../../img/logo/logo_white.svg') 0 0 no-repeat;background-size:20rem auto;width:20rem;height:4.5rem}.scroll{overflow-y:scroll;background:linear-gradient(#30302d 30%,rgba(255,255,255,0)),linear-gradient(rgba(255,255,255,0),#30302d 70%) 0 100%,radial-gradient(farthest-side at 75% 0,rgba(0,0,0,.2),rgba(0,0,0,0)),radial-gradient(farthest-side at 75% 100%,rgba(0,0,0,.2),rgba(0,0,0,0)) 0 100%;background-repeat:no-repeat;background-color:#30302d;background-size:100% 10px,100% 10px,100% 5px,100% 5px}::-webkit-scrollbar{width:1em}::-webkit-scrollbar-track{background:#30302d;border-top:0;border-bottom:0}::-webkit-scrollbar-thumb{background:#606060;border-radius:1em;border:4px solid #30302d}.scroll-shadow{overflow:auto;background:linear-gradient(#30302d 30%,rgba(255,255,255,0)),linear-gradient(rgba(255,255,255,0),#30302d 70%) 0 100%,radial-gradient(farthest-side at 50% 0,rgba(0,0,0,.08),rgba(0,0,0,0)),radial-gradient(farthest-side at 50% 100%,rgba(0,0,0,.08),rgba(0,0,0,0)) 0 100%;background-repeat:no-repeat;background-color:#30302d;background-size:100% 40px,100% 40px,100% 14px,100% 14px;background-attachment:local,local,scroll,scroll}.simplebar-content{overflow:auto;background:linear-gradient(#30302d 30%,rgba(255,255,255,0)),linear-gradient(rgba(255,255,255,0),#30302d 70%) 0 100%,radial-gradient(farthest-side at 50% 0,rgba(0,0,0,.08),rgba(0,0,0,0)),radial-gradient(farthest-side at 50% 100%,rgba(0,0,0,.08),rgba(0,0,0,0)) 0 100%;background-repeat:no-repeat;background-color:#30302d;background-size:100% 40px,100% 40px,100% 14px,100% 14px;background-attachment:local,local,scroll,scroll}[data-simplebar]{position:relative;flex-direction:column;flex-wrap:wrap;justify-content:flex-start;align-content:flex-start;align-items:flex-start;width:inherit;height:inherit;max-width:inherit;max-height:inherit}.simplebar-wrapper{overflow:hidden;width:inherit;height:inherit;max-width:inherit;max-height:inherit}.simplebar-mask{direction:inherit;position:absolute;overflow:hidden;padding:0;margin:0;left:0;top:0;bottom:0;right:0;width:auto!important;height:auto!important;z-index:0}.simplebar-offset{direction:inherit!important;box-sizing:inherit!important;resize:none!important;position:absolute;top:0;left:0;bottom:0;right:0;padding:0;margin:0;-webkit-overflow-scrolling:touch}.simplebar-content-wrapper{direction:inherit;box-sizing:border-box!important;position:relative;display:block;height:100%;width:auto;max-width:100%;max-height:100%;scrollbar-width:none;-ms-overflow-style:none}.simplebar-content-wrapper::-webkit-scrollbar,.simplebar-hide-scrollbar::-webkit-scrollbar{width:0;height:0}.simplebar-content:after,.simplebar-content:before{content:' ';display:table}.simplebar-placeholder{max-height:100%;max-width:100%;width:100%;pointer-events:none}.simplebar-height-auto-observer-wrapper{box-sizing:inherit!important;height:100%;width:inherit;max-width:1px;position:relative;float:left;max-height:1px;overflow:hidden;z-index:-1;padding:0;margin:0;pointer-events:none;flex-grow:inherit;flex-shrink:0;flex-basis:0}.simplebar-height-auto-observer{box-sizing:inherit;display:block;opacity:0;position:absolute;top:0;left:0;height:1000%;width:1000%;min-height:1px;min-width:1px;overflow:hidden;pointer-events:none;z-index:-1}.simplebar-track{z-index:1;position:absolute;right:0;bottom:0;pointer-events:none;overflow:hidden}[data-simplebar].simplebar-dragging .simplebar-content{pointer-events:none;user-select:none;-webkit-user-select:none}[data-simplebar].simplebar-dragging .simplebar-track{pointer-events:all}.simplebar-scrollbar{position:absolute;right:2px;width:7px;min-height:10px}.simplebar-scrollbar:before{position:absolute;content:'';background:#000;border-radius:7px;left:0;right:0;opacity:0;transition:opacity .2s linear}.simplebar-scrollbar.simplebar-visible:before{opacity:.5;transition:opacity 0s linear}.simplebar-track.simplebar-vertical{top:0;width:11px}.simplebar-track.simplebar-vertical .simplebar-scrollbar:before{top:2px;bottom:2px}.simplebar-track.simplebar-horizontal{left:0;height:11px}.simplebar-track.simplebar-horizontal .simplebar-scrollbar:before{height:100%;left:2px;right:2px}.simplebar-track.simplebar-horizontal .simplebar-scrollbar{right:auto;left:0;top:2px;height:7px;min-height:0;min-width:10px;width:auto}[data-simplebar-direction=rtl] .simplebar-track.simplebar-vertical{right:auto;left:0}.hs-dummy-scrollbar-size{direction:rtl;position:fixed;opacity:0;visibility:hidden;height:500px;width:500px;overflow-y:hidden;overflow-x:scroll}.simplebar-hide-scrollbar{position:fixed;left:0;visibility:hidden;overflow-y:scroll;scrollbar-width:none;-ms-overflow-style:none}.shimmer{display:block;position:relative;width:100%;height:100%;content:'';animation:shimmer 2s infinite;background:linear-gradient(45deg,rgba(48,48,45,0) 0,rgba(48,48,45,.1) 30%,rgba(48,48,45,.5) 50%,rgba(48,48,45,0))}@keyframes shimmer{0%{background-position:0 0}100%{background-position:1000px 0}}html{scroll-behavior:smooth}.animated{-webkit-animation-duration:.5s;animation-duration:.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both}@keyframes fadeInUp{0%{opacity:0;-webkit-transform:translateY(20px);-ms-transform:translateY(20px);transform:translateY(20px)}100%{opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}}@keyframes fadeOutUp{from{opacity:1}to{opacity:0;transform:translate3d(0,-100%,0)}}@keyframes fadeInDown{from{opacity:0;transform:translate3d(0,-100%,0)}to{opacity:1;transform:none}}@keyframes pop{0%{opacity:0;transform:scale(1)}55%{opacity:1;transform:scale(1)}65%{opacity:1;transform:scale(1.25)}75%{opacity:1;transform:scale(1)}100%{opacity:1;transform:scale(1)}}.fadeInDown{animation-name:fadeInDown}.fadeOutUp{animation-name:fadeOutUp}.fadeInUp{animation-name:fadeInUp}.pop{animation-name:pop}.page{width:100%;min-width:32rem;top:0;left:0;right:0;bottom:0;position:absolute;overflow:auto}.page .panel{height:100%;width:100%;position:absolute;overflow:auto}.page .panel.main{bottom:3.8rem;height:auto;padding:0}.page .panel.main .grid{padding-bottom:1.6rem}.page .panel.main .row{padding-left:0}.page .panel.main .panel.left{background:#353532}.page .header.second+.panel.main{top:6.875em}.page .header.third+.panel.main{top:10em;border-top:1px solid #0c0c0a}.page .header{width:100%;overflow:hidden}.page .header.first{min-height:3.2rem}.page .header.second{height:7rem}.page .header.third{height:4.8rem}.page .col1,.page .col2,.page .col2_3,.page .col3{position:absolute}.page .col1,.page .panel.left{width:17.25%;box-sizing:border-box}.page .panel.middle{width:82%;left:18%;overflow:hidden}.page .panel.middle.scroll{overflow-y:scroll}.page .col2{width:70%;left:18%}.page .col3,.page .panel.right{width:24%;left:75%}.page .col2_3{width:81.5%;left:18%}.page .panel.main .grid-responsive-12{float:left;width:100%;max-width:none;padding-left:.25em;box-sizing:border-box}@media all and (max-width:1024px){.page .panel.left{display:none}.page .header.second .col2{display:none}.page .header.second .col2_3{display:none}.page .header.third{height:4.5em}.page .header.third .col1,.page .header.third .col2,.page .header.third .col2_3{position:relative;float:left;left:auto;width:auto}.page .header.third .col3{display:none}.page .panel.main .panel.left{display:none}.page .panel.main .panel.middle{width:100%;left:0}.page .panel.main .row{padding-left:.5em}.page .panel.main .panel.aside{min-width:auto}}.grid,.grid-responsive-12{margin:0 auto;padding:0;max-width:1220px}.grid .row,.grid-responsive-12 .row{padding:0 .5em 0 .5em}.grid .row:after,.grid .row:before,.grid-responsive-12 .row:after,.grid-responsive-12 .row:before{content:"";display:table}.grid .row:after,.grid-responsive-12 .row:after{clear:both}.grid .row:after,.grid .row:before,.grid-responsive-12 .row:after,.grid-responsive-12 .row:before{content:"";display:table}.grid .row:after,.grid-responsive-12 .row:after{clear:both}.grid .row .col1,.grid .row .col10,.grid .row .col11,.grid .row .col12,.grid .row .col2,.grid .row .col3,.grid .row .col4,.grid .row .col5,.grid .row .col6,.grid .row .col7,.grid .row .col8,.grid .row .col9,.grid-responsive-12 .row .col1,.grid-responsive-12 .row .col10,.grid-responsive-12 .row .col11,.grid-responsive-12 .row .col12,.grid-responsive-12 .row .col2,.grid-responsive-12 .row .col3,.grid-responsive-12 .row .col4,.grid-responsive-12 .row .col5,.grid-responsive-12 .row .col6,.grid-responsive-12 .row .col7,.grid-responsive-12 .row .col8,.grid-responsive-12 .row .col9{position:relative;left:auto;float:none;width:99%;box-sizing:border-box}.grid .row .col10:after,.grid .row .col10:before,.grid .row .col11:after,.grid .row .col11:before,.grid .row .col12:after,.grid .row .col12:before,.grid .row .col1:after,.grid .row .col1:before,.grid .row .col2:after,.grid .row .col2:before,.grid .row .col3:after,.grid .row .col3:before,.grid .row .col4:after,.grid .row .col4:before,.grid .row .col5:after,.grid .row .col5:before,.grid .row .col6:after,.grid .row .col6:before,.grid .row .col7:after,.grid .row .col7:before,.grid .row .col8:after,.grid .row .col8:before,.grid .row .col9:after,.grid .row .col9:before,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col10:before,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col11:before,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col12:before,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col1:before,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col2:before,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col3:before,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col4:before,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col5:before,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col6:before,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col7:before,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col8:before,.grid-responsive-12 .row .col9:after,.grid-responsive-12 .row .col9:before{content:"";display:table}.grid .row .col10:after,.grid .row .col11:after,.grid .row .col12:after,.grid .row .col1:after,.grid .row .col2:after,.grid .row .col3:after,.grid .row .col4:after,.grid .row .col5:after,.grid .row .col6:after,.grid .row .col7:after,.grid .row .col8:after,.grid .row .col9:after,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col9:after{clear:both}.grid .row .col10:after,.grid .row .col10:before,.grid .row .col11:after,.grid .row .col11:before,.grid .row .col12:after,.grid .row .col12:before,.grid .row .col1:after,.grid .row .col1:before,.grid .row .col2:after,.grid .row .col2:before,.grid .row .col3:after,.grid .row .col3:before,.grid .row .col4:after,.grid .row .col4:before,.grid .row .col5:after,.grid .row .col5:before,.grid .row .col6:after,.grid .row .col6:before,.grid .row .col7:after,.grid .row .col7:before,.grid .row .col8:after,.grid .row .col8:before,.grid .row .col9:after,.grid .row .col9:before,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col10:before,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col11:before,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col12:before,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col1:before,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col2:before,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col3:before,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col4:before,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col5:before,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col6:before,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col7:before,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col8:before,.grid-responsive-12 .row .col9:after,.grid-responsive-12 .row .col9:before{content:"";display:table}.grid .row .col10:after,.grid .row .col11:after,.grid .row .col12:after,.grid .row .col1:after,.grid .row .col2:after,.grid .row .col3:after,.grid .row .col4:after,.grid .row .col5:after,.grid .row .col6:after,.grid .row .col7:after,.grid .row .col8:after,.grid .row .col9:after,.grid-responsive-12 .row .col10:after,.grid-responsive-12 .row .col11:after,.grid-responsive-12 .row .col12:after,.grid-responsive-12 .row .col1:after,.grid-responsive-12 .row .col2:after,.grid-responsive-12 .row .col3:after,.grid-responsive-12 .row .col4:after,.grid-responsive-12 .row .col5:after,.grid-responsive-12 .row .col6:after,.grid-responsive-12 .row .col7:after,.grid-responsive-12 .row .col8:after,.grid-responsive-12 .row .col9:after{clear:both}.grid .row .col1 img,.grid .row .col10 img,.grid .row .col11 img,.grid .row .col12 img,.grid .row .col2 img,.grid .row .col3 img,.grid .row .col4 img,.grid .row .col5 img,.grid .row .col6 img,.grid .row .col7 img,.grid .row .col8 img,.grid .row .col9 img,.grid-responsive-12 .row .col1 img,.grid-responsive-12 .row .col10 img,.grid-responsive-12 .row .col11 img,.grid-responsive-12 .row .col12 img,.grid-responsive-12 .row .col2 img,.grid-responsive-12 .row .col3 img,.grid-responsive-12 .row .col4 img,.grid-responsive-12 .row .col5 img,.grid-responsive-12 .row .col6 img,.grid-responsive-12 .row .col7 img,.grid-responsive-12 .row .col8 img,.grid-responsive-12 .row .col9 img{width:100%;height:auto;display:block}@media all and (min-width:780px){.grid .row{padding:0 .5em 0 .5em}.grid .row .col1,.grid .row .col10,.grid .row .col11,.grid .row .col12,.grid .row .col2,.grid .row .col3,.grid .row .col4,.grid .row .col5,.grid .row .col6,.grid .row .col7,.grid .row .col8,.grid .row .col9{float:left;position:relative;margin:0 3% 0 0}.grid .row .last{margin-right:0}.grid .row .col1{width:5.5%}.grid .row .col2{width:14%}.grid .row .col3{width:22.5%}.grid .row .col4{width:31%}.grid .row .col5{width:39.5%}.grid .row .col6{width:48%}.grid .row .col7{width:56.5%}.grid .row .col8{width:65%}.grid .row .col9{width:73.5%}.grid .row .col10{width:82%}.grid .row .col11{width:90.5%}.grid .row .col12{width:99%;margin:0}.grid .row .push1{margin-left:5.5%}.grid .row .push2{margin-left:14%}.grid .row .push3{margin-left:22.5%}.grid .row .push4{margin-left:31%}}@media all and (max-width:768px){.hidden-xs{display:none}}.tooltip,[data-tooltip]{position:relative;cursor:pointer}.tooltip:after,.tooltip:before,[data-tooltip]:after,[data-tooltip]:before{position:absolute;visibility:hidden;opacity:0;transition:opacity .2s ease-in-out,visibility .2s ease-in-out,transform .2s cubic-bezier(.71, 1.7, .77, 1.24);transform:translate3d(0,0,0);pointer-events:none;text-align:center}.always-show:after,.always-show:before,.tooltip:focus:after,.tooltip:focus:before,.tooltip:hover:after,.tooltip:hover:before,[data-tooltip]:focus:after,[data-tooltip]:focus:before,[data-tooltip]:hover:after,[data-tooltip]:hover:before{visibility:visible;opacity:1}.tooltip:before,[data-tooltip]:before{z-index:990;border:.6rem solid transparent;background:0 0;content:""}.tooltip:after,[data-tooltip]:after{z-index:990;padding:.4rem;width:16rem;background-color:hsla(0,0%,0%,.9);color:#eeeeec;content:attr(data-tooltip);font-size:1.4rem;line-height:2.4rem;font-weight:400;text-shadow:none}.tooltip-top:after,.tooltip-top:before,.tooltip:after,.tooltip:before,[data-tooltip]:after,[data-tooltip]:before{bottom:100%;left:50%}.tooltip-top:before,.tooltip:before,[data-tooltip]:before{margin-left:-.6rem;margin-bottom:-1.2rem;border-top-color:hsla(0,0%,20%,.9)}.tooltip-top:after,.tooltip:after,[data-tooltip]:after{margin-left:-8rem}.tooltip-top.always-show:after,.tooltip-top.always-show:before,.tooltip-top:focus:after,.tooltip-top:focus:before,.tooltip-top:hover:after,.tooltip-top:hover:before,.tooltip.always-show:after,.tooltip.always-show:before,.tooltip:focus:after,.tooltip:focus:before,.tooltip:hover:after,.tooltip:hover:before,[data-tooltip]:focus:after,[data-tooltip]:focus:before,[data-tooltip]:hover:after,[data-tooltip]:hover:before{transform:translateY(-1.2rem)}.tooltip-left:after,.tooltip-left:before{right:100%;bottom:50%;left:auto}.tooltip-left:before{margin-left:0;margin-right:-1.2rem;margin-bottom:0;border-top-color:transparent;border-left-color:hsla(0,0%,20%,.9)}.tooltip-left.always-show:after,.tooltip-left.always-show:before,.tooltip-left:focus:after,.tooltip-left:focus:before,.tooltip-left:hover:after,.tooltip-left:hover:before{transform:translateX(-1.2rem)}.tooltip-bottom:after,.tooltip-bottom:before{top:100%;bottom:auto;left:50%}.tooltip-bottom:before{margin-top:-1.2rem;margin-bottom:0;border-top-color:transparent;border-bottom-color:hsla(0,0%,20%,.9)}.tooltip-bottom.always-show:after,.tooltip-bottom.always-show:before,.tooltip-bottom:focus:after,.tooltip-bottom:focus:before,.tooltip-bottom:hover:after,.tooltip-bottom:hover:before{transform:translateY(1.2rem)}.tooltip-right:after,.tooltip-right:before{bottom:50%;left:100%}.tooltip-right:before{margin-bottom:0;margin-left:-1.2rem;border-top-color:transparent;border-right-color:#222220}.tooltip-right.always-show:after,.tooltip-right.always-show:before,.tooltip-right:focus:after,.tooltip-right:focus:before,.tooltip-right:hover:after,.tooltip-right:hover:before{transform:translateX(1.2rem)}.tooltip-left:before,.tooltip-right:before{top:.3rem}.tooltip-left:after,.tooltip-right:after{margin-left:0;margin-bottom:-1.6rem}.tooltip.large:after,[data-tooltip].large:after{width:240rem}.tooltip-left.large:after,.tooltip-left.large:before,.tooltip-right.large:after,.tooltip-right.large:before{margin-top:.6rem}.tooltip-alt{cursor:pointer;display:inline-block;border-bottom:0}.tooltip-alt .tooltip-text{position:absolute;visibility:hidden;width:180px;background-color:#222220;color:#eeeeec;padding:.5em 1em;transition:opacity .2s ease-in-out,visibility .2s ease-in-out,transform .2s cubic-bezier(.71, 1.7, .77, 1.24);transform:translate3d(0,0,0);z-index:99999}.tooltip-alt .tooltip-text.right{margin-left:1.2rem;margin-top:-2.65rem}.tooltip-alt .tooltip-text.right::after{content:" ";position:absolute;top:1.125em;right:100%;margin-top:-5px;border:5px #333 solid;border-color:transparent #222220 transparent transparent}.tooltip-alt:hover .tooltip-text{transform:translateX(12px);visibility:visible;opacity:1}.tooltip svg,.tooltip-alt svg{fill:#cacac9;top:.125em;position:relative}.openpgp-key textarea{height:12rem}.enter-passphrase .password:after,.enter-passphrase .password:before{content:"";display:table}.enter-passphrase .password:after{clear:both}.enter-passphrase .password:after,.enter-passphrase .password:before{content:"";display:table}.enter-passphrase .password:after{clear:both}.enter-passphrase .password input[type=password],.enter-passphrase .password input[type=text]{float:left;box-sizing:border-box;width:calc(100% - 4.2rem)}.enter-passphrase .password .password-view{width:4.2rem;float:left;margin-top:0;margin-right:0;margin-left:-.1rem;height:3.8rem;border-radius:0;padding:.6rem .8rem .6rem 1rem;background:#0c0c0a;border-left:0;border-top:1px solid #30302d}.enter-passphrase .password .password-view.selected{background:#222220;box-shadow:inset 0 1px 2px rgba(0,0,0,.2)}.enter-passphrase .password .password-view svg{margin-top:.2rem}.enter-passphrase .password input[type=password]:focus~.password-view,.enter-passphrase .password input[type=text]:focus~.password-view{border:1px solid #2894df;border-left:0}.enter-passphrase .password.with-token input[type=password],.enter-passphrase .password.with-token input[type=text]{width:calc(100% - 11.7rem)}.enter-passphrase .password.with-token .password-view{margin-right:.8rem}.enter-passphrase .password.with-token .security-token{width:6rem;float:left;margin-top:0;margin-right:0;border-radius:0;padding:.7rem 0;text-align:center;background:#0c0c0a;border:1px solid #30302d}.password-complexity .complexity-text{float:right;clear:right;width:30%;font-size:1rem;text-align:left;padding-left:0}.password-complexity.not_available .complexity-text{color:#0c0c0a}.password-complexity .progress{width:100%;box-sizing:border-box;border:1px solid #0c0c0a;height:10px;display:block;clear:both;margin:.25em 0 .5em 0;float:left}.password-complexity .progress-bar{background:#000;width:0;height:6px;display:block;float:left;margin:1px}.password-complexity .progress-bar.very-weak{background:#000;width:5%}.password-complexity .progress-bar.weak{background:#d40101;width:10%}.password-complexity .progress-bar.fair{background:#ffbd2e;width:60%}.password-complexity .progress-bar.strong{background:#6c0;width:80%}.password-complexity .progress-bar.very-strong{background:#090;width:99.5%}.password-hints{margin:.5em 0 1em 0}.password-hints li{font-size:1.5rem;line-height:2.4rem}.password-hints li:before{content:"\25CF";color:#444442;padding-right:.5em}.password-hints li.success:before{color:#090}.password-hints li.error:before{color:#d40101}.dialog-wrapper{position:absolute;width:100%;height:100%;z-index:800;background:rgba(0,0,0,.8);overflow:auto}.dialog{position:relative;max-width:48rem;border:1px solid #0c0c0a;background:#444442;margin:auto;margin-top:1%;margin-bottom:4.8rem;box-shadow:0 0 10px 0 #000}.dialog .dialog-header{padding:.8rem 1.6rem 0 1.6rem;height:4.8rem}.dialog .dialog-header h2{margin:.8rem 0 1rem .4rem;font-size:1.8rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.dialog .dialog-header .dialog-header-subtitle{padding-top:.4rem;padding-left:.8rem;font-size:1.4rem;color:#fffffc}.dialog .dialog-header .tooltip-alt{margin-left:.8rem;font-size:1.2rem;font-weight:400;color:#fffffc;line-height:1em}.dialog .dialog-header .tooltip-alt .tooltip-text{white-space:normal}.dialog .dialog-header .dialog-close{margin-top:-3.6rem}.dialog .form-content{background:#30302d;padding:1rem 2rem 2.4rem 2rem}.dialog .error-message{margin-bottom:1rem}.dialog p{margin-top:.8rem;font-size:1.5rem}.dialog p+.checkbox{padding-bottom:.8rem}.dialog p+.checkbox label{font-size:1.6rem}.dialog label{clear:both;font-size:1.5rem}.dialog input[type=text],.dialog textarea{width:100%;box-sizing:border-box}.dialog input[type=text]:after,.dialog input[type=text]:before,.dialog textarea:after,.dialog textarea:before{content:"";display:table}.dialog input[type=text]:after,.dialog textarea:after{clear:both}.dialog input[type=text]:after,.dialog input[type=text]:before,.dialog textarea:after,.dialog textarea:before{content:"";display:table}.dialog input[type=text]:after,.dialog textarea:after{clear:both}.dialog input+.message,.dialog textarea+.message{display:none}.dialog input+.message.error,.dialog textarea+.message.error{display:block;clear:both;width:100%}.dialog .inline-error{color:#ff6b70;font-weight:700}.dialog .accordion-header a{display:inline}.dialog .submit-wrapper{margin:0;clear:both;width:100%;padding:1.2rem 0}.dialog .submit-wrapper .button,.dialog .submit-wrapper .cancel{float:right;margin-right:1.6rem}.dialog .submit-wrapper .button{box-sizing:border-box;min-width:8rem}.dialog .submit-wrapper .primary{font-size:1.8rem;padding:1.2rem 2.4rem}.dialog .submit-wrapper .cancel{margin-top:.7em;font-size:1.6rem}.dialog-close,.dialog-close:hover{display:block;float:right;border:0}.dialog-close:active{-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem;box-shadow:inset 0 1px 2px rgba(0,0,0,.2)}.dialog-close .fa-close{padding-top:15px;width:30px;height:15px;text-align:center;display:block;vertical-align:baseline;line-height:0;border:1px solid #3b3b39;-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem}.dialog-close .svg-icon{padding:7px 0 7px 0;width:30px;height:15px;text-align:center;display:block;vertical-align:baseline;line-height:15px;border:1px solid #3b3b39;-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem}.dialog-close:hover .fa-close,.dialog-close:hover .svg-icon{border:1px solid #3b3b39;-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem}@media all and (max-width:480px){.dialog{margin:0;border:0;box-shadow:none;width:100%;max-width:100%;margin-bottom:2.5em}}.footer{margin-top:0;position:fixed;bottom:0;text-align:right;font-size:1.2rem;width:100%;height:3.8rem;background:#30302d;border-top:1px solid #0c0c0a;z-index:890}.footer .footer-links{padding-top:1rem;width:100%}.footer .footer-links li{display:inline;margin-right:1.5em}.footer .footer-links li.error-message a{background-color:#30302d;color:#ff6b70}.footer .footer-links li .github-star{display:inline;position:absolute;margin-left:-8em;margin-top:-1px}.footer .footer-links a:not(.gh-btn):not(.gh-count){border:0}.avatar img{width:36px;height:36px;border-radius:50%}.big.avatar img{width:72px;height:72px;border-radius:50%}iframe{margin:0;padding:0;border:0;outline:0;font-size:100%;vertical-align:baseline;background:0 0}iframe.full-screen{position:absolute;width:100%;height:100%;z-index:999;border:0;top:0;left:0}iframe.cachette{position:absolute;width:1px;height:1px;z-index:999;border:0;bottom:0;right:0}#user-locale-input{border:none;background:#222220 url('../../../img/controls/chevron-down_white.svg') 92% center/1rem no-repeat;appearance:none;margin-top:.8rem;padding-left:.4rem;padding-right:2rem;line-height:2rem;width:auto}#user-locale-input:focus{outline:0;border:1px solid #2894df;border-radius:0}#user-locale-input option{background-color:#222220}body,html{height:100%}.login.page h1{margin-top:0;font-size:2.4rem;color:#cacac9}.login.page p{font-size:1.6rem;line-height:2.4rem}.login.page .processing-wrapper{display:block;width:16rem;height:16rem;margin:auto}.login.page .processing-wrapper .processing{display:block;text-align:center}.login.page .processing-wrapper .processing:after{display:block;width:16rem;height:16rem;content:" ";background:transparent url('../../../img/controls/loading_dark.svg') center center no-repeat;background-size:auto 40%}.login.page .login-form{min-height:16rem}.login.page .login-form .form-actions{text-align:center;padding-top:1.2em}.login.page .login-form .button+a{font-size:1.6rem;line-height:2.4rem;text-align:center;display:inline-block;margin-top:1.6rem;cursor:pointer}.login.page .email-sent-instructions{text-align:center}.login.page .email-sent-instructions .email-sent-bg{background:transparent url('../../../img/illustrations/email.png') top center no-repeat;background-size:auto 90%;height:16rem}.login.page .email-sent-instructions h1{margin-top:2.4rem}.login.page .email-sent-instructions p{padding:.8rem .8rem 0 .8rem;margin-bottom:0}.login.page .choose-security-token .input-security-token{margin:1em 0 1.5em 0}.login.page .choose-security-token .input-security-token:after,.login.page .choose-security-token .input-security-token:before{content:"";display:table}.login.page .choose-security-token .input-security-token:after{clear:both}.login.page .choose-security-token .input-security-token:after,.login.page .choose-security-token .input-security-token:before{content:"";display:table}.login.page .choose-security-token .input-security-token:after{clear:both}.login.page .choose-security-token .input-security-token label{margin-bottom:.8rem}.login.page .choose-security-token .input-security-token .input.text{-webkit-border-radius:0.3rem;-moz-border-radius:.3rem;border-radius:.3rem;font-size:3rem;max-width:10rem;float:left;text-align:center;margin-right:3rem}.login.page .choose-security-token .input-security-token .circle-picker{float:left}.login.page .choose-security-token .input-security-token .randomize-button-wrapper{float:left;width:10rem;text-align:center;clear:both;cursor:pointer}.login.page .install-extension a.browser-webstore{border:0}.login.page .install-extension a.browser-webstore img{display:block;margin-left:auto;margin-right:auto;max-width:26rem}.login.page .install-extension a.browser-webstore.edge img,.login.page .install-extension a.browser-webstore.firefox img{padding:1.6rem 0}.login.page .introduce-setup-extension .animated-setup-introduction.chrome{background:transparent url('../../../img/illustrations/pin_passbolt.gif') center center no-repeat;background-size:contain;height:25rem}.login.page .introduce-setup-extension .arrow{background:transparent url('../../../img/illustrations/wave-pin_my_extension.svg') center top no-repeat;width:10rem;height:10rem;position:absolute;top:0;right:calc(3.75rem - calc(100vw - 100%))}.login.page .browser-not-supported a.browser{border:0}.login.page .browser-not-supported a.browser img{max-width:26rem;display:block;margin-left:auto;margin-right:auto}.login.page .login .login-user{width:100%;margin:auto}.login.page .login .login-user>*{text-align:center}.login.page .login .login-user .login-user-name{font-weight:700;font-size:1.6rem;line-height:1rem;margin-top:1.6rem}.login.page .login .login-user .login-user-email{font-size:1.6rem;line-height:1rem}.login.page .login-processing{display:flex;flex-direction:column;align-items:center;justify-content:center}@media only screen and (min-width:42rem){body{background:#222220}.login.page{display:grid;height:calc(100% - 4rem);grid-template-columns:1fr 1.5fr 1fr;grid-template-rows:0 1fr 0.05fr;grid-gap:1px;grid-template-areas:". . ." ". login-form ." "footer footer footer"}.login.page .content{grid-area:login-form}.login.page .content .loading-bar{display:block}.login.page .content .logo{margin:1.6em auto;width:20rem;background-size:20rem auto}.login.page .content .login-form{box-shadow:0 0 10px 0 #000;border-radius:.3rem;max-width:37.2rem;margin:auto;padding:4.8rem 4rem 5.6rem 4rem;background:#30302d}.login.page .content .input.select.locale{max-width:45.2rem;margin:auto}} \ No newline at end of file diff --git a/webroot/img/third_party/ChromeWebStore_black.svg b/webroot/img/third_party/ChromeWebStore_black.svg new file mode 100644 index 0000000000..e2ef28672b --- /dev/null +++ b/webroot/img/third_party/ChromeWebStore_black.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/webroot/img/third_party/ChromeWebStore_white.svg b/webroot/img/third_party/ChromeWebStore_white.svg new file mode 100644 index 0000000000..1c2a26bcb1 --- /dev/null +++ b/webroot/img/third_party/ChromeWebStore_white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/webroot/img/third_party/edge-addon-black.svg b/webroot/img/third_party/edge-addon-black.svg new file mode 100644 index 0000000000..50c11e55af --- /dev/null +++ b/webroot/img/third_party/edge-addon-black.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/webroot/img/third_party/edge-addon-white.svg b/webroot/img/third_party/edge-addon-white.svg new file mode 100644 index 0000000000..6c4249a36e --- /dev/null +++ b/webroot/img/third_party/edge-addon-white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/webroot/img/third_party/phpstan.svg b/webroot/img/third_party/phpstan.svg new file mode 100644 index 0000000000..08841eb7b1 --- /dev/null +++ b/webroot/img/third_party/phpstan.svg @@ -0,0 +1 @@ +PHPStan: level 6PHPStanlevel 6 diff --git a/webroot/img/third_party/psalm.svg b/webroot/img/third_party/psalm.svg new file mode 100644 index 0000000000..fe66e051cf --- /dev/null +++ b/webroot/img/third_party/psalm.svg @@ -0,0 +1 @@ +Psalm level4 diff --git a/webroot/js/app/api-app.js b/webroot/js/app/api-app.js index 093ac70a25..8d079fd7e8 100644 --- a/webroot/js/app/api-app.js +++ b/webroot/js/app/api-app.js @@ -1,2 +1,2 @@ /*! For license information please see api-app.js.LICENSE.txt */ -(()=>{"use strict";var e,t,n,r={6223:(e,t,n)=>{var r=n(7294),s=n(3935),a=n(6610),i=n(5991),o=n(379),c=n(6070),l=n(7608),u=n(2122),h=n(8927),p=n(2137),d=n(7757),m=n.n(d),f=n(5697),y=n.n(f),v=n(2045);function g(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,r=(0,l.Z)(e);if(t){var s=(0,l.Z)(this).constructor;n=Reflect.construct(r,arguments,s)}else n=r.apply(this,arguments);return(0,c.Z)(this,n)}}var b=r.createContext({feedbacks:[],displaySuccess:function(){},displayError:function(){},remove:function(){}}),E=function(e){(0,o.Z)(l,e);var t,n,s,c=g(l);function l(e){var t;return(0,a.Z)(this,l),(t=c.call(this,e)).state=t.defaultState,t}return(0,i.Z)(l,[{key:"defaultState",get:function(){return{feedbacks:[],displaySuccess:this.displaySuccess.bind(this),displayError:this.displayError.bind(this),remove:this.remove.bind(this)}}},{key:"displaySuccess",value:(s=(0,p.Z)(m().mark((function e(t){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.setState({feedbacks:[].concat((0,h.Z)(this.state.feedbacks),[{id:(0,v.Z)(),type:"success",message:t}])});case 2:case"end":return e.stop()}}),e,this)}))),function(e){return s.apply(this,arguments)})},{key:"displayError",value:(n=(0,p.Z)(m().mark((function e(t){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.setState({feedbacks:[].concat((0,h.Z)(this.state.feedbacks),[{id:(0,v.Z)(),type:"error",message:t}])});case 2:case"end":return e.stop()}}),e,this)}))),function(e){return n.apply(this,arguments)})},{key:"remove",value:(t=(0,p.Z)(m().mark((function e(t){var n;return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=function(e){return t.id!==e.id},e.next=3,this.setState({feedbacks:this.state.feedbacks.filter(n)});case 3:case"end":return e.stop()}}),e,this)}))),function(e){return t.apply(this,arguments)})},{key:"render",value:function(){return r.createElement(b.Provider,{value:this.state},this.props.children)}}]),l}(r.Component);function k(e){return function(t){(0,o.Z)(s,t);var n=g(s);function s(){return(0,a.Z)(this,s),n.apply(this,arguments)}return(0,i.Z)(s,[{key:"render",value:function(){var t=this;return r.createElement(b.Consumer,null,(function(n){return r.createElement(e,(0,u.Z)({actionFeedbackContext:n},t.props))}))}}]),s}(r.Component)}function w(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,r=(0,l.Z)(e);if(t){var s=(0,l.Z)(this).constructor;n=Reflect.construct(r,arguments,s)}else n=r.apply(this,arguments);return(0,c.Z)(this,n)}}E.displayName="ActionFeedbackContextProvider",E.propTypes={children:y().any};var x=r.createContext({dialogs:[],open:function(){},close:function(){}}),C=function(e){(0,o.Z)(n,e);var t=w(n);function n(e){var r;return(0,a.Z)(this,n),(r=t.call(this,e)).state=r.defaultState,r}return(0,i.Z)(n,[{key:"defaultState",get:function(){var e,t=this;return{dialogs:[],open:function(e,n){var r=(0,v.Z)();return t.setState({dialogs:[].concat((0,h.Z)(t.state.dialogs),[{key:r,Dialog:e,DialogProps:n}])}),r},close:(e=(0,p.Z)(m().mark((function e(n){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,t.setState({dialogs:t.state.dialogs.filter((function(e){return n!==e.key}))});case 2:return e.abrupt("return",e.sent);case 3:case"end":return e.stop()}}),e)}))),function(t){return e.apply(this,arguments)})}}},{key:"render",value:function(){return r.createElement(x.Provider,{value:this.state},this.props.children)}}]),n}(r.Component);function S(e){return function(t){(0,o.Z)(s,t);var n=w(s);function s(){return(0,a.Z)(this,s),n.apply(this,arguments)}return(0,i.Z)(s,[{key:"render",value:function(){var t=this;return r.createElement(x.Consumer,null,(function(n){return r.createElement(e,(0,u.Z)({dialogContext:n},t.props))}))}}]),s}(r.Component)}function R(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,r=(0,l.Z)(e);if(t){var s=(0,l.Z)(this).constructor;n=Reflect.construct(r,arguments,s)}else n=r.apply(this,arguments);return(0,c.Z)(this,n)}}C.displayName="DialogContextProvider",C.propTypes={children:y().any};var N=r.createContext({ContextualMenu:null,show:function(){},hide:function(){}}),Z=function(e){(0,o.Z)(n,e);var t=R(n);function n(e){var r;return(0,a.Z)(this,n),(r=t.call(this,e)).state=r.defaultState,r}return(0,i.Z)(n,[{key:"defaultState",get:function(){var e=this;return{contextualMenus:[],show:function(t,n){return e.setState({contextualMenus:[].concat((0,h.Z)(e.state.contextualMenus),[{ContextualMenuComponent:t,componentProps:n}])})},hide:function(t){return e.setState({contextualMenus:e.state.contextualMenus.filter((function(e,n){return n!==t}))})}}}},{key:"render",value:function(){return r.createElement(N.Provider,{value:this.state},this.props.children)}}]),n}(r.Component);Z.displayName="ContextualMenuContextProvider",Z.propTypes={children:y().any};var T=n(9116),q=n(5145);var U=function(e){(0,o.Z)(u,e);var t,n,s=(t=u,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,l.Z)(t);if(n){var s=(0,l.Z)(this).constructor;e=Reflect.construct(r,arguments,s)}else e=r.apply(this,arguments);return(0,c.Z)(this,e)});function u(e){var t;return(0,a.Z)(this,u),(t=s.call(this,e)).state=t.defaultState,t.bindCallbacks(),t}return(0,i.Z)(u,[{key:"defaultState",get:function(){return{shouldRender:!0,isPersisted:!1,timeoutId:null}}},{key:"componentDidMount",value:function(){this.displayWithTimer(this.props.displayTimeInMs)}},{key:"componentDidUpdate",value:function(e){var t=e&&e.feedback.id!==this.props.feedback.id,n=e&&this.props.displayTimeInMs&&e.displayTimeInMs!==this.props.displayTimeInMs;t?(this.setState({shouldRender:!0}),this.displayWithTimer(this.props.displayTimeInMs)):n&&this.updateTimer(this.props.displayTimeInMs)}},{key:"componentWillUnmount",value:function(){this.state.timeoutId&&clearTimeout(this.state.timeoutId)}},{key:"bindCallbacks",value:function(){this.persist=this.persist.bind(this),this.displayWithTimer=this.displayWithTimer.bind(this),this.close=this.close.bind(this)}},{key:"displayWithTimer",value:function(e){this.state.timeoutId&&clearTimeout(this.state.timeoutId);var t=setTimeout(this.close,e),n=Date.now();this.setState({timeoutId:t,time:n})}},{key:"updateTimer",value:function(e){var t=e-(Date.now()-this.state.time);t>0?this.displayWithTimer(t):(clearTimeout(this.state.timeoutId),this.close())}},{key:"persist",value:function(){this.state.timeoutId&&!this.state.isPersisted&&(clearTimeout(this.state.timeoutId),this.setState({isPersisted:!0}))}},{key:"close",value:function(){this.setState({shouldRender:!1}),setTimeout(this.props.onClose,u.DEFAULT_WAIT_TO_CLOSE_TIME_IN_MS)}},{key:"render",value:function(){return r.createElement(r.Fragment,null,r.createElement("div",{className:"notification",onMouseOver:this.persist,onMouseLeave:this.displayWithTimer,onClick:this.close},r.createElement("div",{className:"message animated ".concat(this.state.shouldRender?"fadeInUp":"fadeOutUp"," warning")},r.createElement("span",{className:"content"},r.createElement("strong",null,"success"===this.props.feedback.type&&r.createElement(r.Fragment,null,r.createElement(T.c,null,"Success"),": "),"error"===this.props.feedback.type&&r.createElement(r.Fragment,null,r.createElement(T.c,null,"Error"),": ")),this.props.feedback.message))))}}],[{key:"DEFAULT_WAIT_TO_CLOSE_TIME_IN_MS",get:function(){return 500}}]),u}(r.Component);U.propTypes={feedback:y().object,onClose:y().func,displayTimeInMs:y().number};const A=(0,q.Z)("common")(U);var D=function(e){(0,o.Z)(h,e);var t,n,s,u=(n=h,s=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,t=(0,l.Z)(n);if(s){var r=(0,l.Z)(this).constructor;e=Reflect.construct(t,arguments,r)}else e=t.apply(this,arguments);return(0,c.Z)(this,e)});function h(e){var t;return(0,a.Z)(this,h),(t=u.call(this,e)).bindCallbacks(),t}return(0,i.Z)(h,[{key:"bindCallbacks",value:function(){this.close=this.close.bind(this)}},{key:"feedbackToDisplay",get:function(){return this.props.actionFeedbackContext.feedbacks[0]}},{key:"length",get:function(){return this.props.actionFeedbackContext.feedbacks.length}},{key:"hasFeedbacks",get:function(){return this.length>0}},{key:"close",value:(t=(0,p.Z)(m().mark((function e(t){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.props.actionFeedbackContext.remove(t);case 2:case"end":return e.stop()}}),e,this)}))),function(e){return t.apply(this,arguments)})},{key:"render",value:function(){var e=this,t=this.length>1?h.DEFAULT_DISPLAY_MIN_TIME_IN_MS:h.DEFAULT_DISPLAY_TIME_IN_MS;return r.createElement(r.Fragment,null,this.hasFeedbacks&&r.createElement("div",{className:"notification-container"},r.createElement(A,{feedback:this.feedbackToDisplay,onClose:function(){return e.close(e.feedbackToDisplay)},displayTimeInMs:t})))}}],[{key:"DEFAULT_DISPLAY_TIME_IN_MS",get:function(){return 5e3}},{key:"DEFAULT_DISPLAY_MIN_TIME_IN_MS",get:function(){return 1200}}]),h}(r.Component);D.propTypes={actionFeedbackContext:y().any};const I=k(D);var P=n(3727),O=n(5977),_=n(6156);var M=r.createContext({user:null,users:null,roles:null,rememberMeOptions:{},resources:null,resource:null,shareResources:null,selectedResources:null,selectedUser:null,folders:null,resourceCommentId:null,mustRefreshComments:!1,siteSettings:null,userSettings:null,onCheckIsAuthenticatedRequested:null});function B(e){return function(t){(0,o.Z)(p,t);var n,s,h=(n=p,s=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,t=(0,l.Z)(n);if(s){var r=(0,l.Z)(this).constructor;e=Reflect.construct(t,arguments,r)}else e=t.apply(this,arguments);return(0,c.Z)(this,e)});function p(){return(0,a.Z)(this,p),h.apply(this,arguments)}return(0,i.Z)(p,[{key:"render",value:function(){var t=this;return r.createElement(M.Consumer,null,(function(n){return r.createElement(e,(0,u.Z)({context:n},t.props))}))}}]),p}(r.Component)}const z=M;function G(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,r=(0,l.Z)(e);if(t){var s=(0,l.Z)(this).constructor;n=Reflect.construct(r,arguments,s)}else n=r.apply(this,arguments);return(0,c.Z)(this,n)}}var F=r.createContext({counter:[],add:function(){},remove:function(){}});(function(e){(0,o.Z)(n,e);var t=G(n);function n(e){var r;return(0,a.Z)(this,n),(r=t.call(this,e)).state=r.defaultState,r}return(0,i.Z)(n,[{key:"defaultState",get:function(){var e=this;return{counter:0,add:function(){e.setState({counter:e.state.counter+1})},remove:function(){e.setState({counter:Math.min(e.state.counter-1,0)})}}}},{key:"render",value:function(){return r.createElement(F.Provider,{value:this.state},this.props.children)}}]),n}(r.Component)).propTypes={children:y().any};var L=n(4699),K=n(5366);const W=function(e){(0,o.Z)(s,e);var t,n,r=(t=s,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,l.Z)(t);if(n){var s=(0,l.Z)(this).constructor;e=Reflect.construct(r,arguments,s)}else e=r.apply(this,arguments);return(0,c.Z)(this,e)});function s(e,t){var n;return(0,a.Z)(this,s),(n=r.call(this,e)).name="PassboltApiFetchError",n.data=t||{},n}return s}((0,K.Z)(Error));const j=function(e){(0,o.Z)(s,e);var t,n,r=(t=s,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,l.Z)(t);if(n){var s=(0,l.Z)(this).constructor;e=Reflect.construct(r,arguments,s)}else e=r.apply(this,arguments);return(0,c.Z)(this,e)});function s(){var e;return(0,a.Z)(this,s),(e=r.call(this,"An internal error occurred. The server response could not be parsed. Please contact your administrator.")).name="PassboltBadResponseError",e}return s}((0,K.Z)(Error));const H=function(e){(0,o.Z)(s,e);var t,n,r=(t=s,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,l.Z)(t);if(n){var s=(0,l.Z)(this).constructor;e=Reflect.construct(r,arguments,s)}else e=r.apply(this,arguments);return(0,c.Z)(this,e)});function s(e){var t;return(0,a.Z)(this,s),e=e||"The service is unavailable",(t=r.call(this,e)).name="PassboltServiceUnavailableError",t}return s}((0,K.Z)(Error));function V(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function Y(e){for(var t=1;t0||this.groupsSuccess.length>0}},{key:"hasErrorOrIgnoreResource",value:function(){return this.usersError.length>0||this.groupsError.length>0||this.usersIgnored.length>0||this.groupsIgnored.length>0}},{key:"getFullReport",value:function(){var e="";return(e=e.concat(this.getUsersFullReport())).concat(this.getGroupsFullReport())}},{key:"getUsersFullReport",value:function(){var e="";if(this.usersSuccess.length>0||this.usersError.length>0||this.usersIgnored.length>0){var t="---------------------------------------------------------------------\n\n ".concat(this.translate("Users"),"\n\n ---------------------------------------------------------------------\n");e=e.concat(t);var n=function(t){return e=e.concat("- ".concat(t.message,"\n"))};this.usersSuccess.length>0&&(e=e.concat("\n".concat(this.translate("Success:"),"\n")),this.usersSuccess.map(n)),this.usersError.length>0&&(e=e.concat("\n".concat(this.translate("Errors:"),"\n")),this.usersError.map(n)),this.usersIgnored.length>0&&(e=e.concat("\n".concat(this.translate("Ignored:"),"\n")),this.usersIgnored.map(n))}return e.concat("\n")}},{key:"getGroupsFullReport",value:function(){var e="";if(this.groupsSuccess.length>0||this.groupsError.length>0||this.groupsIgnored.length>0){var t="---------------------------------------------------------------------\n\n ".concat(this.translate("Groups"),"\n\n ---------------------------------------------------------------------\n");e=e.concat(t);var n=function(t){return e=e.concat("- ".concat(t.message,"\n"))};this.groupsSuccess.length>0&&(e=e.concat("\n".concat(this.translate("Success:"),"\n")),this.groupsSuccess.map(n)),this.groupsError.length>0&&(e=e.concat("\n".concat(this.translate("Errors:"),"\n")),this.groupsError.map(n)),this.groupsIgnored.length>0&&(e=e.concat("\n".concat(this.translate("Ignored:"),"\n")),this.groupsIgnored.map(n))}return e}},{key:"translate",get:function(){return this.props.t}},{key:"render",value:function(){return r.createElement("div",null,this.isLoading()&&r.createElement(Be,{onClose:this.handleClose,title:this.translate("Synchronize simulation")}),!this.isLoading()&&r.createElement(Pe,{className:"ldap-simulate-synchronize-dialog",title:this.translate("Synchronize simulation report"),onClose:this.handleClose,disabled:this.isLoading()},r.createElement("div",{className:"form-content",onSubmit:this.handleFormSubmit},r.createElement("p",null,r.createElement("strong",null,r.createElement(T.c,null,"The operation was successfull."))),r.createElement("p",null),this.hasSuccessResource()&&r.createElement("p",{id:"resources-synchronize"}," ",this.translate("{{users}} and {{groups}} will be synchronized.",{users:this.translate("{{count}} user",{count:this.usersSuccess.length}),groups:this.translate("{{count}} group",{count:this.groupsSuccess.length})})," "),!this.hasSuccessResource()&&r.createElement("p",{id:"no-resources"}," ",r.createElement(T.c,null,"No resources will be synchronized.")," "),this.hasErrorOrIgnoreResource()&&r.createElement("p",{className:"error inline-error"},r.createElement(T.c,null,"Some resources will not be synchronized and will require your attention, see the full report.")),r.createElement("div",{className:"accordion operation-details ".concat(this.state.openFullReport?"":"closed")},r.createElement("div",{className:"accordion-header",onClick:this.handleFullReportClicked},this.state.openListGroupsUsers&&r.createElement(ke,{name:"caret-down",baseline:!0}),!this.state.openListGroupsUsers&&r.createElement(ke,{name:"caret-right",baseline:!0}),r.createElement("a",{role:"link"},r.createElement(T.c,null,"Full report"))),r.createElement("div",{className:"accordion-content"},r.createElement("div",{className:"input text"},r.createElement("textarea",{className:"full_report",readOnly:!0,value:this.getFullReport()})))),r.createElement("p",null)),r.createElement("div",{className:"submit-wrapper clearfix"},r.createElement("a",{className:"button primary ".concat(this.isLoading()?"disabled":""),role:"button",onClick:this.handleSynchronize},r.createElement(T.c,null,"Synchronize")),r.createElement(_e,{disabled:this.isLoading(),onClick:this.handleClose}))))}}]),d}(r.Component);ze.propTypes={onClose:y().func,actionFeedbackContext:y().any,administrationWorkspaceContext:y().object,t:y().func};const Ge=k(se((0,q.Z)("common")(ze)));var Fe=function(e){(0,o.Z)(d,e);var t,n,s,u,h=(s=d,u=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,t=(0,l.Z)(s);if(u){var n=(0,l.Z)(this).constructor;e=Reflect.construct(t,arguments,n)}else e=t.apply(this,arguments);return(0,c.Z)(this,e)});function d(e){var t;return(0,a.Z)(this,d),(t=h.call(this,e)).state=t.defaultState,t.bindEventHandlers(),t}return(0,i.Z)(d,[{key:"defaultState",get:function(){return{loading:!0,openFullReport:!1,userDirectorySynchronizeResult:null}}},{key:"bindEventHandlers",value:function(){this.handleFullReportClicked=this.handleFullReportClicked.bind(this),this.handleClose=this.handleClose.bind(this),this.handleSynchronize=this.handleSynchronize.bind(this)}},{key:"componentDidMount",value:(n=(0,p.Z)(m().mark((function e(){var t,n;return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,e.next=3,this.props.administrationWorkspaceContext.onGetSynchronizeUsersDirectoryRequested();case 3:t=e.sent,n=t.body,this.setState({loading:!1,userDirectorySynchronizeResult:n}),e.next=12;break;case 8:return e.prev=8,e.t0=e.catch(0),e.next=12,this.handleError(e.t0);case 12:case"end":return e.stop()}}),e,this,[[0,8]])}))),function(){return n.apply(this,arguments)})},{key:"handleError",value:(t=(0,p.Z)(m().mark((function e(t){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return console.error(t),e.next=3,this.props.actionFeedbackContext.displayError(t.message);case 3:this.handleClose();case 4:case"end":return e.stop()}}),e,this)}))),function(e){return t.apply(this,arguments)})},{key:"handleFullReportClicked",value:function(){this.setState({openFullReport:!this.state.openFullReport})}},{key:"handleClose",value:function(){this.props.onClose()}},{key:"handleSynchronize",value:function(){this.handleClose()}},{key:"isLoading",value:function(){return this.state.loading}},{key:"users",get:function(){return this.state.userDirectorySynchronizeResult.users}},{key:"groups",get:function(){return this.state.userDirectorySynchronizeResult.groups}},{key:"usersSuccess",get:function(){return this.users.filter((function(e){return"success"===e.status}))}},{key:"groupsSuccess",get:function(){return this.groups.filter((function(e){return"success"===e.status}))}},{key:"usersError",get:function(){return this.users.filter((function(e){return"error"===e.status}))}},{key:"groupsError",get:function(){return this.groups.filter((function(e){return"error"===e.status}))}},{key:"usersIgnored",get:function(){return this.users.filter((function(e){return"ignore"===e.status}))}},{key:"groupsIgnored",get:function(){return this.groups.filter((function(e){return"ignore"===e.status}))}},{key:"hasSuccessResource",value:function(){return this.usersSuccess.length>0||this.groupsSuccess.length>0}},{key:"hasErrorOrIgnoreResource",value:function(){return this.usersError.length>0||this.groupsError.length>0||this.usersIgnored.length>0||this.groupsIgnored.length>0}},{key:"getFullReport",value:function(){var e="";return(e=e.concat(this.getUsersFullReport())).concat(this.getGroupsFullReport())}},{key:"getUsersFullReport",value:function(){var e="";if(this.usersSuccess.length>0||this.usersError.length>0||this.usersIgnored.length>0){var t="---------------------------------------------------------------------\n\n ".concat(this.translate("Users"),"\n\n ---------------------------------------------------------------------\n");e=e.concat(t);var n=function(t){return e=e.concat("- ".concat(t.message,"\n"))};this.usersSuccess.length>0&&(e=e.concat("\n".concat(this.translate("Success:"),"\n")),this.usersSuccess.map(n)),this.usersError.length>0&&(e=e.concat("\n".concat(this.translate("Errors:"),"\n")),this.usersError.map(n)),this.usersIgnored.length>0&&(e=e.concat("\n".concat(this.translate("Ignored:"),"\n")),this.usersIgnored.map(n))}return e.concat("\n")}},{key:"getGroupsFullReport",value:function(){var e="";if(this.groupsSuccess.length>0||this.groupsError.length>0||this.groupsIgnored.length>0){var t="---------------------------------------------------------------------\n\n ".concat(this.translate("Groups"),"\n\n ---------------------------------------------------------------------\n");e=e.concat(t);var n=function(t){return e=e.concat("- ".concat(t.message,"\n"))};this.groupsSuccess.length>0&&(e=e.concat("\n".concat(this.translate("Success:"),"\n")),this.groupsSuccess.map(n)),this.groupsError.length>0&&(e=e.concat("\n".concat(this.translate("Errors:"),"\n")),this.groupsError.map(n)),this.groupsIgnored.length>0&&(e=e.concat("\n".concat(this.translate("Ignored:"),"\n")),this.groupsIgnored.map(n))}return e}},{key:"translate",get:function(){return this.props.t}},{key:"render",value:function(){return r.createElement("div",null,this.isLoading()&&r.createElement(Be,{onClose:this.handleClose,title:this.translate("Synchronize")}),!this.isLoading()&&r.createElement(Pe,{className:"ldap-simulate-synchronize-dialog",title:this.translate("Synchronize report"),onClose:this.handleClose,disabled:this.isLoading()},r.createElement("div",{className:"form-content",onSubmit:this.handleFormSubmit},r.createElement("p",null,r.createElement("strong",null,r.createElement(T.c,null,"The operation was successfull."))),r.createElement("p",null),this.hasSuccessResource()&&r.createElement("p",{id:"resources-synchronize"}," ",this.translate("{{users}} and {{groups}} have been synchronized.",{users:this.translate("{{count}} user",{count:this.usersSuccess.length}),groups:this.translate("{{count}} group",{count:this.groupsSuccess.length})})," "),!this.hasSuccessResource()&&r.createElement("p",{id:"no-resources"}," ",r.createElement(T.c,null,"No resources have been synchronized.")," "),this.hasErrorOrIgnoreResource()&&r.createElement("p",{className:"error inline-error"},r.createElement(T.c,null,"Some resources will not be synchronized and will require your attention, see the full report.")),r.createElement("div",{className:"accordion operation-details ".concat(this.state.openFullReport?"":"closed")},r.createElement("div",{className:"accordion-header",onClick:this.handleFullReportClicked},this.state.openListGroupsUsers&&r.createElement(ke,{name:"caret-down",baseline:!0}),!this.state.openListGroupsUsers&&r.createElement(ke,{name:"caret-right",baseline:!0}),r.createElement("a",{role:"link"},r.createElement(T.c,null,"Full report"))),r.createElement("div",{className:"accordion-content"},r.createElement("div",{className:"input text"},r.createElement("textarea",{className:"full_report",readOnly:!0,value:this.getFullReport()})))),r.createElement("p",null)),r.createElement("div",{className:"submit-wrapper clearfix"},r.createElement("a",{className:"button primary ".concat(this.isLoading()?"disabled":""),role:"button",onClick:this.handleClose},r.createElement(T.c,null,"Ok")))))}}]),d}(r.Component);Fe.propTypes={onClose:y().func,actionFeedbackContext:y().any,administrationWorkspaceContext:y().object,t:y().func};const Le=k(se((0,q.Z)("common")(Fe)));var Ke=function(e){(0,o.Z)(h,e);var t,n,s,u=(n=h,s=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,t=(0,l.Z)(n);if(s){var r=(0,l.Z)(this).constructor;e=Reflect.construct(t,arguments,r)}else e=t.apply(this,arguments);return(0,c.Z)(this,e)});function h(e){var t;return(0,a.Z)(this,h),(t=u.call(this,e)).bindCallbacks(),t}return(0,i.Z)(h,[{key:"bindCallbacks",value:function(){this.handleSaveClick=this.handleSaveClick.bind(this),this.handleTestClick=this.handleTestClick.bind(this),this.handleSimulateSynchronizeClick=this.handleSimulateSynchronizeClick.bind(this),this.handleSynchronizeClick=this.handleSynchronizeClick.bind(this),this.handleEditSubscriptionClick=this.handleEditSubscriptionClick.bind(this)}},{key:"componentDidUpdate",value:(t=(0,p.Z)(m().mark((function e(t){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.handleMustSynchronize(t.administrationWorkspaceContext.must.synchronize);case 2:case"end":return e.stop()}}),e,this)}))),function(e){return t.apply(this,arguments)})},{key:"handleMustSynchronize",value:function(e){this.props.administrationWorkspaceContext.must.synchronize!==e&&this.props.administrationWorkspaceContext.must.synchronize&&(this.handleSynchronizeClick(),this.props.administrationWorkspaceContext.onResetActionsSettings())}},{key:"handleSaveClick",value:function(){this.props.administrationWorkspaceContext.onMustSaveSettings()}},{key:"handleTestClick",value:function(){this.props.administrationWorkspaceContext.onMustTestSettings()}},{key:"handleSimulateSynchronizeClick",value:function(){this.props.dialogContext.open(Ge)}},{key:"handleSynchronizeClick",value:function(){this.props.dialogContext.open(Le)}},{key:"handleEditSubscriptionClick",value:function(){this.props.administrationWorkspaceContext.onMustEditSubscriptionKey()}},{key:"isSaveEnabled",value:function(){return this.props.administrationWorkspaceContext.can.save}},{key:"isTestEnabled",value:function(){return this.props.administrationWorkspaceContext.can.test}},{key:"isSynchronizeEnabled",value:function(){return this.props.administrationWorkspaceContext.can.synchronize}},{key:"isUserDirectorySelected",value:function(){return ae.USER_DIRECTORY===this.props.administrationWorkspaceContext.selectedAdministration}},{key:"isSubscriptionSelected",value:function(){return ae.SUBSCRIPTION===this.props.administrationWorkspaceContext.selectedAdministration}},{key:"translate",get:function(){return this.props.t}},{key:"render",value:function(){return r.createElement("div",{className:"col2_3 actions-wrapper"},r.createElement("div",{className:"actions"},!this.isSubscriptionSelected()&&r.createElement("div",null,r.createElement("li",null,r.createElement("a",{className:"button ".concat(this.isSaveEnabled()?"":"disabled"),onClick:this.handleSaveClick},r.createElement(ke,{name:"save"}),r.createElement("span",null,r.createElement(T.c,null,"Save settings")))),this.isUserDirectorySelected()&&r.createElement("div",null,r.createElement("li",null,r.createElement("a",{className:"button ".concat(this.isTestEnabled()?"":"disabled"),onClick:this.handleTestClick},r.createElement(ke,{name:"plug"}),r.createElement("span",null,r.createElement(T.c,null,"Test settings")))),r.createElement("li",null,r.createElement("a",{className:"button ".concat(this.isSynchronizeEnabled()?"":"disabled"),onClick:this.handleSimulateSynchronizeClick},r.createElement(ke,{name:"magic-wand"}),r.createElement("span",null,r.createElement(T.c,null,"Simulate synchronize")))),r.createElement("li",null,r.createElement("a",{className:"button ".concat(this.isSynchronizeEnabled()?"":"disabled"),onClick:this.handleSynchronizeClick},r.createElement(ke,{name:"refresh"}),r.createElement("span",null,r.createElement(T.c,null,"Synchronize")))))),this.isSubscriptionSelected()&&r.createElement("div",null,r.createElement("li",null,r.createElement("a",{className:"button",onClick:this.handleEditSubscriptionClick},r.createElement(ke,{name:"edit"}),r.createElement("span",null,r.createElement(T.c,null,"Update key")))))))}}]),h}(r.Component);Ke.propTypes={administrationWorkspaceContext:y().object,dialogContext:y().any,t:y().func};const We=S(se((0,q.Z)("common")(Ke)));var je=function(e){(0,o.Z)(u,e);var t,n,s=(t=u,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,l.Z)(t);if(n){var s=(0,l.Z)(this).constructor;e=Reflect.construct(r,arguments,s)}else e=r.apply(this,arguments);return(0,c.Z)(this,e)});function u(){return(0,a.Z)(this,u),s.apply(this,arguments)}return(0,i.Z)(u,[{key:"render",value:function(){var e=0;return r.createElement("div",{className:"breadcrumbs"},r.createElement("ul",{className:"menu"},this.props.items&&this.props.items.map((function(t){return e++,r.createElement("li",{className:"ellipsis",key:e},t)}))))}}]),u}(r.Component);je.propTypes={items:y().array};const He=je;var Ve=function(e){(0,o.Z)(u,e);var t,n,s=(t=u,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,l.Z)(t);if(n){var s=(0,l.Z)(this).constructor;e=Reflect.construct(r,arguments,s)}else e=r.apply(this,arguments);return(0,c.Z)(this,e)});function u(){return(0,a.Z)(this,u),s.apply(this,arguments)}return(0,i.Z)(u,[{key:"render",value:function(){return r.createElement("a",{onClick:this.props.onClick},this.props.name)}}]),u}(r.Component);Ve.propTypes={name:y().string,onClick:y().func};const Ye=Ve;var $e=function(e){(0,o.Z)(h,e);var t,n,s,u=(n=h,s=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,t=(0,l.Z)(n);if(s){var r=(0,l.Z)(this).constructor;e=Reflect.construct(t,arguments,r)}else e=t.apply(this,arguments);return(0,c.Z)(this,e)});function h(){return(0,a.Z)(this,h),u.apply(this,arguments)}return(0,i.Z)(h,[{key:"items",get:function(){switch(this.props.administrationWorkspaceContext.selectedAdministration){case ae.NONE:return[];default:return[r.createElement(Ye,{key:"bread-1",name:this.translate("Administration"),onClick:this.props.navigationContext.onGoToAdministrationRequested}),r.createElement(Ye,{key:"bread-2",name:this.getLastBreadcrumbItemName(),onClick:this.onLastBreadcrumbClick.bind(this)}),r.createElement(Ye,{key:"bread-3",name:this.translate("Settings"),onClick:this.onLastBreadcrumbClick.bind(this)})]}}},{key:"getLastBreadcrumbItemName",value:function(){switch(this.props.administrationWorkspaceContext.selectedAdministration){case ae.MFA:return this.translate("Multi factor authentication");case ae.USER_DIRECTORY:return this.translate("Users Directory");case ae.EMAIL_NOTIFICATION:return this.translate("Email Notification");case ae.SUBSCRIPTION:return this.translate("Subscription");case ae.INTERNATIONALIZATION:return this.translate("Internationalization");default:return""}}},{key:"onLastBreadcrumbClick",value:(t=(0,p.Z)(m().mark((function e(){var t;return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=this.props.location.pathname,this.props.history.push({pathname:t});case 2:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})},{key:"translate",get:function(){return this.props.t}},{key:"render",value:function(){return r.createElement(He,{items:this.items})}}]),h}(r.Component);$e.propTypes={administrationWorkspaceContext:y().object,location:y().object,history:y().object,navigationContext:y().any,t:y().func};const Je=(0,O.EN)(me(se((0,q.Z)("common")($e))));var Qe=function(e){(0,o.Z)(u,e);var t,n,s=(t=u,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,l.Z)(t);if(n){var s=(0,l.Z)(this).constructor;e=Reflect.construct(r,arguments,s)}else e=r.apply(this,arguments);return(0,c.Z)(this,e)});function u(){return(0,a.Z)(this,u),s.apply(this,arguments)}return(0,i.Z)(u,[{key:"hasChildren",value:function(){return this.props.node.group.groups.length>0}},{key:"displayUserName",value:function(e){return"".concat(e.profile.first_name," ").concat(e.profile.last_name)}},{key:"node",get:function(){return this.props.node}},{key:"render",value:function(){var e=this;return r.createElement("ul",{key:this.node.id},"group"===this.node.type&&r.createElement("li",{className:"group"},this.node.group.name,r.createElement("ul",null,Object.values(this.node.group.users).map((function(t){return r.createElement("li",{className:"user",key:t.id},t.errors&&r.createElement("span",{className:"error"},t.directory_name),!t.errors&&r.createElement("span",null,e.displayUserName(t.user)," ",r.createElement("em",null,"(",t.user.username,")")))})),Object.values(this.node.group.groups).map((function(e){return r.createElement(u,{key:"tree-".concat(e.id),node:e})})))),"user"===this.node.type&&r.createElement("li",{className:"user"},this.node.errors&&r.createElement("span",{className:"error"},this.node.directory_name),!this.node.errors&&r.createElement("span",null,this.displayUserName(this.node.user)," ",r.createElement("em",null,"(",this.node.user.username,")"))))}}]),u}(r.Component);Qe.propTypes={node:y().object};const Xe=Qe;var et=function(e){(0,o.Z)(u,e);var t,n,s=(t=u,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,l.Z)(t);if(n){var s=(0,l.Z)(this).constructor;e=Reflect.construct(r,arguments,s)}else e=r.apply(this,arguments);return(0,c.Z)(this,e)});function u(e){var t;return(0,a.Z)(this,u),(t=s.call(this,e)).state=t.defaultState,t.bindEventHandlers(),t}return(0,i.Z)(u,[{key:"defaultState",get:function(){return{loading:!0,openListGroupsUsers:!1,openStructureGroupsUsers:!1,openErrors:!1}}},{key:"bindEventHandlers",value:function(){this.handleListGroupsUsersClicked=this.handleListGroupsUsersClicked.bind(this),this.handleStructureGroupsUsersClicked=this.handleStructureGroupsUsersClicked.bind(this),this.handleErrorsClicked=this.handleErrorsClicked.bind(this),this.handleClose=this.handleClose.bind(this)}},{key:"componentDidMount",value:function(){this.setState({loading:!1})}},{key:"handleListGroupsUsersClicked",value:function(){this.setState({openListGroupsUsers:!this.state.openListGroupsUsers})}},{key:"handleStructureGroupsUsersClicked",value:function(){this.setState({openStructureGroupsUsers:!this.state.openStructureGroupsUsers})}},{key:"handleErrorsClicked",value:function(){this.setState({openErrors:!this.state.openErrors})}},{key:"handleClose",value:function(){this.props.onClose(),this.props.context.setContext({displayTestUserDirectoryDialogProps:null})}},{key:"hasAllInputDisabled",value:function(){return this.state.loading}},{key:"displayUserName",value:function(e){return"".concat(e.profile.first_name," ").concat(e.profile.last_name)}},{key:"users",get:function(){return this.props.context.displayTestUserDirectoryDialogProps.userDirectoryTestResult.users}},{key:"groups",get:function(){return this.props.context.displayTestUserDirectoryDialogProps.userDirectoryTestResult.groups}},{key:"tree",get:function(){return this.props.context.displayTestUserDirectoryDialogProps.userDirectoryTestResult.tree}},{key:"errors",get:function(){return this.props.context.displayTestUserDirectoryDialogProps.userDirectoryTestResult.errors}},{key:"translate",get:function(){return this.props.t}},{key:"render",value:function(){var e=this;return r.createElement(Pe,{className:"ldap-test-settings-dialog",title:"Test settings report",onClose:this.handleClose,disabled:this.hasAllInputDisabled()},r.createElement("div",{className:"form-content"},r.createElement("p",null,r.createElement("strong",null,r.createElement(T.c,null,"A connection could be established. Well done!"))),r.createElement("p",null),r.createElement("div",{className:"ldap-test-settings-report"},r.createElement("p",null,this.translate("{{users}} and {{groups}} have been found.",{users:this.translate("{{count}} user",{count:this.users.length}),groups:this.translate("{{count}} group",{count:this.groups.length})})),r.createElement("div",{className:"accordion directory-list ".concat(this.state.openListGroupsUsers?"":"closed")},r.createElement("div",{className:"accordion-header",onClick:this.handleListGroupsUsersClicked},this.state.openListGroupsUsers&&r.createElement(ke,{name:"caret-down",baseline:!0}),!this.state.openListGroupsUsers&&r.createElement(ke,{name:"caret-right",baseline:!0}),r.createElement("a",{role:"link"},r.createElement(T.c,null,"See list"))),r.createElement("div",{className:"accordion-content"},r.createElement("table",null,r.createElement("tbody",null,r.createElement("tr",null,r.createElement("td",null,r.createElement(T.c,null,"Groups")),r.createElement("td",null,r.createElement(T.c,null,"Users"))),r.createElement("tr",null,r.createElement("td",null,this.groups.map((function(e){return e.errors&&r.createElement("div",{key:e.id},r.createElement("span",{className:"error"},e.directory_name))||r.createElement("div",{key:e.id},e.group.name)}))),r.createElement("td",null,this.users.map((function(t){return t.errors&&r.createElement("div",{key:t.id},r.createElement("span",{className:"error"},t.directory_name))||r.createElement("div",{key:t.id},e.displayUserName(t.user)," ",r.createElement("em",null,"(",t.user.username,")"))})))))))),r.createElement("div",{className:"accordion accordion-directory-structure ".concat(this.state.openStructureGroupsUsers?"":"closed")},r.createElement("div",{className:"accordion-header",onClick:this.handleStructureGroupsUsersClicked},this.state.openStructureGroupsUsers&&r.createElement(ke,{name:"caret-down",baseline:!0}),!this.state.openStructureGroupsUsers&&r.createElement(ke,{name:"caret-right",baseline:!0}),r.createElement("a",{role:"link"},r.createElement(T.c,null,"See structure"))),r.createElement("div",{className:"accordion-content"},r.createElement("div",{className:"directory-structure"},r.createElement("ul",null,r.createElement("li",{className:"group"},"Root",Object.values(this.tree).map((function(e){return r.createElement(Xe,{key:"tree-".concat(e.id),node:e})}))))))),this.errors.length>0&&r.createElement("div",null,r.createElement("p",{className:"directory-errors error"},this.translate("{{count}} entry had errors and will be ignored during synchronization.",{count:this.errors.length})),r.createElement("div",{className:"accordion accordion-directory-errors ".concat(this.state.openErrors?"":"closed")},r.createElement("div",{className:"accordion-header",onClick:this.handleErrorsClicked},this.state.openErrors&&r.createElement(ke,{name:"caret-down",baseline:!0}),!this.state.openErrors&&r.createElement(ke,{name:"caret-right",baseline:!0}),r.createElement("a",{role:"link"},r.createElement(T.c,null,"See error details"))),r.createElement("div",{className:"accordion-content"},r.createElement("div",{className:"directory-errors"},r.createElement("textarea",{value:JSON.stringify(this.errors,null," "),readOnly:!0}))))))),r.createElement("div",{className:"submit-wrapper clearfix"},r.createElement("a",{className:"button primary ".concat(this.hasAllInputDisabled()?"disabled":""),role:"button",onClick:this.handleClose},r.createElement(T.c,null,"OK"))))}}]),u}(r.Component);et.propTypes={context:y().any,onClose:y().func,t:y().func};const tt=B((0,q.Z)("common")(et));var nt=function(e){(0,o.Z)(N,e);var t,n,s,u,h,d,f,y,v,g,b,E,k,w,x,C,S,R=(C=N,S=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,t=(0,l.Z)(C);if(S){var n=(0,l.Z)(this).constructor;e=Reflect.construct(t,arguments,n)}else e=t.apply(this,arguments);return(0,c.Z)(this,e)});function N(e){var t;return(0,a.Z)(this,N),(t=R.call(this,e)).state=t.defaultState,t.createRefs(),t.bindCallbacks(),t}return(0,i.Z)(N,[{key:"defaultState",get:function(){return{loading:!0,processing:!1,openCredentials:!0,openDirectoryConfiguration:!1,openSynchronizationOptions:!1,openConnectionType:!1,openDefaultAdmin:!1,openDefaultGroupAdmin:!1,userDirectoryToggle:!1,directoryType:"ad",connectionType:"plain",host:"",hostError:null,port:"389",portError:null,username:"",password:"",domain:"",domainError:null,baseDn:"",groupPath:"",userPath:"",groupObjectClass:"",userObjectClass:"",useEmailPrefix:!1,emailPrefix:"",emailSuffix:"",defaultAdmin:"",defaultGroupAdmin:"",groupsParentGroup:"",usersParentGroup:"",enabledUsersOnly:!1,createUsers:!0,deleteUsers:!0,createGroups:!0,deleteGroups:!0,updateGroups:!0,defaultAdminSearch:"",defaultGroupAdminSearch:"",users:null,hasAlreadyBeenValidated:!1}}},{key:"componentDidMount",value:(x=(0,p.Z)(m().mark((function e(){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:document.addEventListener("click",this.handleUserDirectoryClickEvent),this.findAllUserDirectorySettings();case 2:case"end":return e.stop()}}),e,this)}))),function(){return x.apply(this,arguments)})},{key:"componentWillUnmount",value:function(){document.removeEventListener("click",this.handleUserDirectoryClickEvent)}},{key:"componentDidUpdate",value:(w=(0,p.Z)(m().mark((function e(t,n){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.handleEnabledTestButton(n.userDirectoryToggle);case 2:return e.next=4,this.handleMustSubmit(t.administrationWorkspaceContext);case 4:case"end":return e.stop()}}),e,this)}))),function(e,t){return w.apply(this,arguments)})},{key:"bindCallbacks",value:function(){this.handleUserDirectoryClickEvent=this.handleUserDirectoryClickEvent.bind(this),this.handleCredentialTitleClicked=this.handleCredentialTitleClicked.bind(this),this.handleDirectoryConfigurationTitleClicked=this.handleDirectoryConfigurationTitleClicked.bind(this),this.handleSynchronizationOptionsTitleClicked=this.handleSynchronizationOptionsTitleClicked.bind(this),this.handleConnectionTypeClicked=this.handleConnectionTypeClicked.bind(this),this.handleDefaultAdminClicked=this.handleDefaultAdminClicked.bind(this),this.handleDefaultGroupAdminClicked=this.handleDefaultGroupAdminClicked.bind(this),this.handleInputChange=this.handleInputChange.bind(this),this.handleHostInputKeyUp=this.handleHostInputKeyUp.bind(this),this.handlePortInputKeyUp=this.handlePortInputKeyUp.bind(this),this.handleDomainInputKeyUp=this.handleDomainInputKeyUp.bind(this),this.stopPropagation=this.stopPropagation.bind(this),this.handleConnectionTypeChange=this.handleConnectionTypeChange.bind(this),this.handleUserToBeDefaultAdminClick=this.handleUserToBeDefaultAdminClick.bind(this),this.handleUserToBeDefaultGroupAdminClick=this.handleUserToBeDefaultGroupAdminClick.bind(this)}},{key:"createRefs",value:function(){this.connectionTypeRef=r.createRef(),this.defaultAdminRef=r.createRef(),this.defaultGroupAdminRef=r.createRef()}},{key:"handleEnabledTestButton",value:function(e){this.state.userDirectoryToggle!==e&&this.props.administrationWorkspaceContext.onTestEnabled(this.state.userDirectoryToggle)}},{key:"handleUserDirectoryClickEvent",value:function(e){null===this.defaultAdminRef.current||this.defaultAdminRef.current.contains(e.target)||this.setState({openDefaultAdmin:!1}),null===this.defaultGroupAdminRef.current||this.defaultGroupAdminRef.current.contains(e.target)||this.setState({openDefaultGroupAdmin:!1}),null===this.connectionTypeRef.current||this.connectionTypeRef.current.contains(e.target)||this.setState({openConnectionType:!1})}},{key:"stopPropagation",value:function(e){e.stopPropagation()}},{key:"handleMustSubmit",value:(k=(0,p.Z)(m().mark((function e(t){var n,r;return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(n=this.props.administrationWorkspaceContext.must.save!==t.must.save,r=this.props.administrationWorkspaceContext.must.test!==t.must.test,!(n&&this.props.administrationWorkspaceContext.must.save||r&&this.props.administrationWorkspaceContext.must.test)){e.next=6;break}return e.next=5,this.handleFormSubmit();case 5:this.props.administrationWorkspaceContext.onResetActionsSettings();case 6:case"end":return e.stop()}}),e,this)}))),function(e){return k.apply(this,arguments)})},{key:"findAllUserDirectorySettings",value:(E=(0,p.Z)(m().mark((function e(){var t,n,r,s,a,i,o,c,l,u,h,p,d,f,y,v,g,b,E,k,w,x,C,S,R,N,Z,T,q,U,A,D=this;return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.props.administrationWorkspaceContext.onGetUsersDirectoryRequested();case 2:return t=e.sent,n=t.body,e.next=6,this.props.administrationWorkspaceContext.onGetUsersRequested();case 6:r=e.sent,s=this.sortUsers(r.body),a="",i="",0!==n.length?(o=!0,c=n.directory_type,l=n.connection_type,u=n.domain_name,h=n.username,p=n.password,d=n.base_dn,f=n.server,y=n.port.toString(),a=n.default_user,i=n.default_group_admin_user,v=n.sync_users_create,g=n.sync_users_delete,b=n.sync_groups_create,E=n.sync_groups_delete,k=n.sync_groups_update,w=!0===n.enabled_users_only,x=n.group_path,C=n.user_path,S=n.group_object_class,R=n.user_object_class,N=n.use_email_prefix_suffix,Z=n.email_prefix,T=n.email_suffix,q=n.groups_parent_group,U=n.users_parent_group,this.setState({loading:!1,users:s,userDirectoryToggle:o,directoryType:c,connectionType:l,domain:u,username:h,password:p,baseDn:d,host:f,port:y,defaultAdmin:a,defaultGroupAdmin:i,enabledUsersOnly:w,groupPath:x,userPath:C,groupObjectClass:S,userObjectClass:R,useEmailPrefix:N,emailPrefix:Z,emailSuffix:T,groupsParentGroup:q,usersParentGroup:U,createUsers:v,deleteUsers:g,createGroups:b,deleteGroups:E,updateGroups:k}),this.props.administrationWorkspaceContext.onTestEnabled(o),this.props.administrationWorkspaceContext.onSynchronizeEnabled(o)):(A=s.find((function(e){return D.props.context.loggedInUser.id===e.id})),a=A.id,i=A.id,this.setState({loading:!1,users:s,defaultAdmin:a,defaultGroupAdmin:i}));case 11:case"end":return e.stop()}}),e,this)}))),function(){return E.apply(this,arguments)})},{key:"sortUsers",value:function(e){var t=function(e){return"".concat(e.profile.first_name," ").concat(e.profile.last_name)};return e.sort((function(e,n){return t(e).localeCompare(t(n))}))}},{key:"handleCredentialTitleClicked",value:function(){this.setState({openCredentials:!this.state.openCredentials})}},{key:"handleDirectoryConfigurationTitleClicked",value:function(){this.setState({openDirectoryConfiguration:!this.state.openDirectoryConfiguration})}},{key:"handleSynchronizationOptionsTitleClicked",value:function(){this.setState({openSynchronizationOptions:!this.state.openSynchronizationOptions})}},{key:"handleConnectionTypeClicked",value:function(){this.hasAllInputDisabled()||this.setState({openConnectionType:!this.state.openConnectionType})}},{key:"handleDefaultAdminClicked",value:function(){this.hasAllInputDisabled()||this.setState({openDefaultAdmin:!this.state.openDefaultAdmin,defaultAdminSearch:""})}},{key:"handleDefaultGroupAdminClicked",value:function(){this.hasAllInputDisabled()||this.setState({openDefaultGroupAdmin:!this.state.openDefaultGroupAdmin,defaultGroupAdminSearch:""})}},{key:"handleHostInputKeyUp",value:function(){if(this.state.hasAlreadyBeenValidated){var e=this.validateHostInput();this.setState(e)}}},{key:"handlePortInputKeyUp",value:function(){if(this.state.hasAlreadyBeenValidated){var e=this.validatePortInput();this.setState(e)}}},{key:"handleDomainInputKeyUp",value:function(){if(this.state.hasAlreadyBeenValidated){var e=this.validateDomainInput();this.setState(e)}}},{key:"handleInputChange",value:function(e){var t=e.target,n="checkbox"===t.type?t.checked:t.value,r=t.name;this.setState((0,_.Z)({},r,n)),this.handleEnabledSaveButton()}},{key:"handleUserToBeDefaultAdminClick",value:function(e){var t=e.target.dataset.id;this.setState({defaultAdmin:t}),this.handleEnabledSaveButton()}},{key:"handleUserToBeDefaultGroupAdminClick",value:function(e){var t=e.target.dataset.id;this.setState({defaultGroupAdmin:t}),this.handleEnabledSaveButton()}},{key:"handleConnectionTypeChange",value:function(e){var t=e.target.dataset.id;this.setState({connectionType:t}),this.handleEnabledSaveButton()}},{key:"handleEnabledSaveButton",value:function(){this.props.administrationWorkspaceContext.can.save||this.props.administrationWorkspaceContext.onSaveEnabled()}},{key:"hasAllInputDisabled",value:function(){return this.state.processing||this.state.loading}},{key:"isUserDirectoryChecked",value:function(){return this.state.userDirectoryToggle}},{key:"isActiveDirectoryChecked",value:function(){return"ad"===this.state.directoryType}},{key:"isOpenLdapChecked",value:function(){return"openldap"===this.state.directoryType}},{key:"isUseEmailPrefixChecked",value:function(){return this.state.useEmailPrefix}},{key:"validate",value:(b=(0,p.Z)(m().mark((function e(){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,Promise.all([this.validateHostInput(),this.validatePortInput(),this.validateDomainInput()]);case 2:case"end":return e.stop()}}),e,this)}))),function(){return b.apply(this,arguments)})},{key:"validateHostInput",value:(g=(0,p.Z)(m().mark((function e(){var t;return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=null,this.state.host.trim().length||(t=this.translate("A host is required.")),e.abrupt("return",this.setState({hostError:t}));case 4:case"end":return e.stop()}}),e,this)}))),function(){return g.apply(this,arguments)})},{key:"validatePortInput",value:(v=(0,p.Z)(m().mark((function e(){var t,n;return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=null,(n=this.state.port.trim()).length?Ne()("^[0-9]+").test(n)||(t=this.translate("Only numeric characters allowed.")):t=this.translate("A port is required."),e.abrupt("return",this.setState({portError:t}));case 4:case"end":return e.stop()}}),e,this)}))),function(){return v.apply(this,arguments)})},{key:"validateDomainInput",value:(y=(0,p.Z)(m().mark((function e(){var t;return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=null,this.state.domain.trim().length||(t=this.translate("A domain name is required.")),e.abrupt("return",this.setState({domainError:t}));case 4:case"end":return e.stop()}}),e,this)}))),function(){return y.apply(this,arguments)})},{key:"hasValidationError",value:function(){return null!==this.state.hostError||null!==this.state.portError||null!==this.state.domainError}},{key:"handleFormSubmit",value:(f=(0,p.Z)(m().mark((function e(){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.setState({hasAlreadyBeenValidated:!0});case 2:if(this.state.processing){e.next=29;break}return e.next=5,this.toggleProcessing();case 5:return e.next=7,this.validate();case 7:if(!this.hasValidationError()){e.next=11;break}return e.next=10,this.toggleProcessing();case 10:return e.abrupt("return");case 11:if(e.prev=11,!this.props.administrationWorkspaceContext.must.save){e.next=19;break}return e.next=15,this.saveUserDirectory();case 15:return e.next=17,this.handleSaveSuccess();case 17:e.next=22;break;case 19:if(!this.props.administrationWorkspaceContext.must.test){e.next=22;break}return e.next=22,this.testUserDirectory();case 22:this.setState({processing:!1}),e.next=29;break;case 25:return e.prev=25,e.t0=e.catch(11),e.next=29,this.handleSaveError(e.t0);case 29:case"end":return e.stop()}}),e,this,[[11,25]])}))),function(){return f.apply(this,arguments)})},{key:"createUserDirectoryDTO",value:function(){var e=this.state.directoryType,t=this.state.domain,n=this.state.connectionType,r=this.state.host,s=parseInt(this.state.port),a=this.state.username,i=this.state.password,o=this.state.baseDn,c=this.state.groupPath,l=this.state.userPath,u=this.state.defaultAdmin,h=this.state.defaultGroupAdmin,p=this.state.groupsParentGroup,d=this.state.usersParentGroup,m=this.state.enabledUsersOnly,f=this.state.createUsers,y=this.state.deleteUsers,v=this.state.createGroups,g=this.state.deleteGroups,b=this.state.updateGroups,E=this.state.userDirectoryToggle,k="",w="",x=!1,C="",S="";return"openldap"==e&&(k=this.state.groupObjectClass,w=this.state.userObjectClass,(x=this.state.useEmailPrefix)&&(C=this.state.emailPrefix,S=this.state.emailSuffix)),{directory_type:e,domain_name:t,connection_type:n,server:r,port:s,username:a,password:i,base_dn:o,group_path:c,user_path:l,group_object_class:k,user_object_class:w,default_user:u,default_group_admin_user:h,groups_parent_group:p,users_parent_group:d,enabled_users_only:m,sync_users_create:f,sync_users_delete:y,sync_groups_create:v,sync_groups_delete:g,sync_groups_update:b,enabled:E,use_email_prefix_suffix:x,email_prefix:C,email_suffix:S}}},{key:"saveUserDirectory",value:(d=(0,p.Z)(m().mark((function e(){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(!this.state.userDirectoryToggle){e.next=6;break}return e.next=3,this.props.administrationWorkspaceContext.onUpdateUsersDirectoryRequested(this.createUserDirectoryDTO());case 3:this.props.administrationWorkspaceContext.onSynchronizeEnabled(!0),e.next=11;break;case 6:return this.setState(this.defaultState),e.next=9,this.props.administrationWorkspaceContext.onDeleteUsersDirectoryRequested();case 9:this.props.administrationWorkspaceContext.onSynchronizeEnabled(!1),this.setState({loading:!1});case 11:case"end":return e.stop()}}),e,this)}))),function(){return d.apply(this,arguments)})},{key:"testUserDirectory",value:(h=(0,p.Z)(m().mark((function e(){var t,n;return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.props.administrationWorkspaceContext.onTestUsersDirectoryRequested(this.createUserDirectoryDTO());case 2:t=e.sent,n={userDirectoryTestResult:t.body},this.props.context.setContext({displayTestUserDirectoryDialogProps:n}),this.props.dialogContext.open(tt);case 6:case"end":return e.stop()}}),e,this)}))),function(){return h.apply(this,arguments)})},{key:"handleSaveSuccess",value:(u=(0,p.Z)(m().mark((function e(){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.props.actionFeedbackContext.displaySuccess(this.translate("The user directory settings for the organization were updated."));case 2:case"end":return e.stop()}}),e,this)}))),function(){return u.apply(this,arguments)})},{key:"handleSaveError",value:(s=(0,p.Z)(m().mark((function e(t){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if("UserAbortsOperationError"!==t.name){e.next=4;break}this.setState({processing:!1}),e.next=8;break;case 4:return console.error(t),e.next=7,this.handleError(t);case 7:this.setState({processing:!1});case 8:case"end":return e.stop()}}),e,this)}))),function(e){return s.apply(this,arguments)})},{key:"handleError",value:(n=(0,p.Z)(m().mark((function e(t){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.props.actionFeedbackContext.displayError(t.message);case 2:case"end":return e.stop()}}),e,this)}))),function(e){return n.apply(this,arguments)})},{key:"toggleProcessing",value:(t=(0,p.Z)(m().mark((function e(){var t;return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.state.processing,e.abrupt("return",this.setState({processing:!t}));case 2:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})},{key:"getUsersAllowedToBeDefaultAdmin",value:function(){if(null!==this.state.users){var e=this.state.users.filter((function(e){return!0===e.active&&"admin"===e.role.name}));return""!==this.state.defaultAdminSearch?this.getUsersMatch(e,this.state.defaultAdminSearch.toLowerCase()):e}return[]}},{key:"getUsersAllowedToBeDefaultGroupAdmin",value:function(){if(null!==this.state.users){var e=this.state.users.filter((function(e){return!0===e.active}));return""!==this.state.defaultGroupAdminSearch?this.getUsersMatch(e,this.state.defaultGroupAdminSearch.toLowerCase()):e}return[]}},{key:"getUsersMatch",value:function(e,t){var n=t&&t.split(/\s+/)||[""],r=function(e,t){return function(e){return new RegExp(function(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}(e),"i")}(e).test(t)};return e.filter((function(e){return n.every((function(t){return function(e,t){return function(e,t){return r(e,t.username)}(e,t)||function(e,t){return r(e,t.profile.first_name)||r(e,t.profile.last_name)}(e,t)}(t,e)}))}))}},{key:"displayUser",value:function(e){return"".concat(e.profile.first_name," ").concat(e.profile.last_name," (").concat(e.username,")")}},{key:"displayDefaultAdmin",value:function(){var e=this;if(null!==this.state.users&&""!==this.state.defaultAdmin){var t=this.state.users.find((function(t){return t.id===e.state.defaultAdmin}));return this.displayUser(t)}return""}},{key:"connectionType",get:function(){return{plain:"ldap://",ssl:"ldaps:// (ssl)",tls:"ldaps:// (tls)"}}},{key:"displayDefaultGroupAdmin",value:function(){var e=this;if(null!==this.state.users&&""!==this.state.defaultGroupAdmin){var t=this.state.users.find((function(t){return t.id===e.state.defaultGroupAdmin}));return this.displayUser(t)}return""}},{key:"translate",get:function(){return this.props.t}},{key:"render",value:function(){var e=this,t=this.getUsersAllowedToBeDefaultAdmin(),n=this.getUsersAllowedToBeDefaultGroupAdmin();return r.createElement("div",{className:"row"},r.createElement("div",{className:"ldap-settings col8"},r.createElement("form",{className:"form"},r.createElement("h3",null,r.createElement("span",{className:"input toggle-switch form-element"},r.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"userDirectoryToggle",onChange:this.handleInputChange,checked:this.state.userDirectoryToggle,disabled:this.hasAllInputDisabled(),id:"userDirectoryToggle"}),r.createElement("label",{className:"toggle-switch-button",htmlFor:"userDirectoryToggle"})),r.createElement("label",null,r.createElement(T.c,null,"Users Directory"))),!this.isUserDirectoryChecked()&&r.createElement("p",{className:"description"},r.createElement(T.c,null,"No Users Directory is configured. Enable it to synchronise your users and groups with passbolt.")),this.isUserDirectoryChecked()&&r.createElement("div",null,r.createElement("p",{className:"description"},r.createElement(T.c,null,"A Users Directory is configured. The users and groups of passbolt will synchronize with it.")),r.createElement("div",{className:"form-content"},r.createElement("div",{className:"accordion section-general ".concat(this.state.openCredentials?"":"closed")},r.createElement("h3",{className:"accordion-header"},r.createElement("a",{onClick:this.handleCredentialTitleClicked},this.state.openCredentials&&r.createElement(ke,{name:"caret-down",baseline:!0}),!this.state.openCredentials&&r.createElement(ke,{name:"caret-right",baseline:!0}),r.createElement(T.c,null,"Credentials"))),r.createElement("div",{className:"accordion-content"},r.createElement("div",{className:"radiolist required"},r.createElement("label",null,r.createElement(T.c,null,"Directory type")),r.createElement("div",{className:"input radio ad openldap form-element "},r.createElement("div",{className:"input radio"},r.createElement("input",{type:"radio",value:"ad",onChange:this.handleInputChange,name:"directoryType",checked:"ad"===this.state.directoryType,id:"directoryTypeAd",disabled:this.hasAllInputDisabled()}),r.createElement("label",{htmlFor:"directoryTypeAd"},r.createElement(T.c,null,"Active Directory"))),r.createElement("div",{className:"input radio"},r.createElement("input",{type:"radio",value:"openldap",onChange:this.handleInputChange,name:"directoryType",checked:"openldap"===this.state.directoryType,id:"directoryTypeOpenLdap",disabled:this.hasAllInputDisabled()}),r.createElement("label",{htmlFor:"directoryTypeOpenLdap"},r.createElement(T.c,null,"Open Ldap"))))),r.createElement("div",{className:"singleline connection_info protocol_host_port clearfix required ad openldap"},r.createElement("label",null,r.createElement(T.c,null,"Server url")),r.createElement("div",{className:"input text field_protocol_host ad openldap"},r.createElement("div",{onClick:this.handleConnectionTypeClicked,ref:this.connectionTypeRef,className:"chosen-container chosen-container-single connection-type ".concat(this.hasAllInputDisabled()?"chosen-disabled":"chosen-container-active"," ").concat(this.state.openConnectionType?"chosen-with-drop":"")},r.createElement("a",{className:"chosen-single"},r.createElement("span",{id:"connection-type-input"},this.connectionType[this.state.connectionType]),r.createElement("div",null,!this.state.openDefaultGroupAdmin&&r.createElement(ke,{name:"caret-down",baseline:!0}),this.state.openDefaultGroupAdmin&&r.createElement(ke,{name:"caret-up",baseline:!0}))),r.createElement("div",{className:"chosen-drop"},r.createElement("ul",{className:"chosen-results"},r.createElement("li",{className:"active-result",onClick:this.handleConnectionTypeChange,"data-id":"plain"},"ldap://"),r.createElement("li",{className:"active-result",onClick:this.handleConnectionTypeChange,"data-id":"ssl"},"ldaps:// (ssl)"),r.createElement("li",{className:"active-result",onClick:this.handleConnectionTypeChange,"data-id":"tls"},"ldaps:// (tls)")))),r.createElement("div",{className:"input text host ad openldap"},r.createElement("input",{id:"server-input",type:"text",className:"required fluid form-element",name:"host",value:this.state.host,onChange:this.handleInputChange,onKeyUp:this.handleHostInputKeyUp,placeholder:this.translate("host"),disabled:this.hasAllInputDisabled()}),this.state.hostError&&r.createElement("div",{id:"server-input-feedback",className:"message error"},this.state.hostError))),r.createElement("div",{className:"input text port ad openldap"},r.createElement("input",{id:"port-input",type:"number",className:"required fluid form-element",name:"port",value:this.state.port,onChange:this.handleInputChange,onKeyUp:this.handlePortInputKeyUp,placeholder:this.translate("port"),disabled:this.hasAllInputDisabled()}),this.state.portError&&r.createElement("div",{id:"port-input-feedback",className:"message error"},this.state.portError))),r.createElement("div",{className:"singleline clearfix"},r.createElement("div",{className:"input text first-field ad openldap"},r.createElement("label",null,r.createElement(T.c,null,"Username")),r.createElement("input",{id:"username-input",type:"text",className:"fluid form-element",name:"username",value:this.state.username,onChange:this.handleInputChange,placeholder:this.translate("username"),disabled:this.hasAllInputDisabled()})),r.createElement("div",{className:"input text last-field ad openldap"},r.createElement("label",null,r.createElement(T.c,null,"Password")),r.createElement("input",{id:"password-input",className:"fluid form-element",name:"password",value:this.state.password,onChange:this.handleInputChange,placeholder:this.translate("password"),type:"password",disabled:this.hasAllInputDisabled()}))),r.createElement("div",{className:"input text required ad openldap"},r.createElement("label",null,r.createElement(T.c,null,"Domain")),r.createElement("input",{id:"domain-name-input",type:"text",name:"domain",value:this.state.domain,onChange:this.handleInputChange,className:"required fluid form-element",onKeyUp:this.handleDomainInputKeyUp,placeholder:"domain.ext",disabled:this.hasAllInputDisabled()}),this.state.domainError&&r.createElement("div",{id:"domain-name-input-feedback",className:"message error"},this.state.domainError)),r.createElement("div",{className:"input text ad openldap"},r.createElement("label",null,r.createElement(T.c,null,"Base DN")),r.createElement("input",{id:"base-dn-input",type:"text",name:"baseDn",value:this.state.baseDn,onChange:this.handleInputChange,className:"fluid form-element",placeholder:"OU=OrgUsers,DC=mydomain,DC=local",disabled:this.hasAllInputDisabled()}),r.createElement("div",{className:"message"},r.createElement(T.c,null,"The base DN (default naming context) for the domain.")," ",r.createElement(T.c,null,"If this is empty then it will be queried from the RootDSE."))))),r.createElement("div",{className:"accordion section-directory-configuration ".concat(this.state.openDirectoryConfiguration?"":"closed")},r.createElement("h3",{className:"accordion-header"},r.createElement("a",{onClick:this.handleDirectoryConfigurationTitleClicked},this.state.openDirectoryConfiguration&&r.createElement(ke,{name:"caret-down",baseline:!0}),!this.state.openDirectoryConfiguration&&r.createElement(ke,{name:"caret-right",baseline:!0}),r.createElement(T.c,null,"Directory configuration"))),r.createElement("div",{className:"accordion-content"},r.createElement("div",{className:"input text ad openldap"},r.createElement("label",null,r.createElement(T.c,null,"Group path")),r.createElement("input",{id:"group-path-input",type:"text",name:"groupPath",value:this.state.groupPath,onChange:this.handleInputChange,className:"required fluid form-element",placeholder:this.translate("Group Path"),disabled:this.hasAllInputDisabled()}),r.createElement("div",{className:"message"},r.createElement(T.c,null,"Group path is used in addition to the base DN while searching groups.")," ",r.createElement(T.c,null,"Leave empty if users and groups are in the same DN."))),r.createElement("div",{className:"input text ad openldap"},r.createElement("label",null,r.createElement(T.c,null,"User path")),r.createElement("input",{id:"user-path-input",type:"text",name:"userPath",value:this.state.userPath,onChange:this.handleInputChange,className:"required fluid form-element",placeholder:this.translate("User Path"),disabled:this.hasAllInputDisabled()}),r.createElement("div",{className:"message"},r.createElement(T.c,null,"User path is used in addition to base DN while searching users."))),this.isOpenLdapChecked()&&r.createElement("div",null,r.createElement("div",{className:"input text openldap"},r.createElement("label",null,r.createElement(T.c,null,"Group object class")),r.createElement("input",{id:"group-object-class-input",type:"text",name:"groupObjectClass",value:this.state.groupObjectClass,onChange:this.handleInputChange,className:"required fluid",placeholder:"GroupObjectClass",disabled:this.hasAllInputDisabled()}),r.createElement("div",{className:"message"},r.createElement(T.c,null,"For Openldap only. Defines which group object to use.")," (",r.createElement(T.c,null,"Default"),": posixGroup)")),r.createElement("div",{className:"input text openldap"},r.createElement("label",null,r.createElement(T.c,null,"User object class")),r.createElement("input",{id:"user-object-class-input",type:"text",name:"userObjectClass",value:this.state.userObjectClass,onChange:this.handleInputChange,className:"required fluid form-element",placeholder:"UserObjectClass",disabled:this.hasAllInputDisabled()}),r.createElement("div",{className:"message"},r.createElement(T.c,null,"For Openldap only. Defines which user object to use.")," (",r.createElement(T.c,null,"Default"),": inetOrgPerson)")),r.createElement("div",{className:"input text openldap"},r.createElement("label",null,r.createElement(T.c,null,"Use email prefix / suffix?")),r.createElement("div",{className:"input toggle-switch openldap form-element"},r.createElement("label",{htmlFor:"use-email-prefix-suffix-toggle-button"},r.createElement(T.c,null,"Build email based on a prefix and suffix?")),r.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"useEmailPrefix",value:this.state.useEmailPrefix,onChange:this.handleInputChange,id:"use-email-prefix-suffix-toggle-button",disabled:this.hasAllInputDisabled()}),r.createElement("label",{className:"toggle-switch-button",htmlFor:"use-email-prefix-suffix-toggle-button"})),r.createElement("div",{className:"message"},r.createElement(T.c,null,"Use this option when user entries do not include an email address by default"))),this.isUseEmailPrefixChecked()&&r.createElement("div",{className:"singleline clearfix",id:"use-email-prefix-suffix-options"},r.createElement("div",{className:"input text first-field openldap"},r.createElement("label",null,r.createElement(T.c,null,"Email prefix")),r.createElement("input",{id:"email-prefix-input",type:"text",name:"emailPrefix",checked:this.state.emailPrefix,onChange:this.handleInputChange,className:"required fluid form-element",placeholder:this.translate("username"),disabled:this.hasAllInputDisabled()}),r.createElement("div",{className:"message"},r.createElement(T.c,null,"The attribute you would like to use for the first part of the email (usually username)."))),r.createElement("div",{className:"input text last-field openldap"},r.createElement("label",null,r.createElement(T.c,null,"Email suffix")),r.createElement("input",{id:"email-suffix-input",type:"text",name:"emailSuffix",value:this.state.emailSuffix,onChange:this.handleInputChange,className:"required form-element",placeholder:this.translate("@your-domain.com"),disabled:this.hasAllInputDisabled()}),r.createElement("div",{className:"message"},r.createElement(T.c,null,"The domain name part of the email (@your-domain-name)."))))))),r.createElement("div",{className:"accordion section-sync-options ".concat(this.state.openSynchronizationOptions?"":"closed")},r.createElement("h3",{className:"accordion-header"},r.createElement("a",{onClick:this.handleSynchronizationOptionsTitleClicked},this.state.openSynchronizationOptions&&r.createElement(ke,{name:"caret-down",baseline:!0}),!this.state.openSynchronizationOptions&&r.createElement(ke,{name:"caret-right",baseline:!0}),r.createElement(T.c,null,"Synchronization options"))),r.createElement("div",{className:"accordion-content"},r.createElement("div",{className:"input select required ad openldap"},r.createElement("label",null,r.createElement(T.c,null,"Default admin")),r.createElement("div",{onClick:this.handleDefaultAdminClicked,ref:this.defaultAdminRef},r.createElement("div",{className:"chosen-container chosen-container-single ".concat(this.hasAllInputDisabled()?"chosen-disabled":"chosen-container-active"," ").concat(this.state.openDefaultAdmin?"chosen-with-drop":"")},r.createElement("a",{className:"chosen-single"},r.createElement("span",{id:"default-user-select"},this.displayDefaultAdmin()),r.createElement("div",null,!this.state.openDefaultAdmin&&r.createElement(ke,{name:"caret-down",baseline:!0}),this.state.openDefaultAdmin&&r.createElement(ke,{name:"caret-up",baseline:!0}))),r.createElement("div",{className:"chosen-drop"},r.createElement("div",{className:"chosen-search",onClick:this.stopPropagation},r.createElement("input",{className:"chosen-search-input",name:"defaultAdminSearch",value:this.state.defaultAdminSearch,onChange:this.handleInputChange,type:"text"}),r.createElement(ke,{name:"search"})),r.createElement("ul",{className:"chosen-results"},t.length>0&&t.map((function(t){return r.createElement("li",{key:t.id,className:"active-result",onClick:e.handleUserToBeDefaultAdminClick,"data-id":t.id},e.displayUser(t))})),0===t.length&&r.createElement("li",{className:"no-results",onClick:this.stopPropagation},r.createElement(T.c,null,"No results match")," ",r.createElement("span",null,this.state.defaultAdminSearch)))))),r.createElement("div",{className:"message"},r.createElement(T.c,null,"The default admin user is the user that will perform the operations for the the directory."))),r.createElement("div",{className:"input select required ad openldap"},r.createElement("label",null,r.createElement(T.c,null,"Default group admin")),r.createElement("div",{onClick:this.handleDefaultGroupAdminClicked,ref:this.defaultGroupAdminRef},r.createElement("div",{className:"chosen-container chosen-container-single ".concat(this.hasAllInputDisabled()?"chosen-disabled":"chosen-container-active"," ").concat(this.state.openDefaultGroupAdmin?"chosen-with-drop":"")},r.createElement("a",{className:"chosen-single"},r.createElement("span",{id:"default-group-admin-user-select"},this.displayDefaultGroupAdmin()),r.createElement("div",null,!this.state.openDefaultGroupAdmin&&r.createElement(ke,{name:"caret-down",baseline:!0}),this.state.openDefaultGroupAdmin&&r.createElement(ke,{name:"caret-up",baseline:!0}))),r.createElement("div",{className:"chosen-drop"},r.createElement("div",{className:"chosen-search",onClick:this.stopPropagation},r.createElement("input",{className:"chosen-search-input",name:"defaultGroupAdminSearch",value:this.state.defaultGroupAdminSearch,onChange:this.handleInputChange,type:"text"}),r.createElement(ke,{name:"search"})),r.createElement("ul",{className:"chosen-results"},n.length>0&&n.map((function(t){return r.createElement("li",{key:t.id,className:"active-result",onClick:e.handleUserToBeDefaultGroupAdminClick,"data-id":t.id},e.displayUser(t))})),0===n.length&&r.createElement("li",{className:"no-results",onClick:this.stopPropagation},r.createElement(T.c,null,"No results match")," ",r.createElement("span",null,this.state.defaultGroupAdminSearch)))))),r.createElement("div",{className:"message"},r.createElement(T.c,null,"The default group manager is the user that will be the group manager of newly created groups."))),r.createElement("div",{className:"input text ad openldap"},r.createElement("label",null,r.createElement(T.c,null,"Groups parent group")),r.createElement("input",{id:"groups-parent-group-input",type:"text",name:"groupsParentGroup",value:this.state.groupsParentGroup,onChange:this.handleInputChange,className:"fluid form-element",placeholder:this.translate("Group name"),disabled:this.hasAllInputDisabled()}),r.createElement("div",{className:"message"},r.createElement(T.c,null,"Synchronize only the groups which are members of this group."))),r.createElement("div",{className:"input text ad openldap"},r.createElement("label",null,r.createElement(T.c,null,"Users parent group")),r.createElement("input",{id:"users-parent-group-input",type:"text",name:"usersParentGroup",value:this.state.usersParentGroup,onChange:this.handleInputChange,className:"fluid form-element",placeholder:this.translate("Group name"),disabled:this.hasAllInputDisabled()}),r.createElement("div",{className:"message"},r.createElement(T.c,null,"Synchronize only the users which are members of this group."))),this.isActiveDirectoryChecked()&&r.createElement("div",{className:"input text clearfix ad"},r.createElement("label",null,r.createElement(T.c,null,"Enabled users only")),r.createElement("div",{className:"input toggle-switch ad form-element"},r.createElement("label",{htmlFor:"enabled-users-only-toggle-button"},r.createElement(T.c,null,"Only synchronize enabled users (AD)")),r.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"enabledUsersOnly",checked:this.state.enabledUsersOnly,onChange:this.handleInputChange,id:"enabled-users-only-toggle-button",disabled:this.hasAllInputDisabled()}),r.createElement("label",{className:"toggle-switch-button",htmlFor:"enabled-users-only-toggle-button"}))),r.createElement("div",{className:"input text clearfix ad openldap"},r.createElement("label",null,r.createElement(T.c,null,"Sync operations")),r.createElement("div",{className:"col6"},r.createElement("div",{className:"input toggle-switch ad openldap form-element"},r.createElement("label",{htmlFor:"sync-users-create-toggle-button"},r.createElement(T.c,null,"Create users")),r.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"createUsers",checked:this.state.createUsers,onChange:this.handleInputChange,id:"sync-users-create-toggle-button",disabled:this.hasAllInputDisabled()}),r.createElement("label",{className:"toggle-switch-button",htmlFor:"sync-users-create-toggle-button"})),r.createElement("div",{className:"input toggle-switch ad openldap form-element"},r.createElement("label",{htmlFor:"sync-users-delete-toggle-button"},r.createElement(T.c,null,"Delete users")),r.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"deleteUsers",checked:this.state.deleteUsers,onChange:this.handleInputChange,id:"sync-users-delete-toggle-button",disabled:this.hasAllInputDisabled()}),r.createElement("label",{className:"toggle-switch-button",htmlFor:"sync-users-delete-toggle-button"}))),r.createElement("div",{className:"col6 last"},r.createElement("div",{className:"input toggle-switch ad openldap form-element"},r.createElement("label",{htmlFor:"sync-groups-create-toggle-button"},r.createElement(T.c,null,"Create groups")),r.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"createGroups",checked:this.state.createGroups,onChange:this.handleInputChange,id:"sync-groups-create-toggle-button",disabled:this.hasAllInputDisabled()}),r.createElement("label",{className:"toggle-switch-button",htmlFor:"sync-groups-create-toggle-button"})),r.createElement("div",{className:"input toggle-switch ad openldap form-element"},r.createElement("label",{htmlFor:"sync-groups-delete-toggle-button"},r.createElement(T.c,null,"Delete groups")),r.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"deleteGroups",checked:this.state.deleteGroups,onChange:this.handleInputChange,id:"sync-groups-delete-toggle-button",disabled:this.hasAllInputDisabled()}),r.createElement("label",{className:"toggle-switch-button",htmlFor:"sync-groups-delete-toggle-button"})),r.createElement("div",{className:"input toggle-switch ad openldap form-element"},r.createElement("label",{htmlFor:"sync-groups-update-toggle-button"},r.createElement(T.c,null,"Update groups")),r.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"updateGroups",checked:this.state.updateGroups,onChange:this.handleInputChange,id:"sync-groups-update-toggle-button",disabled:this.hasAllInputDisabled()}),r.createElement("label",{className:"toggle-switch-button",htmlFor:"sync-groups-update-toggle-button"})))))))))),r.createElement("div",{className:"col4 last"},r.createElement("h2",null,r.createElement(T.c,null,"Need help?")),r.createElement("p",null,r.createElement(T.c,null,"Check out our ldap configuration guide.")),r.createElement("a",{className:"button",href:"https://help.passbolt.com/configure/ldap",target:"_blank",rel:"noopener noreferrer"},r.createElement(ke,{name:"life-ring"}),r.createElement("span",null,r.createElement(T.c,null,"Read documentation")))))}}]),N}(r.Component);nt.propTypes={context:y().object,administrationWorkspaceContext:y().object,actionFeedbackContext:y().any,dialogContext:y().any,t:y().func};const rt=B(k(S(se((0,q.Z)("common")(nt)))));var st=function(e){(0,o.Z)(w,e);var t,n,s,u,h,d,f,y,v,g,b,E,k=(b=w,E=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,t=(0,l.Z)(b);if(E){var n=(0,l.Z)(this).constructor;e=Reflect.construct(t,arguments,n)}else e=t.apply(this,arguments);return(0,c.Z)(this,e)});function w(e){var t;return(0,a.Z)(this,w),(t=k.call(this,e)).state=t.defaultState,t.bindCallbacks(),t}return(0,i.Z)(w,[{key:"defaultState",get:function(){return{loading:!0,processing:!1,hasDatabaseSetting:!1,hasFileConfigSetting:!1,passwordCreate:!0,passwordShare:!0,passwordUpdate:!0,passwordDelete:!0,folderCreate:!0,folderUpdate:!0,folderDelete:!0,folderShare:!0,commentAdd:!0,groupDelete:!0,groupUserAdd:!0,groupUserDelete:!0,groupUserUpdate:!0,groupManagerUpdate:!0,userCreate:!0,userRecover:!0,showDescription:!0,showSecret:!0,showUri:!0,showUsername:!0,showComment:!0}}},{key:"componentDidMount",value:(g=(0,p.Z)(m().mark((function e(){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.findAllEmailNotificationsSettings();case 1:case"end":return e.stop()}}),e,this)}))),function(){return g.apply(this,arguments)})},{key:"componentDidUpdate",value:(v=(0,p.Z)(m().mark((function e(t){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.handleMustSave(t.administrationWorkspaceContext.must.save);case 2:case"end":return e.stop()}}),e,this)}))),function(e){return v.apply(this,arguments)})},{key:"bindCallbacks",value:function(){this.handleInputChange=this.handleInputChange.bind(this)}},{key:"handleMustSave",value:(y=(0,p.Z)(m().mark((function e(t){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(this.props.administrationWorkspaceContext.must.save===t||!this.props.administrationWorkspaceContext.must.save){e.next=5;break}return e.next=4,this.handleFormSubmit();case 4:this.props.administrationWorkspaceContext.onResetActionsSettings();case 5:case"end":return e.stop()}}),e,this)}))),function(e){return y.apply(this,arguments)})},{key:"findAllEmailNotificationsSettings",value:(f=(0,p.Z)(m().mark((function e(){var t,n,r,s,a,i,o,c,l,u,h,p,d,f,y,v,g,b,E,k,w,x,C,S,R;return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.props.administrationWorkspaceContext.onGetEmailNotificationsRequested();case 2:t=e.sent,n=t.body,r=n.sources_database,s=n.sources_file,a=n.send_password_create,i=n.send_password_share,o=n.send_password_update,c=n.send_password_delete,l=n.send_folder_create,u=n.send_folder_update,h=n.send_folder_delete,p=n.send_folder_share,d=n.send_comment_add,f=n.send_group_delete,y=n.send_group_user_add,v=n.send_group_user_delete,g=n.send_group_user_update,b=n.send_group_manager_update,E=n.send_user_create,k=n.send_user_recover,w=n.show_description,x=n.show_secret,C=n.show_uri,S=n.show_username,R=n.show_comment,this.setState({loading:!1,hasDatabaseSetting:r,hasFileConfigSetting:s,passwordCreate:a,passwordShare:i,passwordUpdate:o,passwordDelete:c,folderCreate:l,folderUpdate:u,folderDelete:h,folderShare:p,commentAdd:d,groupDelete:f,groupUserAdd:y,groupUserDelete:v,groupUserUpdate:g,groupManagerUpdate:b,userCreate:E,userRecover:k,showDescription:w,showSecret:x,showUri:C,showUsername:S,showComment:R});case 28:case"end":return e.stop()}}),e,this)}))),function(){return f.apply(this,arguments)})},{key:"handleInputChange",value:function(e){var t=e.target,n=t.checked,r=t.name;this.setState((0,_.Z)({},r,n)),this.handleEnabledSaveButton()}},{key:"handleEnabledSaveButton",value:function(){this.props.administrationWorkspaceContext.can.save||this.props.administrationWorkspaceContext.onSaveEnabled()}},{key:"hasAllInputDisabled",value:function(){return this.state.processing||this.state.loading}},{key:"handleFormSubmit",value:(d=(0,p.Z)(m().mark((function e(){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(this.state.processing){e.next=14;break}return e.next=3,this.toggleProcessing();case 3:return e.prev=3,e.next=6,this.saveEmailNotifications();case 6:return e.next=8,this.handleSaveSuccess();case 8:e.next=14;break;case 10:return e.prev=10,e.t0=e.catch(3),e.next=14,this.handleSaveError(e.t0);case 14:case"end":return e.stop()}}),e,this,[[3,10]])}))),function(){return d.apply(this,arguments)})},{key:"saveEmailNotifications",value:(h=(0,p.Z)(m().mark((function e(){var t;return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t={sources_database:this.state.hasDatabaseSetting,sources_file:this.state.hasFileConfigSetting,send_password_create:this.state.passwordCreate,send_password_share:this.state.passwordShare,send_password_update:this.state.passwordUpdate,send_password_delete:this.state.passwordDelete,send_folder_create:this.state.folderCreate,send_folder_update:this.state.folderUpdate,send_folder_delete:this.state.folderDelete,send_folder_share:this.state.folderShare,send_comment_add:this.state.commentAdd,send_group_delete:this.state.groupDelete,send_group_user_add:this.state.groupUserAdd,send_group_user_delete:this.state.groupUserDelete,send_group_user_update:this.state.groupUserUpdate,send_group_manager_update:this.state.groupManagerUpdate,send_user_create:this.state.userCreate,send_user_recover:this.state.userRecover,show_description:this.state.showDescription,show_secret:this.state.showSecret,show_uri:this.state.showUri,show_username:this.state.showUsername,show_comment:this.state.showComment},e.next=3,this.props.administrationWorkspaceContext.onSaveEmailNotificationsRequested(t);case 3:case"end":return e.stop()}}),e,this)}))),function(){return h.apply(this,arguments)})},{key:"handleSaveSuccess",value:(u=(0,p.Z)(m().mark((function e(){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.props.actionFeedbackContext.displaySuccess(this.translate("The email notification settings were updated."));case 2:this.setState({processing:!1});case 3:case"end":return e.stop()}}),e,this)}))),function(){return u.apply(this,arguments)})},{key:"handleSaveError",value:(s=(0,p.Z)(m().mark((function e(t){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if("UserAbortsOperationError"!==t.name){e.next=4;break}this.setState({processing:!1}),e.next=8;break;case 4:return console.error(t),e.next=7,this.handleError(t);case 7:this.setState({processing:!1});case 8:case"end":return e.stop()}}),e,this)}))),function(e){return s.apply(this,arguments)})},{key:"handleError",value:(n=(0,p.Z)(m().mark((function e(t){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.props.actionFeedbackContext.displayError(t.message);case 2:case"end":return e.stop()}}),e,this)}))),function(e){return n.apply(this,arguments)})},{key:"toggleProcessing",value:(t=(0,p.Z)(m().mark((function e(){var t;return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.state.processing,e.abrupt("return",this.setState({processing:!t}));case 2:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})},{key:"hasDatabaseSetting",value:function(){return this.state.hasDatabaseSetting}},{key:"hasFileConfigSetting",value:function(){return this.state.hasFileConfigSetting}},{key:"translate",get:function(){return this.props.t}},{key:"render",value:function(){return r.createElement("div",{className:"row"},r.createElement("div",{className:"email-notification-settings col8"},this.hasDatabaseSetting()&&this.hasFileConfigSetting()&&r.createElement("div",{className:"warning message",id:"email-notification-setting-overridden-banner"},r.createElement("p",null,r.createElement(T.c,null,"Settings have been found in your database as well as in your passbolt.php (or environment variables).")," ",r.createElement(T.c,null,"The settings displayed in the form below are the one stored in your database and have precedence over others."))),!this.hasDatabaseSetting()&&this.hasFileConfigSetting()&&r.createElement("div",{className:"warning message hidden",id:"email-notification-fileconfig-exists-banner"},r.createElement("p",null,r.createElement(T.c,null,"You seem to have Email Notification Settings defined in your passbolt.php (or via environment variables).")," ",r.createElement(T.c,null,"Submitting the form will overwrite those settings with the ones you choose in the form below."))),r.createElement("h3",null,r.createElement(T.c,null,"Email delivery")),r.createElement("p",null,r.createElement(T.c,null,"In this section you can choose which email notifications will be sent.")),r.createElement("form",{className:"form"},r.createElement("div",{className:"row"},r.createElement("div",{className:"col6"},r.createElement("label",null,r.createElement(T.c,null,"Passwords")),r.createElement("span",{className:"input toggle-switch form-element"},r.createElement("label",{htmlFor:"send-password-create-toggle-button"},r.createElement(T.c,null,"When a password is created, notify its creator.")),r.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"passwordCreate",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.passwordCreate,id:"send-password-create-toggle-button"}),r.createElement("label",{className:"toggle-switch-button",htmlFor:"send-password-create-toggle-button"})),r.createElement("span",{className:"input toggle-switch form-element"},r.createElement("label",{htmlFor:"send-password-update-toggle-button"},r.createElement(T.c,null,"When a password is updated, notify the users who have access to it.")),r.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"passwordUpdate",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.passwordUpdate,id:"send-password-update-toggle-button"}),r.createElement("label",{className:"toggle-switch-button",htmlFor:"send-password-update-toggle-button"})),r.createElement("span",{className:"input toggle-switch form-element"},r.createElement("label",{htmlFor:"send-password-delete-toggle-button"},r.createElement(T.c,null,"When a password is deleted, notify the users who had access to it.")),r.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"passwordDelete",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.passwordDelete,id:"send-password-delete-toggle-button"}),r.createElement("label",{className:"toggle-switch-button",htmlFor:"send-password-delete-toggle-button"})),r.createElement("span",{className:"input toggle-switch form-element"},r.createElement("label",{htmlFor:"send-password-share-toggle-button"},r.createElement(T.c,null,"When a password is shared, notify the users who gain access to it.")),r.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"passwordShare",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.passwordShare,id:"send-password-share-toggle-button"}),r.createElement("label",{className:"toggle-switch-button",htmlFor:"send-password-share-toggle-button"}))),r.createElement("div",{className:"col6 last"},r.createElement("label",null,r.createElement(T.c,null,"Folders")),r.createElement("span",{className:"input toggle-switch form-element"},r.createElement("label",{htmlFor:"send-folder-create-toggle-button"},r.createElement(T.c,null,"When a folder is created, notify its creator.")),r.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"folderCreate",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.folderCreate,id:"send-folder-create-toggle-button"}),r.createElement("label",{className:"toggle-switch-button",htmlFor:"send-folder-create-toggle-button"})),r.createElement("span",{className:"input toggle-switch form-element"},r.createElement("label",{htmlFor:"send-folder-update-toggle-button"},r.createElement(T.c,null,"When a folder is updated, notify the users who have access to it.")),r.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"folderUpdate",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.folderUpdate,id:"send-folder-update-toggle-button"}),r.createElement("label",{className:"toggle-switch-button",htmlFor:"send-folder-update-toggle-button"})),r.createElement("span",{className:"input toggle-switch form-element"},r.createElement("label",{htmlFor:"send-folder-delete-toggle-button"},r.createElement(T.c,null,"When a folder is deleted, notify the users who had access to it.")),r.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"folderDelete",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.folderDelete,id:"send-folder-delete-toggle-button"}),r.createElement("label",{className:"toggle-switch-button",htmlFor:"send-folder-delete-toggle-button"})),r.createElement("span",{className:"input toggle-switch form-element"},r.createElement("label",{htmlFor:"send-folder-share-toggle-button"},r.createElement(T.c,null,"When a folder is shared, notify the users who gain access to it.")),r.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"folderShare",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.folderShare,id:"send-folder-share-toggle-button"}),r.createElement("label",{className:"toggle-switch-button",htmlFor:"send-folder-share-toggle-button"}))),r.createElement("div",{className:"row"}),r.createElement("div",{className:"col6 last"},r.createElement("label",null,r.createElement(T.c,null,"Comments")),r.createElement("span",{className:"input toggle-switch form-element"},r.createElement("label",{htmlFor:"send-comment-add-toggle-button"},r.createElement(T.c,null,"When a comment is posted on a password, notify the users who have access to this password.")),r.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"commentAdd",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.commentAdd,id:"send-comment-add-toggle-button"}),r.createElement("label",{className:"toggle-switch-button",htmlFor:"send-comment-add-toggle-button"})))),r.createElement("div",{className:"row"},r.createElement("div",{className:"col6"},r.createElement("label",null,r.createElement(T.c,null,"Group membership")),r.createElement("span",{className:"input toggle-switch form-element"},r.createElement("label",{htmlFor:"send-group-delete-toggle-button"},r.createElement(T.c,null,"When a group is deleted, notify the users who were member of it.")),r.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"groupDelete",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.groupDelete,id:"send-group-delete-toggle-button"}),r.createElement("label",{className:"toggle-switch-button",htmlFor:"send-group-delete-toggle-button"})),r.createElement("span",{className:"input toggle-switch form-element"},r.createElement("label",{htmlFor:"group-user-add-toggle-button"},r.createElement(T.c,null,"When users are added to a group, notify them.")),r.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"groupUserAdd",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.groupUserAdd,id:"send-group-user-add-toggle-button"}),r.createElement("label",{className:"toggle-switch-button",htmlFor:"send-group-user-add-toggle-button"})),r.createElement("span",{className:"input toggle-switch form-element"},r.createElement("label",{htmlFor:"send-group-user-delete-toggle-button"},r.createElement(T.c,null,"When users are removed from a group, notify them.")),r.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"groupUserDelete",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.groupUserDelete,id:"send-group-user-delete-toggle-button"}),r.createElement("label",{className:"toggle-switch-button",htmlFor:"send-group-user-delete-toggle-button"})),r.createElement("span",{className:"input toggle-switch form-element"},r.createElement("label",{htmlFor:"send-group-user-update-toggle-button"},r.createElement(T.c,null,"When user roles change in a group, notify the corresponding users.")),r.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"groupUserUpdate",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.groupUserUpdate,id:"send-group-user-update-toggle-button"}),r.createElement("label",{className:"toggle-switch-button",htmlFor:"send-group-user-update-toggle-button"}))),r.createElement("div",{className:"col6 last"},r.createElement("label",null,r.createElement(T.c,null,"Group manager")),r.createElement("span",{className:"input toggle-switch form-element"},r.createElement("label",{htmlFor:"send-group-manager-update-toggle-button"},r.createElement(T.c,null,"When members of a group change, notify the group manager(s).")),r.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"groupManagerUpdate",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.groupManagerUpdate,id:"send-group-manager-update-toggle-button"}),r.createElement("label",{className:"toggle-switch-button",htmlFor:"send-group-manager-update-toggle-button"})),r.createElement("label",null,r.createElement(T.c,null,"Registration & Recovery")),r.createElement("span",{className:"input toggle-switch form-element"},r.createElement("label",{htmlFor:"send-user-create-toggle-button"},r.createElement(T.c,null,"When new users are invited to passbolt, notify them.")),r.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"userCreate",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.userCreate,id:"send-user-create-toggle-button"}),r.createElement("label",{className:"toggle-switch-button",htmlFor:"send-user-create-toggle-button"})),r.createElement("span",{className:"input toggle-switch form-element"},r.createElement("label",{htmlFor:"send-user-recover-toggle-button"},r.createElement(T.c,null,"When users try to recover their account, notify them.")),r.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"userRecover",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.userRecover,id:"send-user-recover-toggle-button"}),r.createElement("label",{className:"toggle-switch-button",htmlFor:"send-user-recover-toggle-button"})))),r.createElement("div",{className:"row"},r.createElement("h3",null,r.createElement(T.c,null,"Email content visibility")),r.createElement("p",null,r.createElement(T.c,null,"In this section you can adjust the composition of the emails, e.g. which information will be included in the notification.")),r.createElement("div",{className:"col6"},r.createElement("label",null,r.createElement(T.c,null,"Passwords")),r.createElement("span",{className:"input toggle-switch form-element"},r.createElement("label",{htmlFor:"show-username-toggle-button"},r.createElement(T.c,null,"Username")),r.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"showUsername",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.showUsername,id:"show-username-toggle-button"}),r.createElement("label",{className:"toggle-switch-button",htmlFor:"show-username-toggle-button"})),r.createElement("span",{className:"input toggle-switch form-element"},r.createElement("label",{htmlFor:"show-uri-toggle-button"},r.createElement(T.c,null,"URI")),r.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"showUri",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.showUri,id:"show-uri-toggle-button"}),r.createElement("label",{className:"toggle-switch-button",htmlFor:"show-uri-toggle-button"})),r.createElement("span",{className:"input toggle-switch form-element ready"},r.createElement("label",{htmlFor:"show-secret-toggle-button"},r.createElement(T.c,null,"Encrypted secret")),r.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"showSecret",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.showSecret,id:"show-secret-toggle-button"}),r.createElement("label",{className:"toggle-switch-button",htmlFor:"show-secret-toggle-button"})),r.createElement("span",{className:"input toggle-switch form-element"},r.createElement("label",{htmlFor:"show-description-toggle-button"},r.createElement(T.c,null,"Description")),r.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"showDescription",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.showDescription,id:"show-description-toggle-button"}),r.createElement("label",{className:"toggle-switch-button",htmlFor:"show-description-toggle-button"}))),r.createElement("div",{className:"col6 last"},r.createElement("label",null,r.createElement(T.c,null,"Comments")),r.createElement("span",{className:"input toggle-switch form-element"},r.createElement("label",{htmlFor:"show-comment-toggle-button"},r.createElement(T.c,null,"Comment content")),r.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"showComment",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.showComment,id:"show-comment-toggle-button"}),r.createElement("label",{className:"toggle-switch-button",htmlFor:"show-comment-toggle-button"})))))),r.createElement("div",{className:"col4 last"},r.createElement("h3",null,r.createElement(T.c,null,"Need some help?")),r.createElement("p",null,r.createElement(T.c,null,"For more information about email notification, checkout the dedicated page on the help website.")),r.createElement("a",{className:"button",href:"https://help.passbolt.com/configure/notification/email",target:"_blank",rel:"noopener noreferrer"},r.createElement(ke,{name:"life-ring"}),r.createElement("span",null,r.createElement(T.c,null,"Read documentation")))))}}]),w}(r.Component);st.propTypes={administrationWorkspaceContext:y().object,actionFeedbackContext:y().any,t:y().func};const at=k(se((0,q.Z)("common")(st)));var it=function(e){(0,o.Z)(u,e);var t,n,s=(t=u,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,l.Z)(t);if(n){var s=(0,l.Z)(this).constructor;e=Reflect.construct(r,arguments,s)}else e=r.apply(this,arguments);return(0,c.Z)(this,e)});function u(e){var t;return(0,a.Z)(this,u),(t=s.call(this,e)).state=t.getDefaultState(),t.bindCallbacks(),t.createReferences(),t}return(0,i.Z)(u,[{key:"bindCallbacks",value:function(){this.handleChangeEvent=this.handleChangeEvent.bind(this),this.handleOnSubmitEvent=this.handleOnSubmitEvent.bind(this)}},{key:"getDefaultState",value:function(){return{}}},{key:"createReferences",value:function(){this.searchInputRef=r.createRef()}},{key:"handleChangeEvent",value:function(e){var t=e.target.value;this.props.onSearch&&this.props.onSearch(t)}},{key:"handleOnSubmitEvent",value:function(e){if(e.preventDefault(),this.props.onSearch){var t=this.searchInputRef.current.value;this.props.onSearch(t)}}},{key:"render",value:function(){return r.createElement("div",{className:"col2 search-wrapper"},r.createElement("form",{className:"search",onSubmit:this.handleOnSubmitEvent},r.createElement("div",{className:"input search required"},r.createElement("label",null,"Search"),r.createElement("input",{ref:this.searchInputRef,className:"required",type:"search",disabled:this.props.disabled?"disabled":"",onChange:this.handleChangeEvent,placeholder:this.props.placeholder||this.props.t("Search"),value:this.props.value})),r.createElement("button",{value:"search",type:"submit",disabled:this.props.disabled?"disabled":""},r.createElement(ke,{name:"search"}),r.createElement("span",{className:"visuallyhidden"},"Search"))))}}]),u}(r.Component);it.propTypes={disabled:y().bool,onSearch:y().func,placeholder:y().string,value:y().string,t:y().func},it.defaultProps={disabled:!1};const ot=(0,q.Z)("common")(it);var ct=n(9490);var lt=function(e){(0,o.Z)(u,e);var t,n,s=(t=u,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,l.Z)(t);if(n){var s=(0,l.Z)(this).constructor;e=Reflect.construct(r,arguments,s)}else e=r.apply(this,arguments);return(0,c.Z)(this,e)});function u(e){var t;return(0,a.Z)(this,u),(t=s.call(this,e)).bindCallbacks(),t}return(0,i.Z)(u,[{key:"bindCallbacks",value:function(){this.handleKeyDown=this.handleKeyDown.bind(this)}},{key:"componentDidMount",value:function(){document.addEventListener("keydown",this.handleKeyDown)}},{key:"componentWillUnmount",value:function(){document.removeEventListener("keydown",this.handleKeyDown)}},{key:"getTitle",value:function(){return this.props.context.errorDialogProps&&this.props.context.errorDialogProps.title||this.props.title||u.defaultProps.title}},{key:"getMessage",value:function(){return this.props.context.errorDialogProps&&this.props.context.errorDialogProps.message||this.props.message||u.defaultProps.message}},{key:"handleKeyDown",value:function(e){27!==e.keyCode&&13!==e.keyCode||(e.stopPropagation(),this.props.onClose())}},{key:"render",value:function(){return r.createElement("div",{className:"dialog-wrapper error-dialog"},r.createElement("div",{className:"dialog"},r.createElement("div",{className:"dialog-header"},r.createElement("h2",null,this.getTitle()),r.createElement(Ue,{onClose:this.props.onClose})),r.createElement("div",{className:"dialog-content"},r.createElement("div",{className:"form-content"},r.createElement("p",null,this.getMessage())),r.createElement("div",{className:"submit-wrapper clearfix"},r.createElement("a",{className:"button primary warning",onClick:this.props.onClose},"Ok")))))}}]),u}(r.Component);lt.defaultProps={title:"Oops something went wrong.",message:"An internal error occurred, please try again later."},lt.propTypes={context:y().any,title:y().string,message:y().string,onClose:y().func};const ut=B(lt);var ht=function(e){(0,o.Z)(u,e);var t,n,s=(t=u,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,l.Z)(t);if(n){var s=(0,l.Z)(this).constructor;e=Reflect.construct(r,arguments,s)}else e=r.apply(this,arguments);return(0,c.Z)(this,e)});function u(e){var t;return(0,a.Z)(this,u),(t=s.call(this,e)).bindCallbacks(),t}return(0,i.Z)(u,[{key:"bindCallbacks",value:function(){this.getClassName=this.getClassName.bind(this)}},{key:"getClassName",value:function(){var e="button primary";return this.props.warning&&(e+=" warning"),this.props.disabled&&(e+=" disabled"),this.props.processing&&(e+=" processing"),this.props.big&&(e+=" big"),this.props.fullWidth&&(e+=" full-width"),e}},{key:"render",value:function(){return r.createElement("input",{type:"submit",className:this.getClassName(),disabled:this.props.disabled,value:this.props.value||"Save"})}}]),u}(r.Component);ht.defaultProps={warning:!1},ht.propTypes={processing:y().bool,disabled:y().bool,value:y().string,warning:y().bool,big:y().bool,fullWidth:y().bool};const pt=ht;var dt=function(e){(0,o.Z)(E,e);var t,n,s,u,h,d,f,y,v,g,b=(v=E,g=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,t=(0,l.Z)(v);if(g){var n=(0,l.Z)(this).constructor;e=Reflect.construct(t,arguments,n)}else e=t.apply(this,arguments);return(0,c.Z)(this,e)});function E(e){var t;return(0,a.Z)(this,E),(t=b.call(this,e)).state=t.getDefaultState(),t.initEventHandlers(),t.createInputRef(),t}return(0,i.Z)(E,[{key:"getDefaultState",value:function(){return{key:"",keyError:"",processing:!1,hasBeenValidated:!1}}},{key:"initEventHandlers",value:function(){this.handleCloseClick=this.handleCloseClick.bind(this),this.handleFormSubmit=this.handleFormSubmit.bind(this),this.handleInputChange=this.handleInputChange.bind(this),this.handleKeyInputKeyUp=this.handleKeyInputKeyUp.bind(this),this.handleSelectSubscriptionKeyFile=this.handleSelectSubscriptionKeyFile.bind(this)}},{key:"createInputRef",value:function(){this.keyInputRef=r.createRef(),this.fileUploaderRef=r.createRef()}},{key:"componentDidMount",value:function(){this.setState({key:this.props.context.editSubscriptionKey.key||""})}},{key:"handleFormSubmit",value:(y=(0,p.Z)(m().mark((function e(t){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t.preventDefault(),this.state.processing){e.next=4;break}return e.next=4,this.save();case 4:case"end":return e.stop()}}),e,this)}))),function(e){return y.apply(this,arguments)})},{key:"handleInputChange",value:function(e){var t=e.target,n=t.value,r=t.name;this.setState((0,_.Z)({},r,n))}},{key:"handleKeyInputKeyUp",value:function(){if(this.state.hasAlreadyBeenValidated){var e=this.validateNameInput();this.setState(e)}}},{key:"handleCloseClick",value:function(){this.props.context.setContext({editSubscriptionKey:null}),this.props.onClose()}},{key:"handleSelectSubscriptionKeyFile",value:(f=(0,p.Z)(m().mark((function e(t){var n,r,s;return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=(0,L.Z)(t.target.files,1),r=n[0],e.next=3,this.readSubscriptionKeyFile(r);case 3:return s=e.sent,e.next=6,this.fillSubscriptionKey(s);case 6:if(!this.state.hasBeenValidated){e.next=9;break}return e.next=9,this.validate();case 9:case"end":return e.stop()}}),e,this)}))),function(e){return f.apply(this,arguments)})},{key:"readSubscriptionKeyFile",value:function(e){var t=new FileReader;return new Promise((function(n,r){t.onloadend=function(){try{n(t.result)}catch(e){r(e)}},t.readAsText(e)}))}},{key:"fillSubscriptionKey",value:(d=(0,p.Z)(m().mark((function e(t){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.setState({key:t});case 2:case"end":return e.stop()}}),e,this)}))),function(e){return d.apply(this,arguments)})},{key:"save",value:(h=(0,p.Z)(m().mark((function e(){var t;return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(!this.state.processing){e.next=2;break}return e.abrupt("return");case 2:return e.next=4,this.setState({hasBeenValidated:!0});case 4:return e.next=6,this.toggleProcessing();case 6:return e.next=8,this.validate();case 8:if(e.sent){e.next=13;break}return this.handleValidateError(),e.next=12,this.toggleProcessing();case 12:return e.abrupt("return");case 13:return t={data:this.state.key},e.prev=14,e.next=17,this.props.administrationWorkspaceContext.onUpdateSubscriptionKeyRequested(t);case 17:return e.next=19,this.handleSaveSuccess();case 19:e.next=27;break;case 21:return e.prev=21,e.t0=e.catch(14),e.next=25,this.toggleProcessing();case 25:this.handleSaveError(e.t0),this.focusFieldError();case 27:case"end":return e.stop()}}),e,this,[[14,21]])}))),function(){return h.apply(this,arguments)})},{key:"handleValidateError",value:function(){this.focusFieldError()}},{key:"handleSaveSuccess",value:(u=(0,p.Z)(m().mark((function e(){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.props.actionFeedbackContext.displaySuccess(this.translate("The subscription key has been updated successfully."));case 2:this.props.administrationWorkspaceContext.onMustRefreshSubscriptionKey(),this.props.context.setContext({editSubscriptionKey:null,refreshSubscriptionAnnouncement:!0}),this.props.onClose();case 5:case"end":return e.stop()}}),e,this)}))),function(){return u.apply(this,arguments)})},{key:"handleSaveError",value:(s=(0,p.Z)(m().mark((function e(t){var n;return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:"PassboltSubscriptionError"===t.name?this.setState({keyError:t.message}):"EntityValidationError"===t.name?this.setState({keyError:this.translate("The subscription key is invalid.")}):"PassboltApiFetchError"===t.name&&t.data&&400===t.data.code?this.setState({keyError:t.message}):(console.error(t),n={title:this.translate("There was an unexpected error..."),message:t.message},this.props.context.setContext({errorDialogProps:n}),this.props.dialogContext.open(ut));case 1:case"end":return e.stop()}}),e,this)}))),function(e){return s.apply(this,arguments)})},{key:"focusFieldError",value:function(){this.state.keyError&&this.keyInputRef.current.focus()}},{key:"validateKeyInput",value:function(){var e=this,t=this.state.key.trim(),n="";return t.length||(n=this.translate("A subscription key is required.")),new Promise((function(t){e.setState({keyError:n},t)}))}},{key:"validate",value:(n=(0,p.Z)(m().mark((function e(){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return this.setState({keyError:""}),e.next=3,this.validateKeyInput();case 3:return e.abrupt("return",""===this.state.keyError);case 4:case"end":return e.stop()}}),e,this)}))),function(){return n.apply(this,arguments)})},{key:"toggleProcessing",value:(t=(0,p.Z)(m().mark((function e(){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.setState({processing:!this.state.processing});case 2:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})},{key:"hasAllInputDisabled",value:function(){return this.state.processing}},{key:"translate",get:function(){return this.props.t}},{key:"render",value:function(){return r.createElement(Pe,{title:this.translate("Edit subscription key"),onClose:this.handleCloseClick,disabled:this.state.processing,className:"edit-subscription-dialog"},r.createElement("form",{onSubmit:this.handleFormSubmit,noValidate:!0},r.createElement("div",{className:"form-content"},r.createElement("div",{className:"input textarea required ".concat(this.state.keyError?"error":"")},r.createElement("label",{htmlFor:"edit-tag-form-name"},r.createElement(T.c,null,"Subscription key")),r.createElement("textarea",{id:"edit-subscription-form-key",name:"key",value:this.state.key,onKeyUp:this.handleKeyInputKeyUp,onChange:this.handleInputChange,disabled:this.hasAllInputDisabled(),ref:this.keyInputRef,className:"required full_report",required:"required",autoComplete:"off",autoFocus:!0})),r.createElement("div",{className:"input-file-chooser-wrapper"},r.createElement("div",{className:"input text"},r.createElement("input",{type:"file",ref:this.fileUploaderRef,disabled:this.hasAllInputDisabled(),onChange:this.handleSelectSubscriptionKeyFile}),this.state.keyError&&r.createElement("div",{className:"key error message"},this.state.keyError)))),r.createElement("div",{className:"submit-wrapper clearfix"},r.createElement(pt,{disabled:this.hasAllInputDisabled(),processing:this.state.processing,value:this.translate("Save")}),r.createElement(_e,{disabled:this.hasAllInputDisabled(),onClick:this.handleCloseClick}))))}}]),E}(r.Component);dt.propTypes={context:y().any,onClose:y().func,actionFeedbackContext:y().any,dialogContext:y().any,administrationWorkspaceContext:y().any,t:y().func};const mt=B(se(k(S((0,q.Z)("common")(dt)))));var ft=function(e){(0,o.Z)(b,e);var t,n,s,u,h,d,f,y,v,g=(y=b,v=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,t=(0,l.Z)(y);if(v){var n=(0,l.Z)(this).constructor;e=Reflect.construct(t,arguments,n)}else e=t.apply(this,arguments);return(0,c.Z)(this,e)});function b(e){var t;return(0,a.Z)(this,b),(t=g.call(this,e)).state=t.defaultState,t.bindCallbacks(),t}return(0,i.Z)(b,[{key:"defaultState",get:function(){return{loading:!0,customerId:"",subscriptionId:"",users:null,email:"",expiry:null,created:null,data:null,activeUsers:null}}},{key:"componentDidMount",value:(f=(0,p.Z)(m().mark((function e(){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.findActiveUsers(),this.findSubscriptionKey();case 2:case"end":return e.stop()}}),e,this)}))),function(){return f.apply(this,arguments)})},{key:"componentDidUpdate",value:(d=(0,p.Z)(m().mark((function e(t){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.handleMustRefreshSubscriptionKey(t.administrationWorkspaceContext.must.refreshSubscriptionKey);case 2:return e.next=4,this.handleMustEditSubscriptionKey(t.administrationWorkspaceContext.must.editSubscriptionKey);case 4:case"end":return e.stop()}}),e,this)}))),function(e){return d.apply(this,arguments)})},{key:"bindCallbacks",value:function(){this.handleRenewKey=this.handleRenewKey.bind(this),this.handleUpdateKey=this.handleUpdateKey.bind(this)}},{key:"handleMustRefreshSubscriptionKey",value:(h=(0,p.Z)(m().mark((function e(t){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(this.props.administrationWorkspaceContext.must.refreshSubscriptionKey===t||!this.props.administrationWorkspaceContext.must.refreshSubscriptionKey){e.next=7;break}return e.next=4,this.findActiveUsers();case 4:return e.next=6,this.findSubscriptionKey();case 6:this.props.administrationWorkspaceContext.onResetActionsSettings();case 7:case"end":return e.stop()}}),e,this)}))),function(e){return h.apply(this,arguments)})},{key:"handleMustEditSubscriptionKey",value:(u=(0,p.Z)(m().mark((function e(t){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.props.administrationWorkspaceContext.must.editSubscriptionKey!==t&&this.props.administrationWorkspaceContext.must.editSubscriptionKey&&(this.openEditSubscriptionKey(),this.props.administrationWorkspaceContext.onResetActionsSettings());case 2:case"end":return e.stop()}}),e,this)}))),function(e){return u.apply(this,arguments)})},{key:"openEditSubscriptionKey",value:function(){var e={key:this.state.data};this.props.context.setContext({editSubscriptionKey:e}),this.props.dialogContext.open(mt)}},{key:"findActiveUsers",value:(s=(0,p.Z)(m().mark((function e(){var t;return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.getActiveUsers();case 2:t=e.sent,this.setState({activeUsers:t});case 4:case"end":return e.stop()}}),e,this)}))),function(){return s.apply(this,arguments)})},{key:"findSubscriptionKey",value:(n=(0,p.Z)(m().mark((function e(){var t,n,r,s,a,i,o,c;return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,e.next=3,this.props.context.onGetSubscriptionKeyRequested();case 3:t=e.sent,n=t.customer_id,r=t.subscription_id,s=t.users,a=t.email,i=t.expiry,o=t.created,c=t.data,this.setState({loading:!1,customerId:n,subscriptionId:r,users:s,email:a,expiry:i,created:o,data:c}),e.next=17;break;case 14:e.prev=14,e.t0=e.catch(0),this.handleSubscriptionError(e.t0);case 17:case"end":return e.stop()}}),e,this,[[0,14]])}))),function(){return n.apply(this,arguments)})},{key:"handleSubscriptionError",value:function(e){if("PassboltSubscriptionError"===e.name){var t=e.subscription,n=t.customer_id||"N/A",r=t.subscription_id,s=t.users,a=t.email||"N/A",i=t.expiry,o=t.created,c=t.data;this.setState({loading:!1,customerId:n,subscriptionId:r,users:s,email:a,expiry:i,created:o,data:c})}else this.setState({loading:!1})}},{key:"handleRenewKey",value:function(){this.hasLimitUsersExceeded()?this.props.navigationContext.onGoToNewTab("https://www.passbolt.com/subscription/ee/update/qty?subscription_id=".concat(this.state.subscriptionId,"&customer_id=").concat(this.state.customerId)):(this.hasSubscriptionKeyExpired()||this.hasSubscriptionKeyGoingToExpire())&&this.props.navigationContext.onGoToNewTab("https://www.passbolt.com/subscription/ee/update/renew?subscription_id=".concat(this.state.subscriptionId,"&customer_id=").concat(this.state.customerId))}},{key:"handleUpdateKey",value:function(){this.openEditSubscriptionKey()}},{key:"isLoading",value:function(){return this.state.loading}},{key:"hasSubscriptionKeyExpired",value:function(){return ct.ou.fromISO(this.state.expiry)-1e3&&n<0?this.translate("Just now"):t.toRelative({locale:this.props.context.locale})}},{key:"getActiveUsers",value:(t=(0,p.Z)(m().mark((function e(){var t,n;return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.props.context.port.request("passbolt.users.get-all");case 2:return t=e.sent,n=function(e){return e.active},e.abrupt("return",t.filter(n).length);case 5:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})},{key:"translate",get:function(){return this.props.t}},{key:"render",value:function(){return r.createElement("div",{className:"row"},!this.isLoading()&&r.createElement("div",{className:"subscription-key col8"},r.createElement("h3",null,r.createElement(T.c,null,"Subscription key details")),r.createElement("div",{className:"message animated"},r.createElement("div",{className:"illustration"},this.hasValidSubscription()&&!this.hasSubscriptionKeyGoingToExpire()&&r.createElement("svg",{id:"successAnimation",className:"animated",xmlns:"http://www.w3.org/2000/svg",width:"170",height:"170",viewBox:"0 0 70 70"},r.createElement("circle",{id:"successAnimationCircle",cx:"35",cy:"35",r:"24",stroke:"#000000",strokeWidth:"3",strokeLinecap:"round",fill:"transparent"}),r.createElement("polyline",{id:"successAnimationCheck",stroke:"#000000",strokeWidth:"3",points:"23 34 34 43 47 27",linecap:"round",fill:"transparent"})),this.hasInvalidSubscription()&&r.createElement("svg",{id:"errorAnimation",className:"animated",xmlns:"http://www.w3.org/2000/svg",width:"170",height:"170",viewBox:"0 0 70 70"},r.createElement("circle",{id:"errorAnimationCircle",cx:"35",cy:"35",r:"24",stroke:"#000000",strokeWidth:"3",strokeLinecap:"round",fill:"transparent"}),r.createElement("polyline",{id:"errorAnimationCross",stroke:"#000000",strokeWidth:"3",points:"26 26 44 44",linecap:"round",fill:"transparent"}),r.createElement("polyline",{id:"errorAnimationCross",stroke:"#000000",strokeWidth:"3",points:"44 26 26 44",linecap:"round",fill:"transparent"})),this.hasValidSubscription()&&this.hasSubscriptionKeyGoingToExpire()&&r.createElement("svg",{id:"warningAnimation",className:"animated",xmlns:"http://www.w3.org/2000/svg",width:"170",height:"170",viewBox:"0 0 70 70"},r.createElement("polyline",{id:"warningAnimation",className:"round",stroke:"#000000",strokeWidth:"3",points:"35 9 10 55 60 55 35 9",linecap:"round",fill:"transparent"}),r.createElement("polyline",{id:"warningAnimation",stroke:"#000000",strokeWidth:"3",points:"35 23 35 42",linecap:"square",fill:"transparent"}),r.createElement("polyline",{id:"warningAnimation",stroke:"#000000",strokeWidth:"3",points:"35 50 35 50",linecap:"square",fill:"transparent"}))),r.createElement("div",{className:"subscription-information"},!this.hasSubscriptionKey()&&r.createElement(r.Fragment,null,r.createElement("h4",null,r.createElement(T.c,null,"Your subscription key is either missing or not valid.")),r.createElement("p",null,r.createElement(T.c,null,"Sorry your subscription is either missing or not readable."),r.createElement("br",null),r.createElement(T.c,null,"Update the subscription key and try again.")," ",r.createElement(T.c,null,"If this does not work get in touch with support."))),this.hasValidSubscription()&&this.hasSubscriptionKeyGoingToExpire()&&r.createElement("h4",null,r.createElement(T.c,null,"Your subscription key is going to expire.")),this.hasSubscriptionKey()&&this.hasInvalidSubscription()&&r.createElement("h4",null,r.createElement(T.c,null,"Your subscription key is not valid.")),this.hasValidSubscription()&&!this.hasSubscriptionKeyGoingToExpire()&&r.createElement("h4",null,r.createElement(T.c,null,"Your subscription key is valid and up to date!")),this.hasSubscriptionKey()&&r.createElement("ul",null,r.createElement("li",{className:"customer-id"},r.createElement("span",{className:"label"},r.createElement(T.c,null,"Customer id:")),r.createElement("span",{className:"value"},this.state.customerId)),r.createElement("li",{className:"subscription-id"},r.createElement("span",{className:"label"},r.createElement(T.c,null,"Subscription id:")),r.createElement("span",{className:"value"},this.state.subscriptionId)),r.createElement("li",{className:"email"},r.createElement("span",{className:"label"},r.createElement(T.c,null,"Email:")),r.createElement("span",{className:"value"},this.state.email)),r.createElement("li",{className:"users"},r.createElement("span",{className:"label ".concat(this.hasLimitUsersExceeded()?"error":"")},r.createElement(T.c,null,"Users limit:")),r.createElement("span",{className:"value ".concat(this.hasLimitUsersExceeded()?"error":"")},this.state.users," (",r.createElement(T.c,null,"curently:")," ",this.state.activeUsers,")")),r.createElement("li",{className:"created"},r.createElement("span",{className:"label"},r.createElement(T.c,null,"Valid from:")),r.createElement("span",{className:"value"},this.formatDate(this.state.created))),r.createElement("li",{className:"expiry"},r.createElement("span",{className:"label ".concat(this.hasSubscriptionKeyExpired()?"error":""," ").concat(this.hasSubscriptionKeyGoingToExpire()?"warning":"")},r.createElement(T.c,null,"Expires on:")),r.createElement("span",{className:"value ".concat(this.hasSubscriptionKeyExpired()?"error":""," ").concat(this.hasSubscriptionKeyGoingToExpire()?"warning":"")},this.formatDate(this.state.expiry)," (","".concat(this.hasSubscriptionKeyExpired()?this.translate("expired "):"").concat(this.formatDateTimeAgo(this.state.expiry)),")"))),this.hasSubscriptionToRenew()&&r.createElement(r.Fragment,null,this.hasSubscriptionKey()&&r.createElement("a",{className:"button primary big",role:"button",onClick:this.handleRenewKey},r.createElement(T.c,null,"Renew key")),!this.hasSubscriptionKey()&&r.createElement("a",{className:"button primary big",role:"button",onClick:this.handleUpdateKey},r.createElement(T.c,null,"Update key")),r.createElement("a",{target:"_blank",rel:"noopener noreferrer",href:"https://www.passbolt.com/contact"},r.createElement(T.c,null,"or, contact us")))))),!this.isLoading()&&r.createElement("div",{className:"col4 last"},r.createElement("h2",null,r.createElement(T.c,null,"Need help?")),r.createElement("p",null,r.createElement(T.c,null,"For any change or question related to your passbolt subscription, kindly contact our sales team.")),r.createElement("a",{className:"button",target:"_blank",rel:"noopener noreferrer",href:"https://www.passbolt.com/contact"},r.createElement(ke,{name:"envelope"}),r.createElement("span",null,r.createElement(T.c,null,"Contact Sales")))))}}]),b}(r.Component);ft.propTypes={context:y().any,navigationContext:y().any,administrationWorkspaceContext:y().object,dialogContext:y().any,t:y().func};const yt=B(me(se(S((0,q.Z)("common")(ft)))));var vt=function(e){(0,o.Z)(E,e);var t,n,s,u,h,d,f,y,v,g,b=(v=E,g=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,t=(0,l.Z)(v);if(g){var n=(0,l.Z)(this).constructor;e=Reflect.construct(t,arguments,n)}else e=t.apply(this,arguments);return(0,c.Z)(this,e)});function E(e){var t;return(0,a.Z)(this,E),(t=b.call(this,e)).state=t.defaultState,t.bindCallbacks(),t}return(0,i.Z)(E,[{key:"defaultState",get:function(){return{loading:!0,processing:!1,locale:""}}},{key:"componentDidMount",value:(y=(0,p.Z)(m().mark((function e(){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.setState({locale:this.props.context.siteSettings.locale});case 1:case"end":return e.stop()}}),e,this)}))),function(){return y.apply(this,arguments)})},{key:"componentDidUpdate",value:(f=(0,p.Z)(m().mark((function e(t){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.handleMustSave(t.administrationWorkspaceContext.must.save);case 2:case"end":return e.stop()}}),e,this)}))),function(e){return f.apply(this,arguments)})},{key:"bindCallbacks",value:function(){this.handleInputChange=this.handleInputChange.bind(this)}},{key:"handleMustSave",value:(d=(0,p.Z)(m().mark((function e(t){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(this.props.administrationWorkspaceContext.must.save===t||!this.props.administrationWorkspaceContext.must.save){e.next=5;break}return e.next=4,this.handleFormSubmit();case 4:this.props.administrationWorkspaceContext.onResetActionsSettings();case 5:case"end":return e.stop()}}),e,this)}))),function(e){return d.apply(this,arguments)})},{key:"handleInputChange",value:function(e){var t=e.target,n=t.value,r=t.name;this.setState((0,_.Z)({},r,n)),this.handleEnabledSaveButton()}},{key:"handleEnabledSaveButton",value:function(){this.props.administrationWorkspaceContext.can.save||this.props.administrationWorkspaceContext.onSaveEnabled()}},{key:"hasAllInputDisabled",value:function(){return this.state.processing||this.state.loading}},{key:"handleFormSubmit",value:(h=(0,p.Z)(m().mark((function e(){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(this.state.processing){e.next=14;break}return e.next=3,this.toggleProcessing();case 3:return e.prev=3,e.next=6,this.props.administrationWorkspaceContext.onSaveLocaleRequested(this.state.locale);case 6:return e.next=8,this.handleSaveSuccess();case 8:e.next=14;break;case 10:return e.prev=10,e.t0=e.catch(3),e.next=14,this.handleSaveError(e.t0);case 14:case"end":return e.stop()}}),e,this,[[3,10]])}))),function(){return h.apply(this,arguments)})},{key:"handleSaveSuccess",value:(u=(0,p.Z)(m().mark((function e(){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return this.props.context.onRefreshLocaleRequested(this.state.locale),e.next=3,this.props.actionFeedbackContext.displaySuccess(this.translate("The internationalization settings were updated."));case 3:this.setState({processing:!1});case 4:case"end":return e.stop()}}),e,this)}))),function(){return u.apply(this,arguments)})},{key:"handleSaveError",value:(s=(0,p.Z)(m().mark((function e(t){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if("UserAbortsOperationError"!==t.name){e.next=4;break}this.setState({processing:!1}),e.next=8;break;case 4:return console.error(t),e.next=7,this.handleError(t);case 7:this.setState({processing:!1});case 8:case"end":return e.stop()}}),e,this)}))),function(e){return s.apply(this,arguments)})},{key:"handleError",value:(n=(0,p.Z)(m().mark((function e(t){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.props.actionFeedbackContext.displayError(t.message);case 2:case"end":return e.stop()}}),e,this)}))),function(e){return n.apply(this,arguments)})},{key:"toggleProcessing",value:(t=(0,p.Z)(m().mark((function e(){var t;return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.state.processing,e.abrupt("return",this.setState({processing:!t}));case 2:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})},{key:"supportedLocales",get:function(){return this.props.context.siteSettings.supportedLocales||[]}},{key:"translate",get:function(){return this.props.t}},{key:"render",value:function(){return r.createElement("div",{className:"row"},r.createElement("div",{className:"internationalisation-settings col8"},r.createElement("h3",null,r.createElement(T.c,null,"Internationalisation")),r.createElement("form",{className:"form"},r.createElement("div",{className:"input select locale"},r.createElement("label",{htmlFor:"app-locale-input"},r.createElement(T.c,null,"Language")),r.createElement("select",{className:"large",id:"locale-input",name:"locale",value:this.state.locale,onChange:this.handleInputChange},this.supportedLocales.map((function(e){return r.createElement("option",{key:e.locale,value:e.locale},e.label)}))),r.createElement("p",null,r.createElement(T.c,null,"The default language of the organisation."))))),r.createElement("div",{className:"col4 last"},r.createElement("h3",null,r.createElement(T.c,null,"Want to contribute?")),r.createElement("p",null,r.createElement(T.c,null,"Your language is missing or you discovered an error in the translation, help us to improve passbolt.")),r.createElement("a",{className:"button",href:"https://help.passbolt.com/contribute/translation",target:"_blank",rel:"noopener noreferrer"},r.createElement(ke,{name:"heart-o"}),r.createElement("span",null,r.createElement(T.c,null,"Contribute")))))}}]),E}(r.Component);vt.propTypes={context:y().object,administrationWorkspaceContext:y().object,actionFeedbackContext:y().any,t:y().func};const gt=B(k(se((0,q.Z)("common")(vt))));var bt=function(e){(0,o.Z)(u,e);var t,n,s=(t=u,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,l.Z)(t);if(n){var s=(0,l.Z)(this).constructor;e=Reflect.construct(r,arguments,s)}else e=r.apply(this,arguments);return(0,c.Z)(this,e)});function u(){return(0,a.Z)(this,u),s.apply(this,arguments)}return(0,i.Z)(u,[{key:"isMfaSelected",value:function(){return ae.MFA===this.props.administrationWorkspaceContext.selectedAdministration}},{key:"isUserDirectorySelected",value:function(){return ae.USER_DIRECTORY===this.props.administrationWorkspaceContext.selectedAdministration}},{key:"isEmailNotificationsSelected",value:function(){return ae.EMAIL_NOTIFICATION===this.props.administrationWorkspaceContext.selectedAdministration}},{key:"isSubscriptionSelected",value:function(){return ae.SUBSCRIPTION===this.props.administrationWorkspaceContext.selectedAdministration}},{key:"isInternationalizationSelected",value:function(){return ae.INTERNATIONALIZATION===this.props.administrationWorkspaceContext.selectedAdministration}},{key:"render",value:function(){return r.createElement("div",{id:"container",className:"page administration"},r.createElement("div",{id:"app",tabIndex:"1000"},r.createElement("div",{className:"header first"},r.createElement(ye,null)),r.createElement("div",{className:"header second"},r.createElement(ve,null),r.createElement(ot,{disabled:!0}),r.createElement(xe,{baseUrl:this.props.context.trustedDomain||this.props.context.userSettings.getTrustedDomain(),user:this.props.context.loggedInUser})),r.createElement("div",{className:"header third"},r.createElement("div",{className:"col1 main-action-wrapper"}),r.createElement(We,null)),r.createElement("div",{className:"panel main"},r.createElement("div",null,r.createElement("div",{className:"panel left"},r.createElement(Se,null)),r.createElement("div",{className:"panel middle"},r.createElement(Je,null),r.createElement("div",{className:"workspace-main"},r.createElement("div",{className:"grid grid-responsive-12"},this.isMfaSelected()&&r.createElement(Te,null),this.isUserDirectorySelected()&&r.createElement(rt,null),this.isEmailNotificationsSelected()&&r.createElement(at,null),this.isSubscriptionSelected()&&r.createElement(yt,null),this.isInternationalizationSelected()&&r.createElement(gt,null))))))))}}]),u}(r.Component);bt.propTypes={context:y().any,administrationWorkspaceContext:y().object};const Et=B(se(bt));var kt=function(e){(0,o.Z)(h,e);var t,n,s=(t=h,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,l.Z)(t);if(n){var s=(0,l.Z)(this).constructor;e=Reflect.construct(r,arguments,s)}else e=r.apply(this,arguments);return(0,c.Z)(this,e)});function h(){return(0,a.Z)(this,h),s.apply(this,arguments)}return(0,i.Z)(h,[{key:"privacyUrl",get:function(){return this.props.context.siteSettings.privacyLink}},{key:"creditsUrl",get:function(){return"https://www.passbolt.com/credits"}},{key:"unsafeUrl",get:function(){return"https://help.passbolt.com/faq/hosting/why-unsafe"}},{key:"termsUrl",get:function(){return this.props.context.siteSettings.termsLink}},{key:"versions",get:function(){var e=[],t=this.props.context.siteSettings.version;return t&&e.push(t),this.props.context.extensionVersion&&e.push(this.props.context.extensionVersion),e.join(" / ")}},{key:"isUnsafeMode",get:function(){var e=this.props.context.siteSettings.debug,t=this.props.context.siteSettings.url.startsWith("http://");return e||t}},{key:"translate",get:function(){return this.props.t}},{key:"render",value:function(){return r.createElement("footer",null,r.createElement("div",{className:"footer"},r.createElement("ul",{className:"footer-links"},this.isUnsafeMode&&r.createElement("li",{className:"error-message"},r.createElement("a",{title:"terms of service",href:this.unsafeUrl,target:"_blank",rel:"noopener noreferrer"},r.createElement(T.c,null,"Unsafe mode"))),this.termsUrl&&r.createElement("li",null,r.createElement("a",{href:this.termsUrl,target:"_blank",rel:"noopener noreferrer"},r.createElement(T.c,null,"Terms"))),this.privacyUrl&&r.createElement("li",null,r.createElement("a",{href:this.privacyUrl,target:"_blank",rel:"noopener noreferrer"},r.createElement(T.c,null,"Privacy"))),r.createElement("li",null,r.createElement("a",{href:this.creditsUrl,target:"_blank",rel:"noopener noreferrer"},r.createElement(T.c,null,"Credits"))),r.createElement("li",null,r.createElement("a",(0,u.Z)({href:this.creditsUrl,className:"tooltip-left"},this.versions&&{"data-tooltip":this.versions},{target:"_blank",rel:"noopener noreferrer"}),r.createElement(ke,{name:"heart-o"}))))))}}]),h}(r.Component);kt.propTypes={context:y().any,t:y().func};const wt=B((0,q.Z)("common")(kt));var xt=function(e){(0,o.Z)(u,e);var t,n,s=(t=u,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,l.Z)(t);if(n){var s=(0,l.Z)(this).constructor;e=Reflect.construct(r,arguments,s)}else e=r.apply(this,arguments);return(0,c.Z)(this,e)});function u(){return(0,a.Z)(this,u),s.apply(this,arguments)}return(0,i.Z)(u,[{key:"isMfaEnabled",get:function(){return this.props.context.siteSettings.canIUse("multiFactorAuthentication")}},{key:"canIUseThemeCapability",get:function(){return this.props.context.siteSettings&&this.props.context.siteSettings.canIUse("accountSettings")}},{key:"translate",get:function(){return this.props.t}},{key:"render",value:function(){var e=this,t=function(t){return e.props.location.pathname.endsWith(t)};return r.createElement("div",{className:"navigation first shortcuts"},r.createElement("ul",null,r.createElement("li",null,r.createElement("div",{className:"row ".concat(t("profile")?"selected":"")},r.createElement("div",{className:"main-cell-wrapper"},r.createElement("div",{className:"main-cell"},r.createElement("a",{onClick:this.props.navigationContext.onGoToUserSettingsProfileRequested},r.createElement("span",null,r.createElement(T.c,null,"Profile"))))))),r.createElement("li",null,r.createElement("div",{className:"row ".concat(t("passphrase")?"selected":"")},r.createElement("div",{className:"main-cell-wrapper"},r.createElement("div",{className:"main-cell"},r.createElement("a",{onClick:this.props.navigationContext.onGoToUserSettingsPassphraseRequested},r.createElement("span",null,r.createElement(T.c,null,"Passphrase"))))))),r.createElement("li",null,r.createElement("div",{className:"row ".concat(t("security-token")?"selected":"")},r.createElement("div",{className:"main-cell-wrapper"},r.createElement("div",{className:"main-cell"},r.createElement("a",{onClick:this.props.navigationContext.onGoToUserSettingsSecurityTokenRequested},r.createElement("span",null,r.createElement(T.c,null,"Security Token"))))))),this.canIUseThemeCapability&&r.createElement("li",null,r.createElement("div",{className:"row ".concat(t("theme")?"selected":"")},r.createElement("div",{className:"main-cell-wrapper"},r.createElement("div",{className:"main-cell"},r.createElement("a",{onClick:this.props.navigationContext.onGoToUserSettingsThemeRequested},r.createElement("span",null,r.createElement(T.c,null,"Theme"))))))),this.isMfaEnabled&&r.createElement("li",null,r.createElement("div",{className:"row ".concat(t("mfa")?"selected":"")},r.createElement("div",{className:"main-cell-wrapper"},r.createElement("div",{className:"main-cell"},r.createElement("a",{onClick:this.props.navigationContext.onGoToUserSettingsMfaRequested},r.createElement("span",null,r.createElement(T.c,null,"Multi Factor Authentication"))))))),r.createElement("li",null,r.createElement("div",{className:"row ".concat(t("keys")?"selected":"")},r.createElement("div",{className:"main-cell-wrapper"},r.createElement("div",{className:"main-cell"},r.createElement("a",{onClick:this.props.navigationContext.onGoToUserSettingsKeysRequested},r.createElement("span",null,r.createElement(T.c,null,"Keys inspector")))))))))}}]),u}(r.Component);xt.propTypes={context:y().any,navigationContext:y().any,history:y().object,location:y().object,t:y().func};const Ct=B((0,O.EN)(me((0,q.Z)("common")(xt))));var St=function(e){(0,o.Z)(h,e);var t,n,s,u=(n=h,s=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,t=(0,l.Z)(n);if(s){var r=(0,l.Z)(this).constructor;e=Reflect.construct(t,arguments,r)}else e=t.apply(this,arguments);return(0,c.Z)(this,e)});function h(){return(0,a.Z)(this,h),u.apply(this,arguments)}return(0,i.Z)(h,[{key:"items",get:function(){return[r.createElement(Ye,{key:"bread-1",name:this.translate("All users"),onClick:this.props.navigationContext.onGoToUsersRequested}),r.createElement(Ye,{key:"bread-2",name:this.loggedInUserName,onClick:this.props.navigationContext.onGoToUserSettingsProfileRequested}),r.createElement(Ye,{key:"bread-3",name:this.getLastBreadcrumbItemName,onClick:this.onLastBreadcrumbClick.bind(this)})]}},{key:"loggedInUserName",get:function(){var e=this.props.context.loggedInUser;return e?"".concat(e.profile.first_name," ").concat(e.profile.last_name):""}},{key:"getLastBreadcrumbItemName",get:function(){var e=this,t={profile:this.translate("Profile"),passphrase:this.translate("Passphrase"),"security-token":this.translate("Security Token"),theme:this.translate("Theme"),mfa:this.translate("Multi Factor Authentication"),keys:this.translate("Keys inspector")};return t[Object.keys(t).find((function(t){return e.props.location.pathname.endsWith(t)}))]}},{key:"onLastBreadcrumbClick",value:(t=(0,p.Z)(m().mark((function e(){var t;return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=this.props.location.pathname,this.props.history.push({pathname:t});case 2:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})},{key:"translate",get:function(){return this.props.t}},{key:"render",value:function(){return r.createElement(He,{items:this.items})}}]),h}(r.Component);St.propTypes={context:y().any,location:y().object,history:y().object,navigationContext:y().any,t:y().func};const Rt=B((0,O.EN)(me((0,q.Z)("common")(St))));var Nt=function(e){(0,o.Z)(u,e);var t,n,s=(t=u,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,l.Z)(t);if(n){var s=(0,l.Z)(this).constructor;e=Reflect.construct(r,arguments,s)}else e=r.apply(this,arguments);return(0,c.Z)(this,e)});function u(){return(0,a.Z)(this,u),s.apply(this,arguments)}return(0,i.Z)(u,[{key:"render",value:function(){return r.createElement("iframe",{src:"".concat(this.props.context.trustedDomain,"/mfa/setup/select"),width:"100%",height:"100%"})}}]),u}(r.Component);Nt.propTypes={context:y().any};const Zt=B(Nt);var Tt=function(e){(0,o.Z)(u,e);var t,n,s=(t=u,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,l.Z)(t);if(n){var s=(0,l.Z)(this).constructor;e=Reflect.construct(r,arguments,s)}else e=r.apply(this,arguments);return(0,c.Z)(this,e)});function u(){return(0,a.Z)(this,u),s.apply(this,arguments)}return(0,i.Z)(u,[{key:"render",value:function(){return r.createElement("div",null,r.createElement("div",{className:"header second"},r.createElement(ve,null),r.createElement(ot,{disabled:!0}),r.createElement(xe,{baseUrl:this.props.context.trustedDomain,user:this.props.context.loggedInUser})),r.createElement("div",{className:"header third"}),r.createElement("div",{className:"panel main"},r.createElement("div",{className:"panel left"},r.createElement(Ct,null)),r.createElement("div",{className:"panel middle"},r.createElement(Rt,null),r.createElement(O.AW,{path:"/app/settings/mfa",component:Zt}))))}}]),u}(r.Component);Tt.propTypes={context:y().any};const qt=(0,O.EN)(B(Tt));var Ut=function(e){(0,o.Z)(u,e);var t,n,s=(t=u,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,l.Z)(t);if(n){var s=(0,l.Z)(this).constructor;e=Reflect.construct(r,arguments,s)}else e=r.apply(this,arguments);return(0,c.Z)(this,e)});function u(e){var t;return(0,a.Z)(this,u),(t=s.call(this,e)).initEventHandlers(),t.createReferences(),t}return(0,i.Z)(u,[{key:"initEventHandlers",value:function(){this.handleCloseClick=this.handleCloseClick.bind(this)}},{key:"createReferences",value:function(){this.loginLinkRef=r.createRef()}},{key:"handleCloseClick",value:function(){this.goToLogin()}},{key:"goToLogin",value:function(){this.loginLinkRef.current.click()}},{key:"loginUrl",get:function(){var e=this.props.context.userSettings&&this.props.context.userSettings.getTrustedDomain();return e=e||this.props.context.trustedDomain,"".concat(e,"/auth/login")}},{key:"translate",get:function(){return this.props.t}},{key:"render",value:function(){return r.createElement(Pe,{title:this.translate("Session Expired"),onClose:this.handleCloseClick,className:"session-expired-dialog"},r.createElement("div",{className:"form-content"},r.createElement("p",null,r.createElement(T.c,null,"Your session has expired, you need to sign in."))),r.createElement("div",{className:"submit-wrapper clearfix"},r.createElement("a",{ref:this.loginLinkRef,href:this.loginUrl,className:"primary button",target:"_parent",role:"button",rel:"noopener noreferrer"},r.createElement(T.c,null,"Sign in"))))}}]),u}(r.Component);Ut.propTypes={context:y().any,t:y().func};const At=B((0,O.EN)((0,q.Z)("common")(Ut)));var Dt=function(e){(0,o.Z)(h,e);var t,n,s,u=(n=h,s=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,t=(0,l.Z)(n);if(s){var r=(0,l.Z)(this).constructor;e=Reflect.construct(t,arguments,r)}else e=t.apply(this,arguments);return(0,c.Z)(this,e)});function h(e){var t;return(0,a.Z)(this,h),(t=u.call(this,e)).bindCallbacks(),t.scheduledCheckIsAuthenticatedTimeout=null,t}return(0,i.Z)(h,[{key:"bindCallbacks",value:function(){this.handleSessionExpiredEvent=this.handleSessionExpiredEvent.bind(this)}},{key:"componentDidMount",value:function(){this.scheduleCheckIsAuthenticated()}},{key:"componentWillUnmount",value:function(){clearTimeout(this.scheduledCheckIsAuthenticatedTimeout)}},{key:"scheduleCheckIsAuthenticated",value:function(){var e=this;this.scheduledCheckIsAuthenticatedTimeout=setTimeout((0,p.Z)(m().mark((function t(){return m().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return t.next=2,e.checkIsAuthenticated();case 2:t.sent?e.scheduleCheckIsAuthenticated():e.handleSessionExpiredEvent();case 4:case"end":return t.stop()}}),t)}))),6e4)}},{key:"checkIsAuthenticated",value:(t=(0,p.Z)(m().mark((function e(){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.props.context.onCheckIsAuthenticatedRequested();case 2:return e.abrupt("return",e.sent);case 3:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})},{key:"handleSessionExpiredEvent",value:function(){this.props.dialogContext.open(At)}},{key:"render",value:function(){return r.createElement(r.Fragment,null)}}]),h}(r.Component);Dt.propTypes={context:y().any,dialogContext:y().any};const It=B(S(Dt));function Pt(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,r=(0,l.Z)(e);if(t){var s=(0,l.Z)(this).constructor;n=Reflect.construct(r,arguments,s)}else n=r.apply(this,arguments);return(0,c.Z)(this,n)}}var Ot=r.createContext({announcements:[],show:function(){},close:function(){}}),_t=function(e){(0,o.Z)(n,e);var t=Pt(n);function n(e){var r;return(0,a.Z)(this,n),(r=t.call(this,e)).state=r.defaultState,r}return(0,i.Z)(n,[{key:"defaultState",get:function(){var e,t=this;return{announcements:[],show:function(e,n){var r=(0,v.Z)();return t.setState({announcements:[].concat((0,h.Z)(t.state.announcements),[{key:r,Announcement:e,AnnouncementProps:n}])}),r},close:(e=(0,p.Z)(m().mark((function e(n){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,t.setState({announcements:t.state.announcements.filter((function(e){return n!==e.key}))});case 2:return e.abrupt("return",e.sent);case 3:case"end":return e.stop()}}),e)}))),function(t){return e.apply(this,arguments)})}}},{key:"render",value:function(){return r.createElement(Ot.Provider,{value:this.state},this.props.children)}}]),n}(r.Component);function Mt(e){return function(t){(0,o.Z)(s,t);var n=Pt(s);function s(){return(0,a.Z)(this,s),n.apply(this,arguments)}return(0,i.Z)(s,[{key:"render",value:function(){var t=this;return r.createElement(Ot.Consumer,null,(function(n){return r.createElement(e,(0,u.Z)({announcementContext:n},t.props))}))}}]),s}(r.Component)}_t.displayName="AnnouncementContextProvider",_t.propTypes={children:y().any};var Bt=function(e){(0,o.Z)(u,e);var t,n,s=(t=u,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,l.Z)(t);if(n){var s=(0,l.Z)(this).constructor;e=Reflect.construct(r,arguments,s)}else e=r.apply(this,arguments);return(0,c.Z)(this,e)});function u(e){var t;return(0,a.Z)(this,u),(t=s.call(this,e)).bindCallbacks(),t}return(0,i.Z)(u,[{key:"bindCallbacks",value:function(){this.handleClose=this.handleClose.bind(this)}},{key:"handleClose",value:function(){this.props.onClose()}},{key:"render",value:function(){return r.createElement("div",{className:"".concat(this.props.className," announcement")},r.createElement("div",{className:"announcement-content"},this.props.canClose&&r.createElement("a",{className:"announcement-close",onClick:this.handleClose,role:"button"},r.createElement(ke,{name:"close"}),r.createElement("span",{className:"visually-hidden"},"Close")),this.props.children))}}]),u}(r.Component);Bt.propTypes={children:y().node,className:y().string,canClose:y().bool,onClose:y().func};const zt=Bt;var Gt=function(e){(0,o.Z)(u,e);var t,n,s=(t=u,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,l.Z)(t);if(n){var s=(0,l.Z)(this).constructor;e=Reflect.construct(r,arguments,s)}else e=r.apply(this,arguments);return(0,c.Z)(this,e)});function u(){return(0,a.Z)(this,u),s.apply(this,arguments)}return(0,i.Z)(u,[{key:"formatDateTimeAgo",value:function(e){var t=ct.ou.fromISO(e),n=t.diffNow().toMillis();return n>-1e3&&n<0?this.translate("Just now"):t.toRelative({locale:this.props.context.locale})}},{key:"render",value:function(){return r.createElement(zt,{className:"subscription",onClose:this.props.onClose,canClose:!0},r.createElement("p",null,r.createElement(T.c,null,"Warning: "),r.createElement(T.c,null,"your subscription key will expire")," ",this.formatDateTimeAgo(this.props.expiry),".",r.createElement("a",{onClick:this.props.navigationContext.onGoToAdministrationSubscriptionRequested},r.createElement(T.c,null,"Manage Subscription"))))}}]),u}(r.Component);Gt.propTypes={context:y().any,expiry:y().string,navigationContext:y().any,onClose:y().func};const Ft=B(me(Mt((0,q.Z)("common")(Gt))));var Lt=function(e){(0,o.Z)(u,e);var t,n,s=(t=u,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,l.Z)(t);if(n){var s=(0,l.Z)(this).constructor;e=Reflect.construct(r,arguments,s)}else e=r.apply(this,arguments);return(0,c.Z)(this,e)});function u(){return(0,a.Z)(this,u),s.apply(this,arguments)}return(0,i.Z)(u,[{key:"render",value:function(){return r.createElement(zt,{className:"subscription",onClose:this.props.onClose,canClose:!1},r.createElement("p",null,r.createElement(T.c,null,"Warning: "),r.createElement(T.c,null,"your subscription key has expired. The stability of the application is at risk."),r.createElement("a",{onClick:this.props.navigationContext.onGoToAdministrationSubscriptionRequested},r.createElement(T.c,null,"Manage Subscription"))))}}]),u}(r.Component);Lt.propTypes={navigationContext:y().any,onClose:y().func,i18n:y().any};const Kt=me(Mt((0,q.Z)("common")(Lt)));var Wt=function(e){(0,o.Z)(u,e);var t,n,s=(t=u,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,l.Z)(t);if(n){var s=(0,l.Z)(this).constructor;e=Reflect.construct(r,arguments,s)}else e=r.apply(this,arguments);return(0,c.Z)(this,e)});function u(){return(0,a.Z)(this,u),s.apply(this,arguments)}return(0,i.Z)(u,[{key:"render",value:function(){return r.createElement(zt,{className:"subscription",onClose:this.props.onClose,canClose:!1},r.createElement("p",null,r.createElement(T.c,null,"Warning: "),r.createElement(T.c,null,"your subscription key is not valid. The stability of the application is at risk."),r.createElement("a",{onClick:this.props.navigationContext.onGoToAdministrationSubscriptionRequested},r.createElement(T.c,null,"Manage Subscription"))))}}]),u}(r.Component);Wt.propTypes={navigationContext:y().any,onClose:y().func,i18n:y().any};const jt=me(Mt((0,q.Z)("common")(Wt)));var Ht=function(e){(0,o.Z)(d,e);var t,n,s,u,h=(s=d,u=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,t=(0,l.Z)(s);if(u){var n=(0,l.Z)(this).constructor;e=Reflect.construct(t,arguments,n)}else e=t.apply(this,arguments);return(0,c.Z)(this,e)});function d(e){var t;return(0,a.Z)(this,d),(t=h.call(this,e)).bindCallbacks(),t}return(0,i.Z)(d,[{key:"bindCallbacks",value:function(){this.handleAnnouncementSubscriptionEvent=this.handleAnnouncementSubscriptionEvent.bind(this)}},{key:"componentDidMount",value:function(){this.handleAnnouncementSubscriptionEvent()}},{key:"componentDidUpdate",value:function(e){this.handleRefreshSubscriptionAnnouncement(e.context.refreshSubscriptionAnnouncement)}},{key:"handleRefreshSubscriptionAnnouncement",value:(n=(0,p.Z)(m().mark((function e(t){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(this.props.context.refreshSubscriptionAnnouncement===t||!this.props.context.refreshSubscriptionAnnouncement){e.next=5;break}return e.next=4,this.handleAnnouncementSubscriptionEvent();case 4:this.props.context.setContext({refreshSubscriptionAnnouncement:null});case 5:case"end":return e.stop()}}),e,this)}))),function(e){return n.apply(this,arguments)})},{key:"handleAnnouncementSubscriptionEvent",value:(t=(0,p.Z)(m().mark((function e(){var t;return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return this.hideSubscriptionAnnouncement(),e.prev=1,e.next=4,this.props.context.onGetSubscriptionKeyRequested();case 4:t=e.sent,this.isSubscriptionGoingToExpire(t.expiry)&&this.props.announcementContext.show(Ft,{expiry:t.expiry}),e.next=11;break;case 8:e.prev=8,e.t0=e.catch(1),"PassboltSubscriptionError"===e.t0.name?this.props.announcementContext.show(Kt):this.props.announcementContext.show(jt);case 11:case"end":return e.stop()}}),e,this,[[1,8]])}))),function(){return t.apply(this,arguments)})},{key:"hideSubscriptionAnnouncement",value:function(){var e=this,t=[Ft,Kt,jt];this.props.announcementContext.announcements.forEach((function(n){t.some((function(e){return e===n.Announcement}))&&e.props.announcementContext.close(n.key)}))}},{key:"isSubscriptionGoingToExpire",value:function(e){return ct.ou.fromISO(e)1&&void 0!==arguments[1]?arguments[1]:{};return(0,a.Z)(this,s),(t=r.call(this,e)).name="PassboltSubscriptionError",t.subscription=n,t}return s}((0,K.Z)(Error));var an=function(e){(0,o.Z)(E,e);var t,n,s,u,h,d,f,y,v,g,b=(v=E,g=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,t=(0,l.Z)(v);if(g){var n=(0,l.Z)(this).constructor;e=Reflect.construct(t,arguments,n)}else e=t.apply(this,arguments);return(0,c.Z)(this,e)});function E(e){var t;return(0,a.Z)(this,E),(t=b.call(this,e)).state=t.defaultState,t}return(0,i.Z)(E,[{key:"componentDidMount",value:(y=(0,p.Z)(m().mark((function e(){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.getLoggedInUser();case 2:return e.next=4,this.getSiteSettings();case 4:this.initLocale(),this.removeSplashScreen();case 6:case"end":return e.stop()}}),e,this)}))),function(){return y.apply(this,arguments)})},{key:"defaultState",get:function(){var e=this;return{name:"api",loggedInUser:null,siteSettings:null,trustedDomain:this.baseUrl,basename:new URL(this.baseUrl).pathname,getApiClientOptions:this.getApiClientOptions.bind(this),locale:null,displayTestUserDirectoryDialogProps:{userDirectoryTestResult:null},setContext:function(t){e.setState(t)},onLogoutRequested:function(){return e.onLogoutRequested()},onCheckIsAuthenticatedRequested:function(){return e.onCheckIsAuthenticatedRequested()},onGetSubscriptionKeyRequested:function(){return e.onGetSubscriptionKeyRequested()},onRefreshLocaleRequested:this.onRefreshLocaleRequested.bind(this)}}},{key:"isReady",get:function(){return null!==this.state.siteSettings&&null!==this.state.locale}},{key:"baseUrl",get:function(){var e=document.getElementsByTagName("base")&&document.getElementsByTagName("base")[0];return e?e.attributes.href.value.replace(/\/*$/g,""):(console.error("Unable to retrieve the page base tag"),"")}},{key:"getApiClientOptions",value:function(){return(new rn).setBaseUrl(this.state.trustedDomain).setCsrfToken(this.getCsrfToken())}},{key:"getCsrfToken",value:function(){var e=document.cookie;if(e){var t=e.split("; ");if(t){var n=t.find((function(e){return e.startsWith("csrfToken")}));if(n){var r=n.split("=");return r&&2===r.length?r[1]:void 0}}}}},{key:"getLoggedInUser",value:(f=(0,p.Z)(m().mark((function e(){var t,n,r,s;return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.getApiClientOptions().setResourceName("users"),n=new $(t),e.next=4,n.get("me");case 4:r=e.sent,s=r.body,this.setState({loggedInUser:s});case 7:case"end":return e.stop()}}),e,this)}))),function(){return f.apply(this,arguments)})},{key:"getSiteSettings",value:(d=(0,p.Z)(m().mark((function e(){var t,n,r;return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.getApiClientOptions().setResourceName("settings"),n=new $(t),e.next=4,n.findAll();case 4:return r=e.sent,e.next=7,this.setState({siteSettings:new tn(r.body)});case 7:case"end":return e.stop()}}),e,this)}))),function(){return d.apply(this,arguments)})},{key:"initLocale",value:(h=(0,p.Z)(m().mark((function e(){var t,n;return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.getUserLocale();case 2:if(!(t=e.sent)){e.next=5;break}return e.abrupt("return",this.setState({locale:t.locale}));case 5:return n=this.state.siteSettings.locale,e.abrupt("return",this.setState({locale:n}));case 7:case"end":return e.stop()}}),e,this)}))),function(){return h.apply(this,arguments)})},{key:"getUserLocale",value:(u=(0,p.Z)(m().mark((function e(){var t,n;return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.getUserSettings();case 2:if(t=e.sent,!(n=t.find((function(e){return"locale"===e.property})))){e.next=6;break}return e.abrupt("return",this.state.siteSettings.supportedLocales.find((function(e){return e.locale===n.value})));case 6:case"end":return e.stop()}}),e,this)}))),function(){return u.apply(this,arguments)})},{key:"getUserSettings",value:(s=(0,p.Z)(m().mark((function e(){var t,n,r;return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.getApiClientOptions().setResourceName("account/settings"),n=new $(t),e.next=4,n.findAll();case 4:return r=e.sent,e.abrupt("return",r.body);case 6:case"end":return e.stop()}}),e,this)}))),function(){return s.apply(this,arguments)})},{key:"removeSplashScreen",value:function(){document.getElementsByTagName("html")[0].classList.remove("launching")}},{key:"onLogoutRequested",value:function(){document.location.href="".concat(this.state.trustedDomain,"/auth/logout")}},{key:"onCheckIsAuthenticatedRequested",value:(n=(0,p.Z)(m().mark((function e(){var t,n;return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,t=this.getApiClientOptions().setResourceName("auth"),n=new $(t),e.next=5,n.get("is-authenticated");case 5:return e.abrupt("return",!0);case 8:if(e.prev=8,e.t0=e.catch(0),!(e.t0 instanceof W)){e.next=13;break}if(403!==e.t0.data.code){e.next=13;break}return e.abrupt("return",!1);case 13:throw e.t0;case 14:case"end":return e.stop()}}),e,this,[[0,8]])}))),function(){return n.apply(this,arguments)})},{key:"onGetSubscriptionKeyRequested",value:(t=(0,p.Z)(m().mark((function e(){var t,n,r,s;return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,t=this.getApiClientOptions().setResourceName("ee/subscription"),n=new $(t),e.next=5,n.get("key");case 5:return r=e.sent,e.abrupt("return",r.body);case 9:if(e.prev=9,e.t0=e.catch(0),!(e.t0 instanceof W)){e.next=16;break}if(!e.t0.data||402!==e.t0.data.code){e.next=16;break}throw s=e.t0.data.body,new sn(e.t0.message,s);case 16:throw e.t0;case 17:case"end":return e.stop()}}),e,this,[[0,9]])}))),function(){return t.apply(this,arguments)})},{key:"onRefreshLocaleRequested",value:function(e){this.state.siteSettings.setLocale(e),this.initLocale()}},{key:"render",value:function(){var e=this.isReady;return r.createElement(z.Provider,{value:this.state},e&&this.props.children)}}]),E}(r.Component);an.propTypes={children:y().any};const on=an;var cn=n(7412),ln=n(8718),un=n(3554);var hn=function(e){(0,o.Z)(f,e);var t,n,s,u,h,d=(u=f,h=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,t=(0,l.Z)(u);if(h){var n=(0,l.Z)(this).constructor;e=Reflect.construct(t,arguments,n)}else e=t.apply(this,arguments);return(0,c.Z)(this,e)});function f(e){var t;return(0,a.Z)(this,f),(t=d.call(this,e)).state=t.defaultState,t}return(0,i.Z)(f,[{key:"defaultState",get:function(){return{ready:!1}}},{key:"componentDidMount",value:(s=(0,p.Z)(m().mark((function e(){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,cn.Z.use(ln.Db).use(un.Z).init({lng:this.locale,load:"currentOnly",react:{useSuspense:!1},backend:{loadPath:this.props.loadingPath||"/locales/{{lng}}/{{ns}}.json"},supportedLngs:this.supportedLocales,fallbackLng:!1,ns:["common"],defaultNS:"common",keySeparator:!1,nsSeparator:!1,debug:!1});case 2:this.setState({ready:!0});case 3:case"end":return e.stop()}}),e,this)}))),function(){return s.apply(this,arguments)})},{key:"supportedLocales",get:function(){return this.props.context.siteSettings.supportedLocales?this.props.context.siteSettings.supportedLocales.map((function(e){return e.locale})):[this.locale]}},{key:"locale",get:function(){return this.props.context.locale}},{key:"componentDidUpdate",value:(n=(0,p.Z)(m().mark((function e(t){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.handleLocaleChange(t.context.locale);case 2:case"end":return e.stop()}}),e,this)}))),function(e){return n.apply(this,arguments)})},{key:"handleLocaleChange",value:(t=(0,p.Z)(m().mark((function e(t){return m().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(this.locale===t){e.next=4;break}return e.next=4,cn.Z.changeLanguage(this.locale);case 4:case"end":return e.stop()}}),e,this)}))),function(e){return t.apply(this,arguments)})},{key:"isReady",get:function(){return this.state.ready}},{key:"render",value:function(){return r.createElement(r.Fragment,null,this.isReady&&this.props.children)}}]),f}(r.Component);hn.propTypes={context:y().any,loadingPath:y().any,children:y().any};const pn=B(hn);const dn=function(e){(0,o.Z)(u,e);var t,n,s=(t=u,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,l.Z)(t);if(n){var s=(0,l.Z)(this).constructor;e=Reflect.construct(r,arguments,s)}else e=r.apply(this,arguments);return(0,c.Z)(this,e)});function u(){return(0,a.Z)(this,u),s.apply(this,arguments)}return(0,i.Z)(u,[{key:"render",value:function(){return r.createElement(on,null,r.createElement(z.Consumer,null,(function(e){return r.createElement(pn,{loadingPath:"".concat(e.trustedDomain,"/locales/{{lng}}/{{ns}}.json")},r.createElement(E,null,r.createElement(C,null,r.createElement(_t,null,r.createElement(Z,null,r.createElement(I,null),r.createElement(It,null),e.loggedInUser&&"admin"===e.loggedInUser.role.name&&e.siteSettings.canIUse("ee")&&r.createElement(Vt,null),r.createElement(P.VK,{basename:e.basename},r.createElement(de,null,r.createElement(O.rs,null,r.createElement(O.AW,{exact:!0,path:["/app/administration/subscription"]}),r.createElement(O.AW,{path:"/app/administration"},r.createElement(ne,null,r.createElement(oe,null),r.createElement(le,null),r.createElement($t,null),r.createElement(Et,null))),r.createElement(O.AW,{path:"/app/settings/mfa"},r.createElement(oe,null),r.createElement(le,null),r.createElement($t,null),r.createElement("div",{id:"container",className:"page settings"},r.createElement("div",{id:"app",className:"app",tabIndex:"1000"},r.createElement("div",{className:"header first"},r.createElement(ye,null)),r.createElement(qt,null))))))),r.createElement(wt,null))))))})))}}]),u}(r.Component);var mn=document.createElement("div");document.body.appendChild(mn),s.render(r.createElement(dn,null),mn)}},s={};function a(e){var t=s[e];if(void 0!==t)return t.exports;var n=s[e]={exports:{}};return r[e](n,n.exports,a),n.exports}a.m=r,e=[],a.O=(t,n,r,s)=>{if(!n){var i=1/0;for(l=0;l=s)&&Object.keys(a.O).every((e=>a.O[e](n[c])))?n.splice(c--,1):(o=!1,s0&&e[l-1][2]>s;l--)e[l]=e[l-1];e[l]=[n,r,s]},a.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return a.d(t,{a:t}),t},n=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,a.t=function(e,r){if(1&r&&(e=this(e)),8&r)return e;if("object"==typeof e&&e){if(4&r&&e.__esModule)return e;if(16&r&&"function"==typeof e.then)return e}var s=Object.create(null);a.r(s);var i={};t=t||[null,n({}),n([]),n(n)];for(var o=2&r&&e;"object"==typeof o&&!~t.indexOf(o);o=n(o))Object.getOwnPropertyNames(o).forEach((t=>i[t]=()=>e[t]));return i.default=()=>e,a.d(s,i),s},a.d=(e,t)=>{for(var n in t)a.o(t,n)&&!a.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},a.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),a.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),a.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.j=978,(()=>{var e={978:0};a.O.j=t=>0===e[t];var t=(t,n)=>{var r,s,[i,o,c]=n,l=0;for(r in o)a.o(o,r)&&(a.m[r]=o[r]);if(c)var u=c(a);for(t&&t(n);la(6223)));i=a.O(i)})(); \ No newline at end of file +(()=>{"use strict";var e,t,s,n={4569:(e,t,s)=>{var n=s(7294),a=s(3935),r=s(5697),i=s.n(r),l=s(2045);function o(){return o=Object.assign||function(e){for(var t=1;t{},displayError:()=>{},remove:()=>{}});class h extends n.Component{constructor(e){super(e),this.state=this.defaultState}get defaultState(){return{feedbacks:[],displaySuccess:this.displaySuccess.bind(this),displayError:this.displayError.bind(this),remove:this.remove.bind(this)}}async displaySuccess(e){await this.setState({feedbacks:[...this.state.feedbacks,{id:(0,l.Z)(),type:"success",message:e}]})}async displayError(e){await this.setState({feedbacks:[...this.state.feedbacks,{id:(0,l.Z)(),type:"error",message:e}]})}async remove(e){await this.setState({feedbacks:this.state.feedbacks.filter((t=>e.id!==t.id))})}render(){return n.createElement(c.Provider,{value:this.state},this.props.children)}}function d(e){return class extends n.Component{render(){return n.createElement(c.Consumer,null,(t=>n.createElement(e,o({actionFeedbackContext:t},this.props))))}}}function u(){return u=Object.assign||function(e){for(var t=1;t{},close:()=>{}});class m extends n.Component{constructor(e){super(e),this.state=this.defaultState}get defaultState(){return{dialogs:[],open:(e,t)=>{const s=(0,l.Z)();return this.setState({dialogs:[...this.state.dialogs,{key:s,Dialog:e,DialogProps:t}]}),s},close:async e=>await this.setState({dialogs:this.state.dialogs.filter((t=>e!==t.key))})}}render(){return n.createElement(p.Provider,{value:this.state},this.props.children)}}function g(e){return class extends n.Component{render(){return n.createElement(p.Consumer,null,(t=>n.createElement(e,u({dialogContext:t},this.props))))}}}function E(){return E=Object.assign||function(e){for(var t=1;t{},hide:()=>{}});class y extends n.Component{constructor(e){super(e),this.state=this.defaultState}get defaultState(){return{contextualMenus:[],show:(e,t)=>this.setState({contextualMenus:[...this.state.contextualMenus,{ContextualMenuComponent:e,componentProps:t}]}),hide:e=>this.setState({contextualMenus:this.state.contextualMenus.filter(((t,s)=>s!==e))})}}render(){return n.createElement(b.Provider,{value:this.state},this.props.children)}}y.displayName="ContextualMenuContextProvider",y.propTypes={children:i().any};var f=s(9116),v=s(8831);class C extends n.Component{static get DEFAULT_WAIT_TO_CLOSE_TIME_IN_MS(){return 500}constructor(e){super(e),this.state=this.defaultState,this.bindCallbacks()}get defaultState(){return{shouldRender:!0,isPersisted:!1,timeoutId:null}}componentDidMount(){this.displayWithTimer(this.props.displayTimeInMs)}componentDidUpdate(e){const t=e&&e.feedback.id!==this.props.feedback.id,s=e&&this.props.displayTimeInMs&&e.displayTimeInMs!==this.props.displayTimeInMs;t?(this.setState({shouldRender:!0}),this.displayWithTimer(this.props.displayTimeInMs)):s&&this.updateTimer(this.props.displayTimeInMs)}componentWillUnmount(){this.state.timeoutId&&clearTimeout(this.state.timeoutId)}bindCallbacks(){this.persist=this.persist.bind(this),this.displayWithTimer=this.displayWithTimer.bind(this),this.close=this.close.bind(this)}displayWithTimer(e){this.state.timeoutId&&clearTimeout(this.state.timeoutId);const t=setTimeout(this.close,e),s=Date.now();this.setState({timeoutId:t,time:s})}updateTimer(e){const t=e-(Date.now()-this.state.time);t>0?this.displayWithTimer(t):(clearTimeout(this.state.timeoutId),this.close())}persist(){this.state.timeoutId&&!this.state.isPersisted&&(clearTimeout(this.state.timeoutId),this.setState({isPersisted:!0}))}close(){this.setState({shouldRender:!1}),setTimeout(this.props.onClose,C.DEFAULT_WAIT_TO_CLOSE_TIME_IN_MS)}render(){return n.createElement(n.Fragment,null,n.createElement("div",{className:"notification",onMouseOver:this.persist,onMouseLeave:this.displayWithTimer,onClick:this.close},n.createElement("div",{className:`message animated ${this.state.shouldRender?"fadeInUp":"fadeOutUp"} warning`},n.createElement("span",{className:"content"},n.createElement("strong",null,"success"===this.props.feedback.type&&n.createElement(n.Fragment,null,n.createElement(f.c,null,"Success"),": "),"error"===this.props.feedback.type&&n.createElement(n.Fragment,null,n.createElement(f.c,null,"Error"),": ")),this.props.feedback.message))))}}C.propTypes={feedback:i().object,onClose:i().func,displayTimeInMs:i().number};const w=(0,v.Z)("common")(C);class S extends n.Component{constructor(e){super(e),this.bindCallbacks()}static get DEFAULT_DISPLAY_TIME_IN_MS(){return 5e3}static get DEFAULT_DISPLAY_MIN_TIME_IN_MS(){return 1200}bindCallbacks(){this.close=this.close.bind(this)}get feedbackToDisplay(){return this.props.actionFeedbackContext.feedbacks[0]}get length(){return this.props.actionFeedbackContext.feedbacks.length}get hasFeedbacks(){return this.length>0}async close(e){await this.props.actionFeedbackContext.remove(e)}render(){const e=this.length>1?S.DEFAULT_DISPLAY_MIN_TIME_IN_MS:S.DEFAULT_DISPLAY_TIME_IN_MS;return n.createElement(n.Fragment,null,this.hasFeedbacks&&n.createElement("div",{className:"notification-container"},n.createElement(w,{feedback:this.feedbackToDisplay,onClose:()=>this.close(this.feedbackToDisplay),displayTimeInMs:e})))}}S.propTypes={actionFeedbackContext:i().any};const k=d(S);var x=s(3727),N=s(5977);function T(){return T=Object.assign||function(e){for(var t=1;tn.createElement(e,T({context:t},this.props))))}}}const A=q;function I(){return I=Object.assign||function(e){for(var t=1;t{},remove:()=>{}});class R extends n.Component{constructor(e){super(e),this.state=this.defaultState}get defaultState(){return{counter:0,add:()=>{this.setState({counter:this.state.counter+1})},remove:()=>{this.setState({counter:Math.min(this.state.counter-1,0)})}}}render(){return n.createElement(D.Provider,{value:this.state},this.props.children)}}R.propTypes={children:i().any};class _ extends Error{constructor(e,t){super(e),this.name="PassboltApiFetchError",this.data=t||{}}}const M=_;class z extends Error{constructor(){super("An internal error occurred. The server response could not be parsed. Please contact your administrator."),this.name="PassboltBadResponseError"}}const G=z;class O extends Error{constructor(e){super(e=e||"The service is unavailable"),this.name="PassboltServiceUnavailableError"}}const P=O;class F{constructor(e){if(this.options=e,!this.options.getBaseUrl())throw new TypeError("ApiClient constructor error: baseUrl is required.");if(!this.options.getResourceName())throw new TypeError("ApiClient constructor error: resourceName is required.");try{let e=this.options.getBaseUrl().toString();e.endsWith("/")&&(e=e.slice(0,-1)),this.baseUrl=`${e}/${this.options.getResourceName()}`,this.baseUrl=new URL(this.baseUrl)}catch(e){throw new TypeError("ApiClient constructor error: b.")}this.apiVersion="api-version=v2"}getDefaultHeaders(){return{Accept:"application/json","content-type":"application/json"}}buildFetchOptions(){return{credentials:"include",headers:{...this.getDefaultHeaders(),...this.options.getHeaders()}}}async get(e,t){this.assertValidId(e);const s=this.buildUrl(`${this.baseUrl}/${e}`,t||{});return this.fetchAndHandleResponse("GET",s)}async delete(e,t,s,n){let a;this.assertValidId(e),void 0===n&&(n=!1),a=n?this.buildUrl(`${this.baseUrl}/${e}/dry-run`,s||{}):this.buildUrl(`${this.baseUrl}/${e}`,s||{});let r=null;return t&&(r=this.buildBody(t)),this.fetchAndHandleResponse("DELETE",a,r)}async findAll(e){const t=this.buildUrl(this.baseUrl.toString(),e||{});return await this.fetchAndHandleResponse("GET",t)}async create(e,t){const s=this.buildUrl(this.baseUrl.toString(),t||{}),n=this.buildBody(e);return this.fetchAndHandleResponse("POST",s,n)}async update(e,t,s,n){let a;this.assertValidId(e),void 0===n&&(n=!1),a=n?this.buildUrl(`${this.baseUrl}/${e}/dry-run`,s||{}):this.buildUrl(`${this.baseUrl}/${e}`,s||{});let r=null;return t&&(r=this.buildBody(t)),this.fetchAndHandleResponse("PUT",a,r)}assertValidId(e){if(!e)throw new TypeError("ApiClient.assertValidId error: id cannot be empty");if("string"!=typeof e)throw new TypeError("ApiClient.assertValidId error: id should be a string")}assertMethod(e){"string"!=typeof e&&new TypeError("ApiClient.assertValidMethod method should be a string."),["GET","POST","PUT","DELETE"].indexOf(e)<0&&new TypeError(`ApiClient.assertValidMethod error: method ${e} is not supported.`)}assertUrl(e){if(!e)throw new TypeError("ApliClient.assertUrl error: url is required.");if(!(e instanceof URL))throw new TypeError("ApliClient.assertUrl error: url should be a valid URL object.")}assertBody(e){"string"!=typeof e&&new TypeError("ApiClient.assertBody error: body should be a string.")}buildBody(e){return JSON.stringify(e)}buildUrl(e,t){if("string"!=typeof e)throw new TypeError("ApiClient.buildUrl error: url should be a string.");const s=new URL(`${e}.json?${this.apiVersion}`);t=t||{};for(const[e,n]of Object.entries(t)){if("string"!=typeof e)throw new TypeError("ApiClient.buildUrl error: urlOptions key should be a string.");if("string"==typeof n)s.searchParams.append(e,n);else{if(!Array.isArray(n))throw new TypeError("ApiClient.buildUrl error: urlOptions value should be a string or array.");n.forEach((t=>{s.searchParams.append(e,t)}))}}return s}async fetchAndHandleResponse(e,t,s,n){let a,r;this.assertUrl(t),this.assertMethod(e),s&&this.assertBody(s);const i={...this.buildFetchOptions(),...n};i.method=e,s&&(i.body=s);try{a=await fetch(t.toString(),i)}catch(e){throw new P(e.message)}try{r=await a.json()}catch(e){throw new G}if(!a.ok){const e=r.header.message;throw new M(e,{code:a.status,body:r.body})}return r}}function L(){return L=Object.assign||function(e){for(var t=1;t{},onSaveMfaRequested:()=>{},onGetUsersDirectoryRequested:()=>{},onUpdateUsersDirectoryRequested:()=>{},onDeleteUsersDirectoryRequested:()=>{},onTestUsersDirectoryRequested:()=>{},onGetSimulateSynchronizeUsersDirectoryRequested:()=>{},onGetSynchronizeUsersDirectoryRequested:()=>{},onGetUsersRequested:()=>{},onGetEmailNotificationsRequested:()=>{},onSaveEmailNotificationsRequested:()=>{},onUpdateSubscriptionKeyRequested:()=>{},onSaveLocaleRequested:()=>{},onSaveEnabled:()=>{},onMustSaveSettings:()=>{},onTestEnabled:()=>{},onMustTestSettings:()=>{},onSynchronizeEnabled:()=>{},onMustSynchronizeSettings:()=>{},onMustEditSubscriptionKey:()=>{},onMustRefreshSubscriptionKey:()=>{},onResetActionsSettings:()=>{}});class W extends n.Component{constructor(e){super(e),this.state=this.defaultState}get defaultState(){return{selectedAdministration:H.NONE,can:{save:!1,test:!1,synchronize:!1},must:{save:!1,test:!1,synchronize:!1,editSubscriptionKey:!1,refreshSubscriptionKey:!1},onGetMfaRequested:this.onGetMfaRequested.bind(this),onSaveMfaRequested:this.onSaveMfaRequested.bind(this),onGetUsersDirectoryRequested:this.onGetUsersDirectoryRequested.bind(this),onUpdateUsersDirectoryRequested:this.onUpdateUsersDirectoryRequested.bind(this),onDeleteUsersDirectoryRequested:this.onDeleteUsersDirectoryRequested.bind(this),onTestUsersDirectoryRequested:this.onTestUsersDirectoryRequested.bind(this),onGetSimulateSynchronizeUsersDirectoryRequested:this.onGetSimulateSynchronizeUsersDirectoryRequested.bind(this),onGetSynchronizeUsersDirectoryRequested:this.onGetSynchronizeUsersDirectoryRequested.bind(this),onGetUsersRequested:this.onGetUsersRequested.bind(this),onGetEmailNotificationsRequested:this.onGetEmailNotificationsRequested.bind(this),onSaveEmailNotificationsRequested:this.onSaveEmailNotificationsRequested.bind(this),onUpdateSubscriptionKeyRequested:this.onUpdateSubscriptionKeyRequested.bind(this),onSaveLocaleRequested:this.onSaveLocaleRequested.bind(this),onSaveEnabled:this.handleSaveEnabled.bind(this),onMustSaveSettings:this.handleMustSaveSettings.bind(this),onTestEnabled:this.handleTestEnabled.bind(this),onMustTestSettings:this.handleMustTestSettings.bind(this),onSynchronizeEnabled:this.handleSynchronizeEnabled.bind(this),onMustSynchronizeSettings:this.handleMustSynchronizeSettings.bind(this),onMustEditSubscriptionKey:this.handleMustEditSubscriptionKey.bind(this),onMustRefreshSubscriptionKey:this.handleMustRefreshSubscriptionKey.bind(this),onResetActionsSettings:this.handleResetActionsSettings.bind(this)}}componentDidMount(){this.handleAdministrationMenuRouteChange()}async componentDidUpdate(e){await this.handleRouteChange(e.location)}async handleSaveEnabled(){await this.setState({can:{...this.state.can,save:!0}})}async handleMustSaveSettings(){await this.setState({must:{...this.state.must,save:!0}})}async handleTestEnabled(e){await this.setState({can:{...this.state.can,test:e}})}async handleMustTestSettings(){await this.setState({must:{...this.state.must,test:!0}})}async handleSynchronizeEnabled(e){await this.setState({can:{...this.state.can,synchronize:e}})}async handleMustSynchronizeSettings(){await this.setState({must:{...this.state.must,synchronize:!0}})}async handleMustEditSubscriptionKey(){await this.setState({must:{...this.state.must,editSubscriptionKey:!0}})}async handleMustRefreshSubscriptionKey(){await this.setState({must:{...this.state.must,refreshSubscriptionKey:!0}})}async handleResetActionsSettings(){await this.setState({must:{save:!1,test:!1,synchronize:!1,editSubscriptionKey:!1,refreshSubscriptionKey:!1}})}async handleRouteChange(e){this.props.location.key!==e.key&&await this.handleAdministrationMenuRouteChange()}async handleAdministrationMenuRouteChange(){const e=this.props.location.pathname.includes("mfa"),t=this.props.location.pathname.includes("users-directory"),s=this.props.location.pathname.includes("email-notification"),n=this.props.location.pathname.includes("subscription"),a=this.props.location.pathname.includes("internationalization");let r;e?r=H.MFA:t?r=H.USER_DIRECTORY:s?r=H.EMAIL_NOTIFICATION:n?r=H.SUBSCRIPTION:a&&(r=H.INTERNATIONALIZATION),await this.setState({selectedAdministration:r,can:{save:!1,test:!1,synchronize:!1},must:{save:!1,test:!1,synchronize:!1,editSubscriptionKey:!1,refreshSubscriptionKey:!1}})}async onGetMfaRequested(){const e=this.props.context.getApiClientOptions().setResourceName("mfa/settings");return new F(e).findAll()}async onSaveMfaRequested(e){const t=this.props.context.getApiClientOptions().setResourceName("mfa/settings"),s=new F(t);await s.create(e)}async onGetUsersDirectoryRequested(){const e=this.props.context.getApiClientOptions().setResourceName("directorysync/settings");return new F(e).findAll()}async onUpdateUsersDirectoryRequested(e){const t=this.props.context.getApiClientOptions().setResourceName("directorysync");return new F(t).update("settings",e)}async onDeleteUsersDirectoryRequested(){const e=this.props.context.getApiClientOptions().setResourceName("directorysync");return new F(e).delete("settings")}async onTestUsersDirectoryRequested(e){const t=this.props.context.getApiClientOptions().setResourceName("directorysync/settings/test");return new F(t).create(e)}async onGetSimulateSynchronizeUsersDirectoryRequested(){const e=this.props.context.getApiClientOptions().setResourceName("directorysync");return new F(e).get("synchronize/dry-run")}async onGetSynchronizeUsersDirectoryRequested(){const e=this.props.context.getApiClientOptions().setResourceName("directorysync/synchronize");return new F(e).create({})}async onGetUsersRequested(){const e=this.props.context.getApiClientOptions().setResourceName("users");return new F(e).findAll()}async onGetEmailNotificationsRequested(){const e=this.props.context.getApiClientOptions().setResourceName("settings/emails/notifications");return new F(e).findAll()}async onSaveEmailNotificationsRequested(e){const t=this.props.context.getApiClientOptions().setResourceName("settings/emails/notifications"),s=new F(t);await s.create(e)}async onUpdateSubscriptionKeyRequested(e){return this.props.context.port.request("passbolt.subscription.update",e)}async onSaveLocaleRequested(e){const t=this.props.context.getApiClientOptions().setResourceName("locale/settings");return new F(t).create({value:e})}render(){return n.createElement(K.Provider,{value:this.state},this.props.children)}}W.displayName="AdministrationWorkspaceContextProvider",W.propTypes={context:i().object,children:i().any,location:i().object,match:i().object,history:i().object,loadingContext:i().object};const B=(0,N.EN)(U((j=W,class extends n.Component{render(){return n.createElement(D.Consumer,null,(e=>n.createElement(j,I({loadingContext:e},this.props))))}})));var j;function $(e){return class extends n.Component{render(){return n.createElement(K.Consumer,null,(t=>n.createElement(e,L({administrationWorkspaceContext:t},this.props))))}}}const H={NONE:"NONE",MFA:"MFA",USER_DIRECTORY:"USER-DIRECTORY",EMAIL_NOTIFICATION:"EMAIL-NOTIFICATION",SUBSCRIPTION:"SUBSCRIPTION",INTERNATIONALIZATION:"INTERNATIONALIZATION"};function V(){return V=Object.assign||function(e){for(var t=1;tn.createElement(t,V({key:e,onClose:()=>this.close(e)},s)))),this.props.children)}}Z.propTypes={dialogContext:i().any,children:i().any};const Y=g(Z);function J(){return J=Object.assign||function(e){for(var t=1;tn.createElement(e.ContextualMenuComponent,J({key:t,hide:()=>this.handleHide(t)},e.componentProps)))))}}Q.propTypes={contextualMenuContext:i().any};const X=function(e){return class extends n.Component{render(){return n.createElement(b.Consumer,null,(t=>n.createElement(e,E({contextualMenuContext:t},this.props))))}}}(Q);function ee(){return ee=Object.assign||function(e){for(var t=1;t{},onGoToAdministrationMfaRequested:()=>{},onGoToAdministrationUsersDirectoryRequested:()=>{},onGoToAdministrationEmailNotificationsRequested:()=>{},onGoToAdministrationSubscriptionRequested:()=>{},onGoToPasswordsRequested:()=>{},onGoToUsersRequested:()=>{},onGoToUserSettingsProfileRequested:()=>{},onGoToUserSettingsPassphraseRequested:()=>{},onGoToUserSettingsSecurityTokenRequested:()=>{},onGoToUserSettingsThemeRequested:()=>{},onGoToUserSettingsMfaRequested:()=>{},onGoToUserSettingsKeysRequested:()=>{},onGoToUserSettingsMobileRequested:()=>{},onGoToNewTab:()=>{}});class se extends n.Component{constructor(e){super(e),this.state=this.defaultState}get defaultState(){return{onGoToNewTab:this.onGoToNewTab.bind(this),onGoToAdministrationRequested:this.onGoToAdministrationRequested.bind(this),onGoToAdministrationMfaRequested:this.onGoToAdministrationMfaRequested.bind(this),onGoToAdministrationUsersDirectoryRequested:this.onGoToAdministrationUsersDirectoryRequested.bind(this),onGoToAdministrationEmailNotificationsRequested:this.onGoToAdministrationEmailNotificationsRequested.bind(this),onGoToAdministrationSubscriptionRequested:this.onGoToAdministrationSubscriptionRequested.bind(this),onGoToAdministrationInternationalizationRequested:this.onGoToAdministrationInternationalizationRequested.bind(this),onGoToPasswordsRequested:this.onGoToPasswordsRequested.bind(this),onGoToUsersRequested:this.onGoToUsersRequested.bind(this),onGoToUserSettingsProfileRequested:this.onGoToUserSettingsProfileRequested.bind(this),onGoToUserSettingsPassphraseRequested:this.onGoToUserSettingsPassphraseRequested.bind(this),onGoToUserSettingsSecurityTokenRequested:this.onGoToUserSettingsSecurityTokenRequested.bind(this),onGoToUserSettingsThemeRequested:this.onGoToUserSettingsThemeRequested.bind(this),onGoToUserSettingsMfaRequested:this.onGoToUserSettingsMfaRequested.bind(this),onGoToUserSettingsKeysRequested:this.onGoToUserSettingsKeysRequested.bind(this),onGoToUserSettingsMobileRequested:this.onGoToUserSettingsMobileRequested.bind(this)}}async goTo(e,t){if(e===this.props.context.name)await this.props.history.push({pathname:t});else{const e=`${this.props.context.userSettings?this.props.context.userSettings.getTrustedDomain():this.props.context.trustedDomain}${t}`;window.open(e,"_parent","noopener,noreferrer")}}onGoToNewTab(e){window.open(e,"_blank","noopener,noreferrer")}async onGoToAdministrationRequested(){let e="/app/administration/email-notification";this.isMfaEnabled?e="/app/administration/mfa":this.isUserDirectoryEnabled&&(e="/app/administration/users-directory"),await this.goTo("api",e)}async onGoToAdministrationMfaRequested(){await this.goTo("api","/app/administration/mfa")}async onGoToAdministrationUsersDirectoryRequested(){await this.goTo("api","/app/administration/users-directory")}async onGoToAdministrationEmailNotificationsRequested(){await this.goTo("api","/app/administration/email-notification")}async onGoToAdministrationSubscriptionRequested(){await this.goTo("browser-extension","/app/administration/subscription")}async onGoToAdministrationInternationalizationRequested(){await this.goTo("api","/app/administration/internationalization")}get isMfaEnabled(){const e=this.props.context.siteSettings;return e&&e.canIUse("multiFactorAuthentication")}get isUserDirectoryEnabled(){const e=this.props.context.siteSettings;return e&&e.canIUse("directorySync")}async onGoToPasswordsRequested(){await this.goTo("browser-extension","/app/passwords")}async onGoToUsersRequested(){await this.goTo("browser-extension","/app/users")}async onGoToUserSettingsProfileRequested(){await this.goTo("browser-extension","/app/settings/profile")}async onGoToUserSettingsPassphraseRequested(){await this.goTo("browser-extension","/app/settings/passphrase")}async onGoToUserSettingsSecurityTokenRequested(){await this.goTo("browser-extension","/app/settings/security-token")}async onGoToUserSettingsThemeRequested(){await this.goTo("browser-extension","/app/settings/theme")}async onGoToUserSettingsMfaRequested(){await this.goTo("api","/app/settings/mfa")}async onGoToUserSettingsKeysRequested(){await this.goTo("browser-extension","/app/settings/keys")}async onGoToUserSettingsMobileRequested(){await this.goTo("browser-extension","/app/settings/mobile")}render(){return n.createElement(te.Provider,{value:this.state},this.props.children)}}se.displayName="NavigationContextProvider",se.propTypes={context:i().object,children:i().any,location:i().object,match:i().object,history:i().object};const ne=(0,N.EN)(U(se));function ae(e){return class extends n.Component{render(){return n.createElement(te.Consumer,null,(t=>n.createElement(e,ee({navigationContext:t},this.props))))}}}class re extends n.Component{isSelected(e){let t=!1;return"passwords"===e?t=/^\/app\/(passwords|folders)/.test(this.props.location.pathname):"users"===e?t=/^\/app\/(users|groups)/.test(this.props.location.pathname):"administration"===e&&(t=/^\/app\/administration/.test(this.props.location.pathname)),t}isLoggedInUserAdmin(){return this.props.context.loggedInUser&&"admin"===this.props.context.loggedInUser.role.name}get translate(){return this.props.t}render(){return n.createElement("nav",null,n.createElement("div",{className:"primary navigation top"},n.createElement("ul",null,n.createElement("li",{key:"password"},n.createElement("div",{className:"row "+(this.isSelected("passwords")?"selected":"")},n.createElement("div",{className:"main-cell-wrapper"},n.createElement("div",{className:"main-cell"},n.createElement("a",{onClick:this.props.navigationContext.onGoToPasswordsRequested,role:"button"},n.createElement("span",null,n.createElement(f.c,null,"passwords"))))))),n.createElement("li",{key:"users"},n.createElement("div",{className:"row "+(this.isSelected("users")?"selected":"")},n.createElement("div",{className:"main-cell-wrapper"},n.createElement("div",{className:"main-cell"},n.createElement("a",{onClick:this.props.navigationContext.onGoToUsersRequested,role:"button"},n.createElement("span",null,n.createElement(f.c,null,"users"))))))),this.isLoggedInUserAdmin()&&n.createElement("li",{key:"administration"},n.createElement("div",{className:"row "+(this.isSelected("administration")?"selected":"")},n.createElement("div",{className:"main-cell-wrapper"},n.createElement("div",{className:"main-cell"},n.createElement("a",{onClick:this.props.navigationContext.onGoToAdministrationRequested,role:"button"},n.createElement("span",null,n.createElement(f.c,null,"administration"))))))),n.createElement("li",{key:"help"},n.createElement("div",{className:"row"},n.createElement("div",{className:"main-cell-wrapper"},n.createElement("div",{className:"main-cell"},n.createElement("a",{href:"https://help.passbolt.com",role:"button",target:"_blank",rel:"noopener noreferrer"},n.createElement("span",null,n.createElement(f.c,null,"help"))))))),n.createElement("li",{key:"logout",className:"right"},n.createElement("div",{className:"row"},n.createElement("div",{className:"main-cell-wrapper"},n.createElement("div",{className:"main-cell"},n.createElement("a",{role:"button",onClick:this.props.context.onLogoutRequested},n.createElement("span",null,n.createElement(f.c,null,"sign out"))))))))))}}re.propTypes={context:i().object,navigationContext:i().any,history:i().object,location:i().object,t:i().func};const ie=U((0,N.EN)(ae((0,v.Z)("common")(re))));class le extends n.Component{render(){return n.createElement("div",{className:"col1"},n.createElement("div",{className:"logo no-img"},n.createElement("h1",null,n.createElement("span",null,"Passbolt"))))}}const oe=le;class ce extends n.Component{constructor(e){super(e),this.state=this.getDefaultState(),this.bindCallbacks()}getDefaultState(){return{error:!1}}bindCallbacks(){this.handleError=this.handleError.bind(this)}propsHasUrl(){return this.props.user&&this.props.user.profile&&this.props.user.profile.avatar&&this.props.user.profile.avatar.url&&this.props.user.profile.avatar.url.medium}propsUrlHasProtocol(){return this.props.user.profile.avatar.url.medium.startsWith("https://")||this.props.user.profile.avatar.url.medium.startsWith("http://")}formatUrl(e){return`${this.props.baseUrl}/${e}`}getDefaultAvatarUrl(){return`${this.props.baseUrl}/img/avatar/user.png`}getAvatarSrc(){return!this.state.error&&this.propsHasUrl()?this.propsUrlHasProtocol()?this.props.user.profile.avatar.url.medium:this.formatUrl(this.props.user.profile.avatar.url.medium):this.getDefaultAvatarUrl()}handleError(){console.error(`Could not load avatar image url: ${this.getAvatarSrc()}`),this.setState({error:!0})}getAltText(){return this.props.user&&this.props.user.first_name&&this.props.user.last_name?`Avatar of user ${this.props.user.first_name} ${this.props.user.last_name}.`:"..."}render(){return n.createElement("div",{className:this.props.className},!this.state.error&&n.createElement("img",{src:this.getAvatarSrc(),onError:this.handleError,alt:this.getAltText()}),this.state.error&&n.createElement("img",{src:this.getDefaultAvatarUrl(),alt:this.getAltText()}))}}ce.defaultProps={className:"avatar user-avatar"},ce.propTypes={baseUrl:i().string,user:i().object,className:i().string};const he=ce;class de extends n.Component{getClassName(){let e=`svg-icon ${this.props.name}`;return this.props.big&&(e+=" icon-only"),this.props.baseline&&(e+=" baseline"),this.props.dim&&(e+=" dim"),e}render(){return n.createElement("span",{className:this.getClassName(),onClick:this.props.onClick},"add"===this.props.name&&n.createElement("svg",{xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false","data-prefix":"fas","data-icon":"plus-circle",role:"img",viewBox:"0 0 512 512"},n.createElement("path",{d:"M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm144 276c0 6.6-5.4 12-12 12h-92v92c0 6.6-5.4 12-12 12h-56c-6.6 0-12-5.4-12-12v-92h-92c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h92v-92c0-6.6 5.4-12 12-12h56c6.6 0 12 5.4 12 12v92h92c6.6 0 12 5.4 12 12v56z"})),"ban"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1440 893q0-161-87-295l-754 753q137 89 297 89 111 0 211.5-43.5t173.5-116.5 116-174.5 43-212.5zm-999 299l755-754q-135-91-300-91-148 0-273 73t-198 199-73 274q0 162 89 299zm1223-299q0 157-61 300t-163.5 246-245 164-298.5 61-298.5-61-245-164-163.5-246-61-300 61-299.5 163.5-245.5 245-164 298.5-61 298.5 61 245 164 163.5 245.5 61 299.5z"})),"camera"===this.props.name&&n.createElement("svg",{width:"2048",height:"1792",viewBox:"0 0 2048 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1024 672q119 0 203.5 84.5t84.5 203.5-84.5 203.5-203.5 84.5-203.5-84.5-84.5-203.5 84.5-203.5 203.5-84.5zm704-416q106 0 181 75t75 181v896q0 106-75 181t-181 75h-1408q-106 0-181-75t-75-181v-896q0-106 75-181t181-75h224l51-136q19-49 69.5-84.5t103.5-35.5h512q53 0 103.5 35.5t69.5 84.5l51 136h224zm-704 1152q185 0 316.5-131.5t131.5-316.5-131.5-316.5-316.5-131.5-316.5 131.5-131.5 316.5 131.5 316.5 316.5 131.5z"})),"caret-right"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1152 896q0 26-19 45l-448 448q-19 19-45 19t-45-19-19-45v-896q0-26 19-45t45-19 45 19l448 448q19 19 19 45z"})),"caret-down"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1408 704q0 26-19 45l-448 448q-19 19-45 19t-45-19l-448-448q-19-19-19-45t19-45 45-19h896q26 0 45 19t19 45z"})),"caret-up"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1408 1216q0 26-19 45t-45 19h-896q-26 0-45-19t-19-45 19-45l448-448q19-19 45-19t45 19l448 448q19 19 19 45z"})),"chevron-left"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1427 301l-531 531 531 531q19 19 19 45t-19 45l-166 166q-19 19-45 19t-45-19l-742-742q-19-19-19-45t19-45l742-742q19-19 45-19t45 19l166 166q19 19 19 45t-19 45z"})),"chevron-right"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1363 877l-742 742q-19 19-45 19t-45-19l-166-166q-19-19-19-45t19-45l531-531-531-531q-19-19-19-45t19-45l166-166q19-19 45-19t45 19l742 742q19 19 19 45t-19 45z"})),"close"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1490 1322q0 40-28 68l-136 136q-28 28-68 28t-68-28l-294-294-294 294q-28 28-68 28t-68-28l-136-136q-28-28-28-68t28-68l294-294-294-294q-28-28-28-68t28-68l136-136q28-28 68-28t68 28l294 294 294-294q28-28 68-28t68 28l136 136q28 28 28 68t-28 68l-294 294 294 294q28 28 28 68z"})),"close-circle"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1277 1122q0-26-19-45l-181-181 181-181q19-19 19-45 0-27-19-46l-90-90q-19-19-46-19-26 0-45 19l-181 181-181-181q-19-19-45-19-27 0-46 19l-90 90q-19 19-19 46 0 26 19 45l181 181-181 181q-19 19-19 45 0 27 19 46l90 90q19 19 46 19 26 0 45-19l181-181 181 181q19 19 45 19 27 0 46-19l90-90q19-19 19-46zm387-226q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"})),"cog"===this.props.name&&n.createElement("svg",{xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false","data-prefix":"fas","data-icon":"cog",viewBox:"0 0 512 512"},n.createElement("path",{fill:"currentColor",d:"M487.4 315.7l-42.6-24.6c4.3-23.2 4.3-47 0-70.2l42.6-24.6c4.9-2.8 7.1-8.6 5.5-14-11.1-35.6-30-67.8-54.7-94.6-3.8-4.1-10-5.1-14.8-2.3L380.8 110c-17.9-15.4-38.5-27.3-60.8-35.1V25.8c0-5.6-3.9-10.5-9.4-11.7-36.7-8.2-74.3-7.8-109.2 0-5.5 1.2-9.4 6.1-9.4 11.7V75c-22.2 7.9-42.8 19.8-60.8 35.1L88.7 85.5c-4.9-2.8-11-1.9-14.8 2.3-24.7 26.7-43.6 58.9-54.7 94.6-1.7 5.4.6 11.2 5.5 14L67.3 221c-4.3 23.2-4.3 47 0 70.2l-42.6 24.6c-4.9 2.8-7.1 8.6-5.5 14 11.1 35.6 30 67.8 54.7 94.6 3.8 4.1 10 5.1 14.8 2.3l42.6-24.6c17.9 15.4 38.5 27.3 60.8 35.1v49.2c0 5.6 3.9 10.5 9.4 11.7 36.7 8.2 74.3 7.8 109.2 0 5.5-1.2 9.4-6.1 9.4-11.7v-49.2c22.2-7.9 42.8-19.8 60.8-35.1l42.6 24.6c4.9 2.8 11 1.9 14.8-2.3 24.7-26.7 43.6-58.9 54.7-94.6 1.5-5.5-.7-11.3-5.6-14.1zM256 336c-44.1 0-80-35.9-80-80s35.9-80 80-80 80 35.9 80 80-35.9 80-80 80z"})),"copy-to-clipboard"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M768 1664h896v-640h-416q-40 0-68-28t-28-68v-416h-384v1152zm256-1440v-64q0-13-9.5-22.5t-22.5-9.5h-704q-13 0-22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h704q13 0 22.5-9.5t9.5-22.5zm256 672h299l-299-299v299zm512 128v672q0 40-28 68t-68 28h-960q-40 0-68-28t-28-68v-160h-544q-40 0-68-28t-28-68v-1344q0-40 28-68t68-28h1088q40 0 68 28t28 68v328q21 13 36 28l408 408q28 28 48 76t20 88z"})),"download"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1344 1344q0-26-19-45t-45-19-45 19-19 45 19 45 45 19 45-19 19-45zm256 0q0-26-19-45t-45-19-45 19-19 45 19 45 45 19 45-19 19-45zm128-224v320q0 40-28 68t-68 28h-1472q-40 0-68-28t-28-68v-320q0-40 28-68t68-28h465l135 136q58 56 136 56t136-56l136-136h464q40 0 68 28t28 68zm-325-569q17 41-14 70l-448 448q-18 19-45 19t-45-19l-448-448q-31-29-14-70 17-39 59-39h256v-448q0-26 19-45t45-19h256q26 0 45 19t19 45v448h256q42 0 59 39z"})),"edit"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M888 1184l116-116-152-152-116 116v56h96v96h56zm440-720q-16-16-33 1l-350 350q-17 17-1 33t33-1l350-350q17-17 1-33zm80 594v190q0 119-84.5 203.5t-203.5 84.5h-832q-119 0-203.5-84.5t-84.5-203.5v-832q0-119 84.5-203.5t203.5-84.5h832q63 0 117 25 15 7 18 23 3 17-9 29l-49 49q-14 14-32 8-23-6-45-6h-832q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113v-126q0-13 9-22l64-64q15-15 35-7t20 29zm-96-738l288 288-672 672h-288v-288zm444 132l-92 92-288-288 92-92q28-28 68-28t68 28l152 152q28 28 28 68t-28 68z"})),"envelope"===this.props.name&&n.createElement("svg",{width:"512",height:"512",viewBox:"0 0 512 512",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{fill:"currentColor",d:"M464 64H48C21.49 64 0 85.49 0 112v288c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V112c0-26.51-21.49-48-48-48zm0 48v40.805c-22.422 18.259-58.168 46.651-134.587 106.49-16.841 13.247-50.201 45.072-73.413 44.701-23.208.375-56.579-31.459-73.413-44.701C106.18 199.465 70.425 171.067 48 152.805V112h416zM48 400V214.398c22.914 18.251 55.409 43.862 104.938 82.646 21.857 17.205 60.134 55.186 103.062 54.955 42.717.231 80.509-37.199 103.053-54.947 49.528-38.783 82.032-64.401 104.947-82.653V400H48z"})," "),"eye-open"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1664 960q-152-236-381-353 61 104 61 225 0 185-131.5 316.5t-316.5 131.5-316.5-131.5-131.5-316.5q0-121 61-225-229 117-381 353 133 205 333.5 326.5t434.5 121.5 434.5-121.5 333.5-326.5zm-720-384q0-20-14-34t-34-14q-125 0-214.5 89.5t-89.5 214.5q0 20 14 34t34 14 34-14 14-34q0-86 61-147t147-61q20 0 34-14t14-34zm848 384q0 34-20 69-140 230-376.5 368.5t-499.5 138.5-499.5-139-376.5-368q-20-35-20-69t20-69q140-229 376.5-368t499.5-139 499.5 139 376.5 368q20 35 20 69z"})),"eye-close"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M555 1335l78-141q-87-63-136-159t-49-203q0-121 61-225-229 117-381 353 167 258 427 375zm389-759q0-20-14-34t-34-14q-125 0-214.5 89.5t-89.5 214.5q0 20 14 34t34 14 34-14 14-34q0-86 61-147t147-61q20 0 34-14t14-34zm363-191q0 7-1 9-106 189-316 567t-315 566l-49 89q-10 16-28 16-12 0-134-70-16-10-16-28 0-12 44-87-143-65-263.5-173t-208.5-245q-20-31-20-69t20-69q153-235 380-371t496-136q89 0 180 17l54-97q10-16 28-16 5 0 18 6t31 15.5 33 18.5 31.5 18.5 19.5 11.5q16 10 16 27zm37 447q0 139-79 253.5t-209 164.5l280-502q8 45 8 84zm448 128q0 35-20 69-39 64-109 145-150 172-347.5 267t-419.5 95l74-132q212-18 392.5-137t301.5-307q-115-179-282-294l63-112q95 64 182.5 153t144.5 184q20 34 20 69z"})),"external-link"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1408 928v-480q0-26-19-45t-45-19h-480q-42 0-59 39-17 41 14 70l144 144-534 534q-19 19-19 45t19 45l102 102q19 19 45 19t45-19l534-534 144 144q18 19 45 19 12 0 25-5 39-17 39-59zm256-512v960q0 119-84.5 203.5t-203.5 84.5h-960q-119 0-203.5-84.5t-84.5-203.5v-960q0-119 84.5-203.5t203.5-84.5h960q119 0 203.5 84.5t84.5 203.5z"})),"folder"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1728 608v704q0 92-66 158t-158 66h-1216q-92 0-158-66t-66-158v-960q0-92 66-158t158-66h320q92 0 158 66t66 158v32h672q92 0 158 66t66 158z"})),"folder-shared"===this.props.name&&n.createElement("svg",{viewBox:"2 2 20 20",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M20 6h-8l-2-2H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm-5 3c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm4 8h-8v-1c0-1.33 2.67-2 4-2s4 .67 4 2v1z"})),"heart"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M896 1664q-26 0-44-18l-624-602q-10-8-27.5-26t-55.5-65.5-68-97.5-53.5-121-23.5-138q0-220 127-344t351-124q62 0 126.5 21.5t120 58 95.5 68.5 76 68q36-36 76-68t95.5-68.5 120-58 126.5-21.5q224 0 351 124t127 344q0 221-229 450l-623 600q-18 18-44 18z"})),"heart-o"===this.props.name&&n.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 512 512"},n.createElement("path",{d:"M458.4 64.3C400.6 15.7 311.3 23 256 79.3 200.7 23 111.4 15.6 53.6 64.3-21.6 127.6-10.6 230.8 43 285.5l175.4 178.7c10 10.2 23.4 15.9 37.6 15.9 14.3 0 27.6-5.6 37.6-15.8L469 285.6c53.5-54.7 64.7-157.9-10.6-221.3zm-23.6 187.5L259.4 430.5c-2.4 2.4-4.4 2.4-6.8 0L77.2 251.8c-36.5-37.2-43.9-107.6 7.3-150.7 38.9-32.7 98.9-27.8 136.5 10.5l35 35.7 35-35.7c37.8-38.5 97.8-43.2 136.5-10.6 51.1 43.1 43.5 113.9 7.3 150.8z"})),"heartbeat"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1280 1024h305q-5 6-10 10.5t-9 7.5l-3 4-623 600q-18 18-44 18t-44-18l-624-602q-5-2-21-20h369q22 0 39.5-13.5t22.5-34.5l70-281 190 667q6 20 23 33t39 13q21 0 38-13t23-33l146-485 56 112q18 35 57 35zm512-428q0 145-103 300h-369l-111-221q-8-17-25.5-27t-36.5-8q-45 5-56 46l-129 430-196-686q-6-20-23.5-33t-39.5-13-39 13.5-22 34.5l-116 464h-423q-103-155-103-300 0-220 127-344t351-124q62 0 126.5 21.5t120 58 95.5 68.5 76 68q36-36 76-68t95.5-68.5 120-58 126.5-21.5q224 0 351 124t127 344z"})),"info-circle"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1152 1376v-160q0-14-9-23t-23-9h-96v-512q0-14-9-23t-23-9h-320q-14 0-23 9t-9 23v160q0 14 9 23t23 9h96v320h-96q-14 0-23 9t-9 23v160q0 14 9 23t23 9h448q14 0 23-9t9-23zm-128-896v-160q0-14-9-23t-23-9h-192q-14 0-23 9t-9 23v160q0 14 9 23t23 9h192q14 0 23-9t9-23zm640 416q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"})),"key"===this.props.name&&n.createElement("svg",{width:"512",height:"512",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 512 512"},n.createElement("path",{fill:"currentColor",d:"M512 176.001C512 273.203 433.202 352 336 352c-11.22 0-22.19-1.062-32.827-3.069l-24.012 27.014A23.999 23.999 0 0 1 261.223 384H224v40c0 13.255-10.745 24-24 24h-40v40c0 13.255-10.745 24-24 24H24c-13.255 0-24-10.745-24-24v-78.059c0-6.365 2.529-12.47 7.029-16.971l161.802-161.802C163.108 213.814 160 195.271 160 176 160 78.798 238.797.001 335.999 0 433.488-.001 512 78.511 512 176.001zM336 128c0 26.51 21.49 48 48 48s48-21.49 48-48-21.49-48-48-48-48 21.49-48 48z"})),"link"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1520 1216q0-40-28-68l-208-208q-28-28-68-28-42 0-72 32 3 3 19 18.5t21.5 21.5 15 19 13 25.5 3.5 27.5q0 40-28 68t-68 28q-15 0-27.5-3.5t-25.5-13-19-15-21.5-21.5-18.5-19q-33 31-33 73 0 40 28 68l206 207q27 27 68 27 40 0 68-26l147-146q28-28 28-67zm-703-705q0-40-28-68l-206-207q-28-28-68-28-39 0-68 27l-147 146q-28 28-28 67 0 40 28 68l208 208q27 27 68 27 42 0 72-31-3-3-19-18.5t-21.5-21.5-15-19-13-25.5-3.5-27.5q0-40 28-68t68-28q15 0 27.5 3.5t25.5 13 19 15 21.5 21.5 18.5 19q33-31 33-73zm895 705q0 120-85 203l-147 146q-83 83-203 83-121 0-204-85l-206-207q-83-83-83-203 0-123 88-209l-88-88q-86 88-208 88-120 0-204-84l-208-208q-84-84-84-204t85-203l147-146q83-83 203-83 121 0 204 85l206 207q83 83 83 203 0 123-88 209l88 88q86-88 208-88 120 0 204 84l208 208q84 84 84 204z"})),"lock"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M640 768h512v-192q0-106-75-181t-181-75-181 75-75 181v192zm832 96v576q0 40-28 68t-68 28h-960q-40 0-68-28t-28-68v-576q0-40 28-68t68-28h32v-192q0-184 132-316t316-132 316 132 132 316v192h32q40 0 68 28t28 68z"})),"lock-open"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1376 768q40 0 68 28t28 68v576q0 40-28 68t-68 28h-960q-40 0-68-28t-28-68v-576q0-40 28-68t68-28h32v-320q0-185 131.5-316.5t316.5-131.5 316.5 131.5 131.5 316.5q0 26-19 45t-45 19h-64q-26 0-45-19t-19-45q0-106-75-181t-181-75-181 75-75 181v320h736z"})),"magic-wand"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1254 581l293-293-107-107-293 293zm447-293q0 27-18 45l-1286 1286q-18 18-45 18t-45-18l-198-198q-18-18-18-45t18-45l1286-1286q18-18 45-18t45 18l198 198q18 18 18 45zm-1351-190l98 30-98 30-30 98-30-98-98-30 98-30 30-98zm350 162l196 60-196 60-60 196-60-196-196-60 196-60 60-196zm930 478l98 30-98 30-30 98-30-98-98-30 98-30 30-98zm-640-640l98 30-98 30-30 98-30-98-98-30 98-30 30-98z"})),"plus-circle"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1344 960v-128q0-26-19-45t-45-19h-256v-256q0-26-19-45t-45-19h-128q-26 0-45 19t-19 45v256h-256q-26 0-45 19t-19 45v128q0 26 19 45t45 19h256v256q0 26 19 45t45 19h128q26 0 45-19t19-45v-256h256q26 0 45-19t19-45zm320-64q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"})),"plus-square"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 448 512",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48zm-32 252c0 6.6-5.4 12-12 12h-92v92c0 6.6-5.4 12-12 12h-56c-6.6 0-12-5.4-12-12v-92H92c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h92v-92c0-6.6 5.4-12 12-12h56c6.6 0 12 5.4 12 12v92h92c6.6 0 12 5.4 12 12v56z"})),"filter"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 512 512",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M487.976 0H24.028C2.71 0-8.047 25.866 7.058 40.971L192 225.941V432c0 7.831 3.821 15.17 10.237 19.662l80 55.98C298.02 518.69 320 507.493 320 487.98V225.941l184.947-184.97C520.021 25.896 509.338 0 487.976 0z"})),"life-ring"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 512 512",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M256 504c136.967 0 248-111.033 248-248S392.967 8 256 8 8 119.033 8 256s111.033 248 248 248zm-103.398-76.72l53.411-53.411c31.806 13.506 68.128 13.522 99.974 0l53.411 53.411c-63.217 38.319-143.579 38.319-206.796 0zM336 256c0 44.112-35.888 80-80 80s-80-35.888-80-80 35.888-80 80-80 80 35.888 80 80zm91.28 103.398l-53.411-53.411c13.505-31.806 13.522-68.128 0-99.974l53.411-53.411c38.319 63.217 38.319 143.579 0 206.796zM359.397 84.72l-53.411 53.411c-31.806-13.505-68.128-13.522-99.973 0L152.602 84.72c63.217-38.319 143.579-38.319 206.795 0zM84.72 152.602l53.411 53.411c-13.506 31.806-13.522 68.128 0 99.974L84.72 359.398c-38.319-63.217-38.319-143.579 0-206.796z"})),"plug"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 384 512",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M320,32a32,32,0,0,0-64,0v96h64Zm48,128H16A16,16,0,0,0,0,176v32a16,16,0,0,0,16,16H32v32A160.07,160.07,0,0,0,160,412.8V512h64V412.8A160.07,160.07,0,0,0,352,256V224h16a16,16,0,0,0,16-16V176A16,16,0,0,0,368,160ZM128,32a32,32,0,0,0-64,0v96h64Z"})),"printer"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M448 1536h896v-256h-896v256zm0-640h896v-384h-160q-40 0-68-28t-28-68v-160h-640v640zm1152 64q0-26-19-45t-45-19-45 19-19 45 19 45 45 19 45-19 19-45zm128 0v416q0 13-9.5 22.5t-22.5 9.5h-224v160q0 40-28 68t-68 28h-960q-40 0-68-28t-28-68v-160h-224q-13 0-22.5-9.5t-9.5-22.5v-416q0-79 56.5-135.5t135.5-56.5h64v-544q0-40 28-68t68-28h672q40 0 88 20t76 48l152 152q28 28 48 76t20 88v256h64q79 0 135.5 56.5t56.5 135.5z"})),"refresh"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 512 512",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M370.72 133.28C339.458 104.008 298.888 87.962 255.848 88c-77.458.068-144.328 53.178-162.791 126.85-1.344 5.363-6.122 9.15-11.651 9.15H24.103c-7.498 0-13.194-6.807-11.807-14.176C33.933 94.924 134.813 8 256 8c66.448 0 126.791 26.136 171.315 68.685L463.03 40.97C478.149 25.851 504 36.559 504 57.941V192c0 13.255-10.745 24-24 24H345.941c-21.382 0-32.09-25.851-16.971-40.971l41.75-41.749zM32 296h134.059c21.382 0 32.09 25.851 16.971 40.971l-41.75 41.75c31.262 29.273 71.835 45.319 114.876 45.28 77.418-.07 144.315-53.144 162.787-126.849 1.344-5.363 6.122-9.15 11.651-9.15h57.304c7.498 0 13.194 6.807 11.807 14.176C478.067 417.076 377.187 504 256 504c-66.448 0-126.791-26.136-171.315-68.685L48.97 471.03C33.851 486.149 8 475.441 8 454.059V320c0-13.255 10.745-24 24-24z"})),"question-circle"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1024 1376v-192q0-14-9-23t-23-9h-192q-14 0-23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23-9t9-23zm256-672q0-88-55.5-163t-138.5-116-170-41q-243 0-371 213-15 24 8 42l132 100q7 6 19 6 16 0 25-12 53-68 86-92 34-24 86-24 48 0 85.5 26t37.5 59q0 38-20 61t-68 45q-63 28-115.5 86.5t-52.5 125.5v36q0 14 9 23t23 9h192q14 0 23-9t9-23q0-19 21.5-49.5t54.5-49.5q32-18 49-28.5t46-35 44.5-48 28-60.5 12.5-81zm384 192q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"})),"search"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1216 832q0-185-131.5-316.5t-316.5-131.5-316.5 131.5-131.5 316.5 131.5 316.5 316.5 131.5 316.5-131.5 131.5-316.5zm512 832q0 52-38 90t-90 38q-54 0-90-38l-343-342q-179 124-399 124-143 0-273.5-55.5t-225-150-150-225-55.5-273.5 55.5-273.5 150-225 225-150 273.5-55.5 273.5 55.5 225 150 150 225 55.5 273.5q0 220-124 399l343 343q37 37 37 90z"})),"share"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1764 11q33 24 27 64l-256 1536q-5 29-32 45-14 8-31 8-11 0-24-5l-453-185-242 295q-18 23-49 23-13 0-22-4-19-7-30.5-23.5t-11.5-36.5v-349l864-1059-1069 925-395-162q-37-14-40-55-2-40 32-59l1664-960q15-9 32-9 20 0 36 11z"})),"star"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1728 647q0 22-26 48l-363 354 86 500q1 7 1 20 0 21-10.5 35.5t-30.5 14.5q-19 0-40-12l-449-236-449 236q-22 12-40 12-21 0-31.5-14.5t-10.5-35.5q0-6 2-20l86-500-364-354q-25-27-25-48 0-37 56-46l502-73 225-455q19-41 49-41t49 41l225 455 502 73q56 9 56 46z"})),"save"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 448 512",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M433.941 129.941l-83.882-83.882A48 48 0 0 0 316.118 32H48C21.49 32 0 53.49 0 80v352c0 26.51 21.49 48 48 48h352c26.51 0 48-21.49 48-48V163.882a48 48 0 0 0-14.059-33.941zM272 80v80H144V80h128zm122 352H54a6 6 0 0 1-6-6V86a6 6 0 0 1 6-6h42v104c0 13.255 10.745 24 24 24h176c13.255 0 24-10.745 24-24V83.882l78.243 78.243a6 6 0 0 1 1.757 4.243V426a6 6 0 0 1-6 6zM224 232c-48.523 0-88 39.477-88 88s39.477 88 88 88 88-39.477 88-88-39.477-88-88-88zm0 128c-22.056 0-40-17.944-40-40s17.944-40 40-40 40 17.944 40 40-17.944 40-40 40z"})),"trash"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M704 1376v-704q0-14-9-23t-23-9h-64q-14 0-23 9t-9 23v704q0 14 9 23t23 9h64q14 0 23-9t9-23zm256 0v-704q0-14-9-23t-23-9h-64q-14 0-23 9t-9 23v704q0 14 9 23t23 9h64q14 0 23-9t9-23zm256 0v-704q0-14-9-23t-23-9h-64q-14 0-23 9t-9 23v704q0 14 9 23t23 9h64q14 0 23-9t9-23zm-544-992h448l-48-117q-7-9-17-11h-317q-10 2-17 11zm928 32v64q0 14-9 23t-23 9h-96v948q0 83-47 143.5t-113 60.5h-832q-66 0-113-58.5t-47-141.5v-952h-96q-14 0-23-9t-9-23v-64q0-14 9-23t23-9h309l70-167q15-37 54-63t79-26h320q40 0 79 26t54 63l70 167h309q14 0 23 9t9 23z"})),"upload"===this.props.name&&n.createElement("svg",{width:"2048",height:"1792",viewBox:"0 0 2048 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1344 864q0-14-9-23l-352-352q-9-9-23-9t-23 9l-351 351q-10 12-10 24 0 14 9 23t23 9h224v352q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5-9.5t9.5-22.5v-352h224q13 0 22.5-9.5t9.5-22.5zm640 288q0 159-112.5 271.5t-271.5 112.5h-1088q-185 0-316.5-131.5t-131.5-316.5q0-130 70-240t188-165q-2-30-2-43 0-212 150-362t362-150q156 0 285.5 87t188.5 231q71-62 166-62 106 0 181 75t75 181q0 76-41 138 130 31 213.5 135.5t83.5 238.5z"})),"upload-a"===this.props.name&&n.createElement("svg",{"aria-hidden":"true",focusable:"false","data-prefix":"fas","data-icon":"upload",className:"svg-inline--fa fa-upload fa-w-16",role:"img",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 512 512"},n.createElement("path",{fill:"currentColor",d:"M296 384h-80c-13.3 0-24-10.7-24-24V192h-87.7c-17.8 0-26.7-21.5-14.1-34.1L242.3 5.7c7.5-7.5 19.8-7.5 27.3 0l152.2 152.2c12.6 12.6 3.7 34.1-14.1 34.1H320v168c0 13.3-10.7 24-24 24zm216-8v112c0 13.3-10.7 24-24 24H24c-13.3 0-24-10.7-24-24V376c0-13.3 10.7-24 24-24h136v8c0 30.9 25.1 56 56 56h80c30.9 0 56-25.1 56-56v-8h136c13.3 0 24 10.7 24 24zm-124 88c0-11-9-20-20-20s-20 9-20 20 9 20 20 20 20-9 20-20zm64 0c0-11-9-20-20-20s-20 9-20 20 9 20 20 20 20-9 20-20z"})),"user"===this.props.name&&n.createElement("svg",{width:"448",height:"512",viewBox:"0 0 448 512",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{fill:"currentColor",d:"M224 256c70.7 0 128-57.3 128-128S294.7 0 224 0 96 57.3 96 128s57.3 128 128 128zm89.6 32h-16.7c-22.2 10.2-46.9 16-72.9 16s-50.6-5.8-72.9-16h-16.7C60.2 288 0 348.2 0 422.4V464c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48v-41.6c0-74.2-60.2-134.4-134.4-134.4z",className:""})),"users"===this.props.name&&n.createElement("svg",{width:"640",height:"512",viewBox:"0 0 640 512",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{fill:"currentColor",d:"M96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm448 0c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm32 32h-64c-17.6 0-33.5 7.1-45.1 18.6 40.3 22.1 68.9 62 75.1 109.4h66c17.7 0 32-14.3 32-32v-32c0-35.3-28.7-64-64-64zm-256 0c61.9 0 112-50.1 112-112S381.9 32 320 32 208 82.1 208 144s50.1 112 112 112zm76.8 32h-8.3c-20.8 10-43.9 16-68.5 16s-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h288c26.5 0 48-21.5 48-48v-28.8c0-63.6-51.6-115.2-115.2-115.2zm-223.7-13.4C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z"})),"warning"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1024 1375v-190q0-14-9.5-23.5t-22.5-9.5h-192q-13 0-22.5 9.5t-9.5 23.5v190q0 14 9.5 23.5t22.5 9.5h192q13 0 22.5-9.5t9.5-23.5zm-2-374l18-459q0-12-10-19-13-11-24-11h-220q-11 0-24 11-10 7-10 21l17 457q0 10 10 16.5t24 6.5h185q14 0 23.5-6.5t10.5-16.5zm-14-934l768 1408q35 63-2 126-17 29-46.5 46t-63.5 17h-1536q-34 0-63.5-17t-46.5-46q-37-63-2-126l768-1408q17-31 47-49t65-18 65 18 47 49z"})))}}de.defaultProps={big:!1,baseline:!1,dim:!1,onClick:()=>{}},de.propTypes={name:i().string,big:i().bool,dim:i().bool,baseline:i().bool,onClick:i().func};const ue=de;class pe extends n.Component{constructor(e){super(e),this.state=this.getDefaultState(),this.bindCallbacks(),this.createRefs()}getDefaultState(){return{open:!1,loading:!0}}bindCallbacks(){this.handleDocumentClickEvent=this.handleDocumentClickEvent.bind(this),this.handleDocumentContextualMenuEvent=this.handleDocumentContextualMenuEvent.bind(this),this.handleDocumentDragStartEvent=this.handleDocumentDragStartEvent.bind(this),this.handleToggleMenuClick=this.handleToggleMenuClick.bind(this),this.handleProfileClick=this.handleProfileClick.bind(this),this.handleThemeClick=this.handleThemeClick.bind(this),this.handleLogoutClick=this.handleLogoutClick.bind(this)}componentDidMount(){document.addEventListener("click",this.handleDocumentClickEvent),document.addEventListener("contextmenu",this.handleDocumentContextualMenuEvent),document.addEventListener("dragstart",this.handleDocumentDragStartEvent)}componentWillUnmount(){document.removeEventListener("click",this.handleDocumentClickEvent),document.removeEventListener("contextmenu",this.handleDocumentContextualMenuEvent),document.removeEventListener("dragstart",this.handleDocumentDragStartEvent)}createRefs(){this.userBadgeMenuRef=n.createRef()}get canIUseThemeCapability(){return this.props.context.siteSettings&&this.props.context.siteSettings.canIUse("accountSettings")}handleDocumentClickEvent(e){this.userBadgeMenuRef.current.contains(e.target)||this.closeUserBadgeMenu()}handleDocumentContextualMenuEvent(e){this.userBadgeMenuRef.current.contains(e.target)||this.closeUserBadgeMenu()}handleDocumentDragStartEvent(){this.closeUserBadgeMenu()}closeUserBadgeMenu(){this.setState({open:!1})}getUserFullName(){return this.props.user&&this.props.user.profile?`${this.props.user.profile.first_name} ${this.props.user.profile.last_name}`:"..."}getUserUsername(){return this.props.user&&this.props.user.username?`${this.props.user.username}`:"..."}handleToggleMenuClick(e){e.preventDefault();const t=!this.state.open;this.setState({open:t})}handleProfileClick(){this.props.navigationContext.onGoToUserSettingsProfileRequested(),this.closeUserBadgeMenu()}handleThemeClick(){this.props.navigationContext.onGoToUserSettingsThemeRequested(),this.closeUserBadgeMenu()}handleLogoutClick(){this.props.context.onLogoutRequested(),this.closeUserBadgeMenu()}get translate(){return this.props.t}render(){return n.createElement("div",{className:"col3 profile-wrapper"},n.createElement("div",{className:"user profile dropdown",ref:this.userBadgeMenuRef},n.createElement("div",{onClick:this.handleToggleMenuClick},n.createElement("div",{className:"center-cell-wrapper"},n.createElement("div",{className:"details center-cell"},n.createElement("span",{className:"name"},this.getUserFullName()),n.createElement("span",{className:"email"},this.getUserUsername()))),n.createElement(he,{user:this.props.user,className:"picture left-cell",baseUrl:this.props.baseUrl}),n.createElement("div",{className:"more right-cell"},n.createElement("a",{role:"button"},n.createElement(ue,{name:"caret-down"}),n.createElement("span",null,n.createElement(f.c,null,"more"))))),this.state.open&&n.createElement("ul",{className:"dropdown-content right visible"},n.createElement("li",{key:"profile"},n.createElement("div",{className:"row"},n.createElement("a",{role:"button",tabIndex:"1",onClick:this.handleProfileClick},n.createElement("span",null,n.createElement(f.c,null,"Profile"))))),this.canIUseThemeCapability&&n.createElement("li",{key:"theme"},n.createElement("div",{className:"row"},n.createElement("a",{role:"button",tabIndex:"2",onClick:this.handleThemeClick},n.createElement("span",null,n.createElement(f.c,null,"Theme"))))),n.createElement("li",{key:"logout"},n.createElement("div",{className:"row"},n.createElement("a",{role:"button",tabIndex:"3",onClick:this.handleLogoutClick},n.createElement("span",null,n.createElement(f.c,null,"Sign out"))))))))}}pe.propTypes={context:i().object,navigationContext:i().any,baseUrl:i().string,user:i().object,t:i().func};const me=U(ae((0,v.Z)("common")(pe)));class ge extends n.Component{constructor(e){super(e),this.bindCallbacks()}get isMfaEnabled(){const e=this.props.context.siteSettings;return e&&e.canIUse("multiFactorAuthentication")}get isUserDirectoryEnabled(){const e=this.props.context.siteSettings;return e&&e.canIUse("directorySync")}get canIUseEE(){const e=this.props.context.siteSettings;return e&&e.canIUse("ee")}get canIUseLocale(){return this.props.context.siteSettings&&this.props.context.siteSettings.canIUse("locale")}bindCallbacks(){this.handleMfaClick=this.handleMfaClick.bind(this),this.handleUserDirectoryClick=this.handleUserDirectoryClick.bind(this),this.handleEmailNotificationsClick=this.handleEmailNotificationsClick.bind(this),this.handleSubscriptionClick=this.handleSubscriptionClick.bind(this),this.handleInternationalizationClick=this.handleInternationalizationClick.bind(this)}handleMfaClick(){this.props.navigationContext.onGoToAdministrationMfaRequested()}handleUserDirectoryClick(){this.props.navigationContext.onGoToAdministrationUsersDirectoryRequested()}handleEmailNotificationsClick(){this.props.navigationContext.onGoToAdministrationEmailNotificationsRequested()}handleSubscriptionClick(){this.props.navigationContext.onGoToAdministrationSubscriptionRequested()}handleInternationalizationClick(){this.props.navigationContext.onGoToAdministrationInternationalizationRequested()}isMfaSelected(){return H.MFA===this.props.administrationWorkspaceContext.selectedAdministration}isUserDirectorySelected(){return H.USER_DIRECTORY===this.props.administrationWorkspaceContext.selectedAdministration}isEmailNotificationsSelected(){return H.EMAIL_NOTIFICATION===this.props.administrationWorkspaceContext.selectedAdministration}isSubscriptionSelected(){return H.SUBSCRIPTION===this.props.administrationWorkspaceContext.selectedAdministration}isInternationalizationSelected(){return H.INTERNATIONALIZATION===this.props.administrationWorkspaceContext.selectedAdministration}get translate(){return this.props.t}render(){return n.createElement("div",{className:"navigation-secondary navigation-administration"},n.createElement("ul",{id:"administration_menu",className:"clearfix menu ready"},this.isMfaEnabled&&n.createElement("li",{id:"mfa_menu"},n.createElement("div",{className:"row "+(this.isMfaSelected()?"selected":"")},n.createElement("div",{className:"main-cell-wrapper"},n.createElement("div",{className:"main-cell"},n.createElement("a",{onClick:this.handleMfaClick},n.createElement("span",null,n.createElement(f.c,null,"Multi Factor Authentication"))))))),this.isUserDirectoryEnabled&&n.createElement("li",{id:"user_directory_menu"},n.createElement("div",{className:"row "+(this.isUserDirectorySelected()?"selected":"")},n.createElement("div",{className:"main-cell-wrapper"},n.createElement("div",{className:"main-cell"},n.createElement("a",{onClick:this.handleUserDirectoryClick},n.createElement("span",null,n.createElement(f.c,null,"Users Directory"))))))),n.createElement("li",{id:"email_notification_menu"},n.createElement("div",{className:"row "+(this.isEmailNotificationsSelected()?"selected":"")},n.createElement("div",{className:"main-cell-wrapper"},n.createElement("div",{className:"main-cell"},n.createElement("a",{onClick:this.handleEmailNotificationsClick},n.createElement("span",null,n.createElement(f.c,null,"Email Notifications"))))))),this.canIUseLocale&&n.createElement("li",{id:"internationalization_menu"},n.createElement("div",{className:"row "+(this.isInternationalizationSelected()?"selected":"")},n.createElement("div",{className:"main-cell-wrapper"},n.createElement("div",{className:"main-cell"},n.createElement("a",{onClick:this.handleInternationalizationClick},n.createElement("span",null,n.createElement(f.c,null,"Internationalisation"))))))),this.canIUseEE&&n.createElement("li",{id:"subscription_menu"},n.createElement("div",{className:"row "+(this.isSubscriptionSelected()?"selected":"")},n.createElement("div",{className:"main-cell-wrapper"},n.createElement("div",{className:"main-cell"},n.createElement("a",{onClick:this.handleSubscriptionClick},n.createElement("span",null,n.createElement(f.c,null,"Subscription")))))))))}}ge.propTypes={context:i().object,administrationWorkspaceContext:i().object,history:i().object,navigationContext:i().any,t:i().func};const Ee=(0,N.EN)(U(ae($((0,v.Z)("common")(ge)))));var be=s(648),ye=s.n(be);class fe extends n.Component{constructor(e){super(e),this.state=this.defaultState,this.bindCallbacks()}get defaultState(){return{loading:!0,processing:!1,totpProviderToggle:!0,yubikeyToggle:!1,yubikeyClientIdentifier:"",yubikeyClientIdentifierError:null,yubikeySecretKey:"",yubikeySecretKeyError:null,duoToggle:!1,duoHostname:"",duoHostnameError:null,duoIntegrationKey:"",duoIntegrationKeyError:null,duoSalt:"",duoSaltError:null,duoSecretKey:"",duoSecretKeyError:null}}async componentDidMount(){this.findAllMfaSettings()}async componentDidUpdate(e){await this.handleMustSave(e.administrationWorkspaceContext.must.save)}bindCallbacks(){this.handleInputChange=this.handleInputChange.bind(this)}async handleMustSave(e){this.props.administrationWorkspaceContext.must.save!==e&&this.props.administrationWorkspaceContext.must.save&&(await this.handleFormSubmit(),this.props.administrationWorkspaceContext.onResetActionsSettings())}async findAllMfaSettings(){const e=(await this.props.administrationWorkspaceContext.onGetMfaRequested()).body,t=e.providers,s=t.includes("totp"),n=t.includes("yubikey");let a="",r="";n&&(a=e.yubikey.clientId,r=e.yubikey.secretKey);const i=t.includes("duo");let l="",o="",c="",h="";i&&(l=e.duo.hostName,o=e.duo.integrationKey,c=e.duo.salt,h=e.duo.secretKey),this.setState({loading:!1,totpProviderToggle:s,yubikeyToggle:n,yubikeyClientIdentifier:a,yubikeySecretKey:r,duoToggle:i,duoHostname:l,duoIntegrationKey:o,duoSalt:c,duoSecretKey:h})}handleInputChange(e){const t=e.target,s="checkbox"===t.type?t.checked:t.value,n=t.name;this.setState({[n]:s}),this.handleEnabledSaveButton()}handleEnabledSaveButton(){this.props.administrationWorkspaceContext.can.save||this.props.administrationWorkspaceContext.onSaveEnabled()}hasAllInputDisabled(){return this.state.processing||this.state.loading}isOtpProviderChecked(){return this.state.totpProviderToggle}isYubikeyChecked(){return this.state.yubikeyToggle}isDuoChecked(){return this.state.duoToggle}async validate(){await Promise.all([this.validateYubikeyInput(),this.validateDuoInput()])}async validateYubikeyInput(){let e=null,t=null;if(this.isYubikeyChecked()){const s=this.state.yubikeyClientIdentifier.trim();s.length?ye()("^[0-9]{1,64}$").test(s)||(e=this.translate("The client identifier should be an integer.")):e=this.translate("A client identifier is required.");const n=this.state.yubikeySecretKey.trim();n.length?ye()("^[a-zA-Z0-9\\/=+]{10,128}$").test(n)||(t=this.translate("This secret key is not valid.")):t=this.translate("A secret key is required.")}return this.setState({yubikeyClientIdentifierError:e,yubikeySecretKeyError:t})}async validateDuoInput(){let e=null,t=null,s=null,n=null;if(this.isDuoChecked()){const a=this.state.duoHostname.trim();a.length?ye()("^api-[a-fA-F0-9]{8,16}\\.duosecurity\\.com$").test(a)||(e=this.translate("This is not a valid hostname.")):e=this.translate("A hostname is required.");const r=this.state.duoIntegrationKey.trim();r.length?ye()("^[a-zA-Z0-9]{16,32}$").test(r)||(t=this.translate("This is not a valid integration key.")):t=this.translate("An integration key is required.");const i=this.state.duoSalt.trim();i.length?ye()("^.{40,128}$").test(i)||(s=this.translate("The salt should be between 40 and 128 characters in length.")):s=this.translate("A salt is required.");const l=this.state.duoSecretKey.trim();l.length?ye()("^[a-zA-Z0-9]{32,128}$").test(l)||(n=this.translate("This is not a valid secret key.")):n=this.translate("A secret key is required.")}return this.setState({duoHostnameError:e,duoIntegrationKeyError:t,duoSaltError:s,duoSecretKeyError:n})}hasValidationError(){return this.hasYubikeyError()||this.hasDuoError()}hasYubikeyError(){return null!==this.state.yubikeyClientIdentifierError||null!==this.state.yubikeySecretKeyError}hasDuoError(){return null!==this.state.duoHostnameError||null!==this.state.duoIntegrationKeyError||null!==this.state.duoSaltError||null!==this.state.duoSecretKeyError}async handleFormSubmit(){if(!this.state.processing){if(await this.toggleProcessing(),await this.validate(),this.hasValidationError())return void await this.toggleProcessing();try{await this.saveMfa(),await this.handleSaveSuccess()}catch(e){await this.handleSaveError(e)}}}async saveMfa(){const e=[];this.state.totpProviderToggle&&e.push("totp");let t=null;this.state.yubikeyToggle&&(e.push("yubikey"),t={clientId:this.state.yubikeyClientIdentifier,secretKey:this.state.yubikeySecretKey});let s=null;this.state.duoToggle&&(e.push("duo"),s={hostName:this.state.duoHostname,integrationKey:this.state.duoIntegrationKey,salt:this.state.duoSalt,secretKey:this.state.duoSecretKey}),await this.props.administrationWorkspaceContext.onSaveMfaRequested({providers:e,yubikey:t,duo:s})}async handleSaveSuccess(){await this.props.actionFeedbackContext.displaySuccess(this.translate("The multi factor authentication settings for the organization were updated.")),this.setState({processing:!1})}async handleSaveError(e){"UserAbortsOperationError"===e.name||(console.error(e),await this.handleError(e)),this.setState({processing:!1})}async handleError(e){await this.props.actionFeedbackContext.displayError(e.message)}async toggleProcessing(){const e=this.state.processing;return this.setState({processing:!e})}get translate(){return this.props.t}render(){return n.createElement("div",{className:"row"},n.createElement("div",{className:"mfa-settings col7"},n.createElement("form",{className:"form ready"},n.createElement("div",{className:"provider-section totp"},n.createElement("h3",null,n.createElement("span",{className:"input toggle-switch form-element ready"},n.createElement("input",{id:"totp-provider-toggle-button",type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"totpProviderToggle",onChange:this.handleInputChange,checked:this.state.totpProviderToggle,disabled:this.hasAllInputDisabled()}),n.createElement("label",{className:"toggle-switch-button",htmlFor:"totp-provider-toggle-button"})),n.createElement("label",null,n.createElement(f.c,null,"Time-based One Time Password"))),!this.isOtpProviderChecked()&&n.createElement("p",{className:"description"},n.createElement(f.c,null,"The Time-based One Time Password provider is disabled for all users.")),this.isOtpProviderChecked()&&n.createElement("p",{className:"description"},n.createElement(f.c,null,"The Time-based One Time Password provider is enabled for all users. They can setup this provider in their profile and use it as second factor authentication."))),n.createElement("div",{className:"provider-section yubikey"},n.createElement("h3",null,n.createElement("span",{className:"input toggle-switch form-element"},n.createElement("input",{id:"yubikey-provider-toggle-button",type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"yubikeyToggle",onChange:this.handleInputChange,checked:this.state.yubikeyToggle,disabled:this.hasAllInputDisabled()}),n.createElement("label",{className:"toggle-switch-button",htmlFor:"yubikey-provider-toggle-button"})),n.createElement("label",null,"Yubikey")),!this.isYubikeyChecked()&&n.createElement("p",{className:"description"},n.createElement(f.c,null,"The Yubikey provider is disabled for all users.")),this.isYubikeyChecked()&&n.createElement("div",null,n.createElement("p",{className:"description"},n.createElement(f.c,null,"The Yubikey provider is enabled for all users. They can setup this provider in their profile and use it as second factor authentication.")),n.createElement("div",{className:"form-content"},n.createElement("div",{className:"input text required "+(this.state.yubikeyClientIdentifierError?"error":"")},n.createElement("label",null,n.createElement(f.c,null,"Client identifier")),n.createElement("input",{id:"yubikeyClientIdentifier",type:"text",name:"yubikeyClientIdentifier",required:"required",className:"required fluid form-element ready",placeholder:"123456789",onChange:this.handleInputChange,value:this.state.yubikeyClientIdentifier,disabled:this.hasAllInputDisabled()}),this.state.yubikeyClientIdentifierError&&n.createElement("div",{className:"yubikey_client_identifier error-message"},this.state.yubikeyClientIdentifierError)),n.createElement("div",{className:"input text required "+(this.state.yubikeySecretKeyError?"error":"")},n.createElement("label",null,n.createElement(f.c,null,"Secret key")),n.createElement("input",{id:"yubikeySecretKey",name:"yubikeySecretKey",required:"required",className:"required fluid form-element",placeholder:"**********",type:"password",onChange:this.handleInputChange,value:this.state.yubikeySecretKey,disabled:this.hasAllInputDisabled()}),this.state.yubikeySecretKeyError&&n.createElement("div",{className:"yubikey_secret_key error-message"},this.state.yubikeySecretKeyError))))),n.createElement("div",{className:"provider-section duo"},n.createElement("h3",null,n.createElement("span",{className:"input toggle-switch form-element ready"},n.createElement("input",{id:"duo-provider-toggle-button",type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"duoToggle",onChange:this.handleInputChange,checked:this.state.duoToggle,disabled:this.hasAllInputDisabled()}),n.createElement("label",{className:"toggle-switch-button",htmlFor:"duo-provider-toggle-button"})),n.createElement("label",null,"Duo")),!this.isDuoChecked()&&n.createElement("p",{className:"description"},n.createElement(f.c,null,"The Duo provider is disabled for all users.")),this.isDuoChecked()&&n.createElement("div",null,n.createElement("p",{className:"description enabled"},n.createElement(f.c,null,"The Duo provider is enabled for all users. They can setup this provider in their profile and use it as second factor authentication.")),n.createElement("div",{className:"form-content"},n.createElement("div",{className:"input text required "+(this.state.duoHostnameError?"error":"")},n.createElement("label",null,n.createElement(f.c,null,"Hostname")),n.createElement("input",{id:"duoHostname",type:"text",name:"duoHostname",required:"required",className:"required fluid form-element ready",placeholder:"api-24zlkn4.duosecurity.com",value:this.state.duoHostname,onChange:this.handleInputChange,disabled:this.hasAllInputDisabled()}),this.state.duoHostnameError&&n.createElement("div",{className:"duo_hostname error-message"},this.state.duoHostnameError)),n.createElement("div",{className:"input text required "+(this.state.duoIntegrationKeyError?"error":"")},n.createElement("label",null,n.createElement(f.c,null,"Integration key")),n.createElement("input",{id:"duoIntegrationKey",type:"text",name:"duoIntegrationKey",required:"required",className:"required fluid form-element ready",placeholder:"HASJKDSQJO213123KQSLDF",value:this.state.duoIntegrationKey,onChange:this.handleInputChange,disabled:this.hasAllInputDisabled()}),this.state.duoIntegrationKeyError&&n.createElement("div",{className:"duo_integration_key error-message"},this.state.duoIntegrationKeyError)),n.createElement("div",{className:"input text required "+(this.state.duoSaltError?"error":"")},n.createElement("label",null,n.createElement(f.c,null,"Salt")),n.createElement("input",{id:"duoSalt",name:"duoSalt",required:"required",className:"required fluid form-element ready",placeholder:"**********",type:"password",value:this.state.duoSalt,onChange:this.handleInputChange,disabled:this.hasAllInputDisabled()}),this.state.duoSaltError&&n.createElement("div",{className:"duo_salt error-message"},this.state.duoSaltError)),n.createElement("div",{className:"input text required "+(this.state.duoSecretKeyError?"error":"")},n.createElement("label",null,n.createElement(f.c,null,"Secret key")),n.createElement("input",{id:"duoSecretKey",name:"duoSecretKey",required:"required",className:"required fluid form-element ready",placeholder:"**********",type:"password",value:this.state.duoSecretKey,onChange:this.handleInputChange,disabled:this.hasAllInputDisabled()}),this.state.duoSecretKeyError&&n.createElement("div",{className:"duo_secret_key error-message"},this.state.duoSecretKeyError))))))),n.createElement("div",{className:"col4 last"},n.createElement("div",{className:"sidebar-help"},n.createElement("h3",null,n.createElement(f.c,null,"Need help?")),n.createElement("p",null,n.createElement(f.c,null,"Check out our Multi Factor Authentication configuration guide.")),n.createElement("a",{className:"button",href:"https://help.passbolt.com/configure",target:"_blank",rel:"noopener noreferrer"},n.createElement(ue,{name:"life-ring"}),n.createElement("span",null,n.createElement(f.c,null,"Read the documentation"))))))}}fe.propTypes={administrationWorkspaceContext:i().object,actionFeedbackContext:i().any,t:i().func};const ve=d($((0,v.Z)("common")(fe)));class Ce extends n.Component{constructor(e){super(e),this.bindCallbacks()}bindCallbacks(){this.handleCloseClick=this.handleCloseClick.bind(this)}handleCloseClick(){this.props.onClose()}getClassName(){let e="dialog-close";return this.props.disabled&&(e+=" disabled"),e}render(){return n.createElement("a",{className:this.getClassName(),onClick:this.handleCloseClick,role:"button"},n.createElement(ue,{name:"close"}),n.createElement("span",{className:"visually-hidden"},"Close"))}}Ce.propTypes={onClose:i().func,disabled:i().bool};const we=Ce;class Se extends n.Component{constructor(e){super(e),this.state={top:null},this.tooltipRef=n.createRef(),this.bindCallbacks()}bindCallbacks(){this.setTop=this.setTop.bind(this)}getInlineCss(){return this.state.top?{top:`${this.tooltipRef.current.getBoundingClientRect().top}px`}:{}}setTop(){this.props.offset&&this.setState({top:this.tooltipRef.current.getBoundingClientRect().top})}render(){return n.createElement("div",{className:"more-details tooltip-alt",ref:this.tooltipRef,onMouseEnter:this.setTop},n.createElement(ue,{name:"info-circle"}),n.createElement("div",{className:"tooltip-text right",style:this.getInlineCss()},this.props.children))}}Se.propTypes={offset:i().bool,children:i().node};const ke=Se;class xe extends n.Component{constructor(e){super(e),this.bindCallbacks()}bindCallbacks(){this.handleKeyDown=this.handleKeyDown.bind(this),this.handleClose=this.handleClose.bind(this)}handleKeyDown(e){27===e.keyCode&&this.handleClose()}handleClose(){this.props.disabled||this.props.onClose()}componentDidMount(){document.addEventListener("keydown",this.handleKeyDown)}componentWillUnmount(){document.removeEventListener("keydown",this.handleKeyDown)}render(){return n.createElement("div",{className:`${this.props.className} dialog-wrapper`},n.createElement("div",{className:"dialog"},n.createElement("div",{className:"dialog-header"},n.createElement("h2",null,n.createElement("span",{className:"dialog-header-title"},this.props.title),this.props.subtitle&&n.createElement("span",{className:"dialog-header-subtitle"},this.props.subtitle),this.props.tooltip&&""!==this.props.tooltip&&n.createElement(ke,null,n.createElement("span",null,this.props.tooltip))),n.createElement(we,{onClose:this.handleClose,disabled:this.props.disabled})),n.createElement("div",{className:"dialog-content"},this.props.children)))}}xe.propTypes={children:i().node,className:i().string,title:i().string,subtitle:i().string,tooltip:i().string,loading:i().bool,disabled:i().bool,onClose:i().func};const Ne=xe;class Te extends n.Component{constructor(e){super(e),this.bindCallbacks()}bindCallbacks(){this.handleClick=this.handleClick.bind(this)}handleClick(){this.props.disabled||this.props.onClick()}getClassName(){let e="cancel";return this.props.disabled&&(e+=" disabled"),e}get translate(){return this.props.t}render(){return n.createElement("a",{className:this.getClassName(),role:"button",onClick:this.handleClick},this.translate("Cancel"))}}Te.propTypes={disabled:i().bool,onClick:i().func,t:i().func};const qe=(0,v.Z)("common")(Te);class Ue extends n.Component{constructor(e){super(e),this.bindEventHandlers()}bindEventHandlers(){this.handleClose=this.handleClose.bind(this)}handleClose(){this.props.onClose()}get translate(){return this.props.t}render(){return n.createElement(Ne,{className:"loading-dialog",title:this.props.title,onClose:this.handleClose,disabled:!1},n.createElement("div",{className:"form-content"},n.createElement("label",null,n.createElement(f.c,null,"Take a deep breath and enjoy being in the present moment...")),n.createElement("div",{className:"progress-bar-wrapper"},n.createElement("span",{style:{width:"100%"},className:"progress-bar big infinite"},n.createElement("span",{className:"progress"})))))}}Ue.propTypes={onClose:i().func,title:i().string,t:i().func};const Ae=(0,v.Z)("common")(Ue);class Ie extends n.Component{constructor(e){super(e),this.state=this.defaultState,this.bindEventHandlers()}get defaultState(){return{loading:!0,openFullReport:!1,userDirectorySimulateSynchronizeResult:null}}bindEventHandlers(){this.handleFullReportClicked=this.handleFullReportClicked.bind(this),this.handleClose=this.handleClose.bind(this),this.handleSynchronize=this.handleSynchronize.bind(this)}async componentDidMount(){try{const e=(await this.props.administrationWorkspaceContext.onGetSimulateSynchronizeUsersDirectoryRequested()).body;this.setState({loading:!1,userDirectorySimulateSynchronizeResult:e})}catch(e){await this.handleError(e)}}async handleError(e){console.error(e),await this.props.actionFeedbackContext.displayError(e.message),this.handleClose()}handleFullReportClicked(){this.setState({openFullReport:!this.state.openFullReport})}handleClose(){this.props.onClose()}handleSynchronize(){this.props.administrationWorkspaceContext.onMustSynchronizeSettings(),this.handleClose()}isLoading(){return this.state.loading}get users(){return this.state.userDirectorySimulateSynchronizeResult.users}get groups(){return this.state.userDirectorySimulateSynchronizeResult.groups}get usersSuccess(){return this.users.filter((e=>"success"===e.status))}get groupsSuccess(){return this.groups.filter((e=>"success"===e.status))}get usersError(){return this.users.filter((e=>"error"===e.status))}get groupsError(){return this.groups.filter((e=>"error"===e.status))}get usersIgnored(){return this.users.filter((e=>"ignore"===e.status))}get groupsIgnored(){return this.groups.filter((e=>"ignore"===e.status))}hasSuccessResource(){return this.usersSuccess.length>0||this.groupsSuccess.length>0}hasErrorOrIgnoreResource(){return this.usersError.length>0||this.groupsError.length>0||this.usersIgnored.length>0||this.groupsIgnored.length>0}getFullReport(){let e="";return e=e.concat(this.getUsersFullReport()),e=e.concat(this.getGroupsFullReport()),e}getUsersFullReport(){let e="";if(this.usersSuccess.length>0||this.usersError.length>0||this.usersIgnored.length>0){const t=`---------------------------------------------------------------------\n\n ${this.translate("Users")}\n\n ---------------------------------------------------------------------\n`;e=e.concat(t);const s=t=>e=e.concat(`- ${t.message}\n`);this.usersSuccess.length>0&&(e=e.concat(`\n${this.translate("Success:")}\n`),this.usersSuccess.map(s)),this.usersError.length>0&&(e=e.concat(`\n${this.translate("Errors:")}\n`),this.usersError.map(s)),this.usersIgnored.length>0&&(e=e.concat(`\n${this.translate("Ignored:")}\n`),this.usersIgnored.map(s))}return e.concat("\n")}getGroupsFullReport(){let e="";if(this.groupsSuccess.length>0||this.groupsError.length>0||this.groupsIgnored.length>0){const t=`---------------------------------------------------------------------\n\n ${this.translate("Groups")}\n\n ---------------------------------------------------------------------\n`;e=e.concat(t);const s=t=>e=e.concat(`- ${t.message}\n`);this.groupsSuccess.length>0&&(e=e.concat(`\n${this.translate("Success:")}\n`),this.groupsSuccess.map(s)),this.groupsError.length>0&&(e=e.concat(`\n${this.translate("Errors:")}\n`),this.groupsError.map(s)),this.groupsIgnored.length>0&&(e=e.concat(`\n${this.translate("Ignored:")}\n`),this.groupsIgnored.map(s))}return e}get translate(){return this.props.t}render(){return n.createElement("div",null,this.isLoading()&&n.createElement(Ae,{onClose:this.handleClose,title:this.translate("Synchronize simulation")}),!this.isLoading()&&n.createElement(Ne,{className:"ldap-simulate-synchronize-dialog",title:this.translate("Synchronize simulation report"),onClose:this.handleClose,disabled:this.isLoading()},n.createElement("div",{className:"form-content",onSubmit:this.handleFormSubmit},n.createElement("p",null,n.createElement("strong",null,n.createElement(f.c,null,"The operation was successful."))),n.createElement("p",null),this.hasSuccessResource()&&n.createElement("p",{id:"resources-synchronize"}," ",this.translate("{{users}} and {{groups}} will be synchronized.",{users:this.translate("{{count}} user",{count:this.usersSuccess.length}),groups:this.translate("{{count}} group",{count:this.groupsSuccess.length})})," "),!this.hasSuccessResource()&&n.createElement("p",{id:"no-resources"}," ",n.createElement(f.c,null,"No resources will be synchronized.")," "),this.hasErrorOrIgnoreResource()&&n.createElement("p",{className:"error inline-error"},n.createElement(f.c,null,"Some resources will not be synchronized and will require your attention, see the full report.")),n.createElement("div",{className:"accordion operation-details "+(this.state.openFullReport?"":"closed")},n.createElement("div",{className:"accordion-header",onClick:this.handleFullReportClicked},this.state.openListGroupsUsers&&n.createElement(ue,{name:"caret-down",baseline:!0}),!this.state.openListGroupsUsers&&n.createElement(ue,{name:"caret-right",baseline:!0}),n.createElement("a",{role:"link"},n.createElement(f.c,null,"Full report"))),n.createElement("div",{className:"accordion-content"},n.createElement("div",{className:"input text"},n.createElement("textarea",{className:"full_report",readOnly:!0,value:this.getFullReport()})))),n.createElement("p",null)),n.createElement("div",{className:"submit-wrapper clearfix"},n.createElement("a",{className:"button primary "+(this.isLoading()?"disabled":""),role:"button",onClick:this.handleSynchronize},n.createElement(f.c,null,"Synchronize")),n.createElement(qe,{disabled:this.isLoading(),onClick:this.handleClose}))))}}Ie.propTypes={onClose:i().func,actionFeedbackContext:i().any,administrationWorkspaceContext:i().object,t:i().func};const De=d($((0,v.Z)("common")(Ie)));class Re extends n.Component{constructor(e){super(e),this.state=this.defaultState,this.bindEventHandlers()}get defaultState(){return{loading:!0,openFullReport:!1,userDirectorySynchronizeResult:null}}bindEventHandlers(){this.handleFullReportClicked=this.handleFullReportClicked.bind(this),this.handleClose=this.handleClose.bind(this),this.handleSynchronize=this.handleSynchronize.bind(this)}async componentDidMount(){try{const e=(await this.props.administrationWorkspaceContext.onGetSynchronizeUsersDirectoryRequested()).body;this.setState({loading:!1,userDirectorySynchronizeResult:e})}catch(e){await this.handleError(e)}}async handleError(e){console.error(e),await this.props.actionFeedbackContext.displayError(e.message),this.handleClose()}handleFullReportClicked(){this.setState({openFullReport:!this.state.openFullReport})}handleClose(){this.props.onClose()}handleSynchronize(){this.handleClose()}isLoading(){return this.state.loading}get users(){return this.state.userDirectorySynchronizeResult.users}get groups(){return this.state.userDirectorySynchronizeResult.groups}get usersSuccess(){return this.users.filter((e=>"success"===e.status))}get groupsSuccess(){return this.groups.filter((e=>"success"===e.status))}get usersError(){return this.users.filter((e=>"error"===e.status))}get groupsError(){return this.groups.filter((e=>"error"===e.status))}get usersIgnored(){return this.users.filter((e=>"ignore"===e.status))}get groupsIgnored(){return this.groups.filter((e=>"ignore"===e.status))}hasSuccessResource(){return this.usersSuccess.length>0||this.groupsSuccess.length>0}hasErrorOrIgnoreResource(){return this.usersError.length>0||this.groupsError.length>0||this.usersIgnored.length>0||this.groupsIgnored.length>0}getFullReport(){let e="";return e=e.concat(this.getUsersFullReport()),e=e.concat(this.getGroupsFullReport()),e}getUsersFullReport(){let e="";if(this.usersSuccess.length>0||this.usersError.length>0||this.usersIgnored.length>0){const t=`---------------------------------------------------------------------\n\n ${this.translate("Users")}\n\n ---------------------------------------------------------------------\n`;e=e.concat(t);const s=t=>e=e.concat(`- ${t.message}\n`);this.usersSuccess.length>0&&(e=e.concat(`\n${this.translate("Success:")}\n`),this.usersSuccess.map(s)),this.usersError.length>0&&(e=e.concat(`\n${this.translate("Errors:")}\n`),this.usersError.map(s)),this.usersIgnored.length>0&&(e=e.concat(`\n${this.translate("Ignored:")}\n`),this.usersIgnored.map(s))}return e.concat("\n")}getGroupsFullReport(){let e="";if(this.groupsSuccess.length>0||this.groupsError.length>0||this.groupsIgnored.length>0){const t=`---------------------------------------------------------------------\n\n ${this.translate("Groups")}\n\n ---------------------------------------------------------------------\n`;e=e.concat(t);const s=t=>e=e.concat(`- ${t.message}\n`);this.groupsSuccess.length>0&&(e=e.concat(`\n${this.translate("Success:")}\n`),this.groupsSuccess.map(s)),this.groupsError.length>0&&(e=e.concat(`\n${this.translate("Errors:")}\n`),this.groupsError.map(s)),this.groupsIgnored.length>0&&(e=e.concat(`\n${this.translate("Ignored:")}\n`),this.groupsIgnored.map(s))}return e}get translate(){return this.props.t}render(){return n.createElement("div",null,this.isLoading()&&n.createElement(Ae,{onClose:this.handleClose,title:this.translate("Synchronize")}),!this.isLoading()&&n.createElement(Ne,{className:"ldap-simulate-synchronize-dialog",title:this.translate("Synchronize report"),onClose:this.handleClose,disabled:this.isLoading()},n.createElement("div",{className:"form-content",onSubmit:this.handleFormSubmit},n.createElement("p",null,n.createElement("strong",null,n.createElement(f.c,null,"The operation was successful."))),n.createElement("p",null),this.hasSuccessResource()&&n.createElement("p",{id:"resources-synchronize"}," ",this.translate("{{users}} and {{groups}} have been synchronized.",{users:this.translate("{{count}} user",{count:this.usersSuccess.length}),groups:this.translate("{{count}} group",{count:this.groupsSuccess.length})})," "),!this.hasSuccessResource()&&n.createElement("p",{id:"no-resources"}," ",n.createElement(f.c,null,"No resources have been synchronized.")," "),this.hasErrorOrIgnoreResource()&&n.createElement("p",{className:"error inline-error"},n.createElement(f.c,null,"Some resources will not be synchronized and will require your attention, see the full report.")),n.createElement("div",{className:"accordion operation-details "+(this.state.openFullReport?"":"closed")},n.createElement("div",{className:"accordion-header",onClick:this.handleFullReportClicked},this.state.openListGroupsUsers&&n.createElement(ue,{name:"caret-down",baseline:!0}),!this.state.openListGroupsUsers&&n.createElement(ue,{name:"caret-right",baseline:!0}),n.createElement("a",{role:"link"},n.createElement(f.c,null,"Full report"))),n.createElement("div",{className:"accordion-content"},n.createElement("div",{className:"input text"},n.createElement("textarea",{className:"full_report",readOnly:!0,value:this.getFullReport()})))),n.createElement("p",null)),n.createElement("div",{className:"submit-wrapper clearfix"},n.createElement("a",{className:"button primary "+(this.isLoading()?"disabled":""),role:"button",onClick:this.handleClose},n.createElement(f.c,null,"Ok")))))}}Re.propTypes={onClose:i().func,actionFeedbackContext:i().any,administrationWorkspaceContext:i().object,t:i().func};const _e=d($((0,v.Z)("common")(Re)));class Me extends n.Component{constructor(e){super(e),this.bindCallbacks()}bindCallbacks(){this.handleSaveClick=this.handleSaveClick.bind(this),this.handleTestClick=this.handleTestClick.bind(this),this.handleSimulateSynchronizeClick=this.handleSimulateSynchronizeClick.bind(this),this.handleSynchronizeClick=this.handleSynchronizeClick.bind(this),this.handleEditSubscriptionClick=this.handleEditSubscriptionClick.bind(this)}async componentDidUpdate(e){await this.handleMustSynchronize(e.administrationWorkspaceContext.must.synchronize)}handleMustSynchronize(e){this.props.administrationWorkspaceContext.must.synchronize!==e&&this.props.administrationWorkspaceContext.must.synchronize&&(this.handleSynchronizeClick(),this.props.administrationWorkspaceContext.onResetActionsSettings())}handleSaveClick(){this.props.administrationWorkspaceContext.onMustSaveSettings()}handleTestClick(){this.props.administrationWorkspaceContext.onMustTestSettings()}handleSimulateSynchronizeClick(){this.props.dialogContext.open(De)}handleSynchronizeClick(){this.props.dialogContext.open(_e)}handleEditSubscriptionClick(){this.props.administrationWorkspaceContext.onMustEditSubscriptionKey()}isSaveEnabled(){return this.props.administrationWorkspaceContext.can.save}isTestEnabled(){return this.props.administrationWorkspaceContext.can.test}isSynchronizeEnabled(){return this.props.administrationWorkspaceContext.can.synchronize}isUserDirectorySelected(){return H.USER_DIRECTORY===this.props.administrationWorkspaceContext.selectedAdministration}isSubscriptionSelected(){return H.SUBSCRIPTION===this.props.administrationWorkspaceContext.selectedAdministration}get translate(){return this.props.t}render(){return n.createElement("div",{className:"col2_3 actions-wrapper"},n.createElement("div",{className:"actions"},!this.isSubscriptionSelected()&&n.createElement("div",null,n.createElement("li",null,n.createElement("a",{className:"button "+(this.isSaveEnabled()?"":"disabled"),onClick:this.handleSaveClick},n.createElement(ue,{name:"save"}),n.createElement("span",null,n.createElement(f.c,null,"Save settings")))),this.isUserDirectorySelected()&&n.createElement("div",null,n.createElement("li",null,n.createElement("a",{className:"button "+(this.isTestEnabled()?"":"disabled"),onClick:this.handleTestClick},n.createElement(ue,{name:"plug"}),n.createElement("span",null,n.createElement(f.c,null,"Test settings")))),n.createElement("li",null,n.createElement("a",{className:"button "+(this.isSynchronizeEnabled()?"":"disabled"),onClick:this.handleSimulateSynchronizeClick},n.createElement(ue,{name:"magic-wand"}),n.createElement("span",null,n.createElement(f.c,null,"Simulate synchronize")))),n.createElement("li",null,n.createElement("a",{className:"button "+(this.isSynchronizeEnabled()?"":"disabled"),onClick:this.handleSynchronizeClick},n.createElement(ue,{name:"refresh"}),n.createElement("span",null,n.createElement(f.c,null,"Synchronize")))))),this.isSubscriptionSelected()&&n.createElement("div",null,n.createElement("li",null,n.createElement("a",{className:"button",onClick:this.handleEditSubscriptionClick},n.createElement(ue,{name:"edit"}),n.createElement("span",null,n.createElement(f.c,null,"Update key")))))))}}Me.propTypes={administrationWorkspaceContext:i().object,dialogContext:i().any,t:i().func};const ze=g($((0,v.Z)("common")(Me)));class Ge extends n.Component{render(){let e=0;return n.createElement("div",{className:"breadcrumbs"},n.createElement("ul",{className:"menu"},this.props.items&&this.props.items.map((t=>(e++,n.createElement("li",{className:"ellipsis",key:e},t))))))}}Ge.propTypes={items:i().array};const Oe=Ge;class Pe extends n.Component{render(){return n.createElement("a",{onClick:this.props.onClick},this.props.name)}}Pe.propTypes={name:i().string,onClick:i().func};const Fe=Pe;class Le extends n.Component{get items(){return this.props.administrationWorkspaceContext.selectedAdministration===H.NONE?[]:[n.createElement(Fe,{key:"bread-1",name:this.translate("Administration"),onClick:this.props.navigationContext.onGoToAdministrationRequested}),n.createElement(Fe,{key:"bread-2",name:this.getLastBreadcrumbItemName(),onClick:this.onLastBreadcrumbClick.bind(this)}),n.createElement(Fe,{key:"bread-3",name:this.translate("Settings"),onClick:this.onLastBreadcrumbClick.bind(this)})]}getLastBreadcrumbItemName(){switch(this.props.administrationWorkspaceContext.selectedAdministration){case H.MFA:return this.translate("Multi factor authentication");case H.USER_DIRECTORY:return this.translate("Users Directory");case H.EMAIL_NOTIFICATION:return this.translate("Email Notification");case H.SUBSCRIPTION:return this.translate("Subscription");case H.INTERNATIONALIZATION:return this.translate("Internationalisation");default:return""}}async onLastBreadcrumbClick(){const e=this.props.location.pathname;this.props.history.push({pathname:e})}get translate(){return this.props.t}render(){return n.createElement(Oe,{items:this.items})}}Le.propTypes={administrationWorkspaceContext:i().object,location:i().object,history:i().object,navigationContext:i().any,t:i().func};const Ke=(0,N.EN)(ae($((0,v.Z)("common")(Le))));class We extends n.Component{hasChildren(){return this.props.node.group.groups.length>0}displayUserName(e){return`${e.profile.first_name} ${e.profile.last_name}`}get node(){return this.props.node}render(){return n.createElement("ul",{key:this.node.id},"group"===this.node.type&&n.createElement("li",{className:"group"},this.node.group.name,n.createElement("ul",null,Object.values(this.node.group.users).map((e=>n.createElement("li",{className:"user",key:e.id},e.errors&&n.createElement("span",{className:"error"},e.directory_name),!e.errors&&n.createElement("span",null,this.displayUserName(e.user)," ",n.createElement("em",null,"(",e.user.username,")"))))),Object.values(this.node.group.groups).map((e=>n.createElement(We,{key:`tree-${e.id}`,node:e}))))),"user"===this.node.type&&n.createElement("li",{className:"user"},this.node.errors&&n.createElement("span",{className:"error"},this.node.directory_name),!this.node.errors&&n.createElement("span",null,this.displayUserName(this.node.user)," ",n.createElement("em",null,"(",this.node.user.username,")"))))}}We.propTypes={node:i().object};const Be=We;class je extends n.Component{constructor(e){super(e),this.state=this.defaultState,this.bindEventHandlers()}get defaultState(){return{loading:!0,openListGroupsUsers:!1,openStructureGroupsUsers:!1,openErrors:!1}}bindEventHandlers(){this.handleListGroupsUsersClicked=this.handleListGroupsUsersClicked.bind(this),this.handleStructureGroupsUsersClicked=this.handleStructureGroupsUsersClicked.bind(this),this.handleErrorsClicked=this.handleErrorsClicked.bind(this),this.handleClose=this.handleClose.bind(this)}componentDidMount(){this.setState({loading:!1})}handleListGroupsUsersClicked(){this.setState({openListGroupsUsers:!this.state.openListGroupsUsers})}handleStructureGroupsUsersClicked(){this.setState({openStructureGroupsUsers:!this.state.openStructureGroupsUsers})}handleErrorsClicked(){this.setState({openErrors:!this.state.openErrors})}handleClose(){this.props.onClose(),this.props.context.setContext({displayTestUserDirectoryDialogProps:null})}hasAllInputDisabled(){return this.state.loading}displayUserName(e){return`${e.profile.first_name} ${e.profile.last_name}`}get users(){return this.props.context.displayTestUserDirectoryDialogProps.userDirectoryTestResult.users}get groups(){return this.props.context.displayTestUserDirectoryDialogProps.userDirectoryTestResult.groups}get tree(){return this.props.context.displayTestUserDirectoryDialogProps.userDirectoryTestResult.tree}get errors(){return this.props.context.displayTestUserDirectoryDialogProps.userDirectoryTestResult.errors}get translate(){return this.props.t}render(){return n.createElement(Ne,{className:"ldap-test-settings-dialog",title:"Test settings report",onClose:this.handleClose,disabled:this.hasAllInputDisabled()},n.createElement("div",{className:"form-content"},n.createElement("p",null,n.createElement("strong",null,n.createElement(f.c,null,"A connection could be established. Well done!"))),n.createElement("p",null),n.createElement("div",{className:"ldap-test-settings-report"},n.createElement("p",null,this.translate("{{users}} and {{groups}} have been found.",{users:this.translate("{{count}} user",{count:this.users.length}),groups:this.translate("{{count}} group",{count:this.groups.length})})),n.createElement("div",{className:"accordion directory-list "+(this.state.openListGroupsUsers?"":"closed")},n.createElement("div",{className:"accordion-header",onClick:this.handleListGroupsUsersClicked},this.state.openListGroupsUsers&&n.createElement(ue,{name:"caret-down",baseline:!0}),!this.state.openListGroupsUsers&&n.createElement(ue,{name:"caret-right",baseline:!0}),n.createElement("a",{role:"link"},n.createElement(f.c,null,"See list"))),n.createElement("div",{className:"accordion-content"},n.createElement("table",null,n.createElement("tbody",null,n.createElement("tr",null,n.createElement("td",null,n.createElement(f.c,null,"Groups")),n.createElement("td",null,n.createElement(f.c,null,"Users"))),n.createElement("tr",null,n.createElement("td",null,this.groups.map((e=>e.errors&&n.createElement("div",{key:e.id},n.createElement("span",{className:"error"},e.directory_name))||n.createElement("div",{key:e.id},e.group.name)))),n.createElement("td",null,this.users.map((e=>e.errors&&n.createElement("div",{key:e.id},n.createElement("span",{className:"error"},e.directory_name))||n.createElement("div",{key:e.id},this.displayUserName(e.user)," ",n.createElement("em",null,"(",e.user.username,")")))))))))),n.createElement("div",{className:"accordion accordion-directory-structure "+(this.state.openStructureGroupsUsers?"":"closed")},n.createElement("div",{className:"accordion-header",onClick:this.handleStructureGroupsUsersClicked},this.state.openStructureGroupsUsers&&n.createElement(ue,{name:"caret-down",baseline:!0}),!this.state.openStructureGroupsUsers&&n.createElement(ue,{name:"caret-right",baseline:!0}),n.createElement("a",{role:"link"},n.createElement(f.c,null,"See structure"))),n.createElement("div",{className:"accordion-content"},n.createElement("div",{className:"directory-structure"},n.createElement("ul",null,n.createElement("li",{className:"group"},"Root",Object.values(this.tree).map((e=>n.createElement(Be,{key:`tree-${e.id}`,node:e})))))))),this.errors.length>0&&n.createElement("div",null,n.createElement("p",{className:"directory-errors error"},this.translate("{{count}} entry had errors and will be ignored during synchronization.",{count:this.errors.length})),n.createElement("div",{className:"accordion accordion-directory-errors "+(this.state.openErrors?"":"closed")},n.createElement("div",{className:"accordion-header",onClick:this.handleErrorsClicked},this.state.openErrors&&n.createElement(ue,{name:"caret-down",baseline:!0}),!this.state.openErrors&&n.createElement(ue,{name:"caret-right",baseline:!0}),n.createElement("a",{role:"link"},n.createElement(f.c,null,"See error details"))),n.createElement("div",{className:"accordion-content"},n.createElement("div",{className:"directory-errors"},n.createElement("textarea",{value:JSON.stringify(this.errors,null," "),readOnly:!0}))))))),n.createElement("div",{className:"submit-wrapper clearfix"},n.createElement("a",{className:"button primary "+(this.hasAllInputDisabled()?"disabled":""),role:"button",onClick:this.handleClose},n.createElement(f.c,null,"OK"))))}}je.propTypes={context:i().any,onClose:i().func,t:i().func};const $e=U((0,v.Z)("common")(je));class He extends n.Component{constructor(e){super(e),this.state=this.defaultState,this.createRefs(),this.bindCallbacks()}get defaultState(){return{loading:!0,processing:!1,openCredentials:!0,openDirectoryConfiguration:!1,openSynchronizationOptions:!1,openConnectionType:!1,openDefaultAdmin:!1,openDefaultGroupAdmin:!1,userDirectoryToggle:!1,directoryType:"ad",connectionType:"plain",host:"",hostError:null,port:"389",portError:null,username:"",password:"",domain:"",domainError:null,baseDn:"",groupPath:"",userPath:"",groupObjectClass:"",userObjectClass:"",useEmailPrefix:!1,emailPrefix:"",emailSuffix:"",defaultAdmin:"",defaultGroupAdmin:"",groupsParentGroup:"",usersParentGroup:"",enabledUsersOnly:!1,createUsers:!0,deleteUsers:!0,createGroups:!0,deleteGroups:!0,updateGroups:!0,defaultAdminSearch:"",defaultGroupAdminSearch:"",users:null,hasAlreadyBeenValidated:!1}}async componentDidMount(){document.addEventListener("click",this.handleUserDirectoryClickEvent),this.findAllUserDirectorySettings()}componentWillUnmount(){document.removeEventListener("click",this.handleUserDirectoryClickEvent)}async componentDidUpdate(e,t){await this.handleEnabledTestButton(t.userDirectoryToggle),await this.handleMustSubmit(e.administrationWorkspaceContext)}bindCallbacks(){this.handleUserDirectoryClickEvent=this.handleUserDirectoryClickEvent.bind(this),this.handleCredentialTitleClicked=this.handleCredentialTitleClicked.bind(this),this.handleDirectoryConfigurationTitleClicked=this.handleDirectoryConfigurationTitleClicked.bind(this),this.handleSynchronizationOptionsTitleClicked=this.handleSynchronizationOptionsTitleClicked.bind(this),this.handleConnectionTypeClicked=this.handleConnectionTypeClicked.bind(this),this.handleDefaultAdminClicked=this.handleDefaultAdminClicked.bind(this),this.handleDefaultGroupAdminClicked=this.handleDefaultGroupAdminClicked.bind(this),this.handleInputChange=this.handleInputChange.bind(this),this.handleHostInputKeyUp=this.handleHostInputKeyUp.bind(this),this.handlePortInputKeyUp=this.handlePortInputKeyUp.bind(this),this.handleDomainInputKeyUp=this.handleDomainInputKeyUp.bind(this),this.stopPropagation=this.stopPropagation.bind(this),this.handleConnectionTypeChange=this.handleConnectionTypeChange.bind(this),this.handleUserToBeDefaultAdminClick=this.handleUserToBeDefaultAdminClick.bind(this),this.handleUserToBeDefaultGroupAdminClick=this.handleUserToBeDefaultGroupAdminClick.bind(this)}createRefs(){this.connectionTypeRef=n.createRef(),this.defaultAdminRef=n.createRef(),this.defaultGroupAdminRef=n.createRef()}handleEnabledTestButton(e){this.state.userDirectoryToggle!==e&&this.props.administrationWorkspaceContext.onTestEnabled(this.state.userDirectoryToggle)}handleUserDirectoryClickEvent(e){null===this.defaultAdminRef.current||this.defaultAdminRef.current.contains(e.target)||this.setState({openDefaultAdmin:!1}),null===this.defaultGroupAdminRef.current||this.defaultGroupAdminRef.current.contains(e.target)||this.setState({openDefaultGroupAdmin:!1}),null===this.connectionTypeRef.current||this.connectionTypeRef.current.contains(e.target)||this.setState({openConnectionType:!1})}stopPropagation(e){e.stopPropagation()}async handleMustSubmit(e){const t=this.props.administrationWorkspaceContext.must.save!==e.must.save,s=this.props.administrationWorkspaceContext.must.test!==e.must.test;(t&&this.props.administrationWorkspaceContext.must.save||s&&this.props.administrationWorkspaceContext.must.test)&&(await this.handleFormSubmit(),this.props.administrationWorkspaceContext.onResetActionsSettings())}async findAllUserDirectorySettings(){const e=(await this.props.administrationWorkspaceContext.onGetUsersDirectoryRequested()).body,t=await this.props.administrationWorkspaceContext.onGetUsersRequested(),s=this.sortUsers(t.body);let n="",a="";if(0!==e.length){const t=!0,r=e.directory_type,i=e.connection_type,l=e.domain_name,o=e.username,c=e.password,h=e.base_dn,d=e.server,u=e.port.toString();n=e.default_user,a=e.default_group_admin_user;const p=e.sync_users_create,m=e.sync_users_delete,g=e.sync_groups_create,E=e.sync_groups_delete,b=e.sync_groups_update,y=!0===e.enabled_users_only,f=e.group_path,v=e.user_path,C=e.group_object_class,w=e.user_object_class,S=e.use_email_prefix_suffix,k=e.email_prefix,x=e.email_suffix,N=e.groups_parent_group,T=e.users_parent_group;this.setState({loading:!1,users:s,userDirectoryToggle:t,directoryType:r,connectionType:i,domain:l,username:o,password:c,baseDn:h,host:d,port:u,defaultAdmin:n,defaultGroupAdmin:a,enabledUsersOnly:y,groupPath:f,userPath:v,groupObjectClass:C,userObjectClass:w,useEmailPrefix:S,emailPrefix:k,emailSuffix:x,groupsParentGroup:N,usersParentGroup:T,createUsers:p,deleteUsers:m,createGroups:g,deleteGroups:E,updateGroups:b}),this.props.administrationWorkspaceContext.onTestEnabled(t),this.props.administrationWorkspaceContext.onSynchronizeEnabled(t)}else{const e=s.find((e=>this.props.context.loggedInUser.id===e.id));n=e.id,a=e.id,this.setState({loading:!1,users:s,defaultAdmin:n,defaultGroupAdmin:a})}}sortUsers(e){const t=e=>`${e.profile.first_name} ${e.profile.last_name}`;return e.sort(((e,s)=>t(e).localeCompare(t(s))))}handleCredentialTitleClicked(){this.setState({openCredentials:!this.state.openCredentials})}handleDirectoryConfigurationTitleClicked(){this.setState({openDirectoryConfiguration:!this.state.openDirectoryConfiguration})}handleSynchronizationOptionsTitleClicked(){this.setState({openSynchronizationOptions:!this.state.openSynchronizationOptions})}handleConnectionTypeClicked(){this.hasAllInputDisabled()||this.setState({openConnectionType:!this.state.openConnectionType})}handleDefaultAdminClicked(){this.hasAllInputDisabled()||this.setState({openDefaultAdmin:!this.state.openDefaultAdmin,defaultAdminSearch:""})}handleDefaultGroupAdminClicked(){this.hasAllInputDisabled()||this.setState({openDefaultGroupAdmin:!this.state.openDefaultGroupAdmin,defaultGroupAdminSearch:""})}handleHostInputKeyUp(){if(this.state.hasAlreadyBeenValidated){const e=this.validateHostInput();this.setState(e)}}handlePortInputKeyUp(){if(this.state.hasAlreadyBeenValidated){const e=this.validatePortInput();this.setState(e)}}handleDomainInputKeyUp(){if(this.state.hasAlreadyBeenValidated){const e=this.validateDomainInput();this.setState(e)}}handleInputChange(e){const t=e.target,s="checkbox"===t.type?t.checked:t.value,n=t.name;this.setState({[n]:s}),this.handleEnabledSaveButton()}handleUserToBeDefaultAdminClick(e){const t=e.target.dataset.id;this.setState({defaultAdmin:t}),this.handleEnabledSaveButton()}handleUserToBeDefaultGroupAdminClick(e){const t=e.target.dataset.id;this.setState({defaultGroupAdmin:t}),this.handleEnabledSaveButton()}handleConnectionTypeChange(e){const t=e.target.dataset.id;this.setState({connectionType:t}),this.handleEnabledSaveButton()}handleEnabledSaveButton(){this.props.administrationWorkspaceContext.can.save||this.props.administrationWorkspaceContext.onSaveEnabled()}hasAllInputDisabled(){return this.state.processing||this.state.loading}isUserDirectoryChecked(){return this.state.userDirectoryToggle}isActiveDirectoryChecked(){return"ad"===this.state.directoryType}isOpenLdapChecked(){return"openldap"===this.state.directoryType}isUseEmailPrefixChecked(){return this.state.useEmailPrefix}async validate(){await Promise.all([this.validateHostInput(),this.validatePortInput(),this.validateDomainInput()])}async validateHostInput(){let e=null;return this.state.host.trim().length||(e=this.translate("A host is required.")),this.setState({hostError:e})}async validatePortInput(){let e=null;const t=this.state.port.trim();return t.length?ye()("^[0-9]+").test(t)||(e=this.translate("Only numeric characters allowed.")):e=this.translate("A port is required."),this.setState({portError:e})}async validateDomainInput(){let e=null;return this.state.domain.trim().length||(e=this.translate("A domain name is required.")),this.setState({domainError:e})}hasValidationError(){return null!==this.state.hostError||null!==this.state.portError||null!==this.state.domainError}async handleFormSubmit(){if(await this.setState({hasAlreadyBeenValidated:!0}),!this.state.processing){if(await this.toggleProcessing(),await this.validate(),this.hasValidationError())return void await this.toggleProcessing();try{this.props.administrationWorkspaceContext.must.save?(await this.saveUserDirectory(),await this.handleSaveSuccess()):this.props.administrationWorkspaceContext.must.test&&await this.testUserDirectory(),this.setState({processing:!1})}catch(e){await this.handleSaveError(e)}}}createUserDirectoryDTO(){const e=this.state.directoryType,t=this.state.domain,s=this.state.connectionType,n=this.state.host,a=parseInt(this.state.port),r=this.state.username,i=this.state.password,l=this.state.baseDn,o=this.state.groupPath,c=this.state.userPath,h=this.state.defaultAdmin,d=this.state.defaultGroupAdmin,u=this.state.groupsParentGroup,p=this.state.usersParentGroup,m=this.state.enabledUsersOnly,g=this.state.createUsers,E=this.state.deleteUsers,b=this.state.createGroups,y=this.state.deleteGroups,f=this.state.updateGroups,v=this.state.userDirectoryToggle;let C="",w="",S=!1,k="",x="";return"openldap"==e&&(C=this.state.groupObjectClass,w=this.state.userObjectClass,S=this.state.useEmailPrefix,S&&(k=this.state.emailPrefix,x=this.state.emailSuffix)),{directory_type:e,domain_name:t,connection_type:s,server:n,port:a,username:r,password:i,base_dn:l,group_path:o,user_path:c,group_object_class:C,user_object_class:w,default_user:h,default_group_admin_user:d,groups_parent_group:u,users_parent_group:p,enabled_users_only:m,sync_users_create:g,sync_users_delete:E,sync_groups_create:b,sync_groups_delete:y,sync_groups_update:f,enabled:v,use_email_prefix_suffix:S,email_prefix:k,email_suffix:x}}async saveUserDirectory(){this.state.userDirectoryToggle?(await this.props.administrationWorkspaceContext.onUpdateUsersDirectoryRequested(this.createUserDirectoryDTO()),this.props.administrationWorkspaceContext.onSynchronizeEnabled(!0)):(this.setState(this.defaultState),await this.props.administrationWorkspaceContext.onDeleteUsersDirectoryRequested(),this.props.administrationWorkspaceContext.onSynchronizeEnabled(!1),this.setState({loading:!1}))}async testUserDirectory(){const e={userDirectoryTestResult:(await this.props.administrationWorkspaceContext.onTestUsersDirectoryRequested(this.createUserDirectoryDTO())).body};this.props.context.setContext({displayTestUserDirectoryDialogProps:e}),this.props.dialogContext.open($e)}async handleSaveSuccess(){await this.props.actionFeedbackContext.displaySuccess(this.translate("The user directory settings for the organization were updated."))}async handleSaveError(e){"UserAbortsOperationError"===e.name||(console.error(e),await this.handleError(e)),this.setState({processing:!1})}async handleError(e){await this.props.actionFeedbackContext.displayError(e.message)}async toggleProcessing(){const e=this.state.processing;return this.setState({processing:!e})}getUsersAllowedToBeDefaultAdmin(){if(null!==this.state.users){const e=this.state.users.filter((e=>!0===e.active&&"admin"===e.role.name));return""!==this.state.defaultAdminSearch?this.getUsersMatch(e,this.state.defaultAdminSearch.toLowerCase()):e}return[]}getUsersAllowedToBeDefaultGroupAdmin(){if(null!==this.state.users){const e=this.state.users.filter((e=>!0===e.active));return""!==this.state.defaultGroupAdminSearch?this.getUsersMatch(e,this.state.defaultGroupAdminSearch.toLowerCase()):e}return[]}getUsersMatch(e,t){const s=t&&t.split(/\s+/)||[""],n=(e,t)=>(e=>new RegExp((e=>e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"))(e),"i"))(e).test(t);return e.filter((e=>s.every((t=>((e,t)=>((e,t)=>n(e,t.username))(e,t)||((e,t)=>n(e,t.profile.first_name)||n(e,t.profile.last_name))(e,t))(t,e)))))}displayUser(e){return`${e.profile.first_name} ${e.profile.last_name} (${e.username})`}displayDefaultAdmin(){if(null!==this.state.users&&""!==this.state.defaultAdmin){const e=this.state.users.find((e=>e.id===this.state.defaultAdmin));return this.displayUser(e)}return""}get connectionType(){return{plain:"ldap://",ssl:"ldaps:// (ssl)",tls:"ldaps:// (tls)"}}displayDefaultGroupAdmin(){if(null!==this.state.users&&""!==this.state.defaultGroupAdmin){const e=this.state.users.find((e=>e.id===this.state.defaultGroupAdmin));return this.displayUser(e)}return""}get translate(){return this.props.t}render(){const e=this.getUsersAllowedToBeDefaultAdmin(),t=this.getUsersAllowedToBeDefaultGroupAdmin();return n.createElement("div",{className:"row"},n.createElement("div",{className:"ldap-settings col7"},n.createElement("form",{className:"form"},n.createElement("h3",null,n.createElement("span",{className:"input toggle-switch form-element"},n.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"userDirectoryToggle",onChange:this.handleInputChange,checked:this.state.userDirectoryToggle,disabled:this.hasAllInputDisabled(),id:"userDirectoryToggle"}),n.createElement("label",{className:"toggle-switch-button",htmlFor:"userDirectoryToggle"})),n.createElement("label",null,n.createElement(f.c,null,"Users Directory"))),!this.isUserDirectoryChecked()&&n.createElement("p",{className:"description"},n.createElement(f.c,null,"No Users Directory is configured. Enable it to synchronise your users and groups with passbolt.")),this.isUserDirectoryChecked()&&n.createElement("div",null,n.createElement("p",{className:"description"},n.createElement(f.c,null,"A Users Directory is configured. The users and groups of passbolt will synchronize with it.")),n.createElement("div",{className:"form-content"},n.createElement("div",{className:"accordion section-general "+(this.state.openCredentials?"":"closed")},n.createElement("h3",{className:"accordion-header"},n.createElement("a",{onClick:this.handleCredentialTitleClicked},this.state.openCredentials&&n.createElement(ue,{name:"caret-down",baseline:!0}),!this.state.openCredentials&&n.createElement(ue,{name:"caret-right",baseline:!0}),n.createElement(f.c,null,"Credentials"))),n.createElement("div",{className:"accordion-content"},n.createElement("div",{className:"radiolist required"},n.createElement("label",null,n.createElement(f.c,null,"Directory type")),n.createElement("div",{className:"input radio ad openldap form-element "},n.createElement("div",{className:"input radio"},n.createElement("input",{type:"radio",value:"ad",onChange:this.handleInputChange,name:"directoryType",checked:"ad"===this.state.directoryType,id:"directoryTypeAd",disabled:this.hasAllInputDisabled()}),n.createElement("label",{htmlFor:"directoryTypeAd"},n.createElement(f.c,null,"Active Directory"))),n.createElement("div",{className:"input radio"},n.createElement("input",{type:"radio",value:"openldap",onChange:this.handleInputChange,name:"directoryType",checked:"openldap"===this.state.directoryType,id:"directoryTypeOpenLdap",disabled:this.hasAllInputDisabled()}),n.createElement("label",{htmlFor:"directoryTypeOpenLdap"},n.createElement(f.c,null,"Open Ldap"))))),n.createElement("div",{className:"clearfix required ad openldap"},n.createElement("label",null,n.createElement(f.c,null,"Server url")),n.createElement("div",{className:"input text singleline connection_info ad openldap"},n.createElement("div",{onClick:this.handleConnectionTypeClicked,ref:this.connectionTypeRef,className:`protocol chosen-container chosen-container-single connection-type ${this.hasAllInputDisabled()?"chosen-disabled":"chosen-container-active"} ${this.state.openConnectionType?"chosen-with-drop":""}`},n.createElement("a",{className:"chosen-single"},n.createElement("span",{id:"connection-type-input"},this.connectionType[this.state.connectionType]),n.createElement("div",null,!this.state.openDefaultGroupAdmin&&n.createElement(ue,{name:"caret-down",baseline:!0}),this.state.openDefaultGroupAdmin&&n.createElement(ue,{name:"caret-up",baseline:!0}))),n.createElement("div",{className:"chosen-drop"},n.createElement("ul",{className:"chosen-results"},n.createElement("li",{className:"active-result",onClick:this.handleConnectionTypeChange,"data-id":"plain"},"ldap://"),n.createElement("li",{className:"active-result",onClick:this.handleConnectionTypeChange,"data-id":"ssl"},"ldaps:// (ssl)"),n.createElement("li",{className:"active-result",onClick:this.handleConnectionTypeChange,"data-id":"tls"},"ldaps:// (tls)")))),n.createElement("div",{className:"input text host ad openldap"},n.createElement("input",{id:"server-input",type:"text",className:"required fluid form-element",name:"host",value:this.state.host,onChange:this.handleInputChange,onKeyUp:this.handleHostInputKeyUp,placeholder:this.translate("host"),disabled:this.hasAllInputDisabled()}),this.state.hostError&&n.createElement("div",{id:"server-input-feedback",className:"error-message"},this.state.hostError)),n.createElement("div",{className:"input text port ad openldap"},n.createElement("input",{id:"port-input",type:"number",className:"required fluid form-element",name:"port",value:this.state.port,onChange:this.handleInputChange,onKeyUp:this.handlePortInputKeyUp,placeholder:this.translate("port"),disabled:this.hasAllInputDisabled()}),this.state.portError&&n.createElement("div",{id:"port-input-feedback",className:"error-message"},this.state.portError)))),n.createElement("div",{className:"singleline clearfix"},n.createElement("div",{className:"input text first-field ad openldap"},n.createElement("label",null,n.createElement(f.c,null,"Username")),n.createElement("input",{id:"username-input",type:"text",className:"fluid form-element",name:"username",value:this.state.username,onChange:this.handleInputChange,placeholder:this.translate("username"),disabled:this.hasAllInputDisabled()})),n.createElement("div",{className:"input text last-field ad openldap"},n.createElement("label",null,n.createElement(f.c,null,"Password")),n.createElement("input",{id:"password-input",className:"fluid form-element",name:"password",value:this.state.password,onChange:this.handleInputChange,placeholder:this.translate("password"),type:"password",disabled:this.hasAllInputDisabled()}))),n.createElement("div",{className:"input text required ad openldap"},n.createElement("label",null,n.createElement(f.c,null,"Domain")),n.createElement("input",{id:"domain-name-input",type:"text",name:"domain",value:this.state.domain,onChange:this.handleInputChange,className:"required fluid form-element",onKeyUp:this.handleDomainInputKeyUp,placeholder:"domain.ext",disabled:this.hasAllInputDisabled()}),this.state.domainError&&n.createElement("div",{id:"domain-name-input-feedback",className:"error-message"},this.state.domainError)),n.createElement("div",{className:"input text ad openldap"},n.createElement("label",null,n.createElement(f.c,null,"Base DN")),n.createElement("input",{id:"base-dn-input",type:"text",name:"baseDn",value:this.state.baseDn,onChange:this.handleInputChange,className:"fluid form-element",placeholder:"OU=OrgUsers,DC=mydomain,DC=local",disabled:this.hasAllInputDisabled()}),n.createElement("div",{className:"help-message"},n.createElement(f.c,null,"The base DN (default naming context) for the domain.")," ",n.createElement(f.c,null,"If this is empty then it will be queried from the RootDSE."))))),n.createElement("div",{className:"accordion section-directory-configuration "+(this.state.openDirectoryConfiguration?"":"closed")},n.createElement("h3",{className:"accordion-header"},n.createElement("a",{onClick:this.handleDirectoryConfigurationTitleClicked},this.state.openDirectoryConfiguration&&n.createElement(ue,{name:"caret-down",baseline:!0}),!this.state.openDirectoryConfiguration&&n.createElement(ue,{name:"caret-right",baseline:!0}),n.createElement(f.c,null,"Directory configuration"))),n.createElement("div",{className:"accordion-content"},n.createElement("div",{className:"input text ad openldap"},n.createElement("label",null,n.createElement(f.c,null,"Group path")),n.createElement("input",{id:"group-path-input",type:"text",name:"groupPath",value:this.state.groupPath,onChange:this.handleInputChange,className:"required fluid form-element",placeholder:this.translate("Group Path"),disabled:this.hasAllInputDisabled()}),n.createElement("div",{className:"help-message"},n.createElement(f.c,null,"Group path is used in addition to the base DN while searching groups.")," ",n.createElement(f.c,null,"Leave empty if users and groups are in the same DN."))),n.createElement("div",{className:"input text ad openldap"},n.createElement("label",null,n.createElement(f.c,null,"User path")),n.createElement("input",{id:"user-path-input",type:"text",name:"userPath",value:this.state.userPath,onChange:this.handleInputChange,className:"required fluid form-element",placeholder:this.translate("User Path"),disabled:this.hasAllInputDisabled()}),n.createElement("div",{className:"help-message"},n.createElement(f.c,null,"User path is used in addition to base DN while searching users."))),this.isOpenLdapChecked()&&n.createElement("div",null,n.createElement("div",{className:"input text openldap"},n.createElement("label",null,n.createElement(f.c,null,"Group object class")),n.createElement("input",{id:"group-object-class-input",type:"text",name:"groupObjectClass",value:this.state.groupObjectClass,onChange:this.handleInputChange,className:"required fluid",placeholder:"GroupObjectClass",disabled:this.hasAllInputDisabled()}),n.createElement("div",{className:"help-message"},n.createElement(f.c,null,"For Openldap only. Defines which group object to use.")," (",n.createElement(f.c,null,"Default"),": posixGroup)")),n.createElement("div",{className:"input text openldap"},n.createElement("label",null,n.createElement(f.c,null,"User object class")),n.createElement("input",{id:"user-object-class-input",type:"text",name:"userObjectClass",value:this.state.userObjectClass,onChange:this.handleInputChange,className:"required fluid form-element",placeholder:"UserObjectClass",disabled:this.hasAllInputDisabled()}),n.createElement("div",{className:"help-message"},n.createElement(f.c,null,"For Openldap only. Defines which user object to use.")," (",n.createElement(f.c,null,"Default"),": inetOrgPerson)")),n.createElement("div",{className:"input text openldap"},n.createElement("label",null,n.createElement(f.c,null,"Use email prefix / suffix?")),n.createElement("div",{className:"input toggle-switch openldap form-element"},n.createElement("label",{htmlFor:"use-email-prefix-suffix-toggle-button"},n.createElement(f.c,null,"Build email based on a prefix and suffix?")),n.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"useEmailPrefix",value:this.state.useEmailPrefix,onChange:this.handleInputChange,id:"use-email-prefix-suffix-toggle-button",disabled:this.hasAllInputDisabled()}),n.createElement("label",{className:"toggle-switch-button",htmlFor:"use-email-prefix-suffix-toggle-button"})),n.createElement("div",{className:"help-message"},n.createElement(f.c,null,"Use this option when user entries do not include an email address by default"))),this.isUseEmailPrefixChecked()&&n.createElement("div",{className:"singleline clearfix",id:"use-email-prefix-suffix-options"},n.createElement("div",{className:"input text first-field openldap"},n.createElement("label",null,n.createElement(f.c,null,"Email prefix")),n.createElement("input",{id:"email-prefix-input",type:"text",name:"emailPrefix",checked:this.state.emailPrefix,onChange:this.handleInputChange,className:"required fluid form-element",placeholder:this.translate("username"),disabled:this.hasAllInputDisabled()}),n.createElement("div",{className:"help-message"},n.createElement(f.c,null,"The attribute you would like to use for the first part of the email (usually username)."))),n.createElement("div",{className:"input text last-field openldap"},n.createElement("label",null,n.createElement(f.c,null,"Email suffix")),n.createElement("input",{id:"email-suffix-input",type:"text",name:"emailSuffix",value:this.state.emailSuffix,onChange:this.handleInputChange,className:"required form-element",placeholder:this.translate("@your-domain.com"),disabled:this.hasAllInputDisabled()}),n.createElement("div",{className:"help-message"},n.createElement(f.c,null,"The domain name part of the email (@your-domain-name)."))))))),n.createElement("div",{className:"accordion section-sync-options "+(this.state.openSynchronizationOptions?"":"closed")},n.createElement("h3",{className:"accordion-header"},n.createElement("a",{onClick:this.handleSynchronizationOptionsTitleClicked},this.state.openSynchronizationOptions&&n.createElement(ue,{name:"caret-down",baseline:!0}),!this.state.openSynchronizationOptions&&n.createElement(ue,{name:"caret-right",baseline:!0}),n.createElement(f.c,null,"Synchronization options"))),n.createElement("div",{className:"accordion-content"},n.createElement("div",{className:"input select required ad openldap"},n.createElement("label",null,n.createElement(f.c,null,"Default admin")),n.createElement("div",{onClick:this.handleDefaultAdminClicked,ref:this.defaultAdminRef},n.createElement("div",{className:`chosen-container chosen-container-single ${this.hasAllInputDisabled()?"chosen-disabled":"chosen-container-active"} ${this.state.openDefaultAdmin?"chosen-with-drop":""}`},n.createElement("a",{className:"chosen-single"},n.createElement("span",{id:"default-user-select"},this.displayDefaultAdmin()),n.createElement("div",null,!this.state.openDefaultAdmin&&n.createElement(ue,{name:"caret-down",baseline:!0}),this.state.openDefaultAdmin&&n.createElement(ue,{name:"caret-up",baseline:!0}))),n.createElement("div",{className:"chosen-drop"},n.createElement("div",{className:"chosen-search",onClick:this.stopPropagation},n.createElement("input",{className:"chosen-search-input",name:"defaultAdminSearch",value:this.state.defaultAdminSearch,onChange:this.handleInputChange,type:"text"}),n.createElement(ue,{name:"search"})),n.createElement("ul",{className:"chosen-results"},e.length>0&&e.map((e=>n.createElement("li",{key:e.id,className:"active-result",onClick:this.handleUserToBeDefaultAdminClick,"data-id":e.id},this.displayUser(e)))),0===e.length&&n.createElement("li",{className:"no-results",onClick:this.stopPropagation},n.createElement(f.c,null,"No results match")," ",n.createElement("span",null,this.state.defaultAdminSearch)))))),n.createElement("div",{className:"help-message"},n.createElement(f.c,null,"The default admin user is the user that will perform the operations for the the directory."))),n.createElement("div",{className:"input select required ad openldap"},n.createElement("label",null,n.createElement(f.c,null,"Default group admin")),n.createElement("div",{onClick:this.handleDefaultGroupAdminClicked,ref:this.defaultGroupAdminRef},n.createElement("div",{className:`chosen-container chosen-container-single ${this.hasAllInputDisabled()?"chosen-disabled":"chosen-container-active"} ${this.state.openDefaultGroupAdmin?"chosen-with-drop":""}`},n.createElement("a",{className:"chosen-single"},n.createElement("span",{id:"default-group-admin-user-select"},this.displayDefaultGroupAdmin()),n.createElement("div",null,!this.state.openDefaultGroupAdmin&&n.createElement(ue,{name:"caret-down",baseline:!0}),this.state.openDefaultGroupAdmin&&n.createElement(ue,{name:"caret-up",baseline:!0}))),n.createElement("div",{className:"chosen-drop"},n.createElement("div",{className:"chosen-search",onClick:this.stopPropagation},n.createElement("input",{className:"chosen-search-input",name:"defaultGroupAdminSearch",value:this.state.defaultGroupAdminSearch,onChange:this.handleInputChange,type:"text"}),n.createElement(ue,{name:"search"})),n.createElement("ul",{className:"chosen-results"},t.length>0&&t.map((e=>n.createElement("li",{key:e.id,className:"active-result",onClick:this.handleUserToBeDefaultGroupAdminClick,"data-id":e.id},this.displayUser(e)))),0===t.length&&n.createElement("li",{className:"no-results",onClick:this.stopPropagation},n.createElement(f.c,null,"No results match")," ",n.createElement("span",null,this.state.defaultGroupAdminSearch)))))),n.createElement("div",{className:"help-message"},n.createElement(f.c,null,"The default group manager is the user that will be the group manager of newly created groups."))),n.createElement("div",{className:"input text ad openldap"},n.createElement("label",null,n.createElement(f.c,null,"Groups parent group")),n.createElement("input",{id:"groups-parent-group-input",type:"text",name:"groupsParentGroup",value:this.state.groupsParentGroup,onChange:this.handleInputChange,className:"fluid form-element",placeholder:this.translate("Group name"),disabled:this.hasAllInputDisabled()}),n.createElement("div",{className:"help-message"},n.createElement(f.c,null,"Synchronize only the groups which are members of this group."))),n.createElement("div",{className:"input text ad openldap"},n.createElement("label",null,n.createElement(f.c,null,"Users parent group")),n.createElement("input",{id:"users-parent-group-input",type:"text",name:"usersParentGroup",value:this.state.usersParentGroup,onChange:this.handleInputChange,className:"fluid form-element",placeholder:this.translate("Group name"),disabled:this.hasAllInputDisabled()}),n.createElement("div",{className:"help-message"},n.createElement(f.c,null,"Synchronize only the users which are members of this group."))),this.isActiveDirectoryChecked()&&n.createElement("div",{className:"input text clearfix ad"},n.createElement("label",null,n.createElement(f.c,null,"Enabled users only")),n.createElement("div",{className:"input toggle-switch ad form-element"},n.createElement("label",{htmlFor:"enabled-users-only-toggle-button"},n.createElement(f.c,null,"Only synchronize enabled users (AD)")),n.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"enabledUsersOnly",checked:this.state.enabledUsersOnly,onChange:this.handleInputChange,id:"enabled-users-only-toggle-button",disabled:this.hasAllInputDisabled()}),n.createElement("label",{className:"toggle-switch-button",htmlFor:"enabled-users-only-toggle-button"}))),n.createElement("div",{className:"input text clearfix ad openldap"},n.createElement("label",null,n.createElement(f.c,null,"Sync operations")),n.createElement("div",{className:"col6"},n.createElement("div",{className:"input toggle-switch ad openldap form-element"},n.createElement("label",{htmlFor:"sync-users-create-toggle-button"},n.createElement(f.c,null,"Create users")),n.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"createUsers",checked:this.state.createUsers,onChange:this.handleInputChange,id:"sync-users-create-toggle-button",disabled:this.hasAllInputDisabled()}),n.createElement("label",{className:"toggle-switch-button",htmlFor:"sync-users-create-toggle-button"})),n.createElement("div",{className:"input toggle-switch ad openldap form-element"},n.createElement("label",{htmlFor:"sync-users-delete-toggle-button"},n.createElement(f.c,null,"Delete users")),n.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"deleteUsers",checked:this.state.deleteUsers,onChange:this.handleInputChange,id:"sync-users-delete-toggle-button",disabled:this.hasAllInputDisabled()}),n.createElement("label",{className:"toggle-switch-button",htmlFor:"sync-users-delete-toggle-button"}))),n.createElement("div",{className:"col6 last"},n.createElement("div",{className:"input toggle-switch ad openldap form-element"},n.createElement("label",{htmlFor:"sync-groups-create-toggle-button"},n.createElement(f.c,null,"Create groups")),n.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"createGroups",checked:this.state.createGroups,onChange:this.handleInputChange,id:"sync-groups-create-toggle-button",disabled:this.hasAllInputDisabled()}),n.createElement("label",{className:"toggle-switch-button",htmlFor:"sync-groups-create-toggle-button"})),n.createElement("div",{className:"input toggle-switch ad openldap form-element"},n.createElement("label",{htmlFor:"sync-groups-delete-toggle-button"},n.createElement(f.c,null,"Delete groups")),n.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"deleteGroups",checked:this.state.deleteGroups,onChange:this.handleInputChange,id:"sync-groups-delete-toggle-button",disabled:this.hasAllInputDisabled()}),n.createElement("label",{className:"toggle-switch-button",htmlFor:"sync-groups-delete-toggle-button"})),n.createElement("div",{className:"input toggle-switch ad openldap form-element"},n.createElement("label",{htmlFor:"sync-groups-update-toggle-button"},n.createElement(f.c,null,"Update groups")),n.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"updateGroups",checked:this.state.updateGroups,onChange:this.handleInputChange,id:"sync-groups-update-toggle-button",disabled:this.hasAllInputDisabled()}),n.createElement("label",{className:"toggle-switch-button",htmlFor:"sync-groups-update-toggle-button"})))))))))),n.createElement("div",{className:"col4 last"},n.createElement("div",{className:"sidebar-help"},n.createElement("h3",null,n.createElement(f.c,null,"Need help?")),n.createElement("p",null,n.createElement(f.c,null,"Check out our ldap configuration guide.")),n.createElement("a",{className:"button",href:"https://help.passbolt.com/configure/ldap",target:"_blank",rel:"noopener noreferrer"},n.createElement(ue,{name:"life-ring"}),n.createElement("span",null,n.createElement(f.c,null,"Read the documentation"))))))}}He.propTypes={context:i().object,administrationWorkspaceContext:i().object,actionFeedbackContext:i().any,dialogContext:i().any,t:i().func};const Ve=U(d(g($((0,v.Z)("common")(He)))));class Ze extends n.Component{constructor(e){super(e),this.state=this.defaultState,this.bindCallbacks()}get defaultState(){return{loading:!0,processing:!1,hasDatabaseSetting:!1,hasFileConfigSetting:!1,passwordCreate:!0,passwordShare:!0,passwordUpdate:!0,passwordDelete:!0,folderCreate:!0,folderUpdate:!0,folderDelete:!0,folderShare:!0,commentAdd:!0,groupDelete:!0,groupUserAdd:!0,groupUserDelete:!0,groupUserUpdate:!0,groupManagerUpdate:!0,userCreate:!0,userRecover:!0,showDescription:!0,showSecret:!0,showUri:!0,showUsername:!0,showComment:!0}}async componentDidMount(){this.findAllEmailNotificationsSettings()}async componentDidUpdate(e){await this.handleMustSave(e.administrationWorkspaceContext.must.save)}bindCallbacks(){this.handleInputChange=this.handleInputChange.bind(this)}async handleMustSave(e){this.props.administrationWorkspaceContext.must.save!==e&&this.props.administrationWorkspaceContext.must.save&&(await this.handleFormSubmit(),this.props.administrationWorkspaceContext.onResetActionsSettings())}async findAllEmailNotificationsSettings(){const e=(await this.props.administrationWorkspaceContext.onGetEmailNotificationsRequested()).body,t=e.sources_database,s=e.sources_file,n=e.send_password_create,a=e.send_password_share,r=e.send_password_update,i=e.send_password_delete,l=e.send_folder_create,o=e.send_folder_update,c=e.send_folder_delete,h=e.send_folder_share,d=e.send_comment_add,u=e.send_group_delete,p=e.send_group_user_add,m=e.send_group_user_delete,g=e.send_group_user_update,E=e.send_group_manager_update,b=e.send_user_create,y=e.send_user_recover,f=e.show_description,v=e.show_secret,C=e.show_uri,w=e.show_username,S=e.show_comment;this.setState({loading:!1,hasDatabaseSetting:t,hasFileConfigSetting:s,passwordCreate:n,passwordShare:a,passwordUpdate:r,passwordDelete:i,folderCreate:l,folderUpdate:o,folderDelete:c,folderShare:h,commentAdd:d,groupDelete:u,groupUserAdd:p,groupUserDelete:m,groupUserUpdate:g,groupManagerUpdate:E,userCreate:b,userRecover:y,showDescription:f,showSecret:v,showUri:C,showUsername:w,showComment:S})}handleInputChange(e){const t=e.target,s=t.checked,n=t.name;this.setState({[n]:s}),this.handleEnabledSaveButton()}handleEnabledSaveButton(){this.props.administrationWorkspaceContext.can.save||this.props.administrationWorkspaceContext.onSaveEnabled()}hasAllInputDisabled(){return this.state.processing||this.state.loading}async handleFormSubmit(){if(!this.state.processing){await this.toggleProcessing();try{await this.saveEmailNotifications(),await this.handleSaveSuccess()}catch(e){await this.handleSaveError(e)}}}async saveEmailNotifications(){const e={sources_database:this.state.hasDatabaseSetting,sources_file:this.state.hasFileConfigSetting,send_password_create:this.state.passwordCreate,send_password_share:this.state.passwordShare,send_password_update:this.state.passwordUpdate,send_password_delete:this.state.passwordDelete,send_folder_create:this.state.folderCreate,send_folder_update:this.state.folderUpdate,send_folder_delete:this.state.folderDelete,send_folder_share:this.state.folderShare,send_comment_add:this.state.commentAdd,send_group_delete:this.state.groupDelete,send_group_user_add:this.state.groupUserAdd,send_group_user_delete:this.state.groupUserDelete,send_group_user_update:this.state.groupUserUpdate,send_group_manager_update:this.state.groupManagerUpdate,send_user_create:this.state.userCreate,send_user_recover:this.state.userRecover,show_description:this.state.showDescription,show_secret:this.state.showSecret,show_uri:this.state.showUri,show_username:this.state.showUsername,show_comment:this.state.showComment};await this.props.administrationWorkspaceContext.onSaveEmailNotificationsRequested(e)}async handleSaveSuccess(){await this.props.actionFeedbackContext.displaySuccess(this.translate("The email notification settings were updated.")),this.setState({processing:!1})}async handleSaveError(e){"UserAbortsOperationError"===e.name||(console.error(e),await this.handleError(e)),this.setState({processing:!1})}async handleError(e){await this.props.actionFeedbackContext.displayError(e.message)}async toggleProcessing(){const e=this.state.processing;return this.setState({processing:!e})}hasDatabaseSetting(){return this.state.hasDatabaseSetting}hasFileConfigSetting(){return this.state.hasFileConfigSetting}canUseFolders(){return this.props.context.siteSettings.canIUse("folders")}get translate(){return this.props.t}render(){return n.createElement("div",{className:"row"},n.createElement("div",{className:"email-notification-settings col8"},this.hasDatabaseSetting()&&this.hasFileConfigSetting()&&n.createElement("div",{className:"warning message",id:"email-notification-setting-overridden-banner"},n.createElement("p",null,n.createElement(f.c,null,"Settings have been found in your database as well as in your passbolt.php (or environment variables).")," ",n.createElement(f.c,null,"The settings displayed in the form below are the one stored in your database and have precedence over others."))),!this.hasDatabaseSetting()&&this.hasFileConfigSetting()&&n.createElement("div",{className:"warning message hidden",id:"email-notification-fileconfig-exists-banner"},n.createElement("p",null,n.createElement(f.c,null,"You seem to have Email Notification Settings defined in your passbolt.php (or via environment variables).")," ",n.createElement(f.c,null,"Submitting the form will overwrite those settings with the ones you choose in the form below."))),n.createElement("h3",null,n.createElement(f.c,null,"Email delivery")),n.createElement("p",null,n.createElement(f.c,null,"In this section you can choose which email notifications will be sent.")),n.createElement("form",{className:"form"},n.createElement("div",{className:"row"},n.createElement("div",{className:"col6"},n.createElement("label",null,n.createElement(f.c,null,"Passwords")),n.createElement("span",{className:"input toggle-switch form-element"},n.createElement("label",{htmlFor:"send-password-create-toggle-button"},n.createElement(f.c,null,"When a password is created, notify its creator.")),n.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"passwordCreate",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.passwordCreate,id:"send-password-create-toggle-button"}),n.createElement("label",{className:"toggle-switch-button",htmlFor:"send-password-create-toggle-button"})),n.createElement("span",{className:"input toggle-switch form-element"},n.createElement("label",{htmlFor:"send-password-update-toggle-button"},n.createElement(f.c,null,"When a password is updated, notify the users who have access to it.")),n.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"passwordUpdate",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.passwordUpdate,id:"send-password-update-toggle-button"}),n.createElement("label",{className:"toggle-switch-button",htmlFor:"send-password-update-toggle-button"})),n.createElement("span",{className:"input toggle-switch form-element"},n.createElement("label",{htmlFor:"send-password-delete-toggle-button"},n.createElement(f.c,null,"When a password is deleted, notify the users who had access to it.")),n.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"passwordDelete",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.passwordDelete,id:"send-password-delete-toggle-button"}),n.createElement("label",{className:"toggle-switch-button",htmlFor:"send-password-delete-toggle-button"})),n.createElement("span",{className:"input toggle-switch form-element"},n.createElement("label",{htmlFor:"send-password-share-toggle-button"},n.createElement(f.c,null,"When a password is shared, notify the users who gain access to it.")),n.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"passwordShare",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.passwordShare,id:"send-password-share-toggle-button"}),n.createElement("label",{className:"toggle-switch-button",htmlFor:"send-password-share-toggle-button"}))),this.canUseFolders()&&n.createElement("div",{className:"col6 last"},n.createElement("label",null,n.createElement(f.c,null,"Folders")),n.createElement("span",{className:"input toggle-switch form-element"},n.createElement("label",{htmlFor:"send-folder-create-toggle-button"},n.createElement(f.c,null,"When a folder is created, notify its creator.")),n.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"folderCreate",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.folderCreate,id:"send-folder-create-toggle-button"}),n.createElement("label",{className:"toggle-switch-button",htmlFor:"send-folder-create-toggle-button"})),n.createElement("span",{className:"input toggle-switch form-element"},n.createElement("label",{htmlFor:"send-folder-update-toggle-button"},n.createElement(f.c,null,"When a folder is updated, notify the users who have access to it.")),n.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"folderUpdate",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.folderUpdate,id:"send-folder-update-toggle-button"}),n.createElement("label",{className:"toggle-switch-button",htmlFor:"send-folder-update-toggle-button"})),n.createElement("span",{className:"input toggle-switch form-element"},n.createElement("label",{htmlFor:"send-folder-delete-toggle-button"},n.createElement(f.c,null,"When a folder is deleted, notify the users who had access to it.")),n.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"folderDelete",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.folderDelete,id:"send-folder-delete-toggle-button"}),n.createElement("label",{className:"toggle-switch-button",htmlFor:"send-folder-delete-toggle-button"})),n.createElement("span",{className:"input toggle-switch form-element"},n.createElement("label",{htmlFor:"send-folder-share-toggle-button"},n.createElement(f.c,null,"When a folder is shared, notify the users who gain access to it.")),n.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"folderShare",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.folderShare,id:"send-folder-share-toggle-button"}),n.createElement("label",{className:"toggle-switch-button",htmlFor:"send-folder-share-toggle-button"}))),n.createElement("div",{className:"row"}),n.createElement("div",{className:"col6 last"},n.createElement("label",null,n.createElement(f.c,null,"Comments")),n.createElement("span",{className:"input toggle-switch form-element"},n.createElement("label",{htmlFor:"send-comment-add-toggle-button"},n.createElement(f.c,null,"When a comment is posted on a password, notify the users who have access to this password.")),n.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"commentAdd",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.commentAdd,id:"send-comment-add-toggle-button"}),n.createElement("label",{className:"toggle-switch-button",htmlFor:"send-comment-add-toggle-button"})))),n.createElement("div",{className:"row"},n.createElement("div",{className:"col6"},n.createElement("label",null,n.createElement(f.c,null,"Group membership")),n.createElement("span",{className:"input toggle-switch form-element"},n.createElement("label",{htmlFor:"send-group-delete-toggle-button"},n.createElement(f.c,null,"When a group is deleted, notify the users who were members of it.")),n.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"groupDelete",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.groupDelete,id:"send-group-delete-toggle-button"}),n.createElement("label",{className:"toggle-switch-button",htmlFor:"send-group-delete-toggle-button"})),n.createElement("span",{className:"input toggle-switch form-element"},n.createElement("label",{htmlFor:"group-user-add-toggle-button"},n.createElement(f.c,null,"When users are added to a group, notify them.")),n.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"groupUserAdd",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.groupUserAdd,id:"send-group-user-add-toggle-button"}),n.createElement("label",{className:"toggle-switch-button",htmlFor:"send-group-user-add-toggle-button"})),n.createElement("span",{className:"input toggle-switch form-element"},n.createElement("label",{htmlFor:"send-group-user-delete-toggle-button"},n.createElement(f.c,null,"When users are removed from a group, notify them.")),n.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"groupUserDelete",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.groupUserDelete,id:"send-group-user-delete-toggle-button"}),n.createElement("label",{className:"toggle-switch-button",htmlFor:"send-group-user-delete-toggle-button"})),n.createElement("span",{className:"input toggle-switch form-element"},n.createElement("label",{htmlFor:"send-group-user-update-toggle-button"},n.createElement(f.c,null,"When user roles change in a group, notify the corresponding users.")),n.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"groupUserUpdate",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.groupUserUpdate,id:"send-group-user-update-toggle-button"}),n.createElement("label",{className:"toggle-switch-button",htmlFor:"send-group-user-update-toggle-button"}))),n.createElement("div",{className:"col6 last"},n.createElement("label",null,n.createElement(f.c,null,"Group manager")),n.createElement("span",{className:"input toggle-switch form-element"},n.createElement("label",{htmlFor:"send-group-manager-update-toggle-button"},n.createElement(f.c,null,"When members of a group change, notify the group manager(s).")),n.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"groupManagerUpdate",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.groupManagerUpdate,id:"send-group-manager-update-toggle-button"}),n.createElement("label",{className:"toggle-switch-button",htmlFor:"send-group-manager-update-toggle-button"})),n.createElement("label",null,n.createElement(f.c,null,"Registration & Recovery")),n.createElement("span",{className:"input toggle-switch form-element"},n.createElement("label",{htmlFor:"send-user-create-toggle-button"},n.createElement(f.c,null,"When new users are invited to passbolt, notify them.")),n.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"userCreate",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.userCreate,id:"send-user-create-toggle-button"}),n.createElement("label",{className:"toggle-switch-button",htmlFor:"send-user-create-toggle-button"})),n.createElement("span",{className:"input toggle-switch form-element"},n.createElement("label",{htmlFor:"send-user-recover-toggle-button"},n.createElement(f.c,null,"When users try to recover their account, notify them.")),n.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"userRecover",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.userRecover,id:"send-user-recover-toggle-button"}),n.createElement("label",{className:"toggle-switch-button",htmlFor:"send-user-recover-toggle-button"})))),n.createElement("div",{className:"row"},n.createElement("h3",null,n.createElement(f.c,null,"Email content visibility")),n.createElement("p",null,n.createElement(f.c,null,"In this section you can adjust the composition of the emails, e.g. which information will be included in the notification.")),n.createElement("div",{className:"col6"},n.createElement("label",null,n.createElement(f.c,null,"Passwords")),n.createElement("span",{className:"input toggle-switch form-element"},n.createElement("label",{htmlFor:"show-username-toggle-button"},n.createElement(f.c,null,"Username")),n.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"showUsername",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.showUsername,id:"show-username-toggle-button"}),n.createElement("label",{className:"toggle-switch-button",htmlFor:"show-username-toggle-button"})),n.createElement("span",{className:"input toggle-switch form-element"},n.createElement("label",{htmlFor:"show-uri-toggle-button"},n.createElement(f.c,null,"URI")),n.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"showUri",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.showUri,id:"show-uri-toggle-button"}),n.createElement("label",{className:"toggle-switch-button",htmlFor:"show-uri-toggle-button"})),n.createElement("span",{className:"input toggle-switch form-element ready"},n.createElement("label",{htmlFor:"show-secret-toggle-button"},n.createElement(f.c,null,"Encrypted secret")),n.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"showSecret",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.showSecret,id:"show-secret-toggle-button"}),n.createElement("label",{className:"toggle-switch-button",htmlFor:"show-secret-toggle-button"})),n.createElement("span",{className:"input toggle-switch form-element"},n.createElement("label",{htmlFor:"show-description-toggle-button"},n.createElement(f.c,null,"Description")),n.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"showDescription",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.showDescription,id:"show-description-toggle-button"}),n.createElement("label",{className:"toggle-switch-button",htmlFor:"show-description-toggle-button"}))),n.createElement("div",{className:"col6 last"},n.createElement("label",null,n.createElement(f.c,null,"Comments")),n.createElement("span",{className:"input toggle-switch form-element"},n.createElement("label",{htmlFor:"show-comment-toggle-button"},n.createElement(f.c,null,"Comment content")),n.createElement("input",{type:"checkbox",className:"toggle-switch-checkbox checkbox",name:"showComment",disabled:this.hasAllInputDisabled(),onChange:this.handleInputChange,checked:this.state.showComment,id:"show-comment-toggle-button"}),n.createElement("label",{className:"toggle-switch-button",htmlFor:"show-comment-toggle-button"})))))),n.createElement("div",{className:"col4 last"},n.createElement("div",{className:"sidebar-help"},n.createElement("h3",null,n.createElement(f.c,null,"Need some help?")),n.createElement("p",null,n.createElement(f.c,null,"For more information about email notification, checkout the dedicated page on the help website.")),n.createElement("a",{className:"button",href:"https://help.passbolt.com/configure/notification/email",target:"_blank",rel:"noopener noreferrer"},n.createElement(ue,{name:"life-ring"}),n.createElement("span",null,n.createElement(f.c,null,"Read the documentation"))))))}}Ze.propTypes={context:i().any,administrationWorkspaceContext:i().object,actionFeedbackContext:i().any,t:i().func};const Ye=U(d($((0,v.Z)("common")(Ze))));class Je extends n.Component{constructor(e){super(e),this.state=this.getDefaultState(),this.bindCallbacks(),this.createReferences()}bindCallbacks(){this.handleChangeEvent=this.handleChangeEvent.bind(this),this.handleOnSubmitEvent=this.handleOnSubmitEvent.bind(this)}getDefaultState(){return{}}createReferences(){this.searchInputRef=n.createRef()}handleChangeEvent(e){const t=e.target.value;this.props.onSearch&&this.props.onSearch(t)}handleOnSubmitEvent(e){if(e.preventDefault(),this.props.onSearch){const e=this.searchInputRef.current.value;this.props.onSearch(e)}}render(){return n.createElement("div",{className:"col2 search-wrapper"},n.createElement("form",{className:"search",onSubmit:this.handleOnSubmitEvent},n.createElement("div",{className:"input search required"},n.createElement("label",null,"Search"),n.createElement("input",{ref:this.searchInputRef,className:"required",type:"search",disabled:this.props.disabled?"disabled":"",onChange:this.handleChangeEvent,placeholder:this.props.placeholder||this.props.t("Search"),value:this.props.value})),n.createElement("button",{className:"button",value:"search",type:"submit",disabled:this.props.disabled?"disabled":""},n.createElement(ue,{name:"search"}),n.createElement("span",{className:"visuallyhidden"},"Search"))))}}Je.propTypes={disabled:i().bool,onSearch:i().func,placeholder:i().string,value:i().string,t:i().func},Je.defaultProps={disabled:!1};const Qe=(0,v.Z)("common")(Je);var Xe=s(9490);class et extends n.Component{constructor(e){super(e),this.bindCallbacks()}bindCallbacks(){this.handleKeyDown=this.handleKeyDown.bind(this)}componentDidMount(){document.addEventListener("keydown",this.handleKeyDown)}componentWillUnmount(){document.removeEventListener("keydown",this.handleKeyDown)}getTitle(){return this.props.context.errorDialogProps&&this.props.context.errorDialogProps.title||this.props.title||et.defaultProps.title}getMessage(){return this.props.context.errorDialogProps&&this.props.context.errorDialogProps.message||this.props.message||et.defaultProps.message}handleKeyDown(e){27!==e.keyCode&&13!==e.keyCode||(e.stopPropagation(),this.props.onClose())}render(){return n.createElement("div",{className:"dialog-wrapper error-dialog"},n.createElement("div",{className:"dialog"},n.createElement("div",{className:"dialog-header"},n.createElement("h2",null,this.getTitle()),n.createElement(we,{onClose:this.props.onClose})),n.createElement("div",{className:"dialog-content"},n.createElement("div",{className:"form-content"},n.createElement("p",null,this.getMessage())),n.createElement("div",{className:"submit-wrapper clearfix"},n.createElement("a",{className:"button primary warning",onClick:this.props.onClose},"Ok")))))}}et.defaultProps={title:"Oops something went wrong.",message:"An internal error occurred, please try again later."},et.propTypes={context:i().any,title:i().string,message:i().string,onClose:i().func};const tt=U(et);class st extends n.Component{constructor(e){super(e),this.bindCallbacks()}bindCallbacks(){this.getClassName=this.getClassName.bind(this)}getClassName(){let e="button primary";return this.props.warning&&(e+=" warning"),this.props.disabled&&(e+=" disabled"),this.props.processing&&(e+=" processing"),this.props.big&&(e+=" big"),this.props.medium&&(e+=" medium"),this.props.fullWidth&&(e+=" full-width"),e}render(){return n.createElement("input",{type:"submit",className:this.getClassName(),disabled:this.props.disabled,value:this.props.value||"Save"})}}st.defaultProps={warning:!1},st.propTypes={processing:i().bool,disabled:i().bool,value:i().string,warning:i().bool,big:i().bool,medium:i().bool,fullWidth:i().bool};const nt=st;class at extends n.Component{constructor(e){super(e),this.state=this.getDefaultState(),this.initEventHandlers(),this.createInputRef()}getDefaultState(){return{key:"",keyError:"",processing:!1,hasBeenValidated:!1}}initEventHandlers(){this.handleCloseClick=this.handleCloseClick.bind(this),this.handleFormSubmit=this.handleFormSubmit.bind(this),this.handleInputChange=this.handleInputChange.bind(this),this.handleKeyInputKeyUp=this.handleKeyInputKeyUp.bind(this),this.handleSelectSubscriptionKeyFile=this.handleSelectSubscriptionKeyFile.bind(this)}createInputRef(){this.keyInputRef=n.createRef(),this.fileUploaderRef=n.createRef()}componentDidMount(){this.setState({key:this.props.context.editSubscriptionKey.key||""})}async handleFormSubmit(e){e.preventDefault(),this.state.processing||await this.save()}handleInputChange(e){const t=e.target,s=t.value,n=t.name;this.setState({[n]:s})}handleKeyInputKeyUp(){if(this.state.hasAlreadyBeenValidated){const e=this.validateNameInput();this.setState(e)}}handleCloseClick(){this.props.context.setContext({editSubscriptionKey:null}),this.props.onClose()}async handleSelectSubscriptionKeyFile(e){const[t]=e.target.files,s=await this.readSubscriptionKeyFile(t);await this.fillSubscriptionKey(s),this.state.hasBeenValidated&&await this.validate()}readSubscriptionKeyFile(e){const t=new FileReader;return new Promise(((s,n)=>{t.onloadend=()=>{try{s(t.result)}catch(e){n(e)}},t.readAsText(e)}))}async fillSubscriptionKey(e){await this.setState({key:e})}async save(){if(this.state.processing)return;if(await this.setState({hasBeenValidated:!0}),await this.toggleProcessing(),!await this.validate())return this.handleValidateError(),void await this.toggleProcessing();const e={data:this.state.key};try{await this.props.administrationWorkspaceContext.onUpdateSubscriptionKeyRequested(e),await this.handleSaveSuccess()}catch(e){await this.toggleProcessing(),this.handleSaveError(e),this.focusFieldError()}}handleValidateError(){this.focusFieldError()}async handleSaveSuccess(){await this.props.actionFeedbackContext.displaySuccess(this.translate("The subscription key has been updated successfully.")),this.props.administrationWorkspaceContext.onMustRefreshSubscriptionKey(),this.props.context.setContext({editSubscriptionKey:null,refreshSubscriptionAnnouncement:!0}),this.props.onClose()}async handleSaveError(e){if("PassboltSubscriptionError"===e.name)this.setState({keyError:e.message});else if("EntityValidationError"===e.name)this.setState({keyError:this.translate("The subscription key is invalid.")});else if("PassboltApiFetchError"===e.name&&e.data&&400===e.data.code)this.setState({keyError:e.message});else{console.error(e);const t={title:this.translate("There was an unexpected error..."),message:e.message};this.props.context.setContext({errorDialogProps:t}),this.props.dialogContext.open(tt)}}focusFieldError(){this.state.keyError&&this.keyInputRef.current.focus()}validateKeyInput(){const e=this.state.key.trim();let t="";return e.length||(t=this.translate("A subscription key is required.")),new Promise((e=>{this.setState({keyError:t},e)}))}async validate(){return this.setState({keyError:""}),await this.validateKeyInput(),""===this.state.keyError}async toggleProcessing(){await this.setState({processing:!this.state.processing})}hasAllInputDisabled(){return this.state.processing}get translate(){return this.props.t}render(){return n.createElement(Ne,{title:this.translate("Edit subscription key"),onClose:this.handleCloseClick,disabled:this.state.processing,className:"edit-subscription-dialog"},n.createElement("form",{onSubmit:this.handleFormSubmit,noValidate:!0},n.createElement("div",{className:"form-content"},n.createElement("div",{className:"input textarea required "+(this.state.keyError?"error":"")},n.createElement("label",{htmlFor:"edit-tag-form-name"},n.createElement(f.c,null,"Subscription key")),n.createElement("textarea",{id:"edit-subscription-form-key",name:"key",value:this.state.key,onKeyUp:this.handleKeyInputKeyUp,onChange:this.handleInputChange,disabled:this.hasAllInputDisabled(),ref:this.keyInputRef,className:"required full_report",required:"required",autoComplete:"off",autoFocus:!0})),n.createElement("div",{className:"input-file-chooser-wrapper"},n.createElement("div",{className:"input text"},n.createElement("input",{type:"file",ref:this.fileUploaderRef,disabled:this.hasAllInputDisabled(),onChange:this.handleSelectSubscriptionKeyFile}),this.state.keyError&&n.createElement("div",{className:"key error-message"},this.state.keyError)))),n.createElement("div",{className:"submit-wrapper clearfix"},n.createElement(nt,{disabled:this.hasAllInputDisabled(),processing:this.state.processing,value:this.translate("Save")}),n.createElement(qe,{disabled:this.hasAllInputDisabled(),onClick:this.handleCloseClick}))))}}at.propTypes={context:i().any,onClose:i().func,actionFeedbackContext:i().any,dialogContext:i().any,administrationWorkspaceContext:i().any,t:i().func};const rt=U($(d(g((0,v.Z)("common")(at)))));class it extends n.Component{render(){return n.createElement("div",{className:"illustration icon-feedback"},"success"===this.props.name&&n.createElement("svg",{id:"icon-feedback-success",className:"animated",xmlns:"http://www.w3.org/2000/svg",width:"170",height:"170",viewBox:"0 0 70 70"},n.createElement("circle",{className:"success-animation-circle",cx:"35",cy:"35",r:"24",stroke:"#000",strokeWidth:"3",strokeLinecap:"round",fill:"transparent"}),n.createElement("polyline",{className:"success-animation-line",stroke:"#000",strokeWidth:"3",points:"23 34 34 43 47 27",linecap:"round",fill:"transparent"})),"warning"===this.props.name&&n.createElement("svg",{id:"icon-feedback-warning",className:"animated",width:"160",height:"160",xmlns:"http://www.w3.org/2000/svg",viewBox:"-16 -16 100 100"},n.createElement("g",{transform:"translate(1.285714 1.857143)",fill:"none",fillRule:"evenodd"},n.createElement("path",{className:"warning-animation-line",d:"M.71428571 70.1428571H70.7142857c-23.3333333-46.6666666-35-69.99999996-35-69.99999996L.71428571 70.1428571z",stroke:"#000",strokeWidth:"4",strokeLinejoin:"round"}),n.createElement("path",{className:"warning-animation-line",stroke:"#000",strokeWidth:"5",fill:"#000",fillRule:"nonzero",strokeLinecap:"round",d:"M36.3214286 28.2244898v19.4285714"}),n.createElement("circle",{className:"warning-animation-circle",fill:"#000",cx:"36.2142857",cy:"57.6428571",r:"3.5"}))),"error"===this.props.name&&n.createElement("svg",{id:"icon-feedback-error",className:"animated",xmlns:"http://www.w3.org/2000/svg",width:"170",height:"170",viewBox:"0 0 70 70"},n.createElement("circle",{className:"error-animation-circle",cx:"35",cy:"35",r:"24",stroke:"#000000",strokeWidth:"3",strokeLinecap:"round",fill:"transparent"}),n.createElement("polyline",{className:"error-animation-line",stroke:"#000",strokeWidth:"3",points:"26 26 44 44",linecap:"round",fill:"transparent"}),n.createElement("polyline",{className:"error-animation-line",stroke:"#000",strokeWidth:"3",points:"44 26 26 44",linecap:"round",fill:"transparent"})))}}it.defaultProps={},it.propTypes={name:i().string};const lt=it;class ot extends n.Component{constructor(e){super(e),this.state=this.defaultState,this.bindCallbacks()}get defaultState(){return{loading:!0,customerId:"",subscriptionId:"",users:null,email:"",expiry:null,created:null,data:null,activeUsers:null}}async componentDidMount(){this.findActiveUsers(),this.findSubscriptionKey()}async componentDidUpdate(e){await this.handleMustRefreshSubscriptionKey(e.administrationWorkspaceContext.must.refreshSubscriptionKey),await this.handleMustEditSubscriptionKey(e.administrationWorkspaceContext.must.editSubscriptionKey)}bindCallbacks(){this.handleRenewKey=this.handleRenewKey.bind(this),this.handleUpdateKey=this.handleUpdateKey.bind(this)}async handleMustRefreshSubscriptionKey(e){this.props.administrationWorkspaceContext.must.refreshSubscriptionKey!==e&&this.props.administrationWorkspaceContext.must.refreshSubscriptionKey&&(await this.findActiveUsers(),await this.findSubscriptionKey(),this.props.administrationWorkspaceContext.onResetActionsSettings())}async handleMustEditSubscriptionKey(e){this.props.administrationWorkspaceContext.must.editSubscriptionKey!==e&&this.props.administrationWorkspaceContext.must.editSubscriptionKey&&(this.openEditSubscriptionKey(),this.props.administrationWorkspaceContext.onResetActionsSettings())}openEditSubscriptionKey(){const e={key:this.state.data};this.props.context.setContext({editSubscriptionKey:e}),this.props.dialogContext.open(rt)}async findActiveUsers(){const e=await this.getActiveUsers();this.setState({activeUsers:e})}async findSubscriptionKey(){try{const e=await this.props.context.onGetSubscriptionKeyRequested(),t=e.customer_id,s=e.subscription_id,n=e.users,a=e.email,r=e.expiry,i=e.created,l=e.data;this.setState({loading:!1,customerId:t,subscriptionId:s,users:n,email:a,expiry:r,created:i,data:l})}catch(e){this.handleSubscriptionError(e)}}handleSubscriptionError(e){if("PassboltSubscriptionError"===e.name){const t=e.subscription,s=t.customer_id||"N/A",n=t.subscription_id,a=t.users,r=t.email||"N/A",i=t.expiry,l=t.created,o=t.data;this.setState({loading:!1,customerId:s,subscriptionId:n,users:a,email:r,expiry:i,created:l,data:o})}else this.setState({loading:!1})}handleRenewKey(){this.hasLimitUsersExceeded()?this.props.navigationContext.onGoToNewTab(`https://www.passbolt.com/subscription/ee/update/qty?subscription_id=${this.state.subscriptionId}&customer_id=${this.state.customerId}`):(this.hasSubscriptionKeyExpired()||this.hasSubscriptionKeyGoingToExpire())&&this.props.navigationContext.onGoToNewTab(`https://www.passbolt.com/subscription/ee/update/renew?subscription_id=${this.state.subscriptionId}&customer_id=${this.state.customerId}`)}handleUpdateKey(){this.openEditSubscriptionKey()}isLoading(){return this.state.loading}hasSubscriptionKeyExpired(){return Xe.ou.fromISO(this.state.expiry)-1e3&&s<0?this.translate("Just now"):t.toRelative({locale:this.props.context.locale})}async getActiveUsers(){return(await this.props.context.port.request("passbolt.users.get-all")).filter((e=>e.active)).length}get translate(){return this.props.t}render(){return n.createElement("div",{className:"row"},!this.isLoading()&&n.createElement("div",{className:"subscription-key col8"},n.createElement("h3",null,n.createElement(f.c,null,"Subscription key details")),n.createElement("div",{className:"feedback-card"},this.hasValidSubscription()&&!this.hasSubscriptionKeyGoingToExpire()&&n.createElement(lt,{name:"success"}),this.hasInvalidSubscription()&&n.createElement(lt,{name:"warning"}),this.hasValidSubscription()&&this.hasSubscriptionKeyGoingToExpire()&&n.createElement(lt,{name:"error"}),n.createElement("div",{className:"subscription-information"},!this.hasSubscriptionKey()&&n.createElement(n.Fragment,null,n.createElement("h4",null,n.createElement(f.c,null,"Your subscription key is either missing or not valid.")),n.createElement("p",null,n.createElement(f.c,null,"Sorry your subscription is either missing or not readable."),n.createElement("br",null),n.createElement(f.c,null,"Update the subscription key and try again.")," ",n.createElement(f.c,null,"If this does not work get in touch with support."))),this.hasValidSubscription()&&this.hasSubscriptionKeyGoingToExpire()&&n.createElement("h4",null,n.createElement(f.c,null,"Your subscription key is going to expire.")),this.hasSubscriptionKey()&&this.hasInvalidSubscription()&&n.createElement("h4",null,n.createElement(f.c,null,"Your subscription key is not valid.")),this.hasValidSubscription()&&!this.hasSubscriptionKeyGoingToExpire()&&n.createElement("h4",null,n.createElement(f.c,null,"Your subscription key is valid and up to date!")),this.hasSubscriptionKey()&&n.createElement("ul",null,n.createElement("li",{className:"customer-id"},n.createElement("span",{className:"label"},n.createElement(f.c,null,"Customer id:")),n.createElement("span",{className:"value"},this.state.customerId)),n.createElement("li",{className:"subscription-id"},n.createElement("span",{className:"label"},n.createElement(f.c,null,"Subscription id:")),n.createElement("span",{className:"value"},this.state.subscriptionId)),n.createElement("li",{className:"email"},n.createElement("span",{className:"label"},n.createElement(f.c,null,"Email:")),n.createElement("span",{className:"value"},this.state.email)),n.createElement("li",{className:"users"},n.createElement("span",{className:"label "+(this.hasLimitUsersExceeded()?"error":"")},n.createElement(f.c,null,"Users limit:")),n.createElement("span",{className:"value "+(this.hasLimitUsersExceeded()?"error":"")},this.state.users," (",n.createElement(f.c,null,"currently:")," ",this.state.activeUsers,")")),n.createElement("li",{className:"created"},n.createElement("span",{className:"label"},n.createElement(f.c,null,"Valid from:")),n.createElement("span",{className:"value"},this.formatDate(this.state.created))),n.createElement("li",{className:"expiry"},n.createElement("span",{className:`label ${this.hasSubscriptionKeyExpired()?"error":""} ${this.hasSubscriptionKeyGoingToExpire()?"warning":""}`},n.createElement(f.c,null,"Expires on:")),n.createElement("span",{className:`value ${this.hasSubscriptionKeyExpired()?"error":""} ${this.hasSubscriptionKeyGoingToExpire()?"warning":""}`},this.formatDate(this.state.expiry)," (",`${this.hasSubscriptionKeyExpired()?this.translate("expired "):""}${this.formatDateTimeAgo(this.state.expiry)}`,")"))),this.hasSubscriptionToRenew()&&n.createElement(n.Fragment,null,this.hasSubscriptionKey()&&n.createElement("a",{className:"button primary big",role:"button",onClick:this.handleRenewKey},n.createElement(f.c,null,"Renew key")),!this.hasSubscriptionKey()&&n.createElement("a",{className:"button primary big",role:"button",onClick:this.handleUpdateKey},n.createElement(f.c,null,"Update key")),n.createElement("a",{target:"_blank",rel:"noopener noreferrer",href:"https://www.passbolt.com/contact"},n.createElement(f.c,null,"or, contact us")))))),!this.isLoading()&&n.createElement("div",{className:"col4 last"},n.createElement("div",{className:"sidebar-help"},n.createElement("h3",null,n.createElement(f.c,null,"Need help?")),n.createElement("p",null,n.createElement(f.c,null,"For any change or question related to your passbolt subscription, kindly contact our sales team.")),n.createElement("a",{className:"button",target:"_blank",rel:"noopener noreferrer",href:"https://www.passbolt.com/contact"},n.createElement(ue,{name:"envelope"}),n.createElement("span",null,n.createElement(f.c,null,"Contact Sales"))))))}}ot.propTypes={context:i().any,navigationContext:i().any,administrationWorkspaceContext:i().object,dialogContext:i().any,t:i().func};const ct=U(ae($(g((0,v.Z)("common")(ot)))));class ht extends n.Component{constructor(e){super(e),this.state=this.defaultState,this.bindCallbacks()}get defaultState(){return{loading:!0,processing:!1,locale:""}}async componentDidMount(){this.setState({locale:this.props.context.siteSettings.locale})}async componentDidUpdate(e){await this.handleMustSave(e.administrationWorkspaceContext.must.save)}bindCallbacks(){this.handleInputChange=this.handleInputChange.bind(this)}async handleMustSave(e){this.props.administrationWorkspaceContext.must.save!==e&&this.props.administrationWorkspaceContext.must.save&&(await this.handleFormSubmit(),this.props.administrationWorkspaceContext.onResetActionsSettings())}handleInputChange(e){const t=e.target,s=t.value,n=t.name;this.setState({[n]:s}),this.handleEnabledSaveButton()}handleEnabledSaveButton(){this.props.administrationWorkspaceContext.can.save||this.props.administrationWorkspaceContext.onSaveEnabled()}hasAllInputDisabled(){return this.state.processing||this.state.loading}async handleFormSubmit(){if(!this.state.processing){await this.toggleProcessing();try{await this.props.administrationWorkspaceContext.onSaveLocaleRequested(this.state.locale),await this.handleSaveSuccess()}catch(e){await this.handleSaveError(e)}}}async handleSaveSuccess(){this.props.context.onRefreshLocaleRequested(this.state.locale),await this.props.actionFeedbackContext.displaySuccess(this.translate("The internationalization settings were updated.")),this.setState({processing:!1})}async handleSaveError(e){"UserAbortsOperationError"===e.name||(console.error(e),await this.handleError(e)),this.setState({processing:!1})}async handleError(e){await this.props.actionFeedbackContext.displayError(e.message)}async toggleProcessing(){const e=this.state.processing;return this.setState({processing:!e})}get supportedLocales(){return this.props.context.siteSettings.supportedLocales||[]}get translate(){return this.props.t}render(){return n.createElement("div",{className:"row"},n.createElement("div",{className:"internationalisation-settings col7"},n.createElement("h3",null,n.createElement(f.c,null,"Internationalisation")),n.createElement("form",{className:"form"},n.createElement("div",{className:"input select locale"},n.createElement("label",{htmlFor:"app-locale-input"},n.createElement(f.c,null,"Language")),n.createElement("select",{className:"medium",id:"locale-input",name:"locale",value:this.state.locale,onChange:this.handleInputChange},this.supportedLocales.map((e=>n.createElement("option",{key:e.locale,value:e.locale},e.label)))),n.createElement("p",null,n.createElement(f.c,null,"The default language of the organisation."))))),n.createElement("div",{className:"col4 last"},n.createElement("div",{className:"sidebar-help"},n.createElement("h3",null,n.createElement(f.c,null,"Want to contribute?")),n.createElement("p",null,n.createElement(f.c,null,"Your language is missing or you discovered an error in the translation, help us to improve passbolt.")),n.createElement("a",{className:"button",href:"https://help.passbolt.com/contribute/translation",target:"_blank",rel:"noopener noreferrer"},n.createElement(ue,{name:"heart-o"}),n.createElement("span",null,n.createElement(f.c,null,"Contribute"))))))}}ht.propTypes={context:i().object,administrationWorkspaceContext:i().object,actionFeedbackContext:i().any,t:i().func};const dt=U(d($((0,v.Z)("common")(ht))));class ut extends n.Component{isMfaSelected(){return H.MFA===this.props.administrationWorkspaceContext.selectedAdministration}isUserDirectorySelected(){return H.USER_DIRECTORY===this.props.administrationWorkspaceContext.selectedAdministration}isEmailNotificationsSelected(){return H.EMAIL_NOTIFICATION===this.props.administrationWorkspaceContext.selectedAdministration}isSubscriptionSelected(){return H.SUBSCRIPTION===this.props.administrationWorkspaceContext.selectedAdministration}isInternationalizationSelected(){return H.INTERNATIONALIZATION===this.props.administrationWorkspaceContext.selectedAdministration}render(){return n.createElement("div",{id:"container",className:"page administration"},n.createElement("div",{id:"app",tabIndex:"1000"},n.createElement("div",{className:"header first"},n.createElement(ie,null)),n.createElement("div",{className:"header second"},n.createElement(oe,null),n.createElement(Qe,{disabled:!0}),n.createElement(me,{baseUrl:this.props.context.trustedDomain||this.props.context.userSettings.getTrustedDomain(),user:this.props.context.loggedInUser})),n.createElement("div",{className:"header third"},n.createElement("div",{className:"col1 main-action-wrapper"}),n.createElement(ze,null)),n.createElement("div",{className:"panel main"},n.createElement("div",null,n.createElement("div",{className:"panel left"},n.createElement(Ee,null)),n.createElement("div",{className:"panel middle"},n.createElement(Ke,null),n.createElement("div",{className:"workspace-main"},n.createElement("div",{className:"grid grid-responsive-12"},this.isMfaSelected()&&n.createElement(ve,null),this.isUserDirectorySelected()&&n.createElement(Ve,null),this.isEmailNotificationsSelected()&&n.createElement(Ye,null),this.isSubscriptionSelected()&&n.createElement(ct,null),this.isInternationalizationSelected()&&n.createElement(dt,null))))))))}}ut.propTypes={context:i().any,administrationWorkspaceContext:i().object};const pt=U($(ut));function mt(){return mt=Object.assign||function(e){for(var t=1;tthis.props.location.pathname.endsWith(e);return n.createElement("div",{className:"navigation-secondary navigation-shortcuts"},n.createElement("ul",null,n.createElement("li",null,n.createElement("div",{className:"row "+(e("profile")?"selected":"")},n.createElement("div",{className:"main-cell-wrapper"},n.createElement("div",{className:"main-cell"},n.createElement("a",{onClick:this.props.navigationContext.onGoToUserSettingsProfileRequested},n.createElement("span",null,n.createElement(f.c,null,"Profile"))))))),n.createElement("li",null,n.createElement("div",{className:"row "+(e("keys")?"selected":"")},n.createElement("div",{className:"main-cell-wrapper"},n.createElement("div",{className:"main-cell"},n.createElement("a",{onClick:this.props.navigationContext.onGoToUserSettingsKeysRequested},n.createElement("span",null,n.createElement(f.c,null,"Keys inspector"))))))),n.createElement("li",null,n.createElement("div",{className:"row "+(e("passphrase")?"selected":"")},n.createElement("div",{className:"main-cell-wrapper"},n.createElement("div",{className:"main-cell"},n.createElement("a",{onClick:this.props.navigationContext.onGoToUserSettingsPassphraseRequested},n.createElement("span",null,n.createElement(f.c,null,"Passphrase"))))))),n.createElement("li",null,n.createElement("div",{className:"row "+(e("security-token")?"selected":"")},n.createElement("div",{className:"main-cell-wrapper"},n.createElement("div",{className:"main-cell"},n.createElement("a",{onClick:this.props.navigationContext.onGoToUserSettingsSecurityTokenRequested},n.createElement("span",null,n.createElement(f.c,null,"Security Token"))))))),this.canIUseThemeCapability&&n.createElement("li",null,n.createElement("div",{className:"row "+(e("theme")?"selected":"")},n.createElement("div",{className:"main-cell-wrapper"},n.createElement("div",{className:"main-cell"},n.createElement("a",{onClick:this.props.navigationContext.onGoToUserSettingsThemeRequested},n.createElement("span",null,n.createElement(f.c,null,"Theme"))))))),this.isMfaEnabled&&n.createElement("li",null,n.createElement("div",{className:"row "+(e("mfa")?"selected":"")},n.createElement("div",{className:"main-cell-wrapper"},n.createElement("div",{className:"main-cell"},n.createElement("a",{onClick:this.props.navigationContext.onGoToUserSettingsMfaRequested},n.createElement("span",null,n.createElement(f.c,null,"Multi Factor Authentication"))))))),this.canIUseMobileCapability&&n.createElement("li",null,n.createElement("div",{className:"row "+(e("mobile")?"selected":"")},n.createElement("div",{className:"main-cell-wrapper"},n.createElement("div",{className:"main-cell"},n.createElement("a",{onClick:this.props.navigationContext.onGoToUserSettingsMobileRequested},n.createElement("span",null,n.createElement(f.c,null,"Mobile setup")))))))))}}bt.propTypes={context:i().any,navigationContext:i().any,history:i().object,location:i().object,t:i().func};const yt=U((0,N.EN)(ae((0,v.Z)("common")(bt))));class ft extends n.Component{get items(){return[n.createElement(Fe,{key:"bread-1",name:this.translate("All users"),onClick:this.props.navigationContext.onGoToUsersRequested}),n.createElement(Fe,{key:"bread-2",name:this.loggedInUserName,onClick:this.props.navigationContext.onGoToUserSettingsProfileRequested}),n.createElement(Fe,{key:"bread-3",name:this.getLastBreadcrumbItemName,onClick:this.onLastBreadcrumbClick.bind(this)})]}get loggedInUserName(){const e=this.props.context.loggedInUser;return e?`${e.profile.first_name} ${e.profile.last_name}`:""}get getLastBreadcrumbItemName(){const e={profile:this.translate("Profile"),passphrase:this.translate("Passphrase"),"security-token":this.translate("Security Token"),theme:this.translate("Theme"),mfa:this.translate("Multi Factor Authentication"),keys:this.translate("Keys inspector"),mobile:this.translate("Mobile transfer")};return e[Object.keys(e).find((e=>this.props.location.pathname.endsWith(e)))]}async onLastBreadcrumbClick(){const e=this.props.location.pathname;this.props.history.push({pathname:e})}get translate(){return this.props.t}render(){return n.createElement(Oe,{items:this.items})}}ft.propTypes={context:i().any,location:i().object,history:i().object,navigationContext:i().any,t:i().func};const vt=U((0,N.EN)(ae((0,v.Z)("common")(ft))));class Ct extends n.Component{render(){return n.createElement("iframe",{src:`${this.props.context.trustedDomain}/mfa/setup/select`,width:"100%",height:"100%"})}}Ct.propTypes={context:i().any};const wt=U(Ct);class St extends n.Component{render(){return n.createElement("div",null,n.createElement("div",{className:"header second"},n.createElement(oe,null),n.createElement(Qe,{disabled:!0}),n.createElement(me,{baseUrl:this.props.context.trustedDomain,user:this.props.context.loggedInUser})),n.createElement("div",{className:"header third"}),n.createElement("div",{className:"panel main"},n.createElement("div",{className:"panel left"},n.createElement(yt,null)),n.createElement("div",{className:"panel middle"},n.createElement(vt,null),n.createElement(N.AW,{path:"/app/settings/mfa",component:wt}))))}}St.propTypes={context:i().any};const kt=(0,N.EN)(U(St));class xt extends n.Component{constructor(e){super(e),this.initEventHandlers(),this.createReferences()}initEventHandlers(){this.handleCloseClick=this.handleCloseClick.bind(this)}createReferences(){this.loginLinkRef=n.createRef()}handleCloseClick(){this.goToLogin()}goToLogin(){this.loginLinkRef.current.click()}get loginUrl(){let e=this.props.context.userSettings&&this.props.context.userSettings.getTrustedDomain();return e=e||this.props.context.trustedDomain,`${e}/auth/login`}get translate(){return this.props.t}render(){return n.createElement(Ne,{title:this.translate("Session Expired"),onClose:this.handleCloseClick,className:"session-expired-dialog"},n.createElement("div",{className:"form-content"},n.createElement("p",null,n.createElement(f.c,null,"Your session has expired, you need to sign in."))),n.createElement("div",{className:"submit-wrapper clearfix"},n.createElement("a",{ref:this.loginLinkRef,href:this.loginUrl,className:"primary button",target:"_parent",role:"button",rel:"noopener noreferrer"},n.createElement(f.c,null,"Sign in"))))}}xt.propTypes={context:i().any,t:i().func};const Nt=U((0,N.EN)((0,v.Z)("common")(xt)));class Tt extends n.Component{constructor(e){super(e),this.bindCallbacks(),this.scheduledCheckIsAuthenticatedTimeout=null}bindCallbacks(){this.handleSessionExpiredEvent=this.handleSessionExpiredEvent.bind(this)}componentDidMount(){this.scheduleCheckIsAuthenticated()}componentWillUnmount(){clearTimeout(this.scheduledCheckIsAuthenticatedTimeout)}scheduleCheckIsAuthenticated(){this.scheduledCheckIsAuthenticatedTimeout=setTimeout((async()=>{await this.checkIsAuthenticated()?this.scheduleCheckIsAuthenticated():this.handleSessionExpiredEvent()}),6e4)}async checkIsAuthenticated(){return await this.props.context.onCheckIsAuthenticatedRequested()}handleSessionExpiredEvent(){this.props.dialogContext.open(Nt)}render(){return n.createElement(n.Fragment,null)}}Tt.propTypes={context:i().any,dialogContext:i().any};const qt=U(g(Tt));function Ut(){return Ut=Object.assign||function(e){for(var t=1;t{},close:()=>{}});class It extends n.Component{constructor(e){super(e),this.state=this.defaultState}get defaultState(){return{announcements:[],show:(e,t)=>{const s=(0,l.Z)();return this.setState({announcements:[...this.state.announcements,{key:s,Announcement:e,AnnouncementProps:t}]}),s},close:async e=>await this.setState({announcements:this.state.announcements.filter((t=>e!==t.key))})}}render(){return n.createElement(At.Provider,{value:this.state},this.props.children)}}function Dt(e){return class extends n.Component{render(){return n.createElement(At.Consumer,null,(t=>n.createElement(e,Ut({announcementContext:t},this.props))))}}}It.displayName="AnnouncementContextProvider",It.propTypes={children:i().any};class Rt extends n.Component{constructor(e){super(e),this.bindCallbacks()}bindCallbacks(){this.handleClose=this.handleClose.bind(this)}handleClose(){this.props.onClose()}render(){return n.createElement("div",{className:`${this.props.className} announcement`},n.createElement("div",{className:"announcement-content"},this.props.canClose&&n.createElement("a",{className:"announcement-close",onClick:this.handleClose,role:"button"},n.createElement(ue,{name:"close"}),n.createElement("span",{className:"visually-hidden"},"Close")),this.props.children))}}Rt.propTypes={children:i().node,className:i().string,canClose:i().bool,onClose:i().func};const _t=Rt;class Mt extends n.Component{formatDateTimeAgo(e){const t=Xe.ou.fromISO(e),s=t.diffNow().toMillis();return s>-1e3&&s<0?this.translate("Just now"):t.toRelative({locale:this.props.context.locale})}render(){return n.createElement(_t,{className:"subscription",onClose:this.props.onClose,canClose:!0},n.createElement("p",null,n.createElement(f.c,null,"Warning: "),n.createElement(f.c,null,"your subscription key will expire")," ",this.formatDateTimeAgo(this.props.expiry),".",n.createElement("a",{onClick:this.props.navigationContext.onGoToAdministrationSubscriptionRequested},n.createElement(f.c,null,"Manage Subscription"))))}}Mt.propTypes={context:i().any,expiry:i().string,navigationContext:i().any,onClose:i().func};const zt=U(ae(Dt((0,v.Z)("common")(Mt))));class Gt extends n.Component{render(){return n.createElement(_t,{className:"subscription",onClose:this.props.onClose,canClose:!1},n.createElement("p",null,n.createElement(f.c,null,"Warning: "),n.createElement(f.c,null,"your subscription key has expired. The stability of the application is at risk."),n.createElement("a",{onClick:this.props.navigationContext.onGoToAdministrationSubscriptionRequested},n.createElement(f.c,null,"Manage Subscription"))))}}Gt.propTypes={navigationContext:i().any,onClose:i().func,i18n:i().any};const Ot=ae(Dt((0,v.Z)("common")(Gt)));class Pt extends n.Component{render(){return n.createElement(_t,{className:"subscription",onClose:this.props.onClose,canClose:!1},n.createElement("p",null,n.createElement(f.c,null,"Warning: "),n.createElement(f.c,null,"your subscription key is not valid. The stability of the application is at risk."),n.createElement("a",{onClick:this.props.navigationContext.onGoToAdministrationSubscriptionRequested},n.createElement(f.c,null,"Manage Subscription"))))}}Pt.propTypes={navigationContext:i().any,onClose:i().func,i18n:i().any};const Ft=ae(Dt((0,v.Z)("common")(Pt)));class Lt extends n.Component{constructor(e){super(e),this.bindCallbacks()}bindCallbacks(){this.handleAnnouncementSubscriptionEvent=this.handleAnnouncementSubscriptionEvent.bind(this)}componentDidMount(){this.handleAnnouncementSubscriptionEvent()}componentDidUpdate(e){this.handleRefreshSubscriptionAnnouncement(e.context.refreshSubscriptionAnnouncement)}async handleRefreshSubscriptionAnnouncement(e){this.props.context.refreshSubscriptionAnnouncement!==e&&this.props.context.refreshSubscriptionAnnouncement&&(await this.handleAnnouncementSubscriptionEvent(),this.props.context.setContext({refreshSubscriptionAnnouncement:null}))}async handleAnnouncementSubscriptionEvent(){this.hideSubscriptionAnnouncement();try{const e=await this.props.context.onGetSubscriptionKeyRequested();this.isSubscriptionGoingToExpire(e.expiry)&&this.props.announcementContext.show(zt,{expiry:e.expiry})}catch(e){"PassboltSubscriptionError"===e.name?this.props.announcementContext.show(Ot):this.props.announcementContext.show(Ft)}}hideSubscriptionAnnouncement(){const e=[zt,Ot,Ft];this.props.announcementContext.announcements.forEach((t=>{e.some((e=>e===t.Announcement))&&this.props.announcementContext.close(t.key)}))}isSubscriptionGoingToExpire(e){return Xe.ou.fromISO(e)n.createElement(t,Wt({key:e,onClose:()=>this.close(e)},s)))),this.props.children)}}Bt.propTypes={announcementContext:i().any,children:i().any};const jt=Dt(Bt),$t=(e,t)=>t.split(".").reduce(((e,t)=>void 0===e?e:e[t]),e),Ht=(e,t)=>{if(void 0===e||"string"!=typeof e||!e.length)return!1;if((t=t||{}).whitelistedProtocols&&!Array.isArray(t.whitelistedProtocols))throw new TypeError("The whitelistedProtocols should be an array of string.");if(t.defaultProtocol&&"string"!=typeof t.defaultProtocol)throw new TypeError("The defaultProtocol should be a string.");const s=t.whitelistedProtocols||[Vt.HTTP,Vt.HTTPS],n=[Vt.JAVASCRIPT],a=t.defaultProtocol||"";!/^((?!:\/\/).)*:\/\//.test(e)&&a&&(e=`${a}//${e}`);try{const t=new URL(e);return!n.includes(t.protocol)&&!!s.includes(t.protocol)&&t.href}catch(e){return!1}},Vt={FTP:"http:",FTPS:"https:",HTTP:"http:",HTTPS:"https:",JAVASCRIPT:"javascript:",SSH:"ssh:"};class Zt{constructor(e){this.settings=e}canIUse(e){let t=!1;const s=`passbolt.plugins.${e}`,n=$t(this.settings,s)||null;if(n&&"object"==typeof n){const e=$t(n,"enabled");void 0!==e&&!0!==e||(t=!0)}return t}getPluginSettings(e){const t=`passbolt.plugins.${e}`;return $t(this.settings,t)}getRememberMeOptions(){return(this.getPluginSettings("rememberMe")||{}).options||{}}get hasRememberMeUntilILogoutOption(){return void 0!==(this.getRememberMeOptions()||{})[-1]}getServerTimezone(){return $t(this.settings,"passbolt.app.server_timezone")}get termsLink(){const e=$t(this.settings,"passbolt.legal.terms.url");return!!e&&Ht(e)}get privacyLink(){const e=$t(this.settings,"passbolt.legal.privacy_policy.url");return!!e&&Ht(e)}get registrationPublic(){return!0===$t(this.settings,"passbolt.registration.public")}get debug(){return!0===$t(this.settings,"app.debug")}get url(){return $t(this.settings,"app.url")||""}get version(){return $t(this.settings,"app.version.number")}get locale(){return $t(this.settings,"app.locale")||Zt.DEFAULT_LOCALE.locale}async setLocale(e){this.settings.app.locale=e}get supportedLocales(){return $t(this.settings,"passbolt.plugins.locale.options")||Zt.DEFAULT_SUPPORTED_LOCALES}get generatorConfiguration(){return $t(this.settings,"passbolt.plugins.generator.configuration")}static get DEFAULT_SUPPORTED_LOCALES(){return[Zt.DEFAULT_LOCALE]}static get DEFAULT_LOCALE(){return{locale:"en-UK",label:"English"}}}class Yt{constructor(e){this.setToken(e)}setToken(e){this.validate(e),this.token=e}validate(e){if(!e)throw new TypeError("CSRF token cannot be empty.");if("string"!=typeof e)throw new TypeError("CSRF token should be a string.")}toFetchHeaders(){return{"X-CSRF-Token":this.token}}}class Jt{setBaseUrl(e){if(!e)throw new TypeError("ApiClientOption baseUrl is required.");if("string"==typeof e)try{this.baseUrl=new URL(e)}catch(e){throw new TypeError("ApiClientOption baseUrl is invalid.")}else{if(!(e instanceof URL))throw new TypeError("ApiClientOptions baseurl should be a string or URL");this.baseUrl=e}return this}setCsrfToken(e){if(!e)throw new TypeError("ApiClientOption csrfToken is required.");if("string"==typeof e)this.csrfToken=new Yt(e);else{if(!(e instanceof Yt))throw new TypeError("ApiClientOption csrfToken should be a string or a valid CsrfToken.");this.csrfToken=e}return this}setResourceName(e){if(!e)throw new TypeError("ApiClientOptions.setResourceName resourceName is required.");if("string"!=typeof e)throw new TypeError("ApiClientOptions.setResourceName resourceName should be a valid string.");return this.resourceName=e,this}getBaseUrl(){return this.baseUrl}getResourceName(){return this.resourceName}getHeaders(){if(this.csrfToken)return this.csrfToken.toFetchHeaders()}}class Qt extends Error{constructor(e,t={}){super(e),this.name="PassboltSubscriptionError",this.subscription=t}}const Xt=Qt;class es extends n.Component{constructor(e){super(e),this.state=this.defaultState}async componentDidMount(){await this.getLoggedInUser(),await this.getSiteSettings(),this.initLocale(),this.removeSplashScreen()}get defaultState(){return{name:"api",loggedInUser:null,siteSettings:null,trustedDomain:this.baseUrl,basename:new URL(this.baseUrl).pathname,getApiClientOptions:this.getApiClientOptions.bind(this),locale:null,displayTestUserDirectoryDialogProps:{userDirectoryTestResult:null},setContext:e=>{this.setState(e)},onLogoutRequested:()=>this.onLogoutRequested(),onCheckIsAuthenticatedRequested:()=>this.onCheckIsAuthenticatedRequested(),onGetSubscriptionKeyRequested:()=>this.onGetSubscriptionKeyRequested(),onRefreshLocaleRequested:this.onRefreshLocaleRequested.bind(this)}}get isReady(){return null!==this.state.siteSettings&&null!==this.state.locale}get baseUrl(){const e=document.getElementsByTagName("base")&&document.getElementsByTagName("base")[0];return e?e.attributes.href.value.replace(/\/*$/g,""):(console.error("Unable to retrieve the page base tag"),"")}getApiClientOptions(){return(new Jt).setBaseUrl(this.state.trustedDomain).setCsrfToken(this.getCsrfToken())}getCsrfToken(){const e=document.cookie;if(!e)return;const t=e.split("; ");if(!t)return;const s=t.find((e=>e.startsWith("csrfToken")));if(!s)return;const n=s.split("=");return n&&2===n.length?n[1]:void 0}async getLoggedInUser(){const e=this.getApiClientOptions().setResourceName("users"),t=new F(e),s=(await t.get("me")).body;this.setState({loggedInUser:s})}async getSiteSettings(){const e=this.getApiClientOptions().setResourceName("settings"),t=new F(e),s=await t.findAll();await this.setState({siteSettings:new Zt(s.body)})}async initLocale(){const e=await this.getUserLocale();if(e)return this.setState({locale:e.locale});const t=this.state.siteSettings.locale;return this.setState({locale:t})}async getUserLocale(){const e=(await this.getUserSettings()).find((e=>"locale"===e.property));if(e)return this.state.siteSettings.supportedLocales.find((t=>t.locale===e.value))}async getUserSettings(){const e=this.getApiClientOptions().setResourceName("account/settings"),t=new F(e);return(await t.findAll()).body}removeSplashScreen(){document.getElementsByTagName("html")[0].classList.remove("launching")}onLogoutRequested(){document.location.href=`${this.state.trustedDomain}/auth/logout`}async onCheckIsAuthenticatedRequested(){try{const e=this.getApiClientOptions().setResourceName("auth"),t=new F(e);return await t.get("is-authenticated"),!0}catch(e){if(e instanceof M&&401===e.data.code)return!1;throw e}}async onGetSubscriptionKeyRequested(){try{const e=this.getApiClientOptions().setResourceName("ee/subscription"),t=new F(e);return(await t.get("key")).body}catch(e){if(e instanceof M&&e.data&&402===e.data.code){const t=e.data.body;throw new Xt(e.message,t)}throw e}}onRefreshLocaleRequested(e){this.state.siteSettings.setLocale(e),this.initLocale()}render(){const e=this.isReady;return n.createElement(A.Provider,{value:this.state},e&&this.props.children)}}es.propTypes={children:i().any};const ts=es;var ss=s(1932),ns=s(8718),as=s(3554);class rs extends n.Component{constructor(e){super(e),this.state=this.defaultState}get defaultState(){return{ready:!1}}async componentDidMount(){await ss.Z.use(ns.Db).use(as.Z).init({lng:this.locale,load:"currentOnly",react:{useSuspense:!1},backend:{loadPath:this.props.loadingPath||"/locales/{{lng}}/{{ns}}.json"},supportedLngs:this.supportedLocales,fallbackLng:!1,ns:["common"],defaultNS:"common",keySeparator:!1,nsSeparator:!1,debug:!1}),this.setState({ready:!0})}get supportedLocales(){return this.props.context.siteSettings.supportedLocales?this.props.context.siteSettings.supportedLocales.map((e=>e.locale)):[this.locale]}get locale(){return this.props.context.locale}async componentDidUpdate(e){await this.handleLocaleChange(e.context.locale)}async handleLocaleChange(e){this.locale!==e&&await ss.Z.changeLanguage(this.locale)}get isReady(){return this.state.ready}render(){return n.createElement(n.Fragment,null,this.isReady&&this.props.children)}}rs.propTypes={context:i().any,loadingPath:i().any,children:i().any};const is=U(rs);class ls extends n.Component{render(){return n.createElement(ts,null,n.createElement(A.Consumer,null,(e=>n.createElement(is,{loadingPath:`${e.trustedDomain}/locales/{{lng}}/{{ns}}.json`},n.createElement(h,null,n.createElement(m,null,n.createElement(It,null,n.createElement(y,null,n.createElement(k,null),n.createElement(qt,null),e.loggedInUser&&"admin"===e.loggedInUser.role.name&&e.siteSettings.canIUse("ee")&&n.createElement(Kt,null),n.createElement(x.VK,{basename:e.basename},n.createElement(ne,null,n.createElement(N.rs,null,n.createElement(N.AW,{exact:!0,path:["/app/administration/subscription"]}),n.createElement(N.AW,{path:"/app/administration"},n.createElement(B,null,n.createElement(Y,null),n.createElement(X,null),n.createElement(jt,null),n.createElement(pt,null))),n.createElement(N.AW,{path:"/app/settings/mfa"},n.createElement(Y,null),n.createElement(X,null),n.createElement(jt,null),n.createElement("div",{id:"container",className:"page settings"},n.createElement("div",{id:"app",className:"app",tabIndex:"1000"},n.createElement("div",{className:"header first"},n.createElement(ie,null)),n.createElement(kt,null))))))),n.createElement(Et,null)))))))))}}const os=ls,cs=document.createElement("div");document.body.appendChild(cs),a.render(n.createElement(os,null),cs)}},a={};function r(e){var t=a[e];if(void 0!==t)return t.exports;var s=a[e]={exports:{}};return n[e].call(s.exports,s,s.exports,r),s.exports}r.m=n,e=[],r.O=(t,s,n,a)=>{if(!s){var i=1/0;for(h=0;h=a)&&Object.keys(r.O).every((e=>r.O[e](s[o])))?s.splice(o--,1):(l=!1,a0&&e[h-1][2]>a;h--)e[h]=e[h-1];e[h]=[s,n,a]},r.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return r.d(t,{a:t}),t},s=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,r.t=function(e,n){if(1&n&&(e=this(e)),8&n)return e;if("object"==typeof e&&e){if(4&n&&e.__esModule)return e;if(16&n&&"function"==typeof e.then)return e}var a=Object.create(null);r.r(a);var i={};t=t||[null,s({}),s([]),s(s)];for(var l=2&n&&e;"object"==typeof l&&!~t.indexOf(l);l=s(l))Object.getOwnPropertyNames(l).forEach((t=>i[t]=()=>e[t]));return i.default=()=>e,r.d(a,i),a},r.d=(e,t)=>{for(var s in t)r.o(t,s)&&!r.o(e,s)&&Object.defineProperty(e,s,{enumerable:!0,get:t[s]})},r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.j=978,(()=>{var e={978:0};r.O.j=t=>0===e[t];var t=(t,s)=>{var n,a,[i,l,o]=s,c=0;for(n in l)r.o(l,n)&&(r.m[n]=l[n]);if(o)var h=o(r);for(t&&t(s);cr(4569)));i=r.O(i)})(); \ No newline at end of file diff --git a/webroot/js/app/api-recover.js b/webroot/js/app/api-recover.js index 5ea0df04ec..449679460d 100644 --- a/webroot/js/app/api-recover.js +++ b/webroot/js/app/api-recover.js @@ -1,2 +1,2 @@ /*! For license information please see api-recover.js.LICENSE.txt */ -(()=>{"use strict";var e,t,r,n={1873:(e,t,r)=>{var n=r(7294),o=r(3935),s=r(2137),a=r(6610),i=r(5991),c=r(379),l=r(6070),u=r(7608),h=r(7757),p=r.n(h),f=r(2122);var v=n.createContext({user:null,users:null,roles:null,rememberMeOptions:{},resources:null,resource:null,shareResources:null,selectedResources:null,selectedUser:null,folders:null,resourceCommentId:null,mustRefreshComments:!1,siteSettings:null,userSettings:null,onCheckIsAuthenticatedRequested:null});function m(e){return function(t){(0,c.Z)(h,t);var r,o,s=(r=h,o=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,t=(0,u.Z)(r);if(o){var n=(0,u.Z)(this).constructor;e=Reflect.construct(t,arguments,n)}else e=t.apply(this,arguments);return(0,l.Z)(this,e)});function h(){return(0,a.Z)(this,h),s.apply(this,arguments)}return(0,i.Z)(h,[{key:"render",value:function(){var t=this;return n.createElement(v.Consumer,null,(function(r){return n.createElement(e,(0,f.Z)({context:r},t.props))}))}}]),h}(n.Component)}const d=v;var g=r(5697),w=r.n(g),y=r(4699),q=r(6156),E=r(5366);const b=function(e){(0,c.Z)(o,e);var t,r,n=(t=o,r=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,n=(0,u.Z)(t);if(r){var o=(0,u.Z)(this).constructor;e=Reflect.construct(n,arguments,o)}else e=n.apply(this,arguments);return(0,l.Z)(this,e)});function o(e,t){var r;return(0,a.Z)(this,o),(r=n.call(this,e)).name="PassboltApiFetchError",r.data=t||{},r}return o}((0,E.Z)(Error));const x=function(e){(0,c.Z)(o,e);var t,r,n=(t=o,r=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,n=(0,u.Z)(t);if(r){var o=(0,u.Z)(this).constructor;e=Reflect.construct(n,arguments,o)}else e=n.apply(this,arguments);return(0,l.Z)(this,e)});function o(){var e;return(0,a.Z)(this,o),(e=n.call(this,"An internal error occurred. The server response could not be parsed. Please contact your administrator.")).name="PassboltBadResponseError",e}return o}((0,E.Z)(Error));const k=function(e){(0,c.Z)(o,e);var t,r,n=(t=o,r=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,n=(0,u.Z)(t);if(r){var o=(0,u.Z)(this).constructor;e=Reflect.construct(n,arguments,o)}else e=n.apply(this,arguments);return(0,l.Z)(this,e)});function o(e){var t;return(0,a.Z)(this,o),e=e||"The service is unavailable",(t=n.call(this,e)).name="PassboltServiceUnavailableError",t}return o}((0,E.Z)(Error));function R(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function Z(e){for(var t=1;t-1?S:e.indexOf("samsungbrowser")>-1?"samsung":e.indexOf("opera")>-1||e.indexOf("opr")>-1?"opera":e.indexOf("trident")>-1?"internet-explorer":e.indexOf("edge")>-1?"edge":e.indexOf("chrome")>-1?z:e.indexOf("safari")>-1?"safari":"unknown"}const O=function(e,t){return t.split(".").reduce((function(e,t){return void 0===e?e:e[t]}),e)};function B(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var r,n=(0,u.Z)(e);if(t){var o=(0,u.Z)(this).constructor;r=Reflect.construct(n,arguments,o)}else r=n.apply(this,arguments);return(0,l.Z)(this,r)}}var L=n.createContext({userId:null,token:null,state:null,onInitializeRecoverRequested:function(){}}),A=function(e){(0,c.Z)(l,e);var t,r,o=B(l);function l(e){var t;return(0,a.Z)(this,l),(t=o.call(this,e)).state=Object.assign(t.defaultState,e.value),t}return(0,i.Z)(l,[{key:"defaultState",get:function(){return{userId:null,token:null,state:N.INITIAL_STATE,onInitializeRecoverRequested:this.onInitializeRecoverRequested.bind(this)}}},{key:"onInitializeRecoverRequested",value:(r=(0,s.Z)(p().mark((function e(){return p().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(this.state.userId&&this.state.token){e.next=2;break}return e.abrupt("return",this.setState({state:N.ERROR_STATE}));case 2:if(this.isBrowserSupported()){e.next=4;break}return e.abrupt("return",this.setState({state:N.DOWNLOAD_SUPPORTED_BROWSER_STATE}));case 4:return e.next=6,this.verifyRecoverInfo().then(this.handleRecoverVerifySuccess.bind(this)).catch(this.handleRecoverVerifyError.bind(this));case 6:case"end":return e.stop()}}),e,this)}))),function(){return r.apply(this,arguments)})},{key:"handleRecoverVerifySuccess",value:function(){this.setState({state:N.INSTALL_EXTENSION_STATE})}},{key:"handleRecoverVerifyError",value:function(e){return e instanceof b&&O(e,"data.body.token.expired")?this.setState({state:N.TOKEN_EXPIRED_STATE}):this.setState({state:N.ERROR_STATE})}},{key:"isBrowserSupported",value:function(){var e=C();return[z,S].includes(e)}},{key:"verifyRecoverInfo",value:(t=(0,s.Z)(p().mark((function e(){var t,r,n,o;return p().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return(t=this.props.context.getApiClientOptions()).setResourceName("setup"),r=new T(t),e.next=5,r.get("recover/".concat(this.state.userId,"/").concat(this.state.token));case 5:return n=e.sent,o=n.body,e.abrupt("return",o);case 8:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})},{key:"render",value:function(){return n.createElement(L.Provider,{value:this.state},this.props.children)}}]),l}(n.Component);A.propTypes={context:w().any,value:w().any,children:w().any};const U=m(A);function P(e){return function(t){(0,c.Z)(o,t);var r=B(o);function o(){return(0,a.Z)(this,o),r.apply(this,arguments)}return(0,i.Z)(o,[{key:"render",value:function(){var t=this;return n.createElement(L.Consumer,null,(function(r){return n.createElement(e,(0,f.Z)({apiRecoverContext:r},t.props))}))}}]),o}(n.Component)}var N={INITIAL_STATE:"Initial state",DOWNLOAD_SUPPORTED_BROWSER_STATE:"Download supported browser state",INSTALL_EXTENSION_STATE:"Install extension state",TOKEN_EXPIRED_STATE:"Token expired state",ERROR_STATE:"Error state"};const M=function(e){(0,c.Z)(s,e);var t,r,o=(t=s,r=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,n=(0,u.Z)(t);if(r){var o=(0,u.Z)(this).constructor;e=Reflect.construct(n,arguments,o)}else e=n.apply(this,arguments);return(0,l.Z)(this,e)});function s(){return(0,a.Z)(this,s),o.apply(this,arguments)}return(0,i.Z)(s,[{key:"render",value:function(){return n.createElement("div",{className:"login-processing"},n.createElement("div",{className:"processing-wrapper"},n.createElement("div",{className:"processing"})),n.createElement("h1",null,"Please wait..."))}}]),s}(n.Component);var _=r(9116),D=r(5145);var I="https://chrome.google.com/webstore/detail/passbolt-extension/didegimhafipceonhjepacocaffmoppf",V=function(e){(0,c.Z)(s,e);var t,r,o=(t=s,r=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,n=(0,u.Z)(t);if(r){var o=(0,u.Z)(this).constructor;e=Reflect.construct(n,arguments,o)}else e=n.apply(this,arguments);return(0,l.Z)(this,e)});function s(e){var t;return(0,a.Z)(this,s),(t=o.call(this,e)).state=t.getDefaultState(),t.bindCallbacks(),t}return(0,i.Z)(s,[{key:"getDefaultState",value:function(){return{browserName:C()}}},{key:"bindCallbacks",value:function(){this.handleRefreshClick=this.handleRefreshClick.bind(this)}},{key:"browserStoreThumbnailUrl",get:function(){switch(this.state.browserName){case z:return"".concat(this.props.context.trustedDomain,"/img/third_party/ChromeWebStore_black.png");case S:return"".concat(this.props.context.trustedDomain,"/img/third_party/FirefoxAMO_black.svg");default:return"".concat(this.props.context.trustedDomain,"/img/third_party/ChromeWebStore_black.png")}}},{key:"storeUrl",get:function(){switch(this.state.browserName){case z:return I;case S:return"https://addons.mozilla.org/fr/firefox/addon/passbolt";default:return I}}},{key:"storeClassName",get:function(){return"browser-webstore ".concat(this.state.browserName)}},{key:"handleRefreshClick",value:function(){window.location.reload()}},{key:"translate",get:function(){return this.props.t}},{key:"render",value:function(){return n.createElement("div",{className:"install-extension"},n.createElement("h1",null,n.createElement(_.c,null,"Please install the browser extension.")),n.createElement("p",null,n.createElement(_.c,null,"Please download the browser extension and refresh this page to continue.")),this.state.browserName&&n.createElement("a",{href:this.storeUrl,className:this.storeClassName,target:"_blank",rel:"noopener noreferrer"},n.createElement("img",{src:this.browserStoreThumbnailUrl})),n.createElement("div",{className:"form-actions"},n.createElement("a",{href:this.storeUrl,className:"button primary big full-width",role:"button",target:"_blank",rel:"noopener noreferrer"},n.createElement(_.c,null,"Download extension")),n.createElement("a",{onClick:this.handleRefreshClick,role:"button"},n.createElement(_.c,null,"Refresh to detect extension"))))}}]),s}(n.Component);V.propTypes={context:w().any,t:w().func};const j=m((0,D.Z)("common")(V));var H="https://www.mozilla.org/firefox/download/thanks/",F=function(e){(0,c.Z)(s,e);var t,r,o=(t=s,r=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,n=(0,u.Z)(t);if(r){var o=(0,u.Z)(this).constructor;e=Reflect.construct(n,arguments,o)}else e=n.apply(this,arguments);return(0,l.Z)(this,e)});function s(){return(0,a.Z)(this,s),o.apply(this,arguments)}return(0,i.Z)(s,[{key:"translate",get:function(){return this.props.t}},{key:"render",value:function(){return n.createElement("div",{className:"browser-not-supported"},n.createElement("h1",null,n.createElement(_.c,null,"Sorry, your browser is not supported.")),n.createElement("p",null,n.createElement(_.c,null,"Please download chrome or firefox to get started with passbolt.")),n.createElement("a",{href:"".concat(H),className:"browser",target:"_blank",rel:"noopener noreferrer"},n.createElement("img",{src:"".concat(this.props.context.trustedDomain,"/img/third_party/firefox_logo.png")})),n.createElement("div",{className:"form-actions"},n.createElement("a",{href:H,className:"button primary big full-width",role:"button",target:"_blank",rel:"noopener noreferrer"},n.createElement(_.c,null,"Download Firefox")),n.createElement("a",{href:"https://www.google.com/chrome/",role:"button",target:"_blank",rel:"noopener noreferrer"},n.createElement(_.c,null,"Download Chrome"))))}}]),s}(n.Component);F.propTypes={context:w().any,t:w().func};const W=m((0,D.Z)("common")(F));var X=function(e){(0,c.Z)(s,e);var t,r,o=(t=s,r=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,n=(0,u.Z)(t);if(r){var o=(0,u.Z)(this).constructor;e=Reflect.construct(n,arguments,o)}else e=n.apply(this,arguments);return(0,l.Z)(this,e)});function s(){return(0,a.Z)(this,s),o.apply(this,arguments)}return(0,i.Z)(s,[{key:"translate",get:function(){return this.props.t}},{key:"render",value:function(){return n.createElement("div",{className:"setup-error"},n.createElement("h1",null,n.createElement(_.c,null,"Access to this service requires an invitation.")),n.createElement("p",null,n.createElement(_.c,null,"This email is not associated with any approved users on this domain.")," ",n.createElement(_.c,null,"Please contact your administrator to request an invitation link.")),n.createElement("div",{className:"form-actions"},n.createElement("a",{href:"".concat(this.props.context.trustedDomain,"/users/recover"),className:"button primary big full-width",role:"button"},n.createElement(_.c,null,"Try with another email"))))}}]),s}(n.Component);X.propTypes={context:w().any,t:w().func};const K=m((0,D.Z)("common")(X));var G=function(e){(0,c.Z)(s,e);var t,r,o=(t=s,r=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,n=(0,u.Z)(t);if(r){var o=(0,u.Z)(this).constructor;e=Reflect.construct(n,arguments,o)}else e=n.apply(this,arguments);return(0,l.Z)(this,e)});function s(){return(0,a.Z)(this,s),o.apply(this,arguments)}return(0,i.Z)(s,[{key:"translate",get:function(){return this.props.t}},{key:"render",value:function(){return n.createElement("div",{className:"setup-error"},n.createElement("h1",null,n.createElement(_.c,null,"The invitation is expired.")),n.createElement("p",null,n.createElement(_.c,null,"You can request another invitation email by clicking on the button below.")),n.createElement("div",{className:"form-actions"},n.createElement("a",{href:"".concat(this.props.context.trustedDomain,"/users/recover"),className:"button primary big full-width",role:"button"},n.createElement(_.c,null,"Request invitation"))))}}]),s}(n.Component);G.propTypes={context:w().any,t:w().func};const J=m((0,D.Z)("common")(G));var $=function(e){(0,c.Z)(s,e);var t,r,o=(t=s,r=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,n=(0,u.Z)(t);if(r){var o=(0,u.Z)(this).constructor;e=Reflect.construct(n,arguments,o)}else e=n.apply(this,arguments);return(0,l.Z)(this,e)});function s(){return(0,a.Z)(this,s),o.apply(this,arguments)}return(0,i.Z)(s,[{key:"componentDidMount",value:function(){this.initializeRecover()}},{key:"initializeRecover",value:function(){var e=this;setTimeout((function(){return e.props.apiRecoverContext.onInitializeRecoverRequested()}),1e3)}},{key:"render",value:function(){switch(this.props.apiRecoverContext.state){case N.INSTALL_EXTENSION_STATE:return n.createElement(j,null);case N.DOWNLOAD_SUPPORTED_BROWSER_STATE:return n.createElement(W,null);case N.TOKEN_EXPIRED_STATE:return n.createElement(J,null);case N.ERROR_STATE:return n.createElement(K,null);default:return n.createElement(M,null)}}}]),s}(n.Component);$.propTypes={apiRecoverContext:w().object};const Y=P($);var Q=function(){function e(t){(0,a.Z)(this,e),this.setToken(t)}return(0,i.Z)(e,[{key:"setToken",value:function(e){this.validate(e),this.token=e}},{key:"validate",value:function(e){if(!e)throw new TypeError("CSRF token cannot be empty.");if("string"!=typeof e)throw new TypeError("CSRF token should be a string.")}},{key:"toFetchHeaders",value:function(){return{"X-CSRF-Token":this.token}}}]),e}(),ee=function(){function e(){(0,a.Z)(this,e)}return(0,i.Z)(e,[{key:"setBaseUrl",value:function(e){if(!e)throw new TypeError("ApiClientOption baseUrl is required.");if("string"==typeof e)try{this.baseUrl=new URL(e)}catch(e){throw new TypeError("ApiClientOption baseUrl is invalid.")}else{if(!(e instanceof URL))throw new TypeError("ApiClientOptions baseurl should be a string or URL");this.baseUrl=e}return this}},{key:"setCsrfToken",value:function(e){if(!e)throw new TypeError("ApiClientOption csrfToken is required.");if("string"==typeof e)this.csrfToken=new Q(e);else{if(!(e instanceof Q))throw new TypeError("ApiClientOption csrfToken should be a string or a valid CsrfToken.");this.csrfToken=e}return this}},{key:"setResourceName",value:function(e){if(!e)throw new TypeError("ApiClientOptions.setResourceName resourceName is required.");if("string"!=typeof e)throw new TypeError("ApiClientOptions.setResourceName resourceName should be a valid string.");return this.resourceName=e,this}},{key:"getBaseUrl",value:function(){return this.baseUrl}},{key:"getResourceName",value:function(){return this.resourceName}},{key:"getHeaders",value:function(){if(this.csrfToken)return this.csrfToken.toFetchHeaders()}}]),e}();var te=function(e){(0,c.Z)(s,e);var t,r,o=(t=s,r=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,n=(0,u.Z)(t);if(r){var o=(0,u.Z)(this).constructor;e=Reflect.construct(n,arguments,o)}else e=n.apply(this,arguments);return(0,l.Z)(this,e)});function s(){return(0,a.Z)(this,s),o.apply(this,arguments)}return(0,i.Z)(s,[{key:"getClassName",value:function(){var e="svg-icon ".concat(this.props.name);return this.props.big&&(e+=" icon-only"),this.props.baseline&&(e+=" baseline"),e}},{key:"render",value:function(){return n.createElement("span",{className:this.getClassName(),onClick:this.props.onClick},"ban"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1440 893q0-161-87-295l-754 753q137 89 297 89 111 0 211.5-43.5t173.5-116.5 116-174.5 43-212.5zm-999 299l755-754q-135-91-300-91-148 0-273 73t-198 199-73 274q0 162 89 299zm1223-299q0 157-61 300t-163.5 246-245 164-298.5 61-298.5-61-245-164-163.5-246-61-300 61-299.5 163.5-245.5 245-164 298.5-61 298.5 61 245 164 163.5 245.5 61 299.5z"})),"camera"===this.props.name&&n.createElement("svg",{width:"2048",height:"1792",viewBox:"0 0 2048 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1024 672q119 0 203.5 84.5t84.5 203.5-84.5 203.5-203.5 84.5-203.5-84.5-84.5-203.5 84.5-203.5 203.5-84.5zm704-416q106 0 181 75t75 181v896q0 106-75 181t-181 75h-1408q-106 0-181-75t-75-181v-896q0-106 75-181t181-75h224l51-136q19-49 69.5-84.5t103.5-35.5h512q53 0 103.5 35.5t69.5 84.5l51 136h224zm-704 1152q185 0 316.5-131.5t131.5-316.5-131.5-316.5-316.5-131.5-316.5 131.5-131.5 316.5 131.5 316.5 316.5 131.5z"})),"caret-right"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1152 896q0 26-19 45l-448 448q-19 19-45 19t-45-19-19-45v-896q0-26 19-45t45-19 45 19l448 448q19 19 19 45z"})),"caret-down"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1408 704q0 26-19 45l-448 448q-19 19-45 19t-45-19l-448-448q-19-19-19-45t19-45 45-19h896q26 0 45 19t19 45z"})),"caret-up"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1408 1216q0 26-19 45t-45 19h-896q-26 0-45-19t-19-45 19-45l448-448q19-19 45-19t45 19l448 448q19 19 19 45z"})),"chevron-left"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1427 301l-531 531 531 531q19 19 19 45t-19 45l-166 166q-19 19-45 19t-45-19l-742-742q-19-19-19-45t19-45l742-742q19-19 45-19t45 19l166 166q19 19 19 45t-19 45z"})),"chevron-right"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1363 877l-742 742q-19 19-45 19t-45-19l-166-166q-19-19-19-45t19-45l531-531-531-531q-19-19-19-45t19-45l166-166q19-19 45-19t45 19l742 742q19 19 19 45t-19 45z"})),"close"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1490 1322q0 40-28 68l-136 136q-28 28-68 28t-68-28l-294-294-294 294q-28 28-68 28t-68-28l-136-136q-28-28-28-68t28-68l294-294-294-294q-28-28-28-68t28-68l136-136q28-28 68-28t68 28l294 294 294-294q28-28 68-28t68 28l136 136q28 28 28 68t-28 68l-294 294 294 294q28 28 28 68z"})),"close-circle"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1277 1122q0-26-19-45l-181-181 181-181q19-19 19-45 0-27-19-46l-90-90q-19-19-46-19-26 0-45 19l-181 181-181-181q-19-19-45-19-27 0-46 19l-90 90q-19 19-19 46 0 26 19 45l181 181-181 181q-19 19-19 45 0 27 19 46l90 90q19 19 46 19 26 0 45-19l181-181 181 181q19 19 45 19 27 0 46-19l90-90q19-19 19-46zm387-226q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"})),"copy-to-clipboard"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M768 1664h896v-640h-416q-40 0-68-28t-28-68v-416h-384v1152zm256-1440v-64q0-13-9.5-22.5t-22.5-9.5h-704q-13 0-22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h704q13 0 22.5-9.5t9.5-22.5zm256 672h299l-299-299v299zm512 128v672q0 40-28 68t-68 28h-960q-40 0-68-28t-28-68v-160h-544q-40 0-68-28t-28-68v-1344q0-40 28-68t68-28h1088q40 0 68 28t28 68v328q21 13 36 28l408 408q28 28 48 76t20 88z"})),"download"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1344 1344q0-26-19-45t-45-19-45 19-19 45 19 45 45 19 45-19 19-45zm256 0q0-26-19-45t-45-19-45 19-19 45 19 45 45 19 45-19 19-45zm128-224v320q0 40-28 68t-68 28h-1472q-40 0-68-28t-28-68v-320q0-40 28-68t68-28h465l135 136q58 56 136 56t136-56l136-136h464q40 0 68 28t28 68zm-325-569q17 41-14 70l-448 448q-18 19-45 19t-45-19l-448-448q-31-29-14-70 17-39 59-39h256v-448q0-26 19-45t45-19h256q26 0 45 19t19 45v448h256q42 0 59 39z"})),"edit"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M888 1184l116-116-152-152-116 116v56h96v96h56zm440-720q-16-16-33 1l-350 350q-17 17-1 33t33-1l350-350q17-17 1-33zm80 594v190q0 119-84.5 203.5t-203.5 84.5h-832q-119 0-203.5-84.5t-84.5-203.5v-832q0-119 84.5-203.5t203.5-84.5h832q63 0 117 25 15 7 18 23 3 17-9 29l-49 49q-14 14-32 8-23-6-45-6h-832q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113v-126q0-13 9-22l64-64q15-15 35-7t20 29zm-96-738l288 288-672 672h-288v-288zm444 132l-92 92-288-288 92-92q28-28 68-28t68 28l152 152q28 28 28 68t-28 68z"})),"envelope"===this.props.name&&n.createElement("svg",{width:"512",height:"512",viewBox:"0 0 512 512",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{fill:"currentColor",d:"M464 64H48C21.49 64 0 85.49 0 112v288c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V112c0-26.51-21.49-48-48-48zm0 48v40.805c-22.422 18.259-58.168 46.651-134.587 106.49-16.841 13.247-50.201 45.072-73.413 44.701-23.208.375-56.579-31.459-73.413-44.701C106.18 199.465 70.425 171.067 48 152.805V112h416zM48 400V214.398c22.914 18.251 55.409 43.862 104.938 82.646 21.857 17.205 60.134 55.186 103.062 54.955 42.717.231 80.509-37.199 103.053-54.947 49.528-38.783 82.032-64.401 104.947-82.653V400H48z"})," "),"eye-open"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1664 960q-152-236-381-353 61 104 61 225 0 185-131.5 316.5t-316.5 131.5-316.5-131.5-131.5-316.5q0-121 61-225-229 117-381 353 133 205 333.5 326.5t434.5 121.5 434.5-121.5 333.5-326.5zm-720-384q0-20-14-34t-34-14q-125 0-214.5 89.5t-89.5 214.5q0 20 14 34t34 14 34-14 14-34q0-86 61-147t147-61q20 0 34-14t14-34zm848 384q0 34-20 69-140 230-376.5 368.5t-499.5 138.5-499.5-139-376.5-368q-20-35-20-69t20-69q140-229 376.5-368t499.5-139 499.5 139 376.5 368q20 35 20 69z"})),"eye-close"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M555 1335l78-141q-87-63-136-159t-49-203q0-121 61-225-229 117-381 353 167 258 427 375zm389-759q0-20-14-34t-34-14q-125 0-214.5 89.5t-89.5 214.5q0 20 14 34t34 14 34-14 14-34q0-86 61-147t147-61q20 0 34-14t14-34zm363-191q0 7-1 9-106 189-316 567t-315 566l-49 89q-10 16-28 16-12 0-134-70-16-10-16-28 0-12 44-87-143-65-263.5-173t-208.5-245q-20-31-20-69t20-69q153-235 380-371t496-136q89 0 180 17l54-97q10-16 28-16 5 0 18 6t31 15.5 33 18.5 31.5 18.5 19.5 11.5q16 10 16 27zm37 447q0 139-79 253.5t-209 164.5l280-502q8 45 8 84zm448 128q0 35-20 69-39 64-109 145-150 172-347.5 267t-419.5 95l74-132q212-18 392.5-137t301.5-307q-115-179-282-294l63-112q95 64 182.5 153t144.5 184q20 34 20 69z"})),"external-link"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1408 928v-480q0-26-19-45t-45-19h-480q-42 0-59 39-17 41 14 70l144 144-534 534q-19 19-19 45t19 45l102 102q19 19 45 19t45-19l534-534 144 144q18 19 45 19 12 0 25-5 39-17 39-59zm256-512v960q0 119-84.5 203.5t-203.5 84.5h-960q-119 0-203.5-84.5t-84.5-203.5v-960q0-119 84.5-203.5t203.5-84.5h960q119 0 203.5 84.5t84.5 203.5z"})),"folder"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1728 608v704q0 92-66 158t-158 66h-1216q-92 0-158-66t-66-158v-960q0-92 66-158t158-66h320q92 0 158 66t66 158v32h672q92 0 158 66t66 158z"})),"folder-shared"===this.props.name&&n.createElement("svg",{viewBox:"2 2 20 20",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M20 6h-8l-2-2H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm-5 3c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm4 8h-8v-1c0-1.33 2.67-2 4-2s4 .67 4 2v1z"})),"heart"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M896 1664q-26 0-44-18l-624-602q-10-8-27.5-26t-55.5-65.5-68-97.5-53.5-121-23.5-138q0-220 127-344t351-124q62 0 126.5 21.5t120 58 95.5 68.5 76 68q36-36 76-68t95.5-68.5 120-58 126.5-21.5q224 0 351 124t127 344q0 221-229 450l-623 600q-18 18-44 18z"})),"heart-o"===this.props.name&&n.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 512 512"},n.createElement("path",{d:"M458.4 64.3C400.6 15.7 311.3 23 256 79.3 200.7 23 111.4 15.6 53.6 64.3-21.6 127.6-10.6 230.8 43 285.5l175.4 178.7c10 10.2 23.4 15.9 37.6 15.9 14.3 0 27.6-5.6 37.6-15.8L469 285.6c53.5-54.7 64.7-157.9-10.6-221.3zm-23.6 187.5L259.4 430.5c-2.4 2.4-4.4 2.4-6.8 0L77.2 251.8c-36.5-37.2-43.9-107.6 7.3-150.7 38.9-32.7 98.9-27.8 136.5 10.5l35 35.7 35-35.7c37.8-38.5 97.8-43.2 136.5-10.6 51.1 43.1 43.5 113.9 7.3 150.8z"})),"heartbeat"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1280 1024h305q-5 6-10 10.5t-9 7.5l-3 4-623 600q-18 18-44 18t-44-18l-624-602q-5-2-21-20h369q22 0 39.5-13.5t22.5-34.5l70-281 190 667q6 20 23 33t39 13q21 0 38-13t23-33l146-485 56 112q18 35 57 35zm512-428q0 145-103 300h-369l-111-221q-8-17-25.5-27t-36.5-8q-45 5-56 46l-129 430-196-686q-6-20-23.5-33t-39.5-13-39 13.5-22 34.5l-116 464h-423q-103-155-103-300 0-220 127-344t351-124q62 0 126.5 21.5t120 58 95.5 68.5 76 68q36-36 76-68t95.5-68.5 120-58 126.5-21.5q224 0 351 124t127 344z"})),"info-circle"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1152 1376v-160q0-14-9-23t-23-9h-96v-512q0-14-9-23t-23-9h-320q-14 0-23 9t-9 23v160q0 14 9 23t23 9h96v320h-96q-14 0-23 9t-9 23v160q0 14 9 23t23 9h448q14 0 23-9t9-23zm-128-896v-160q0-14-9-23t-23-9h-192q-14 0-23 9t-9 23v160q0 14 9 23t23 9h192q14 0 23-9t9-23zm640 416q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"})),"key"===this.props.name&&n.createElement("svg",{width:"512",height:"512",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 512 512"},n.createElement("path",{fill:"currentColor",d:"M512 176.001C512 273.203 433.202 352 336 352c-11.22 0-22.19-1.062-32.827-3.069l-24.012 27.014A23.999 23.999 0 0 1 261.223 384H224v40c0 13.255-10.745 24-24 24h-40v40c0 13.255-10.745 24-24 24H24c-13.255 0-24-10.745-24-24v-78.059c0-6.365 2.529-12.47 7.029-16.971l161.802-161.802C163.108 213.814 160 195.271 160 176 160 78.798 238.797.001 335.999 0 433.488-.001 512 78.511 512 176.001zM336 128c0 26.51 21.49 48 48 48s48-21.49 48-48-21.49-48-48-48-48 21.49-48 48z"})),"link"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1520 1216q0-40-28-68l-208-208q-28-28-68-28-42 0-72 32 3 3 19 18.5t21.5 21.5 15 19 13 25.5 3.5 27.5q0 40-28 68t-68 28q-15 0-27.5-3.5t-25.5-13-19-15-21.5-21.5-18.5-19q-33 31-33 73 0 40 28 68l206 207q27 27 68 27 40 0 68-26l147-146q28-28 28-67zm-703-705q0-40-28-68l-206-207q-28-28-68-28-39 0-68 27l-147 146q-28 28-28 67 0 40 28 68l208 208q27 27 68 27 42 0 72-31-3-3-19-18.5t-21.5-21.5-15-19-13-25.5-3.5-27.5q0-40 28-68t68-28q15 0 27.5 3.5t25.5 13 19 15 21.5 21.5 18.5 19q33-31 33-73zm895 705q0 120-85 203l-147 146q-83 83-203 83-121 0-204-85l-206-207q-83-83-83-203 0-123 88-209l-88-88q-86 88-208 88-120 0-204-84l-208-208q-84-84-84-204t85-203l147-146q83-83 203-83 121 0 204 85l206 207q83 83 83 203 0 123-88 209l88 88q86-88 208-88 120 0 204 84l208 208q84 84 84 204z"})),"lock"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M640 768h512v-192q0-106-75-181t-181-75-181 75-75 181v192zm832 96v576q0 40-28 68t-68 28h-960q-40 0-68-28t-28-68v-576q0-40 28-68t68-28h32v-192q0-184 132-316t316-132 316 132 132 316v192h32q40 0 68 28t28 68z"})),"lock-open"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1376 768q40 0 68 28t28 68v576q0 40-28 68t-68 28h-960q-40 0-68-28t-28-68v-576q0-40 28-68t68-28h32v-320q0-185 131.5-316.5t316.5-131.5 316.5 131.5 131.5 316.5q0 26-19 45t-45 19h-64q-26 0-45-19t-19-45q0-106-75-181t-181-75-181 75-75 181v320h736z"})),"magic-wand"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1254 581l293-293-107-107-293 293zm447-293q0 27-18 45l-1286 1286q-18 18-45 18t-45-18l-198-198q-18-18-18-45t18-45l1286-1286q18-18 45-18t45 18l198 198q18 18 18 45zm-1351-190l98 30-98 30-30 98-30-98-98-30 98-30 30-98zm350 162l196 60-196 60-60 196-60-196-196-60 196-60 60-196zm930 478l98 30-98 30-30 98-30-98-98-30 98-30 30-98zm-640-640l98 30-98 30-30 98-30-98-98-30 98-30 30-98z"})),"plus-circle"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1344 960v-128q0-26-19-45t-45-19h-256v-256q0-26-19-45t-45-19h-128q-26 0-45 19t-19 45v256h-256q-26 0-45 19t-19 45v128q0 26 19 45t45 19h256v256q0 26 19 45t45 19h128q26 0 45-19t19-45v-256h256q26 0 45-19t19-45zm320-64q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"})),"plus-square"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 448 512",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48zm-32 252c0 6.6-5.4 12-12 12h-92v92c0 6.6-5.4 12-12 12h-56c-6.6 0-12-5.4-12-12v-92H92c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h92v-92c0-6.6 5.4-12 12-12h56c6.6 0 12 5.4 12 12v92h92c6.6 0 12 5.4 12 12v56z"})),"filter"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 512 512",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M487.976 0H24.028C2.71 0-8.047 25.866 7.058 40.971L192 225.941V432c0 7.831 3.821 15.17 10.237 19.662l80 55.98C298.02 518.69 320 507.493 320 487.98V225.941l184.947-184.97C520.021 25.896 509.338 0 487.976 0z"})),"life-ring"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 512 512",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M256 504c136.967 0 248-111.033 248-248S392.967 8 256 8 8 119.033 8 256s111.033 248 248 248zm-103.398-76.72l53.411-53.411c31.806 13.506 68.128 13.522 99.974 0l53.411 53.411c-63.217 38.319-143.579 38.319-206.796 0zM336 256c0 44.112-35.888 80-80 80s-80-35.888-80-80 35.888-80 80-80 80 35.888 80 80zm91.28 103.398l-53.411-53.411c13.505-31.806 13.522-68.128 0-99.974l53.411-53.411c38.319 63.217 38.319 143.579 0 206.796zM359.397 84.72l-53.411 53.411c-31.806-13.505-68.128-13.522-99.973 0L152.602 84.72c63.217-38.319 143.579-38.319 206.795 0zM84.72 152.602l53.411 53.411c-13.506 31.806-13.522 68.128 0 99.974L84.72 359.398c-38.319-63.217-38.319-143.579 0-206.796z"})),"plug"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 384 512",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M320,32a32,32,0,0,0-64,0v96h64Zm48,128H16A16,16,0,0,0,0,176v32a16,16,0,0,0,16,16H32v32A160.07,160.07,0,0,0,160,412.8V512h64V412.8A160.07,160.07,0,0,0,352,256V224h16a16,16,0,0,0,16-16V176A16,16,0,0,0,368,160ZM128,32a32,32,0,0,0-64,0v96h64Z"})),"printer"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M448 1536h896v-256h-896v256zm0-640h896v-384h-160q-40 0-68-28t-28-68v-160h-640v640zm1152 64q0-26-19-45t-45-19-45 19-19 45 19 45 45 19 45-19 19-45zm128 0v416q0 13-9.5 22.5t-22.5 9.5h-224v160q0 40-28 68t-68 28h-960q-40 0-68-28t-28-68v-160h-224q-13 0-22.5-9.5t-9.5-22.5v-416q0-79 56.5-135.5t135.5-56.5h64v-544q0-40 28-68t68-28h672q40 0 88 20t76 48l152 152q28 28 48 76t20 88v256h64q79 0 135.5 56.5t56.5 135.5z"})),"refresh"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 512 512",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M370.72 133.28C339.458 104.008 298.888 87.962 255.848 88c-77.458.068-144.328 53.178-162.791 126.85-1.344 5.363-6.122 9.15-11.651 9.15H24.103c-7.498 0-13.194-6.807-11.807-14.176C33.933 94.924 134.813 8 256 8c66.448 0 126.791 26.136 171.315 68.685L463.03 40.97C478.149 25.851 504 36.559 504 57.941V192c0 13.255-10.745 24-24 24H345.941c-21.382 0-32.09-25.851-16.971-40.971l41.75-41.749zM32 296h134.059c21.382 0 32.09 25.851 16.971 40.971l-41.75 41.75c31.262 29.273 71.835 45.319 114.876 45.28 77.418-.07 144.315-53.144 162.787-126.849 1.344-5.363 6.122-9.15 11.651-9.15h57.304c7.498 0 13.194 6.807 11.807 14.176C478.067 417.076 377.187 504 256 504c-66.448 0-126.791-26.136-171.315-68.685L48.97 471.03C33.851 486.149 8 475.441 8 454.059V320c0-13.255 10.745-24 24-24z"})),"question-circle"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1024 1376v-192q0-14-9-23t-23-9h-192q-14 0-23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23-9t9-23zm256-672q0-88-55.5-163t-138.5-116-170-41q-243 0-371 213-15 24 8 42l132 100q7 6 19 6 16 0 25-12 53-68 86-92 34-24 86-24 48 0 85.5 26t37.5 59q0 38-20 61t-68 45q-63 28-115.5 86.5t-52.5 125.5v36q0 14 9 23t23 9h192q14 0 23-9t9-23q0-19 21.5-49.5t54.5-49.5q32-18 49-28.5t46-35 44.5-48 28-60.5 12.5-81zm384 192q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"})),"search"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1216 832q0-185-131.5-316.5t-316.5-131.5-316.5 131.5-131.5 316.5 131.5 316.5 316.5 131.5 316.5-131.5 131.5-316.5zm512 832q0 52-38 90t-90 38q-54 0-90-38l-343-342q-179 124-399 124-143 0-273.5-55.5t-225-150-150-225-55.5-273.5 55.5-273.5 150-225 225-150 273.5-55.5 273.5 55.5 225 150 150 225 55.5 273.5q0 220-124 399l343 343q37 37 37 90z"})),"share"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1764 11q33 24 27 64l-256 1536q-5 29-32 45-14 8-31 8-11 0-24-5l-453-185-242 295q-18 23-49 23-13 0-22-4-19-7-30.5-23.5t-11.5-36.5v-349l864-1059-1069 925-395-162q-37-14-40-55-2-40 32-59l1664-960q15-9 32-9 20 0 36 11z"})),"star"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1728 647q0 22-26 48l-363 354 86 500q1 7 1 20 0 21-10.5 35.5t-30.5 14.5q-19 0-40-12l-449-236-449 236q-22 12-40 12-21 0-31.5-14.5t-10.5-35.5q0-6 2-20l86-500-364-354q-25-27-25-48 0-37 56-46l502-73 225-455q19-41 49-41t49 41l225 455 502 73q56 9 56 46z"})),"save"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 448 512",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M433.941 129.941l-83.882-83.882A48 48 0 0 0 316.118 32H48C21.49 32 0 53.49 0 80v352c0 26.51 21.49 48 48 48h352c26.51 0 48-21.49 48-48V163.882a48 48 0 0 0-14.059-33.941zM272 80v80H144V80h128zm122 352H54a6 6 0 0 1-6-6V86a6 6 0 0 1 6-6h42v104c0 13.255 10.745 24 24 24h176c13.255 0 24-10.745 24-24V83.882l78.243 78.243a6 6 0 0 1 1.757 4.243V426a6 6 0 0 1-6 6zM224 232c-48.523 0-88 39.477-88 88s39.477 88 88 88 88-39.477 88-88-39.477-88-88-88zm0 128c-22.056 0-40-17.944-40-40s17.944-40 40-40 40 17.944 40 40-17.944 40-40 40z"})),"trash"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M704 1376v-704q0-14-9-23t-23-9h-64q-14 0-23 9t-9 23v704q0 14 9 23t23 9h64q14 0 23-9t9-23zm256 0v-704q0-14-9-23t-23-9h-64q-14 0-23 9t-9 23v704q0 14 9 23t23 9h64q14 0 23-9t9-23zm256 0v-704q0-14-9-23t-23-9h-64q-14 0-23 9t-9 23v704q0 14 9 23t23 9h64q14 0 23-9t9-23zm-544-992h448l-48-117q-7-9-17-11h-317q-10 2-17 11zm928 32v64q0 14-9 23t-23 9h-96v948q0 83-47 143.5t-113 60.5h-832q-66 0-113-58.5t-47-141.5v-952h-96q-14 0-23-9t-9-23v-64q0-14 9-23t23-9h309l70-167q15-37 54-63t79-26h320q40 0 79 26t54 63l70 167h309q14 0 23 9t9 23z"})),"upload"===this.props.name&&n.createElement("svg",{width:"2048",height:"1792",viewBox:"0 0 2048 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1344 864q0-14-9-23l-352-352q-9-9-23-9t-23 9l-351 351q-10 12-10 24 0 14 9 23t23 9h224v352q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5-9.5t9.5-22.5v-352h224q13 0 22.5-9.5t9.5-22.5zm640 288q0 159-112.5 271.5t-271.5 112.5h-1088q-185 0-316.5-131.5t-131.5-316.5q0-130 70-240t188-165q-2-30-2-43 0-212 150-362t362-150q156 0 285.5 87t188.5 231q71-62 166-62 106 0 181 75t75 181q0 76-41 138 130 31 213.5 135.5t83.5 238.5z"})),"upload-a"===this.props.name&&n.createElement("svg",{"aria-hidden":"true",focusable:"false","data-prefix":"fas","data-icon":"upload",className:"svg-inline--fa fa-upload fa-w-16",role:"img",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 512 512"},n.createElement("path",{fill:"currentColor",d:"M296 384h-80c-13.3 0-24-10.7-24-24V192h-87.7c-17.8 0-26.7-21.5-14.1-34.1L242.3 5.7c7.5-7.5 19.8-7.5 27.3 0l152.2 152.2c12.6 12.6 3.7 34.1-14.1 34.1H320v168c0 13.3-10.7 24-24 24zm216-8v112c0 13.3-10.7 24-24 24H24c-13.3 0-24-10.7-24-24V376c0-13.3 10.7-24 24-24h136v8c0 30.9 25.1 56 56 56h80c30.9 0 56-25.1 56-56v-8h136c13.3 0 24 10.7 24 24zm-124 88c0-11-9-20-20-20s-20 9-20 20 9 20 20 20 20-9 20-20zm64 0c0-11-9-20-20-20s-20 9-20 20 9 20 20 20 20-9 20-20z"})),"user"===this.props.name&&n.createElement("svg",{width:"448",height:"512",viewBox:"0 0 448 512",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{fill:"currentColor",d:"M224 256c70.7 0 128-57.3 128-128S294.7 0 224 0 96 57.3 96 128s57.3 128 128 128zm89.6 32h-16.7c-22.2 10.2-46.9 16-72.9 16s-50.6-5.8-72.9-16h-16.7C60.2 288 0 348.2 0 422.4V464c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48v-41.6c0-74.2-60.2-134.4-134.4-134.4z",className:""})),"users"===this.props.name&&n.createElement("svg",{width:"640",height:"512",viewBox:"0 0 640 512",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{fill:"currentColor",d:"M96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm448 0c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm32 32h-64c-17.6 0-33.5 7.1-45.1 18.6 40.3 22.1 68.9 62 75.1 109.4h66c17.7 0 32-14.3 32-32v-32c0-35.3-28.7-64-64-64zm-256 0c61.9 0 112-50.1 112-112S381.9 32 320 32 208 82.1 208 144s50.1 112 112 112zm76.8 32h-8.3c-20.8 10-43.9 16-68.5 16s-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h288c26.5 0 48-21.5 48-48v-28.8c0-63.6-51.6-115.2-115.2-115.2zm-223.7-13.4C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z"})),"warning"===this.props.name&&n.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},n.createElement("path",{d:"M1024 1375v-190q0-14-9.5-23.5t-22.5-9.5h-192q-13 0-22.5 9.5t-9.5 23.5v190q0 14 9.5 23.5t22.5 9.5h192q13 0 22.5-9.5t9.5-23.5zm-2-374l18-459q0-12-10-19-13-11-24-11h-220q-11 0-24 11-10 7-10 21l17 457q0 10 10 16.5t24 6.5h185q14 0 23.5-6.5t10.5-16.5zm-14-934l768 1408q35 63-2 126-17 29-46.5 46t-63.5 17h-1536q-34 0-63.5-17t-46.5-46q-37-63-2-126l768-1408q17-31 47-49t65-18 65 18 47 49z"})))}}]),s}(n.Component);te.defaultProps={big:!1,baseline:!1,onClick:function(){}},te.propTypes={name:w().string,big:w().bool,baseline:w().bool,onClick:w().func};const re=te;var ne=function(e){(0,c.Z)(s,e);var t,r,o=(t=s,r=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,n=(0,u.Z)(t);if(r){var o=(0,u.Z)(this).constructor;e=Reflect.construct(n,arguments,o)}else e=n.apply(this,arguments);return(0,l.Z)(this,e)});function s(){return(0,a.Z)(this,s),o.apply(this,arguments)}return(0,i.Z)(s,[{key:"privacyUrl",get:function(){return this.props.context.siteSettings.privacyLink}},{key:"creditsUrl",get:function(){return"https://www.passbolt.com/credits"}},{key:"unsafeUrl",get:function(){return"https://help.passbolt.com/faq/hosting/why-unsafe"}},{key:"termsUrl",get:function(){return this.props.context.siteSettings.termsLink}},{key:"versions",get:function(){var e=[],t=this.props.context.siteSettings.version;return t&&e.push(t),this.props.context.extensionVersion&&e.push(this.props.context.extensionVersion),e.join(" / ")}},{key:"isUnsafeMode",get:function(){var e=this.props.context.siteSettings.debug,t=this.props.context.siteSettings.url.startsWith("http://");return e||t}},{key:"translate",get:function(){return this.props.t}},{key:"render",value:function(){return n.createElement("footer",null,n.createElement("div",{className:"footer"},n.createElement("ul",{className:"footer-links"},this.isUnsafeMode&&n.createElement("li",{className:"error-message"},n.createElement("a",{title:"terms of service",href:this.unsafeUrl,target:"_blank",rel:"noopener noreferrer"},n.createElement(_.c,null,"Unsafe mode"))),this.termsUrl&&n.createElement("li",null,n.createElement("a",{href:this.termsUrl,target:"_blank",rel:"noopener noreferrer"},n.createElement(_.c,null,"Terms"))),this.privacyUrl&&n.createElement("li",null,n.createElement("a",{href:this.privacyUrl,target:"_blank",rel:"noopener noreferrer"},n.createElement(_.c,null,"Privacy"))),n.createElement("li",null,n.createElement("a",{href:this.creditsUrl,target:"_blank",rel:"noopener noreferrer"},n.createElement(_.c,null,"Credits"))),n.createElement("li",null,n.createElement("a",(0,f.Z)({href:this.creditsUrl,className:"tooltip-left"},this.versions&&{"data-tooltip":this.versions},{target:"_blank",rel:"noopener noreferrer"}),n.createElement(re,{name:"heart-o"}))))))}}]),s}(n.Component);ne.propTypes={context:w().any,t:w().func};const oe=m((0,D.Z)("common")(ne));var se=r(484);const ae=function(e,t){if(void 0===e||"string"!=typeof e||!e.length)return!1;if((t=t||{}).whitelistedProtocols&&!Array.isArray(t.whitelistedProtocols))throw new TypeError("The whitelistedProtocols should be an array of string.");if(t.defaultProtocol&&"string"!=typeof t.defaultProtocol)throw new TypeError("The defaultProtocol should be a string.");var r=t.whitelistedProtocols||[ie.HTTP,ie.HTTPS],n=[ie.JAVASCRIPT],o=t.defaultProtocol||"";!/^((?!:\/\/).)*:\/\//.test(e)&&o&&(e="".concat(o,"//").concat(e));try{var s=new URL(e);return!n.includes(s.protocol)&&!!r.includes(s.protocol)&&s.href}catch(e){return!1}};var ie={FTP:"http:",FTPS:"https:",HTTP:"http:",HTTPS:"https:",JAVASCRIPT:"javascript:",SSH:"ssh:"},ce=function(){function e(t){(0,a.Z)(this,e),this.settings=t}var t;return(0,i.Z)(e,[{key:"canIUse",value:function(e){var t=!1,r="passbolt.plugins.".concat(e),n=O(this.settings,r)||null;if(n&&"object"===(0,se.Z)(n)){var o=O(n,"enabled");void 0!==o&&!0!==o||(t=!0)}return t}},{key:"getPluginSettings",value:function(e){var t="passbolt.plugins.".concat(e);return O(this.settings,t)}},{key:"getRememberMeOptions",value:function(){return(this.getPluginSettings("rememberMe")||{}).options||{}}},{key:"hasRememberMeUntilILogoutOption",get:function(){return void 0!==(this.getRememberMeOptions()||{})[-1]}},{key:"getServerTimezone",value:function(){return O(this.settings,"passbolt.app.server_timezone")}},{key:"termsLink",get:function(){var e=O(this.settings,"passbolt.legal.terms.url");return!!e&&ae(e)}},{key:"privacyLink",get:function(){var e=O(this.settings,"passbolt.legal.privacy_policy.url");return!!e&&ae(e)}},{key:"registrationPublic",get:function(){return!0===O(this.settings,"passbolt.registration.public")}},{key:"debug",get:function(){return!0===O(this.settings,"app.debug")}},{key:"url",get:function(){return O(this.settings,"app.url")||""}},{key:"version",get:function(){return O(this.settings,"app.version.number")}},{key:"locale",get:function(){return O(this.settings,"app.locale")||e.DEFAULT_LOCALE.locale}},{key:"setLocale",value:(t=(0,s.Z)(p().mark((function e(t){return p().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.settings.app.locale=t;case 1:case"end":return e.stop()}}),e,this)}))),function(e){return t.apply(this,arguments)})},{key:"supportedLocales",get:function(){return O(this.settings,"passbolt.plugins.locale.options")||e.DEFAULT_SUPPORTED_LOCALES}}],[{key:"DEFAULT_SUPPORTED_LOCALES",get:function(){return[e.DEFAULT_LOCALE]}},{key:"DEFAULT_LOCALE",get:function(){return{locale:"en-UK",label:"English"}}}]),e}(),le=r(7412),ue=r(8718),he=r(3554);var pe=function(e){(0,c.Z)(m,e);var t,r,o,h,f,v=(h=m,f=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,t=(0,u.Z)(h);if(f){var r=(0,u.Z)(this).constructor;e=Reflect.construct(t,arguments,r)}else e=t.apply(this,arguments);return(0,l.Z)(this,e)});function m(e){var t;return(0,a.Z)(this,m),(t=v.call(this,e)).state=t.defaultState,t}return(0,i.Z)(m,[{key:"defaultState",get:function(){return{ready:!1}}},{key:"componentDidMount",value:(o=(0,s.Z)(p().mark((function e(){return p().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,le.Z.use(ue.Db).use(he.Z).init({lng:this.locale,load:"currentOnly",react:{useSuspense:!1},backend:{loadPath:this.props.loadingPath||"/locales/{{lng}}/{{ns}}.json"},supportedLngs:this.supportedLocales,fallbackLng:!1,ns:["common"],defaultNS:"common",keySeparator:!1,nsSeparator:!1,debug:!1});case 2:this.setState({ready:!0});case 3:case"end":return e.stop()}}),e,this)}))),function(){return o.apply(this,arguments)})},{key:"supportedLocales",get:function(){return this.props.context.siteSettings.supportedLocales?this.props.context.siteSettings.supportedLocales.map((function(e){return e.locale})):[this.locale]}},{key:"locale",get:function(){return this.props.context.locale}},{key:"componentDidUpdate",value:(r=(0,s.Z)(p().mark((function e(t){return p().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.handleLocaleChange(t.context.locale);case 2:case"end":return e.stop()}}),e,this)}))),function(e){return r.apply(this,arguments)})},{key:"handleLocaleChange",value:(t=(0,s.Z)(p().mark((function e(t){return p().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(this.locale===t){e.next=4;break}return e.next=4,le.Z.changeLanguage(this.locale);case 4:case"end":return e.stop()}}),e,this)}))),function(e){return t.apply(this,arguments)})},{key:"isReady",get:function(){return this.state.ready}},{key:"render",value:function(){return n.createElement(n.Fragment,null,this.isReady&&this.props.children)}}]),m}(n.Component);pe.propTypes={context:w().any,loadingPath:w().any,children:w().any};const fe=m(pe);var ve=function(e){(0,c.Z)(y,e);var t,r,o,h,f,v,m,d,g,w=(d=y,g=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,t=(0,u.Z)(d);if(g){var r=(0,u.Z)(this).constructor;e=Reflect.construct(t,arguments,r)}else e=t.apply(this,arguments);return(0,l.Z)(this,e)});function y(e){var t;return(0,a.Z)(this,y),(t=w.call(this,e)).state=t.defaultState,t.bindHandlers(),t}return(0,i.Z)(y,[{key:"componentDidMount",value:(m=(0,s.Z)(p().mark((function e(){return p().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.initLocale();case 2:case"end":return e.stop()}}),e,this)}))),function(){return m.apply(this,arguments)})},{key:"componentDidUpdate",value:(v=(0,s.Z)(p().mark((function e(t){return p().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.handleLocaleChange(t.context.locale);case 2:case"end":return e.stop()}}),e,this)}))),function(e){return v.apply(this,arguments)})},{key:"handleLocaleChange",value:(f=(0,s.Z)(p().mark((function e(t){return p().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(this.props.context.locale===t){e.next=4;break}return e.next=4,this.setState({locale:this.props.context.locale});case 4:case"end":return e.stop()}}),e,this)}))),function(e){return f.apply(this,arguments)})},{key:"defaultState",get:function(){return{loading:!0,locale:null,processing:!1}}},{key:"areActionsAllowed",get:function(){return!this.state.processing}},{key:"bindHandlers",value:function(){this.handleLocaleInputChange=this.handleLocaleInputChange.bind(this)}},{key:"handleLocaleInputChange",value:(h=(0,s.Z)(p().mark((function e(t){var r,n;return p().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return r=t.target,n=r.value,e.next=4,this.updateLocale(n);case 4:case"end":return e.stop()}}),e,this)}))),function(e){return h.apply(this,arguments)})},{key:"updateLocale",value:(o=(0,s.Z)(p().mark((function e(t){return p().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.toggleProcessing();case 2:return e.next=4,this.props.context.onUpdateLocaleRequested(t);case 4:return e.next=6,this.toggleProcessing();case 6:case"end":return e.stop()}}),e,this)}))),function(e){return o.apply(this,arguments)})},{key:"initLocale",value:(r=(0,s.Z)(p().mark((function e(){return p().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.setState({locale:this.props.context.locale,loading:!1});case 2:case"end":return e.stop()}}),e,this)}))),function(){return r.apply(this,arguments)})},{key:"toggleProcessing",value:(t=(0,s.Z)(p().mark((function e(){var t;return p().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.state.processing,e.abrupt("return",this.setState({processing:!t}));case 2:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})},{key:"isLoading",value:function(){return this.state.loading}},{key:"render",value:function(){return n.createElement(n.Fragment,null,!this.isLoading()&&n.createElement("div",{className:"input select locale"},n.createElement("select",{id:"user-locale-input",name:"locale",value:this.state.locale,disabled:!this.areActionsAllowed,onChange:this.handleLocaleInputChange},this.props.context.siteSettings.supportedLocales.map((function(e){return n.createElement("option",{key:e.locale,value:e.locale},e.label)})))))}}]),y}(n.Component);ve.propTypes={context:w().any};const me=m(ve);var de=function(e){(0,c.Z)(s,e);var t,r,o=(t=s,r=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,n=(0,u.Z)(t);if(r){var o=(0,u.Z)(this).constructor;e=Reflect.construct(n,arguments,o)}else e=n.apply(this,arguments);return(0,l.Z)(this,e)});function s(){return(0,a.Z)(this,s),o.apply(this,arguments)}return(0,i.Z)(s,[{key:"statesToHideLocaleSwitch",get:function(){return[N.INITIAL_STATE]}},{key:"mustDisplayLocaleSwitch",get:function(){return!this.statesToHideLocaleSwitch.includes(this.props.apiRecoverContext.state)}},{key:"render",value:function(){return n.createElement(n.Fragment,null,this.mustDisplayLocaleSwitch&&n.createElement(me,null))}}]),s}(n.Component);de.propTypes={apiRecoverContext:w().any};const ge=P(de);const we=function(e){(0,c.Z)(m,e);var t,r,o,h,f,v=(h=m,f=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,t=(0,u.Z)(h);if(f){var r=(0,u.Z)(this).constructor;e=Reflect.construct(t,arguments,r)}else e=t.apply(this,arguments);return(0,l.Z)(this,e)});function m(e){var t;return(0,a.Z)(this,m),(t=v.call(this,e)).state=t.defaultState,t.userId=null,t.token=null,t.initializeProperties(),t}return(0,i.Z)(m,[{key:"defaultState",get:function(){return{siteSettings:null,trustedDomain:this.baseUrl,getApiClientOptions:this.getApiClientOptions.bind(this),locale:null,onUpdateLocaleRequested:this.onUpdateLocaleRequested.bind(this)}}},{key:"componentDidMount",value:(o=(0,s.Z)(p().mark((function e(){return p().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.getSiteSettings();case 2:this.initLocale();case 3:case"end":return e.stop()}}),e,this)}))),function(){return o.apply(this,arguments)})},{key:"initializeProperties",value:function(){var e="[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[0-5][a-fA-F0-9]{3}-[089aAbB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}",t="setup/recover/(".concat(e,")/(").concat(e,")$"),r=new RegExp(t),n=window.location.pathname.match(r);n?(this.userId=n[1],this.token=n[2]):console.error("Unable to retrieve the user id and token from the url")}},{key:"baseUrl",get:function(){var e=document.getElementsByTagName("base")&&document.getElementsByTagName("base")[0];return e?e.attributes.href.value.replace(/\/*$/g,""):(console.error("Unable to retrieve the page base tag"),"")}},{key:"getApiClientOptions",value:function(){return(new ee).setBaseUrl(this.state.trustedDomain)}},{key:"getSiteSettings",value:(r=(0,s.Z)(p().mark((function e(){var t,r,n,o,s;return p().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.getApiClientOptions().setResourceName("settings"),r=new T(t),e.next=4,r.findAll();case 4:return n=e.sent,o=n.body,s=new ce(o),e.next=9,this.setState({siteSettings:s});case 9:case"end":return e.stop()}}),e,this)}))),function(){return r.apply(this,arguments)})},{key:"initLocale",value:function(){var e=this.getUrlLocale()||this.getBrowserLocale()||this.getBrowserSimilarLocale()||this.state.siteSettings.locale;this.setState({locale:e})}},{key:"getUrlLocale",value:function(){var e=new URL(window.location.href).searchParams.get("locale");if(e){var t=this.state.siteSettings.supportedLocales.find((function(t){return e===t.locale}));if(t)return t.locale}}},{key:"getBrowserLocale",value:function(){var e=this.state.siteSettings.supportedLocales.find((function(e){return navigator.language===e.locale}));if(e)return e.locale}},{key:"getBrowserSimilarLocale",value:function(){var e=navigator.language.split("-")[0],t=this.state.siteSettings.supportedLocales.find((function(t){return e===t.locale.split("-")[0]}));if(t)return t.locale}},{key:"onUpdateLocaleRequested",value:(t=(0,s.Z)(p().mark((function e(t){return p().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.setState({locale:t});case 2:this.setUrlLocale(t);case 3:case"end":return e.stop()}}),e,this)}))),function(e){return t.apply(this,arguments)})},{key:"setUrlLocale",value:function(e){var t=new URL(window.location.href);t.searchParams.set("locale",e),window.history.replaceState(null,null,t)}},{key:"isReady",value:function(){return null!==this.state.siteSettings&&null!==this.state.locale}},{key:"render",value:function(){return n.createElement(d.Provider,{value:this.state},this.isReady()&&n.createElement(fe,{loadingPath:"".concat(this.state.trustedDomain,"/locales/{{lng}}/{{ns}}.json")},n.createElement(U,{value:{userId:this.userId,token:this.token}},n.createElement("div",{id:"container",className:"container page login"},n.createElement("div",{className:"content"},n.createElement("div",{className:"header"},n.createElement("div",{className:"logo"},n.createElement("span",{className:"visually-hidden"},"Passbolt"))),n.createElement("div",{className:"login-form"},n.createElement(Y,null)),n.createElement(ge,null))),n.createElement(oe,null))))}}]),m}(n.Component);var ye=document.createElement("div");document.body.appendChild(ye),o.render(n.createElement(we,null),ye)}},o={};function s(e){var t=o[e];if(void 0!==t)return t.exports;var r=o[e]={exports:{}};return n[e](r,r.exports,s),r.exports}s.m=n,e=[],s.O=(t,r,n,o)=>{if(!r){var a=1/0;for(l=0;l=o)&&Object.keys(s.O).every((e=>s.O[e](r[c])))?r.splice(c--,1):(i=!1,o0&&e[l-1][2]>o;l--)e[l]=e[l-1];e[l]=[r,n,o]},s.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return s.d(t,{a:t}),t},r=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,s.t=function(e,n){if(1&n&&(e=this(e)),8&n)return e;if("object"==typeof e&&e){if(4&n&&e.__esModule)return e;if(16&n&&"function"==typeof e.then)return e}var o=Object.create(null);s.r(o);var a={};t=t||[null,r({}),r([]),r(r)];for(var i=2&n&&e;"object"==typeof i&&!~t.indexOf(i);i=r(i))Object.getOwnPropertyNames(i).forEach((t=>a[t]=()=>e[t]));return a.default=()=>e,s.d(o,a),o},s.d=(e,t)=>{for(var r in t)s.o(t,r)&&!s.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},s.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),s.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),s.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},s.j=928,(()=>{var e={928:0};s.O.j=t=>0===e[t];var t=(t,r)=>{var n,o,[a,i,c]=r,l=0;for(n in i)s.o(i,n)&&(s.m[n]=i[n]);if(c)var u=c(s);for(t&&t(r);ls(1873)));a=s.O(a)})(); \ No newline at end of file +(()=>{"use strict";var e,t,r,s={1873:(e,t,r)=>{var s=r(7294),n=r(3935);function a(){return a=Object.assign||function(e){for(var t=1;ts.createElement(e,a({context:t},this.props))))}}}const i=o;var c=r(5697),h=r.n(c);class p extends Error{constructor(e,t){super(e),this.name="PassboltApiFetchError",this.data=t||{}}}const m=p;class d extends Error{constructor(){super("An internal error occurred. The server response could not be parsed. Please contact your administrator."),this.name="PassboltBadResponseError"}}const u=d;class g extends Error{constructor(e){super(e=e||"The service is unavailable"),this.name="PassboltServiceUnavailableError"}}const w=g;class v{constructor(e){if(this.options=e,!this.options.getBaseUrl())throw new TypeError("ApiClient constructor error: baseUrl is required.");if(!this.options.getResourceName())throw new TypeError("ApiClient constructor error: resourceName is required.");try{let e=this.options.getBaseUrl().toString();e.endsWith("/")&&(e=e.slice(0,-1)),this.baseUrl=`${e}/${this.options.getResourceName()}`,this.baseUrl=new URL(this.baseUrl)}catch(e){throw new TypeError("ApiClient constructor error: b.")}this.apiVersion="api-version=v2"}getDefaultHeaders(){return{Accept:"application/json","content-type":"application/json"}}buildFetchOptions(){return{credentials:"include",headers:{...this.getDefaultHeaders(),...this.options.getHeaders()}}}async get(e,t){this.assertValidId(e);const r=this.buildUrl(`${this.baseUrl}/${e}`,t||{});return this.fetchAndHandleResponse("GET",r)}async delete(e,t,r,s){let n;this.assertValidId(e),void 0===s&&(s=!1),n=s?this.buildUrl(`${this.baseUrl}/${e}/dry-run`,r||{}):this.buildUrl(`${this.baseUrl}/${e}`,r||{});let a=null;return t&&(a=this.buildBody(t)),this.fetchAndHandleResponse("DELETE",n,a)}async findAll(e){const t=this.buildUrl(this.baseUrl.toString(),e||{});return await this.fetchAndHandleResponse("GET",t)}async create(e,t){const r=this.buildUrl(this.baseUrl.toString(),t||{}),s=this.buildBody(e);return this.fetchAndHandleResponse("POST",r,s)}async update(e,t,r,s){let n;this.assertValidId(e),void 0===s&&(s=!1),n=s?this.buildUrl(`${this.baseUrl}/${e}/dry-run`,r||{}):this.buildUrl(`${this.baseUrl}/${e}`,r||{});let a=null;return t&&(a=this.buildBody(t)),this.fetchAndHandleResponse("PUT",n,a)}assertValidId(e){if(!e)throw new TypeError("ApiClient.assertValidId error: id cannot be empty");if("string"!=typeof e)throw new TypeError("ApiClient.assertValidId error: id should be a string")}assertMethod(e){"string"!=typeof e&&new TypeError("ApiClient.assertValidMethod method should be a string."),["GET","POST","PUT","DELETE"].indexOf(e)<0&&new TypeError(`ApiClient.assertValidMethod error: method ${e} is not supported.`)}assertUrl(e){if(!e)throw new TypeError("ApliClient.assertUrl error: url is required.");if(!(e instanceof URL))throw new TypeError("ApliClient.assertUrl error: url should be a valid URL object.")}assertBody(e){"string"!=typeof e&&new TypeError("ApiClient.assertBody error: body should be a string.")}buildBody(e){return JSON.stringify(e)}buildUrl(e,t){if("string"!=typeof e)throw new TypeError("ApiClient.buildUrl error: url should be a string.");const r=new URL(`${e}.json?${this.apiVersion}`);t=t||{};for(const[e,s]of Object.entries(t)){if("string"!=typeof e)throw new TypeError("ApiClient.buildUrl error: urlOptions key should be a string.");if("string"==typeof s)r.searchParams.append(e,s);else{if(!Array.isArray(s))throw new TypeError("ApiClient.buildUrl error: urlOptions value should be a string or array.");s.forEach((t=>{r.searchParams.append(e,t)}))}}return r}async fetchAndHandleResponse(e,t,r,s){let n,a;this.assertUrl(t),this.assertMethod(e),r&&this.assertBody(r);const o={...this.buildFetchOptions(),...s};o.method=e,r&&(o.body=r);try{n=await fetch(t.toString(),o)}catch(e){throw new w(e.message)}try{a=await n.json()}catch(e){throw new u}if(!n.ok){const e=a.header.message;throw new m(e,{code:n.status,body:a.body})}return a}}const q="chrome",E="edge",f="firefox";function b(){const e=window.navigator.userAgent.toLowerCase();let t;return t=e.indexOf("firefox")>-1?f:e.indexOf("samsungbrowser")>-1?"samsung":e.indexOf("opera")>-1||e.indexOf("opr")>-1?"opera":e.indexOf("trident")>-1?"internet-explorer":e.indexOf("edg")>-1?E:e.indexOf("chrome")>-1?q:e.indexOf("safari")>-1?"safari":"unknown",t}const y=(e,t)=>t.split(".").reduce(((e,t)=>void 0===e?e:e[t]),e);function x(){return x=Object.assign||function(e){for(var t=1;t{}});class z extends s.Component{constructor(e){super(e),this.state=Object.assign(this.defaultState,e.value)}get defaultState(){return{userId:null,token:null,state:L.INITIAL_STATE,onInitializeRecoverRequested:this.onInitializeRecoverRequested.bind(this)}}async onInitializeRecoverRequested(){return this.state.userId&&this.state.token?this.isBrowserSupported()?void await this.verifyRecoverInfo().then(this.handleRecoverVerifySuccess.bind(this)).catch(this.handleRecoverVerifyError.bind(this)):this.setState({state:L.DOWNLOAD_SUPPORTED_BROWSER_STATE}):this.setState({state:L.ERROR_STATE})}handleRecoverVerifySuccess(){this.setState({state:L.INSTALL_EXTENSION_STATE})}handleRecoverVerifyError(e){return e instanceof m&&y(e,"data.body.token.expired")?this.setState({state:L.TOKEN_EXPIRED_STATE}):this.setState({state:L.ERROR_STATE})}isBrowserSupported(){const e=b();return[q,f,E].includes(e)}async verifyRecoverInfo(){const e=this.props.context.getApiClientOptions();e.setResourceName("setup");const t=new v(e),{body:r}=await t.get(`recover/${this.state.userId}/${this.state.token}`);return r}render(){return s.createElement(T.Provider,{value:this.state},this.props.children)}}z.propTypes={context:h().any,value:h().any,children:h().any};const S=l(z);function C(e){return class extends s.Component{render(){return s.createElement(T.Consumer,null,(t=>s.createElement(e,x({apiRecoverContext:t},this.props))))}}}const L={INITIAL_STATE:"Initial state",DOWNLOAD_SUPPORTED_BROWSER_STATE:"Download supported browser state",INSTALL_EXTENSION_STATE:"Install extension state",TOKEN_EXPIRED_STATE:"Token expired state",ERROR_STATE:"Error state"};var R=r(9116);class A extends s.Component{render(){return s.createElement("div",{className:"login-processing"},s.createElement("div",{className:"processing-wrapper"},s.createElement("div",{className:"processing"})),s.createElement("h1",null,s.createElement(R.c,null,"Please wait...")))}}const U=A;var O=r(8831);const k="https://chrome.google.com/webstore/detail/passbolt-extension/didegimhafipceonhjepacocaffmoppf";class M extends s.Component{constructor(e){super(e),this.state=this.getDefaultState(),this.bindCallbacks()}getDefaultState(){const e=window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light";return{browserName:b(),theme:e}}bindCallbacks(){this.handleRefreshClick=this.handleRefreshClick.bind(this)}get browserStoreThumbnailUrl(){const e="dark"===this.state.theme?"white":"black";switch(this.state.browserName){case f:return`${this.props.context.trustedDomain}/img/third_party/FirefoxAMO_${e}.svg`;case E:return`${this.props.context.trustedDomain}/img/third_party/edge-addon-${e}.svg`;default:return`${this.props.context.trustedDomain}/img/third_party/ChromeWebStore_${e}.svg`}}get storeUrl(){switch(this.state.browserName){case q:return k;case f:return"https://addons.mozilla.org/firefox/addon/passbolt";case E:return"https://microsoftedge.microsoft.com/addons/detail/passbolt-extension/ljeppgjhohmhpbdhjjjbiflabdgfkhpo";default:return k}}get storeClassName(){return`browser-webstore ${this.state.browserName}`}handleRefreshClick(){window.location.reload()}get translate(){return this.props.t}render(){return s.createElement("div",{className:"install-extension"},s.createElement("h1",null,s.createElement(R.c,null,"Please install the browser extension.")),s.createElement("p",null,s.createElement(R.c,null,"Please download the browser extension and refresh this page to continue.")),this.state.browserName&&s.createElement("a",{href:this.storeUrl,className:this.storeClassName,target:"_blank",rel:"noopener noreferrer"},s.createElement("img",{src:this.browserStoreThumbnailUrl})),s.createElement("div",{className:"form-actions"},s.createElement("a",{href:this.storeUrl,className:"button primary big full-width",role:"button",target:"_blank",rel:"noopener noreferrer"},s.createElement(R.c,null,"Download extension")),s.createElement("a",{onClick:this.handleRefreshClick,role:"button"},s.createElement(R.c,null,"Refresh to detect extension"))))}}M.propTypes={context:h().any,t:h().func};const N=l((0,O.Z)("common")(M)),P="https://www.mozilla.org/firefox/download/thanks/";class B extends s.Component{get translate(){return this.props.t}render(){return s.createElement("div",{className:"browser-not-supported"},s.createElement("h1",null,s.createElement(R.c,null,"Sorry, your browser is not supported.")),s.createElement("p",null,s.createElement(R.c,null,"Please download chrome or firefox to get started with passbolt.")),s.createElement("a",{href:`${P}`,className:"browser",target:"_blank",rel:"noopener noreferrer"},s.createElement("img",{src:`${this.props.context.trustedDomain}/img/third_party/firefox_logo.png`})),s.createElement("div",{className:"form-actions"},s.createElement("a",{href:P,className:"button primary big full-width",role:"button",target:"_blank",rel:"noopener noreferrer"},s.createElement(R.c,null,"Download Firefox")),s.createElement("a",{href:"https://www.google.com/chrome/",role:"button",target:"_blank",rel:"noopener noreferrer"},s.createElement(R.c,null,"Download Chrome"))))}}B.propTypes={context:h().any,t:h().func};const _=l((0,O.Z)("common")(B));class I extends s.Component{get translate(){return this.props.t}render(){return s.createElement("div",{className:"setup-error"},s.createElement("h1",null,s.createElement(R.c,null,"Access to this service requires an invitation.")),s.createElement("p",null,s.createElement(R.c,null,"This email is not associated with any approved users on this domain.")," ",s.createElement(R.c,null,"Please contact your administrator to request an invitation link.")),s.createElement("div",{className:"form-actions"},s.createElement("a",{href:`${this.props.context.trustedDomain}/users/recover`,className:"button primary big full-width",role:"button"},s.createElement(R.c,null,"Try with another email"))))}}I.propTypes={context:h().any,t:h().func};const D=l((0,O.Z)("common")(I));class V extends s.Component{get translate(){return this.props.t}render(){return s.createElement("div",{className:"setup-error"},s.createElement("h1",null,s.createElement(R.c,null,"The invitation is expired.")),s.createElement("p",null,s.createElement(R.c,null,"You can request another invitation email by clicking on the button below.")),s.createElement("div",{className:"form-actions"},s.createElement("a",{href:`${this.props.context.trustedDomain}/users/recover`,className:"button primary big full-width",role:"button"},s.createElement(R.c,null,"Request invitation"))))}}V.propTypes={context:h().any,t:h().func};const j=l((0,O.Z)("common")(V));class H extends s.Component{componentDidMount(){this.initializeRecover()}initializeRecover(){setTimeout((()=>this.props.apiRecoverContext.onInitializeRecoverRequested()),1e3)}render(){switch(this.props.apiRecoverContext.state){case L.INSTALL_EXTENSION_STATE:return s.createElement(N,null);case L.DOWNLOAD_SUPPORTED_BROWSER_STATE:return s.createElement(_,null);case L.TOKEN_EXPIRED_STATE:return s.createElement(j,null);case L.ERROR_STATE:return s.createElement(D,null);default:return s.createElement(U,null)}}}H.propTypes={apiRecoverContext:h().object};const $=C(H);class F{constructor(e){this.setToken(e)}setToken(e){this.validate(e),this.token=e}validate(e){if(!e)throw new TypeError("CSRF token cannot be empty.");if("string"!=typeof e)throw new TypeError("CSRF token should be a string.")}toFetchHeaders(){return{"X-CSRF-Token":this.token}}}class Z{setBaseUrl(e){if(!e)throw new TypeError("ApiClientOption baseUrl is required.");if("string"==typeof e)try{this.baseUrl=new URL(e)}catch(e){throw new TypeError("ApiClientOption baseUrl is invalid.")}else{if(!(e instanceof URL))throw new TypeError("ApiClientOptions baseurl should be a string or URL");this.baseUrl=e}return this}setCsrfToken(e){if(!e)throw new TypeError("ApiClientOption csrfToken is required.");if("string"==typeof e)this.csrfToken=new F(e);else{if(!(e instanceof F))throw new TypeError("ApiClientOption csrfToken should be a string or a valid CsrfToken.");this.csrfToken=e}return this}setResourceName(e){if(!e)throw new TypeError("ApiClientOptions.setResourceName resourceName is required.");if("string"!=typeof e)throw new TypeError("ApiClientOptions.setResourceName resourceName should be a valid string.");return this.resourceName=e,this}getBaseUrl(){return this.baseUrl}getResourceName(){return this.resourceName}getHeaders(){if(this.csrfToken)return this.csrfToken.toFetchHeaders()}}class W extends s.Component{getClassName(){let e=`svg-icon ${this.props.name}`;return this.props.big&&(e+=" icon-only"),this.props.baseline&&(e+=" baseline"),this.props.dim&&(e+=" dim"),e}render(){return s.createElement("span",{className:this.getClassName(),onClick:this.props.onClick},"add"===this.props.name&&s.createElement("svg",{xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false","data-prefix":"fas","data-icon":"plus-circle",role:"img",viewBox:"0 0 512 512"},s.createElement("path",{d:"M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm144 276c0 6.6-5.4 12-12 12h-92v92c0 6.6-5.4 12-12 12h-56c-6.6 0-12-5.4-12-12v-92h-92c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h92v-92c0-6.6 5.4-12 12-12h56c6.6 0 12 5.4 12 12v92h92c6.6 0 12 5.4 12 12v56z"})),"ban"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1440 893q0-161-87-295l-754 753q137 89 297 89 111 0 211.5-43.5t173.5-116.5 116-174.5 43-212.5zm-999 299l755-754q-135-91-300-91-148 0-273 73t-198 199-73 274q0 162 89 299zm1223-299q0 157-61 300t-163.5 246-245 164-298.5 61-298.5-61-245-164-163.5-246-61-300 61-299.5 163.5-245.5 245-164 298.5-61 298.5 61 245 164 163.5 245.5 61 299.5z"})),"camera"===this.props.name&&s.createElement("svg",{width:"2048",height:"1792",viewBox:"0 0 2048 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1024 672q119 0 203.5 84.5t84.5 203.5-84.5 203.5-203.5 84.5-203.5-84.5-84.5-203.5 84.5-203.5 203.5-84.5zm704-416q106 0 181 75t75 181v896q0 106-75 181t-181 75h-1408q-106 0-181-75t-75-181v-896q0-106 75-181t181-75h224l51-136q19-49 69.5-84.5t103.5-35.5h512q53 0 103.5 35.5t69.5 84.5l51 136h224zm-704 1152q185 0 316.5-131.5t131.5-316.5-131.5-316.5-316.5-131.5-316.5 131.5-131.5 316.5 131.5 316.5 316.5 131.5z"})),"caret-right"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1152 896q0 26-19 45l-448 448q-19 19-45 19t-45-19-19-45v-896q0-26 19-45t45-19 45 19l448 448q19 19 19 45z"})),"caret-down"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1408 704q0 26-19 45l-448 448q-19 19-45 19t-45-19l-448-448q-19-19-19-45t19-45 45-19h896q26 0 45 19t19 45z"})),"caret-up"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1408 1216q0 26-19 45t-45 19h-896q-26 0-45-19t-19-45 19-45l448-448q19-19 45-19t45 19l448 448q19 19 19 45z"})),"chevron-left"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1427 301l-531 531 531 531q19 19 19 45t-19 45l-166 166q-19 19-45 19t-45-19l-742-742q-19-19-19-45t19-45l742-742q19-19 45-19t45 19l166 166q19 19 19 45t-19 45z"})),"chevron-right"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1363 877l-742 742q-19 19-45 19t-45-19l-166-166q-19-19-19-45t19-45l531-531-531-531q-19-19-19-45t19-45l166-166q19-19 45-19t45 19l742 742q19 19 19 45t-19 45z"})),"close"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1490 1322q0 40-28 68l-136 136q-28 28-68 28t-68-28l-294-294-294 294q-28 28-68 28t-68-28l-136-136q-28-28-28-68t28-68l294-294-294-294q-28-28-28-68t28-68l136-136q28-28 68-28t68 28l294 294 294-294q28-28 68-28t68 28l136 136q28 28 28 68t-28 68l-294 294 294 294q28 28 28 68z"})),"close-circle"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1277 1122q0-26-19-45l-181-181 181-181q19-19 19-45 0-27-19-46l-90-90q-19-19-46-19-26 0-45 19l-181 181-181-181q-19-19-45-19-27 0-46 19l-90 90q-19 19-19 46 0 26 19 45l181 181-181 181q-19 19-19 45 0 27 19 46l90 90q19 19 46 19 26 0 45-19l181-181 181 181q19 19 45 19 27 0 46-19l90-90q19-19 19-46zm387-226q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"})),"cog"===this.props.name&&s.createElement("svg",{xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false","data-prefix":"fas","data-icon":"cog",viewBox:"0 0 512 512"},s.createElement("path",{fill:"currentColor",d:"M487.4 315.7l-42.6-24.6c4.3-23.2 4.3-47 0-70.2l42.6-24.6c4.9-2.8 7.1-8.6 5.5-14-11.1-35.6-30-67.8-54.7-94.6-3.8-4.1-10-5.1-14.8-2.3L380.8 110c-17.9-15.4-38.5-27.3-60.8-35.1V25.8c0-5.6-3.9-10.5-9.4-11.7-36.7-8.2-74.3-7.8-109.2 0-5.5 1.2-9.4 6.1-9.4 11.7V75c-22.2 7.9-42.8 19.8-60.8 35.1L88.7 85.5c-4.9-2.8-11-1.9-14.8 2.3-24.7 26.7-43.6 58.9-54.7 94.6-1.7 5.4.6 11.2 5.5 14L67.3 221c-4.3 23.2-4.3 47 0 70.2l-42.6 24.6c-4.9 2.8-7.1 8.6-5.5 14 11.1 35.6 30 67.8 54.7 94.6 3.8 4.1 10 5.1 14.8 2.3l42.6-24.6c17.9 15.4 38.5 27.3 60.8 35.1v49.2c0 5.6 3.9 10.5 9.4 11.7 36.7 8.2 74.3 7.8 109.2 0 5.5-1.2 9.4-6.1 9.4-11.7v-49.2c22.2-7.9 42.8-19.8 60.8-35.1l42.6 24.6c4.9 2.8 11 1.9 14.8-2.3 24.7-26.7 43.6-58.9 54.7-94.6 1.5-5.5-.7-11.3-5.6-14.1zM256 336c-44.1 0-80-35.9-80-80s35.9-80 80-80 80 35.9 80 80-35.9 80-80 80z"})),"copy-to-clipboard"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M768 1664h896v-640h-416q-40 0-68-28t-28-68v-416h-384v1152zm256-1440v-64q0-13-9.5-22.5t-22.5-9.5h-704q-13 0-22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h704q13 0 22.5-9.5t9.5-22.5zm256 672h299l-299-299v299zm512 128v672q0 40-28 68t-68 28h-960q-40 0-68-28t-28-68v-160h-544q-40 0-68-28t-28-68v-1344q0-40 28-68t68-28h1088q40 0 68 28t28 68v328q21 13 36 28l408 408q28 28 48 76t20 88z"})),"download"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1344 1344q0-26-19-45t-45-19-45 19-19 45 19 45 45 19 45-19 19-45zm256 0q0-26-19-45t-45-19-45 19-19 45 19 45 45 19 45-19 19-45zm128-224v320q0 40-28 68t-68 28h-1472q-40 0-68-28t-28-68v-320q0-40 28-68t68-28h465l135 136q58 56 136 56t136-56l136-136h464q40 0 68 28t28 68zm-325-569q17 41-14 70l-448 448q-18 19-45 19t-45-19l-448-448q-31-29-14-70 17-39 59-39h256v-448q0-26 19-45t45-19h256q26 0 45 19t19 45v448h256q42 0 59 39z"})),"edit"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M888 1184l116-116-152-152-116 116v56h96v96h56zm440-720q-16-16-33 1l-350 350q-17 17-1 33t33-1l350-350q17-17 1-33zm80 594v190q0 119-84.5 203.5t-203.5 84.5h-832q-119 0-203.5-84.5t-84.5-203.5v-832q0-119 84.5-203.5t203.5-84.5h832q63 0 117 25 15 7 18 23 3 17-9 29l-49 49q-14 14-32 8-23-6-45-6h-832q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113v-126q0-13 9-22l64-64q15-15 35-7t20 29zm-96-738l288 288-672 672h-288v-288zm444 132l-92 92-288-288 92-92q28-28 68-28t68 28l152 152q28 28 28 68t-28 68z"})),"envelope"===this.props.name&&s.createElement("svg",{width:"512",height:"512",viewBox:"0 0 512 512",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{fill:"currentColor",d:"M464 64H48C21.49 64 0 85.49 0 112v288c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V112c0-26.51-21.49-48-48-48zm0 48v40.805c-22.422 18.259-58.168 46.651-134.587 106.49-16.841 13.247-50.201 45.072-73.413 44.701-23.208.375-56.579-31.459-73.413-44.701C106.18 199.465 70.425 171.067 48 152.805V112h416zM48 400V214.398c22.914 18.251 55.409 43.862 104.938 82.646 21.857 17.205 60.134 55.186 103.062 54.955 42.717.231 80.509-37.199 103.053-54.947 49.528-38.783 82.032-64.401 104.947-82.653V400H48z"})," "),"eye-open"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1664 960q-152-236-381-353 61 104 61 225 0 185-131.5 316.5t-316.5 131.5-316.5-131.5-131.5-316.5q0-121 61-225-229 117-381 353 133 205 333.5 326.5t434.5 121.5 434.5-121.5 333.5-326.5zm-720-384q0-20-14-34t-34-14q-125 0-214.5 89.5t-89.5 214.5q0 20 14 34t34 14 34-14 14-34q0-86 61-147t147-61q20 0 34-14t14-34zm848 384q0 34-20 69-140 230-376.5 368.5t-499.5 138.5-499.5-139-376.5-368q-20-35-20-69t20-69q140-229 376.5-368t499.5-139 499.5 139 376.5 368q20 35 20 69z"})),"eye-close"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M555 1335l78-141q-87-63-136-159t-49-203q0-121 61-225-229 117-381 353 167 258 427 375zm389-759q0-20-14-34t-34-14q-125 0-214.5 89.5t-89.5 214.5q0 20 14 34t34 14 34-14 14-34q0-86 61-147t147-61q20 0 34-14t14-34zm363-191q0 7-1 9-106 189-316 567t-315 566l-49 89q-10 16-28 16-12 0-134-70-16-10-16-28 0-12 44-87-143-65-263.5-173t-208.5-245q-20-31-20-69t20-69q153-235 380-371t496-136q89 0 180 17l54-97q10-16 28-16 5 0 18 6t31 15.5 33 18.5 31.5 18.5 19.5 11.5q16 10 16 27zm37 447q0 139-79 253.5t-209 164.5l280-502q8 45 8 84zm448 128q0 35-20 69-39 64-109 145-150 172-347.5 267t-419.5 95l74-132q212-18 392.5-137t301.5-307q-115-179-282-294l63-112q95 64 182.5 153t144.5 184q20 34 20 69z"})),"external-link"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1408 928v-480q0-26-19-45t-45-19h-480q-42 0-59 39-17 41 14 70l144 144-534 534q-19 19-19 45t19 45l102 102q19 19 45 19t45-19l534-534 144 144q18 19 45 19 12 0 25-5 39-17 39-59zm256-512v960q0 119-84.5 203.5t-203.5 84.5h-960q-119 0-203.5-84.5t-84.5-203.5v-960q0-119 84.5-203.5t203.5-84.5h960q119 0 203.5 84.5t84.5 203.5z"})),"folder"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1728 608v704q0 92-66 158t-158 66h-1216q-92 0-158-66t-66-158v-960q0-92 66-158t158-66h320q92 0 158 66t66 158v32h672q92 0 158 66t66 158z"})),"folder-shared"===this.props.name&&s.createElement("svg",{viewBox:"2 2 20 20",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M20 6h-8l-2-2H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm-5 3c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm4 8h-8v-1c0-1.33 2.67-2 4-2s4 .67 4 2v1z"})),"heart"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M896 1664q-26 0-44-18l-624-602q-10-8-27.5-26t-55.5-65.5-68-97.5-53.5-121-23.5-138q0-220 127-344t351-124q62 0 126.5 21.5t120 58 95.5 68.5 76 68q36-36 76-68t95.5-68.5 120-58 126.5-21.5q224 0 351 124t127 344q0 221-229 450l-623 600q-18 18-44 18z"})),"heart-o"===this.props.name&&s.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 512 512"},s.createElement("path",{d:"M458.4 64.3C400.6 15.7 311.3 23 256 79.3 200.7 23 111.4 15.6 53.6 64.3-21.6 127.6-10.6 230.8 43 285.5l175.4 178.7c10 10.2 23.4 15.9 37.6 15.9 14.3 0 27.6-5.6 37.6-15.8L469 285.6c53.5-54.7 64.7-157.9-10.6-221.3zm-23.6 187.5L259.4 430.5c-2.4 2.4-4.4 2.4-6.8 0L77.2 251.8c-36.5-37.2-43.9-107.6 7.3-150.7 38.9-32.7 98.9-27.8 136.5 10.5l35 35.7 35-35.7c37.8-38.5 97.8-43.2 136.5-10.6 51.1 43.1 43.5 113.9 7.3 150.8z"})),"heartbeat"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1280 1024h305q-5 6-10 10.5t-9 7.5l-3 4-623 600q-18 18-44 18t-44-18l-624-602q-5-2-21-20h369q22 0 39.5-13.5t22.5-34.5l70-281 190 667q6 20 23 33t39 13q21 0 38-13t23-33l146-485 56 112q18 35 57 35zm512-428q0 145-103 300h-369l-111-221q-8-17-25.5-27t-36.5-8q-45 5-56 46l-129 430-196-686q-6-20-23.5-33t-39.5-13-39 13.5-22 34.5l-116 464h-423q-103-155-103-300 0-220 127-344t351-124q62 0 126.5 21.5t120 58 95.5 68.5 76 68q36-36 76-68t95.5-68.5 120-58 126.5-21.5q224 0 351 124t127 344z"})),"info-circle"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1152 1376v-160q0-14-9-23t-23-9h-96v-512q0-14-9-23t-23-9h-320q-14 0-23 9t-9 23v160q0 14 9 23t23 9h96v320h-96q-14 0-23 9t-9 23v160q0 14 9 23t23 9h448q14 0 23-9t9-23zm-128-896v-160q0-14-9-23t-23-9h-192q-14 0-23 9t-9 23v160q0 14 9 23t23 9h192q14 0 23-9t9-23zm640 416q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"})),"key"===this.props.name&&s.createElement("svg",{width:"512",height:"512",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 512 512"},s.createElement("path",{fill:"currentColor",d:"M512 176.001C512 273.203 433.202 352 336 352c-11.22 0-22.19-1.062-32.827-3.069l-24.012 27.014A23.999 23.999 0 0 1 261.223 384H224v40c0 13.255-10.745 24-24 24h-40v40c0 13.255-10.745 24-24 24H24c-13.255 0-24-10.745-24-24v-78.059c0-6.365 2.529-12.47 7.029-16.971l161.802-161.802C163.108 213.814 160 195.271 160 176 160 78.798 238.797.001 335.999 0 433.488-.001 512 78.511 512 176.001zM336 128c0 26.51 21.49 48 48 48s48-21.49 48-48-21.49-48-48-48-48 21.49-48 48z"})),"link"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1520 1216q0-40-28-68l-208-208q-28-28-68-28-42 0-72 32 3 3 19 18.5t21.5 21.5 15 19 13 25.5 3.5 27.5q0 40-28 68t-68 28q-15 0-27.5-3.5t-25.5-13-19-15-21.5-21.5-18.5-19q-33 31-33 73 0 40 28 68l206 207q27 27 68 27 40 0 68-26l147-146q28-28 28-67zm-703-705q0-40-28-68l-206-207q-28-28-68-28-39 0-68 27l-147 146q-28 28-28 67 0 40 28 68l208 208q27 27 68 27 42 0 72-31-3-3-19-18.5t-21.5-21.5-15-19-13-25.5-3.5-27.5q0-40 28-68t68-28q15 0 27.5 3.5t25.5 13 19 15 21.5 21.5 18.5 19q33-31 33-73zm895 705q0 120-85 203l-147 146q-83 83-203 83-121 0-204-85l-206-207q-83-83-83-203 0-123 88-209l-88-88q-86 88-208 88-120 0-204-84l-208-208q-84-84-84-204t85-203l147-146q83-83 203-83 121 0 204 85l206 207q83 83 83 203 0 123-88 209l88 88q86-88 208-88 120 0 204 84l208 208q84 84 84 204z"})),"lock"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M640 768h512v-192q0-106-75-181t-181-75-181 75-75 181v192zm832 96v576q0 40-28 68t-68 28h-960q-40 0-68-28t-28-68v-576q0-40 28-68t68-28h32v-192q0-184 132-316t316-132 316 132 132 316v192h32q40 0 68 28t28 68z"})),"lock-open"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1376 768q40 0 68 28t28 68v576q0 40-28 68t-68 28h-960q-40 0-68-28t-28-68v-576q0-40 28-68t68-28h32v-320q0-185 131.5-316.5t316.5-131.5 316.5 131.5 131.5 316.5q0 26-19 45t-45 19h-64q-26 0-45-19t-19-45q0-106-75-181t-181-75-181 75-75 181v320h736z"})),"magic-wand"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1254 581l293-293-107-107-293 293zm447-293q0 27-18 45l-1286 1286q-18 18-45 18t-45-18l-198-198q-18-18-18-45t18-45l1286-1286q18-18 45-18t45 18l198 198q18 18 18 45zm-1351-190l98 30-98 30-30 98-30-98-98-30 98-30 30-98zm350 162l196 60-196 60-60 196-60-196-196-60 196-60 60-196zm930 478l98 30-98 30-30 98-30-98-98-30 98-30 30-98zm-640-640l98 30-98 30-30 98-30-98-98-30 98-30 30-98z"})),"plus-circle"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1344 960v-128q0-26-19-45t-45-19h-256v-256q0-26-19-45t-45-19h-128q-26 0-45 19t-19 45v256h-256q-26 0-45 19t-19 45v128q0 26 19 45t45 19h256v256q0 26 19 45t45 19h128q26 0 45-19t19-45v-256h256q26 0 45-19t19-45zm320-64q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"})),"plus-square"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 448 512",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48zm-32 252c0 6.6-5.4 12-12 12h-92v92c0 6.6-5.4 12-12 12h-56c-6.6 0-12-5.4-12-12v-92H92c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h92v-92c0-6.6 5.4-12 12-12h56c6.6 0 12 5.4 12 12v92h92c6.6 0 12 5.4 12 12v56z"})),"filter"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 512 512",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M487.976 0H24.028C2.71 0-8.047 25.866 7.058 40.971L192 225.941V432c0 7.831 3.821 15.17 10.237 19.662l80 55.98C298.02 518.69 320 507.493 320 487.98V225.941l184.947-184.97C520.021 25.896 509.338 0 487.976 0z"})),"life-ring"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 512 512",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M256 504c136.967 0 248-111.033 248-248S392.967 8 256 8 8 119.033 8 256s111.033 248 248 248zm-103.398-76.72l53.411-53.411c31.806 13.506 68.128 13.522 99.974 0l53.411 53.411c-63.217 38.319-143.579 38.319-206.796 0zM336 256c0 44.112-35.888 80-80 80s-80-35.888-80-80 35.888-80 80-80 80 35.888 80 80zm91.28 103.398l-53.411-53.411c13.505-31.806 13.522-68.128 0-99.974l53.411-53.411c38.319 63.217 38.319 143.579 0 206.796zM359.397 84.72l-53.411 53.411c-31.806-13.505-68.128-13.522-99.973 0L152.602 84.72c63.217-38.319 143.579-38.319 206.795 0zM84.72 152.602l53.411 53.411c-13.506 31.806-13.522 68.128 0 99.974L84.72 359.398c-38.319-63.217-38.319-143.579 0-206.796z"})),"plug"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 384 512",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M320,32a32,32,0,0,0-64,0v96h64Zm48,128H16A16,16,0,0,0,0,176v32a16,16,0,0,0,16,16H32v32A160.07,160.07,0,0,0,160,412.8V512h64V412.8A160.07,160.07,0,0,0,352,256V224h16a16,16,0,0,0,16-16V176A16,16,0,0,0,368,160ZM128,32a32,32,0,0,0-64,0v96h64Z"})),"printer"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M448 1536h896v-256h-896v256zm0-640h896v-384h-160q-40 0-68-28t-28-68v-160h-640v640zm1152 64q0-26-19-45t-45-19-45 19-19 45 19 45 45 19 45-19 19-45zm128 0v416q0 13-9.5 22.5t-22.5 9.5h-224v160q0 40-28 68t-68 28h-960q-40 0-68-28t-28-68v-160h-224q-13 0-22.5-9.5t-9.5-22.5v-416q0-79 56.5-135.5t135.5-56.5h64v-544q0-40 28-68t68-28h672q40 0 88 20t76 48l152 152q28 28 48 76t20 88v256h64q79 0 135.5 56.5t56.5 135.5z"})),"refresh"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 512 512",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M370.72 133.28C339.458 104.008 298.888 87.962 255.848 88c-77.458.068-144.328 53.178-162.791 126.85-1.344 5.363-6.122 9.15-11.651 9.15H24.103c-7.498 0-13.194-6.807-11.807-14.176C33.933 94.924 134.813 8 256 8c66.448 0 126.791 26.136 171.315 68.685L463.03 40.97C478.149 25.851 504 36.559 504 57.941V192c0 13.255-10.745 24-24 24H345.941c-21.382 0-32.09-25.851-16.971-40.971l41.75-41.749zM32 296h134.059c21.382 0 32.09 25.851 16.971 40.971l-41.75 41.75c31.262 29.273 71.835 45.319 114.876 45.28 77.418-.07 144.315-53.144 162.787-126.849 1.344-5.363 6.122-9.15 11.651-9.15h57.304c7.498 0 13.194 6.807 11.807 14.176C478.067 417.076 377.187 504 256 504c-66.448 0-126.791-26.136-171.315-68.685L48.97 471.03C33.851 486.149 8 475.441 8 454.059V320c0-13.255 10.745-24 24-24z"})),"question-circle"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1024 1376v-192q0-14-9-23t-23-9h-192q-14 0-23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23-9t9-23zm256-672q0-88-55.5-163t-138.5-116-170-41q-243 0-371 213-15 24 8 42l132 100q7 6 19 6 16 0 25-12 53-68 86-92 34-24 86-24 48 0 85.5 26t37.5 59q0 38-20 61t-68 45q-63 28-115.5 86.5t-52.5 125.5v36q0 14 9 23t23 9h192q14 0 23-9t9-23q0-19 21.5-49.5t54.5-49.5q32-18 49-28.5t46-35 44.5-48 28-60.5 12.5-81zm384 192q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"})),"search"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1216 832q0-185-131.5-316.5t-316.5-131.5-316.5 131.5-131.5 316.5 131.5 316.5 316.5 131.5 316.5-131.5 131.5-316.5zm512 832q0 52-38 90t-90 38q-54 0-90-38l-343-342q-179 124-399 124-143 0-273.5-55.5t-225-150-150-225-55.5-273.5 55.5-273.5 150-225 225-150 273.5-55.5 273.5 55.5 225 150 150 225 55.5 273.5q0 220-124 399l343 343q37 37 37 90z"})),"share"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1764 11q33 24 27 64l-256 1536q-5 29-32 45-14 8-31 8-11 0-24-5l-453-185-242 295q-18 23-49 23-13 0-22-4-19-7-30.5-23.5t-11.5-36.5v-349l864-1059-1069 925-395-162q-37-14-40-55-2-40 32-59l1664-960q15-9 32-9 20 0 36 11z"})),"star"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1728 647q0 22-26 48l-363 354 86 500q1 7 1 20 0 21-10.5 35.5t-30.5 14.5q-19 0-40-12l-449-236-449 236q-22 12-40 12-21 0-31.5-14.5t-10.5-35.5q0-6 2-20l86-500-364-354q-25-27-25-48 0-37 56-46l502-73 225-455q19-41 49-41t49 41l225 455 502 73q56 9 56 46z"})),"save"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 448 512",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M433.941 129.941l-83.882-83.882A48 48 0 0 0 316.118 32H48C21.49 32 0 53.49 0 80v352c0 26.51 21.49 48 48 48h352c26.51 0 48-21.49 48-48V163.882a48 48 0 0 0-14.059-33.941zM272 80v80H144V80h128zm122 352H54a6 6 0 0 1-6-6V86a6 6 0 0 1 6-6h42v104c0 13.255 10.745 24 24 24h176c13.255 0 24-10.745 24-24V83.882l78.243 78.243a6 6 0 0 1 1.757 4.243V426a6 6 0 0 1-6 6zM224 232c-48.523 0-88 39.477-88 88s39.477 88 88 88 88-39.477 88-88-39.477-88-88-88zm0 128c-22.056 0-40-17.944-40-40s17.944-40 40-40 40 17.944 40 40-17.944 40-40 40z"})),"trash"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M704 1376v-704q0-14-9-23t-23-9h-64q-14 0-23 9t-9 23v704q0 14 9 23t23 9h64q14 0 23-9t9-23zm256 0v-704q0-14-9-23t-23-9h-64q-14 0-23 9t-9 23v704q0 14 9 23t23 9h64q14 0 23-9t9-23zm256 0v-704q0-14-9-23t-23-9h-64q-14 0-23 9t-9 23v704q0 14 9 23t23 9h64q14 0 23-9t9-23zm-544-992h448l-48-117q-7-9-17-11h-317q-10 2-17 11zm928 32v64q0 14-9 23t-23 9h-96v948q0 83-47 143.5t-113 60.5h-832q-66 0-113-58.5t-47-141.5v-952h-96q-14 0-23-9t-9-23v-64q0-14 9-23t23-9h309l70-167q15-37 54-63t79-26h320q40 0 79 26t54 63l70 167h309q14 0 23 9t9 23z"})),"upload"===this.props.name&&s.createElement("svg",{width:"2048",height:"1792",viewBox:"0 0 2048 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1344 864q0-14-9-23l-352-352q-9-9-23-9t-23 9l-351 351q-10 12-10 24 0 14 9 23t23 9h224v352q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5-9.5t9.5-22.5v-352h224q13 0 22.5-9.5t9.5-22.5zm640 288q0 159-112.5 271.5t-271.5 112.5h-1088q-185 0-316.5-131.5t-131.5-316.5q0-130 70-240t188-165q-2-30-2-43 0-212 150-362t362-150q156 0 285.5 87t188.5 231q71-62 166-62 106 0 181 75t75 181q0 76-41 138 130 31 213.5 135.5t83.5 238.5z"})),"upload-a"===this.props.name&&s.createElement("svg",{"aria-hidden":"true",focusable:"false","data-prefix":"fas","data-icon":"upload",className:"svg-inline--fa fa-upload fa-w-16",role:"img",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 512 512"},s.createElement("path",{fill:"currentColor",d:"M296 384h-80c-13.3 0-24-10.7-24-24V192h-87.7c-17.8 0-26.7-21.5-14.1-34.1L242.3 5.7c7.5-7.5 19.8-7.5 27.3 0l152.2 152.2c12.6 12.6 3.7 34.1-14.1 34.1H320v168c0 13.3-10.7 24-24 24zm216-8v112c0 13.3-10.7 24-24 24H24c-13.3 0-24-10.7-24-24V376c0-13.3 10.7-24 24-24h136v8c0 30.9 25.1 56 56 56h80c30.9 0 56-25.1 56-56v-8h136c13.3 0 24 10.7 24 24zm-124 88c0-11-9-20-20-20s-20 9-20 20 9 20 20 20 20-9 20-20zm64 0c0-11-9-20-20-20s-20 9-20 20 9 20 20 20 20-9 20-20z"})),"user"===this.props.name&&s.createElement("svg",{width:"448",height:"512",viewBox:"0 0 448 512",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{fill:"currentColor",d:"M224 256c70.7 0 128-57.3 128-128S294.7 0 224 0 96 57.3 96 128s57.3 128 128 128zm89.6 32h-16.7c-22.2 10.2-46.9 16-72.9 16s-50.6-5.8-72.9-16h-16.7C60.2 288 0 348.2 0 422.4V464c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48v-41.6c0-74.2-60.2-134.4-134.4-134.4z",className:""})),"users"===this.props.name&&s.createElement("svg",{width:"640",height:"512",viewBox:"0 0 640 512",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{fill:"currentColor",d:"M96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm448 0c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm32 32h-64c-17.6 0-33.5 7.1-45.1 18.6 40.3 22.1 68.9 62 75.1 109.4h66c17.7 0 32-14.3 32-32v-32c0-35.3-28.7-64-64-64zm-256 0c61.9 0 112-50.1 112-112S381.9 32 320 32 208 82.1 208 144s50.1 112 112 112zm76.8 32h-8.3c-20.8 10-43.9 16-68.5 16s-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h288c26.5 0 48-21.5 48-48v-28.8c0-63.6-51.6-115.2-115.2-115.2zm-223.7-13.4C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z"})),"warning"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1024 1375v-190q0-14-9.5-23.5t-22.5-9.5h-192q-13 0-22.5 9.5t-9.5 23.5v190q0 14 9.5 23.5t22.5 9.5h192q13 0 22.5-9.5t9.5-23.5zm-2-374l18-459q0-12-10-19-13-11-24-11h-220q-11 0-24 11-10 7-10 21l17 457q0 10 10 16.5t24 6.5h185q14 0 23.5-6.5t10.5-16.5zm-14-934l768 1408q35 63-2 126-17 29-46.5 46t-63.5 17h-1536q-34 0-63.5-17t-46.5-46q-37-63-2-126l768-1408q17-31 47-49t65-18 65 18 47 49z"})))}}W.defaultProps={big:!1,baseline:!1,dim:!1,onClick:()=>{}},W.propTypes={name:h().string,big:h().bool,dim:h().bool,baseline:h().bool,onClick:h().func};const X=W;function K(){return K=Object.assign||function(e){for(var t=1;t{if(void 0===e||"string"!=typeof e||!e.length)return!1;if((t=t||{}).whitelistedProtocols&&!Array.isArray(t.whitelistedProtocols))throw new TypeError("The whitelistedProtocols should be an array of string.");if(t.defaultProtocol&&"string"!=typeof t.defaultProtocol)throw new TypeError("The defaultProtocol should be a string.");const r=t.whitelistedProtocols||[Q.HTTP,Q.HTTPS],s=[Q.JAVASCRIPT],n=t.defaultProtocol||"";!/^((?!:\/\/).)*:\/\//.test(e)&&n&&(e=`${n}//${e}`);try{const t=new URL(e);return!s.includes(t.protocol)&&!!r.includes(t.protocol)&&t.href}catch(e){return!1}},Q={FTP:"http:",FTPS:"https:",HTTP:"http:",HTTPS:"https:",JAVASCRIPT:"javascript:",SSH:"ssh:"};class ee{constructor(e){this.settings=e}canIUse(e){let t=!1;const r=`passbolt.plugins.${e}`,s=y(this.settings,r)||null;if(s&&"object"==typeof s){const e=y(s,"enabled");void 0!==e&&!0!==e||(t=!0)}return t}getPluginSettings(e){const t=`passbolt.plugins.${e}`;return y(this.settings,t)}getRememberMeOptions(){return(this.getPluginSettings("rememberMe")||{}).options||{}}get hasRememberMeUntilILogoutOption(){return void 0!==(this.getRememberMeOptions()||{})[-1]}getServerTimezone(){return y(this.settings,"passbolt.app.server_timezone")}get termsLink(){const e=y(this.settings,"passbolt.legal.terms.url");return!!e&&Y(e)}get privacyLink(){const e=y(this.settings,"passbolt.legal.privacy_policy.url");return!!e&&Y(e)}get registrationPublic(){return!0===y(this.settings,"passbolt.registration.public")}get debug(){return!0===y(this.settings,"app.debug")}get url(){return y(this.settings,"app.url")||""}get version(){return y(this.settings,"app.version.number")}get locale(){return y(this.settings,"app.locale")||ee.DEFAULT_LOCALE.locale}async setLocale(e){this.settings.app.locale=e}get supportedLocales(){return y(this.settings,"passbolt.plugins.locale.options")||ee.DEFAULT_SUPPORTED_LOCALES}get generatorConfiguration(){return y(this.settings,"passbolt.plugins.generator.configuration")}static get DEFAULT_SUPPORTED_LOCALES(){return[ee.DEFAULT_LOCALE]}static get DEFAULT_LOCALE(){return{locale:"en-UK",label:"English"}}}var te=r(1932),re=r(8718),se=r(3554);class ne extends s.Component{constructor(e){super(e),this.state=this.defaultState}get defaultState(){return{ready:!1}}async componentDidMount(){await te.Z.use(re.Db).use(se.Z).init({lng:this.locale,load:"currentOnly",react:{useSuspense:!1},backend:{loadPath:this.props.loadingPath||"/locales/{{lng}}/{{ns}}.json"},supportedLngs:this.supportedLocales,fallbackLng:!1,ns:["common"],defaultNS:"common",keySeparator:!1,nsSeparator:!1,debug:!1}),this.setState({ready:!0})}get supportedLocales(){return this.props.context.siteSettings.supportedLocales?this.props.context.siteSettings.supportedLocales.map((e=>e.locale)):[this.locale]}get locale(){return this.props.context.locale}async componentDidUpdate(e){await this.handleLocaleChange(e.context.locale)}async handleLocaleChange(e){this.locale!==e&&await te.Z.changeLanguage(this.locale)}get isReady(){return this.state.ready}render(){return s.createElement(s.Fragment,null,this.isReady&&this.props.children)}}ne.propTypes={context:h().any,loadingPath:h().any,children:h().any};const ae=l(ne);class oe extends s.Component{constructor(e){super(e),this.state=this.defaultState,this.bindHandlers()}async componentDidMount(){await this.initLocale()}async componentDidUpdate(e){await this.handleLocaleChange(e.context.locale)}async handleLocaleChange(e){this.props.context.locale!==e&&await this.setState({locale:this.props.context.locale})}get defaultState(){return{loading:!0,locale:null,processing:!1}}get areActionsAllowed(){return!this.state.processing}bindHandlers(){this.handleLocaleInputChange=this.handleLocaleInputChange.bind(this)}async handleLocaleInputChange(e){const t=e.target.value;await this.updateLocale(t)}async updateLocale(e){await this.toggleProcessing(),await this.props.context.onUpdateLocaleRequested(e),await this.toggleProcessing()}async initLocale(){await this.setState({locale:this.props.context.locale,loading:!1})}async toggleProcessing(){const e=this.state.processing;return this.setState({processing:!e})}isLoading(){return this.state.loading}render(){return s.createElement(s.Fragment,null,!this.isLoading()&&s.createElement("div",{className:"input select locale"},s.createElement("select",{id:"user-locale-input",name:"locale",value:this.state.locale,disabled:!this.areActionsAllowed,onChange:this.handleLocaleInputChange},this.props.context.siteSettings.supportedLocales.map((e=>s.createElement("option",{key:e.locale,value:e.locale},e.label))))))}}oe.propTypes={context:h().any};const le=l(oe);class ie extends s.Component{get statesToHideLocaleSwitch(){return[L.INITIAL_STATE]}get mustDisplayLocaleSwitch(){return!this.statesToHideLocaleSwitch.includes(this.props.apiRecoverContext.state)}render(){return s.createElement(s.Fragment,null,this.mustDisplayLocaleSwitch&&s.createElement(le,null))}}ie.propTypes={apiRecoverContext:h().any};const ce=C(ie);class he extends s.Component{constructor(e){super(e),this.state=this.defaultState,this.userId=null,this.token=null,this.initializeProperties()}get defaultState(){return{siteSettings:null,trustedDomain:this.baseUrl,getApiClientOptions:this.getApiClientOptions.bind(this),locale:null,onUpdateLocaleRequested:this.onUpdateLocaleRequested.bind(this)}}async componentDidMount(){await this.getSiteSettings(),this.initLocale()}initializeProperties(){const e="[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[0-5][a-fA-F0-9]{3}-[089aAbB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}",t=new RegExp(`setup/recover/(${e})/(${e})$`),r=window.location.pathname.match(t);r?(this.userId=r[1],this.token=r[2]):console.error("Unable to retrieve the user id and token from the url")}get baseUrl(){const e=document.getElementsByTagName("base")&&document.getElementsByTagName("base")[0];return e?e.attributes.href.value.replace(/\/*$/g,""):(console.error("Unable to retrieve the page base tag"),"")}getApiClientOptions(){return(new Z).setBaseUrl(this.state.trustedDomain)}async getSiteSettings(){const e=this.getApiClientOptions().setResourceName("settings"),t=new v(e),{body:r}=await t.findAll(),s=new ee(r);await this.setState({siteSettings:s})}initLocale(){const e=this.getUrlLocale()||this.getBrowserLocale()||this.getBrowserSimilarLocale()||this.state.siteSettings.locale;this.setState({locale:e})}getUrlLocale(){const e=new URL(window.location.href).searchParams.get("locale");if(e){const t=this.state.siteSettings.supportedLocales.find((t=>e===t.locale));if(t)return t.locale}}getBrowserLocale(){const e=this.state.siteSettings.supportedLocales.find((e=>navigator.language===e.locale));if(e)return e.locale}getBrowserSimilarLocale(){const e=navigator.language.split("-")[0],t=this.state.siteSettings.supportedLocales.find((t=>e===t.locale.split("-")[0]));if(t)return t.locale}async onUpdateLocaleRequested(e){await this.setState({locale:e}),this.setUrlLocale(e)}setUrlLocale(e){const t=new URL(window.location.href);t.searchParams.set("locale",e),window.history.replaceState(null,null,t)}isReady(){return null!==this.state.siteSettings&&null!==this.state.locale}render(){return s.createElement(i.Provider,{value:this.state},this.isReady()&&s.createElement(ae,{loadingPath:`${this.state.trustedDomain}/locales/{{lng}}/{{ns}}.json`},s.createElement(S,{value:{userId:this.userId,token:this.token}},s.createElement("div",{id:"container",className:"container page login"},s.createElement("div",{className:"content"},s.createElement("div",{className:"header"},s.createElement("div",{className:"logo"},s.createElement("span",{className:"visually-hidden"},"Passbolt"))),s.createElement("div",{className:"login-form"},s.createElement($,null)),s.createElement(ce,null))),s.createElement(J,null))))}}const pe=he,me=document.createElement("div");document.body.appendChild(me),n.render(s.createElement(pe,null),me)}},n={};function a(e){var t=n[e];if(void 0!==t)return t.exports;var r=n[e]={exports:{}};return s[e].call(r.exports,r,r.exports,a),r.exports}a.m=s,e=[],a.O=(t,r,s,n)=>{if(!r){var o=1/0;for(h=0;h=n)&&Object.keys(a.O).every((e=>a.O[e](r[i])))?r.splice(i--,1):(l=!1,n0&&e[h-1][2]>n;h--)e[h]=e[h-1];e[h]=[r,s,n]},a.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return a.d(t,{a:t}),t},r=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,a.t=function(e,s){if(1&s&&(e=this(e)),8&s)return e;if("object"==typeof e&&e){if(4&s&&e.__esModule)return e;if(16&s&&"function"==typeof e.then)return e}var n=Object.create(null);a.r(n);var o={};t=t||[null,r({}),r([]),r(r)];for(var l=2&s&&e;"object"==typeof l&&!~t.indexOf(l);l=r(l))Object.getOwnPropertyNames(l).forEach((t=>o[t]=()=>e[t]));return o.default=()=>e,a.d(n,o),n},a.d=(e,t)=>{for(var r in t)a.o(t,r)&&!a.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},a.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),a.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),a.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.j=928,(()=>{var e={928:0};a.O.j=t=>0===e[t];var t=(t,r)=>{var s,n,[o,l,i]=r,c=0;for(s in l)a.o(l,s)&&(a.m[s]=l[s]);if(i)var h=i(a);for(t&&t(r);ca(1873)));o=a.O(o)})(); \ No newline at end of file diff --git a/webroot/js/app/api-setup.js b/webroot/js/app/api-setup.js index 1830c6612b..6de664316c 100644 --- a/webroot/js/app/api-setup.js +++ b/webroot/js/app/api-setup.js @@ -1,2 +1,2 @@ /*! For license information please see api-setup.js.LICENSE.txt */ -(()=>{"use strict";var e,t,n,r={9395:(e,t,n)=>{var r=n(7294),o=n(3935),s=n(2137),a=n(6610),i=n(5991),c=n(379),l=n(6070),u=n(7608),h=n(7757),p=n.n(h),f=n(2122);var v=r.createContext({user:null,users:null,roles:null,rememberMeOptions:{},resources:null,resource:null,shareResources:null,selectedResources:null,selectedUser:null,folders:null,resourceCommentId:null,mustRefreshComments:!1,siteSettings:null,userSettings:null,onCheckIsAuthenticatedRequested:null});function m(e){return function(t){(0,c.Z)(h,t);var n,o,s=(n=h,o=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,t=(0,u.Z)(n);if(o){var r=(0,u.Z)(this).constructor;e=Reflect.construct(t,arguments,r)}else e=t.apply(this,arguments);return(0,l.Z)(this,e)});function h(){return(0,a.Z)(this,h),s.apply(this,arguments)}return(0,i.Z)(h,[{key:"render",value:function(){var t=this;return r.createElement(v.Consumer,null,(function(n){return r.createElement(e,(0,f.Z)({context:n},t.props))}))}}]),h}(r.Component)}const d=v;var g=n(5697),w=n.n(g),y=n(4699),q=n(6156),E=n(5366);const b=function(e){(0,c.Z)(o,e);var t,n,r=(t=o,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,u.Z)(t);if(n){var o=(0,u.Z)(this).constructor;e=Reflect.construct(r,arguments,o)}else e=r.apply(this,arguments);return(0,l.Z)(this,e)});function o(e,t){var n;return(0,a.Z)(this,o),(n=r.call(this,e)).name="PassboltApiFetchError",n.data=t||{},n}return o}((0,E.Z)(Error));const x=function(e){(0,c.Z)(o,e);var t,n,r=(t=o,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,u.Z)(t);if(n){var o=(0,u.Z)(this).constructor;e=Reflect.construct(r,arguments,o)}else e=r.apply(this,arguments);return(0,l.Z)(this,e)});function o(){var e;return(0,a.Z)(this,o),(e=r.call(this,"An internal error occurred. The server response could not be parsed. Please contact your administrator.")).name="PassboltBadResponseError",e}return o}((0,E.Z)(Error));const k=function(e){(0,c.Z)(o,e);var t,n,r=(t=o,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,u.Z)(t);if(n){var o=(0,u.Z)(this).constructor;e=Reflect.construct(r,arguments,o)}else e=r.apply(this,arguments);return(0,l.Z)(this,e)});function o(e){var t;return(0,a.Z)(this,o),e=e||"The service is unavailable",(t=r.call(this,e)).name="PassboltServiceUnavailableError",t}return o}((0,E.Z)(Error));function R(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function Z(e){for(var t=1;t-1?z:e.indexOf("samsungbrowser")>-1?"samsung":e.indexOf("opera")>-1||e.indexOf("opr")>-1?"opera":e.indexOf("trident")>-1?"internet-explorer":e.indexOf("edge")>-1?"edge":e.indexOf("chrome")>-1?T:e.indexOf("safari")>-1?"safari":"unknown"}const O=function(e,t){return t.split(".").reduce((function(e,t){return void 0===e?e:e[t]}),e)};function B(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,r=(0,u.Z)(e);if(t){var o=(0,u.Z)(this).constructor;n=Reflect.construct(r,arguments,o)}else n=r.apply(this,arguments);return(0,l.Z)(this,n)}}var L=r.createContext({userId:null,token:null,state:null,onInitializeSetupRequested:function(){}}),A=function(e){(0,c.Z)(l,e);var t,n,o=B(l);function l(e){var t;return(0,a.Z)(this,l),(t=o.call(this,e)).state=Object.assign(t.defaultState,e.value),t}return(0,i.Z)(l,[{key:"defaultState",get:function(){return{userId:null,token:null,state:N.INITIAL_STATE,onInitializeSetupRequested:this.onInitializeSetupRequested.bind(this)}}},{key:"onInitializeSetupRequested",value:(n=(0,s.Z)(p().mark((function e(){return p().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(this.state.userId&&this.state.token){e.next=2;break}return e.abrupt("return",this.setState({state:N.ERROR_STATE}));case 2:if(this.isBrowserSupported()){e.next=4;break}return e.abrupt("return",this.setState({state:N.DOWNLOAD_SUPPORTED_BROWSER_STATE}));case 4:return e.next=6,this.verifySetupInfo().then(this.handleSetupVerifySuccess.bind(this)).catch(this.handleSetupVerifyError.bind(this));case 6:case"end":return e.stop()}}),e,this)}))),function(){return n.apply(this,arguments)})},{key:"handleSetupVerifySuccess",value:function(){this.setState({state:N.INSTALL_EXTENSION_STATE})}},{key:"handleSetupVerifyError",value:function(e){return e instanceof b&&O(e,"data.body.token.expired")?this.setState({state:N.TOKEN_EXPIRED_STATE}):this.setState({state:N.ERROR_STATE})}},{key:"isBrowserSupported",value:function(){var e=C();return[T,z].includes(e)}},{key:"verifySetupInfo",value:(t=(0,s.Z)(p().mark((function e(){var t,n,r,o;return p().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return(t=this.props.context.getApiClientOptions()).setResourceName("setup"),n=new S(t),e.next=5,n.get("install/".concat(this.state.userId,"/").concat(this.state.token));case 5:return r=e.sent,o=r.body,e.abrupt("return",o);case 8:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})},{key:"render",value:function(){return r.createElement(L.Provider,{value:this.state},this.props.children)}}]),l}(r.Component);A.propTypes={context:w().any,value:w().any,children:w().any};const U=m(A);function P(e){return function(t){(0,c.Z)(o,t);var n=B(o);function o(){return(0,a.Z)(this,o),n.apply(this,arguments)}return(0,i.Z)(o,[{key:"render",value:function(){var t=this;return r.createElement(L.Consumer,null,(function(n){return r.createElement(e,(0,f.Z)({apiSetupContext:n},t.props))}))}}]),o}(r.Component)}var N={INITIAL_STATE:"Initial state",DOWNLOAD_SUPPORTED_BROWSER_STATE:"Download supported browser state",INSTALL_EXTENSION_STATE:"Install extension state",TOKEN_EXPIRED_STATE:"Token expired state",ERROR_STATE:"Error state"},M=function(){function e(t){(0,a.Z)(this,e),this.setToken(t)}return(0,i.Z)(e,[{key:"setToken",value:function(e){this.validate(e),this.token=e}},{key:"validate",value:function(e){if(!e)throw new TypeError("CSRF token cannot be empty.");if("string"!=typeof e)throw new TypeError("CSRF token should be a string.")}},{key:"toFetchHeaders",value:function(){return{"X-CSRF-Token":this.token}}}]),e}(),_=function(){function e(){(0,a.Z)(this,e)}return(0,i.Z)(e,[{key:"setBaseUrl",value:function(e){if(!e)throw new TypeError("ApiClientOption baseUrl is required.");if("string"==typeof e)try{this.baseUrl=new URL(e)}catch(e){throw new TypeError("ApiClientOption baseUrl is invalid.")}else{if(!(e instanceof URL))throw new TypeError("ApiClientOptions baseurl should be a string or URL");this.baseUrl=e}return this}},{key:"setCsrfToken",value:function(e){if(!e)throw new TypeError("ApiClientOption csrfToken is required.");if("string"==typeof e)this.csrfToken=new M(e);else{if(!(e instanceof M))throw new TypeError("ApiClientOption csrfToken should be a string or a valid CsrfToken.");this.csrfToken=e}return this}},{key:"setResourceName",value:function(e){if(!e)throw new TypeError("ApiClientOptions.setResourceName resourceName is required.");if("string"!=typeof e)throw new TypeError("ApiClientOptions.setResourceName resourceName should be a valid string.");return this.resourceName=e,this}},{key:"getBaseUrl",value:function(){return this.baseUrl}},{key:"getResourceName",value:function(){return this.resourceName}},{key:"getHeaders",value:function(){if(this.csrfToken)return this.csrfToken.toFetchHeaders()}}]),e}();const D=function(e){(0,c.Z)(s,e);var t,n,o=(t=s,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,u.Z)(t);if(n){var o=(0,u.Z)(this).constructor;e=Reflect.construct(r,arguments,o)}else e=r.apply(this,arguments);return(0,l.Z)(this,e)});function s(){return(0,a.Z)(this,s),o.apply(this,arguments)}return(0,i.Z)(s,[{key:"render",value:function(){return r.createElement("div",{className:"login-processing"},r.createElement("div",{className:"processing-wrapper"},r.createElement("div",{className:"processing"})),r.createElement("h1",null,"Please wait..."))}}]),s}(r.Component);var I=n(9116),V=n(5145);var j="https://chrome.google.com/webstore/detail/passbolt-extension/didegimhafipceonhjepacocaffmoppf",H=function(e){(0,c.Z)(s,e);var t,n,o=(t=s,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,u.Z)(t);if(n){var o=(0,u.Z)(this).constructor;e=Reflect.construct(r,arguments,o)}else e=r.apply(this,arguments);return(0,l.Z)(this,e)});function s(e){var t;return(0,a.Z)(this,s),(t=o.call(this,e)).state=t.getDefaultState(),t.bindCallbacks(),t}return(0,i.Z)(s,[{key:"getDefaultState",value:function(){return{browserName:C()}}},{key:"bindCallbacks",value:function(){this.handleRefreshClick=this.handleRefreshClick.bind(this)}},{key:"browserStoreThumbnailUrl",get:function(){switch(this.state.browserName){case T:return"".concat(this.props.context.trustedDomain,"/img/third_party/ChromeWebStore_black.png");case z:return"".concat(this.props.context.trustedDomain,"/img/third_party/FirefoxAMO_black.svg");default:return"".concat(this.props.context.trustedDomain,"/img/third_party/ChromeWebStore_black.png")}}},{key:"storeUrl",get:function(){switch(this.state.browserName){case T:return j;case z:return"https://addons.mozilla.org/fr/firefox/addon/passbolt";default:return j}}},{key:"storeClassName",get:function(){return"browser-webstore ".concat(this.state.browserName)}},{key:"handleRefreshClick",value:function(){window.location.reload()}},{key:"translate",get:function(){return this.props.t}},{key:"render",value:function(){return r.createElement("div",{className:"install-extension"},r.createElement("h1",null,r.createElement(I.c,null,"Please install the browser extension.")),r.createElement("p",null,r.createElement(I.c,null,"Please download the browser extension and refresh this page to continue.")),this.state.browserName&&r.createElement("a",{href:this.storeUrl,className:this.storeClassName,target:"_blank",rel:"noopener noreferrer"},r.createElement("img",{src:this.browserStoreThumbnailUrl})),r.createElement("div",{className:"form-actions"},r.createElement("a",{href:this.storeUrl,className:"button primary big full-width",role:"button",target:"_blank",rel:"noopener noreferrer"},r.createElement(I.c,null,"Download extension")),r.createElement("a",{onClick:this.handleRefreshClick,role:"button"},r.createElement(I.c,null,"Refresh to detect extension"))))}}]),s}(r.Component);H.propTypes={context:w().any,t:w().func};const F=m((0,V.Z)("common")(H));var W="https://www.mozilla.org/firefox/download/thanks/",X=function(e){(0,c.Z)(s,e);var t,n,o=(t=s,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,u.Z)(t);if(n){var o=(0,u.Z)(this).constructor;e=Reflect.construct(r,arguments,o)}else e=r.apply(this,arguments);return(0,l.Z)(this,e)});function s(){return(0,a.Z)(this,s),o.apply(this,arguments)}return(0,i.Z)(s,[{key:"translate",get:function(){return this.props.t}},{key:"render",value:function(){return r.createElement("div",{className:"browser-not-supported"},r.createElement("h1",null,r.createElement(I.c,null,"Sorry, your browser is not supported.")),r.createElement("p",null,r.createElement(I.c,null,"Please download chrome or firefox to get started with passbolt.")),r.createElement("a",{href:"".concat(W),className:"browser",target:"_blank",rel:"noopener noreferrer"},r.createElement("img",{src:"".concat(this.props.context.trustedDomain,"/img/third_party/firefox_logo.png")})),r.createElement("div",{className:"form-actions"},r.createElement("a",{href:W,className:"button primary big full-width",role:"button",target:"_blank",rel:"noopener noreferrer"},r.createElement(I.c,null,"Download Firefox")),r.createElement("a",{href:"https://www.google.com/chrome/",role:"button",target:"_blank",rel:"noopener noreferrer"},r.createElement(I.c,null,"Download Chrome"))))}}]),s}(r.Component);X.propTypes={context:w().any,t:w().func};const K=m((0,V.Z)("common")(X));var G=function(e){(0,c.Z)(s,e);var t,n,o=(t=s,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,u.Z)(t);if(n){var o=(0,u.Z)(this).constructor;e=Reflect.construct(r,arguments,o)}else e=r.apply(this,arguments);return(0,l.Z)(this,e)});function s(){return(0,a.Z)(this,s),o.apply(this,arguments)}return(0,i.Z)(s,[{key:"translate",get:function(){return this.props.t}},{key:"render",value:function(){return r.createElement("div",{className:"setup-error"},r.createElement("h1",null,r.createElement(I.c,null,"Access to this service requires an invitation.")),r.createElement("p",null,r.createElement(I.c,null,"This email is not associated with any approved users on this domain.")," ",r.createElement(I.c,null,"Please contact your administrator to request an invitation link.")),r.createElement("div",{className:"form-actions"},r.createElement("a",{href:"".concat(this.props.context.trustedDomain,"/users/recover"),className:"button primary big full-width",role:"button"},r.createElement(I.c,null,"Try with another email"))))}}]),s}(r.Component);G.propTypes={context:w().any,t:w().func};const J=m((0,V.Z)("common")(G));var $=function(e){(0,c.Z)(s,e);var t,n,o=(t=s,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,u.Z)(t);if(n){var o=(0,u.Z)(this).constructor;e=Reflect.construct(r,arguments,o)}else e=r.apply(this,arguments);return(0,l.Z)(this,e)});function s(){return(0,a.Z)(this,s),o.apply(this,arguments)}return(0,i.Z)(s,[{key:"translate",get:function(){return this.props.t}},{key:"render",value:function(){return r.createElement("div",{className:"setup-error"},r.createElement("h1",null,r.createElement(I.c,null,"The invitation is expired.")),r.createElement("p",null,r.createElement(I.c,null,"You can request another invitation email by clicking on the button below.")),r.createElement("div",{className:"form-actions"},r.createElement("a",{href:"".concat(this.props.context.trustedDomain,"/users/recover"),className:"button primary big full-width",role:"button"},r.createElement(I.c,null,"Request invitation"))))}}]),s}(r.Component);$.propTypes={context:w().any,t:w().func};const Y=m((0,V.Z)("common")($));var Q=function(e){(0,c.Z)(s,e);var t,n,o=(t=s,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,u.Z)(t);if(n){var o=(0,u.Z)(this).constructor;e=Reflect.construct(r,arguments,o)}else e=r.apply(this,arguments);return(0,l.Z)(this,e)});function s(){return(0,a.Z)(this,s),o.apply(this,arguments)}return(0,i.Z)(s,[{key:"componentDidMount",value:function(){this.initializeSetup()}},{key:"initializeSetup",value:function(){var e=this;setTimeout((function(){return e.props.apiSetupContext.onInitializeSetupRequested()}),1e3)}},{key:"render",value:function(){switch(this.props.apiSetupContext.state){case N.INSTALL_EXTENSION_STATE:return r.createElement(F,null);case N.DOWNLOAD_SUPPORTED_BROWSER_STATE:return r.createElement(K,null);case N.TOKEN_EXPIRED_STATE:return r.createElement(Y,null);case N.ERROR_STATE:return r.createElement(J,null);default:return r.createElement(D,null)}}}]),s}(r.Component);Q.propTypes={apiSetupContext:w().object};const ee=P(Q);var te=function(e){(0,c.Z)(s,e);var t,n,o=(t=s,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,u.Z)(t);if(n){var o=(0,u.Z)(this).constructor;e=Reflect.construct(r,arguments,o)}else e=r.apply(this,arguments);return(0,l.Z)(this,e)});function s(){return(0,a.Z)(this,s),o.apply(this,arguments)}return(0,i.Z)(s,[{key:"getClassName",value:function(){var e="svg-icon ".concat(this.props.name);return this.props.big&&(e+=" icon-only"),this.props.baseline&&(e+=" baseline"),e}},{key:"render",value:function(){return r.createElement("span",{className:this.getClassName(),onClick:this.props.onClick},"ban"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1440 893q0-161-87-295l-754 753q137 89 297 89 111 0 211.5-43.5t173.5-116.5 116-174.5 43-212.5zm-999 299l755-754q-135-91-300-91-148 0-273 73t-198 199-73 274q0 162 89 299zm1223-299q0 157-61 300t-163.5 246-245 164-298.5 61-298.5-61-245-164-163.5-246-61-300 61-299.5 163.5-245.5 245-164 298.5-61 298.5 61 245 164 163.5 245.5 61 299.5z"})),"camera"===this.props.name&&r.createElement("svg",{width:"2048",height:"1792",viewBox:"0 0 2048 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1024 672q119 0 203.5 84.5t84.5 203.5-84.5 203.5-203.5 84.5-203.5-84.5-84.5-203.5 84.5-203.5 203.5-84.5zm704-416q106 0 181 75t75 181v896q0 106-75 181t-181 75h-1408q-106 0-181-75t-75-181v-896q0-106 75-181t181-75h224l51-136q19-49 69.5-84.5t103.5-35.5h512q53 0 103.5 35.5t69.5 84.5l51 136h224zm-704 1152q185 0 316.5-131.5t131.5-316.5-131.5-316.5-316.5-131.5-316.5 131.5-131.5 316.5 131.5 316.5 316.5 131.5z"})),"caret-right"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1152 896q0 26-19 45l-448 448q-19 19-45 19t-45-19-19-45v-896q0-26 19-45t45-19 45 19l448 448q19 19 19 45z"})),"caret-down"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1408 704q0 26-19 45l-448 448q-19 19-45 19t-45-19l-448-448q-19-19-19-45t19-45 45-19h896q26 0 45 19t19 45z"})),"caret-up"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1408 1216q0 26-19 45t-45 19h-896q-26 0-45-19t-19-45 19-45l448-448q19-19 45-19t45 19l448 448q19 19 19 45z"})),"chevron-left"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1427 301l-531 531 531 531q19 19 19 45t-19 45l-166 166q-19 19-45 19t-45-19l-742-742q-19-19-19-45t19-45l742-742q19-19 45-19t45 19l166 166q19 19 19 45t-19 45z"})),"chevron-right"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1363 877l-742 742q-19 19-45 19t-45-19l-166-166q-19-19-19-45t19-45l531-531-531-531q-19-19-19-45t19-45l166-166q19-19 45-19t45 19l742 742q19 19 19 45t-19 45z"})),"close"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1490 1322q0 40-28 68l-136 136q-28 28-68 28t-68-28l-294-294-294 294q-28 28-68 28t-68-28l-136-136q-28-28-28-68t28-68l294-294-294-294q-28-28-28-68t28-68l136-136q28-28 68-28t68 28l294 294 294-294q28-28 68-28t68 28l136 136q28 28 28 68t-28 68l-294 294 294 294q28 28 28 68z"})),"close-circle"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1277 1122q0-26-19-45l-181-181 181-181q19-19 19-45 0-27-19-46l-90-90q-19-19-46-19-26 0-45 19l-181 181-181-181q-19-19-45-19-27 0-46 19l-90 90q-19 19-19 46 0 26 19 45l181 181-181 181q-19 19-19 45 0 27 19 46l90 90q19 19 46 19 26 0 45-19l181-181 181 181q19 19 45 19 27 0 46-19l90-90q19-19 19-46zm387-226q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"})),"copy-to-clipboard"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M768 1664h896v-640h-416q-40 0-68-28t-28-68v-416h-384v1152zm256-1440v-64q0-13-9.5-22.5t-22.5-9.5h-704q-13 0-22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h704q13 0 22.5-9.5t9.5-22.5zm256 672h299l-299-299v299zm512 128v672q0 40-28 68t-68 28h-960q-40 0-68-28t-28-68v-160h-544q-40 0-68-28t-28-68v-1344q0-40 28-68t68-28h1088q40 0 68 28t28 68v328q21 13 36 28l408 408q28 28 48 76t20 88z"})),"download"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1344 1344q0-26-19-45t-45-19-45 19-19 45 19 45 45 19 45-19 19-45zm256 0q0-26-19-45t-45-19-45 19-19 45 19 45 45 19 45-19 19-45zm128-224v320q0 40-28 68t-68 28h-1472q-40 0-68-28t-28-68v-320q0-40 28-68t68-28h465l135 136q58 56 136 56t136-56l136-136h464q40 0 68 28t28 68zm-325-569q17 41-14 70l-448 448q-18 19-45 19t-45-19l-448-448q-31-29-14-70 17-39 59-39h256v-448q0-26 19-45t45-19h256q26 0 45 19t19 45v448h256q42 0 59 39z"})),"edit"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M888 1184l116-116-152-152-116 116v56h96v96h56zm440-720q-16-16-33 1l-350 350q-17 17-1 33t33-1l350-350q17-17 1-33zm80 594v190q0 119-84.5 203.5t-203.5 84.5h-832q-119 0-203.5-84.5t-84.5-203.5v-832q0-119 84.5-203.5t203.5-84.5h832q63 0 117 25 15 7 18 23 3 17-9 29l-49 49q-14 14-32 8-23-6-45-6h-832q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113v-126q0-13 9-22l64-64q15-15 35-7t20 29zm-96-738l288 288-672 672h-288v-288zm444 132l-92 92-288-288 92-92q28-28 68-28t68 28l152 152q28 28 28 68t-28 68z"})),"envelope"===this.props.name&&r.createElement("svg",{width:"512",height:"512",viewBox:"0 0 512 512",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{fill:"currentColor",d:"M464 64H48C21.49 64 0 85.49 0 112v288c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V112c0-26.51-21.49-48-48-48zm0 48v40.805c-22.422 18.259-58.168 46.651-134.587 106.49-16.841 13.247-50.201 45.072-73.413 44.701-23.208.375-56.579-31.459-73.413-44.701C106.18 199.465 70.425 171.067 48 152.805V112h416zM48 400V214.398c22.914 18.251 55.409 43.862 104.938 82.646 21.857 17.205 60.134 55.186 103.062 54.955 42.717.231 80.509-37.199 103.053-54.947 49.528-38.783 82.032-64.401 104.947-82.653V400H48z"})," "),"eye-open"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1664 960q-152-236-381-353 61 104 61 225 0 185-131.5 316.5t-316.5 131.5-316.5-131.5-131.5-316.5q0-121 61-225-229 117-381 353 133 205 333.5 326.5t434.5 121.5 434.5-121.5 333.5-326.5zm-720-384q0-20-14-34t-34-14q-125 0-214.5 89.5t-89.5 214.5q0 20 14 34t34 14 34-14 14-34q0-86 61-147t147-61q20 0 34-14t14-34zm848 384q0 34-20 69-140 230-376.5 368.5t-499.5 138.5-499.5-139-376.5-368q-20-35-20-69t20-69q140-229 376.5-368t499.5-139 499.5 139 376.5 368q20 35 20 69z"})),"eye-close"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M555 1335l78-141q-87-63-136-159t-49-203q0-121 61-225-229 117-381 353 167 258 427 375zm389-759q0-20-14-34t-34-14q-125 0-214.5 89.5t-89.5 214.5q0 20 14 34t34 14 34-14 14-34q0-86 61-147t147-61q20 0 34-14t14-34zm363-191q0 7-1 9-106 189-316 567t-315 566l-49 89q-10 16-28 16-12 0-134-70-16-10-16-28 0-12 44-87-143-65-263.5-173t-208.5-245q-20-31-20-69t20-69q153-235 380-371t496-136q89 0 180 17l54-97q10-16 28-16 5 0 18 6t31 15.5 33 18.5 31.5 18.5 19.5 11.5q16 10 16 27zm37 447q0 139-79 253.5t-209 164.5l280-502q8 45 8 84zm448 128q0 35-20 69-39 64-109 145-150 172-347.5 267t-419.5 95l74-132q212-18 392.5-137t301.5-307q-115-179-282-294l63-112q95 64 182.5 153t144.5 184q20 34 20 69z"})),"external-link"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1408 928v-480q0-26-19-45t-45-19h-480q-42 0-59 39-17 41 14 70l144 144-534 534q-19 19-19 45t19 45l102 102q19 19 45 19t45-19l534-534 144 144q18 19 45 19 12 0 25-5 39-17 39-59zm256-512v960q0 119-84.5 203.5t-203.5 84.5h-960q-119 0-203.5-84.5t-84.5-203.5v-960q0-119 84.5-203.5t203.5-84.5h960q119 0 203.5 84.5t84.5 203.5z"})),"folder"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1728 608v704q0 92-66 158t-158 66h-1216q-92 0-158-66t-66-158v-960q0-92 66-158t158-66h320q92 0 158 66t66 158v32h672q92 0 158 66t66 158z"})),"folder-shared"===this.props.name&&r.createElement("svg",{viewBox:"2 2 20 20",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M20 6h-8l-2-2H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm-5 3c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm4 8h-8v-1c0-1.33 2.67-2 4-2s4 .67 4 2v1z"})),"heart"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M896 1664q-26 0-44-18l-624-602q-10-8-27.5-26t-55.5-65.5-68-97.5-53.5-121-23.5-138q0-220 127-344t351-124q62 0 126.5 21.5t120 58 95.5 68.5 76 68q36-36 76-68t95.5-68.5 120-58 126.5-21.5q224 0 351 124t127 344q0 221-229 450l-623 600q-18 18-44 18z"})),"heart-o"===this.props.name&&r.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 512 512"},r.createElement("path",{d:"M458.4 64.3C400.6 15.7 311.3 23 256 79.3 200.7 23 111.4 15.6 53.6 64.3-21.6 127.6-10.6 230.8 43 285.5l175.4 178.7c10 10.2 23.4 15.9 37.6 15.9 14.3 0 27.6-5.6 37.6-15.8L469 285.6c53.5-54.7 64.7-157.9-10.6-221.3zm-23.6 187.5L259.4 430.5c-2.4 2.4-4.4 2.4-6.8 0L77.2 251.8c-36.5-37.2-43.9-107.6 7.3-150.7 38.9-32.7 98.9-27.8 136.5 10.5l35 35.7 35-35.7c37.8-38.5 97.8-43.2 136.5-10.6 51.1 43.1 43.5 113.9 7.3 150.8z"})),"heartbeat"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1280 1024h305q-5 6-10 10.5t-9 7.5l-3 4-623 600q-18 18-44 18t-44-18l-624-602q-5-2-21-20h369q22 0 39.5-13.5t22.5-34.5l70-281 190 667q6 20 23 33t39 13q21 0 38-13t23-33l146-485 56 112q18 35 57 35zm512-428q0 145-103 300h-369l-111-221q-8-17-25.5-27t-36.5-8q-45 5-56 46l-129 430-196-686q-6-20-23.5-33t-39.5-13-39 13.5-22 34.5l-116 464h-423q-103-155-103-300 0-220 127-344t351-124q62 0 126.5 21.5t120 58 95.5 68.5 76 68q36-36 76-68t95.5-68.5 120-58 126.5-21.5q224 0 351 124t127 344z"})),"info-circle"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1152 1376v-160q0-14-9-23t-23-9h-96v-512q0-14-9-23t-23-9h-320q-14 0-23 9t-9 23v160q0 14 9 23t23 9h96v320h-96q-14 0-23 9t-9 23v160q0 14 9 23t23 9h448q14 0 23-9t9-23zm-128-896v-160q0-14-9-23t-23-9h-192q-14 0-23 9t-9 23v160q0 14 9 23t23 9h192q14 0 23-9t9-23zm640 416q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"})),"key"===this.props.name&&r.createElement("svg",{width:"512",height:"512",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 512 512"},r.createElement("path",{fill:"currentColor",d:"M512 176.001C512 273.203 433.202 352 336 352c-11.22 0-22.19-1.062-32.827-3.069l-24.012 27.014A23.999 23.999 0 0 1 261.223 384H224v40c0 13.255-10.745 24-24 24h-40v40c0 13.255-10.745 24-24 24H24c-13.255 0-24-10.745-24-24v-78.059c0-6.365 2.529-12.47 7.029-16.971l161.802-161.802C163.108 213.814 160 195.271 160 176 160 78.798 238.797.001 335.999 0 433.488-.001 512 78.511 512 176.001zM336 128c0 26.51 21.49 48 48 48s48-21.49 48-48-21.49-48-48-48-48 21.49-48 48z"})),"link"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1520 1216q0-40-28-68l-208-208q-28-28-68-28-42 0-72 32 3 3 19 18.5t21.5 21.5 15 19 13 25.5 3.5 27.5q0 40-28 68t-68 28q-15 0-27.5-3.5t-25.5-13-19-15-21.5-21.5-18.5-19q-33 31-33 73 0 40 28 68l206 207q27 27 68 27 40 0 68-26l147-146q28-28 28-67zm-703-705q0-40-28-68l-206-207q-28-28-68-28-39 0-68 27l-147 146q-28 28-28 67 0 40 28 68l208 208q27 27 68 27 42 0 72-31-3-3-19-18.5t-21.5-21.5-15-19-13-25.5-3.5-27.5q0-40 28-68t68-28q15 0 27.5 3.5t25.5 13 19 15 21.5 21.5 18.5 19q33-31 33-73zm895 705q0 120-85 203l-147 146q-83 83-203 83-121 0-204-85l-206-207q-83-83-83-203 0-123 88-209l-88-88q-86 88-208 88-120 0-204-84l-208-208q-84-84-84-204t85-203l147-146q83-83 203-83 121 0 204 85l206 207q83 83 83 203 0 123-88 209l88 88q86-88 208-88 120 0 204 84l208 208q84 84 84 204z"})),"lock"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M640 768h512v-192q0-106-75-181t-181-75-181 75-75 181v192zm832 96v576q0 40-28 68t-68 28h-960q-40 0-68-28t-28-68v-576q0-40 28-68t68-28h32v-192q0-184 132-316t316-132 316 132 132 316v192h32q40 0 68 28t28 68z"})),"lock-open"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1376 768q40 0 68 28t28 68v576q0 40-28 68t-68 28h-960q-40 0-68-28t-28-68v-576q0-40 28-68t68-28h32v-320q0-185 131.5-316.5t316.5-131.5 316.5 131.5 131.5 316.5q0 26-19 45t-45 19h-64q-26 0-45-19t-19-45q0-106-75-181t-181-75-181 75-75 181v320h736z"})),"magic-wand"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1254 581l293-293-107-107-293 293zm447-293q0 27-18 45l-1286 1286q-18 18-45 18t-45-18l-198-198q-18-18-18-45t18-45l1286-1286q18-18 45-18t45 18l198 198q18 18 18 45zm-1351-190l98 30-98 30-30 98-30-98-98-30 98-30 30-98zm350 162l196 60-196 60-60 196-60-196-196-60 196-60 60-196zm930 478l98 30-98 30-30 98-30-98-98-30 98-30 30-98zm-640-640l98 30-98 30-30 98-30-98-98-30 98-30 30-98z"})),"plus-circle"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1344 960v-128q0-26-19-45t-45-19h-256v-256q0-26-19-45t-45-19h-128q-26 0-45 19t-19 45v256h-256q-26 0-45 19t-19 45v128q0 26 19 45t45 19h256v256q0 26 19 45t45 19h128q26 0 45-19t19-45v-256h256q26 0 45-19t19-45zm320-64q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"})),"plus-square"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 448 512",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48zm-32 252c0 6.6-5.4 12-12 12h-92v92c0 6.6-5.4 12-12 12h-56c-6.6 0-12-5.4-12-12v-92H92c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h92v-92c0-6.6 5.4-12 12-12h56c6.6 0 12 5.4 12 12v92h92c6.6 0 12 5.4 12 12v56z"})),"filter"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 512 512",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M487.976 0H24.028C2.71 0-8.047 25.866 7.058 40.971L192 225.941V432c0 7.831 3.821 15.17 10.237 19.662l80 55.98C298.02 518.69 320 507.493 320 487.98V225.941l184.947-184.97C520.021 25.896 509.338 0 487.976 0z"})),"life-ring"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 512 512",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M256 504c136.967 0 248-111.033 248-248S392.967 8 256 8 8 119.033 8 256s111.033 248 248 248zm-103.398-76.72l53.411-53.411c31.806 13.506 68.128 13.522 99.974 0l53.411 53.411c-63.217 38.319-143.579 38.319-206.796 0zM336 256c0 44.112-35.888 80-80 80s-80-35.888-80-80 35.888-80 80-80 80 35.888 80 80zm91.28 103.398l-53.411-53.411c13.505-31.806 13.522-68.128 0-99.974l53.411-53.411c38.319 63.217 38.319 143.579 0 206.796zM359.397 84.72l-53.411 53.411c-31.806-13.505-68.128-13.522-99.973 0L152.602 84.72c63.217-38.319 143.579-38.319 206.795 0zM84.72 152.602l53.411 53.411c-13.506 31.806-13.522 68.128 0 99.974L84.72 359.398c-38.319-63.217-38.319-143.579 0-206.796z"})),"plug"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 384 512",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M320,32a32,32,0,0,0-64,0v96h64Zm48,128H16A16,16,0,0,0,0,176v32a16,16,0,0,0,16,16H32v32A160.07,160.07,0,0,0,160,412.8V512h64V412.8A160.07,160.07,0,0,0,352,256V224h16a16,16,0,0,0,16-16V176A16,16,0,0,0,368,160ZM128,32a32,32,0,0,0-64,0v96h64Z"})),"printer"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M448 1536h896v-256h-896v256zm0-640h896v-384h-160q-40 0-68-28t-28-68v-160h-640v640zm1152 64q0-26-19-45t-45-19-45 19-19 45 19 45 45 19 45-19 19-45zm128 0v416q0 13-9.5 22.5t-22.5 9.5h-224v160q0 40-28 68t-68 28h-960q-40 0-68-28t-28-68v-160h-224q-13 0-22.5-9.5t-9.5-22.5v-416q0-79 56.5-135.5t135.5-56.5h64v-544q0-40 28-68t68-28h672q40 0 88 20t76 48l152 152q28 28 48 76t20 88v256h64q79 0 135.5 56.5t56.5 135.5z"})),"refresh"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 512 512",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M370.72 133.28C339.458 104.008 298.888 87.962 255.848 88c-77.458.068-144.328 53.178-162.791 126.85-1.344 5.363-6.122 9.15-11.651 9.15H24.103c-7.498 0-13.194-6.807-11.807-14.176C33.933 94.924 134.813 8 256 8c66.448 0 126.791 26.136 171.315 68.685L463.03 40.97C478.149 25.851 504 36.559 504 57.941V192c0 13.255-10.745 24-24 24H345.941c-21.382 0-32.09-25.851-16.971-40.971l41.75-41.749zM32 296h134.059c21.382 0 32.09 25.851 16.971 40.971l-41.75 41.75c31.262 29.273 71.835 45.319 114.876 45.28 77.418-.07 144.315-53.144 162.787-126.849 1.344-5.363 6.122-9.15 11.651-9.15h57.304c7.498 0 13.194 6.807 11.807 14.176C478.067 417.076 377.187 504 256 504c-66.448 0-126.791-26.136-171.315-68.685L48.97 471.03C33.851 486.149 8 475.441 8 454.059V320c0-13.255 10.745-24 24-24z"})),"question-circle"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1024 1376v-192q0-14-9-23t-23-9h-192q-14 0-23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23-9t9-23zm256-672q0-88-55.5-163t-138.5-116-170-41q-243 0-371 213-15 24 8 42l132 100q7 6 19 6 16 0 25-12 53-68 86-92 34-24 86-24 48 0 85.5 26t37.5 59q0 38-20 61t-68 45q-63 28-115.5 86.5t-52.5 125.5v36q0 14 9 23t23 9h192q14 0 23-9t9-23q0-19 21.5-49.5t54.5-49.5q32-18 49-28.5t46-35 44.5-48 28-60.5 12.5-81zm384 192q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"})),"search"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1216 832q0-185-131.5-316.5t-316.5-131.5-316.5 131.5-131.5 316.5 131.5 316.5 316.5 131.5 316.5-131.5 131.5-316.5zm512 832q0 52-38 90t-90 38q-54 0-90-38l-343-342q-179 124-399 124-143 0-273.5-55.5t-225-150-150-225-55.5-273.5 55.5-273.5 150-225 225-150 273.5-55.5 273.5 55.5 225 150 150 225 55.5 273.5q0 220-124 399l343 343q37 37 37 90z"})),"share"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1764 11q33 24 27 64l-256 1536q-5 29-32 45-14 8-31 8-11 0-24-5l-453-185-242 295q-18 23-49 23-13 0-22-4-19-7-30.5-23.5t-11.5-36.5v-349l864-1059-1069 925-395-162q-37-14-40-55-2-40 32-59l1664-960q15-9 32-9 20 0 36 11z"})),"star"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1728 647q0 22-26 48l-363 354 86 500q1 7 1 20 0 21-10.5 35.5t-30.5 14.5q-19 0-40-12l-449-236-449 236q-22 12-40 12-21 0-31.5-14.5t-10.5-35.5q0-6 2-20l86-500-364-354q-25-27-25-48 0-37 56-46l502-73 225-455q19-41 49-41t49 41l225 455 502 73q56 9 56 46z"})),"save"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 448 512",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M433.941 129.941l-83.882-83.882A48 48 0 0 0 316.118 32H48C21.49 32 0 53.49 0 80v352c0 26.51 21.49 48 48 48h352c26.51 0 48-21.49 48-48V163.882a48 48 0 0 0-14.059-33.941zM272 80v80H144V80h128zm122 352H54a6 6 0 0 1-6-6V86a6 6 0 0 1 6-6h42v104c0 13.255 10.745 24 24 24h176c13.255 0 24-10.745 24-24V83.882l78.243 78.243a6 6 0 0 1 1.757 4.243V426a6 6 0 0 1-6 6zM224 232c-48.523 0-88 39.477-88 88s39.477 88 88 88 88-39.477 88-88-39.477-88-88-88zm0 128c-22.056 0-40-17.944-40-40s17.944-40 40-40 40 17.944 40 40-17.944 40-40 40z"})),"trash"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M704 1376v-704q0-14-9-23t-23-9h-64q-14 0-23 9t-9 23v704q0 14 9 23t23 9h64q14 0 23-9t9-23zm256 0v-704q0-14-9-23t-23-9h-64q-14 0-23 9t-9 23v704q0 14 9 23t23 9h64q14 0 23-9t9-23zm256 0v-704q0-14-9-23t-23-9h-64q-14 0-23 9t-9 23v704q0 14 9 23t23 9h64q14 0 23-9t9-23zm-544-992h448l-48-117q-7-9-17-11h-317q-10 2-17 11zm928 32v64q0 14-9 23t-23 9h-96v948q0 83-47 143.5t-113 60.5h-832q-66 0-113-58.5t-47-141.5v-952h-96q-14 0-23-9t-9-23v-64q0-14 9-23t23-9h309l70-167q15-37 54-63t79-26h320q40 0 79 26t54 63l70 167h309q14 0 23 9t9 23z"})),"upload"===this.props.name&&r.createElement("svg",{width:"2048",height:"1792",viewBox:"0 0 2048 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1344 864q0-14-9-23l-352-352q-9-9-23-9t-23 9l-351 351q-10 12-10 24 0 14 9 23t23 9h224v352q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5-9.5t9.5-22.5v-352h224q13 0 22.5-9.5t9.5-22.5zm640 288q0 159-112.5 271.5t-271.5 112.5h-1088q-185 0-316.5-131.5t-131.5-316.5q0-130 70-240t188-165q-2-30-2-43 0-212 150-362t362-150q156 0 285.5 87t188.5 231q71-62 166-62 106 0 181 75t75 181q0 76-41 138 130 31 213.5 135.5t83.5 238.5z"})),"upload-a"===this.props.name&&r.createElement("svg",{"aria-hidden":"true",focusable:"false","data-prefix":"fas","data-icon":"upload",className:"svg-inline--fa fa-upload fa-w-16",role:"img",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 512 512"},r.createElement("path",{fill:"currentColor",d:"M296 384h-80c-13.3 0-24-10.7-24-24V192h-87.7c-17.8 0-26.7-21.5-14.1-34.1L242.3 5.7c7.5-7.5 19.8-7.5 27.3 0l152.2 152.2c12.6 12.6 3.7 34.1-14.1 34.1H320v168c0 13.3-10.7 24-24 24zm216-8v112c0 13.3-10.7 24-24 24H24c-13.3 0-24-10.7-24-24V376c0-13.3 10.7-24 24-24h136v8c0 30.9 25.1 56 56 56h80c30.9 0 56-25.1 56-56v-8h136c13.3 0 24 10.7 24 24zm-124 88c0-11-9-20-20-20s-20 9-20 20 9 20 20 20 20-9 20-20zm64 0c0-11-9-20-20-20s-20 9-20 20 9 20 20 20 20-9 20-20z"})),"user"===this.props.name&&r.createElement("svg",{width:"448",height:"512",viewBox:"0 0 448 512",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{fill:"currentColor",d:"M224 256c70.7 0 128-57.3 128-128S294.7 0 224 0 96 57.3 96 128s57.3 128 128 128zm89.6 32h-16.7c-22.2 10.2-46.9 16-72.9 16s-50.6-5.8-72.9-16h-16.7C60.2 288 0 348.2 0 422.4V464c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48v-41.6c0-74.2-60.2-134.4-134.4-134.4z",className:""})),"users"===this.props.name&&r.createElement("svg",{width:"640",height:"512",viewBox:"0 0 640 512",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{fill:"currentColor",d:"M96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm448 0c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm32 32h-64c-17.6 0-33.5 7.1-45.1 18.6 40.3 22.1 68.9 62 75.1 109.4h66c17.7 0 32-14.3 32-32v-32c0-35.3-28.7-64-64-64zm-256 0c61.9 0 112-50.1 112-112S381.9 32 320 32 208 82.1 208 144s50.1 112 112 112zm76.8 32h-8.3c-20.8 10-43.9 16-68.5 16s-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h288c26.5 0 48-21.5 48-48v-28.8c0-63.6-51.6-115.2-115.2-115.2zm-223.7-13.4C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z"})),"warning"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1024 1375v-190q0-14-9.5-23.5t-22.5-9.5h-192q-13 0-22.5 9.5t-9.5 23.5v190q0 14 9.5 23.5t22.5 9.5h192q13 0 22.5-9.5t9.5-23.5zm-2-374l18-459q0-12-10-19-13-11-24-11h-220q-11 0-24 11-10 7-10 21l17 457q0 10 10 16.5t24 6.5h185q14 0 23.5-6.5t10.5-16.5zm-14-934l768 1408q35 63-2 126-17 29-46.5 46t-63.5 17h-1536q-34 0-63.5-17t-46.5-46q-37-63-2-126l768-1408q17-31 47-49t65-18 65 18 47 49z"})))}}]),s}(r.Component);te.defaultProps={big:!1,baseline:!1,onClick:function(){}},te.propTypes={name:w().string,big:w().bool,baseline:w().bool,onClick:w().func};const ne=te;var re=function(e){(0,c.Z)(s,e);var t,n,o=(t=s,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,u.Z)(t);if(n){var o=(0,u.Z)(this).constructor;e=Reflect.construct(r,arguments,o)}else e=r.apply(this,arguments);return(0,l.Z)(this,e)});function s(){return(0,a.Z)(this,s),o.apply(this,arguments)}return(0,i.Z)(s,[{key:"privacyUrl",get:function(){return this.props.context.siteSettings.privacyLink}},{key:"creditsUrl",get:function(){return"https://www.passbolt.com/credits"}},{key:"unsafeUrl",get:function(){return"https://help.passbolt.com/faq/hosting/why-unsafe"}},{key:"termsUrl",get:function(){return this.props.context.siteSettings.termsLink}},{key:"versions",get:function(){var e=[],t=this.props.context.siteSettings.version;return t&&e.push(t),this.props.context.extensionVersion&&e.push(this.props.context.extensionVersion),e.join(" / ")}},{key:"isUnsafeMode",get:function(){var e=this.props.context.siteSettings.debug,t=this.props.context.siteSettings.url.startsWith("http://");return e||t}},{key:"translate",get:function(){return this.props.t}},{key:"render",value:function(){return r.createElement("footer",null,r.createElement("div",{className:"footer"},r.createElement("ul",{className:"footer-links"},this.isUnsafeMode&&r.createElement("li",{className:"error-message"},r.createElement("a",{title:"terms of service",href:this.unsafeUrl,target:"_blank",rel:"noopener noreferrer"},r.createElement(I.c,null,"Unsafe mode"))),this.termsUrl&&r.createElement("li",null,r.createElement("a",{href:this.termsUrl,target:"_blank",rel:"noopener noreferrer"},r.createElement(I.c,null,"Terms"))),this.privacyUrl&&r.createElement("li",null,r.createElement("a",{href:this.privacyUrl,target:"_blank",rel:"noopener noreferrer"},r.createElement(I.c,null,"Privacy"))),r.createElement("li",null,r.createElement("a",{href:this.creditsUrl,target:"_blank",rel:"noopener noreferrer"},r.createElement(I.c,null,"Credits"))),r.createElement("li",null,r.createElement("a",(0,f.Z)({href:this.creditsUrl,className:"tooltip-left"},this.versions&&{"data-tooltip":this.versions},{target:"_blank",rel:"noopener noreferrer"}),r.createElement(ne,{name:"heart-o"}))))))}}]),s}(r.Component);re.propTypes={context:w().any,t:w().func};const oe=m((0,V.Z)("common")(re));var se=n(484);const ae=function(e,t){if(void 0===e||"string"!=typeof e||!e.length)return!1;if((t=t||{}).whitelistedProtocols&&!Array.isArray(t.whitelistedProtocols))throw new TypeError("The whitelistedProtocols should be an array of string.");if(t.defaultProtocol&&"string"!=typeof t.defaultProtocol)throw new TypeError("The defaultProtocol should be a string.");var n=t.whitelistedProtocols||[ie.HTTP,ie.HTTPS],r=[ie.JAVASCRIPT],o=t.defaultProtocol||"";!/^((?!:\/\/).)*:\/\//.test(e)&&o&&(e="".concat(o,"//").concat(e));try{var s=new URL(e);return!r.includes(s.protocol)&&!!n.includes(s.protocol)&&s.href}catch(e){return!1}};var ie={FTP:"http:",FTPS:"https:",HTTP:"http:",HTTPS:"https:",JAVASCRIPT:"javascript:",SSH:"ssh:"},ce=function(){function e(t){(0,a.Z)(this,e),this.settings=t}var t;return(0,i.Z)(e,[{key:"canIUse",value:function(e){var t=!1,n="passbolt.plugins.".concat(e),r=O(this.settings,n)||null;if(r&&"object"===(0,se.Z)(r)){var o=O(r,"enabled");void 0!==o&&!0!==o||(t=!0)}return t}},{key:"getPluginSettings",value:function(e){var t="passbolt.plugins.".concat(e);return O(this.settings,t)}},{key:"getRememberMeOptions",value:function(){return(this.getPluginSettings("rememberMe")||{}).options||{}}},{key:"hasRememberMeUntilILogoutOption",get:function(){return void 0!==(this.getRememberMeOptions()||{})[-1]}},{key:"getServerTimezone",value:function(){return O(this.settings,"passbolt.app.server_timezone")}},{key:"termsLink",get:function(){var e=O(this.settings,"passbolt.legal.terms.url");return!!e&&ae(e)}},{key:"privacyLink",get:function(){var e=O(this.settings,"passbolt.legal.privacy_policy.url");return!!e&&ae(e)}},{key:"registrationPublic",get:function(){return!0===O(this.settings,"passbolt.registration.public")}},{key:"debug",get:function(){return!0===O(this.settings,"app.debug")}},{key:"url",get:function(){return O(this.settings,"app.url")||""}},{key:"version",get:function(){return O(this.settings,"app.version.number")}},{key:"locale",get:function(){return O(this.settings,"app.locale")||e.DEFAULT_LOCALE.locale}},{key:"setLocale",value:(t=(0,s.Z)(p().mark((function e(t){return p().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.settings.app.locale=t;case 1:case"end":return e.stop()}}),e,this)}))),function(e){return t.apply(this,arguments)})},{key:"supportedLocales",get:function(){return O(this.settings,"passbolt.plugins.locale.options")||e.DEFAULT_SUPPORTED_LOCALES}}],[{key:"DEFAULT_SUPPORTED_LOCALES",get:function(){return[e.DEFAULT_LOCALE]}},{key:"DEFAULT_LOCALE",get:function(){return{locale:"en-UK",label:"English"}}}]),e}(),le=n(7412),ue=n(8718),he=n(3554);var pe=function(e){(0,c.Z)(m,e);var t,n,o,h,f,v=(h=m,f=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,t=(0,u.Z)(h);if(f){var n=(0,u.Z)(this).constructor;e=Reflect.construct(t,arguments,n)}else e=t.apply(this,arguments);return(0,l.Z)(this,e)});function m(e){var t;return(0,a.Z)(this,m),(t=v.call(this,e)).state=t.defaultState,t}return(0,i.Z)(m,[{key:"defaultState",get:function(){return{ready:!1}}},{key:"componentDidMount",value:(o=(0,s.Z)(p().mark((function e(){return p().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,le.Z.use(ue.Db).use(he.Z).init({lng:this.locale,load:"currentOnly",react:{useSuspense:!1},backend:{loadPath:this.props.loadingPath||"/locales/{{lng}}/{{ns}}.json"},supportedLngs:this.supportedLocales,fallbackLng:!1,ns:["common"],defaultNS:"common",keySeparator:!1,nsSeparator:!1,debug:!1});case 2:this.setState({ready:!0});case 3:case"end":return e.stop()}}),e,this)}))),function(){return o.apply(this,arguments)})},{key:"supportedLocales",get:function(){return this.props.context.siteSettings.supportedLocales?this.props.context.siteSettings.supportedLocales.map((function(e){return e.locale})):[this.locale]}},{key:"locale",get:function(){return this.props.context.locale}},{key:"componentDidUpdate",value:(n=(0,s.Z)(p().mark((function e(t){return p().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.handleLocaleChange(t.context.locale);case 2:case"end":return e.stop()}}),e,this)}))),function(e){return n.apply(this,arguments)})},{key:"handleLocaleChange",value:(t=(0,s.Z)(p().mark((function e(t){return p().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(this.locale===t){e.next=4;break}return e.next=4,le.Z.changeLanguage(this.locale);case 4:case"end":return e.stop()}}),e,this)}))),function(e){return t.apply(this,arguments)})},{key:"isReady",get:function(){return this.state.ready}},{key:"render",value:function(){return r.createElement(r.Fragment,null,this.isReady&&this.props.children)}}]),m}(r.Component);pe.propTypes={context:w().any,loadingPath:w().any,children:w().any};const fe=m(pe);var ve=function(e){(0,c.Z)(y,e);var t,n,o,h,f,v,m,d,g,w=(d=y,g=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,t=(0,u.Z)(d);if(g){var n=(0,u.Z)(this).constructor;e=Reflect.construct(t,arguments,n)}else e=t.apply(this,arguments);return(0,l.Z)(this,e)});function y(e){var t;return(0,a.Z)(this,y),(t=w.call(this,e)).state=t.defaultState,t.bindHandlers(),t}return(0,i.Z)(y,[{key:"componentDidMount",value:(m=(0,s.Z)(p().mark((function e(){return p().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.initLocale();case 2:case"end":return e.stop()}}),e,this)}))),function(){return m.apply(this,arguments)})},{key:"componentDidUpdate",value:(v=(0,s.Z)(p().mark((function e(t){return p().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.handleLocaleChange(t.context.locale);case 2:case"end":return e.stop()}}),e,this)}))),function(e){return v.apply(this,arguments)})},{key:"handleLocaleChange",value:(f=(0,s.Z)(p().mark((function e(t){return p().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(this.props.context.locale===t){e.next=4;break}return e.next=4,this.setState({locale:this.props.context.locale});case 4:case"end":return e.stop()}}),e,this)}))),function(e){return f.apply(this,arguments)})},{key:"defaultState",get:function(){return{loading:!0,locale:null,processing:!1}}},{key:"areActionsAllowed",get:function(){return!this.state.processing}},{key:"bindHandlers",value:function(){this.handleLocaleInputChange=this.handleLocaleInputChange.bind(this)}},{key:"handleLocaleInputChange",value:(h=(0,s.Z)(p().mark((function e(t){var n,r;return p().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=t.target,r=n.value,e.next=4,this.updateLocale(r);case 4:case"end":return e.stop()}}),e,this)}))),function(e){return h.apply(this,arguments)})},{key:"updateLocale",value:(o=(0,s.Z)(p().mark((function e(t){return p().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.toggleProcessing();case 2:return e.next=4,this.props.context.onUpdateLocaleRequested(t);case 4:return e.next=6,this.toggleProcessing();case 6:case"end":return e.stop()}}),e,this)}))),function(e){return o.apply(this,arguments)})},{key:"initLocale",value:(n=(0,s.Z)(p().mark((function e(){return p().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.setState({locale:this.props.context.locale,loading:!1});case 2:case"end":return e.stop()}}),e,this)}))),function(){return n.apply(this,arguments)})},{key:"toggleProcessing",value:(t=(0,s.Z)(p().mark((function e(){var t;return p().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.state.processing,e.abrupt("return",this.setState({processing:!t}));case 2:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})},{key:"isLoading",value:function(){return this.state.loading}},{key:"render",value:function(){return r.createElement(r.Fragment,null,!this.isLoading()&&r.createElement("div",{className:"input select locale"},r.createElement("select",{id:"user-locale-input",name:"locale",value:this.state.locale,disabled:!this.areActionsAllowed,onChange:this.handleLocaleInputChange},this.props.context.siteSettings.supportedLocales.map((function(e){return r.createElement("option",{key:e.locale,value:e.locale},e.label)})))))}}]),y}(r.Component);ve.propTypes={context:w().any};const me=m(ve);var de=function(e){(0,c.Z)(s,e);var t,n,o=(t=s,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,u.Z)(t);if(n){var o=(0,u.Z)(this).constructor;e=Reflect.construct(r,arguments,o)}else e=r.apply(this,arguments);return(0,l.Z)(this,e)});function s(){return(0,a.Z)(this,s),o.apply(this,arguments)}return(0,i.Z)(s,[{key:"statesToHideLocaleSwitch",get:function(){return[N.INITIAL_STATE]}},{key:"mustDisplayLocaleSwitch",get:function(){return!this.statesToHideLocaleSwitch.includes(this.props.apiSetupContext.state)}},{key:"render",value:function(){return r.createElement(r.Fragment,null,this.mustDisplayLocaleSwitch&&r.createElement(me,null))}}]),s}(r.Component);de.propTypes={apiSetupContext:w().any};const ge=P(de);const we=function(e){(0,c.Z)(m,e);var t,n,o,h,f,v=(h=m,f=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,t=(0,u.Z)(h);if(f){var n=(0,u.Z)(this).constructor;e=Reflect.construct(t,arguments,n)}else e=t.apply(this,arguments);return(0,l.Z)(this,e)});function m(e){var t;return(0,a.Z)(this,m),(t=v.call(this,e)).state=t.defaultState,t.userId=null,t.token=null,t.initializeProperties(),t}return(0,i.Z)(m,[{key:"defaultState",get:function(){return{siteSettings:null,trustedDomain:this.baseUrl,getApiClientOptions:this.getApiClientOptions.bind(this),locale:null,onUpdateLocaleRequested:this.onUpdateLocaleRequested.bind(this)}}},{key:"componentDidMount",value:(o=(0,s.Z)(p().mark((function e(){return p().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.getSiteSettings();case 2:this.initLocale();case 3:case"end":return e.stop()}}),e,this)}))),function(){return o.apply(this,arguments)})},{key:"initializeProperties",value:function(){var e="[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[0-5][a-fA-F0-9]{3}-[089aAbB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}",t="setup/install/(".concat(e,")/(").concat(e,")$"),n=new RegExp(t),r=window.location.pathname.match(n);r?(this.userId=r[1],this.token=r[2]):console.error("Unable to retrieve the user id and token from the url")}},{key:"baseUrl",get:function(){var e=document.getElementsByTagName("base")&&document.getElementsByTagName("base")[0];return e?e.attributes.href.value.replace(/\/*$/g,""):(console.error("Unable to retrieve the page base tag"),"")}},{key:"getApiClientOptions",value:function(){return(new _).setBaseUrl(this.state.trustedDomain)}},{key:"getSiteSettings",value:(n=(0,s.Z)(p().mark((function e(){var t,n,r,o,s;return p().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.getApiClientOptions().setResourceName("settings"),n=new S(t),e.next=4,n.findAll();case 4:return r=e.sent,o=r.body,s=new ce(o),e.next=9,this.setState({siteSettings:s});case 9:case"end":return e.stop()}}),e,this)}))),function(){return n.apply(this,arguments)})},{key:"initLocale",value:function(){var e=this.getUrlLocale()||this.getBrowserLocale()||this.getBrowserSimilarLocale()||this.state.siteSettings.locale;this.setState({locale:e}),this.setUrlLocale(e)}},{key:"getUrlLocale",value:function(){var e=new URL(window.location.href).searchParams.get("locale");if(e){var t=this.state.siteSettings.supportedLocales.find((function(t){return e===t.locale}));if(t)return t.locale}}},{key:"getBrowserLocale",value:function(){var e=this.state.siteSettings.supportedLocales.find((function(e){return navigator.language===e.locale}));if(e)return e.locale}},{key:"getBrowserSimilarLocale",value:function(){var e=navigator.language.split("-")[0],t=this.state.siteSettings.supportedLocales.find((function(t){return e===t.locale.split("-")[0]}));if(t)return t.locale}},{key:"onUpdateLocaleRequested",value:(t=(0,s.Z)(p().mark((function e(t){return p().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.setState({locale:t});case 2:this.setUrlLocale(t);case 3:case"end":return e.stop()}}),e,this)}))),function(e){return t.apply(this,arguments)})},{key:"setUrlLocale",value:function(e){var t=new URL(window.location.href);t.searchParams.set("locale",e),window.history.replaceState(null,null,t)}},{key:"isReady",value:function(){return null!==this.state.siteSettings&&null!==this.state.locale}},{key:"render",value:function(){return r.createElement(d.Provider,{value:this.state},this.isReady()&&r.createElement(fe,{loadingPath:"".concat(this.state.trustedDomain,"/locales/{{lng}}/{{ns}}.json")},r.createElement(U,{value:{userId:this.userId,token:this.token}},r.createElement("div",{id:"container",className:"container page login"},r.createElement("div",{className:"content"},r.createElement("div",{className:"header"},r.createElement("div",{className:"logo"},r.createElement("span",{className:"visually-hidden"},"Passbolt"))),r.createElement("div",{className:"login-form"},r.createElement(ee,null)),r.createElement(ge,null))),r.createElement(oe,null))))}}]),m}(r.Component);var ye=document.createElement("div");document.body.appendChild(ye),o.render(r.createElement(we,null),ye)}},o={};function s(e){var t=o[e];if(void 0!==t)return t.exports;var n=o[e]={exports:{}};return r[e](n,n.exports,s),n.exports}s.m=r,e=[],s.O=(t,n,r,o)=>{if(!n){var a=1/0;for(l=0;l=o)&&Object.keys(s.O).every((e=>s.O[e](n[c])))?n.splice(c--,1):(i=!1,o0&&e[l-1][2]>o;l--)e[l]=e[l-1];e[l]=[n,r,o]},s.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return s.d(t,{a:t}),t},n=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,s.t=function(e,r){if(1&r&&(e=this(e)),8&r)return e;if("object"==typeof e&&e){if(4&r&&e.__esModule)return e;if(16&r&&"function"==typeof e.then)return e}var o=Object.create(null);s.r(o);var a={};t=t||[null,n({}),n([]),n(n)];for(var i=2&r&&e;"object"==typeof i&&!~t.indexOf(i);i=n(i))Object.getOwnPropertyNames(i).forEach((t=>a[t]=()=>e[t]));return a.default=()=>e,s.d(o,a),o},s.d=(e,t)=>{for(var n in t)s.o(t,n)&&!s.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},s.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),s.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),s.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},s.j=240,(()=>{var e={240:0};s.O.j=t=>0===e[t];var t=(t,n)=>{var r,o,[a,i,c]=n,l=0;for(r in i)s.o(i,r)&&(s.m[r]=i[r]);if(c)var u=c(s);for(t&&t(n);ls(9395)));a=s.O(a)})(); \ No newline at end of file +(()=>{"use strict";var e,t,r,s={9395:(e,t,r)=>{var s=r(7294),n=r(3935);function a(){return a=Object.assign||function(e){for(var t=1;ts.createElement(e,a({context:t},this.props))))}}}const i=l;var c=r(5697),h=r.n(c);class p extends Error{constructor(e,t){super(e),this.name="PassboltApiFetchError",this.data=t||{}}}const m=p;class d extends Error{constructor(){super("An internal error occurred. The server response could not be parsed. Please contact your administrator."),this.name="PassboltBadResponseError"}}const u=d;class g extends Error{constructor(e){super(e=e||"The service is unavailable"),this.name="PassboltServiceUnavailableError"}}const w=g;class v{constructor(e){if(this.options=e,!this.options.getBaseUrl())throw new TypeError("ApiClient constructor error: baseUrl is required.");if(!this.options.getResourceName())throw new TypeError("ApiClient constructor error: resourceName is required.");try{let e=this.options.getBaseUrl().toString();e.endsWith("/")&&(e=e.slice(0,-1)),this.baseUrl=`${e}/${this.options.getResourceName()}`,this.baseUrl=new URL(this.baseUrl)}catch(e){throw new TypeError("ApiClient constructor error: b.")}this.apiVersion="api-version=v2"}getDefaultHeaders(){return{Accept:"application/json","content-type":"application/json"}}buildFetchOptions(){return{credentials:"include",headers:{...this.getDefaultHeaders(),...this.options.getHeaders()}}}async get(e,t){this.assertValidId(e);const r=this.buildUrl(`${this.baseUrl}/${e}`,t||{});return this.fetchAndHandleResponse("GET",r)}async delete(e,t,r,s){let n;this.assertValidId(e),void 0===s&&(s=!1),n=s?this.buildUrl(`${this.baseUrl}/${e}/dry-run`,r||{}):this.buildUrl(`${this.baseUrl}/${e}`,r||{});let a=null;return t&&(a=this.buildBody(t)),this.fetchAndHandleResponse("DELETE",n,a)}async findAll(e){const t=this.buildUrl(this.baseUrl.toString(),e||{});return await this.fetchAndHandleResponse("GET",t)}async create(e,t){const r=this.buildUrl(this.baseUrl.toString(),t||{}),s=this.buildBody(e);return this.fetchAndHandleResponse("POST",r,s)}async update(e,t,r,s){let n;this.assertValidId(e),void 0===s&&(s=!1),n=s?this.buildUrl(`${this.baseUrl}/${e}/dry-run`,r||{}):this.buildUrl(`${this.baseUrl}/${e}`,r||{});let a=null;return t&&(a=this.buildBody(t)),this.fetchAndHandleResponse("PUT",n,a)}assertValidId(e){if(!e)throw new TypeError("ApiClient.assertValidId error: id cannot be empty");if("string"!=typeof e)throw new TypeError("ApiClient.assertValidId error: id should be a string")}assertMethod(e){"string"!=typeof e&&new TypeError("ApiClient.assertValidMethod method should be a string."),["GET","POST","PUT","DELETE"].indexOf(e)<0&&new TypeError(`ApiClient.assertValidMethod error: method ${e} is not supported.`)}assertUrl(e){if(!e)throw new TypeError("ApliClient.assertUrl error: url is required.");if(!(e instanceof URL))throw new TypeError("ApliClient.assertUrl error: url should be a valid URL object.")}assertBody(e){"string"!=typeof e&&new TypeError("ApiClient.assertBody error: body should be a string.")}buildBody(e){return JSON.stringify(e)}buildUrl(e,t){if("string"!=typeof e)throw new TypeError("ApiClient.buildUrl error: url should be a string.");const r=new URL(`${e}.json?${this.apiVersion}`);t=t||{};for(const[e,s]of Object.entries(t)){if("string"!=typeof e)throw new TypeError("ApiClient.buildUrl error: urlOptions key should be a string.");if("string"==typeof s)r.searchParams.append(e,s);else{if(!Array.isArray(s))throw new TypeError("ApiClient.buildUrl error: urlOptions value should be a string or array.");s.forEach((t=>{r.searchParams.append(e,t)}))}}return r}async fetchAndHandleResponse(e,t,r,s){let n,a;this.assertUrl(t),this.assertMethod(e),r&&this.assertBody(r);const l={...this.buildFetchOptions(),...s};l.method=e,r&&(l.body=r);try{n=await fetch(t.toString(),l)}catch(e){throw new w(e.message)}try{a=await n.json()}catch(e){throw new u}if(!n.ok){const e=a.header.message;throw new m(e,{code:n.status,body:a.body})}return a}}const q="chrome",E="edge",f="firefox";function b(){const e=window.navigator.userAgent.toLowerCase();let t;return t=e.indexOf("firefox")>-1?f:e.indexOf("samsungbrowser")>-1?"samsung":e.indexOf("opera")>-1||e.indexOf("opr")>-1?"opera":e.indexOf("trident")>-1?"internet-explorer":e.indexOf("edg")>-1?E:e.indexOf("chrome")>-1?q:e.indexOf("safari")>-1?"safari":"unknown",t}const y=(e,t)=>t.split(".").reduce(((e,t)=>void 0===e?e:e[t]),e);function x(){return x=Object.assign||function(e){for(var t=1;t{}});class T extends s.Component{constructor(e){super(e),this.state=Object.assign(this.defaultState,e.value)}get defaultState(){return{userId:null,token:null,state:L.INITIAL_STATE,onInitializeSetupRequested:this.onInitializeSetupRequested.bind(this)}}async onInitializeSetupRequested(){return this.state.userId&&this.state.token?this.isBrowserSupported()?void await this.verifySetupInfo().then(this.handleSetupVerifySuccess.bind(this)).catch(this.handleSetupVerifyError.bind(this)):this.setState({state:L.DOWNLOAD_SUPPORTED_BROWSER_STATE}):this.setState({state:L.ERROR_STATE})}handleSetupVerifySuccess(){this.setState({state:L.INSTALL_EXTENSION_STATE})}handleSetupVerifyError(e){return e instanceof m&&y(e,"data.body.token.expired")?this.setState({state:L.TOKEN_EXPIRED_STATE}):this.setState({state:L.ERROR_STATE})}isBrowserSupported(){const e=b();return[q,f,E].includes(e)}async verifySetupInfo(){const e=this.props.context.getApiClientOptions();e.setResourceName("setup");const t=new v(e),{body:r}=await t.get(`install/${this.state.userId}/${this.state.token}`);return r}render(){return s.createElement(S.Provider,{value:this.state},this.props.children)}}T.propTypes={context:h().any,value:h().any,children:h().any};const z=o(T);function C(e){return class extends s.Component{render(){return s.createElement(S.Consumer,null,(t=>s.createElement(e,x({apiSetupContext:t},this.props))))}}}const L={INITIAL_STATE:"Initial state",DOWNLOAD_SUPPORTED_BROWSER_STATE:"Download supported browser state",INSTALL_EXTENSION_STATE:"Install extension state",TOKEN_EXPIRED_STATE:"Token expired state",ERROR_STATE:"Error state"};class A{constructor(e){this.setToken(e)}setToken(e){this.validate(e),this.token=e}validate(e){if(!e)throw new TypeError("CSRF token cannot be empty.");if("string"!=typeof e)throw new TypeError("CSRF token should be a string.")}toFetchHeaders(){return{"X-CSRF-Token":this.token}}}class U{setBaseUrl(e){if(!e)throw new TypeError("ApiClientOption baseUrl is required.");if("string"==typeof e)try{this.baseUrl=new URL(e)}catch(e){throw new TypeError("ApiClientOption baseUrl is invalid.")}else{if(!(e instanceof URL))throw new TypeError("ApiClientOptions baseurl should be a string or URL");this.baseUrl=e}return this}setCsrfToken(e){if(!e)throw new TypeError("ApiClientOption csrfToken is required.");if("string"==typeof e)this.csrfToken=new A(e);else{if(!(e instanceof A))throw new TypeError("ApiClientOption csrfToken should be a string or a valid CsrfToken.");this.csrfToken=e}return this}setResourceName(e){if(!e)throw new TypeError("ApiClientOptions.setResourceName resourceName is required.");if("string"!=typeof e)throw new TypeError("ApiClientOptions.setResourceName resourceName should be a valid string.");return this.resourceName=e,this}getBaseUrl(){return this.baseUrl}getResourceName(){return this.resourceName}getHeaders(){if(this.csrfToken)return this.csrfToken.toFetchHeaders()}}var O=r(9116);class R extends s.Component{render(){return s.createElement("div",{className:"login-processing"},s.createElement("div",{className:"processing-wrapper"},s.createElement("div",{className:"processing"})),s.createElement("h1",null,s.createElement(O.c,null,"Please wait...")))}}const k=R;var M=r(8831);const N="https://chrome.google.com/webstore/detail/passbolt-extension/didegimhafipceonhjepacocaffmoppf";class P extends s.Component{constructor(e){super(e),this.state=this.getDefaultState(),this.bindCallbacks()}getDefaultState(){const e=window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light";return{browserName:b(),theme:e}}bindCallbacks(){this.handleRefreshClick=this.handleRefreshClick.bind(this)}get browserStoreThumbnailUrl(){const e="dark"===this.state.theme?"white":"black";switch(this.state.browserName){case f:return`${this.props.context.trustedDomain}/img/third_party/FirefoxAMO_${e}.svg`;case E:return`${this.props.context.trustedDomain}/img/third_party/edge-addon-${e}.svg`;default:return`${this.props.context.trustedDomain}/img/third_party/ChromeWebStore_${e}.svg`}}get storeUrl(){switch(this.state.browserName){case q:return N;case f:return"https://addons.mozilla.org/firefox/addon/passbolt";case E:return"https://microsoftedge.microsoft.com/addons/detail/passbolt-extension/ljeppgjhohmhpbdhjjjbiflabdgfkhpo";default:return N}}get storeClassName(){return`browser-webstore ${this.state.browserName}`}handleRefreshClick(){window.location.reload()}get translate(){return this.props.t}render(){return s.createElement("div",{className:"install-extension"},s.createElement("h1",null,s.createElement(O.c,null,"Please install the browser extension.")),s.createElement("p",null,s.createElement(O.c,null,"Please download the browser extension and refresh this page to continue.")),this.state.browserName&&s.createElement("a",{href:this.storeUrl,className:this.storeClassName,target:"_blank",rel:"noopener noreferrer"},s.createElement("img",{src:this.browserStoreThumbnailUrl})),s.createElement("div",{className:"form-actions"},s.createElement("a",{href:this.storeUrl,className:"button primary big full-width",role:"button",target:"_blank",rel:"noopener noreferrer"},s.createElement(O.c,null,"Download extension")),s.createElement("a",{onClick:this.handleRefreshClick,role:"button"},s.createElement(O.c,null,"Refresh to detect extension"))))}}P.propTypes={context:h().any,t:h().func};const B=o((0,M.Z)("common")(P)),_="https://www.mozilla.org/firefox/download/thanks/";class I extends s.Component{get translate(){return this.props.t}render(){return s.createElement("div",{className:"browser-not-supported"},s.createElement("h1",null,s.createElement(O.c,null,"Sorry, your browser is not supported.")),s.createElement("p",null,s.createElement(O.c,null,"Please download chrome or firefox to get started with passbolt.")),s.createElement("a",{href:`${_}`,className:"browser",target:"_blank",rel:"noopener noreferrer"},s.createElement("img",{src:`${this.props.context.trustedDomain}/img/third_party/firefox_logo.png`})),s.createElement("div",{className:"form-actions"},s.createElement("a",{href:_,className:"button primary big full-width",role:"button",target:"_blank",rel:"noopener noreferrer"},s.createElement(O.c,null,"Download Firefox")),s.createElement("a",{href:"https://www.google.com/chrome/",role:"button",target:"_blank",rel:"noopener noreferrer"},s.createElement(O.c,null,"Download Chrome"))))}}I.propTypes={context:h().any,t:h().func};const D=o((0,M.Z)("common")(I));class V extends s.Component{get translate(){return this.props.t}render(){return s.createElement("div",{className:"setup-error"},s.createElement("h1",null,s.createElement(O.c,null,"Access to this service requires an invitation.")),s.createElement("p",null,s.createElement(O.c,null,"This email is not associated with any approved users on this domain.")," ",s.createElement(O.c,null,"Please contact your administrator to request an invitation link.")),s.createElement("div",{className:"form-actions"},s.createElement("a",{href:`${this.props.context.trustedDomain}/users/recover`,className:"button primary big full-width",role:"button"},s.createElement(O.c,null,"Try with another email"))))}}V.propTypes={context:h().any,t:h().func};const j=o((0,M.Z)("common")(V));class H extends s.Component{get translate(){return this.props.t}render(){return s.createElement("div",{className:"setup-error"},s.createElement("h1",null,s.createElement(O.c,null,"The invitation is expired.")),s.createElement("p",null,s.createElement(O.c,null,"You can request another invitation email by clicking on the button below.")),s.createElement("div",{className:"form-actions"},s.createElement("a",{href:`${this.props.context.trustedDomain}/users/recover`,className:"button primary big full-width",role:"button"},s.createElement(O.c,null,"Request invitation"))))}}H.propTypes={context:h().any,t:h().func};const $=o((0,M.Z)("common")(H));class F extends s.Component{componentDidMount(){this.initializeSetup()}initializeSetup(){setTimeout((()=>this.props.apiSetupContext.onInitializeSetupRequested()),1e3)}render(){switch(this.props.apiSetupContext.state){case L.INSTALL_EXTENSION_STATE:return s.createElement(B,null);case L.DOWNLOAD_SUPPORTED_BROWSER_STATE:return s.createElement(D,null);case L.TOKEN_EXPIRED_STATE:return s.createElement($,null);case L.ERROR_STATE:return s.createElement(j,null);default:return s.createElement(k,null)}}}F.propTypes={apiSetupContext:h().object};const Z=C(F);class W extends s.Component{getClassName(){let e=`svg-icon ${this.props.name}`;return this.props.big&&(e+=" icon-only"),this.props.baseline&&(e+=" baseline"),this.props.dim&&(e+=" dim"),e}render(){return s.createElement("span",{className:this.getClassName(),onClick:this.props.onClick},"add"===this.props.name&&s.createElement("svg",{xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false","data-prefix":"fas","data-icon":"plus-circle",role:"img",viewBox:"0 0 512 512"},s.createElement("path",{d:"M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm144 276c0 6.6-5.4 12-12 12h-92v92c0 6.6-5.4 12-12 12h-56c-6.6 0-12-5.4-12-12v-92h-92c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h92v-92c0-6.6 5.4-12 12-12h56c6.6 0 12 5.4 12 12v92h92c6.6 0 12 5.4 12 12v56z"})),"ban"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1440 893q0-161-87-295l-754 753q137 89 297 89 111 0 211.5-43.5t173.5-116.5 116-174.5 43-212.5zm-999 299l755-754q-135-91-300-91-148 0-273 73t-198 199-73 274q0 162 89 299zm1223-299q0 157-61 300t-163.5 246-245 164-298.5 61-298.5-61-245-164-163.5-246-61-300 61-299.5 163.5-245.5 245-164 298.5-61 298.5 61 245 164 163.5 245.5 61 299.5z"})),"camera"===this.props.name&&s.createElement("svg",{width:"2048",height:"1792",viewBox:"0 0 2048 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1024 672q119 0 203.5 84.5t84.5 203.5-84.5 203.5-203.5 84.5-203.5-84.5-84.5-203.5 84.5-203.5 203.5-84.5zm704-416q106 0 181 75t75 181v896q0 106-75 181t-181 75h-1408q-106 0-181-75t-75-181v-896q0-106 75-181t181-75h224l51-136q19-49 69.5-84.5t103.5-35.5h512q53 0 103.5 35.5t69.5 84.5l51 136h224zm-704 1152q185 0 316.5-131.5t131.5-316.5-131.5-316.5-316.5-131.5-316.5 131.5-131.5 316.5 131.5 316.5 316.5 131.5z"})),"caret-right"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1152 896q0 26-19 45l-448 448q-19 19-45 19t-45-19-19-45v-896q0-26 19-45t45-19 45 19l448 448q19 19 19 45z"})),"caret-down"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1408 704q0 26-19 45l-448 448q-19 19-45 19t-45-19l-448-448q-19-19-19-45t19-45 45-19h896q26 0 45 19t19 45z"})),"caret-up"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1408 1216q0 26-19 45t-45 19h-896q-26 0-45-19t-19-45 19-45l448-448q19-19 45-19t45 19l448 448q19 19 19 45z"})),"chevron-left"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1427 301l-531 531 531 531q19 19 19 45t-19 45l-166 166q-19 19-45 19t-45-19l-742-742q-19-19-19-45t19-45l742-742q19-19 45-19t45 19l166 166q19 19 19 45t-19 45z"})),"chevron-right"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1363 877l-742 742q-19 19-45 19t-45-19l-166-166q-19-19-19-45t19-45l531-531-531-531q-19-19-19-45t19-45l166-166q19-19 45-19t45 19l742 742q19 19 19 45t-19 45z"})),"close"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1490 1322q0 40-28 68l-136 136q-28 28-68 28t-68-28l-294-294-294 294q-28 28-68 28t-68-28l-136-136q-28-28-28-68t28-68l294-294-294-294q-28-28-28-68t28-68l136-136q28-28 68-28t68 28l294 294 294-294q28-28 68-28t68 28l136 136q28 28 28 68t-28 68l-294 294 294 294q28 28 28 68z"})),"close-circle"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1277 1122q0-26-19-45l-181-181 181-181q19-19 19-45 0-27-19-46l-90-90q-19-19-46-19-26 0-45 19l-181 181-181-181q-19-19-45-19-27 0-46 19l-90 90q-19 19-19 46 0 26 19 45l181 181-181 181q-19 19-19 45 0 27 19 46l90 90q19 19 46 19 26 0 45-19l181-181 181 181q19 19 45 19 27 0 46-19l90-90q19-19 19-46zm387-226q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"})),"cog"===this.props.name&&s.createElement("svg",{xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false","data-prefix":"fas","data-icon":"cog",viewBox:"0 0 512 512"},s.createElement("path",{fill:"currentColor",d:"M487.4 315.7l-42.6-24.6c4.3-23.2 4.3-47 0-70.2l42.6-24.6c4.9-2.8 7.1-8.6 5.5-14-11.1-35.6-30-67.8-54.7-94.6-3.8-4.1-10-5.1-14.8-2.3L380.8 110c-17.9-15.4-38.5-27.3-60.8-35.1V25.8c0-5.6-3.9-10.5-9.4-11.7-36.7-8.2-74.3-7.8-109.2 0-5.5 1.2-9.4 6.1-9.4 11.7V75c-22.2 7.9-42.8 19.8-60.8 35.1L88.7 85.5c-4.9-2.8-11-1.9-14.8 2.3-24.7 26.7-43.6 58.9-54.7 94.6-1.7 5.4.6 11.2 5.5 14L67.3 221c-4.3 23.2-4.3 47 0 70.2l-42.6 24.6c-4.9 2.8-7.1 8.6-5.5 14 11.1 35.6 30 67.8 54.7 94.6 3.8 4.1 10 5.1 14.8 2.3l42.6-24.6c17.9 15.4 38.5 27.3 60.8 35.1v49.2c0 5.6 3.9 10.5 9.4 11.7 36.7 8.2 74.3 7.8 109.2 0 5.5-1.2 9.4-6.1 9.4-11.7v-49.2c22.2-7.9 42.8-19.8 60.8-35.1l42.6 24.6c4.9 2.8 11 1.9 14.8-2.3 24.7-26.7 43.6-58.9 54.7-94.6 1.5-5.5-.7-11.3-5.6-14.1zM256 336c-44.1 0-80-35.9-80-80s35.9-80 80-80 80 35.9 80 80-35.9 80-80 80z"})),"copy-to-clipboard"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M768 1664h896v-640h-416q-40 0-68-28t-28-68v-416h-384v1152zm256-1440v-64q0-13-9.5-22.5t-22.5-9.5h-704q-13 0-22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h704q13 0 22.5-9.5t9.5-22.5zm256 672h299l-299-299v299zm512 128v672q0 40-28 68t-68 28h-960q-40 0-68-28t-28-68v-160h-544q-40 0-68-28t-28-68v-1344q0-40 28-68t68-28h1088q40 0 68 28t28 68v328q21 13 36 28l408 408q28 28 48 76t20 88z"})),"download"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1344 1344q0-26-19-45t-45-19-45 19-19 45 19 45 45 19 45-19 19-45zm256 0q0-26-19-45t-45-19-45 19-19 45 19 45 45 19 45-19 19-45zm128-224v320q0 40-28 68t-68 28h-1472q-40 0-68-28t-28-68v-320q0-40 28-68t68-28h465l135 136q58 56 136 56t136-56l136-136h464q40 0 68 28t28 68zm-325-569q17 41-14 70l-448 448q-18 19-45 19t-45-19l-448-448q-31-29-14-70 17-39 59-39h256v-448q0-26 19-45t45-19h256q26 0 45 19t19 45v448h256q42 0 59 39z"})),"edit"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M888 1184l116-116-152-152-116 116v56h96v96h56zm440-720q-16-16-33 1l-350 350q-17 17-1 33t33-1l350-350q17-17 1-33zm80 594v190q0 119-84.5 203.5t-203.5 84.5h-832q-119 0-203.5-84.5t-84.5-203.5v-832q0-119 84.5-203.5t203.5-84.5h832q63 0 117 25 15 7 18 23 3 17-9 29l-49 49q-14 14-32 8-23-6-45-6h-832q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113v-126q0-13 9-22l64-64q15-15 35-7t20 29zm-96-738l288 288-672 672h-288v-288zm444 132l-92 92-288-288 92-92q28-28 68-28t68 28l152 152q28 28 28 68t-28 68z"})),"envelope"===this.props.name&&s.createElement("svg",{width:"512",height:"512",viewBox:"0 0 512 512",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{fill:"currentColor",d:"M464 64H48C21.49 64 0 85.49 0 112v288c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V112c0-26.51-21.49-48-48-48zm0 48v40.805c-22.422 18.259-58.168 46.651-134.587 106.49-16.841 13.247-50.201 45.072-73.413 44.701-23.208.375-56.579-31.459-73.413-44.701C106.18 199.465 70.425 171.067 48 152.805V112h416zM48 400V214.398c22.914 18.251 55.409 43.862 104.938 82.646 21.857 17.205 60.134 55.186 103.062 54.955 42.717.231 80.509-37.199 103.053-54.947 49.528-38.783 82.032-64.401 104.947-82.653V400H48z"})," "),"eye-open"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1664 960q-152-236-381-353 61 104 61 225 0 185-131.5 316.5t-316.5 131.5-316.5-131.5-131.5-316.5q0-121 61-225-229 117-381 353 133 205 333.5 326.5t434.5 121.5 434.5-121.5 333.5-326.5zm-720-384q0-20-14-34t-34-14q-125 0-214.5 89.5t-89.5 214.5q0 20 14 34t34 14 34-14 14-34q0-86 61-147t147-61q20 0 34-14t14-34zm848 384q0 34-20 69-140 230-376.5 368.5t-499.5 138.5-499.5-139-376.5-368q-20-35-20-69t20-69q140-229 376.5-368t499.5-139 499.5 139 376.5 368q20 35 20 69z"})),"eye-close"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M555 1335l78-141q-87-63-136-159t-49-203q0-121 61-225-229 117-381 353 167 258 427 375zm389-759q0-20-14-34t-34-14q-125 0-214.5 89.5t-89.5 214.5q0 20 14 34t34 14 34-14 14-34q0-86 61-147t147-61q20 0 34-14t14-34zm363-191q0 7-1 9-106 189-316 567t-315 566l-49 89q-10 16-28 16-12 0-134-70-16-10-16-28 0-12 44-87-143-65-263.5-173t-208.5-245q-20-31-20-69t20-69q153-235 380-371t496-136q89 0 180 17l54-97q10-16 28-16 5 0 18 6t31 15.5 33 18.5 31.5 18.5 19.5 11.5q16 10 16 27zm37 447q0 139-79 253.5t-209 164.5l280-502q8 45 8 84zm448 128q0 35-20 69-39 64-109 145-150 172-347.5 267t-419.5 95l74-132q212-18 392.5-137t301.5-307q-115-179-282-294l63-112q95 64 182.5 153t144.5 184q20 34 20 69z"})),"external-link"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1408 928v-480q0-26-19-45t-45-19h-480q-42 0-59 39-17 41 14 70l144 144-534 534q-19 19-19 45t19 45l102 102q19 19 45 19t45-19l534-534 144 144q18 19 45 19 12 0 25-5 39-17 39-59zm256-512v960q0 119-84.5 203.5t-203.5 84.5h-960q-119 0-203.5-84.5t-84.5-203.5v-960q0-119 84.5-203.5t203.5-84.5h960q119 0 203.5 84.5t84.5 203.5z"})),"folder"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1728 608v704q0 92-66 158t-158 66h-1216q-92 0-158-66t-66-158v-960q0-92 66-158t158-66h320q92 0 158 66t66 158v32h672q92 0 158 66t66 158z"})),"folder-shared"===this.props.name&&s.createElement("svg",{viewBox:"2 2 20 20",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M20 6h-8l-2-2H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm-5 3c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm4 8h-8v-1c0-1.33 2.67-2 4-2s4 .67 4 2v1z"})),"heart"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M896 1664q-26 0-44-18l-624-602q-10-8-27.5-26t-55.5-65.5-68-97.5-53.5-121-23.5-138q0-220 127-344t351-124q62 0 126.5 21.5t120 58 95.5 68.5 76 68q36-36 76-68t95.5-68.5 120-58 126.5-21.5q224 0 351 124t127 344q0 221-229 450l-623 600q-18 18-44 18z"})),"heart-o"===this.props.name&&s.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 512 512"},s.createElement("path",{d:"M458.4 64.3C400.6 15.7 311.3 23 256 79.3 200.7 23 111.4 15.6 53.6 64.3-21.6 127.6-10.6 230.8 43 285.5l175.4 178.7c10 10.2 23.4 15.9 37.6 15.9 14.3 0 27.6-5.6 37.6-15.8L469 285.6c53.5-54.7 64.7-157.9-10.6-221.3zm-23.6 187.5L259.4 430.5c-2.4 2.4-4.4 2.4-6.8 0L77.2 251.8c-36.5-37.2-43.9-107.6 7.3-150.7 38.9-32.7 98.9-27.8 136.5 10.5l35 35.7 35-35.7c37.8-38.5 97.8-43.2 136.5-10.6 51.1 43.1 43.5 113.9 7.3 150.8z"})),"heartbeat"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1280 1024h305q-5 6-10 10.5t-9 7.5l-3 4-623 600q-18 18-44 18t-44-18l-624-602q-5-2-21-20h369q22 0 39.5-13.5t22.5-34.5l70-281 190 667q6 20 23 33t39 13q21 0 38-13t23-33l146-485 56 112q18 35 57 35zm512-428q0 145-103 300h-369l-111-221q-8-17-25.5-27t-36.5-8q-45 5-56 46l-129 430-196-686q-6-20-23.5-33t-39.5-13-39 13.5-22 34.5l-116 464h-423q-103-155-103-300 0-220 127-344t351-124q62 0 126.5 21.5t120 58 95.5 68.5 76 68q36-36 76-68t95.5-68.5 120-58 126.5-21.5q224 0 351 124t127 344z"})),"info-circle"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1152 1376v-160q0-14-9-23t-23-9h-96v-512q0-14-9-23t-23-9h-320q-14 0-23 9t-9 23v160q0 14 9 23t23 9h96v320h-96q-14 0-23 9t-9 23v160q0 14 9 23t23 9h448q14 0 23-9t9-23zm-128-896v-160q0-14-9-23t-23-9h-192q-14 0-23 9t-9 23v160q0 14 9 23t23 9h192q14 0 23-9t9-23zm640 416q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"})),"key"===this.props.name&&s.createElement("svg",{width:"512",height:"512",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 512 512"},s.createElement("path",{fill:"currentColor",d:"M512 176.001C512 273.203 433.202 352 336 352c-11.22 0-22.19-1.062-32.827-3.069l-24.012 27.014A23.999 23.999 0 0 1 261.223 384H224v40c0 13.255-10.745 24-24 24h-40v40c0 13.255-10.745 24-24 24H24c-13.255 0-24-10.745-24-24v-78.059c0-6.365 2.529-12.47 7.029-16.971l161.802-161.802C163.108 213.814 160 195.271 160 176 160 78.798 238.797.001 335.999 0 433.488-.001 512 78.511 512 176.001zM336 128c0 26.51 21.49 48 48 48s48-21.49 48-48-21.49-48-48-48-48 21.49-48 48z"})),"link"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1520 1216q0-40-28-68l-208-208q-28-28-68-28-42 0-72 32 3 3 19 18.5t21.5 21.5 15 19 13 25.5 3.5 27.5q0 40-28 68t-68 28q-15 0-27.5-3.5t-25.5-13-19-15-21.5-21.5-18.5-19q-33 31-33 73 0 40 28 68l206 207q27 27 68 27 40 0 68-26l147-146q28-28 28-67zm-703-705q0-40-28-68l-206-207q-28-28-68-28-39 0-68 27l-147 146q-28 28-28 67 0 40 28 68l208 208q27 27 68 27 42 0 72-31-3-3-19-18.5t-21.5-21.5-15-19-13-25.5-3.5-27.5q0-40 28-68t68-28q15 0 27.5 3.5t25.5 13 19 15 21.5 21.5 18.5 19q33-31 33-73zm895 705q0 120-85 203l-147 146q-83 83-203 83-121 0-204-85l-206-207q-83-83-83-203 0-123 88-209l-88-88q-86 88-208 88-120 0-204-84l-208-208q-84-84-84-204t85-203l147-146q83-83 203-83 121 0 204 85l206 207q83 83 83 203 0 123-88 209l88 88q86-88 208-88 120 0 204 84l208 208q84 84 84 204z"})),"lock"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M640 768h512v-192q0-106-75-181t-181-75-181 75-75 181v192zm832 96v576q0 40-28 68t-68 28h-960q-40 0-68-28t-28-68v-576q0-40 28-68t68-28h32v-192q0-184 132-316t316-132 316 132 132 316v192h32q40 0 68 28t28 68z"})),"lock-open"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1376 768q40 0 68 28t28 68v576q0 40-28 68t-68 28h-960q-40 0-68-28t-28-68v-576q0-40 28-68t68-28h32v-320q0-185 131.5-316.5t316.5-131.5 316.5 131.5 131.5 316.5q0 26-19 45t-45 19h-64q-26 0-45-19t-19-45q0-106-75-181t-181-75-181 75-75 181v320h736z"})),"magic-wand"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1254 581l293-293-107-107-293 293zm447-293q0 27-18 45l-1286 1286q-18 18-45 18t-45-18l-198-198q-18-18-18-45t18-45l1286-1286q18-18 45-18t45 18l198 198q18 18 18 45zm-1351-190l98 30-98 30-30 98-30-98-98-30 98-30 30-98zm350 162l196 60-196 60-60 196-60-196-196-60 196-60 60-196zm930 478l98 30-98 30-30 98-30-98-98-30 98-30 30-98zm-640-640l98 30-98 30-30 98-30-98-98-30 98-30 30-98z"})),"plus-circle"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1344 960v-128q0-26-19-45t-45-19h-256v-256q0-26-19-45t-45-19h-128q-26 0-45 19t-19 45v256h-256q-26 0-45 19t-19 45v128q0 26 19 45t45 19h256v256q0 26 19 45t45 19h128q26 0 45-19t19-45v-256h256q26 0 45-19t19-45zm320-64q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"})),"plus-square"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 448 512",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48zm-32 252c0 6.6-5.4 12-12 12h-92v92c0 6.6-5.4 12-12 12h-56c-6.6 0-12-5.4-12-12v-92H92c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h92v-92c0-6.6 5.4-12 12-12h56c6.6 0 12 5.4 12 12v92h92c6.6 0 12 5.4 12 12v56z"})),"filter"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 512 512",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M487.976 0H24.028C2.71 0-8.047 25.866 7.058 40.971L192 225.941V432c0 7.831 3.821 15.17 10.237 19.662l80 55.98C298.02 518.69 320 507.493 320 487.98V225.941l184.947-184.97C520.021 25.896 509.338 0 487.976 0z"})),"life-ring"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 512 512",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M256 504c136.967 0 248-111.033 248-248S392.967 8 256 8 8 119.033 8 256s111.033 248 248 248zm-103.398-76.72l53.411-53.411c31.806 13.506 68.128 13.522 99.974 0l53.411 53.411c-63.217 38.319-143.579 38.319-206.796 0zM336 256c0 44.112-35.888 80-80 80s-80-35.888-80-80 35.888-80 80-80 80 35.888 80 80zm91.28 103.398l-53.411-53.411c13.505-31.806 13.522-68.128 0-99.974l53.411-53.411c38.319 63.217 38.319 143.579 0 206.796zM359.397 84.72l-53.411 53.411c-31.806-13.505-68.128-13.522-99.973 0L152.602 84.72c63.217-38.319 143.579-38.319 206.795 0zM84.72 152.602l53.411 53.411c-13.506 31.806-13.522 68.128 0 99.974L84.72 359.398c-38.319-63.217-38.319-143.579 0-206.796z"})),"plug"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 384 512",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M320,32a32,32,0,0,0-64,0v96h64Zm48,128H16A16,16,0,0,0,0,176v32a16,16,0,0,0,16,16H32v32A160.07,160.07,0,0,0,160,412.8V512h64V412.8A160.07,160.07,0,0,0,352,256V224h16a16,16,0,0,0,16-16V176A16,16,0,0,0,368,160ZM128,32a32,32,0,0,0-64,0v96h64Z"})),"printer"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M448 1536h896v-256h-896v256zm0-640h896v-384h-160q-40 0-68-28t-28-68v-160h-640v640zm1152 64q0-26-19-45t-45-19-45 19-19 45 19 45 45 19 45-19 19-45zm128 0v416q0 13-9.5 22.5t-22.5 9.5h-224v160q0 40-28 68t-68 28h-960q-40 0-68-28t-28-68v-160h-224q-13 0-22.5-9.5t-9.5-22.5v-416q0-79 56.5-135.5t135.5-56.5h64v-544q0-40 28-68t68-28h672q40 0 88 20t76 48l152 152q28 28 48 76t20 88v256h64q79 0 135.5 56.5t56.5 135.5z"})),"refresh"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 512 512",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M370.72 133.28C339.458 104.008 298.888 87.962 255.848 88c-77.458.068-144.328 53.178-162.791 126.85-1.344 5.363-6.122 9.15-11.651 9.15H24.103c-7.498 0-13.194-6.807-11.807-14.176C33.933 94.924 134.813 8 256 8c66.448 0 126.791 26.136 171.315 68.685L463.03 40.97C478.149 25.851 504 36.559 504 57.941V192c0 13.255-10.745 24-24 24H345.941c-21.382 0-32.09-25.851-16.971-40.971l41.75-41.749zM32 296h134.059c21.382 0 32.09 25.851 16.971 40.971l-41.75 41.75c31.262 29.273 71.835 45.319 114.876 45.28 77.418-.07 144.315-53.144 162.787-126.849 1.344-5.363 6.122-9.15 11.651-9.15h57.304c7.498 0 13.194 6.807 11.807 14.176C478.067 417.076 377.187 504 256 504c-66.448 0-126.791-26.136-171.315-68.685L48.97 471.03C33.851 486.149 8 475.441 8 454.059V320c0-13.255 10.745-24 24-24z"})),"question-circle"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1024 1376v-192q0-14-9-23t-23-9h-192q-14 0-23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23-9t9-23zm256-672q0-88-55.5-163t-138.5-116-170-41q-243 0-371 213-15 24 8 42l132 100q7 6 19 6 16 0 25-12 53-68 86-92 34-24 86-24 48 0 85.5 26t37.5 59q0 38-20 61t-68 45q-63 28-115.5 86.5t-52.5 125.5v36q0 14 9 23t23 9h192q14 0 23-9t9-23q0-19 21.5-49.5t54.5-49.5q32-18 49-28.5t46-35 44.5-48 28-60.5 12.5-81zm384 192q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"})),"search"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1216 832q0-185-131.5-316.5t-316.5-131.5-316.5 131.5-131.5 316.5 131.5 316.5 316.5 131.5 316.5-131.5 131.5-316.5zm512 832q0 52-38 90t-90 38q-54 0-90-38l-343-342q-179 124-399 124-143 0-273.5-55.5t-225-150-150-225-55.5-273.5 55.5-273.5 150-225 225-150 273.5-55.5 273.5 55.5 225 150 150 225 55.5 273.5q0 220-124 399l343 343q37 37 37 90z"})),"share"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1764 11q33 24 27 64l-256 1536q-5 29-32 45-14 8-31 8-11 0-24-5l-453-185-242 295q-18 23-49 23-13 0-22-4-19-7-30.5-23.5t-11.5-36.5v-349l864-1059-1069 925-395-162q-37-14-40-55-2-40 32-59l1664-960q15-9 32-9 20 0 36 11z"})),"star"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1728 647q0 22-26 48l-363 354 86 500q1 7 1 20 0 21-10.5 35.5t-30.5 14.5q-19 0-40-12l-449-236-449 236q-22 12-40 12-21 0-31.5-14.5t-10.5-35.5q0-6 2-20l86-500-364-354q-25-27-25-48 0-37 56-46l502-73 225-455q19-41 49-41t49 41l225 455 502 73q56 9 56 46z"})),"save"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 448 512",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M433.941 129.941l-83.882-83.882A48 48 0 0 0 316.118 32H48C21.49 32 0 53.49 0 80v352c0 26.51 21.49 48 48 48h352c26.51 0 48-21.49 48-48V163.882a48 48 0 0 0-14.059-33.941zM272 80v80H144V80h128zm122 352H54a6 6 0 0 1-6-6V86a6 6 0 0 1 6-6h42v104c0 13.255 10.745 24 24 24h176c13.255 0 24-10.745 24-24V83.882l78.243 78.243a6 6 0 0 1 1.757 4.243V426a6 6 0 0 1-6 6zM224 232c-48.523 0-88 39.477-88 88s39.477 88 88 88 88-39.477 88-88-39.477-88-88-88zm0 128c-22.056 0-40-17.944-40-40s17.944-40 40-40 40 17.944 40 40-17.944 40-40 40z"})),"trash"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M704 1376v-704q0-14-9-23t-23-9h-64q-14 0-23 9t-9 23v704q0 14 9 23t23 9h64q14 0 23-9t9-23zm256 0v-704q0-14-9-23t-23-9h-64q-14 0-23 9t-9 23v704q0 14 9 23t23 9h64q14 0 23-9t9-23zm256 0v-704q0-14-9-23t-23-9h-64q-14 0-23 9t-9 23v704q0 14 9 23t23 9h64q14 0 23-9t9-23zm-544-992h448l-48-117q-7-9-17-11h-317q-10 2-17 11zm928 32v64q0 14-9 23t-23 9h-96v948q0 83-47 143.5t-113 60.5h-832q-66 0-113-58.5t-47-141.5v-952h-96q-14 0-23-9t-9-23v-64q0-14 9-23t23-9h309l70-167q15-37 54-63t79-26h320q40 0 79 26t54 63l70 167h309q14 0 23 9t9 23z"})),"upload"===this.props.name&&s.createElement("svg",{width:"2048",height:"1792",viewBox:"0 0 2048 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1344 864q0-14-9-23l-352-352q-9-9-23-9t-23 9l-351 351q-10 12-10 24 0 14 9 23t23 9h224v352q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5-9.5t9.5-22.5v-352h224q13 0 22.5-9.5t9.5-22.5zm640 288q0 159-112.5 271.5t-271.5 112.5h-1088q-185 0-316.5-131.5t-131.5-316.5q0-130 70-240t188-165q-2-30-2-43 0-212 150-362t362-150q156 0 285.5 87t188.5 231q71-62 166-62 106 0 181 75t75 181q0 76-41 138 130 31 213.5 135.5t83.5 238.5z"})),"upload-a"===this.props.name&&s.createElement("svg",{"aria-hidden":"true",focusable:"false","data-prefix":"fas","data-icon":"upload",className:"svg-inline--fa fa-upload fa-w-16",role:"img",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 512 512"},s.createElement("path",{fill:"currentColor",d:"M296 384h-80c-13.3 0-24-10.7-24-24V192h-87.7c-17.8 0-26.7-21.5-14.1-34.1L242.3 5.7c7.5-7.5 19.8-7.5 27.3 0l152.2 152.2c12.6 12.6 3.7 34.1-14.1 34.1H320v168c0 13.3-10.7 24-24 24zm216-8v112c0 13.3-10.7 24-24 24H24c-13.3 0-24-10.7-24-24V376c0-13.3 10.7-24 24-24h136v8c0 30.9 25.1 56 56 56h80c30.9 0 56-25.1 56-56v-8h136c13.3 0 24 10.7 24 24zm-124 88c0-11-9-20-20-20s-20 9-20 20 9 20 20 20 20-9 20-20zm64 0c0-11-9-20-20-20s-20 9-20 20 9 20 20 20 20-9 20-20z"})),"user"===this.props.name&&s.createElement("svg",{width:"448",height:"512",viewBox:"0 0 448 512",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{fill:"currentColor",d:"M224 256c70.7 0 128-57.3 128-128S294.7 0 224 0 96 57.3 96 128s57.3 128 128 128zm89.6 32h-16.7c-22.2 10.2-46.9 16-72.9 16s-50.6-5.8-72.9-16h-16.7C60.2 288 0 348.2 0 422.4V464c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48v-41.6c0-74.2-60.2-134.4-134.4-134.4z",className:""})),"users"===this.props.name&&s.createElement("svg",{width:"640",height:"512",viewBox:"0 0 640 512",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{fill:"currentColor",d:"M96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm448 0c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm32 32h-64c-17.6 0-33.5 7.1-45.1 18.6 40.3 22.1 68.9 62 75.1 109.4h66c17.7 0 32-14.3 32-32v-32c0-35.3-28.7-64-64-64zm-256 0c61.9 0 112-50.1 112-112S381.9 32 320 32 208 82.1 208 144s50.1 112 112 112zm76.8 32h-8.3c-20.8 10-43.9 16-68.5 16s-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h288c26.5 0 48-21.5 48-48v-28.8c0-63.6-51.6-115.2-115.2-115.2zm-223.7-13.4C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z"})),"warning"===this.props.name&&s.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M1024 1375v-190q0-14-9.5-23.5t-22.5-9.5h-192q-13 0-22.5 9.5t-9.5 23.5v190q0 14 9.5 23.5t22.5 9.5h192q13 0 22.5-9.5t9.5-23.5zm-2-374l18-459q0-12-10-19-13-11-24-11h-220q-11 0-24 11-10 7-10 21l17 457q0 10 10 16.5t24 6.5h185q14 0 23.5-6.5t10.5-16.5zm-14-934l768 1408q35 63-2 126-17 29-46.5 46t-63.5 17h-1536q-34 0-63.5-17t-46.5-46q-37-63-2-126l768-1408q17-31 47-49t65-18 65 18 47 49z"})))}}W.defaultProps={big:!1,baseline:!1,dim:!1,onClick:()=>{}},W.propTypes={name:h().string,big:h().bool,dim:h().bool,baseline:h().bool,onClick:h().func};const X=W;function K(){return K=Object.assign||function(e){for(var t=1;t{if(void 0===e||"string"!=typeof e||!e.length)return!1;if((t=t||{}).whitelistedProtocols&&!Array.isArray(t.whitelistedProtocols))throw new TypeError("The whitelistedProtocols should be an array of string.");if(t.defaultProtocol&&"string"!=typeof t.defaultProtocol)throw new TypeError("The defaultProtocol should be a string.");const r=t.whitelistedProtocols||[Q.HTTP,Q.HTTPS],s=[Q.JAVASCRIPT],n=t.defaultProtocol||"";!/^((?!:\/\/).)*:\/\//.test(e)&&n&&(e=`${n}//${e}`);try{const t=new URL(e);return!s.includes(t.protocol)&&!!r.includes(t.protocol)&&t.href}catch(e){return!1}},Q={FTP:"http:",FTPS:"https:",HTTP:"http:",HTTPS:"https:",JAVASCRIPT:"javascript:",SSH:"ssh:"};class ee{constructor(e){this.settings=e}canIUse(e){let t=!1;const r=`passbolt.plugins.${e}`,s=y(this.settings,r)||null;if(s&&"object"==typeof s){const e=y(s,"enabled");void 0!==e&&!0!==e||(t=!0)}return t}getPluginSettings(e){const t=`passbolt.plugins.${e}`;return y(this.settings,t)}getRememberMeOptions(){return(this.getPluginSettings("rememberMe")||{}).options||{}}get hasRememberMeUntilILogoutOption(){return void 0!==(this.getRememberMeOptions()||{})[-1]}getServerTimezone(){return y(this.settings,"passbolt.app.server_timezone")}get termsLink(){const e=y(this.settings,"passbolt.legal.terms.url");return!!e&&Y(e)}get privacyLink(){const e=y(this.settings,"passbolt.legal.privacy_policy.url");return!!e&&Y(e)}get registrationPublic(){return!0===y(this.settings,"passbolt.registration.public")}get debug(){return!0===y(this.settings,"app.debug")}get url(){return y(this.settings,"app.url")||""}get version(){return y(this.settings,"app.version.number")}get locale(){return y(this.settings,"app.locale")||ee.DEFAULT_LOCALE.locale}async setLocale(e){this.settings.app.locale=e}get supportedLocales(){return y(this.settings,"passbolt.plugins.locale.options")||ee.DEFAULT_SUPPORTED_LOCALES}get generatorConfiguration(){return y(this.settings,"passbolt.plugins.generator.configuration")}static get DEFAULT_SUPPORTED_LOCALES(){return[ee.DEFAULT_LOCALE]}static get DEFAULT_LOCALE(){return{locale:"en-UK",label:"English"}}}var te=r(1932),re=r(8718),se=r(3554);class ne extends s.Component{constructor(e){super(e),this.state=this.defaultState}get defaultState(){return{ready:!1}}async componentDidMount(){await te.Z.use(re.Db).use(se.Z).init({lng:this.locale,load:"currentOnly",react:{useSuspense:!1},backend:{loadPath:this.props.loadingPath||"/locales/{{lng}}/{{ns}}.json"},supportedLngs:this.supportedLocales,fallbackLng:!1,ns:["common"],defaultNS:"common",keySeparator:!1,nsSeparator:!1,debug:!1}),this.setState({ready:!0})}get supportedLocales(){return this.props.context.siteSettings.supportedLocales?this.props.context.siteSettings.supportedLocales.map((e=>e.locale)):[this.locale]}get locale(){return this.props.context.locale}async componentDidUpdate(e){await this.handleLocaleChange(e.context.locale)}async handleLocaleChange(e){this.locale!==e&&await te.Z.changeLanguage(this.locale)}get isReady(){return this.state.ready}render(){return s.createElement(s.Fragment,null,this.isReady&&this.props.children)}}ne.propTypes={context:h().any,loadingPath:h().any,children:h().any};const ae=o(ne);class le extends s.Component{constructor(e){super(e),this.state=this.defaultState,this.bindHandlers()}async componentDidMount(){await this.initLocale()}async componentDidUpdate(e){await this.handleLocaleChange(e.context.locale)}async handleLocaleChange(e){this.props.context.locale!==e&&await this.setState({locale:this.props.context.locale})}get defaultState(){return{loading:!0,locale:null,processing:!1}}get areActionsAllowed(){return!this.state.processing}bindHandlers(){this.handleLocaleInputChange=this.handleLocaleInputChange.bind(this)}async handleLocaleInputChange(e){const t=e.target.value;await this.updateLocale(t)}async updateLocale(e){await this.toggleProcessing(),await this.props.context.onUpdateLocaleRequested(e),await this.toggleProcessing()}async initLocale(){await this.setState({locale:this.props.context.locale,loading:!1})}async toggleProcessing(){const e=this.state.processing;return this.setState({processing:!e})}isLoading(){return this.state.loading}render(){return s.createElement(s.Fragment,null,!this.isLoading()&&s.createElement("div",{className:"input select locale"},s.createElement("select",{id:"user-locale-input",name:"locale",value:this.state.locale,disabled:!this.areActionsAllowed,onChange:this.handleLocaleInputChange},this.props.context.siteSettings.supportedLocales.map((e=>s.createElement("option",{key:e.locale,value:e.locale},e.label))))))}}le.propTypes={context:h().any};const oe=o(le);class ie extends s.Component{get statesToHideLocaleSwitch(){return[L.INITIAL_STATE]}get mustDisplayLocaleSwitch(){return!this.statesToHideLocaleSwitch.includes(this.props.apiSetupContext.state)}render(){return s.createElement(s.Fragment,null,this.mustDisplayLocaleSwitch&&s.createElement(oe,null))}}ie.propTypes={apiSetupContext:h().any};const ce=C(ie);class he extends s.Component{constructor(e){super(e),this.state=this.defaultState,this.userId=null,this.token=null,this.initializeProperties()}get defaultState(){return{siteSettings:null,trustedDomain:this.baseUrl,getApiClientOptions:this.getApiClientOptions.bind(this),locale:null,onUpdateLocaleRequested:this.onUpdateLocaleRequested.bind(this)}}async componentDidMount(){await this.getSiteSettings(),this.initLocale()}initializeProperties(){const e="[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[0-5][a-fA-F0-9]{3}-[089aAbB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}",t=new RegExp(`setup/install/(${e})/(${e})$`),r=window.location.pathname.match(t);r?(this.userId=r[1],this.token=r[2]):console.error("Unable to retrieve the user id and token from the url")}get baseUrl(){const e=document.getElementsByTagName("base")&&document.getElementsByTagName("base")[0];return e?e.attributes.href.value.replace(/\/*$/g,""):(console.error("Unable to retrieve the page base tag"),"")}getApiClientOptions(){return(new U).setBaseUrl(this.state.trustedDomain)}async getSiteSettings(){const e=this.getApiClientOptions().setResourceName("settings"),t=new v(e),{body:r}=await t.findAll(),s=new ee(r);await this.setState({siteSettings:s})}initLocale(){const e=this.getUrlLocale()||this.getBrowserLocale()||this.getBrowserSimilarLocale()||this.state.siteSettings.locale;this.setState({locale:e}),this.setUrlLocale(e)}getUrlLocale(){const e=new URL(window.location.href).searchParams.get("locale");if(e){const t=this.state.siteSettings.supportedLocales.find((t=>e===t.locale));if(t)return t.locale}}getBrowserLocale(){const e=this.state.siteSettings.supportedLocales.find((e=>navigator.language===e.locale));if(e)return e.locale}getBrowserSimilarLocale(){const e=navigator.language.split("-")[0],t=this.state.siteSettings.supportedLocales.find((t=>e===t.locale.split("-")[0]));if(t)return t.locale}async onUpdateLocaleRequested(e){await this.setState({locale:e}),this.setUrlLocale(e)}setUrlLocale(e){const t=new URL(window.location.href);t.searchParams.set("locale",e),window.history.replaceState(null,null,t)}isReady(){return null!==this.state.siteSettings&&null!==this.state.locale}render(){return s.createElement(i.Provider,{value:this.state},this.isReady()&&s.createElement(ae,{loadingPath:`${this.state.trustedDomain}/locales/{{lng}}/{{ns}}.json`},s.createElement(z,{value:{userId:this.userId,token:this.token}},s.createElement("div",{id:"container",className:"container page login"},s.createElement("div",{className:"content"},s.createElement("div",{className:"header"},s.createElement("div",{className:"logo"},s.createElement("span",{className:"visually-hidden"},"Passbolt"))),s.createElement("div",{className:"login-form"},s.createElement(Z,null)),s.createElement(ce,null))),s.createElement(J,null))))}}const pe=he,me=document.createElement("div");document.body.appendChild(me),n.render(s.createElement(pe,null),me)}},n={};function a(e){var t=n[e];if(void 0!==t)return t.exports;var r=n[e]={exports:{}};return s[e].call(r.exports,r,r.exports,a),r.exports}a.m=s,e=[],a.O=(t,r,s,n)=>{if(!r){var l=1/0;for(h=0;h=n)&&Object.keys(a.O).every((e=>a.O[e](r[i])))?r.splice(i--,1):(o=!1,n0&&e[h-1][2]>n;h--)e[h]=e[h-1];e[h]=[r,s,n]},a.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return a.d(t,{a:t}),t},r=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,a.t=function(e,s){if(1&s&&(e=this(e)),8&s)return e;if("object"==typeof e&&e){if(4&s&&e.__esModule)return e;if(16&s&&"function"==typeof e.then)return e}var n=Object.create(null);a.r(n);var l={};t=t||[null,r({}),r([]),r(r)];for(var o=2&s&&e;"object"==typeof o&&!~t.indexOf(o);o=r(o))Object.getOwnPropertyNames(o).forEach((t=>l[t]=()=>e[t]));return l.default=()=>e,a.d(n,l),n},a.d=(e,t)=>{for(var r in t)a.o(t,r)&&!a.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},a.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),a.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),a.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.j=240,(()=>{var e={240:0};a.O.j=t=>0===e[t];var t=(t,r)=>{var s,n,[l,o,i]=r,c=0;for(s in o)a.o(o,s)&&(a.m[s]=o[s]);if(i)var h=i(a);for(t&&t(r);ca(9395)));l=a.O(l)})(); \ No newline at end of file diff --git a/webroot/js/app/api-triage.js b/webroot/js/app/api-triage.js index 474f6f11af..03b4c8c0bc 100644 --- a/webroot/js/app/api-triage.js +++ b/webroot/js/app/api-triage.js @@ -1,2 +1,2 @@ /*! For license information please see api-triage.js.LICENSE.txt */ -(()=>{"use strict";var e,t,n,r={8107:(e,t,n)=>{var r=n(7294),s=n(3935),a=n(2137),i=n(6610),o=n(5991),c=n(379),l=n(6070),u=n(7608),h=n(7757),p=n.n(h),f=n(2122);var m=r.createContext({user:null,users:null,roles:null,rememberMeOptions:{},resources:null,resource:null,shareResources:null,selectedResources:null,selectedUser:null,folders:null,resourceCommentId:null,mustRefreshComments:!1,siteSettings:null,userSettings:null,onCheckIsAuthenticatedRequested:null});function d(e){return function(t){(0,c.Z)(h,t);var n,s,a=(n=h,s=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,t=(0,u.Z)(n);if(s){var r=(0,u.Z)(this).constructor;e=Reflect.construct(t,arguments,r)}else e=t.apply(this,arguments);return(0,l.Z)(this,e)});function h(){return(0,i.Z)(this,h),a.apply(this,arguments)}return(0,o.Z)(h,[{key:"render",value:function(){var t=this;return r.createElement(m.Consumer,null,(function(n){return r.createElement(e,(0,f.Z)({context:n},t.props))}))}}]),h}(r.Component)}const v=m;var g=function(){function e(t){(0,i.Z)(this,e),this.setToken(t)}return(0,o.Z)(e,[{key:"setToken",value:function(e){this.validate(e),this.token=e}},{key:"validate",value:function(e){if(!e)throw new TypeError("CSRF token cannot be empty.");if("string"!=typeof e)throw new TypeError("CSRF token should be a string.")}},{key:"toFetchHeaders",value:function(){return{"X-CSRF-Token":this.token}}}]),e}(),y=function(){function e(){(0,i.Z)(this,e)}return(0,o.Z)(e,[{key:"setBaseUrl",value:function(e){if(!e)throw new TypeError("ApiClientOption baseUrl is required.");if("string"==typeof e)try{this.baseUrl=new URL(e)}catch(e){throw new TypeError("ApiClientOption baseUrl is invalid.")}else{if(!(e instanceof URL))throw new TypeError("ApiClientOptions baseurl should be a string or URL");this.baseUrl=e}return this}},{key:"setCsrfToken",value:function(e){if(!e)throw new TypeError("ApiClientOption csrfToken is required.");if("string"==typeof e)this.csrfToken=new g(e);else{if(!(e instanceof g))throw new TypeError("ApiClientOption csrfToken should be a string or a valid CsrfToken.");this.csrfToken=e}return this}},{key:"setResourceName",value:function(e){if(!e)throw new TypeError("ApiClientOptions.setResourceName resourceName is required.");if("string"!=typeof e)throw new TypeError("ApiClientOptions.setResourceName resourceName should be a valid string.");return this.resourceName=e,this}},{key:"getBaseUrl",value:function(){return this.baseUrl}},{key:"getResourceName",value:function(){return this.resourceName}},{key:"getHeaders",value:function(){if(this.csrfToken)return this.csrfToken.toFetchHeaders()}}]),e}(),w=n(5697),q=n.n(w),E=n(4699),k=n(6156),b=n(5366);const x=function(e){(0,c.Z)(s,e);var t,n,r=(t=s,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,u.Z)(t);if(n){var s=(0,u.Z)(this).constructor;e=Reflect.construct(r,arguments,s)}else e=r.apply(this,arguments);return(0,l.Z)(this,e)});function s(e,t){var n;return(0,i.Z)(this,s),(n=r.call(this,e)).name="PassboltApiFetchError",n.data=t||{},n}return s}((0,b.Z)(Error));const R=function(e){(0,c.Z)(s,e);var t,n,r=(t=s,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,u.Z)(t);if(n){var s=(0,u.Z)(this).constructor;e=Reflect.construct(r,arguments,s)}else e=r.apply(this,arguments);return(0,l.Z)(this,e)});function s(){var e;return(0,i.Z)(this,s),(e=r.call(this,"An internal error occurred. The server response could not be parsed. Please contact your administrator.")).name="PassboltBadResponseError",e}return s}((0,b.Z)(Error));const Z=function(e){(0,c.Z)(s,e);var t,n,r=(t=s,n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,r=(0,u.Z)(t);if(n){var s=(0,u.Z)(this).constructor;e=Reflect.construct(r,arguments,s)}else e=r.apply(this,arguments);return(0,l.Z)(this,e)});function s(e){var t;return(0,i.Z)(this,s),e=e||"The service is unavailable",(t=r.call(this,e)).name="PassboltServiceUnavailableError",t}return s}((0,b.Z)(Error));function T(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function S(e){for(var t=1;t{if(!n){var i=1/0;for(l=0;l=s)&&Object.keys(a.O).every((e=>a.O[e](n[c])))?n.splice(c--,1):(o=!1,s0&&e[l-1][2]>s;l--)e[l]=e[l-1];e[l]=[n,r,s]},a.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return a.d(t,{a:t}),t},n=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,a.t=function(e,r){if(1&r&&(e=this(e)),8&r)return e;if("object"==typeof e&&e){if(4&r&&e.__esModule)return e;if(16&r&&"function"==typeof e.then)return e}var s=Object.create(null);a.r(s);var i={};t=t||[null,n({}),n([]),n(n)];for(var o=2&r&&e;"object"==typeof o&&!~t.indexOf(o);o=n(o))Object.getOwnPropertyNames(o).forEach((t=>i[t]=()=>e[t]));return i.default=()=>e,a.d(s,i),s},a.d=(e,t)=>{for(var n in t)a.o(t,n)&&!a.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},a.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),a.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),a.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.j=326,(()=>{var e={326:0};a.O.j=t=>0===e[t];var t=(t,n)=>{var r,s,[i,o,c]=n,l=0;for(r in o)a.o(o,r)&&(a.m[r]=o[r]);if(c)var u=c(a);for(t&&t(n);la(8107)));i=a.O(i)})(); \ No newline at end of file +(()=>{"use strict";var e,t,s,r={8107:(e,t,s)=>{var r=s(7294),n=s(3935);function a(){return a=Object.assign||function(e){for(var t=1;tr.createElement(e,a({context:t},this.props))))}}}const o=i;class h{constructor(e){this.setToken(e)}setToken(e){this.validate(e),this.token=e}validate(e){if(!e)throw new TypeError("CSRF token cannot be empty.");if("string"!=typeof e)throw new TypeError("CSRF token should be a string.")}toFetchHeaders(){return{"X-CSRF-Token":this.token}}}class c{setBaseUrl(e){if(!e)throw new TypeError("ApiClientOption baseUrl is required.");if("string"==typeof e)try{this.baseUrl=new URL(e)}catch(e){throw new TypeError("ApiClientOption baseUrl is invalid.")}else{if(!(e instanceof URL))throw new TypeError("ApiClientOptions baseurl should be a string or URL");this.baseUrl=e}return this}setCsrfToken(e){if(!e)throw new TypeError("ApiClientOption csrfToken is required.");if("string"==typeof e)this.csrfToken=new h(e);else{if(!(e instanceof h))throw new TypeError("ApiClientOption csrfToken should be a string or a valid CsrfToken.");this.csrfToken=e}return this}setResourceName(e){if(!e)throw new TypeError("ApiClientOptions.setResourceName resourceName is required.");if("string"!=typeof e)throw new TypeError("ApiClientOptions.setResourceName resourceName should be a valid string.");return this.resourceName=e,this}getBaseUrl(){return this.baseUrl}getResourceName(){return this.resourceName}getHeaders(){if(this.csrfToken)return this.csrfToken.toFetchHeaders()}}var p=s(5697),m=s.n(p);class d extends Error{constructor(e,t){super(e),this.name="PassboltApiFetchError",this.data=t||{}}}const u=d;class g extends Error{constructor(){super("An internal error occurred. The server response could not be parsed. Please contact your administrator."),this.name="PassboltBadResponseError"}}const v=g;class w extends Error{constructor(e){super(e=e||"The service is unavailable"),this.name="PassboltServiceUnavailableError"}}const q=w;class E{constructor(e){if(this.options=e,!this.options.getBaseUrl())throw new TypeError("ApiClient constructor error: baseUrl is required.");if(!this.options.getResourceName())throw new TypeError("ApiClient constructor error: resourceName is required.");try{let e=this.options.getBaseUrl().toString();e.endsWith("/")&&(e=e.slice(0,-1)),this.baseUrl=`${e}/${this.options.getResourceName()}`,this.baseUrl=new URL(this.baseUrl)}catch(e){throw new TypeError("ApiClient constructor error: b.")}this.apiVersion="api-version=v2"}getDefaultHeaders(){return{Accept:"application/json","content-type":"application/json"}}buildFetchOptions(){return{credentials:"include",headers:{...this.getDefaultHeaders(),...this.options.getHeaders()}}}async get(e,t){this.assertValidId(e);const s=this.buildUrl(`${this.baseUrl}/${e}`,t||{});return this.fetchAndHandleResponse("GET",s)}async delete(e,t,s,r){let n;this.assertValidId(e),void 0===r&&(r=!1),n=r?this.buildUrl(`${this.baseUrl}/${e}/dry-run`,s||{}):this.buildUrl(`${this.baseUrl}/${e}`,s||{});let a=null;return t&&(a=this.buildBody(t)),this.fetchAndHandleResponse("DELETE",n,a)}async findAll(e){const t=this.buildUrl(this.baseUrl.toString(),e||{});return await this.fetchAndHandleResponse("GET",t)}async create(e,t){const s=this.buildUrl(this.baseUrl.toString(),t||{}),r=this.buildBody(e);return this.fetchAndHandleResponse("POST",s,r)}async update(e,t,s,r){let n;this.assertValidId(e),void 0===r&&(r=!1),n=r?this.buildUrl(`${this.baseUrl}/${e}/dry-run`,s||{}):this.buildUrl(`${this.baseUrl}/${e}`,s||{});let a=null;return t&&(a=this.buildBody(t)),this.fetchAndHandleResponse("PUT",n,a)}assertValidId(e){if(!e)throw new TypeError("ApiClient.assertValidId error: id cannot be empty");if("string"!=typeof e)throw new TypeError("ApiClient.assertValidId error: id should be a string")}assertMethod(e){"string"!=typeof e&&new TypeError("ApiClient.assertValidMethod method should be a string."),["GET","POST","PUT","DELETE"].indexOf(e)<0&&new TypeError(`ApiClient.assertValidMethod error: method ${e} is not supported.`)}assertUrl(e){if(!e)throw new TypeError("ApliClient.assertUrl error: url is required.");if(!(e instanceof URL))throw new TypeError("ApliClient.assertUrl error: url should be a valid URL object.")}assertBody(e){"string"!=typeof e&&new TypeError("ApiClient.assertBody error: body should be a string.")}buildBody(e){return JSON.stringify(e)}buildUrl(e,t){if("string"!=typeof e)throw new TypeError("ApiClient.buildUrl error: url should be a string.");const s=new URL(`${e}.json?${this.apiVersion}`);t=t||{};for(const[e,r]of Object.entries(t)){if("string"!=typeof e)throw new TypeError("ApiClient.buildUrl error: urlOptions key should be a string.");if("string"==typeof r)s.searchParams.append(e,r);else{if(!Array.isArray(r))throw new TypeError("ApiClient.buildUrl error: urlOptions value should be a string or array.");r.forEach((t=>{s.searchParams.append(e,t)}))}}return s}async fetchAndHandleResponse(e,t,s,r){let n,a;this.assertUrl(t),this.assertMethod(e),s&&this.assertBody(s);const i={...this.buildFetchOptions(),...r};i.method=e,s&&(i.body=s);try{n=await fetch(t.toString(),i)}catch(e){throw new q(e.message)}try{a=await n.json()}catch(e){throw new v}if(!n.ok){const e=a.header.message;throw new u(e,{code:n.status,body:a.body})}return a}}function f(){return f=Object.assign||function(e){for(var t=1;t{},onTriageRequested:()=>{},onRegistrationRequested:()=>{}});class b extends r.Component{constructor(e){super(e),this.state=Object.assign(this.defaultState,e.value)}get defaultState(){return{state:C.INITIAL_STATE,onInitializeTriageRequested:this.onInitializeTriageRequested.bind(this),onTriageRequested:this.onTriageRequested.bind(this),onRegistrationRequested:this.onRegistrationRequested.bind(this)}}async onInitializeTriageRequested(){return this.setState({state:C.USERNAME_STATE})}async onTriageRequested(e){const t={username:e},s=this.props.context.getApiClientOptions();s.setResourceName("users/recover");const r=new E(s);await r.create(t).then(this.handleTriageSuccess.bind(this)).catch((t=>this.handleTriageError(t,e)))}async handleTriageSuccess(){return this.setState({state:C.CHECK_MAILBOX_STATE})}async handleTriageError(e,t){e.data&&404===e.data.code&&(this.props.context.siteSettings.registrationPublic?this.setState({username:t,state:C.NAME_STATE}):this.setState({username:t,state:C.ERROR_STATE}))}async onRegistrationRequested(e,t){const s={username:this.state.username,profile:{first_name:e,last_name:t}},r=this.props.context.getApiClientOptions().setResourceName("users/register"),n=new E(r);await n.create(s).then(this.handleRegistrationSuccess.bind(this)).catch(this.handleRegistrationError.bind(this))}async handleRegistrationSuccess(){return this.setState({state:C.CHECK_MAILBOX_STATE})}async handleRegistrationError(){this.setState({state:C.ERROR_STATE})}render(){return r.createElement(y.Provider,{value:this.state},this.props.children)}}b.propTypes={context:m().any,value:m().any,children:m().any};const x=l(b);function T(e){return class extends r.Component{render(){return r.createElement(y.Consumer,null,(t=>r.createElement(e,f({apiTriageContext:t},this.props))))}}}const C={INITIAL_STATE:"Initial State",USERNAME_STATE:"Enter username state",CHECK_MAILBOX_STATE:"Check mailbox state",NAME_STATE:"Enter name state",NAME_ERROR:"Error state"};var S=s(9116);class z extends r.Component{render(){return r.createElement("div",{className:"login-processing"},r.createElement("div",{className:"processing-wrapper"},r.createElement("div",{className:"processing"})),r.createElement("h1",null,r.createElement(S.c,null,"Please wait...")))}}const L=z;var A=s(648),U=s.n(A);class R extends r.Component{constructor(e){super(e),this.bindCallbacks()}bindCallbacks(){this.getClassName=this.getClassName.bind(this)}getClassName(){let e="button primary";return this.props.warning&&(e+=" warning"),this.props.disabled&&(e+=" disabled"),this.props.processing&&(e+=" processing"),this.props.big&&(e+=" big"),this.props.medium&&(e+=" medium"),this.props.fullWidth&&(e+=" full-width"),e}render(){return r.createElement("input",{type:"submit",className:this.getClassName(),disabled:this.props.disabled,value:this.props.value||"Save"})}}R.defaultProps={warning:!1},R.propTypes={processing:m().bool,disabled:m().bool,value:m().string,warning:m().bool,big:m().bool,medium:m().bool,fullWidth:m().bool};const M=R;var k=s(8831);class O extends r.Component{constructor(e){super(e),this.state=this.defaultState,this.createInputRefs(),this.bindEventHandlers()}get defaultState(){return{loading:!0,processing:!1,username:"",usernameError:null,agreedTerms:!1,agreedTermsError:null,hasAlreadyBeenValidated:!1}}componentDidMount(){null!==this.props.context.siteSettings&&this.setState({loading:!1},(()=>{this.focusUsernameElement()}))}async componentDidUpdate(){this.state.loading&&null!==this.props.context.siteSettings&&this.setState({loading:!1},(()=>{this.focusUsernameElement()}))}focusUsernameElement(){this.isFocusOnBrowserExtension()||this.usernameRef.current.focus()}isFocusOnBrowserExtension(){const e=document.activeElement;return!!e&&"iframe"===e.tagName.toLowerCase()}bindEventHandlers(){this.handleInputChange=this.handleInputChange.bind(this),this.handleFormSubmit=this.handleFormSubmit.bind(this),this.handleUsernameInputOnKeyUp=this.handleUsernameInputOnKeyUp.bind(this)}createInputRefs(){this.usernameRef=r.createRef()}async handleInputChange(e){const t=e.target,s="checkbox"===t.type?t.checked:t.value,r=t.name;await this.setState({[r]:s}),this.state.hasAlreadyBeenValidated&&await this.validate()}handleUsernameInputOnKeyUp(){if(this.state.hasAlreadyBeenValidated){const e=this.validateUsernameInput();this.setState(e)}}async handleFormSubmit(e){if(e.preventDefault(),await this.setState({hasAlreadyBeenValidated:!0}),!this.state.processing){if(await this.toggleProcessing(),await this.validate(),this.hasValidationError())return void await this.toggleProcessing();this.props.apiTriageContext.onTriageRequested(this.state.username.trim())}}async toggleProcessing(){const e=this.state.processing;return this.setState({processing:!e})}async validate(){return await Promise.all([this.validateUsernameInput(),this.validateAgreedTerms()]),this.hasValidationError()}async validateUsernameInput(){let e=null;const t=this.state.username.trim();return t.length?this.isEmail(t)||(e=this.translate("Please enter a valid email address.")):e=this.translate("A username is required."),this.setState({username:t,usernameError:e})}isEmail(e){return U()("^[\\p{L}0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[\\p{L}0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[_\\p{L}0-9][-_\\p{L}0-9]*\\.)*(?:[\\p{L}0-9][-\\p{L}0-9]{0,62})\\.(?:(?:[a-z]{2}\\.)?[a-z]{2,})$").test(e)}async validateAgreedTerms(){let e=!1;const t=this.privacyLink||this.termsLink,s=this.state.agreedTerms;return t&&!s&&(e=!0),this.setState({agreedTermsError:e})}hasValidationError(){return null!==this.state.usernameError||this.state.agreedTermsError}hasAllInputDisabled(){return this.state.processing||this.state.loading}get privacyLink(){return!!this.props.context.siteSettings&&this.props.context.siteSettings.privacyLink}get termsLink(){return!!this.props.context.siteSettings&&this.props.context.siteSettings.termsLink}get translate(){return this.props.t}render(){return r.createElement("div",{className:"enter-username"},r.createElement("h1",null,r.createElement(S.c,null,"Please enter your email to continue.")),r.createElement("form",{acceptCharset:"utf-8",onSubmit:this.handleFormSubmit,noValidate:!0},r.createElement("div",{className:"input text required "+(this.state.usernameError?"error":"")},r.createElement("label",{htmlFor:"username"},r.createElement(S.c,null,"Email")),r.createElement("input",{id:"username-input",type:"text",ref:this.usernameRef,name:"username",value:this.state.username,onKeyUp:this.handleUsernameInputOnKeyUp,onChange:this.handleInputChange,placeholder:this.translate("you@organization.com"),required:"required",disabled:this.hasAllInputDisabled()}),this.state.usernameError&&r.createElement("div",{className:"error-message"},this.state.usernameError)),(this.privacyLink||this.termsLink)&&r.createElement("div",{className:"input checkbox "+(this.state.agreedTermsError?"error":"")},r.createElement("input",{type:"checkbox",name:"agreedTerms",value:this.state.agreedTerms,onChange:this.handleInputChange,id:"checkbox-terms",disabled:this.hasAllInputDisabled()}),r.createElement("label",{htmlFor:"checkbox-terms"},(this.privacyLink||this.termsLink)&&r.createElement("span",null,this.termsLink&&!this.privacyLink&&r.createElement(S.c,null,"I accept the ",r.createElement("a",{href:this.termsLink,target:"_blank",rel:"noopener noreferrer"},"terms")),!this.termsLink&&this.privacyLink&&r.createElement(S.c,null,"I accept the ",r.createElement("a",{href:this.privacyLink,target:"_blank",rel:"noopener noreferrer"},"privacy policy")),this.termsLink&&this.privacyLink&&r.createElement(S.c,null,"I accept the ",r.createElement("a",{href:this.termsLink,target:"_blank",rel:"noopener noreferrer"},"terms")," and the ",r.createElement("a",{href:this.privacyLink,target:"_blank",rel:"noopener noreferrer"},"privacy policy"))))),r.createElement("div",{className:"form-actions"},r.createElement(M,{disabled:this.hasAllInputDisabled(),big:!0,processing:this.state.processing,fullWidth:!0,value:this.translate("Next")}))))}}O.propTypes={apiTriageContext:m().object,context:m().any,t:m().func};const I=l(T((0,k.Z)("common")(O)));class B extends r.Component{constructor(e){super(e),this.state=this.defaultState,this.createInputRefs(),this.bindEventHandlers()}componentDidMount(){this.setState({loading:!1},(()=>{this.firstnameRef.current.focus()}))}get defaultState(){return{loading:!0,processing:!1,firstname:"",firstnameError:null,lastname:"",lastnameError:null,hasAlreadyBeenValidated:!1}}bindEventHandlers(){this.handleInputChange=this.handleInputChange.bind(this),this.handleFormSubmit=this.handleFormSubmit.bind(this),this.handleFirstnameInputOnKeyUp=this.handleFirstnameInputOnKeyUp.bind(this),this.handleLastnameInputOnKeyUp=this.handleLastnameInputOnKeyUp.bind(this)}createInputRefs(){this.firstnameRef=r.createRef(),this.lastnameRef=r.createRef()}handleInputChange(e){const t=e.target,s=t.value,r=t.name;this.setState({[r]:s})}handleFirstnameInputOnKeyUp(){if(this.state.hasAlreadyBeenValidated){const e=this.validateFirstnameInput();this.setState(e)}}handleLastnameInputOnKeyUp(){if(this.state.hasAlreadyBeenValidated){const e=this.validateLastnameInput();this.setState(e)}}async handleFormSubmit(e){if(e.preventDefault(),await this.setState({hasAlreadyBeenValidated:!0}),!this.state.processing){if(await this.toggleProcessing(),await this.validate(),this.hasValidationError())return await this.toggleProcessing(),void this.focusFirstFieldError();await this.props.apiTriageContext.onRegistrationRequested(this.state.firstname,this.state.lastname)}}async toggleProcessing(){const e=this.state.processing;return this.setState({processing:!e})}async validate(){return await Promise.all([this.validateFirstnameInput(),this.validateLastnameInput()]),this.hasValidationError()}async validateFirstnameInput(){let e=null;return this.state.firstname.trim().length||(e=this.translate("A first name is required.")),this.setState({firstnameError:e})}async validateLastnameInput(){let e=null;return this.state.lastname.trim().length||(e=this.translate("A last name is required.")),this.setState({lastnameError:e})}focusFirstFieldError(){this.state.firstnameError?this.firstnameRef.current.focus():this.state.lastnameError&&this.lastnameRef.current.focus()}hasValidationError(){return null!==this.state.firstnameError||null!==this.state.lastnameError}hasAllInputDisabled(){return this.state.processing||this.state.loading}get translate(){return this.props.t}render(){return r.createElement("div",{className:"enter-name"},r.createElement("h1",null,r.createElement(S.c,null,"New here? Enter your name to get started.")),r.createElement("form",{acceptCharset:"utf-8",onSubmit:this.handleFormSubmit,noValidate:!0},r.createElement("div",{className:"input text required "+(this.state.firstnameError?"error":"")},r.createElement("label",{htmlFor:"firstname"},r.createElement(S.c,null,"First Name")),r.createElement("input",{id:"firstname-input",type:"text",name:"firstname",ref:this.firstnameRef,value:this.state.firstname,onKeyUp:this.handleFirstnameInputOnKeyUp,onChange:this.handleInputChange,disabled:this.hasAllInputDisabled(),placeholder:this.translate("first name"),required:"required"}),this.state.firstnameError&&r.createElement("div",{className:"error-message"},this.state.firstnameError)),r.createElement("div",{className:"input text required "+(this.state.lastnameError?"error":"")},r.createElement("label",{htmlFor:"lastname"},r.createElement(S.c,null,"Last Name")),r.createElement("input",{id:"lastname-input",type:"text",name:"lastname",ref:this.lastnameRef,value:this.state.lastname,onKeyUp:this.handleLastnameInputOnKeyUp,onChange:this.handleInputChange,disabled:this.hasAllInputDisabled(),placeholder:this.translate("last name"),required:"required"}),this.state.lastnameError&&r.createElement("div",{className:"error-message"},this.state.lastnameError)),r.createElement("div",{className:"form-actions"},r.createElement(M,{disabled:this.hasAllInputDisabled(),big:!0,fullWidth:!0,processing:this.state.processing,value:this.translate("Sign up")}),r.createElement("a",{href:`${this.props.context.trustedDomain}/auth/login?locale=${this.props.context.locale}`},r.createElement(S.c,null,"I already have an account")))))}}B.propTypes={apiTriageContext:m().object,context:m().any,t:m().func};const N=l(T((0,k.Z)("common")(B)));class P extends r.Component{get translate(){return this.props.t}render(){return r.createElement("div",{className:"email-sent-instructions"},r.createElement("div",{className:"email-sent-bg"}),r.createElement("h1",null,r.createElement(S.c,null,"Check your mailbox!")),r.createElement("p",null,r.createElement(S.c,null,"We sent you a link to verify your email."),r.createElement("br",null),r.createElement(S.c,null,"Check your spam folder if you do not hear from us after a while.")))}}P.propTypes={t:m().func};const _=(0,k.Z)("common")(P);class V extends r.Component{get translate(){return this.props.t}render(){return r.createElement("div",{className:"setup-error"},r.createElement("h1",null,r.createElement(S.c,null,"Access to this service requires an invitation.")),r.createElement("p",null,r.createElement(S.c,null,"This email is not associated with any approved users on this domain.")," ",r.createElement(S.c,null,"Please contact your administrator to request an invitation link.")),r.createElement("div",{className:"form-actions"},r.createElement("a",{href:`${this.props.context.trustedDomain}/users/recover`,className:"button primary big full-width",role:"button"},r.createElement(S.c,null,"Try with another email"))))}}V.propTypes={context:m().any,t:m().func};const H=l((0,k.Z)("common")(V));class F extends r.Component{componentDidMount(){this.initializeTriage()}initializeTriage(){setTimeout((()=>this.props.apiTriageContext.onInitializeTriageRequested()),1e3)}render(){switch(this.props.apiTriageContext.state){case C.USERNAME_STATE:return r.createElement(I,null);case C.CHECK_MAILBOX_STATE:return r.createElement(_,null);case C.NAME_STATE:return r.createElement(N,null);case C.ERROR_STATE:return r.createElement(H,null);default:return r.createElement(L,null)}}}F.propTypes={apiTriageContext:m().object};const D=T(F),j=(e,t)=>t.split(".").reduce(((e,t)=>void 0===e?e:e[t]),e),$=(e,t)=>{if(void 0===e||"string"!=typeof e||!e.length)return!1;if((t=t||{}).whitelistedProtocols&&!Array.isArray(t.whitelistedProtocols))throw new TypeError("The whitelistedProtocols should be an array of string.");if(t.defaultProtocol&&"string"!=typeof t.defaultProtocol)throw new TypeError("The defaultProtocol should be a string.");const s=t.whitelistedProtocols||[K.HTTP,K.HTTPS],r=[K.JAVASCRIPT],n=t.defaultProtocol||"";!/^((?!:\/\/).)*:\/\//.test(e)&&n&&(e=`${n}//${e}`);try{const t=new URL(e);return!r.includes(t.protocol)&&!!s.includes(t.protocol)&&t.href}catch(e){return!1}},K={FTP:"http:",FTPS:"https:",HTTP:"http:",HTTPS:"https:",JAVASCRIPT:"javascript:",SSH:"ssh:"};class Z{constructor(e){this.settings=e}canIUse(e){let t=!1;const s=`passbolt.plugins.${e}`,r=j(this.settings,s)||null;if(r&&"object"==typeof r){const e=j(r,"enabled");void 0!==e&&!0!==e||(t=!0)}return t}getPluginSettings(e){const t=`passbolt.plugins.${e}`;return j(this.settings,t)}getRememberMeOptions(){return(this.getPluginSettings("rememberMe")||{}).options||{}}get hasRememberMeUntilILogoutOption(){return void 0!==(this.getRememberMeOptions()||{})[-1]}getServerTimezone(){return j(this.settings,"passbolt.app.server_timezone")}get termsLink(){const e=j(this.settings,"passbolt.legal.terms.url");return!!e&&$(e)}get privacyLink(){const e=j(this.settings,"passbolt.legal.privacy_policy.url");return!!e&&$(e)}get registrationPublic(){return!0===j(this.settings,"passbolt.registration.public")}get debug(){return!0===j(this.settings,"app.debug")}get url(){return j(this.settings,"app.url")||""}get version(){return j(this.settings,"app.version.number")}get locale(){return j(this.settings,"app.locale")||Z.DEFAULT_LOCALE.locale}async setLocale(e){this.settings.app.locale=e}get supportedLocales(){return j(this.settings,"passbolt.plugins.locale.options")||Z.DEFAULT_SUPPORTED_LOCALES}get generatorConfiguration(){return j(this.settings,"passbolt.plugins.generator.configuration")}static get DEFAULT_SUPPORTED_LOCALES(){return[Z.DEFAULT_LOCALE]}static get DEFAULT_LOCALE(){return{locale:"en-UK",label:"English"}}}class W extends r.Component{getClassName(){let e=`svg-icon ${this.props.name}`;return this.props.big&&(e+=" icon-only"),this.props.baseline&&(e+=" baseline"),this.props.dim&&(e+=" dim"),e}render(){return r.createElement("span",{className:this.getClassName(),onClick:this.props.onClick},"add"===this.props.name&&r.createElement("svg",{xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false","data-prefix":"fas","data-icon":"plus-circle",role:"img",viewBox:"0 0 512 512"},r.createElement("path",{d:"M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm144 276c0 6.6-5.4 12-12 12h-92v92c0 6.6-5.4 12-12 12h-56c-6.6 0-12-5.4-12-12v-92h-92c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h92v-92c0-6.6 5.4-12 12-12h56c6.6 0 12 5.4 12 12v92h92c6.6 0 12 5.4 12 12v56z"})),"ban"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1440 893q0-161-87-295l-754 753q137 89 297 89 111 0 211.5-43.5t173.5-116.5 116-174.5 43-212.5zm-999 299l755-754q-135-91-300-91-148 0-273 73t-198 199-73 274q0 162 89 299zm1223-299q0 157-61 300t-163.5 246-245 164-298.5 61-298.5-61-245-164-163.5-246-61-300 61-299.5 163.5-245.5 245-164 298.5-61 298.5 61 245 164 163.5 245.5 61 299.5z"})),"camera"===this.props.name&&r.createElement("svg",{width:"2048",height:"1792",viewBox:"0 0 2048 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1024 672q119 0 203.5 84.5t84.5 203.5-84.5 203.5-203.5 84.5-203.5-84.5-84.5-203.5 84.5-203.5 203.5-84.5zm704-416q106 0 181 75t75 181v896q0 106-75 181t-181 75h-1408q-106 0-181-75t-75-181v-896q0-106 75-181t181-75h224l51-136q19-49 69.5-84.5t103.5-35.5h512q53 0 103.5 35.5t69.5 84.5l51 136h224zm-704 1152q185 0 316.5-131.5t131.5-316.5-131.5-316.5-316.5-131.5-316.5 131.5-131.5 316.5 131.5 316.5 316.5 131.5z"})),"caret-right"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1152 896q0 26-19 45l-448 448q-19 19-45 19t-45-19-19-45v-896q0-26 19-45t45-19 45 19l448 448q19 19 19 45z"})),"caret-down"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1408 704q0 26-19 45l-448 448q-19 19-45 19t-45-19l-448-448q-19-19-19-45t19-45 45-19h896q26 0 45 19t19 45z"})),"caret-up"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1408 1216q0 26-19 45t-45 19h-896q-26 0-45-19t-19-45 19-45l448-448q19-19 45-19t45 19l448 448q19 19 19 45z"})),"chevron-left"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1427 301l-531 531 531 531q19 19 19 45t-19 45l-166 166q-19 19-45 19t-45-19l-742-742q-19-19-19-45t19-45l742-742q19-19 45-19t45 19l166 166q19 19 19 45t-19 45z"})),"chevron-right"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1363 877l-742 742q-19 19-45 19t-45-19l-166-166q-19-19-19-45t19-45l531-531-531-531q-19-19-19-45t19-45l166-166q19-19 45-19t45 19l742 742q19 19 19 45t-19 45z"})),"close"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1490 1322q0 40-28 68l-136 136q-28 28-68 28t-68-28l-294-294-294 294q-28 28-68 28t-68-28l-136-136q-28-28-28-68t28-68l294-294-294-294q-28-28-28-68t28-68l136-136q28-28 68-28t68 28l294 294 294-294q28-28 68-28t68 28l136 136q28 28 28 68t-28 68l-294 294 294 294q28 28 28 68z"})),"close-circle"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1277 1122q0-26-19-45l-181-181 181-181q19-19 19-45 0-27-19-46l-90-90q-19-19-46-19-26 0-45 19l-181 181-181-181q-19-19-45-19-27 0-46 19l-90 90q-19 19-19 46 0 26 19 45l181 181-181 181q-19 19-19 45 0 27 19 46l90 90q19 19 46 19 26 0 45-19l181-181 181 181q19 19 45 19 27 0 46-19l90-90q19-19 19-46zm387-226q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"})),"cog"===this.props.name&&r.createElement("svg",{xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false","data-prefix":"fas","data-icon":"cog",viewBox:"0 0 512 512"},r.createElement("path",{fill:"currentColor",d:"M487.4 315.7l-42.6-24.6c4.3-23.2 4.3-47 0-70.2l42.6-24.6c4.9-2.8 7.1-8.6 5.5-14-11.1-35.6-30-67.8-54.7-94.6-3.8-4.1-10-5.1-14.8-2.3L380.8 110c-17.9-15.4-38.5-27.3-60.8-35.1V25.8c0-5.6-3.9-10.5-9.4-11.7-36.7-8.2-74.3-7.8-109.2 0-5.5 1.2-9.4 6.1-9.4 11.7V75c-22.2 7.9-42.8 19.8-60.8 35.1L88.7 85.5c-4.9-2.8-11-1.9-14.8 2.3-24.7 26.7-43.6 58.9-54.7 94.6-1.7 5.4.6 11.2 5.5 14L67.3 221c-4.3 23.2-4.3 47 0 70.2l-42.6 24.6c-4.9 2.8-7.1 8.6-5.5 14 11.1 35.6 30 67.8 54.7 94.6 3.8 4.1 10 5.1 14.8 2.3l42.6-24.6c17.9 15.4 38.5 27.3 60.8 35.1v49.2c0 5.6 3.9 10.5 9.4 11.7 36.7 8.2 74.3 7.8 109.2 0 5.5-1.2 9.4-6.1 9.4-11.7v-49.2c22.2-7.9 42.8-19.8 60.8-35.1l42.6 24.6c4.9 2.8 11 1.9 14.8-2.3 24.7-26.7 43.6-58.9 54.7-94.6 1.5-5.5-.7-11.3-5.6-14.1zM256 336c-44.1 0-80-35.9-80-80s35.9-80 80-80 80 35.9 80 80-35.9 80-80 80z"})),"copy-to-clipboard"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M768 1664h896v-640h-416q-40 0-68-28t-28-68v-416h-384v1152zm256-1440v-64q0-13-9.5-22.5t-22.5-9.5h-704q-13 0-22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h704q13 0 22.5-9.5t9.5-22.5zm256 672h299l-299-299v299zm512 128v672q0 40-28 68t-68 28h-960q-40 0-68-28t-28-68v-160h-544q-40 0-68-28t-28-68v-1344q0-40 28-68t68-28h1088q40 0 68 28t28 68v328q21 13 36 28l408 408q28 28 48 76t20 88z"})),"download"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1344 1344q0-26-19-45t-45-19-45 19-19 45 19 45 45 19 45-19 19-45zm256 0q0-26-19-45t-45-19-45 19-19 45 19 45 45 19 45-19 19-45zm128-224v320q0 40-28 68t-68 28h-1472q-40 0-68-28t-28-68v-320q0-40 28-68t68-28h465l135 136q58 56 136 56t136-56l136-136h464q40 0 68 28t28 68zm-325-569q17 41-14 70l-448 448q-18 19-45 19t-45-19l-448-448q-31-29-14-70 17-39 59-39h256v-448q0-26 19-45t45-19h256q26 0 45 19t19 45v448h256q42 0 59 39z"})),"edit"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M888 1184l116-116-152-152-116 116v56h96v96h56zm440-720q-16-16-33 1l-350 350q-17 17-1 33t33-1l350-350q17-17 1-33zm80 594v190q0 119-84.5 203.5t-203.5 84.5h-832q-119 0-203.5-84.5t-84.5-203.5v-832q0-119 84.5-203.5t203.5-84.5h832q63 0 117 25 15 7 18 23 3 17-9 29l-49 49q-14 14-32 8-23-6-45-6h-832q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113v-126q0-13 9-22l64-64q15-15 35-7t20 29zm-96-738l288 288-672 672h-288v-288zm444 132l-92 92-288-288 92-92q28-28 68-28t68 28l152 152q28 28 28 68t-28 68z"})),"envelope"===this.props.name&&r.createElement("svg",{width:"512",height:"512",viewBox:"0 0 512 512",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{fill:"currentColor",d:"M464 64H48C21.49 64 0 85.49 0 112v288c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V112c0-26.51-21.49-48-48-48zm0 48v40.805c-22.422 18.259-58.168 46.651-134.587 106.49-16.841 13.247-50.201 45.072-73.413 44.701-23.208.375-56.579-31.459-73.413-44.701C106.18 199.465 70.425 171.067 48 152.805V112h416zM48 400V214.398c22.914 18.251 55.409 43.862 104.938 82.646 21.857 17.205 60.134 55.186 103.062 54.955 42.717.231 80.509-37.199 103.053-54.947 49.528-38.783 82.032-64.401 104.947-82.653V400H48z"})," "),"eye-open"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1664 960q-152-236-381-353 61 104 61 225 0 185-131.5 316.5t-316.5 131.5-316.5-131.5-131.5-316.5q0-121 61-225-229 117-381 353 133 205 333.5 326.5t434.5 121.5 434.5-121.5 333.5-326.5zm-720-384q0-20-14-34t-34-14q-125 0-214.5 89.5t-89.5 214.5q0 20 14 34t34 14 34-14 14-34q0-86 61-147t147-61q20 0 34-14t14-34zm848 384q0 34-20 69-140 230-376.5 368.5t-499.5 138.5-499.5-139-376.5-368q-20-35-20-69t20-69q140-229 376.5-368t499.5-139 499.5 139 376.5 368q20 35 20 69z"})),"eye-close"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M555 1335l78-141q-87-63-136-159t-49-203q0-121 61-225-229 117-381 353 167 258 427 375zm389-759q0-20-14-34t-34-14q-125 0-214.5 89.5t-89.5 214.5q0 20 14 34t34 14 34-14 14-34q0-86 61-147t147-61q20 0 34-14t14-34zm363-191q0 7-1 9-106 189-316 567t-315 566l-49 89q-10 16-28 16-12 0-134-70-16-10-16-28 0-12 44-87-143-65-263.5-173t-208.5-245q-20-31-20-69t20-69q153-235 380-371t496-136q89 0 180 17l54-97q10-16 28-16 5 0 18 6t31 15.5 33 18.5 31.5 18.5 19.5 11.5q16 10 16 27zm37 447q0 139-79 253.5t-209 164.5l280-502q8 45 8 84zm448 128q0 35-20 69-39 64-109 145-150 172-347.5 267t-419.5 95l74-132q212-18 392.5-137t301.5-307q-115-179-282-294l63-112q95 64 182.5 153t144.5 184q20 34 20 69z"})),"external-link"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1408 928v-480q0-26-19-45t-45-19h-480q-42 0-59 39-17 41 14 70l144 144-534 534q-19 19-19 45t19 45l102 102q19 19 45 19t45-19l534-534 144 144q18 19 45 19 12 0 25-5 39-17 39-59zm256-512v960q0 119-84.5 203.5t-203.5 84.5h-960q-119 0-203.5-84.5t-84.5-203.5v-960q0-119 84.5-203.5t203.5-84.5h960q119 0 203.5 84.5t84.5 203.5z"})),"folder"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1728 608v704q0 92-66 158t-158 66h-1216q-92 0-158-66t-66-158v-960q0-92 66-158t158-66h320q92 0 158 66t66 158v32h672q92 0 158 66t66 158z"})),"folder-shared"===this.props.name&&r.createElement("svg",{viewBox:"2 2 20 20",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M20 6h-8l-2-2H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm-5 3c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm4 8h-8v-1c0-1.33 2.67-2 4-2s4 .67 4 2v1z"})),"heart"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M896 1664q-26 0-44-18l-624-602q-10-8-27.5-26t-55.5-65.5-68-97.5-53.5-121-23.5-138q0-220 127-344t351-124q62 0 126.5 21.5t120 58 95.5 68.5 76 68q36-36 76-68t95.5-68.5 120-58 126.5-21.5q224 0 351 124t127 344q0 221-229 450l-623 600q-18 18-44 18z"})),"heart-o"===this.props.name&&r.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 512 512"},r.createElement("path",{d:"M458.4 64.3C400.6 15.7 311.3 23 256 79.3 200.7 23 111.4 15.6 53.6 64.3-21.6 127.6-10.6 230.8 43 285.5l175.4 178.7c10 10.2 23.4 15.9 37.6 15.9 14.3 0 27.6-5.6 37.6-15.8L469 285.6c53.5-54.7 64.7-157.9-10.6-221.3zm-23.6 187.5L259.4 430.5c-2.4 2.4-4.4 2.4-6.8 0L77.2 251.8c-36.5-37.2-43.9-107.6 7.3-150.7 38.9-32.7 98.9-27.8 136.5 10.5l35 35.7 35-35.7c37.8-38.5 97.8-43.2 136.5-10.6 51.1 43.1 43.5 113.9 7.3 150.8z"})),"heartbeat"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1280 1024h305q-5 6-10 10.5t-9 7.5l-3 4-623 600q-18 18-44 18t-44-18l-624-602q-5-2-21-20h369q22 0 39.5-13.5t22.5-34.5l70-281 190 667q6 20 23 33t39 13q21 0 38-13t23-33l146-485 56 112q18 35 57 35zm512-428q0 145-103 300h-369l-111-221q-8-17-25.5-27t-36.5-8q-45 5-56 46l-129 430-196-686q-6-20-23.5-33t-39.5-13-39 13.5-22 34.5l-116 464h-423q-103-155-103-300 0-220 127-344t351-124q62 0 126.5 21.5t120 58 95.5 68.5 76 68q36-36 76-68t95.5-68.5 120-58 126.5-21.5q224 0 351 124t127 344z"})),"info-circle"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1152 1376v-160q0-14-9-23t-23-9h-96v-512q0-14-9-23t-23-9h-320q-14 0-23 9t-9 23v160q0 14 9 23t23 9h96v320h-96q-14 0-23 9t-9 23v160q0 14 9 23t23 9h448q14 0 23-9t9-23zm-128-896v-160q0-14-9-23t-23-9h-192q-14 0-23 9t-9 23v160q0 14 9 23t23 9h192q14 0 23-9t9-23zm640 416q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"})),"key"===this.props.name&&r.createElement("svg",{width:"512",height:"512",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 512 512"},r.createElement("path",{fill:"currentColor",d:"M512 176.001C512 273.203 433.202 352 336 352c-11.22 0-22.19-1.062-32.827-3.069l-24.012 27.014A23.999 23.999 0 0 1 261.223 384H224v40c0 13.255-10.745 24-24 24h-40v40c0 13.255-10.745 24-24 24H24c-13.255 0-24-10.745-24-24v-78.059c0-6.365 2.529-12.47 7.029-16.971l161.802-161.802C163.108 213.814 160 195.271 160 176 160 78.798 238.797.001 335.999 0 433.488-.001 512 78.511 512 176.001zM336 128c0 26.51 21.49 48 48 48s48-21.49 48-48-21.49-48-48-48-48 21.49-48 48z"})),"link"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1520 1216q0-40-28-68l-208-208q-28-28-68-28-42 0-72 32 3 3 19 18.5t21.5 21.5 15 19 13 25.5 3.5 27.5q0 40-28 68t-68 28q-15 0-27.5-3.5t-25.5-13-19-15-21.5-21.5-18.5-19q-33 31-33 73 0 40 28 68l206 207q27 27 68 27 40 0 68-26l147-146q28-28 28-67zm-703-705q0-40-28-68l-206-207q-28-28-68-28-39 0-68 27l-147 146q-28 28-28 67 0 40 28 68l208 208q27 27 68 27 42 0 72-31-3-3-19-18.5t-21.5-21.5-15-19-13-25.5-3.5-27.5q0-40 28-68t68-28q15 0 27.5 3.5t25.5 13 19 15 21.5 21.5 18.5 19q33-31 33-73zm895 705q0 120-85 203l-147 146q-83 83-203 83-121 0-204-85l-206-207q-83-83-83-203 0-123 88-209l-88-88q-86 88-208 88-120 0-204-84l-208-208q-84-84-84-204t85-203l147-146q83-83 203-83 121 0 204 85l206 207q83 83 83 203 0 123-88 209l88 88q86-88 208-88 120 0 204 84l208 208q84 84 84 204z"})),"lock"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M640 768h512v-192q0-106-75-181t-181-75-181 75-75 181v192zm832 96v576q0 40-28 68t-68 28h-960q-40 0-68-28t-28-68v-576q0-40 28-68t68-28h32v-192q0-184 132-316t316-132 316 132 132 316v192h32q40 0 68 28t28 68z"})),"lock-open"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1376 768q40 0 68 28t28 68v576q0 40-28 68t-68 28h-960q-40 0-68-28t-28-68v-576q0-40 28-68t68-28h32v-320q0-185 131.5-316.5t316.5-131.5 316.5 131.5 131.5 316.5q0 26-19 45t-45 19h-64q-26 0-45-19t-19-45q0-106-75-181t-181-75-181 75-75 181v320h736z"})),"magic-wand"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1254 581l293-293-107-107-293 293zm447-293q0 27-18 45l-1286 1286q-18 18-45 18t-45-18l-198-198q-18-18-18-45t18-45l1286-1286q18-18 45-18t45 18l198 198q18 18 18 45zm-1351-190l98 30-98 30-30 98-30-98-98-30 98-30 30-98zm350 162l196 60-196 60-60 196-60-196-196-60 196-60 60-196zm930 478l98 30-98 30-30 98-30-98-98-30 98-30 30-98zm-640-640l98 30-98 30-30 98-30-98-98-30 98-30 30-98z"})),"plus-circle"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1344 960v-128q0-26-19-45t-45-19h-256v-256q0-26-19-45t-45-19h-128q-26 0-45 19t-19 45v256h-256q-26 0-45 19t-19 45v128q0 26 19 45t45 19h256v256q0 26 19 45t45 19h128q26 0 45-19t19-45v-256h256q26 0 45-19t19-45zm320-64q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"})),"plus-square"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 448 512",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48zm-32 252c0 6.6-5.4 12-12 12h-92v92c0 6.6-5.4 12-12 12h-56c-6.6 0-12-5.4-12-12v-92H92c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h92v-92c0-6.6 5.4-12 12-12h56c6.6 0 12 5.4 12 12v92h92c6.6 0 12 5.4 12 12v56z"})),"filter"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 512 512",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M487.976 0H24.028C2.71 0-8.047 25.866 7.058 40.971L192 225.941V432c0 7.831 3.821 15.17 10.237 19.662l80 55.98C298.02 518.69 320 507.493 320 487.98V225.941l184.947-184.97C520.021 25.896 509.338 0 487.976 0z"})),"life-ring"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 512 512",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M256 504c136.967 0 248-111.033 248-248S392.967 8 256 8 8 119.033 8 256s111.033 248 248 248zm-103.398-76.72l53.411-53.411c31.806 13.506 68.128 13.522 99.974 0l53.411 53.411c-63.217 38.319-143.579 38.319-206.796 0zM336 256c0 44.112-35.888 80-80 80s-80-35.888-80-80 35.888-80 80-80 80 35.888 80 80zm91.28 103.398l-53.411-53.411c13.505-31.806 13.522-68.128 0-99.974l53.411-53.411c38.319 63.217 38.319 143.579 0 206.796zM359.397 84.72l-53.411 53.411c-31.806-13.505-68.128-13.522-99.973 0L152.602 84.72c63.217-38.319 143.579-38.319 206.795 0zM84.72 152.602l53.411 53.411c-13.506 31.806-13.522 68.128 0 99.974L84.72 359.398c-38.319-63.217-38.319-143.579 0-206.796z"})),"plug"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 384 512",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M320,32a32,32,0,0,0-64,0v96h64Zm48,128H16A16,16,0,0,0,0,176v32a16,16,0,0,0,16,16H32v32A160.07,160.07,0,0,0,160,412.8V512h64V412.8A160.07,160.07,0,0,0,352,256V224h16a16,16,0,0,0,16-16V176A16,16,0,0,0,368,160ZM128,32a32,32,0,0,0-64,0v96h64Z"})),"printer"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M448 1536h896v-256h-896v256zm0-640h896v-384h-160q-40 0-68-28t-28-68v-160h-640v640zm1152 64q0-26-19-45t-45-19-45 19-19 45 19 45 45 19 45-19 19-45zm128 0v416q0 13-9.5 22.5t-22.5 9.5h-224v160q0 40-28 68t-68 28h-960q-40 0-68-28t-28-68v-160h-224q-13 0-22.5-9.5t-9.5-22.5v-416q0-79 56.5-135.5t135.5-56.5h64v-544q0-40 28-68t68-28h672q40 0 88 20t76 48l152 152q28 28 48 76t20 88v256h64q79 0 135.5 56.5t56.5 135.5z"})),"refresh"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 512 512",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M370.72 133.28C339.458 104.008 298.888 87.962 255.848 88c-77.458.068-144.328 53.178-162.791 126.85-1.344 5.363-6.122 9.15-11.651 9.15H24.103c-7.498 0-13.194-6.807-11.807-14.176C33.933 94.924 134.813 8 256 8c66.448 0 126.791 26.136 171.315 68.685L463.03 40.97C478.149 25.851 504 36.559 504 57.941V192c0 13.255-10.745 24-24 24H345.941c-21.382 0-32.09-25.851-16.971-40.971l41.75-41.749zM32 296h134.059c21.382 0 32.09 25.851 16.971 40.971l-41.75 41.75c31.262 29.273 71.835 45.319 114.876 45.28 77.418-.07 144.315-53.144 162.787-126.849 1.344-5.363 6.122-9.15 11.651-9.15h57.304c7.498 0 13.194 6.807 11.807 14.176C478.067 417.076 377.187 504 256 504c-66.448 0-126.791-26.136-171.315-68.685L48.97 471.03C33.851 486.149 8 475.441 8 454.059V320c0-13.255 10.745-24 24-24z"})),"question-circle"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1024 1376v-192q0-14-9-23t-23-9h-192q-14 0-23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23-9t9-23zm256-672q0-88-55.5-163t-138.5-116-170-41q-243 0-371 213-15 24 8 42l132 100q7 6 19 6 16 0 25-12 53-68 86-92 34-24 86-24 48 0 85.5 26t37.5 59q0 38-20 61t-68 45q-63 28-115.5 86.5t-52.5 125.5v36q0 14 9 23t23 9h192q14 0 23-9t9-23q0-19 21.5-49.5t54.5-49.5q32-18 49-28.5t46-35 44.5-48 28-60.5 12.5-81zm384 192q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"})),"search"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1216 832q0-185-131.5-316.5t-316.5-131.5-316.5 131.5-131.5 316.5 131.5 316.5 316.5 131.5 316.5-131.5 131.5-316.5zm512 832q0 52-38 90t-90 38q-54 0-90-38l-343-342q-179 124-399 124-143 0-273.5-55.5t-225-150-150-225-55.5-273.5 55.5-273.5 150-225 225-150 273.5-55.5 273.5 55.5 225 150 150 225 55.5 273.5q0 220-124 399l343 343q37 37 37 90z"})),"share"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1764 11q33 24 27 64l-256 1536q-5 29-32 45-14 8-31 8-11 0-24-5l-453-185-242 295q-18 23-49 23-13 0-22-4-19-7-30.5-23.5t-11.5-36.5v-349l864-1059-1069 925-395-162q-37-14-40-55-2-40 32-59l1664-960q15-9 32-9 20 0 36 11z"})),"star"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1728 647q0 22-26 48l-363 354 86 500q1 7 1 20 0 21-10.5 35.5t-30.5 14.5q-19 0-40-12l-449-236-449 236q-22 12-40 12-21 0-31.5-14.5t-10.5-35.5q0-6 2-20l86-500-364-354q-25-27-25-48 0-37 56-46l502-73 225-455q19-41 49-41t49 41l225 455 502 73q56 9 56 46z"})),"save"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 448 512",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M433.941 129.941l-83.882-83.882A48 48 0 0 0 316.118 32H48C21.49 32 0 53.49 0 80v352c0 26.51 21.49 48 48 48h352c26.51 0 48-21.49 48-48V163.882a48 48 0 0 0-14.059-33.941zM272 80v80H144V80h128zm122 352H54a6 6 0 0 1-6-6V86a6 6 0 0 1 6-6h42v104c0 13.255 10.745 24 24 24h176c13.255 0 24-10.745 24-24V83.882l78.243 78.243a6 6 0 0 1 1.757 4.243V426a6 6 0 0 1-6 6zM224 232c-48.523 0-88 39.477-88 88s39.477 88 88 88 88-39.477 88-88-39.477-88-88-88zm0 128c-22.056 0-40-17.944-40-40s17.944-40 40-40 40 17.944 40 40-17.944 40-40 40z"})),"trash"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M704 1376v-704q0-14-9-23t-23-9h-64q-14 0-23 9t-9 23v704q0 14 9 23t23 9h64q14 0 23-9t9-23zm256 0v-704q0-14-9-23t-23-9h-64q-14 0-23 9t-9 23v704q0 14 9 23t23 9h64q14 0 23-9t9-23zm256 0v-704q0-14-9-23t-23-9h-64q-14 0-23 9t-9 23v704q0 14 9 23t23 9h64q14 0 23-9t9-23zm-544-992h448l-48-117q-7-9-17-11h-317q-10 2-17 11zm928 32v64q0 14-9 23t-23 9h-96v948q0 83-47 143.5t-113 60.5h-832q-66 0-113-58.5t-47-141.5v-952h-96q-14 0-23-9t-9-23v-64q0-14 9-23t23-9h309l70-167q15-37 54-63t79-26h320q40 0 79 26t54 63l70 167h309q14 0 23 9t9 23z"})),"upload"===this.props.name&&r.createElement("svg",{width:"2048",height:"1792",viewBox:"0 0 2048 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1344 864q0-14-9-23l-352-352q-9-9-23-9t-23 9l-351 351q-10 12-10 24 0 14 9 23t23 9h224v352q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5-9.5t9.5-22.5v-352h224q13 0 22.5-9.5t9.5-22.5zm640 288q0 159-112.5 271.5t-271.5 112.5h-1088q-185 0-316.5-131.5t-131.5-316.5q0-130 70-240t188-165q-2-30-2-43 0-212 150-362t362-150q156 0 285.5 87t188.5 231q71-62 166-62 106 0 181 75t75 181q0 76-41 138 130 31 213.5 135.5t83.5 238.5z"})),"upload-a"===this.props.name&&r.createElement("svg",{"aria-hidden":"true",focusable:"false","data-prefix":"fas","data-icon":"upload",className:"svg-inline--fa fa-upload fa-w-16",role:"img",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 512 512"},r.createElement("path",{fill:"currentColor",d:"M296 384h-80c-13.3 0-24-10.7-24-24V192h-87.7c-17.8 0-26.7-21.5-14.1-34.1L242.3 5.7c7.5-7.5 19.8-7.5 27.3 0l152.2 152.2c12.6 12.6 3.7 34.1-14.1 34.1H320v168c0 13.3-10.7 24-24 24zm216-8v112c0 13.3-10.7 24-24 24H24c-13.3 0-24-10.7-24-24V376c0-13.3 10.7-24 24-24h136v8c0 30.9 25.1 56 56 56h80c30.9 0 56-25.1 56-56v-8h136c13.3 0 24 10.7 24 24zm-124 88c0-11-9-20-20-20s-20 9-20 20 9 20 20 20 20-9 20-20zm64 0c0-11-9-20-20-20s-20 9-20 20 9 20 20 20 20-9 20-20z"})),"user"===this.props.name&&r.createElement("svg",{width:"448",height:"512",viewBox:"0 0 448 512",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{fill:"currentColor",d:"M224 256c70.7 0 128-57.3 128-128S294.7 0 224 0 96 57.3 96 128s57.3 128 128 128zm89.6 32h-16.7c-22.2 10.2-46.9 16-72.9 16s-50.6-5.8-72.9-16h-16.7C60.2 288 0 348.2 0 422.4V464c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48v-41.6c0-74.2-60.2-134.4-134.4-134.4z",className:""})),"users"===this.props.name&&r.createElement("svg",{width:"640",height:"512",viewBox:"0 0 640 512",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{fill:"currentColor",d:"M96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm448 0c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm32 32h-64c-17.6 0-33.5 7.1-45.1 18.6 40.3 22.1 68.9 62 75.1 109.4h66c17.7 0 32-14.3 32-32v-32c0-35.3-28.7-64-64-64zm-256 0c61.9 0 112-50.1 112-112S381.9 32 320 32 208 82.1 208 144s50.1 112 112 112zm76.8 32h-8.3c-20.8 10-43.9 16-68.5 16s-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h288c26.5 0 48-21.5 48-48v-28.8c0-63.6-51.6-115.2-115.2-115.2zm-223.7-13.4C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z"})),"warning"===this.props.name&&r.createElement("svg",{width:"1792",height:"1792",viewBox:"0 0 1792 1792",xmlns:"http://www.w3.org/2000/svg"},r.createElement("path",{d:"M1024 1375v-190q0-14-9.5-23.5t-22.5-9.5h-192q-13 0-22.5 9.5t-9.5 23.5v190q0 14 9.5 23.5t22.5 9.5h192q13 0 22.5-9.5t9.5-23.5zm-2-374l18-459q0-12-10-19-13-11-24-11h-220q-11 0-24 11-10 7-10 21l17 457q0 10 10 16.5t24 6.5h185q14 0 23.5-6.5t10.5-16.5zm-14-934l768 1408q35 63-2 126-17 29-46.5 46t-63.5 17h-1536q-34 0-63.5-17t-46.5-46q-37-63-2-126l768-1408q17-31 47-49t65-18 65 18 47 49z"})))}}W.defaultProps={big:!1,baseline:!1,dim:!1,onClick:()=>{}},W.propTypes={name:m().string,big:m().bool,dim:m().bool,baseline:m().bool,onClick:m().func};const X=W;function G(){return G=Object.assign||function(e){for(var t=1;te.locale)):[this.locale]}get locale(){return this.props.context.locale}async componentDidUpdate(e){await this.handleLocaleChange(e.context.locale)}async handleLocaleChange(e){this.locale!==e&&await Y.Z.changeLanguage(this.locale)}get isReady(){return this.state.ready}render(){return r.createElement(r.Fragment,null,this.isReady&&this.props.children)}}se.propTypes={context:m().any,loadingPath:m().any,children:m().any};const re=l(se);class ne extends r.Component{constructor(e){super(e),this.state=this.defaultState,this.bindHandlers()}async componentDidMount(){await this.initLocale()}async componentDidUpdate(e){await this.handleLocaleChange(e.context.locale)}async handleLocaleChange(e){this.props.context.locale!==e&&await this.setState({locale:this.props.context.locale})}get defaultState(){return{loading:!0,locale:null,processing:!1}}get areActionsAllowed(){return!this.state.processing}bindHandlers(){this.handleLocaleInputChange=this.handleLocaleInputChange.bind(this)}async handleLocaleInputChange(e){const t=e.target.value;await this.updateLocale(t)}async updateLocale(e){await this.toggleProcessing(),await this.props.context.onUpdateLocaleRequested(e),await this.toggleProcessing()}async initLocale(){await this.setState({locale:this.props.context.locale,loading:!1})}async toggleProcessing(){const e=this.state.processing;return this.setState({processing:!e})}isLoading(){return this.state.loading}render(){return r.createElement(r.Fragment,null,!this.isLoading()&&r.createElement("div",{className:"input select locale"},r.createElement("select",{id:"user-locale-input",name:"locale",value:this.state.locale,disabled:!this.areActionsAllowed,onChange:this.handleLocaleInputChange},this.props.context.siteSettings.supportedLocales.map((e=>r.createElement("option",{key:e.locale,value:e.locale},e.label))))))}}ne.propTypes={context:m().any};const ae=l(ne);class ie extends r.Component{get statesToHideLocaleSwitch(){return[C.INITIAL_STATE]}get mustDisplayLocaleSwitch(){return!this.statesToHideLocaleSwitch.includes(this.props.apiTriageContext.state)}render(){return r.createElement(r.Fragment,null,this.mustDisplayLocaleSwitch&&r.createElement(ae,null))}}ie.propTypes={apiTriageContext:m().any};const le=T(ie);class oe extends r.Component{constructor(e){super(e),this.state=this.defaultState}get defaultState(){return{siteSettings:null,trustedDomain:this.baseUrl,getApiClientOptions:this.getApiClientOptions.bind(this),locale:null,onUpdateLocaleRequested:this.onUpdateLocaleRequested.bind(this)}}async componentDidMount(){await this.getSiteSettings(),this.initLocale()}get baseUrl(){const e=document.getElementsByTagName("base")&&document.getElementsByTagName("base")[0];return e?e.attributes.href.value.replace(/\/*$/g,""):(console.error("Unable to retrieve the page base tag"),"")}getApiClientOptions(){return(new c).setBaseUrl(this.state.trustedDomain).setCsrfToken(this.getCsrfToken())}getCsrfToken(){const e=document.cookie;if(!e)return;const t=e.split("; ");if(!t)return;const s=t.find((e=>e.startsWith("csrfToken")));if(!s)return;const r=s.split("=");return r&&2===r.length?r[1]:void 0}async getSiteSettings(){const e=this.getApiClientOptions().setResourceName("settings"),t=new E(e),{body:s}=await t.findAll(),r=new Z(s);await this.setState({siteSettings:r})}initLocale(){const e=this.getUrlLocale()||this.getBrowserLocale()||this.getBrowserSimilarLocale()||this.state.siteSettings.locale;this.setState({locale:e}),this.setUrlLocale(e)}getUrlLocale(){const e=new URL(window.location.href).searchParams.get("locale");if(e){const t=this.state.siteSettings.supportedLocales.find((t=>e===t.locale));if(t)return t.locale}}getBrowserLocale(){const e=this.state.siteSettings.supportedLocales.find((e=>navigator.language===e.locale));if(e)return e.locale}getBrowserSimilarLocale(){const e=navigator.language.split("-")[0],t=this.state.siteSettings.supportedLocales.find((t=>e===t.locale.split("-")[0]));if(t)return t.locale}async onUpdateLocaleRequested(e){await this.setState({locale:e}),this.setUrlLocale(e)}setUrlLocale(e){const t=new URL(window.location.href);t.searchParams.set("locale",e),window.history.replaceState(null,null,t)}isReady(){return null!==this.state.siteSettings&&null!==this.state.locale}render(){return r.createElement(o.Provider,{value:this.state},this.isReady()&&r.createElement(re,{loadingPath:`${this.state.trustedDomain}/locales/{{lng}}/{{ns}}.json`},r.createElement(x,null,r.createElement("div",{id:"container",className:"container page login"},r.createElement("div",{className:"content"},r.createElement("div",{className:"header"},r.createElement("div",{className:"logo"},r.createElement("span",{className:"visually-hidden"},"Passbolt"))),r.createElement("div",{className:"login-form"},r.createElement(D,null)),r.createElement(le,null))),r.createElement(Q,null))))}}const he=oe,ce=document.createElement("div");document.body.appendChild(ce),n.render(r.createElement(he,null),ce)}},n={};function a(e){var t=n[e];if(void 0!==t)return t.exports;var s=n[e]={exports:{}};return r[e].call(s.exports,s,s.exports,a),s.exports}a.m=r,e=[],a.O=(t,s,r,n)=>{if(!s){var i=1/0;for(c=0;c=n)&&Object.keys(a.O).every((e=>a.O[e](s[o])))?s.splice(o--,1):(l=!1,n0&&e[c-1][2]>n;c--)e[c]=e[c-1];e[c]=[s,r,n]},a.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return a.d(t,{a:t}),t},s=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,a.t=function(e,r){if(1&r&&(e=this(e)),8&r)return e;if("object"==typeof e&&e){if(4&r&&e.__esModule)return e;if(16&r&&"function"==typeof e.then)return e}var n=Object.create(null);a.r(n);var i={};t=t||[null,s({}),s([]),s(s)];for(var l=2&r&&e;"object"==typeof l&&!~t.indexOf(l);l=s(l))Object.getOwnPropertyNames(l).forEach((t=>i[t]=()=>e[t]));return i.default=()=>e,a.d(n,i),n},a.d=(e,t)=>{for(var s in t)a.o(t,s)&&!a.o(e,s)&&Object.defineProperty(e,s,{enumerable:!0,get:t[s]})},a.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),a.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),a.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.j=326,(()=>{var e={326:0};a.O.j=t=>0===e[t];var t=(t,s)=>{var r,n,[i,l,o]=s,h=0;for(r in l)a.o(l,r)&&(a.m[r]=l[r]);if(o)var c=o(a);for(t&&t(s);ha(8107)));i=a.O(i)})(); \ No newline at end of file diff --git a/webroot/js/app/api-vendors.js b/webroot/js/app/api-vendors.js index acb75b8c0d..6a13b5999f 100644 --- a/webroot/js/app/api-vendors.js +++ b/webroot/js/app/api-vendors.js @@ -1,2 +1,2 @@ /*! For license information please see api-vendors.js.LICENSE.txt */ -(self.webpackChunkpassbolt_styleguide=self.webpackChunkpassbolt_styleguide||[]).push([[596],{6419:(e,t,n)=>{e.exports=n(7698)},1511:(e,t,n)=>{e.exports=n(3363)},7766:(e,t,n)=>{e.exports=n(8065)},7175:(e,t,n)=>{e.exports=n(8842)},8914:(e,t,n)=>{e.exports=n(6279)},8580:(e,t,n)=>{e.exports=n(3778)},1643:(e,t,n)=>{e.exports=n(9373)},2991:(e,t,n)=>{e.exports=n(1798)},2366:(e,t,n)=>{e.exports=n(2527)},3649:(e,t,n)=>{e.exports=n(2073)},7302:(e,t,n)=>{e.exports=n(2856)},4943:(e,t,n)=>{e.exports=n(4471)},3978:(e,t,n)=>{e.exports=n(1910)},4198:(e,t,n)=>{e.exports=n(4888)},5420:(e,t,n)=>{e.exports=n(2547)},3592:(e,t,n)=>{e.exports=n(7385)},8363:(e,t,n)=>{e.exports=n(1522)},9996:(e,t,n)=>{e.exports=n(2209)},6976:(e,t,n)=>{e.exports=n(1258)},5238:(e,t,n)=>{e.exports=n(1493)},1446:(e,t,n)=>{e.exports=n(6600)},4243:e=>{e.exports=function(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n{var r=n(8363);e.exports=function(e){if(r(e))return e},e.exports.default=e.exports,e.exports.__esModule=!0},530:e=>{e.exports=function(e){return e&&e.__esModule?e:{default:e}},e.exports.default=e.exports,e.exports.__esModule=!0},5056:(e,t,n)=>{var r=n(1446),u=n(9996);e.exports=function(e,t){var n=e&&(void 0!==r&&u(e)||e["@@iterator"]);if(null!=n){var a,d,i=[],o=!0,c=!1;try{for(n=n.call(e);!(o=(a=n.next()).done)&&(i.push(a.value),!t||i.length!==t);o=!0);}catch(e){c=!0,d=e}finally{try{o||null==n.return||n.return()}finally{if(c)throw d}}return i}},e.exports.default=e.exports,e.exports.__esModule=!0},9736:e=>{e.exports=function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")},e.exports.default=e.exports,e.exports.__esModule=!0},8777:(e,t,n)=>{var r=n(7726),u=n(5056),a=n(9299),d=n(9736);e.exports=function(e,t){return r(e)||u(e,t)||a(e,t)||d()},e.exports.default=e.exports,e.exports.__esModule=!0},9299:(e,t,n)=>{var r=n(5238),u=n(3592),a=n(4243);e.exports=function(e,t){var n;if(e){if("string"==typeof e)return a(e,t);var d=r(n=Object.prototype.toString.call(e)).call(n,8,-1);return"Object"===d&&e.constructor&&(d=e.constructor.name),"Map"===d||"Set"===d?u(e):"Arguments"===d||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(d)?a(e,t):void 0}},e.exports.default=e.exports,e.exports.__esModule=!0},676:(e,t,n)=>{"use strict";function r(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);nr})},3614:(e,t,n)=>{"use strict";if(n.d(t,{Z:()=>u}),978==n.j)var r=n(676);function u(e){if(Array.isArray(e))return(0,r.Z)(e)}},3349:(e,t,n)=>{"use strict";function r(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}n.d(t,{Z:()=>r})},2137:(e,t,n)=>{"use strict";function r(e,t,n,r,u,a,d){try{var i=e[a](d),o=i.value}catch(e){return void n(e)}i.done?t(o):Promise.resolve(o).then(r,u)}function u(e){return function(){var t=this,n=arguments;return new Promise((function(u,a){var d=e.apply(t,n);function i(e){r(d,u,a,i,o,"next",e)}function o(e){r(d,u,a,i,o,"throw",e)}i(void 0)}))}}n.d(t,{Z:()=>u})},6610:(e,t,n)=>{"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}n.d(t,{Z:()=>r})},5991:(e,t,n)=>{"use strict";function r(e,t){for(var n=0;nu})},6156:(e,t,n)=>{"use strict";function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}n.d(t,{Z:()=>r})},2122:(e,t,n)=>{"use strict";function r(){return(r=Object.assign||function(e){for(var t=1;tr})},7608:(e,t,n)=>{"use strict";function r(e){return(r=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}n.d(t,{Z:()=>r})},379:(e,t,n)=>{"use strict";n.d(t,{Z:()=>u});var r=n(4665);function u(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&(0,r.Z)(e,t)}},1788:(e,t,n)=>{"use strict";if(n.d(t,{Z:()=>u}),978==n.j)var r=n(4665);function u(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,(0,r.Z)(e,t)}},6410:(e,t,n)=>{"use strict";function r(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}n.d(t,{Z:()=>r})},2303:(e,t,n)=>{"use strict";function r(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}n.d(t,{Z:()=>r})},1253:(e,t,n)=>{"use strict";n.d(t,{Z:()=>u});var r=n(9756);function u(e,t){if(null==e)return{};var n,u,a=(0,r.Z)(e,t);if(Object.getOwnPropertySymbols){var d=Object.getOwnPropertySymbols(e);for(u=0;u=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}},9756:(e,t,n)=>{"use strict";function r(e,t){if(null==e)return{};var n,r,u={},a=Object.keys(e);for(r=0;r=0||(u[n]=e[n]);return u}n.d(t,{Z:()=>r})},6070:(e,t,n)=>{"use strict";n.d(t,{Z:()=>a});var r=n(484),u=n(3349);function a(e,t){return!t||"object"!==(0,r.Z)(t)&&"function"!=typeof t?(0,u.Z)(e):t}},4665:(e,t,n)=>{"use strict";function r(e,t){return(r=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}n.d(t,{Z:()=>r})},4699:(e,t,n)=>{"use strict";n.d(t,{Z:()=>u});var r=n(2961);function u(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){var n=e&&("undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"]);if(null!=n){var r,u,a=[],d=!0,i=!1;try{for(n=n.call(e);!(d=(r=n.next()).done)&&(a.push(r.value),!t||a.length!==t);d=!0);}catch(e){i=!0,u=e}finally{try{d||null==n.return||n.return()}finally{if(i)throw u}}return a}}(e,t)||(0,r.Z)(e,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}},8927:(e,t,n)=>{"use strict";if(n.d(t,{Z:()=>i}),978==n.j)var r=n(3614);if(978==n.j)var u=n(6410);if(978==n.j)var a=n(2961);if(978==n.j)var d=n(2303);function i(e){return(0,r.Z)(e)||(0,u.Z)(e)||(0,a.Z)(e)||(0,d.Z)()}},484:(e,t,n)=>{"use strict";function r(e){return(r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}n.d(t,{Z:()=>r})},2961:(e,t,n)=>{"use strict";n.d(t,{Z:()=>u});var r=n(676);function u(e,t){if(e){if("string"==typeof e)return(0,r.Z)(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?(0,r.Z)(e,t):void 0}}},5366:(e,t,n)=>{"use strict";n.d(t,{Z:()=>i});var r=n(7608),u=n(4665);function a(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}function d(e,t,n){return(d=a()?Reflect.construct:function(e,t,n){var r=[null];r.push.apply(r,t);var a=new(Function.bind.apply(e,r));return n&&(0,u.Z)(a,n.prototype),a}).apply(null,arguments)}function i(e){var t="function"==typeof Map?new Map:void 0;return(i=function(e){if(null===e||(n=e,-1===Function.toString.call(n).indexOf("[native code]")))return e;var n;if("function"!=typeof e)throw new TypeError("Super expression must either be null or a function");if(void 0!==t){if(t.has(e))return t.get(e);t.set(e,a)}function a(){return d(e,arguments,(0,r.Z)(this).constructor)}return a.prototype=Object.create(e.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}}),(0,u.Z)(a,e)})(e)}},7757:(e,t,n)=>{e.exports=n(5666)},4493:(e,t,n)=>{n(7971),n(3242);var r=n(4058);e.exports=r.Array.from},4034:(e,t,n)=>{n(2737);var r=n(4058);e.exports=r.Array.isArray},5367:(e,t,n)=>{n(5906);var r=n(5703);e.exports=r("Array").concat},9324:(e,t,n)=>{n(2437);var r=n(5703);e.exports=r("Array").forEach},991:(e,t,n)=>{n(7690);var r=n(5703);e.exports=r("Array").includes},8700:(e,t,n)=>{n(9076);var r=n(5703);e.exports=r("Array").indexOf},3866:(e,t,n)=>{n(8787);var r=n(5703);e.exports=r("Array").map},2999:(e,t,n)=>{n(1876);var r=n(5703);e.exports=r("Array").reduce},4900:(e,t,n)=>{n(186);var r=n(5703);e.exports=r("Array").slice},2948:(e,t,n)=>{n(4115);var r=n(5703);e.exports=r("Array").sort},6043:(e,t,n)=>{var r=n(5367),u=Array.prototype;e.exports=function(e){var t=e.concat;return e===u||e instanceof Array&&t===u.concat?r:t}},6793:(e,t,n)=>{var r=n(1677),u=RegExp.prototype;e.exports=function(e){return(e===u||e instanceof RegExp)&&!("flags"in e)?r(e):e.flags}},8557:(e,t,n)=>{var r=n(991),u=n(1631),a=Array.prototype,d=String.prototype;e.exports=function(e){var t=e.includes;return e===a||e instanceof Array&&t===a.includes?r:"string"==typeof e||e===d||e instanceof String&&t===d.includes?u:t}},4570:(e,t,n)=>{var r=n(8700),u=Array.prototype;e.exports=function(e){var t=e.indexOf;return e===u||e instanceof Array&&t===u.indexOf?r:t}},8287:(e,t,n)=>{var r=n(3866),u=Array.prototype;e.exports=function(e){var t=e.map;return e===u||e instanceof Array&&t===u.map?r:t}},8025:(e,t,n)=>{var r=n(2999),u=Array.prototype;e.exports=function(e){var t=e.reduce;return e===u||e instanceof Array&&t===u.reduce?r:t}},9601:(e,t,n)=>{var r=n(4900),u=Array.prototype;e.exports=function(e){var t=e.slice;return e===u||e instanceof Array&&t===u.slice?r:t}},9355:(e,t,n)=>{var r=n(2948),u=Array.prototype;e.exports=function(e){var t=e.sort;return e===u||e instanceof Array&&t===u.sort?r:t}},5254:(e,t,n)=>{n(3882);var r=n(4058).Object;e.exports=function(e,t){return r.create(e,t)}},8171:(e,t,n)=>{n(6450);var r=n(4058).Object,u=e.exports=function(e,t,n){return r.defineProperty(e,t,n)};r.defineProperty.sham&&(u.sham=!0)},8524:(e,t,n)=>{n(4038);var r=n(4058);e.exports=r.parseInt},1677:(e,t,n)=>{n(4261);var r=n(6777);e.exports=function(e){return r.call(e)}},1631:(e,t,n)=>{n(1035);var r=n(5703);e.exports=r("String").includes},7473:(e,t,n)=>{n(5906),n(5967),n(5824),n(8555),n(2615),n(1732),n(5903),n(1825),n(8394),n(5915),n(1766),n(9791),n(9911),n(4315),n(3131),n(4714),n(659),n(9120),n(5327),n(1502);var r=n(4058);e.exports=r.Symbol},7385:(e,t,n)=>{var r=n(4493);e.exports=r},1522:(e,t,n)=>{var r=n(4034);e.exports=r},2209:(e,t,n)=>{n(7634),n(7971);var r=n(2902);e.exports=r},1258:(e,t,n)=>{n(7634),n(7971);var r=n(3476);e.exports=r},1493:(e,t,n)=>{var r=n(9601);e.exports=r},6600:(e,t,n)=>{var r=n(7473);n(8783),n(3975),n(5799),n(5414),n(6774),n(620),n(6172),e.exports=r},3916:e=>{e.exports=function(e){if("function"!=typeof e)throw TypeError(String(e)+" is not a function");return e}},1851:(e,t,n)=>{var r=n(941);e.exports=function(e){if(!r(e)&&null!==e)throw TypeError("Can't set "+String(e)+" as a prototype");return e}},8479:e=>{e.exports=function(){}},6059:(e,t,n)=>{var r=n(941);e.exports=function(e){if(!r(e))throw TypeError(String(e)+" is not an object");return e}},6837:(e,t,n)=>{"use strict";var r=n(3610).forEach,u=n(4194)("forEach");e.exports=u?[].forEach:function(e){return r(this,e,arguments.length>1?arguments[1]:void 0)}},1354:(e,t,n)=>{"use strict";var r=n(6843),u=n(9678),a=n(5196),d=n(6782),i=n(3057),o=n(5449),c=n(2902);e.exports=function(e){var t,n,l,s,f,p,m=u(e),h="function"==typeof this?this:Array,v=arguments.length,y=v>1?arguments[1]:void 0,g=void 0!==y,b=c(m),w=0;if(g&&(y=r(y,v>2?arguments[2]:void 0,2)),null==b||h==Array&&d(b))for(n=new h(t=i(m.length));t>w;w++)p=g?y(m[w],w):m[w],o(n,w,p);else for(f=(s=b.call(m)).next,n=new h;!(l=f.call(s)).done;w++)p=g?a(s,y,[l.value,w],!0):l.value,o(n,w,p);return n.length=w,n}},1692:(e,t,n)=>{var r=n(4529),u=n(3057),a=n(9413),d=function(e){return function(t,n,d){var i,o=r(t),c=u(o.length),l=a(d,c);if(e&&n!=n){for(;c>l;)if((i=o[l++])!=i)return!0}else for(;c>l;l++)if((e||l in o)&&o[l]===n)return e||l||0;return!e&&-1}};e.exports={includes:d(!0),indexOf:d(!1)}},3610:(e,t,n)=>{var r=n(6843),u=n(7026),a=n(9678),d=n(3057),i=n(4692),o=[].push,c=function(e){var t=1==e,n=2==e,c=3==e,l=4==e,s=6==e,f=7==e,p=5==e||s;return function(m,h,v,y){for(var g,b,w=a(m),x=u(w),k=r(h,v,3),S=d(x.length),E=0,T=y||i,_=t?T(m,S):n||f?T(m,0):void 0;S>E;E++)if((p||E in x)&&(b=k(g=x[E],E,w),e))if(t)_[E]=b;else if(b)switch(e){case 3:return!0;case 5:return g;case 6:return E;case 2:o.call(_,g)}else switch(e){case 4:return!1;case 7:o.call(_,g)}return s?-1:c||l?l:_}};e.exports={forEach:c(0),map:c(1),filter:c(2),some:c(3),every:c(4),find:c(5),findIndex:c(6),filterOut:c(7)}},568:(e,t,n)=>{var r=n(5981),u=n(9813),a=n(3385),d=u("species");e.exports=function(e){return a>=51||!r((function(){var t=[];return(t.constructor={})[d]=function(){return{foo:1}},1!==t[e](Boolean).foo}))}},4194:(e,t,n)=>{"use strict";var r=n(5981);e.exports=function(e,t){var n=[][e];return!!n&&r((function(){n.call(null,t||function(){throw 1},1)}))}},6499:(e,t,n)=>{var r=n(3916),u=n(9678),a=n(7026),d=n(3057),i=function(e){return function(t,n,i,o){r(n);var c=u(t),l=a(c),s=d(c.length),f=e?s-1:0,p=e?-1:1;if(i<2)for(;;){if(f in l){o=l[f],f+=p;break}if(f+=p,e?f<0:s<=f)throw TypeError("Reduce of empty array with no initial value")}for(;e?f>=0:s>f;f+=p)f in l&&(o=n(o,l[f],f,c));return o}};e.exports={left:i(!1),right:i(!0)}},4692:(e,t,n)=>{var r=n(941),u=n(1052),a=n(9813)("species");e.exports=function(e,t){var n;return u(e)&&("function"!=typeof(n=e.constructor)||n!==Array&&!u(n.prototype)?r(n)&&null===(n=n[a])&&(n=void 0):n=void 0),new(void 0===n?Array:n)(0===t?0:t)}},5196:(e,t,n)=>{var r=n(6059),u=n(7609);e.exports=function(e,t,n,a){try{return a?t(r(n)[0],n[1]):t(n)}catch(t){throw u(e),t}}},1385:(e,t,n)=>{var r=n(9813)("iterator"),u=!1;try{var a=0,d={next:function(){return{done:!!a++}},return:function(){u=!0}};d[r]=function(){return this},Array.from(d,(function(){throw 2}))}catch(e){}e.exports=function(e,t){if(!t&&!u)return!1;var n=!1;try{var a={};a[r]=function(){return{next:function(){return{done:n=!0}}}},e(a)}catch(e){}return n}},2532:e=>{var t={}.toString;e.exports=function(e){return t.call(e).slice(8,-1)}},9697:(e,t,n)=>{var r=n(2885),u=n(2532),a=n(9813)("toStringTag"),d="Arguments"==u(function(){return arguments}());e.exports=r?u:function(e){var t,n,r;return void 0===e?"Undefined":null===e?"Null":"string"==typeof(n=function(e,t){try{return e[t]}catch(e){}}(t=Object(e),a))?n:d?u(t):"Object"==(r=u(t))&&"function"==typeof t.callee?"Arguments":r}},7772:(e,t,n)=>{var r=n(9813)("match");e.exports=function(e){var t=/./;try{"/./"[e](t)}catch(n){try{return t[r]=!1,"/./"[e](t)}catch(e){}}return!1}},4160:(e,t,n)=>{var r=n(5981);e.exports=!r((function(){function e(){}return e.prototype.constructor=null,Object.getPrototypeOf(new e)!==e.prototype}))},1046:(e,t,n)=>{"use strict";var r=n(5143).IteratorPrototype,u=n(9290),a=n(1887),d=n(904),i=n(2077),o=function(){return this};e.exports=function(e,t,n){var c=t+" Iterator";return e.prototype=u(r,{next:a(1,n)}),d(e,c,!1,!0),i[c]=o,e}},2029:(e,t,n)=>{var r=n(5746),u=n(5988),a=n(1887);e.exports=r?function(e,t,n){return u.f(e,t,a(1,n))}:function(e,t,n){return e[t]=n,e}},1887:e=>{e.exports=function(e,t){return{enumerable:!(1&e),configurable:!(2&e),writable:!(4&e),value:t}}},5449:(e,t,n)=>{"use strict";var r=n(6935),u=n(5988),a=n(1887);e.exports=function(e,t,n){var d=r(t);d in e?u.f(e,d,a(0,n)):e[d]=n}},7771:(e,t,n)=>{"use strict";var r=n(6887),u=n(1046),a=n(249),d=n(8929),i=n(904),o=n(2029),c=n(9754),l=n(9813),s=n(2529),f=n(2077),p=n(5143),m=p.IteratorPrototype,h=p.BUGGY_SAFARI_ITERATORS,v=l("iterator"),y="keys",g="values",b="entries",w=function(){return this};e.exports=function(e,t,n,l,p,x,k){u(n,t,l);var S,E,T,_=function(e){if(e===p&&N)return N;if(!h&&e in C)return C[e];switch(e){case y:case g:case b:return function(){return new n(this,e)}}return function(){return new n(this)}},O=t+" Iterator",I=!1,C=e.prototype,P=C[v]||C["@@iterator"]||p&&C[p],N=!h&&P||_(p),M="Array"==t&&C.entries||P;if(M&&(S=a(M.call(new e)),m!==Object.prototype&&S.next&&(s||a(S)===m||(d?d(S,m):"function"!=typeof S[v]&&o(S,v,w)),i(S,O,!0,!0),s&&(f[O]=w))),p==g&&P&&P.name!==g&&(I=!0,N=function(){return P.call(this)}),s&&!k||C[v]===N||o(C,v,N),f[t]=N,p)if(E={values:_(g),keys:x?N:_(y),entries:_(b)},k)for(T in E)(h||I||!(T in C))&&c(C,T,E[T]);else r({target:t,proto:!0,forced:h||I},E);return E}},6349:(e,t,n)=>{var r=n(4058),u=n(7457),a=n(1477),d=n(5988).f;e.exports=function(e){var t=r.Symbol||(r.Symbol={});u(t,e)||d(t,e,{value:a.f(e)})}},5746:(e,t,n)=>{var r=n(5981);e.exports=!r((function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]}))},1333:(e,t,n)=>{var r=n(1899),u=n(941),a=r.document,d=u(a)&&u(a.createElement);e.exports=function(e){return d?a.createElement(e):{}}},3281:e=>{e.exports={CSSRuleList:0,CSSStyleDeclaration:0,CSSValueList:0,ClientRectList:0,DOMRectList:0,DOMStringList:0,DOMTokenList:1,DataTransferItemList:0,FileList:0,HTMLAllCollection:0,HTMLCollection:0,HTMLFormElement:0,HTMLSelectElement:0,MediaList:0,MimeTypeArray:0,NamedNodeMap:0,NodeList:1,PaintRequestList:0,Plugin:0,PluginArray:0,SVGLengthList:0,SVGNumberList:0,SVGPathSegList:0,SVGPointList:0,SVGStringList:0,SVGTransformList:0,SourceBufferList:0,StyleSheetList:0,TextTrackCueList:0,TextTrackList:0,TouchList:0}},6049:(e,t,n)=>{var r=n(2532),u=n(1899);e.exports="process"==r(u.process)},2861:(e,t,n)=>{var r=n(626);e.exports=r("navigator","userAgent")||""},3385:(e,t,n)=>{var r,u,a=n(1899),d=n(2861),i=a.process,o=i&&i.versions,c=o&&o.v8;c?u=(r=c.split("."))[0]<4?1:r[0]+r[1]:d&&(!(r=d.match(/Edge\/(\d+)/))||r[1]>=74)&&(r=d.match(/Chrome\/(\d+)/))&&(u=r[1]),e.exports=u&&+u},5703:(e,t,n)=>{var r=n(4058);e.exports=function(e){return r[e+"Prototype"]}},6759:e=>{e.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]},6887:(e,t,n)=>{"use strict";var r=n(1899),u=n(9677).f,a=n(7252),d=n(4058),i=n(6843),o=n(2029),c=n(7457),l=function(e){var t=function(t,n,r){if(this instanceof e){switch(arguments.length){case 0:return new e;case 1:return new e(t);case 2:return new e(t,n)}return new e(t,n,r)}return e.apply(this,arguments)};return t.prototype=e.prototype,t};e.exports=function(e,t){var n,s,f,p,m,h,v,y,g=e.target,b=e.global,w=e.stat,x=e.proto,k=b?r:w?r[g]:(r[g]||{}).prototype,S=b?d:d[g]||(d[g]={}),E=S.prototype;for(f in t)n=!a(b?f:g+(w?".":"#")+f,e.forced)&&k&&c(k,f),m=S[f],n&&(h=e.noTargetGet?(y=u(k,f))&&y.value:k[f]),p=n&&h?h:t[f],n&&typeof m==typeof p||(v=e.bind&&n?i(p,r):e.wrap&&n?l(p):x&&"function"==typeof p?i(Function.call,p):p,(e.sham||p&&p.sham||m&&m.sham)&&o(v,"sham",!0),S[f]=v,x&&(c(d,s=g+"Prototype")||o(d,s,{}),d[s][f]=p,e.real&&E&&!E[f]&&o(E,f,p)))}},5981:e=>{e.exports=function(e){try{return!!e()}catch(e){return!0}}},6843:(e,t,n)=>{var r=n(3916);e.exports=function(e,t,n){if(r(e),void 0===t)return e;switch(n){case 0:return function(){return e.call(t)};case 1:return function(n){return e.call(t,n)};case 2:return function(n,r){return e.call(t,n,r)};case 3:return function(n,r,u){return e.call(t,n,r,u)}}return function(){return e.apply(t,arguments)}}},626:(e,t,n)=>{var r=n(4058),u=n(1899),a=function(e){return"function"==typeof e?e:void 0};e.exports=function(e,t){return arguments.length<2?a(r[e])||a(u[e]):r[e]&&r[e][t]||u[e]&&u[e][t]}},2902:(e,t,n)=>{var r=n(9697),u=n(2077),a=n(9813)("iterator");e.exports=function(e){if(null!=e)return e[a]||e["@@iterator"]||u[r(e)]}},3476:(e,t,n)=>{var r=n(6059),u=n(2902);e.exports=function(e){var t=u(e);if("function"!=typeof t)throw TypeError(String(e)+" is not iterable");return r(t.call(e))}},1899:(e,t,n)=>{var r=function(e){return e&&e.Math==Math&&e};e.exports=r("object"==typeof globalThis&&globalThis)||r("object"==typeof window&&window)||r("object"==typeof self&&self)||r("object"==typeof n.g&&n.g)||function(){return this}()||Function("return this")()},7457:(e,t,n)=>{var r=n(9678),u={}.hasOwnProperty;e.exports=function(e,t){return u.call(r(e),t)}},7748:e=>{e.exports={}},5463:(e,t,n)=>{var r=n(626);e.exports=r("document","documentElement")},2840:(e,t,n)=>{var r=n(5746),u=n(5981),a=n(1333);e.exports=!r&&!u((function(){return 7!=Object.defineProperty(a("div"),"a",{get:function(){return 7}}).a}))},7026:(e,t,n)=>{var r=n(5981),u=n(2532),a="".split;e.exports=r((function(){return!Object("z").propertyIsEnumerable(0)}))?function(e){return"String"==u(e)?a.call(e,""):Object(e)}:Object},1302:(e,t,n)=>{var r=n(3030),u=Function.toString;"function"!=typeof r.inspectSource&&(r.inspectSource=function(e){return u.call(e)}),e.exports=r.inspectSource},5402:(e,t,n)=>{var r,u,a,d=n(8019),i=n(1899),o=n(941),c=n(2029),l=n(7457),s=n(3030),f=n(4262),p=n(7748),m="Object already initialized",h=i.WeakMap;if(d){var v=s.state||(s.state=new h),y=v.get,g=v.has,b=v.set;r=function(e,t){if(g.call(v,e))throw new TypeError(m);return t.facade=e,b.call(v,e,t),t},u=function(e){return y.call(v,e)||{}},a=function(e){return g.call(v,e)}}else{var w=f("state");p[w]=!0,r=function(e,t){if(l(e,w))throw new TypeError(m);return t.facade=e,c(e,w,t),t},u=function(e){return l(e,w)?e[w]:{}},a=function(e){return l(e,w)}}e.exports={set:r,get:u,has:a,enforce:function(e){return a(e)?u(e):r(e,{})},getterFor:function(e){return function(t){var n;if(!o(t)||(n=u(t)).type!==e)throw TypeError("Incompatible receiver, "+e+" required");return n}}}},6782:(e,t,n)=>{var r=n(9813),u=n(2077),a=r("iterator"),d=Array.prototype;e.exports=function(e){return void 0!==e&&(u.Array===e||d[a]===e)}},1052:(e,t,n)=>{var r=n(2532);e.exports=Array.isArray||function(e){return"Array"==r(e)}},7252:(e,t,n)=>{var r=n(5981),u=/#|\.prototype\./,a=function(e,t){var n=i[d(e)];return n==c||n!=o&&("function"==typeof t?r(t):!!t)},d=a.normalize=function(e){return String(e).replace(u,".").toLowerCase()},i=a.data={},o=a.NATIVE="N",c=a.POLYFILL="P";e.exports=a},941:e=>{e.exports=function(e){return"object"==typeof e?null!==e:"function"==typeof e}},2529:e=>{e.exports=!0},685:(e,t,n)=>{var r=n(941),u=n(2532),a=n(9813)("match");e.exports=function(e){var t;return r(e)&&(void 0!==(t=e[a])?!!t:"RegExp"==u(e))}},7609:(e,t,n)=>{var r=n(6059);e.exports=function(e){var t=e.return;if(void 0!==t)return r(t.call(e)).value}},5143:(e,t,n)=>{"use strict";var r,u,a,d=n(5981),i=n(249),o=n(2029),c=n(7457),l=n(9813),s=n(2529),f=l("iterator"),p=!1;[].keys&&("next"in(a=[].keys())?(u=i(i(a)))!==Object.prototype&&(r=u):p=!0);var m=null==r||d((function(){var e={};return r[f].call(e)!==e}));m&&(r={}),s&&!m||c(r,f)||o(r,f,(function(){return this})),e.exports={IteratorPrototype:r,BUGGY_SAFARI_ITERATORS:p}},2077:e=>{e.exports={}},2497:(e,t,n)=>{var r=n(3385),u=n(5981);e.exports=!!Object.getOwnPropertySymbols&&!u((function(){return!String(Symbol())||!Symbol.sham&&r&&r<41}))},8019:(e,t,n)=>{var r=n(1899),u=n(1302),a=r.WeakMap;e.exports="function"==typeof a&&/native code/.test(u(a))},344:(e,t,n)=>{var r=n(685);e.exports=function(e){if(r(e))throw TypeError("The method doesn't accept regular expressions");return e}},9806:(e,t,n)=>{var r=n(1899),u=n(4853).trim,a=n(3483),d=r.parseInt,i=/^[+-]?0[Xx]/,o=8!==d(a+"08")||22!==d(a+"0x16");e.exports=o?function(e,t){var n=u(String(e));return d(n,t>>>0||(i.test(n)?16:10))}:d},9290:(e,t,n)=>{var r,u=n(6059),a=n(9938),d=n(6759),i=n(7748),o=n(5463),c=n(1333),l=n(4262)("IE_PROTO"),s=function(){},f=function(e){return"