From abc0e91e278f7938b264598ac0c60d18c5a9e8a0 Mon Sep 17 00:00:00 2001
From: hackerman <3372410+aeneasr@users.noreply.github.com>
Date: Fri, 1 May 2020 17:44:01 +0200
Subject: [PATCH] feat: add cypress e2e tests (#334)
---
.circleci/config.yml | 95 +++-
.editorconfig | 11 +
.gitignore | 4 +-
.schema/config.schema.json | 30 +-
Makefile | 16 +-
README.md | 23 +-
cmd/daemon/serve.go | 3 +-
cmd/tools.go | 2 +
continuity/container.go | 4 +-
continuity/manager_cookie.go | 16 +-
continuity/manager_test.go | 25 +-
.../kratos/email-password/sqlite/db.sqlite | Bin 163840 -> 0 bytes
courier/courier.go | 45 +-
cypress.json | 3 +
cypress/fixtures/example.json | 5 +
cypress/helpers/index.js | 53 ++
.../profiles/email/login/error.spec.js | 53 ++
.../profiles/email/login/success.spec.js | 32 ++
.../profiles/email/login/ui.spec.js | 15 +
.../profiles/email/logout/success.spec.js | 22 +
.../email/registration/errors.spec.js | 104 ++++
.../email/registration/success.spec.js | 32 ++
.../profiles/email/registration/ui.spec.js | 24 +
.../profiles/email/settings/errors.spec.js | 198 +++++++
.../profiles/email/settings/success.spec.js | 134 +++++
.../profiles/email/settings/ui.spec.js | 13 +
.../verify/registration/errors.spec.js | 47 ++
.../verify/registration/success.spec.js | 32 ++
.../profiles/verify/settings/error.spec.js | 52 ++
.../profiles/verify/settings/success.spec.js | 43 ++
.../profiles/verify/verify/errors.spec.js | 53 ++
.../profiles/verify/verify/success.spec.js | 52 ++
cypress/plugins/index.js | 21 +
cypress/support/commands.js | 224 ++++++++
cypress/support/index.js | 20 +
cypress/tsconfig.json | 12 +
go.mod | 6 +-
go.sum | 13 +-
package-lock.json | 504 ++++++++++++++----
package.json | 12 +-
{scripts => script}/test-envs.sh | 0
selfservice/flow/login/request.go | 4 -
selfservice/flow/login/request_test.go | 1 -
selfservice/flow/registration/request.go | 4 -
selfservice/flow/registration/request_test.go | 1 -
selfservice/flow/settings/strategy_profile.go | 36 +-
.../flow/settings/strategy_profile_test.go | 2 +
selfservice/flow/verify/handler.go | 3 +-
selfservice/strategy/password/settings.go | 32 +-
test/e2e/.gitignore | 2 +
test/e2e/profiles/email/.kratos.yml | 21 +
.../email/identity.traits.schema.json | 31 ++
test/e2e/profiles/kratos.base.yml | 35 ++
test/e2e/profiles/verify/.kratos.yml | 30 ++
.../verify/identity.traits.schema.json | 28 +
test/e2e/run.sh | 157 ++++++
.../wrongTypes.main.yaml | 0
.../allKeys.main.yaml | 0
.../allKeys.selfServiceAfterLogin.yaml | 0
.../allKeys.selfServiceAfterLoginHooks.yaml | 0
...eys.selfServiceAfterRegistrationHooks.yaml | 0
.../allKeys.selfServiceBefore.yaml | 0
.../allKeys.selfServiceOIDCProvider.yaml | 0
.../allKeys.selfServiceRedirectHook.yaml | 0
.../allKeys.selfServiceSessionIssuerHook.yaml | 0
...allKeys.selfServiceSessionRevokerHook.yaml | 0
{tests => test/schema}/schema_test.go | 2 +-
x/cookie.go | 2 +-
68 files changed, 2245 insertions(+), 199 deletions(-)
create mode 100644 .editorconfig
delete mode 100644 contrib/quickstart/kratos/email-password/sqlite/db.sqlite
create mode 100644 cypress.json
create mode 100644 cypress/fixtures/example.json
create mode 100644 cypress/helpers/index.js
create mode 100644 cypress/integration/profiles/email/login/error.spec.js
create mode 100644 cypress/integration/profiles/email/login/success.spec.js
create mode 100644 cypress/integration/profiles/email/login/ui.spec.js
create mode 100644 cypress/integration/profiles/email/logout/success.spec.js
create mode 100644 cypress/integration/profiles/email/registration/errors.spec.js
create mode 100644 cypress/integration/profiles/email/registration/success.spec.js
create mode 100644 cypress/integration/profiles/email/registration/ui.spec.js
create mode 100644 cypress/integration/profiles/email/settings/errors.spec.js
create mode 100644 cypress/integration/profiles/email/settings/success.spec.js
create mode 100644 cypress/integration/profiles/email/settings/ui.spec.js
create mode 100644 cypress/integration/profiles/verify/registration/errors.spec.js
create mode 100644 cypress/integration/profiles/verify/registration/success.spec.js
create mode 100644 cypress/integration/profiles/verify/settings/error.spec.js
create mode 100644 cypress/integration/profiles/verify/settings/success.spec.js
create mode 100644 cypress/integration/profiles/verify/verify/errors.spec.js
create mode 100644 cypress/integration/profiles/verify/verify/success.spec.js
create mode 100644 cypress/plugins/index.js
create mode 100644 cypress/support/commands.js
create mode 100644 cypress/support/index.js
create mode 100644 cypress/tsconfig.json
rename {scripts => script}/test-envs.sh (100%)
create mode 100644 test/e2e/.gitignore
create mode 100644 test/e2e/profiles/email/.kratos.yml
create mode 100644 test/e2e/profiles/email/identity.traits.schema.json
create mode 100644 test/e2e/profiles/kratos.base.yml
create mode 100644 test/e2e/profiles/verify/.kratos.yml
create mode 100644 test/e2e/profiles/verify/identity.traits.schema.json
create mode 100755 test/e2e/run.sh
rename {tests => test/schema}/fixtures/config.schema.test.failure/wrongTypes.main.yaml (100%)
rename {tests => test/schema}/fixtures/config.schema.test.success/allKeys.main.yaml (100%)
rename {tests => test/schema}/fixtures/config.schema.test.success/allKeys.selfServiceAfterLogin.yaml (100%)
rename {tests => test/schema}/fixtures/config.schema.test.success/allKeys.selfServiceAfterLoginHooks.yaml (100%)
rename {tests => test/schema}/fixtures/config.schema.test.success/allKeys.selfServiceAfterRegistrationHooks.yaml (100%)
rename {tests => test/schema}/fixtures/config.schema.test.success/allKeys.selfServiceBefore.yaml (100%)
rename {tests => test/schema}/fixtures/config.schema.test.success/allKeys.selfServiceOIDCProvider.yaml (100%)
rename {tests => test/schema}/fixtures/config.schema.test.success/allKeys.selfServiceRedirectHook.yaml (100%)
rename {tests => test/schema}/fixtures/config.schema.test.success/allKeys.selfServiceSessionIssuerHook.yaml (100%)
rename {tests => test/schema}/fixtures/config.schema.test.success/allKeys.selfServiceSessionRevokerHook.yaml (100%)
rename {tests => test/schema}/schema_test.go (99%)
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 8a631b03addf..4dcbbb823475 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -41,7 +41,7 @@ jobs:
image: cockroachdb/cockroach:v19.2.0
command: start --insecure
-
- image: oryd/hydra:v1.2.2
+ image: oryd/hydra:v1.4.10
environment:
- DSN=memory
- URLS_SELF_ISSUER=http://127.0.0.1:4444/
@@ -72,6 +72,55 @@ jobs:
-
run: test -z "$CIRCLE_PR_NUMBER" && goveralls -service=circle-ci -coverprofile=coverage.txt -repotoken=$COVERALLS_REPO_TOKEN || echo "forks are not allowed to push to coveralls"
+ test-e2e:
+ parameters:
+ flavor:
+ type: string
+ docker:
+ - image: oryd/e2e-env:latest
+ environment:
+ - GO111MODULE=on
+ - TEST_DATABASE_MYSQL=mysql://root:test@(localhost:3306)/mysql?parseTime=true&multiStatements=true
+ - TEST_DATABASE_COCKROACHDB=cockroach://root@localhost:26257/defaultdb?sslmode=disable
+ - TEST_DATABASE_POSTGRESQL=postgres://test:test@localhost:5432/kratos?sslmode=disable
+ - image: postgres:9.6
+ environment:
+ - POSTGRES_USER=test
+ - POSTGRES_PASSWORD=test
+ - POSTGRES_DB=kratos
+ - image: cockroachdb/cockroach:v2.1.6
+ command: start --insecure
+ - image: mysql:5.7
+ environment:
+ - MYSQL_ROOT_PASSWORD=test
+ - image: oryd/mailslurper:latest-smtps
+ working_directory: /go/src/github.com/ory/kratos
+ steps:
+ - checkout
+ # core node_modules cache
+ - restore_cache:
+ keys:
+ - v1-deps-{{ checksum "package-lock.json" }}
+ - v1-deps
+ - run: npm ci
+ - save_cache:
+ key: v1-deps-{{ checksum "package-lock.json" }}
+ # cache NPM modules and the folder with the Cypress binary
+ paths:
+ - ~/.npm
+ - ~/.cache
+ # go modules cache
+ - restore_cache:
+ keys:
+ - go-mod-v1-{{ checksum "go.sum" }}
+ - run: go mod download
+ - save_cache:
+ key: go-mod-v1-{{ checksum "go.sum" }}
+ paths:
+ - "/go/pkg/mod"
+ - run: make deps
+ - run: ./test/e2e/run.sh << parameters.flavor >>
+
workflows:
tbr:
jobs:
@@ -81,6 +130,30 @@ workflows:
filters:
tags:
only: /.*/
+ - test-e2e:
+ name: test-e2e-sqlite
+ flavor: sqlite
+ filters:
+ tags:
+ only: /.*/
+ - test-e2e:
+ name: test-e2e-postgres
+ flavor: postgres
+ filters:
+ tags:
+ only: /.*/
+ - test-e2e:
+ name: test-e2e-mysql
+ flavor: mysql
+ filters:
+ tags:
+ only: /.*/
+ - test-e2e:
+ name: test-e2e-cockroach
+ flavor: cockroach
+ filters:
+ tags:
+ only: /.*/
-
test:
filters:
@@ -90,6 +163,10 @@ workflows:
appname: Ory_Kratos
requires:
- test
+ - test-e2e-sqlite
+ - test-e2e-postgres
+ - test-e2e-mysql
+ - test-e2e-cockroach
- golangci/lint
filters:
tags:
@@ -99,6 +176,10 @@ workflows:
- docs/build:
requires:
- test
+ - test-e2e-sqlite
+ - test-e2e-postgres
+ - test-e2e-mysql
+ - test-e2e-cockroach
- golangci/lint
filters:
tags:
@@ -108,6 +189,10 @@ workflows:
- sdk/release:
requires:
- test
+ - test-e2e-sqlite
+ - test-e2e-postgres
+ - test-e2e-mysql
+ - test-e2e-cockroach
- golangci/lint
- sdk/generate
- goreleaser/release
@@ -119,6 +204,10 @@ workflows:
- changelog/generate:
requires:
- test
+ - test-e2e-sqlite
+ - test-e2e-postgres
+ - test-e2e-mysql
+ - test-e2e-cockroach
- golangci/lint
filters:
tags:
@@ -135,6 +224,10 @@ workflows:
requires:
- goreleaser/test
- test
+ - test-e2e-sqlite
+ - test-e2e-postgres
+ - test-e2e-mysql
+ - test-e2e-cockroach
- golangci/lint
filters:
branches:
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 000000000000..a543c21279ba
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,11 @@
+[*]
+charset = utf-8
+end_of_line = lf
+indent_size = 2
+indent_style = space
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.go]
+indent_size = 4
+indent_style = tab
diff --git a/.gitignore b/.gitignore
index a53d8e0b8f5f..68fcd972d259 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,4 +8,6 @@ packrd/
*-packr.go
dist/
node_modules
-.bin/
+.bin/*
+cypress/videos
+cypress/screenshots
diff --git a/.schema/config.schema.json b/.schema/config.schema.json
index 1a9969e8fdd7..7e6fa5334a37 100644
--- a/.schema/config.schema.json
+++ b/.schema/config.schema.json
@@ -31,13 +31,13 @@
"type": "boolean"
}
},
- "additionalItems": false,
+ "additionalProperties": false,
"required": [
"default_redirect_url"
]
}
},
- "additionalItems": false,
+ "additionalProperties": false,
"required": [
"hook",
"config"
@@ -50,7 +50,7 @@
"const": "revoke_active_sessions"
}
},
- "additionalItems": false,
+ "additionalProperties": false,
"required": [
"hook"
]
@@ -62,7 +62,7 @@
"const": "verify"
}
},
- "additionalItems": false,
+ "additionalProperties": false,
"required": [
"hook"
]
@@ -74,7 +74,7 @@
"const": "session"
}
},
- "additionalItems": false,
+ "additionalProperties": false,
"required": [
"hook"
]
@@ -147,7 +147,7 @@
}
}
},
- "additionalItems": false,
+ "additionalProperties": false,
"required": [
"id",
"provider",
@@ -272,7 +272,7 @@
},
"selfServiceBefore": {
"type": "object",
- "additionalItems": false,
+ "additionalProperties": false,
"properties": {
"hooks": {
"type": "array",
@@ -296,18 +296,18 @@
"properties": {
"selfservice": {
"type": "object",
- "additionalItems": false,
+ "additionalProperties": false,
"required": [
"logout"
],
"properties": {
"strategies": {
"type": "object",
- "additionalItems": false,
+ "additionalProperties": false,
"properties": {
"password": {
"type": "object",
- "additionalItems": false,
+ "additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean"
@@ -316,14 +316,14 @@
},
"oidc": {
"type": "object",
- "additionalItems": false,
+ "additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean"
},
"config": {
"type": "object",
- "additionalItems": false,
+ "additionalProperties": false,
"properties": {
"providers": {
"title": "OpenID Connect and OAuth2 Providers",
@@ -351,14 +351,14 @@
]
}
},
- "additionalItems": false,
+ "additionalProperties": false,
"required": [
"redirect_to"
]
},
"settings": {
"type": "object",
- "additionalItems": false,
+ "additionalProperties": false,
"properties": {
"request_lifespan": {
"type": "string",
@@ -434,7 +434,7 @@
"$ref": "#/definitions/selfServiceAfterLogin"
}
},
- "additionalItems": false
+ "additionalProperties": false
},
"registration": {
"type": "object",
diff --git a/Makefile b/Makefile
index 5777207fc690..48e998d9d663 100644
--- a/Makefile
+++ b/Makefile
@@ -5,7 +5,7 @@ K := $(foreach exec,$(EXECUTABLES),\
$(if $(shell which $(exec)),some string,$(error "No $(exec) in PATH")))
export GO111MODULE := on
-export PATH := .bin:${PATH}
+export PATH := $(pwd)/.bin:${PATH}
deps:
ifneq ("v0", $(shell cat .bin/.lock))
@@ -18,7 +18,8 @@ ifneq ("v0", $(shell cat .bin/.lock))
go build -o .bin/goimports golang.org/x/tools/cmd/goimports
go build -o .bin/swagutil github.com/ory/sdk/swagutil
go build -o .bin/packr2 github.com/gobuffalo/packr/v2/packr2
- npm i
+ go build -o .bin/yq github.com/mikefarah/yq
+ npm ci
echo "v0" > .bin/.lock
endif
@@ -60,7 +61,7 @@ test-resetdb:
.PHONY: test
test: test-resetdb
- source scripts/test-envs.sh && go test -tags sqlite -count=1 ./...
+ source script/test-envs.sh && go test -tags sqlite -count=1 ./...
# Generates the SDKs
.PHONY: sdk
@@ -90,8 +91,17 @@ quickstart-dev:
.PHONY: format
format: deps
goreturns -w -local github.com/ory $$(listx .)
+ npm run format
# Runs tests in short mode, without database adapters
.PHONY: docker
docker:
docker build -f .docker/Dockerfile-build -t oryd/kratos:latest .
+
+.PHONY: test-e2e
+test-e2e: test-resetdb
+ source script/test-envs.sh
+ test/e2e/run.sh sqlite
+ test/e2e/run.sh postgres
+ test/e2e/run.sh cockroach
+ test/e2e/run.sh mysql
diff --git a/README.md b/README.md
index 3440eed997ee..481458d7a373 100644
--- a/README.md
+++ b/README.md
@@ -20,7 +20,7 @@
-ORY Kratos is the first and only cloud native Identity and User Management System in the world. Finally, it is no longer necessary to implement a User Login process for the umpteenth time!
+ORY Kratos is the first and only cloud native Identity and User Management System in the world. Finally, it is no longer necessary to implement a User Login process for the umpteenth time!
@@ -327,6 +327,12 @@ It is possible to develop ORY Kratos on Windows, but please be aware that all gu
When cloning ORY Kratos, run `make tools`. It will download several required dependencies. If you haven't run the command
in a while it's probably a good idea to run it again.
+#### Install from source
+
+```shell script
+make install
+```
+
#### Formatting Code
You can format all code using `make format`. Our CI checks if your code is properly formatted.
@@ -383,6 +389,21 @@ go test -tags sqlite ./...
cd client; go test -tags sqlite .
```
+##### End-to-End Tests
+
+We use [Cypress](https://www.cypress.io) to run our e2e tests. You can run all tests using:
+
+```shell script
+make test-e2e
+```
+
+If you intend developing e2e tests, run the following command for more details:
+
+```shell script
+./test/e2e/run.sh
+```
+
+
#### Build Docker
You can build a development Docker Image using:
diff --git a/cmd/daemon/serve.go b/cmd/daemon/serve.go
index 8a9b4f6252bf..f39d41f36c00 100644
--- a/cmd/daemon/serve.go
+++ b/cmd/daemon/serve.go
@@ -172,10 +172,11 @@ func sqa(cmd *cobra.Command, d driver.Driver) *metricsx.Service {
func bgTasks(d driver.Driver, wg *sync.WaitGroup, cmd *cobra.Command, args []string) {
defer wg.Done()
+ d.Logger().Println("Courier worker started.")
if err := graceful.Graceful(d.Registry().Courier().Work, d.Registry().Courier().Shutdown); err != nil {
d.Logger().WithError(err).Fatalf("Failed to run courier worker.")
}
- d.Logger().Println("courier worker was shutdown gracefully")
+ d.Logger().Println("Courier worker was shutdown gracefully.")
}
func ServeAll(d driver.Driver) func(cmd *cobra.Command, args []string) {
diff --git a/cmd/tools.go b/cmd/tools.go
index 208282c9e90f..908fe3c480bc 100644
--- a/cmd/tools.go
+++ b/cmd/tools.go
@@ -16,6 +16,8 @@ import (
_ "github.com/gobuffalo/packr/v2"
_ "github.com/jteeuwen/go-bindata"
+ _ "github.com/mikefarah/yq"
+
_ "github.com/ory/sdk/swagutil"
_ "github.com/davidrjonas/semver-cli"
)
diff --git a/continuity/container.go b/continuity/container.go
index 413a72e73899..c71720f1fd74 100644
--- a/continuity/container.go
+++ b/continuity/container.go
@@ -53,11 +53,11 @@ func NewContainer(name string, o managerOptions) *Container {
func (c *Container) Valid(identity uuid.UUID) error {
if c.ExpiresAt.Before(time.Now()) {
- return errors.WithStack(herodot.ErrBadRequest.WithReasonf("You mast restart the flow because the resumable session has expired."))
+ return errors.WithStack(herodot.ErrBadRequest.WithReasonf("You must restart the flow because the resumable session has expired."))
}
if identity != uuid.Nil && x.DerefUUID(c.IdentityID) != identity {
- return errors.WithStack(herodot.ErrBadRequest.WithReasonf("You mast restart the flow because the resumable session was initiated by another person."))
+ return errors.WithStack(herodot.ErrBadRequest.WithReasonf("You must restart the flow because the resumable session was initiated by another person."))
}
return nil
diff --git a/continuity/manager_cookie.go b/continuity/manager_cookie.go
index 5bb34cfba321..6636dd5695c7 100644
--- a/continuity/manager_cookie.go
+++ b/continuity/manager_cookie.go
@@ -17,9 +17,9 @@ import (
)
var _ Manager = new(ManagerCookie)
-var ErrNotResumable = *herodot.ErrBadRequest.WithReasonf("No resumable session could be found in the HTTP Header.")
+var ErrNotResumable = *herodot.ErrBadRequest.WithError("session is not resumable").WithReasonf("No resumable session could be found in the HTTP Header.")
-const sessionKeyID = "id"
+const cookieName = "ory_kratos_continuity"
type (
managerCookieDependencies interface {
@@ -47,8 +47,8 @@ func (m *ManagerCookie) Pause(ctx context.Context, w http.ResponseWriter, r *htt
}
c := NewContainer(name, *o)
- if err := x.SessionPersistValues(w, r, m.d.CookieManager(), name, map[string]interface{}{
- sessionKeyID: c.ID.String(),
+ if err := x.SessionPersistValues(w, r, m.d.CookieManager(), cookieName, map[string]interface{}{
+ name: c.ID.String(),
}); err != nil {
return err
}
@@ -72,7 +72,7 @@ func (m *ManagerCookie) Continue(ctx context.Context, r *http.Request, name stri
}
if err := container.Valid(o.iid); err != nil {
- return nil, errors.WithStack(ErrNotResumable.WithDebugf("%+v", err))
+ return nil, err
}
if o.payloadRaw != nil && container.Payload != nil {
@@ -90,7 +90,7 @@ func (m *ManagerCookie) Continue(ctx context.Context, r *http.Request, name stri
func (m *ManagerCookie) sid(ctx context.Context, r *http.Request, name string) (uuid.UUID, error) {
var sid uuid.UUID
- if s, err := x.SessionGetString(r, m.d.CookieManager(), name, sessionKeyID); err != nil {
+ if s, err := x.SessionGetString(r, m.d.CookieManager(), cookieName, name); err != nil {
return sid, errors.WithStack(ErrNotResumable.WithDebugf("%+v", err))
} else if sid = x.ParseUUID(s); sid == uuid.Nil {
return sid, errors.WithStack(ErrNotResumable.WithDebug("sid is not a valid uuid"))
@@ -123,8 +123,8 @@ func (m ManagerCookie) Abort(ctx context.Context, w http.ResponseWriter, r *http
return err
}
- if err := x.SessionPersistValues(w, r, m.d.CookieManager(), name, map[string]interface{}{
- sessionKeyID: "",
+ if err := x.SessionPersistValues(w, r, m.d.CookieManager(), cookieName, map[string]interface{}{
+ name: "",
}); err != nil {
return err
}
diff --git a/continuity/manager_test.go b/continuity/manager_test.go
index d18427e0f159..124a37b86b9a 100644
--- a/continuity/manager_test.go
+++ b/continuity/manager_test.go
@@ -79,12 +79,12 @@ func TestManager(t *testing.T) {
})
router.DELETE("/:name", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
- c, err := p.Continue(r.Context(), r, ps.ByName("name"), tc.ro...)
+ err := p.Abort(r.Context(), w, r, ps.ByName("name"))
if err != nil {
writer.WriteError(w, r, err)
return
}
- writer.Write(w, r, c)
+ w.WriteHeader(http.StatusNoContent)
})
ts := httptest.NewServer(router)
@@ -190,6 +190,27 @@ func TestManager(t *testing.T) {
assert.JSONEq(t, b.String(), gjson.GetBytes(body, "payload").Raw, "%s", body)
assert.Contains(t, href, gjson.GetBytes(body, "name").String(), "%s", body)
})
+
+ t.Run("case=pause, abort, and continue session with failure", func(t *testing.T) {
+ href := genid()
+ res, err := cl.Do(x.NewTestHTTPRequest(t, "PUT", href, nil))
+ require.NoError(t, err)
+ require.NoError(t, res.Body.Close())
+ require.Equal(t, http.StatusNoContent, res.StatusCode)
+
+ res, err = cl.Do(x.NewTestHTTPRequest(t, "DELETE", href, nil))
+ require.NoError(t, err)
+ t.Cleanup(func() { require.NoError(t, res.Body.Close()) })
+ require.Equal(t, http.StatusNoContent, res.StatusCode)
+
+ res, err = cl.Do(x.NewTestHTTPRequest(t, "GET", href, nil))
+ require.NoError(t, err)
+ t.Cleanup(func() { require.NoError(t, res.Body.Close()) })
+
+ require.Equal(t, http.StatusBadRequest, res.StatusCode)
+ body := x.MustReadAll(res.Body)
+ assert.Contains(t, gjson.GetBytes(body, "error.reason").String(), "resumable session")
+ })
})
}
})
diff --git a/contrib/quickstart/kratos/email-password/sqlite/db.sqlite b/contrib/quickstart/kratos/email-password/sqlite/db.sqlite
deleted file mode 100644
index 97d952585f32ef7a3490c8061c3eb8a0ab0f7e9e..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 163840
zcmeI*?Qh#ieg|;TvPDO>Bk$Ak?)GxaXmh(3){6CLTfwHOV$)u)W!aIXyLf|upvaNU
zjV#$D(OD3tVw8+FtwefZ`7A->~S5Vqf%Lv_nd^
z^*~bcCfR1SU+*T7I74#gGe3?v9Fy9-mr)JEm#W&XWbkp%q=#WVf6Vh9kLN1=Ge!TL
z{c!Zf@Yx&sFVphc>E%_=%{#vzp{YZGFU&ksfj@mOhmY8j3+i7E81
zTh$FsGSq5?zS*x4-4J()u~SuaG1_m=H%|Spu-y7O^TcaN#WLx&p58OVs|z_Hofr6g
z`ff(xgFW*H`6+);Rf2q8xS!`Yaw}`;+!ntoY)$)vXVt1{tnH;oHsXTnIC8}Y36awJrddu`L<2zSNm*x-L28TX$Se{RUfyV?&lzH$98E`Jy72mXE}A1
zsUKUP6skfhhH4y(G~TV3Qdw*)E2%`&TVB(bM?G>+G+VilUAb2f_?7IUaG$4L_GNYv
zG=sd?Iy6cy3K=>%^0VCPD)V^Tiq9VV>ybRx9;rQYyM^mT3A=k2`^`G^P|vX^vfpo8
zA7qoox%YWE=HvEPokG-RQ!N2SKZSZ^7(ZPdWw~UMd75%+S>0w6`jwt-=-SUXeI(fI
zN;Ho52(6QS%bl;_l;L@P$h7Rtu02)a)QTM+R_EJk=@FW>wA*f5&tc|s+KoT7e8tB-
zNM0gX>?1_?*WY1T?%o}yzT()2StSuG(yrM$w*8isW3=xct9^scy_A)Yb_?dNLv?P1
zCE
z=A^9~rg7JdDEeyHwmLMyt^9Q8q8hrW%R6LOvW_)t0C08&PtV~RIXdcSp4&Z3&*=}v
zhke}g^&UpF@aHJ>wK+5$HOH0pu_2Zd!c0Bd7@;*vzpHLH2Vw{G)72c1(dcZ}>^#kD
z8x#CcJGj%3gjn&+86P;F=dz1e4t-u8R
zn+=?J0-p!|Gw`p0zYF{<@aOa`ejoq=2tWV=5P$##AOHafKmY;|c&-AI>`*8ciKe2_
zNaJUI($}$MZgQk!$?W8C$CAXPw_{0sa;RfTY?A3%67|!%uz??&IN#Ip)h-n}Bvt5hkd|iuXN4gdzhPxKUy7&U|d1Rwwb
z2tWV=5P$##AOHafVEm65fB*y_009U<00Izz00bZa0SFAf0Dk{}@Y@(Qga8B}009U<
z00Izz00bZa0SI9JA29#{2tWV=5P$##AOHafKmY;|7<>WD{||l}qlOTG00bZa0SG_<
z0uX=z1Rwwb%>N?>AOHafKmY;|fB*y_009U<00M(Afbsv}w=rr60SG_<0uX=z1Rwwb
z2tWV=5HNrLpSk)E9{N9iAOHafKmY;|fB*y_009U<00J+iz)70%7mhbKC+fFX*Aj_2
zX>K+_!g8z>2`5ssiEv7i=EI3(G8&yFk%+929mCkWH8T^9&E1U9|Dv}NG<8NN^@YaJ*f)#0(
z&;(i6N}^GHL@G4dINmdt?Wl@kz1PToP1Ok9fT7jMbnrkb*UStrYF;+@>BH&Zo>o0j
z6{68%R%+$)r+(Yn+GKn_k#w1^rRm9dBmHrf87*8ZJUymuUXo*}*?B@|xRnxZ^HM5Y
zET&4~xtLO#O-gbpl8Ws~x_(&IlzxbxbGutp=dBHXR+c+vOAy_--A|$VYIiDJF;p54
z+C{q?GN1QE6v3kElV8Ndf6h(A)w-^RNQU;pQb0G7xVu^fzLdF&+r=n
zFKb7SazOwB5P$##AOHafKmY;|fWS9SV9Hxy`nlLQWX}KhbA_mJ|Nk#MSAX%1vqXg<
z009U<00Izz00bZa0SG|gB^NkZVa5t|_MUm;|Jqt273q!tiP>m4MdFe0
zd^9d6B$*UT(yaY2vbfy%-}+lCUB9!DjNFXPC1+#F+2&33?K$Y}|K~a4bIo7uD8&*oy^rP&3)?og|C_s>kH!*C^Z(8CWb9)8ANT*iz{}fB*y_009U<00Izz00bNa%=!O*u8cJ9|NpgT;+JE;cGwgaLjVF0fB*y_009U<
z00J+K!0BG|F#xM;BwkF=N1#W-v4|vv6UEp(eGCA75IRZ4Q_)0fJ|!h0=6(153=E$0
z?cl8ws;zg0Upxk&pF;I@JNTEnN&7|3&-VEk06HrhkHq55N!j-KWctwdv*hM{^WzU1
zPXX9nTT|v@q$n%#@SIHM!igx6!m0WBVmO|R72`_0IIBqH?9TRngukL@KP%Gt!vOjz
z+t;!6pY@3Y^vM~jq2DCCl3KoaRzc(Krw?uJ|2OCNQ;}4>`M?OfP_ojSUrS&<|KGa*
z|9A8u0Qi9b1Rwwb2tWV=5P$##AOHafK;UH%Fz^5G=PI9d|9?N%h9&|YPhfc36Znh3
z=*0hA{kMsUiJy-v{iK`(x;4yHLcuCveBY$21OiK?#Gh$2cxkYA*A%CD>m{8=);C@iH5nLNKx
z$mN7=UNloT^Xat>x*|E+qYshQ?boE~=A&~DNVB=R_uF!5x>4q{ozhihjYx)5p=f$-
zPjOE7hong#S6lBVz^o^;x<{66mRC-11X!!R=ek15c7@iEvdu8r6;kKzZJXb%y_=5*
z?qYQ=gc&&Uj-*Y+XG7<$Z#G1=Qc|~V8t+*eXxhKDo)cDqpwRakbCZ#$kOPjs8%5W1QvGRi=JyeNr?Yb!;4qw70OHdMuU2i^m`9EwAazqaL{@
znhjFOuG}jK{7QCFxX;u6)O?z9zq<&U5!h=TdfZ+VGIVm}XSvl?=JB=_Np(6_*nQ~RZ`Psvc#hqK{eIgz2%9X_swJRk|Ij1D`045>
z%O#V{)09)o>NcCuuk>s~*Dk@SzqZ+xXg(F5*2%u*&R1~C@Jt`k9iIR#JF{y~`iOjo
z4&G@yEj>cBmUi21>p9GPPP_4kmaq7@2gyqWi+zOX{`xyC%iX)f)K?t)Fsme@`5b?z
ze#^=++INrDzCq_+%F0K(1#{P-Iyb^{cW6OxJH@XRKz2dgNbvQHv>a5iR^V3Vl67$C9Jwc(Oh=#BxHIsYe?lv_|Q7
z)$PVNSBMAnOC0DoSeVh-tl4>**ET3SbG2@v5ssZXC~9*rp&f?h!n82Wv0F!BI#(QO
zZ>G*-eE7|5Ja14!VE(4GpsOH6UkWkB&1=+br+WUC6u3JzDkH)TP@uN4lb<
zD0GOXw=}eU@^!jttL;yM-(k6ElsT<-o!b&kt7>iMw2dX->mY9#zc(_t9U*5qI*$vT
zkc#8T@=((0WdizDCVa74EuWtuKJ&nFns5Eyl=gn-d?@_xyP=aCZ(1>V`iX@kS8E#m
z!VNmo)pco`=$-Gs-wxE7?8akuPeFP%nirOZTx&&jLo#Z#@3BcLR+Zz9*rqait@tsK
z=^(#TS|hT$r_%G>?xbhzg#3T$NpFUo!nKn@jCL-0YvH>-?nhCl4YUpi8)4LbNvN^x
z+Q|XOa@VdgPwq4$S~oi{^O&&y{cd~XoGo^1loxl!gnk8(IpCmOgUiK_ma`@MbGK(|
zHm6f6bAa747QgslbT~Ehhi1O*;})(tw_d~FY_rCzHy?Z7V7Uhmn5PfB`X2h_K_!*;
zXS-5`9$~u_>Ad4?4Fi{K-|lSZi>r(-Yq^EN+4ab;_NrQ`s_k|ksf`O_qF$3_s#&Vh
z>wKMEW|voN-)_e^)ou+Mva}G>
zy!HKcp?dZ@%RQoXIJ{gPYWqE_aiduchpU5jWoxZXV-J_F&%sf4`PC!om
zuQcX(Ufi_d`5ce!QSa6z|NoqQ4D_6ydH=s>vSV0}w-A5;1Rwwb2tWV=5P$##AOHaf
z43vO*|3Ak61J$Ic3gs1O7o009U<00Izz00bZa0SG{#O912lE)pz&00bZa0SG_<
z0uX=z1Rwwb2n?J6#{UD?q^J-CAOHafKmY;|fB*y_009UBkxss#DGa6iv)e=fo_lVZv@N#vi?f0z4jb=3p*_C?*fxm2R8hf~Wg=Tn`
z+oBa(?W00+RUtj=@%Ck!<7|ulmD|i6s=xmh%ZaqaV!tKs13&mB@U606hTSUu*7tqf
z?y^(Y=pMXL*W~M`KfcCt%gfBuL#s=4&Fq@UH#M4Fp=(~ZZqTf-P5)vY+TF`-JF3R#
z1EJk)XK!R3kTzSfK2SP(sa~C@Yqxt|eY3NdcO-2pJ{x*&{o=HLX+0;bEN9J+4t}a-
zXCXc(ED7{eU_sdAo6A+An|VTJzqKf21p0X;OS&v6#2>ozJs-Cjc8V;yTGLdbiMzCa
Om9}XQ8|_^3*8c$oQxcH?
diff --git a/courier/courier.go b/courier/courier.go
index b909501a424f..f93d0c4b2441 100644
--- a/courier/courier.go
+++ b/courier/courier.go
@@ -10,9 +10,8 @@ import (
"github.com/cenkalti/backoff"
"github.com/gofrs/uuid"
"github.com/pkg/errors"
- "gopkg.in/gomail.v2"
- "github.com/ory/x/errorsx"
+ gomail "github.com/ory/mail/v3"
"github.com/ory/kratos/driver/configuration"
"github.com/ory/kratos/x"
@@ -24,7 +23,7 @@ type (
x.LoggingProvider
}
Courier struct {
- dialer *gomail.Dialer
+ Dialer *gomail.Dialer
d smtpDependencies
c configuration.Provider
// graceful shutdown handling
@@ -38,23 +37,34 @@ type (
func NewSMTP(d smtpDependencies, c configuration.Provider) *Courier {
uri := c.CourierSMTPURL()
- sslSkipVerify, _ := strconv.ParseBool(uri.Query().Get("skip_ssl_verify"))
password, _ := uri.User.Password()
port, _ := strconv.ParseInt(uri.Port(), 10, 64)
ctx, cancel := context.WithCancel(context.Background())
+
+ var ssl bool
+ var tlsConfig *tls.Config
+ if uri.Scheme == "smtps" {
+ ssl = true
+ sslSkipVerify, _ := strconv.ParseBool(uri.Query().Get("skip_ssl_verify"))
+ // #nosec G402 This is ok (and required!) because it is configurable and disabled by default.
+ tlsConfig = &tls.Config{InsecureSkipVerify: sslSkipVerify}
+ }
+
return &Courier{
d: d,
c: c,
ctx: ctx,
shutdown: cancel,
- dialer: &gomail.Dialer{
- Host: uri.Hostname(),
- Port: int(port),
- Username: uri.User.Username(),
- Password: password,
- SSL: uri.Scheme == "smtps",
- /* #nosec we need to support SMTP servers wihout TLS */
- TLSConfig: &tls.Config{InsecureSkipVerify: sslSkipVerify},
+ Dialer: &gomail.Dialer{
+ /* #nosec we need to support SMTP servers without TLS */
+ TLSConfig: tlsConfig,
+ Host: uri.Hostname(),
+ Port: int(port),
+ Username: uri.User.Username(),
+ Password: password,
+ SSL: ssl,
+ Timeout: time.Second * 10,
+ RetryFailure: true,
},
}
}
@@ -96,7 +106,7 @@ func (m *Courier) Work() error {
select {
case <-m.ctx.Done():
- if m.ctx.Err() == context.Canceled {
+ if errors.Is(m.ctx.Err(), context.Canceled) {
return nil
}
return m.ctx.Err()
@@ -115,12 +125,11 @@ func (m *Courier) watchMessages(ctx context.Context, errChan chan error) {
if err := backoff.Retry(func() error {
messages, err := m.d.CourierPersister().NextMessages(ctx, 10)
if err != nil {
- if errorsx.Cause(err) == ErrQueueEmpty {
+ if errors.Is(err, ErrQueueEmpty) {
return nil
}
return err
}
-
for k := range messages {
var msg = messages[k]
@@ -134,11 +143,11 @@ func (m *Courier) watchMessages(ctx context.Context, errChan chan error) {
gm.SetBody("text/plain", msg.Body)
gm.AddAlternative("text/html", msg.Body)
- if err := m.dialer.DialAndSend(gm); err != nil {
+ if err := m.Dialer.DialAndSend(ctx, gm); err != nil {
m.d.Logger().
WithError(err).
- WithField("smtp_server", fmt.Sprintf("%s:%d", m.dialer.Host, m.dialer.Port)).
- WithField("smtp_ssl_enabled", m.dialer.SSL).
+ WithField("smtp_server", fmt.Sprintf("%s:%d", m.Dialer.Host, m.Dialer.Port)).
+ WithField("smtp_ssl_enabled", m.Dialer.SSL).
// WithField("email_to", msg.Recipient).
WithField("message_from", from).
Error("Unable to send email using SMTP connection.")
diff --git a/cypress.json b/cypress.json
new file mode 100644
index 000000000000..c5691a9ac2b7
--- /dev/null
+++ b/cypress.json
@@ -0,0 +1,3 @@
+{
+ "chromeWebSecurity": false
+}
diff --git a/cypress/fixtures/example.json b/cypress/fixtures/example.json
new file mode 100644
index 000000000000..da18d9352a17
--- /dev/null
+++ b/cypress/fixtures/example.json
@@ -0,0 +1,5 @@
+{
+ "name": "Using fixtures to represent data",
+ "email": "hello@cypress.io",
+ "body": "Fixtures are a great way to mock data for responses to routes"
+}
\ No newline at end of file
diff --git a/cypress/helpers/index.js b/cypress/helpers/index.js
new file mode 100644
index 000000000000..97565386c19e
--- /dev/null
+++ b/cypress/helpers/index.js
@@ -0,0 +1,53 @@
+const email = () =>
+ Math.random().toString(36).substring(7) +
+ '@' +
+ Math.random().toString(36).substring(7)
+const password = () => Math.random().toString(36)
+
+const assertAddress = ({ isVerified, email }) => ({ identity }) => {
+ expect(identity).to.have.property('addresses')
+ expect(identity.addresses).to.have.length(1)
+
+ const address = identity.addresses[0]
+ expect(address.id).to.not.be.empty
+ expect(address.verified).to.equal(isVerified)
+ expect(address.value).to.equal(email)
+
+ if (isVerified) {
+ expect(address.verified_at).to.not.be.null
+ } else {
+ expect(address.verified_at).to.be.null
+ }
+}
+
+const parseHtml = (html) => new DOMParser().parseFromString(html, 'text/html')
+
+module.exports = {
+ APP_URL: (Cypress.env('app_url') || 'http://127.0.0.1:4455').replace(
+ /\/$/,
+ ''
+ ),
+ MAIL_API: (Cypress.env('mail_url') || 'http://127.0.0.1:4437').replace(
+ /\/$/,
+ ''
+ ),
+ website: 'https://www.ory.sh/',
+ parseHtml,
+ gen: {
+ email,
+ password,
+ identity: () => ({ email: email(), password: password() }),
+ },
+ assertAddress,
+
+ // Format is
+ // http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/verification/email/confirm/OdTRmdMKe0DfF6ScaOFYgWJwoAprTxnA
+ verifyHrefPattern: /^http:.*\/.ory\/kratos\/public\/self-service\/browser\/flows\/verification\/email\/confirm\/([a-zA-Z0-9]+)$/,
+
+ // intervals define how long to wait for something,
+ pollInterval: 100, // how long to wait before retry
+
+ // Adding 1+ second on top because MySQL doesn't do millisecs.
+ verifyLifespan: 5000 + 1050,
+ privilegedLifespan: 5000 + 1050,
+}
diff --git a/cypress/integration/profiles/email/login/error.spec.js b/cypress/integration/profiles/email/login/error.spec.js
new file mode 100644
index 000000000000..3db05de39de3
--- /dev/null
+++ b/cypress/integration/profiles/email/login/error.spec.js
@@ -0,0 +1,53 @@
+import { APP_URL, gen } from '../../../../helpers'
+
+context('Login', () => {
+ beforeEach(() => {
+ cy.visit(APP_URL + '/auth/login')
+ })
+
+ it('fails when CSRF cookies are missing', () => {
+ cy.clearCookies()
+
+ cy.get('input[name="identifier"]').type('i-do-not-exist')
+ cy.get('input[name="password"]').type('invalid-password')
+
+ cy.get('button[type="submit"]').click()
+
+ // FIXME https://github.com/ory/kratos/issues/91
+ cy.get('html').should('contain.text', 'CSRF token is missing or invalid')
+ })
+
+ describe('shows validation errors when invalid signup data is used', () => {
+ it('should show an error when the identifier is missing', () => {
+ cy.get('button[type="submit"]').click()
+ cy.get('.form-errors .message').should(
+ 'contain.text',
+ 'missing properties: identifier'
+ )
+ })
+
+ it('should show an error when the password is missing', () => {
+ const identity = gen.email()
+ cy.get('input[name="identifier"]')
+ .type(identity)
+ .should('have.value', identity)
+
+ cy.get('button[type="submit"]').click()
+ cy.get('.form-errors .message').should(
+ 'contain.text',
+ 'missing properties: password'
+ )
+ })
+
+ it('should show fail to sign in', () => {
+ cy.get('input[name="identifier"]').type('i-do-not-exist')
+ cy.get('input[name="password"]').type('invalid-password')
+
+ cy.get('button[type="submit"]').click()
+ cy.get('.form-errors .message').should(
+ 'contain.text',
+ 'credentials are invalid'
+ )
+ })
+ })
+})
diff --git a/cypress/integration/profiles/email/login/success.spec.js b/cypress/integration/profiles/email/login/success.spec.js
new file mode 100644
index 000000000000..186371cfedd1
--- /dev/null
+++ b/cypress/integration/profiles/email/login/success.spec.js
@@ -0,0 +1,32 @@
+import { APP_URL, gen, password, website } from '../../../../helpers'
+
+context('Login', () => {
+ const email = gen.email()
+ const password = gen.password()
+
+ before(() => {
+ cy.register({ email, password, fields: { 'traits.website': website } })
+ })
+
+ beforeEach(() => {
+ cy.clearCookies()
+ cy.visit(APP_URL + '/auth/login')
+ })
+
+ it('should sign up and be logged in', () => {
+ cy.get('input[name="identifier"]').type(email)
+ cy.get('input[name="password"]').type(password)
+ cy.get('button[type="submit"]').click()
+
+ cy.session().should((session) => {
+ const { identity } = session
+ expect(identity.id).to.not.be.empty
+ expect(identity.traits_schema_id).to.equal('default')
+ expect(identity.traits_schema_url).to.equal(
+ `${APP_URL}/.ory/kratos/public/schemas/default`
+ )
+ expect(identity.traits.website).to.equal(website)
+ expect(identity.traits.email).to.equal(email)
+ })
+ })
+})
diff --git a/cypress/integration/profiles/email/login/ui.spec.js b/cypress/integration/profiles/email/login/ui.spec.js
new file mode 100644
index 000000000000..5acaae14d1e2
--- /dev/null
+++ b/cypress/integration/profiles/email/login/ui.spec.js
@@ -0,0 +1,15 @@
+import { APP_URL } from '../../../../helpers'
+
+context('Login', () => {
+ beforeEach(() => {
+ cy.visit(APP_URL + '/auth/login')
+ })
+
+ describe('use ui elements', () => {
+ it('clicks the log in link', () => {
+ cy.get('a[href*="auth/registration"]').click()
+ cy.location('pathname').should('include', 'auth/registration')
+ cy.location('search').should('not.be.empty', 'request')
+ })
+ })
+})
diff --git a/cypress/integration/profiles/email/logout/success.spec.js b/cypress/integration/profiles/email/logout/success.spec.js
new file mode 100644
index 000000000000..ff6dab50863c
--- /dev/null
+++ b/cypress/integration/profiles/email/logout/success.spec.js
@@ -0,0 +1,22 @@
+import { APP_URL, gen, password, website } from '../../../../helpers'
+
+context('Login', () => {
+ const email = gen.email()
+ const password = gen.password()
+
+ before(() => {
+ cy.register({ email, password, fields: { 'traits.website': website } })
+ })
+
+ beforeEach(() => {
+ cy.visit(APP_URL + '/')
+ })
+
+ it('should sign out and be able to sign in again', () => {
+ cy.get('a[href*="logout"]').click()
+
+ cy.noSession()
+
+ cy.url().should('include', '/auth/login')
+ })
+})
diff --git a/cypress/integration/profiles/email/registration/errors.spec.js b/cypress/integration/profiles/email/registration/errors.spec.js
new file mode 100644
index 000000000000..89aeb44530d0
--- /dev/null
+++ b/cypress/integration/profiles/email/registration/errors.spec.js
@@ -0,0 +1,104 @@
+import { APP_URL, gen } from '../../../../helpers'
+
+context('Registration', () => {
+ beforeEach(() => {
+ cy.visit(APP_URL + '/auth/registration')
+ })
+
+ const identity = gen.email()
+ const password = gen.password()
+
+ it('fails when CSRF cookies are missing', () => {
+ cy.clearCookies()
+
+ cy.get('input[name="traits.website"]').type('https://www.ory.sh')
+ cy.get('input[name="traits.email"]')
+ .type(identity)
+ .should('have.value', identity)
+ cy.get('input[name="password"]')
+ .type('123456')
+ .should('have.value', '123456')
+
+ cy.get('button[type="submit"]').click()
+
+ // FIXME https://github.com/ory/kratos/issues/91
+ cy.get('html').should('contain.text', 'CSRF token is missing or invalid')
+ })
+
+ describe('show errors when invalid signup data is used', () => {
+ it('should show an error when the password has leaked before', () => {
+ cy.get('input[name="traits.website"]').type('https://www.ory.sh')
+ cy.get('input[name="traits.email"]')
+ .type(identity)
+ .should('have.value', identity)
+ cy.get('input[name="password"]')
+ .type('123456')
+ .should('have.value', '123456')
+
+ cy.get('button[type="submit"]').click()
+ cy.get('.form-errors .message').should('contain.text', 'data breaches')
+ })
+
+ it('should show an error when the password is to similar', () => {
+ cy.get('input[name="traits.website"]').type('https://www.ory.sh')
+ cy.get('input[name="traits.email"]').type(identity)
+ cy.get('input[name="password"]').type(identity)
+
+ cy.get('button[type="submit"]').click()
+ cy.get('.form-errors .message').should('contain.text', 'to similar')
+ })
+
+ it('should show an error when the password is empty', () => {
+ cy.get('input[name="traits.website"]').type('https://www.ory.sh')
+ cy.get('input[name="traits.email"]').type(identity)
+
+ cy.get('button[type="submit"]').click()
+ cy.get('.form-errors .message').should('contain.text', 'missing')
+ })
+
+ it('should show an error when the email is empty', () => {
+ cy.get('input[name="traits.website"]').type('https://www.ory.sh')
+ cy.get('input[name="password"]').type(password)
+
+ cy.get('button[type="submit"]').click()
+ cy.get('.form-errors .message').should('contain.text', 'valid "email"')
+ })
+
+ it('should show an error when the email is not an email', () => {
+ cy.get('input[name="traits.website"]').type('https://www.ory.sh')
+ cy.get('input[name="password"]').type('not-an-email')
+
+ cy.get('button[type="submit"]').click()
+ cy.get('.form-errors .message').should('contain.text', 'valid "email"')
+ })
+
+ it('should show a missing indicator if no fields are set', () => {
+ cy.get('button[type="submit"]').click()
+ cy.get('.form-errors .message').should(
+ 'contain.text',
+ 'missing properties'
+ )
+ })
+
+ it('should show an error when the website is not a valid URI', () => {
+ cy.get('input[name="traits.website"]')
+ .type('1234')
+ .then(($input) => {
+ expect($input[0].validationMessage).to.contain('URL')
+ })
+ })
+
+ it('should show an error when the website is too short', () => {
+ cy.get('input[name="traits.website"]').type('http://s')
+
+ // fixme https://github.com/ory/kratos/issues/368
+ cy.get('input[name="password"]').type(password)
+
+ cy.get('button[type="submit"]').click()
+ cy.get('.form-errors .message').should(
+ 'contain.text',
+ 'length must be >= 10'
+ )
+ })
+ })
+})
diff --git a/cypress/integration/profiles/email/registration/success.spec.js b/cypress/integration/profiles/email/registration/success.spec.js
new file mode 100644
index 000000000000..74430d7dd0e6
--- /dev/null
+++ b/cypress/integration/profiles/email/registration/success.spec.js
@@ -0,0 +1,32 @@
+import { APP_URL, gen } from '../../../../helpers'
+
+context('Registration', () => {
+ beforeEach(() => {
+ cy.visit(APP_URL + '/auth/registration')
+ })
+
+ it('should sign up and be logged in', () => {
+ const email = gen.email()
+ const password = gen.password()
+ const website = 'https://www.ory.sh/'
+ cy.get('input[name="traits.email"]').type(email)
+ cy.get('input[name="traits.website').type(website)
+ cy.get('input[name="password"]').type(password)
+
+ cy.get('button[type="submit"]').click()
+ cy.get('pre').should('contain.text', email)
+ cy.get('.greeting').should('contain.text', 'Welcome back')
+
+ cy.session().should((session) => {
+ const { identity } = session
+ expect(identity.id).to.not.be.empty
+ expect(identity.addresses).to.be.undefined
+ expect(identity.traits_schema_id).to.equal('default')
+ expect(identity.traits_schema_url).to.equal(
+ `${APP_URL}/.ory/kratos/public/schemas/default`
+ )
+ expect(identity.traits.website).to.equal(website)
+ expect(identity.traits.email).to.equal(email)
+ })
+ })
+})
diff --git a/cypress/integration/profiles/email/registration/ui.spec.js b/cypress/integration/profiles/email/registration/ui.spec.js
new file mode 100644
index 000000000000..a7559dd295cf
--- /dev/null
+++ b/cypress/integration/profiles/email/registration/ui.spec.js
@@ -0,0 +1,24 @@
+import { APP_URL } from '../../../../helpers'
+
+context('Registration', () => {
+ beforeEach(() => {
+ cy.visit(APP_URL + '/auth/registration')
+ })
+
+ describe('use ui elements', () => {
+ it('clicks the visibility toggle to show the password', () => {
+ cy.get('input[name="password"]').type('some password')
+ cy.get('input[name="password"]').should('have.prop', 'type', 'password')
+ cy.get('.password-visibility-toggle').click()
+ cy.get('input[name="password"]').should('have.prop', 'type', 'text')
+ cy.get('.password-visibility-toggle').click()
+ cy.get('input[name="password"]').should('have.prop', 'type', 'password')
+ })
+
+ it('clicks the log in link', () => {
+ cy.get('a[href*="auth/login"]').click()
+ cy.location('pathname').should('include', 'auth/login')
+ cy.location('search').should('not.be.empty', 'request')
+ })
+ })
+})
diff --git a/cypress/integration/profiles/email/settings/errors.spec.js b/cypress/integration/profiles/email/settings/errors.spec.js
new file mode 100644
index 000000000000..4afb8ee69ef4
--- /dev/null
+++ b/cypress/integration/profiles/email/settings/errors.spec.js
@@ -0,0 +1,198 @@
+// TODO: implement wrong credentials when reauthing
+// TODO: implement other account when reauthing
+import {
+ APP_URL,
+ gen,
+ password,
+ privilegedLifespan,
+ website,
+} from '../../../../helpers'
+
+context('Settings', () => {
+ let email = gen.email()
+ let password = gen.password()
+
+ const emailSecond = `second-${gen.email()}`
+ const passwordSecond = gen.password()
+
+ const up = (value) => `not-${value}`
+ const down = (value) => value.replace(/not-/, '')
+
+ before(() => {
+ cy.register({
+ email: emailSecond,
+ password: passwordSecond,
+ fields: { 'traits.website': 'https://github.com/ory/kratos' },
+ })
+ cy.clearCookies()
+ cy.register({ email, password, fields: { 'traits.website': website } })
+ })
+
+ beforeEach(() => {
+ cy.clearCookies()
+ cy.login({ email, password })
+ cy.visit(APP_URL + '/settings')
+ })
+
+ it('fails when CSRF cookies are missing', () => {
+ cy.clearCookies()
+
+ cy.get('#user-profile button[type="submit"]').click()
+
+ // FIXME https://github.com/ory/kratos/issues/91
+ cy.get('html').should('contain.text', 'CSRF token is missing or invalid')
+ })
+
+ describe('profile', () => {
+ it('fails with validation errors', () => {
+ cy.get('#user-profile input[name="traits.website"]')
+ .clear()
+ .type('http://s')
+ cy.get('#user-profile button[type="submit"]').click()
+ cy.get('#user-profile .form-errors .message').should(
+ 'contain.text',
+ 'length must be >= 10'
+ )
+ })
+
+ it('fails because reauth is another person', () => {
+ cy.get('#user-profile input[name="traits.email"]').clear().type(up(email))
+ cy.waitForPrivilegedSessionToExpire() // wait for the privileged session to time out
+ cy.get('#user-profile button[type="submit"]').click()
+
+ cy.reauth({
+ expect: { email },
+ type: { email: emailSecond, password: passwordSecond },
+ })
+
+ cy.get('pre code').should(
+ 'contain.text',
+ 'You must restart the flow because the resumable session was initiated by another person.'
+ )
+
+ // Try to log in with updated credentials -> should fail
+ cy.clearCookies()
+ cy.login({ email: up(email), password, expectSession: false })
+ })
+
+ it('does not update data because resumable session was removed', () => {
+ cy.get('#user-profile input[name="traits.email"]').clear().type(up(email))
+ cy.waitForPrivilegedSessionToExpire() // wait for the privileged session to time out
+ cy.get('#user-profile button[type="submit"]').click()
+
+ cy.clearCookies()
+ cy.login({ email, password })
+
+ cy.session().should((session) => {
+ const { identity } = session
+ expect(identity.traits.email).to.equal(email)
+ })
+ })
+
+ it('does not update without re-auth', () => {
+ cy.get('#user-profile input[name="traits.email"]').clear().type(up(email))
+ cy.waitForPrivilegedSessionToExpire() // wait for the privileged session to time out
+ cy.get('#user-profile button[type="submit"]').click()
+
+ cy.visit(APP_URL + '/')
+
+ cy.session().should((session) => {
+ const { identity } = session
+ expect(identity.traits.email).to.equal(email)
+ })
+ })
+
+ it('does not resume another failed request', () => {
+ // checks here that we're checking settingsRequest.id == cookie.stored.id
+ cy.get('#user-profile input[name="traits.email"]').clear().type(up(email))
+ cy.waitForPrivilegedSessionToExpire() // wait for the privileged session to time out
+ cy.get('#user-profile button[type="submit"]').click()
+
+ cy.visit(APP_URL + '/settings')
+ cy.get('#user-profile input[name="traits.website"]')
+ .clear()
+ .type('http://github.com/aeneasr')
+ cy.get('#user-profile button[type="submit"]').click()
+
+ cy.session().should((session) => {
+ const { identity } = session
+ expect(identity.traits.email).to.equal(email) // this is NOT up(email)
+ expect(identity.traits.website).to.equal('http://github.com/aeneasr') // this is NOT up(email)
+ })
+ })
+ })
+
+ describe('password', () => {
+ it('fails if password policy is violated', () => {
+ cy.get('#user-password input[name="password"]').clear().type('123456')
+ cy.get('#user-password button[type="submit"]').click()
+ cy.get('#user-password .form-errors .message').should(
+ 'contain.text',
+ 'data breaches'
+ )
+ })
+
+ it('fails because reauth is another person', () => {
+ cy.get('#user-password input[name="password"]').clear().type(up(password))
+ cy.waitForPrivilegedSessionToExpire() // wait for the privileged session to time out
+ cy.get('#user-password button[type="submit"]').click()
+
+ cy.reauth({
+ expect: { email },
+ type: { email: emailSecond, password: passwordSecond },
+ })
+
+ cy.get('pre code').should(
+ 'contain.text',
+ 'You must restart the flow because the resumable session was initiated by another person.'
+ )
+
+ // Try to log in with updated credentials -> should fail
+ cy.clearCookies()
+ cy.login({ email, password: up(password), expectSession: false })
+ })
+
+ it('does not update without re-auth', () => {
+ cy.get('#user-password input[name="password"]').clear().type(up(password))
+ cy.waitForPrivilegedSessionToExpire() // wait for the privileged session to time out
+ cy.get('#user-password button[type="submit"]').click()
+
+ cy.visit(APP_URL + '/')
+ cy.clearCookies()
+ cy.login({ email, password: up(password), expectSession: false })
+ })
+
+ it('does not update data because resumable session was removed', () => {
+ cy.get('#user-password input[name="password"]').clear().type(up(password))
+ cy.waitForPrivilegedSessionToExpire() // wait for the privileged session to time out
+ cy.get('#user-password button[type="submit"]').click()
+
+ cy.clearCookies()
+ cy.login({ email, password })
+ cy.clearCookies()
+ cy.login({ email, password: up(password), expectSession: false })
+ })
+
+ it('does not resume another queued request', () => {
+ // checks here that we're checking settingsRequest.id == cookie.stored.id
+ cy.get('#user-password input[name="password"]')
+ .clear()
+ .type(up(up(password)))
+ cy.waitForPrivilegedSessionToExpire() // wait for the privileged session to time out
+ cy.get('#user-password button[type="submit"]').click()
+
+ password = up(password)
+ cy.visit(APP_URL + '/settings')
+ cy.get('#user-password input[name="password"]').clear().type(password)
+ cy.get('#user-password button[type="submit"]').click()
+
+ cy.reauth({ expect: { email }, type: { password: down(password) } })
+
+ cy.clearCookies()
+ cy.login({ email, password })
+
+ cy.clearCookies()
+ cy.login({ email, password: up(password), expectSession: false })
+ })
+ })
+})
diff --git a/cypress/integration/profiles/email/settings/success.spec.js b/cypress/integration/profiles/email/settings/success.spec.js
new file mode 100644
index 000000000000..1652d274389a
--- /dev/null
+++ b/cypress/integration/profiles/email/settings/success.spec.js
@@ -0,0 +1,134 @@
+import {
+ APP_URL,
+ gen,
+ password,
+ privilegedLifespan,
+ website,
+} from '../../../../helpers'
+
+context('Settings', () => {
+ let email = gen.email()
+ let password = gen.password()
+
+ const up = (value) => `not-${value}`
+ const down = (value) => value.replace(/not-/, '')
+
+ before(() => {
+ cy.register({ email, password, fields: { 'traits.website': website } })
+ })
+
+ beforeEach(() => {
+ cy.clearCookies()
+ cy.login({ email, password })
+ cy.visit(APP_URL + '/settings')
+ })
+
+ it('shows all settings forms', () => {
+ cy.get('#user-profile h3').should('contain.text', 'Profile')
+ cy.get('#user-profile input[name="traits.email"]').should(
+ 'contain.value',
+ email
+ )
+ cy.get('#user-profile input[name="traits.website"]').should(
+ 'contain.value',
+ website
+ )
+
+ cy.get('#user-password h3').should('contain.text', 'Password')
+ cy.get('#user-password input[name="password"]').should('be.empty')
+ })
+
+ describe('password', () => {
+ it('modifies the password with privileged session', () => {
+ password = up(password)
+ cy.get('#user-password input[name="password"]').clear().type(password)
+ cy.get('#user-password button[type="submit"]').click()
+ cy.get('.container').should(
+ 'contain.text',
+ 'Your changes have been saved!'
+ )
+ cy.get('#user-password input[name="password"]').should('be.empty')
+ })
+
+ it('is unable to log in with the old password', () => {
+ cy.clearCookies()
+ cy.login({
+ email: email,
+ password: down(password),
+ expectSession: false,
+ })
+ })
+
+ it('modifies the password with an unprivileged session', () => {
+ password = up(password)
+ cy.get('#user-password input[name="password"]').clear().type(password)
+ cy.waitForPrivilegedSessionToExpire() // wait for the privileged session to time out
+ cy.get('#user-password button[type="submit"]').click()
+
+ cy.reauth({ expect: { email }, type: { password: down(password) } })
+
+ cy.url().should('include', '/settings')
+ cy.get('.container').should(
+ 'contain.text',
+ 'Your changes have been saved!'
+ )
+ cy.get('#user-password input[name="password"]').should('be.empty')
+ })
+ })
+
+ describe('profile', () => {
+ it('modifies an unprotected trait', () => {
+ cy.get('#user-profile input[name="traits.website"]')
+ .clear()
+ .type('https://github.com/ory')
+ cy.get('#user-profile button[type="submit"]').click()
+ cy.get('.container').should(
+ 'contain.text',
+ 'Your changes have been saved!'
+ )
+ cy.get('#user-profile input[name="traits.website"]').should(
+ 'contain.value',
+ 'https://github.com/ory'
+ )
+ })
+
+ it('modifies a protected trait with privileged session', () => {
+ email = up(email)
+ cy.get('#user-profile input[name="traits.email"]').clear().type(email)
+ cy.get('#user-profile button[type="submit"]').click()
+ cy.get('.container').should(
+ 'contain.text',
+ 'Your changes have been saved!'
+ )
+ cy.get('#user-profile input[name="traits.email"]').should(
+ 'contain.value',
+ email
+ )
+ })
+
+ it('is unable to log in with the old email', () => {
+ cy.clearCookies()
+ cy.visit(APP_URL + '/auth/login')
+ cy.login({ email: down(email), password, expectSession: false })
+ })
+
+ it('modifies a protected trait with unprivileged session', () => {
+ email = up(email)
+ cy.get('#user-profile input[name="traits.email"]').clear().type(email)
+ cy.waitForPrivilegedSessionToExpire() // wait for the privileged session to time out
+ cy.get('#user-profile button[type="submit"]').click()
+
+ cy.reauth({ expect: { email: down(email) }, type: { password } })
+
+ cy.url().should('include', '/settings')
+ cy.get('.container').should(
+ 'contain.text',
+ 'Your changes have been saved!'
+ )
+ cy.get('#user-profile input[name="traits.email"]').should(
+ 'contain.value',
+ email
+ )
+ })
+ })
+})
diff --git a/cypress/integration/profiles/email/settings/ui.spec.js b/cypress/integration/profiles/email/settings/ui.spec.js
new file mode 100644
index 000000000000..efb6ee00b4cf
--- /dev/null
+++ b/cypress/integration/profiles/email/settings/ui.spec.js
@@ -0,0 +1,13 @@
+context('Settings', () => {
+ beforeEach(() => {
+ cy.register({ fields: { 'traits.website': 'https://www.ory.sh/' } })
+ })
+
+ describe('use ui elements', () => {
+ it('clicks the settings link', () => {
+ cy.get('a[href*="settings"]').click()
+ cy.location('pathname').should('include', 'settings')
+ cy.location('search').should('not.be.empty', 'request')
+ })
+ })
+})
diff --git a/cypress/integration/profiles/verify/registration/errors.spec.js b/cypress/integration/profiles/verify/registration/errors.spec.js
new file mode 100644
index 000000000000..7836cd74ef95
--- /dev/null
+++ b/cypress/integration/profiles/verify/registration/errors.spec.js
@@ -0,0 +1,47 @@
+import {
+ APP_URL,
+ assertAddress,
+ gen,
+ parseHtml,
+ verifyHrefPattern,
+} from '../../../../helpers'
+
+context('Registration', () => {
+ describe('error flow', () => {
+ let identity
+ beforeEach(() => {
+ cy.visit(APP_URL + '/auth/registration')
+ cy.deleteMail()
+
+ identity = gen.identity()
+ cy.register(identity)
+ cy.login(identity)
+ })
+
+ it('is unable to verify the email address if the code is no longer valid and resend the code', () => {
+ cy.verifyEmailButExpired({ expect: { email: identity.email } })
+
+ cy.get('input[name="to_verify"]').should('be.empty')
+ cy.get('input[name="to_verify"]').type(identity.email)
+ cy.get('button[type="submit"]').click()
+
+ cy.location('pathname').should('eq', '/')
+
+ cy.verifyEmail({ expect: { email: identity.email } })
+ })
+
+ it('is unable to verify the email address if the code is incorrect', () => {
+ cy.getMail().then((mail) => {
+ const link = parseHtml(mail.body).querySelector('a')
+
+ expect(verifyHrefPattern.test(link.href)).to.be.true
+
+ cy.visit(link.href + '-not') // add random stuff to the confirm challenge
+ cy.log(link.href)
+ cy.session().then(
+ assertAddress({ isVerified: false, email: identity.email })
+ )
+ })
+ })
+ })
+})
diff --git a/cypress/integration/profiles/verify/registration/success.spec.js b/cypress/integration/profiles/verify/registration/success.spec.js
new file mode 100644
index 000000000000..d037a49780db
--- /dev/null
+++ b/cypress/integration/profiles/verify/registration/success.spec.js
@@ -0,0 +1,32 @@
+import { APP_URL, assertAddress, gen } from '../../../../helpers'
+
+context('Registration', () => {
+ describe('successful flow', () => {
+ beforeEach(() => {
+ cy.visit(APP_URL + '/auth/registration')
+ cy.deleteMail()
+ })
+
+ afterEach(() => {
+ cy.deleteMail()
+ })
+
+ const up = (value) => `up-${value}`
+ const { email, password } = gen.identity()
+ it('is able to verify the email address after sign up', () => {
+ cy.register({ email, password })
+ cy.login({ email, password })
+ cy.session().then(assertAddress({ isVerified: false, email }))
+
+ cy.verifyEmail({ expect: { email } })
+ })
+
+ xit('sends the warning email on double sign up', () => {
+ // FIXME https://github.com/ory/kratos/issues/133
+ cy.clearCookies()
+ cy.register({ email, password: up(password) })
+
+ cy.verifyEmail({ expect: { email } })
+ })
+ })
+})
diff --git a/cypress/integration/profiles/verify/settings/error.spec.js b/cypress/integration/profiles/verify/settings/error.spec.js
new file mode 100644
index 000000000000..dcfdb74a0523
--- /dev/null
+++ b/cypress/integration/profiles/verify/settings/error.spec.js
@@ -0,0 +1,52 @@
+import {
+ APP_URL,
+ assertAddress,
+ gen,
+ parseHtml,
+ verifyHrefPattern,
+} from '../../../../helpers'
+
+context('Settings', () => {
+ describe('error flow', () => {
+ let identity
+ before(() => {
+ cy.deleteMail()
+ })
+
+ beforeEach(() => {
+ identity = gen.identity()
+ cy.register(identity)
+ cy.deleteMail({ atLeast: 1 }) // clean up registration email
+
+ cy.login(identity)
+ cy.visit(APP_URL + '/settings')
+ })
+
+ it('is unable to verify the email address if the code is no longer valid', () => {
+ const email = `not-${identity.email}`
+ cy.get('#user-profile input[name="traits.email"]').clear().type(email)
+ cy.get('#user-profile button[type="submit"]').click()
+ cy.verifyEmailButExpired({ expect: { email } })
+ })
+
+ it('is unable to verify the email address if the code is incorrect', () => {
+ const email = `not-${identity.email}`
+ cy.get('#user-profile input[name="traits.email"]').clear().type(email)
+ cy.get('#user-profile button[type="submit"]').click()
+
+ cy.getMail().then((mail) => {
+ const link = parseHtml(mail.body).querySelector('a')
+
+ expect(verifyHrefPattern.test(link.href)).to.be.true
+
+ cy.visit(link.href + '-not') // add random stuff to the confirm challenge
+ cy.log(link.href)
+ cy.session().then(assertAddress({ isVerified: false, email }))
+ })
+ })
+
+ xit('should not update the traits until the email has been verified and the old email has accepted the change', () => {
+ // FIXME https://github.com/ory/kratos/issues/292
+ })
+ })
+})
diff --git a/cypress/integration/profiles/verify/settings/success.spec.js b/cypress/integration/profiles/verify/settings/success.spec.js
new file mode 100644
index 000000000000..d820dfdc21d6
--- /dev/null
+++ b/cypress/integration/profiles/verify/settings/success.spec.js
@@ -0,0 +1,43 @@
+import { APP_URL, assertAddress, gen } from '../../../../helpers'
+
+context('Settings', () => {
+ describe('successful flow', () => {
+ let identity
+
+ before(() => {
+ cy.deleteMail()
+ })
+
+ beforeEach(() => {
+ identity = gen.identity()
+ cy.register(identity)
+ cy.deleteMail({ atLeast: 1 }) // clean up registration email
+
+ cy.login(identity)
+ cy.visit(APP_URL + '/settings')
+ })
+
+ it('should update the verify address and request a verification email', () => {
+ const email = `not-${identity.email}`
+ cy.get('#user-profile input[name="traits.email"]').clear().type(email)
+ cy.get('#user-profile button[type="submit"]').click()
+ cy.get('.container').should(
+ 'contain.text',
+ 'Your changes have been saved!'
+ )
+ cy.get('#user-profile input[name="traits.email"]').should(
+ 'contain.value',
+ email
+ )
+ cy.session().then(assertAddress({ isVerified: false, email }))
+
+ cy.verifyEmail({ expect: { email } })
+
+ cy.location('pathname').should('eq', '/')
+ })
+
+ xit('should should be able to allow or deny (and revert?) the address change', () => {
+ // FIXME https://github.com/ory/kratos/issues/292
+ })
+ })
+})
diff --git a/cypress/integration/profiles/verify/verify/errors.spec.js b/cypress/integration/profiles/verify/verify/errors.spec.js
new file mode 100644
index 000000000000..e3b9b894a7dc
--- /dev/null
+++ b/cypress/integration/profiles/verify/verify/errors.spec.js
@@ -0,0 +1,53 @@
+import {
+ APP_URL,
+ assertAddress,
+ gen,
+ parseHtml,
+ verifyHrefPattern,
+} from '../../../../helpers'
+
+context('Verify', () => {
+ describe('error flow', () => {
+ let identity
+ before(() => {
+ cy.deleteMail()
+ })
+
+ beforeEach(() => {
+ identity = gen.identity()
+ cy.register(identity)
+ cy.deleteMail({ atLeast: 1 }) // clean up registration email
+
+ cy.login(identity)
+ cy.visit(APP_URL + '/verify')
+ })
+
+ it('is unable to verify the email address if the code is expired', () => {
+ cy.get('input[name="to_verify"]').type(identity.email)
+ cy.get('button[type="submit"]').click()
+
+ cy.location('pathname').should('eq', '/')
+
+ cy.verifyEmailButExpired({ expect: { email: identity.email } })
+ })
+
+ it('is unable to verify the email address if the code is incorrect', () => {
+ cy.get('input[name="to_verify"]').type(identity.email)
+ cy.get('button[type="submit"]').click()
+
+ cy.location('pathname').should('eq', '/')
+
+ cy.getMail().then((mail) => {
+ const link = parseHtml(mail.body).querySelector('a')
+
+ expect(verifyHrefPattern.test(link.href)).to.be.true
+
+ cy.visit(link.href + '-not') // add random stuff to the confirm challenge
+ cy.log(link.href)
+ cy.session().then(
+ assertAddress({ isVerified: false, email: identity.email })
+ )
+ })
+ })
+ })
+})
diff --git a/cypress/integration/profiles/verify/verify/success.spec.js b/cypress/integration/profiles/verify/verify/success.spec.js
new file mode 100644
index 000000000000..f84869f6703a
--- /dev/null
+++ b/cypress/integration/profiles/verify/verify/success.spec.js
@@ -0,0 +1,52 @@
+import { APP_URL, assertAddress, gen } from '../../../../helpers'
+
+context('Verify', () => {
+ describe('successful flow', () => {
+ let identity
+
+ before(() => {
+ cy.deleteMail()
+ })
+
+ beforeEach(() => {
+ identity = gen.identity()
+ cy.register(identity)
+ cy.deleteMail({ atLeast: 1 }) // clean up registration email
+
+ cy.login(identity)
+ cy.visit(APP_URL + '/verify')
+ })
+
+ it('should request verification and receive an email and verify it', () => {
+ cy.get('input[name="to_verify"]').type(identity.email)
+ cy.get('button[type="submit"]').click()
+
+ cy.location('pathname').should('eq', '/')
+
+ cy.verifyEmail({ expect: { email: identity.email } })
+
+ cy.location('pathname').should('eq', '/')
+ })
+
+ it('should request verification for an email that does not exist yet', () => {
+ const email = `not-${identity.email}`
+ cy.get('input[name="to_verify"]').type(email)
+ cy.get('button[type="submit"]').click()
+
+ cy.getMail().should((message) => {
+ expect(message.subject.trim()).to.equal(
+ 'Someone tried to verify this email address'
+ )
+ expect(message.fromAddress.trim()).to.equal('no-reply@ory.kratos.sh')
+ expect(message.toAddresses).to.have.length(1)
+ expect(message.toAddresses[0].trim()).to.equal(email)
+ })
+
+ cy.session().then(
+ assertAddress({ isVerified: false, email: identity.email })
+ )
+
+ cy.location('pathname').should('eq', '/')
+ })
+ })
+})
diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js
new file mode 100644
index 000000000000..aa9918d21530
--- /dev/null
+++ b/cypress/plugins/index.js
@@ -0,0 +1,21 @@
+///
+// ***********************************************************
+// This example plugins/index.js can be used to load plugins
+//
+// You can change the location of this file or turn off loading
+// the plugins file with the 'pluginsFile' configuration option.
+//
+// You can read more here:
+// https://on.cypress.io/plugins-guide
+// ***********************************************************
+
+// This function is called when a project is opened or re-opened (e.g. due to
+// the project's config changing)
+
+/**
+ * @type {Cypress.PluginConfig}
+ */
+module.exports = (on, config) => {
+ // `on` is used to hook into various events Cypress emits
+ // `config` is the resolved Cypress config
+}
diff --git a/cypress/support/commands.js b/cypress/support/commands.js
new file mode 100644
index 000000000000..67458ff9c497
--- /dev/null
+++ b/cypress/support/commands.js
@@ -0,0 +1,224 @@
+// ***********************************************
+// This example commands.js shows you how to
+// create various custom commands and overwrite
+// existing commands.
+//
+// For more comprehensive examples of custom
+// commands please read more here:
+// https://on.cypress.io/custom-commands
+// ***********************************************
+//
+//
+// -- This is a parent command --
+// Cypress.Commands.add("login", (email, password) => { ... })
+//
+//
+// -- This is a child command --
+// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
+//
+//
+// -- This is a dual command --
+// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
+//
+//
+// -- This will overwrite an existing command --
+// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
+import {
+ APP_URL,
+ assertAddress,
+ gen,
+ MAIL_API,
+ parseHtml,
+ pollInterval,
+ privilegedLifespan,
+} from '../helpers'
+
+Cypress.Commands.add(
+ 'register',
+ ({ email = gen.email(), password = gen.password(), fields = {} } = {}) => {
+ cy.visit(APP_URL + '/auth/registration')
+ cy.get('input[name="traits.email"]').type(email)
+ cy.get('input[name="password"]').type(password)
+ Object.keys(fields).forEach((key) => {
+ const value = fields[key]
+ cy.get(`input[name="${key}"]`).clear().type(value)
+ })
+ cy.get('button[type="submit"]').click()
+ }
+)
+
+Cypress.Commands.add('login', ({ email, password, expectSession = true }) => {
+ cy.visit(APP_URL + '/auth/login')
+ cy.get('input[name="identifier"]').clear().type(email)
+ cy.get('input[name="password"]').clear().type(password)
+ cy.get('button[type="submit"]').click()
+ if (expectSession) {
+ cy.session()
+ } else {
+ cy.noSession()
+ }
+})
+
+Cypress.Commands.add(
+ 'reauth',
+ ({
+ expect: { email },
+ type: { email: temail, password: tpassword } = {},
+ }) => {
+ cy.url().should('include', '/auth/login')
+ cy.get('input[name="identifier"]').should('have.value', email)
+ if (temail) {
+ cy.get('input[name="identifier"]').clear().type(temail)
+ }
+ if (tpassword) {
+ cy.get('input[name="password"]').clear().type(tpassword)
+ }
+ cy.get('button[type="submit"]').click()
+ }
+)
+
+Cypress.Commands.add('deleteMail', ({ atLeast = 0 } = {}) => {
+ let tries = 0
+ let count = 0
+ const req = () =>
+ cy
+ .request('DELETE', `${MAIL_API}/mail`, { pruneCode: 'all' })
+ .then(({ body }) => {
+ count += parseInt(body)
+ if (count < atLeast && tries < 100) {
+ cy.log(
+ `Expected at least ${atLeast} messages but deleteted only ${count} so far (body: ${body})`
+ )
+ tries++
+ cy.wait(pollInterval)
+ return req()
+ }
+
+ return Promise.resolve()
+ })
+
+ return req()
+})
+
+Cypress.Commands.add('session', () =>
+ cy
+ .request('GET', `${APP_URL}/.ory/kratos/public/sessions/whoami`)
+ .then((response) => {
+ expect(response.body.sid).to.not.be.empty
+ expect(
+ Cypress.moment().isBefore(Cypress.moment(response.body.expires_at))
+ ).to.be.true
+
+ // Add a grace second for MySQL which does not support millisecs.
+ expect(
+ Cypress.moment().isAfter(
+ Cypress.moment(response.body.issued_at).subtract(1, 's')
+ )
+ ).to.be.true
+ expect(
+ Cypress.moment().isAfter(
+ Cypress.moment(response.body.authenticated_at).subtract(1, 's')
+ )
+ ).to.be.true
+ expect(response.body.identity).to.exist
+ return response.body
+ })
+)
+
+Cypress.Commands.add('noSession', () =>
+ cy
+ .request({
+ method: 'GET',
+ url: `${APP_URL}/.ory/kratos/public/sessions/whoami`,
+ failOnStatusCode: false,
+ })
+ .then((request) => {
+ expect(request.status).to.eq(401)
+ return request
+ })
+)
+
+Cypress.Commands.add('verifyEmail', ({ expect: { email } = {} } = {}) =>
+ cy.getMail().then((message) => {
+ expect(message.subject.trim()).to.equal('Please verify your email address')
+ expect(message.fromAddress.trim()).to.equal('no-reply@ory.kratos.sh')
+ expect(message.toAddresses).to.have.length(1)
+ expect(message.toAddresses[0].trim()).to.equal(email)
+
+ const link = parseHtml(message.body).querySelector('a')
+ expect(link).to.not.be.null
+ expect(link.href).to.contain(APP_URL)
+
+ cy.visit(link.href)
+ cy.location('pathname').should('not.contain', 'verify')
+ cy.session().should(assertAddress({ isVerified: true, email }))
+ })
+)
+
+// Uses the verification email but waits so that it expires
+Cypress.Commands.add(
+ 'verifyEmailButExpired',
+ ({ expect: { email } = {} } = {}) =>
+ cy.getMail().then((message) => {
+ expect(message.subject.trim()).to.equal(
+ 'Please verify your email address'
+ )
+ expect(message.fromAddress.trim()).to.equal('no-reply@ory.kratos.sh')
+ expect(message.toAddresses).to.have.length(1)
+ expect(message.toAddresses[0].trim()).to.equal(email)
+
+ const link = parseHtml(message.body).querySelector('a')
+ cy.session().should((session) => {
+ assertAddress({ isVerified: false, email: email })(session)
+ cy.wait(
+ Cypress.moment(session.identity.addresses[0].expires_at).diff(
+ Cypress.moment()
+ ) + 100
+ )
+ })
+
+ cy.visit(link.href)
+ cy.location('pathname').should('include', 'verify')
+ cy.location('search').should('not.be.empty', 'request')
+ cy.get('.form-errors .message').should('contain.text', 'code has expired')
+
+ cy.session().should(assertAddress({ isVerified: false, email: email }))
+ })
+)
+
+// Uses the verification email but waits so that it expires
+Cypress.Commands.add('waitForPrivilegedSessionToExpire', () => {
+ cy.session().should((session) => {
+ expect(session.authenticated_at).to.not.be.empty
+ cy.wait(
+ Cypress.moment(session.authenticated_at)
+ .add(privilegedLifespan)
+ .diff(Cypress.moment()) + 100
+ )
+ })
+})
+
+Cypress.Commands.add('getMail', ({ removeMail = true } = {}) => {
+ let tries = 0
+ const req = () =>
+ cy.request(`${MAIL_API}/mail`).then((response) => {
+ expect(response.body).to.have.property('mailItems')
+ const count = response.body.mailItems.length
+ if (count === 0 && tries < 100) {
+ tries++
+ cy.wait(pollInterval)
+ return req()
+ }
+
+ expect(count).to.equal(1)
+ if (removeMail) {
+ return cy
+ .deleteMail({ atLeast: count })
+ .then(() => Promise.resolve(response.body.mailItems[0]))
+ }
+
+ return Promise.resolve(response.body.mailItems[0])
+ })
+
+ return req()
+})
diff --git a/cypress/support/index.js b/cypress/support/index.js
new file mode 100644
index 000000000000..d68db96df269
--- /dev/null
+++ b/cypress/support/index.js
@@ -0,0 +1,20 @@
+// ***********************************************************
+// This example support/index.js is processed and
+// loaded automatically before your test files.
+//
+// This is a great place to put global configuration and
+// behavior that modifies Cypress.
+//
+// You can change the location of this file or turn off
+// automatically serving support files with the
+// 'supportFile' configuration option.
+//
+// You can read more here:
+// https://on.cypress.io/configuration
+// ***********************************************************
+
+// Import commands.js using ES2015 syntax:
+import './commands'
+
+// Alternatively you can use CommonJS syntax:
+// require('./commands')
diff --git a/cypress/tsconfig.json b/cypress/tsconfig.json
new file mode 100644
index 000000000000..b4871a1ab2cc
--- /dev/null
+++ b/cypress/tsconfig.json
@@ -0,0 +1,12 @@
+{
+ "compilerOptions": {
+ "strict": true,
+ "baseUrl": "../node_modules",
+ "target": "es5",
+ "lib": ["es5", "dom"],
+ "types": ["cypress"]
+ },
+ "include": [
+ "**/*.ts"
+ ]
+}
diff --git a/go.mod b/go.mod
index 3038e01c1ee3..951789f79151 100644
--- a/go.mod
+++ b/go.mod
@@ -40,8 +40,10 @@ require (
github.com/leodido/go-urn v1.1.0 // indirect
github.com/markbates/pkger v0.12.8
github.com/mattn/goveralls v0.0.5
+ github.com/mikefarah/yq v1.15.0
github.com/mitchellh/go-homedir v1.1.0
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
+ github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 // indirect
github.com/ory/analytics-go/v4 v4.0.0
github.com/ory/dockertest v3.3.5+incompatible
github.com/ory/go-acc v0.1.0
@@ -49,7 +51,8 @@ require (
github.com/ory/graceful v0.1.1
github.com/ory/herodot v0.8.2
github.com/ory/jsonschema/v3 v3.0.1
- github.com/ory/sdk/swagutil v0.0.0-20200430101046-db494fac5eb6
+ github.com/ory/mail/v3 v3.0.0
+ github.com/ory/sdk/swagutil v0.0.0-20200425113349-92ce176f501e
github.com/ory/viper v1.7.4
github.com/ory/x v0.0.116
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2
@@ -67,5 +70,4 @@ require (
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
golang.org/x/tools v0.0.0-20200331202046-9d5940d49312
gopkg.in/go-playground/validator.v9 v9.28.0
- gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
)
diff --git a/go.sum b/go.sum
index 8dda51496493..b75d933ea0ed 100644
--- a/go.sum
+++ b/go.sum
@@ -827,6 +827,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/microcosm-cc/bluemonday v1.0.2 h1:5lPfLTTAvAbtS0VqT+94yOtFnGfUWYyx0+iToC3Os3s=
github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
+github.com/mikefarah/yq v1.15.0 h1:ViMYNRG5UB7hzm8olxMFqPtkpMXXKO4g32/v9JUa62o=
+github.com/mikefarah/yq v1.15.0/go.mod h1:Klb/IuhiBF3HOsJXH3YR+xURVe3KFmm0jxwiklmdjP4=
github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
@@ -863,6 +865,8 @@ github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
github.com/onsi/gomega v1.6.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
+github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
+github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
@@ -897,8 +901,12 @@ github.com/ory/herodot v0.8.2 h1:Lq5DpT81tkcegzp1QFqVFlcDWNCcq9xIq5FQ191rI0E=
github.com/ory/herodot v0.8.2/go.mod h1:kFWnruHnnokHH4e7tbkGyHOjHGj70sJTrdiz01Xcq4Y=
github.com/ory/jsonschema/v3 v3.0.1 h1:xzV7w2rt/Qn+jvh71joIXNKKOCqqNyTlaIxdxU0IQJc=
github.com/ory/jsonschema/v3 v3.0.1/go.mod h1:jgLHekkFk0uiGdEWGleC+tOm6JSSP8cbf17PnBuGXlw=
-github.com/ory/sdk/swagutil v0.0.0-20200430101046-db494fac5eb6 h1:oKOQcB9DZy7govO5k12s9Ofwm4d4yZiMLmeMZE6QWZY=
-github.com/ory/sdk/swagutil v0.0.0-20200430101046-db494fac5eb6/go.mod h1:Ufg1eAyz+Zt3+oweSZVThG13ewewWCKwBmoNmK8Z0co=
+github.com/ory/mail v2.3.1+incompatible h1:vHntHDHtQXamt2T+iwTTlCoBkDvILUeujE9Ocwe9md4=
+github.com/ory/mail v2.3.1+incompatible/go.mod h1:87D9/1gB6ewElQoN0lXJ0ayfqcj3cW3qCTXh+5E9mfU=
+github.com/ory/mail/v3 v3.0.0 h1:8LFMRj473vGahFD/ntiotWEd4S80FKYFtiZTDfOQ+sM=
+github.com/ory/mail/v3 v3.0.0/go.mod h1:JGAVeZF8YAlxbaFDUHqRZAKBCSeW2w1vuxf28hFbZAw=
+github.com/ory/sdk/swagutil v0.0.0-20200425113349-92ce176f501e h1:X2MrG0850tnYPHyHNOCugvqcs0nfnqfmXHo3HrUFPdc=
+github.com/ory/sdk/swagutil v0.0.0-20200425113349-92ce176f501e/go.mod h1:Ufg1eAyz+Zt3+oweSZVThG13ewewWCKwBmoNmK8Z0co=
github.com/ory/viper v1.5.6/go.mod h1:TYmpFpKLxjQwvT4f0QPpkOn4sDXU1kDgAwJpgLYiQ28=
github.com/ory/viper v1.7.4 h1:3RWBt7Pq9kSFNxLaRT0ljNdbtaWisCQG1cLPn2Yd4UY=
github.com/ory/viper v1.7.4/go.mod h1:T6sodNZKNGPpashUOk7EtXz2isovz8oCd57GNVkkNmE=
@@ -1431,6 +1439,7 @@ gopkg.in/ini.v1 v1.54.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.55.0 h1:E8yzL5unfpW3M6fz/eB7Cb5MQAYSZ7GKo4Qth+N2sgQ=
gopkg.in/ini.v1 v1.55.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/mail.v2 v2.0.0-20180731213649-a0242b2233b4/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw=
+gopkg.in/mail.v2 v2.3.1/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v2 v2.1.9/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/square/go-jose.v2 v2.2.2 h1:orlkJ3myw8CN1nVQHBFfloD+L3egixIa4FvUP6RosSA=
diff --git a/package-lock.json b/package-lock.json
index 9f3356d6bf72..3bed804c263e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6,6 +6,7 @@
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
"integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==",
+ "dev": true,
"requires": {
"@babel/highlight": "^7.8.3"
}
@@ -13,12 +14,14 @@
"@babel/helper-validator-identifier": {
"version": "7.9.5",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz",
- "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g=="
+ "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==",
+ "dev": true
},
"@babel/highlight": {
"version": "7.9.0",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz",
"integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==",
+ "dev": true,
"requires": {
"@babel/helper-validator-identifier": "^7.9.0",
"chalk": "^2.0.0",
@@ -29,6 +32,7 @@
"version": "7.9.6",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.6.tgz",
"integrity": "sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ==",
+ "dev": true,
"requires": {
"regenerator-runtime": "^0.13.4"
}
@@ -115,6 +119,55 @@
}
}
},
+ "@hapi/address": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@hapi/address/-/address-4.0.1.tgz",
+ "integrity": "sha512-0oEP5UiyV4f3d6cBL8F3Z5S7iWSX39Knnl0lY8i+6gfmmIBj44JCBNtcMgwyS+5v7j3VYavNay0NFHDS+UGQcw==",
+ "dev": true,
+ "requires": {
+ "@hapi/hoek": "^9.0.0"
+ }
+ },
+ "@hapi/formula": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@hapi/formula/-/formula-2.0.0.tgz",
+ "integrity": "sha512-V87P8fv7PI0LH7LiVi8Lkf3x+KCO7pQozXRssAHNXXL9L1K+uyu4XypLXwxqVDKgyQai6qj3/KteNlrqDx4W5A==",
+ "dev": true
+ },
+ "@hapi/hoek": {
+ "version": "9.0.4",
+ "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.0.4.tgz",
+ "integrity": "sha512-EwaJS7RjoXUZ2cXXKZZxZqieGtc7RbvQhUy8FwDoMQtxWVi14tFjeFCYPZAM1mBCpOpiBpyaZbb9NeHc7eGKgw==",
+ "dev": true
+ },
+ "@hapi/joi": {
+ "version": "17.1.1",
+ "resolved": "https://registry.npmjs.org/@hapi/joi/-/joi-17.1.1.tgz",
+ "integrity": "sha512-p4DKeZAoeZW4g3u7ZeRo+vCDuSDgSvtsB/NpfjXEHTUjSeINAi/RrVOWiVQ1isaoLzMvFEhe8n5065mQq1AdQg==",
+ "dev": true,
+ "requires": {
+ "@hapi/address": "^4.0.1",
+ "@hapi/formula": "^2.0.0",
+ "@hapi/hoek": "^9.0.0",
+ "@hapi/pinpoint": "^2.0.0",
+ "@hapi/topo": "^5.0.0"
+ }
+ },
+ "@hapi/pinpoint": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@hapi/pinpoint/-/pinpoint-2.0.0.tgz",
+ "integrity": "sha512-vzXR5MY7n4XeIvLpfl3HtE3coZYO4raKXW766R6DZw/6aLqR26iuZ109K7a0NtF2Db0jxqh7xz2AxkUwpUFybw==",
+ "dev": true
+ },
+ "@hapi/topo": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.0.0.tgz",
+ "integrity": "sha512-tFJlT47db0kMqVm3H4nQYgn6Pwg10GTZHb1pwmSiv1K4ks6drQOtfEF5ZnPjkvC+y4/bUPHK+bc87QvLcL+WMw==",
+ "dev": true,
+ "requires": {
+ "@hapi/hoek": "^9.0.0"
+ }
+ },
"@samverschueren/stream-to-observable": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz",
@@ -139,7 +192,8 @@
"@types/caseless": {
"version": "0.12.2",
"resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz",
- "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w=="
+ "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==",
+ "dev": true
},
"@types/chai": {
"version": "4.2.7",
@@ -160,7 +214,8 @@
"@types/color-name": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
- "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ=="
+ "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
+ "dev": true
},
"@types/jquery": {
"version": "3.3.31",
@@ -192,17 +247,20 @@
"@types/node": {
"version": "13.13.4",
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.4.tgz",
- "integrity": "sha512-x26ur3dSXgv5AwKS0lNfbjpCakGIduWU1DU91Zz58ONRWrIKGunmZBNv4P7N+e27sJkiGDsw/3fT4AtsqQBrBA=="
+ "integrity": "sha512-x26ur3dSXgv5AwKS0lNfbjpCakGIduWU1DU91Zz58ONRWrIKGunmZBNv4P7N+e27sJkiGDsw/3fT4AtsqQBrBA==",
+ "dev": true
},
"@types/nunjucks": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/@types/nunjucks/-/nunjucks-3.1.3.tgz",
- "integrity": "sha512-42IiIIBdoB7ZDwCVhCWYT4fMCj+4TeacuVgh7xyT2du5EhkpA+OFeeDdYTFCUt1MrHb8Aw7ZqFvr8s1bwP9l8w=="
+ "integrity": "sha512-42IiIIBdoB7ZDwCVhCWYT4fMCj+4TeacuVgh7xyT2du5EhkpA+OFeeDdYTFCUt1MrHb8Aw7ZqFvr8s1bwP9l8w==",
+ "dev": true
},
"@types/request": {
"version": "2.48.4",
"resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.4.tgz",
"integrity": "sha512-W1t1MTKYR8PxICH+A4HgEIPuAC3sbljoEVfyZbeFJJDbr30guDspJri2XOaM2E+Un7ZjrihaDi7cf6fPa2tbgw==",
+ "dev": true,
"requires": {
"@types/caseless": "*",
"@types/node": "*",
@@ -214,6 +272,7 @@
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
"integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
+ "dev": true,
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.6",
@@ -247,17 +306,20 @@
"@types/tough-cookie": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.0.tgz",
- "integrity": "sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A=="
+ "integrity": "sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A==",
+ "dev": true
},
"a-sync-waterfall": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz",
- "integrity": "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA=="
+ "integrity": "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==",
+ "dev": true
},
"ajv": {
"version": "6.12.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz",
"integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==",
+ "dev": true,
"requires": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -293,6 +355,7 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
"integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==",
+ "dev": true,
"optional": true,
"requires": {
"normalize-path": "^3.0.0",
@@ -308,12 +371,14 @@
"asap": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
- "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
+ "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=",
+ "dev": true
},
"asn1": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
"integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
+ "dev": true,
"requires": {
"safer-buffer": "~2.1.0"
}
@@ -321,7 +386,8 @@
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
- "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
+ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
+ "dev": true
},
"async": {
"version": "3.2.0",
@@ -332,17 +398,20 @@
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
- "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
+ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
+ "dev": true
},
"aws-sign2": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
- "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
+ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
+ "dev": true
},
"aws4": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz",
- "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug=="
+ "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==",
+ "dev": true
},
"balanced-match": {
"version": "1.0.0",
@@ -354,6 +423,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
"integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
+ "dev": true,
"requires": {
"tweetnacl": "^0.14.3"
}
@@ -362,6 +432,7 @@
"version": "0.6.7",
"resolved": "https://registry.npmjs.org/better-ajv-errors/-/better-ajv-errors-0.6.7.tgz",
"integrity": "sha512-PYgt/sCzR4aGpyNy5+ViSQ77ognMnWq7745zM+/flYO4/Yisdtp9wDQW2IKCyVYPUxQt3E/b5GBSwfhd1LPdlg==",
+ "dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
"@babel/runtime": "^7.0.0",
@@ -376,6 +447,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz",
"integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==",
+ "dev": true,
"optional": true
},
"bluebird": {
@@ -398,6 +470,7 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
"optional": true,
"requires": {
"fill-range": "^7.0.1"
@@ -424,22 +497,26 @@
"call-me-maybe": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz",
- "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms="
+ "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=",
+ "dev": true
},
"camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
- "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true
},
"caseless": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
- "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
+ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
+ "dev": true
},
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
@@ -450,6 +527,7 @@
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
"requires": {
"color-convert": "^1.9.0"
}
@@ -458,6 +536,7 @@
"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"
}
@@ -474,6 +553,7 @@
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.0.tgz",
"integrity": "sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ==",
+ "dev": true,
"optional": true,
"requires": {
"anymatch": "~3.1.1",
@@ -548,6 +628,7 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
+ "dev": true,
"requires": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
@@ -557,12 +638,14 @@
"ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
- "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
+ "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+ "dev": true
},
"ansi-styles": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
+ "dev": true,
"requires": {
"@types/color-name": "^1.1.1",
"color-convert": "^2.0.1"
@@ -572,6 +655,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
"requires": {
"color-name": "~1.1.4"
}
@@ -579,17 +663,20 @@
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
},
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true
},
"string-width": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
"integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
+ "dev": true,
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
@@ -600,6 +687,7 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "dev": true,
"requires": {
"ansi-regex": "^5.0.0"
}
@@ -608,6 +696,7 @@
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+ "dev": true,
"requires": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
@@ -619,22 +708,26 @@
"co": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
- "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ="
+ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
+ "dev": true
},
"code-error-fragment": {
"version": "0.0.230",
"resolved": "https://registry.npmjs.org/code-error-fragment/-/code-error-fragment-0.0.230.tgz",
- "integrity": "sha512-cadkfKp6932H8UkhzE/gcUqhRMNf8jHzkAN7+5Myabswaghu4xABTgPHDCjW+dBAJxj/SpkTYokpzDqY4pCzQw=="
+ "integrity": "sha512-cadkfKp6932H8UkhzE/gcUqhRMNf8jHzkAN7+5Myabswaghu4xABTgPHDCjW+dBAJxj/SpkTYokpzDqY4pCzQw==",
+ "dev": true
},
"code-point-at": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
- "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
+ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
+ "dev": true
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
"requires": {
"color-name": "1.1.3"
}
@@ -642,12 +735,14 @@
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
},
"colorful": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/colorful/-/colorful-2.1.0.tgz",
- "integrity": "sha1-aovcvC2kKlDbO0iC+iUmOqofLY4="
+ "integrity": "sha1-aovcvC2kKlDbO0iC+iUmOqofLY4=",
+ "dev": true
},
"colors": {
"version": "1.4.0",
@@ -660,6 +755,7 @@
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dev": true,
"requires": {
"delayed-stream": "~1.0.0"
}
@@ -697,17 +793,20 @@
"core-js": {
"version": "3.6.5",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz",
- "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA=="
+ "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==",
+ "dev": true
},
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
- "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
+ "dev": true
},
"cross-spawn": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
"integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+ "dev": true,
"requires": {
"nice-try": "^1.0.4",
"path-key": "^2.0.1",
@@ -774,6 +873,7 @@
"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"
}
@@ -788,6 +888,7 @@
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
"requires": {
"ms": "^2.1.1"
}
@@ -795,17 +896,20 @@
"decamelize": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
- "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
+ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+ "dev": true
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
- "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
+ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
+ "dev": true
},
"ecc-jsbn": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
"integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
+ "dev": true,
"requires": {
"jsbn": "~0.1.0",
"safer-buffer": "^2.1.0"
@@ -820,12 +924,14 @@
"emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
},
"end-of-stream": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+ "dev": true,
"requires": {
"once": "^1.4.0"
}
@@ -833,12 +939,14 @@
"es6-promise": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz",
- "integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM="
+ "integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=",
+ "dev": true
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true
},
"eventemitter2": {
"version": "4.1.2",
@@ -850,6 +958,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
"integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
+ "dev": true,
"requires": {
"cross-spawn": "^6.0.0",
"get-stream": "^4.0.0",
@@ -878,7 +987,8 @@
"extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
- "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "dev": true
},
"extract-zip": {
"version": "1.7.0",
@@ -912,22 +1022,26 @@
"extsprintf": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
- "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
+ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
+ "dev": true
},
"fast-deep-equal": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz",
- "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA=="
+ "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==",
+ "dev": true
},
"fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
- "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
},
"fast-safe-stringify": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz",
- "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA=="
+ "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==",
+ "dev": true
},
"fd-slicer": {
"version": "1.1.0",
@@ -952,6 +1066,7 @@
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
"optional": true,
"requires": {
"to-regex-range": "^5.0.1"
@@ -961,6 +1076,7 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dev": true,
"requires": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
@@ -969,12 +1085,14 @@
"forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
- "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
+ "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
+ "dev": true
},
"form-data": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
"integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+ "dev": true,
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.6",
@@ -1002,17 +1120,20 @@
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
"integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
+ "dev": true,
"optional": true
},
"get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
- "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true
},
"get-stream": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
"integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
+ "dev": true,
"requires": {
"pump": "^3.0.0"
}
@@ -1030,6 +1151,7 @@
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
"integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+ "dev": true,
"requires": {
"assert-plus": "^1.0.0"
}
@@ -1052,6 +1174,7 @@
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
"integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
+ "dev": true,
"optional": true,
"requires": {
"is-glob": "^4.0.1"
@@ -1075,17 +1198,20 @@
"grapheme-splitter": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
- "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ=="
+ "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
+ "dev": true
},
"har-schema": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
- "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
+ "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
+ "dev": true
},
"har-validator": {
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
"integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
+ "dev": true,
"requires": {
"ajv": "^6.5.5",
"har-schema": "^2.0.0"
@@ -1103,12 +1229,14 @@
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true
},
"http-signature": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
+ "dev": true,
"requires": {
"assert-plus": "^1.0.0",
"jsprim": "^1.2.2",
@@ -1118,7 +1246,8 @@
"http2-client": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/http2-client/-/http2-client-1.3.3.tgz",
- "integrity": "sha512-nUxLymWQ9pzkzTmir24p2RtsgruLmhje7lH3hLX1IpwvyTg77fW+1brenPPP3USAR+rQ36p5sTA/x7sjCJVkAA=="
+ "integrity": "sha512-nUxLymWQ9pzkzTmir24p2RtsgruLmhje7lH3hLX1IpwvyTg77fW+1brenPPP3USAR+rQ36p5sTA/x7sjCJVkAA==",
+ "dev": true
},
"indent-string": {
"version": "3.2.0",
@@ -1151,12 +1280,14 @@
"invert-kv": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz",
- "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA=="
+ "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==",
+ "dev": true
},
"is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
"optional": true,
"requires": {
"binary-extensions": "^2.0.0"
@@ -1175,17 +1306,20 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "dev": true,
"optional": true
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
- "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
},
"is-glob": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
"integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+ "dev": true,
"optional": true,
"requires": {
"is-extglob": "^2.1.1"
@@ -1205,6 +1339,7 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
"optional": true
},
"is-observable": {
@@ -1234,12 +1369,14 @@
"is-stream": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
- "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
+ "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
+ "dev": true
},
"is-typedarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
- "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
+ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
+ "dev": true
},
"isarray": {
"version": "1.0.0",
@@ -1250,42 +1387,50 @@
"isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
},
"isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
- "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
+ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
+ "dev": true
},
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
},
"jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
- "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
+ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
+ "dev": true
},
"json-schema": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
- "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
+ "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
+ "dev": true
},
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
- "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
},
"json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
- "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
+ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
+ "dev": true
},
"json-to-ast": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/json-to-ast/-/json-to-ast-2.1.0.tgz",
"integrity": "sha512-W9Lq347r8tA1DfMvAGn9QNcgYm4Wm7Yc+k8e6vezpMnRT+NHbtlxgNBXRVjXe9YM6eTn6+p/MKOlV/aABJcSnQ==",
+ "dev": true,
"requires": {
"code-error-fragment": "0.0.230",
"grapheme-splitter": "^1.0.4"
@@ -1303,12 +1448,14 @@
"jsonpointer": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz",
- "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk="
+ "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=",
+ "dev": true
},
"jsprim": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
"integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
+ "dev": true,
"requires": {
"assert-plus": "1.0.0",
"extsprintf": "1.3.0",
@@ -1326,6 +1473,7 @@
"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"
}
@@ -1333,7 +1481,8 @@
"leven": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
- "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A=="
+ "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",
+ "dev": true
},
"listr": {
"version": "0.14.3",
@@ -1459,6 +1608,7 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dev": true,
"requires": {
"p-locate": "^4.1.0"
}
@@ -1529,6 +1679,7 @@
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz",
"integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==",
+ "dev": true,
"requires": {
"p-defer": "^1.0.0"
}
@@ -1537,6 +1688,7 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz",
"integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==",
+ "dev": true,
"requires": {
"map-age-cleaner": "^0.1.1",
"mimic-fn": "^2.0.0",
@@ -1546,19 +1698,22 @@
"mimic-fn": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
- "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "dev": true
}
}
},
"mime-db": {
"version": "1.43.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
- "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ=="
+ "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==",
+ "dev": true
},
"mime-types": {
"version": "2.1.26",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
"integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
+ "dev": true,
"requires": {
"mime-db": "1.43.0"
}
@@ -1602,17 +1757,20 @@
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
},
"nice-try": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
- "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ=="
+ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
+ "dev": true
},
"node-fetch-h2": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/node-fetch-h2/-/node-fetch-h2-2.3.0.tgz",
"integrity": "sha512-ofRW94Ab0T4AOh5Fk8t0h8OBWrmjb0SSB20xh1H8YnPV9EJ+f5AMoYSUQ2zgJ4Iq2HAK0I2l5/Nequ8YzFS3Hg==",
+ "dev": true,
"requires": {
"http2-client": "^1.2.5"
}
@@ -1621,6 +1779,7 @@
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/node-readfiles/-/node-readfiles-0.2.0.tgz",
"integrity": "sha1-271K8SE04uY1wkXvk//Pb2BnOl0=",
+ "dev": true,
"requires": {
"es6-promise": "^3.2.1"
}
@@ -1629,12 +1788,14 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
"optional": true
},
"npm-run-path": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
"integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
+ "dev": true,
"requires": {
"path-key": "^2.0.0"
}
@@ -1642,12 +1803,14 @@
"number-is-nan": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
- "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
+ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
+ "dev": true
},
"nunjucks": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.1.tgz",
"integrity": "sha512-LYlVuC1ZNSalQQkLNNPvcgPt2M9FTY9bs39mTCuFXtqh7jWbYzhDlmz2M6onPiXEhdZo+b9anRhc+uBGuJZ2bQ==",
+ "dev": true,
"requires": {
"a-sync-waterfall": "^1.0.0",
"asap": "^2.0.3",
@@ -1658,7 +1821,8 @@
"commander": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz",
- "integrity": "sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow=="
+ "integrity": "sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==",
+ "dev": true
}
}
},
@@ -1666,6 +1830,7 @@
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/oas-kit-common/-/oas-kit-common-1.0.8.tgz",
"integrity": "sha512-pJTS2+T0oGIwgjGpw7sIRU8RQMcUoKCDWFLdBqKB2BNmGpbBMH2sdqAaOXUg8OzonZHU0L7vfJu1mJFEiYDWOQ==",
+ "dev": true,
"requires": {
"fast-safe-stringify": "^2.0.7"
}
@@ -1674,6 +1839,7 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/oas-linter/-/oas-linter-3.1.2.tgz",
"integrity": "sha512-mv3HBG9aQz8PLGvonewIN9Y2Ra8QL6jvotRvf7NCdZ20n5vg4dO4y61UZh6s+KRDfJaU1PO+9Oxrn3EUN4Xygw==",
+ "dev": true,
"requires": {
"should": "^13.2.1",
"yaml": "^1.8.3"
@@ -1683,6 +1849,7 @@
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/oas-resolver/-/oas-resolver-2.3.2.tgz",
"integrity": "sha512-toGCUv8wyZZmUAAsw4jn+511xNpUFW2ZLp4sAZ7xpERIeosrbxBxtkVxot9kXvdUHtPjRafi5+bkJ56TwQeYSQ==",
+ "dev": true,
"requires": {
"node-fetch-h2": "^2.3.0",
"oas-kit-common": "^1.0.8",
@@ -1694,17 +1861,20 @@
"ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
- "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
+ "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+ "dev": true
},
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true
},
"string-width": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
"integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
+ "dev": true,
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
@@ -1715,6 +1885,7 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "dev": true,
"requires": {
"ansi-regex": "^5.0.0"
}
@@ -1723,6 +1894,7 @@
"version": "15.3.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz",
"integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==",
+ "dev": true,
"requires": {
"cliui": "^6.0.0",
"decamelize": "^1.2.0",
@@ -1742,12 +1914,14 @@
"oas-schema-walker": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/oas-schema-walker/-/oas-schema-walker-1.1.4.tgz",
- "integrity": "sha512-foVDDS0RJYMfhQEDh/WdBuCzydTcsCnGo9EeD8SpWq1uW10JXiz+8SfYVDA7LO87kjmlnTRZle/2gr5qxabaEA=="
+ "integrity": "sha512-foVDDS0RJYMfhQEDh/WdBuCzydTcsCnGo9EeD8SpWq1uW10JXiz+8SfYVDA7LO87kjmlnTRZle/2gr5qxabaEA==",
+ "dev": true
},
"oas-validator": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/oas-validator/-/oas-validator-3.4.0.tgz",
"integrity": "sha512-l/SxykuACi2U51osSsBXTxdsFc8Fw41xI7AsZkzgVgWJAzoEFaaNptt35WgY9C3757RUclsm6ye5GvSyYoozLQ==",
+ "dev": true,
"requires": {
"ajv": "^5.5.2",
"better-ajv-errors": "^0.6.7",
@@ -1765,6 +1939,7 @@
"version": "5.5.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
"integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
+ "dev": true,
"requires": {
"co": "^4.6.0",
"fast-deep-equal": "^1.0.0",
@@ -1775,19 +1950,22 @@
"fast-deep-equal": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
- "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ="
+ "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=",
+ "dev": true
},
"json-schema-traverse": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
- "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A="
+ "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=",
+ "dev": true
}
}
},
"oauth-sign": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
- "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
+ "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
+ "dev": true
},
"object-assign": {
"version": "4.1.1",
@@ -1799,6 +1977,7 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
"requires": {
"wrappy": "1"
}
@@ -1813,6 +1992,7 @@
"version": "0.1.36",
"resolved": "https://registry.npmjs.org/openapi-generator/-/openapi-generator-0.1.36.tgz",
"integrity": "sha512-cIPjWeIzUa3bJK4XwzPp5wLS6trUo1WNTAvT4zGSNtkvaD1Vc9H65WZu2/W+NfQ+lhVvCsv4l9dIYtyqlNHiEg==",
+ "dev": true,
"requires": {
"@types/nunjucks": "^3.1.0",
"@types/request": "^2.48.0",
@@ -1829,19 +2009,22 @@
"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=="
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
}
}
},
"openapi3-ts": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/openapi3-ts/-/openapi3-ts-1.3.0.tgz",
- "integrity": "sha512-Xk3hsB0PzB4dzr/r/FdmK+VfQbZH7lQQ2iipMS1/1eoz1wUvh5R7rmOakYvw0bQJJE6PYrOLx8UHsYmzgTr+YQ=="
+ "integrity": "sha512-Xk3hsB0PzB4dzr/r/FdmK+VfQbZH7lQQ2iipMS1/1eoz1wUvh5R7rmOakYvw0bQJJE6PYrOLx8UHsYmzgTr+YQ==",
+ "dev": true
},
"os-locale": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz",
"integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==",
+ "dev": true,
"requires": {
"execa": "^1.0.0",
"lcid": "^2.0.0",
@@ -1857,22 +2040,26 @@
"p-defer": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz",
- "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww="
+ "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=",
+ "dev": true
},
"p-finally": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
- "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
+ "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
+ "dev": true
},
"p-is-promise": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz",
- "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg=="
+ "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==",
+ "dev": true
},
"p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
"requires": {
"p-try": "^2.0.0"
}
@@ -1881,6 +2068,7 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
"requires": {
"p-limit": "^2.2.0"
}
@@ -1894,12 +2082,14 @@
"p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
- "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true
},
"path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
- "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true
},
"path-is-absolute": {
"version": "1.0.1",
@@ -1916,7 +2106,8 @@
"path-key": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
- "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A="
+ "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
+ "dev": true
},
"pend": {
"version": "1.2.0",
@@ -1927,12 +2118,14 @@
"performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
- "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
+ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
+ "dev": true
},
"picomatch": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
"integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
+ "dev": true,
"optional": true
},
"pify": {
@@ -1941,6 +2134,12 @@
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
"dev": true
},
+ "prettier": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.4.tgz",
+ "integrity": "sha512-SVJIQ51spzFDvh4fIbCLvciiDMCrRhlN3mbZvv/+ycjvmF5E73bKdGfU8QDLNmjYJf+lsGnDBC4UUnvTe5OO0w==",
+ "dev": true
+ },
"pretty-bytes": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.3.0.tgz",
@@ -1956,12 +2155,14 @@
"psl": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
- "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ=="
+ "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==",
+ "dev": true
},
"pump": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "dev": true,
"requires": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
@@ -1970,12 +2171,14 @@
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
- "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "dev": true
},
"qs": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
- "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
+ "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
+ "dev": true
},
"querystring": {
"version": "0.2.0",
@@ -2016,6 +2219,7 @@
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz",
"integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==",
+ "dev": true,
"optional": true,
"requires": {
"picomatch": "^2.2.1"
@@ -2024,17 +2228,20 @@
"reftools": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/reftools/-/reftools-1.1.1.tgz",
- "integrity": "sha512-7ySkzK7YpUeJP16rzJqEXTZ7IrAq/AL/p+wWejD9wdKQOe+mYYVAOB3w5ZTs2eoHfmAidwr/6PcC+q+LzPF/DQ=="
+ "integrity": "sha512-7ySkzK7YpUeJP16rzJqEXTZ7IrAq/AL/p+wWejD9wdKQOe+mYYVAOB3w5ZTs2eoHfmAidwr/6PcC+q+LzPF/DQ==",
+ "dev": true
},
"regenerator-runtime": {
"version": "0.13.5",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
- "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA=="
+ "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==",
+ "dev": true
},
"request": {
"version": "2.88.2",
"resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
"integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
+ "dev": true,
"requires": {
"aws-sign2": "~0.7.0",
"aws4": "^1.8.0",
@@ -2067,15 +2274,37 @@
"throttleit": "^1.0.0"
}
},
+ "request-promise-core": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz",
+ "integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.15"
+ }
+ },
+ "request-promise-native": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.8.tgz",
+ "integrity": "sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ==",
+ "dev": true,
+ "requires": {
+ "request-promise-core": "1.1.3",
+ "stealthy-require": "^1.1.1",
+ "tough-cookie": "^2.3.3"
+ }
+ },
"require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
- "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
+ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+ "dev": true
},
"require-main-filename": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
- "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
+ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+ "dev": true
},
"restore-cursor": {
"version": "1.0.1",
@@ -2108,27 +2337,32 @@
"safe-buffer": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
- "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
+ "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==",
+ "dev": true
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
- "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
},
"set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
- "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
+ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
+ "dev": true
},
"shebang-command": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
"integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+ "dev": true,
"requires": {
"shebang-regex": "^1.0.0"
}
@@ -2136,12 +2370,14 @@
"shebang-regex": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
- "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
+ "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",
@@ -2154,6 +2390,7 @@
"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"
}
@@ -2162,6 +2399,7 @@
"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.3.0",
"should-type-adaptors": "^1.0.1"
@@ -2170,12 +2408,14 @@
"should-type": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz",
- "integrity": "sha1-B1bYzoRt/QmEOmlHcZ36DUz/XPM="
+ "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.3.0",
"should-util": "^1.0.0"
@@ -2184,12 +2424,14 @@
"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=="
+ "integrity": "sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==",
+ "dev": true
},
"signal-exit": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
- "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA=="
+ "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==",
+ "dev": true
},
"slice-ansi": {
"version": "0.0.4",
@@ -2201,6 +2443,7 @@
"version": "1.16.1",
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
"integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
+ "dev": true,
"requires": {
"asn1": "~0.2.3",
"assert-plus": "^1.0.0",
@@ -2213,10 +2456,17 @@
"tweetnacl": "~0.14.0"
}
},
+ "stealthy-require": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
+ "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=",
+ "dev": true
+ },
"string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "dev": true,
"requires": {
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^4.0.0"
@@ -2225,12 +2475,14 @@
"ansi-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
- "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "dev": true
},
"strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "dev": true,
"requires": {
"ansi-regex": "^3.0.0"
}
@@ -2266,7 +2518,8 @@
"strip-eof": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
- "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8="
+ "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
+ "dev": true
},
"supports-color": {
"version": "7.1.0",
@@ -2289,6 +2542,7 @@
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/swagger2openapi/-/swagger2openapi-5.4.0.tgz",
"integrity": "sha512-f5QqfXawiVijhjMtYqWZ55ESHPZFqrPC8L9idhIiuSX8O2qsa1i4MVGtCM3TQF+Smzr/6WfT/7zBuzG3aTgPAA==",
+ "dev": true,
"requires": {
"better-ajv-errors": "^0.6.1",
"call-me-maybe": "^1.0.1",
@@ -2328,6 +2582,7 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
"optional": true,
"requires": {
"is-number": "^7.0.0"
@@ -2337,6 +2592,7 @@
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
+ "dev": true,
"requires": {
"psl": "^1.1.28",
"punycode": "^2.1.1"
@@ -2345,12 +2601,14 @@
"tslib": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz",
- "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA=="
+ "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==",
+ "dev": true
},
"tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+ "dev": true,
"requires": {
"safe-buffer": "^5.0.1"
}
@@ -2358,7 +2616,8 @@
"tweetnacl": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
- "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
+ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
+ "dev": true
},
"typedarray": {
"version": "0.0.6",
@@ -2382,6 +2641,7 @@
"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.0"
}
@@ -2413,22 +2673,39 @@
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
- "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
+ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+ "dev": true
},
"verror": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
"integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+ "dev": true,
"requires": {
"assert-plus": "^1.0.0",
"core-util-is": "1.0.2",
"extsprintf": "^1.2.0"
}
},
+ "wait-on": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-4.0.2.tgz",
+ "integrity": "sha512-Qpmgm3Hw/sXm7xK68FBsYy5r+Uid94/QymwnEjn9GTpfiWTUVYm0bccivVwY/BXGYO2r+5Cd8S/DzrRZqHK/9w==",
+ "dev": true,
+ "requires": {
+ "@hapi/joi": "^17.1.1",
+ "lodash": "^4.17.15",
+ "minimist": "^1.2.5",
+ "request": "^2.88.2",
+ "request-promise-native": "^1.0.8",
+ "rxjs": "^6.5.5"
+ }
+ },
"which": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
"integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dev": true,
"requires": {
"isexe": "^2.0.0"
}
@@ -2436,7 +2713,8 @@
"which-module": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
- "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
+ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
+ "dev": true
},
"wrap-ansi": {
"version": "3.0.1",
@@ -2468,17 +2746,20 @@
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
},
"y18n": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
- "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w=="
+ "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
+ "dev": true
},
"yaml": {
"version": "1.9.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.9.2.tgz",
"integrity": "sha512-HPT7cGGI0DuRcsO51qC1j9O16Dh1mZ2bnXwsi0jrSpsLz0WxOLSLXfkABVl6bZO629py3CU+OMJtpNHDLB97kg==",
+ "dev": true,
"requires": {
"@babel/runtime": "^7.9.2"
}
@@ -2487,6 +2768,7 @@
"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.0.0",
"decamelize": "^1.2.0",
@@ -2505,12 +2787,14 @@
"ansi-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
- "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "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",
@@ -2521,6 +2805,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "dev": true,
"requires": {
"locate-path": "^3.0.0"
}
@@ -2528,12 +2813,14 @@
"get-caller-file": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
- "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w=="
+ "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
+ "dev": true
},
"is-fullwidth-code-point": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+ "dev": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@@ -2542,6 +2829,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "dev": true,
"requires": {
"p-locate": "^3.0.0",
"path-exists": "^3.0.0"
@@ -2551,6 +2839,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "dev": true,
"requires": {
"p-limit": "^2.0.0"
}
@@ -2558,17 +2847,20 @@
"path-exists": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
- "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
+ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+ "dev": true
},
"require-main-filename": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
- "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE="
+ "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
+ "dev": true
},
"strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "dev": true,
"requires": {
"ansi-regex": "^3.0.0"
}
@@ -2577,6 +2869,7 @@
"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.1",
"strip-ansi": "^3.0.1"
@@ -2585,12 +2878,14 @@
"ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+ "dev": true
},
"string-width": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+ "dev": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@@ -2601,6 +2896,7 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "dev": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@@ -2611,6 +2907,7 @@
"version": "11.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz",
"integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==",
+ "dev": true,
"requires": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
@@ -2622,6 +2919,7 @@
"version": "18.1.3",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
+ "dev": true,
"requires": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
diff --git a/package.json b/package.json
index df904f875f42..a6ad8ea60bf4 100644
--- a/package.json
+++ b/package.json
@@ -1,9 +1,15 @@
{
"private": true,
- "devDependencies": {
- "cypress": "^4.3.0"
+ "scripts": {
+ "format": "prettier --no-semi --single-quote --trailing-comma es5 --write \"cypress/**/*.js\"",
+ "test": "cypress run",
+ "test:watch": "cypress open",
+ "wait-on": "wait-on"
},
- "dependencies": {
+ "devDependencies": {
+ "cypress": "4.5.0",
+ "prettier": "2.0.4",
+ "wait-on": "4.0.2",
"openapi-generator": "^0.1.36"
}
}
diff --git a/scripts/test-envs.sh b/script/test-envs.sh
similarity index 100%
rename from scripts/test-envs.sh
rename to script/test-envs.sh
diff --git a/selfservice/flow/login/request.go b/selfservice/flow/login/request.go
index 4817de010129..9b245c123d00 100644
--- a/selfservice/flow/login/request.go
+++ b/selfservice/flow/login/request.go
@@ -12,7 +12,6 @@ import (
"github.com/gofrs/uuid"
"github.com/pkg/errors"
- "github.com/ory/herodot"
"github.com/ory/x/urlx"
"github.com/ory/kratos/identity"
@@ -129,9 +128,6 @@ func (r *Request) Valid() error {
return errors.WithStack(newRequestExpiredError(time.Since(r.ExpiresAt)))
}
- if r.IssuedAt.After(time.Now()) {
- return errors.WithStack(herodot.ErrBadRequest.WithReason("The login request was issued in the future."))
- }
return nil
}
diff --git a/selfservice/flow/login/request_test.go b/selfservice/flow/login/request_test.go
index ba7bd2495d68..b36c76218ff5 100644
--- a/selfservice/flow/login/request_test.go
+++ b/selfservice/flow/login/request_test.go
@@ -41,7 +41,6 @@ func TestRequest(t *testing.T) {
r: &login.Request{ExpiresAt: time.Now().Add(time.Hour), IssuedAt: time.Now().Add(-time.Minute)},
valid: true,
},
- {r: &login.Request{ExpiresAt: time.Now().Add(time.Hour), IssuedAt: time.Now().Add(time.Minute)}},
{r: &login.Request{ExpiresAt: time.Now().Add(-time.Hour), IssuedAt: time.Now().Add(-time.Minute)}},
} {
if tc.valid {
diff --git a/selfservice/flow/registration/request.go b/selfservice/flow/registration/request.go
index 5b7ee928b2d5..00323ed2aad7 100644
--- a/selfservice/flow/registration/request.go
+++ b/selfservice/flow/registration/request.go
@@ -8,7 +8,6 @@ import (
"github.com/gofrs/uuid"
"github.com/pkg/errors"
- "github.com/ory/herodot"
"github.com/ory/x/urlx"
"github.com/ory/kratos/identity"
@@ -130,8 +129,5 @@ func (r *Request) Valid() error {
if r.ExpiresAt.Before(time.Now()) {
return errors.WithStack(newRequestExpiredError(time.Since(r.ExpiresAt)))
}
- if r.IssuedAt.After(time.Now()) {
- return errors.WithStack(herodot.ErrBadRequest.WithReason("The registration request was issued in the future."))
- }
return nil
}
diff --git a/selfservice/flow/registration/request_test.go b/selfservice/flow/registration/request_test.go
index 0abd3331b2fb..7053b4939db2 100644
--- a/selfservice/flow/registration/request_test.go
+++ b/selfservice/flow/registration/request_test.go
@@ -41,7 +41,6 @@ func TestRequest(t *testing.T) {
r: ®istration.Request{ExpiresAt: time.Now().Add(time.Hour), IssuedAt: time.Now().Add(-time.Minute)},
valid: true,
},
- {r: ®istration.Request{ExpiresAt: time.Now().Add(time.Hour), IssuedAt: time.Now().Add(time.Minute)}},
{r: ®istration.Request{ExpiresAt: time.Now().Add(-time.Hour), IssuedAt: time.Now().Add(-time.Minute)}},
} {
if tc.valid {
diff --git a/selfservice/flow/settings/strategy_profile.go b/selfservice/flow/settings/strategy_profile.go
index da6d1b3b62d6..3bbff77b52f6 100644
--- a/selfservice/flow/settings/strategy_profile.go
+++ b/selfservice/flow/settings/strategy_profile.go
@@ -27,11 +27,15 @@ import (
)
const (
- StrategyProfile = "profile"
- PublicSettingsProfilePath = "/self-service/browser/flows/settings/strategies/profile"
- strategyProfileContinuityName = "settings_profile"
+ StrategyProfile = "profile"
+ PublicSettingsProfilePath = "/self-service/browser/flows/settings/strategies/profile"
)
+func strategyProfileContinuityName(rid string) string {
+ // Use one individual container per request ID to prevent resuming other request IDs.
+ return "ory_kratos_settings_profile." + rid
+}
+
var _ Strategy = new(StrategyTraits)
type (
@@ -147,12 +151,24 @@ func (s *StrategyTraits) handleSubmit(w http.ResponseWriter, r *http.Request, ps
}
var p completeSelfServiceBrowserSettingsStrategyProfileFlowPayload
- if _, err := s.d.ContinuityManager().Continue(r.Context(), r,
- strategyProfileContinuityName,
+ rid := r.URL.Query().Get("request")
+ p = completeSelfServiceBrowserSettingsStrategyProfileFlowPayload{RequestID: rid}
+ if len(rid) == 0 {
+ s.handleSettingsError(w, r, nil, ss, json.RawMessage(ss.Identity.Traits), &p, errors.WithStack(herodot.ErrBadRequest.WithReasonf("The request query parameter is missing.")))
+ return
+ }
+
+ if _, err = s.d.ContinuityManager().Continue(r.Context(), r,
+ strategyProfileContinuityName(rid),
continuity.WithIdentity(ss.Identity),
continuity.WithPayload(&p),
); err == nil {
- s.continueFlow(w, r, ss, &p)
+ if p.RequestID == r.URL.Query().Get("request") {
+ s.continueFlow(w, r, ss, &p)
+ return
+ }
+ } else if !errors.Is(err, &continuity.ErrNotResumable) {
+ s.handleSettingsError(w, r, nil, ss, json.RawMessage(ss.Identity.Traits), &p, err)
return
}
@@ -173,12 +189,6 @@ func (s *StrategyTraits) handleSubmit(w http.ResponseWriter, r *http.Request, ps
return
}
- rid := r.URL.Query().Get("request")
- if len(rid) == 0 {
- s.handleSettingsError(w, r, nil, ss, json.RawMessage(ss.Identity.Traits), &p, errors.WithStack(herodot.ErrBadRequest.WithReasonf("The request query parameter is missing.")))
- return
- }
-
p.RequestID = rid
s.continueFlow(w, r, ss, &p)
}
@@ -286,7 +296,7 @@ func (s *StrategyTraits) handleSettingsError(w http.ResponseWriter, r *http.Requ
if errors.Is(err, ErrRequestNeedsReAuthentication) {
if err := s.d.ContinuityManager().Pause(
r.Context(), w, r,
- strategyProfileContinuityName,
+ strategyProfileContinuityName(r.URL.Query().Get("request")),
continuity.WithPayload(p),
continuity.WithIdentity(ss.Identity),
continuity.WithLifespan(time.Minute*15),
diff --git a/selfservice/flow/settings/strategy_profile_test.go b/selfservice/flow/settings/strategy_profile_test.go
index 1793748a608f..1e438be93713 100644
--- a/selfservice/flow/settings/strategy_profile_test.go
+++ b/selfservice/flow/settings/strategy_profile_test.go
@@ -42,6 +42,8 @@ func TestStrategyTraits(t *testing.T) {
ui := testhelpers.NewSettingsUITestServer(t)
viper.Set(configuration.ViperKeySelfServicePrivilegedAuthenticationAfter, "1ns")
+ _ = testhelpers.NewErrorTestServer(t, reg)
+
primaryIdentity := &identity.Identity{
ID: x.NewUUID(),
Credentials: map[identity.CredentialsType]identity.Credentials{
diff --git a/selfservice/flow/verify/handler.go b/selfservice/flow/verify/handler.go
index ccf49349eef4..f8a57bca1d91 100644
--- a/selfservice/flow/verify/handler.go
+++ b/selfservice/flow/verify/handler.go
@@ -336,7 +336,7 @@ func (h *Handler) verify(w http.ResponseWriter, r *http.Request, ps httprouter.P
}
if err := h.d.PrivilegedIdentityPool().VerifyAddress(r.Context(), ps.ByName("code")); err != nil {
- if errorsx.Cause(err) == sqlcon.ErrNoRows {
+ if errors.Is(err, sqlcon.ErrNoRows) {
a := NewRequest(
h.c.SelfServiceSettingsRequestLifespan(), r, via,
urlx.AppendPaths(h.c.SelfPublicURL(), strings.ReplaceAll(PublicVerificationCompletePath, ":via", string(via))), h.d.GenerateCSRFToken,
@@ -352,6 +352,7 @@ func (h *Handler) verify(w http.ResponseWriter, r *http.Request, ps httprouter.P
urlx.CopyWithQuery(h.c.VerificationURL(), url.Values{"request": {a.ID.String()}}).String(),
http.StatusFound,
)
+ return
}
h.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err)
diff --git a/selfservice/strategy/password/settings.go b/selfservice/strategy/password/settings.go
index 82d7d36a1289..3b7a9de23bf2 100644
--- a/selfservice/strategy/password/settings.go
+++ b/selfservice/strategy/password/settings.go
@@ -24,10 +24,14 @@ import (
)
const (
- SettingsPath = "/self-service/browser/flows/settings/strategies/password"
- continuityKeySettings = "settings_password"
+ SettingsPath = "/self-service/browser/flows/settings/strategies/password"
)
+func continuityKeySettings(rid string) string {
+ // Use one individual container per request ID to prevent resuming other request IDs.
+ return "settings_password." + rid
+}
+
func (s *Strategy) RegisterSettingsRoutes(router *x.RouterPublic) {
router.POST(SettingsPath, s.submitSettingsFlow)
router.GET(SettingsPath, s.submitSettingsFlow)
@@ -79,18 +83,24 @@ func (s *Strategy) submitSettingsFlow(w http.ResponseWriter, r *http.Request, ps
return
}
+ rid := r.URL.Query().Get("request")
+ if len(rid) == 0 {
+ s.handleSettingsError(w, r, nil, ss, nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf("The request query parameter is missing.")))
+ return
+ }
+
var p completeSelfServiceBrowserSettingsPasswordFlowPayload
if _, err := s.d.ContinuityManager().Continue(r.Context(), r,
- continuityKeySettings,
+ continuityKeySettings(r.URL.Query().Get("request")),
continuity.WithIdentity(ss.Identity),
continuity.WithPayload(&p)); err == nil {
- s.completeSettingsFlow(w, r, ss, &p)
+ if p.RequestID == r.URL.Query().Get("request") {
+ s.completeSettingsFlow(w, r, ss, &p)
+ return
+ }
return
- }
-
- rid := r.URL.Query().Get("request")
- if len(rid) == 0 {
- s.handleSettingsError(w, r, nil, ss, &p, errors.WithStack(herodot.ErrBadRequest.WithReasonf("The request query parameter is missing.")))
+ } else if !errors.Is(err, &continuity.ErrNotResumable) {
+ s.handleSettingsError(w, r, nil, ss, &p, err)
return
}
@@ -105,7 +115,7 @@ func (s *Strategy) submitSettingsFlow(w http.ResponseWriter, r *http.Request, ps
}
if err := s.d.ContinuityManager().Pause(
r.Context(), w, r,
- continuityKeySettings,
+ continuityKeySettings(r.URL.Query().Get("request")),
continuity.WithPayload(&p),
continuity.WithIdentity(ss.Identity),
continuity.WithLifespan(time.Minute*15),
@@ -211,7 +221,7 @@ func (s *Strategy) handleSettingsError(w http.ResponseWriter, r *http.Request, r
if errors.Is(err, settings.ErrRequestNeedsReAuthentication) {
if err := s.d.ContinuityManager().Pause(
r.Context(), w, r,
- continuityKeySettings,
+ continuityKeySettings(r.URL.Query().Get("request")),
continuity.WithPayload(p),
continuity.WithIdentity(ss.Identity),
continuity.WithLifespan(time.Minute*15),
diff --git a/test/e2e/.gitignore b/test/e2e/.gitignore
new file mode 100644
index 000000000000..ddb9705a2def
--- /dev/null
+++ b/test/e2e/.gitignore
@@ -0,0 +1,2 @@
+*.log
+kratos.generated.yml
diff --git a/test/e2e/profiles/email/.kratos.yml b/test/e2e/profiles/email/.kratos.yml
new file mode 100644
index 000000000000..3047e6e34f92
--- /dev/null
+++ b/test/e2e/profiles/email/.kratos.yml
@@ -0,0 +1,21 @@
+selfservice:
+ strategies:
+ password:
+ enabled: true
+
+ settings:
+ privileged_session_max_age: 5000ms
+
+ logout:
+ redirect_to: http://127.0.0.1:4455/auth/login
+
+ registration:
+ after:
+ password:
+ hooks:
+ -
+ hook: session
+
+identity:
+ traits:
+ default_schema_url: file://test/e2e/profiles/email/identity.traits.schema.json
diff --git a/test/e2e/profiles/email/identity.traits.schema.json b/test/e2e/profiles/email/identity.traits.schema.json
new file mode 100644
index 000000000000..cc9f72a2bcbe
--- /dev/null
+++ b/test/e2e/profiles/email/identity.traits.schema.json
@@ -0,0 +1,31 @@
+{
+ "$id": "https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json",
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "Person",
+ "type": "object",
+ "properties": {
+ "email": {
+ "type": "string",
+ "format": "email",
+ "title": "E-Mail",
+ "minLength": 3,
+ "ory.sh/kratos": {
+ "credentials": {
+ "password": {
+ "identifier": true
+ }
+ }
+ }
+ },
+ "website": {
+ "type": "string",
+ "format": "uri",
+ "minLength": 10
+ }
+ },
+ "required": [
+ "email",
+ "website"
+ ],
+ "additionalProperties": false
+}
diff --git a/test/e2e/profiles/kratos.base.yml b/test/e2e/profiles/kratos.base.yml
new file mode 100644
index 000000000000..32b1c066105f
--- /dev/null
+++ b/test/e2e/profiles/kratos.base.yml
@@ -0,0 +1,35 @@
+log:
+ level: debug
+
+secrets:
+ session:
+ - PLEASE-CHANGE-ME-I-AM-VERY-INSECURE
+
+urls:
+ login_ui: http://127.0.0.1:4455/auth/login
+ registration_ui: http://127.0.0.1:4455/auth/registration
+ error_ui: http://127.0.0.1:4455/error
+ settings_ui: http://127.0.0.1:4455/settings
+ verify_ui: http://127.0.0.1:4455/verify
+
+ # These are undefined because not available in this demo
+ mfa_ui: http://127.0.0.1:4455/
+
+ self:
+ public: http://127.0.0.1:4455/.ory/kratos/public/
+ admin: http://kratos:4434/
+ default_return_to: http://127.0.0.1:4455/
+ whitelisted_return_to_urls:
+ - http://127.0.0.1:4455
+
+hashers:
+ argon2:
+ parallelism: 1
+ memory: 16384
+ iterations: 1
+ salt_length: 16
+ key_length: 16
+
+courier:
+ smtp:
+ connection_uri: smtps://test:test@127.0.0.1:1025/?skip_ssl_verify=true
diff --git a/test/e2e/profiles/verify/.kratos.yml b/test/e2e/profiles/verify/.kratos.yml
new file mode 100644
index 000000000000..afa6b62fe9e4
--- /dev/null
+++ b/test/e2e/profiles/verify/.kratos.yml
@@ -0,0 +1,30 @@
+selfservice:
+ strategies:
+ password:
+ enabled: true
+
+ settings:
+ privileged_session_max_age: 1m
+ after:
+ profile:
+ hooks:
+ -
+ hook: verify
+
+ verify:
+ return_to: http://127.0.0.1:4455/
+ link_lifespan: 5s
+
+ logout:
+ redirect_to: http://127.0.0.1:4455/auth/login
+
+ registration:
+ after:
+ password:
+ hooks:
+ -
+ hook: verify
+
+identity:
+ traits:
+ default_schema_url: file://test/e2e/profiles/verify/identity.traits.schema.json
diff --git a/test/e2e/profiles/verify/identity.traits.schema.json b/test/e2e/profiles/verify/identity.traits.schema.json
new file mode 100644
index 000000000000..424efd557d93
--- /dev/null
+++ b/test/e2e/profiles/verify/identity.traits.schema.json
@@ -0,0 +1,28 @@
+{
+ "$id": "https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json",
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "Person",
+ "type": "object",
+ "properties": {
+ "email": {
+ "type": "string",
+ "format": "email",
+ "title": "E-Mail",
+ "minLength": 3,
+ "ory.sh/kratos": {
+ "credentials": {
+ "password": {
+ "identifier": true
+ }
+ },
+ "verification": {
+ "via": "email"
+ }
+ }
+ }
+ },
+ "required": [
+ "email"
+ ],
+ "additionalProperties": false
+}
diff --git a/test/e2e/run.sh b/test/e2e/run.sh
new file mode 100755
index 000000000000..c188379a5379
--- /dev/null
+++ b/test/e2e/run.sh
@@ -0,0 +1,157 @@
+#!/bin/bash
+
+set -euxo pipefail
+
+cd "$( dirname "${BASH_SOURCE[0]}" )/../.."
+
+export PATH=.bin:$PATH
+export KRATOS_PUBLIC_URL=http://127.0.0.1:4433/
+export KRATOS_ADMIN_URL=http://127.0.0.1:4434/
+
+! nc -zv 127.0.0.1 4434
+! nc -zv 127.0.0.1 4433
+! nc -zv 127.0.0.1 4455
+
+base=$(pwd)
+
+if [ -z ${KRATOS_APP_PATH+x} ]; then
+ dir="$(mktemp -d -t ci-XXXXXXXXXX)/kratos-selfservice-ui-node"
+ git clone git@github.com:ory/kratos-selfservice-ui-node.git "$dir"
+ (cd "$dir"; npm i && npm run build)
+else
+ dir="${KRATOS_APP_PATH}"
+fi
+
+kratos=./test/e2e/.bin/kratos
+go build -tags sqlite -o $kratos .
+
+if [ -z ${CI+x} ]; then
+ docker rm mailslurper -f || true
+ docker run --name mailslurper -p 4436:4436 -p 4437:4437 -p 1025:1025 oryd/mailslurper:latest-smtps > "${base}/test/e2e/mailslurper.e2e.log" 2>&1 &
+fi
+
+dev=no
+for i in "$@"
+do
+case $i in
+ --dev)
+ dev=yes
+ shift # past argument=value
+ ;;
+esac
+done
+
+run() {
+ profile=$2
+ killall kratos || true
+ killall node || true
+
+ if [ -z ${KRATOS_APP_PATH+x} ]; then
+ (cd "$dir"; PORT=4455 SECURITY_MODE=cookie npm run serve \
+ > "${base}/test/e2e/secureapp.e2e.log" 2>&1 &)
+ else
+ (cd "$dir"; PORT=4455 SECURITY_MODE=cookie npm run start \
+ > "${base}/test/e2e/secureapp.e2e.log" 2>&1 &)
+ fi
+
+ export DSN=${1}
+ $kratos migrate sql -e --yes
+
+ yq merge test/e2e/profiles/kratos.base.yml "test/e2e/profiles/${profile}/.kratos.yml" > test/e2e/kratos.generated.yml
+ ($kratos serve --dev -c test/e2e/kratos.generated.yml > "${base}/test/e2e/kratos.${profile}.e2e.log" 2>&1 &)
+
+ npm run wait-on -- -t 10000 http-get://127.0.0.1:4434/health/ready \
+ http-get://127.0.0.1:4455/health \
+ http-get://127.0.0.1:4437/mail
+
+ if [[ $dev = "yes" ]]; then
+ npm run test:watch -- --config integrationFolder="cypress/integration/profiles/$profile"
+ else
+ npm run test -- --config integrationFolder="cypress/integration/profiles/$profile"
+ fi
+}
+
+usage() {
+ echo $"This script runs the e2e tests.
+
+To run the tests just pick a database name:
+
+ $0
+
+ Supported databases are 'sqlite', 'mysql', 'postgres', 'cockroach':
+
+ $0 sqlite
+ $0 mysql
+ $0 postgres
+ $0 cockroach
+ ...
+
+ If you are using a database other than SQLite, you need to set
+ an environment variable that points to it:
+
+ export TEST_DATABASE_MYSQL=...
+ export TEST_DATABASE_POSTGRESQL=...
+ export TEST_DATABASE_COCKROACHDB=...
+ $0
+
+ The Makefile has a helper for that which uses Docker to start the
+ databases:
+
+ make test-resetdb
+ source scripts/test-envs.sh
+ $0
+
+To run e2e tests in dev mode (useful for writing them), run:
+
+ $0 --dev
+
+ Supported profiles are 'email', 'verify':
+
+ $0 --dev email
+ $0 --dev verify
+ ...
+
+If you are making changes to the kratos-selfservice-ui-node
+project as well, point the 'KRATOS_APP_PATH' environment variable to
+the path where the kratos-selfservice-ui-node project is checked out:
+
+ export KRATOS_APP_PATH=$HOME/workspace/kratos-selfservice-ui-node
+ $0 ..."
+}
+
+if [[ $dev = "yes" ]]; then
+ if [ -z ${2+x} ]; then
+ usage
+ exit 1
+ fi
+fi
+
+export TEST_DATABASE_SQLITE="sqlite:///$(mktemp -d -t ci-XXXXXXXXXX)/db.sqlite?_fk=true"
+case "$1" in
+ sqlite)
+ db="${TEST_DATABASE_SQLITE}"
+ ;;
+
+ mysql)
+ db="${TEST_DATABASE_MYSQL}"
+ ;;
+
+ postgres)
+ db="${TEST_DATABASE_POSTGRESQL}"
+ ;;
+
+ cockroach)
+ db="${TEST_DATABASE_COCKROACHDB}"
+ ;;
+
+ *)
+ usage
+ exit 1
+esac
+
+if [[ $dev = "yes" ]]; then
+ run "${db}" "$2"
+else
+ run "${db}" email
+ run "${db}" verify
+fi
diff --git a/tests/fixtures/config.schema.test.failure/wrongTypes.main.yaml b/test/schema/fixtures/config.schema.test.failure/wrongTypes.main.yaml
similarity index 100%
rename from tests/fixtures/config.schema.test.failure/wrongTypes.main.yaml
rename to test/schema/fixtures/config.schema.test.failure/wrongTypes.main.yaml
diff --git a/tests/fixtures/config.schema.test.success/allKeys.main.yaml b/test/schema/fixtures/config.schema.test.success/allKeys.main.yaml
similarity index 100%
rename from tests/fixtures/config.schema.test.success/allKeys.main.yaml
rename to test/schema/fixtures/config.schema.test.success/allKeys.main.yaml
diff --git a/tests/fixtures/config.schema.test.success/allKeys.selfServiceAfterLogin.yaml b/test/schema/fixtures/config.schema.test.success/allKeys.selfServiceAfterLogin.yaml
similarity index 100%
rename from tests/fixtures/config.schema.test.success/allKeys.selfServiceAfterLogin.yaml
rename to test/schema/fixtures/config.schema.test.success/allKeys.selfServiceAfterLogin.yaml
diff --git a/tests/fixtures/config.schema.test.success/allKeys.selfServiceAfterLoginHooks.yaml b/test/schema/fixtures/config.schema.test.success/allKeys.selfServiceAfterLoginHooks.yaml
similarity index 100%
rename from tests/fixtures/config.schema.test.success/allKeys.selfServiceAfterLoginHooks.yaml
rename to test/schema/fixtures/config.schema.test.success/allKeys.selfServiceAfterLoginHooks.yaml
diff --git a/tests/fixtures/config.schema.test.success/allKeys.selfServiceAfterRegistrationHooks.yaml b/test/schema/fixtures/config.schema.test.success/allKeys.selfServiceAfterRegistrationHooks.yaml
similarity index 100%
rename from tests/fixtures/config.schema.test.success/allKeys.selfServiceAfterRegistrationHooks.yaml
rename to test/schema/fixtures/config.schema.test.success/allKeys.selfServiceAfterRegistrationHooks.yaml
diff --git a/tests/fixtures/config.schema.test.success/allKeys.selfServiceBefore.yaml b/test/schema/fixtures/config.schema.test.success/allKeys.selfServiceBefore.yaml
similarity index 100%
rename from tests/fixtures/config.schema.test.success/allKeys.selfServiceBefore.yaml
rename to test/schema/fixtures/config.schema.test.success/allKeys.selfServiceBefore.yaml
diff --git a/tests/fixtures/config.schema.test.success/allKeys.selfServiceOIDCProvider.yaml b/test/schema/fixtures/config.schema.test.success/allKeys.selfServiceOIDCProvider.yaml
similarity index 100%
rename from tests/fixtures/config.schema.test.success/allKeys.selfServiceOIDCProvider.yaml
rename to test/schema/fixtures/config.schema.test.success/allKeys.selfServiceOIDCProvider.yaml
diff --git a/tests/fixtures/config.schema.test.success/allKeys.selfServiceRedirectHook.yaml b/test/schema/fixtures/config.schema.test.success/allKeys.selfServiceRedirectHook.yaml
similarity index 100%
rename from tests/fixtures/config.schema.test.success/allKeys.selfServiceRedirectHook.yaml
rename to test/schema/fixtures/config.schema.test.success/allKeys.selfServiceRedirectHook.yaml
diff --git a/tests/fixtures/config.schema.test.success/allKeys.selfServiceSessionIssuerHook.yaml b/test/schema/fixtures/config.schema.test.success/allKeys.selfServiceSessionIssuerHook.yaml
similarity index 100%
rename from tests/fixtures/config.schema.test.success/allKeys.selfServiceSessionIssuerHook.yaml
rename to test/schema/fixtures/config.schema.test.success/allKeys.selfServiceSessionIssuerHook.yaml
diff --git a/tests/fixtures/config.schema.test.success/allKeys.selfServiceSessionRevokerHook.yaml b/test/schema/fixtures/config.schema.test.success/allKeys.selfServiceSessionRevokerHook.yaml
similarity index 100%
rename from tests/fixtures/config.schema.test.success/allKeys.selfServiceSessionRevokerHook.yaml
rename to test/schema/fixtures/config.schema.test.success/allKeys.selfServiceSessionRevokerHook.yaml
diff --git a/tests/schema_test.go b/test/schema/schema_test.go
similarity index 99%
rename from tests/schema_test.go
rename to test/schema/schema_test.go
index c5d0052d0407..31528e280d1c 100644
--- a/tests/schema_test.go
+++ b/test/schema/schema_test.go
@@ -1,4 +1,4 @@
-package tests
+package schema
import (
"bytes"
diff --git a/x/cookie.go b/x/cookie.go
index f653902043e6..94ed102e467c 100644
--- a/x/cookie.go
+++ b/x/cookie.go
@@ -31,7 +31,7 @@ func SessionGetString(r *http.Request, s sessions.Store, id string, key interfac
}
if v, ok := cookie.Values[key]; !ok {
- return "", errors.Errorf("key %s does not exist in cookie", key)
+ return "", errors.Errorf("key %s does not exist in cookie: %+v", key, cookie.Values)
} else if vv, ok := v.(string); !ok {
return "", errors.Errorf("value of key %s is not of type string in cookie", key)
} else {