From 35f227de11fd2b3f542f920571ea93a7890ad11c Mon Sep 17 00:00:00 2001 From: Oleg Gaidarenko Date: Fri, 17 May 2019 14:57:26 +0300 Subject: [PATCH] Feature: LDAP refactoring (#16950) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * incapsulates multipleldap logic under one module * abstracts users upsert and get logic * changes some of the text error messages and import sort sequence * heavily refactors the LDAP module – LDAP module now only deals with LDAP related behaviour * integrates affected auth_proxy module and their tests * refactoring of the auth_proxy logic --- go.mod | 1 + go.sum | 2 + pkg/extensions/main.go | 1 + pkg/login/auth.go | 15 +- pkg/login/auth_test.go | 32 +- pkg/login/ldap_login.go | 38 +- pkg/login/ldap_login_test.go | 106 ++-- pkg/middleware/auth_proxy.go | 6 +- pkg/middleware/auth_proxy/auth_proxy.go | 136 ++-- pkg/middleware/auth_proxy/auth_proxy_test.go | 92 ++- pkg/middleware/middleware.go | 3 +- pkg/services/ldap/hooks.go | 5 - pkg/services/ldap/ldap.go | 582 ++++++++--------- pkg/services/ldap/ldap_helpers_test.go | 140 +++++ pkg/services/ldap/ldap_login_test.go | 120 +++- pkg/services/ldap/ldap_test.go | 583 ++++-------------- pkg/services/ldap/settings.go | 6 +- pkg/services/ldap/test.go | 49 +- pkg/services/multildap/multildap.go | 204 ++++++ pkg/services/sqlstore/sqlstore.go | 10 +- pkg/services/user/user.go | 39 ++ pkg/setting/setting.go | 1 + .../brianvoe/gofakeit/BENCHMARKS.md | 134 ++++ .../brianvoe/gofakeit/CODE_OF_CONDUCT.md | 46 ++ .../brianvoe/gofakeit/CONTRIBUTING.md | 1 + .../github.com/brianvoe/gofakeit/LICENSE.txt | 20 + vendor/github.com/brianvoe/gofakeit/README.md | 254 ++++++++ vendor/github.com/brianvoe/gofakeit/TODO.txt | 3 + .../github.com/brianvoe/gofakeit/address.go | 131 ++++ vendor/github.com/brianvoe/gofakeit/beer.go | 45 ++ vendor/github.com/brianvoe/gofakeit/bool.go | 10 + vendor/github.com/brianvoe/gofakeit/color.go | 44 ++ .../github.com/brianvoe/gofakeit/company.go | 30 + .../github.com/brianvoe/gofakeit/contact.go | 40 ++ .../github.com/brianvoe/gofakeit/currency.go | 38 ++ .../brianvoe/gofakeit/data/address.go | 15 + .../github.com/brianvoe/gofakeit/data/beer.go | 10 + .../brianvoe/gofakeit/data/colors.go | 7 + .../brianvoe/gofakeit/data/company.go | 9 + .../brianvoe/gofakeit/data/computer.go | 8 + .../brianvoe/gofakeit/data/contact.go | 6 + .../brianvoe/gofakeit/data/currency.go | 7 + .../github.com/brianvoe/gofakeit/data/data.go | 28 + .../brianvoe/gofakeit/data/datetime.go | 9 + .../brianvoe/gofakeit/data/files.go | 7 + .../brianvoe/gofakeit/data/hacker.go | 20 + .../brianvoe/gofakeit/data/hipster.go | 6 + .../brianvoe/gofakeit/data/internet.go | 8 + .../github.com/brianvoe/gofakeit/data/job.go | 8 + .../brianvoe/gofakeit/data/log_level.go | 8 + .../brianvoe/gofakeit/data/lorem.go | 6 + .../brianvoe/gofakeit/data/payment.go | 20 + .../brianvoe/gofakeit/data/person.go | 9 + .../brianvoe/gofakeit/data/status_code.go | 7 + .../brianvoe/gofakeit/data/vehicle.go | 10 + .../github.com/brianvoe/gofakeit/datetime.go | 77 +++ vendor/github.com/brianvoe/gofakeit/doc.go | 10 + vendor/github.com/brianvoe/gofakeit/faker.go | 15 + vendor/github.com/brianvoe/gofakeit/file.go | 11 + .../github.com/brianvoe/gofakeit/generate.go | 41 ++ vendor/github.com/brianvoe/gofakeit/hacker.go | 35 ++ .../github.com/brianvoe/gofakeit/hipster.go | 20 + vendor/github.com/brianvoe/gofakeit/image.go | 8 + .../github.com/brianvoe/gofakeit/internet.go | 55 ++ vendor/github.com/brianvoe/gofakeit/job.go | 34 + .../github.com/brianvoe/gofakeit/log_level.go | 15 + vendor/github.com/brianvoe/gofakeit/logo.png | Bin 0 -> 36022 bytes vendor/github.com/brianvoe/gofakeit/misc.go | 132 ++++ vendor/github.com/brianvoe/gofakeit/name.go | 26 + vendor/github.com/brianvoe/gofakeit/number.go | 84 +++ .../github.com/brianvoe/gofakeit/password.go | 68 ++ .../github.com/brianvoe/gofakeit/payment.go | 81 +++ vendor/github.com/brianvoe/gofakeit/person.go | 45 ++ .../brianvoe/gofakeit/status_code.go | 11 + vendor/github.com/brianvoe/gofakeit/string.go | 48 ++ vendor/github.com/brianvoe/gofakeit/struct.go | 87 +++ vendor/github.com/brianvoe/gofakeit/unique.go | 34 + .../brianvoe/gofakeit/user_agent.go | 92 +++ .../github.com/brianvoe/gofakeit/vehicle.go | 55 ++ vendor/github.com/brianvoe/gofakeit/words.go | 100 +++ vendor/github.com/robfig/cron/README.md | 2 +- vendor/github.com/robfig/cron/doc.go | 2 +- vendor/modules.txt | 3 + 83 files changed, 3375 insertions(+), 991 deletions(-) delete mode 100644 pkg/services/ldap/hooks.go create mode 100644 pkg/services/ldap/ldap_helpers_test.go create mode 100644 pkg/services/multildap/multildap.go create mode 100644 pkg/services/user/user.go create mode 100644 vendor/github.com/brianvoe/gofakeit/BENCHMARKS.md create mode 100644 vendor/github.com/brianvoe/gofakeit/CODE_OF_CONDUCT.md create mode 100644 vendor/github.com/brianvoe/gofakeit/CONTRIBUTING.md create mode 100644 vendor/github.com/brianvoe/gofakeit/LICENSE.txt create mode 100644 vendor/github.com/brianvoe/gofakeit/README.md create mode 100644 vendor/github.com/brianvoe/gofakeit/TODO.txt create mode 100644 vendor/github.com/brianvoe/gofakeit/address.go create mode 100644 vendor/github.com/brianvoe/gofakeit/beer.go create mode 100644 vendor/github.com/brianvoe/gofakeit/bool.go create mode 100644 vendor/github.com/brianvoe/gofakeit/color.go create mode 100644 vendor/github.com/brianvoe/gofakeit/company.go create mode 100644 vendor/github.com/brianvoe/gofakeit/contact.go create mode 100644 vendor/github.com/brianvoe/gofakeit/currency.go create mode 100644 vendor/github.com/brianvoe/gofakeit/data/address.go create mode 100644 vendor/github.com/brianvoe/gofakeit/data/beer.go create mode 100644 vendor/github.com/brianvoe/gofakeit/data/colors.go create mode 100644 vendor/github.com/brianvoe/gofakeit/data/company.go create mode 100644 vendor/github.com/brianvoe/gofakeit/data/computer.go create mode 100644 vendor/github.com/brianvoe/gofakeit/data/contact.go create mode 100644 vendor/github.com/brianvoe/gofakeit/data/currency.go create mode 100644 vendor/github.com/brianvoe/gofakeit/data/data.go create mode 100644 vendor/github.com/brianvoe/gofakeit/data/datetime.go create mode 100644 vendor/github.com/brianvoe/gofakeit/data/files.go create mode 100644 vendor/github.com/brianvoe/gofakeit/data/hacker.go create mode 100644 vendor/github.com/brianvoe/gofakeit/data/hipster.go create mode 100644 vendor/github.com/brianvoe/gofakeit/data/internet.go create mode 100644 vendor/github.com/brianvoe/gofakeit/data/job.go create mode 100644 vendor/github.com/brianvoe/gofakeit/data/log_level.go create mode 100644 vendor/github.com/brianvoe/gofakeit/data/lorem.go create mode 100644 vendor/github.com/brianvoe/gofakeit/data/payment.go create mode 100644 vendor/github.com/brianvoe/gofakeit/data/person.go create mode 100644 vendor/github.com/brianvoe/gofakeit/data/status_code.go create mode 100644 vendor/github.com/brianvoe/gofakeit/data/vehicle.go create mode 100644 vendor/github.com/brianvoe/gofakeit/datetime.go create mode 100644 vendor/github.com/brianvoe/gofakeit/doc.go create mode 100644 vendor/github.com/brianvoe/gofakeit/faker.go create mode 100644 vendor/github.com/brianvoe/gofakeit/file.go create mode 100644 vendor/github.com/brianvoe/gofakeit/generate.go create mode 100644 vendor/github.com/brianvoe/gofakeit/hacker.go create mode 100644 vendor/github.com/brianvoe/gofakeit/hipster.go create mode 100644 vendor/github.com/brianvoe/gofakeit/image.go create mode 100644 vendor/github.com/brianvoe/gofakeit/internet.go create mode 100644 vendor/github.com/brianvoe/gofakeit/job.go create mode 100644 vendor/github.com/brianvoe/gofakeit/log_level.go create mode 100644 vendor/github.com/brianvoe/gofakeit/logo.png create mode 100644 vendor/github.com/brianvoe/gofakeit/misc.go create mode 100644 vendor/github.com/brianvoe/gofakeit/name.go create mode 100644 vendor/github.com/brianvoe/gofakeit/number.go create mode 100644 vendor/github.com/brianvoe/gofakeit/password.go create mode 100644 vendor/github.com/brianvoe/gofakeit/payment.go create mode 100644 vendor/github.com/brianvoe/gofakeit/person.go create mode 100644 vendor/github.com/brianvoe/gofakeit/status_code.go create mode 100644 vendor/github.com/brianvoe/gofakeit/string.go create mode 100644 vendor/github.com/brianvoe/gofakeit/struct.go create mode 100644 vendor/github.com/brianvoe/gofakeit/unique.go create mode 100644 vendor/github.com/brianvoe/gofakeit/user_agent.go create mode 100644 vendor/github.com/brianvoe/gofakeit/vehicle.go create mode 100644 vendor/github.com/brianvoe/gofakeit/words.go diff --git a/go.mod b/go.mod index 106d8b72661..619f5183a5c 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/aws/aws-sdk-go v1.18.5 github.com/benbjohnson/clock v0.0.0-20161215174838-7dc76406b6d3 github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737 + github.com/brianvoe/gofakeit v3.17.0+incompatible github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd // indirect github.com/codegangsta/cli v1.20.0 github.com/davecgh/go-spew v1.1.1 diff --git a/go.sum b/go.sum index 55223ecbc74..3c77812fbf7 100644 --- a/go.sum +++ b/go.sum @@ -16,6 +16,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLM github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737 h1:rRISKWyXfVxvoa702s91Zl5oREZTrR3yv+tXrrX7G/g= github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60= +github.com/brianvoe/gofakeit v3.17.0+incompatible h1:C1+30+c0GtjgGDtRC+iePZeP1WMiwsWCELNJhmc7aIc= +github.com/brianvoe/gofakeit v3.17.0+incompatible/go.mod h1:kfwdRA90vvNhPutZWfH7WPaDzUjz+CZFqG+rPkOjGOc= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w= diff --git a/pkg/extensions/main.go b/pkg/extensions/main.go index 6ee742a4d8e..cbe9ec2b7b0 100644 --- a/pkg/extensions/main.go +++ b/pkg/extensions/main.go @@ -1,6 +1,7 @@ package extensions import ( + _ "github.com/brianvoe/gofakeit" _ "github.com/gobwas/glob" _ "github.com/robfig/cron" _ "gopkg.in/square/go-jose.v2" diff --git a/pkg/login/auth.go b/pkg/login/auth.go index 56f614d92de..51eda933c6e 100644 --- a/pkg/login/auth.go +++ b/pkg/login/auth.go @@ -4,8 +4,8 @@ import ( "errors" "github.com/grafana/grafana/pkg/bus" - m "github.com/grafana/grafana/pkg/models" - LDAP "github.com/grafana/grafana/pkg/services/ldap" + "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/ldap" ) var ( @@ -25,7 +25,8 @@ func Init() { bus.AddHandler("auth", AuthenticateUser) } -func AuthenticateUser(query *m.LoginUserQuery) error { +// AuthenticateUser authenticates the user via username & password +func AuthenticateUser(query *models.LoginUserQuery) error { if err := validateLoginAttempts(query.Username); err != nil { return err } @@ -35,24 +36,24 @@ func AuthenticateUser(query *m.LoginUserQuery) error { } err := loginUsingGrafanaDB(query) - if err == nil || (err != m.ErrUserNotFound && err != ErrInvalidCredentials) { + if err == nil || (err != models.ErrUserNotFound && err != ErrInvalidCredentials) { return err } ldapEnabled, ldapErr := loginUsingLdap(query) if ldapEnabled { - if ldapErr == nil || ldapErr != LDAP.ErrInvalidCredentials { + if ldapErr == nil || ldapErr != ldap.ErrInvalidCredentials { return ldapErr } err = ldapErr } - if err == ErrInvalidCredentials || err == LDAP.ErrInvalidCredentials { + if err == ErrInvalidCredentials || err == ldap.ErrInvalidCredentials { saveInvalidLoginAttempt(query) } - if err == m.ErrUserNotFound { + if err == models.ErrUserNotFound { return ErrInvalidCredentials } diff --git a/pkg/login/auth_test.go b/pkg/login/auth_test.go index 85ad3bc07dc..658560b0ce1 100644 --- a/pkg/login/auth_test.go +++ b/pkg/login/auth_test.go @@ -6,8 +6,8 @@ import ( . "github.com/smartystreets/goconvey/convey" - m "github.com/grafana/grafana/pkg/models" - LDAP "github.com/grafana/grafana/pkg/services/ldap" + "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/ldap" ) func TestAuthenticateUser(t *testing.T) { @@ -17,7 +17,7 @@ func TestAuthenticateUser(t *testing.T) { mockLoginUsingGrafanaDB(nil, sc) mockLoginUsingLdap(false, nil, sc) - loginQuery := m.LoginUserQuery{ + loginQuery := models.LoginUserQuery{ Username: "user", Password: "", } @@ -84,7 +84,7 @@ func TestAuthenticateUser(t *testing.T) { authScenario("When a non-existing grafana user authenticate and ldap disabled", func(sc *authScenarioContext) { mockLoginAttemptValidation(nil, sc) - mockLoginUsingGrafanaDB(m.ErrUserNotFound, sc) + mockLoginUsingGrafanaDB(models.ErrUserNotFound, sc) mockLoginUsingLdap(false, nil, sc) mockSaveInvalidLoginAttempt(sc) @@ -101,14 +101,14 @@ func TestAuthenticateUser(t *testing.T) { authScenario("When a non-existing grafana user authenticate and invalid ldap credentials", func(sc *authScenarioContext) { mockLoginAttemptValidation(nil, sc) - mockLoginUsingGrafanaDB(m.ErrUserNotFound, sc) - mockLoginUsingLdap(true, LDAP.ErrInvalidCredentials, sc) + mockLoginUsingGrafanaDB(models.ErrUserNotFound, sc) + mockLoginUsingLdap(true, ldap.ErrInvalidCredentials, sc) mockSaveInvalidLoginAttempt(sc) err := AuthenticateUser(sc.loginUserQuery) Convey("it should result in", func() { - So(err, ShouldEqual, LDAP.ErrInvalidCredentials) + So(err, ShouldEqual, ldap.ErrInvalidCredentials) So(sc.loginAttemptValidationWasCalled, ShouldBeTrue) So(sc.grafanaLoginWasCalled, ShouldBeTrue) So(sc.ldapLoginWasCalled, ShouldBeTrue) @@ -118,7 +118,7 @@ func TestAuthenticateUser(t *testing.T) { authScenario("When a non-existing grafana user authenticate and valid ldap credentials", func(sc *authScenarioContext) { mockLoginAttemptValidation(nil, sc) - mockLoginUsingGrafanaDB(m.ErrUserNotFound, sc) + mockLoginUsingGrafanaDB(models.ErrUserNotFound, sc) mockLoginUsingLdap(true, nil, sc) mockSaveInvalidLoginAttempt(sc) @@ -136,7 +136,7 @@ func TestAuthenticateUser(t *testing.T) { authScenario("When a non-existing grafana user authenticate and ldap returns unexpected error", func(sc *authScenarioContext) { customErr := errors.New("custom") mockLoginAttemptValidation(nil, sc) - mockLoginUsingGrafanaDB(m.ErrUserNotFound, sc) + mockLoginUsingGrafanaDB(models.ErrUserNotFound, sc) mockLoginUsingLdap(true, customErr, sc) mockSaveInvalidLoginAttempt(sc) @@ -154,13 +154,13 @@ func TestAuthenticateUser(t *testing.T) { authScenario("When grafana user authenticate with invalid credentials and invalid ldap credentials", func(sc *authScenarioContext) { mockLoginAttemptValidation(nil, sc) mockLoginUsingGrafanaDB(ErrInvalidCredentials, sc) - mockLoginUsingLdap(true, LDAP.ErrInvalidCredentials, sc) + mockLoginUsingLdap(true, ldap.ErrInvalidCredentials, sc) mockSaveInvalidLoginAttempt(sc) err := AuthenticateUser(sc.loginUserQuery) Convey("it should result in", func() { - So(err, ShouldEqual, LDAP.ErrInvalidCredentials) + So(err, ShouldEqual, ldap.ErrInvalidCredentials) So(sc.loginAttemptValidationWasCalled, ShouldBeTrue) So(sc.grafanaLoginWasCalled, ShouldBeTrue) So(sc.ldapLoginWasCalled, ShouldBeTrue) @@ -171,7 +171,7 @@ func TestAuthenticateUser(t *testing.T) { } type authScenarioContext struct { - loginUserQuery *m.LoginUserQuery + loginUserQuery *models.LoginUserQuery grafanaLoginWasCalled bool ldapLoginWasCalled bool loginAttemptValidationWasCalled bool @@ -181,14 +181,14 @@ type authScenarioContext struct { type authScenarioFunc func(sc *authScenarioContext) func mockLoginUsingGrafanaDB(err error, sc *authScenarioContext) { - loginUsingGrafanaDB = func(query *m.LoginUserQuery) error { + loginUsingGrafanaDB = func(query *models.LoginUserQuery) error { sc.grafanaLoginWasCalled = true return err } } func mockLoginUsingLdap(enabled bool, err error, sc *authScenarioContext) { - loginUsingLdap = func(query *m.LoginUserQuery) (bool, error) { + loginUsingLdap = func(query *models.LoginUserQuery) (bool, error) { sc.ldapLoginWasCalled = true return enabled, err } @@ -202,7 +202,7 @@ func mockLoginAttemptValidation(err error, sc *authScenarioContext) { } func mockSaveInvalidLoginAttempt(sc *authScenarioContext) { - saveInvalidLoginAttempt = func(query *m.LoginUserQuery) { + saveInvalidLoginAttempt = func(query *models.LoginUserQuery) { sc.saveInvalidLoginAttemptWasCalled = true } } @@ -215,7 +215,7 @@ func authScenario(desc string, fn authScenarioFunc) { origSaveInvalidLoginAttempt := saveInvalidLoginAttempt sc := &authScenarioContext{ - loginUserQuery: &m.LoginUserQuery{ + loginUserQuery: &models.LoginUserQuery{ Username: "user", Password: "pwd", IpAddress: "192.168.1.1:56433", diff --git a/pkg/login/ldap_login.go b/pkg/login/ldap_login.go index abd861cbe6d..0b41e612a2e 100644 --- a/pkg/login/ldap_login.go +++ b/pkg/login/ldap_login.go @@ -2,13 +2,20 @@ package login import ( "github.com/grafana/grafana/pkg/models" - LDAP "github.com/grafana/grafana/pkg/services/ldap" + "github.com/grafana/grafana/pkg/services/multildap" + "github.com/grafana/grafana/pkg/services/user" + "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/util/errutil" ) -var newLDAP = LDAP.New -var getLDAPConfig = LDAP.GetConfig -var isLDAPEnabled = LDAP.IsEnabled +// getLDAPConfig gets LDAP config +var getLDAPConfig = multildap.GetConfig + +// isLDAPEnabled checks if LDAP is enabled +var isLDAPEnabled = multildap.IsEnabled + +// newLDAP creates multiple LDAP instance +var newLDAP = multildap.New // loginUsingLdap logs in user using LDAP. It returns whether LDAP is enabled and optional error and query arg will be // populated with the logged in user if successful. @@ -23,18 +30,21 @@ var loginUsingLdap = func(query *models.LoginUserQuery) (bool, error) { if err != nil { return true, errutil.Wrap("Failed to get LDAP config", err) } - if len(config.Servers) == 0 { - return true, ErrNoLDAPServers - } - for _, server := range config.Servers { - auth := newLDAP(server) + externalUser, err := newLDAP(config.Servers).Login(query) + if err != nil { + return true, err + } - err := auth.Login(query) - if err == nil || err != LDAP.ErrInvalidCredentials { - return true, err - } + login, err := user.Upsert(&user.UpsertArgs{ + ExternalUser: externalUser, + SignupAllowed: setting.LdapAllowSignup, + }) + if err != nil { + return true, err } - return true, LDAP.ErrInvalidCredentials + query.User = login + + return true, nil } diff --git a/pkg/login/ldap_login_test.go b/pkg/login/ldap_login_test.go index 3ea82d0a8ed..b93a4890bc3 100644 --- a/pkg/login/ldap_login_test.go +++ b/pkg/login/ldap_login_test.go @@ -6,8 +6,9 @@ import ( . "github.com/smartystreets/goconvey/convey" - m "github.com/grafana/grafana/pkg/models" - LDAP "github.com/grafana/grafana/pkg/services/ldap" + "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/ldap" + "github.com/grafana/grafana/pkg/services/multildap" "github.com/grafana/grafana/pkg/setting" ) @@ -18,11 +19,11 @@ func TestLdapLogin(t *testing.T) { Convey("Given ldap enabled and no server configured", func() { setting.LdapEnabled = true - ldapLoginScenario("When login", func(sc *ldapLoginScenarioContext) { + LDAPLoginScenario("When login", func(sc *LDAPLoginScenarioContext) { sc.withLoginResult(false) - getLDAPConfig = func() (*LDAP.Config, error) { - config := &LDAP.Config{ - Servers: []*LDAP.ServerConfig{}, + getLDAPConfig = func() (*ldap.Config, error) { + config := &ldap.Config{ + Servers: []*ldap.ServerConfig{}, } return config, nil @@ -35,11 +36,11 @@ func TestLdapLogin(t *testing.T) { }) Convey("it should return no LDAP servers error", func() { - So(err, ShouldEqual, ErrNoLDAPServers) + So(err, ShouldEqual, errTest) }) Convey("it should not call ldap login", func() { - So(sc.ldapAuthenticatorMock.loginCalled, ShouldBeFalse) + So(sc.LDAPAuthenticatorMock.loginCalled, ShouldBeTrue) }) }) }) @@ -47,9 +48,9 @@ func TestLdapLogin(t *testing.T) { Convey("Given ldap disabled", func() { setting.LdapEnabled = false - ldapLoginScenario("When login", func(sc *ldapLoginScenarioContext) { + LDAPLoginScenario("When login", func(sc *LDAPLoginScenarioContext) { sc.withLoginResult(false) - enabled, err := loginUsingLdap(&m.LoginUserQuery{ + enabled, err := loginUsingLdap(&models.LoginUserQuery{ Username: "user", Password: "pwd", }) @@ -63,75 +64,88 @@ func TestLdapLogin(t *testing.T) { }) Convey("it should not call ldap login", func() { - So(sc.ldapAuthenticatorMock.loginCalled, ShouldBeFalse) + So(sc.LDAPAuthenticatorMock.loginCalled, ShouldBeFalse) }) }) }) }) } -func mockLdapAuthenticator(valid bool) *mockAuth { - mock := &mockAuth{ - validLogin: valid, - } - - newLDAP = func(server *LDAP.ServerConfig) LDAP.IAuth { - return mock - } - - return mock -} - type mockAuth struct { validLogin bool loginCalled bool } -func (auth *mockAuth) Login(query *m.LoginUserQuery) error { +func (auth *mockAuth) Login(query *models.LoginUserQuery) ( + *models.ExternalUserInfo, + error, +) { auth.loginCalled = true if !auth.validLogin { - return errTest + return nil, errTest } - return nil + return nil, nil } -func (auth *mockAuth) Users() ([]*LDAP.UserInfo, error) { +func (auth *mockAuth) Users(logins []string) ( + []*models.ExternalUserInfo, + error, +) { return nil, nil } -func (auth *mockAuth) SyncUser(query *m.LoginUserQuery) error { +func (auth *mockAuth) User(login string) ( + *models.ExternalUserInfo, + error, +) { + return nil, nil +} + +func (auth *mockAuth) Add(dn string, values map[string][]string) error { return nil } -func (auth *mockAuth) GetGrafanaUserFor(ctx *m.ReqContext, ldapUser *LDAP.UserInfo) (*m.User, error) { - return nil, nil +func (auth *mockAuth) Remove(dn string) error { + return nil +} + +func mockLDAPAuthenticator(valid bool) *mockAuth { + mock := &mockAuth{ + validLogin: valid, + } + + newLDAP = func(servers []*ldap.ServerConfig) multildap.IMultiLDAP { + return mock + } + + return mock } -type ldapLoginScenarioContext struct { - loginUserQuery *m.LoginUserQuery - ldapAuthenticatorMock *mockAuth +type LDAPLoginScenarioContext struct { + loginUserQuery *models.LoginUserQuery + LDAPAuthenticatorMock *mockAuth } -type ldapLoginScenarioFunc func(c *ldapLoginScenarioContext) +type LDAPLoginScenarioFunc func(c *LDAPLoginScenarioContext) -func ldapLoginScenario(desc string, fn ldapLoginScenarioFunc) { +func LDAPLoginScenario(desc string, fn LDAPLoginScenarioFunc) { Convey(desc, func() { mock := &mockAuth{} - sc := &ldapLoginScenarioContext{ - loginUserQuery: &m.LoginUserQuery{ + sc := &LDAPLoginScenarioContext{ + loginUserQuery: &models.LoginUserQuery{ Username: "user", Password: "pwd", IpAddress: "192.168.1.1:56433", }, - ldapAuthenticatorMock: mock, + LDAPAuthenticatorMock: mock, } - getLDAPConfig = func() (*LDAP.Config, error) { - config := &LDAP.Config{ - Servers: []*LDAP.ServerConfig{ + getLDAPConfig = func() (*ldap.Config, error) { + config := &ldap.Config{ + Servers: []*ldap.ServerConfig{ { Host: "", }, @@ -141,19 +155,19 @@ func ldapLoginScenario(desc string, fn ldapLoginScenarioFunc) { return config, nil } - newLDAP = func(server *LDAP.ServerConfig) LDAP.IAuth { + newLDAP = func(server []*ldap.ServerConfig) multildap.IMultiLDAP { return mock } defer func() { - newLDAP = LDAP.New - getLDAPConfig = LDAP.GetConfig + newLDAP = multildap.New + getLDAPConfig = multildap.GetConfig }() fn(sc) }) } -func (sc *ldapLoginScenarioContext) withLoginResult(valid bool) { - sc.ldapAuthenticatorMock = mockLdapAuthenticator(valid) +func (sc *LDAPLoginScenarioContext) withLoginResult(valid bool) { + sc.LDAPAuthenticatorMock = mockLDAPAuthenticator(valid) } diff --git a/pkg/middleware/auth_proxy.go b/pkg/middleware/auth_proxy.go index d3d8d8e77d8..890fd5e4f24 100644 --- a/pkg/middleware/auth_proxy.go +++ b/pkg/middleware/auth_proxy.go @@ -35,8 +35,8 @@ func initContextWithAuthProxy(store *remotecache.RemoteCache, ctx *m.ReqContext, return true } - // Try to get user id from various sources - id, err := auth.GetUserID() + // Try to log in user from various providers + id, err := auth.Login() if err != nil { ctx.Handle(500, err.Error(), err.DetailsError) return true @@ -54,7 +54,7 @@ func initContextWithAuthProxy(store *remotecache.RemoteCache, ctx *m.ReqContext, ctx.IsSignedIn = true // Remember user data it in cache - if err := auth.Remember(); err != nil { + if err := auth.Remember(id); err != nil { ctx.Handle(500, err.Error(), err.DetailsError) return true } diff --git a/pkg/middleware/auth_proxy/auth_proxy.go b/pkg/middleware/auth_proxy/auth_proxy.go index 98bacbeccf4..4cb2de38c7f 100644 --- a/pkg/middleware/auth_proxy/auth_proxy.go +++ b/pkg/middleware/auth_proxy/auth_proxy.go @@ -12,6 +12,8 @@ import ( "github.com/grafana/grafana/pkg/infra/remotecache" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/ldap" + "github.com/grafana/grafana/pkg/services/multildap" + "github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/setting" ) @@ -21,10 +23,14 @@ const ( CachePrefix = "auth-proxy-sync-ttl:%s" ) -var ( - getLDAPConfig = ldap.GetConfig - isLDAPEnabled = ldap.IsEnabled -) +// getLDAPConfig gets LDAP config +var getLDAPConfig = ldap.GetConfig + +// isLDAPEnabled checks if LDAP is enabled +var isLDAPEnabled = ldap.IsEnabled + +// newLDAP creates multiple LDAP instance +var newLDAP = multildap.New // AuthProxy struct type AuthProxy struct { @@ -33,13 +39,13 @@ type AuthProxy struct { orgID int64 header string - LDAP func(server *ldap.ServerConfig) ldap.IAuth - - enabled bool - whitelistIP string - headerType string - headers map[string]string - cacheTTL int + enabled bool + LdapAllowSignup bool + AuthProxyAutoSignUp bool + whitelistIP string + headerType string + headers map[string]string + cacheTTL int } // Error auth proxy specific error @@ -78,13 +84,13 @@ func New(options *Options) *AuthProxy { orgID: options.OrgID, header: header, - LDAP: ldap.New, - - enabled: setting.AuthProxyEnabled, - headerType: setting.AuthProxyHeaderProperty, - headers: setting.AuthProxyHeaders, - whitelistIP: setting.AuthProxyWhitelist, - cacheTTL: setting.AuthProxyLdapSyncTtl, + enabled: setting.AuthProxyEnabled, + headerType: setting.AuthProxyHeaderProperty, + headers: setting.AuthProxyHeaders, + whitelistIP: setting.AuthProxyWhitelist, + cacheTTL: setting.AuthProxyLdapSyncTtl, + LdapAllowSignup: setting.LdapAllowSignup, + AuthProxyAutoSignUp: setting.AuthProxyAutoSignUp, } } @@ -144,34 +150,22 @@ func (auth *AuthProxy) IsAllowedIP() (bool, *Error) { return false, newError("Proxy authentication required", err) } -// InCache checks if we have user in cache -func (auth *AuthProxy) InCache() bool { - userID, _ := auth.GetUserIDViaCache() - - if userID == 0 { - return false - } - - return true -} - // getKey forms a key for the cache func (auth *AuthProxy) getKey() string { return fmt.Sprintf(CachePrefix, auth.header) } -// GetUserID gets user id with whatever means possible -func (auth *AuthProxy) GetUserID() (int64, *Error) { - if auth.InCache() { +// Login logs in user id with whatever means possible +func (auth *AuthProxy) Login() (int64, *Error) { + id, _ := auth.GetUserViaCache() + if id != 0 { // Error here means absent cache - we don't need to handle that - id, _ := auth.GetUserIDViaCache() - return id, nil } if isLDAPEnabled() { - id, err := auth.GetUserIDViaLDAP() + id, err := auth.LoginViaLDAP() if err == ldap.ErrInvalidCredentials { return 0, newError( @@ -181,16 +175,16 @@ func (auth *AuthProxy) GetUserID() (int64, *Error) { } if err != nil { - return 0, newError("Failed to sync user", err) + return 0, newError("Failed to get the user", err) } return id, nil } - id, err := auth.GetUserIDViaHeader() + id, err := auth.LoginViaHeader() if err != nil { return 0, newError( - "Failed to login as user specified in auth proxy header", + "Failed to log in as user, specified in auth proxy header", err, ) } @@ -198,8 +192,8 @@ func (auth *AuthProxy) GetUserID() (int64, *Error) { return id, nil } -// GetUserIDViaCache gets the user from cache -func (auth *AuthProxy) GetUserIDViaCache() (int64, error) { +// GetUserViaCache gets user id from cache +func (auth *AuthProxy) GetUserViaCache() (int64, error) { var ( cacheKey = auth.getKey() userID, err = auth.store.Get(cacheKey) @@ -212,33 +206,34 @@ func (auth *AuthProxy) GetUserIDViaCache() (int64, error) { return userID.(int64), nil } -// GetUserIDViaLDAP gets user via LDAP request -func (auth *AuthProxy) GetUserIDViaLDAP() (int64, *Error) { - query := &models.LoginUserQuery{ - ReqContext: auth.ctx, - Username: auth.header, - } - +// LoginViaLDAP logs in user via LDAP request +func (auth *AuthProxy) LoginViaLDAP() (int64, *Error) { config, err := getLDAPConfig() if err != nil { return 0, newError("Failed to get LDAP config", nil) } - if len(config.Servers) == 0 { - return 0, newError("No LDAP servers available", nil) + + extUser, err := newLDAP(config.Servers).User(auth.header) + if err != nil { + return 0, newError(err.Error(), nil) } - for _, server := range config.Servers { - author := auth.LDAP(server) - if err := author.SyncUser(query); err != nil { - return 0, newError(err.Error(), nil) - } + // Have to sync grafana and LDAP user during log in + user, err := user.Upsert(&user.UpsertArgs{ + ReqContext: auth.ctx, + SignupAllowed: auth.LdapAllowSignup, + ExternalUser: extUser, + }) + if err != nil { + return 0, newError(err.Error(), nil) } - return query.User.Id, nil + return user.Id, nil } -// GetUserIDViaHeader gets user from the header only -func (auth *AuthProxy) GetUserIDViaHeader() (int64, error) { +// LoginViaHeader logs in user from the header only +// TODO: refactor - cyclomatic complexity should be much lower +func (auth *AuthProxy) LoginViaHeader() (int64, error) { extUser := &models.ExternalUserInfo{ AuthModule: "authproxy", AuthId: auth.header, @@ -269,18 +264,16 @@ func (auth *AuthProxy) GetUserIDViaHeader() (int64, error) { } } - // add/update user in grafana - cmd := &models.UpsertUserCommand{ + result, err := user.Upsert(&user.UpsertArgs{ ReqContext: auth.ctx, + SignupAllowed: true, ExternalUser: extUser, - SignupAllowed: setting.AuthProxyAutoSignUp, - } - err := bus.Dispatch(cmd) + }) if err != nil { return 0, err } - return cmd.Result.Id, nil + return result.Id, nil } // GetSignedUser get full signed user info @@ -298,21 +291,18 @@ func (auth *AuthProxy) GetSignedUser(userID int64) (*models.SignedInUser, *Error } // Remember user in cache -func (auth *AuthProxy) Remember() *Error { +func (auth *AuthProxy) Remember(id int64) *Error { + key := auth.getKey() - // Make sure we do not rewrite the expiration time - if auth.InCache() { + // Check if user already in cache + userID, _ := auth.store.Get(key) + if userID != nil { return nil } - var ( - key = auth.getKey() - value, _ = auth.GetUserIDViaCache() - expiration = time.Duration(-auth.cacheTTL) * time.Minute - - err = auth.store.Set(key, value, expiration) - ) + expiration := time.Duration(-auth.cacheTTL) * time.Minute + err := auth.store.Set(key, id, expiration) if err != nil { return newError(err.Error(), nil) } diff --git a/pkg/middleware/auth_proxy/auth_proxy_test.go b/pkg/middleware/auth_proxy/auth_proxy_test.go index fbddca81d40..4a1edc66ba5 100644 --- a/pkg/middleware/auth_proxy/auth_proxy_test.go +++ b/pkg/middleware/auth_proxy/auth_proxy_test.go @@ -1,6 +1,7 @@ package authproxy import ( + "errors" "fmt" "net/http" "testing" @@ -8,24 +9,40 @@ import ( . "github.com/smartystreets/goconvey/convey" "gopkg.in/macaron.v1" + "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/infra/remotecache" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/ldap" + "github.com/grafana/grafana/pkg/services/multildap" "github.com/grafana/grafana/pkg/setting" ) -type TestLDAP struct { - ldap.Auth - ID int64 - syncCalled bool +type TestMultiLDAP struct { + multildap.MultiLDAP + ID int64 + userCalled bool + loginCalled bool } -func (stub *TestLDAP) SyncUser(query *models.LoginUserQuery) error { - stub.syncCalled = true - query.User = &models.User{ - Id: stub.ID, +func (stub *TestMultiLDAP) Login(query *models.LoginUserQuery) ( + *models.ExternalUserInfo, error, +) { + stub.loginCalled = true + result := &models.ExternalUserInfo{ + UserId: stub.ID, } - return nil + return result, nil +} + +func (stub *TestMultiLDAP) User(login string) ( + *models.ExternalUserInfo, + error, +) { + stub.userCalled = true + result := &models.ExternalUserInfo{ + UserId: stub.ID, + } + return result, nil } func TestMiddlewareContext(t *testing.T) { @@ -44,7 +61,7 @@ func TestMiddlewareContext(t *testing.T) { }, } - Convey("gets data from the cache", func() { + Convey("logs in user from the cache", func() { store := remotecache.NewFakeStore(t) key := fmt.Sprintf(CachePrefix, name) store.Set(key, int64(33), 0) @@ -55,53 +72,64 @@ func TestMiddlewareContext(t *testing.T) { OrgID: 4, }) - id, err := auth.GetUserID() + id, err := auth.Login() So(err, ShouldBeNil) So(id, ShouldEqual, 33) }) Convey("LDAP", func() { - Convey("gets data from the LDAP", func() { + Convey("logs in via LDAP", func() { + bus.AddHandler("test", func(cmd *models.UpsertUserCommand) error { + cmd.Result = &models.User{ + Id: 42, + } + + return nil + }) + isLDAPEnabled = func() bool { return true } + stub := &TestMultiLDAP{ + ID: 42, + } + getLDAPConfig = func() (*ldap.Config, error) { config := &ldap.Config{ Servers: []*ldap.ServerConfig{ - {}, + { + SearchBaseDNs: []string{"BaseDNHere"}, + }, }, } return config, nil } + newLDAP = func(servers []*ldap.ServerConfig) multildap.IMultiLDAP { + return stub + } + defer func() { + newLDAP = multildap.New isLDAPEnabled = ldap.IsEnabled getLDAPConfig = ldap.GetConfig }() store := remotecache.NewFakeStore(t) - auth := New(&Options{ + server := New(&Options{ Store: store, Ctx: ctx, OrgID: 4, }) - stub := &TestLDAP{ - ID: 42, - } - - auth.LDAP = func(server *ldap.ServerConfig) ldap.IAuth { - return stub - } - - id, err := auth.GetUserID() + id, err := server.Login() So(err, ShouldBeNil) So(id, ShouldEqual, 42) - So(stub.syncCalled, ShouldEqual, true) + So(stub.userCalled, ShouldEqual, true) }) Convey("gets nice error if ldap is enabled but not configured", func() { @@ -110,13 +138,11 @@ func TestMiddlewareContext(t *testing.T) { } getLDAPConfig = func() (*ldap.Config, error) { - config := &ldap.Config{ - Servers: []*ldap.ServerConfig{}, - } - return config, nil + return nil, errors.New("Something went wrong") } defer func() { + newLDAP = multildap.New isLDAPEnabled = ldap.IsEnabled getLDAPConfig = ldap.GetConfig }() @@ -129,20 +155,20 @@ func TestMiddlewareContext(t *testing.T) { OrgID: 4, }) - stub := &TestLDAP{ + stub := &TestMultiLDAP{ ID: 42, } - auth.LDAP = func(server *ldap.ServerConfig) ldap.IAuth { + newLDAP = func(servers []*ldap.ServerConfig) multildap.IMultiLDAP { return stub } - id, err := auth.GetUserID() + id, err := auth.Login() So(err, ShouldNotBeNil) - So(err.Error(), ShouldContainSubstring, "Failed to sync user") + So(err.Error(), ShouldContainSubstring, "Failed to get the user") So(id, ShouldNotEqual, 42) - So(stub.syncCalled, ShouldEqual, false) + So(stub.loginCalled, ShouldEqual, false) }) }) diff --git a/pkg/middleware/middleware.go b/pkg/middleware/middleware.go index ec7194a7fc6..1b465e7a4e5 100644 --- a/pkg/middleware/middleware.go +++ b/pkg/middleware/middleware.go @@ -7,6 +7,8 @@ import ( "strings" "time" + macaron "gopkg.in/macaron.v1" + "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/components/apikeygen" "github.com/grafana/grafana/pkg/infra/log" @@ -14,7 +16,6 @@ import ( m "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/util" - macaron "gopkg.in/macaron.v1" ) var ( diff --git a/pkg/services/ldap/hooks.go b/pkg/services/ldap/hooks.go deleted file mode 100644 index ece98e5e73b..00000000000 --- a/pkg/services/ldap/hooks.go +++ /dev/null @@ -1,5 +0,0 @@ -package ldap - -var ( - hookDial func(*Auth) error -) diff --git a/pkg/services/ldap/ldap.go b/pkg/services/ldap/ldap.go index d6b5b69d752..20953db97c3 100644 --- a/pkg/services/ldap/ldap.go +++ b/pkg/services/ldap/ldap.go @@ -8,39 +8,38 @@ import ( "io/ioutil" "strings" - "github.com/davecgh/go-spew/spew" - LDAP "gopkg.in/ldap.v3" + "gopkg.in/ldap.v3" - "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/infra/log" - models "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/setting" + "github.com/grafana/grafana/pkg/models" ) // IConnection is interface for LDAP connection manipulation type IConnection interface { Bind(username, password string) error UnauthenticatedBind(username string) error - Search(*LDAP.SearchRequest) (*LDAP.SearchResult, error) + Add(*ldap.AddRequest) error + Del(*ldap.DelRequest) error + Search(*ldap.SearchRequest) (*ldap.SearchResult, error) StartTLS(*tls.Config) error Close() } -// IAuth is interface for LDAP authorization -type IAuth interface { - Login(query *models.LoginUserQuery) error - SyncUser(query *models.LoginUserQuery) error - GetGrafanaUserFor( - ctx *models.ReqContext, - user *UserInfo, - ) (*models.User, error) - Users() ([]*UserInfo, error) +// IServer is interface for LDAP authorization +type IServer interface { + Login(*models.LoginUserQuery) (*models.ExternalUserInfo, error) + Add(string, map[string][]string) error + Remove(string) error + Users([]string) ([]*models.ExternalUserInfo, error) + ExtractGrafanaUser(*UserInfo) (*models.ExternalUserInfo, error) + Dial() error + Close() } -// Auth is basic struct of LDAP authorization -type Auth struct { - server *ServerConfig - conn IConnection +// Server is basic struct of LDAP authorization +type Server struct { + config *ServerConfig + connection IConnection requireSecondBind bool log log.Logger } @@ -52,28 +51,24 @@ var ( ) var dial = func(network, addr string) (IConnection, error) { - return LDAP.Dial(network, addr) + return ldap.Dial(network, addr) } // New creates the new LDAP auth -func New(server *ServerConfig) IAuth { - return &Auth{ - server: server, +func New(config *ServerConfig) IServer { + return &Server{ + config: config, log: log.New("ldap"), } } // Dial dials in the LDAP -func (auth *Auth) Dial() error { - if hookDial != nil { - return hookDial(auth) - } - +func (server *Server) Dial() error { var err error var certPool *x509.CertPool - if auth.server.RootCACert != "" { + if server.config.RootCACert != "" { certPool = x509.NewCertPool() - for _, caCertFile := range strings.Split(auth.server.RootCACert, " ") { + for _, caCertFile := range strings.Split(server.config.RootCACert, " ") { pem, err := ioutil.ReadFile(caCertFile) if err != nil { return err @@ -84,35 +79,35 @@ func (auth *Auth) Dial() error { } } var clientCert tls.Certificate - if auth.server.ClientCert != "" && auth.server.ClientKey != "" { - clientCert, err = tls.LoadX509KeyPair(auth.server.ClientCert, auth.server.ClientKey) + if server.config.ClientCert != "" && server.config.ClientKey != "" { + clientCert, err = tls.LoadX509KeyPair(server.config.ClientCert, server.config.ClientKey) if err != nil { return err } } - for _, host := range strings.Split(auth.server.Host, " ") { - address := fmt.Sprintf("%s:%d", host, auth.server.Port) - if auth.server.UseSSL { + for _, host := range strings.Split(server.config.Host, " ") { + address := fmt.Sprintf("%s:%d", host, server.config.Port) + if server.config.UseSSL { tlsCfg := &tls.Config{ - InsecureSkipVerify: auth.server.SkipVerifySSL, + InsecureSkipVerify: server.config.SkipVerifySSL, ServerName: host, RootCAs: certPool, } if len(clientCert.Certificate) > 0 { tlsCfg.Certificates = append(tlsCfg.Certificates, clientCert) } - if auth.server.StartTLS { - auth.conn, err = dial("tcp", address) + if server.config.StartTLS { + server.connection, err = dial("tcp", address) if err == nil { - if err = auth.conn.StartTLS(tlsCfg); err == nil { + if err = server.connection.StartTLS(tlsCfg); err == nil { return nil } } } else { - auth.conn, err = LDAP.DialTLS("tcp", address, tlsCfg) + server.connection, err = ldap.DialTLS("tcp", address, tlsCfg) } } else { - auth.conn, err = dial("tcp", address) + server.connection, err = dial("tcp", address) } if err == nil { @@ -122,91 +117,206 @@ func (auth *Auth) Dial() error { return err } -// Login logs in the user -func (auth *Auth) Login(query *models.LoginUserQuery) error { - // connect to ldap server - if err := auth.Dial(); err != nil { - return err - } - defer auth.conn.Close() +// Close closes the LDAP connection +func (server *Server) Close() { + server.connection.Close() +} - // perform initial authentication - if err := auth.initialBind(query.Username, query.Password); err != nil { - return err +// Login intialBinds the user, search it and then serialize it +func (server *Server) Login(query *models.LoginUserQuery) ( + *models.ExternalUserInfo, error, +) { + + // Perform initial authentication + err := server.intialBind(query.Username, query.Password) + if err != nil { + return nil, err } - // find user entry & attributes - user, err := auth.searchForUser(query.Username) + // Find user entry & attributes + users, err := server.Users([]string{query.Username}) if err != nil { - return err + return nil, err } - auth.log.Debug("Ldap User found", "info", spew.Sdump(user)) + // If we couldn't find the user - + // we should show incorrect credentials err + if len(users) == 0 { + return nil, ErrInvalidCredentials + } - // check if a second user bind is needed - if auth.requireSecondBind { - err = auth.secondBind(user, query.Password) + // Check if a second user bind is needed + user := users[0] + if server.requireSecondBind { + err = server.secondBind(user, query.Password) if err != nil { - return err + return nil, err } } - grafanaUser, err := auth.GetGrafanaUserFor(query.ReqContext, user) + return user, nil +} + +// Add adds stuff to LDAP +func (server *Server) Add(dn string, values map[string][]string) error { + err := server.intialBind( + server.config.BindDN, + server.config.BindPassword, + ) if err != nil { return err } - query.User = grafanaUser - return nil -} + attributes := make([]ldap.Attribute, 0) + for key, value := range values { + attributes = append(attributes, ldap.Attribute{ + Type: key, + Vals: value, + }) + } -// SyncUser syncs user with Grafana -func (auth *Auth) SyncUser(query *models.LoginUserQuery) error { - // connect to ldap server - err := auth.Dial() + request := &ldap.AddRequest{ + DN: dn, + Attributes: attributes, + } + + err = server.connection.Add(request) if err != nil { return err } - defer auth.conn.Close() - err = auth.serverBind() + return nil +} + +// Remove removes stuff from LDAP +func (server *Server) Remove(dn string) error { + err := server.intialBind( + server.config.BindDN, + server.config.BindPassword, + ) if err != nil { return err } - // find user entry & attributes - user, err := auth.searchForUser(query.Username) + request := ldap.NewDelRequest(dn, nil) + err = server.connection.Del(request) if err != nil { - auth.log.Error("Failed searching for user in ldap", "error", err) return err } - auth.log.Debug("Ldap User found", "info", spew.Sdump(user)) + return nil +} + +// Users gets LDAP users +func (server *Server) Users(logins []string) ( + []*models.ExternalUserInfo, + error, +) { + var result *ldap.SearchResult + var err error + var config = server.config - grafanaUser, err := auth.GetGrafanaUserFor(query.ReqContext, user) + for _, base := range config.SearchBaseDNs { + result, err = server.connection.Search( + server.getSearchRequest(base, logins), + ) + if err != nil { + return nil, err + } + + if len(result.Entries) > 0 { + break + } + } + + serializedUsers, err := server.serializeUsers(result) if err != nil { - return err + return nil, err + } + + return serializedUsers, nil +} + +// ExtractGrafanaUser extracts external user info from LDAP user +func (server *Server) ExtractGrafanaUser(user *UserInfo) (*models.ExternalUserInfo, error) { + result := server.buildGrafanaUser(user) + if err := server.validateGrafanaUser(result); err != nil { + return nil, err + } + + return result, nil +} + +// validateGrafanaUser validates user access. +// If there are no ldap group mappings access is true +// otherwise a single group must match +func (server *Server) validateGrafanaUser(user *models.ExternalUserInfo) error { + if len(server.config.Groups) > 0 && len(user.OrgRoles) < 1 { + server.log.Error( + "user does not belong in any of the specified LDAP groups", + "username", user.Login, + "groups", user.Groups, + ) + return ErrInvalidCredentials } - query.User = grafanaUser return nil } -func (auth *Auth) GetGrafanaUserFor( - ctx *models.ReqContext, - user *UserInfo, -) (*models.User, error) { +// getSearchRequest returns LDAP search request for users +func (server *Server) getSearchRequest( + base string, + logins []string, +) *ldap.SearchRequest { + attributes := []string{} + + inputs := server.config.Attr + attributes = appendIfNotEmpty( + attributes, + inputs.Username, + inputs.Surname, + inputs.Email, + inputs.Name, + inputs.MemberOf, + ) + + search := "" + for _, login := range logins { + query := strings.Replace( + server.config.SearchFilter, + "%s", ldap.EscapeFilter(login), + -1, + ) + + search = search + query + } + + filter := fmt.Sprintf("(|%s)", search) + + return &ldap.SearchRequest{ + BaseDN: base, + Scope: ldap.ScopeWholeSubtree, + DerefAliases: ldap.NeverDerefAliases, + Attributes: attributes, + Filter: filter, + } +} + +// buildGrafanaUser extracts info from UserInfo model to ExternalUserInfo +func (server *Server) buildGrafanaUser(user *UserInfo) *models.ExternalUserInfo { extUser := &models.ExternalUserInfo{ AuthModule: "ldap", AuthId: user.DN, - Name: fmt.Sprintf("%s %s", user.FirstName, user.LastName), - Login: user.Username, - Email: user.Email, - Groups: user.MemberOf, - OrgRoles: map[int64]models.RoleType{}, + Name: strings.TrimSpace( + fmt.Sprintf("%s %s", user.FirstName, user.LastName), + ), + Login: user.Username, + Email: user.Email, + Groups: user.MemberOf, + OrgRoles: map[int64]models.RoleType{}, } - for _, group := range auth.server.Groups { + for _, group := range server.config.Groups { // only use the first match for each org if extUser.OrgRoles[group.OrgId] != "" { continue @@ -220,49 +330,28 @@ func (auth *Auth) GetGrafanaUserFor( } } - // validate that the user has access - // if there are no ldap group mappings access is true - // otherwise a single group must match - if len(auth.server.Groups) > 0 && len(extUser.OrgRoles) < 1 { - auth.log.Info( - "Ldap Auth: user does not belong in any of the specified ldap groups", - "username", user.Username, - "groups", user.MemberOf, - ) - return nil, ErrInvalidCredentials - } - - // add/update user in grafana - upsertUserCmd := &models.UpsertUserCommand{ - ReqContext: ctx, - ExternalUser: extUser, - SignupAllowed: setting.LdapAllowSignup, - } - - err := bus.Dispatch(upsertUserCmd) - if err != nil { - return nil, err - } - - return upsertUserCmd.Result, nil + return extUser } -func (auth *Auth) serverBind() error { +func (server *Server) serverBind() error { bindFn := func() error { - return auth.conn.Bind(auth.server.BindDN, auth.server.BindPassword) + return server.connection.Bind( + server.config.BindDN, + server.config.BindPassword, + ) } - if auth.server.BindPassword == "" { + if server.config.BindPassword == "" { bindFn = func() error { - return auth.conn.UnauthenticatedBind(auth.server.BindDN) + return server.connection.UnauthenticatedBind(server.config.BindDN) } } // bind_dn and bind_password to bind if err := bindFn(); err != nil { - auth.log.Info("LDAP initial bind failed, %v", err) + server.log.Info("LDAP initial bind failed, %v", err) - if ldapErr, ok := err.(*LDAP.Error); ok { + if ldapErr, ok := err.(*ldap.Error); ok { if ldapErr.ResultCode == 49 { return ErrInvalidCredentials } @@ -273,11 +362,15 @@ func (auth *Auth) serverBind() error { return nil } -func (auth *Auth) secondBind(user *UserInfo, userPassword string) error { - if err := auth.conn.Bind(user.DN, userPassword); err != nil { - auth.log.Info("Second bind failed", "error", err) +func (server *Server) secondBind( + user *models.ExternalUserInfo, + userPassword string, +) error { + err := server.connection.Bind(user.AuthId, userPassword) + if err != nil { + server.log.Info("Second bind failed", "error", err) - if ldapErr, ok := err.(*LDAP.Error); ok { + if ldapErr, ok := err.(*ldap.Error); ok { if ldapErr.ResultCode == 49 { return ErrInvalidCredentials } @@ -288,31 +381,31 @@ func (auth *Auth) secondBind(user *UserInfo, userPassword string) error { return nil } -func (auth *Auth) initialBind(username, userPassword string) error { - if auth.server.BindPassword != "" || auth.server.BindDN == "" { - userPassword = auth.server.BindPassword - auth.requireSecondBind = true +func (server *Server) intialBind(username, userPassword string) error { + if server.config.BindPassword != "" || server.config.BindDN == "" { + userPassword = server.config.BindPassword + server.requireSecondBind = true } - bindPath := auth.server.BindDN + bindPath := server.config.BindDN if strings.Contains(bindPath, "%s") { - bindPath = fmt.Sprintf(auth.server.BindDN, username) + bindPath = fmt.Sprintf(server.config.BindDN, username) } bindFn := func() error { - return auth.conn.Bind(bindPath, userPassword) + return server.connection.Bind(bindPath, userPassword) } if userPassword == "" { bindFn = func() error { - return auth.conn.UnauthenticatedBind(bindPath) + return server.connection.UnauthenticatedBind(bindPath) } } if err := bindFn(); err != nil { - auth.log.Info("Initial bind failed", "error", err) + server.log.Info("Initial bind failed", "error", err) - if ldapErr, ok := err.(*LDAP.Error); ok { + if ldapErr, ok := err.(*ldap.Error); ok { if ldapErr.ResultCode == 49 { return ErrInvalidCredentials } @@ -323,199 +416,124 @@ func (auth *Auth) initialBind(username, userPassword string) error { return nil } -func (auth *Auth) searchForUser(username string) (*UserInfo, error) { - var searchResult *LDAP.SearchResult - var err error - - for _, searchBase := range auth.server.SearchBaseDNs { - attributes := make([]string, 0) - inputs := auth.server.Attr - attributes = appendIfNotEmpty(attributes, - inputs.Username, - inputs.Surname, - inputs.Email, - inputs.Name, - inputs.MemberOf) - - searchReq := LDAP.SearchRequest{ - BaseDN: searchBase, - Scope: LDAP.ScopeWholeSubtree, - DerefAliases: LDAP.NeverDerefAliases, - Attributes: attributes, - Filter: strings.Replace( - auth.server.SearchFilter, - "%s", LDAP.EscapeFilter(username), - -1, - ), - } - - auth.log.Debug("Ldap Search For User Request", "info", spew.Sdump(searchReq)) - - searchResult, err = auth.conn.Search(&searchReq) - if err != nil { - return nil, err - } - - if len(searchResult.Entries) > 0 { - break - } - } - - if len(searchResult.Entries) == 0 { - return nil, ErrInvalidCredentials - } - - if len(searchResult.Entries) > 1 { - return nil, errors.New("Ldap search matched more than one entry, please review your filter setting") - } - +// requestMemberOf use this function when POSIX LDAP schema does not support memberOf, so it manually search the groups +func (server *Server) requestMemberOf(searchResult *ldap.SearchResult) ([]string, error) { var memberOf []string - if auth.server.GroupSearchFilter == "" { - memberOf = getLdapAttrArray(auth.server.Attr.MemberOf, searchResult) - } else { - // If we are using a POSIX LDAP schema it won't support memberOf, so we manually search the groups - var groupSearchResult *LDAP.SearchResult - for _, groupSearchBase := range auth.server.GroupSearchBaseDNs { - var filter_replace string - if auth.server.GroupSearchFilterUserAttribute == "" { - filter_replace = getLdapAttr(auth.server.Attr.Username, searchResult) - } else { - filter_replace = getLdapAttr(auth.server.GroupSearchFilterUserAttribute, searchResult) - } - - filter := strings.Replace( - auth.server.GroupSearchFilter, "%s", - LDAP.EscapeFilter(filter_replace), - -1, - ) - - auth.log.Info("Searching for user's groups", "filter", filter) - - // support old way of reading settings - groupIdAttribute := auth.server.Attr.MemberOf - // but prefer dn attribute if default settings are used - if groupIdAttribute == "" || groupIdAttribute == "memberOf" { - groupIdAttribute = "dn" - } - - groupSearchReq := LDAP.SearchRequest{ - BaseDN: groupSearchBase, - Scope: LDAP.ScopeWholeSubtree, - DerefAliases: LDAP.NeverDerefAliases, - Attributes: []string{groupIdAttribute}, - Filter: filter, - } - - groupSearchResult, err = auth.conn.Search(&groupSearchReq) - if err != nil { - return nil, err - } - if len(groupSearchResult.Entries) > 0 { - for i := range groupSearchResult.Entries { - memberOf = append(memberOf, getLdapAttrN(groupIdAttribute, groupSearchResult, i)) - } - break - } + for _, groupSearchBase := range server.config.GroupSearchBaseDNs { + var filterReplace string + if server.config.GroupSearchFilterUserAttribute == "" { + filterReplace = getLdapAttr(server.config.Attr.Username, searchResult) + } else { + filterReplace = getLdapAttr(server.config.GroupSearchFilterUserAttribute, searchResult) } - } - - return &UserInfo{ - DN: searchResult.Entries[0].DN, - LastName: getLdapAttr(auth.server.Attr.Surname, searchResult), - FirstName: getLdapAttr(auth.server.Attr.Name, searchResult), - Username: getLdapAttr(auth.server.Attr.Username, searchResult), - Email: getLdapAttr(auth.server.Attr.Email, searchResult), - MemberOf: memberOf, - }, nil -} -func (ldap *Auth) Users() ([]*UserInfo, error) { - var result *LDAP.SearchResult - var err error - server := ldap.server - - if err := ldap.Dial(); err != nil { - return nil, err - } - defer ldap.conn.Close() - - for _, base := range server.SearchBaseDNs { - attributes := make([]string, 0) - inputs := server.Attr - attributes = appendIfNotEmpty( - attributes, - inputs.Username, - inputs.Surname, - inputs.Email, - inputs.Name, - inputs.MemberOf, + filter := strings.Replace( + server.config.GroupSearchFilter, "%s", + ldap.EscapeFilter(filterReplace), + -1, ) - req := LDAP.SearchRequest{ - BaseDN: base, - Scope: LDAP.ScopeWholeSubtree, - DerefAliases: LDAP.NeverDerefAliases, - Attributes: attributes, + server.log.Info("Searching for user's groups", "filter", filter) + + // support old way of reading settings + groupIDAttribute := server.config.Attr.MemberOf + // but prefer dn attribute if default settings are used + if groupIDAttribute == "" || groupIDAttribute == "memberOf" { + groupIDAttribute = "dn" + } - // Doing a star here to get all the users in one go - Filter: strings.Replace(server.SearchFilter, "%s", "*", -1), + groupSearchReq := ldap.SearchRequest{ + BaseDN: groupSearchBase, + Scope: ldap.ScopeWholeSubtree, + DerefAliases: ldap.NeverDerefAliases, + Attributes: []string{groupIDAttribute}, + Filter: filter, } - result, err = ldap.conn.Search(&req) + groupSearchResult, err := server.connection.Search(&groupSearchReq) if err != nil { return nil, err } - if len(result.Entries) > 0 { + if len(groupSearchResult.Entries) > 0 { + for i := range groupSearchResult.Entries { + memberOf = append(memberOf, getLdapAttrN(groupIDAttribute, groupSearchResult, i)) + } break } } - return ldap.serializeUsers(result), nil + return memberOf, nil } -func (ldap *Auth) serializeUsers(users *LDAP.SearchResult) []*UserInfo { - var serialized []*UserInfo +// serializeUsers serializes the users +// from LDAP result to ExternalInfo struct +func (server *Server) serializeUsers( + users *ldap.SearchResult, +) ([]*models.ExternalUserInfo, error) { + var serialized []*models.ExternalUserInfo for index := range users.Entries { - serialize := &UserInfo{ + memberOf, err := server.getMemberOf(users) + if err != nil { + return nil, err + } + + userInfo := &UserInfo{ DN: getLdapAttrN( "dn", users, index, ), LastName: getLdapAttrN( - ldap.server.Attr.Surname, + server.config.Attr.Surname, users, index, ), FirstName: getLdapAttrN( - ldap.server.Attr.Name, + server.config.Attr.Name, users, index, ), Username: getLdapAttrN( - ldap.server.Attr.Username, + server.config.Attr.Username, users, index, ), Email: getLdapAttrN( - ldap.server.Attr.Email, - users, - index, - ), - MemberOf: getLdapAttrArrayN( - ldap.server.Attr.MemberOf, + server.config.Attr.Email, users, index, ), + MemberOf: memberOf, } - serialized = append(serialized, serialize) + serialized = append( + serialized, + server.buildGrafanaUser(userInfo), + ) + } + + return serialized, nil +} + +// getMemberOf finds memberOf property or request it +func (server *Server) getMemberOf(search *ldap.SearchResult) ( + []string, error, +) { + if server.config.GroupSearchFilter == "" { + memberOf := getLdapAttrArray(server.config.Attr.MemberOf, search) + + return memberOf, nil + } + + memberOf, err := server.requestMemberOf(search) + if err != nil { + return nil, err } - return serialized + return memberOf, nil } func appendIfNotEmpty(slice []string, values ...string) []string { @@ -527,11 +545,11 @@ func appendIfNotEmpty(slice []string, values ...string) []string { return slice } -func getLdapAttr(name string, result *LDAP.SearchResult) string { +func getLdapAttr(name string, result *ldap.SearchResult) string { return getLdapAttrN(name, result, 0) } -func getLdapAttrN(name string, result *LDAP.SearchResult, n int) string { +func getLdapAttrN(name string, result *ldap.SearchResult, n int) string { if strings.ToLower(name) == "dn" { return result.Entries[n].DN } @@ -545,11 +563,11 @@ func getLdapAttrN(name string, result *LDAP.SearchResult, n int) string { return "" } -func getLdapAttrArray(name string, result *LDAP.SearchResult) []string { +func getLdapAttrArray(name string, result *ldap.SearchResult) []string { return getLdapAttrArrayN(name, result, 0) } -func getLdapAttrArrayN(name string, result *LDAP.SearchResult, n int) []string { +func getLdapAttrArrayN(name string, result *ldap.SearchResult, n int) []string { for _, attr := range result.Entries[n].Attributes { if attr.Name == name { return attr.Values diff --git a/pkg/services/ldap/ldap_helpers_test.go b/pkg/services/ldap/ldap_helpers_test.go new file mode 100644 index 00000000000..d995d0fd699 --- /dev/null +++ b/pkg/services/ldap/ldap_helpers_test.go @@ -0,0 +1,140 @@ +package ldap + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" + "gopkg.in/ldap.v3" + + "github.com/grafana/grafana/pkg/infra/log" +) + +func TestLDAPHelpers(t *testing.T) { + Convey("serializeUsers()", t, func() { + Convey("simple case", func() { + server := &Server{ + config: &ServerConfig{ + Attr: AttributeMap{ + Username: "username", + Name: "name", + MemberOf: "memberof", + Email: "email", + }, + SearchBaseDNs: []string{"BaseDNHere"}, + }, + connection: &mockConnection{}, + log: log.New("test-logger"), + } + + entry := ldap.Entry{ + DN: "dn", Attributes: []*ldap.EntryAttribute{ + {Name: "username", Values: []string{"roelgerrits"}}, + {Name: "surname", Values: []string{"Gerrits"}}, + {Name: "email", Values: []string{"roel@test.com"}}, + {Name: "name", Values: []string{"Roel"}}, + {Name: "memberof", Values: []string{"admins"}}, + }} + users := &ldap.SearchResult{Entries: []*ldap.Entry{&entry}} + + result, err := server.serializeUsers(users) + + So(err, ShouldBeNil) + So(result[0].Login, ShouldEqual, "roelgerrits") + So(result[0].Email, ShouldEqual, "roel@test.com") + So(result[0].Groups, ShouldContain, "admins") + }) + + Convey("without lastname", func() { + server := &Server{ + config: &ServerConfig{ + Attr: AttributeMap{ + Username: "username", + Name: "name", + MemberOf: "memberof", + Email: "email", + }, + SearchBaseDNs: []string{"BaseDNHere"}, + }, + connection: &mockConnection{}, + log: log.New("test-logger"), + } + + entry := ldap.Entry{ + DN: "dn", Attributes: []*ldap.EntryAttribute{ + {Name: "username", Values: []string{"roelgerrits"}}, + {Name: "email", Values: []string{"roel@test.com"}}, + {Name: "name", Values: []string{"Roel"}}, + {Name: "memberof", Values: []string{"admins"}}, + }} + users := &ldap.SearchResult{Entries: []*ldap.Entry{&entry}} + + result, err := server.serializeUsers(users) + + So(err, ShouldBeNil) + So(result[0].Name, ShouldEqual, "Roel") + }) + }) + + Convey("serverBind()", t, func() { + Convey("Given bind dn and password configured", func() { + connection := &mockConnection{} + var actualUsername, actualPassword string + connection.bindProvider = func(username, password string) error { + actualUsername = username + actualPassword = password + return nil + } + server := &Server{ + connection: connection, + config: &ServerConfig{ + BindDN: "o=users,dc=grafana,dc=org", + BindPassword: "bindpwd", + }, + } + err := server.serverBind() + So(err, ShouldBeNil) + So(actualUsername, ShouldEqual, "o=users,dc=grafana,dc=org") + So(actualPassword, ShouldEqual, "bindpwd") + }) + + Convey("Given bind dn configured", func() { + connection := &mockConnection{} + unauthenticatedBindWasCalled := false + var actualUsername string + connection.unauthenticatedBindProvider = func(username string) error { + unauthenticatedBindWasCalled = true + actualUsername = username + return nil + } + server := &Server{ + connection: connection, + config: &ServerConfig{ + BindDN: "o=users,dc=grafana,dc=org", + }, + } + err := server.serverBind() + So(err, ShouldBeNil) + So(unauthenticatedBindWasCalled, ShouldBeTrue) + So(actualUsername, ShouldEqual, "o=users,dc=grafana,dc=org") + }) + + Convey("Given empty bind dn and password", func() { + connection := &mockConnection{} + unauthenticatedBindWasCalled := false + var actualUsername string + connection.unauthenticatedBindProvider = func(username string) error { + unauthenticatedBindWasCalled = true + actualUsername = username + return nil + } + server := &Server{ + connection: connection, + config: &ServerConfig{}, + } + err := server.serverBind() + So(err, ShouldBeNil) + So(unauthenticatedBindWasCalled, ShouldBeTrue) + So(actualUsername, ShouldBeEmpty) + }) + }) +} diff --git a/pkg/services/ldap/ldap_login_test.go b/pkg/services/ldap/ldap_login_test.go index b8dd502667e..9da82cc6e98 100644 --- a/pkg/services/ldap/ldap_login_test.go +++ b/pkg/services/ldap/ldap_login_test.go @@ -7,23 +7,94 @@ import ( "gopkg.in/ldap.v3" "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/user" ) -func TestLdapLogin(t *testing.T) { - Convey("Login using ldap", t, func() { - AuthScenario("When login with invalid credentials", func(scenario *scenarioContext) { - conn := &mockLdapConn{} +func TestLDAPLogin(t *testing.T) { + Convey("Login()", t, func() { + authScenario("When user is log in and updated", func(sc *scenarioContext) { + // arrange + mockConnection := &mockConnection{} + + auth := &Server{ + config: &ServerConfig{ + Host: "", + RootCACert: "", + Groups: []*GroupToOrgRole{ + {GroupDN: "*", OrgRole: "Admin"}, + }, + Attr: AttributeMap{ + Username: "username", + Surname: "surname", + Email: "email", + Name: "name", + MemberOf: "memberof", + }, + SearchBaseDNs: []string{"BaseDNHere"}, + }, + connection: mockConnection, + log: log.New("test-logger"), + } + + entry := ldap.Entry{ + DN: "dn", Attributes: []*ldap.EntryAttribute{ + {Name: "username", Values: []string{"roelgerrits"}}, + {Name: "surname", Values: []string{"Gerrits"}}, + {Name: "email", Values: []string{"roel@test.com"}}, + {Name: "name", Values: []string{"Roel"}}, + {Name: "memberof", Values: []string{"admins"}}, + }} + result := ldap.SearchResult{Entries: []*ldap.Entry{&entry}} + mockConnection.setSearchResult(&result) + + query := &models.LoginUserQuery{ + Username: "roelgerrits", + } + + sc.userQueryReturns(&models.User{ + Id: 1, + Email: "roel@test.net", + Name: "Roel Gerrits", + Login: "roelgerrits", + }) + sc.userOrgsQueryReturns([]*models.UserOrgDTO{}) + + // act + extUser, _ := auth.Login(query) + userInfo, err := user.Upsert(&user.UpsertArgs{ + SignupAllowed: true, + ExternalUser: extUser, + }) + + // assert + + // Check absence of the error + So(err, ShouldBeNil) + + // User should be searched in ldap + So(mockConnection.searchCalled, ShouldBeTrue) + + // Info should be updated (email differs) + So(userInfo.Email, ShouldEqual, "roel@test.com") + + // User should have admin privileges + So(sc.addOrgUserCmd.Role, ShouldEqual, "Admin") + }) + + authScenario("When login with invalid credentials", func(scenario *scenarioContext) { + connection := &mockConnection{} entry := ldap.Entry{} result := ldap.SearchResult{Entries: []*ldap.Entry{&entry}} - conn.setSearchResult(&result) + connection.setSearchResult(&result) - conn.bindProvider = func(username, password string) error { + connection.bindProvider = func(username, password string) error { return &ldap.Error{ ResultCode: 49, } } - auth := &Auth{ - server: &ServerConfig{ + auth := &Server{ + config: &ServerConfig{ Attr: AttributeMap{ Username: "username", Name: "name", @@ -31,19 +102,19 @@ func TestLdapLogin(t *testing.T) { }, SearchBaseDNs: []string{"BaseDNHere"}, }, - conn: conn, - log: log.New("test-logger"), + connection: connection, + log: log.New("test-logger"), } - err := auth.Login(scenario.loginUserQuery) + _, err := auth.Login(scenario.loginUserQuery) Convey("it should return invalid credentials error", func() { So(err, ShouldEqual, ErrInvalidCredentials) }) }) - AuthScenario("When login with valid credentials", func(scenario *scenarioContext) { - conn := &mockLdapConn{} + authScenario("When login with valid credentials", func(scenario *scenarioContext) { + connection := &mockConnection{} entry := ldap.Entry{ DN: "dn", Attributes: []*ldap.EntryAttribute{ {Name: "username", Values: []string{"markelog"}}, @@ -54,13 +125,13 @@ func TestLdapLogin(t *testing.T) { }, } result := ldap.SearchResult{Entries: []*ldap.Entry{&entry}} - conn.setSearchResult(&result) + connection.setSearchResult(&result) - conn.bindProvider = func(username, password string) error { + connection.bindProvider = func(username, password string) error { return nil } - auth := &Auth{ - server: &ServerConfig{ + auth := &Server{ + config: &ServerConfig{ Attr: AttributeMap{ Username: "username", Name: "name", @@ -68,19 +139,14 @@ func TestLdapLogin(t *testing.T) { }, SearchBaseDNs: []string{"BaseDNHere"}, }, - conn: conn, - log: log.New("test-logger"), + connection: connection, + log: log.New("test-logger"), } - err := auth.Login(scenario.loginUserQuery) + resp, err := auth.Login(scenario.loginUserQuery) - Convey("it should not return error", func() { - So(err, ShouldBeNil) - }) - - Convey("it should get user", func() { - So(scenario.loginUserQuery.User.Login, ShouldEqual, "markelog") - }) + So(err, ShouldBeNil) + So(resp.Login, ShouldEqual, "markelog") }) }) } diff --git a/pkg/services/ldap/ldap_test.go b/pkg/services/ldap/ldap_test.go index 4da041ae164..266fe22a4fc 100644 --- a/pkg/services/ldap/ldap_test.go +++ b/pkg/services/ldap/ldap_test.go @@ -1,496 +1,157 @@ package ldap import ( - "context" "testing" . "github.com/smartystreets/goconvey/convey" - "gopkg.in/ldap.v3" + ldap "gopkg.in/ldap.v3" - "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/infra/log" - m "github.com/grafana/grafana/pkg/models" ) func TestAuth(t *testing.T) { - Convey("initialBind", t, func() { - Convey("Given bind dn and password configured", func() { - conn := &mockLdapConn{} - var actualUsername, actualPassword string - conn.bindProvider = func(username, password string) error { - actualUsername = username - actualPassword = password - return nil - } - Auth := &Auth{ - conn: conn, - server: &ServerConfig{ - BindDN: "cn=%s,o=users,dc=grafana,dc=org", - BindPassword: "bindpwd", - }, - } - err := Auth.initialBind("user", "pwd") - So(err, ShouldBeNil) - So(Auth.requireSecondBind, ShouldBeTrue) - So(actualUsername, ShouldEqual, "cn=user,o=users,dc=grafana,dc=org") - So(actualPassword, ShouldEqual, "bindpwd") - }) + Convey("Add()", t, func() { + connection := &mockConnection{} - Convey("Given bind dn configured", func() { - conn := &mockLdapConn{} - var actualUsername, actualPassword string - conn.bindProvider = func(username, password string) error { - actualUsername = username - actualPassword = password - return nil - } - Auth := &Auth{ - conn: conn, - server: &ServerConfig{ - BindDN: "cn=%s,o=users,dc=grafana,dc=org", - }, - } - err := Auth.initialBind("user", "pwd") - So(err, ShouldBeNil) - So(Auth.requireSecondBind, ShouldBeFalse) - So(actualUsername, ShouldEqual, "cn=user,o=users,dc=grafana,dc=org") - So(actualPassword, ShouldEqual, "pwd") - }) + auth := &Server{ + config: &ServerConfig{ + SearchBaseDNs: []string{"BaseDNHere"}, + }, + connection: connection, + log: log.New("test-logger"), + } - Convey("Given empty bind dn and password", func() { - conn := &mockLdapConn{} - unauthenticatedBindWasCalled := false - var actualUsername string - conn.unauthenticatedBindProvider = func(username string) error { - unauthenticatedBindWasCalled = true - actualUsername = username - return nil - } - Auth := &Auth{ - conn: conn, - server: &ServerConfig{}, - } - err := Auth.initialBind("user", "pwd") - So(err, ShouldBeNil) - So(Auth.requireSecondBind, ShouldBeTrue) - So(unauthenticatedBindWasCalled, ShouldBeTrue) - So(actualUsername, ShouldBeEmpty) - }) - }) + Convey("Adds user", func() { + err := auth.Add( + "cn=ldap-tuz,ou=users,dc=grafana,dc=org", + map[string][]string{ + "mail": {"ldap-viewer@grafana.com"}, + "userPassword": {"grafana"}, + "objectClass": { + "person", + "top", + "inetOrgPerson", + "organizationalPerson", + }, + "sn": {"ldap-tuz"}, + "cn": {"ldap-tuz"}, + }, + ) + + hasMail := false + hasUserPassword := false + hasObjectClass := false + hasSN := false + hasCN := false - Convey("serverBind", t, func() { - Convey("Given bind dn and password configured", func() { - conn := &mockLdapConn{} - var actualUsername, actualPassword string - conn.bindProvider = func(username, password string) error { - actualUsername = username - actualPassword = password - return nil - } - Auth := &Auth{ - conn: conn, - server: &ServerConfig{ - BindDN: "o=users,dc=grafana,dc=org", - BindPassword: "bindpwd", - }, - } - err := Auth.serverBind() So(err, ShouldBeNil) - So(actualUsername, ShouldEqual, "o=users,dc=grafana,dc=org") - So(actualPassword, ShouldEqual, "bindpwd") - }) - - Convey("Given bind dn configured", func() { - conn := &mockLdapConn{} - unauthenticatedBindWasCalled := false - var actualUsername string - conn.unauthenticatedBindProvider = func(username string) error { - unauthenticatedBindWasCalled = true - actualUsername = username - return nil - } - Auth := &Auth{ - conn: conn, - server: &ServerConfig{ - BindDN: "o=users,dc=grafana,dc=org", - }, + So(connection.addParams.Controls, ShouldBeNil) + So(connection.addCalled, ShouldBeTrue) + So( + connection.addParams.DN, + ShouldEqual, + "cn=ldap-tuz,ou=users,dc=grafana,dc=org", + ) + + attrs := connection.addParams.Attributes + for _, value := range attrs { + if value.Type == "mail" { + So(value.Vals, ShouldContain, "ldap-viewer@grafana.com") + hasMail = true + } + + if value.Type == "userPassword" { + hasUserPassword = true + So(value.Vals, ShouldContain, "grafana") + } + + if value.Type == "objectClass" { + hasObjectClass = true + So(value.Vals, ShouldContain, "person") + So(value.Vals, ShouldContain, "top") + So(value.Vals, ShouldContain, "inetOrgPerson") + So(value.Vals, ShouldContain, "organizationalPerson") + } + + if value.Type == "sn" { + hasSN = true + So(value.Vals, ShouldContain, "ldap-tuz") + } + + if value.Type == "cn" { + hasCN = true + So(value.Vals, ShouldContain, "ldap-tuz") + } } - err := Auth.serverBind() - So(err, ShouldBeNil) - So(unauthenticatedBindWasCalled, ShouldBeTrue) - So(actualUsername, ShouldEqual, "o=users,dc=grafana,dc=org") - }) - Convey("Given empty bind dn and password", func() { - conn := &mockLdapConn{} - unauthenticatedBindWasCalled := false - var actualUsername string - conn.unauthenticatedBindProvider = func(username string) error { - unauthenticatedBindWasCalled = true - actualUsername = username - return nil - } - Auth := &Auth{ - conn: conn, - server: &ServerConfig{}, - } - err := Auth.serverBind() - So(err, ShouldBeNil) - So(unauthenticatedBindWasCalled, ShouldBeTrue) - So(actualUsername, ShouldBeEmpty) + So(hasMail, ShouldBeTrue) + So(hasUserPassword, ShouldBeTrue) + So(hasObjectClass, ShouldBeTrue) + So(hasSN, ShouldBeTrue) + So(hasCN, ShouldBeTrue) }) }) - Convey("When translating ldap user to grafana user", t, func() { + Convey("Remove()", t, func() { + connection := &mockConnection{} - var user1 = &m.User{} - - bus.AddHandlerCtx("test", func(ctx context.Context, cmd *m.UpsertUserCommand) error { - cmd.Result = user1 - cmd.Result.Login = "torkelo" - return nil - }) - - Convey("Given no ldap group map match", func() { - Auth := New(&ServerConfig{ - Groups: []*GroupToOrgRole{{}}, - }) - _, err := Auth.GetGrafanaUserFor(nil, &UserInfo{}) - - So(err, ShouldEqual, ErrInvalidCredentials) - }) - - AuthScenario("Given wildcard group match", func(sc *scenarioContext) { - Auth := New(&ServerConfig{ - Groups: []*GroupToOrgRole{ - {GroupDN: "*", OrgRole: "Admin"}, - }, - }) - - sc.userQueryReturns(user1) - - result, err := Auth.GetGrafanaUserFor(nil, &UserInfo{}) - So(err, ShouldBeNil) - So(result, ShouldEqual, user1) - }) - - AuthScenario("Given exact group match", func(sc *scenarioContext) { - Auth := New(&ServerConfig{ - Groups: []*GroupToOrgRole{ - {GroupDN: "cn=users", OrgRole: "Admin"}, - }, - }) - - sc.userQueryReturns(user1) - - result, err := Auth.GetGrafanaUserFor(nil, &UserInfo{MemberOf: []string{"cn=users"}}) - So(err, ShouldBeNil) - So(result, ShouldEqual, user1) - }) - - AuthScenario("Given group match with different case", func(sc *scenarioContext) { - Auth := New(&ServerConfig{ - Groups: []*GroupToOrgRole{ - {GroupDN: "cn=users", OrgRole: "Admin"}, - }, - }) - - sc.userQueryReturns(user1) - - result, err := Auth.GetGrafanaUserFor(nil, &UserInfo{MemberOf: []string{"CN=users"}}) - So(err, ShouldBeNil) - So(result, ShouldEqual, user1) - }) - - AuthScenario("Given no existing grafana user", func(sc *scenarioContext) { - Auth := New(&ServerConfig{ - Groups: []*GroupToOrgRole{ - {GroupDN: "cn=admin", OrgRole: "Admin"}, - {GroupDN: "cn=editor", OrgRole: "Editor"}, - {GroupDN: "*", OrgRole: "Viewer"}, - }, - }) - - sc.userQueryReturns(nil) - - result, err := Auth.GetGrafanaUserFor(nil, &UserInfo{ - DN: "torkelo", - Username: "torkelo", - Email: "my@email.com", - MemberOf: []string{"cn=editor"}, - }) - - So(err, ShouldBeNil) - - Convey("Should return new user", func() { - So(result.Login, ShouldEqual, "torkelo") - }) - - Convey("Should set isGrafanaAdmin to false by default", func() { - So(result.IsAdmin, ShouldBeFalse) - }) - - }) - - }) - - Convey("When syncing ldap groups to grafana org roles", t, func() { - AuthScenario("given no current user orgs", func(sc *scenarioContext) { - Auth := New(&ServerConfig{ - Groups: []*GroupToOrgRole{ - {GroupDN: "cn=users", OrgRole: "Admin"}, - }, - }) - - sc.userOrgsQueryReturns([]*m.UserOrgDTO{}) - _, err := Auth.GetGrafanaUserFor(nil, &UserInfo{ - MemberOf: []string{"cn=users"}, - }) - - Convey("Should create new org user", func() { - So(err, ShouldBeNil) - So(sc.addOrgUserCmd, ShouldNotBeNil) - So(sc.addOrgUserCmd.Role, ShouldEqual, m.ROLE_ADMIN) - }) - }) - - AuthScenario("given different current org role", func(sc *scenarioContext) { - Auth := New(&ServerConfig{ - Groups: []*GroupToOrgRole{ - {GroupDN: "cn=users", OrgId: 1, OrgRole: "Admin"}, - }, - }) - - sc.userOrgsQueryReturns([]*m.UserOrgDTO{{OrgId: 1, Role: m.ROLE_EDITOR}}) - _, err := Auth.GetGrafanaUserFor(nil, &UserInfo{ - MemberOf: []string{"cn=users"}, - }) - - Convey("Should update org role", func() { - So(err, ShouldBeNil) - So(sc.updateOrgUserCmd, ShouldNotBeNil) - So(sc.updateOrgUserCmd.Role, ShouldEqual, m.ROLE_ADMIN) - So(sc.setUsingOrgCmd.OrgId, ShouldEqual, 1) - }) - }) - - AuthScenario("given current org role is removed in ldap", func(sc *scenarioContext) { - Auth := New(&ServerConfig{ - Groups: []*GroupToOrgRole{ - {GroupDN: "cn=users", OrgId: 2, OrgRole: "Admin"}, - }, - }) - - sc.userOrgsQueryReturns([]*m.UserOrgDTO{ - {OrgId: 1, Role: m.ROLE_EDITOR}, - {OrgId: 2, Role: m.ROLE_EDITOR}, - }) - _, err := Auth.GetGrafanaUserFor(nil, &UserInfo{ - MemberOf: []string{"cn=users"}, - }) - - Convey("Should remove org role", func() { - So(err, ShouldBeNil) - So(sc.removeOrgUserCmd, ShouldNotBeNil) - So(sc.setUsingOrgCmd.OrgId, ShouldEqual, 2) - }) - }) - - AuthScenario("given org role is updated in config", func(sc *scenarioContext) { - Auth := New(&ServerConfig{ - Groups: []*GroupToOrgRole{ - {GroupDN: "cn=admin", OrgId: 1, OrgRole: "Admin"}, - {GroupDN: "cn=users", OrgId: 1, OrgRole: "Viewer"}, - }, - }) - - sc.userOrgsQueryReturns([]*m.UserOrgDTO{{OrgId: 1, Role: m.ROLE_EDITOR}}) - _, err := Auth.GetGrafanaUserFor(nil, &UserInfo{ - MemberOf: []string{"cn=users"}, - }) - - Convey("Should update org role", func() { - So(err, ShouldBeNil) - So(sc.removeOrgUserCmd, ShouldBeNil) - So(sc.updateOrgUserCmd, ShouldNotBeNil) - So(sc.setUsingOrgCmd.OrgId, ShouldEqual, 1) - }) - }) - - AuthScenario("given multiple matching ldap groups", func(sc *scenarioContext) { - Auth := New(&ServerConfig{ - Groups: []*GroupToOrgRole{ - {GroupDN: "cn=admins", OrgId: 1, OrgRole: "Admin"}, - {GroupDN: "*", OrgId: 1, OrgRole: "Viewer"}, - }, - }) - - sc.userOrgsQueryReturns([]*m.UserOrgDTO{{OrgId: 1, Role: m.ROLE_ADMIN}}) - _, err := Auth.GetGrafanaUserFor(nil, &UserInfo{ - MemberOf: []string{"cn=admins"}, - }) - - Convey("Should take first match, and ignore subsequent matches", func() { - So(err, ShouldBeNil) - So(sc.updateOrgUserCmd, ShouldBeNil) - So(sc.setUsingOrgCmd.OrgId, ShouldEqual, 1) - }) - }) - - AuthScenario("given multiple matching ldap groups and no existing groups", func(sc *scenarioContext) { - Auth := New(&ServerConfig{ - Groups: []*GroupToOrgRole{ - {GroupDN: "cn=admins", OrgId: 1, OrgRole: "Admin"}, - {GroupDN: "*", OrgId: 1, OrgRole: "Viewer"}, - }, - }) - - sc.userOrgsQueryReturns([]*m.UserOrgDTO{}) - _, err := Auth.GetGrafanaUserFor(nil, &UserInfo{ - MemberOf: []string{"cn=admins"}, - }) - - Convey("Should take first match, and ignore subsequent matches", func() { - So(err, ShouldBeNil) - So(sc.addOrgUserCmd.Role, ShouldEqual, m.ROLE_ADMIN) - So(sc.setUsingOrgCmd.OrgId, ShouldEqual, 1) - }) - - Convey("Should not update permissions unless specified", func() { - So(err, ShouldBeNil) - So(sc.updateUserPermissionsCmd, ShouldBeNil) - }) - }) - - AuthScenario("given ldap groups with grafana_admin=true", func(sc *scenarioContext) { - trueVal := true - - Auth := New(&ServerConfig{ - Groups: []*GroupToOrgRole{ - {GroupDN: "cn=admins", OrgId: 1, OrgRole: "Admin", IsGrafanaAdmin: &trueVal}, - }, - }) - - sc.userOrgsQueryReturns([]*m.UserOrgDTO{}) - _, err := Auth.GetGrafanaUserFor(nil, &UserInfo{ - MemberOf: []string{"cn=admins"}, - }) - - Convey("Should create user with admin set to true", func() { - So(err, ShouldBeNil) - So(sc.updateUserPermissionsCmd.IsGrafanaAdmin, ShouldBeTrue) - }) - }) - }) - - Convey("When calling SyncUser", t, func() { - mockLdapConnection := &mockLdapConn{} - - auth := &Auth{ - server: &ServerConfig{ - Host: "", - RootCACert: "", - Groups: []*GroupToOrgRole{ - {GroupDN: "*", OrgRole: "Admin"}, - }, - Attr: AttributeMap{ - Username: "username", - Surname: "surname", - Email: "email", - Name: "name", - MemberOf: "memberof", - }, + auth := &Server{ + config: &ServerConfig{ SearchBaseDNs: []string{"BaseDNHere"}, }, - conn: mockLdapConnection, - log: log.New("test-logger"), - } - - dialCalled := false - dial = func(network, addr string) (IConnection, error) { - dialCalled = true - return mockLdapConnection, nil + connection: connection, + log: log.New("test-logger"), } - entry := ldap.Entry{ - DN: "dn", Attributes: []*ldap.EntryAttribute{ - {Name: "username", Values: []string{"roelgerrits"}}, - {Name: "surname", Values: []string{"Gerrits"}}, - {Name: "email", Values: []string{"roel@test.com"}}, - {Name: "name", Values: []string{"Roel"}}, - {Name: "memberof", Values: []string{"admins"}}, - }} - result := ldap.SearchResult{Entries: []*ldap.Entry{&entry}} - mockLdapConnection.setSearchResult(&result) + Convey("Removes the user", func() { + dn := "cn=ldap-tuz,ou=users,dc=grafana,dc=org" + err := auth.Remove(dn) - AuthScenario("When ldapUser found call syncInfo and orgRoles", func(sc *scenarioContext) { - // arrange - query := &m.LoginUserQuery{ - Username: "roelgerrits", - } - - hookDial = nil - - sc.userQueryReturns(&m.User{ - Id: 1, - Email: "roel@test.net", - Name: "Roel Gerrits", - Login: "roelgerrits", - }) - sc.userOrgsQueryReturns([]*m.UserOrgDTO{}) - - // act - syncErrResult := auth.SyncUser(query) - - // assert - So(dialCalled, ShouldBeTrue) - So(syncErrResult, ShouldBeNil) - // User should be searched in ldap - So(mockLdapConnection.searchCalled, ShouldBeTrue) - // Info should be updated (email differs) - So(sc.updateUserCmd.Email, ShouldEqual, "roel@test.com") - // User should have admin privileges - So(sc.addOrgUserCmd.UserId, ShouldEqual, 1) - So(sc.addOrgUserCmd.Role, ShouldEqual, "Admin") + So(err, ShouldBeNil) + So(connection.delCalled, ShouldBeTrue) + So(connection.delParams.Controls, ShouldBeNil) + So(connection.delParams.DN, ShouldEqual, dn) }) }) - Convey("When searching for a user and not all five attributes are mapped", t, func() { - mockLdapConnection := &mockLdapConn{} - entry := ldap.Entry{ - DN: "dn", Attributes: []*ldap.EntryAttribute{ - {Name: "username", Values: []string{"roelgerrits"}}, - {Name: "surname", Values: []string{"Gerrits"}}, - {Name: "email", Values: []string{"roel@test.com"}}, - {Name: "name", Values: []string{"Roel"}}, - {Name: "memberof", Values: []string{"admins"}}, - }} - result := ldap.SearchResult{Entries: []*ldap.Entry{&entry}} - mockLdapConnection.setSearchResult(&result) - - // Set up attribute map without surname and email - Auth := &Auth{ - server: &ServerConfig{ - Attr: AttributeMap{ - Username: "username", - Name: "name", - MemberOf: "memberof", - }, - SearchBaseDNs: []string{"BaseDNHere"}, - }, - conn: mockLdapConnection, - log: log.New("test-logger"), - } + Convey("Users()", t, func() { + Convey("find one user", func() { + mockConnection := &mockConnection{} + entry := ldap.Entry{ + DN: "dn", Attributes: []*ldap.EntryAttribute{ + {Name: "username", Values: []string{"roelgerrits"}}, + {Name: "surname", Values: []string{"Gerrits"}}, + {Name: "email", Values: []string{"roel@test.com"}}, + {Name: "name", Values: []string{"Roel"}}, + {Name: "memberof", Values: []string{"admins"}}, + }} + result := ldap.SearchResult{Entries: []*ldap.Entry{&entry}} + mockConnection.setSearchResult(&result) + + // Set up attribute map without surname and email + server := &Server{ + config: &ServerConfig{ + Attr: AttributeMap{ + Username: "username", + Name: "name", + MemberOf: "memberof", + }, + SearchBaseDNs: []string{"BaseDNHere"}, + }, + connection: mockConnection, + log: log.New("test-logger"), + } - searchResult, err := Auth.searchForUser("roelgerrits") + searchResult, err := server.Users([]string{"roelgerrits"}) - So(err, ShouldBeNil) - So(searchResult, ShouldNotBeNil) + So(err, ShouldBeNil) + So(searchResult, ShouldNotBeNil) - // User should be searched in ldap - So(mockLdapConnection.searchCalled, ShouldBeTrue) + // User should be searched in ldap + So(mockConnection.searchCalled, ShouldBeTrue) - // No empty attributes should be added to the search request - So(len(mockLdapConnection.searchAttributes), ShouldEqual, 3) + // No empty attributes should be added to the search request + So(len(mockConnection.searchAttributes), ShouldEqual, 3) + }) }) } diff --git a/pkg/services/ldap/settings.go b/pkg/services/ldap/settings.go index 0a0f66d9d73..15f1583fea7 100644 --- a/pkg/services/ldap/settings.go +++ b/pkg/services/ldap/settings.go @@ -13,10 +13,12 @@ import ( "github.com/grafana/grafana/pkg/util/errutil" ) +// Config holds list of connections to LDAP type Config struct { Servers []*ServerConfig `toml:"servers"` } +// ServerConfig holds connection data to LDAP type ServerConfig struct { Host string `toml:"host"` Port int `toml:"port"` @@ -108,11 +110,11 @@ func readConfig(configFile string) (*Config, error) { _, err := toml.DecodeFile(configFile, result) if err != nil { - return nil, errutil.Wrap("Failed to load ldap config file", err) + return nil, errutil.Wrap("Failed to load LDAP config file", err) } if len(result.Servers) == 0 { - return nil, xerrors.New("ldap enabled but no ldap servers defined in config file") + return nil, xerrors.New("LDAP enabled but no LDAP servers defined in config file") } // set default org id diff --git a/pkg/services/ldap/test.go b/pkg/services/ldap/test.go index 98d169b9a1a..07fd9c6317c 100644 --- a/pkg/services/ldap/test.go +++ b/pkg/services/ldap/test.go @@ -12,15 +12,22 @@ import ( "github.com/grafana/grafana/pkg/services/login" ) -type mockLdapConn struct { - result *ldap.SearchResult - searchCalled bool - searchAttributes []string +type mockConnection struct { + searchResult *ldap.SearchResult + searchCalled bool + searchAttributes []string + + addParams *ldap.AddRequest + addCalled bool + + delParams *ldap.DelRequest + delCalled bool + bindProvider func(username, password string) error unauthenticatedBindProvider func(username string) error } -func (c *mockLdapConn) Bind(username, password string) error { +func (c *mockConnection) Bind(username, password string) error { if c.bindProvider != nil { return c.bindProvider(username, password) } @@ -28,7 +35,7 @@ func (c *mockLdapConn) Bind(username, password string) error { return nil } -func (c *mockLdapConn) UnauthenticatedBind(username string) error { +func (c *mockConnection) UnauthenticatedBind(username string) error { if c.unauthenticatedBindProvider != nil { return c.unauthenticatedBindProvider(username) } @@ -36,23 +43,35 @@ func (c *mockLdapConn) UnauthenticatedBind(username string) error { return nil } -func (c *mockLdapConn) Close() {} +func (c *mockConnection) Close() {} -func (c *mockLdapConn) setSearchResult(result *ldap.SearchResult) { - c.result = result +func (c *mockConnection) setSearchResult(result *ldap.SearchResult) { + c.searchResult = result } -func (c *mockLdapConn) Search(sr *ldap.SearchRequest) (*ldap.SearchResult, error) { +func (c *mockConnection) Search(sr *ldap.SearchRequest) (*ldap.SearchResult, error) { c.searchCalled = true c.searchAttributes = sr.Attributes - return c.result, nil + return c.searchResult, nil +} + +func (c *mockConnection) Add(request *ldap.AddRequest) error { + c.addCalled = true + c.addParams = request + return nil } -func (c *mockLdapConn) StartTLS(*tls.Config) error { +func (c *mockConnection) Del(request *ldap.DelRequest) error { + c.delCalled = true + c.delParams = request return nil } -func AuthScenario(desc string, fn scenarioFunc) { +func (c *mockConnection) StartTLS(*tls.Config) error { + return nil +} + +func authScenario(desc string, fn scenarioFunc) { Convey(desc, func() { defer bus.ClearBusHandlers() @@ -64,10 +83,6 @@ func AuthScenario(desc string, fn scenarioFunc) { }, } - hookDial = func(auth *Auth) error { - return nil - } - loginService := &login.LoginService{ Bus: bus.GetBus(), } diff --git a/pkg/services/multildap/multildap.go b/pkg/services/multildap/multildap.go new file mode 100644 index 00000000000..1b309c646e1 --- /dev/null +++ b/pkg/services/multildap/multildap.go @@ -0,0 +1,204 @@ +package multildap + +import ( + "errors" + + "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/ldap" +) + +// GetConfig gets LDAP config +var GetConfig = ldap.GetConfig + +// IsEnabled checks if LDAP is enabled +var IsEnabled = ldap.IsEnabled + +// ErrInvalidCredentials is returned if username and password do not match +var ErrInvalidCredentials = ldap.ErrInvalidCredentials + +// ErrNoLDAPServers is returned when there is no LDAP servers specified +var ErrNoLDAPServers = errors.New("No LDAP servers are configured") + +// ErrDidNotFindUser if request for user is unsuccessful +var ErrDidNotFindUser = errors.New("Did not find a user") + +// IMultiLDAP is interface for MultiLDAP +type IMultiLDAP interface { + Login(query *models.LoginUserQuery) ( + *models.ExternalUserInfo, error, + ) + + Users(logins []string) ( + []*models.ExternalUserInfo, error, + ) + + User(login string) ( + *models.ExternalUserInfo, error, + ) + + Add(dn string, values map[string][]string) error + Remove(dn string) error +} + +// MultiLDAP is basic struct of LDAP authorization +type MultiLDAP struct { + configs []*ldap.ServerConfig +} + +// New creates the new LDAP auth +func New(configs []*ldap.ServerConfig) IMultiLDAP { + return &MultiLDAP{ + configs: configs, + } +} + +// Add adds user to the *first* defined LDAP +func (multiples *MultiLDAP) Add( + dn string, + values map[string][]string, +) error { + if len(multiples.configs) == 0 { + return ErrNoLDAPServers + } + + config := multiples.configs[0] + ldap := ldap.New(config) + + if err := ldap.Dial(); err != nil { + return err + } + + defer ldap.Close() + + err := ldap.Add(dn, values) + if err != nil { + return err + } + + return nil +} + +// Remove removes user from the *first* defined LDAP +func (multiples *MultiLDAP) Remove(dn string) error { + if len(multiples.configs) == 0 { + return ErrNoLDAPServers + } + + config := multiples.configs[0] + ldap := ldap.New(config) + + if err := ldap.Dial(); err != nil { + return err + } + + defer ldap.Close() + + err := ldap.Remove(dn) + if err != nil { + return err + } + + return nil +} + +// Login tries to log in the user in multiples LDAP +func (multiples *MultiLDAP) Login(query *models.LoginUserQuery) ( + *models.ExternalUserInfo, error, +) { + if len(multiples.configs) == 0 { + return nil, ErrNoLDAPServers + } + + for _, config := range multiples.configs { + server := ldap.New(config) + + if err := server.Dial(); err != nil { + return nil, err + } + + defer server.Close() + + user, err := server.Login(query) + + if user != nil { + return user, nil + } + + // Continue if we couldn't find the user + if err == ErrInvalidCredentials { + continue + } + + if err != nil { + return nil, err + } + + return user, nil + } + + // Return invalid credentials if we couldn't find the user anywhere + return nil, ErrInvalidCredentials +} + +// User gets a user by login +func (multiples *MultiLDAP) User(login string) ( + *models.ExternalUserInfo, + error, +) { + + if len(multiples.configs) == 0 { + return nil, ErrNoLDAPServers + } + + search := []string{login} + for _, config := range multiples.configs { + server := ldap.New(config) + + if err := server.Dial(); err != nil { + return nil, err + } + + defer server.Close() + + users, err := server.Users(search) + if err != nil { + return nil, err + } + + if len(users) != 0 { + return users[0], nil + } + } + + return nil, ErrDidNotFindUser +} + +// Users gets users from multiple LDAP servers +func (multiples *MultiLDAP) Users(logins []string) ( + []*models.ExternalUserInfo, + error, +) { + var result []*models.ExternalUserInfo + + if len(multiples.configs) == 0 { + return nil, ErrNoLDAPServers + } + + for _, config := range multiples.configs { + server := ldap.New(config) + + if err := server.Dial(); err != nil { + return nil, err + } + + defer server.Close() + + users, err := server.Users(logins) + if err != nil { + return nil, err + } + result = append(result, users...) + } + + return result, nil +} diff --git a/pkg/services/sqlstore/sqlstore.go b/pkg/services/sqlstore/sqlstore.go index 44d0f545bfc..58bcc557859 100644 --- a/pkg/services/sqlstore/sqlstore.go +++ b/pkg/services/sqlstore/sqlstore.go @@ -8,7 +8,6 @@ import ( "path" "path/filepath" "strings" - "testing" "time" "github.com/go-sql-driver/mysql" @@ -280,7 +279,14 @@ func (ss *SqlStore) readConfig() { ss.dbCfg.CacheMode = sec.Key("cache_mode").MustString("private") } -func InitTestDB(t *testing.T) *SqlStore { +// Interface of arguments for testing db +type ITestDB interface { + Helper() + Fatalf(format string, args ...interface{}) +} + +// InitTestDB initiliaze test DB +func InitTestDB(t ITestDB) *SqlStore { t.Helper() sqlstore := &SqlStore{} sqlstore.skipEnsureAdmin = true diff --git a/pkg/services/user/user.go b/pkg/services/user/user.go new file mode 100644 index 00000000000..94762c811b0 --- /dev/null +++ b/pkg/services/user/user.go @@ -0,0 +1,39 @@ +package user + +import ( + "github.com/grafana/grafana/pkg/bus" + "github.com/grafana/grafana/pkg/models" +) + +// UpsertArgs are object for Upsert method +type UpsertArgs struct { + ReqContext *models.ReqContext + ExternalUser *models.ExternalUserInfo + SignupAllowed bool +} + +// Upsert add/update grafana user +func Upsert(args *UpsertArgs) (*models.User, error) { + query := &models.UpsertUserCommand{ + ReqContext: args.ReqContext, + ExternalUser: args.ExternalUser, + SignupAllowed: args.SignupAllowed, + } + err := bus.Dispatch(query) + if err != nil { + return nil, err + } + + return query.Result, nil +} + +// Get the users +func Get( + query *models.SearchUsersQuery, +) ([]*models.UserSearchHitDTO, error) { + if err := bus.Dispatch(query); err != nil { + return nil, err + } + + return query.Result.Users, nil +} diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index 192f300021b..194617f3bcb 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -805,6 +805,7 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error { // auth proxy authProxy := iniFile.Section("auth.proxy") AuthProxyEnabled = authProxy.Key("enabled").MustBool(false) + AuthProxyHeaderName, err = valueAsString(authProxy, "header_name", "") if err != nil { return err diff --git a/vendor/github.com/brianvoe/gofakeit/BENCHMARKS.md b/vendor/github.com/brianvoe/gofakeit/BENCHMARKS.md new file mode 100644 index 00000000000..ec6e6d7a376 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/BENCHMARKS.md @@ -0,0 +1,134 @@ +go test -bench=. -benchmem +goos: darwin +goarch: amd64 +pkg: github.com/brianvoe/gofakeit +Table generated with tablesgenerator.com/markdown_tables + +| Benchmark | Ops | CPU | MEM | MEM alloc | +|---------------------------------|-----------|-------------|------------|--------------| +| BenchmarkAddress-4 | 1000000 | 1998 ns/op | 248 B/op | 7 allocs/op | +| BenchmarkStreet-4 | 1000000 | 1278 ns/op | 62 B/op | 3 allocs/op | +| BenchmarkStreetNumber-4 | 5000000 | 344 ns/op | 36 B/op | 2 allocs/op | +| BenchmarkStreetPrefix-4 | 10000000 | 121 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkStreetName-4 | 10000000 | 122 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkStreetSuffix-4 | 10000000 | 122 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkCity-4 | 5000000 | 326 ns/op | 15 B/op | 1 allocs/op | +| BenchmarkState-4 | 10000000 | 120 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkStateAbr-4 | 10000000 | 122 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkZip-4 | 5000000 | 315 ns/op | 5 B/op | 1 allocs/op | +| BenchmarkCountry-4 | 10000000 | 126 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkCountryAbr-4 | 10000000 | 123 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkLatitude-4 | 100000000 | 23.6 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkLongitude-4 | 100000000 | 23.6 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkLatitudeInRange-4 | 50000000 | 27.7 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkLongitudeInRange-4 | 50000000 | 27.8 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkBeerName-4 | 20000000 | 104 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkBeerStyle-4 | 10000000 | 119 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkBeerHop-4 | 20000000 | 105 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkBeerYeast-4 | 20000000 | 106 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkBeerMalt-4 | 20000000 | 114 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkBeerIbu-4 | 20000000 | 71.0 ns/op | 8 B/op | 1 allocs/op | +| BenchmarkBeerAlcohol-4 | 5000000 | 335 ns/op | 40 B/op | 3 allocs/op | +| BenchmarkBeerBlg-4 | 5000000 | 338 ns/op | 48 B/op | 3 allocs/op | +| BenchmarkBool-4 | 50000000 | 34.2 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkColor-4 | 20000000 | 112 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkSafeColor-4 | 20000000 | 102 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkHexColor-4 | 3000000 | 491 ns/op | 24 B/op | 3 allocs/op | +| BenchmarkRGBColor-4 | 20000000 | 103 ns/op | 32 B/op | 1 allocs/op | +| BenchmarkCompany-4 | 5000000 | 353 ns/op | 22 B/op | 1 allocs/op | +| BenchmarkCompanySuffix-4 | 20000000 | 89.6 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkBuzzWord-4 | 20000000 | 99.0 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkBS-4 | 20000000 | 100 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkContact-4 | 1000000 | 1121 ns/op | 178 B/op | 7 allocs/op | +| BenchmarkPhone-4 | 5000000 | 346 ns/op | 16 B/op | 1 allocs/op | +| BenchmarkPhoneFormatted-4 | 3000000 | 456 ns/op | 16 B/op | 1 allocs/op | +| BenchmarkEmail-4 | 2000000 | 715 ns/op | 130 B/op | 5 allocs/op | +| BenchmarkCurrency-4 | 10000000 | 125 ns/op | 32 B/op | 1 allocs/op | +| BenchmarkCurrencyShort-4 | 20000000 | 104 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkCurrencyLong-4 | 20000000 | 105 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkPrice-4 | 50000000 | 27.2 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkDate-4 | 5000000 | 371 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkDateRange-4 | 10000000 | 238 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkMonth-4 | 30000000 | 44.6 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkDay-4 | 50000000 | 39.2 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkWeekDay-4 | 30000000 | 44.7 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkYear-4 | 20000000 | 115 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkHour-4 | 30000000 | 39.9 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkMinute-4 | 50000000 | 40.4 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkSecond-4 | 30000000 | 40.6 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkNanoSecond-4 | 30000000 | 42.2 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkTimeZone-4 | 20000000 | 105 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkTimeZoneFull-4 | 20000000 | 118 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkTimeZoneAbv-4 | 20000000 | 105 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkTimeZoneOffset-4 | 10000000 | 147 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkMimeType-4 | 20000000 | 99.9 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkExtension-4 | 20000000 | 109 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkGenerate-4 | 1000000 | 1588 ns/op | 414 B/op | 11 allocs/op | +| BenchmarkHackerPhrase-4 | 300000 | 4576 ns/op | 2295 B/op | 26 allocs/op | +| BenchmarkHackerAbbreviation-4 | 20000000 | 101 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkHackerAdjective-4 | 20000000 | 101 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkHackerNoun-4 | 20000000 | 104 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkHackerVerb-4 | 20000000 | 113 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkHackerIngverb-4 | 20000000 | 98.6 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkHipsterWord-4 | 20000000 | 100 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkHipsterSentence-4 | 1000000 | 1636 ns/op | 353 B/op | 3 allocs/op | +| BenchmarkHipsterParagraph-4 | 50000 | 31677 ns/op | 12351 B/op | 64 allocs/op | +| BenchmarkImageURL-4 | 20000000 | 108 ns/op | 38 B/op | 3 allocs/op | +| BenchmarkDomainName-4 | 3000000 | 491 ns/op | 76 B/op | 3 allocs/op | +| BenchmarkDomainSuffix-4 | 20000000 | 99.4 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkURL-4 | 1000000 | 1201 ns/op | 278 B/op | 8 allocs/op | +| BenchmarkHTTPMethod-4 | 20000000 | 100 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkIPv4Address-4 | 3000000 | 407 ns/op | 48 B/op | 5 allocs/op | +| BenchmarkIPv6Address-4 | 3000000 | 552 ns/op | 96 B/op | 7 allocs/op | +| BenchmarkUsername-4 | 5000000 | 307 ns/op | 16 B/op | 2 allocs/op | +| BenchmarkJob-4 | 2000000 | 726 ns/op | 86 B/op | 2 allocs/op | +| BenchmarkJobTitle-4 | 20000000 | 98.7 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkJobDescriptor-4 | 20000000 | 98.9 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkJobLevel-4 | 20000000 | 110 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkLogLevel-4 | 20000000 | 107 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkReplaceWithNumbers-4 | 3000000 | 570 ns/op | 32 B/op | 1 allocs/op | +| BenchmarkName-4 | 5000000 | 285 ns/op | 17 B/op | 1 allocs/op | +| BenchmarkFirstName-4 | 20000000 | 102 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkLastName-4 | 20000000 | 100 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkNamePrefix-4 | 20000000 | 98.0 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkNameSuffix-4 | 20000000 | 109 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkNumber-4 | 50000000 | 34.5 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkUint8-4 | 50000000 | 28.5 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkUint16-4 | 50000000 | 28.5 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkUint32-4 | 50000000 | 27.0 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkUint64-4 | 50000000 | 34.6 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkInt8-4 | 50000000 | 28.5 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkInt16-4 | 50000000 | 28.4 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkInt32-4 | 50000000 | 27.0 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkInt64-4 | 50000000 | 34.9 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkFloat32-4 | 50000000 | 27.7 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkFloat32Range-4 | 50000000 | 27.9 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkFloat64-4 | 50000000 | 25.9 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkFloat64Range-4 | 50000000 | 26.5 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkNumerify-4 | 5000000 | 354 ns/op | 16 B/op | 1 allocs/op | +| BenchmarkShuffleInts-4 | 10000000 | 226 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkPassword-4 | 2000000 | 655 ns/op | 304 B/op | 6 allocs/op | +| BenchmarkCreditCard-4 | 2000000 | 997 ns/op | 88 B/op | 4 allocs/op | +| BenchmarkCreditCardType-4 | 20000000 | 92.7 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkCreditCardNumber-4 | 3000000 | 572 ns/op | 16 B/op | 1 allocs/op | +| BenchmarkCreditCardNumberLuhn-4 | 300000 | 5815 ns/op | 159 B/op | 9 allocs/op | +| BenchmarkCreditCardExp-4 | 10000000 | 129 ns/op | 5 B/op | 1 allocs/op | +| BenchmarkCreditCardCvv-4 | 10000000 | 128 ns/op | 3 B/op | 1 allocs/op | +| BenchmarkSSN-4 | 20000000 | 84.2 ns/op | 16 B/op | 1 allocs/op | +| BenchmarkGender-4 | 50000000 | 38.0 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkPerson-4 | 300000 | 5563 ns/op | 805 B/op | 26 allocs/op | +| BenchmarkSimpleStatusCode-4 | 20000000 | 72.9 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkStatusCode-4 | 20000000 | 75.8 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkLetter-4 | 50000000 | 38.4 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkDigit-4 | 50000000 | 38.2 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkLexify-4 | 10000000 | 222 ns/op | 8 B/op | 1 allocs/op | +| BenchmarkShuffleStrings-4 | 10000000 | 197 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkUUID-4 | 20000000 | 106 ns/op | 48 B/op | 1 allocs/op | +| BenchmarkUserAgent-4 | 1000000 | 1236 ns/op | 305 B/op | 5 allocs/op | +| BenchmarkChromeUserAgent-4 | 2000000 | 881 ns/op | 188 B/op | 5 allocs/op | +| BenchmarkFirefoxUserAgent-4 | 1000000 | 1595 ns/op | 386 B/op | 7 allocs/op | +| BenchmarkSafariUserAgent-4 | 1000000 | 1396 ns/op | 551 B/op | 7 allocs/op | +| BenchmarkOperaUserAgent-4 | 2000000 | 950 ns/op | 216 B/op | 5 allocs/op | +| BenchmarkWord-4 | 20000000 | 99.1 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkSentence-4 | 1000000 | 1540 ns/op | 277 B/op | 2 allocs/op | +| BenchmarkParagraph-4 | 50000 | 30978 ns/op | 11006 B/op | 61 allocs/op | \ No newline at end of file diff --git a/vendor/github.com/brianvoe/gofakeit/CODE_OF_CONDUCT.md b/vendor/github.com/brianvoe/gofakeit/CODE_OF_CONDUCT.md new file mode 100644 index 00000000000..99d12c90fec --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at brian@webiswhatido.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/vendor/github.com/brianvoe/gofakeit/CONTRIBUTING.md b/vendor/github.com/brianvoe/gofakeit/CONTRIBUTING.md new file mode 100644 index 00000000000..5a4812c28ee --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/CONTRIBUTING.md @@ -0,0 +1 @@ +# Make a pull request and submit it and ill take a look at it. Thanks! diff --git a/vendor/github.com/brianvoe/gofakeit/LICENSE.txt b/vendor/github.com/brianvoe/gofakeit/LICENSE.txt new file mode 100644 index 00000000000..21984c9d5ea --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/LICENSE.txt @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) [year] [fullname] + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/vendor/github.com/brianvoe/gofakeit/README.md b/vendor/github.com/brianvoe/gofakeit/README.md new file mode 100644 index 00000000000..4e3723fd511 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/README.md @@ -0,0 +1,254 @@ +![alt text](https://raw.githubusercontent.com/brianvoe/gofakeit/master/logo.png) + +# gofakeit [![Go Report Card](https://goreportcard.com/badge/github.com/brianvoe/gofakeit)](https://goreportcard.com/report/github.com/brianvoe/gofakeit) [![Build Status](https://travis-ci.org/brianvoe/gofakeit.svg?branch=master)](https://travis-ci.org/brianvoe/gofakeit) [![codecov.io](https://codecov.io/github/brianvoe/gofakeit/branch/master/graph/badge.svg)](https://codecov.io/github/brianvoe/gofakeit) [![GoDoc](https://godoc.org/github.com/brianvoe/gofakeit?status.svg)](https://godoc.org/github.com/brianvoe/gofakeit) [![license](http://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://raw.githubusercontent.com/brianvoe/gofakeit/master/LICENSE.txt) +Random data generator written in go + +Buy Me A Coffee + +### Features +- Every function has an example and a benchmark, +[see benchmarks](https://github.com/brianvoe/gofakeit/blob/master/BENCHMARKS.md) +- Zero dependencies +- Randomizes user defined structs +- Numerous functions for regular use + +### 120+ Functions!!! +If there is something that is generic enough missing from this package [add an issue](https://github.com/brianvoe/gofakeit/issues) and let me know what you need. +Most of the time i'll add it! + +## Person +```go +Person() *PersonInfo +Name() string +NamePrefix() string +NameSuffix() string +FirstName() string +LastName() string +Gender() string +SSN() string +Contact() *ContactInfo +Email() string +Phone() string +PhoneFormatted() string +Username() string +Password(lower bool, upper bool, numeric bool, special bool, space bool, num int) string +``` + +## Address +```go +Address() *AddressInfo +City() string +Country() string +CountryAbr() string +State() string +StateAbr() string +StatusCode() string +Street() string +StreetName() string +StreetNumber() string +StreetPrefix() string +StreetSuffix() string +Zip() string +Latitude() float64 +LatitudeInRange() (float64, error) +Longitude() float64 +LongitudeInRange() (float64, error) +``` + +## Beer +```go +BeerAlcohol() string +BeerBlg() string +BeerHop() string +BeerIbu() string +BeerMalt() string +BeerName() string +BeerStyle() string +BeerYeast() string +``` + +## Cars +```go +Vehicle() *VehicleInfo +CarMaker() string +CarModel() string +VehicleType() string +FuelType() string +TransmissionGearType() string +``` + +## Words +```go +Word() string +Sentence(wordCount int) string +Paragraph(paragraphCount int, sentenceCount int, wordCount int, separator string) string +Question() string +Quote() string +``` + +## Misc +```go +Struct(v interface{}) +Generate() string +Bool() bool +UUID() string +``` + +## Colors +```go +Color() string +HexColor() string +RGBColor() string +SafeColor() string +``` + +## Internet +```go +URL() string +ImageURL(width int, height int) string +DomainName() string +DomainSuffix() string +IPv4Address() string +IPv6Address() string +SimpleStatusCode() int +LogLevel(logType string) string +HTTPMethod() string +UserAgent() string +ChromeUserAgent() string +FirefoxUserAgent() string +OperaUserAgent() string +SafariUserAgent() string +``` + +## Date/Time +```go +Date() time.Time +DateRange(start, end time.Time) time.Time +NanoSecond() int +Second() int +Minute() int +Hour() int +Month() string +Day() int +WeekDay() string +Year() int +TimeZone() string +TimeZoneAbv() string +TimeZoneFull() string +TimeZoneOffset() float32 +``` + +## Payment +```go +Price(min, max float64) float64 +CreditCard() *CreditCardInfo +CreditCardCvv() string +CreditCardExp() string +CreditCardNumber() int +CreditCardNumberLuhn() int +CreditCardType() string +Currency() *CurrencyInfo +CurrencyLong() string +CurrencyShort() string +``` + +## Company +```go +BS() string +BuzzWord() string +Company() string +CompanySuffix() string +Job() *JobInfo +JobDescriptor() string +JobLevel() string +JobTitle() string +``` + +## Hacker +```go +HackerAbbreviation() string +HackerAdjective() string +HackerIngverb() string +HackerNoun() string +HackerPhrase() string +HackerVerb() string +``` + +## Hipster +```go +HipsterWord() string +HipsterSentence(wordCount int) string +HipsterParagraph(paragraphCount int, sentenceCount int, wordCount int, separator string) string +``` + +## File +```go +Extension() string +MimeType() string +``` + +## Numbers +```go +Number(min int, max int) int +Numerify(str string) string +Int8() int8 +Int16() int16 +Int32() int32 +Int64() int64 +Uint8() uint8 +Uint16() uint16 +Uint32() uint32 +Uint64() uint64 +Float32() float32 +Float32Range(min, max float32) float32 +Float64() float64 +Float64Range(min, max float64) float64 +ShuffleInts(a []int) +``` + +## String +```go +Digit() string +Letter() string +Lexify(str string) string +RandString(a []string) string +ShuffleStrings(a []string) +``` + +## Documentation +[![GoDoc](https://godoc.org/github.com/brianvoe/gofakeit?status.svg)](https://godoc.org/github.com/brianvoe/gofakeit) + +## Example +```go +import "github.com/brianvoe/gofakeit" + +gofakeit.Name() // Markus Moen +gofakeit.Email() // alaynawuckert@kozey.biz +gofakeit.Phone() // (570)245-7485 +gofakeit.BS() // front-end +gofakeit.BeerName() // Duvel +gofakeit.Color() // MediumOrchid +gofakeit.Company() // Moen, Pagac and Wuckert +gofakeit.CreditCardNumber() // 4287271570245748 +gofakeit.HackerPhrase() // Connecting the array won't do anything, we need to generate the haptic COM driver! +gofakeit.JobTitle() // Director +gofakeit.Password(true, true, true, true, true, 32) // WV10MzLxq2DX79w1omH97_0ga59j8!kj +gofakeit.CurrencyShort() // USD +// 120+ more!!! + +// Create structs with random injected data +type Foo struct { + Bar string + Baz string + Int int + Pointer *int + Skip *string `fake:"skip"` // Set to "skip" to not generate data for +} +var f Foo +gofakeit.Struct(&f) +fmt.Printf("f.Bar:%s\n", f.Bar) // f.Bar:hrukpttuezptneuvunh +fmt.Printf("f.Baz:%s\n", f.Baz) // f.Baz:uksqvgzadxlgghejkmv +fmt.Printf("f.Int:%d\n", f.Int) // f.Int:-7825289004089916589 +fmt.Printf("f.Pointer:%d\n", *f.Pointer) // f.Pointer:-343806609094473732 +fmt.Printf("f.Skip:%v\n", f.Skip) // f.Skip: +``` diff --git a/vendor/github.com/brianvoe/gofakeit/TODO.txt b/vendor/github.com/brianvoe/gofakeit/TODO.txt new file mode 100644 index 00000000000..7a492842136 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/TODO.txt @@ -0,0 +1,3 @@ +* Take a look at [chance.js](http://chancejs.com/) and see if i missed anything. +* Look into [National Baby Name List](http://www.ssa.gov/oact/babynames/limits.html) and see if that makes sense to replace over what we currently have. +* Look at [data list](https://github.com/dariusk/corpora/tree/master/data) and see if it makes sense to add that data in or if it seems unncessary. diff --git a/vendor/github.com/brianvoe/gofakeit/address.go b/vendor/github.com/brianvoe/gofakeit/address.go new file mode 100644 index 00000000000..82fc6b00e19 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/address.go @@ -0,0 +1,131 @@ +package gofakeit + +import ( + "errors" + "math/rand" + "strings" +) + +// AddressInfo is a struct full of address information +type AddressInfo struct { + Address string + Street string + City string + State string + Zip string + Country string + Latitude float64 + Longitude float64 +} + +// Address will generate a struct of address information +func Address() *AddressInfo { + street := Street() + city := City() + state := State() + zip := Zip() + + return &AddressInfo{ + Address: street + ", " + city + ", " + state + " " + zip, + Street: street, + City: city, + State: state, + Zip: zip, + Country: Country(), + Latitude: Latitude(), + Longitude: Longitude(), + } +} + +// Street will generate a random address street string +func Street() (street string) { + switch randInt := randIntRange(1, 2); randInt { + case 1: + street = StreetNumber() + " " + StreetPrefix() + " " + StreetName() + StreetSuffix() + case 2: + street = StreetNumber() + " " + StreetName() + StreetSuffix() + } + + return +} + +// StreetNumber will generate a random address street number string +func StreetNumber() string { + return strings.TrimLeft(replaceWithNumbers(getRandValue([]string{"address", "number"})), "0") +} + +// StreetPrefix will generate a random address street prefix string +func StreetPrefix() string { + return getRandValue([]string{"address", "street_prefix"}) +} + +// StreetName will generate a random address street name string +func StreetName() string { + return getRandValue([]string{"address", "street_name"}) +} + +// StreetSuffix will generate a random address street suffix string +func StreetSuffix() string { + return getRandValue([]string{"address", "street_suffix"}) +} + +// City will generate a random city string +func City() (city string) { + switch randInt := randIntRange(1, 3); randInt { + case 1: + city = FirstName() + StreetSuffix() + case 2: + city = LastName() + StreetSuffix() + case 3: + city = StreetPrefix() + " " + LastName() + } + + return +} + +// State will generate a random state string +func State() string { + return getRandValue([]string{"address", "state"}) +} + +// StateAbr will generate a random abbreviated state string +func StateAbr() string { + return getRandValue([]string{"address", "state_abr"}) +} + +// Zip will generate a random Zip code string +func Zip() string { + return replaceWithNumbers(getRandValue([]string{"address", "zip"})) +} + +// Country will generate a random country string +func Country() string { + return getRandValue([]string{"address", "country"}) +} + +// CountryAbr will generate a random abbreviated country string +func CountryAbr() string { + return getRandValue([]string{"address", "country_abr"}) +} + +// Latitude will generate a random latitude float64 +func Latitude() float64 { return (rand.Float64() * 180) - 90 } + +// LatitudeInRange will generate a random latitude within the input range +func LatitudeInRange(min, max float64) (float64, error) { + if min > max || min < -90 || min > 90 || max < -90 || max > 90 { + return 0, errors.New("input range is invalid") + } + return randFloat64Range(min, max), nil +} + +// Longitude will generate a random longitude float64 +func Longitude() float64 { return (rand.Float64() * 360) - 180 } + +// LongitudeInRange will generate a random longitude within the input range +func LongitudeInRange(min, max float64) (float64, error) { + if min > max || min < -180 || min > 180 || max < -180 || max > 180 { + return 0, errors.New("input range is invalid") + } + return randFloat64Range(min, max), nil +} diff --git a/vendor/github.com/brianvoe/gofakeit/beer.go b/vendor/github.com/brianvoe/gofakeit/beer.go new file mode 100644 index 00000000000..53297d53780 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/beer.go @@ -0,0 +1,45 @@ +package gofakeit + +import "strconv" + +// Faker::Beer.blg #=> "18.5°Blg" + +// BeerName will return a random beer name +func BeerName() string { + return getRandValue([]string{"beer", "name"}) +} + +// BeerStyle will return a random beer style +func BeerStyle() string { + return getRandValue([]string{"beer", "style"}) +} + +// BeerHop will return a random beer hop +func BeerHop() string { + return getRandValue([]string{"beer", "hop"}) +} + +// BeerYeast will return a random beer yeast +func BeerYeast() string { + return getRandValue([]string{"beer", "yeast"}) +} + +// BeerMalt will return a random beer malt +func BeerMalt() string { + return getRandValue([]string{"beer", "malt"}) +} + +// BeerIbu will return a random beer ibu value between 10 and 100 +func BeerIbu() string { + return strconv.Itoa(randIntRange(10, 100)) + " IBU" +} + +// BeerAlcohol will return a random beer alcohol level between 2.0 and 10.0 +func BeerAlcohol() string { + return strconv.FormatFloat(randFloat64Range(2.0, 10.0), 'f', 1, 64) + "%" +} + +// BeerBlg will return a random beer blg between 5.0 and 20.0 +func BeerBlg() string { + return strconv.FormatFloat(randFloat64Range(5.0, 20.0), 'f', 1, 64) + "°Blg" +} diff --git a/vendor/github.com/brianvoe/gofakeit/bool.go b/vendor/github.com/brianvoe/gofakeit/bool.go new file mode 100644 index 00000000000..f63eeedd324 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/bool.go @@ -0,0 +1,10 @@ +package gofakeit + +// Bool will generate a random boolean value +func Bool() bool { + if randIntRange(0, 1) == 1 { + return true + } + + return false +} diff --git a/vendor/github.com/brianvoe/gofakeit/color.go b/vendor/github.com/brianvoe/gofakeit/color.go new file mode 100644 index 00000000000..63a737e99a6 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/color.go @@ -0,0 +1,44 @@ +package gofakeit + +import "math/rand" + +// Color will generate a random color string +func Color() string { + return getRandValue([]string{"color", "full"}) +} + +// SafeColor will generate a random safe color string +func SafeColor() string { + return getRandValue([]string{"color", "safe"}) +} + +// HexColor will generate a random hexadecimal color string +func HexColor() string { + color := make([]byte, 6) + hashQuestion := []byte("?#") + for i := 0; i < 6; i++ { + color[i] = hashQuestion[rand.Intn(2)] + } + + return "#" + replaceWithLetters(replaceWithNumbers(string(color))) + + // color := "" + // for i := 1; i <= 6; i++ { + // color += RandString([]string{"?", "#"}) + // } + + // // Replace # with number + // color = replaceWithNumbers(color) + + // // Replace ? with letter + // for strings.Count(color, "?") > 0 { + // color = strings.Replace(color, "?", RandString(letters), 1) + // } + + // return "#" + color +} + +// RGBColor will generate a random int slice color +func RGBColor() []int { + return []int{randIntRange(0, 255), randIntRange(0, 255), randIntRange(0, 255)} +} diff --git a/vendor/github.com/brianvoe/gofakeit/company.go b/vendor/github.com/brianvoe/gofakeit/company.go new file mode 100644 index 00000000000..abdb2aa698f --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/company.go @@ -0,0 +1,30 @@ +package gofakeit + +// Company will generate a random company name string +func Company() (company string) { + switch randInt := randIntRange(1, 3); randInt { + case 1: + company = LastName() + ", " + LastName() + " and " + LastName() + case 2: + company = LastName() + "-" + LastName() + case 3: + company = LastName() + " " + CompanySuffix() + } + + return +} + +// CompanySuffix will generate a random company suffix string +func CompanySuffix() string { + return getRandValue([]string{"company", "suffix"}) +} + +// BuzzWord will generate a random company buzz word string +func BuzzWord() string { + return getRandValue([]string{"company", "buzzwords"}) +} + +// BS will generate a random company bs string +func BS() string { + return getRandValue([]string{"company", "bs"}) +} diff --git a/vendor/github.com/brianvoe/gofakeit/contact.go b/vendor/github.com/brianvoe/gofakeit/contact.go new file mode 100644 index 00000000000..1eb0ae05303 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/contact.go @@ -0,0 +1,40 @@ +package gofakeit + +import ( + "strings" +) + +// ContactInfo struct full of contact info +type ContactInfo struct { + Phone string + Email string +} + +// Contact will generate a struct with information randomly populated contact information +func Contact() *ContactInfo { + return &ContactInfo{ + Phone: Phone(), + Email: Email(), + } +} + +// Phone will generate a random phone number string +func Phone() string { + return replaceWithNumbers("##########") +} + +// PhoneFormatted will generate a random phone number string +func PhoneFormatted() string { + return replaceWithNumbers(getRandValue([]string{"contact", "phone"})) +} + +// Email will generate a random email string +func Email() string { + var email string + + email = getRandValue([]string{"person", "first"}) + getRandValue([]string{"person", "last"}) + email += "@" + email += getRandValue([]string{"person", "last"}) + "." + getRandValue([]string{"internet", "domain_suffix"}) + + return strings.ToLower(email) +} diff --git a/vendor/github.com/brianvoe/gofakeit/currency.go b/vendor/github.com/brianvoe/gofakeit/currency.go new file mode 100644 index 00000000000..c25e4d62a7a --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/currency.go @@ -0,0 +1,38 @@ +package gofakeit + +import ( + "math" + "math/rand" + + "github.com/brianvoe/gofakeit/data" +) + +// CurrencyInfo is a struct of currency information +type CurrencyInfo struct { + Short string + Long string +} + +// Currency will generate a struct with random currency information +func Currency() *CurrencyInfo { + index := rand.Intn(len(data.Data["currency"]["short"])) + return &CurrencyInfo{ + Short: data.Data["currency"]["short"][index], + Long: data.Data["currency"]["long"][index], + } +} + +// CurrencyShort will generate a random short currency value +func CurrencyShort() string { + return getRandValue([]string{"currency", "short"}) +} + +// CurrencyLong will generate a random long currency name +func CurrencyLong() string { + return getRandValue([]string{"currency", "long"}) +} + +// Price will take in a min and max value and return a formatted price +func Price(min, max float64) float64 { + return math.Floor(randFloat64Range(min, max)*100) / 100 +} diff --git a/vendor/github.com/brianvoe/gofakeit/data/address.go b/vendor/github.com/brianvoe/gofakeit/data/address.go new file mode 100644 index 00000000000..671cdda9137 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/data/address.go @@ -0,0 +1,15 @@ +package data + +// Address consists of address information +var Address = map[string][]string{ + "number": {"#####", "####", "###"}, + "street_prefix": {"North", "East", "West", "South", "New", "Lake", "Port"}, + "street_name": {"Alley", "Avenue", "Branch", "Bridge", "Brook", "Brooks", "Burg", "Burgs", "Bypass", "Camp", "Canyon", "Cape", "Causeway", "Center", "Centers", "Circle", "Circles", "Cliff", "Cliffs", "Club", "Common", "Corner", "Corners", "Course", "Court", "Courts", "Cove", "Coves", "Creek", "Crescent", "Crest", "Crossing", "Crossroad", "Curve", "Dale", "Dam", "Divide", "Drive", "Drive", "Drives", "Estate", "Estates", "Expressway", "Extension", "Extensions", "Fall", "Falls", "Ferry", "Field", "Fields", "Flat", "Flats", "Ford", "Fords", "Forest", "Forge", "Forges", "Fork", "Forks", "Fort", "Freeway", "Garden", "Gardens", "Gateway", "Glen", "Glens", "Green", "Greens", "Grove", "Groves", "Harbor", "Harbors", "Haven", "Heights", "Highway", "Hill", "Hills", "Hollow", "Inlet", "Inlet", "Island", "Island", "Islands", "Islands", "Isle", "Isle", "Junction", "Junctions", "Key", "Keys", "Knoll", "Knolls", "Lake", "Lakes", "Land", "Landing", "Lane", "Light", "Lights", "Loaf", "Lock", "Locks", "Locks", "Lodge", "Lodge", "Loop", "Mall", "Manor", "Manors", "Meadow", "Meadows", "Mews", "Mill", "Mills", "Mission", "Mission", "Motorway", "Mount", "Mountain", "Mountain", "Mountains", "Mountains", "Neck", "Orchard", "Oval", "Overpass", "Park", "Parks", "Parkway", "Parkways", "Pass", "Passage", "Path", "Pike", "Pine", "Pines", "Place", "Plain", "Plains", "Plains", "Plaza", "Plaza", "Point", "Points", "Port", "Port", "Ports", "Ports", "Prairie", "Prairie", "Radial", "Ramp", "Ranch", "Rapid", "Rapids", "Rest", "Ridge", "Ridges", "River", "Road", "Road", "Roads", "Roads", "Route", "Row", "Rue", "Run", "Shoal", "Shoals", "Shore", "Shores", "Skyway", "Spring", "Springs", "Springs", "Spur", "Spurs", "Square", "Square", "Squares", "Squares", "Station", "Station", "Stravenue", "Stravenue", "Stream", "Stream", "Street", "Street", "Streets", "Summit", "Summit", "Terrace", "Throughway", "Trace", "Track", "Trafficway", "Trail", "Trail", "Tunnel", "Tunnel", "Turnpike", "Turnpike", "Underpass", "Union", "Unions", "Valley", "Valleys", "Via", "Viaduct", "View", "Views", "Village", "Village", "Villages", "Ville", "Vista", "Vista", "Walk", "Walks", "Wall", "Way", "Ways", "Well", "Wells"}, + "street_suffix": {"town", "ton", "land", "ville", "berg", "burgh", "borough", "bury", "view", "port", "mouth", "stad", "furt", "chester", "mouth", "fort", "haven", "side", "shire"}, + "city": {"{address.street_prefix} {name.first}{address.street_suffix}", "{address.street_prefix} {name.first}", "{name.first}{address.street_suffix}", "{name.last}{address.street_suffix}"}, + "state": {"Alabama", "Alaska", "Arizona", "Arkansas", "California", "Colorado", "Connecticut", "Delaware", "Florida", "Georgia", "Hawaii", "Idaho", "Illinois", "Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana", "Maine", "Maryland", "Massachusetts", "Michigan", "Minnesota", "Mississippi", "Missouri", "Montana", "Nebraska", "Nevada", "New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina", "North Dakota", "Ohio", "Oklahoma", "Oregon", "Pennsylvania", "Rhode Island", "South Carolina", "South Dakota", "Tennessee", "Texas", "Utah", "Vermont", "Virginia", "Washington", "West Virginia", "Wisconsin", "Wyoming"}, + "state_abr": {"AL", "AK", "AS", "AZ", "AR", "CA", "CO", "CT", "DE", "DC", "FM", "FL", "GA", "GU", "HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA", "ME", "MH", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE", "NV", "NH", "NJ", "NM", "NY", "NC", "ND", "MP", "OH", "OK", "OR", "PW", "PA", "PR", "RI", "SC", "SD", "TN", "TX", "UT", "VT", "VI", "VA", "WA", "WV", "WI", "WY", "AE", "AA", "AP"}, + "zip": {"#####"}, + "country": {"Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra", "Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina", "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan", "Bahamas", "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium", "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia", "Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil", "British Indian Ocean Territory", "British Virgin Islands", "Brunei Darussalam", "Bulgaria", "Burkina Faso", "Burundi", "Cambodia", "Cameroon", "Canada", "Cape Verde", "Cayman Islands", "Central African Republic", "Chad", "Chile", "China", "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo", "Congo", "Cook Islands", "Costa Rica", "Cote Divoire", "Croatia", "Cuba", "Cyprus", "Czech Republic", "Denmark", "Djibouti", "Dominica", "Dominican Republic", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea", "Estonia", "Ethiopia", "Faroe Islands", "Falkland Islands", "Fiji", "Finland", "France", "French Guiana", "French Polynesia", "French Southern Territories", "Gabon", "Gambia", "Georgia", "Germany", "Ghana", "Gibraltar", "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guernsey", "Guinea", "Guinea-Bissau", "Guyana", "Haiti", "Heard Island and McDonald Islands", "Holy See (Vatican City State)", "Honduras", "Hong Kong", "Hungary", "Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland", "Isle of Man", "Israel", "Italy", "Jamaica", "Japan", "Jersey", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "Korea", "Korea", "Kuwait", "Kyrgyz Republic", "Lao Peoples Democratic Republic", "Latvia", "Lebanon", "Lesotho", "Liberia", "Libyan Arab Jamahiriya", "Liechtenstein", "Lithuania", "Luxembourg", "Macao", "Macedonia", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands", "Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia", "Moldova", "Monaco", "Mongolia", "Montenegro", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia", "Nauru", "Nepal", "Netherlands Antilles", "Netherlands", "New Caledonia", "New Zealand", "Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "Northern Mariana Islands", "Norway", "Oman", "Pakistan", "Palau", "Palestinian Territory", "Panama", "Papua New Guinea", "Paraguay", "Peru", "Philippines", "Pitcairn Islands", "Poland", "Portugal", "Puerto Rico", "Qatar", "Reunion", "Romania", "Russian Federation", "Rwanda", "Saint Barthelemy", "Saint Helena", "Saint Kitts and Nevis", "Saint Lucia", "Saint Martin", "Saint Pierre and Miquelon", "Saint Vincent and the Grenadines", "Samoa", "San Marino", "Sao Tome and Principe", "Saudi Arabia", "Senegal", "Serbia", "Seychelles", "Sierra Leone", "Singapore", "Slovakia (Slovak Republic)", "Slovenia", "Solomon Islands", "Somalia", "South Africa", "South Georgia and the South Sandwich Islands", "Spain", "Sri Lanka", "Sudan", "Suriname", "Svalbard & Jan Mayen Islands", "Swaziland", "Sweden", "Switzerland", "Syrian Arab Republic", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "Timor-Leste", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey", "Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Uganda", "Ukraine", "United Arab Emirates", "United Kingdom", "United States of America", "United States Minor Outlying Islands", "United States Virgin Islands", "Uruguay", "Uzbekistan", "Vanuatu", "Venezuela", "Vietnam", "Wallis and Futuna", "Western Sahara", "Yemen", "Zambia", "Zimbabwe"}, + "country_abr": {"AF", "AL", "DZ", "AS", "AD", "AO", "AI", "AQ", "AG", "AR", "AM", "AW", "AU", "AT", "AZ", "BS", "BH", "BD", "BB", "BY", "BE", "BZ", "BJ", "BM", "BT", "BO", "BA", "BW", "BV", "BR", "IO", "BN", "BG", "BF", "BI", "KH", "CM", "CA", "CV", "KY", "CF", "TD", "CL", "CN", "CX", "CC", "CO", "KM", "CG", "CK", "CR", "CI", "HR", "CU", "CY", "CZ", "DK", "DJ", "DM", "DO", "TL", "EC", "EG", "SV", "GQ", "ER", "EE", "ET", "FK", "FO", "FJ", "FI", "FR", "FX", "GF", "PF", "TF", "GA", "GM", "GE", "DE", "GH", "GI", "GR", "GL", "GD", "GP", "GU", "GT", "GN", "GW", "GY", "HT", "HM", "HN", "HK", "HU", "IS", "IN", "ID", "IR", "IQ", "IE", "IL", "IT", "JM", "JP", "JO", "KZ", "KE", "KI", "KP", "KR", "KW", "KG", "LA", "LV", "LB", "LS", "LR", "LY", "LI", "LT", "LU", "MO", "MK", "MG", "MW", "MY", "MV", "ML", "MT", "MH", "MQ", "MR", "MU", "YT", "MX", "FM", "MD", "MC", "MN", "MS", "MA", "MZ", "MM", "NA", "NR", "NP", "NL", "AN", "NC", "NZ", "NI", "NE", "NG", "NU", "NF", "MP", "NO", "OM", "PK", "PW", "PA", "PG", "PY", "PE", "PH", "PN", "PL", "PT", "PR", "QA", "RE", "RO", "RU", "RW", "KN", "LC", "VC", "WS", "SM", "ST", "SA", "SN", "RS", "SC", "SL", "SG", "SK", "SI", "SB", "SO", "ZA", "ES", "LK", "SH", "PM", "SD", "SR", "SJ", "SZ", "SE", "CH", "SY", "TW", "TJ", "TZ", "TH", "TG", "TK", "TO", "TT", "TN", "TR", "TM", "TC", "TV", "UG", "UA", "AE", "GB", "US", "UM", "UY", "UZ", "VU", "VA", "VE", "VN", "VG", "VI", "WF", "EH", "YE", "YU", "ZR", "ZM", "ZW"}, +} diff --git a/vendor/github.com/brianvoe/gofakeit/data/beer.go b/vendor/github.com/brianvoe/gofakeit/data/beer.go new file mode 100644 index 00000000000..1192907d5f2 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/data/beer.go @@ -0,0 +1,10 @@ +package data + +// Beer consists of various beer information +var Beer = map[string][]string{ + "name": {"Pliny The Elder", "Founders Kentucky Breakfast", "Trappistes Rochefort 10", "HopSlam Ale", "Stone Imperial Russian Stout", "St. Bernardus Abt 12", "Founders Breakfast Stout", "Weihenstephaner Hefeweissbier", "Péché Mortel", "Celebrator Doppelbock", "Duvel", "Dreadnaught IPA", "Nugget Nectar", "La Fin Du Monde", "Bourbon County Stout", "Old Rasputin Russian Imperial Stout", "Two Hearted Ale", "Ruination IPA", "Schneider Aventinus", "Double Bastard Ale", "90 Minute IPA", "Hop Rod Rye", "Trappistes Rochefort 8", "Chimay Grande Réserve", "Stone IPA", "Arrogant Bastard Ale", "Edmund Fitzgerald Porter", "Chocolate St", "Oak Aged Yeti Imperial Stout", "Ten FIDY", "Storm King Stout", "Shakespeare Oatmeal", "Alpha King Pale Ale", "Westmalle Trappist Tripel", "Samuel Smith’s Imperial IPA", "Yeti Imperial Stout", "Hennepin", "Samuel Smith’s Oatmeal Stout", "Brooklyn Black", "Oaked Arrogant Bastard Ale", "Sublimely Self-Righteous Ale", "Trois Pistoles", "Bell’s Expedition", "Sierra Nevada Celebration Ale", "Sierra Nevada Bigfoot Barleywine Style Ale", "Racer 5 India Pale Ale, Bear Republic Bre", "Orval Trappist Ale", "Hercules Double IPA", "Maharaj", "Maudite"}, + "hop": {"Ahtanum", "Amarillo", "Bitter Gold", "Bravo", "Brewer’s Gold", "Bullion", "Cascade", "Cashmere", "Centennial", "Chelan", "Chinook", "Citra", "Cluster", "Columbia", "Columbus", "Comet", "Crystal", "Equinox", "Eroica", "Fuggle", "Galena", "Glacier", "Golding", "Hallertau", "Horizon", "Liberty", "Magnum", "Millennium", "Mosaic", "Mt. Hood", "Mt. Rainier", "Newport", "Northern Brewer", "Nugget", "Olympic", "Palisade", "Perle", "Saaz", "Santiam", "Simcoe", "Sorachi Ace", "Sterling", "Summit", "Tahoma", "Tettnang", "TriplePearl", "Ultra", "Vanguard", "Warrior", "Willamette", "Yakima Gol"}, + "yeast": {"1007 - German Ale", "1010 - American Wheat", "1028 - London Ale", "1056 - American Ale", "1084 - Irish Ale", "1098 - British Ale", "1099 - Whitbread Ale", "1187 - Ringwood Ale", "1272 - American Ale II", "1275 - Thames Valley Ale", "1318 - London Ale III", "1332 - Northwest Ale", "1335 - British Ale II", "1450 - Dennys Favorite 50", "1469 - West Yorkshire Ale", "1728 - Scottish Ale", "1968 - London ESB Ale", "2565 - Kölsch", "1214 - Belgian Abbey", "1388 - Belgian Strong Ale", "1762 - Belgian Abbey II", "3056 - Bavarian Wheat Blend", "3068 - Weihenstephan Weizen", "3278 - Belgian Lambic Blend", "3333 - German Wheat", "3463 - Forbidden Fruit", "3522 - Belgian Ardennes", "3638 - Bavarian Wheat", "3711 - French Saison", "3724 - Belgian Saison", "3763 - Roeselare Ale Blend", "3787 - Trappist High Gravity", "3942 - Belgian Wheat", "3944 - Belgian Witbier", "2000 - Budvar Lager", "2001 - Urquell Lager", "2007 - Pilsen Lager", "2035 - American Lager", "2042 - Danish Lager", "2112 - California Lager", "2124 - Bohemian Lager", "2206 - Bavarian Lager", "2278 - Czech Pils", "2308 - Munich Lager", "2633 - Octoberfest Lager Blend", "5112 - Brettanomyces bruxellensis", "5335 - Lactobacillus", "5526 - Brettanomyces lambicus", "5733 - Pediococcus"}, + "malt": {"Black malt", "Caramel", "Carapils", "Chocolate", "Munich", "Caramel", "Carapils", "Chocolate malt", "Munich", "Pale", "Roasted barley", "Rye malt", "Special roast", "Victory", "Vienna", "Wheat mal"}, + "style": {"Light Lager", "Pilsner", "European Amber Lager", "Dark Lager", "Bock", "Light Hybrid Beer", "Amber Hybrid Beer", "English Pale Ale", "Scottish And Irish Ale", "Merican Ale", "English Brown Ale", "Porter", "Stout", "India Pale Ale", "German Wheat And Rye Beer", "Belgian And French Ale", "Sour Ale", "Belgian Strong Ale", "Strong Ale", "Fruit Beer", "Vegetable Beer", "Smoke-flavored", "Wood-aged Beer"}, +} diff --git a/vendor/github.com/brianvoe/gofakeit/data/colors.go b/vendor/github.com/brianvoe/gofakeit/data/colors.go new file mode 100644 index 00000000000..3aca817d69f --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/data/colors.go @@ -0,0 +1,7 @@ +package data + +// Colors consists of color information +var Colors = map[string][]string{ + "safe": {"black", "maroon", "green", "navy", "olive", "purple", "teal", "lime", "blue", "silver", "gray", "yellow", "fuchsia", "aqua", "white"}, + "full": {"AliceBlue", "AntiqueWhite", "Aqua", "Aquamarine", "Azure", "Beige", "Bisque", "Black", "BlanchedAlmond", "Blue", "BlueViolet", "Brown", "BurlyWood", "CadetBlue", "Chartreuse", "Chocolate", "Coral", "CornflowerBlue", "Cornsilk", "Crimson", "Cyan", "DarkBlue", "DarkCyan", "DarkGoldenRod", "DarkGray", "DarkGreen", "DarkKhaki", "DarkMagenta", "DarkOliveGreen", "Darkorange", "DarkOrchid", "DarkRed", "DarkSalmon", "DarkSeaGreen", "DarkSlateBlue", "DarkSlateGray", "DarkTurquoise", "DarkViolet", "DeepPink", "DeepSkyBlue", "DimGray", "DimGrey", "DodgerBlue", "FireBrick", "FloralWhite", "ForestGreen", "Fuchsia", "Gainsboro", "GhostWhite", "Gold", "GoldenRod", "Gray", "Green", "GreenYellow", "HoneyDew", "HotPink", "IndianRed ", "Indigo ", "Ivory", "Khaki", "Lavender", "LavenderBlush", "LawnGreen", "LemonChiffon", "LightBlue", "LightCoral", "LightCyan", "LightGoldenRodYellow", "LightGray", "LightGreen", "LightPink", "LightSalmon", "LightSeaGreen", "LightSkyBlue", "LightSlateGray", "LightSteelBlue", "LightYellow", "Lime", "LimeGreen", "Linen", "Magenta", "Maroon", "MediumAquaMarine", "MediumBlue", "MediumOrchid", "MediumPurple", "MediumSeaGreen", "MediumSlateBlue", "MediumSpringGreen", "MediumTurquoise", "MediumVioletRed", "MidnightBlue", "MintCream", "MistyRose", "Moccasin", "NavajoWhite", "Navy", "OldLace", "Olive", "OliveDrab", "Orange", "OrangeRed", "Orchid", "PaleGoldenRod", "PaleGreen", "PaleTurquoise", "PaleVioletRed", "PapayaWhip", "PeachPuff", "Peru", "Pink", "Plum", "PowderBlue", "Purple", "Red", "RosyBrown", "RoyalBlue", "SaddleBrown", "Salmon", "SandyBrown", "SeaGreen", "SeaShell", "Sienna", "Silver", "SkyBlue", "SlateBlue", "SlateGray", "Snow", "SpringGreen", "SteelBlue", "Tan", "Teal", "Thistle", "Tomato", "Turquoise", "Violet", "Wheat", "White", "WhiteSmoke", "Yellow", "YellowGreen"}, +} diff --git a/vendor/github.com/brianvoe/gofakeit/data/company.go b/vendor/github.com/brianvoe/gofakeit/data/company.go new file mode 100644 index 00000000000..b2a3790c7c6 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/data/company.go @@ -0,0 +1,9 @@ +package data + +// Company consists of company information +var Company = map[string][]string{ + "name": {"{person.last} {company.suffix}", "{person.last}-{person.last}", "{person.last}, {person.last} and {person.last}"}, + "suffix": {"Inc", "and Sons", "LLC", "Group"}, + "buzzwords": {"Adaptive", "Advanced", "Ameliorated", "Assimilated", "Automated", "Balanced", "Business-focused", "Centralized", "Cloned", "Compatible", "Configurable", "Cross-group", "Cross-platform", "Customer-focused", "Customizable", "De-engineered", "Decentralized", "Devolved", "Digitized", "Distributed", "Diverse", "Down-sized", "Enhanced", "Enterprise-wide", "Ergonomic", "Exclusive", "Expanded", "Extended", "Face to face", "Focused", "Front-line", "Fully-configurable", "Function-based", "Fundamental", "Future-proofed", "Grass-roots", "Horizontal", "Implemented", "Innovative", "Integrated", "Intuitive", "Inverse", "Managed", "Mandatory", "Monitored", "Multi-channelled", "Multi-lateral", "Multi-layered", "Multi-tiered", "Networked", "Object-based", "Open-architected", "Open-source", "Operative", "Optimized", "Optional", "Organic", "Organized", "Persevering", "Persistent", "Phased", "Polarised", "Pre-emptive", "Proactive", "Profit-focused", "Profound", "Programmable", "Progressive", "Public-key", "Quality-focused", "Re-contextualized", "Re-engineered", "Reactive", "Realigned", "Reduced", "Reverse-engineered", "Right-sized", "Robust", "Seamless", "Secured", "Self-enabling", "Sharable", "Stand-alone", "Streamlined", "Switchable", "Synchronised", "Synergistic", "Synergized", "Team-oriented", "Total", "Triple-buffered", "Universal", "Up-sized", "Upgradable", "User-centric", "User-friendly", "Versatile", "Virtual", "Vision-oriented", "Visionary", "24 hour", "24/7", "3rd generation", "4th generation", "5th generation", "6th generation", "actuating", "analyzing", "asymmetric", "asynchronous", "attitude-oriented", "background", "bandwidth-monitored", "bi-directional", "bifurcated", "bottom-line", "clear-thinking", "client-driven", "client-server", "coherent", "cohesive", "composite", "content-based", "context-sensitive", "contextually-based", "dedicated", "demand-driven", "didactic", "directional", "discrete", "disintermediate", "dynamic", "eco-centric", "empowering", "encompassing", "even-keeled", "executive", "explicit", "exuding", "fault-tolerant", "foreground", "fresh-thinking", "full-range", "global", "grid-enabled", "heuristic", "high-level", "holistic", "homogeneous", "human-resource", "hybrid", "impactful", "incremental", "intangible", "interactive", "intermediate", "leading edge", "local", "logistical", "maximized", "methodical", "mission-critical", "mobile", "modular", "motivating", "multi-state", "multi-tasking", "multimedia", "national", "needs-based", "neutral", "next generation", "non-volatile", "object-oriented", "optimal", "optimizing", "radical", "real-time", "reciprocal", "regional", "responsive", "scalable", "secondary", "solution-oriented", "stable", "static", "system-worthy", "systematic", "systemic", "tangible", "tertiary", "transitional", "uniform", "upward-trending", "user-facing", "value-added", "web-enabled", "well-modulated", "zero administration", "zero defect", "zero tolerance", "Graphic Interface", "Graphical User Interface", "ability", "access", "adapter", "algorithm", "alliance", "analyzer", "application", "approach", "architecture", "archive", "array", "artificial intelligence", "attitude", "benchmark", "budgetary management", "capability", "capacity", "challenge", "circuit", "collaboration", "complexity", "concept", "conglomeration", "contingency", "core", "customer loyalty", "data-warehouse", "database", "definition", "emulation", "encoding", "encryption", "extranet", "firmware", "flexibility", "focus group", "forecast", "frame", "framework", "function", "functionalities", "groupware", "hardware", "help-desk", "hierarchy", "hub", "implementation", "info-mediaries", "infrastructure", "initiative", "installation", "instruction set", "interface", "internet solution", "intranet", "knowledge base", "knowledge user", "leverage", "local area network", "matrices", "matrix", "methodology", "middleware", "migration", "model", "moderator", "monitoring", "moratorium", "neural-net", "open architecture", "open system", "orchestration", "paradigm", "parallelism", "policy", "portal", "pricing structure", "process improvement", "product", "productivity", "project", "projection", "protocol", "secured line", "service-desk", "software", "solution", "standardization", "strategy", "structure", "success", "superstructure", "support", "synergy", "system engine", "task-force", "throughput", "time-frame", "toolset", "utilisation", "website", "workforce"}, + "bs": {"aggregate", "architect", "benchmark", "brand", "cultivate", "deliver", "deploy", "disintermediate", "drive", "e-enable", "embrace", "empower", "enable", "engage", "engineer", "enhance", "envisioneer", "evolve", "expedite", "exploit", "extend", "facilitate", "generate", "grow", "harness", "implement", "incentivize", "incubate", "innovate", "integrate", "iterate", "leverage", "matrix", "maximize", "mesh", "monetize", "morph", "optimize", "orchestrate", "productize", "recontextualize", "redefine", "reintermediate", "reinvent", "repurpose", "revolutionize", "scale", "seize", "strategize", "streamline", "syndicate", "synergize", "synthesize", "target", "transform", "transition", "unleash", "utilize", "visualize", "whiteboard", "24/365", "24/7", "B2B", "B2C", "back-end", "best-of-breed", "bleeding-edge", "bricks-and-clicks", "clicks-and-mortar", "collaborative", "compelling", "cross-media", "cross-platform", "customized", "cutting-edge", "distributed", "dot-com", "dynamic", "e-business", "efficient", "end-to-end", "enterprise", "extensible", "frictionless", "front-end", "global", "granular", "holistic", "impactful", "innovative", "integrated", "interactive", "intuitive", "killer", "leading-edge", "magnetic", "mission-critical", "next-generation", "one-to-one", "open-source", "out-of-the-box", "plug-and-play", "proactive", "real-time", "revolutionary", "rich", "robust", "scalable", "seamless", "sexy", "sticky", "strategic", "synergistic", "transparent", "turn-key", "ubiquitous", "user-centric", "value-added", "vertical", "viral", "virtual", "visionary", "web-enabled", "wireless", "world-class", "ROI", "action-items", "applications", "architectures", "bandwidth", "channels", "communities", "content", "convergence", "deliverables", "e-business", "e-commerce", "e-markets", "e-services", "e-tailers", "experiences", "eyeballs", "functionalities", "infomediaries", "infrastructures", "initiatives", "interfaces", "markets", "methodologies", "metrics", "mindshare", "models", "networks", "niches", "paradigms", "partnerships", "platforms", "portals", "relationships", "schemas", "solutions", "supply-chains", "synergies", "systems", "technologies", "users", "vortals", "web services", "web-readiness"}, +} diff --git a/vendor/github.com/brianvoe/gofakeit/data/computer.go b/vendor/github.com/brianvoe/gofakeit/data/computer.go new file mode 100644 index 00000000000..b682c6f820c --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/data/computer.go @@ -0,0 +1,8 @@ +package data + +// Computer consists of computer information +var Computer = map[string][]string{ + "linux_processor": {"i686", "x86_64"}, + "mac_processor": {"Intel", "PPC", "U; Intel", "U; PPC"}, + "windows_platform": {"Windows NT 6.2", "Windows NT 6.1", "Windows NT 6.0", "Windows NT 5.2", "Windows NT 5.1", "Windows NT 5.01", "Windows NT 5.0", "Windows NT 4.0", "Windows 98; Win 9x 4.90", "Windows 98", "Windows 95", "Windows CE"}, +} diff --git a/vendor/github.com/brianvoe/gofakeit/data/contact.go b/vendor/github.com/brianvoe/gofakeit/data/contact.go new file mode 100644 index 00000000000..88b957961db --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/data/contact.go @@ -0,0 +1,6 @@ +package data + +// Contact consists of contact information +var Contact = map[string][]string{ + "phone": {"###-###-####", "(###)###-####", "1-###-###-####", "###.###.####"}, +} diff --git a/vendor/github.com/brianvoe/gofakeit/data/currency.go b/vendor/github.com/brianvoe/gofakeit/data/currency.go new file mode 100644 index 00000000000..13b8019973c --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/data/currency.go @@ -0,0 +1,7 @@ +package data + +// Currency consists of currency information +var Currency = map[string][]string{ + "short": {"AED", "AFN", "ALL", "AMD", "ANG", "AOA", "ARS", "AUD", "AWG", "AZN", "BAM", "BBD", "BDT", "BGN", "BHD", "BIF", "BMD", "BND", "BOB", "BRL", "BSD", "BTN", "BWP", "BYR", "BZD", "CAD", "CDF", "CHF", "CLP", "CNY", "COP", "CRC", "CUC", "CUP", "CVE", "CZK", "DJF", "DKK", "DOP", "DZD", "EGP", "ERN", "ETB", "EUR", "FJD", "FKP", "GBP", "GEL", "GGP", "GHS", "GIP", "GMD", "GNF", "GTQ", "GYD", "HKD", "HNL", "HRK", "HTG", "HUF", "IDR", "ILS", "IMP", "INR", "IQD", "IRR", "ISK", "JEP", "JMD", "JOD", "JPY", "KES", "KGS", "KHR", "KMF", "KPW", "KRW", "KWD", "KYD", "KZT", "LAK", "LBP", "LKR", "LRD", "LSL", "LTL", "LYD", "MAD", "MDL", "MGA", "MKD", "MMK", "MNT", "MOP", "MRO", "MUR", "MVR", "MWK", "MXN", "MYR", "MZN", "NAD", "NGN", "NIO", "NOK", "NPR", "NZD", "OMR", "PAB", "PEN", "PGK", "PHP", "PKR", "PLN", "PYG", "QAR", "RON", "RSD", "RUB", "RWF", "SAR", "SBD", "SCR", "SDG", "SEK", "SGD", "SHP", "SLL", "SOS", "SPL", "SRD", "STD", "SVC", "SYP", "SZL", "THB", "TJS", "TMT", "TND", "TOP", "TRY", "TTD", "TVD", "TWD", "TZS", "UAH", "UGX", "USD", "UYU", "UZS", "VEF", "VND", "VUV", "WST", "XAF", "XCD", "XDR", "XOF", "XPF", "YER", "ZAR", "ZMW", "ZWD"}, + "long": {"United Arab Emirates Dirham", "Afghanistan Afghani", "Albania Lek", "Armenia Dram", "Netherlands Antilles Guilder", "Angola Kwanza", "Argentina Peso", "Australia Dollar", "Aruba Guilder", "Azerbaijan New Manat", "Bosnia and Herzegovina Convertible Marka", "Barbados Dollar", "Bangladesh Taka", "Bulgaria Lev", "Bahrain Dinar", "Burundi Franc", "Bermuda Dollar", "Brunei Darussalam Dollar", "Bolivia Boliviano", "Brazil Real", "Bahamas Dollar", "Bhutan Ngultrum", "Botswana Pula", "Belarus Ruble", "Belize Dollar", "Canada Dollar", "Congo/Kinshasa Franc", "Switzerland Franc", "Chile Peso", "China Yuan Renminbi", "Colombia Peso", "Costa Rica Colon", "Cuba Convertible Peso", "Cuba Peso", "Cape Verde Escudo", "Czech Republic Koruna", "Djibouti Franc", "Denmark Krone", "Dominican Republic Peso", "Algeria Dinar", "Egypt Pound", "Eritrea Nakfa", "Ethiopia Birr", "Euro Member Countries", "Fiji Dollar", "Falkland Islands (Malvinas) Pound", "United Kingdom Pound", "Georgia Lari", "Guernsey Pound", "Ghana Cedi", "Gibraltar Pound", "Gambia Dalasi", "Guinea Franc", "Guatemala Quetzal", "Guyana Dollar", "Hong Kong Dollar", "Honduras Lempira", "Croatia Kuna", "Haiti Gourde", "Hungary Forint", "Indonesia Rupiah", "Israel Shekel", "Isle of Man Pound", "India Rupee", "Iraq Dinar", "Iran Rial", "Iceland Krona", "Jersey Pound", "Jamaica Dollar", "Jordan Dinar", "Japan Yen", "Kenya Shilling", "Kyrgyzstan Som", "Cambodia Riel", "Comoros Franc", "Korea (North) Won", "Korea (South) Won", "Kuwait Dinar", "Cayman Islands Dollar", "Kazakhstan Tenge", "Laos Kip", "Lebanon Pound", "Sri Lanka Rupee", "Liberia Dollar", "Lesotho Loti", "Lithuania Litas", "Libya Dinar", "Morocco Dirham", "Moldova Leu", "Madagascar Ariary", "Macedonia Denar", "Myanmar (Burma) Kyat", "Mongolia Tughrik", "Macau Pataca", "Mauritania Ouguiya", "Mauritius Rupee", "Maldives (Maldive Islands) Rufiyaa", "Malawi Kwacha", "Mexico Peso", "Malaysia Ringgit", "Mozambique Metical", "Namibia Dollar", "Nigeria Naira", "Nicaragua Cordoba", "Norway Krone", "Nepal Rupee", "New Zealand Dollar", "Oman Rial", "Panama Balboa", "Peru Nuevo Sol", "Papua New Guinea Kina", "Philippines Peso", "Pakistan Rupee", "Poland Zloty", "Paraguay Guarani", "Qatar Riyal", "Romania New Leu", "Serbia Dinar", "Russia Ruble", "Rwanda Franc", "Saudi Arabia Riyal", "Solomon Islands Dollar", "Seychelles Rupee", "Sudan Pound", "Sweden Krona", "Singapore Dollar", "Saint Helena Pound", "Sierra Leone Leone", "Somalia Shilling", "Seborga Luigino", "Suriname Dollar", "São Tomé and Príncipe Dobra", "El Salvador Colon", "Syria Pound", "Swaziland Lilangeni", "Thailand Baht", "Tajikistan Somoni", "Turkmenistan Manat", "Tunisia Dinar", "Tonga Pa'anga", "Turkey Lira", "Trinidad and Tobago Dollar", "Tuvalu Dollar", "Taiwan New Dollar", "Tanzania Shilling", "Ukraine Hryvnia", "Uganda Shilling", "United States Dollar", "Uruguay Peso", "Uzbekistan Som", "Venezuela Bolivar", "Viet Nam Dong", "Vanuatu Vatu", "Samoa Tala", "Communauté Financière Africaine (BEAC) CFA Franc BEAC", "East Caribbean Dollar", "International Monetary Fund (IMF) Special Drawing Rights", "Communauté Financière Africaine (BCEAO) Franc", "Comptoirs Français du Pacifique (CFP) Franc", "Yemen Rial", "South Africa Rand", "Zambia Kwacha", "Zimbabwe Dollar"}, +} diff --git a/vendor/github.com/brianvoe/gofakeit/data/data.go b/vendor/github.com/brianvoe/gofakeit/data/data.go new file mode 100644 index 00000000000..d751c999435 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/data/data.go @@ -0,0 +1,28 @@ +package data + +// Data consists of the main set of fake information +var Data = map[string]map[string][]string{ + "person": Person, + "contact": Contact, + "address": Address, + "company": Company, + "job": Job, + "lorem": Lorem, + "internet": Internet, + "file": Files, + "color": Colors, + "computer": Computer, + "payment": Payment, + "hipster": Hipster, + "beer": Beer, + "hacker": Hacker, + "currency": Currency, + "log_level": LogLevels, + "timezone": TimeZone, + "vehicle": Vehicle, +} + +// IntData consists of the main set of fake information (integer only) +var IntData = map[string]map[string][]int{ + "status_code": StatusCodes, +} diff --git a/vendor/github.com/brianvoe/gofakeit/data/datetime.go b/vendor/github.com/brianvoe/gofakeit/data/datetime.go new file mode 100644 index 00000000000..3347120a67e --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/data/datetime.go @@ -0,0 +1,9 @@ +package data + +// TimeZone is an array of short and long timezones +var TimeZone = map[string][]string{ + "offset": {"-12", "-11", "-10", "-8", "-7", "-7", "-8", "-7", "-6", "-6", "-6", "-5", "-5", "-6", "-5", "-4", "-4", "-4.5", "-4", "-3", "-4", "-4", "-4", "-2.5", "-3", "-3", "-3", "-3", "-3", "-3", "-2", "-1", "0", "-1", "1", "0", "0", "1", "1", "0", "2", "2", "2", "2", "1", "1", "3", "3", "2", "3", "3", "2", "3", "3", "3", "2", "3", "3", "3", "3", "3", "3", "4", "4.5", "4", "5", "4", "4", "4", "4.5", "5", "5", "5", "5.5", "5.5", "5.75", "6", "6", "6.5", "7", "7", "8", "8", "8", "8", "8", "8", "9", "9", "9", "9.5", "9.5", "10", "10", "10", "10", "10", "11", "11", "12", "12", "12", "12", "13", "13", "13"}, + "abr": {"DST", "U", "HST", "AKDT", "PDT", "PDT", "PST", "UMST", "MDT", "MDT", "CAST", "CDT", "CDT", "CCST", "SPST", "EDT", "UEDT", "VST", "PYT", "ADT", "CBST", "SWST", "PSST", "NDT", "ESAST", "AST", "SEST", "GDT", "MST", "BST", "U", "MDT", "ADT", "CVST", "MDT", "UTC", "GMT", "BST", "GDT", "GST", "WEDT", "CEDT", "RDT", "CEDT", "WCAST", "NST", "GDT", "MEDT", "EST", "SDT", "EEDT", "SAST", "FDT", "TDT", "JDT", "LST", "JST", "AST", "KST", "AST", "EAST", "MSK", "SAMT", "IDT", "AST", "ADT", "MST", "GST", "CST", "AST", "WAST", "YEKT", "PKT", "IST", "SLST", "NST", "CAST", "BST", "MST", "SAST", "NCAST", "CST", "NAST", "MPST", "WAST", "TST", "UST", "NAEST", "JST", "KST", "CAST", "ACST", "EAST", "AEST", "WPST", "TST", "YST", "CPST", "VST", "NZST", "U", "FST", "MST", "KDT", "TST", "SST"}, + "text": {"Dateline Standard Time", "UTC-11", "Hawaiian Standard Time", "Alaskan Standard Time", "Pacific Standard Time (Mexico)", "Pacific Daylight Time", "Pacific Standard Time", "US Mountain Standard Time", "Mountain Standard Time (Mexico)", "Mountain Standard Time", "Central America Standard Time", "Central Standard Time", "Central Standard Time (Mexico)", "Canada Central Standard Time", "SA Pacific Standard Time", "Eastern Standard Time", "US Eastern Standard Time", "Venezuela Standard Time", "Paraguay Standard Time", "Atlantic Standard Time", "Central Brazilian Standard Time", "SA Western Standard Time", "Pacific SA Standard Time", "Newfoundland Standard Time", "E. South America Standard Time", "Argentina Standard Time", "SA Eastern Standard Time", "Greenland Standard Time", "Montevideo Standard Time", "Bahia Standard Time", "UTC-02", "Mid-Atlantic Standard Time", "Azores Standard Time", "Cape Verde Standard Time", "Morocco Standard Time", "UTC", "Greenwich Mean Time", "British Summer Time", "GMT Standard Time", "Greenwich Standard Time", "W. Europe Standard Time", "Central Europe Standard Time", "Romance Standard Time", "Central European Standard Time", "W. Central Africa Standard Time", "Namibia Standard Time", "GTB Standard Time", "Middle East Standard Time", "Egypt Standard Time", "Syria Standard Time", "E. Europe Standard Time", "South Africa Standard Time", "FLE Standard Time", "Turkey Standard Time", "Israel Standard Time", "Libya Standard Time", "Jordan Standard Time", "Arabic Standard Time", "Kaliningrad Standard Time", "Arab Standard Time", "E. Africa Standard Time", "Moscow Standard Time", "Samara Time", "Iran Standard Time", "Arabian Standard Time", "Azerbaijan Standard Time", "Mauritius Standard Time", "Georgian Standard Time", "Caucasus Standard Time", "Afghanistan Standard Time", "West Asia Standard Time", "Yekaterinburg Time", "Pakistan Standard Time", "India Standard Time", "Sri Lanka Standard Time", "Nepal Standard Time", "Central Asia Standard Time", "Bangladesh Standard Time", "Myanmar Standard Time", "SE Asia Standard Time", "N. Central Asia Standard Time", "China Standard Time", "North Asia Standard Time", "Singapore Standard Time", "W. Australia Standard Time", "Taipei Standard Time", "Ulaanbaatar Standard Time", "North Asia East Standard Time", "Japan Standard Time", "Korea Standard Time", "Cen. Australia Standard Time", "AUS Central Standard Time", "E. Australia Standard Time", "AUS Eastern Standard Time", "West Pacific Standard Time", "Tasmania Standard Time", "Yakutsk Standard Time", "Central Pacific Standard Time", "Vladivostok Standard Time", "New Zealand Standard Time", "UTC+12", "Fiji Standard Time", "Magadan Standard Time", "Kamchatka Standard Time", "Tonga Standard Time", "Samoa Standard Time"}, + "full": {"(UTC-12:00) International Date Line West", "(UTC-11:00) Coordinated Universal Time-11", "(UTC-10:00) Hawaii", "(UTC-09:00) Alaska", "(UTC-08:00) Baja California", "(UTC-07:00) Pacific Time (US & Canada)", "(UTC-08:00) Pacific Time (US & Canada)", "(UTC-07:00) Arizona", "(UTC-07:00) Chihuahua, La Paz, Mazatlan", "(UTC-07:00) Mountain Time (US & Canada)", "(UTC-06:00) Central America", "(UTC-06:00) Central Time (US & Canada)", "(UTC-06:00) Guadalajara, Mexico City, Monterrey", "(UTC-06:00) Saskatchewan", "(UTC-05:00) Bogota, Lima, Quito", "(UTC-05:00) Eastern Time (US & Canada)", "(UTC-05:00) Indiana (East)", "(UTC-04:30) Caracas", "(UTC-04:00) Asuncion", "(UTC-04:00) Atlantic Time (Canada)", "(UTC-04:00) Cuiaba", "(UTC-04:00) Georgetown, La Paz, Manaus, San Juan", "(UTC-04:00) Santiago", "(UTC-03:30) Newfoundland", "(UTC-03:00) Brasilia", "(UTC-03:00) Buenos Aires", "(UTC-03:00) Cayenne, Fortaleza", "(UTC-03:00) Greenland", "(UTC-03:00) Montevideo", "(UTC-03:00) Salvador", "(UTC-02:00) Coordinated Universal Time-02", "(UTC-02:00) Mid-Atlantic - Old", "(UTC-01:00) Azores", "(UTC-01:00) Cape Verde Is.", "(UTC) Casablanca", "(UTC) Coordinated Universal Time", "(UTC) Edinburgh, London", "(UTC+01:00) Edinburgh, London", "(UTC) Dublin, Lisbon", "(UTC) Monrovia, Reykjavik", "(UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna", "(UTC+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague", "(UTC+01:00) Brussels, Copenhagen, Madrid, Paris", "(UTC+01:00) Sarajevo, Skopje, Warsaw, Zagreb", "(UTC+01:00) West Central Africa", "(UTC+01:00) Windhoek", "(UTC+02:00) Athens, Bucharest", "(UTC+02:00) Beirut", "(UTC+02:00) Cairo", "(UTC+02:00) Damascus", "(UTC+02:00) E. Europe", "(UTC+02:00) Harare, Pretoria", "(UTC+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius", "(UTC+03:00) Istanbul", "(UTC+02:00) Jerusalem", "(UTC+02:00) Tripoli", "(UTC+03:00) Amman", "(UTC+03:00) Baghdad", "(UTC+03:00) Kaliningrad, Minsk", "(UTC+03:00) Kuwait, Riyadh", "(UTC+03:00) Nairobi", "(UTC+03:00) Moscow, St. Petersburg, Volgograd", "(UTC+04:00) Samara, Ulyanovsk, Saratov", "(UTC+03:30) Tehran", "(UTC+04:00) Abu Dhabi, Muscat", "(UTC+04:00) Baku", "(UTC+04:00) Port Louis", "(UTC+04:00) Tbilisi", "(UTC+04:00) Yerevan", "(UTC+04:30) Kabul", "(UTC+05:00) Ashgabat, Tashkent", "(UTC+05:00) Yekaterinburg", "(UTC+05:00) Islamabad, Karachi", "(UTC+05:30) Chennai, Kolkata, Mumbai, New Delhi", "(UTC+05:30) Sri Jayawardenepura", "(UTC+05:45) Kathmandu", "(UTC+06:00) Astana", "(UTC+06:00) Dhaka", "(UTC+06:30) Yangon (Rangoon)", "(UTC+07:00) Bangkok, Hanoi, Jakarta", "(UTC+07:00) Novosibirsk", "(UTC+08:00) Beijing, Chongqing, Hong Kong, Urumqi", "(UTC+08:00) Krasnoyarsk", "(UTC+08:00) Kuala Lumpur, Singapore", "(UTC+08:00) Perth", "(UTC+08:00) Taipei", "(UTC+08:00) Ulaanbaatar", "(UTC+09:00) Irkutsk", "(UTC+09:00) Osaka, Sapporo, Tokyo", "(UTC+09:00) Seoul", "(UTC+09:30) Adelaide", "(UTC+09:30) Darwin", "(UTC+10:00) Brisbane", "(UTC+10:00) Canberra, Melbourne, Sydney", "(UTC+10:00) Guam, Port Moresby", "(UTC+10:00) Hobart", "(UTC+10:00) Yakutsk", "(UTC+11:00) Solomon Is., New Caledonia", "(UTC+11:00) Vladivostok", "(UTC+12:00) Auckland, Wellington", "(UTC+12:00) Coordinated Universal Time+12", "(UTC+12:00) Fiji", "(UTC+12:00) Magadan", "(UTC+12:00) Petropavlovsk-Kamchatsky - Old", "(UTC+13:00) Nuku'alofa", "(UTC+13:00) Samoa"}, +} diff --git a/vendor/github.com/brianvoe/gofakeit/data/files.go b/vendor/github.com/brianvoe/gofakeit/data/files.go new file mode 100644 index 00000000000..363b840017f --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/data/files.go @@ -0,0 +1,7 @@ +package data + +// Files consists of file information +var Files = map[string][]string{ + "mime_type": {"x-world/x-3dmf", "application/octet-stream", "application/x-authorware-bin", "application/x-authorware-map", "application/x-authorware-seg", "text/vnd.abc", "text/html", "video/animaflex", "application/postscript", "audio/aiff", "audio/x-aiff", "audio/aiff", "audio/x-aiff", "audio/aiff", "audio/x-aiff", "application/x-aim", "text/x-audiosoft-intra", "application/x-navi-animation", "application/x-nokia-9000-communicator-add-on-software", "application/mime", "application/octet-stream", "application/arj", "application/octet-stream", "image/x-jg", "video/x-ms-asf", "text/x-asm", "text/asp", "application/x-mplayer2", "video/x-ms-asf", "video/x-ms-asf-plugin", "audio/basic", "audio/x-au", "application/x-troff-msvideo", "video/avi", "video/msvideo", "video/x-msvideo", "video/avs-video", "application/x-bcpio", "application/mac-binary", "application/macbinary", "application/octet-stream", "application/x-binary", "application/x-macbinary", "image/bmp", "image/bmp", "image/x-windows-bmp", "application/book", "application/book", "application/x-bzip2", "application/x-bsh", "application/x-bzip", "application/x-bzip2", "text/plain", "text/x-c", "text/plain", "application/vnd.ms-pki.seccat", "text/plain", "text/x-c", "application/clariscad", "application/x-cocoa", "application/cdf", "application/x-cdf", "application/x-netcdf", "application/pkix-cert", "application/x-x509-ca-cert", "application/x-chat", "application/x-chat", "application/java", "application/java-byte-code", "application/x-java-class", "application/octet-stream", "text/plain", "text/plain", "application/x-cpio", "text/x-c", "application/mac-compactpro", "application/x-compactpro", "application/x-cpt", "application/pkcs-crl", "application/pkix-crl", "application/pkix-cert", "application/x-x509-ca-cert", "application/x-x509-user-cert", "application/x-csh", "text/x-script.csh", "application/x-pointplus", "text/css", "text/plain", "application/x-director", "application/x-deepv", "text/plain", "application/x-x509-ca-cert", "video/x-dv", "application/x-director", "video/dl", "video/x-dl", "application/msword", "application/msword", "application/commonground", "application/drafting", "application/octet-stream", "video/x-dv", "application/x-dvi", "drawing/x-dwf (old)", "model/vnd.dwf", "application/acad", "image/vnd.dwg", "image/x-dwg", "application/dxf", "image/vnd.dwg", "image/x-dwg", "application/x-director", "text/x-script.elisp", "application/x-bytecode.elisp (compiled elisp)", "application/x-elc", "application/x-envoy", "application/postscript", "application/x-esrehber", "text/x-setext", "application/envoy", "application/x-envoy", "application/octet-stream", "text/plain", "text/x-fortran", "text/x-fortran", "text/plain", "text/x-fortran", "application/vnd.fdf", "application/fractals", "image/fif", "video/fli", "video/x-fli", "image/florian", "text/vnd.fmi.flexstor", "video/x-atomic3d-feature", "text/plain", "text/x-fortran", "image/vnd.fpx", "image/vnd.net-fpx", "application/freeloader", "audio/make", "text/plain", "image/g3fax", "image/gif", "video/gl", "video/x-gl", "audio/x-gsm", "audio/x-gsm", "application/x-gsp", "application/x-gss", "application/x-gtar", "application/x-compressed", "application/x-gzip", "application/x-gzip", "multipart/x-gzip", "text/plain", "text/x-h", "application/x-hdf", "application/x-helpfile", "application/vnd.hp-hpgl", "text/plain", "text/x-h", "text/x-script", "application/hlp", "application/x-helpfile", "application/x-winhelp", "application/vnd.hp-hpgl", "application/vnd.hp-hpgl", "application/binhex", "application/binhex4", "application/mac-binhex", "application/mac-binhex40", "application/x-binhex40", "application/x-mac-binhex40", "application/hta", "text/x-component", "text/html", "text/html", "text/html", "text/webviewhtml", "text/html", "x-conference/x-cooltalk", "image/x-icon", "text/plain", "image/ief", "image/ief", "application/iges", "model/iges", "application/iges", "model/iges", "application/x-ima", "application/x-httpd-imap", "application/inf", "application/x-internett-signup", "application/x-ip2", "video/x-isvideo", "audio/it", "application/x-inventor", "i-world/i-vrml", "application/x-livescreen", "audio/x-jam", "text/plain", "text/x-java-source", "text/plain", "text/x-java-source", "application/x-java-commerce", "image/jpeg", "image/pjpeg", "image/jpeg", "image/jpeg", "image/pjpeg", "image/jpeg", "image/pjpeg", "image/jpeg", "image/pjpeg", "image/x-jps", "application/x-javascript", "image/jutvision", "audio/midi", "music/x-karaoke", "application/x-ksh", "text/x-script.ksh", "audio/nspaudio", "audio/x-nspaudio", "audio/x-liveaudio", "application/x-latex", "application/lha", "application/octet-stream", "application/x-lha", "application/octet-stream", "text/plain", "audio/nspaudio", "audio/x-nspaudio", "text/plain", "application/x-lisp", "text/x-script.lisp", "text/plain", "text/x-la-asf", "application/x-latex", "application/octet-stream", "application/x-lzh", "application/lzx", "application/octet-stream", "application/x-lzx", "text/plain", "text/x-m", "video/mpeg", "audio/mpeg", "video/mpeg", "audio/x-mpequrl", "application/x-troff-man", "application/x-navimap", "text/plain", "application/mbedlet", "application/mcad", "application/x-mathcad", "image/vasa", "text/mcf", "application/netmc", "application/x-troff-me", "message/rfc822", "message/rfc822", "application/x-midi", "audio/midi", "audio/x-mid", "audio/x-midi", "music/crescendo", "x-music/x-midi", "application/x-midi", "audio/midi", "audio/x-mid", "audio/x-midi", "music/crescendo", "x-music/x-midi", "application/x-frame", "application/x-mif", "message/rfc822", "www/mime", "video/x-motion-jpeg", "application/base64", "application/x-meme", "application/base64", "audio/mod", "audio/x-mod", "video/quicktime", "video/quicktime", "video/x-sgi-movie", "audio/mpeg", "audio/x-mpeg", "video/mpeg", "video/x-mpeg", "video/x-mpeq2a", "audio/mpeg3", "audio/x-mpeg-3", "video/mpeg", "video/x-mpeg", "audio/mpeg", "video/mpeg", "application/x-project", "video/mpeg", "video/mpeg", "audio/mpeg", "video/mpeg", "audio/mpeg", "application/vnd.ms-project", "application/x-project", "application/x-project", "application/x-project", "application/marc", "application/x-troff-ms", "video/x-sgi-movie", "audio/make", "application/x-vnd.audioexplosion.mzz", "image/naplps", "image/naplps", "application/x-netcdf", "application/vnd.nokia.configuration-message", "image/x-niff", "image/x-niff", "application/x-mix-transfer", "application/x-conference", "application/x-navidoc", "application/octet-stream", "application/oda", "application/x-omc", "application/x-omcdatamaker", "application/x-omcregerator", "text/x-pascal", "application/pkcs10", "application/x-pkcs10", "application/pkcs-12", "application/x-pkcs12", "application/x-pkcs7-signature", "application/pkcs7-mime", "application/x-pkcs7-mime", "application/pkcs7-mime", "application/x-pkcs7-mime", "application/x-pkcs7-certreqresp", "application/pkcs7-signature", "application/pro_eng", "text/pascal", "image/x-portable-bitmap", "application/vnd.hp-pcl", "application/x-pcl", "image/x-pict", "image/x-pcx", "chemical/x-pdb", "application/pdf", "audio/make", "audio/make.my.funk", "image/x-portable-graymap", "image/x-portable-greymap", "image/pict", "image/pict", "application/x-newton-compatible-pkg", "application/vnd.ms-pki.pko", "text/plain", "text/x-script.perl", "application/x-pixclscript", "image/x-xpixmap", "text/x-script.perl-module", "application/x-pagemaker", "application/x-pagemaker", "image/png", "application/x-portable-anymap", "image/x-portable-anymap", "application/mspowerpoint", "application/vnd.ms-powerpoint", "model/x-pov", "application/vnd.ms-powerpoint", "image/x-portable-pixmap", "application/mspowerpoint", "application/vnd.ms-powerpoint", "application/mspowerpoint", "application/powerpoint", "application/vnd.ms-powerpoint", "application/x-mspowerpoint", "application/mspowerpoint", "application/x-freelance", "application/pro_eng", "application/postscript", "application/octet-stream", "paleovu/x-pv", "application/vnd.ms-powerpoint", "text/x-script.phyton", "application/x-bytecode.python", "audio/vnd.qcelp", "x-world/x-3dmf", "x-world/x-3dmf", "image/x-quicktime", "video/quicktime", "video/x-qtc", "image/x-quicktime", "image/x-quicktime", "audio/x-pn-realaudio", "audio/x-pn-realaudio-plugin", "audio/x-realaudio", "audio/x-pn-realaudio", "application/x-cmu-raster", "image/cmu-raster", "image/x-cmu-raster", "image/cmu-raster", "text/x-script.rexx", "image/vnd.rn-realflash", "image/x-rgb", "application/vnd.rn-realmedia", "audio/x-pn-realaudio", "audio/mid", "audio/x-pn-realaudio", "audio/x-pn-realaudio", "audio/x-pn-realaudio-plugin", "application/ringing-tones", "application/vnd.nokia.ringing-tone", "application/vnd.rn-realplayer", "application/x-troff", "image/vnd.rn-realpix", "audio/x-pn-realaudio-plugin", "text/richtext", "text/vnd.rn-realtext", "application/rtf", "application/x-rtf", "text/richtext", "application/rtf", "text/richtext", "video/vnd.rn-realvideo", "text/x-asm", "audio/s3m", "application/octet-stream", "application/x-tbook", "application/x-lotusscreencam", "text/x-script.guile", "text/x-script.scheme", "video/x-scm", "text/plain", "application/sdp", "application/x-sdp", "application/sounder", "application/sea", "application/x-sea", "application/set", "text/sgml", "text/x-sgml", "text/sgml", "text/x-sgml", "application/x-bsh", "application/x-sh", "application/x-shar", "text/x-script.sh", "application/x-bsh", "application/x-shar", "text/html", "text/x-server-parsed-html", "audio/x-psid", "application/x-sit", "application/x-stuffit", "application/x-koan", "application/x-koan", "application/x-koan", "application/x-koan", "application/x-seelogo", "application/smil", "application/smil", "audio/basic", "audio/x-adpcm", "application/solids", "application/x-pkcs7-certificates", "text/x-speech", "application/futuresplash", "application/x-sprite", "application/x-sprite", "application/x-wais-source", "text/x-server-parsed-html", "application/streamingmedia", "application/vnd.ms-pki.certstore", "application/step", "application/sla", "application/vnd.ms-pki.stl", "application/x-navistyle", "application/step", "application/x-sv4cpio", "application/x-sv4crc", "image/vnd.dwg", "image/x-dwg", "application/x-world", "x-world/x-svr", "application/x-shockwave-flash", "application/x-troff", "text/x-speech", "application/x-tar", "application/toolbook", "application/x-tbook", "application/x-tcl", "text/x-script.tcl", "text/x-script.tcsh", "application/x-tex", "application/x-texinfo", "application/x-texinfo", "application/plain", "text/plain", "application/gnutar", "application/x-compressed", "image/tiff", "image/x-tiff", "image/tiff", "image/x-tiff", "application/x-troff", "audio/tsp-audio", "application/dsptype", "audio/tsplayer", "text/tab-separated-values", "image/florian", "text/plain", "text/x-uil", "text/uri-list", "text/uri-list", "application/i-deas", "text/uri-list", "text/uri-list", "application/x-ustar", "multipart/x-ustar", "application/octet-stream", "text/x-uuencode", "text/x-uuencode", "application/x-cdlink", "text/x-vcalendar", "application/vda", "video/vdo", "application/groupwise", "video/vivo", "video/vnd.vivo", "video/vivo", "video/vnd.vivo", "application/vocaltec-media-desc", "application/vocaltec-media-file", "audio/voc", "audio/x-voc", "video/vosaic", "audio/voxware", "audio/x-twinvq-plugin", "audio/x-twinvq", "audio/x-twinvq-plugin", "application/x-vrml", "model/vrml", "x-world/x-vrml", "x-world/x-vrt", "application/x-visio", "application/x-visio", "application/x-visio", "application/wordperfect6.0", "application/wordperfect6.1", "application/msword", "audio/wav", "audio/x-wav", "application/x-qpro", "image/vnd.wap.wbmp", "application/vnd.xara", "application/msword", "application/x-123", "windows/metafile", "text/vnd.wap.wml", "application/vnd.wap.wmlc", "text/vnd.wap.wmlscript", "application/vnd.wap.wmlscriptc", "application/msword", "application/wordperfect", "application/wordperfect", "application/wordperfect6.0", "application/wordperfect", "application/wordperfect", "application/x-wpwin", "application/x-lotus", "application/mswrite", "application/x-wri", "application/x-world", "model/vrml", "x-world/x-vrml", "model/vrml", "x-world/x-vrml", "text/scriplet", "application/x-wais-source", "application/x-wintalk", "image/x-xbitmap", "image/x-xbm", "image/xbm", "video/x-amt-demorun", "xgl/drawing", "image/vnd.xiff", "application/excel", "application/excel", "application/x-excel", "application/x-msexcel", "application/excel", "application/vnd.ms-excel", "application/x-excel", "application/excel", "application/vnd.ms-excel", "application/x-excel", "application/excel", "application/x-excel", "application/excel", "application/x-excel", "application/excel", "application/vnd.ms-excel", "application/x-excel", "application/excel", "application/vnd.ms-excel", "application/x-excel", "application/excel", "application/vnd.ms-excel", "application/x-excel", "application/x-msexcel", "application/excel", "application/x-excel", "application/excel", "application/x-excel", "application/excel", "application/vnd.ms-excel", "application/x-excel", "application/x-msexcel", "audio/xm", "application/xml", "text/xml", "xgl/movie", "application/x-vnd.ls-xpix", "image/x-xpixmap", "image/xpm", "image/png", "video/x-amt-showrun", "image/x-xwd", "image/x-xwindowdump", "chemical/x-pdb", "application/x-compress", "application/x-compressed", "application/x-compressed", "application/x-zip-compressed", "application/zip", "multipart/x-zip", "application/octet-stream", "text/x-script.zsh"}, + "extension": {"doc", "docx", "log", "msg", "odt", "pages", "rtf", "tex", "txt", "wpd", "wps", "csv", "dat", "gbr", "ged", "key", "keychain", "pps", "ppt", "pptx", "sdf", "tar", "vcf", "xml", "aif", "iff", "mid", "mpa", "ra", "wav", "wma", "asf", "asx", "avi", "flv", "mov", "mpg", "rm", "srt", "swf", "vob", "wmv", "max", "obj", "bmp", "dds", "gif", "jpg", "png", "psd", "pspimage", "tga", "thm", "tif", "tiff", "yuv", "ai", "eps", "ps", "svg", "indd", "pct", "pdf", "xlr", "xls", "xlsx", "accdb", "db", "dbf", "mdb", "pdb", "sql", "apk", "app", "bat", "cgi", "com", "exe", "gadget", "jar", "pif", "vb", "wsf", "dem", "gam", "nes", "rom", "sav", "dwg", "dxf", "gpx", "kml", "kmz", "asp", "aspx", "cer", "cfm", "csr", "css", "htm", "html", "js", "jsp", "php", "rss", "xhtml", "crx", "plugin", "fnt", "fon", "otf", "ttf", "cab", "cpl", "cur", "deskthemepack", "dll", "dmp", "drv", "icns", "ico", "lnk", "sys", "cfg", "ini", "prf", "hqx", "mim", "uue", "cbr", "deb", "gz", "pkg", "rar", "rpm", "sitx", "gz", "zip", "zipx", "bin", "cue", "dmg", "iso", "mdf", "toast", "vcd", "class", "cpp", "cs", "dtd", "fla", "java", "lua", "pl", "py", "sh", "sln", "swift", "vcxproj", "xcodeproj", "bak", "tmp", "crdownload", "ics", "msi", "part", "torrent"}, +} diff --git a/vendor/github.com/brianvoe/gofakeit/data/hacker.go b/vendor/github.com/brianvoe/gofakeit/data/hacker.go new file mode 100644 index 00000000000..4735f7d560a --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/data/hacker.go @@ -0,0 +1,20 @@ +package data + +// Hacker consists of random hacker phrases +var Hacker = map[string][]string{ + "abbreviation": {"TCP", "HTTP", "SDD", "RAM", "GB", "CSS", "SSL", "AGP", "SQL", "FTP", "PCI", "AI", "ADP", "RSS", "XML", "EXE", "COM", "HDD", "THX", "SMTP", "SMS", "USB", "PNG", "SAS", "IB", "SCSI", "JSON", "XSS", "JBOD"}, + "adjective": {"auxiliary", "primary", "back-end", "digital", "open-source", "virtual", "cross-platform", "redundant", "online", "haptic", "multi-byte", "bluetooth", "wireless", "1080p", "neural", "optical", "solid state", "mobile"}, + "noun": {"driver", "protocol", "bandwidth", "panel", "microchip", "program", "port", "card", "array", "interface", "system", "sensor", "firewall", "hard drive", "pixel", "alarm", "feed", "monitor", "application", "transmitter", "bus", "circuit", "capacitor", "matrix"}, + "verb": {"back up", "bypass", "hack", "override", "compress", "copy", "navigate", "index", "connect", "generate", "quantify", "calculate", "synthesize", "input", "transmit", "program", "reboot", "parse"}, + "ingverb": {"backing up", "bypassing", "hacking", "overriding", "compressing", "copying", "navigating", "indexing", "connecting", "generating", "quantifying", "calculating", "synthesizing", "transmitting", "programming", "parsing"}, + "phrase": { + "If we {hacker.verb} the {hacker.noun}, we can get to the {hacker.abbreviation} {hacker.noun} through the {hacker.adjective} {hacker.abbreviation} {hacker.noun}!", + "We need to {hacker.verb} the {hacker.adjective} {hacker.abbreviation} {hacker.noun}!", + "Try to {hacker.verb} the {hacker.abbreviation} {hacker.noun}, maybe it will {hacker.verb} the {hacker.adjective} {hacker.noun}!", + "You can't {hacker.verb} the {hacker.noun} without {hacker.ingverb} the {hacker.adjective} {hacker.abbreviation} {hacker.noun}!", + "Use the {hacker.adjective} {hacker.abbreviation} {hacker.noun}, then you can {hacker.verb} the {hacker.adjective} {hacker.noun}!", + "The {hacker.abbreviation} {hacker.noun} is down, {hacker.verb} the {hacker.adjective} {hacker.noun} so we can {hacker.verb} the {hacker.abbreviation} {hacker.noun}!", + "{hacker.ingverb} the {hacker.noun} won't do anything, we need to {hacker.verb} the {hacker.adjective} {hacker.abbreviation} {hacker.noun}!", + "I'll {hacker.verb} the {hacker.adjective} {hacker.abbreviation} {hacker.noun}, that should {hacker.verb} the {hacker.abbreviation} {hacker.noun}!", + }, +} diff --git a/vendor/github.com/brianvoe/gofakeit/data/hipster.go b/vendor/github.com/brianvoe/gofakeit/data/hipster.go new file mode 100644 index 00000000000..f036f4639bc --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/data/hipster.go @@ -0,0 +1,6 @@ +package data + +// Hipster consists of random hipster words +var Hipster = map[string][]string{ + "word": {"Wes Anderson", "chicharrones", "narwhal", "food truck", "marfa", "aesthetic", "keytar", "art party", "sustainable", "forage", "mlkshk", "gentrify", "locavore", "swag", "hoodie", "microdosing", "VHS", "before they sold out", "pabst", "plaid", "Thundercats", "freegan", "scenester", "hella", "occupy", "truffaut", "raw denim", "beard", "post-ironic", "photo booth", "twee", "90's", "pitchfork", "cray", "cornhole", "kale chips", "pour-over", "yr", "five dollar toast", "kombucha", "you probably haven't heard of them", "mustache", "fixie", "try-hard", "franzen", "kitsch", "austin", "stumptown", "keffiyeh", "whatever", "tumblr", "DIY", "shoreditch", "biodiesel", "vegan", "pop-up", "banjo", "kogi", "cold-pressed", "letterpress", "chambray", "butcher", "synth", "trust fund", "hammock", "farm-to-table", "intelligentsia", "loko", "ugh", "offal", "poutine", "gastropub", "Godard", "jean shorts", "sriracha", "dreamcatcher", "leggings", "fashion axe", "church-key", "meggings", "tote bag", "disrupt", "readymade", "helvetica", "flannel", "meh", "roof", "hashtag", "knausgaard", "cronut", "schlitz", "green juice", "waistcoat", "normcore", "viral", "ethical", "actually", "fingerstache", "humblebrag", "deep v", "wayfarers", "tacos", "taxidermy", "selvage", "put a bird on it", "ramps", "portland", "retro", "kickstarter", "bushwick", "brunch", "distillery", "migas", "flexitarian", "XOXO", "small batch", "messenger bag", "heirloom", "tofu", "bicycle rights", "bespoke", "salvia", "wolf", "selfies", "echo", "park", "listicle", "craft beer", "chartreuse", "sartorial", "pinterest", "mumblecore", "kinfolk", "vinyl", "etsy", "umami", "8-bit", "polaroid", "banh mi", "crucifix", "bitters", "brooklyn", "PBR&B", "drinking", "vinegar", "squid", "tattooed", "skateboard", "vice", "authentic", "literally", "lomo", "celiac", "health", "goth", "artisan", "chillwave", "blue bottle", "pickled", "next level", "neutra", "organic", "Yuccie", "paleo", "blog", "single-origin coffee", "seitan", "street", "gluten-free", "mixtape", "venmo", "irony", "everyday", "carry", "slow-carb", "3 wolf moon", "direct trade", "lo-fi", "tousled", "tilde", "semiotics", "cred", "chia", "master", "cleanse", "ennui", "quinoa", "pug", "iPhone", "fanny pack", "cliche", "cardigan", "asymmetrical", "meditation", "YOLO", "typewriter", "pork belly", "shabby chic", "+1", "lumbersexual", "williamsburg"}, +} diff --git a/vendor/github.com/brianvoe/gofakeit/data/internet.go b/vendor/github.com/brianvoe/gofakeit/data/internet.go new file mode 100644 index 00000000000..1f16db95c76 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/data/internet.go @@ -0,0 +1,8 @@ +package data + +// Internet consists of various internet information +var Internet = map[string][]string{ + "browser": {"firefox", "chrome", "internetExplorer", "opera", "safari"}, + "domain_suffix": {"com", "biz", "info", "name", "net", "org", "io"}, + "http_method": {"HEAD", "GET", "POST", "PUT", "PATCH", "DELETE"}, +} diff --git a/vendor/github.com/brianvoe/gofakeit/data/job.go b/vendor/github.com/brianvoe/gofakeit/data/job.go new file mode 100644 index 00000000000..905dd74ee02 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/data/job.go @@ -0,0 +1,8 @@ +package data + +// Job consists of job data +var Job = map[string][]string{ + "title": {"Administrator", "Agent", "Analyst", "Architect", "Assistant", "Associate", "Consultant", "Coordinator", "Designer", "Developer", "Director", "Engineer", "Executive", "Facilitator", "Liaison", "Manager", "Officer", "Orchestrator", "Planner", "Producer", "Representative", "Specialist", "Strategist", "Supervisor", "Technician"}, + "descriptor": {"Central", "Chief", "Corporate", "Customer", "Direct", "District", "Dynamic", "Dynamic", "Forward", "Future", "Global", "Human", "Internal", "International", "Investor", "Lead", "Legacy", "National", "Principal", "Product", "Regional", "Senior"}, + "level": {"Accountability", "Accounts", "Applications", "Assurance", "Brand", "Branding", "Communications", "Configuration", "Creative", "Data", "Directives", "Division", "Factors", "Functionality", "Group", "Identity", "Implementation", "Infrastructure", "Integration", "Interactions", "Intranet", "Marketing", "Markets", "Metrics", "Mobility", "Operations", "Optimization", "Paradigm", "Program", "Quality", "Research", "Response", "Security", "Solutions", "Tactics", "Usability", "Web"}, +} diff --git a/vendor/github.com/brianvoe/gofakeit/data/log_level.go b/vendor/github.com/brianvoe/gofakeit/data/log_level.go new file mode 100644 index 00000000000..01d98b63c6b --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/data/log_level.go @@ -0,0 +1,8 @@ +package data + +// LogLevels consists of log levels for several types +var LogLevels = map[string][]string{ + "general": {"error", "warning", "info", "fatal", "trace", "debug"}, + "syslog": {"emerg", "alert", "crit", "err", "warning", "notice", "info", "debug"}, + "apache": {"emerg", "alert", "crit", "error", "warn", "notice", "info", "debug", "trace1-8"}, +} diff --git a/vendor/github.com/brianvoe/gofakeit/data/lorem.go b/vendor/github.com/brianvoe/gofakeit/data/lorem.go new file mode 100644 index 00000000000..b0a8f8a1378 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/data/lorem.go @@ -0,0 +1,6 @@ +package data + +// Lorem consists of lorem ipsum information +var Lorem = map[string][]string{ + "word": {"alias", "consequatur", "aut", "perferendis", "sit", "voluptatem", "accusantium", "doloremque", "aperiam", "eaque", "ipsa", "quae", "ab", "illo", "inventore", "veritatis", "et", "quasi", "architecto", "beatae", "vitae", "dicta", "sunt", "explicabo", "aspernatur", "aut", "odit", "aut", "fugit", "sed", "quia", "consequuntur", "magni", "dolores", "eos", "qui", "ratione", "voluptatem", "sequi", "nesciunt", "neque", "dolorem", "ipsum", "quia", "dolor", "sit", "amet", "consectetur", "adipisci", "velit", "sed", "quia", "non", "numquam", "eius", "modi", "tempora", "incidunt", "ut", "labore", "et", "dolore", "magnam", "aliquam", "quaerat", "voluptatem", "ut", "enim", "ad", "minima", "veniam", "quis", "nostrum", "exercitationem", "ullam", "corporis", "nemo", "enim", "ipsam", "voluptatem", "quia", "voluptas", "sit", "suscipit", "laboriosam", "nisi", "ut", "aliquid", "ex", "ea", "commodi", "consequatur", "quis", "autem", "vel", "eum", "iure", "reprehenderit", "qui", "in", "ea", "voluptate", "velit", "esse", "quam", "nihil", "molestiae", "et", "iusto", "odio", "dignissimos", "ducimus", "qui", "blanditiis", "praesentium", "laudantium", "totam", "rem", "voluptatum", "deleniti", "atque", "corrupti", "quos", "dolores", "et", "quas", "molestias", "excepturi", "sint", "occaecati", "cupiditate", "non", "provident", "sed", "ut", "perspiciatis", "unde", "omnis", "iste", "natus", "error", "similique", "sunt", "in", "culpa", "qui", "officia", "deserunt", "mollitia", "animi", "id", "est", "laborum", "et", "dolorum", "fuga", "et", "harum", "quidem", "rerum", "facilis", "est", "et", "expedita", "distinctio", "nam", "libero", "tempore", "cum", "soluta", "nobis", "est", "eligendi", "optio", "cumque", "nihil", "impedit", "quo", "porro", "quisquam", "est", "qui", "minus", "id", "quod", "maxime", "placeat", "facere", "possimus", "omnis", "voluptas", "assumenda", "est", "omnis", "dolor", "repellendus", "temporibus", "autem", "quibusdam", "et", "aut", "consequatur", "vel", "illum", "qui", "dolorem", "eum", "fugiat", "quo", "voluptas", "nulla", "pariatur", "at", "vero", "eos", "et", "accusamus", "officiis", "debitis", "aut", "rerum", "necessitatibus", "saepe", "eveniet", "ut", "et", "voluptates", "repudiandae", "sint", "et", "molestiae", "non", "recusandae", "itaque", "earum", "rerum", "hic", "tenetur", "a", "sapiente", "delectus", "ut", "aut", "reiciendis", "voluptatibus", "maiores", "doloribus", "asperiores", "repellat"}, +} diff --git a/vendor/github.com/brianvoe/gofakeit/data/payment.go b/vendor/github.com/brianvoe/gofakeit/data/payment.go new file mode 100644 index 00000000000..e50903a72af --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/data/payment.go @@ -0,0 +1,20 @@ +package data + +// Payment contains payment information +var Payment = map[string][]string{ + "card_type": {"Visa", "MasterCard", "American Express", "Discover"}, + "number": { + // Visa + "4###############", + "4###############", + // Mastercard + "222100##########", + "272099##########", + // American Express + "34#############", + "37#############", + // Discover + "65##############", + "65##############", + }, +} diff --git a/vendor/github.com/brianvoe/gofakeit/data/person.go b/vendor/github.com/brianvoe/gofakeit/data/person.go new file mode 100644 index 00000000000..129b59ba6e3 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/data/person.go @@ -0,0 +1,9 @@ +package data + +// Person consists of a slice of people information +var Person = map[string][]string{ + "prefix": {"Mr.", "Mrs.", "Ms.", "Miss", "Dr."}, + "suffix": {"Jr.", "Sr.", "I", "II", "III", "IV", "V", "MD", "DDS", "PhD", "DVM"}, + "first": {"Aaliyah", "Aaron", "Abagail", "Abbey", "Abbie", "Abbigail", "Abby", "Abdiel", "Abdul", "Abdullah", "Abe", "Abel", "Abelardo", "Abigail", "Abigale", "Abigayle", "Abner", "Abraham", "Ada", "Adah", "Adalberto", "Adaline", "Adam", "Adan", "Addie", "Addison", "Adela", "Adelbert", "Adele", "Adelia", "Adeline", "Adell", "Adella", "Adelle", "Aditya", "Adolf", "Adolfo", "Adolph", "Adolphus", "Adonis", "Adrain", "Adrian", "Adriana", "Adrianna", "Adriel", "Adrien", "Adrienne", "Afton", "Aglae", "Agnes", "Agustin", "Agustina", "Ahmad", "Ahmed", "Aida", "Aidan", "Aiden", "Aileen", "Aimee", "Aisha", "Aiyana", "Akeem", "Al", "Alaina", "Alan", "Alana", "Alanis", "Alanna", "Alayna", "Alba", "Albert", "Alberta", "Albertha", "Alberto", "Albin", "Albina", "Alda", "Alden", "Alec", "Aleen", "Alejandra", "Alejandrin", "Alek", "Alena", "Alene", "Alessandra", "Alessandro", "Alessia", "Aletha", "Alex", "Alexa", "Alexander", "Alexandra", "Alexandre", "Alexandrea", "Alexandria", "Alexandrine", "Alexandro", "Alexane", "Alexanne", "Alexie", "Alexis", "Alexys", "Alexzander", "Alf", "Alfonso", "Alfonzo", "Alford", "Alfred", "Alfreda", "Alfredo", "Ali", "Alia", "Alice", "Alicia", "Alisa", "Alisha", "Alison", "Alivia", "Aliya", "Aliyah", "Aliza", "Alize", "Allan", "Allen", "Allene", "Allie", "Allison", "Ally", "Alphonso", "Alta", "Althea", "Alva", "Alvah", "Alvena", "Alvera", "Alverta", "Alvina", "Alvis", "Alyce", "Alycia", "Alysa", "Alysha", "Alyson", "Alysson", "Amalia", "Amanda", "Amani", "Amara", "Amari", "Amaya", "Amber", "Ambrose", "Amelia", "Amelie", "Amely", "America", "Americo", "Amie", "Amina", "Amir", "Amira", "Amiya", "Amos", "Amparo", "Amy", "Amya", "Ana", "Anabel", "Anabelle", "Anahi", "Anais", "Anastacio", "Anastasia", "Anderson", "Andre", "Andreane", "Andreanne", "Andres", "Andrew", "Andy", "Angel", "Angela", "Angelica", "Angelina", "Angeline", "Angelita", "Angelo", "Angie", "Angus", "Anibal", "Anika", "Anissa", "Anita", "Aniya", "Aniyah", "Anjali", "Anna", "Annabel", "Annabell", "Annabelle", "Annalise", "Annamae", "Annamarie", "Anne", "Annetta", "Annette", "Annie", "Ansel", "Ansley", "Anthony", "Antoinette", "Antone", "Antonetta", "Antonette", "Antonia", "Antonietta", "Antonina", "Antonio", "Antwan", "Antwon", "Anya", "April", "Ara", "Araceli", "Aracely", "Arch", "Archibald", "Ardella", "Arden", "Ardith", "Arely", "Ari", "Ariane", "Arianna", "Aric", "Ariel", "Arielle", "Arjun", "Arlene", "Arlie", "Arlo", "Armand", "Armando", "Armani", "Arnaldo", "Arne", "Arno", "Arnold", "Arnoldo", "Arnulfo", "Aron", "Art", "Arthur", "Arturo", "Arvel", "Arvid", "Arvilla", "Aryanna", "Asa", "Asha", "Ashlee", "Ashleigh", "Ashley", "Ashly", "Ashlynn", "Ashton", "Ashtyn", "Asia", "Assunta", "Astrid", "Athena", "Aubree", "Aubrey", "Audie", "Audra", "Audreanne", "Audrey", "August", "Augusta", "Augustine", "Augustus", "Aurelia", "Aurelie", "Aurelio", "Aurore", "Austen", "Austin", "Austyn", "Autumn", "Ava", "Avery", "Avis", "Axel", "Ayana", "Ayden", "Ayla", "Aylin", "Baby", "Bailee", "Bailey", "Barbara", "Barney", "Baron", "Barrett", "Barry", "Bart", "Bartholome", "Barton", "Baylee", "Beatrice", "Beau", "Beaulah", "Bell", "Bella", "Belle", "Ben", "Benedict", "Benjamin", "Bennett", "Bennie", "Benny", "Benton", "Berenice", "Bernadette", "Bernadine", "Bernard", "Bernardo", "Berneice", "Bernhard", "Bernice", "Bernie", "Berniece", "Bernita", "Berry", "Bert", "Berta", "Bertha", "Bertram", "Bertrand", "Beryl", "Bessie", "Beth", "Bethany", "Bethel", "Betsy", "Bette", "Bettie", "Betty", "Bettye", "Beulah", "Beverly", "Bianka", "Bill", "Billie", "Billy", "Birdie", "Blair", "Blaise", "Blake", "Blanca", "Blanche", "Blaze", "Bo", "Bobbie", "Bobby", "Bonita", "Bonnie", "Boris", "Boyd", "Brad", "Braden", "Bradford", "Bradley", "Bradly", "Brady", "Braeden", "Brain", "Brandi", "Brando", "Brandon", "Brandt", "Brandy", "Brandyn", "Brannon", "Branson", "Brant", "Braulio", "Braxton", "Brayan", "Breana", "Breanna", "Breanne", "Brenda", "Brendan", "Brenden", "Brendon", "Brenna", "Brennan", "Brennon", "Brent", "Bret", "Brett", "Bria", "Brian", "Briana", "Brianne", "Brice", "Bridget", "Bridgette", "Bridie", "Brielle", "Brigitte", "Brionna", "Brisa", "Britney", "Brittany", "Brock", "Broderick", "Brody", "Brook", "Brooke", "Brooklyn", "Brooks", "Brown", "Bruce", "Bryana", "Bryce", "Brycen", "Bryon", "Buck", "Bud", "Buddy", "Buford", "Bulah", "Burdette", "Burley", "Burnice", "Buster", "Cade", "Caden", "Caesar", "Caitlyn", "Cale", "Caleb", "Caleigh", "Cali", "Calista", "Callie", "Camden", "Cameron", "Camila", "Camilla", "Camille", "Camren", "Camron", "Camryn", "Camylle", "Candace", "Candelario", "Candice", "Candida", "Candido", "Cara", "Carey", "Carissa", "Carlee", "Carleton", "Carley", "Carli", "Carlie", "Carlo", "Carlos", "Carlotta", "Carmel", "Carmela", "Carmella", "Carmelo", "Carmen", "Carmine", "Carol", "Carolanne", "Carole", "Carolina", "Caroline", "Carolyn", "Carolyne", "Carrie", "Carroll", "Carson", "Carter", "Cary", "Casandra", "Casey", "Casimer", "Casimir", "Casper", "Cassandra", "Cassandre", "Cassidy", "Cassie", "Catalina", "Caterina", "Catharine", "Catherine", "Cathrine", "Cathryn", "Cathy", "Cayla", "Ceasar", "Cecelia", "Cecil", "Cecile", "Cecilia", "Cedrick", "Celestine", "Celestino", "Celia", "Celine", "Cesar", "Chad", "Chadd", "Chadrick", "Chaim", "Chance", "Chandler", "Chanel", "Chanelle", "Charity", "Charlene", "Charles", "Charley", "Charlie", "Charlotte", "Chase", "Chasity", "Chauncey", "Chaya", "Chaz", "Chelsea", "Chelsey", "Chelsie", "Chesley", "Chester", "Chet", "Cheyanne", "Cheyenne", "Chloe", "Chris", "Christ", "Christa", "Christelle", "Christian", "Christiana", "Christina", "Christine", "Christop", "Christophe", "Christopher", "Christy", "Chyna", "Ciara", "Cicero", "Cielo", "Cierra", "Cindy", "Citlalli", "Clair", "Claire", "Clara", "Clarabelle", "Clare", "Clarissa", "Clark", "Claud", "Claude", "Claudia", "Claudie", "Claudine", "Clay", "Clemens", "Clement", "Clementina", "Clementine", "Clemmie", "Cleo", "Cleora", "Cleta", "Cletus", "Cleve", "Cleveland", "Clifford", "Clifton", "Clint", "Clinton", "Clotilde", "Clovis", "Cloyd", "Clyde", "Coby", "Cody", "Colby", "Cole", "Coleman", "Colin", "Colleen", "Collin", "Colt", "Colten", "Colton", "Columbus", "Concepcion", "Conner", "Connie", "Connor", "Conor", "Conrad", "Constance", "Constantin", "Consuelo", "Cooper", "Cora", "Coralie", "Corbin", "Cordelia", "Cordell", "Cordia", "Cordie", "Corene", "Corine", "Cornelius", "Cornell", "Corrine", "Cortez", "Cortney", "Cory", "Coty", "Courtney", "Coy", "Craig", "Crawford", "Creola", "Cristal", "Cristian", "Cristina", "Cristobal", "Cristopher", "Cruz", "Crystal", "Crystel", "Cullen", "Curt", "Curtis", "Cydney", "Cynthia", "Cyril", "Cyrus", "Dagmar", "Dahlia", "Daija", "Daisha", "Daisy", "Dakota", "Dale", "Dallas", "Dallin", "Dalton", "Damaris", "Dameon", "Damian", "Damien", "Damion", "Damon", "Dan", "Dana", "Dandre", "Dane", "Dangelo", "Dangelo", "Danial", "Daniela", "Daniella", "Danielle", "Danika", "Dannie", "Danny", "Dante", "Danyka", "Daphne", "Daphnee", "Daphney", "Darby", "Daren", "Darian", "Dariana", "Darien", "Dario", "Darion", "Darius", "Darlene", "Daron", "Darrel", "Darrell", "Darren", "Darrick", "Darrin", "Darrion", "Darron", "Darryl", "Darwin", "Daryl", "Dashawn", "Dasia", "Dave", "David", "Davin", "Davion", "Davon", "Davonte", "Dawn", "Dawson", "Dax", "Dayana", "Dayna", "Dayne", "Dayton", "Dean", "Deangelo", "Deanna", "Deborah", "Declan", "Dedric", "Dedrick", "Dee", "Deion", "Deja", "Dejah", "Dejon", "Dejuan", "Delaney", "Delbert", "Delfina", "Delia", "Delilah", "Dell", "Della", "Delmer", "Delores", "Delpha", "Delphia", "Delphine", "Delta", "Demarco", "Demarcus", "Demario", "Demetris", "Demetrius", "Demond", "Dena", "Denis", "Dennis", "Deon", "Deondre", "Deontae", "Deonte", "Dereck", "Derek", "Derick", "Deron", "Derrick", "Deshaun", "Deshawn", "Desiree", "Desmond", "Dessie", "Destany", "Destin", "Destinee", "Destiney", "Destini", "Destiny", "Devan", "Devante", "Deven", "Devin", "Devon", "Devonte", "Devyn", "Dewayne", "Dewitt", "Dexter", "Diamond", "Diana", "Dianna", "Diego", "Dillan", "Dillon", "Dimitri", "Dina", "Dino", "Dion", "Dixie", "Dock", "Dolly", "Dolores", "Domenic", "Domenica", "Domenick", "Domenico", "Domingo", "Dominic", "Dominique", "Don", "Donald", "Donato", "Donavon", "Donna", "Donnell", "Donnie", "Donny", "Dora", "Dorcas", "Dorian", "Doris", "Dorothea", "Dorothy", "Dorris", "Dortha", "Dorthy", "Doug", "Douglas", "Dovie", "Doyle", "Drake", "Drew", "Duane", "Dudley", "Dulce", "Duncan", "Durward", "Dustin", "Dusty", "Dwight", "Dylan", "Earl", "Earlene", "Earline", "Earnest", "Earnestine", "Easter", "Easton", "Ebba", "Ebony", "Ed", "Eda", "Edd", "Eddie", "Eden", "Edgar", "Edgardo", "Edison", "Edmond", "Edmund", "Edna", "Eduardo", "Edward", "Edwardo", "Edwin", "Edwina", "Edyth", "Edythe", "Effie", "Efrain", "Efren", "Eileen", "Einar", "Eino", "Eladio", "Elaina", "Elbert", "Elda", "Eldon", "Eldora", "Eldred", "Eldridge", "Eleanora", "Eleanore", "Eleazar", "Electa", "Elena", "Elenor", "Elenora", "Eleonore", "Elfrieda", "Eli", "Elian", "Eliane", "Elias", "Eliezer", "Elijah", "Elinor", "Elinore", "Elisa", "Elisabeth", "Elise", "Eliseo", "Elisha", "Elissa", "Eliza", "Elizabeth", "Ella", "Ellen", "Ellie", "Elliot", "Elliott", "Ellis", "Ellsworth", "Elmer", "Elmira", "Elmo", "Elmore", "Elna", "Elnora", "Elody", "Eloisa", "Eloise", "Elouise", "Eloy", "Elroy", "Elsa", "Else", "Elsie", "Elta", "Elton", "Elva", "Elvera", "Elvie", "Elvis", "Elwin", "Elwyn", "Elyse", "Elyssa", "Elza", "Emanuel", "Emelia", "Emelie", "Emely", "Emerald", "Emerson", "Emery", "Emie", "Emil", "Emile", "Emilia", "Emiliano", "Emilie", "Emilio", "Emily", "Emma", "Emmalee", "Emmanuel", "Emmanuelle", "Emmet", "Emmett", "Emmie", "Emmitt", "Emmy", "Emory", "Ena", "Enid", "Enoch", "Enola", "Enos", "Enrico", "Enrique", "Ephraim", "Era", "Eriberto", "Eric", "Erica", "Erich", "Erick", "Ericka", "Erik", "Erika", "Erin", "Erling", "Erna", "Ernest", "Ernestina", "Ernestine", "Ernesto", "Ernie", "Ervin", "Erwin", "Eryn", "Esmeralda", "Esperanza", "Esta", "Esteban", "Estefania", "Estel", "Estell", "Estella", "Estelle", "Estevan", "Esther", "Estrella", "Etha", "Ethan", "Ethel", "Ethelyn", "Ethyl", "Ettie", "Eudora", "Eugene", "Eugenia", "Eula", "Eulah", "Eulalia", "Euna", "Eunice", "Eusebio", "Eva", "Evalyn", "Evan", "Evangeline", "Evans", "Eve", "Eveline", "Evelyn", "Everardo", "Everett", "Everette", "Evert", "Evie", "Ewald", "Ewell", "Ezekiel", "Ezequiel", "Ezra", "Fabian", "Fabiola", "Fae", "Fannie", "Fanny", "Fatima", "Faustino", "Fausto", "Favian", "Fay", "Faye", "Federico", "Felicia", "Felicita", "Felicity", "Felipa", "Felipe", "Felix", "Felton", "Fermin", "Fern", "Fernando", "Ferne", "Fidel", "Filiberto", "Filomena", "Finn", "Fiona", "Flavie", "Flavio", "Fleta", "Fletcher", "Flo", "Florence", "Florencio", "Florian", "Florida", "Florine", "Flossie", "Floy", "Floyd", "Ford", "Forest", "Forrest", "Foster", "Frances", "Francesca", "Francesco", "Francis", "Francisca", "Francisco", "Franco", "Frank", "Frankie", "Franz", "Fred", "Freda", "Freddie", "Freddy", "Frederic", "Frederick", "Frederik", "Frederique", "Fredrick", "Fredy", "Freeda", "Freeman", "Freida", "Frida", "Frieda", "Friedrich", "Fritz", "Furman", "Gabe", "Gabriel", "Gabriella", "Gabrielle", "Gaetano", "Gage", "Gail", "Gardner", "Garett", "Garfield", "Garland", "Garnet", "Garnett", "Garret", "Garrett", "Garrick", "Garrison", "Garry", "Garth", "Gaston", "Gavin", "Gay", "Gayle", "Gaylord", "Gene", "General", "Genesis", "Genevieve", "Gennaro", "Genoveva", "Geo", "Geoffrey", "George", "Georgette", "Georgiana", "Georgianna", "Geovanni", "Geovanny", "Geovany", "Gerald", "Geraldine", "Gerard", "Gerardo", "Gerda", "Gerhard", "Germaine", "German", "Gerry", "Gerson", "Gertrude", "Gia", "Gianni", "Gideon", "Gilbert", "Gilberto", "Gilda", "Giles", "Gillian", "Gina", "Gino", "Giovani", "Giovanna", "Giovanni", "Giovanny", "Gisselle", "Giuseppe", "Gladyce", "Gladys", "Glen", "Glenda", "Glenna", "Glennie", "Gloria", "Godfrey", "Golda", "Golden", "Gonzalo", "Gordon", "Grace", "Gracie", "Graciela", "Grady", "Graham", "Grant", "Granville", "Grayce", "Grayson", "Green", "Greg", "Gregg", "Gregoria", "Gregorio", "Gregory", "Greta", "Gretchen", "Greyson", "Griffin", "Grover", "Guadalupe", "Gudrun", "Guido", "Guillermo", "Guiseppe", "Gunnar", "Gunner", "Gus", "Gussie", "Gust", "Gustave", "Guy", "Gwen", "Gwendolyn", "Hadley", "Hailee", "Hailey", "Hailie", "Hal", "Haleigh", "Haley", "Halie", "Halle", "Hallie", "Hank", "Hanna", "Hannah", "Hans", "Hardy", "Harley", "Harmon", "Harmony", "Harold", "Harrison", "Harry", "Harvey", "Haskell", "Hassan", "Hassie", "Hattie", "Haven", "Hayden", "Haylee", "Hayley", "Haylie", "Hazel", "Hazle", "Heath", "Heather", "Heaven", "Heber", "Hector", "Heidi", "Helen", "Helena", "Helene", "Helga", "Hellen", "Helmer", "Heloise", "Henderson", "Henri", "Henriette", "Henry", "Herbert", "Herman", "Hermann", "Hermina", "Herminia", "Herminio", "Hershel", "Herta", "Hertha", "Hester", "Hettie", "Hilario", "Hilbert", "Hilda", "Hildegard", "Hillard", "Hillary", "Hilma", "Hilton", "Hipolito", "Hiram", "Hobart", "Holden", "Hollie", "Hollis", "Holly", "Hope", "Horace", "Horacio", "Hortense", "Hosea", "Houston", "Howard", "Howell", "Hoyt", "Hubert", "Hudson", "Hugh", "Hulda", "Humberto", "Hunter", "Hyman", "Ian", "Ibrahim", "Icie", "Ida", "Idell", "Idella", "Ignacio", "Ignatius", "Ike", "Ila", "Ilene", "Iliana", "Ima", "Imani", "Imelda", "Immanuel", "Imogene", "Ines", "Irma", "Irving", "Irwin", "Isaac", "Isabel", "Isabell", "Isabella", "Isabelle", "Isac", "Isadore", "Isai", "Isaiah", "Isaias", "Isidro", "Ismael", "Isobel", "Isom", "Israel", "Issac", "Itzel", "Iva", "Ivah", "Ivory", "Ivy", "Izabella", "Izaiah", "Jabari", "Jace", "Jacey", "Jacinthe", "Jacinto", "Jack", "Jackeline", "Jackie", "Jacklyn", "Jackson", "Jacky", "Jaclyn", "Jacquelyn", "Jacques", "Jacynthe", "Jada", "Jade", "Jaden", "Jadon", "Jadyn", "Jaeden", "Jaida", "Jaiden", "Jailyn", "Jaime", "Jairo", "Jakayla", "Jake", "Jakob", "Jaleel", "Jalen", "Jalon", "Jalyn", "Jamaal", "Jamal", "Jamar", "Jamarcus", "Jamel", "Jameson", "Jamey", "Jamie", "Jamil", "Jamir", "Jamison", "Jammie", "Jan", "Jana", "Janae", "Jane", "Janelle", "Janessa", "Janet", "Janice", "Janick", "Janie", "Janis", "Janiya", "Jannie", "Jany", "Jaquan", "Jaquelin", "Jaqueline", "Jared", "Jaren", "Jarod", "Jaron", "Jarred", "Jarrell", "Jarret", "Jarrett", "Jarrod", "Jarvis", "Jasen", "Jasmin", "Jason", "Jasper", "Jaunita", "Javier", "Javon", "Javonte", "Jay", "Jayce", "Jaycee", "Jayda", "Jayde", "Jayden", "Jaydon", "Jaylan", "Jaylen", "Jaylin", "Jaylon", "Jayme", "Jayne", "Jayson", "Jazlyn", "Jazmin", "Jazmyn", "Jazmyne", "Jean", "Jeanette", "Jeanie", "Jeanne", "Jed", "Jedediah", "Jedidiah", "Jeff", "Jefferey", "Jeffery", "Jeffrey", "Jeffry", "Jena", "Jenifer", "Jennie", "Jennifer", "Jennings", "Jennyfer", "Jensen", "Jerad", "Jerald", "Jeramie", "Jeramy", "Jerel", "Jeremie", "Jeremy", "Jermain", "Jermaine", "Jermey", "Jerod", "Jerome", "Jeromy", "Jerrell", "Jerrod", "Jerrold", "Jerry", "Jess", "Jesse", "Jessica", "Jessie", "Jessika", "Jessy", "Jessyca", "Jesus", "Jett", "Jettie", "Jevon", "Jewel", "Jewell", "Jillian", "Jimmie", "Jimmy", "Jo", "Joan", "Joana", "Joanie", "Joanne", "Joannie", "Joanny", "Joany", "Joaquin", "Jocelyn", "Jodie", "Jody", "Joe", "Joel", "Joelle", "Joesph", "Joey", "Johan", "Johann", "Johanna", "Johathan", "John", "Johnathan", "Johnathon", "Johnnie", "Johnny", "Johnpaul", "Johnson", "Jolie", "Jon", "Jonas", "Jonatan", "Jonathan", "Jonathon", "Jordan", "Jordane", "Jordi", "Jordon", "Jordy", "Jordyn", "Jorge", "Jose", "Josefa", "Josefina", "Joseph", "Josephine", "Josh", "Joshua", "Joshuah", "Josiah", "Josiane", "Josianne", "Josie", "Josue", "Jovan", "Jovani", "Jovanny", "Jovany", "Joy", "Joyce", "Juana", "Juanita", "Judah", "Judd", "Jude", "Judge", "Judson", "Judy", "Jules", "Julia", "Julian", "Juliana", "Julianne", "Julie", "Julien", "Juliet", "Julio", "Julius", "June", "Junior", "Junius", "Justen", "Justice", "Justina", "Justine", "Juston", "Justus", "Justyn", "Juvenal", "Juwan", "Kacey", "Kaci", "Kacie", "Kade", "Kaden", "Kadin", "Kaela", "Kaelyn", "Kaia", "Kailee", "Kailey", "Kailyn", "Kaitlin", "Kaitlyn", "Kale", "Kaleb", "Kaleigh", "Kaley", "Kali", "Kallie", "Kameron", "Kamille", "Kamren", "Kamron", "Kamryn", "Kane", "Kara", "Kareem", "Karelle", "Karen", "Kari", "Kariane", "Karianne", "Karina", "Karine", "Karl", "Karlee", "Karley", "Karli", "Karlie", "Karolann", "Karson", "Kasandra", "Kasey", "Kassandra", "Katarina", "Katelin", "Katelyn", "Katelynn", "Katharina", "Katherine", "Katheryn", "Kathleen", "Kathlyn", "Kathryn", "Kathryne", "Katlyn", "Katlynn", "Katrina", "Katrine", "Kattie", "Kavon", "Kay", "Kaya", "Kaycee", "Kayden", "Kayla", "Kaylah", "Kaylee", "Kayleigh", "Kayley", "Kayli", "Kaylie", "Kaylin", "Keagan", "Keanu", "Keara", "Keaton", "Keegan", "Keeley", "Keely", "Keenan", "Keira", "Keith", "Kellen", "Kelley", "Kelli", "Kellie", "Kelly", "Kelsi", "Kelsie", "Kelton", "Kelvin", "Ken", "Kendall", "Kendra", "Kendrick", "Kenna", "Kennedi", "Kennedy", "Kenneth", "Kennith", "Kenny", "Kenton", "Kenya", "Kenyatta", "Kenyon", "Keon", "Keshaun", "Keshawn", "Keven", "Kevin", "Kevon", "Keyon", "Keyshawn", "Khalid", "Khalil", "Kian", "Kiana", "Kianna", "Kiara", "Kiarra", "Kiel", "Kiera", "Kieran", "Kiley", "Kim", "Kimberly", "King", "Kip", "Kira", "Kirk", "Kirsten", "Kirstin", "Kitty", "Kobe", "Koby", "Kody", "Kolby", "Kole", "Korbin", "Korey", "Kory", "Kraig", "Kris", "Krista", "Kristian", "Kristin", "Kristina", "Kristofer", "Kristoffer", "Kristopher", "Kristy", "Krystal", "Krystel", "Krystina", "Kurt", "Kurtis", "Kyla", "Kyle", "Kylee", "Kyleigh", "Kyler", "Kylie", "Kyra", "Lacey", "Lacy", "Ladarius", "Lafayette", "Laila", "Laisha", "Lamar", "Lambert", "Lamont", "Lance", "Landen", "Lane", "Laney", "Larissa", "Laron", "Larry", "Larue", "Laura", "Laurel", "Lauren", "Laurence", "Lauretta", "Lauriane", "Laurianne", "Laurie", "Laurine", "Laury", "Lauryn", "Lavada", "Lavern", "Laverna", "Laverne", "Lavina", "Lavinia", "Lavon", "Lavonne", "Lawrence", "Lawson", "Layla", "Layne", "Lazaro", "Lea", "Leann", "Leanna", "Leanne", "Leatha", "Leda", "Lee", "Leif", "Leila", "Leilani", "Lela", "Lelah", "Leland", "Lelia", "Lempi", "Lemuel", "Lenna", "Lennie", "Lenny", "Lenora", "Lenore", "Leo", "Leola", "Leon", "Leonard", "Leonardo", "Leone", "Leonel", "Leonie", "Leonor", "Leonora", "Leopold", "Leopoldo", "Leora", "Lera", "Lesley", "Leslie", "Lesly", "Lessie", "Lester", "Leta", "Letha", "Letitia", "Levi", "Lew", "Lewis", "Lexi", "Lexie", "Lexus", "Lia", "Liam", "Liana", "Libbie", "Libby", "Lila", "Lilian", "Liliana", "Liliane", "Lilla", "Lillian", "Lilliana", "Lillie", "Lilly", "Lily", "Lilyan", "Lina", "Lincoln", "Linda", "Lindsay", "Lindsey", "Linnea", "Linnie", "Linwood", "Lionel", "Lisa", "Lisandro", "Lisette", "Litzy", "Liza", "Lizeth", "Lizzie", "Llewellyn", "Lloyd", "Logan", "Lois", "Lola", "Lolita", "Loma", "Lon", "London", "Lonie", "Lonnie", "Lonny", "Lonzo", "Lora", "Loraine", "Loren", "Lorena", "Lorenz", "Lorenza", "Lorenzo", "Lori", "Lorine", "Lorna", "Lottie", "Lou", "Louie", "Louisa", "Lourdes", "Louvenia", "Lowell", "Loy", "Loyal", "Loyce", "Lucas", "Luciano", "Lucie", "Lucienne", "Lucile", "Lucinda", "Lucio", "Lucious", "Lucius", "Lucy", "Ludie", "Ludwig", "Lue", "Luella", "Luigi", "Luis", "Luisa", "Lukas", "Lula", "Lulu", "Luna", "Lupe", "Lura", "Lurline", "Luther", "Luz", "Lyda", "Lydia", "Lyla", "Lynn", "Lyric", "Lysanne", "Mabel", "Mabelle", "Mable", "Mac", "Macey", "Maci", "Macie", "Mack", "Mackenzie", "Macy", "Madaline", "Madalyn", "Maddison", "Madeline", "Madelyn", "Madelynn", "Madge", "Madie", "Madilyn", "Madisen", "Madison", "Madisyn", "Madonna", "Madyson", "Mae", "Maegan", "Maeve", "Mafalda", "Magali", "Magdalen", "Magdalena", "Maggie", "Magnolia", "Magnus", "Maia", "Maida", "Maiya", "Major", "Makayla", "Makenna", "Makenzie", "Malachi", "Malcolm", "Malika", "Malinda", "Mallie", "Mallory", "Malvina", "Mandy", "Manley", "Manuel", "Manuela", "Mara", "Marc", "Marcel", "Marcelina", "Marcelino", "Marcella", "Marcelle", "Marcellus", "Marcelo", "Marcia", "Marco", "Marcos", "Marcus", "Margaret", "Margarete", "Margarett", "Margaretta", "Margarette", "Margarita", "Marge", "Margie", "Margot", "Margret", "Marguerite", "Maria", "Mariah", "Mariam", "Marian", "Mariana", "Mariane", "Marianna", "Marianne", "Mariano", "Maribel", "Marie", "Mariela", "Marielle", "Marietta", "Marilie", "Marilou", "Marilyne", "Marina", "Mario", "Marion", "Marisa", "Marisol", "Maritza", "Marjolaine", "Marjorie", "Marjory", "Mark", "Markus", "Marlee", "Marlen", "Marlene", "Marley", "Marlin", "Marlon", "Marques", "Marquis", "Marquise", "Marshall", "Marta", "Martin", "Martina", "Martine", "Marty", "Marvin", "Mary", "Maryam", "Maryjane", "Maryse", "Mason", "Mateo", "Mathew", "Mathias", "Mathilde", "Matilda", "Matilde", "Matt", "Matteo", "Mattie", "Maud", "Maude", "Maudie", "Maureen", "Maurice", "Mauricio", "Maurine", "Maverick", "Mavis", "Max", "Maxie", "Maxime", "Maximilian", "Maximillia", "Maximillian", "Maximo", "Maximus", "Maxine", "Maxwell", "May", "Maya", "Maybell", "Maybelle", "Maye", "Maymie", "Maynard", "Mayra", "Mazie", "Mckayla", "Mckenna", "Mckenzie", "Meagan", "Meaghan", "Meda", "Megane", "Meggie", "Meghan", "Mekhi", "Melany", "Melba", "Melisa", "Melissa", "Mellie", "Melody", "Melvin", "Melvina", "Melyna", "Melyssa", "Mercedes", "Meredith", "Merl", "Merle", "Merlin", "Merritt", "Mertie", "Mervin", "Meta", "Mia", "Micaela", "Micah", "Michael", "Michaela", "Michale", "Micheal", "Michel", "Michele", "Michelle", "Miguel", "Mikayla", "Mike", "Mikel", "Milan", "Miles", "Milford", "Miller", "Millie", "Milo", "Milton", "Mina", "Minerva", "Minnie", "Miracle", "Mireille", "Mireya", "Misael", "Missouri", "Misty", "Mitchel", "Mitchell", "Mittie", "Modesta", "Modesto", "Mohamed", "Mohammad", "Mohammed", "Moises", "Mollie", "Molly", "Mona", "Monica", "Monique", "Monroe", "Monserrat", "Monserrate", "Montana", "Monte", "Monty", "Morgan", "Moriah", "Morris", "Mortimer", "Morton", "Mose", "Moses", "Moshe", "Mossie", "Mozell", "Mozelle", "Muhammad", "Muriel", "Murl", "Murphy", "Murray", "Mustafa", "Mya", "Myah", "Mylene", "Myles", "Myra", "Myriam", "Myrl", "Myrna", "Myron", "Myrtice", "Myrtie", "Myrtis", "Myrtle", "Nadia", "Nakia", "Name", "Nannie", "Naomi", "Naomie", "Napoleon", "Narciso", "Nash", "Nasir", "Nat", "Natalia", "Natalie", "Natasha", "Nathan", "Nathanael", "Nathanial", "Nathaniel", "Nathen", "Nayeli", "Neal", "Ned", "Nedra", "Neha", "Neil", "Nelda", "Nella", "Nelle", "Nellie", "Nels", "Nelson", "Neoma", "Nestor", "Nettie", "Neva", "Newell", "Newton", "Nia", "Nicholas", "Nicholaus", "Nichole", "Nick", "Nicklaus", "Nickolas", "Nico", "Nicola", "Nicolas", "Nicole", "Nicolette", "Nigel", "Nikita", "Nikki", "Nikko", "Niko", "Nikolas", "Nils", "Nina", "Noah", "Noble", "Noe", "Noel", "Noelia", "Noemi", "Noemie", "Noemy", "Nola", "Nolan", "Nona", "Nora", "Norbert", "Norberto", "Norene", "Norma", "Norris", "Norval", "Norwood", "Nova", "Novella", "Nya", "Nyah", "Nyasia", "Obie", "Oceane", "Ocie", "Octavia", "Oda", "Odell", "Odessa", "Odie", "Ofelia", "Okey", "Ola", "Olaf", "Ole", "Olen", "Oleta", "Olga", "Olin", "Oliver", "Ollie", "Oma", "Omari", "Omer", "Ona", "Onie", "Opal", "Ophelia", "Ora", "Oral", "Oran", "Oren", "Orie", "Orin", "Orion", "Orland", "Orlando", "Orlo", "Orpha", "Orrin", "Orval", "Orville", "Osbaldo", "Osborne", "Oscar", "Osvaldo", "Oswald", "Oswaldo", "Otha", "Otho", "Otilia", "Otis", "Ottilie", "Ottis", "Otto", "Ova", "Owen", "Ozella", "Pablo", "Paige", "Palma", "Pamela", "Pansy", "Paolo", "Paris", "Parker", "Pascale", "Pasquale", "Pat", "Patience", "Patricia", "Patrick", "Patsy", "Pattie", "Paul", "Paula", "Pauline", "Paxton", "Payton", "Pearl", "Pearlie", "Pearline", "Pedro", "Peggie", "Penelope", "Percival", "Percy", "Perry", "Pete", "Peter", "Petra", "Peyton", "Philip", "Phoebe", "Phyllis", "Pierce", "Pierre", "Pietro", "Pink", "Pinkie", "Piper", "Polly", "Porter", "Precious", "Presley", "Preston", "Price", "Prince", "Princess", "Priscilla", "Providenci", "Prudence", "Queen", "Queenie", "Quentin", "Quincy", "Quinn", "Quinten", "Quinton", "Rachael", "Rachel", "Rachelle", "Rae", "Raegan", "Rafael", "Rafaela", "Raheem", "Rahsaan", "Rahul", "Raina", "Raleigh", "Ralph", "Ramiro", "Ramon", "Ramona", "Randal", "Randall", "Randi", "Randy", "Ransom", "Raoul", "Raphael", "Raphaelle", "Raquel", "Rashad", "Rashawn", "Rasheed", "Raul", "Raven", "Ray", "Raymond", "Raymundo", "Reagan", "Reanna", "Reba", "Rebeca", "Rebecca", "Rebeka", "Rebekah", "Reece", "Reed", "Reese", "Regan", "Reggie", "Reginald", "Reid", "Reilly", "Reina", "Reinhold", "Remington", "Rene", "Renee", "Ressie", "Reta", "Retha", "Retta", "Reuben", "Reva", "Rex", "Rey", "Reyes", "Reymundo", "Reyna", "Reynold", "Rhea", "Rhett", "Rhianna", "Rhiannon", "Rhoda", "Ricardo", "Richard", "Richie", "Richmond", "Rick", "Rickey", "Rickie", "Ricky", "Rico", "Rigoberto", "Riley", "Rita", "River", "Robb", "Robbie", "Robert", "Roberta", "Roberto", "Robin", "Robyn", "Rocio", "Rocky", "Rod", "Roderick", "Rodger", "Rodolfo", "Rodrick", "Rodrigo", "Roel", "Rogelio", "Roger", "Rogers", "Rolando", "Rollin", "Roma", "Romaine", "Roman", "Ron", "Ronaldo", "Ronny", "Roosevelt", "Rory", "Rosa", "Rosalee", "Rosalia", "Rosalind", "Rosalinda", "Rosalyn", "Rosamond", "Rosanna", "Rosario", "Roscoe", "Rose", "Rosella", "Roselyn", "Rosemarie", "Rosemary", "Rosendo", "Rosetta", "Rosie", "Rosina", "Roslyn", "Ross", "Rossie", "Rowan", "Rowena", "Rowland", "Roxane", "Roxanne", "Roy", "Royal", "Royce", "Rozella", "Ruben", "Rubie", "Ruby", "Rubye", "Rudolph", "Rudy", "Rupert", "Russ", "Russel", "Russell", "Rusty", "Ruth", "Ruthe", "Ruthie", "Ryan", "Ryann", "Ryder", "Rylan", "Rylee", "Ryleigh", "Ryley", "Sabina", "Sabrina", "Sabryna", "Sadie", "Sadye", "Sage", "Saige", "Sallie", "Sally", "Salma", "Salvador", "Salvatore", "Sam", "Samanta", "Samantha", "Samara", "Samir", "Sammie", "Sammy", "Samson", "Sandra", "Sandrine", "Sandy", "Sanford", "Santa", "Santiago", "Santina", "Santino", "Santos", "Sarah", "Sarai", "Sarina", "Sasha", "Saul", "Savanah", "Savanna", "Savannah", "Savion", "Scarlett", "Schuyler", "Scot", "Scottie", "Scotty", "Seamus", "Sean", "Sebastian", "Sedrick", "Selena", "Selina", "Selmer", "Serena", "Serenity", "Seth", "Shad", "Shaina", "Shakira", "Shana", "Shane", "Shanel", "Shanelle", "Shania", "Shanie", "Shaniya", "Shanna", "Shannon", "Shanny", "Shanon", "Shany", "Sharon", "Shaun", "Shawn", "Shawna", "Shaylee", "Shayna", "Shayne", "Shea", "Sheila", "Sheldon", "Shemar", "Sheridan", "Sherman", "Sherwood", "Shirley", "Shyann", "Shyanne", "Sibyl", "Sid", "Sidney", "Sienna", "Sierra", "Sigmund", "Sigrid", "Sigurd", "Silas", "Sim", "Simeon", "Simone", "Sincere", "Sister", "Skye", "Skyla", "Skylar", "Sofia", "Soledad", "Solon", "Sonia", "Sonny", "Sonya", "Sophia", "Sophie", "Spencer", "Stacey", "Stacy", "Stan", "Stanford", "Stanley", "Stanton", "Stefan", "Stefanie", "Stella", "Stephan", "Stephania", "Stephanie", "Stephany", "Stephen", "Stephon", "Sterling", "Steve", "Stevie", "Stewart", "Stone", "Stuart", "Summer", "Sunny", "Susan", "Susana", "Susanna", "Susie", "Suzanne", "Sven", "Syble", "Sydnee", "Sydney", "Sydni", "Sydnie", "Sylvan", "Sylvester", "Sylvia", "Tabitha", "Tad", "Talia", "Talon", "Tamara", "Tamia", "Tania", "Tanner", "Tanya", "Tara", "Taryn", "Tate", "Tatum", "Tatyana", "Taurean", "Tavares", "Taya", "Taylor", "Teagan", "Ted", "Telly", "Terence", "Teresa", "Terrance", "Terrell", "Terrence", "Terrill", "Terry", "Tess", "Tessie", "Tevin", "Thad", "Thaddeus", "Thalia", "Thea", "Thelma", "Theo", "Theodora", "Theodore", "Theresa", "Therese", "Theresia", "Theron", "Thomas", "Thora", "Thurman", "Tia", "Tiana", "Tianna", "Tiara", "Tierra", "Tiffany", "Tillman", "Timmothy", "Timmy", "Timothy", "Tina", "Tito", "Titus", "Tobin", "Toby", "Tod", "Tom", "Tomas", "Tomasa", "Tommie", "Toney", "Toni", "Tony", "Torey", "Torrance", "Torrey", "Toy", "Trace", "Tracey", "Tracy", "Travis", "Travon", "Tre", "Tremaine", "Tremayne", "Trent", "Trenton", "Tressa", "Tressie", "Treva", "Trever", "Trevion", "Trevor", "Trey", "Trinity", "Trisha", "Tristian", "Tristin", "Triston", "Troy", "Trudie", "Trycia", "Trystan", "Turner", "Twila", "Tyler", "Tyra", "Tyree", "Tyreek", "Tyrel", "Tyrell", "Tyrese", "Tyrique", "Tyshawn", "Tyson", "Ubaldo", "Ulices", "Ulises", "Una", "Unique", "Urban", "Uriah", "Uriel", "Ursula", "Vada", "Valentin", "Valentina", "Valentine", "Valerie", "Vallie", "Van", "Vance", "Vanessa", "Vaughn", "Veda", "Velda", "Vella", "Velma", "Velva", "Vena", "Verda", "Verdie", "Vergie", "Verla", "Verlie", "Vern", "Verna", "Verner", "Vernice", "Vernie", "Vernon", "Verona", "Veronica", "Vesta", "Vicenta", "Vicente", "Vickie", "Vicky", "Victor", "Victoria", "Vida", "Vidal", "Vilma", "Vince", "Vincent", "Vincenza", "Vincenzo", "Vinnie", "Viola", "Violet", "Violette", "Virgie", "Virgil", "Virginia", "Virginie", "Vita", "Vito", "Viva", "Vivian", "Viviane", "Vivianne", "Vivien", "Vivienne", "Vladimir", "Wade", "Waino", "Waldo", "Walker", "Wallace", "Walter", "Walton", "Wanda", "Ward", "Warren", "Watson", "Wava", "Waylon", "Wayne", "Webster", "Weldon", "Wellington", "Wendell", "Wendy", "Werner", "Westley", "Weston", "Whitney", "Wilber", "Wilbert", "Wilburn", "Wiley", "Wilford", "Wilfred", "Wilfredo", "Wilfrid", "Wilhelm", "Wilhelmine", "Will", "Willa", "Willard", "William", "Willie", "Willis", "Willow", "Willy", "Wilma", "Wilmer", "Wilson", "Wilton", "Winfield", "Winifred", "Winnifred", "Winona", "Winston", "Woodrow", "Wyatt", "Wyman", "Xander", "Xavier", "Xzavier", "Yadira", "Yasmeen", "Yasmin", "Yasmine", "Yazmin", "Yesenia", "Yessenia", "Yolanda", "Yoshiko", "Yvette", "Yvonne", "Zachariah", "Zachary", "Zachery", "Zack", "Zackary", "Zackery", "Zakary", "Zander", "Zane", "Zaria", "Zechariah", "Zelda", "Zella", "Zelma", "Zena", "Zetta", "Zion", "Zita", "Zoe", "Zoey", "Zoie", "Zoila", "Zola", "Zora", "Zula"}, + "last": {"Abbott", "Abernathy", "Abshire", "Adams", "Altenwerth", "Anderson", "Ankunding", "Armstrong", "Auer", "Aufderhar", "Bahringer", "Bailey", "Balistreri", "Barrows", "Bartell", "Bartoletti", "Barton", "Bashirian", "Batz", "Bauch", "Baumbach", "Bayer", "Beahan", "Beatty", "Bechtelar", "Becker", "Bednar", "Beer", "Beier", "Berge", "Bergnaum", "Bergstrom", "Bernhard", "Bernier", "Bins", "Blanda", "Blick", "Block", "Bode", "Boehm", "Bogan", "Bogisich", "Borer", "Bosco", "Botsford", "Boyer", "Boyle", "Bradtke", "Brakus", "Braun", "Breitenberg", "Brekke", "Brown", "Bruen", "Buckridge", "Carroll", "Carter", "Cartwright", "Casper", "Cassin", "Champlin", "Christiansen", "Cole", "Collier", "Collins", "Conn", "Connelly", "Conroy", "Considine", "Corkery", "Cormier", "Corwin", "Cremin", "Crist", "Crona", "Cronin", "Crooks", "Cruickshank", "Cummerata", "Cummings", "Dach", "Damore", "Daniel", "Dare", "Daugherty", "Davis", "Deckow", "Denesik", "Dibbert", "Dickens", "Dicki", "Dickinson", "Dietrich", "Donnelly", "Dooley", "Douglas", "Doyle", "DuBuque", "Durgan", "Ebert", "Effertz", "Eichmann", "Emard", "Emmerich", "Erdman", "Ernser", "Fadel", "Fahey", "Farrell", "Fay", "Feeney", "Feest", "Feil", "Ferry", "Fisher", "Flatley", "Frami", "Franecki", "Friesen", "Fritsch", "Funk", "Gaylord", "Gerhold", "Gerlach", "Gibson", "Gislason", "Gleason", "Gleichner", "Glover", "Goldner", "Goodwin", "Gorczany", "Gottlieb", "Goyette", "Grady", "Graham", "Grant", "Green", "Greenfelder", "Greenholt", "Grimes", "Gulgowski", "Gusikowski", "Gutkowski", "Gutmann", "Haag", "Hackett", "Hagenes", "Hahn", "Haley", "Halvorson", "Hamill", "Hammes", "Hand", "Hane", "Hansen", "Harber", "Harris", "Hartmann", "Harvey", "Hauck", "Hayes", "Heaney", "Heathcote", "Hegmann", "Heidenreich", "Heller", "Herman", "Hermann", "Hermiston", "Herzog", "Hessel", "Hettinger", "Hickle", "Hilll", "Hills", "Hilpert", "Hintz", "Hirthe", "Hodkiewicz", "Hoeger", "Homenick", "Hoppe", "Howe", "Howell", "Hudson", "Huel", "Huels", "Hyatt", "Jacobi", "Jacobs", "Jacobson", "Jakubowski", "Jaskolski", "Jast", "Jenkins", "Jerde", "Jewess", "Johns", "Johnson", "Johnston", "Jones", "Kassulke", "Kautzer", "Keebler", "Keeling", "Kemmer", "Kerluke", "Kertzmann", "Kessler", "Kiehn", "Kihn", "Kilback", "King", "Kirlin", "Klein", "Kling", "Klocko", "Koch", "Koelpin", "Koepp", "Kohler", "Konopelski", "Koss", "Kovacek", "Kozey", "Krajcik", "Kreiger", "Kris", "Kshlerin", "Kub", "Kuhic", "Kuhlman", "Kuhn", "Kulas", "Kunde", "Kunze", "Kuphal", "Kutch", "Kuvalis", "Labadie", "Lakin", "Lang", "Langosh", "Langworth", "Larkin", "Larson", "Leannon", "Lebsack", "Ledner", "Leffler", "Legros", "Lehner", "Lemke", "Lesch", "Leuschke", "Lind", "Lindgren", "Littel", "Little", "Lockman", "Lowe", "Lubowitz", "Lueilwitz", "Luettgen", "Lynch", "Macejkovic", "Maggio", "Mann", "Mante", "Marks", "Marquardt", "Marvin", "Mayer", "Mayert", "McClure", "McCullough", "McDermott", "McGlynn", "McKenzie", "McLaughlin", "Medhurst", "Mertz", "Metz", "Miller", "Mills", "Mitchell", "Moen", "Mohr", "Monahan", "Moore", "Morar", "Morissette", "Mosciski", "Mraz", "Mueller", "Muller", "Murazik", "Murphy", "Murray", "Nader", "Nicolas", "Nienow", "Nikolaus", "Nitzsche", "Nolan", "Oberbrunner", "Okuneva", "Olson", "Ondricka", "OReilly", "Orn", "Ortiz", "Osinski", "Pacocha", "Padberg", "Pagac", "Parisian", "Parker", "Paucek", "Pfannerstill", "Pfeffer", "Pollich", "Pouros", "Powlowski", "Predovic", "Price", "Prohaska", "Prosacco", "Purdy", "Quigley", "Quitzon", "Rath", "Ratke", "Rau", "Raynor", "Reichel", "Reichert", "Reilly", "Reinger", "Rempel", "Renner", "Reynolds", "Rice", "Rippin", "Ritchie", "Robel", "Roberts", "Rodriguez", "Rogahn", "Rohan", "Rolfson", "Romaguera", "Roob", "Rosenbaum", "Rowe", "Ruecker", "Runolfsdottir", "Runolfsson", "Runte", "Russel", "Rutherford", "Ryan", "Sanford", "Satterfield", "Sauer", "Sawayn", "Schaden", "Schaefer", "Schamberger", "Schiller", "Schimmel", "Schinner", "Schmeler", "Schmidt", "Schmitt", "Schneider", "Schoen", "Schowalter", "Schroeder", "Schulist", "Schultz", "Schumm", "Schuppe", "Schuster", "Senger", "Shanahan", "Shields", "Simonis", "Sipes", "Skiles", "Smith", "Smitham", "Spencer", "Spinka", "Sporer", "Stamm", "Stanton", "Stark", "Stehr", "Steuber", "Stiedemann", "Stokes", "Stoltenberg", "Stracke", "Streich", "Stroman", "Strosin", "Swaniawski", "Swift", "Terry", "Thiel", "Thompson", "Tillman", "Torp", "Torphy", "Towne", "Toy", "Trantow", "Tremblay", "Treutel", "Tromp", "Turcotte", "Turner", "Ullrich", "Upton", "Vandervort", "Veum", "Volkman", "Von", "VonRueden", "Waelchi", "Walker", "Walsh", "Walter", "Ward", "Waters", "Watsica", "Weber", "Wehner", "Weimann", "Weissnat", "Welch", "West", "White", "Wiegand", "Wilderman", "Wilkinson", "Will", "Williamson", "Willms", "Windler", "Wintheiser", "Wisoky", "Wisozk", "Witting", "Wiza", "Wolf", "Wolff", "Wuckert", "Wunsch", "Wyman", "Yost", "Yundt", "Zboncak", "Zemlak", "Ziemann", "Zieme", "Zulauf"}, +} diff --git a/vendor/github.com/brianvoe/gofakeit/data/status_code.go b/vendor/github.com/brianvoe/gofakeit/data/status_code.go new file mode 100644 index 00000000000..7d78fd99502 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/data/status_code.go @@ -0,0 +1,7 @@ +package data + +// StatusCodes consists of commonly used HTTP status codes +var StatusCodes = map[string][]int{ + "simple": {200, 301, 302, 400, 404, 500}, + "general": {100, 200, 201, 203, 204, 205, 301, 302, 304, 400, 401, 403, 404, 405, 406, 416, 500, 501, 502, 503, 504}, +} diff --git a/vendor/github.com/brianvoe/gofakeit/data/vehicle.go b/vendor/github.com/brianvoe/gofakeit/data/vehicle.go new file mode 100644 index 00000000000..3b96728bcca --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/data/vehicle.go @@ -0,0 +1,10 @@ +package data + +// Vehicle Beer consists of various beer information +var Vehicle = map[string][]string{ + "vehicle_type": {"Passenger car mini", "Passenger car light", "Passenger car compact", "Passenger car medium", "Passenger car heavy", "Sport utility vehicle", "Pickup truck", "Van"}, + "fuel_type": {"Gasoline", "Methanol", "Ethanol", "Diesel", "LPG", "CNG", "Electric"}, + "transmission_type": {"Manual", "Automatic"}, + "maker": {"Alfa Romeo", "Aston Martin", "Audi", "Bentley", "Benz", "BMW", "Bugatti", "Cadillac", "Chevrolet", "Chrysler", "Citroen", "Corvette", "DAF", "Dacia", "Daewoo", "Daihatsu", "Datsun", "De Lorean", "Dino", "Dodge", "Farboud", "Ferrari", "Fiat", "Ford", "Honda", "Hummer", "Hyundai", "Jaguar", "Jeep", "KIA", "Koenigsegg", "Lada", "Lamborghini", "Lancia", "Land Rover", "Lexus", "Ligier", "Lincoln", "Lotus", "Martini", "Maserati", "Maybach", "Mazda", "McLaren", "Mercedes", "Mercedes-Benz", "Mini", "Mitsubishi", "Nissan", "Noble", "Opel", "Peugeot", "Pontiac", "Porsche", "Renault", "Rolls-Royce", "Rover", "Saab", "Seat", "Skoda", "Smart", "Spyker", "Subaru", "Suzuki", "Toyota", "Tesla", "Vauxhall", "Volkswagen", "Volvo"}, + "model": {"Db9 Coupe", "Db9 Coupe Manual", "Db9 Volante", "V12 Vanquish S", "V8 Vantage", "A3", "A4", "A4 Avant Quattro", "A4 Cabriolet", "A4 Cabriolet Quattro", "A4 Quattro", "A6", "A6 Avant Quattro", "A6 Quattro", "A8 L", "Gti", "Passat", "S4", "S4 Avant", "S4 Cabriolet", "Tt Coupe", "Tt Roadster", "Bentley Arnage", "Continental Flying Spur", "Continental Gt", " 325ci Convertible", " 325i", " 325xi", " 325xi Sport Wagon", " 330ci Convertible", " 330i", " 330xi", " 525i", " 525xi", " 530i", " 530xi", " 530xi Sport Wagon", " 550i", " 650ci", " 650ci Convertible", " 750li", " 760li", " M3", " M3 Convertible", " M5", " M6", " Mini Cooper", " Mini Cooper Convertible", " Mini Cooper S", " Mini Cooper S Convertible", " X3", " X5", " X5 4.8is", " Z4 3.0 Si Coupe", " Z4 3.0i", " Z4 3.0si", " Z4 M Roadster", "Veyron", "300c/srt-8", "Caravan 2wd", "Charger", "Commander 4wd", "Crossfire Roadster", "Dakota Pickup 2wd", "Dakota Pickup 4wd", "Durango 2wd", "Durango 4wd", "Grand Cherokee 2wd", "Grand Cherokee 4wd", "Liberty/cherokee 2wd", "Liberty/cherokee 4wd", "Pacifica 2wd", "Pacifica Awd", "Pt Cruiser", "Ram 1500 Pickup 2wd", "Ram 1500 Pickup 4wd", "Sebring 4-dr", "Stratus 4-dr", "Town & Country 2wd", "Viper Convertible", "Wrangler/tj 4wd", "F430", "Ferrari 612 Scaglietti", "Ferrari F141", "B4000 4wd", "Crown Victoria Police", "E150 Club Wagon", "E150 Econoline 2wd", "Escape 4wd", "Escape Fwd", "Escape Hybrid 4wd", "Escape Hybrid Fwd", "Expedition 2wd", "Explorer 2wd", "Explorer 4wd", "F150 Ffv 2wd", "F150 Ffv 4wd", "F150 Pickup 2wd", "F150 Pickup 4wd", "Five Hundred Awd", "Focus Fwd", "Focus Station Wag", "Freestar Wagon Fwd", "Freestyle Awd", "Freestyle Fwd", "Grand Marquis", "Gt 2wd", "Ls", "Mark Lt", "Milan", "Monterey Wagon Fwd", "Mountaineer 4wd", "Mustang", "Navigator 2wd", "Ranger Pickup 2wd", "Ranger Pickup 4wd", "Taurus", "Taurus Ethanol Ffv", "Thunderbird", "Town Car", "Zephyr", "B9 Tribeca Awd", "Baja Awd", "Forester Awd", "Impreza Awd", "Impreza Wgn/outback Spt Awd", "Legacy Awd", "Legacy Wagon Awd", "Outback Awd", "Outback Wagon Awd", "9-3 Convertible", "9-3 Sport Sedan", "9-5 Sedan", "C15 Silverado Hybrid 2wd", "C1500 Silverado 2wd", "C1500 Suburban 2wd", "C1500 Tahoe 2wd", "C1500 Yukon 2wd", "Cobalt", "Colorado 2wd", "Colorado 4wd", "Colorado Cab Chassis Inc 2wd", "Colorado Crew Cab 2wd", "Colorado Crew Cab 4wd", "Corvette", "Cts", "Dts", "Envoy 2wd", "Envoy Xl 4wd", "Equinox Awd", "Equinox Fwd", "Escalade 2wd", "Escalade Esv Awd", "G15/25chev Van 2wd Conv", "G1500/2500 Chevy Express 2wd", "G1500/2500 Chevy Van 2wd", "G6", "G6 Gt/gtp Convertible", "Grand Prix", "Gto", "H3 4wd", "Hhr Fwd", "I-280 2wd Ext Cab", "Impala", "K15 Silverado Hybrid 4wd", "K1500 Avalanche 4wd", "K1500 Silverado 4wd", "K1500 Tahoe 4wd", "Lacrosse/allure", "Limousine", "Malibu", "Montana Sv6 Awd", "Monte Carlo", "Rendezvous Awd", "Rendezvous Fwd", "Solstice", "Srx 2wd", "Srx Awd", "Ssr Pickup 2wd", "Sts", "Sts Awd", "Terraza Fwd", "Trailblazer 2wd", "Trailblazer 4wd", "Trailblazer Awd", "Trailblazer Ext 4wd", "Uplander Fwd", "Vue Awd", "Vue Fwd", "Xlr", "Aveo", "Forenza", "Forenza Wagon", "Verona", "Accord", "Accord Hybrid", "Civic", "Civic Hybrid", "Cr-v 4wd", "Element 2wd", "Element 4wd", "Insight", "Mdx 4wd", "Odyssey 2wd", "Pilot 2wd", "Pilot 4wd", "Ridgeline 4wd", "Rl", "Rsx", "S2000", "Tl", "Tsx", "Accent", "Azera", "Elantra", "Santafe 2wd", "Santafe 4wd", "Sonata", "Tiburon", "Tucson 2wd", "Tucson 4wd", "S-type 3.0 Litre", "S-type 4.2 Litre", "S-type R", "Vdp Lwb", "Xj8", "Xk8 Convertible", "Xkr Convertible", "X-type", "X-type Sport Brake", "Amanti", "Optima", "Optima(ms)", "Rio", "Sedona", "Sorento 2wd", "Sorento 4wd", "Spectra(ld)", "Sportage 2wd", "Sportage 4wd", "L-140/715 Gallardo", "L-147/148 Murcielago", "Lr3", "Range Rover", "Range Rover Sport", "Elise/exige", "Coupe Cambiocorsa/gt/g-sport", "Quattroporte", "Mazda 3", "Mazda 5", "Mazda 6", "Mazda 6 Sport Wagon", "Mazda Rx-8", "Mpv", "Mx-5", "C230", "C280", "C280 4matic", "C350", "C350 4matic", "C55 Amg", "Cl65 Amg", "Clk350", "Clk350 (cabriolet)", "Clk55 Amg (cabriolet)", "Cls500", "Cls55 Amg", "E320 Cdi", "E350", "E350 (wagon)", "E350 4matic", "E350 4matic (wagon)", "E500", "E55 Amg", "E55 Amg (wagon)", "Maybach 57s", "Maybach 62", "Ml350", "Ml500", "R350", "R500", "S350", "S430", "Sl500", "Sl600", "Sl65 Amg", "Slk280", "Slk350", "Slr", "Eclipse", "Endeavor 2wd", "Endeavor 4wd", "Galant", "Lancer", "Lancer Evolution", "Lancer Sportback", "Montero", "Outlander 2wd", "Outlander 4wd", "Vibe", "350z", "350z Roadster", "Altima", "Armada 2wd", "Armada 4wd", "Frontier 2wd", "Frontier V6-2wd", "Frontier V6-4wd", "Fx35 Awd", "Fx35 Rwd", "Fx45 Awd", "G35", "M35", "M35x", "M45", "Maxima", "Murano Awd", "Murano Fwd", "Pathfinder 2wd", "Pathfinder 4wd", "Q45", "Q45 Sport", "Quest", "Qx56 4wd", "Sentra", "Titan 2wd", "Titan 4wd", "Xterra 2wd", "Xterra 4wd", "Boxster", "Boxster S", "Carrera 2 Coupe", "Cayenne", "Cayenne S", "Cayenne Turbo", "Cayman S", "Phantom", "F150 Supercrew 4wd", "C8 Spyder", "Aerio", "Aerio Sx", "Aerio Sx Awd", "Grand Vitara Xl-7", "Grand Vitara Xl-7 4wd", "Grand Vitara Xv6", "Grand Vitara Xv6 Awd", "4runner 2wd", "4runner 4wd", "Avalon", "Camry", "Camry Solara", "Camry Solara Convertible", "Corolla", "Corolla Matrix", "Es 330", "Gs 300 4wd", "Gs 300/gs 430", "Gx 470", "Highlander 2wd", "Highlander 4wd", "Highlander Hybrid 2wd", "Highlander Hybrid 4wd", "Is 250", "Is 250 Awd", "Is 350", "Ls 430", "Lx 470", "Prius", "Rav4 2wd", "Rav4 4wd", "Rx 330 2wd", "Rx 330 4wd", "Rx 400h 4wd", "Sc 430", "Scion Tc", "Scion Xa", "Scion Xb", "Sequoia 2wd", "Sequoia 4wd", "Sienna 2wd", "Sienna 4wd", "Toyota Tacoma 2wd", "Toyota Tacoma 4wd", "Toyota Tundra 2wd", "Toyota Tundra 4wd", "Yaris", "A3 Quattro", "Golf", "Jetta", "New Beetle", "New Beetle Convertible", "Passat Wagon 4motion", "Phaeton", "Rabbit", "Touareg", "Tt Coupe Quattro", "Tt Roadster Quattro", "C70 Convertible", "S40 Awd", "S40 Fwd", "S60 Awd", "S60 Fwd", "S60 R Awd", "S80 Fwd", "V50 Awd", "V70 Fwd", "V70 R Awd", "Xc 70 Awd", "Xc 90 Awd", "Xc 90 Fwd"}, +} diff --git a/vendor/github.com/brianvoe/gofakeit/datetime.go b/vendor/github.com/brianvoe/gofakeit/datetime.go new file mode 100644 index 00000000000..8c064473d3d --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/datetime.go @@ -0,0 +1,77 @@ +package gofakeit + +import ( + "strconv" + "time" +) + +// Date will generate a random time.Time struct +func Date() time.Time { + return time.Date(Year(), time.Month(Number(0, 12)), Day(), Hour(), Minute(), Second(), NanoSecond(), time.UTC) +} + +// DateRange will generate a random time.Time struct between a start and end date +func DateRange(start, end time.Time) time.Time { + return time.Unix(0, int64(Number(int(start.UnixNano()), int(end.UnixNano())))).UTC() +} + +// Month will generate a random month string +func Month() string { + return time.Month(Number(1, 12)).String() +} + +// Day will generate a random day between 1 - 31 +func Day() int { + return Number(1, 31) +} + +// WeekDay will generate a random weekday string (Monday-Sunday) +func WeekDay() string { + return time.Weekday(Number(0, 6)).String() +} + +// Year will generate a random year between 1900 - current year +func Year() int { + return Number(1900, time.Now().Year()) +} + +// Hour will generate a random hour - in military time +func Hour() int { + return Number(0, 23) +} + +// Minute will generate a random minute +func Minute() int { + return Number(0, 59) +} + +// Second will generate a random second +func Second() int { + return Number(0, 59) +} + +// NanoSecond will generate a random nano second +func NanoSecond() int { + return Number(0, 999999999) +} + +// TimeZone will select a random timezone string +func TimeZone() string { + return getRandValue([]string{"timezone", "text"}) +} + +// TimeZoneFull will select a random full timezone string +func TimeZoneFull() string { + return getRandValue([]string{"timezone", "full"}) +} + +// TimeZoneAbv will select a random timezone abbreviation string +func TimeZoneAbv() string { + return getRandValue([]string{"timezone", "abr"}) +} + +// TimeZoneOffset will select a random timezone offset +func TimeZoneOffset() float32 { + value, _ := strconv.ParseFloat(getRandValue([]string{"timezone", "offset"}), 32) + return float32(value) +} diff --git a/vendor/github.com/brianvoe/gofakeit/doc.go b/vendor/github.com/brianvoe/gofakeit/doc.go new file mode 100644 index 00000000000..c53335e634f --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/doc.go @@ -0,0 +1,10 @@ +/* +Package gofakeit is a random data generator written in go + +Every function has an example and a benchmark + +See the full list here https://godoc.org/github.com/brianvoe/gofakeit + +80+ Functions!!! +*/ +package gofakeit diff --git a/vendor/github.com/brianvoe/gofakeit/faker.go b/vendor/github.com/brianvoe/gofakeit/faker.go new file mode 100644 index 00000000000..38062d5cdf9 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/faker.go @@ -0,0 +1,15 @@ +package gofakeit + +import ( + "math/rand" + "time" +) + +// Seed random. Setting seed to 0 will use time.Now().UnixNano() +func Seed(seed int64) { + if seed == 0 { + rand.Seed(time.Now().UTC().UnixNano()) + } else { + rand.Seed(seed) + } +} diff --git a/vendor/github.com/brianvoe/gofakeit/file.go b/vendor/github.com/brianvoe/gofakeit/file.go new file mode 100644 index 00000000000..6c1e8d56cba --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/file.go @@ -0,0 +1,11 @@ +package gofakeit + +// MimeType will generate a random mime file type +func MimeType() string { + return getRandValue([]string{"file", "mime_type"}) +} + +// Extension will generate a random file extension +func Extension() string { + return getRandValue([]string{"file", "extension"}) +} diff --git a/vendor/github.com/brianvoe/gofakeit/generate.go b/vendor/github.com/brianvoe/gofakeit/generate.go new file mode 100644 index 00000000000..284eef8bb10 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/generate.go @@ -0,0 +1,41 @@ +package gofakeit + +import ( + "strings" +) + +// Generate fake information from given string. String should contain {category.subcategory} +// +// Ex: {person.first} - random firstname +// +// Ex: {person.first}###{person.last}@{person.last}.{internet.domain_suffix} - billy834smith@smith.com +// +// Ex: ### - 481 - random numbers +// +// Ex: ??? - fda - random letters +// +// For a complete list possible categories use the Categories() function. +func Generate(dataVal string) string { + // Identify items between brackets: {person.first} + for strings.Count(dataVal, "{") > 0 && strings.Count(dataVal, "}") > 0 { + catValue := "" + startIndex := strings.Index(dataVal, "{") + endIndex := strings.Index(dataVal, "}") + replace := dataVal[(startIndex + 1):endIndex] + categories := strings.Split(replace, ".") + + if len(categories) >= 2 && dataCheck([]string{categories[0], categories[1]}) { + catValue = getRandValue([]string{categories[0], categories[1]}) + } + + dataVal = strings.Replace(dataVal, "{"+replace+"}", catValue, 1) + } + + // Replace # with numbers + dataVal = replaceWithNumbers(dataVal) + + // Replace ? with letters + dataVal = replaceWithLetters(dataVal) + + return dataVal +} diff --git a/vendor/github.com/brianvoe/gofakeit/hacker.go b/vendor/github.com/brianvoe/gofakeit/hacker.go new file mode 100644 index 00000000000..0ac73b7109f --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/hacker.go @@ -0,0 +1,35 @@ +package gofakeit + +import "strings" + +// HackerPhrase will return a random hacker sentence +func HackerPhrase() string { + words := strings.Split(Generate(getRandValue([]string{"hacker", "phrase"})), " ") + words[0] = strings.Title(words[0]) + return strings.Join(words, " ") +} + +// HackerAbbreviation will return a random hacker abbreviation +func HackerAbbreviation() string { + return getRandValue([]string{"hacker", "abbreviation"}) +} + +// HackerAdjective will return a random hacker adjective +func HackerAdjective() string { + return getRandValue([]string{"hacker", "adjective"}) +} + +// HackerNoun will return a random hacker noun +func HackerNoun() string { + return getRandValue([]string{"hacker", "noun"}) +} + +// HackerVerb will return a random hacker verb +func HackerVerb() string { + return getRandValue([]string{"hacker", "verb"}) +} + +// HackerIngverb will return a random hacker ingverb +func HackerIngverb() string { + return getRandValue([]string{"hacker", "ingverb"}) +} diff --git a/vendor/github.com/brianvoe/gofakeit/hipster.go b/vendor/github.com/brianvoe/gofakeit/hipster.go new file mode 100644 index 00000000000..3166a9966a1 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/hipster.go @@ -0,0 +1,20 @@ +package gofakeit + +// HipsterWord will return a single hipster word +func HipsterWord() string { + return getRandValue([]string{"hipster", "word"}) +} + +// HipsterSentence will generate a random sentence +func HipsterSentence(wordCount int) string { + return sentence(wordCount, HipsterWord) +} + +// HipsterParagraph will generate a random paragraphGenerator +// Set Paragraph Count +// Set Sentence Count +// Set Word Count +// Set Paragraph Separator +func HipsterParagraph(paragraphCount int, sentenceCount int, wordCount int, separator string) string { + return paragraphGenerator(paragrapOptions{paragraphCount, sentenceCount, wordCount, separator}, HipsterSentence) +} diff --git a/vendor/github.com/brianvoe/gofakeit/image.go b/vendor/github.com/brianvoe/gofakeit/image.go new file mode 100644 index 00000000000..de5a2e6d916 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/image.go @@ -0,0 +1,8 @@ +package gofakeit + +import "strconv" + +// ImageURL will generate a random Image Based Upon Height And Width. https://picsum.photos/ +func ImageURL(width int, height int) string { + return "https://picsum.photos/" + strconv.Itoa(width) + "/" + strconv.Itoa(height) +} diff --git a/vendor/github.com/brianvoe/gofakeit/internet.go b/vendor/github.com/brianvoe/gofakeit/internet.go new file mode 100644 index 00000000000..69dd700e523 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/internet.go @@ -0,0 +1,55 @@ +package gofakeit + +import ( + "fmt" + "math/rand" + "strings" +) + +// DomainName will generate a random url domain name +func DomainName() string { + return strings.ToLower(JobDescriptor()+BS()) + "." + DomainSuffix() +} + +// DomainSuffix will generate a random domain suffix +func DomainSuffix() string { + return getRandValue([]string{"internet", "domain_suffix"}) +} + +// URL will generate a random url string +func URL() string { + url := "http" + RandString([]string{"s", ""}) + "://www." + url += DomainName() + + // Slugs + num := Number(1, 4) + slug := make([]string, num) + for i := 0; i < num; i++ { + slug[i] = BS() + } + url += "/" + strings.ToLower(strings.Join(slug, "/")) + + return url +} + +// HTTPMethod will generate a random http method +func HTTPMethod() string { + return getRandValue([]string{"internet", "http_method"}) +} + +// IPv4Address will generate a random version 4 ip address +func IPv4Address() string { + num := func() int { return 2 + rand.Intn(254) } + return fmt.Sprintf("%d.%d.%d.%d", num(), num(), num(), num()) +} + +// IPv6Address will generate a random version 6 ip address +func IPv6Address() string { + num := 65536 + return fmt.Sprintf("2001:cafe:%x:%x:%x:%x:%x:%x", rand.Intn(num), rand.Intn(num), rand.Intn(num), rand.Intn(num), rand.Intn(num), rand.Intn(num)) +} + +// Username will genrate a random username based upon picking a random lastname and random numbers at the end +func Username() string { + return getRandValue([]string{"person", "last"}) + replaceWithNumbers("####") +} diff --git a/vendor/github.com/brianvoe/gofakeit/job.go b/vendor/github.com/brianvoe/gofakeit/job.go new file mode 100644 index 00000000000..c156bde7724 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/job.go @@ -0,0 +1,34 @@ +package gofakeit + +// JobInfo is a struct of job information +type JobInfo struct { + Company string + Title string + Descriptor string + Level string +} + +// Job will generate a struct with random job information +func Job() *JobInfo { + return &JobInfo{ + Company: Company(), + Title: JobTitle(), + Descriptor: JobDescriptor(), + Level: JobLevel(), + } +} + +// JobTitle will generate a random job title string +func JobTitle() string { + return getRandValue([]string{"job", "title"}) +} + +// JobDescriptor will generate a random job descriptor string +func JobDescriptor() string { + return getRandValue([]string{"job", "descriptor"}) +} + +// JobLevel will generate a random job level string +func JobLevel() string { + return getRandValue([]string{"job", "level"}) +} diff --git a/vendor/github.com/brianvoe/gofakeit/log_level.go b/vendor/github.com/brianvoe/gofakeit/log_level.go new file mode 100644 index 00000000000..bde9bf31058 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/log_level.go @@ -0,0 +1,15 @@ +package gofakeit + +import ( + "github.com/brianvoe/gofakeit/data" +) + +// LogLevel will generate a random log level +// See data/LogLevels for list of available levels +func LogLevel(logType string) string { + if _, ok := data.LogLevels[logType]; ok { + return getRandValue([]string{"log_level", logType}) + } + + return getRandValue([]string{"log_level", "general"}) +} diff --git a/vendor/github.com/brianvoe/gofakeit/logo.png b/vendor/github.com/brianvoe/gofakeit/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..a97962030afda2145a035db6a01b2f3c256384fa GIT binary patch literal 36022 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 '9' { + return false + } + if i&1 == odd { + sum += t[c-'0'] + } else { + sum += int(c - '0') + } + } + return sum%10 == 0 +} diff --git a/vendor/github.com/brianvoe/gofakeit/person.go b/vendor/github.com/brianvoe/gofakeit/person.go new file mode 100644 index 00000000000..5fd6cbe22a1 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/person.go @@ -0,0 +1,45 @@ +package gofakeit + +import "strconv" + +// SSN will generate a random Social Security Number +func SSN() string { + return strconv.Itoa(randIntRange(100000000, 999999999)) +} + +// Gender will generate a random gender string +func Gender() string { + if Bool() == true { + return "male" + } + + return "female" +} + +// PersonInfo is a struct of person information +type PersonInfo struct { + FirstName string + LastName string + Gender string + SSN string + Image string + Job *JobInfo + Address *AddressInfo + Contact *ContactInfo + CreditCard *CreditCardInfo +} + +// Person will generate a struct with person information +func Person() *PersonInfo { + return &PersonInfo{ + FirstName: FirstName(), + LastName: LastName(), + Gender: Gender(), + SSN: SSN(), + Image: ImageURL(300, 300) + "/people", + Job: Job(), + Address: Address(), + Contact: Contact(), + CreditCard: CreditCard(), + } +} diff --git a/vendor/github.com/brianvoe/gofakeit/status_code.go b/vendor/github.com/brianvoe/gofakeit/status_code.go new file mode 100644 index 00000000000..1751c0fbe40 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/status_code.go @@ -0,0 +1,11 @@ +package gofakeit + +// SimpleStatusCode will generate a random simple status code +func SimpleStatusCode() int { + return getRandIntValue([]string{"status_code", "simple"}) +} + +// StatusCode will generate a random status code +func StatusCode() int { + return getRandIntValue([]string{"status_code", "general"}) +} diff --git a/vendor/github.com/brianvoe/gofakeit/string.go b/vendor/github.com/brianvoe/gofakeit/string.go new file mode 100644 index 00000000000..fc646cf38ac --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/string.go @@ -0,0 +1,48 @@ +package gofakeit + +import ( + "math/rand" +) + +// Letter will generate a single random lower case ASCII letter +func Letter() string { + return string(randLetter()) +} + +// Digit will generate a single ASCII digit +func Digit() string { + return string(randDigit()) +} + +// Lexify will replace ? will random generated letters +func Lexify(str string) string { + return replaceWithLetters(str) +} + +// ShuffleStrings will randomize a slice of strings +func ShuffleStrings(a []string) { + swap := func(i, j int) { + a[i], a[j] = a[j], a[i] + } + //to avoid upgrading to 1.10 I copied the algorithm + n := len(a) + if n <= 1 { + return + } + + //if size is > int32 probably it will never finish, or ran out of entropy + i := n - 1 + for ; i > 0; i-- { + j := int(rand.Int31n(int32(i + 1))) + swap(i, j) + } +} + +// RandString will take in a slice of string and return a randomly selected value +func RandString(a []string) string { + size := len(a) + if size == 0 { + return "" + } + return a[rand.Intn(size)] +} diff --git a/vendor/github.com/brianvoe/gofakeit/struct.go b/vendor/github.com/brianvoe/gofakeit/struct.go new file mode 100644 index 00000000000..2c68a9a3cb2 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/struct.go @@ -0,0 +1,87 @@ +package gofakeit + +import ( + "reflect" +) + +// Struct fills in exported elements of a struct with random data +// based on the value of `fake` tag of exported elements. +// Use `fake:"skip"` to explicitly skip an element. +// All built-in types are supported, with templating support +// for string types. +func Struct(v interface{}) { + r(reflect.TypeOf(v), reflect.ValueOf(v), "") +} + +func r(t reflect.Type, v reflect.Value, template string) { + switch t.Kind() { + case reflect.Ptr: + rPointer(t, v, template) + case reflect.Struct: + rStruct(t, v) + case reflect.String: + rString(template, v) + case reflect.Uint8: + v.SetUint(uint64(Uint8())) + case reflect.Uint16: + v.SetUint(uint64(Uint16())) + case reflect.Uint32: + v.SetUint(uint64(Uint32())) + case reflect.Uint64: + //capped at [0, math.MaxInt64) + v.SetUint(uint64(Uint64())) + case reflect.Int: + v.SetInt(int64(Int64())) + case reflect.Int8: + v.SetInt(int64(Int8())) + case reflect.Int16: + v.SetInt(int64(Int16())) + case reflect.Int32: + v.SetInt(int64(Int32())) + case reflect.Int64: + v.SetInt(int64(Int64())) + case reflect.Float64: + v.SetFloat(Float64()) + case reflect.Float32: + v.SetFloat(float64(Float32())) + case reflect.Bool: + v.SetBool(Bool()) + } +} + +func rString(template string, v reflect.Value) { + if template != "" { + r := Generate(template) + v.SetString(r) + } else { + v.SetString(Generate("???????????????????")) + // we don't have a String(len int) string function!! + } +} + +func rStruct(t reflect.Type, v reflect.Value) { + n := t.NumField() + for i := 0; i < n; i++ { + elementT := t.Field(i) + elementV := v.Field(i) + fake := true + t, ok := elementT.Tag.Lookup("fake") + if ok && t == "skip" { + fake = false + } + if fake && elementV.CanSet() { + r(elementT.Type, elementV, t) + } + } +} + +func rPointer(t reflect.Type, v reflect.Value, template string) { + elemT := t.Elem() + if v.IsNil() { + nv := reflect.New(elemT) + r(elemT, nv.Elem(), template) + v.Set(nv) + } else { + r(elemT, v.Elem(), template) + } +} diff --git a/vendor/github.com/brianvoe/gofakeit/unique.go b/vendor/github.com/brianvoe/gofakeit/unique.go new file mode 100644 index 00000000000..4b969a7e9b8 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/unique.go @@ -0,0 +1,34 @@ +package gofakeit + +import ( + "encoding/hex" + "math/rand" +) + +// UUID (version 4) will generate a random unique identifier based upon random nunbers +// Format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +func UUID() string { + version := byte(4) + uuid := make([]byte, 16) + rand.Read(uuid) + + // Set version + uuid[6] = (uuid[6] & 0x0f) | (version << 4) + + // Set variant + uuid[8] = (uuid[8] & 0xbf) | 0x80 + + buf := make([]byte, 36) + var dash byte = '-' + hex.Encode(buf[0:8], uuid[0:4]) + buf[8] = dash + hex.Encode(buf[9:13], uuid[4:6]) + buf[13] = dash + hex.Encode(buf[14:18], uuid[6:8]) + buf[18] = dash + hex.Encode(buf[19:23], uuid[8:10]) + buf[23] = dash + hex.Encode(buf[24:], uuid[10:]) + + return string(buf) +} diff --git a/vendor/github.com/brianvoe/gofakeit/user_agent.go b/vendor/github.com/brianvoe/gofakeit/user_agent.go new file mode 100644 index 00000000000..2ba33412145 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/user_agent.go @@ -0,0 +1,92 @@ +package gofakeit + +import "strconv" + +// UserAgent will generate a random broswer user agent +func UserAgent() string { + randNum := randIntRange(0, 4) + switch randNum { + case 0: + return ChromeUserAgent() + case 1: + return FirefoxUserAgent() + case 2: + return SafariUserAgent() + case 3: + return OperaUserAgent() + default: + return ChromeUserAgent() + } +} + +// ChromeUserAgent will generate a random chrome browser user agent string +func ChromeUserAgent() string { + randNum1 := strconv.Itoa(randIntRange(531, 536)) + strconv.Itoa(randIntRange(0, 2)) + randNum2 := strconv.Itoa(randIntRange(36, 40)) + randNum3 := strconv.Itoa(randIntRange(800, 899)) + return "Mozilla/5.0 " + "(" + randomPlatform() + ") AppleWebKit/" + randNum1 + " (KHTML, like Gecko) Chrome/" + randNum2 + ".0." + randNum3 + ".0 Mobile Safari/" + randNum1 +} + +// FirefoxUserAgent will generate a random firefox broswer user agent string +func FirefoxUserAgent() string { + ver := "Gecko/" + Date().Format("2006-02-01") + " Firefox/" + strconv.Itoa(randIntRange(35, 37)) + ".0" + platforms := []string{ + "(" + windowsPlatformToken() + "; " + "en-US" + "; rv:1.9." + strconv.Itoa(randIntRange(0, 3)) + ".20) " + ver, + "(" + linuxPlatformToken() + "; rv:" + strconv.Itoa(randIntRange(5, 8)) + ".0) " + ver, + "(" + macPlatformToken() + " rv:" + strconv.Itoa(randIntRange(2, 7)) + ".0) " + ver, + } + + return "Mozilla/5.0 " + RandString(platforms) +} + +// SafariUserAgent will generate a random safari browser user agent string +func SafariUserAgent() string { + randNum := strconv.Itoa(randIntRange(531, 536)) + "." + strconv.Itoa(randIntRange(1, 51)) + "." + strconv.Itoa(randIntRange(1, 8)) + ver := strconv.Itoa(randIntRange(4, 6)) + "." + strconv.Itoa(randIntRange(0, 2)) + + mobileDevices := []string{ + "iPhone; CPU iPhone OS", + "iPad; CPU OS", + } + + platforms := []string{ + "(Windows; U; " + windowsPlatformToken() + ") AppleWebKit/" + randNum + " (KHTML, like Gecko) Version/" + ver + " Safari/" + randNum, + "(" + macPlatformToken() + " rv:" + strconv.Itoa(randIntRange(4, 7)) + ".0; en-US) AppleWebKit/" + randNum + " (KHTML, like Gecko) Version/" + ver + " Safari/" + randNum, + "(" + RandString(mobileDevices) + " " + strconv.Itoa(randIntRange(7, 9)) + "_" + strconv.Itoa(randIntRange(0, 3)) + "_" + strconv.Itoa(randIntRange(1, 3)) + " like Mac OS X; " + "en-US" + ") AppleWebKit/" + randNum + " (KHTML, like Gecko) Version/" + strconv.Itoa(randIntRange(3, 5)) + ".0.5 Mobile/8B" + strconv.Itoa(randIntRange(111, 120)) + " Safari/6" + randNum, + } + + return "Mozilla/5.0 " + RandString(platforms) +} + +// OperaUserAgent will generate a random opera browser user agent string +func OperaUserAgent() string { + platform := "(" + randomPlatform() + "; en-US) Presto/2." + strconv.Itoa(randIntRange(8, 13)) + "." + strconv.Itoa(randIntRange(160, 355)) + " Version/" + strconv.Itoa(randIntRange(10, 13)) + ".00" + + return "Opera/" + strconv.Itoa(randIntRange(8, 10)) + "." + strconv.Itoa(randIntRange(10, 99)) + " " + platform +} + +// linuxPlatformToken will generate a random linux platform +func linuxPlatformToken() string { + return "X11; Linux " + getRandValue([]string{"computer", "linux_processor"}) +} + +// macPlatformToken will generate a random mac platform +func macPlatformToken() string { + return "Macintosh; " + getRandValue([]string{"computer", "mac_processor"}) + " Mac OS X 10_" + strconv.Itoa(randIntRange(5, 9)) + "_" + strconv.Itoa(randIntRange(0, 10)) +} + +// windowsPlatformToken will generate a random windows platform +func windowsPlatformToken() string { + return getRandValue([]string{"computer", "windows_platform"}) +} + +// randomPlatform will generate a random platform +func randomPlatform() string { + platforms := []string{ + linuxPlatformToken(), + macPlatformToken(), + windowsPlatformToken(), + } + + return RandString(platforms) +} diff --git a/vendor/github.com/brianvoe/gofakeit/vehicle.go b/vendor/github.com/brianvoe/gofakeit/vehicle.go new file mode 100644 index 00000000000..093fe3a1d84 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/vehicle.go @@ -0,0 +1,55 @@ +package gofakeit + +// VehicleInfo is a struct dataset of all vehicle information +type VehicleInfo struct { + // Vehicle type + VehicleType string + // Fuel type + Fuel string + // Transmission type + TransmissionGear string + // Brand name + Brand string + // Vehicle model + Model string + // Vehicle model year + Year int +} + +// Vehicle will generate a struct with vehicle information +func Vehicle() *VehicleInfo { + return &VehicleInfo{ + VehicleType: VehicleType(), + Fuel: FuelType(), + TransmissionGear: TransmissionGearType(), + Brand: CarMaker(), + Model: CarModel(), + Year: Year(), + } + +} + +// VehicleType will generate a random vehicle type string +func VehicleType() string { + return getRandValue([]string{"vehicle", "vehicle_type"}) +} + +// FuelType will return a random fuel type +func FuelType() string { + return getRandValue([]string{"vehicle", "fuel_type"}) +} + +// TransmissionGearType will return a random transmission gear type +func TransmissionGearType() string { + return getRandValue([]string{"vehicle", "transmission_type"}) +} + +// CarMaker will return a random car maker +func CarMaker() string { + return getRandValue([]string{"vehicle", "maker"}) +} + +// CarModel will return a random car model +func CarModel() string { + return getRandValue([]string{"vehicle", "model"}) +} diff --git a/vendor/github.com/brianvoe/gofakeit/words.go b/vendor/github.com/brianvoe/gofakeit/words.go new file mode 100644 index 00000000000..631e45c7ddd --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/words.go @@ -0,0 +1,100 @@ +package gofakeit + +import ( + "bytes" + "strings" + "unicode" +) + +type paragrapOptions struct { + paragraphCount int + sentenceCount int + wordCount int + separator string +} + +const bytesPerWordEstimation = 6 + +type sentenceGenerator func(wordCount int) string +type wordGenerator func() string + +// Word will generate a random word +func Word() string { + return getRandValue([]string{"lorem", "word"}) +} + +// Sentence will generate a random sentence +func Sentence(wordCount int) string { + return sentence(wordCount, Word) +} + +// Paragraph will generate a random paragraphGenerator +// Set Paragraph Count +// Set Sentence Count +// Set Word Count +// Set Paragraph Separator +func Paragraph(paragraphCount int, sentenceCount int, wordCount int, separator string) string { + return paragraphGenerator(paragrapOptions{paragraphCount, sentenceCount, wordCount, separator}, Sentence) +} + +func sentence(wordCount int, word wordGenerator) string { + if wordCount <= 0 { + return "" + } + + wordSeparator := ' ' + sentence := bytes.Buffer{} + sentence.Grow(wordCount * bytesPerWordEstimation) + + for i := 0; i < wordCount; i++ { + word := word() + if i == 0 { + runes := []rune(word) + runes[0] = unicode.ToTitle(runes[0]) + word = string(runes) + } + sentence.WriteString(word) + if i < wordCount-1 { + sentence.WriteRune(wordSeparator) + } + } + sentence.WriteRune('.') + return sentence.String() +} + +func paragraphGenerator(opts paragrapOptions, sentecer sentenceGenerator) string { + if opts.paragraphCount <= 0 || opts.sentenceCount <= 0 || opts.wordCount <= 0 { + return "" + } + + //to avoid making Go 1.10 dependency, we cannot use strings.Builder + paragraphs := bytes.Buffer{} + //we presume the length + paragraphs.Grow(opts.paragraphCount * opts.sentenceCount * opts.wordCount * bytesPerWordEstimation) + wordSeparator := ' ' + + for i := 0; i < opts.paragraphCount; i++ { + for e := 0; e < opts.sentenceCount; e++ { + paragraphs.WriteString(sentecer(opts.wordCount)) + if e < opts.sentenceCount-1 { + paragraphs.WriteRune(wordSeparator) + } + } + + if i < opts.paragraphCount-1 { + paragraphs.WriteString(opts.separator) + } + } + + return paragraphs.String() +} + +// Question will return a random question +func Question() string { + return strings.Replace(HipsterSentence(Number(3, 10)), ".", "?", 1) +} + +// Quote will return a random quote from a random person +func Quote() string { + return `"` + HipsterSentence(Number(3, 10)) + `" - ` + FirstName() + " " + LastName() +} diff --git a/vendor/github.com/robfig/cron/README.md b/vendor/github.com/robfig/cron/README.md index ec40c95fcb9..4e0ae1c25f3 100644 --- a/vendor/github.com/robfig/cron/README.md +++ b/vendor/github.com/robfig/cron/README.md @@ -1,4 +1,4 @@ -[![GoDoc](http://godoc.org/github.com/robfig/cron?status.png)](http://godoc.org/github.com/robfig/cron) +[![GoDoc](http://godoc.org/github.com/robfig/cron?status.png)](http://godoc.org/github.com/robfig/cron) [![Build Status](https://travis-ci.org/robfig/cron.svg?branch=master)](https://travis-ci.org/robfig/cron) # cron diff --git a/vendor/github.com/robfig/cron/doc.go b/vendor/github.com/robfig/cron/doc.go index d02ec2f3b56..1ce84f7bf46 100644 --- a/vendor/github.com/robfig/cron/doc.go +++ b/vendor/github.com/robfig/cron/doc.go @@ -84,7 +84,7 @@ You may use one of several pre-defined schedules in place of a cron expression. Intervals -You may also schedule a job to execute at fixed intervals, starting at the time it's added +You may also schedule a job to execute at fixed intervals, starting at the time it's added or cron is run. This is supported by formatting the cron spec like this: @every diff --git a/vendor/modules.txt b/vendor/modules.txt index 19e66874848..1cf623aa57d 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -56,6 +56,9 @@ github.com/benbjohnson/clock github.com/beorn7/perks/quantile # github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737 github.com/bradfitz/gomemcache/memcache +# github.com/brianvoe/gofakeit v3.17.0+incompatible +github.com/brianvoe/gofakeit +github.com/brianvoe/gofakeit/data # github.com/codegangsta/cli v1.20.0 github.com/codegangsta/cli # github.com/davecgh/go-spew v1.1.1