diff --git a/.gitignore b/.gitignore index f2cb9b4e..6eaa44db 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 ff24e4f3..b6bc9058 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 7e147962..3d69b3b8 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 a708a53b..174da059 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 c0002df5..b8643237 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 d556721d..b13b3fe3 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 8023cd1e..326d3897 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 b28003d9..dc5a64fe 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 612652ad..5b1aef47 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 3ddca4fc..43050929 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 073adf5e..f949611d 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 6eddf438..b4127fab 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 13cd4e85..a191018f 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 185c5b1e..728e01a7 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 63d43050..d9c0e8cc 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 607ff70d..00000000 --- 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 04bdb3e8..99ff5197 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 f7914042..644e2a65 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 b24c146a..9776ba99 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 f2ab7784..00000000 --- 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 1eac19a9..f0646071 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 5a4441ae..00000000 --- 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 e6fe7d83..411aae76 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 db433342..5cf7d82e 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 045ae636..a3d3211f 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 ab0ba620..00000000 --- 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 70be1616..00000000 --- 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 3654be48..00000000 --- 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 15af1cca..4b34c08e 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 38e23c92..76f18d13 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 40441a93..0531e9d9 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 cb390858..9c3fda7a 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 ffb6547d..12785e67 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 8c46627a..792c8774 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 958c9411..220ad487 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 eb0d053a..cf5e9238 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 e9f12f8e..9eb67550 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 e19f9e75..6debf895 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 e63c61c5..2b57f472 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 eb3ea68c..7ab33e28 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 3b893052..cf61596d 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 721e302d..f9f404e8 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 0c318b07..17a16df8 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 21e5a7af..63781632 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 0a874ebf..00000000 --- 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 df6b04c8..00000000 --- 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 00000000..4f94510b --- /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 00000000..0f74c2d0 --- /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 a7d5d9b8..5394b8ce 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 14717685..2ac9b8ce 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 13a0f853..7f7b31e5 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 e975e33b..bb58ce22 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 f186515e..2232929d 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 803814ca..4907a2be 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 d8b99fa0..fc18fcc5 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 5766dbd1..f1255188 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 00000000..0e64db1c --- /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 00000000..f42c0e42 --- /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 bad35cf3..586c5766 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 00000000..e96faabc --- /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 00000000..870119cc --- /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 00000000..f82c4ada --- /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 00000000..4f57fc23 --- /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 00000000..606fb6ba --- /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 00000000..c3076ad6 --- /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 00000000..cc7f4d9f --- /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 00000000..29d32c70 --- /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 00000000..11434c83 --- /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 00000000..4a2735f6 --- /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 00000000..a39e9db5 --- /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 00000000..80e0d12a --- /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 00000000..3dbd480f --- /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 00000000..abfa7596 --- /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 00000000..7af7286f --- /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 00000000..b03195d7 --- /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 00000000..a9d332a2 --- /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 00000000..6f8511a1 --- /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 00000000..516e6230 --- /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 00000000..91f84259 --- /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 00000000..33d62cda --- /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 00000000..68d1d1c7 --- /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 00000000..48f916c5 --- /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 00000000..879cd74c --- /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 00000000..14849c29 --- /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 00000000..9df63308 --- /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 00000000..a68c7e29 --- /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 00000000..30e863eb --- /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 00000000..de91871a --- /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 00000000..d3341eca --- /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 00000000..f808b843 --- /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 00000000..487a66e6 --- /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 00000000..7e8b2462 --- /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 00000000..43699e3b --- /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 00000000..75652835 --- /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 00000000..8d543fd5 --- /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 00000000..d8042de8 --- /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 00000000..c91b4467 --- /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 00000000..e4d1c2f7 --- /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 00000000..7953a065 --- /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 00000000..3db95b16 --- /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 00000000..1dad98d5 --- /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 00000000..6a2ed442 --- /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 00000000..9e968dec --- /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 a36b9fbe..00000000 --- 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 97c76216..00000000 --- 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 5ac11a54..00000000 --- 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 7281f9a8..00000000 --- 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 425b2c96..00000000 --- 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 55874000..00000000 --- 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 392f96e1..770e86f1 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 7c82219a..5e079e5d 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 51% rename from test/suites/basic-bypass-no-redirect/test.ts rename to test/suites/BypassAll/test.ts index eeb61823..6f298412 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 d8863bc2..c2c167b5 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 00000000..5ae03dd2 --- /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 5e67806c..b34cc421 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 67aedd27..68fd859c 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 00000000..1cba01c0 --- /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 00000000..0ec0498e --- /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 989981cb..00000000 --- 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 fd00f93c..00000000 --- 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 4b967aa4..00000000 --- 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 15326855..00000000 --- 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 a05d053d..00000000 --- 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 00084e27..00000000 --- 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 0de403d3..00000000 --- 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 f32d1d7e..00000000 --- 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 c82dd2fb..00000000 --- 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 779f2322..00000000 --- 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 8a2b1d39..00000000 --- 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 17033315..00000000 --- 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 bee6a33c..00000000 --- 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 4c6d7fa4..00000000 --- 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 c7e192cb..00000000 --- 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 420a6fc9..00000000 --- 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 c2da6df4..00000000 --- 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 f3bcede2..00000000 --- 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 751dfbc5..00000000 --- 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 adee3e67..00000000 --- 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 6308a6c4..00000000 --- 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 c015abed..00000000 --- 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 a12d72a1..00000000 --- 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 5c432c12..00000000 --- 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 0acd18b1..00000000 --- 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 5c26e690..00000000 --- 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 00000000..6581bc00 --- /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 00000000..3e2a23ba --- /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 6406572e..a3ec2261 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) {