Commit 35f227de by Oleg Gaidarenko Committed by GitHub

Feature: LDAP refactoring (#16950)

* 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
parent 1a808851
......@@ -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
......
......@@ -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=
......
package extensions
import (
_ "github.com/brianvoe/gofakeit"
_ "github.com/gobwas/glob"
_ "github.com/robfig/cron"
_ "gopkg.in/square/go-jose.v2"
......
......@@ -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
}
......
......@@ -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",
......
......@@ -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
}
......@@ -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)
}
......@@ -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
}
......
......@@ -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)
}
......
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)
})
})
......
......@@ -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 (
......
package ldap
var (
hookDial func(*Auth) error
)
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)
})
})
}
......@@ -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")
})
})
}
......@@ -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
......
......@@ -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(),
}
......
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
}
......@@ -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
......
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
}
......@@ -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
......
# 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/
# Make a pull request and submit it and ill take a look at it. Thanks!
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
![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
<a href="https://www.buymeacoffee.com/brianvoe" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" style="height: auto !important;width: auto !important;" ></a>
### 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:<nil>
```
* 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.
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
}
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"
}
package gofakeit
// Bool will generate a random boolean value
func Bool() bool {
if randIntRange(0, 1) == 1 {
return true
}
return false
}
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)}
}
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"})
}
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)
}
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
}
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"},
}
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"},
}
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"},
}
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"},
}
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"},
}
package data
// Contact consists of contact information
var Contact = map[string][]string{
"phone": {"###-###-####", "(###)###-####", "1-###-###-####", "###.###.####"},
}
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"},
}
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,
}
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"},
}
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}!",
},
}
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"},
}
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"},
}
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"},
}
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"},
}
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"},
}
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##############",
},
}
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},
}
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"},
}
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)
}
/*
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
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)
}
}
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"})
}
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
}
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"})
}
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)
}
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)
}
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("####")
}
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"})
}
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"})
}
package gofakeit
import (
"math/rand"
"github.com/brianvoe/gofakeit/data"
)
const hashtag = '#'
const questionmark = '?'
// Check if in lib
func dataCheck(dataVal []string) bool {
var checkOk bool
if len(dataVal) == 2 {
_, checkOk = data.Data[dataVal[0]]
if checkOk {
_, checkOk = data.Data[dataVal[0]][dataVal[1]]
}
}
return checkOk
}
// Check if in lib
func intDataCheck(dataVal []string) bool {
if len(dataVal) != 2 {
return false
}
_, checkOk := data.IntData[dataVal[0]]
if checkOk {
_, checkOk = data.IntData[dataVal[0]][dataVal[1]]
}
return checkOk
}
// Get Random Value
func getRandValue(dataVal []string) string {
if !dataCheck(dataVal) {
return ""
}
return data.Data[dataVal[0]][dataVal[1]][rand.Intn(len(data.Data[dataVal[0]][dataVal[1]]))]
}
// Get Random Integer Value
func getRandIntValue(dataVal []string) int {
if !intDataCheck(dataVal) {
return 0
}
return data.IntData[dataVal[0]][dataVal[1]][rand.Intn(len(data.IntData[dataVal[0]][dataVal[1]]))]
}
// Replace # with numbers
func replaceWithNumbers(str string) string {
if str == "" {
return str
}
bytestr := []byte(str)
for i := 0; i < len(bytestr); i++ {
if bytestr[i] == hashtag {
bytestr[i] = byte(randDigit())
}
}
if bytestr[0] == '0' {
bytestr[0] = byte(rand.Intn(8)+1) + '0'
}
return string(bytestr)
}
// Replace ? with ASCII lowercase letters
func replaceWithLetters(str string) string {
if str == "" {
return str
}
bytestr := []byte(str)
for i := 0; i < len(bytestr); i++ {
if bytestr[i] == questionmark {
bytestr[i] = byte(randLetter())
}
}
return string(bytestr)
}
// Generate random lowercase ASCII letter
func randLetter() rune {
return rune(byte(rand.Intn(26)) + 'a')
}
// Generate random ASCII digit
func randDigit() rune {
return rune(byte(rand.Intn(10)) + '0')
}
// Generate random integer between min and max
func randIntRange(min, max int) int {
if min == max {
return min
}
return rand.Intn((max+1)-min) + min
}
func randFloat32Range(min, max float32) float32 {
if min == max {
return min
}
return rand.Float32()*(max-min) + min
}
func randFloat64Range(min, max float64) float64 {
if min == max {
return min
}
return rand.Float64()*(max-min) + min
}
// Categories will return a map string array of available data categories and sub categories
func Categories() map[string][]string {
types := make(map[string][]string)
for category, subCategoriesMap := range data.Data {
subCategories := make([]string, 0)
for subType := range subCategoriesMap {
subCategories = append(subCategories, subType)
}
types[category] = subCategories
}
return types
}
package gofakeit
// Name will generate a random First and Last Name
func Name() string {
return getRandValue([]string{"person", "first"}) + " " + getRandValue([]string{"person", "last"})
}
// FirstName will generate a random first name
func FirstName() string {
return getRandValue([]string{"person", "first"})
}
// LastName will generate a random last name
func LastName() string {
return getRandValue([]string{"person", "last"})
}
// NamePrefix will generate a random name prefix
func NamePrefix() string {
return getRandValue([]string{"person", "prefix"})
}
// NameSuffix will generate a random name suffix
func NameSuffix() string {
return getRandValue([]string{"person", "suffix"})
}
package gofakeit
import (
"math"
"math/rand"
)
// Number will generate a random number between given min And max
func Number(min int, max int) int {
return randIntRange(min, max)
}
// Uint8 will generate a random uint8 value
func Uint8() uint8 {
return uint8(randIntRange(0, math.MaxUint8))
}
// Uint16 will generate a random uint16 value
func Uint16() uint16 {
return uint16(randIntRange(0, math.MaxUint16))
}
// Uint32 will generate a random uint32 value
func Uint32() uint32 {
return uint32(randIntRange(0, math.MaxInt32))
}
// Uint64 will generate a random uint64 value
func Uint64() uint64 {
return uint64(rand.Int63n(math.MaxInt64))
}
// Int8 will generate a random Int8 value
func Int8() int8 {
return int8(randIntRange(math.MinInt8, math.MaxInt8))
}
// Int16 will generate a random int16 value
func Int16() int16 {
return int16(randIntRange(math.MinInt16, math.MaxInt16))
}
// Int32 will generate a random int32 value
func Int32() int32 {
return int32(randIntRange(math.MinInt32, math.MaxInt32))
}
// Int64 will generate a random int64 value
func Int64() int64 {
return rand.Int63n(math.MaxInt64) + math.MinInt64
}
// Float32 will generate a random float32 value
func Float32() float32 {
return randFloat32Range(math.SmallestNonzeroFloat32, math.MaxFloat32)
}
// Float32Range will generate a random float32 value between min and max
func Float32Range(min, max float32) float32 {
return randFloat32Range(min, max)
}
// Float64 will generate a random float64 value
func Float64() float64 {
return randFloat64Range(math.SmallestNonzeroFloat64, math.MaxFloat64)
}
// Float64Range will generate a random float64 value between min and max
func Float64Range(min, max float64) float64 {
return randFloat64Range(min, max)
}
// Numerify will replace # with random numerical values
func Numerify(str string) string {
return replaceWithNumbers(str)
}
// ShuffleInts will randomize a slice of ints
func ShuffleInts(a []int) {
for i := range a {
j := rand.Intn(i + 1)
a[i], a[j] = a[j], a[i]
}
}
package gofakeit
import (
"math/rand"
)
const lowerStr = "abcdefghijklmnopqrstuvwxyz"
const upperStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
const numericStr = "0123456789"
const specialStr = "!@#$%&*+-=?"
const spaceStr = " "
// Password will generate a random password
// Minimum number length of 5 if less than
func Password(lower bool, upper bool, numeric bool, special bool, space bool, num int) string {
// Make sure the num minimun is at least 5
if num < 5 {
num = 5
}
i := 0
b := make([]byte, num)
var passString string
if lower {
passString += lowerStr
b[i] = lowerStr[rand.Int63()%int64(len(lowerStr))]
i++
}
if upper {
passString += upperStr
b[i] = upperStr[rand.Int63()%int64(len(upperStr))]
i++
}
if numeric {
passString += numericStr
b[i] = numericStr[rand.Int63()%int64(len(numericStr))]
i++
}
if special {
passString += specialStr
b[i] = specialStr[rand.Int63()%int64(len(specialStr))]
i++
}
if space {
passString += spaceStr
b[i] = spaceStr[rand.Int63()%int64(len(spaceStr))]
i++
}
// Set default if empty
if passString == "" {
passString = lowerStr + numericStr
}
// Loop through and add it up
for i <= num-1 {
b[i] = passString[rand.Int63()%int64(len(passString))]
i++
}
// Shuffle bytes
for i := range b {
j := rand.Intn(i + 1)
b[i], b[j] = b[j], b[i]
}
return string(b)
}
package gofakeit
import "strconv"
import "time"
var currentYear = time.Now().Year() - 2000
// CreditCardInfo is a struct containing credit variables
type CreditCardInfo struct {
Type string
Number int
Exp string
Cvv string
}
// CreditCard will generate a struct full of credit card information
func CreditCard() *CreditCardInfo {
return &CreditCardInfo{
Type: CreditCardType(),
Number: CreditCardNumber(),
Exp: CreditCardExp(),
Cvv: CreditCardCvv(),
}
}
// CreditCardType will generate a random credit card type string
func CreditCardType() string {
return getRandValue([]string{"payment", "card_type"})
}
// CreditCardNumber will generate a random credit card number int
func CreditCardNumber() int {
integer, _ := strconv.Atoi(replaceWithNumbers(getRandValue([]string{"payment", "number"})))
return integer
}
// CreditCardNumberLuhn will generate a random credit card number int that passes luhn test
func CreditCardNumberLuhn() int {
cc := ""
for i := 0; i < 100000; i++ {
cc = replaceWithNumbers(getRandValue([]string{"payment", "number"}))
if luhn(cc) {
break
}
}
integer, _ := strconv.Atoi(cc)
return integer
}
// CreditCardExp will generate a random credit card expiration date string
// Exp date will always be a future date
func CreditCardExp() string {
month := strconv.Itoa(randIntRange(1, 12))
if len(month) == 1 {
month = "0" + month
}
return month + "/" + strconv.Itoa(randIntRange(currentYear+1, currentYear+10))
}
// CreditCardCvv will generate a random CVV number - Its a string because you could have 017 as an exp date
func CreditCardCvv() string {
return Numerify("###")
}
// luhn check is used for checking if credit card is valid
func luhn(s string) bool {
var t = [...]int{0, 2, 4, 6, 8, 1, 3, 5, 7, 9}
odd := len(s) & 1
var sum int
for i, c := range s {
if c < '0' || c > '9' {
return false
}
if i&1 == odd {
sum += t[c-'0']
} else {
sum += int(c - '0')
}
}
return sum%10 == 0
}
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(),
}
}
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"})
}
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)]
}
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)
}
}
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)
}
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)
}
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"})
}
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()
}
[![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
......
......@@ -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 <duration>
......
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment