From a991379a747409893678e4d23868a2ceafce7cf8 Mon Sep 17 00:00:00 2001 From: Clement Michaud Date: Sat, 2 Nov 2019 15:32:58 +0100 Subject: [PATCH] Declare suites as Go structs and bootstrap e2e test framework in Go. Some tests are not fully rewritten in Go, a typescript wrapper is called instead until we remove the remaining TS tests and dependencies. Also, dockerize every components (mainly Authelia backend, frontend and kind) so that the project does not interfere with user host anymore (open ports for instance). The only remaining intrusive change is the one done during bootstrap to add entries in /etc/hosts. It will soon be avoided using authelia.com domain that I own. --- .gitignore | 4 +- .travis.yml | 33 +- bootstrap.sh | 2 +- client/.env.development | 1 + client/package.json | 3 +- cmd/authelia-scripts/cmd_bootstrap.go | 44 +- cmd/authelia-scripts/cmd_build.go | 33 +- cmd/authelia-scripts/cmd_ci.go | 38 +- cmd/authelia-scripts/cmd_clean.go | 4 +- cmd/authelia-scripts/cmd_docker.go | 65 +- cmd/authelia-scripts/cmd_serve.go | 7 +- cmd/authelia-scripts/cmd_suites.go | 332 +-- cmd/authelia-scripts/cmd_unittest.go | 10 +- cmd/authelia-scripts/constants.go | 3 - cmd/authelia-scripts/docker.go | 24 +- cmd/authelia-scripts/exec.go | 52 - cmd/authelia-scripts/main.go | 36 +- cmd/authelia-suites/main.go | 125 + docker-compose.yml | 3 +- example/compose/authelia/Dockerfile.backend | 8 + example/compose/authelia/Dockerfile.frontend | 9 + .../authelia/docker-compose.backend.yml | 20 + .../docker-compose.frontend-forward.yml | 8 + .../authelia/docker-compose.frontend.yml | 14 + example/compose/authelia/docker-compose.yml | 2 +- .../compose/authelia/resources/entrypoint.sh | 7 + example/compose/authelia/resources/nginx.conf | 17 + .../compose/authelia/resources/reflex.conf | 1 + example/compose/authelia/resources/run.sh | 8 + example/compose/duo-api/docker-compose.yml | 2 +- example/compose/httpbin/docker-compose.yml | 2 +- example/compose/kind/Dockerfile | 17 + example/compose/kind/config.yml | 4 + example/compose/kind/docker-compose.yml | 38 + example/compose/kind/entrypoint-dashboard.sh | 4 + example/compose/kind/entrypoint.sh | 5 + example/compose/kind/patch-kubeconfig.sh | 7 + example/compose/ldap/docker-compose.admin.yml | 2 +- example/compose/ldap/docker-compose.yml | 2 +- example/compose/mongo/docker-compose.yml | 2 +- .../compose/nginx/backend/docker-compose.yml | 2 +- .../nginx/kubernetes/docker-compose.yml | 18 - example/compose/nginx/kubernetes/nginx.conf | 30 - .../compose/nginx/kubernetes/ssl/server.cert | 19 - .../compose/nginx/kubernetes/ssl/server.key | 27 - example/compose/nginx/portal/.gitignore | 2 - .../compose/nginx/portal/docker-compose.yml | 3 +- .../portal/{nginx.conf.ejs => nginx.conf} | 54 +- example/compose/nginx/portal/render.js | 40 - example/compose/redis/docker-compose.yml | 2 +- example/compose/smtp/docker-compose.yml | 2 +- example/compose/squid/docker-compose.yml | 2 +- example/compose/traefik/.gitignore | 1 - example/compose/traefik/docker-compose.yml | 4 +- example/compose/traefik/render.js | 40 - .../{traefik.toml.ejs => traefik.toml} | 24 +- example/kube/apps/apps.yml | 13 +- example/kube/authelia/deployment.yml | 2 +- example/kube/authelia/ssl/tls.crt | 17 - example/kube/authelia/ssl/tls.csr | 15 - example/kube/authelia/ssl/tls.key | 27 - example/kube/bootstrap-authelia.sh | 2 +- example/kube/bootstrap.sh | 2 +- .../kube/ingress-controller/deployment.yml | 10 +- example/kube/ingress-controller/rbac.yml | 10 +- example/kube/ingress-controller/service.yml | 4 +- example/kube/ldap/deployment.yml | 2 +- example/kube/mail/deployment.yml | 2 +- example/kube/storage/mongo.yml | 2 +- example/kube/storage/redis.yml | 2 +- example/kube/test.yml | 2 +- go.mod | 7 + go.sum | 132 + notification/smtp_notifier.go | 4 + package-lock.json | 2198 +++++------------ package.json | 9 - regulation/regulator_test.go | 9 +- scripts/clean-environment.ts | 8 - scripts/run-environment.ts | 80 - scripts/setup-environment.ts | 10 + scripts/teardown-environment.ts | 10 + .../Basic/configuration.yml | 4 +- .../Basic/users.yml | 0 .../BypassAll/configuration.yml | 4 +- .../BypassAll/users.yml | 0 .../DuoPush/configuration.yml | 4 +- .../DuoPush/users.yml | 0 .../HighAvailability/configuration.yml | 38 +- .../LDAP/configuration.yml | 26 +- .../Mongo/configuration.yml | 28 +- .../Mongo/users.yml | 0 .../NetworkACL/configuration.yml | 12 +- .../NetworkACL/users.yml | 0 .../ShortTimeouts/configuration.yml | 26 +- .../ShortTimeouts/users.yml | 0 suites/Standalone/configuration.yml | 83 + suites/Standalone/users.yml | 28 + .../Traefik/configuration.yml | 21 +- .../Traefik/users.yml | 0 suites/action_http.go | 26 + suites/action_login.go | 47 + suites/action_logout.go | 8 + suites/action_mail.go | 34 + suites/action_register.go | 9 + suites/action_totp.go | 25 + suites/action_visit.go | 27 + suites/constants.go | 21 + suites/docker.go | 53 + suites/environment.go | 51 + suites/kubernetes.go | 122 + suites/registry.go | 63 + suites/scenario_one_factor_test.go | 80 + suites/scenario_two_factor_test.go | 65 + suites/suite_basic.go | 39 + suites/suite_basic_test.go | 17 + suites/suite_bypass_all.go | 41 + suites/suite_bypass_all_test.go | 17 + suites/suite_duo_push.go | 42 + suites/suite_duo_push_test.go | 17 + suites/suite_high_availability.go | 45 + suites/suite_high_availability_test.go | 19 + suites/suite_kubernetes.go | 111 + suites/suite_kubernetes_test.go | 20 + suites/suite_ldap.go | 42 + suites/suite_ldap_test.go | 20 + suites/suite_mongo.go | 42 + suites/suite_mongo_test.go | 20 + suites/suite_network_acl.go | 44 + suites/suite_network_acl_test.go | 17 + suites/suite_short_timeouts.go | 40 + suites/suite_short_timeouts_test.go | 17 + suites/suite_standalone.go | 40 + suites/suite_standalone_test.go | 20 + suites/suite_traefik.go | 40 + suites/suite_traefik_test.go | 20 + suites/suites.go | 101 + suites/verify_body_contains.go | 8 + suites/verify_is_first_factor_page.go | 7 + suites/verify_is_second_factor_page.go | 7 + suites/verify_notification.go | 9 + suites/verify_secret_authorized.go | 7 + suites/verify_url_is.go | 22 + suites/webdriver.go | 125 + test/helpers/context/AutheliaServer.ts | 26 - .../helpers/context/AutheliaServerFromDist.ts | 42 - .../context/AutheliaServerInterface.ts | 7 - .../context/AutheliaServerWithHotReload.ts | 183 -- test/helpers/context/kubernetes/Kubernetes.ts | 27 - .../context/kubernetes/KubernetesManager.ts | 20 - .../scenarii/AuthenticationBlacklisting.ts | 2 +- .../scenarii/AlreadyLoggedIn.ts | 0 .../scenarii/BackendProtection.ts | 0 .../{basic => Basic}/scenarii/BadPassword.ts | 0 .../{basic => Basic}/scenarii/BypassPolicy.ts | 0 .../scenarii/NoDuoPushOption.ts | 0 .../{basic => Basic}/scenarii/RegisterTotp.ts | 0 .../scenarii/ResetPassword.ts | 0 .../scenarii/TOTPValidation.ts | 0 .../scenarii/VerifyEndpoint.ts | 0 test/suites/{basic => Basic}/test.ts | 8 +- .../README.md | 0 .../scenarii/BypassPolicy.ts | 0 .../scenarii/CustomHeadersForwarded.ts | 0 .../scenarii/NoDefaultRedirectionUrl.ts | 0 .../test.ts | 18 +- test/suites/{duo-push => DuoPush}/README.md | 0 .../scenarii/DuoPushNotification.ts | 0 .../scenarii/Prefered2faMethod.ts | 0 test/suites/{duo-push => DuoPush}/test.ts | 16 +- .../README.md | 0 test/suites/HighAvailability/environment.ts | 17 + .../scenarii/AccessControl.ts | 0 .../scenarii/AutheliaRestart.ts | 0 .../scenarii/AuthenticationRegulation.ts | 0 .../scenarii/BasicAuthentication.ts | 0 .../scenarii/CustomHeadersForwarded.ts | 0 .../EnforceInternalRedirectionsOnly.ts | 0 .../scenarii/MongoConnectionRecovery.ts | 8 +- .../test.ts | 4 +- test/suites/{basic => NetworkACL}/README.md | 0 .../scenarii/NetworkACLs.ts | 0 test/suites/NetworkACL/test.ts | 13 + .../README.md | 0 .../scenarii/Inactivity.ts | 0 test/suites/ShortTimeouts/test.ts | 9 + .../basic-bypass-no-redirect/environment.ts | 39 - test/suites/basic/environment.ts | 34 - .../basic/scenarii/RequiredTwoFactor.ts | 30 - test/suites/docker-image/README.md | 13 - test/suites/docker-image/config.yml | 261 -- test/suites/docker-image/environment.ts | 37 - test/suites/docker-image/test.ts | 27 - test/suites/duo-push/environment.ts | 36 - test/suites/high-availability/environment.ts | 49 - test/suites/kubernetes/README.md | 12 - test/suites/kubernetes/environment.ts | 148 -- test/suites/kubernetes/test.ts | 10 - test/suites/ldap/README.md | 12 - test/suites/ldap/environment.ts | 45 - test/suites/ldap/test.ts | 10 - test/suites/mongo/README.md | 11 - test/suites/mongo/environment.ts | 47 - test/suites/mongo/test.ts | 12 - test/suites/network-acls/README.md | 13 - test/suites/network-acls/environment.ts | 37 - test/suites/network-acls/test.ts | 13 - test/suites/short-timeouts/environment.ts | 34 - test/suites/short-timeouts/test.ts | 7 - test/suites/traefik/README.md | 11 - test/suites/traefik/environment.ts | 34 - test/suites/traefik/test.ts | 17 - utils/check.go | 25 + utils/exec.go | 126 + {cmd/authelia-scripts => utils}/files.go | 6 +- 214 files changed, 3509 insertions(+), 3815 deletions(-) delete mode 100644 cmd/authelia-scripts/exec.go create mode 100644 cmd/authelia-suites/main.go create mode 100644 example/compose/authelia/Dockerfile.backend create mode 100644 example/compose/authelia/Dockerfile.frontend create mode 100644 example/compose/authelia/docker-compose.backend.yml create mode 100644 example/compose/authelia/docker-compose.frontend-forward.yml create mode 100644 example/compose/authelia/docker-compose.frontend.yml create mode 100755 example/compose/authelia/resources/entrypoint.sh create mode 100644 example/compose/authelia/resources/nginx.conf create mode 100644 example/compose/authelia/resources/reflex.conf create mode 100755 example/compose/authelia/resources/run.sh create mode 100644 example/compose/kind/Dockerfile create mode 100644 example/compose/kind/config.yml create mode 100644 example/compose/kind/docker-compose.yml create mode 100755 example/compose/kind/entrypoint-dashboard.sh create mode 100755 example/compose/kind/entrypoint.sh create mode 100755 example/compose/kind/patch-kubeconfig.sh delete mode 100644 example/compose/nginx/kubernetes/docker-compose.yml delete mode 100644 example/compose/nginx/kubernetes/nginx.conf delete mode 100644 example/compose/nginx/kubernetes/ssl/server.cert delete mode 100644 example/compose/nginx/kubernetes/ssl/server.key delete mode 100644 example/compose/nginx/portal/.gitignore rename example/compose/nginx/portal/{nginx.conf.ejs => nginx.conf} (83%) delete mode 100755 example/compose/nginx/portal/render.js delete mode 100644 example/compose/traefik/.gitignore delete mode 100755 example/compose/traefik/render.js rename example/compose/traefik/{traefik.toml.ejs => traefik.toml} (69%) delete mode 100644 example/kube/authelia/ssl/tls.crt delete mode 100644 example/kube/authelia/ssl/tls.csr delete mode 100644 example/kube/authelia/ssl/tls.key delete mode 100644 scripts/clean-environment.ts delete mode 100644 scripts/run-environment.ts create mode 100644 scripts/setup-environment.ts create mode 100644 scripts/teardown-environment.ts rename test/suites/basic/config.yml => suites/Basic/configuration.yml (96%) rename test/suites/basic-bypass-no-redirect/users_database.yml => suites/Basic/users.yml (100%) rename test/suites/basic-bypass-no-redirect/config.yml => suites/BypassAll/configuration.yml (92%) rename test/suites/basic/users_database.yml => suites/BypassAll/users.yml (100%) rename test/suites/duo-push/config.yml => suites/DuoPush/configuration.yml (96%) rename test/suites/duo-push/users_database.yml => suites/DuoPush/users.yml (100%) rename test/suites/high-availability/config.yml => suites/HighAvailability/configuration.yml (93%) rename test/suites/ldap/config.yml => suites/LDAP/configuration.yml (86%) rename test/suites/mongo/config.yml => suites/Mongo/configuration.yml (77%) rename test/suites/mongo/users_database.yml => suites/Mongo/users.yml (100%) rename test/suites/network-acls/config.yml => suites/NetworkACL/configuration.yml (90%) rename test/suites/network-acls/users_database.yml => suites/NetworkACL/users.yml (100%) rename test/suites/short-timeouts/config.yml => suites/ShortTimeouts/configuration.yml (88%) rename test/suites/short-timeouts/users_database.yml => suites/ShortTimeouts/users.yml (100%) create mode 100644 suites/Standalone/configuration.yml create mode 100644 suites/Standalone/users.yml rename test/suites/traefik/config.yml => suites/Traefik/configuration.yml (66%) rename test/suites/traefik/users_database.yml => suites/Traefik/users.yml (100%) create mode 100644 suites/action_http.go create mode 100644 suites/action_login.go create mode 100644 suites/action_logout.go create mode 100644 suites/action_mail.go create mode 100644 suites/action_register.go create mode 100644 suites/action_totp.go create mode 100644 suites/action_visit.go create mode 100644 suites/constants.go create mode 100644 suites/docker.go create mode 100644 suites/environment.go create mode 100644 suites/kubernetes.go create mode 100644 suites/registry.go create mode 100644 suites/scenario_one_factor_test.go create mode 100644 suites/scenario_two_factor_test.go create mode 100644 suites/suite_basic.go create mode 100644 suites/suite_basic_test.go create mode 100644 suites/suite_bypass_all.go create mode 100644 suites/suite_bypass_all_test.go create mode 100644 suites/suite_duo_push.go create mode 100644 suites/suite_duo_push_test.go create mode 100644 suites/suite_high_availability.go create mode 100644 suites/suite_high_availability_test.go create mode 100644 suites/suite_kubernetes.go create mode 100644 suites/suite_kubernetes_test.go create mode 100644 suites/suite_ldap.go create mode 100644 suites/suite_ldap_test.go create mode 100644 suites/suite_mongo.go create mode 100644 suites/suite_mongo_test.go create mode 100644 suites/suite_network_acl.go create mode 100644 suites/suite_network_acl_test.go create mode 100644 suites/suite_short_timeouts.go create mode 100644 suites/suite_short_timeouts_test.go create mode 100644 suites/suite_standalone.go create mode 100644 suites/suite_standalone_test.go create mode 100644 suites/suite_traefik.go create mode 100644 suites/suite_traefik_test.go create mode 100644 suites/suites.go create mode 100644 suites/verify_body_contains.go create mode 100644 suites/verify_is_first_factor_page.go create mode 100644 suites/verify_is_second_factor_page.go create mode 100644 suites/verify_notification.go create mode 100644 suites/verify_secret_authorized.go create mode 100644 suites/verify_url_is.go create mode 100644 suites/webdriver.go delete mode 100644 test/helpers/context/AutheliaServer.ts delete mode 100644 test/helpers/context/AutheliaServerFromDist.ts delete mode 100644 test/helpers/context/AutheliaServerInterface.ts delete mode 100644 test/helpers/context/AutheliaServerWithHotReload.ts delete mode 100644 test/helpers/context/kubernetes/Kubernetes.ts delete mode 100644 test/helpers/context/kubernetes/KubernetesManager.ts rename test/suites/{basic => Basic}/scenarii/AlreadyLoggedIn.ts (100%) rename test/suites/{basic => Basic}/scenarii/BackendProtection.ts (100%) rename test/suites/{basic => Basic}/scenarii/BadPassword.ts (100%) rename test/suites/{basic => Basic}/scenarii/BypassPolicy.ts (100%) rename test/suites/{basic => Basic}/scenarii/NoDuoPushOption.ts (100%) rename test/suites/{basic => Basic}/scenarii/RegisterTotp.ts (100%) rename test/suites/{basic => Basic}/scenarii/ResetPassword.ts (100%) rename test/suites/{basic => Basic}/scenarii/TOTPValidation.ts (100%) rename test/suites/{basic => Basic}/scenarii/VerifyEndpoint.ts (100%) rename test/suites/{basic => Basic}/test.ts (73%) rename test/suites/{basic-bypass-no-redirect => BypassAll}/README.md (100%) rename test/suites/{basic-bypass-no-redirect => BypassAll}/scenarii/BypassPolicy.ts (100%) rename test/suites/{basic-bypass-no-redirect => BypassAll}/scenarii/CustomHeadersForwarded.ts (100%) rename test/suites/{basic-bypass-no-redirect => BypassAll}/scenarii/NoDefaultRedirectionUrl.ts (100%) rename test/suites/{basic-bypass-no-redirect => BypassAll}/test.ts (50%) rename test/suites/{duo-push => DuoPush}/README.md (100%) rename test/suites/{duo-push => DuoPush}/scenarii/DuoPushNotification.ts (100%) rename test/suites/{duo-push => DuoPush}/scenarii/Prefered2faMethod.ts (100%) rename test/suites/{duo-push => DuoPush}/test.ts (50%) rename test/suites/{high-availability => HighAvailability}/README.md (100%) create mode 100644 test/suites/HighAvailability/environment.ts rename test/suites/{high-availability => HighAvailability}/scenarii/AccessControl.ts (100%) rename test/suites/{high-availability => HighAvailability}/scenarii/AutheliaRestart.ts (100%) rename test/suites/{high-availability => HighAvailability}/scenarii/AuthenticationRegulation.ts (100%) rename test/suites/{high-availability => HighAvailability}/scenarii/BasicAuthentication.ts (100%) rename test/suites/{high-availability => HighAvailability}/scenarii/CustomHeadersForwarded.ts (100%) rename test/suites/{high-availability => HighAvailability}/scenarii/EnforceInternalRedirectionsOnly.ts (100%) rename test/suites/{high-availability => HighAvailability}/scenarii/MongoConnectionRecovery.ts (70%) rename test/suites/{high-availability => HighAvailability}/test.ts (83%) rename test/suites/{basic => NetworkACL}/README.md (100%) rename test/suites/{network-acls => NetworkACL}/scenarii/NetworkACLs.ts (100%) create mode 100644 test/suites/NetworkACL/test.ts rename test/suites/{short-timeouts => ShortTimeouts}/README.md (100%) rename test/suites/{short-timeouts => ShortTimeouts}/scenarii/Inactivity.ts (100%) create mode 100644 test/suites/ShortTimeouts/test.ts delete mode 100644 test/suites/basic-bypass-no-redirect/environment.ts delete mode 100644 test/suites/basic/environment.ts delete mode 100644 test/suites/basic/scenarii/RequiredTwoFactor.ts delete mode 100644 test/suites/docker-image/README.md delete mode 100644 test/suites/docker-image/config.yml delete mode 100644 test/suites/docker-image/environment.ts delete mode 100644 test/suites/docker-image/test.ts delete mode 100644 test/suites/duo-push/environment.ts delete mode 100644 test/suites/high-availability/environment.ts delete mode 100644 test/suites/kubernetes/README.md delete mode 100644 test/suites/kubernetes/environment.ts delete mode 100644 test/suites/kubernetes/test.ts delete mode 100644 test/suites/ldap/README.md delete mode 100644 test/suites/ldap/environment.ts delete mode 100644 test/suites/ldap/test.ts delete mode 100644 test/suites/mongo/README.md delete mode 100644 test/suites/mongo/environment.ts delete mode 100644 test/suites/mongo/test.ts delete mode 100644 test/suites/network-acls/README.md delete mode 100644 test/suites/network-acls/environment.ts delete mode 100644 test/suites/network-acls/test.ts delete mode 100644 test/suites/short-timeouts/environment.ts delete mode 100644 test/suites/short-timeouts/test.ts delete mode 100644 test/suites/traefik/README.md delete mode 100644 test/suites/traefik/environment.ts delete mode 100644 test/suites/traefik/test.ts create mode 100644 utils/check.go create mode 100644 utils/exec.go rename {cmd/authelia-scripts => utils}/files.go (88%) diff --git a/.gitignore b/.gitignore index f2cb9b4e6..6eaa44db9 100644 --- a/.gitignore +++ b/.gitignore @@ -35,12 +35,10 @@ example/ldap/private.ldif Configuration.schema.json -users_database.test.yml - .suite .kube .idea .authelia-interrupt -qemu-*-static \ No newline at end of file +qemu-*-static diff --git a/.travis.yml b/.travis.yml index ff24e4f36..b6bc90584 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,25 +10,34 @@ services: - ntp - xvfb +addons: + chrome: stable + apt: + sources: + - google-chrome + packages: + - libgif-dev + - google-chrome-stable + +install: # Install ChromeDriver (64bits; replace 64 with 32 for 32bits). + - wget -N https://chromedriver.storage.googleapis.com/78.0.3904.70/chromedriver_linux64.zip -P ~/ + - unzip ~/chromedriver_linux64.zip -d ~/ + - rm ~/chromedriver_linux64.zip + - sudo mv -f ~/chromedriver /usr/local/share/ + - sudo chmod +x /usr/local/share/chromedriver + - sudo ln -s /usr/local/share/chromedriver /usr/bin/chromedriver + before_script: - export PATH=./cmd/authelia-scripts/:/tmp:$PATH + - curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash + - nvm install v11 && nvm use v11 && npm i + - source bootstrap.sh jobs: include: - stage: test - addons: - chrome: stable - apt: - sources: - - google-chrome - packages: - - libgif-dev - - google-chrome-stable script: - - curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash - - nvm install v11 && nvm use v11 && npm i - - source bootstrap.sh - - authelia-scripts ci + - authelia-scripts --log-level debug ci # TODO(c.michaud): publish built artifact on Github. - &build-images stage: build images diff --git a/bootstrap.sh b/bootstrap.sh index 7e147962b..3d69b3b85 100644 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -1,6 +1,6 @@ #!/bin/bash -export PATH=./cmd/authelia-scripts/:/tmp:$PATH +export PATH=./cmd/authelia-scripts/:/tmp:$PATH:./node_modules/.bin if [ -z "$OLD_PS1" ]; then OLD_PS1="$PS1" diff --git a/client/.env.development b/client/.env.development index a708a53b5..174da0599 100644 --- a/client/.env.development +++ b/client/.env.development @@ -1,2 +1,3 @@ +HOST=authelia-frontend REACT_APP_CSP_CONTENT="default-src 'unsafe-inline'; script-src * 'unsafe-inline'; img-src * data:; style-src 'unsafe-inline'; connect-src * 'unsafe-inline' extensions:" diff --git a/client/package.json b/client/package.json index c0002df5e..b86432378 100644 --- a/client/package.json +++ b/client/package.json @@ -47,5 +47,6 @@ "not dead", "not ie <= 11", "not op_mini all" - ] + ], + "proxy": "http://authelia-backend:9091/" } diff --git a/cmd/authelia-scripts/cmd_bootstrap.go b/cmd/authelia-scripts/cmd_bootstrap.go index d556721df..b13b3fe3a 100644 --- a/cmd/authelia-scripts/cmd_bootstrap.go +++ b/cmd/authelia-scripts/cmd_bootstrap.go @@ -8,6 +8,7 @@ import ( "os/exec" "strings" + "github.com/clems4ever/authelia/utils" "github.com/spf13/cobra" ) @@ -41,7 +42,7 @@ var hostEntries = []HostEntry{ } func runCommand(cmd string, args ...string) { - command := CommandWithStdout(cmd, args...) + command := utils.CommandWithStdout(cmd, args...) err := command.Run() if err != nil { @@ -66,7 +67,7 @@ func checkCommandExist(cmd string) { } func installClientNpmPackages() { - command := CommandWithStdout("npm", "ci") + command := utils.CommandWithStdout("npm", "ci") command.Dir = "client" err := command.Run() @@ -97,20 +98,10 @@ func shell(cmd string) { func buildHelperDockerImages() { shell("docker build -t authelia-example-backend example/compose/nginx/backend") shell("docker build -t authelia-duo-api example/compose/duo-api") -} -func installKubernetesDependencies() { - if exist, err := FileExists("/tmp/kind"); err == nil && !exist { - shell("wget -nv https://github.com/kubernetes-sigs/kind/releases/download/v0.5.1/kind-linux-amd64 -O /tmp/kind && chmod +x /tmp/kind") - } else { - bootstrapPrintln("Skip installing Kind since it's already installed") - } - - if exist, err := FileExists("/tmp/kubectl"); err == nil && !exist { - shell("wget -nv https://storage.googleapis.com/kubernetes-release/release/v1.13.0/bin/linux/amd64/kubectl -O /tmp/kubectl && chmod +x /tmp/kubectl") - } else { - bootstrapPrintln("Skip installing Kubectl since it's already installed") - } + shell("docker-compose -f docker-compose.yml -f example/compose/kind/docker-compose.yml build") + shell("docker-compose -f docker-compose.yml -f example/compose/authelia/docker-compose.backend.yml build --build-arg USER_ID=$(id -u) --build-arg GROUP_ID=$(id -g)") + shell("docker-compose -f docker-compose.yml -f example/compose/authelia/docker-compose.frontend.yml build --build-arg USER_ID=$(id -u) --build-arg GROUP_ID=$(id -g)") } func prepareHostsFile() { @@ -203,24 +194,31 @@ func Bootstrap(cobraCmd *cobra.Command, args []string) { bootstrapPrintln("Getting versions of tools") readVersions() + bootstrapPrintln("Checking if GOPATH is set") + + goPathFound := false + for _, v := range os.Environ() { + if strings.HasPrefix(v, "GOPATH=") { + goPathFound = true + break + } + } + + if !goPathFound { + log.Fatal("GOPATH is not set") + } + bootstrapPrintln("Installing NPM packages for development...") installNpmPackages() - bootstrapPrintln("Install NPM packages for frontend...") - installClientNpmPackages() - bootstrapPrintln("Building development Docker images...") buildHelperDockerImages() - dockerBuildOfficialImage(defaultArch) - - bootstrapPrintln("Installing Kubernetes dependencies for testing in /tmp... (no junk installed on host)") - installKubernetesDependencies() createTemporaryDirectory() bootstrapPrintln("Preparing /etc/hosts to serve subdomains of example.com...") prepareHostsFile() - bootstrapPrintln("Run 'authelia-scripts suites start docker-image' to start Authelia and visit https://home.example.com:8080.") + bootstrapPrintln("Run 'authelia-scripts suites setup Standalone' to start Authelia and visit https://home.example.com:8080.") bootstrapPrintln("More details at https://github.com/clems4ever/authelia/blob/master/docs/getting-started.md") } diff --git a/cmd/authelia-scripts/cmd_build.go b/cmd/authelia-scripts/cmd_build.go index 8023cd1eb..326d38979 100644 --- a/cmd/authelia-scripts/cmd_build.go +++ b/cmd/authelia-scripts/cmd_build.go @@ -1,14 +1,15 @@ package main import ( - "fmt" "os" + "github.com/clems4ever/authelia/utils" + log "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) func buildAutheliaBinary() { - cmd := CommandWithStdout("go", "build", "-o", "../../"+OutputDir+"/authelia") + cmd := utils.CommandWithStdout("go", "build", "-o", "../../"+OutputDir+"/authelia") cmd.Dir = "cmd/authelia" cmd.Env = append(os.Environ(), "GOOS=linux", "GOARCH=amd64", "CGO_ENABLED=1") @@ -21,35 +22,43 @@ func buildAutheliaBinary() { } func buildFrontend() { - cmd := CommandWithStdout("npm", "run", "build") + // Install npm dependencies + cmd := utils.CommandWithStdout("npm", "ci") cmd.Dir = "client" - err := cmd.Run() - if err != nil { - panic(err) + if err := cmd.Run(); err != nil { + log.Fatal(err) } - err = os.Rename("client/build", OutputDir+"/public_html") + // Then build the frontend + cmd = utils.CommandWithStdout("npm", "run", "build") + cmd.Dir = "client" - if err != nil { - panic(err) + if err := cmd.Run(); err != nil { + log.Fatal(err) + } + + if err := os.Rename("client/build", OutputDir+"/public_html"); err != nil { + log.Fatal(err) } } // Build build Authelia func Build(cobraCmd *cobra.Command, args []string) { + log.Info("Building Authelia...") + Clean(cobraCmd, args) - fmt.Println("Creating `" + OutputDir + "` directory") + log.Debug("Creating `" + OutputDir + "` directory") err := os.MkdirAll(OutputDir, os.ModePerm) if err != nil { panic(err) } - fmt.Println("Building Authelia Go binary...") + log.Debug("Building Authelia Go binary...") buildAutheliaBinary() - fmt.Println("Building Authelia frontend...") + log.Debug("Building Authelia frontend...") buildFrontend() } diff --git a/cmd/authelia-scripts/cmd_ci.go b/cmd/authelia-scripts/cmd_ci.go index b28003d94..dc5a64fe6 100644 --- a/cmd/authelia-scripts/cmd_ci.go +++ b/cmd/authelia-scripts/cmd_ci.go @@ -1,8 +1,8 @@ package main import ( - "fmt" - + "github.com/clems4ever/authelia/utils" + log "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -18,34 +18,18 @@ const dockerPullCommandLine = "docker-compose -f docker-compose.yml " + // RunCI run the CI scripts func RunCI(cmd *cobra.Command, args []string) { - command := CommandWithStdout("bash", "-c", dockerPullCommandLine) - err := command.Run() - - if err != nil { - panic(err) + log.Info("=====> Build stage") + if err := utils.CommandWithStdout("authelia-scripts", "--log-level", "debug", "build").Run(); err != nil { + log.Fatal(err) } - fmt.Println("===== Build stage =====") - command = CommandWithStdout("authelia-scripts", "build") - err = command.Run() - - if err != nil { - panic(err) + log.Info("=====> Unit testing stage") + if err := utils.CommandWithStdout("authelia-scripts", "--log-level", "debug", "unittest").Run(); err != nil { + log.Fatal(err) } - fmt.Println("===== Unit testing stage =====") - command = CommandWithStdout("authelia-scripts", "unittest") - err = command.Run() - - if err != nil { - panic(err) - } - - fmt.Println("===== End-to-end testing stage =====") - command = CommandWithStdout("authelia-scripts", "suites", "test", "--headless", "--only-forbidden") - err = command.Run() - - if err != nil { - panic(err) + log.Info("=====> End-to-end testing stage") + if err := utils.CommandWithStdout("authelia-scripts", "--log-level", "debug", "suites", "test", "--headless", "--only-forbidden").Run(); err != nil { + log.Fatal(err) } } diff --git a/cmd/authelia-scripts/cmd_clean.go b/cmd/authelia-scripts/cmd_clean.go index 612652ad3..5b1aef47f 100644 --- a/cmd/authelia-scripts/cmd_clean.go +++ b/cmd/authelia-scripts/cmd_clean.go @@ -1,15 +1,15 @@ package main import ( - "fmt" "os" + log "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) // Clean artifacts built and installed by authelia-scripts func Clean(cobraCmd *cobra.Command, args []string) { - fmt.Println("Removing `" + OutputDir + "` directory") + log.Debug("Removing `" + OutputDir + "` directory") err := os.RemoveAll(OutputDir) if err != nil { diff --git a/cmd/authelia-scripts/cmd_docker.go b/cmd/authelia-scripts/cmd_docker.go index 3ddca4fcc..430509297 100644 --- a/cmd/authelia-scripts/cmd_docker.go +++ b/cmd/authelia-scripts/cmd_docker.go @@ -3,10 +3,11 @@ package main import ( "errors" "fmt" - "log" "os" "strings" + "github.com/clems4ever/authelia/utils" + log "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -40,25 +41,25 @@ func dockerBuildOfficialImage(arch string) error { } if arch == "arm32v7" { - err := CommandWithStdout("docker", "run", "--rm", "--privileged", "multiarch/qemu-user-static", "--reset", "-p", "yes").Run() + err := utils.CommandWithStdout("docker", "run", "--rm", "--privileged", "multiarch/qemu-user-static", "--reset", "-p", "yes").Run() if err != nil { panic(err) } - err = CommandWithStdout("bash", "-c", "wget https://github.com/multiarch/qemu-user-static/releases/download/v4.1.0-1/qemu-arm-static -O ./qemu-arm-static && chmod +x ./qemu-arm-static").Run() + err = utils.CommandWithStdout("bash", "-c", "wget https://github.com/multiarch/qemu-user-static/releases/download/v4.1.0-1/qemu-arm-static -O ./qemu-arm-static && chmod +x ./qemu-arm-static").Run() if err != nil { panic(err) } } else if arch == "arm64v8" { - err := CommandWithStdout("docker", "run", "--rm", "--privileged", "multiarch/qemu-user-static", "--reset", "-p", "yes").Run() + err := utils.CommandWithStdout("docker", "run", "--rm", "--privileged", "multiarch/qemu-user-static", "--reset", "-p", "yes").Run() if err != nil { panic(err) } - err = CommandWithStdout("bash", "-c", "wget https://github.com/multiarch/qemu-user-static/releases/download/v4.1.0-1/qemu-aarch64-static -O ./qemu-aarch64-static && chmod +x ./qemu-aarch64-static").Run() + err = utils.CommandWithStdout("bash", "-c", "wget https://github.com/multiarch/qemu-user-static/releases/download/v4.1.0-1/qemu-aarch64-static -O ./qemu-aarch64-static && chmod +x ./qemu-aarch64-static").Run() if err != nil { panic(err) @@ -73,6 +74,7 @@ var DockerBuildCmd = &cobra.Command{ Use: "build", Short: "Build the docker image of Authelia", Run: func(cmd *cobra.Command, args []string) { + log.Infof("Building Docker image %s...", DockerImageName) checkArchIsSupported(arch) err := dockerBuildOfficialImage(arch) @@ -94,6 +96,7 @@ var DockerPushCmd = &cobra.Command{ Use: "push-image", Short: "Publish Authelia docker image to Dockerhub", Run: func(cmd *cobra.Command, args []string) { + log.Infof("Pushing Docker image %s to dockerhub...", DockerImageName) checkArchIsSupported(arch) publishDockerImage(arch) }, @@ -104,6 +107,7 @@ var DockerManifestCmd = &cobra.Command{ Use: "push-manifest", Short: "Publish Authelia docker manifest to Dockerhub", Run: func(cmd *cobra.Command, args []string) { + log.Infof("Pushing Docker manifest of %s to dockerhub...", DockerImageName) publishDockerManifest() }, } @@ -113,76 +117,59 @@ func login(docker *Docker) { password := os.Getenv("DOCKER_PASSWORD") if username == "" { - panic(errors.New("DOCKER_USERNAME is empty")) + log.Fatal(errors.New("DOCKER_USERNAME is empty")) } if password == "" { - panic(errors.New("DOCKER_PASSWORD is empty")) + log.Fatal(errors.New("DOCKER_PASSWORD is empty")) } - fmt.Println("Login to dockerhub as " + username) + log.Debug("Login to dockerhub as " + username) err := docker.Login(username, password) if err != nil { - fmt.Println("Login to dockerhub failed") - panic(err) + log.Fatal("Login to dockerhub failed", err) } } func deploy(docker *Docker, tag string) { imageWithTag := DockerImageName + ":" + tag - fmt.Println("===================================================") - fmt.Println("Docker image " + imageWithTag + " will be deployed on Dockerhub.") - fmt.Println("===================================================") + log.Debug("Docker image " + imageWithTag + " will be deployed on Dockerhub.") - err := docker.Tag(DockerImageName, imageWithTag) - - if err != nil { - panic(err) + if err := docker.Tag(DockerImageName, imageWithTag); err != nil { + log.Fatal(err) } - err = docker.Push(imageWithTag) - - if err != nil { - panic(err) + if err := docker.Push(imageWithTag); err != nil { + log.Fatal(err) } } func deployManifest(docker *Docker, tag string, amd64tag string, arm32v7tag string, arm64v8tag string) { dockerImagePrefix := DockerImageName + ":" - fmt.Println("===================================================") - fmt.Println("Docker manifest " + dockerImagePrefix + tag + " will be deployed on Dockerhub.") - fmt.Println("===================================================") + log.Debug("Docker manifest " + dockerImagePrefix + tag + " will be deployed on Dockerhub.") err := docker.Manifest(dockerImagePrefix+tag, dockerImagePrefix+amd64tag, dockerImagePrefix+arm32v7tag, dockerImagePrefix+arm64v8tag) if err != nil { - panic(err) + log.Fatal(err) } tags := []string{amd64tag, arm32v7tag, arm64v8tag} for _, t := range tags { - fmt.Println("===================================================") - fmt.Println("Docker removing tag for " + dockerImagePrefix + t + " on Dockerhub.") - fmt.Println("===================================================") + log.Debug("Docker removing tag for " + dockerImagePrefix + t + " on Dockerhub.") - err = docker.CleanTag(t) - - if err != nil { + if err := docker.CleanTag(t); err != nil { panic(err) } } - fmt.Println("===================================================") - fmt.Println("Docker pushing README.md to Dockerhub.") - fmt.Println("===================================================") + log.Debug("Docker pushing README.md to Dockerhub.") - err = docker.PublishReadme() - - if err != nil { - panic(err) + if err := docker.PublishReadme(); err != nil { + log.Fatal(err) } } @@ -201,7 +188,7 @@ func publishDockerImage(arch string) { deploy(docker, travisTag+"-"+arch) deploy(docker, "latest-"+arch) } else { - fmt.Println("Docker image will not be published") + log.Info("Docker image will not be published") } } diff --git a/cmd/authelia-scripts/cmd_serve.go b/cmd/authelia-scripts/cmd_serve.go index 073adf5ed..f949611dd 100644 --- a/cmd/authelia-scripts/cmd_serve.go +++ b/cmd/authelia-scripts/cmd_serve.go @@ -3,12 +3,15 @@ package main import ( "os" + "github.com/clems4ever/authelia/utils" + log "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) // ServeCmd serve authelia with the provided configuration func ServeCmd(cobraCmd *cobra.Command, args []string) { - cmd := CommandWithStdout(OutputDir+"/authelia", "-config", args[0]) + log.Infof("Running Authelia with config %s...", args[0]) + cmd := utils.CommandWithStdout(OutputDir+"/authelia", "-config", args[0]) cmd.Env = append(os.Environ(), "PUBLIC_DIR=dist/public_html") - RunCommandUntilCtrlC(cmd) + utils.RunCommandUntilCtrlC(cmd) } diff --git a/cmd/authelia-scripts/cmd_suites.go b/cmd/authelia-scripts/cmd_suites.go index 6eddf438c..b4127fabf 100644 --- a/cmd/authelia-scripts/cmd_suites.go +++ b/cmd/authelia-scripts/cmd_suites.go @@ -5,48 +5,33 @@ import ( "fmt" "io/ioutil" "os" - "os/exec" "os/signal" + "sort" "strings" "syscall" + "time" + "github.com/clems4ever/authelia/suites" + "github.com/clems4ever/authelia/utils" + log "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) -func listDirectories(path string) ([]string, error) { - files, err := ioutil.ReadDir(path) - if err != nil { - return nil, err - } +// ErrNotAvailableSuite error raised when suite is not available. +var ErrNotAvailableSuite = errors.New("unavailable suite") - dirs := make([]string, 0) +// ErrNoRunningSuite error raised when no suite is running +var ErrNoRunningSuite = errors.New("no running suite") - for _, f := range files { - if f.IsDir() { - dirs = append(dirs, f.Name()) - } - } +// runningSuiteFile name of the file containing the currently running suite +var runningSuiteFile = ".suite" - return dirs, nil -} +var headless bool +var onlyForbidden bool -func listSuites() ([]string, error) { - return listDirectories("./test/suites/") -} - -func suiteAvailable(suite string, suites []string) (bool, error) { - suites, err := listSuites() - - if err != nil { - return false, err - } - - for _, s := range suites { - if s == suite { - return true, nil - } - } - return false, nil +func init() { + SuitesTestCmd.Flags().BoolVar(&headless, "headless", false, "Run tests in headless mode") + SuitesTestCmd.Flags().BoolVar(&onlyForbidden, "only-forbidden", false, "Mocha 'only' filters are forbidden") } // SuitesListCmd Command for listing the available suites @@ -54,117 +39,166 @@ var SuitesListCmd = &cobra.Command{ Use: "list", Short: "List available suites.", Run: func(cmd *cobra.Command, args []string) { - suites, err := listSuites() - - if err != nil { - panic(err) - } - - fmt.Println(strings.Join(suites, "\n")) + fmt.Println(strings.Join(listSuites(), "\n")) }, Args: cobra.ExactArgs(0), } -// SuitesCleanCmd Command for cleaning suite environments -var SuitesCleanCmd = &cobra.Command{ - Use: "clean", - Short: "Clean suite environments.", +// SuitesSetupCmd Command for setuping a suite environment +var SuitesSetupCmd = &cobra.Command{ + Use: "setup [suite]", + Short: "Setup a Go suite environment. Suites can be listed using the list command.", Run: func(cmd *cobra.Command, args []string) { - command := CommandWithStdout("bash", "-c", - "./node_modules/.bin/ts-node -P test/tsconfig.json -- ./scripts/clean-environment.ts") - err := command.Run() + providedSuite := args[0] + runningSuite, err := getRunningSuite() if err != nil { - panic(err) - } - }, - Args: cobra.ExactArgs(0), -} - -// SuitesStartCmd Command for starting a suite -var SuitesStartCmd = &cobra.Command{ - Use: "start [suite]", - Short: "Start a suite. Suites can be listed using the list command.", - Run: func(cmd *cobra.Command, args []string) { - suites, err := listSuites() - - if err != nil { - panic(err) + log.Fatal(err) } - selectedSuite := args[0] - - available, err := suiteAvailable(selectedSuite, suites) - - if err != nil { - panic(err) + if runningSuite != "" && runningSuite != providedSuite { + log.Fatal("A suite is already running") } - if !available { - panic(errors.New("Suite named " + selectedSuite + " does not exist")) - } - - err = ioutil.WriteFile(RunningSuiteFile, []byte(selectedSuite), 0644) - - if err != nil { - panic(err) - } - - signalChannel := make(chan os.Signal) - signal.Notify(signalChannel, os.Interrupt, syscall.SIGTERM) - - cmdline := "./node_modules/.bin/ts-node -P test/tsconfig.json -- ./scripts/run-environment.ts " + selectedSuite - command := CommandWithStdout("bash", "-c", cmdline) - command.Env = append(os.Environ(), "ENVIRONMENT=dev") - - err = command.Run() - - if err != nil { - panic(err) - } - - err = os.Remove(RunningSuiteFile) - - if err != nil { - panic(err) + if err := setupSuite(providedSuite); err != nil { + log.Fatal(err) } }, Args: cobra.ExactArgs(1), } +// SuitesTeardownCmd Command for tearing down a suite environment +var SuitesTeardownCmd = &cobra.Command{ + Use: "teardown [suite]", + Short: "Teardown a Go suite environment. Suites can be listed using the list command.", + Run: func(cmd *cobra.Command, args []string) { + var suiteName string + if len(args) == 1 { + suiteName = args[0] + } else { + runningSuite, err := getRunningSuite() + + if err != nil { + panic(err) + } + + if runningSuite == "" { + panic(ErrNoRunningSuite) + } + suiteName = runningSuite + } + + if err := teardownSuite(suiteName); err != nil { + panic(err) + } + }, + Args: cobra.MaximumNArgs(1), +} + // SuitesTestCmd Command for testing a suite var SuitesTestCmd = &cobra.Command{ Use: "test [suite]", Short: "Test a suite. Suites can be listed using the list command.", - Run: func(cmd *cobra.Command, args []string) { - runningSuite, err := getRunningSuite() - if err != nil { - panic(err) + Run: testSuite, + Args: cobra.MaximumNArgs(1), +} + +func listSuites() []string { + suiteNames := make([]string, 0) + for _, k := range suites.GlobalRegistry.Suites() { + suiteNames = append(suiteNames, k) + } + sort.Strings(suiteNames) + return suiteNames +} + +func checkSuiteAvailable(suite string) error { + suites := listSuites() + + for _, s := range suites { + if s == suite { + return nil + } + } + return ErrNotAvailableSuite +} + +func runSuiteSetupTeardown(command string, suite string) error { + selectedSuite := suite + err := checkSuiteAvailable(selectedSuite) + + if err != nil { + if err == ErrNotAvailableSuite { + log.Fatal(errors.New("Suite named " + selectedSuite + " does not exist")) + } + log.Fatal(err) + } + + s := suites.GlobalRegistry.Get(selectedSuite) + + cmd := utils.CommandWithStdout("bash", "-c", "go run cmd/authelia-suites/*.go "+command+" "+selectedSuite) + cmd.Env = os.Environ() + return utils.RunCommandWithTimeout(cmd, s.SetUpTimeout) +} + +func setupSuite(suiteName string) error { + log.Infof("Setup environment for suite %s...", suiteName) + signalChannel := make(chan os.Signal) + signal.Notify(signalChannel, os.Interrupt, syscall.SIGTERM) + + interrupted := false + + go func() { + <-signalChannel + interrupted = true + }() + + if errSetup := runSuiteSetupTeardown("setup", suiteName); errSetup != nil || interrupted { + teardownSuite(suiteName) + return errSetup + } + + return nil +} + +func teardownSuite(suiteName string) error { + log.Infof("Tear down environment for suite %s...", suiteName) + return runSuiteSetupTeardown("teardown", suiteName) +} + +func testSuite(cmd *cobra.Command, args []string) { + runningSuite, err := getRunningSuite() + if err != nil { + log.Fatal(err) + } + + if len(args) == 1 { + suite := args[0] + + if runningSuite != "" && suite != runningSuite { + log.Fatal(errors.New("Running suite (" + runningSuite + ") is different than suite to be tested (" + suite + "). Shutdown running suite and retry")) } - if len(args) == 1 { - suite := args[0] - - if runningSuite != "" && suite != runningSuite { - panic(errors.New("Running suite (" + runningSuite + ") is different than suite to be tested (" + suite + "). Shutdown running suite and retry")) + if err := runSuiteTests(suite, runningSuite == ""); err != nil { + log.Fatal(err) + } + } else { + if runningSuite != "" { + fmt.Println("Running suite (" + runningSuite + ") detected. Run tests of that suite") + if err := runSuiteTests(runningSuite, false); err != nil { + log.Fatal(err) } - - runSuiteTests(suite, runningSuite == "") } else { - if runningSuite != "" { - fmt.Println("Running suite (" + runningSuite + ") detected. Run tests of that suite") - runSuiteTests(runningSuite, false) - } else { - fmt.Println("No suite provided therefore all suites will be tested") - runAllSuites() + fmt.Println("No suite provided therefore all suites will be tested") + if err := runAllSuites(); err != nil { + log.Fatal(err) } } - }, - Args: cobra.MaximumNArgs(1), + } } func getRunningSuite() (string, error) { - exist, err := FileExists(RunningSuiteFile) + exist, err := utils.FileExists(runningSuiteFile) if err != nil { return "", err @@ -174,61 +208,51 @@ func getRunningSuite() (string, error) { return "", nil } - b, err := ioutil.ReadFile(RunningSuiteFile) + b, err := ioutil.ReadFile(runningSuiteFile) return string(b), err } -func runSuiteTests(suite string, withEnv bool) { - mochaArgs := []string{"--exit", "--colors", "--require", "ts-node/register", "test/suites/" + suite + "/test.ts"} - if onlyForbidden { - mochaArgs = append(mochaArgs, "--forbid-only", "--forbid-pending") +func runSuiteTests(suiteName string, withEnv bool) error { + if withEnv { + if err := setupSuite(suiteName); err != nil { + return err + } } - mochaCmdLine := "./node_modules/.bin/mocha " + strings.Join(mochaArgs, " ") - fmt.Println(mochaCmdLine) + suite := suites.GlobalRegistry.Get(suiteName) - headlessValue := "n" + // Default value is 1 minute + timeout := "60s" + if suite.TestTimeout > 0 { + timeout = fmt.Sprintf("%ds", int64(suite.TestTimeout/time.Second)) + } + testCmdLine := fmt.Sprintf("go test ./suites -timeout %s -run '^(Test%sSuite)$'", timeout, suiteName) + + log.Infof("Running tests of suite %s...", suiteName) + log.Debugf("Running tests with command: %s", testCmdLine) + + cmd := utils.CommandWithStdout("bash", "-c", testCmdLine) + cmd.Env = os.Environ() if headless { - headlessValue = "y" + cmd.Env = append(cmd.Env, "HEADLESS=y") } - var cmd *exec.Cmd + testErr := cmd.Run() if withEnv { - cmd = CommandWithStdout("bash", "-c", - "./node_modules/.bin/ts-node ./scripts/run-environment.ts "+suite+" '"+mochaCmdLine+"'") - } else { - cmd = CommandWithStdout("bash", "-c", mochaCmdLine) + teardownSuite(suiteName) } - cmd.Env = append(os.Environ(), - "TS_NODE_PROJECT=test/tsconfig.json", - "HEADLESS="+headlessValue, - "ENVIRONMENT=dev") - - err := cmd.Run() - - if err != nil { - panic(err) - } + return testErr } -func runAllSuites() { - suites, err := listSuites() - - if err != nil { - panic(err) - } - - for _, s := range suites { - runSuiteTests(s, true) +func runAllSuites() error { + log.Info("Start running all suites") + for _, s := range listSuites() { + if err := runSuiteTests(s, true); err != nil { + return err + } } -} - -var headless bool -var onlyForbidden bool - -func init() { - SuitesTestCmd.Flags().BoolVar(&headless, "headless", false, "Run tests in headless mode") - SuitesTestCmd.Flags().BoolVar(&onlyForbidden, "only-forbidden", false, "Mocha 'only' filters are forbidden") + log.Info("All suites passed successfully") + return nil } diff --git a/cmd/authelia-scripts/cmd_unittest.go b/cmd/authelia-scripts/cmd_unittest.go index 13cd4e854..a191018f7 100644 --- a/cmd/authelia-scripts/cmd_unittest.go +++ b/cmd/authelia-scripts/cmd_unittest.go @@ -1,11 +1,15 @@ package main -import "github.com/spf13/cobra" +import ( + "github.com/clems4ever/authelia/utils" + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) // RunUnitTest run the unit tests func RunUnitTest(cobraCmd *cobra.Command, args []string) { - err := CommandWithStdout("go", "test", "./...").Run() + err := utils.Shell("go test $(go list ./... | grep -v suites)").Run() if err != nil { - panic(err) + log.Fatal(err) } } diff --git a/cmd/authelia-scripts/constants.go b/cmd/authelia-scripts/constants.go index 185c5b1eb..728e01a7f 100644 --- a/cmd/authelia-scripts/constants.go +++ b/cmd/authelia-scripts/constants.go @@ -8,6 +8,3 @@ var DockerImageName = "clems4ever/authelia" // IntermediateDockerImageName local name of the docker image var IntermediateDockerImageName = "authelia:dist" - -// RunningSuiteFile name of the file containing the currently running suite -var RunningSuiteFile = ".suite" diff --git a/cmd/authelia-scripts/docker.go b/cmd/authelia-scripts/docker.go index 63d430502..d9c0e8ccd 100644 --- a/cmd/authelia-scripts/docker.go +++ b/cmd/authelia-scripts/docker.go @@ -1,57 +1,61 @@ package main +import ( + "github.com/clems4ever/authelia/utils" +) + // Docker a docker object type Docker struct{} // Build build a docker image func (d *Docker) Build(tag, dockerfile, target string) error { - return CommandWithStdout("docker", "build", "-t", tag, "-f", dockerfile, target).Run() + return utils.CommandWithStdout("docker", "build", "-t", tag, "-f", dockerfile, target).Run() } // Tag tag a docker image. func (d *Docker) Tag(image, tag string) error { - return CommandWithStdout("docker", "tag", image, tag).Run() + return utils.CommandWithStdout("docker", "tag", image, tag).Run() } // Login login to the dockerhub registry. func (d *Docker) Login(username, password string) error { - return CommandWithStdout("docker", "login", "-u", username, "-p", password).Run() + return utils.CommandWithStdout("docker", "login", "-u", username, "-p", password).Run() } // Push push a docker image to dockerhub. func (d *Docker) Push(tag string) error { - return CommandWithStdout("docker", "push", tag).Run() + return utils.CommandWithStdout("docker", "push", tag).Run() } // Manifest push a docker manifest to dockerhub. func (d *Docker) Manifest(tag, amd64tag, arm32v7tag, arm64v8tag string) error { - err := CommandWithStdout("docker", "manifest", "create", tag, amd64tag, arm32v7tag, arm64v8tag).Run() + err := utils.CommandWithStdout("docker", "manifest", "create", tag, amd64tag, arm32v7tag, arm64v8tag).Run() if err != nil { panic(err) } - err = CommandWithStdout("docker", "manifest", "annotate", tag, arm32v7tag, "--os", "linux", "--arch", "arm").Run() + err = utils.CommandWithStdout("docker", "manifest", "annotate", tag, arm32v7tag, "--os", "linux", "--arch", "arm").Run() if err != nil { panic(err) } - err = CommandWithStdout("docker", "manifest", "annotate", tag, arm64v8tag, "--os", "linux", "--arch", "arm64", "--variant", "v8").Run() + err = utils.CommandWithStdout("docker", "manifest", "annotate", tag, arm64v8tag, "--os", "linux", "--arch", "arm64", "--variant", "v8").Run() if err != nil { panic(err) } - return CommandWithStdout("docker", "manifest", "push", "--purge", tag).Run() + return utils.CommandWithStdout("docker", "manifest", "push", "--purge", tag).Run() } // CleanTag remove a tag from dockerhub. func (d *Docker) CleanTag(tag string) error { - return CommandWithStdout("bash", "-c", "curl -s -o /dev/null -u $DOCKER_USERNAME:$DOCKER_PASSWORD -X DELETE https://cloud.docker.com/v2/repositories/"+DockerImageName+"/tags/"+tag+"/").Run() + return utils.CommandWithStdout("bash", "-c", "curl -s -o /dev/null -u $DOCKER_USERNAME:$DOCKER_PASSWORD -X DELETE https://cloud.docker.com/v2/repositories/"+DockerImageName+"/tags/"+tag+"/").Run() } // PublishReadme push README.md to dockerhub. func (d *Docker) PublishReadme() error { - return CommandWithStdout("bash", "-c", `jq -n --arg msg "$( server { listen 8080 ssl; server_name login.example.com; resolver 127.0.0.11 ipv6=off; - set $backend_endpoint <%= authelia_backend %>; - - ssl_certificate /etc/ssl/server.cert; - ssl_certificate_key /etc/ssl/server.key; - - add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; - add_header X-Frame-Options "SAMEORIGIN"; - - # Serves the portal application. - location / { - proxy_pass $backend_endpoint; - } - - location /static { - proxy_pass $backend_endpoint; - } - - # Serve the backend API for the portal. - location /api { - proxy_set_header X-Real-IP $remote_addr; - - # Required by Authelia because "trust proxy" option is used. - # See https://expressjs.com/en/guide/behind-proxies.html - proxy_set_header X-Forwarded-Proto $scheme; - - # Required by Authelia to build correct links for identity validation. - proxy_set_header X-Forwarded-Host $http_host; - - # Needed for network ACLs to work. It appends the IP of the client to the list of IPs - # and allows Authelia to use it to match the network-based ACLs. - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - - proxy_intercept_errors on; - - proxy_pass $backend_endpoint; - } - } - <% } else { %> - server { - listen 8080 ssl; - server_name login.example.com; - - resolver 127.0.0.11 ipv6=off; - set $frontend_endpoint http://192.168.240.1:3000; - set $backend_endpoint <%= authelia_backend %>; + set $frontend_endpoint http://authelia-frontend:3000; + set $backend_endpoint http://authelia-backend:9091; ssl_certificate /etc/ssl/server.cert; ssl_certificate_key /etc/ssl/server.key; @@ -94,11 +48,11 @@ http { proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; + proxy_set_header Host "127.0.0.1"; proxy_pass $frontend_endpoint; } } - <% } %> # Serves the home page. server { @@ -131,7 +85,7 @@ http { mx1.mail.example.com mx2.mail.example.com; resolver 127.0.0.11 ipv6=off; - set $upstream_verify <%= authelia_backend %>/api/verify; + set $upstream_verify http://authelia-backend:9091/api/verify; set $upstream_endpoint http://nginx-backend; set $upstream_headers http://httpbin:8000/headers; diff --git a/example/compose/nginx/portal/render.js b/example/compose/nginx/portal/render.js deleted file mode 100755 index 607ff70d3..000000000 --- a/example/compose/nginx/portal/render.js +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env node - -const ejs = require('ejs'); -const fs = require('fs'); -const program = require('commander'); - -let backend; - -program - .version('0.1.0') - .option('-p, --production', 'Render template for production.') - .arguments('[backend]') - .action((backendArg) => backend = backendArg) - .parse(process.argv) - -const options = { - production: false, -} - -if (!backend) { - backend = 'http://192.168.240.1:9091' -} - -if (program.production) { - options['production'] = true; -} - -options['authelia_backend'] = backend; - -const templatePath = __dirname + '/nginx.conf.ejs'; -const outputPath = __dirname + '/nginx.conf'; - -html = ejs.renderFile(templatePath, options, (err, conf) => { - try { - var fd = fs.openSync(outputPath, 'w'); - fs.writeFileSync(fd, conf); - } catch (e) { - fs.writeFileSync(outputPath, conf); - } -}); \ No newline at end of file diff --git a/example/compose/redis/docker-compose.yml b/example/compose/redis/docker-compose.yml index 04bdb3e8f..99ff5197a 100644 --- a/example/compose/redis/docker-compose.yml +++ b/example/compose/redis/docker-compose.yml @@ -1,4 +1,4 @@ -version: '2' +version: '3' services: redis: image: redis:4.0-alpine diff --git a/example/compose/smtp/docker-compose.yml b/example/compose/smtp/docker-compose.yml index f79140424..644e2a658 100644 --- a/example/compose/smtp/docker-compose.yml +++ b/example/compose/smtp/docker-compose.yml @@ -1,4 +1,4 @@ -version: '2' +version: '3' services: smtp: image: schickling/mailcatcher diff --git a/example/compose/squid/docker-compose.yml b/example/compose/squid/docker-compose.yml index b24c146a9..9776ba993 100644 --- a/example/compose/squid/docker-compose.yml +++ b/example/compose/squid/docker-compose.yml @@ -1,4 +1,4 @@ -version: '2' +version: '3' services: # Simulates client 1. client-1: diff --git a/example/compose/traefik/.gitignore b/example/compose/traefik/.gitignore deleted file mode 100644 index f2ab7784c..000000000 --- a/example/compose/traefik/.gitignore +++ /dev/null @@ -1 +0,0 @@ -traefik.toml \ No newline at end of file diff --git a/example/compose/traefik/docker-compose.yml b/example/compose/traefik/docker-compose.yml index 1eac19a97..f06460713 100644 --- a/example/compose/traefik/docker-compose.yml +++ b/example/compose/traefik/docker-compose.yml @@ -1,4 +1,4 @@ -version: '2' +version: '3' services: traefik: image: traefik:v1.7.9-alpine @@ -11,4 +11,4 @@ services: networks: authelianet: # Set the IP to be able to query on port 443 - ipv4_address: 192.168.240.100 + ipv4_address: 192.168.240.100 \ No newline at end of file diff --git a/example/compose/traefik/render.js b/example/compose/traefik/render.js deleted file mode 100755 index 5a4441ae8..000000000 --- a/example/compose/traefik/render.js +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env node - -const ejs = require('ejs'); -const fs = require('fs'); -const program = require('commander'); - -let backend; - -program - .version('0.1.0') - .option('-p, --production', 'Render template for production.') - .arguments('[backend]') - .action((backendArg) => backend = backendArg) - .parse(process.argv) - -const options = { - production: false, -} - -if (!backend) { - backend = 'http://192.168.240.1:9091' -} - -if (program.production) { - options['production'] = true; -} - -options['authelia_backend'] = backend; - -const templatePath = __dirname + '/traefik.toml.ejs'; -const outputPath = __dirname + '/traefik.toml'; - -html = ejs.renderFile(templatePath, options, (err, conf) => { - try { - var fd = fs.openSync(outputPath, 'w'); - fs.writeFileSync(fd, conf); - } catch (e) { - fs.writeFileSync(outputPath, conf); - } -}); \ No newline at end of file diff --git a/example/compose/traefik/traefik.toml.ejs b/example/compose/traefik/traefik.toml similarity index 69% rename from example/compose/traefik/traefik.toml.ejs rename to example/compose/traefik/traefik.toml index e6fe7d83a..411aae760 100644 --- a/example/compose/traefik/traefik.toml.ejs +++ b/example/compose/traefik/traefik.toml @@ -17,10 +17,6 @@ logLevel = "DEBUG" [file] -# TODO(c.michaud): remove this template by providing a proxy doing -# the routing depending on the mode (production or dev) -<% if (!production) { %> - [frontends] [frontends.authelia_api] backend = "authelia_api_backend" @@ -35,27 +31,11 @@ logLevel = "DEBUG" [backends] [backends.authelia_api_backend] [backends.authelia_api_backend.servers.server] - url = "http://192.168.240.1:9091" + url = "http://authelia-backend:9091" [backends.authelia_front_backend] [backends.authelia_front_backend.servers.server] - url = "http://192.168.240.1:3000" - -<% } else { %> - -[frontends] - [frontends.authelia] - backend = "authelia_backend" - [frontends.authelia.routes.route0] - rule = "Host:login.example.com" - -[backends] - [backends.authelia_backend] - [backends.authelia_backend.servers.server] - url = "http://192.168.240.1:9091" - -<% } %> - + url = "http://authelia-frontend:3000" [api] # This is exposed via a subdomain and a proxy diff --git a/example/kube/apps/apps.yml b/example/kube/apps/apps.yml index db4333429..5cf7d82e3 100644 --- a/example/kube/apps/apps.yml +++ b/example/kube/apps/apps.yml @@ -1,17 +1,20 @@ --- -apiVersion: extensions/v1beta1 +apiVersion: apps/v1 kind: Deployment metadata: name: test-app namespace: authelia labels: - k8s-app: test-app + app: test-app spec: replicas: 1 + selector: + matchLabels: + app: test-app template: metadata: labels: - k8s-app: test-app + app: test-app spec: containers: - name: test-app @@ -28,10 +31,10 @@ metadata: name: test-app-service namespace: authelia labels: - k8s-app: test-app + app: test-app spec: selector: - k8s-app: test-app + app: test-app ports: - port: 80 name: http diff --git a/example/kube/authelia/deployment.yml b/example/kube/authelia/deployment.yml index 045ae6366..a3d3211f0 100644 --- a/example/kube/authelia/deployment.yml +++ b/example/kube/authelia/deployment.yml @@ -1,5 +1,5 @@ --- -apiVersion: apps/v1beta2 +apiVersion: apps/v1 kind: Deployment metadata: name: authelia diff --git a/example/kube/authelia/ssl/tls.crt b/example/kube/authelia/ssl/tls.crt deleted file mode 100644 index ab0ba6200..000000000 --- a/example/kube/authelia/ssl/tls.crt +++ /dev/null @@ -1,17 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICvjCCAaYCCQCJYt0VhOelKjANBgkqhkiG9w0BAQsFADAhMR8wHQYDVQQDDBZs -b2dpbi5rdWJlLmV4YW1wbGUuY29tMB4XDTE4MDMwNDE1MTQzMVoXDTE5MDMwNDE1 -MTQzMVowITEfMB0GA1UEAwwWbG9naW4ua3ViZS5leGFtcGxlLmNvbTCCASIwDQYJ -KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMIlUUppqDLXQCey+OqC4YIhsZFhus0S -0OcNKBhMcUpKdaqtMf8n8mUtGCByUTf+LMBOyv/WrdcGH5pwlylyERPfDsUFF+5W -LjhHGjMZVKWHOadb25HpO9IZUyyC+5PepfrHlxS5EhTQXymA7yjaXSizfH0uF9Le -mF/RoqArtDfq/2/golcX5YkRt6FwbGrypHG0MuREyMN7H+XmKyC4Cwc1ECbROrWv -C5491Fvw4fW0zWa6M1z56kzA+X7ZleiemiY0vm7hzlm8qztd449pJzweb/Gl2r7n -LdFK+H2jbkn07Z//rwlm8Wlwtb3GLOTgisNv5jALpCDdgiSmUc+G+f0CAwEAATAN -BgkqhkiG9w0BAQsFAAOCAQEAUm+gRqlUIGK3UKA+z1Si2EpFeOpSkfBbMjwWQAea -yEY+XtUxQSWmbTx6Cp1miVwSp4ldd0nYVCpesv94FoI3ahktZGafcfviYgyCNPXl -QBREQ3NU9TBLHOmCygL8JlzKLtKABKTiGsDahPmBaMogCbvswFqccZ1EtLRcrI48 -FFGS7K4ku561AK+WqFS8yxFKcudJSfmLeEZ0uNazEbh8kIgA5dXtapv6lBhPQ6nN -MPZO321PWGysvj3RXDagYQOPBLX7NhnoFDCoeJKbPQ9lTLOAI0aQnpNoFZnoiWc3 -NNLboVSTPQ3jyumAAm7tXS/KWI5Samfp8Cgu7uqhPLdHYg== ------END CERTIFICATE----- diff --git a/example/kube/authelia/ssl/tls.csr b/example/kube/authelia/ssl/tls.csr deleted file mode 100644 index 70be16165..000000000 --- a/example/kube/authelia/ssl/tls.csr +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN CERTIFICATE REQUEST----- -MIICZjCCAU4CAQAwITEfMB0GA1UEAwwWbG9naW4ua3ViZS5leGFtcGxlLmNvbTCC -ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMIlUUppqDLXQCey+OqC4YIh -sZFhus0S0OcNKBhMcUpKdaqtMf8n8mUtGCByUTf+LMBOyv/WrdcGH5pwlylyERPf -DsUFF+5WLjhHGjMZVKWHOadb25HpO9IZUyyC+5PepfrHlxS5EhTQXymA7yjaXSiz -fH0uF9LemF/RoqArtDfq/2/golcX5YkRt6FwbGrypHG0MuREyMN7H+XmKyC4Cwc1 -ECbROrWvC5491Fvw4fW0zWa6M1z56kzA+X7ZleiemiY0vm7hzlm8qztd449pJzwe -b/Gl2r7nLdFK+H2jbkn07Z//rwlm8Wlwtb3GLOTgisNv5jALpCDdgiSmUc+G+f0C -AwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IBAQCHO3wzf1jCOcTmo5NBnCendtEb/IAl -aTBCW3b2+QDRQBGgpQb+JeDjHjIzp5FgzzJVF0XTA8H8jmR56lPTXNlWESzUh1oV -on8QcbPi97nuhIEJNfk7K6gAiK11fULBoNUgI7PsRvAneo2PsCEHGtNsdoU4Ii7A -CuUtKeeZCdbxVM2HradSJ9vvxRmOuIfsQJbUaH0F/Z3A0l0UQbp1AUOWFcJ6XDkX -SgDkMCkXJV53SlwGZm8q6Hj8zwP7Tlk6Nkzcn3ZMDB76o92QSVoi1V07NrvRUvcc -2/eekJBWfpzy1LkaovYGBow4ose8V5nMyH9feXlReCVk2aHYTYbEmQRj ------END CERTIFICATE REQUEST----- diff --git a/example/kube/authelia/ssl/tls.key b/example/kube/authelia/ssl/tls.key deleted file mode 100644 index 3654be488..000000000 --- a/example/kube/authelia/ssl/tls.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAwiVRSmmoMtdAJ7L46oLhgiGxkWG6zRLQ5w0oGExxSkp1qq0x -/yfyZS0YIHJRN/4swE7K/9at1wYfmnCXKXIRE98OxQUX7lYuOEcaMxlUpYc5p1vb -kek70hlTLIL7k96l+seXFLkSFNBfKYDvKNpdKLN8fS4X0t6YX9GioCu0N+r/b+Ci -VxfliRG3oXBsavKkcbQy5ETIw3sf5eYrILgLBzUQJtE6ta8Lnj3UW/Dh9bTNZroz -XPnqTMD5ftmV6J6aJjS+buHOWbyrO13jj2knPB5v8aXavuct0Ur4faNuSfTtn/+v -CWbxaXC1vcYs5OCKw2/mMAukIN2CJKZRz4b5/QIDAQABAoIBAQCkTwLqcFs6k/Om -5ZBGoPgLs0pdmRGIR7lnIjphvihPUI8fIK9km8FIoY5+v2E/ey0SoFyrg1vi1Drg -8RLtr60GXUxZsALd4jABzyM8Rd7erIA9xL8iUPsgx/Adhsk2D0P35v1VO4Ay/1ra -fFVsBMq9DJJ6Ow1MmLjqtzfkSLigbRRSPwaS081oW570cg9ABc1Cpp9sdLjG2Il0 -Eyet0qe0fiJAOlnE+tMRls9AoGYLG61msb1OhkpKfaNdw6IolkSGQZDqqsf1cSE3 -I7ypsE0LLtDeCU/jsUMjDHBwerqTANUHO5Y4PZ3hSJN55p/IGEiUeAMYs+dqtFx8 -xc/KfV2BAoGBAP+2nR73QjWdqJ0A4IdRq811eZM+NTWbobKRSay+T3Ve8QcRqc41 -YXJYqRhX23me3p9CxHDMVoXYtWS1nlXnsOxk60idffEIf5tbjzEYi1dIdLoCfbVW -dZS1ZsZh4GZ3If8e78R+9IBQ6+SFvsVocRXpkf6VHp6jB3mXH0XCyNXdAoGBAMJd -CORqmdrmCbfZnn7G3cZ7kTS05inMkj/svtDb+tkcy2x+pfL9y+SfeAf+o5AGl6pN -CsiiGJTVj/Wtic572zdT198UFyWjDrgYUMNzvL9430hnZkySF/E8f1XHD8Sb4P65 -CVGJeVKuEHTXcas9F3VYln/87WGDVrtVowO408KhAoGACFiSej9BtvRFW5J6wY/l -1pfd9vNR00UYGvbo+61edIs7vKpT63oMiynfov7DGA4aYAJS3QeeT1IKYZYX69/b -A2wrzbvuL17Co3RykPynF5syzBtmtPN0dP0StKjfJRkAUA5XbwdhvYpmmJfQ6SqG -fluYO0HstOrHRK2tBJ7d5TUCgYBt9mDPihgdpkQdRfvL0gsq/kH6xdXqFBkyHWkf -lTVonEfizAxrW3d9k1M/gqtbEr+/0/Kj7EFoAyN9ZX8v2Rb/SGo7hYxK+OOc9/TJ -f7NryKDav9U6wPTWwNlx2DttiptSwbEp9lMzmdMpp7JhpSCefU44fwp2Pu5U8nBV -7L2xwQKBgHln1Y4EZ9SQDA0jFiSUNoCkkUJFox8752FsPolCna3GmBAYJn8+Oumj -VbLPJvJxHmXMn+JN+rxxFve/DxV1TJqsan5F7i5xp0Ck4rm+TU0ZxvHW75yNG8ER -bNGkvo1dme3fh8YETH6sqePTtbJ04hMfNhn1/iu89s6+ft4cqnpk ------END RSA PRIVATE KEY----- diff --git a/example/kube/bootstrap-authelia.sh b/example/kube/bootstrap-authelia.sh index 15af1cca1..4b34c08e3 100755 --- a/example/kube/bootstrap-authelia.sh +++ b/example/kube/bootstrap-authelia.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh start_authelia() { kubectl create configmap authelia-config --namespace=authelia --from-file=authelia/configs/config.yml diff --git a/example/kube/bootstrap.sh b/example/kube/bootstrap.sh index 38e23c926..76f18d130 100755 --- a/example/kube/bootstrap.sh +++ b/example/kube/bootstrap.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh start_apps() { # Create TLS certificate and key for HTTPS termination diff --git a/example/kube/ingress-controller/deployment.yml b/example/kube/ingress-controller/deployment.yml index 40441a93b..0531e9d9a 100644 --- a/example/kube/ingress-controller/deployment.yml +++ b/example/kube/ingress-controller/deployment.yml @@ -1,19 +1,21 @@ --- -apiVersion: extensions/v1beta1 +apiVersion: apps/v1 kind: Deployment metadata: name: nginx-ingress-controller namespace: authelia labels: - k8s-app: nginx-ingress-controller + app: nginx-ingress-controller spec: replicas: 1 revisionHistoryLimit: 0 + selector: + matchLabels: + app: nginx-ingress-controller template: metadata: labels: - k8s-app: nginx-ingress-controller - name: nginx-ingress-controller + app: nginx-ingress-controller annotations: prometheus.io/port: '10254' prometheus.io/scrape: 'true' diff --git a/example/kube/ingress-controller/rbac.yml b/example/kube/ingress-controller/rbac.yml index cb3908588..9c3fda7a7 100644 --- a/example/kube/ingress-controller/rbac.yml +++ b/example/kube/ingress-controller/rbac.yml @@ -4,7 +4,7 @@ metadata: name: nginx-ingress-controller-serviceaccount namespace: authelia labels: - k8s-app: nginx-ingress-controller + app: nginx-ingress-controller --- apiVersion: rbac.authorization.k8s.io/v1beta1 @@ -12,7 +12,7 @@ kind: ClusterRole metadata: name: nginx-ingress-controller-clusterrole labels: - k8s-app: nginx-ingress-controller + app: nginx-ingress-controller rules: - apiGroups: - "" @@ -68,7 +68,7 @@ metadata: name: nginx-ingress-controller-role namespace: authelia labels: - k8s-app: nginx-ingress-controller + app: nginx-ingress-controller rules: - apiGroups: - "" @@ -112,7 +112,7 @@ metadata: name: nginx-ingress-controller-role-nisa-binding namespace: authelia labels: - k8s-app: nginx-ingress-controller + app: nginx-ingress-controller roleRef: apiGroup: rbac.authorization.k8s.io kind: Role @@ -128,7 +128,7 @@ kind: ClusterRoleBinding metadata: name: nginx-ingress-controller-clusterrole-nisa-binding labels: - k8s-app: nginx-ingress-controller + app: nginx-ingress-controller roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole diff --git a/example/kube/ingress-controller/service.yml b/example/kube/ingress-controller/service.yml index ffb6547d3..12785e67d 100644 --- a/example/kube/ingress-controller/service.yml +++ b/example/kube/ingress-controller/service.yml @@ -5,10 +5,10 @@ metadata: name: nginx-ingress-controller-service namespace: authelia labels: - k8s-app: nginx-ingress-controller + app: nginx-ingress-controller spec: selector: - k8s-app: nginx-ingress-controller + app: nginx-ingress-controller type: NodePort ports: - port: 80 diff --git a/example/kube/ldap/deployment.yml b/example/kube/ldap/deployment.yml index 8c46627ae..792c87745 100644 --- a/example/kube/ldap/deployment.yml +++ b/example/kube/ldap/deployment.yml @@ -1,5 +1,5 @@ --- -apiVersion: apps/v1beta2 +apiVersion: apps/v1 kind: Deployment metadata: name: ldap diff --git a/example/kube/mail/deployment.yml b/example/kube/mail/deployment.yml index 958c94110..220ad487f 100644 --- a/example/kube/mail/deployment.yml +++ b/example/kube/mail/deployment.yml @@ -1,5 +1,5 @@ --- -apiVersion: apps/v1beta2 +apiVersion: apps/v1 kind: Deployment metadata: name: mailcatcher diff --git a/example/kube/storage/mongo.yml b/example/kube/storage/mongo.yml index eb0d053a8..cf5e92386 100644 --- a/example/kube/storage/mongo.yml +++ b/example/kube/storage/mongo.yml @@ -1,5 +1,5 @@ --- -apiVersion: apps/v1beta2 +apiVersion: apps/v1 kind: Deployment metadata: name: mongo diff --git a/example/kube/storage/redis.yml b/example/kube/storage/redis.yml index e9f12f8e7..9eb67550a 100644 --- a/example/kube/storage/redis.yml +++ b/example/kube/storage/redis.yml @@ -1,5 +1,5 @@ --- -apiVersion: apps/v1beta2 +apiVersion: apps/v1 kind: Deployment metadata: name: redis diff --git a/example/kube/test.yml b/example/kube/test.yml index e19f9e757..6debf895b 100644 --- a/example/kube/test.yml +++ b/example/kube/test.yml @@ -1,5 +1,5 @@ --- -apiVersion: apps/v1beta2 +apiVersion: apps/v1 kind: Deployment metadata: name: test-app1 diff --git a/go.mod b/go.mod index e63c61c5b..2b57f4729 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.13 require ( github.com/Workiva/go-datastructures v1.0.50 github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a + github.com/cespare/reflex v0.2.0 // indirect github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74 github.com/fasthttp/router v0.5.2 @@ -12,12 +13,18 @@ require ( github.com/go-stack/stack v1.8.0 // indirect github.com/golang/mock v1.3.1 github.com/golang/snappy v0.0.1 // indirect + github.com/google/martian v2.1.0+incompatible + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect + github.com/kr/pty v1.1.8 // indirect github.com/mattn/go-sqlite3 v1.11.0 + github.com/ogier/pflag v0.0.1 // indirect + github.com/otiai10/copy v1.0.2 github.com/pquerna/otp v1.2.0 github.com/simia-tech/crypt v0.2.0 github.com/sirupsen/logrus v1.4.2 github.com/spf13/cobra v0.0.5 github.com/stretchr/testify v1.4.0 + github.com/tebeka/selenium v0.9.9 github.com/tstranex/u2f v1.0.0 github.com/valyala/fasthttp v1.6.0 github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c // indirect diff --git a/go.sum b/go.sum index eb3ea68ca..7ab33e28b 100644 --- a/go.sum +++ b/go.sum @@ -1,16 +1,33 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.41.0/go.mod h1:OauMR7DV8fzvZIl2qg6rkaIhD/vmgk4iwEw/h6ercmg= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/BurntSushi/xgbutil v0.0.0-20160919175755-f7c97cef3b4e h1:4ZrkT/RzpnROylmoQL57iVUL57wGKTR5O6KpVnbm2tA= +github.com/BurntSushi/xgbutil v0.0.0-20160919175755-f7c97cef3b4e/go.mod h1:uw9h2sd4WWHOPdJ13MQpwK5qYWKYDumDqxWWIknEQ+k= github.com/Workiva/go-datastructures v1.0.50 h1:slDmfW6KCHcC7U+LP3DDBbm4fqTwZGn1beOFPfGaLvo= github.com/Workiva/go-datastructures v1.0.50/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= +github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= +github.com/cespare/reflex v0.2.0 h1:6d9WpWJseKjJvZEevKP7Pk42nPx2+BUTqmhNk8wZPwM= +github.com/cespare/reflex v0.2.0/go.mod h1:ooqOLJ4algvHP/oYvKWfWJ9tFUzCLDk5qkIJduMYrgI= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/creack/pty v1.1.7 h1:6pwm8kMQKCmgUg0ZHTm5+/YvRK0s3THD/28+T6/kk4A= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -22,31 +39,69 @@ github.com/fasthttp/router v0.5.2 h1:xdmx8uYc9IFDtlbG2/FhE1Gyowv7/sqMgMonRjoW0Yo github.com/fasthttp/router v0.5.2/go.mod h1:Y5JAeRTSPwSLoUgH4x75UnT1j1IcAgVshMDMMrnNmKQ= github.com/fasthttp/session v1.1.3 h1:2qjxNltI7iv0yh7frsIdhbsGmSoRnTajU8xtpC6Hd80= github.com/fasthttp/session v1.1.3/go.mod h1:DRxVb1PWFtAUTE4U+GgggsVkUaQyacoL8TN+3o4/yLw= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/go-redis/redis v6.15.2+incompatible h1:9SpNVG76gr6InJGxoZ6IuuxaCOQwDAhzyXg+Bs+0Sb4= github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-github/v27 v27.0.4/go.mod h1:/0Gr8pJ55COkmv+S/yPKCczSkUPIM/LnFyubufRNIS0= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/klauspost/compress v1.8.2 h1:Bx0qjetmNjdFXASH02NSAREKpiaDwkO1DRZ3dV2KCcs= github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A/Q= github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/ogier/pflag v0.0.1 h1:RW6JSWSu/RkSatfcLtogGfFgpim5p7ARQ10ECk5O750= +github.com/ogier/pflag v0.0.1/go.mod h1:zkFki7tvTa0tafRvTBIZTvzYyAu6kQhPZFnshFFPE+g= +github.com/otiai10/copy v1.0.2 h1:DDNipYy6RkIkjMwy+AWzgKiNTyj2RUI9yEMeETEpVyc= +github.com/otiai10/copy v1.0.2/go.mod h1:c7RpqBkwMom4bYTSkLSym4VSJz/XtncWRAj/J4PEIMY= +github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= +github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= @@ -63,13 +118,17 @@ github.com/simia-tech/crypt v0.2.0 h1:cU8qdqUYNuEFKSMq15yaB2aI1aC5vrn6dFOonT6Kg6 github.com/simia-tech/crypt v0.2.0/go.mod h1:DMwvjPTzsiHrjqHVW5HvIbF4vUUzMCYDKVLsPWmLdTo= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -78,6 +137,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/tebeka/selenium v0.9.9 h1:cNziB+etNgyH/7KlNI7RMC1ua5aH1+5wUlFQyzeMh+w= +github.com/tebeka/selenium v0.9.9/go.mod h1:5Fr8+pUvU6B1OiPfkdCKdXZyr5znvVkxuPd0NOdZCQc= github.com/tinylib/msgp v1.1.0 h1:9fQd+ICuRIu/ue4vxJZu6/LzxN0HwMds2nq/0cFvxHU= github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tstranex/u2f v1.0.0 h1:HhJkSzDDlVSVIVt7pDJwCHQj67k7A5EeBgPmeD+pVsQ= @@ -95,22 +156,89 @@ github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0 github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= go.mongodb.org/mongo-driver v1.1.2 h1:jxcFYjlkl8xaERsgLo+RNquI0epW6zuy/ZRQs6jnrFA= go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116161606-93218def8b18/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190624190245-7f2218787638/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190626174449-989357319d63/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM= gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -119,3 +247,7 @@ gopkg.in/ldap.v3 v3.1.0/go.mod h1:dQjCc0R0kfyFjIlWNMH1DORwUASZyDxo2Ry1B51dXaQ= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/notification/smtp_notifier.go b/notification/smtp_notifier.go index 3b893052d..cf61596d7 100644 --- a/notification/smtp_notifier.go +++ b/notification/smtp_notifier.go @@ -43,6 +43,10 @@ func (n *SMTPNotifier) unauthenticatedSend(recipient string, msg string) error { // Connect to the remote SMTP server. c, err := smtp.Dial(n.address) + if err != nil { + return err + } + // Set the sender and recipient first if err := c.Mail(n.sender); err != nil { return err diff --git a/package-lock.json b/package-lock.json index 721e302d3..f9f404e89 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", "dev": true, "requires": { - "@babel/highlight": "7.0.0" + "@babel/highlight": "^7.0.0" } }, "@babel/highlight": { @@ -19,46 +19,11 @@ "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", "dev": true, "requires": { - "chalk": "2.4.2", - "esutils": "2.0.2", - "js-tokens": "4.0.0" + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" } }, - "@sinonjs/commons": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.4.0.tgz", - "integrity": "sha512-9jHK3YF/8HtJ9wCAbG+j8cD0i0+ATS9A7gXFqS36TblLPNy6rEEc+SB0imo91eCboGaBYGV/MT1/br/J+EE7Tw==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/formatio": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz", - "integrity": "sha512-ls6CAMA6/5gG+O/IdsBcblvnd8qcO/l1TYoNeAzp3wcISOxlPXQEus0mLcdwazEkWjaBdaJ3TaxmNgCLWwvWzg==", - "dev": true, - "requires": { - "samsam": "1.3.0" - } - }, - "@sinonjs/samsam": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.2.tgz", - "integrity": "sha512-ILO/rR8LfAb60Y1Yfp9vxfYAASK43NFC2mLzpvLUbCQY/Qu8YwReboseu8aheCEkyElZF2L2T9mHcR2bgdvZyA==", - "dev": true, - "requires": { - "@sinonjs/commons": "1.4.0", - "array-from": "2.1.1", - "lodash": "4.17.15" - } - }, - "@sinonjs/text-encoding": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", - "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", - "dev": true - }, "@types/bluebird": { "version": "3.5.27", "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.27.tgz", @@ -77,8 +42,8 @@ "integrity": "sha512-PDkSRY7KltW3M60hSBlerxI8SFPXsO3AL/aRVsO4Kh9IHRW74Ih75gUuTd/aE4LSSFqypb10UIX3QzOJwBQMGQ==", "dev": true, "requires": { - "@types/events": "3.0.0", - "@types/node": "12.0.10" + "@types/events": "*", + "@types/node": "*" } }, "@types/commander": { @@ -87,7 +52,7 @@ "integrity": "sha512-0QEFiR8ljcHp9bAbWxecjVRuAMr16ivPiGOw6KFQBVrVd0RQIcM3xKdRisH2EDWgVWujiYtHwhSkSUoAAGzH7Q==", "dev": true, "requires": { - "commander": "2.20.0" + "commander": "*" } }, "@types/events": { @@ -102,7 +67,7 @@ "integrity": "sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ==", "dev": true, "requires": { - "@types/node": "12.0.10" + "@types/node": "*" } }, "@types/glob": { @@ -111,9 +76,9 @@ "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", "dev": true, "requires": { - "@types/events": "3.0.0", - "@types/minimatch": "3.0.3", - "@types/node": "12.0.10" + "@types/events": "*", + "@types/minimatch": "*", + "@types/node": "*" } }, "@types/minimatch": { @@ -146,7 +111,7 @@ "integrity": "sha512-+bKtuxhj/TYSSP1r4CZhfmyA0vm/aDRQNo7vbAgf6/cZajn0SAniGGST07yvI4Q+q169WTa2/x9gEHfJrkcALw==", "dev": true, "requires": { - "@types/node": "12.0.10" + "@types/node": "*" } }, "@types/query-string": { @@ -161,7 +126,7 @@ "integrity": "sha512-255dzsOLJdXFHBio9/aMHGozNkoiBUgc+g2nlNjbTSp5qcAlmpm4Z6Xs3pKOBLNIKdZbA2BkUxWvYSIwKra0Yw==", "dev": true, "requires": { - "@types/node": "12.0.10" + "@types/node": "*" } }, "@types/request": { @@ -170,10 +135,10 @@ "integrity": "sha512-ZgEZ1TiD+KGA9LiAAPPJL68Id2UWfeSO62ijSXZjFJArVV+2pKcsVHmrcu+1oiE3q6eDGiFiSolRc4JHoerBBg==", "dev": true, "requires": { - "@types/caseless": "0.12.2", - "@types/form-data": "2.2.1", - "@types/node": "12.0.10", - "@types/tough-cookie": "2.3.5" + "@types/caseless": "*", + "@types/form-data": "*", + "@types/node": "*", + "@types/tough-cookie": "*" } }, "@types/request-promise": { @@ -182,8 +147,8 @@ "integrity": "sha512-RId7eFsUKxfal1LirDDIcOp9u3MM3NXFDBcC3sqIMcmu7f4U6DsCEMD8RbLZtnPrQlN5Jc79di/WPsIEDO4keg==", "dev": true, "requires": { - "@types/bluebird": "3.5.27", - "@types/request": "2.48.1" + "@types/bluebird": "*", + "@types/request": "*" } }, "@types/selenium-webdriver": { @@ -192,65 +157,31 @@ "integrity": "sha512-lMC2G0ItF2xv4UCiwbJGbnJlIuUixHrioOhNGHSCsYCJ8l4t9hMCUimCytvFv7qy6AfSzRxhRHoGa+UqaqwyeA==", "dev": true }, - "@types/sinon": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-4.3.3.tgz", - "integrity": "sha512-Tt7w/ylBS/OEAlSCwzB0Db1KbxnkycP/1UkQpbvKFYoUuRn4uYsC3xh5TRPrOjTy0i8TIkSz1JdNL4GPVdf3KQ==", - "dev": true - }, "@types/speakeasy": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/speakeasy/-/speakeasy-2.0.4.tgz", "integrity": "sha512-WcZalHN3tlh+StC8cszTuh2SkX+vn5s4K+eMwa2fXM4t3GDeYg6JVrpchHs9InqTkgXXsEtE8KNXaQxfkIdmng==", "dev": true, "requires": { - "@types/node": "12.0.10" + "@types/node": "*" } }, - "@types/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha1-EHPEvIJHVK49EM+riKsCN7qWTk0=", - "dev": true - }, "@types/tough-cookie": { "version": "2.3.5", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.5.tgz", "integrity": "sha512-SCcK7mvGi3+ZNz833RRjFIxrn4gI1PPR3NtuIS+6vMkvmsGjosqTJwRt5bAEFLRz+wtJMWv8+uOnZf2hi2QXTg==", "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 - }, "ajv": { "version": "6.10.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", "dev": true, "requires": { - "fast-deep-equal": "2.0.1", - "fast-json-stable-stringify": "2.0.0", - "json-schema-traverse": "0.4.1", - "uri-js": "4.2.2" - } - }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", - "dev": true, - "optional": true - }, - "ansi-align": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", - "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", - "dev": true, - "requires": { - "string-width": "2.1.1" + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" } }, "ansi-colors": { @@ -271,7 +202,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.3" + "color-convert": "^1.9.0" } }, "anymatch": { @@ -280,8 +211,8 @@ "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", "dev": true, "requires": { - "micromatch": "3.1.10", - "normalize-path": "2.1.1" + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" }, "dependencies": { "normalize-path": { @@ -290,7 +221,7 @@ "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "dev": true, "requires": { - "remove-trailing-separator": "1.1.0" + "remove-trailing-separator": "^1.0.1" } } } @@ -301,7 +232,7 @@ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "requires": { - "sprintf-js": "1.0.3" + "sprintf-js": "~1.0.2" } }, "arr-diff": { @@ -322,19 +253,13 @@ "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", "dev": true }, - "array-from": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", - "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=", - "dev": true - }, "array-union": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", "dev": true, "requires": { - "array-uniq": "1.0.3" + "array-uniq": "^1.0.1" } }, "array-uniq": { @@ -361,7 +286,7 @@ "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", "dev": true, "requires": { - "safer-buffer": "2.1.2" + "safer-buffer": "~2.1.0" } }, "assert-plus": { @@ -376,12 +301,6 @@ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", "dev": true }, - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", - "dev": true - }, "async-each": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", @@ -424,13 +343,13 @@ "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", "dev": true, "requires": { - "cache-base": "1.0.1", - "class-utils": "0.3.6", - "component-emitter": "1.3.0", - "define-property": "1.0.0", - "isobject": "3.0.1", - "mixin-deep": "1.3.2", - "pascalcase": "0.1.1" + "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": { @@ -439,7 +358,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "1.0.2" + "is-descriptor": "^1.0.0" } }, "is-accessor-descriptor": { @@ -448,7 +367,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-data-descriptor": { @@ -457,7 +376,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-descriptor": { @@ -466,9 +385,9 @@ "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" + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } } } @@ -485,7 +404,7 @@ "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", "dev": true, "requires": { - "tweetnacl": "0.14.5" + "tweetnacl": "^0.14.3" } }, "binary-extensions": { @@ -500,36 +419,13 @@ "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==", "dev": true }, - "boxen": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", - "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", - "dev": true, - "requires": { - "ansi-align": "2.0.0", - "camelcase": "4.1.0", - "chalk": "2.4.2", - "cli-boxes": "1.0.0", - "string-width": "2.1.1", - "term-size": "1.2.0", - "widest-line": "2.0.1" - }, - "dependencies": { - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - } - } - }, "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", + "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, @@ -539,16 +435,16 @@ "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.3", - "snapdragon": "0.8.2", - "snapdragon-node": "2.1.1", - "split-string": "3.1.0", - "to-regex": "3.0.2" + "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": { @@ -557,7 +453,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -586,15 +482,15 @@ "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", "dev": true, "requires": { - "collection-visit": "1.0.0", - "component-emitter": "1.3.0", - "get-value": "2.0.6", - "has-value": "1.0.0", - "isobject": "3.0.1", - "set-value": "2.0.1", - "to-object-path": "0.3.0", - "union-value": "1.0.1", - "unset-value": "1.0.0" + "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" } }, "camelcase": { @@ -603,12 +499,6 @@ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, - "capture-stack-trace": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", - "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==", - "dev": true - }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -621,9 +511,9 @@ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.5.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, "dependencies": { "supports-color": { @@ -632,7 +522,7 @@ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } } } @@ -643,18 +533,18 @@ "integrity": "sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g==", "dev": true, "requires": { - "anymatch": "2.0.0", - "async-each": "1.0.3", - "braces": "2.3.2", - "fsevents": "1.2.9", - "glob-parent": "3.1.0", - "inherits": "2.0.4", - "is-binary-path": "1.0.1", - "is-glob": "4.0.1", - "normalize-path": "3.0.0", - "path-is-absolute": "1.0.1", - "readdirp": "2.2.1", - "upath": "1.1.2" + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" } }, "chromedriver": { @@ -663,29 +553,23 @@ "integrity": "sha512-mZa1IVx4HD8rDaItWbnS470mmypgiWsDiu98r0NkiT4uLm3qrANl4vOU6no6vtWtLQiW5kt1POcIbjeNpsLbXA==", "dev": true, "requires": { - "del": "4.1.1", - "extract-zip": "1.6.7", - "mkdirp": "0.5.1", - "request": "2.88.0", - "tcp-port-used": "1.0.1" + "del": "^4.1.1", + "extract-zip": "^1.6.7", + "mkdirp": "^0.5.1", + "request": "^2.88.0", + "tcp-port-used": "^1.0.1" } }, - "ci-info": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", - "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", - "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.1", - "static-extend": "0.1.2" + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" }, "dependencies": { "define-property": { @@ -694,26 +578,20 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } } } }, - "cli-boxes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", - "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=", - "dev": true - }, "cliui": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", "dev": true, "requires": { - "string-width": "2.1.1", - "strip-ansi": "4.0.0", - "wrap-ansi": "2.1.0" + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" } }, "code-point-at": { @@ -728,8 +606,8 @@ "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", "dev": true, "requires": { - "map-visit": "1.0.0", - "object-visit": "1.0.1" + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" } }, "color-convert": { @@ -753,7 +631,7 @@ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, "requires": { - "delayed-stream": "1.0.0" + "delayed-stream": "~1.0.0" } }, "commander": { @@ -780,24 +658,10 @@ "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", "dev": true, "requires": { - "buffer-from": "1.1.1", - "inherits": "2.0.4", - "readable-stream": "2.3.6", - "typedarray": "0.0.6" - } - }, - "configstore": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz", - "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==", - "dev": true, - "requires": { - "dot-prop": "4.2.0", - "graceful-fs": "4.2.0", - "make-dir": "1.3.0", - "unique-string": "1.0.0", - "write-file-atomic": "2.4.3", - "xdg-basedir": "3.0.0" + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" } }, "copy-descriptor": { @@ -812,41 +676,26 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, - "create-error-class": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", - "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", - "dev": true, - "requires": { - "capture-stack-trace": "1.0.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.5", - "path-key": "2.0.1", - "semver": "5.7.0", - "shebang-command": "1.2.0", - "which": "1.3.1" + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" } }, - "crypto-random-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", - "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=", - "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" + "assert-plus": "^1.0.0" } }, "debug": { @@ -870,12 +719,6 @@ "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", "dev": true }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true - }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -888,7 +731,7 @@ "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", "dev": true, "requires": { - "object-keys": "1.1.1" + "object-keys": "^1.0.12" } }, "define-property": { @@ -897,8 +740,8 @@ "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", "dev": true, "requires": { - "is-descriptor": "1.0.2", - "isobject": "3.0.1" + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" }, "dependencies": { "is-accessor-descriptor": { @@ -907,7 +750,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-data-descriptor": { @@ -916,7 +759,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-descriptor": { @@ -925,9 +768,9 @@ "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" + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } } } @@ -938,13 +781,13 @@ "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", "dev": true, "requires": { - "@types/glob": "7.1.1", - "globby": "6.1.0", - "is-path-cwd": "2.2.0", - "is-path-in-cwd": "2.1.0", - "p-map": "2.1.0", - "pify": "4.0.1", - "rimraf": "2.6.3" + "@types/glob": "^7.1.1", + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" }, "dependencies": { "pify": { @@ -967,35 +810,20 @@ "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", "dev": true }, - "dot-prop": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", - "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", - "dev": true, - "requires": { - "is-obj": "1.0.1" - } - }, "double-ended-queue": { "version": "2.1.0-0", "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz", "integrity": "sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw=", "dev": true }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", - "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.1", - "safer-buffer": "2.1.2" + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" } }, "ejs": { @@ -1016,7 +844,7 @@ "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", "dev": true, "requires": { - "once": "1.4.0" + "once": "^1.4.0" } }, "es-abstract": { @@ -1025,12 +853,12 @@ "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", "dev": true, "requires": { - "es-to-primitive": "1.2.0", - "function-bind": "1.1.1", - "has": "1.0.3", - "is-callable": "1.1.4", - "is-regex": "1.0.4", - "object-keys": "1.1.1" + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" } }, "es-to-primitive": { @@ -1039,9 +867,9 @@ "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", "dev": true, "requires": { - "is-callable": "1.1.4", - "is-date-object": "1.0.1", - "is-symbol": "1.0.2" + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" } }, "escape-string-regexp": { @@ -1050,49 +878,12 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, - "escodegen": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", - "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", - "dev": true, - "requires": { - "esprima": "2.7.3", - "estraverse": "1.9.3", - "esutils": "2.0.2", - "optionator": "0.8.2", - "source-map": "0.2.0" - }, - "dependencies": { - "esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", - "dev": true - }, - "source-map": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", - "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", - "dev": true, - "optional": true, - "requires": { - "amdefine": "1.0.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": "1.9.3", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", - "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", - "dev": true - }, "esutils": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", @@ -1105,13 +896,13 @@ "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", "dev": true, "requires": { - "cross-spawn": "6.0.5", - "get-stream": "4.1.0", - "is-stream": "1.1.0", - "npm-run-path": "2.0.2", - "p-finally": "1.0.0", - "signal-exit": "3.0.2", - "strip-eof": "1.0.0" + "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" } }, "expand-brackets": { @@ -1120,13 +911,13 @@ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, "requires": { - "debug": "2.6.9", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "posix-character-classes": "0.1.1", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "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": { "define-property": { @@ -1135,7 +926,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } }, "extend-shallow": { @@ -1144,7 +935,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -1161,8 +952,8 @@ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", "dev": true, "requires": { - "assign-symbols": "1.0.0", - "is-extendable": "1.0.1" + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" }, "dependencies": { "is-extendable": { @@ -1171,7 +962,7 @@ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { - "is-plain-object": "2.0.4" + "is-plain-object": "^2.0.4" } } } @@ -1182,14 +973,14 @@ "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.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "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": { @@ -1198,7 +989,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "1.0.2" + "is-descriptor": "^1.0.0" } }, "extend-shallow": { @@ -1207,7 +998,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } }, "is-accessor-descriptor": { @@ -1216,7 +1007,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-data-descriptor": { @@ -1225,7 +1016,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-descriptor": { @@ -1234,9 +1025,9 @@ "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" + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } } } @@ -1271,19 +1062,13 @@ "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", "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 - }, "fd-slicer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", "dev": true, "requires": { - "pend": "1.2.0" + "pend": "~1.2.0" } }, "fill-range": { @@ -1292,10 +1077,10 @@ "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.1" + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" }, "dependencies": { "extend-shallow": { @@ -1304,7 +1089,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -1315,7 +1100,7 @@ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "requires": { - "locate-path": "3.0.0" + "locate-path": "^3.0.0" } }, "flat": { @@ -1324,7 +1109,7 @@ "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", "dev": true, "requires": { - "is-buffer": "2.0.3" + "is-buffer": "~2.0.3" }, "dependencies": { "is-buffer": { @@ -1353,9 +1138,9 @@ "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "dev": true, "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.8", - "mime-types": "2.1.24" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" } }, "fragment-cache": { @@ -1364,7 +1149,7 @@ "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", "dev": true, "requires": { - "map-cache": "0.2.2" + "map-cache": "^0.2.2" } }, "fs.realpath": { @@ -1380,8 +1165,8 @@ "dev": true, "optional": true, "requires": { - "nan": "2.14.0", - "node-pre-gyp": "0.12.0" + "nan": "^2.12.1", + "node-pre-gyp": "^0.12.0" }, "dependencies": { "abbrev": { @@ -1393,7 +1178,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -1407,21 +1193,23 @@ "dev": true, "optional": true, "requires": { - "delegates": "1.0.0", - "readable-stream": "2.3.6" + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" } }, "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { - "balanced-match": "1.0.0", + "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, @@ -1434,17 +1222,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -1458,7 +1249,7 @@ "dev": true, "optional": true, "requires": { - "ms": "2.1.1" + "ms": "^2.1.1" } }, "deep-extend": { @@ -1485,7 +1276,7 @@ "dev": true, "optional": true, "requires": { - "minipass": "2.3.5" + "minipass": "^2.2.1" } }, "fs.realpath": { @@ -1500,14 +1291,14 @@ "dev": true, "optional": true, "requires": { - "aproba": "1.2.0", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.3" + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" } }, "glob": { @@ -1516,12 +1307,12 @@ "dev": true, "optional": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "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" } }, "has-unicode": { @@ -1536,7 +1327,7 @@ "dev": true, "optional": true, "requires": { - "safer-buffer": "2.1.2" + "safer-buffer": ">= 2.1.2 < 3" } }, "ignore-walk": { @@ -1545,7 +1336,7 @@ "dev": true, "optional": true, "requires": { - "minimatch": "3.0.4" + "minimatch": "^3.0.4" } }, "inflight": { @@ -1554,14 +1345,15 @@ "dev": true, "optional": true, "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" + "once": "^1.3.0", + "wrappy": "1" } }, "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -1573,8 +1365,9 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, "isarray": { @@ -1587,22 +1380,25 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { - "brace-expansion": "1.1.11" + "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, + "optional": true, "requires": { - "safe-buffer": "5.1.2", - "yallist": "3.0.3" + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" } }, "minizlib": { @@ -1611,13 +1407,14 @@ "dev": true, "optional": true, "requires": { - "minipass": "2.3.5" + "minipass": "^2.2.1" } }, "mkdirp": { "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -1634,9 +1431,9 @@ "dev": true, "optional": true, "requires": { - "debug": "4.1.1", - "iconv-lite": "0.4.24", - "sax": "1.2.4" + "debug": "^4.1.0", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" } }, "node-pre-gyp": { @@ -1645,16 +1442,16 @@ "dev": true, "optional": true, "requires": { - "detect-libc": "1.0.3", - "mkdirp": "0.5.1", - "needle": "2.3.0", - "nopt": "4.0.1", - "npm-packlist": "1.4.1", - "npmlog": "4.1.2", - "rc": "1.2.8", - "rimraf": "2.6.3", - "semver": "5.7.0", - "tar": "4.4.8" + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" } }, "nopt": { @@ -1663,8 +1460,8 @@ "dev": true, "optional": true, "requires": { - "abbrev": "1.1.1", - "osenv": "0.1.5" + "abbrev": "1", + "osenv": "^0.1.4" } }, "npm-bundled": { @@ -1679,8 +1476,8 @@ "dev": true, "optional": true, "requires": { - "ignore-walk": "3.0.1", - "npm-bundled": "1.0.6" + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" } }, "npmlog": { @@ -1689,16 +1486,17 @@ "dev": true, "optional": true, "requires": { - "are-we-there-yet": "1.1.5", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" } }, "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -1710,8 +1508,9 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { - "wrappy": "1.0.2" + "wrappy": "1" } }, "os-homedir": { @@ -1732,8 +1531,8 @@ "dev": true, "optional": true, "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" } }, "path-is-absolute": { @@ -1754,10 +1553,10 @@ "dev": true, "optional": true, "requires": { - "deep-extend": "0.6.0", - "ini": "1.3.5", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" }, "dependencies": { "minimist": { @@ -1774,13 +1573,13 @@ "dev": true, "optional": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "2.0.0", - "safe-buffer": "5.1.2", - "string_decoder": "1.1.1", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, "rimraf": { @@ -1789,13 +1588,14 @@ "dev": true, "optional": true, "requires": { - "glob": "7.1.3" + "glob": "^7.1.3" } }, "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -1831,10 +1631,11 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } }, "string_decoder": { @@ -1843,15 +1644,16 @@ "dev": true, "optional": true, "requires": { - "safe-buffer": "5.1.2" + "safe-buffer": "~5.1.0" } }, "strip-ansi": { "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "^2.0.0" } }, "strip-json-comments": { @@ -1866,13 +1668,13 @@ "dev": true, "optional": true, "requires": { - "chownr": "1.1.1", - "fs-minipass": "1.2.5", - "minipass": "2.3.5", - "minizlib": "1.2.1", - "mkdirp": "0.5.1", - "safe-buffer": "5.1.2", - "yallist": "3.0.3" + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" } }, "util-deprecate": { @@ -1887,18 +1689,20 @@ "dev": true, "optional": true, "requires": { - "string-width": "1.0.2" + "string-width": "^1.0.2 || 2" } }, "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, @@ -1920,7 +1724,7 @@ "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", "dev": true, "requires": { - "pump": "3.0.0" + "pump": "^3.0.0" } }, "get-value": { @@ -1935,7 +1739,7 @@ "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", "dev": true, "requires": { - "assert-plus": "1.0.0" + "assert-plus": "^1.0.0" } }, "glob": { @@ -1944,12 +1748,12 @@ "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", "dev": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.4", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "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" } }, "glob-parent": { @@ -1958,8 +1762,8 @@ "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", "dev": true, "requires": { - "is-glob": "3.1.0", - "path-dirname": "1.0.2" + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" }, "dependencies": { "is-glob": { @@ -1968,31 +1772,22 @@ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", "dev": true, "requires": { - "is-extglob": "2.1.1" + "is-extglob": "^2.1.0" } } } }, - "global-dirs": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", - "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", - "dev": true, - "requires": { - "ini": "1.3.5" - } - }, "globby": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", "dev": true, "requires": { - "array-union": "1.0.2", - "glob": "7.1.4", - "object-assign": "4.1.1", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" }, "dependencies": { "pify": { @@ -2003,33 +1798,6 @@ } } }, - "got": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", - "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", - "dev": true, - "requires": { - "create-error-class": "3.0.2", - "duplexer3": "0.1.4", - "get-stream": "3.0.0", - "is-redirect": "1.0.0", - "is-retry-allowed": "1.1.0", - "is-stream": "1.1.0", - "lowercase-keys": "1.0.1", - "safe-buffer": "5.1.2", - "timed-out": "4.0.1", - "unzip-response": "2.0.1", - "url-parse-lax": "1.0.0" - }, - "dependencies": { - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - } - } - }, "graceful-fs": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.0.tgz", @@ -2042,26 +1810,6 @@ "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "dev": true }, - "handlebars": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.4.3.tgz", - "integrity": "sha512-B0W4A2U1ww3q7VVthTKfh+epHx+q4mCt6iK+zEAzbMBpWQAwxCeKxEGpj/1oQTpzPXDNSOG7hmG14TsISH50yw==", - "dev": true, - "requires": { - "neo-async": "2.6.1", - "optimist": "0.6.1", - "source-map": "0.6.1", - "uglify-js": "3.6.3" - }, - "dependencies": { - "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 - } - } - }, "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -2074,8 +1822,8 @@ "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", "dev": true, "requires": { - "ajv": "6.10.0", - "har-schema": "2.0.0" + "ajv": "^6.5.5", + "har-schema": "^2.0.0" } }, "has": { @@ -2084,7 +1832,7 @@ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "requires": { - "function-bind": "1.1.1" + "function-bind": "^1.1.1" } }, "has-flag": { @@ -2105,9 +1853,9 @@ "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", "dev": true, "requires": { - "get-value": "2.0.6", - "has-values": "1.0.0", - "isobject": "3.0.1" + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" } }, "has-values": { @@ -2116,8 +1864,8 @@ "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", "dev": true, "requires": { - "is-number": "3.0.0", - "kind-of": "4.0.0" + "is-number": "^3.0.0", + "kind-of": "^4.0.0" }, "dependencies": { "kind-of": { @@ -2126,7 +1874,7 @@ "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -2143,43 +1891,25 @@ "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "dev": true, "requires": { - "assert-plus": "1.0.0", - "jsprim": "1.4.1", - "sshpk": "1.16.1" + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" } }, - "ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", - "dev": true - }, "immediate": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", "dev": true }, - "import-lazy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", - "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": { - "once": "1.4.0", - "wrappy": "1.0.2" + "once": "^1.3.0", + "wrappy": "1" } }, "inherits": { @@ -2188,12 +1918,6 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true - }, "invert-kv": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", @@ -2212,7 +1936,7 @@ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -2221,7 +1945,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -2232,7 +1956,7 @@ "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", "dev": true, "requires": { - "binary-extensions": "1.13.1" + "binary-extensions": "^1.0.0" } }, "is-buffer": { @@ -2247,22 +1971,13 @@ "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", "dev": true }, - "is-ci": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", - "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", - "dev": true, - "requires": { - "ci-info": "1.6.0" - } - }, "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.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -2271,7 +1986,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -2288,9 +2003,9 @@ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" }, "dependencies": { "kind-of": { @@ -2325,32 +2040,16 @@ "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", "dev": true, "requires": { - "is-extglob": "2.1.1" + "is-extglob": "^2.1.1" } }, - "is-installed-globally": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", - "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", - "dev": true, - "requires": { - "global-dirs": "0.1.1", - "is-path-inside": "1.0.1" - } - }, - "is-npm": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", - "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=", - "dev": true - }, "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.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -2359,17 +2058,11 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } }, - "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", - "dev": true - }, "is-path-cwd": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", @@ -2382,7 +2075,7 @@ "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", "dev": true, "requires": { - "is-path-inside": "2.1.0" + "is-path-inside": "^2.1.0" }, "dependencies": { "is-path-inside": { @@ -2391,50 +2084,29 @@ "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", "dev": true, "requires": { - "path-is-inside": "1.0.2" + "path-is-inside": "^1.0.2" } } } }, - "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", - "dev": true, - "requires": { - "path-is-inside": "1.0.2" - } - }, "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" + "isobject": "^3.0.1" } }, - "is-redirect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", - "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=", - "dev": true - }, "is-regex": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", "dev": true, "requires": { - "has": "1.0.3" + "has": "^1.0.1" } }, - "is-retry-allowed": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", - "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=", - "dev": true - }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", @@ -2447,7 +2119,7 @@ "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", "dev": true, "requires": { - "has-symbols": "1.0.0" + "has-symbols": "^1.0.0" } }, "is-typedarray": { @@ -2474,9 +2146,9 @@ "integrity": "sha512-+WaJvnaA7aJySz2q/8sLjMb2Mw14KTplHmSwcSpZ/fWJPkUmqw3YTzSWbPJ7OAwRvdYTWF2Wg+yYJ1AdP5Z8CA==", "dev": true, "requires": { - "deep-is": "0.1.3", - "ip-regex": "2.1.0", - "is-url": "1.2.4" + "deep-is": "^0.1.3", + "ip-regex": "^2.1.0", + "is-url": "^1.2.2" } }, "isarray": { @@ -2503,85 +2175,6 @@ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", "dev": true }, - "istanbul": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.4.5.tgz", - "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=", - "dev": true, - "requires": { - "abbrev": "1.0.9", - "async": "1.5.2", - "escodegen": "1.8.1", - "esprima": "2.7.3", - "glob": "5.0.15", - "handlebars": "4.4.3", - "js-yaml": "3.13.1", - "mkdirp": "0.5.1", - "nopt": "3.0.6", - "once": "1.4.0", - "resolve": "1.1.7", - "supports-color": "3.2.3", - "which": "1.3.1", - "wordwrap": "1.0.0" - }, - "dependencies": { - "abbrev": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", - "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", - "dev": true - }, - "esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", - "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.6", - "inherits": "2.0.4", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", - "dev": true, - "requires": { - "abbrev": "1.0.9" - } - }, - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -2594,8 +2187,8 @@ "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", "dev": true, "requires": { - "argparse": "1.0.10", - "esprima": "4.0.1" + "argparse": "^1.0.7", + "esprima": "^4.0.0" } }, "jsbn": { @@ -2640,50 +2233,25 @@ "integrity": "sha512-iCMBbo4eE5rb1VCpm5qXOAaUiRKRUKiItn8ah2YQQx9qymmSAY98eyQfioChEYcVQLh0zxJ3wS4A0mh90AVPvw==", "dev": true, "requires": { - "lie": "3.3.0", - "pako": "1.0.10", - "readable-stream": "2.3.6", - "set-immediate-shim": "1.0.1" + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "set-immediate-shim": "~1.0.1" } }, - "just-extend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.0.2.tgz", - "integrity": "sha512-FrLwOgm+iXrPV+5zDU6Jqu4gCRXbWEQg2O3SKONsWE4w7AXFRkryS53bpWdaL9cNol+AmR3AEYz6kn+o0fCPnw==", - "dev": true - }, "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true }, - "latest-version": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", - "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", - "dev": true, - "requires": { - "package-json": "4.0.1" - } - }, "lcid": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", "dev": true, "requires": { - "invert-kv": "2.0.0" - } - }, - "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" + "invert-kv": "^2.0.0" } }, "lie": { @@ -2692,7 +2260,7 @@ "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", "dev": true, "requires": { - "immediate": "3.0.6" + "immediate": "~3.0.5" } }, "locate-path": { @@ -2701,8 +2269,8 @@ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, "requires": { - "p-locate": "3.0.0", - "path-exists": "3.0.0" + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" } }, "lodash": { @@ -2711,50 +2279,13 @@ "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", "dev": true }, - "lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", - "dev": true - }, "log-symbols": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", "dev": true, "requires": { - "chalk": "2.4.2" - } - }, - "lolex": { - "version": "2.7.5", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.7.5.tgz", - "integrity": "sha512-l9x0+1offnKKIzYVjyXU2SiwhXDLekRzKyhnbyldPHvC7BvLPVpdNUNR2KeMAiCN2D/kLNttZgQD5WjSxuBx3Q==", - "dev": true - }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true - }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "1.0.2", - "yallist": "2.1.2" - } - }, - "make-dir": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", - "dev": true, - "requires": { - "pify": "3.0.0" + "chalk": "^2.0.1" } }, "make-error": { @@ -2769,7 +2300,7 @@ "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", "dev": true, "requires": { - "p-defer": "1.0.0" + "p-defer": "^1.0.0" } }, "map-cache": { @@ -2784,7 +2315,7 @@ "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", "dev": true, "requires": { - "object-visit": "1.0.1" + "object-visit": "^1.0.0" } }, "mem": { @@ -2793,9 +2324,9 @@ "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", "dev": true, "requires": { - "map-age-cleaner": "0.1.3", - "mimic-fn": "2.1.0", - "p-is-promise": "2.1.0" + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" } }, "micromatch": { @@ -2804,19 +2335,19 @@ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": true, "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "braces": "2.3.2", - "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.13", - "object.pick": "1.3.0", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "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" } }, "mime-db": { @@ -2846,7 +2377,7 @@ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "brace-expansion": "1.1.11" + "brace-expansion": "^1.1.7" } }, "minimist": { @@ -2861,8 +2392,8 @@ "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "dev": true, "requires": { - "for-in": "1.0.2", - "is-extendable": "1.0.1" + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" }, "dependencies": { "is-extendable": { @@ -2871,7 +2402,7 @@ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { - "is-plain-object": "2.0.4" + "is-plain-object": "^2.0.4" } } } @@ -2928,7 +2459,7 @@ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "2.1.1" + "ms": "^2.1.1" } }, "get-caller-file": { @@ -2943,12 +2474,12 @@ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "dev": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.4", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "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" } }, "ms": { @@ -2969,9 +2500,9 @@ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "dev": true, "requires": { - "emoji-regex": "7.0.3", - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "5.2.0" + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" } }, "strip-ansi": { @@ -2980,7 +2511,7 @@ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { - "ansi-regex": "4.1.0" + "ansi-regex": "^4.1.0" } }, "supports-color": { @@ -2989,7 +2520,7 @@ "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } }, "yargs": { @@ -2998,17 +2529,17 @@ "integrity": "sha512-WyEoxgyTD3w5XRpAQNYUB9ycVH/PQrToaTXdYXRdOXvEy1l19br+VJsc0vcO8PTGg5ro/l/GY7F/JMEBmI0BxA==", "dev": true, "requires": { - "cliui": "4.1.0", - "find-up": "3.0.0", - "get-caller-file": "2.0.5", - "os-locale": "3.1.0", - "require-directory": "2.1.1", - "require-main-filename": "2.0.0", - "set-blocking": "2.0.0", - "string-width": "3.1.0", - "which-module": "2.0.0", - "y18n": "4.0.0", - "yargs-parser": "13.0.0" + "cliui": "^4.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "os-locale": "^3.1.0", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.0.0" } }, "yargs-parser": { @@ -3017,8 +2548,8 @@ "integrity": "sha512-w2LXjoL8oRdRQN+hOyppuXs+V/fVAYtpcrRxZuF7Kt/Oc+Jr2uAcVntaUTNT6w5ihoWfFDpNY8CPx1QskxZ/pw==", "dev": true, "requires": { - "camelcase": "5.3.1", - "decamelize": "1.2.0" + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" } } } @@ -3048,70 +2579,33 @@ "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", "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.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "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" } }, - "neo-async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", - "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", - "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 }, - "nise": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.0.tgz", - "integrity": "sha512-Z3sfYEkLFzFmL8KY6xnSJLRxwQwYBjOXi/24lb62ZnZiGA0JUzGGTI6TBIgfCSMIDl9Jlu8SRmHNACLTemDHww==", - "dev": true, - "requires": { - "@sinonjs/formatio": "3.2.1", - "@sinonjs/text-encoding": "0.7.1", - "just-extend": "4.0.2", - "lolex": "4.1.0", - "path-to-regexp": "1.7.0" - }, - "dependencies": { - "@sinonjs/formatio": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.1.tgz", - "integrity": "sha512-tsHvOB24rvyvV2+zKMmPkZ7dXX6LSLKZ7aOtXY6Edklp0uRcgGpOsQTTGTcWViFyx4uhWc6GV8QdnALbIbIdeQ==", - "dev": true, - "requires": { - "@sinonjs/commons": "1.4.0", - "@sinonjs/samsam": "3.3.2" - } - }, - "lolex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-4.1.0.tgz", - "integrity": "sha512-BYxIEXiVq5lGIXeVHnsFzqa1TxN5acnKnPCdlZSpzm8viNEOhiigupA4vTQ9HEFQ6nLTQ9wQOgBknJgzUYQ9Aw==", - "dev": true - } - } - }, "node-environment-flags": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz", "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==", "dev": true, "requires": { - "object.getownpropertydescriptors": "2.0.3", - "semver": "5.7.0" + "object.getownpropertydescriptors": "^2.0.3", + "semver": "^5.7.0" } }, "node-fetch": { @@ -3120,59 +2614,6 @@ "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==", "dev": true }, - "nodemon": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-1.19.1.tgz", - "integrity": "sha512-/DXLzd/GhiaDXXbGId5BzxP1GlsqtMGM9zTmkWrgXtSqjKmGSbLicM/oAy4FR0YWm14jCHRwnR31AHS2dYFHrg==", - "dev": true, - "requires": { - "chokidar": "2.1.6", - "debug": "3.2.6", - "ignore-by-default": "1.0.1", - "minimatch": "3.0.4", - "pstree.remy": "1.1.7", - "semver": "5.7.0", - "supports-color": "5.5.0", - "touch": "3.1.0", - "undefsafe": "2.0.2", - "update-notifier": "2.5.0" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "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 - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "3.0.0" - } - } - } - }, - "nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", - "dev": true, - "requires": { - "abbrev": "1.1.1" - } - }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -3185,7 +2626,7 @@ "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "dev": true, "requires": { - "path-key": "2.0.1" + "path-key": "^2.0.0" } }, "number-is-nan": { @@ -3212,9 +2653,9 @@ "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", "dev": true, "requires": { - "copy-descriptor": "0.1.1", - "define-property": "0.2.5", - "kind-of": "3.2.2" + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" }, "dependencies": { "define-property": { @@ -3223,7 +2664,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } }, "kind-of": { @@ -3232,7 +2673,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -3249,7 +2690,7 @@ "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", "dev": true, "requires": { - "isobject": "3.0.1" + "isobject": "^3.0.0" } }, "object.assign": { @@ -3258,10 +2699,10 @@ "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", "dev": true, "requires": { - "define-properties": "1.1.3", - "function-bind": "1.1.1", - "has-symbols": "1.0.0", - "object-keys": "1.1.1" + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" } }, "object.getownpropertydescriptors": { @@ -3270,8 +2711,8 @@ "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", "dev": true, "requires": { - "define-properties": "1.1.3", - "es-abstract": "1.13.0" + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" } }, "object.pick": { @@ -3280,7 +2721,7 @@ "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", "dev": true, "requires": { - "isobject": "3.0.1" + "isobject": "^3.0.1" } }, "once": { @@ -3289,39 +2730,7 @@ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { - "wrappy": "1.0.2" - } - }, - "optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "dev": true, - "requires": { - "minimist": "0.0.8", - "wordwrap": "0.0.3" - }, - "dependencies": { - "wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", - "dev": true - } - } - }, - "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", - "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", - "wordwrap": "1.0.0" + "wrappy": "1" } }, "os-locale": { @@ -3330,9 +2739,9 @@ "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", "dev": true, "requires": { - "execa": "1.0.0", - "lcid": "2.0.0", - "mem": "4.3.0" + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" } }, "os-tmpdir": { @@ -3365,7 +2774,7 @@ "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", "dev": true, "requires": { - "p-try": "2.2.0" + "p-try": "^2.0.0" } }, "p-locate": { @@ -3374,7 +2783,7 @@ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, "requires": { - "p-limit": "2.2.0" + "p-limit": "^2.0.0" } }, "p-map": { @@ -3389,18 +2798,6 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, - "package-json": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", - "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", - "dev": true, - "requires": { - "got": "6.7.1", - "registry-auth-token": "3.4.0", - "registry-url": "3.1.0", - "semver": "5.7.0" - } - }, "pako": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", @@ -3449,23 +2846,6 @@ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "dev": true }, - "path-to-regexp": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", - "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", - "dev": true, - "requires": { - "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 - } - } - }, "pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -3478,12 +2858,6 @@ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "dev": true }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, "pinkie": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", @@ -3496,7 +2870,7 @@ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", "dev": true, "requires": { - "pinkie": "2.0.4" + "pinkie": "^2.0.0" } }, "posix-character-classes": { @@ -3505,50 +2879,26 @@ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", "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=", - "dev": true - }, - "prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", - "dev": true - }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, "psl": { "version": "1.1.33", "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.33.tgz", "integrity": "sha512-LTDP2uSrsc7XCb5lO7A8BI1qYxRe/8EqlRvMeEl6rsnYAqDOl8xHR+8lSAIVfrNaSAlTPTNOCgNjWcoUL3AZsw==", "dev": true }, - "pstree.remy": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.7.tgz", - "integrity": "sha512-xsMgrUwRpuGskEzBFkH8NmTimbZ5PcPup0LA8JJkHIm2IMUbQcpo3yeLNWVrufEYjh8YwtSVh0xz6UeWc5Oh5A==", - "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==", "dev": true, "requires": { - "end-of-stream": "1.4.1", - "once": "1.4.0" + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } }, "punycode": { @@ -3569,29 +2919,9 @@ "integrity": "sha512-g6y0Lbq10a5pPQpjlFuojfMfV1Pd2Jw9h75ypiYPPia3Gcq2rgkKiIwbkS6JxH7c5f5u/B/sB+d13PU+g1eu4Q==", "dev": true, "requires": { - "decode-uri-component": "0.2.0", - "split-on-first": "1.1.0", - "strict-uri-encode": "2.0.0" - } - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "requires": { - "deep-extend": "0.6.0", - "ini": "1.3.5", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } + "decode-uri-component": "^0.2.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" } }, "readable-stream": { @@ -3600,13 +2930,13 @@ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.4", - "isarray": "1.0.0", - "process-nextick-args": "2.0.1", - "safe-buffer": "5.1.2", - "string_decoder": "1.1.1", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, "readdirp": { @@ -3615,9 +2945,9 @@ "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", "dev": true, "requires": { - "graceful-fs": "4.2.0", - "micromatch": "3.1.10", - "readable-stream": "2.3.6" + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" } }, "redis": { @@ -3626,9 +2956,9 @@ "integrity": "sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A==", "dev": true, "requires": { - "double-ended-queue": "2.1.0-0", - "redis-commands": "1.5.0", - "redis-parser": "2.6.0" + "double-ended-queue": "^2.1.0-0", + "redis-commands": "^1.2.0", + "redis-parser": "^2.6.0" } }, "redis-commands": { @@ -3649,27 +2979,8 @@ "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", "dev": true, "requires": { - "extend-shallow": "3.0.2", - "safe-regex": "1.1.0" - } - }, - "registry-auth-token": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz", - "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", - "dev": true, - "requires": { - "rc": "1.2.8", - "safe-buffer": "5.1.2" - } - }, - "registry-url": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", - "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", - "dev": true, - "requires": { - "rc": "1.2.8" + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" } }, "remove-trailing-separator": { @@ -3696,26 +3007,26 @@ "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", "dev": true, "requires": { - "aws-sign2": "0.7.0", - "aws4": "1.8.0", - "caseless": "0.12.0", - "combined-stream": "1.0.8", - "extend": "3.0.2", - "forever-agent": "0.6.1", - "form-data": "2.3.3", - "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.24", - "oauth-sign": "0.9.0", - "performance-now": "2.1.0", - "qs": "6.5.2", - "safe-buffer": "5.1.2", - "tough-cookie": "2.4.3", - "tunnel-agent": "0.6.0", - "uuid": "3.3.2" + "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.0", + "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.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" } }, "request-promise": { @@ -3724,10 +3035,10 @@ "integrity": "sha512-8wgMrvE546PzbR5WbYxUQogUnUDfM0S7QIFZMID+J73vdFARkFy+HElj4T+MWYhpXwlLp0EQ8Zoj8xUA0he4Vg==", "dev": true, "requires": { - "bluebird": "3.5.5", + "bluebird": "^3.5.0", "request-promise-core": "1.1.2", - "stealthy-require": "1.1.1", - "tough-cookie": "2.4.3" + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" } }, "request-promise-core": { @@ -3736,7 +3047,7 @@ "integrity": "sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag==", "dev": true, "requires": { - "lodash": "4.17.15" + "lodash": "^4.17.11" } }, "require-directory": { @@ -3757,7 +3068,7 @@ "integrity": "sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==", "dev": true, "requires": { - "path-parse": "1.0.6" + "path-parse": "^1.0.6" } }, "resolve-url": { @@ -3778,7 +3089,7 @@ "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", "dev": true, "requires": { - "glob": "7.1.4" + "glob": "^7.1.3" } }, "safe-buffer": { @@ -3793,7 +3104,7 @@ "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { - "ret": "0.1.15" + "ret": "~0.1.10" } }, "safer-buffer": { @@ -3802,12 +3113,6 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, - "samsam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz", - "integrity": "sha512-1HwIYD/8UlOtFS3QO3w7ey+SdSDFE4HRNLZoZRYVQefrOY3l17epswImeB1ijgJFQJodIaHcwkp3r/myBjFVbg==", - "dev": true - }, "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", @@ -3820,10 +3125,10 @@ "integrity": "sha512-etJt20d8qInkxMAHIm5SEpPBSS+CdxVcybnxzSIB/GlWErb8pIWrArz/VA6VfUW0/6tIcokepXQ5ufvdzqqk1A==", "dev": true, "requires": { - "jszip": "3.2.1", - "rimraf": "2.6.3", + "jszip": "^3.1.5", + "rimraf": "^2.6.3", "tmp": "0.0.30", - "xml2js": "0.4.19" + "xml2js": "^0.4.19" }, "dependencies": { "tmp": { @@ -3832,7 +3137,7 @@ "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=", "dev": true, "requires": { - "os-tmpdir": "1.0.2" + "os-tmpdir": "~1.0.1" } } } @@ -3843,15 +3148,6 @@ "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", "dev": true }, - "semver-diff": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", - "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", - "dev": true, - "requires": { - "semver": "5.7.0" - } - }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -3870,10 +3166,10 @@ "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.4", - "split-string": "3.1.0" + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" }, "dependencies": { "extend-shallow": { @@ -3882,7 +3178,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -3893,7 +3189,7 @@ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "dev": true, "requires": { - "shebang-regex": "1.0.0" + "shebang-regex": "^1.0.0" } }, "shebang-regex": { @@ -3902,106 +3198,26 @@ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, - "should": { - "version": "13.2.3", - "resolved": "https://registry.npmjs.org/should/-/should-13.2.3.tgz", - "integrity": "sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==", - "dev": true, - "requires": { - "should-equal": "2.0.0", - "should-format": "3.0.3", - "should-type": "1.4.0", - "should-type-adaptors": "1.1.0", - "should-util": "1.0.1" - } - }, - "should-equal": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz", - "integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==", - "dev": true, - "requires": { - "should-type": "1.4.0" - } - }, - "should-format": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz", - "integrity": "sha1-m/yPdPo5IFxT04w01xcwPidxJPE=", - "dev": true, - "requires": { - "should-type": "1.4.0", - "should-type-adaptors": "1.1.0" - } - }, - "should-type": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz", - "integrity": "sha1-B1bYzoRt/QmEOmlHcZ36DUz/XPM=", - "dev": true - }, - "should-type-adaptors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz", - "integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==", - "dev": true, - "requires": { - "should-type": "1.4.0", - "should-util": "1.0.1" - } - }, - "should-util": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.1.tgz", - "integrity": "sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==", - "dev": true - }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, - "sinon": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-5.1.1.tgz", - "integrity": "sha512-h/3uHscbt5pQNxkf7Y/Lb9/OM44YNCicHakcq73ncbrIS8lXg+ZGOZbtuU+/km4YnyiCYfQQEwANaReJz7KDfw==", - "dev": true, - "requires": { - "@sinonjs/formatio": "2.0.0", - "diff": "3.5.0", - "lodash.get": "4.4.2", - "lolex": "2.7.5", - "nise": "1.5.0", - "supports-color": "5.5.0", - "type-detect": "4.0.8" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "3.0.0" - } - } - } - }, "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.2", - "debug": "2.6.9", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "map-cache": "0.2.2", - "source-map": "0.5.7", - "source-map-resolve": "0.5.2", - "use": "3.1.1" + "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": { "define-property": { @@ -4010,7 +3226,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } }, "extend-shallow": { @@ -4019,7 +3235,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -4030,9 +3246,9 @@ "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", "dev": true, "requires": { - "define-property": "1.0.0", - "isobject": "3.0.1", - "snapdragon-util": "3.0.1" + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" }, "dependencies": { "define-property": { @@ -4041,7 +3257,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "1.0.2" + "is-descriptor": "^1.0.0" } }, "is-accessor-descriptor": { @@ -4050,7 +3266,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-data-descriptor": { @@ -4059,7 +3275,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-descriptor": { @@ -4068,9 +3284,9 @@ "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" + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } } } @@ -4081,7 +3297,7 @@ "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.2.0" }, "dependencies": { "kind-of": { @@ -4090,7 +3306,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -4107,11 +3323,11 @@ "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", "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" + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" } }, "source-map-support": { @@ -4120,8 +3336,8 @@ "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==", "dev": true, "requires": { - "buffer-from": "1.1.1", - "source-map": "0.6.1" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" }, "dependencies": { "source-map": { @@ -4159,7 +3375,7 @@ "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", "dev": true, "requires": { - "extend-shallow": "3.0.2" + "extend-shallow": "^3.0.0" } }, "sprintf-js": { @@ -4174,15 +3390,15 @@ "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", "dev": true, "requires": { - "asn1": "0.2.4", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.2", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.2", - "getpass": "0.1.7", - "jsbn": "0.1.1", - "safer-buffer": "2.1.2", - "tweetnacl": "0.14.5" + "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" } }, "static-extend": { @@ -4191,8 +3407,8 @@ "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", "dev": true, "requires": { - "define-property": "0.2.5", - "object-copy": "0.1.0" + "define-property": "^0.2.5", + "object-copy": "^0.1.0" }, "dependencies": { "define-property": { @@ -4201,7 +3417,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } } } @@ -4224,8 +3440,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" } }, "string_decoder": { @@ -4234,7 +3450,7 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "safe-buffer": "5.1.2" + "safe-buffer": "~5.1.0" } }, "strip-ansi": { @@ -4243,7 +3459,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^3.0.0" } }, "strip-eof": { @@ -4274,7 +3490,7 @@ "integrity": "sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg==", "dev": true, "requires": { - "ms": "2.1.2" + "ms": "^2.1.1" } }, "ms": { @@ -4285,71 +3501,13 @@ } } }, - "term-size": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", - "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", - "dev": true, - "requires": { - "execa": "0.7.0" - }, - "dependencies": { - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "requires": { - "lru-cache": "4.1.5", - "shebang-command": "1.2.0", - "which": "1.3.1" - } - }, - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "dev": true, - "requires": { - "cross-spawn": "5.1.0", - "get-stream": "3.0.0", - "is-stream": "1.1.0", - "npm-run-path": "2.0.2", - "p-finally": "1.0.0", - "signal-exit": "3.0.2", - "strip-eof": "1.0.0" - } - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - } - } - }, - "timed-out": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", - "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", - "dev": true - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "1.0.2" - } - }, "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.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -4358,7 +3516,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -4369,10 +3527,10 @@ "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" + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" } }, "to-regex-range": { @@ -4381,17 +3539,8 @@ "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", "dev": true, "requires": { - "is-number": "3.0.0", - "repeat-string": "1.6.1" - } - }, - "touch": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", - "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", - "dev": true, - "requires": { - "nopt": "1.0.10" + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" } }, "tough-cookie": { @@ -4400,8 +3549,8 @@ "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", "dev": true, "requires": { - "psl": "1.1.33", - "punycode": "1.4.1" + "psl": "^1.1.24", + "punycode": "^1.4.1" }, "dependencies": { "punycode": { @@ -4424,14 +3573,14 @@ "integrity": "sha512-ZNT+OEGfUNVMGkpIaDJJ44Zq3Yr0bkU/ugN1PHbU+/01Z7UV1fsELRiTx1KuQNvQ1A3pGh3y25iYF6jXgxV21A==", "dev": true, "requires": { - "arrify": "1.0.1", - "buffer-from": "1.1.1", - "diff": "3.5.0", - "make-error": "1.3.5", - "minimist": "1.2.0", - "mkdirp": "0.5.1", - "source-map-support": "0.5.12", - "yn": "2.0.0" + "arrify": "^1.0.0", + "buffer-from": "^1.1.0", + "diff": "^3.1.0", + "make-error": "^1.1.1", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "source-map-support": "^0.5.6", + "yn": "^2.0.0" }, "dependencies": { "minimist": { @@ -4454,19 +3603,19 @@ "integrity": "sha512-Q3kXkuDEijQ37nXZZLKErssQVnwCV/+23gFEMROi8IlbaBG6tXqLPQJ5Wjcyt/yHPKBC+hD5SzuGaMora+ZS6w==", "dev": true, "requires": { - "@babel/code-frame": "7.0.0", - "builtin-modules": "1.1.1", - "chalk": "2.4.2", - "commander": "2.20.0", - "diff": "3.5.0", - "glob": "7.1.4", - "js-yaml": "3.13.1", - "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "resolve": "1.11.1", - "semver": "5.7.0", - "tslib": "1.10.0", - "tsutils": "2.29.0" + "@babel/code-frame": "^7.0.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^3.2.0", + "glob": "^7.1.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.8.0", + "tsutils": "^2.29.0" } }, "tsutils": { @@ -4475,7 +3624,7 @@ "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", "dev": true, "requires": { - "tslib": "1.10.0" + "tslib": "^1.8.1" } }, "tunnel-agent": { @@ -4484,7 +3633,7 @@ "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "dev": true, "requires": { - "safe-buffer": "5.1.2" + "safe-buffer": "^5.0.1" } }, "tweetnacl": { @@ -4493,21 +3642,6 @@ "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 - }, "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -4520,61 +3654,16 @@ "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==", "dev": true }, - "uglify-js": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.3.tgz", - "integrity": "sha512-KfQUgOqTkLp2aZxrMbCuKCDGW9slFYu2A23A36Gs7sGzTLcRBDORdOi5E21KWHFIfkY8kzgi/Pr1cXCh0yIp5g==", - "dev": true, - "optional": true, - "requires": { - "commander": "2.20.3", - "source-map": "0.6.1" - }, - "dependencies": { - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "optional": 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, - "optional": true - } - } - }, - "undefsafe": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.2.tgz", - "integrity": "sha1-Il9rngM3Zj4Njnz9aG/Cg2zKznY=", - "dev": true, - "requires": { - "debug": "2.6.9" - } - }, "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" - } - }, - "unique-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", - "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", - "dev": true, - "requires": { - "crypto-random-string": "1.0.0" + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" } }, "unset-value": { @@ -4583,8 +3672,8 @@ "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", "dev": true, "requires": { - "has-value": "0.3.1", - "isobject": "3.0.1" + "has-value": "^0.3.1", + "isobject": "^3.0.0" }, "dependencies": { "has-value": { @@ -4593,9 +3682,9 @@ "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", "dev": true, "requires": { - "get-value": "2.0.6", - "has-values": "0.1.4", - "isobject": "2.1.0" + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" }, "dependencies": { "isobject": { @@ -4617,43 +3706,19 @@ } } }, - "unzip-response": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", - "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=", - "dev": true - }, "upath": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.2.tgz", "integrity": "sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==", "dev": true }, - "update-notifier": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz", - "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", - "dev": true, - "requires": { - "boxen": "1.3.0", - "chalk": "2.4.2", - "configstore": "3.1.2", - "import-lazy": "2.1.0", - "is-ci": "1.2.1", - "is-installed-globally": "0.1.0", - "is-npm": "1.0.0", - "latest-version": "3.1.0", - "semver-diff": "2.1.0", - "xdg-basedir": "3.0.0" - } - }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", "dev": true, "requires": { - "punycode": "2.1.1" + "punycode": "^2.1.0" } }, "urix": { @@ -4662,15 +3727,6 @@ "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", "dev": true }, - "url-parse-lax": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", - "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", - "dev": true, - "requires": { - "prepend-http": "1.0.4" - } - }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", @@ -4695,9 +3751,9 @@ "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", "dev": true, "requires": { - "assert-plus": "1.0.0", + "assert-plus": "^1.0.0", "core-util-is": "1.0.2", - "extsprintf": "1.3.0" + "extsprintf": "^1.2.0" } }, "which": { @@ -4706,7 +3762,7 @@ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "requires": { - "isexe": "2.0.0" + "isexe": "^2.0.0" } }, "which-module": { @@ -4721,32 +3777,17 @@ "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, "requires": { - "string-width": "2.1.1" + "string-width": "^1.0.2 || 2" } }, - "widest-line": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", - "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", - "dev": true, - "requires": { - "string-width": "2.1.1" - } - }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - }, "wrap-ansi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1" + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" }, "dependencies": { "ansi-regex": { @@ -4761,7 +3802,7 @@ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, "string-width": { @@ -4770,9 +3811,9 @@ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } }, "strip-ansi": { @@ -4781,7 +3822,7 @@ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "^2.0.0" } } } @@ -4792,31 +3833,14 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, - "write-file-atomic": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", - "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", - "dev": true, - "requires": { - "graceful-fs": "4.2.0", - "imurmurhash": "0.1.4", - "signal-exit": "3.0.2" - } - }, - "xdg-basedir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", - "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", - "dev": true - }, "xml2js": { "version": "0.4.19", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", "dev": true, "requires": { - "sax": "1.2.4", - "xmlbuilder": "9.0.7" + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" } }, "xmlbuilder": { @@ -4831,30 +3855,24 @@ "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", "dev": true }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - }, "yargs": { "version": "12.0.5", "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", "dev": true, "requires": { - "cliui": "4.1.0", - "decamelize": "1.2.0", - "find-up": "3.0.0", - "get-caller-file": "1.0.3", - "os-locale": "3.1.0", - "require-directory": "2.1.1", - "require-main-filename": "1.0.1", - "set-blocking": "2.0.0", - "string-width": "2.1.1", - "which-module": "2.0.0", - "y18n": "4.0.0", - "yargs-parser": "11.1.1" + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" } }, "yargs-parser": { @@ -4863,8 +3881,8 @@ "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", "dev": true, "requires": { - "camelcase": "5.3.1", - "decamelize": "1.2.0" + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" } }, "yargs-unparser": { @@ -4873,9 +3891,9 @@ "integrity": "sha512-HK25qidFTCVuj/D1VfNiEndpLIeJN78aqgR23nL3y4N0U/91cOAzqfHlF8n2BvoNDcZmJKin3ddNSvOxSr8flw==", "dev": true, "requires": { - "flat": "4.1.0", - "lodash": "4.17.15", - "yargs": "12.0.5" + "flat": "^4.1.0", + "lodash": "^4.17.11", + "yargs": "^12.0.5" } }, "yauzl": { @@ -4884,7 +3902,7 @@ "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", "dev": true, "requires": { - "fd-slicer": "1.0.1" + "fd-slicer": "~1.0.1" } }, "yn": { diff --git a/package.json b/package.json index 0c318b070..17a16df89 100644 --- a/package.json +++ b/package.json @@ -32,32 +32,23 @@ }, "dependencies": {}, "devDependencies": { - "@types/chokidar": "^1.7.5", - "@types/commander": "^2.12.2", "@types/mocha": "^5.2.6", - "@types/mockdate": "^2.0.0", "@types/node-fetch": "^2.1.4", "@types/query-string": "^5.1.0", - "@types/redis": "^2.8.14", "@types/request": "^2.0.5", "@types/request-promise": "^4.1.38", "@types/selenium-webdriver": "^3.0.16", "@types/speakeasy": "^2.0.2", - "chokidar": "^2.0.4", "chromedriver": "^77.0.0", - "commander": "^2.19.0", "ejs": "^2.6.2", "mocha": "^6.1.4", - "mockdate": "^2.0.1", "node-fetch": "^2.3.0", "query-string": "^6.0.0", "readable-stream": "^2.3.3", - "redis": "^2.8.0", "request": "^2.88.0", "request-promise": "^4.2.2", "selenium-webdriver": "^4.0.0-alpha.4", "speakeasy": "^2.0.0", - "tree-kill": "^1.2.1", "ts-node": "^6.0.1", "tslint": "^5.2.0", "typescript": "^2.9.2" diff --git a/regulation/regulator_test.go b/regulation/regulator_test.go index 21e5a7afc..637816326 100644 --- a/regulation/regulator_test.go +++ b/regulation/regulator_test.go @@ -4,16 +4,13 @@ import ( "testing" "time" - "github.com/stretchr/testify/suite" - - "github.com/clems4ever/authelia/models" - - "github.com/stretchr/testify/assert" - "github.com/clems4ever/authelia/configuration/schema" "github.com/clems4ever/authelia/mocks" + "github.com/clems4ever/authelia/models" "github.com/clems4ever/authelia/regulation" "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" ) type RegulatorSuite struct { diff --git a/scripts/clean-environment.ts b/scripts/clean-environment.ts deleted file mode 100644 index 0a874ebf5..000000000 --- a/scripts/clean-environment.ts +++ /dev/null @@ -1,8 +0,0 @@ -var ListSuites = require('./utils/ListSuites'); - -const suites = ListSuites(); - -suites.forEach(async (suite: string) => { - var { teardown } = require(`../test/suites/${suite}/environment`);; - teardown().catch((err: Error) => console.error(err)); -}); \ No newline at end of file diff --git a/scripts/run-environment.ts b/scripts/run-environment.ts deleted file mode 100644 index df6b04c85..000000000 --- a/scripts/run-environment.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { exec } from './utils/exec'; - -const userSuite = process.argv[2]; -const command = process.argv[3]; // The command to run once the env is up. - -var { setup, setup_timeout, teardown, teardown_timeout } = require(`../test/suites/${userSuite}/environment`); - -function sleep(ms: number) { - return new Promise(resolve => setTimeout(resolve, ms)); -} - -let teardownInProgress = false; - -async function block() { - while (true) { - await sleep(10000); - } -} - -async function blockOrRun(cmd: string | null) { - if (cmd) { - await exec(cmd); - } else { - await block(); - } -} - -process.on('SIGINT', function() { - if (teardownInProgress) return; - teardownInProgress = true; - - stop() - .then(() => process.exit(0)) - .catch(() => process.exit(1)); -}); - -async function stop() { - const timer = setTimeout(() => { - console.error('Teardown timed out...'); - process.exit(1); - }, teardown_timeout); - - try { - console.log('>>> Tearing down environment <<<'); - await teardown(); - clearTimeout(timer); - } catch(err) { - console.error(err); - throw err; - } -} - -async function start() { - const timer = setTimeout(async () => { - console.error('Setup timed out...'); - try { - await teardown(); - } catch(err) { - process.exit(1) - } - }, setup_timeout); - console.log('>>> Setting up environment <<<'); - try { - await setup(); - await sleep(200); - clearTimeout(timer); - await blockOrRun(command); - if (!teardownInProgress) { - await stop(); - process.exit(0); - } - } - catch (err) { - console.error(err); - await stop(); - process.exit(1); - } -} - -start(); \ No newline at end of file diff --git a/scripts/setup-environment.ts b/scripts/setup-environment.ts new file mode 100644 index 000000000..4f94510bf --- /dev/null +++ b/scripts/setup-environment.ts @@ -0,0 +1,10 @@ +var { setup } = require(`../test/suites/${process.argv[2]}/environment`); + +(async function() { + try { + await setup(); + } catch(err) { + console.error(err); + process.exit(1); + } +})() diff --git a/scripts/teardown-environment.ts b/scripts/teardown-environment.ts new file mode 100644 index 000000000..0f74c2d01 --- /dev/null +++ b/scripts/teardown-environment.ts @@ -0,0 +1,10 @@ +var { teardown } = require(`../test/suites/${process.argv[2]}/environment`); + +(async function() { + try { + await teardown(); + } catch(err) { + console.error(err); + process.exit(1); + } +})() diff --git a/test/suites/basic/config.yml b/suites/Basic/configuration.yml similarity index 96% rename from test/suites/basic/config.yml rename to suites/Basic/configuration.yml index a7d5d9b8d..5394b8cef 100644 --- a/test/suites/basic/config.yml +++ b/suites/Basic/configuration.yml @@ -12,7 +12,7 @@ jwt_secret: very_important_secret authentication_backend: file: - path: ./test/suites/basic/users_database.test.yml + path: users.yml session: secret: unsecure_session_secret @@ -91,7 +91,7 @@ regulation: notifier: # Use a SMTP server for sending notifications smtp: - host: 127.0.0.1 + host: smtp port: 1025 sender: admin@example.com diff --git a/test/suites/basic-bypass-no-redirect/users_database.yml b/suites/Basic/users.yml similarity index 100% rename from test/suites/basic-bypass-no-redirect/users_database.yml rename to suites/Basic/users.yml diff --git a/test/suites/basic-bypass-no-redirect/config.yml b/suites/BypassAll/configuration.yml similarity index 92% rename from test/suites/basic-bypass-no-redirect/config.yml rename to suites/BypassAll/configuration.yml index 147176855..2ac9b8ce3 100644 --- a/test/suites/basic-bypass-no-redirect/config.yml +++ b/suites/BypassAll/configuration.yml @@ -10,7 +10,7 @@ jwt_secret: unsecure_secret authentication_backend: file: - path: ./test/suites/basic/users_database.test.yml + path: users.yml session: secret: unsecure_session_secret @@ -38,7 +38,7 @@ access_control: notifier: smtp: - host: 127.0.0.1 + host: smtp port: 1025 sender: admin@example.com diff --git a/test/suites/basic/users_database.yml b/suites/BypassAll/users.yml similarity index 100% rename from test/suites/basic/users_database.yml rename to suites/BypassAll/users.yml diff --git a/test/suites/duo-push/config.yml b/suites/DuoPush/configuration.yml similarity index 96% rename from test/suites/duo-push/config.yml rename to suites/DuoPush/configuration.yml index 13a0f853e..7f7b31e5f 100644 --- a/test/suites/duo-push/config.yml +++ b/suites/DuoPush/configuration.yml @@ -10,7 +10,7 @@ jwt_secret: very_important_secret authentication_backend: file: - path: ./test/suites/basic/users_database.test.yml + path: users.yml session: secret: unsecure_session_secret @@ -95,7 +95,7 @@ regulation: notifier: # Use a SMTP server for sending notifications smtp: - host: 127.0.0.1 + host: smtp port: 1025 sender: admin@example.com diff --git a/test/suites/duo-push/users_database.yml b/suites/DuoPush/users.yml similarity index 100% rename from test/suites/duo-push/users_database.yml rename to suites/DuoPush/users.yml diff --git a/test/suites/high-availability/config.yml b/suites/HighAvailability/configuration.yml similarity index 93% rename from test/suites/high-availability/config.yml rename to suites/HighAvailability/configuration.yml index e975e33b1..bb58ce22b 100644 --- a/test/suites/high-availability/config.yml +++ b/suites/HighAvailability/configuration.yml @@ -44,7 +44,7 @@ authentication_backend: # production. ldap: # The url of the ldap server - url: ldap://127.0.0.1 + url: ldap://openldap # The base dn for every entries base_dn: dc=example,dc=com @@ -87,7 +87,6 @@ authentication_backend: ## file: ## path: ./users_database.yml - # Access Control # # Access control is a list of rules defining the authorizations applied for one @@ -139,43 +138,42 @@ access_control: # Rules applied to 'admin' group - domain: mx2.mail.example.com - subject: 'group:admin' + subject: "group:admin" policy: deny # Rules applied to user 'john' - - domain: '*.example.com' - subject: 'user:john' + - domain: "*.example.com" + subject: "user:john" policy: two_factor - - domain: '*.example.com' - subject: 'group:admin' + - domain: "*.example.com" + subject: "group:admin" policy: two_factor # Rules applied to 'dev' group - domain: dev.example.com resources: - - '^/groups/dev/.*$' - subject: 'group:dev' + - "^/groups/dev/.*$" + subject: "group:dev" policy: two_factor # Rules applied to user 'harry' - domain: dev.example.com resources: - - '^/users/harry/.*$' - subject: 'user:harry' + - "^/users/harry/.*$" + subject: "user:harry" policy: two_factor # Rules applied to user 'bob' - - domain: '*.mail.example.com' - subject: 'user:bob' + - domain: "*.mail.example.com" + subject: "user:bob" policy: two_factor - - domain: 'dev.example.com' + - domain: "dev.example.com" resources: - - '^/users/bob/.*$' - subject: 'user:bob' + - "^/users/bob/.*$" + subject: "user:bob" policy: two_factor - # Configuration of session cookies # # The session cookies identify the user once logged in. @@ -199,7 +197,7 @@ session: # The redis connection details redis: - host: 127.0.0.1 + host: redis port: 6379 password: authelia @@ -230,7 +228,7 @@ storage: # Settings to connect to mongo server mongo: - url: mongodb://127.0.0.1 + url: mongodb://mongo database: authelia auth: username: authelia @@ -244,6 +242,6 @@ storage: notifier: # Use a SMTP server for sending notifications smtp: - host: 127.0.0.1 + host: smtp port: 1025 sender: admin@example.com diff --git a/test/suites/ldap/config.yml b/suites/LDAP/configuration.yml similarity index 86% rename from test/suites/ldap/config.yml rename to suites/LDAP/configuration.yml index f186515e4..2232929de 100644 --- a/test/suites/ldap/config.yml +++ b/suites/LDAP/configuration.yml @@ -13,7 +13,7 @@ jwt_secret: very_important_secret authentication_backend: ldap: # The url of the ldap server - url: 127.0.0.1:389 + url: ldap://openldap # The base dn for every entries base_dn: dc=example,dc=com @@ -66,22 +66,21 @@ totp: access_control: default_policy: deny rules: - - domain: 'public.example.com' - policy: bypass - - domain: 'admin.example.com' - policy: two_factor - - domain: 'secure.example.com' - policy: two_factor - - domain: 'singlefactor.example.com' - policy: one_factor - + - domain: "public.example.com" + policy: bypass + - domain: "admin.example.com" + policy: two_factor + - domain: "secure.example.com" + policy: two_factor + - domain: "singlefactor.example.com" + policy: one_factor # Configuration of the authentication regulation mechanism. -regulation: +regulation: # Set it to 0 to disable max_retries. max_retries: 3 - # The user is banned if the authenticaction failed `max_retries` times in a `find_time` seconds window. + # The user is banned if the authenticaction failed `max_retries` times in a `find_time` seconds window. find_time: 300 # The length of time before a banned user can login again. @@ -90,7 +89,6 @@ regulation: notifier: # Use a SMTP server for sending notifications smtp: - host: 127.0.0.1 + host: smtp port: 1025 sender: admin@example.com - diff --git a/test/suites/mongo/config.yml b/suites/Mongo/configuration.yml similarity index 77% rename from test/suites/mongo/config.yml rename to suites/Mongo/configuration.yml index 803814cae..4907a2be8 100644 --- a/test/suites/mongo/config.yml +++ b/suites/Mongo/configuration.yml @@ -12,7 +12,7 @@ jwt_secret: very_important_secret authentication_backend: file: - path: ./test/suites/mongo/users_database.test.yml + path: users.yml session: secret: unsecure_session_secret @@ -23,7 +23,7 @@ session: # Configuration of the storage backend used to store data and secrets. i.e. totp data storage: mongo: - url: mongodb://127.0.0.1 + url: mongodb://mongo database: authelia auth: username: authelia @@ -39,22 +39,21 @@ totp: access_control: default_policy: deny rules: - - domain: 'public.example.com' - policy: bypass - - domain: 'admin.example.com' - policy: two_factor - - domain: 'secure.example.com' - policy: two_factor - - domain: 'singlefactor.example.com' - policy: one_factor - + - domain: "public.example.com" + policy: bypass + - domain: "admin.example.com" + policy: two_factor + - domain: "secure.example.com" + policy: two_factor + - domain: "singlefactor.example.com" + policy: one_factor # Configuration of the authentication regulation mechanism. -regulation: +regulation: # Set it to 0 to disable max_retries. max_retries: 3 - # The user is banned if the authenticaction failed `max_retries` times in a `find_time` seconds window. + # The user is banned if the authenticaction failed `max_retries` times in a `find_time` seconds window. find_time: 8 # The length of time before a banned user can login again. @@ -63,7 +62,6 @@ regulation: notifier: # Use a SMTP server for sending notifications smtp: - host: 127.0.0.1 + host: smtp port: 1025 sender: admin@example.com - diff --git a/test/suites/mongo/users_database.yml b/suites/Mongo/users.yml similarity index 100% rename from test/suites/mongo/users_database.yml rename to suites/Mongo/users.yml diff --git a/test/suites/network-acls/config.yml b/suites/NetworkACL/configuration.yml similarity index 90% rename from test/suites/network-acls/config.yml rename to suites/NetworkACL/configuration.yml index d8b99fa0e..fc18fcc53 100644 --- a/test/suites/network-acls/config.yml +++ b/suites/NetworkACL/configuration.yml @@ -10,7 +10,7 @@ jwt_secret: unsecure_password authentication_backend: file: - path: ./test/suites/basic/users_database.test.yml + path: users.yml session: secret: unsecure_session_secret @@ -25,7 +25,7 @@ storage: # Access Control # -# Access control is a set of rules you can use to restrict user access to certain +# Access control is a set of rules you can use to restrict user access to certain # resources. access_control: default_policy: deny @@ -44,12 +44,11 @@ access_control: - domain: secure.example.com policy: two_factor - # Configuration of the authentication regulation mechanism. -regulation: +regulation: # Set it to 0 to disable max_retries. max_retries: 3 - # The user is banned if the authenticaction failed `max_retries` times in a `find_time` seconds window. + # The user is banned if the authenticaction failed `max_retries` times in a `find_time` seconds window. find_time: 300 # The length of time before a banned user can login again. ban_time: 900 @@ -57,7 +56,6 @@ regulation: notifier: # Use a SMTP server for sending notifications smtp: - host: 127.0.0.1 + host: smtp port: 1025 sender: admin@example.com - diff --git a/test/suites/network-acls/users_database.yml b/suites/NetworkACL/users.yml similarity index 100% rename from test/suites/network-acls/users_database.yml rename to suites/NetworkACL/users.yml diff --git a/test/suites/short-timeouts/config.yml b/suites/ShortTimeouts/configuration.yml similarity index 88% rename from test/suites/short-timeouts/config.yml rename to suites/ShortTimeouts/configuration.yml index 5766dbd11..f1255188a 100644 --- a/test/suites/short-timeouts/config.yml +++ b/suites/ShortTimeouts/configuration.yml @@ -12,7 +12,7 @@ default_redirection_url: https://home.example.com:8080/ authentication_backend: file: - path: ./test/suites/short-timeouts/users_database.test.yml + path: users.yml session: secret: unsecure_session_secret @@ -34,7 +34,7 @@ totp: # Access Control # -# Access control is a set of rules you can use to restrict user access to certain +# Access control is a set of rules you can use to restrict user access to certain # resources. access_control: # Default policy can either be `bypass`, `one_factor`, `two_factor` or `deny`. @@ -44,39 +44,38 @@ access_control: - domain: singlefactor.example.com policy: one_factor - - domain: '*.example.com' + - domain: "*.example.com" subject: "group:admins" policy: two_factor - domain: dev.example.com resources: - - '^/users/john/.*$' + - "^/users/john/.*$" subject: "user:john" policy: two_factor - domain: dev.example.com resources: - - '^/users/harry/.*$' + - "^/users/harry/.*$" subject: "user:harry" policy: two_factor - - domain: '*.mail.example.com' + - domain: "*.mail.example.com" subject: "user:bob" policy: two_factor - domain: dev.example.com resources: - - '^/users/bob/.*$' + - "^/users/bob/.*$" subject: "user:bob" policy: two_factor - # Configuration of the authentication regulation mechanism. -regulation: +regulation: # Set it to 0 to disable max_retries. max_retries: 3 - # The user is banned if the authenticaction failed `max_retries` times in a `find_time` seconds window. + # The user is banned if the authenticaction failed `max_retries` times in a `find_time` seconds window. find_time: 3 # The length of time before a banned user can login again. @@ -94,16 +93,15 @@ notifier: # filename: /tmp/authelia/notification.txt # Use your email account to send the notifications. You can use an app password. - # List of valid services can be found here: https://nodemailer.com/smtp/well-known/ + # List of valid services can be found here: https://nodemailer.com/smtp/well-known/ ## email: ## username: user@example.com ## password: yourpassword ## sender: admin@example.com ## service: gmail - + # Use a SMTP server for sending notifications smtp: - host: 127.0.0.1 + host: smtp port: 1025 sender: admin@example.com - diff --git a/test/suites/short-timeouts/users_database.yml b/suites/ShortTimeouts/users.yml similarity index 100% rename from test/suites/short-timeouts/users_database.yml rename to suites/ShortTimeouts/users.yml diff --git a/suites/Standalone/configuration.yml b/suites/Standalone/configuration.yml new file mode 100644 index 000000000..0e64db1ce --- /dev/null +++ b/suites/Standalone/configuration.yml @@ -0,0 +1,83 @@ +############################################################### +# Authelia minimal configuration # +############################################################### + +port: 9091 + +logs_level: debug + +default_redirection_url: https://home.example.com:8080/ + +jwt_secret: very_important_secret + +authentication_backend: + file: + path: users.yml + +session: + secret: unsecure_session_secret + domain: example.com + expiration: 3600 # 1 hour + inactivity: 300 # 5 minutes + +storage: + local: + path: db.sqlite3 + +totp: + issuer: example.com + +access_control: + default_policy: deny + + rules: + - domain: singlefactor.example.com + policy: one_factor + + - domain: public.example.com + policy: bypass + + - domain: secure.example.com + policy: two_factor + + - domain: '*.example.com' + subject: "group:admins" + policy: two_factor + + - domain: dev.example.com + resources: + - '^/users/john/.*$' + subject: "user:john" + policy: two_factor + + - domain: dev.example.com + resources: + - '^/users/harry/.*$' + subject: "user:harry" + policy: two_factor + + - domain: '*.mail.example.com' + subject: "user:bob" + policy: two_factor + + - domain: dev.example.com + resources: + - '^/users/bob/.*$' + subject: "user:bob" + policy: two_factor + + +regulation: + # Set it to 0 to disable max_retries. + max_retries: 3 + # The user is banned if the authenticaction failed `max_retries` times in a `find_time` seconds window. + find_time: 300 + # The length of time before a banned user can login again. + ban_time: 900 + +notifier: + smtp: + host: smtp + port: 1025 + sender: admin@example.com + diff --git a/suites/Standalone/users.yml b/suites/Standalone/users.yml new file mode 100644 index 000000000..f42c0e42f --- /dev/null +++ b/suites/Standalone/users.yml @@ -0,0 +1,28 @@ +############################################################### +# Users Database # +############################################################### + +# This file can be used if you do not have an LDAP set up. + +users: + john: + password: "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" + email: john.doe@authelia.com + groups: + - admins + - dev + + harry: + password: "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" + email: harry.potter@authelia.com + groups: [] + + bob: + password: "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" + email: bob.dylan@authelia.com + groups: + - dev + + james: + password: "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" + email: james.dean@authelia.com \ No newline at end of file diff --git a/test/suites/traefik/config.yml b/suites/Traefik/configuration.yml similarity index 66% rename from test/suites/traefik/config.yml rename to suites/Traefik/configuration.yml index bad35cf36..586c57662 100644 --- a/test/suites/traefik/config.yml +++ b/suites/Traefik/configuration.yml @@ -10,7 +10,7 @@ jwt_secret: unsecure_secret authentication_backend: file: - path: ./test/suites/basic/users_database.test.yml + path: users.yml session: secret: unsecure_session_secret @@ -25,18 +25,17 @@ storage: access_control: default_policy: bypass rules: - - domain: 'public.example.com' - policy: bypass - - domain: 'admin.example.com' - policy: two_factor - - domain: 'secure.example.com' - policy: two_factor - - domain: 'singlefactor.example.com' - policy: one_factor + - domain: "public.example.com" + policy: bypass + - domain: "admin.example.com" + policy: two_factor + - domain: "secure.example.com" + policy: two_factor + - domain: "singlefactor.example.com" + policy: one_factor notifier: smtp: - host: 127.0.0.1 + host: smtp port: 1025 sender: admin@example.com - diff --git a/test/suites/traefik/users_database.yml b/suites/Traefik/users.yml similarity index 100% rename from test/suites/traefik/users_database.yml rename to suites/Traefik/users.yml diff --git a/suites/action_http.go b/suites/action_http.go new file mode 100644 index 000000000..e96faabca --- /dev/null +++ b/suites/action_http.go @@ -0,0 +1,26 @@ +package suites + +import ( + "crypto/tls" + "io/ioutil" + "net/http" + + "github.com/stretchr/testify/assert" +) + +func doHTTPGetQuery(s *SeleniumSuite, url string) []byte { + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + client := &http.Client{Transport: tr} + req, err := http.NewRequest("GET", url, nil) + assert.NoError(s.T(), err) + + req.Header.Add("Accept", "application/json") + resp, err := client.Do(req) + assert.NoError(s.T(), err) + + defer resp.Body.Close() + body, _ := ioutil.ReadAll(resp.Body) + return body +} diff --git a/suites/action_login.go b/suites/action_login.go new file mode 100644 index 000000000..870119cc8 --- /dev/null +++ b/suites/action_login.go @@ -0,0 +1,47 @@ +package suites + +import ( + "context" + + "github.com/stretchr/testify/assert" +) + +func doFillLoginPageAndClick(ctx context.Context, s *SeleniumSuite, username, password string, keepMeLoggedIn bool) { + usernameElement := WaitElementLocatedByID(ctx, s, "username") + err := usernameElement.SendKeys(username) + assert.NoError(s.T(), err) + + passwordElement := WaitElementLocatedByID(ctx, s, "password") + err = passwordElement.SendKeys(password) + assert.NoError(s.T(), err) + + if keepMeLoggedIn { + keepMeLoggedInElement := WaitElementLocatedByID(ctx, s, "remember-checkbox") + err = keepMeLoggedInElement.Click() + assert.NoError(s.T(), err) + } + + buttonElement := WaitElementLocatedByTagName(ctx, s, "button") + err = buttonElement.Click() + assert.NoError(s.T(), err) +} + +func doLoginOneFactor(ctx context.Context, s *SeleniumSuite, username, password string, keepMeLoggedIn bool, targetURL string) { + doVisitLoginPage(ctx, s, targetURL) + doFillLoginPageAndClick(ctx, s, username, password, keepMeLoggedIn) +} + +func doLoginTwoFactor(ctx context.Context, s *SeleniumSuite, username, password string, keepMeLoggedIn bool, otpSecret, targetURL string) { + doLoginOneFactor(ctx, s, username, password, keepMeLoggedIn, targetURL) + verifyIsSecondFactorPage(ctx, s) + doValidateTOTP(ctx, s, otpSecret) +} + +func doLoginAndRegisterTOTP(ctx context.Context, s *SeleniumSuite, username, password string, keepMeLoggedIn bool) string { + doLoginOneFactor(ctx, s, username, password, keepMeLoggedIn, "") + secret := doRegisterTOTP(ctx, s) + s.Assert().NotNil(secret) + doVisit(s, LoginBaseURL) + verifyIsSecondFactorPage(ctx, s) + return secret +} diff --git a/suites/action_logout.go b/suites/action_logout.go new file mode 100644 index 000000000..f82c4ada1 --- /dev/null +++ b/suites/action_logout.go @@ -0,0 +1,8 @@ +package suites + +import "context" + +func doLogout(ctx context.Context, s *SeleniumSuite) { + doVisit(s, "https://login.example.com:8080/#/logout") + verifyIsFirstFactorPage(ctx, s) +} diff --git a/suites/action_mail.go b/suites/action_mail.go new file mode 100644 index 000000000..4f57fc23e --- /dev/null +++ b/suites/action_mail.go @@ -0,0 +1,34 @@ +package suites + +import ( + "encoding/json" + "fmt" + "log" + "regexp" + + "github.com/stretchr/testify/assert" +) + +type message struct { + ID int `json:"id"` +} + +func doGetLinkFromLastMail(s *SeleniumSuite) string { + res := doHTTPGetQuery(s, fmt.Sprintf("%s/messages", MailBaseURL)) + messages := make([]message, 0) + err := json.Unmarshal(res, &messages) + assert.NoError(s.T(), err) + assert.Greater(s.T(), len(messages), 0) + + messageID := messages[len(messages)-1].ID + + res = doHTTPGetQuery(s, fmt.Sprintf("%s/messages/%d.html", MailBaseURL, messageID)) + + re := regexp.MustCompile(`.*<\/a>`) + matches := re.FindStringSubmatch(string(res)) + + if len(matches) != 2 { + log.Fatal("Number of match for link in email is not equal to one") + } + return matches[1] +} diff --git a/suites/action_register.go b/suites/action_register.go new file mode 100644 index 000000000..606fb6ba6 --- /dev/null +++ b/suites/action_register.go @@ -0,0 +1,9 @@ +package suites + +import "context" + +func doRegisterThenLogout(ctx context.Context, s *SeleniumSuite, username, password string) string { + secret := doLoginAndRegisterTOTP(ctx, s, username, password, false) + doLogout(ctx, s) + return secret +} diff --git a/suites/action_totp.go b/suites/action_totp.go new file mode 100644 index 000000000..c3076ad68 --- /dev/null +++ b/suites/action_totp.go @@ -0,0 +1,25 @@ +package suites + +import ( + "context" + "time" + + "github.com/pquerna/otp/totp" +) + +func doRegisterTOTP(ctx context.Context, s *SeleniumSuite) string { + WaitElementLocatedByClassName(ctx, s, "register-totp").Click() + verifyBodyContains(ctx, s, "Please check your e-mails") + link := doGetLinkFromLastMail(s) + doVisit(s, link) + secret, err := WaitElementLocatedByClassName(ctx, s, "base32-secret").Text() + s.Assert().NoError(err) + return secret +} + +func doValidateTOTP(ctx context.Context, s *SeleniumSuite, secret string) { + code, err := totp.GenerateCode(secret, time.Now()) + s.Assert().NoError(err) + WaitElementLocatedByID(ctx, s, "totp-token").SendKeys(code) + WaitElementLocatedByID(ctx, s, "totp-button").Click() +} diff --git a/suites/action_visit.go b/suites/action_visit.go new file mode 100644 index 000000000..cc7f4d9f7 --- /dev/null +++ b/suites/action_visit.go @@ -0,0 +1,27 @@ +package suites + +import ( + "context" + "fmt" + "net/url" + + "github.com/stretchr/testify/assert" +) + +func doVisit(s *SeleniumSuite, url string) { + err := s.WebDriver().Get(url) + assert.NoError(s.T(), err) +} + +func doVisitAndVerifyURLIs(ctx context.Context, s *SeleniumSuite, url string) { + doVisit(s, url) + verifyURLIs(ctx, s, url) +} + +func doVisitLoginPage(ctx context.Context, s *SeleniumSuite, targetURL string) { + suffix := "" + if targetURL != "" { + suffix = fmt.Sprintf("?rd=%s", url.QueryEscape(targetURL)) + } + doVisitAndVerifyURLIs(ctx, s, fmt.Sprintf("%s%s", LoginBaseURL, suffix)) +} diff --git a/suites/constants.go b/suites/constants.go new file mode 100644 index 000000000..29d32c709 --- /dev/null +++ b/suites/constants.go @@ -0,0 +1,21 @@ +package suites + +import "fmt" + +// BaseDomain the base domain +var BaseDomain = "example.com:8080" + +// LoginBaseURL the base URL of the login portal +var LoginBaseURL = fmt.Sprintf("https://login.%s/#/", BaseDomain) + +// SingleFactorBaseURL the base URL of the singlefactor domain +var SingleFactorBaseURL = fmt.Sprintf("https://singlefactor.%s", BaseDomain) + +// AdminBaseURL the base URL of the admin domain +var AdminBaseURL = fmt.Sprintf("https://admin.%s", BaseDomain) + +// MailBaseURL the base URL of the mail domain +var MailBaseURL = fmt.Sprintf("https://mail.%s", BaseDomain) + +// HomeBaseURL the base URL of the home domain +var HomeBaseURL = fmt.Sprintf("https://home.%s/", BaseDomain) diff --git a/suites/docker.go b/suites/docker.go new file mode 100644 index 000000000..11434c835 --- /dev/null +++ b/suites/docker.go @@ -0,0 +1,53 @@ +package suites + +import ( + "os" + "os/exec" + "strings" + + "github.com/clems4ever/authelia/utils" + log "github.com/sirupsen/logrus" +) + +// DockerEnvironment represent a docker environment +type DockerEnvironment struct { + dockerComposeFiles []string +} + +// NewDockerEnvironment create a new docker environment +func NewDockerEnvironment(files []string) *DockerEnvironment { + return &DockerEnvironment{dockerComposeFiles: files} +} + +func (de *DockerEnvironment) createCommandWithStdout(cmd string) *exec.Cmd { + dockerCmdLine := "docker-compose -f " + strings.Join(de.dockerComposeFiles, " -f ") + " " + cmd + log.Trace(dockerCmdLine) + return utils.CommandWithStdout("bash", "-c", dockerCmdLine) +} + +func (de *DockerEnvironment) createCommand(cmd string) *exec.Cmd { + dockerCmdLine := "docker-compose -f " + strings.Join(de.dockerComposeFiles, " -f ") + " " + cmd + log.Trace(dockerCmdLine) + return exec.Command("bash", "-c", dockerCmdLine) +} + +// Up spawn a docker environment +func (de *DockerEnvironment) Up(suitePath string) error { + cmd := de.createCommandWithStdout("up -d") + cmd.Env = append(os.Environ(), "SUITE_PATH="+suitePath) + return cmd.Run() +} + +// Down spawn a docker environment +func (de *DockerEnvironment) Down(suitePath string) error { + cmd := de.createCommandWithStdout("down -v") + cmd.Env = append(os.Environ(), "SUITE_PATH="+suitePath) + return cmd.Run() +} + +// Logs get logs of a given service of the environment +func (de *DockerEnvironment) Logs(service string, flags []string) (string, error) { + cmd := de.createCommand("logs " + strings.Join(flags, " ") + " " + service) + content, err := cmd.Output() + return string(content), err +} diff --git a/suites/environment.go b/suites/environment.go new file mode 100644 index 000000000..4a2735f64 --- /dev/null +++ b/suites/environment.go @@ -0,0 +1,51 @@ +package suites + +import ( + "fmt" + "strings" + "time" + + "github.com/clems4ever/authelia/utils" + log "github.com/sirupsen/logrus" +) + +func waitUntilServiceLogDetected( + interval time.Duration, + timeout time.Duration, + dockerEnvironment *DockerEnvironment, + service string, + logPattern string) error { + log.Debug("Waiting for service " + service + " to be ready...") + err := utils.CheckUntil(5*time.Second, 1*time.Minute, func() (bool, error) { + logs, err := dockerEnvironment.Logs(service, []string{"--tail", "20"}) + fmt.Printf(".") + + if err != nil { + return false, err + } + return strings.Contains(logs, logPattern), nil + }) + + fmt.Print("\n") + return err +} + +func waitUntilAutheliaIsReady(dockerEnvironment *DockerEnvironment) error { + err := waitUntilServiceLogDetected( + 5*time.Second, + 90*time.Second, + dockerEnvironment, + "authelia-backend", + "Authelia is listening on") + + if err != nil { + return err + } + + return waitUntilServiceLogDetected( + 5*time.Second, + 90*time.Second, + dockerEnvironment, + "authelia-frontend", + "You can now view authelia-portal in the browser.") +} diff --git a/suites/kubernetes.go b/suites/kubernetes.go new file mode 100644 index 000000000..a39e9db5e --- /dev/null +++ b/suites/kubernetes.go @@ -0,0 +1,122 @@ +package suites + +import ( + "fmt" + "os/exec" + "strings" + "time" + + "github.com/clems4ever/authelia/utils" +) + +var kindImageName = "authelia-kind-proxy" +var dockerCmdLine = fmt.Sprintf("docker-compose -f docker-compose.yml -f example/compose/kind/docker-compose.yml run --rm %s", kindImageName) + +// Kind used for running kind commands +type Kind struct{} + +func kindCommand(cmdline string) *exec.Cmd { + cmd := fmt.Sprintf("%s %s", dockerCmdLine, cmdline) + return utils.Shell(cmd) +} + +// CreateCluster create a new Kubernetes cluster +func (k Kind) CreateCluster() error { + cmd := kindCommand("kind create cluster --config /etc/kind/config.yml") + if err := cmd.Run(); err != nil { + return err + } + + cmd = kindCommand("patch-kubeconfig.sh") + if err := cmd.Run(); err != nil { + return err + } + + // This command is necessary to fix the coredns loop detected when using user-defined docker network + // In that case /etc/resolv.conf use 127.0.0.11 as DNS and CoreDNS thinks it is talking to itself which is wrong. + // This IP is the docker internal DNS so it is safe to disable the loop check. + cmd = kindCommand("sh -c 'kubectl -n kube-system get configmap/coredns -o yaml | grep -v loop | kubectl replace -f -'") + if err := cmd.Run(); err != nil { + return err + } + return nil +} + +// DeleteCluster delete a Kubernetes cluster +func (k Kind) DeleteCluster() error { + cmd := kindCommand("kind delete cluster") + return cmd.Run() +} + +// ClusterExists check whether a cluster exists +func (k Kind) ClusterExists() (bool, error) { + cmd := kindCommand("kind get clusters") + cmd.Stdout = nil + cmd.Stderr = nil + output, err := cmd.Output() + + if err != nil { + return false, err + } + + return strings.Contains(string(output), "kind"), nil +} + +// LoadImage load an image in the Kubernetes container +func (k Kind) LoadImage(imageName string) error { + cmd := kindCommand(fmt.Sprintf("kind load docker-image %s", imageName)) + return cmd.Run() +} + +// Kubectl used for running kubectl commands +type Kubectl struct{} + +// StartProxy start a proxy +func (k Kubectl) StartProxy() error { + cmd := utils.Shell("docker-compose -f docker-compose.yml -f example/compose/kind/docker-compose.yml up -d authelia-kind-proxy") + return cmd.Run() +} + +// StopProxy stop a proxy +func (k Kubectl) StopProxy() error { + cmd := utils.Shell("docker-compose -f docker-compose.yml -f example/compose/kind/docker-compose.yml rm -s -f authelia-kind-proxy") + return cmd.Run() +} + +// DeployThirdparties deploy thirdparty services (ldap, db, ingress controllers, etc...) +func (k Kubectl) DeployThirdparties() error { + cmd := kindCommand("sh -c 'cd /authelia && ./bootstrap.sh'") + return cmd.Run() +} + +// DeployAuthelia deploy Authelia application +func (k Kubectl) DeployAuthelia() error { + cmd := kindCommand("sh -c 'cd /authelia && ./bootstrap-authelia.sh'") + return cmd.Run() +} + +// WaitPodsReady wait for all pods to be ready +func (k Kubectl) WaitPodsReady(timeout time.Duration) error { + return utils.CheckUntil(5*time.Second, timeout, func() (bool, error) { + cmd := kindCommand("kubectl get -n authelia pods --no-headers") + cmd.Stdout = nil + cmd.Stderr = nil + output, _ := cmd.Output() + + lines := strings.Split(string(output), "\n") + + nonEmptyLines := make([]string, 0) + for _, line := range lines { + if line != "" { + nonEmptyLines = append(nonEmptyLines, line) + } + } + + for _, line := range nonEmptyLines { + if !strings.Contains(line, "1/1") { + return false, nil + } + } + return true, nil + }) +} diff --git a/suites/registry.go b/suites/registry.go new file mode 100644 index 000000000..80e0d12a3 --- /dev/null +++ b/suites/registry.go @@ -0,0 +1,63 @@ +package suites + +import ( + "fmt" + "time" + + log "github.com/sirupsen/logrus" +) + +// Suite the definition of a suite +type Suite struct { + TestTimeout time.Duration + SetUp func(tmpPath string) error + SetUpTimeout time.Duration + TearDown func(tmpPath string) error + TearDownTimeout time.Duration + + // A textual description of the suite purpose. + Description string +} + +// Registry represent a registry of suite by name +type Registry struct { + registry map[string]Suite +} + +// GlobalRegistry a global registry used by Authelia tooling +var GlobalRegistry *Registry + +func init() { + GlobalRegistry = NewSuitesRegistry() +} + +// NewSuitesRegistry create a suites registry +func NewSuitesRegistry() *Registry { + return &Registry{make(map[string]Suite)} +} + +// Register register a suite by name +func (sr *Registry) Register(name string, suite Suite) { + if _, found := sr.registry[name]; found { + log.Fatal(fmt.Sprintf("Trying to register the suite %s multiple times", name)) + } + sr.registry[name] = suite +} + +// Get return a suite by name +func (sr *Registry) Get(name string) Suite { + s, found := sr.registry[name] + if !found { + log.Fatal(fmt.Sprintf("The suite %s does not exist", name)) + } + return s +} + +// Suites list available suites +func (sr *Registry) Suites() []string { + suites := make([]string, 0) + for k := range sr.registry { + suites = append(suites, k) + } + return suites +} diff --git a/suites/scenario_one_factor_test.go b/suites/scenario_one_factor_test.go new file mode 100644 index 000000000..3dbd480f0 --- /dev/null +++ b/suites/scenario_one_factor_test.go @@ -0,0 +1,80 @@ +package suites + +import ( + "context" + "fmt" + "log" + "testing" + "time" + + "github.com/stretchr/testify/suite" +) + +type OneFactorSuite struct { + *SeleniumSuite +} + +func NewOneFactorSuite() *OneFactorSuite { + return &OneFactorSuite{ + SeleniumSuite: new(SeleniumSuite), + } +} + +func (s *OneFactorSuite) SetupSuite() { + wds, err := StartWebDriver() + + if err != nil { + log.Fatal(err) + } + + s.SeleniumSuite.WebDriverSession = wds +} + +func (s *OneFactorSuite) TearDownSuite() { + err := s.WebDriverSession.Stop() + + if err != nil { + log.Fatal(err) + } +} + +func (s *OneFactorSuite) SetupTest() { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + doLogout(ctx, s.SeleniumSuite) + doVisit(s.SeleniumSuite, HomeBaseURL) + verifyURLIs(ctx, s.SeleniumSuite, HomeBaseURL) +} + +func (s *OneFactorSuite) TestShouldAuthorizeSecretAfterOneFactor() { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + targetURL := fmt.Sprintf("%s/secret.html", SingleFactorBaseURL) + doLoginOneFactor(ctx, s.SeleniumSuite, "john", "password", false, targetURL) + verifySecretAuthorized(ctx, s.SeleniumSuite) +} + +func (s *OneFactorSuite) TestShouldRedirectToSecondFactor() { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + targetURL := fmt.Sprintf("%s/secret.html", AdminBaseURL) + doLoginOneFactor(ctx, s.SeleniumSuite, "john", "password", false, targetURL) + verifyIsSecondFactorPage(ctx, s.SeleniumSuite) +} + +func (s *OneFactorSuite) TestShouldDenyAccessOnBadPassword() { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + targetURL := fmt.Sprintf("%s/secret.html", AdminBaseURL) + doLoginOneFactor(ctx, s.SeleniumSuite, "john", "bad-password", false, targetURL) + verifyIsFirstFactorPage(ctx, s.SeleniumSuite) + verifyNotificationDisplayed(ctx, s.SeleniumSuite, "Authentication failed. Check your credentials.") +} + +func TestRunOneFactor(t *testing.T) { + suite.Run(t, NewOneFactorSuite()) +} diff --git a/suites/scenario_two_factor_test.go b/suites/scenario_two_factor_test.go new file mode 100644 index 000000000..abfa75967 --- /dev/null +++ b/suites/scenario_two_factor_test.go @@ -0,0 +1,65 @@ +package suites + +import ( + "context" + "fmt" + "log" + "testing" + "time" + + "github.com/stretchr/testify/suite" +) + +type TwoFactorSuite struct { + *SeleniumSuite +} + +func NewTwoFactorSuite() *TwoFactorSuite { + return &TwoFactorSuite{ + SeleniumSuite: new(SeleniumSuite), + } +} + +func (s *TwoFactorSuite) SetupSuite() { + wds, err := StartWebDriver() + + if err != nil { + log.Fatal(err) + } + + s.SeleniumSuite.WebDriverSession = wds +} + +func (s *TwoFactorSuite) TearDownSuite() { + err := s.WebDriverSession.Stop() + + if err != nil { + log.Fatal(err) + } +} + +func (s *TwoFactorSuite) SetupTest() { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + doLogout(ctx, s.SeleniumSuite) + doVisit(s.SeleniumSuite, HomeBaseURL) + verifyURLIs(ctx, s.SeleniumSuite, HomeBaseURL) +} + +func (s *TwoFactorSuite) TestShouldAuthorizeSecretAfterTwoFactor() { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // Register TOTP secret and logout. + secret := doRegisterThenLogout(ctx, s.SeleniumSuite, "john", "password") + + targetURL := fmt.Sprintf("%s/secret.html", AdminBaseURL) + doLoginTwoFactor(ctx, s.SeleniumSuite, "john", "password", false, secret, targetURL) + + verifySecretAuthorized(ctx, s.SeleniumSuite) +} + +func TestRunTwoFactor(t *testing.T) { + suite.Run(t, NewTwoFactorSuite()) +} diff --git a/suites/suite_basic.go b/suites/suite_basic.go new file mode 100644 index 000000000..7af7286ff --- /dev/null +++ b/suites/suite_basic.go @@ -0,0 +1,39 @@ +package suites + +import ( + "time" +) + +var basicSuiteName = "Basic" + +func init() { + dockerEnvironment := NewDockerEnvironment([]string{ + "docker-compose.yml", + "example/compose/authelia/docker-compose.backend.yml", + "example/compose/authelia/docker-compose.frontend.yml", + "example/compose/nginx/backend/docker-compose.yml", + "example/compose/nginx/portal/docker-compose.yml", + "example/compose/smtp/docker-compose.yml", + }) + + setup := func(suitePath string) error { + if err := dockerEnvironment.Up(suitePath); err != nil { + return err + } + return waitUntilAutheliaIsReady(dockerEnvironment) + } + + teardown := func(suitePath string) error { + return dockerEnvironment.Down(suitePath) + } + + GlobalRegistry.Register(basicSuiteName, Suite{ + TestTimeout: 1 * time.Minute, + SetUp: setup, + SetUpTimeout: 5 * time.Minute, + TearDown: teardown, + TearDownTimeout: 1 * time.Minute, + Description: `This suite is used to test Authelia in a standalone +configuration with in-memory sessions and a local sqlite db stored on disk`, + }) +} diff --git a/suites/suite_basic_test.go b/suites/suite_basic_test.go new file mode 100644 index 000000000..b03195d78 --- /dev/null +++ b/suites/suite_basic_test.go @@ -0,0 +1,17 @@ +package suites + +import ( + "testing" +) + +type BasicSuite struct { + *SeleniumSuite +} + +func NewBasicSuite() *BasicSuite { + return &BasicSuite{SeleniumSuite: new(SeleniumSuite)} +} + +func TestBasicSuite(t *testing.T) { + RunTypescriptSuite(t, basicSuiteName) +} diff --git a/suites/suite_bypass_all.go b/suites/suite_bypass_all.go new file mode 100644 index 000000000..a9d332a27 --- /dev/null +++ b/suites/suite_bypass_all.go @@ -0,0 +1,41 @@ +package suites + +import ( + "time" +) + +var bypassAllSuiteName = "BypassAll" + +func init() { + dockerEnvironment := NewDockerEnvironment([]string{ + "docker-compose.yml", + "example/compose/authelia/docker-compose.backend.yml", + "example/compose/authelia/docker-compose.frontend.yml", + "example/compose/nginx/backend/docker-compose.yml", + "example/compose/nginx/portal/docker-compose.yml", + "example/compose/httpbin/docker-compose.yml", + "example/compose/smtp/docker-compose.yml", + "example/compose/duo-api/docker-compose.yml", + }) + + setup := func(suitePath string) error { + if err := dockerEnvironment.Up(suitePath); err != nil { + return err + } + + return waitUntilAutheliaIsReady(dockerEnvironment) + } + + teardown := func(suitePath string) error { + return dockerEnvironment.Down(suitePath) + } + + GlobalRegistry.Register(bypassAllSuiteName, Suite{ + SetUp: setup, + SetUpTimeout: 5 * time.Minute, + TestTimeout: 1 * time.Minute, + TearDown: teardown, + TearDownTimeout: 2 * time.Minute, + Description: "This suite has been created to test Authelia with a bypass policy on all resources", + }) +} diff --git a/suites/suite_bypass_all_test.go b/suites/suite_bypass_all_test.go new file mode 100644 index 000000000..6f8511a10 --- /dev/null +++ b/suites/suite_bypass_all_test.go @@ -0,0 +1,17 @@ +package suites + +import ( + "testing" +) + +type BypassAllSuite struct { + *SeleniumSuite +} + +func NewBypassAllSuite() *BypassAllSuite { + return &BypassAllSuite{SeleniumSuite: new(SeleniumSuite)} +} + +func TestBypassAllSuite(t *testing.T) { + RunTypescriptSuite(t, bypassAllSuiteName) +} diff --git a/suites/suite_duo_push.go b/suites/suite_duo_push.go new file mode 100644 index 000000000..516e6230f --- /dev/null +++ b/suites/suite_duo_push.go @@ -0,0 +1,42 @@ +package suites + +import ( + "time" +) + +var duoPushSuiteName = "DuoPush" + +func init() { + dockerEnvironment := NewDockerEnvironment([]string{ + "docker-compose.yml", + "example/compose/authelia/docker-compose.backend.yml", + "example/compose/authelia/docker-compose.frontend.yml", + "example/compose/nginx/backend/docker-compose.yml", + "example/compose/nginx/portal/docker-compose.yml", + "example/compose/duo-api/docker-compose.yml", + }) + + setup := func(suitePath string) error { + if err := dockerEnvironment.Up(suitePath); err != nil { + return err + } + + return waitUntilAutheliaIsReady(dockerEnvironment) + } + + teardown := func(suitePath string) error { + return dockerEnvironment.Down(suitePath) + } + + GlobalRegistry.Register(duoPushSuiteName, Suite{ + SetUp: setup, + SetUpTimeout: 5 * time.Minute, + TestTimeout: 1 * time.Minute, + TearDown: teardown, + TearDownTimeout: 2 * time.Minute, + + Description: `This suite has been created to test Authelia against +the Duo API for push notifications. It allows a user to validate second factor +with a mobile phone.`, + }) +} diff --git a/suites/suite_duo_push_test.go b/suites/suite_duo_push_test.go new file mode 100644 index 000000000..91f842590 --- /dev/null +++ b/suites/suite_duo_push_test.go @@ -0,0 +1,17 @@ +package suites + +import ( + "testing" +) + +type DuoPushSuite struct { + *SeleniumSuite +} + +func NewDuoPushSuite() *DuoPushSuite { + return &DuoPushSuite{SeleniumSuite: new(SeleniumSuite)} +} + +func TestDuoPushSuite(t *testing.T) { + RunTypescriptSuite(t, duoPushSuiteName) +} diff --git a/suites/suite_high_availability.go b/suites/suite_high_availability.go new file mode 100644 index 000000000..33d62cda6 --- /dev/null +++ b/suites/suite_high_availability.go @@ -0,0 +1,45 @@ +package suites + +import ( + "time" +) + +var highAvailabilitySuiteName = "HighAvailability" + +func init() { + dockerEnvironment := NewDockerEnvironment([]string{ + "docker-compose.yml", + "example/compose/authelia/docker-compose.backend.yml", + "example/compose/authelia/docker-compose.frontend.yml", + "example/compose/mongo/docker-compose.yml", + "example/compose/redis/docker-compose.yml", + "example/compose/nginx/backend/docker-compose.yml", + "example/compose/nginx/portal/docker-compose.yml", + "example/compose/smtp/docker-compose.yml", + "example/compose/httpbin/docker-compose.yml", + "example/compose/ldap/docker-compose.admin.yml", // This is just used for administration, not for testing. + "example/compose/ldap/docker-compose.yml", + }) + + setup := func(suitePath string) error { + if err := dockerEnvironment.Up(suitePath); err != nil { + return err + } + + return waitUntilAutheliaIsReady(dockerEnvironment) + } + + teardown := func(suitePath string) error { + return dockerEnvironment.Down(suitePath) + } + + GlobalRegistry.Register(highAvailabilitySuiteName, Suite{ + SetUp: setup, + SetUpTimeout: 5 * time.Minute, + TestTimeout: 1 * time.Minute, + TearDown: teardown, + TearDownTimeout: 2 * time.Minute, + Description: `This suite is made to test Authelia in a *complete* +environment, that is, with all components making Authelia highly available.`, + }) +} diff --git a/suites/suite_high_availability_test.go b/suites/suite_high_availability_test.go new file mode 100644 index 000000000..68d1d1c78 --- /dev/null +++ b/suites/suite_high_availability_test.go @@ -0,0 +1,19 @@ +package suites + +import ( + "testing" +) + +type HighAvailabilitySuite struct { + *SeleniumSuite +} + +func NewHighAvailabilitySuite() *HighAvailabilitySuite { + return &HighAvailabilitySuite{SeleniumSuite: new(SeleniumSuite)} +} + +func TestHighAvailabilitySuite(t *testing.T) { + RunTypescriptSuite(t, highAvailabilitySuiteName) + + TestRunOneFactor(t) +} diff --git a/suites/suite_kubernetes.go b/suites/suite_kubernetes.go new file mode 100644 index 000000000..48f916c55 --- /dev/null +++ b/suites/suite_kubernetes.go @@ -0,0 +1,111 @@ +package suites + +import ( + "fmt" + "time" + + "github.com/clems4ever/authelia/utils" + log "github.com/sirupsen/logrus" +) + +var kubernetesSuiteName = "Kubernetes" + +func init() { + kind := Kind{} + kubectl := Kubectl{} + + setup := func(suitePath string) error { + exists, err := kind.ClusterExists() + + if err != nil { + return err + } + + if exists { + log.Debug("Kubernetes cluster already exists") + } else { + err = kind.CreateCluster() + + if err != nil { + return err + } + } + + log.Debug("Building authelia:dist image...") + if err := utils.Shell("authelia-scripts docker build").Run(); err != nil { + return err + } + + log.Debug("Loading images into Kubernetes container...") + if err = loadDockerImages(); err != nil { + return err + } + + log.Debug("Deploying thirdparties...") + if err = kubectl.DeployThirdparties(); err != nil { + return err + } + + log.Debug("Waiting for services to be ready...") + if err := waitAllPodsAreReady(5 * time.Minute); err != nil { + return err + } + + log.Debug("Deploying Authelia...") + if err = kubectl.DeployAuthelia(); err != nil { + return err + } + + log.Debug("Waiting for services to be ready...") + if err := waitAllPodsAreReady(2 * time.Minute); err != nil { + return err + } + + log.Debug("Starting proxy...") + if err := kubectl.StartProxy(); err != nil { + return err + } + return nil + } + + teardown := func(suitePath string) error { + kubectl.StopProxy() + return kind.DeleteCluster() + } + + GlobalRegistry.Register(kubernetesSuiteName, Suite{ + TestTimeout: 60 * time.Second, + SetUp: setup, + SetUpTimeout: 10 * time.Minute, + TearDown: teardown, + TearDownTimeout: 10 * time.Minute, + Description: "This suite has been created to test Authelia in a Kubernetes context and using nginx as the ingress controller.", + }) +} + +func loadDockerImages() error { + kind := Kind{} + images := []string{"authelia:dist", "authelia-example-backend"} + + for _, image := range images { + err := kind.LoadImage(image) + + if err != nil { + return err + } + } + + return nil +} + +func waitAllPodsAreReady(timeout time.Duration) error { + kubectl := Kubectl{} + // Wait in case the deployment has just been done and some services do not appear in kubectl logs. + time.Sleep(1 * time.Second) + fmt.Println("Check services are running") + if err := kubectl.WaitPodsReady(timeout); err != nil { + return err + } + fmt.Println("All pods are ready") + return nil +} diff --git a/suites/suite_kubernetes_test.go b/suites/suite_kubernetes_test.go new file mode 100644 index 000000000..879cd74ce --- /dev/null +++ b/suites/suite_kubernetes_test.go @@ -0,0 +1,20 @@ +package suites + +import ( + "testing" + + "github.com/stretchr/testify/suite" +) + +type KubernetesSuite struct { + *SeleniumSuite +} + +func NewKubernetesSuite() *KubernetesSuite { + return &KubernetesSuite{SeleniumSuite: new(SeleniumSuite)} +} + +func TestKubernetesSuite(t *testing.T) { + suite.Run(t, NewOneFactorSuite()) + suite.Run(t, NewTwoFactorSuite()) +} diff --git a/suites/suite_ldap.go b/suites/suite_ldap.go new file mode 100644 index 000000000..14849c29d --- /dev/null +++ b/suites/suite_ldap.go @@ -0,0 +1,42 @@ +package suites + +import ( + "time" +) + +var ldapSuiteName = "LDAP" + +func init() { + dockerEnvironment := NewDockerEnvironment([]string{ + "docker-compose.yml", + "example/compose/authelia/docker-compose.backend.yml", + "example/compose/authelia/docker-compose.frontend.yml", + "example/compose/nginx/backend/docker-compose.yml", + "example/compose/nginx/portal/docker-compose.yml", + "example/compose/smtp/docker-compose.yml", + "example/compose/ldap/docker-compose.yml", + }) + + setup := func(suitePath string) error { + err := dockerEnvironment.Up(suitePath) + + if err != nil { + return err + } + + return waitUntilAutheliaIsReady(dockerEnvironment) + } + + teardown := func(suitePath string) error { + err := dockerEnvironment.Down(suitePath) + return err + } + + GlobalRegistry.Register(ldapSuiteName, Suite{ + SetUp: setup, + SetUpTimeout: 5 * time.Minute, + TestTimeout: 1 * time.Minute, + TearDown: teardown, + TearDownTimeout: 2 * time.Minute, + }) +} diff --git a/suites/suite_ldap_test.go b/suites/suite_ldap_test.go new file mode 100644 index 000000000..9df63308f --- /dev/null +++ b/suites/suite_ldap_test.go @@ -0,0 +1,20 @@ +package suites + +import ( + "testing" + + "github.com/stretchr/testify/suite" +) + +type LDAPSuite struct { + *SeleniumSuite +} + +func NewLDAPSuite() *LDAPSuite { + return &LDAPSuite{SeleniumSuite: new(SeleniumSuite)} +} + +func TestLDAPSuite(t *testing.T) { + suite.Run(t, NewOneFactorSuite()) + suite.Run(t, NewTwoFactorSuite()) +} diff --git a/suites/suite_mongo.go b/suites/suite_mongo.go new file mode 100644 index 000000000..a68c7e290 --- /dev/null +++ b/suites/suite_mongo.go @@ -0,0 +1,42 @@ +package suites + +import ( + "time" +) + +var mongoSuiteName = "Mongo" + +func init() { + dockerEnvironment := NewDockerEnvironment([]string{ + "docker-compose.yml", + "example/compose/authelia/docker-compose.backend.yml", + "example/compose/authelia/docker-compose.frontend.yml", + "example/compose/nginx/backend/docker-compose.yml", + "example/compose/nginx/portal/docker-compose.yml", + "example/compose/smtp/docker-compose.yml", + "example/compose/mongo/docker-compose.yml", + "example/compose/ldap/docker-compose.yml", + }) + + setup := func(suitePath string) error { + err := dockerEnvironment.Up(suitePath) + + if err != nil { + return err + } + + return waitUntilAutheliaIsReady(dockerEnvironment) + } + + teardown := func(suitePath string) error { + err := dockerEnvironment.Down(suitePath) + return err + } + + GlobalRegistry.Register(mongoSuiteName, Suite{ + SetUp: setup, + SetUpTimeout: 5 * time.Minute, + TearDown: teardown, + TearDownTimeout: 2 * time.Minute, + }) +} diff --git a/suites/suite_mongo_test.go b/suites/suite_mongo_test.go new file mode 100644 index 000000000..30e863eb6 --- /dev/null +++ b/suites/suite_mongo_test.go @@ -0,0 +1,20 @@ +package suites + +import ( + "testing" + + "github.com/stretchr/testify/suite" +) + +type MongoSuite struct { + *SeleniumSuite +} + +func NewMongoSuite() *MongoSuite { + return &MongoSuite{SeleniumSuite: new(SeleniumSuite)} +} + +func TestMongoSuite(t *testing.T) { + suite.Run(t, NewOneFactorSuite()) + suite.Run(t, NewTwoFactorSuite()) +} diff --git a/suites/suite_network_acl.go b/suites/suite_network_acl.go new file mode 100644 index 000000000..de91871a5 --- /dev/null +++ b/suites/suite_network_acl.go @@ -0,0 +1,44 @@ +package suites + +import ( + "time" +) + +var networkACLSuiteName = "NetworkACL" + +func init() { + dockerEnvironment := NewDockerEnvironment([]string{ + "docker-compose.yml", + "example/compose/authelia/docker-compose.backend.yml", + "example/compose/authelia/docker-compose.frontend.yml", + "example/compose/nginx/backend/docker-compose.yml", + "example/compose/nginx/portal/docker-compose.yml", + "example/compose/squid/docker-compose.yml", + "example/compose/smtp/docker-compose.yml", + // To debug headers + "example/compose/httpbin/docker-compose.yml", + }) + + setup := func(suitePath string) error { + if err := dockerEnvironment.Up(suitePath); err != nil { + return err + } + + return waitUntilAutheliaIsReady(dockerEnvironment) + } + + teardown := func(suitePath string) error { + return dockerEnvironment.Down(suitePath) + } + + GlobalRegistry.Register(networkACLSuiteName, Suite{ + SetUp: setup, + SetUpTimeout: 5 * time.Minute, + TestTimeout: 1 * time.Minute, + TearDown: teardown, + TearDownTimeout: 2 * time.Minute, + Description: `This suite has been created to test Authelia with basic feature in a non highly-available setup. +Authelia basically use an in-memory cache to store user sessions and persist data on disk instead +of using a remote database. Also, the user accounts are stored in file-based database.`, + }) +} diff --git a/suites/suite_network_acl_test.go b/suites/suite_network_acl_test.go new file mode 100644 index 000000000..d3341eca3 --- /dev/null +++ b/suites/suite_network_acl_test.go @@ -0,0 +1,17 @@ +package suites + +import ( + "testing" +) + +type NetworkACLSuite struct { + *SeleniumSuite +} + +func NewNetworkACLSuite() *NetworkACLSuite { + return &NetworkACLSuite{SeleniumSuite: new(SeleniumSuite)} +} + +func TestNetworkACLSuite(t *testing.T) { + RunTypescriptSuite(t, networkACLSuiteName) +} diff --git a/suites/suite_short_timeouts.go b/suites/suite_short_timeouts.go new file mode 100644 index 000000000..f808b843d --- /dev/null +++ b/suites/suite_short_timeouts.go @@ -0,0 +1,40 @@ +package suites + +import ( + "time" +) + +var shortTimeoutsSuiteName = "ShortTimeouts" + +func init() { + dockerEnvironment := NewDockerEnvironment([]string{ + "docker-compose.yml", + "example/compose/authelia/docker-compose.backend.yml", + "example/compose/authelia/docker-compose.frontend.yml", + "example/compose/nginx/backend/docker-compose.yml", + "example/compose/nginx/portal/docker-compose.yml", + "example/compose/smtp/docker-compose.yml", + }) + + setup := func(suitePath string) error { + if err := dockerEnvironment.Up(suitePath); err != nil { + return err + } + + return waitUntilAutheliaIsReady(dockerEnvironment) + } + + teardown := func(suitePath string) error { + return dockerEnvironment.Down(suitePath) + } + + GlobalRegistry.Register(shortTimeoutsSuiteName, Suite{ + SetUp: setup, + SetUpTimeout: 5 * time.Minute, + TestTimeout: 1 * time.Minute, + TearDown: teardown, + TearDownTimeout: 2 * time.Minute, + Description: `This suite has been created to configure Authelia with short timeouts for sessions expiration +in order to test the inactivity feature and the remember me feature.`, + }) +} diff --git a/suites/suite_short_timeouts_test.go b/suites/suite_short_timeouts_test.go new file mode 100644 index 000000000..487a66e63 --- /dev/null +++ b/suites/suite_short_timeouts_test.go @@ -0,0 +1,17 @@ +package suites + +import ( + "testing" +) + +type ShortTimeoutsSuite struct { + *SeleniumSuite +} + +func NewShortTimeoutsSuite() *ShortTimeoutsSuite { + return &ShortTimeoutsSuite{SeleniumSuite: new(SeleniumSuite)} +} + +func TestShortTimeoutsSuite(t *testing.T) { + RunTypescriptSuite(t, shortTimeoutsSuiteName) +} diff --git a/suites/suite_standalone.go b/suites/suite_standalone.go new file mode 100644 index 000000000..7e8b2462c --- /dev/null +++ b/suites/suite_standalone.go @@ -0,0 +1,40 @@ +package suites + +import ( + "time" +) + +var standaloneSuiteName = "Standalone" + +func init() { + dockerEnvironment := NewDockerEnvironment([]string{ + "docker-compose.yml", + "example/compose/authelia/docker-compose.backend.yml", + "example/compose/authelia/docker-compose.frontend.yml", + "example/compose/nginx/backend/docker-compose.yml", + "example/compose/nginx/portal/docker-compose.yml", + "example/compose/smtp/docker-compose.yml", + }) + + setup := func(suitePath string) error { + err := dockerEnvironment.Up(suitePath) + + if err != nil { + return err + } + + return waitUntilAutheliaIsReady(dockerEnvironment) + } + + teardown := func(suitePath string) error { + err := dockerEnvironment.Down(suitePath) + return err + } + + GlobalRegistry.Register(standaloneSuiteName, Suite{ + SetUp: setup, + SetUpTimeout: 5 * time.Minute, + TearDown: teardown, + TearDownTimeout: 5 * time.Minute, + }) +} diff --git a/suites/suite_standalone_test.go b/suites/suite_standalone_test.go new file mode 100644 index 000000000..43699e3b2 --- /dev/null +++ b/suites/suite_standalone_test.go @@ -0,0 +1,20 @@ +package suites + +import ( + "testing" + + "github.com/stretchr/testify/suite" +) + +type StandaloneSuite struct { + *SeleniumSuite +} + +func NewStandaloneSuite() *StandaloneSuite { + return &StandaloneSuite{SeleniumSuite: new(SeleniumSuite)} +} + +func TestStandaloneSuite(t *testing.T) { + suite.Run(t, NewOneFactorSuite()) + suite.Run(t, NewTwoFactorSuite()) +} diff --git a/suites/suite_traefik.go b/suites/suite_traefik.go new file mode 100644 index 000000000..75652835b --- /dev/null +++ b/suites/suite_traefik.go @@ -0,0 +1,40 @@ +package suites + +import ( + "time" +) + +var traefikSuiteName = "Traefik" + +func init() { + dockerEnvironment := NewDockerEnvironment([]string{ + "docker-compose.yml", + "example/compose/authelia/docker-compose.backend.yml", + "example/compose/authelia/docker-compose.frontend.yml", + "example/compose/nginx/backend/docker-compose.yml", + "example/compose/traefik/docker-compose.yml", + "example/compose/smtp/docker-compose.yml", + }) + + setup := func(suitePath string) error { + err := dockerEnvironment.Up(suitePath) + + if err != nil { + return err + } + + return waitUntilAutheliaIsReady(dockerEnvironment) + } + + teardown := func(suitePath string) error { + err := dockerEnvironment.Down(suitePath) + return err + } + + GlobalRegistry.Register(traefikSuiteName, Suite{ + SetUp: setup, + SetUpTimeout: 5 * time.Minute, + TearDown: teardown, + TearDownTimeout: 2 * time.Minute, + }) +} diff --git a/suites/suite_traefik_test.go b/suites/suite_traefik_test.go new file mode 100644 index 000000000..8d543fd58 --- /dev/null +++ b/suites/suite_traefik_test.go @@ -0,0 +1,20 @@ +package suites + +import ( + "testing" + + "github.com/stretchr/testify/suite" +) + +type TraefikSuite struct { + *SeleniumSuite +} + +func NewTraefikSuite() *TraefikSuite { + return &TraefikSuite{SeleniumSuite: new(SeleniumSuite)} +} + +func TestTraefikSuite(t *testing.T) { + suite.Run(t, NewOneFactorSuite()) + suite.Run(t, NewTwoFactorSuite()) +} diff --git a/suites/suites.go b/suites/suites.go new file mode 100644 index 000000000..d8042de84 --- /dev/null +++ b/suites/suites.go @@ -0,0 +1,101 @@ +package suites + +import ( + "context" + "errors" + "fmt" + "os" + "testing" + + "github.com/clems4ever/authelia/utils" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" + "github.com/tebeka/selenium" +) + +// SeleniumSuite is a selenium suite +type SeleniumSuite struct { + suite.Suite + + *WebDriverSession +} + +// WebDriver return the webdriver of the suite +func (s *SeleniumSuite) WebDriver() selenium.WebDriver { + return s.WebDriverSession.WebDriver +} + +// Wait wait until condition holds true +func (s *SeleniumSuite) Wait(ctx context.Context, condition selenium.Condition) error { + done := make(chan error, 1) + go func() { + done <- s.WebDriverSession.WebDriver.Wait(condition) + }() + + select { + case <-ctx.Done(): + return errors.New("waiting timeout reached") + case err := <-done: + return err + } +} + +func rootPath() string { + rootPath := os.Getenv("ROOT_PATH") + + // If env variable is not provided, use relative path. + if rootPath == "" { + rootPath = ".." + } + return rootPath +} + +func relativePath(path string) string { + return fmt.Sprintf("%s/%s", rootPath(), path) +} + +// RunTypescriptSuite run the tests of the typescript suite +func RunTypescriptSuite(t *testing.T, suite string) { + forbidFlags := "" + if os.Getenv("ONLY_FORBIDDEN") == "true" { + forbidFlags = "--forbid-only --forbid-pending" + } + + cmdline := "./node_modules/.bin/mocha" + + " --exit --require ts-node/register " + forbidFlags + " " + + fmt.Sprintf("test/suites/%s/test.ts", suite) + + command := utils.CommandWithStdout("bash", "-c", cmdline) + command.Dir = rootPath() + command.Env = append( + os.Environ(), + "ENVIRONMENT=dev", + fmt.Sprintf("TS_NODE_PROJECT=%s", "test/tsconfig.json")) + + assert.NoError(t, command.Run()) +} + +// SetupTeardown binding setup and teardown functors +type SetupTeardown struct { + Setup func(suitePath string) error + Teardown func(suitePath string) error +} + +// CreateTypescriptSetupTeardown create a setup and teardown functor from the suite name +func CreateTypescriptSetupTeardown(suite string) SetupTeardown { + setup := func(suitePath string) error { + cmdline := "./node_modules/.bin/ts-node -P test/tsconfig.json -- ./scripts/setup-environment.ts " + suite + command := utils.CommandWithStdout("bash", "-c", cmdline) + command.Env = append(os.Environ(), "ENVIRONMENT=dev", fmt.Sprintf("SUITE_PATH=%s", suitePath)) + return command.Run() + } + + teardown := func(suitePath string) error { + cmdline := "./node_modules/.bin/ts-node -P test/tsconfig.json -- ./scripts/teardown-environment.ts " + suite + command := utils.CommandWithStdout("bash", "-c", cmdline) + command.Env = append(os.Environ(), "ENVIRONMENT=dev", fmt.Sprintf("SUITE_PATH=%s", suitePath)) + return command.Run() + } + + return SetupTeardown{Setup: setup, Teardown: teardown} +} diff --git a/suites/verify_body_contains.go b/suites/verify_body_contains.go new file mode 100644 index 000000000..c91b44678 --- /dev/null +++ b/suites/verify_body_contains.go @@ -0,0 +1,8 @@ +package suites + +import "context" + +func verifyBodyContains(ctx context.Context, s *SeleniumSuite, pattern string) { + bodyElement := WaitElementLocatedByTagName(ctx, s, "body") + WaitElementTextContains(ctx, s, bodyElement, pattern) +} diff --git a/suites/verify_is_first_factor_page.go b/suites/verify_is_first_factor_page.go new file mode 100644 index 000000000..e4d1c2f7b --- /dev/null +++ b/suites/verify_is_first_factor_page.go @@ -0,0 +1,7 @@ +package suites + +import "context" + +func verifyIsFirstFactorPage(ctx context.Context, s *SeleniumSuite) { + WaitElementLocatedByClassName(ctx, s, "first-factor-step") +} diff --git a/suites/verify_is_second_factor_page.go b/suites/verify_is_second_factor_page.go new file mode 100644 index 000000000..7953a0655 --- /dev/null +++ b/suites/verify_is_second_factor_page.go @@ -0,0 +1,7 @@ +package suites + +import "context" + +func verifyIsSecondFactorPage(ctx context.Context, s *SeleniumSuite) { + WaitElementLocatedByClassName(ctx, s, "second-factor-step") +} diff --git a/suites/verify_notification.go b/suites/verify_notification.go new file mode 100644 index 000000000..3db95b163 --- /dev/null +++ b/suites/verify_notification.go @@ -0,0 +1,9 @@ +package suites + +import "context" + +func verifyNotificationDisplayed(ctx context.Context, s *SeleniumSuite, message string) { + txt, err := WaitElementLocatedByClassName(ctx, s, "notification").Text() + s.Assert().NoError(err) + s.Assert().Equal(message, txt) +} diff --git a/suites/verify_secret_authorized.go b/suites/verify_secret_authorized.go new file mode 100644 index 000000000..1dad98d5b --- /dev/null +++ b/suites/verify_secret_authorized.go @@ -0,0 +1,7 @@ +package suites + +import "context" + +func verifySecretAuthorized(ctx context.Context, s *SeleniumSuite) { + verifyBodyContains(ctx, s, "This is a very important secret!") +} diff --git a/suites/verify_url_is.go b/suites/verify_url_is.go new file mode 100644 index 000000000..6a2ed4421 --- /dev/null +++ b/suites/verify_url_is.go @@ -0,0 +1,22 @@ +package suites + +import ( + "context" + + "github.com/stretchr/testify/assert" + "github.com/tebeka/selenium" +) + +func verifyURLIs(ctx context.Context, s *SeleniumSuite, url string) { + err := s.Wait(ctx, func(driver selenium.WebDriver) (bool, error) { + currentURL, err := driver.CurrentURL() + + if err != nil { + return false, err + } + + return currentURL == url, nil + }) + + assert.NoError(s.T(), err) +} diff --git a/suites/webdriver.go b/suites/webdriver.go new file mode 100644 index 000000000..9e968dec1 --- /dev/null +++ b/suites/webdriver.go @@ -0,0 +1,125 @@ +package suites + +import ( + "context" + "fmt" + "os" + "strings" + + "github.com/stretchr/testify/assert" + "github.com/tebeka/selenium" + "github.com/tebeka/selenium/chrome" +) + +// WebDriverSession binding a selenium service and a webdriver. +type WebDriverSession struct { + service *selenium.Service + WebDriver selenium.WebDriver +} + +// StartWebDriver create a selenium session +func StartWebDriver() (*WebDriverSession, error) { + port := 4444 + service, err := selenium.NewChromeDriverService("/usr/bin/chromedriver", port) + + if err != nil { + return nil, err + } + + chromeCaps := chrome.Capabilities{ + Path: "/usr/bin/google-chrome-stable", + } + + if os.Getenv("HEADLESS") != "" { + chromeCaps.Args = append(chromeCaps.Args, "--headless") + } + + caps := selenium.Capabilities{} + caps.AddChrome(chromeCaps) + + wd, err := selenium.NewRemote(caps, fmt.Sprintf("http://localhost:%d/wd/hub", port)) + if err != nil { + service.Stop() + panic(err) + } + + return &WebDriverSession{ + service: service, + WebDriver: wd, + }, nil +} + +// Stop stop the selenium session +func (wds *WebDriverSession) Stop() error { + err := wds.WebDriver.Quit() + + if err != nil { + return err + } + + return wds.service.Stop() +} + +// WithWebdriver run some actions against a webdriver +func WithWebdriver(fn func(webdriver selenium.WebDriver) error) error { + wds, err := StartWebDriver() + + if err != nil { + return err + } + + defer wds.Stop() + + return fn(wds.WebDriver) +} + +func waitElementLocated(ctx context.Context, s *SeleniumSuite, by, value string) selenium.WebElement { + var el selenium.WebElement + err := s.Wait(ctx, func(driver selenium.WebDriver) (bool, error) { + var err error + el, err = driver.FindElement(by, value) + + if err != nil { + if strings.Contains(err.Error(), "no such element") { + return false, nil + } + return false, err + } + + return el != nil, nil + }) + + assert.NoError(s.T(), err) + assert.NotNil(s.T(), el, "Element has not been located") + return el +} + +// WaitElementLocatedByID wait an element is located by id +func WaitElementLocatedByID(ctx context.Context, s *SeleniumSuite, id string) selenium.WebElement { + return waitElementLocated(ctx, s, selenium.ByID, id) +} + +// WaitElementLocatedByTagName wait an element is located by tag name +func WaitElementLocatedByTagName(ctx context.Context, s *SeleniumSuite, tagName string) selenium.WebElement { + return waitElementLocated(ctx, s, selenium.ByTagName, tagName) +} + +// WaitElementLocatedByClassName wait an element is located by class name +func WaitElementLocatedByClassName(ctx context.Context, s *SeleniumSuite, className string) selenium.WebElement { + return waitElementLocated(ctx, s, selenium.ByClassName, className) +} + +// WaitElementTextContains wait the text of an element contains a pattern +func WaitElementTextContains(ctx context.Context, s *SeleniumSuite, element selenium.WebElement, pattern string) { + assert.NotNil(s.T(), element) + + s.Wait(ctx, func(driver selenium.WebDriver) (bool, error) { + text, err := element.Text() + + if err != nil { + return false, err + } + + return strings.Contains(text, pattern), nil + }) +} diff --git a/test/helpers/context/AutheliaServer.ts b/test/helpers/context/AutheliaServer.ts deleted file mode 100644 index a36b9fbe8..000000000 --- a/test/helpers/context/AutheliaServer.ts +++ /dev/null @@ -1,26 +0,0 @@ -import fs from 'fs'; -import AutheliaServerWithHotReload from "./AutheliaServerWithHotReload"; -import AutheliaServerInterface from './AutheliaServerInterface'; -import AutheliaServerFromDist from './AutheliaServerFromDist'; - -class AutheliaServer implements AutheliaServerInterface { - private runnerImpl: AutheliaServerInterface; - - constructor(configPath: string, watchPaths: string[] = []) { - if (fs.existsSync('.suite')) { - this.runnerImpl = new AutheliaServerWithHotReload(configPath, watchPaths); - } else { - this.runnerImpl = new AutheliaServerFromDist(configPath, true); - } - } - - async start() { - await this.runnerImpl.start(); - } - - async stop() { - await this.runnerImpl.stop(); - } -} - -export default AutheliaServer; \ No newline at end of file diff --git a/test/helpers/context/AutheliaServerFromDist.ts b/test/helpers/context/AutheliaServerFromDist.ts deleted file mode 100644 index 97c762166..000000000 --- a/test/helpers/context/AutheliaServerFromDist.ts +++ /dev/null @@ -1,42 +0,0 @@ -import AutheliaServerInterface from './AutheliaServerInterface'; -import ChildProcess from 'child_process'; -import treeKill = require('tree-kill'); -import fs from 'fs'; - -class AutheliaServerFromDist implements AutheliaServerInterface { - private configPath: string; - private logInFile: boolean; - private serverProcess: ChildProcess.ChildProcess | undefined; - - constructor(configPath: string, logInFile: boolean = false) { - this.configPath = configPath; - this.logInFile = logInFile; - } - - async start() { - console.log("Spawn authelia server from dist using config " + this.configPath); - this.serverProcess = ChildProcess.spawn('./cmd/authelia-scripts/authelia-scripts serve ' + this.configPath, { - shell: true, - env: process.env, - } as any); - if (!this.serverProcess || !this.serverProcess.stdout || !this.serverProcess.stderr) return; - if (this.logInFile) { - var logStream = fs.createWriteStream('/tmp/authelia-server.log', {flags: 'a'}); - this.serverProcess.stdout.pipe(logStream); - this.serverProcess.stderr.pipe(logStream); - } else { - this.serverProcess.stdout.pipe(process.stdout); - this.serverProcess.stderr.pipe(process.stderr); - } - this.serverProcess.on('exit', (statusCode) => { - console.log('Authelia server exited with code ' + statusCode); - }) - } - - async stop() { - if (!this.serverProcess) return; - treeKill(this.serverProcess.pid, 'SIGKILL'); - } -} - -export default AutheliaServerFromDist; \ No newline at end of file diff --git a/test/helpers/context/AutheliaServerInterface.ts b/test/helpers/context/AutheliaServerInterface.ts deleted file mode 100644 index 5ac11a54a..000000000 --- a/test/helpers/context/AutheliaServerInterface.ts +++ /dev/null @@ -1,7 +0,0 @@ - -interface AutheliaServerInterface { - start(): Promise; - stop(): Promise -} - -export default AutheliaServerInterface; \ No newline at end of file diff --git a/test/helpers/context/AutheliaServerWithHotReload.ts b/test/helpers/context/AutheliaServerWithHotReload.ts deleted file mode 100644 index 7281f9a8b..000000000 --- a/test/helpers/context/AutheliaServerWithHotReload.ts +++ /dev/null @@ -1,183 +0,0 @@ -import Chokidar from 'chokidar'; -import fs from 'fs'; -import ChildProcess from 'child_process'; -import kill from 'tree-kill'; -import AutheliaServerInterface from './AutheliaServerInterface'; -import sleep from '../utils/sleep'; - -class AutheliaServerWithHotReload implements AutheliaServerInterface { - private watcher: Chokidar.FSWatcher; - private configPath: string; - private AUTHELIA_INTERRUPT_FILENAME = '.authelia-interrupt'; - private serverProcess: ChildProcess.ChildProcess | undefined; - private clientProcess: ChildProcess.ChildProcess | undefined; - private filesChangedBuffer: string[] = []; - private changeInProgress: boolean = false; - private isInterrupted: boolean = false - - constructor(configPath: string, watchedPaths: string[]) { - this.configPath = configPath; - const pathsToReload = ['**/*.go', - this.AUTHELIA_INTERRUPT_FILENAME, configPath].concat(watchedPaths); - console.log("Authelia will reload on changes of files or directories in " + pathsToReload.join(', ')); - this.watcher = Chokidar.watch(pathsToReload, { - persistent: true, - ignoreInitial: true, - }); - } - - private async startServer() { - if (this.serverProcess) return; - this.serverProcess = ChildProcess.spawn('go', - ['run', 'main.go', '-config', this.configPath], { - env: { - ...process.env, - NODE_TLS_REJECT_UNAUTHORIZED: "0", - }, - }); - if (!this.serverProcess || !this.serverProcess.stdout || !this.serverProcess.stderr) return; - this.serverProcess.stdout.pipe(process.stdout); - this.serverProcess.stderr.pipe(process.stderr); - this.serverProcess.on('exit', () => { - if (!this.serverProcess) return; - console.log('Authelia server with pid=%s exited.', this.serverProcess.pid); - this.serverProcess.removeAllListeners(); - this.serverProcess = undefined; - }); - } - - private killServer() { - return new Promise((resolve, reject) => { - if (this.serverProcess) { - const pid = this.serverProcess.pid; - try { - const timeout = setTimeout( - () => reject(new Error(`Server with pid=${pid} not killed after timeout.`)), 10000); - this.serverProcess.on('exit', () => { - clearTimeout(timeout); - resolve(); - }); - kill(this.serverProcess.pid, 'SIGKILL'); - } catch (e) { - reject(e); - } - } else { - resolve(); - } - }); - } - - private async startClient() { - if (this.clientProcess) return; - - this.clientProcess = ChildProcess.spawn('npm', ['run', 'start'], { - cwd: './client', - env: { - ...process.env, - 'BROWSER': 'none' - } - }); - if (!this.clientProcess || !this.clientProcess.stdout || !this.clientProcess.stderr) return; - this.clientProcess.stdout.pipe(process.stdout); - this.clientProcess.stderr.pipe(process.stderr); - this.clientProcess.on('exit', () => { - if (!this.clientProcess) return; - console.log('Authelia client exited with pid=%s.', this.clientProcess.pid); - this.clientProcess.removeAllListeners(); - this.clientProcess = undefined; - }) - } - - private killClient() { - return new Promise((resolve, reject) => { - if (this.clientProcess) { - const pid = this.clientProcess.pid; - try { - const timeout = setTimeout( - () => reject(new Error(`Server with pid=${pid} not killed after timeout.`)), 10000); - this.clientProcess.on('exit', () => { - clearTimeout(timeout); - resolve(); - }); - kill(this.clientProcess.pid, 'SIGKILL'); - } catch (e) { - reject(e); - } - } else { - resolve(); - } - }); - } - - /** - * Handle file changes. - * @param path The path of the file that has been changed. - */ - private onFilesChanged = async (paths: string[]) => { - const interruptFileExist = fs.existsSync(this.AUTHELIA_INTERRUPT_FILENAME); - const interruptFileModified = paths.filter( - (p) => p === this.AUTHELIA_INTERRUPT_FILENAME).length > 0; - if (interruptFileExist) { - if (interruptFileModified) { - console.log('Authelia is being interrupted.'); - this.isInterrupted = true; - await this.killServer(); - } - return; - } else if (this.isInterrupted && interruptFileModified && !interruptFileExist){ - console.log('Authelia is restarting.'); - await this.startServer(); - this.isInterrupted = false; - return; - } - - await this.killServer(); - await this.startServer(); - - if (this.filesChangedBuffer.length > 0) { - await this.consumeFileChanged(); - } - } - - private async consumeFileChanged() { - this.changeInProgress = true; - const paths = this.filesChangedBuffer; - this.filesChangedBuffer = []; - try { - await this.onFilesChanged(paths); - } catch(e) { - console.error(e); - } - this.changeInProgress = false; - } - - private enqueueFileChanged(path: string) { - console.log(`File ${path} has been changed, reloading...`); - this.filesChangedBuffer.push(path); - if (this.changeInProgress) return; - this.consumeFileChanged(); - } - - async start() { - if (fs.existsSync(this.AUTHELIA_INTERRUPT_FILENAME)) { - console.log('Authelia is interrupted. Consider removing ' + this.AUTHELIA_INTERRUPT_FILENAME + ' if it\'s not expected.'); - return; - } - - console.log('Start watching file changes...'); - this.watcher.on('add', (p) => this.enqueueFileChanged(p)); - this.watcher.on('unlink', (p) => this.enqueueFileChanged(p)); - this.watcher.on('change', (p) => this.enqueueFileChanged(p)); - - this.startClient(); - this.startServer(); - } - - async stop() { - await this.killClient(); - await this.killServer(); - await sleep(2000); - } -} - -export default AutheliaServerWithHotReload; \ No newline at end of file diff --git a/test/helpers/context/kubernetes/Kubernetes.ts b/test/helpers/context/kubernetes/Kubernetes.ts deleted file mode 100644 index 425b2c962..000000000 --- a/test/helpers/context/kubernetes/Kubernetes.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { exec } from '../../../helpers/utils/exec'; - -class Kubernetes { - kubeConfig: string; - - constructor(kubeConfig: string) { - this.kubeConfig = kubeConfig; - } - - async apply(configPath: string) { - await exec('kubectl apply -f ' + configPath, { - env: { - KUBECONFIG: this.kubeConfig, - } - }) - } - - async loadDockerImage(image: string) { - await exec('kind load docker-image ' + image), { - env: { - KUBECONFIG: this.kubeConfig, - } - }; - } -} - -export default Kubernetes; \ No newline at end of file diff --git a/test/helpers/context/kubernetes/KubernetesManager.ts b/test/helpers/context/kubernetes/KubernetesManager.ts deleted file mode 100644 index 558740006..000000000 --- a/test/helpers/context/kubernetes/KubernetesManager.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { exec } from '../../../helpers/utils/exec'; -import { execSync } from 'child_process'; -import Kubernetes from './Kubernetes'; - -class KubernetesManager { - static async create() { - await exec('kind create cluster'); - - const configPath = execSync('kind get kubeconfig-path --name="kind"', { - env: process.env - }).toString('utf-8').trim(); - return new Kubernetes(configPath); - } - - static async delete() { - await exec('kind delete cluster'); - } -} - -export default KubernetesManager; \ No newline at end of file diff --git a/test/helpers/scenarii/AuthenticationBlacklisting.ts b/test/helpers/scenarii/AuthenticationBlacklisting.ts index 392f96e1f..770e86f12 100644 --- a/test/helpers/scenarii/AuthenticationBlacklisting.ts +++ b/test/helpers/scenarii/AuthenticationBlacklisting.ts @@ -31,7 +31,7 @@ export default function(regulationMilliseconds: number) { // when providing good credentials, the hacker is regulated and see same message as previously. await LoginAs(this.driver, "james", "bad-password"); - await VerifyNotificationDisplayed(this.driver, "Authentication failed. Check your credentials."); + await VerifyNotificationDisplayed(this.driver, "Please retry in a few minutes."); await ClearFieldById(this.driver, "username"); // Wait the regulation ban time before retrying with correct credentials. diff --git a/test/suites/basic/scenarii/AlreadyLoggedIn.ts b/test/suites/Basic/scenarii/AlreadyLoggedIn.ts similarity index 100% rename from test/suites/basic/scenarii/AlreadyLoggedIn.ts rename to test/suites/Basic/scenarii/AlreadyLoggedIn.ts diff --git a/test/suites/basic/scenarii/BackendProtection.ts b/test/suites/Basic/scenarii/BackendProtection.ts similarity index 100% rename from test/suites/basic/scenarii/BackendProtection.ts rename to test/suites/Basic/scenarii/BackendProtection.ts diff --git a/test/suites/basic/scenarii/BadPassword.ts b/test/suites/Basic/scenarii/BadPassword.ts similarity index 100% rename from test/suites/basic/scenarii/BadPassword.ts rename to test/suites/Basic/scenarii/BadPassword.ts diff --git a/test/suites/basic/scenarii/BypassPolicy.ts b/test/suites/Basic/scenarii/BypassPolicy.ts similarity index 100% rename from test/suites/basic/scenarii/BypassPolicy.ts rename to test/suites/Basic/scenarii/BypassPolicy.ts diff --git a/test/suites/basic/scenarii/NoDuoPushOption.ts b/test/suites/Basic/scenarii/NoDuoPushOption.ts similarity index 100% rename from test/suites/basic/scenarii/NoDuoPushOption.ts rename to test/suites/Basic/scenarii/NoDuoPushOption.ts diff --git a/test/suites/basic/scenarii/RegisterTotp.ts b/test/suites/Basic/scenarii/RegisterTotp.ts similarity index 100% rename from test/suites/basic/scenarii/RegisterTotp.ts rename to test/suites/Basic/scenarii/RegisterTotp.ts diff --git a/test/suites/basic/scenarii/ResetPassword.ts b/test/suites/Basic/scenarii/ResetPassword.ts similarity index 100% rename from test/suites/basic/scenarii/ResetPassword.ts rename to test/suites/Basic/scenarii/ResetPassword.ts diff --git a/test/suites/basic/scenarii/TOTPValidation.ts b/test/suites/Basic/scenarii/TOTPValidation.ts similarity index 100% rename from test/suites/basic/scenarii/TOTPValidation.ts rename to test/suites/Basic/scenarii/TOTPValidation.ts diff --git a/test/suites/basic/scenarii/VerifyEndpoint.ts b/test/suites/Basic/scenarii/VerifyEndpoint.ts similarity index 100% rename from test/suites/basic/scenarii/VerifyEndpoint.ts rename to test/suites/Basic/scenarii/VerifyEndpoint.ts diff --git a/test/suites/basic/test.ts b/test/suites/Basic/test.ts similarity index 73% rename from test/suites/basic/test.ts rename to test/suites/Basic/test.ts index 7c82219a2..5e079e5d1 100644 --- a/test/suites/basic/test.ts +++ b/test/suites/Basic/test.ts @@ -5,21 +5,18 @@ import ResetPassword from './scenarii/ResetPassword'; import TOTPValidation from './scenarii/TOTPValidation'; import BackendProtection from './scenarii/BackendProtection'; import VerifyEndpoint from './scenarii/VerifyEndpoint'; -import RequiredTwoFactor from './scenarii/RequiredTwoFactor'; import AlreadyLoggedIn from './scenarii/AlreadyLoggedIn'; import { exec } from '../../helpers/utils/exec'; -import TwoFactorAuthentication from "../../helpers/scenarii/TwoFactorAuthentication"; import BypassPolicy from "./scenarii/BypassPolicy"; import NoDuoPushOption from "./scenarii/NoDuoPushOption"; -AutheliaSuite(__dirname, function() { +AutheliaSuite("/tmp/authelia/suites/Basic/", function() { this.timeout(10000); beforeEach(async function() { - await exec(`cp ${__dirname}/users_database.yml ${__dirname}/users_database.test.yml`); + await exec(`cp ${__dirname}/../../../suites/Basic/users.yml /tmp/authelia/suites/Basic/users.yml`); }); - describe('Two-factor authentication', TwoFactorAuthentication()); describe('Bypass policy', BypassPolicy) describe('Backend protection', BackendProtection); describe('Verify API endpoint', VerifyEndpoint); @@ -27,7 +24,6 @@ AutheliaSuite(__dirname, function() { describe('Reset password', ResetPassword); describe('TOTP Registration', RegisterTotp); describe('TOTP Validation', TOTPValidation); - describe('Required two factor', RequiredTwoFactor); describe('Already logged in', AlreadyLoggedIn); describe('No Duo Push method available', NoDuoPushOption); }); \ No newline at end of file diff --git a/test/suites/basic-bypass-no-redirect/README.md b/test/suites/BypassAll/README.md similarity index 100% rename from test/suites/basic-bypass-no-redirect/README.md rename to test/suites/BypassAll/README.md diff --git a/test/suites/basic-bypass-no-redirect/scenarii/BypassPolicy.ts b/test/suites/BypassAll/scenarii/BypassPolicy.ts similarity index 100% rename from test/suites/basic-bypass-no-redirect/scenarii/BypassPolicy.ts rename to test/suites/BypassAll/scenarii/BypassPolicy.ts diff --git a/test/suites/basic-bypass-no-redirect/scenarii/CustomHeadersForwarded.ts b/test/suites/BypassAll/scenarii/CustomHeadersForwarded.ts similarity index 100% rename from test/suites/basic-bypass-no-redirect/scenarii/CustomHeadersForwarded.ts rename to test/suites/BypassAll/scenarii/CustomHeadersForwarded.ts diff --git a/test/suites/basic-bypass-no-redirect/scenarii/NoDefaultRedirectionUrl.ts b/test/suites/BypassAll/scenarii/NoDefaultRedirectionUrl.ts similarity index 100% rename from test/suites/basic-bypass-no-redirect/scenarii/NoDefaultRedirectionUrl.ts rename to test/suites/BypassAll/scenarii/NoDefaultRedirectionUrl.ts diff --git a/test/suites/basic-bypass-no-redirect/test.ts b/test/suites/BypassAll/test.ts similarity index 50% rename from test/suites/basic-bypass-no-redirect/test.ts rename to test/suites/BypassAll/test.ts index eeb618237..6f298412a 100644 --- a/test/suites/basic-bypass-no-redirect/test.ts +++ b/test/suites/BypassAll/test.ts @@ -8,14 +8,14 @@ process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0 as any; process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0 as any; -AutheliaSuite(__dirname, function() { - this.timeout(10000); - - beforeEach(async function() { - await exec(`cp ${__dirname}/users_database.yml ${__dirname}/users_database.test.yml`); - }); +AutheliaSuite(__dirname, function () { + this.timeout(10000); - describe('Bypass policy', BypassPolicy); - describe("No default redirection", NoDefaultRedirectionUrl); - describe("Custom headers forwarded on bypass", CustomHeadersForwarded); + beforeEach(async function () { + await exec(`cp ${__dirname}/../../../suites/BypassAll/users.yml /tmp/authelia/suites/BypassAll/users.yml`); + }); + + describe('Bypass policy', BypassPolicy); + describe("No default redirection", NoDefaultRedirectionUrl); + describe("Custom headers forwarded on bypass", CustomHeadersForwarded); }); \ No newline at end of file diff --git a/test/suites/duo-push/README.md b/test/suites/DuoPush/README.md similarity index 100% rename from test/suites/duo-push/README.md rename to test/suites/DuoPush/README.md diff --git a/test/suites/duo-push/scenarii/DuoPushNotification.ts b/test/suites/DuoPush/scenarii/DuoPushNotification.ts similarity index 100% rename from test/suites/duo-push/scenarii/DuoPushNotification.ts rename to test/suites/DuoPush/scenarii/DuoPushNotification.ts diff --git a/test/suites/duo-push/scenarii/Prefered2faMethod.ts b/test/suites/DuoPush/scenarii/Prefered2faMethod.ts similarity index 100% rename from test/suites/duo-push/scenarii/Prefered2faMethod.ts rename to test/suites/DuoPush/scenarii/Prefered2faMethod.ts diff --git a/test/suites/duo-push/test.ts b/test/suites/DuoPush/test.ts similarity index 50% rename from test/suites/duo-push/test.ts rename to test/suites/DuoPush/test.ts index d8863bc2a..c2c167b5f 100644 --- a/test/suites/duo-push/test.ts +++ b/test/suites/DuoPush/test.ts @@ -6,13 +6,13 @@ import Prefered2faMethod from "./scenarii/Prefered2faMethod"; // required to query duo-api over https process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0 as any; -AutheliaSuite(__dirname, function() { - this.timeout(10000); - - beforeEach(async function() { - await exec(`cp ${__dirname}/users_database.yml ${__dirname}/users_database.test.yml`); - }); +AutheliaSuite(__dirname, function () { + this.timeout(10000); - describe("Duo Push Notication", DuoPushNotification); - describe("Prefered 2FA methods", Prefered2faMethod); + beforeEach(async function () { + await exec(`cp ${__dirname}/../../../suites/DuoPush/users.yml /tmp/authelia/suites/DuoPush/users.yml`); + }); + + describe("Duo Push Notication", DuoPushNotification); + describe("Prefered 2FA methods", Prefered2faMethod); }); \ No newline at end of file diff --git a/test/suites/high-availability/README.md b/test/suites/HighAvailability/README.md similarity index 100% rename from test/suites/high-availability/README.md rename to test/suites/HighAvailability/README.md diff --git a/test/suites/HighAvailability/environment.ts b/test/suites/HighAvailability/environment.ts new file mode 100644 index 000000000..5ae03dd28 --- /dev/null +++ b/test/suites/HighAvailability/environment.ts @@ -0,0 +1,17 @@ +const composeFiles = [ + 'docker-compose.yml', + 'example/compose/authelia/docker-compose.backend.yml', + 'example/compose/authelia/docker-compose.frontend.yml', + 'example/compose/mongo/docker-compose.yml', + 'example/compose/redis/docker-compose.yml', + 'example/compose/nginx/backend/docker-compose.yml', + 'example/compose/nginx/portal/docker-compose.yml', + 'example/compose/smtp/docker-compose.yml', + 'example/compose/httpbin/docker-compose.yml', + 'example/compose/ldap/docker-compose.admin.yml', // This is just used for administration, not for testing. + 'example/compose/ldap/docker-compose.yml' +] + +export { + composeFiles, +}; \ No newline at end of file diff --git a/test/suites/high-availability/scenarii/AccessControl.ts b/test/suites/HighAvailability/scenarii/AccessControl.ts similarity index 100% rename from test/suites/high-availability/scenarii/AccessControl.ts rename to test/suites/HighAvailability/scenarii/AccessControl.ts diff --git a/test/suites/high-availability/scenarii/AutheliaRestart.ts b/test/suites/HighAvailability/scenarii/AutheliaRestart.ts similarity index 100% rename from test/suites/high-availability/scenarii/AutheliaRestart.ts rename to test/suites/HighAvailability/scenarii/AutheliaRestart.ts diff --git a/test/suites/high-availability/scenarii/AuthenticationRegulation.ts b/test/suites/HighAvailability/scenarii/AuthenticationRegulation.ts similarity index 100% rename from test/suites/high-availability/scenarii/AuthenticationRegulation.ts rename to test/suites/HighAvailability/scenarii/AuthenticationRegulation.ts diff --git a/test/suites/high-availability/scenarii/BasicAuthentication.ts b/test/suites/HighAvailability/scenarii/BasicAuthentication.ts similarity index 100% rename from test/suites/high-availability/scenarii/BasicAuthentication.ts rename to test/suites/HighAvailability/scenarii/BasicAuthentication.ts diff --git a/test/suites/high-availability/scenarii/CustomHeadersForwarded.ts b/test/suites/HighAvailability/scenarii/CustomHeadersForwarded.ts similarity index 100% rename from test/suites/high-availability/scenarii/CustomHeadersForwarded.ts rename to test/suites/HighAvailability/scenarii/CustomHeadersForwarded.ts diff --git a/test/suites/high-availability/scenarii/EnforceInternalRedirectionsOnly.ts b/test/suites/HighAvailability/scenarii/EnforceInternalRedirectionsOnly.ts similarity index 100% rename from test/suites/high-availability/scenarii/EnforceInternalRedirectionsOnly.ts rename to test/suites/HighAvailability/scenarii/EnforceInternalRedirectionsOnly.ts diff --git a/test/suites/high-availability/scenarii/MongoConnectionRecovery.ts b/test/suites/HighAvailability/scenarii/MongoConnectionRecovery.ts similarity index 70% rename from test/suites/high-availability/scenarii/MongoConnectionRecovery.ts rename to test/suites/HighAvailability/scenarii/MongoConnectionRecovery.ts index 5e67806cd..b34cc4214 100644 --- a/test/suites/high-availability/scenarii/MongoConnectionRecovery.ts +++ b/test/suites/HighAvailability/scenarii/MongoConnectionRecovery.ts @@ -5,20 +5,18 @@ import Logout from "../../../helpers/Logout"; import { composeFiles } from '../environment'; import DockerCompose from "../../../helpers/context/DockerCompose"; -export default function() { +export default function () { const dockerCompose = new DockerCompose(composeFiles); WithDriver(); - it("should be able to login after mongo restarts", async function() { + it.only("should be able to login after mongo restarts", async function () { this.timeout(30000); - + const secret = await LoginAndRegisterTotp(this.driver, "john", "password", true); await dockerCompose.restart('mongo'); await Logout(this.driver); await FullLogin(this.driver, "john", secret, "https://admin.example.com:8080/secret.html"); - // TODO(clems4ever): logout here but right now visiting login.example.com redirects to home.example.com - // according to the configuration so it's not possible to click on Logout link. }); } \ No newline at end of file diff --git a/test/suites/high-availability/test.ts b/test/suites/HighAvailability/test.ts similarity index 83% rename from test/suites/high-availability/test.ts rename to test/suites/HighAvailability/test.ts index 67aedd279..68fd859ce 100644 --- a/test/suites/high-availability/test.ts +++ b/test/suites/HighAvailability/test.ts @@ -6,16 +6,14 @@ import CustomHeadersForwarded from "./scenarii/CustomHeadersForwarded"; import BasicAuthentication from "./scenarii/BasicAuthentication"; import AutheliaRestart from "./scenarii/AutheliaRestart"; import AuthenticationRegulation from "./scenarii/AuthenticationRegulation"; -import SingleFactorAuthentication from "../../helpers/scenarii/SingleFactorAuthentication"; -AutheliaSuite(__dirname, function() { +AutheliaSuite(__dirname, function () { this.timeout(10000); describe('Custom headers forwarded to backend', CustomHeadersForwarded); describe('Access control', AccessControl); describe('Mongo broken connection recovery', MongoConnectionRecovery); describe('Enforce internal redirections only', EnforceInternalRedirectionsOnly); - describe('Single-factor authentication', SingleFactorAuthentication()); describe('Basic authentication', BasicAuthentication); describe('Authelia restart', AutheliaRestart); describe('Authentication regulation', AuthenticationRegulation); diff --git a/test/suites/basic/README.md b/test/suites/NetworkACL/README.md similarity index 100% rename from test/suites/basic/README.md rename to test/suites/NetworkACL/README.md diff --git a/test/suites/network-acls/scenarii/NetworkACLs.ts b/test/suites/NetworkACL/scenarii/NetworkACLs.ts similarity index 100% rename from test/suites/network-acls/scenarii/NetworkACLs.ts rename to test/suites/NetworkACL/scenarii/NetworkACLs.ts diff --git a/test/suites/NetworkACL/test.ts b/test/suites/NetworkACL/test.ts new file mode 100644 index 000000000..1cba01c03 --- /dev/null +++ b/test/suites/NetworkACL/test.ts @@ -0,0 +1,13 @@ +import AutheliaSuite from "../../helpers/context/AutheliaSuite"; +import { exec } from '../../helpers/utils/exec'; +import NetworkACLs from "./scenarii/NetworkACLs"; + +AutheliaSuite(__dirname, function () { + this.timeout(10000); + + beforeEach(async function () { + await exec(`cp ${__dirname}/../../../suites/NetworkACL/users.yml /tmp/authelia/suites/NetworkACL/users.yml`); + }); + + describe("Network ACLs", NetworkACLs); +}); \ No newline at end of file diff --git a/test/suites/short-timeouts/README.md b/test/suites/ShortTimeouts/README.md similarity index 100% rename from test/suites/short-timeouts/README.md rename to test/suites/ShortTimeouts/README.md diff --git a/test/suites/short-timeouts/scenarii/Inactivity.ts b/test/suites/ShortTimeouts/scenarii/Inactivity.ts similarity index 100% rename from test/suites/short-timeouts/scenarii/Inactivity.ts rename to test/suites/ShortTimeouts/scenarii/Inactivity.ts diff --git a/test/suites/ShortTimeouts/test.ts b/test/suites/ShortTimeouts/test.ts new file mode 100644 index 000000000..0ec0498e1 --- /dev/null +++ b/test/suites/ShortTimeouts/test.ts @@ -0,0 +1,9 @@ +import AutheliaSuite from "../../helpers/context/AutheliaSuite"; +import Inactivity from './scenarii/Inactivity'; +import AuthenticationBlacklisting from "../../helpers/scenarii/AuthenticationBlacklisting"; + +AutheliaSuite(__dirname, function () { + this.timeout(10000); + describe('Inactivity period', Inactivity); + describe('Authentication blacklisting', AuthenticationBlacklisting(10000)); +}); \ No newline at end of file diff --git a/test/suites/basic-bypass-no-redirect/environment.ts b/test/suites/basic-bypass-no-redirect/environment.ts deleted file mode 100644 index 989981cb0..000000000 --- a/test/suites/basic-bypass-no-redirect/environment.ts +++ /dev/null @@ -1,39 +0,0 @@ -import fs from 'fs'; -import { exec } from "../../helpers/utils/exec"; -import AutheliaServer from "../../helpers/context/AutheliaServer"; -import DockerEnvironment from "../../helpers/context/DockerEnvironment"; - -// required to query duo-api over https -process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0 as any; - -const autheliaServer = new AutheliaServer(__dirname + '/config.yml'); -const dockerEnv = new DockerEnvironment([ - 'docker-compose.yml', - 'example/compose/nginx/backend/docker-compose.yml', - 'example/compose/nginx/portal/docker-compose.yml', - 'example/compose/httpbin/docker-compose.yml', - 'example/compose/smtp/docker-compose.yml', - 'example/compose/duo-api/docker-compose.yml', -]) - -async function setup() { - await exec(`cp ${__dirname}/users_database.yml ${__dirname}/users_database.test.yml`); - await exec('./example/compose/nginx/portal/render.js ' + (fs.existsSync('.suite') ? '': '--production')); - await dockerEnv.start(); - await autheliaServer.start(); -} - -async function teardown() { - await autheliaServer.stop(); - await dockerEnv.stop(); -} - -const setup_timeout = 30000; -const teardown_timeout = 30000; - -export { - setup, - setup_timeout, - teardown, - teardown_timeout -}; \ No newline at end of file diff --git a/test/suites/basic/environment.ts b/test/suites/basic/environment.ts deleted file mode 100644 index fd00f93c5..000000000 --- a/test/suites/basic/environment.ts +++ /dev/null @@ -1,34 +0,0 @@ -import fs from 'fs'; -import { exec } from "../../helpers/utils/exec"; -import AutheliaServer from "../../helpers/context/AutheliaServer"; -import DockerEnvironment from "../../helpers/context/DockerEnvironment"; - -const autheliaServer = new AutheliaServer(__dirname + '/config.yml', [__dirname + '/users_database.yml']); -const dockerEnv = new DockerEnvironment([ - 'docker-compose.yml', - 'example/compose/nginx/backend/docker-compose.yml', - 'example/compose/nginx/portal/docker-compose.yml', - 'example/compose/smtp/docker-compose.yml', -]) - -async function setup() { - await exec(`cp ${__dirname}/users_database.yml ${__dirname}/users_database.test.yml`); - await exec('./example/compose/nginx/portal/render.js ' + (fs.existsSync('.suite') ? '': '--production')); - await dockerEnv.start(); - await autheliaServer.start(); -} - -async function teardown() { - await autheliaServer.stop(); - await dockerEnv.stop(); -} - -const setup_timeout = 30000; -const teardown_timeout = 30000; - -export { - setup, - setup_timeout, - teardown, - teardown_timeout -}; \ No newline at end of file diff --git a/test/suites/basic/scenarii/RequiredTwoFactor.ts b/test/suites/basic/scenarii/RequiredTwoFactor.ts deleted file mode 100644 index 4b967aa4c..000000000 --- a/test/suites/basic/scenarii/RequiredTwoFactor.ts +++ /dev/null @@ -1,30 +0,0 @@ -import LoginAndRegisterTotp from '../../../helpers/LoginAndRegisterTotp'; -import VerifyUrlIs from '../../../helpers/assertions/VerifyUrlIs'; -import { StartDriver, StopDriver } from '../../../helpers/context/WithDriver'; -import VerifyIsSecondFactorStage from '../../../helpers/assertions/VerifyIsSecondFactorStage'; -import VisitPage from '../../../helpers/VisitPage'; -import FillLoginPageAndClick from '../../../helpers/FillLoginPageAndClick'; -import Logout from '../../../helpers/Logout'; - -export default function() { - describe('User tries to access a page protected by second factor while he only passed first factor', function() { - before(async function() { - this.driver = await StartDriver(); - const secret = await LoginAndRegisterTotp(this.driver, "john", "password", true); - if (!secret) throw new Error('No secret!'); - - await VisitPage(this.driver, "https://admin.example.com:8080/secret.html"); - await VerifyUrlIs(this.driver, "https://login.example.com:8080/#/?rd=https://admin.example.com:8080/secret.html"); - }); - - after(async function() { - await Logout(this.driver); - await StopDriver(this.driver); - }); - - it("should reach second factor page of login portal", async function() { - await VisitPage(this.driver, "https://admin.example.com:8080/secret.html"); - await VerifyIsSecondFactorStage(this.driver); - }); - }); -} \ No newline at end of file diff --git a/test/suites/docker-image/README.md b/test/suites/docker-image/README.md deleted file mode 100644 index 153268559..000000000 --- a/test/suites/docker-image/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Dockerhub suite - -This suite is made to quickly test that the Docker image of Authelia runs properly when spawned. -It can also be used for you to test Authelia without building it since the latest image will be -pulled from Dockerhub. - -## Components - -This suite will spawn an highly-available setup with nginx, mongo, redis, OpenLDAP, etc... - -## Tests - -Check if the image runs and does not crash unexpectedly and do a simple authentication with 2FA. \ No newline at end of file diff --git a/test/suites/docker-image/config.yml b/test/suites/docker-image/config.yml deleted file mode 100644 index a05d053dd..000000000 --- a/test/suites/docker-image/config.yml +++ /dev/null @@ -1,261 +0,0 @@ -############################################################### -# Authelia configuration # -############################################################### - -# The port to listen on -port: 9091 - -# Log level -# -# Level of verbosity for logs -logs_level: debug - -jwt_secret: unsecure_secret - -# Default redirection URL -# -# If user tries to authenticate without any referer, Authelia -# does not know where to redirect the user to at the end of the -# authentication process. -# This parameter allows you to specify the default redirection -# URL Authelia will use in such a case. -# -# Note: this parameter is optional. If not provided, user won't -# be redirected upon successful authentication. -default_redirection_url: https://home.example.com:8080/ - -# TOTP Issuer Name -# -# This will be the issuer name displayed in Google Authenticator -# See: https://github.com/google/google-authenticator/wiki/Key-Uri-Format for more info on issuer names -totp: - issuer: authelia.com - -# The authentication backend to use for verifying user passwords -# and retrieve information such as email address and groups -# users belong to. -# -# There are two supported backends: `ldap` and `file`. -authentication_backend: - # LDAP backend configuration. - # - # This backend allows Authelia to be scaled to more - # than one instance and therefore is recommended for - # production. - ldap: - # The url of the ldap server - url: ldap://openldap - - # The base dn for every entries - base_dn: dc=example,dc=com - - # An additional dn to define the scope to all users - additional_users_dn: ou=users - - # The users filter used to find the user DN - # {0} is a matcher replaced by username. - # 'cn={0}' by default. - users_filter: (cn={0}) - - # An additional dn to define the scope of groups - additional_groups_dn: ou=groups - - # The groups filter used for retrieving groups of a given user. - # {0} is a matcher replaced by username. - # {dn} is a matcher replaced by user DN. - # 'member={dn}' by default. - groups_filter: (&(member={dn})(objectclass=groupOfNames)) - - # The attribute holding the name of the group - group_name_attribute: cn - - # The attribute holding the mail address of the user - mail_attribute: mail - - # The username and password of the admin user. - user: cn=admin,dc=example,dc=com - password: password - - # File backend configuration. - # - # With this backend, the users database is stored in a file - # which is updated when users reset their passwords. - # Therefore, this backend is meant to be used in a dev environment - # and not in production since it prevents Authelia to be scaled to - # more than one instance. - # - ## file: - ## path: ./users_database.yml - - -# Access Control -# -# Access control is a list of rules defining the authorizations applied for one -# resource to users or group of users. -# -# If 'access_control' is not defined, ACL rules are disabled and the `bypass` -# rule is applied, i.e., access is allowed to anyone. Otherwise restrictions follow -# the rules defined. -# -# Note: One can use the wildcard * to match any subdomain. -# It must stand at the beginning of the pattern. (example: *.mydomain.com) -# -# Note: You must put patterns containing wildcards between simple quotes for the YAML -# to be syntaxically correct. -# -# Definition: A `rule` is an object with the following keys: `domain`, `subject`, -# `policy` and `resources`. -# -# - `domain` defines which domain or set of domains the rule applies to. -# -# - `subject` defines the subject to apply authorizations to. This parameter is -# optional and matching any user if not provided. If provided, the parameter -# represents either a user or a group. It should be of the form 'user:' -# or 'group:'. -# -# - `policy` is the policy to apply to resources. It must be either `bypass`, -# `one_factor`, `two_factor` or `deny`. -# -# - `resources` is a list of regular expressions that matches a set of resources to -# apply the policy to. This parameter is optional and matches any resource if not -# provided. -# -# Note: the order of the rules is important. The first policy matching -# (domain, resource, subject) applies. -access_control: - # Default policy can either be `bypass`, `one_factor`, `two_factor` or `deny`. - # It is the policy applied to any resource if there is no policy to be applied - # to the user. - default_policy: deny - - rules: - # Rules applied to everyone - - domain: public.example.com - policy: two_factor - - domain: singlefactor.example.com - policy: one_factor - - # Rules applied to 'admin' group - - domain: 'mx2.mail.example.com' - subject: 'group:admin' - policy: deny - - domain: '*.example.com' - subject: 'group:admin' - policy: two_factor - - # Rules applied to 'dev' group - - domain: dev.example.com - resources: - - '^/groups/dev/.*$' - subject: 'group:dev' - policy: two_factor - - # Rules applied to user 'john' - - domain: dev.example.com - resources: - - '^/users/john/.*$' - subject: 'user:john' - policy: two_factor - - - # Rules applied to user 'harry' - - domain: dev.example.com - resources: - - '^/users/harry/.*$' - subject: 'user:harry' - policy: two_factor - - # Rules applied to user 'bob' - - domain: '*.mail.example.com' - subject: 'user:bob' - policy: two_factor - - domain: 'dev.example.com' - resources: - - '^/users/bob/.*$' - subject: 'user:bob' - policy: two_factor - - -# Configuration of session cookies -# -# The session cookies identify the user once logged in. -session: - # The name of the session cookie. (default: authelia_session). - name: authelia_session - - # The secret to encrypt the session cookie. - secret: unsecure_session_secret - - # The time in ms before the cookie expires and session is reset. - expiration: 3600 # 1 hour - - # The inactivity time in ms before the session is reset. - inactivity: 300 # 5 minutes - - # The domain to protect. - # Note: the authenticator must also be in that domain. If empty, the cookie - # is restricted to the subdomain of the issuer. - domain: example.com - - # The redis connection details - redis: - host: redis - port: 6379 - password: authelia - -# Configuration of the authentication regulation mechanism. -# -# This mechanism prevents attackers from brute forcing the first factor. -# It bans the user if too many attempts are done in a short period of -# time. -regulation: - # The number of failed login attempts before user is banned. - # Set it to 0 to disable regulation. - max_retries: 3 - - # The time range during which the user can attempt login before being banned. - # The user is banned if the authenticaction failed `max_retries` times in a `find_time` seconds window. - find_time: 15 - - # The length of time before a banned user can login again. - ban_time: 20 - -# Configuration of the storage backend used to store data and secrets. -# -# You must use only an available configuration: local, mongo -storage: - # The directory where the DB files will be saved - ## local: - ## path: /var/lib/authelia/store - - # Settings to connect to mongo server - mongo: - url: mongodb://mongo - database: authelia - auth: - username: authelia - password: authelia - -# Configuration of the notification system. -# -# Notifications are sent to users when they require a password reset, a u2f -# registration or a TOTP registration. -# Use only an available configuration: filesystem, gmail -notifier: - # For testing purpose, notifications can be sent in a file - ## filesystem: - ## filename: /tmp/authelia/notification.txt - - # Use your email account to send the notifications. You can use an app password. - # List of valid services can be found here: https://nodemailer.com/smtp/well-known/ - ## email: - ## username: user@example.com - ## password: yourpassword - ## sender: admin@example.com - ## service: gmail - - # Use a SMTP server for sending notifications - smtp: - host: smtp - port: 1025 - sender: admin@example.com diff --git a/test/suites/docker-image/environment.ts b/test/suites/docker-image/environment.ts deleted file mode 100644 index 00084e278..000000000 --- a/test/suites/docker-image/environment.ts +++ /dev/null @@ -1,37 +0,0 @@ -import DockerEnvironment from "../../helpers/context/DockerEnvironment"; -import { exec } from "../../helpers/utils/exec"; - -const composeFiles = [ - 'docker-compose.yml', - 'example/compose/authelia/docker-compose.yml', - 'example/compose/mongo/docker-compose.yml', - 'example/compose/redis/docker-compose.yml', - 'example/compose/nginx/backend/docker-compose.yml', - 'example/compose/nginx/portal/docker-compose.yml', - 'example/compose/smtp/docker-compose.yml', - 'example/compose/httpbin/docker-compose.yml', - 'example/compose/ldap/docker-compose.admin.yml', // This is just used for administration, not for testing. - 'example/compose/ldap/docker-compose.yml' -] - -const dockerEnv = new DockerEnvironment(composeFiles); - -async function setup() { - await exec('./example/compose/nginx/portal/render.js --production http://authelia:9091'); - await dockerEnv.start(); -} - -async function teardown() { - await dockerEnv.stop(); -} - -const setup_timeout = 30000; -const teardown_timeout = 30000; - -export { - setup, - setup_timeout, - teardown, - teardown_timeout, - composeFiles -}; \ No newline at end of file diff --git a/test/suites/docker-image/test.ts b/test/suites/docker-image/test.ts deleted file mode 100644 index 0de403d3d..000000000 --- a/test/suites/docker-image/test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import AutheliaSuite from '../../helpers/context/AutheliaSuite'; -import DockerCompose from '../../helpers/context/DockerCompose'; -import { composeFiles } from './environment'; -import Assert from 'assert'; -import SingleFactorAuthentication from '../../helpers/scenarii/SingleFactorAuthentication'; -import TwoFactorAuthentication from '../../helpers/scenarii/TwoFactorAuthentication'; - -AutheliaSuite(__dirname, function() { - this.timeout(15000); - const dockerCompose = new DockerCompose(composeFiles); - - describe('Check the container', function() { - it('should be running', async function() { - const stdout = await dockerCompose.ps(); - const lines = stdout.split("\n"); - const autheliaLine = lines.filter(l => l.indexOf('authelia_1') > -1); - if (autheliaLine.length != 1) { - throw new Error('Authelia container not found...'); - } - // check if the container is up. - Assert(autheliaLine[0].indexOf(' Up ') > -1); - }); - }); - - describe('Single-factor authentication', SingleFactorAuthentication()) - describe('Two-factor authentication', TwoFactorAuthentication()); -}); \ No newline at end of file diff --git a/test/suites/duo-push/environment.ts b/test/suites/duo-push/environment.ts deleted file mode 100644 index f32d1d7e0..000000000 --- a/test/suites/duo-push/environment.ts +++ /dev/null @@ -1,36 +0,0 @@ -import fs from 'fs'; -import { exec } from "../../helpers/utils/exec"; -import AutheliaServer from "../../helpers/context/AutheliaServer"; -import DockerEnvironment from "../../helpers/context/DockerEnvironment"; - -process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0 as any; - -const autheliaServer = new AutheliaServer(__dirname + '/config.yml', [__dirname + '/users_database.yml']); -const dockerEnv = new DockerEnvironment([ - 'docker-compose.yml', - 'example/compose/nginx/backend/docker-compose.yml', - 'example/compose/nginx/portal/docker-compose.yml', - 'example/compose/duo-api/docker-compose.yml', -]) - -async function setup() { - await exec(`cp ${__dirname}/users_database.yml ${__dirname}/users_database.test.yml`); - await exec('./example/compose/nginx/portal/render.js ' + (fs.existsSync('.suite') ? '': '--production')); - await dockerEnv.start(); - await autheliaServer.start(); -} - -async function teardown() { - await autheliaServer.stop(); - await dockerEnv.stop(); -} - -const setup_timeout = 30000; -const teardown_timeout = 30000; - -export { - setup, - setup_timeout, - teardown, - teardown_timeout -}; \ No newline at end of file diff --git a/test/suites/high-availability/environment.ts b/test/suites/high-availability/environment.ts deleted file mode 100644 index c82dd2fbc..000000000 --- a/test/suites/high-availability/environment.ts +++ /dev/null @@ -1,49 +0,0 @@ -import DockerEnvironment from "../../helpers/context/DockerEnvironment"; -import AutheliaServer from "../../helpers/context/AutheliaServer"; -import { exec } from "../../helpers/utils/exec"; -import fs from 'fs'; - -const composeFiles = [ - 'docker-compose.yml', - 'example/compose/mongo/docker-compose.yml', - 'example/compose/redis/docker-compose.yml', - 'example/compose/nginx/backend/docker-compose.yml', - 'example/compose/nginx/portal/docker-compose.yml', - 'example/compose/smtp/docker-compose.yml', - 'example/compose/httpbin/docker-compose.yml', - 'example/compose/ldap/docker-compose.admin.yml', // This is just used for administration, not for testing. - 'example/compose/ldap/docker-compose.yml' -] - -const dockerEnv = new DockerEnvironment(composeFiles); -const autheliaServer = new AutheliaServer(__dirname + '/config.yml'); - -async function setup() { - // In dev mode Authelia has the server served on one port and the frontend on another port. - await exec('./example/compose/nginx/portal/render.js ' + (fs.existsSync('.suite') ? '': '--production')); - - console.log(`Prepare environment with docker-compose...`); - await dockerEnv.start(); - - console.log('Start Authelia server.'); - await autheliaServer.start(); -} - -async function teardown() { - console.log('Stop Authelia server.'); - await autheliaServer.stop(); - - console.log(`Cleanup environment with docker-compose...`); - await dockerEnv.stop(); -} - -const setup_timeout = 30000; -const teardown_timeout = 30000; - -export { - setup, - setup_timeout, - teardown, - teardown_timeout, - composeFiles -}; \ No newline at end of file diff --git a/test/suites/kubernetes/README.md b/test/suites/kubernetes/README.md deleted file mode 100644 index 779f2322c..000000000 --- a/test/suites/kubernetes/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# Kubernetes suite - -This suite has been created to test Authelia in Kubernetes with a nginx-ingress-controller. - -## Components - -This suite spawns nginx-ingress-controller, redis, mongo, ldap and a fake webmail to catch -emails sent by Authelia. The configuration of all those services is located in *example/kube*. - -## Tests - -This suite tests if single and two-factor is working. diff --git a/test/suites/kubernetes/environment.ts b/test/suites/kubernetes/environment.ts deleted file mode 100644 index 8a2b1d396..000000000 --- a/test/suites/kubernetes/environment.ts +++ /dev/null @@ -1,148 +0,0 @@ -import KubernetesManager from '../../helpers/context/kubernetes/KubernetesManager'; -import Kubernetes from '../../helpers/context/kubernetes/Kubernetes'; -import { exec } from '../../helpers/utils/exec'; -import WaitUntil from '../../helpers/utils/WaitUntil'; -import { spawn, execSync, ChildProcess } from 'child_process'; -import treeKill = require('tree-kill'); -import Redis, { RedisClient } from 'redis'; -import sleep from '../../helpers/utils/sleep'; -import DockerEnvironment from '../../helpers/context/DockerEnvironment'; - -let portFowardingProcess: ChildProcess; - -function arePodsReady(kubernetes: Kubernetes): boolean { - const lines = execSync('kubectl get -n authelia pods --no-headers', { env: { - KUBECONFIG: kubernetes.kubeConfig, - ...process.env, - }}).toString('utf-8').split("\n").filter((x) => x !== ''); - console.log(lines.join('\n')); - return lines.reduce((acc: boolean, line: string) => { - return acc && line.indexOf('1/1') > -1; - }, true); -} - -function servicesReady(kubernetes: Kubernetes): Promise { - return WaitUntil(async () => arePodsReady(kubernetes), - 600000, 15000, 5000, 5000); -} - -function redisConnected(redisClient: RedisClient): Promise { - return new Promise((resolve, reject) => { - console.log('Wait for redis to be connected.'); - redisClient.on('connect', function() { - resolve(); - }); - }) -} - -function redisPingOk(redisClient: RedisClient): Promise { - return new Promise((resolve, reject) => { - console.log('Send PING to redis.'); - redisClient.ping((err, msg) => { - if (err) { - reject(err); - return; - } - - if (msg == 'PONG') { - resolve(true); - return; - } - resolve(false); - }); - }); -} - -async function redisReady(kubernetes: Kubernetes): Promise { - const redisPortForward = spawn('kubectl', - ['port-forward', '-n', 'authelia', 'service/redis-service', '8080:6379'], { - env: {KUBECONFIG: kubernetes.kubeConfig, ...process.env} - }); - // Wait for the port to be open. - await sleep(2000); - - const redisClient = Redis.createClient({ - port: 8080, - no_ready_check: true, - retry_strategy: () => 3000, - }); - - try { - await redisConnected(redisClient); - await WaitUntil(async() => await redisPingOk(redisClient), 30000, 5000); - } catch(err) { - console.error(err); - } finally { - treeKill(redisPortForward.pid); - } -} - -function startAutheliaPortForwarding(kubernetes: Kubernetes) { - // Serve applications on port 8080 - portFowardingProcess = spawn('kubectl port-forward --address 0.0.0.0 -n authelia service/nginx-ingress-controller-service 8080:443', { - shell: true, - env: {KUBECONFIG: kubernetes.kubeConfig, ...process.env} - } as any); - if (!portFowardingProcess) return; - portFowardingProcess.stdout!.pipe(process.stdout); - portFowardingProcess.stderr!.pipe(process.stderr); -} - -const dockerEnv = new DockerEnvironment([ - 'docker-compose.yml', - 'example/compose/nginx/kubernetes/docker-compose.yml', -]); - - -async function setup() { - let kubernetes: Kubernetes; - if (!process.env['KUBECONFIG']) { - kubernetes = await KubernetesManager.create(); - } else { - kubernetes = new Kubernetes(process.env['KUBECONFIG'] as string); - } - - await kubernetes.loadDockerImage('authelia:dist'); - await kubernetes.loadDockerImage('authelia-example-backend'); - - await exec('./bootstrap.sh', { - cwd: './example/kube', - env: {KUBECONFIG: kubernetes.kubeConfig} - }); - - await servicesReady(kubernetes); - await redisReady(kubernetes); - await exec('./bootstrap-authelia.sh', { - cwd: './example/kube', - env: {KUBECONFIG: kubernetes.kubeConfig} - }); - await servicesReady(kubernetes); - - await dockerEnv.start(); - - startAutheliaPortForwarding(kubernetes); -} - -async function teardown() { - if (portFowardingProcess) { - console.log('Stopping port forwarding (%s)...', portFowardingProcess.pid); - treeKill(portFowardingProcess.pid, 'SIGKILL'); - // Wait for the signal to be sent. - await sleep(1000); - } - - await dockerEnv.stop(); - - if (process.env['KUBECONFIG']) return; - await KubernetesManager.delete(); -} - -const setup_timeout = 600000; -const teardown_timeout = 30000; - -export { - setup, - setup_timeout, - teardown, - teardown_timeout -}; \ No newline at end of file diff --git a/test/suites/kubernetes/test.ts b/test/suites/kubernetes/test.ts deleted file mode 100644 index 17033315b..000000000 --- a/test/suites/kubernetes/test.ts +++ /dev/null @@ -1,10 +0,0 @@ -import AutheliaSuite from '../../helpers/context/AutheliaSuite'; -import TwoFactorAuthentication from '../../helpers/scenarii/TwoFactorAuthentication'; -import SingleFactorAuthentication from '../../helpers/scenarii/SingleFactorAuthentication'; - -AutheliaSuite(__dirname, function() { - this.timeout(30000); - - describe('Single-factor authentication', SingleFactorAuthentication(30000)); - describe('Two-factor authentication', TwoFactorAuthentication(30000)); -}); \ No newline at end of file diff --git a/test/suites/ldap/README.md b/test/suites/ldap/README.md deleted file mode 100644 index bee6a33c0..000000000 --- a/test/suites/ldap/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# LDAP suite - -This suite is made to test Authelia with an LDAP server - -## Components - -This suite will spawn nginx as the edge reverse proxy and an LDAP server as the user -storage. - -## Tests - -Basic authentication tests \ No newline at end of file diff --git a/test/suites/ldap/environment.ts b/test/suites/ldap/environment.ts deleted file mode 100644 index 4c6d7fa49..000000000 --- a/test/suites/ldap/environment.ts +++ /dev/null @@ -1,45 +0,0 @@ -import DockerEnvironment from "../../helpers/context/DockerEnvironment"; -import AutheliaServer from "../../helpers/context/AutheliaServer"; -import { exec } from "../../helpers/utils/exec"; -import fs from 'fs'; - -const composeFiles = [ - 'docker-compose.yml', - 'example/compose/nginx/backend/docker-compose.yml', - 'example/compose/nginx/portal/docker-compose.yml', - 'example/compose/smtp/docker-compose.yml', - 'example/compose/ldap/docker-compose.yml' -] - -const dockerEnv = new DockerEnvironment(composeFiles); -const autheliaServer = new AutheliaServer(__dirname + '/config.yml'); - -async function setup() { - // In dev mode Authelia has the server served on one port and the frontend on another port. - await exec('./example/compose/nginx/portal/render.js ' + (fs.existsSync('.suite') ? '': '--production')); - - console.log(`Prepare environment with docker-compose...`); - await dockerEnv.start(); - - console.log('Start Authelia server.'); - await autheliaServer.start(); -} - -async function teardown() { - console.log('Stop Authelia server.'); - await autheliaServer.stop(); - - console.log(`Cleanup environment with docker-compose...`); - await dockerEnv.stop(); -} - -const setup_timeout = 30000; -const teardown_timeout = 30000; - -export { - setup, - setup_timeout, - teardown, - teardown_timeout, - composeFiles -}; \ No newline at end of file diff --git a/test/suites/ldap/test.ts b/test/suites/ldap/test.ts deleted file mode 100644 index c7e192cb6..000000000 --- a/test/suites/ldap/test.ts +++ /dev/null @@ -1,10 +0,0 @@ -import AutheliaSuite from "../../helpers/context/AutheliaSuite"; -import SingleFactorAuthentication from "../../helpers/scenarii/SingleFactorAuthentication"; -import TwoFactorAuthentication from "../../helpers/scenarii/TwoFactorAuthentication"; - -AutheliaSuite(__dirname, function() { - this.timeout(10000); - - describe('Single-factor authentication', SingleFactorAuthentication()) - describe('Two-factor authentication', TwoFactorAuthentication()); -}); \ No newline at end of file diff --git a/test/suites/mongo/README.md b/test/suites/mongo/README.md deleted file mode 100644 index 420a6fc97..000000000 --- a/test/suites/mongo/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Mongo suite - -This suite is made to test Authelia with a Mongo storage backend - -## Components - -This suite will spawn nginx as the edge reverse proxy and a mongo server. - -## Tests - -Basic authentication tests \ No newline at end of file diff --git a/test/suites/mongo/environment.ts b/test/suites/mongo/environment.ts deleted file mode 100644 index c2da6df49..000000000 --- a/test/suites/mongo/environment.ts +++ /dev/null @@ -1,47 +0,0 @@ -import DockerEnvironment from "../../helpers/context/DockerEnvironment"; -import AutheliaServer from "../../helpers/context/AutheliaServer"; -import { exec } from "../../helpers/utils/exec"; -import fs from 'fs'; - -const composeFiles = [ - 'docker-compose.yml', - 'example/compose/nginx/backend/docker-compose.yml', - 'example/compose/nginx/portal/docker-compose.yml', - 'example/compose/smtp/docker-compose.yml', - 'example/compose/mongo/docker-compose.yml', - 'example/compose/ldap/docker-compose.yml' -] - -const dockerEnv = new DockerEnvironment(composeFiles); -const autheliaServer = new AutheliaServer(__dirname + '/config.yml'); - -async function setup() { - await exec(`cp ${__dirname}/users_database.yml ${__dirname}/users_database.test.yml`); - // In dev mode Authelia has the server served on one port and the frontend on another port. - await exec('./example/compose/nginx/portal/render.js ' + (fs.existsSync('.suite') ? '': '--production')); - - console.log(`Prepare environment with docker-compose...`); - await dockerEnv.start(); - - console.log('Start Authelia server.'); - await autheliaServer.start(); -} - -async function teardown() { - console.log('Stop Authelia server.'); - await autheliaServer.stop(); - - console.log(`Cleanup environment with docker-compose...`); - await dockerEnv.stop(); -} - -const setup_timeout = 30000; -const teardown_timeout = 30000; - -export { - setup, - setup_timeout, - teardown, - teardown_timeout, - composeFiles -}; \ No newline at end of file diff --git a/test/suites/mongo/test.ts b/test/suites/mongo/test.ts deleted file mode 100644 index f3bcede26..000000000 --- a/test/suites/mongo/test.ts +++ /dev/null @@ -1,12 +0,0 @@ -import AutheliaSuite from "../../helpers/context/AutheliaSuite"; -import SingleFactorAuthentication from "../../helpers/scenarii/SingleFactorAuthentication"; -import TwoFactorAuthentication from "../../helpers/scenarii/TwoFactorAuthentication"; -import AuthenticationBlacklisting from "../../helpers/scenarii/AuthenticationBlacklisting"; - -AutheliaSuite(__dirname, function() { - this.timeout(20000); - - describe('Single-factor authentication', SingleFactorAuthentication()) - describe('Two-factor authentication', TwoFactorAuthentication()); - describe('Authentication blacklisting', AuthenticationBlacklisting(12000)); -}); \ No newline at end of file diff --git a/test/suites/network-acls/README.md b/test/suites/network-acls/README.md deleted file mode 100644 index 751dfbc5c..000000000 --- a/test/suites/network-acls/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Basic suite - -This suite has been created to test Authelia with basic feature in a non highly-available setup. -Authelia basically use an in-memory cache to store user sessions and persist data on disk instead -of using a remote database. Also, the user accounts are stored in file-based database. - -## Components - -Authelia, nginx, fake webmail for registering devices. - -## Tests - -Broad range of tests. \ No newline at end of file diff --git a/test/suites/network-acls/environment.ts b/test/suites/network-acls/environment.ts deleted file mode 100644 index adee3e677..000000000 --- a/test/suites/network-acls/environment.ts +++ /dev/null @@ -1,37 +0,0 @@ -import fs from 'fs'; -import { exec } from "../../helpers/utils/exec"; -import AutheliaServer from "../../helpers/context/AutheliaServer"; -import DockerEnvironment from "../../helpers/context/DockerEnvironment"; - -const autheliaServer = new AutheliaServer(__dirname + '/config.yml', [__dirname + '/users_database.yml']); -const dockerEnv = new DockerEnvironment([ - 'docker-compose.yml', - 'example/compose/nginx/backend/docker-compose.yml', - 'example/compose/nginx/portal/docker-compose.yml', - 'example/compose/squid/docker-compose.yml', - 'example/compose/smtp/docker-compose.yml', - // To debug headers - 'example/compose/httpbin/docker-compose.yml', -]) - -async function setup() { - await exec(`cp ${__dirname}/users_database.yml ${__dirname}/users_database.test.yml`); - await exec('./example/compose/nginx/portal/render.js ' + (fs.existsSync('.suite') ? '': '--production')); - await dockerEnv.start(); - await autheliaServer.start(); -} - -async function teardown() { - await autheliaServer.stop(); - await dockerEnv.stop(); -} - -const setup_timeout = 30000; -const teardown_timeout = 30000; - -export { - setup, - setup_timeout, - teardown, - teardown_timeout -}; \ No newline at end of file diff --git a/test/suites/network-acls/test.ts b/test/suites/network-acls/test.ts deleted file mode 100644 index 6308a6c42..000000000 --- a/test/suites/network-acls/test.ts +++ /dev/null @@ -1,13 +0,0 @@ -import AutheliaSuite from "../../helpers/context/AutheliaSuite"; -import { exec } from '../../helpers/utils/exec'; -import NetworkACLs from "./scenarii/NetworkACLs"; - -AutheliaSuite(__dirname, function() { - this.timeout(10000); - - beforeEach(async function() { - await exec(`cp ${__dirname}/users_database.yml ${__dirname}/users_database.test.yml`); - }); - - describe("Network ACLs", NetworkACLs); -}); \ No newline at end of file diff --git a/test/suites/short-timeouts/environment.ts b/test/suites/short-timeouts/environment.ts deleted file mode 100644 index c015abedc..000000000 --- a/test/suites/short-timeouts/environment.ts +++ /dev/null @@ -1,34 +0,0 @@ -import fs from 'fs'; -import { exec } from "../../helpers/utils/exec"; -import AutheliaServer from "../../helpers/context/AutheliaServer"; -import DockerEnvironment from "../../helpers/context/DockerEnvironment"; - -const autheliaServer = new AutheliaServer(__dirname + '/config.yml'); -const dockerEnv = new DockerEnvironment([ - 'docker-compose.yml', - 'example/compose/nginx/backend/docker-compose.yml', - 'example/compose/nginx/portal/docker-compose.yml', - 'example/compose/smtp/docker-compose.yml', -]) - -async function setup() { - await exec(`cp ${__dirname}/users_database.yml ${__dirname}/users_database.test.yml`); - await exec('./example/compose/nginx/portal/render.js ' + (fs.existsSync('.suite') ? '': '--production')); - await dockerEnv.start(); - await autheliaServer.start(); -} - -async function teardown() { - await autheliaServer.stop(); - await dockerEnv.stop(); -} - -const setup_timeout = 30000; -const teardown_timeout = 30000; - -export { - setup, - setup_timeout, - teardown, - teardown_timeout -}; \ No newline at end of file diff --git a/test/suites/short-timeouts/test.ts b/test/suites/short-timeouts/test.ts deleted file mode 100644 index a12d72a14..000000000 --- a/test/suites/short-timeouts/test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import AutheliaSuite from "../../helpers/context/AutheliaSuite"; -import Inactivity from './scenarii/Inactivity'; - -AutheliaSuite(__dirname, function() { - this.timeout(10000); - describe('Inactivity period', Inactivity); -}); \ No newline at end of file diff --git a/test/suites/traefik/README.md b/test/suites/traefik/README.md deleted file mode 100644 index 5c432c127..000000000 --- a/test/suites/traefik/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Traefik suite - -This suite has been created to test Authelia against Traefik. - -## Components - -Authelia, Traefik, fake webmail for registering devices. - -## Tests - -Authentication tests. \ No newline at end of file diff --git a/test/suites/traefik/environment.ts b/test/suites/traefik/environment.ts deleted file mode 100644 index 0acd18b17..000000000 --- a/test/suites/traefik/environment.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { exec } from "../../helpers/utils/exec"; -import AutheliaServer from "../../helpers/context/AutheliaServer"; -import DockerEnvironment from "../../helpers/context/DockerEnvironment"; -import * as fs from "fs"; - -const autheliaServer = new AutheliaServer(__dirname + '/config.yml'); -const dockerEnv = new DockerEnvironment([ - 'docker-compose.yml', - 'example/compose/nginx/backend/docker-compose.yml', - 'example/compose/traefik/docker-compose.yml', - 'example/compose/smtp/docker-compose.yml', -]) - -async function setup() { - await exec('./example/compose/traefik/render.js ' + (fs.existsSync('.suite') ? '': '--production')); - await exec(`cp ${__dirname}/users_database.yml ${__dirname}/users_database.test.yml`); - await dockerEnv.start(); - await autheliaServer.start(); -} - -async function teardown() { - await autheliaServer.stop(); - await dockerEnv.stop(); -} - -const setup_timeout = 30000; -const teardown_timeout = 30000; - -export { - setup, - setup_timeout, - teardown, - teardown_timeout -}; \ No newline at end of file diff --git a/test/suites/traefik/test.ts b/test/suites/traefik/test.ts deleted file mode 100644 index 5c26e690d..000000000 --- a/test/suites/traefik/test.ts +++ /dev/null @@ -1,17 +0,0 @@ -import AutheliaSuite from "../../helpers/context/AutheliaSuite"; -import { exec } from '../../helpers/utils/exec'; -import TwoFactorAuthentication from "../../helpers/scenarii/TwoFactorAuthentication"; -import SingleFactorAuthentication from "../../helpers/scenarii/SingleFactorAuthentication"; -import * as fs from "fs"; - -AutheliaSuite(__dirname, function() { - this.timeout(10000); - - beforeEach(async function() { - await exec('./example/compose/traefik/render.js ' + (fs.existsSync('.suite') ? '': '--production')); - await exec(`cp ${__dirname}/users_database.yml ${__dirname}/users_database.test.yml`); - }); - - describe('Single-factor authentication', SingleFactorAuthentication()); - describe('Second factor authentication', TwoFactorAuthentication()); -}); \ No newline at end of file diff --git a/utils/check.go b/utils/check.go new file mode 100644 index 000000000..6581bc007 --- /dev/null +++ b/utils/check.go @@ -0,0 +1,25 @@ +package utils + +import ( + "fmt" + "time" +) + +// CheckUntil regurly check a predicate until it's true or time out is reached +func CheckUntil(interval time.Duration, timeout time.Duration, predicate func() (bool, error)) error { + for { + select { + case <-time.After(interval): + predTrue, err := predicate() + if predTrue { + return nil + } + + if err != nil { + return err + } + case <-time.After(timeout): + return fmt.Errorf("Timeout of %ds reached", int64(timeout/time.Second)) + } + } +} diff --git a/utils/exec.go b/utils/exec.go new file mode 100644 index 000000000..3e2a23ba2 --- /dev/null +++ b/utils/exec.go @@ -0,0 +1,126 @@ +package utils + +import ( + "bufio" + "fmt" + "os" + "os/exec" + "os/signal" + "sync" + "syscall" + "time" + + log "github.com/sirupsen/logrus" +) + +// CommandWithStdout create a command forwarding stdout and stderr to the OS streams +func CommandWithStdout(name string, args ...string) *exec.Cmd { + cmd := exec.Command(name, args...) + if log.GetLevel() > log.InfoLevel { + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + } + return cmd +} + +// Shell create a shell command +func Shell(command string) *exec.Cmd { + return CommandWithStdout("bash", "-c", command) +} + +// RunCommandUntilCtrlC run a command until ctrl-c is hit +func RunCommandUntilCtrlC(cmd *exec.Cmd) { + mutex := sync.Mutex{} + cond := sync.NewCond(&mutex) + signalChannel := make(chan os.Signal) + signal.Notify(signalChannel, os.Interrupt, syscall.SIGTERM) + + mutex.Lock() + + go func() { + mutex.Lock() + f := bufio.NewWriter(os.Stdout) + defer f.Flush() + + fmt.Println("Hit Ctrl+C to shutdown...") + + err := cmd.Run() + + if err != nil { + fmt.Println(err) + cond.Broadcast() + mutex.Unlock() + return + } + + <-signalChannel + cond.Broadcast() + mutex.Unlock() + }() + + cond.Wait() +} + +// RunFuncUntilCtrlC run a function until ctrl-c is hit +func RunFuncUntilCtrlC(fn func() error) error { + mutex := sync.Mutex{} + cond := sync.NewCond(&mutex) + errorChannel := make(chan error) + signalChannel := make(chan os.Signal) + signal.Notify(signalChannel, os.Interrupt, syscall.SIGTERM) + + mutex.Lock() + + go func() { + mutex.Lock() + f := bufio.NewWriter(os.Stdout) + defer f.Flush() + + fmt.Println("Hit Ctrl+C to shutdown...") + + err := fn() + + if err != nil { + errorChannel <- err + fmt.Println(err) + cond.Broadcast() + mutex.Unlock() + return + } + + errorChannel <- nil + <-signalChannel + cond.Broadcast() + mutex.Unlock() + }() + + cond.Wait() + return <-errorChannel +} + +// RunCommandWithTimeout run a command with timeout. +func RunCommandWithTimeout(cmd *exec.Cmd, timeout time.Duration) error { + // Start a process: + if err := cmd.Start(); err != nil { + log.Fatal(err) + } + + // Wait for the process to finish or kill it after a timeout (whichever happens first): + done := make(chan error, 1) + go func() { + done <- cmd.Wait() + }() + + select { + case <-time.After(timeout): + fmt.Printf("Timeout of %ds reached... Killing process...\n", int64(timeout/time.Second)) + err := cmd.Process.Kill() + + if err != nil { + return err + } + return fmt.Errorf("timeout of %ds reached", int64(timeout/time.Second)) + case err := <-done: + return err + } +} diff --git a/cmd/authelia-scripts/files.go b/utils/files.go similarity index 88% rename from cmd/authelia-scripts/files.go rename to utils/files.go index 6406572e3..a3ec22613 100644 --- a/cmd/authelia-scripts/files.go +++ b/utils/files.go @@ -1,6 +1,8 @@ -package main +package utils -import "os" +import ( + "os" +) // FileExists returns whether the given file or directory exists func FileExists(path string) (bool, error) {