Add support for helper env-variable that create super users
This commit is contained in:
committed by
Mumbi Francis
parent
84fe246ea9
commit
a26b0dd606
+52
-45
@@ -39,7 +39,7 @@
|
||||
|
||||
###############################################################################
|
||||
# Allow insecure (invalid, expired TLS/SSL certificates) connections.
|
||||
#
|
||||
#
|
||||
# [IMPORTANT]
|
||||
# ====
|
||||
# We strongly recommend keeping this value set to false except for local development or demos.
|
||||
@@ -209,7 +209,7 @@
|
||||
|
||||
###############################################################################
|
||||
# Password for the web console endpoint. When running in dev environment, password is not required.
|
||||
#
|
||||
#
|
||||
# Corteza intentionally sets default password to random chars to prevent security incidents.
|
||||
# Type: string
|
||||
# Default: <no value>
|
||||
@@ -296,7 +296,7 @@
|
||||
# Email sending
|
||||
#
|
||||
# Configure your local SMTP server or use one of the available providers.
|
||||
#
|
||||
#
|
||||
# These values are copied to settings when the server starts and can be managed from the administration console.
|
||||
# We recommend you remove these values after they are copied to settings.
|
||||
# If server detects difference between these options and settings, it shows a warning in the log on server start.
|
||||
@@ -436,7 +436,7 @@
|
||||
|
||||
###############################################################################
|
||||
# Password security allows you to disable constraints to which passwords must conform to.
|
||||
#
|
||||
#
|
||||
# [CAUTION]
|
||||
# ====
|
||||
# Disabling password security can be useful for development environments as it removes the need for complex passwords.
|
||||
@@ -448,12 +448,12 @@
|
||||
|
||||
###############################################################################
|
||||
# Algoritm to be use for JWT signature.
|
||||
#
|
||||
#
|
||||
# Supported valus:
|
||||
# - HS256, HS384, HS512
|
||||
# - PS256, PS384, PS512,
|
||||
# - RS256, RS384, RS512
|
||||
#
|
||||
#
|
||||
# Provide shared secret string for HS256, HS384, HS512 and full private key or path to the file PS* and RS* algorithms.
|
||||
# Type: string
|
||||
# Default: HS512
|
||||
@@ -462,7 +462,7 @@
|
||||
###############################################################################
|
||||
# Secret used for signing JWT tokens.
|
||||
# Value is used only when HS256, HS384 or HS512 algorithm is used.
|
||||
#
|
||||
#
|
||||
# [IMPORTANT]
|
||||
# ====
|
||||
# If secret is not set, system auto-generates one from DB_DSN and HOSTNAME environment variables.
|
||||
@@ -486,7 +486,7 @@
|
||||
|
||||
###############################################################################
|
||||
# Lifetime of the refresh token. Should be much longer than lifetime of the access token.
|
||||
#
|
||||
#
|
||||
# Refresh tokens are used to exchange expired access tokens with new ones.
|
||||
# Type: time.Duration
|
||||
# Default: 72h
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
###############################################################################
|
||||
# Redirect URL to be sent with OAuth2 authentication request to provider
|
||||
#
|
||||
#
|
||||
# `provider` placeholder is replaced with the actual value when used.
|
||||
# Type: string
|
||||
# Default: <no value>
|
||||
@@ -502,7 +502,7 @@
|
||||
|
||||
###############################################################################
|
||||
# Secret used for securing cookies
|
||||
#
|
||||
#
|
||||
# [IMPORTANT]
|
||||
# ====
|
||||
# If secret is not set, system auto-generates one from DB_DSN and HOSTNAME environment variables.
|
||||
@@ -545,21 +545,21 @@
|
||||
|
||||
###############################################################################
|
||||
# Maximum time user is allowed to stay idle when logged in without "remember-me" option and before session is expired.
|
||||
#
|
||||
#
|
||||
# Recomended value is between an hour and a day.
|
||||
#
|
||||
#
|
||||
# [IMPORTANT]
|
||||
# ====
|
||||
# This affects only profile (/auth) pages. Using applications (admin, compose, ...) does not prolong the session.
|
||||
# ====
|
||||
#
|
||||
#
|
||||
# Type: time.Duration
|
||||
# Default: 24h
|
||||
# AUTH_SESSION_LIFETIME=24h
|
||||
|
||||
###############################################################################
|
||||
# Duration of the session in /auth lasts when user logs-in with "remember-me" option.
|
||||
#
|
||||
#
|
||||
# If set to 0, "remember-me" option is removed.
|
||||
# Type: time.Duration
|
||||
# Default: 8640h
|
||||
@@ -586,7 +586,7 @@
|
||||
|
||||
###############################################################################
|
||||
# Secret used for securing CSRF protection
|
||||
#
|
||||
#
|
||||
# [IMPORTANT]
|
||||
# ====
|
||||
# If secret is not set, system auto-generates one from DB_DSN and HOSTNAME environment variables.
|
||||
@@ -616,19 +616,19 @@
|
||||
|
||||
###############################################################################
|
||||
# Handle for OAuth2 client used for automatic redirect from /auth/oauth2/go endpoint.
|
||||
#
|
||||
#
|
||||
# This simplifies configuration for OAuth2 flow for Corteza Web applications as it removes
|
||||
# the need to suply redirection URL and client ID (oauth2/go endpoint does that internally)
|
||||
#
|
||||
#
|
||||
# Type: string
|
||||
# Default: corteza-webapp
|
||||
# AUTH_DEFAULT_CLIENT=corteza-webapp
|
||||
|
||||
###############################################################################
|
||||
# Path to js, css, images and template source files
|
||||
#
|
||||
#
|
||||
# When corteza starts, if path exists it tries to load template files from it.
|
||||
#
|
||||
#
|
||||
# When empty path is set (default value), embedded files are used.
|
||||
# Type: string
|
||||
# Default: <no value>
|
||||
@@ -637,12 +637,21 @@
|
||||
###############################################################################
|
||||
# When enabled, corteza reloads template before every execution.
|
||||
# Enable this for debugging or when developing auth templates.
|
||||
#
|
||||
#
|
||||
# Should be disabled in production where templates do not change between server restarts.
|
||||
# Type: bool
|
||||
# Default: <no value>
|
||||
# AUTH_DEVELOPMENT_MODE=<no value>
|
||||
|
||||
###############################################################################
|
||||
# When set, Corteza creates one or more users with the configured values using provided email as a password.
|
||||
# It skips existing (email, handle). All new users are assigned to all bypass roles.
|
||||
#
|
||||
# When set in production, Corteza stops and reports an error
|
||||
# Type: string
|
||||
# Default: <no value>
|
||||
# AUTH_PROVISION_SUPER_USER=<no value>
|
||||
|
||||
###############################################################################
|
||||
###############################################################################
|
||||
# Connection to Corredor
|
||||
@@ -815,17 +824,15 @@
|
||||
###############################################################################
|
||||
# List of compa delimited languages (language tags) to enable.
|
||||
# In case when an enabled language can not be loaded, error is logged.
|
||||
#
|
||||
#
|
||||
# When loading language configurations (config.xml) from the configured path(s).
|
||||
#
|
||||
#
|
||||
# Type: string
|
||||
# Default: en
|
||||
# LOCALE_LANGUAGES=en
|
||||
|
||||
###############################################################################
|
||||
# One or more paths to locale config and translation files, separated by colon
|
||||
#
|
||||
# When with LOCALE_DEVELOPMENT_MODE=true, default value for path is ../../locale
|
||||
# One or more paths to locale config and translation files, separated by colon
|
||||
# Type: string
|
||||
# Default: <no value>
|
||||
# LOCALE_PATH=<no value>
|
||||
@@ -834,7 +841,7 @@
|
||||
# Name of the query string parameter used to pass the language tag (it overrides Accept-Language header).
|
||||
# Set it to empty string to disable detection from the query string.
|
||||
# This parameter is ignored if only one language is enabled
|
||||
#
|
||||
#
|
||||
# Type: string
|
||||
# Default: lng
|
||||
# LOCALE_QUERY_STRING_PARAM=lng
|
||||
@@ -865,9 +872,9 @@
|
||||
|
||||
###############################################################################
|
||||
# Disables json format for logging and enables more human-readable output with colors.
|
||||
#
|
||||
#
|
||||
# Disable for production.
|
||||
#
|
||||
#
|
||||
# Type: bool
|
||||
# Default: <no value>
|
||||
# LOG_DEBUG=<no value>
|
||||
@@ -875,11 +882,11 @@
|
||||
###############################################################################
|
||||
# Minimum logging level. If set to "warn",
|
||||
# Levels warn, error, dpanic panic and fatal will be logged.
|
||||
#
|
||||
#
|
||||
# Recommended value for production: warn
|
||||
#
|
||||
#
|
||||
# Possible values: debug, info, warn, error, dpanic, panic, fatal
|
||||
#
|
||||
#
|
||||
# Type: string
|
||||
# Default: warn
|
||||
# LOG_LEVEL=warn
|
||||
@@ -887,25 +894,25 @@
|
||||
###############################################################################
|
||||
# Log filtering rules by level and name (log-level:log-namespace).
|
||||
# Please note that level (LOG_LEVEL) is applied before filter and it affects the final output!
|
||||
#
|
||||
#
|
||||
# Leave unset for production.
|
||||
#
|
||||
#
|
||||
# Example:
|
||||
# `warn+:* *:auth,workflow.*`
|
||||
# Log warnings, errors, panic, fatals. Everything from auth and workflow is logged.
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
# See more examples and documentation here: https://github.com/moul/zapfilter
|
||||
#
|
||||
#
|
||||
# Type: string
|
||||
# Default: <no value>
|
||||
# LOG_FILTER=<no value>
|
||||
|
||||
###############################################################################
|
||||
# Set to true to see where the logging was called from.
|
||||
#
|
||||
#
|
||||
# Disable for production.
|
||||
#
|
||||
#
|
||||
# Type: bool
|
||||
# Default: <no value>
|
||||
# LOG_INCLUDE_CALLER=<no value>
|
||||
@@ -913,9 +920,9 @@
|
||||
###############################################################################
|
||||
# Include stack-trace when logging at a specified level or below.
|
||||
# Disable for production.
|
||||
#
|
||||
#
|
||||
# Possible values: debug, info, warn, error, dpanic, panic, fatal
|
||||
#
|
||||
#
|
||||
# Type: string
|
||||
# Default: dpanic
|
||||
# LOG_STACKTRACE_LEVEL=dpanic
|
||||
@@ -1010,11 +1017,11 @@
|
||||
#
|
||||
# Provisioning allows you to configure a {PRODUCT_NAME} instance when deployed.
|
||||
# It occurs automatically after the {PRODUCT_NAME} server starts.
|
||||
#
|
||||
#
|
||||
# [IMPORTANT]
|
||||
# ====
|
||||
# We recommend you to keep provisioning enabled as it simplifies version updates by updating the database and updating settings.
|
||||
#
|
||||
#
|
||||
# If you're doing local development or some debugging, you can disable this.
|
||||
# ====
|
||||
#
|
||||
@@ -1040,7 +1047,7 @@
|
||||
# ====
|
||||
# These parameters help in the development and testing process.
|
||||
# When you are deploying to production, these should be disabled to improve performance and reduce storage usage.
|
||||
#
|
||||
#
|
||||
# You should configure external services such as Sentry or ELK to keep track of logs and error reports.
|
||||
# ====
|
||||
#
|
||||
@@ -1146,7 +1153,7 @@
|
||||
# Delay system startup
|
||||
#
|
||||
# You can configure these options to defer API execution until another external (HTTP) service is up and running.
|
||||
#
|
||||
#
|
||||
# [ TIP ]
|
||||
# ====
|
||||
# Delaying API execution can come in handy in complex setups where execution order is important.
|
||||
@@ -1170,7 +1177,7 @@
|
||||
###############################################################################
|
||||
# Space delimited list of hosts and/or URLs to probe.
|
||||
# Host format: `host` or `host:443` (port will default to 80).
|
||||
#
|
||||
#
|
||||
# [NOTE]
|
||||
# ====
|
||||
# Services are probed in parallel.
|
||||
|
||||
@@ -231,6 +231,14 @@ auth: schema.#optionsGroup & {
|
||||
Should be disabled in production where templates do not change between server restarts.
|
||||
"""
|
||||
}
|
||||
provision_super_user: {
|
||||
description: """
|
||||
When set, Corteza creates one or more users with the configured values using provided email as a password.
|
||||
It skips existing (email, handle). All new users are assigned to all bypass roles.
|
||||
|
||||
When set in production, Corteza stops and reports an error
|
||||
"""
|
||||
}
|
||||
}
|
||||
title: "Authentication"
|
||||
}
|
||||
|
||||
Generated
+1
@@ -123,6 +123,7 @@ type (
|
||||
DefaultClient string `env:"AUTH_DEFAULT_CLIENT"`
|
||||
AssetsPath string `env:"AUTH_ASSETS_PATH"`
|
||||
DevelopmentMode bool `env:"AUTH_DEVELOPMENT_MODE"`
|
||||
ProvisionSuperUser string `env:"AUTH_PROVISION_SUPER_USER"`
|
||||
}
|
||||
|
||||
CorredorOpt struct {
|
||||
|
||||
@@ -2,16 +2,30 @@ package provision
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
internalAuth "github.com/cortezaproject/corteza/server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza/server/pkg/handle"
|
||||
"github.com/cortezaproject/corteza/server/pkg/id"
|
||||
"github.com/cortezaproject/corteza/server/pkg/mail"
|
||||
"github.com/cortezaproject/corteza/server/pkg/options"
|
||||
"github.com/cortezaproject/corteza/server/system/service"
|
||||
"go.uber.org/zap"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/errors"
|
||||
"github.com/cortezaproject/corteza/server/store"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
)
|
||||
|
||||
var (
|
||||
nextID = func() uint64 {
|
||||
return id.Next()
|
||||
}
|
||||
)
|
||||
|
||||
// Sets email-related settings (if not set) under "auth.internal..."
|
||||
//
|
||||
//
|
||||
func emailSettings(ctx context.Context, s store.Storer) error {
|
||||
var (
|
||||
val, has = os.LookupEnv("SMTP_HOST")
|
||||
@@ -43,3 +57,112 @@ func emailSettings(ctx context.Context, s store.Storer) error {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// addAuthSuperUsers assigns BYPASS roles to users from AUTH_PROVISION_SUPER_USER value
|
||||
// When in Production, Corteza should stop and report an error.
|
||||
func addAuthSuperUsers(ctx context.Context, log *zap.Logger, s store.Storer, authOpt options.AuthOpt) (err error) {
|
||||
var (
|
||||
envOpt = options.Environment()
|
||||
)
|
||||
|
||||
if authOpt.ProvisionSuperUser == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if envOpt.IsProduction() {
|
||||
log.Warn(fmt.Sprint("when in production environment (ENVIRONMENT=production) you cannot provision " +
|
||||
"super users; set the environment to dev (ENVIRONMENT=dev) to provision super users"))
|
||||
return
|
||||
}
|
||||
|
||||
users := strings.Split(authOpt.ProvisionSuperUser, ";")
|
||||
|
||||
for _, usr := range users {
|
||||
u := prepareUser(usr)
|
||||
|
||||
//check if the email address is valid
|
||||
if !mail.IsValidAddress(u.Email) {
|
||||
log.Warn(fmt.Sprintf("Email address %s is invalid", u.Email))
|
||||
continue
|
||||
}
|
||||
|
||||
// skip existing email
|
||||
_, err = s.LookupUserByEmail(ctx, u.Email)
|
||||
if err != store.ErrNotFound {
|
||||
log.Warn(fmt.Sprintf("Email address already %s exists", u.Email))
|
||||
continue
|
||||
}
|
||||
|
||||
// skip existing handle
|
||||
_, err = s.LookupUserByHandle(ctx, u.Handle)
|
||||
if err != store.ErrNotFound {
|
||||
log.Warn(fmt.Sprintf("Handle %s already exists", u.Handle))
|
||||
continue
|
||||
}
|
||||
|
||||
err = store.Tx(ctx, s, func(ctx context.Context, s store.Storer) (err error) {
|
||||
if err = store.CreateUser(ctx, s, u); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = service.SetPasswordCredentials(ctx, s, u.ID, u.Email); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Warn(fmt.Sprintf("User {userID: %d, email: %s} created", u.ID, u.Email))
|
||||
|
||||
//assign the user a bypass role
|
||||
for _, r := range internalAuth.BypassRoles() {
|
||||
m := &types.RoleMember{UserID: u.ID, RoleID: r.ID}
|
||||
if err = store.CreateRoleMember(ctx, s, m); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// prepareUser creates and fills a new user depending on the number of arguments provided
|
||||
func prepareUser(user string) *types.User {
|
||||
u := &types.User{
|
||||
ID: nextID(),
|
||||
CreatedAt: *now(),
|
||||
}
|
||||
|
||||
usr := strings.Split(user, ",")
|
||||
u.Email = usr[0]
|
||||
u.EmailConfirmed = true
|
||||
u.Handle = createUserHandle(u)
|
||||
|
||||
if len(usr) > 1 {
|
||||
u.Handle = usr[1]
|
||||
}
|
||||
|
||||
if len(usr) > 2 {
|
||||
u.Name = usr[2]
|
||||
}
|
||||
|
||||
return u
|
||||
}
|
||||
|
||||
func createUserHandle(u *types.User) (hdl string) {
|
||||
hdl, _ = handle.Cast(
|
||||
func(lookup string) bool {
|
||||
return true
|
||||
},
|
||||
// use email w/o domain
|
||||
regexp.
|
||||
MustCompile("(@.*)$").
|
||||
ReplaceAllString(u.Email, ""),
|
||||
)
|
||||
|
||||
return hdl
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ func Run(ctx context.Context, log *zap.Logger, s store.Storer, provisionOpt opti
|
||||
func() error { return authAddExternals(ctx, log.Named("auth.externals"), s) },
|
||||
func() error { return oidcAutoDiscovery(ctx, log.Named("auth.oidc-auto-discovery"), s, authOpt) },
|
||||
func() error { return defaultAuthClient(ctx, log.Named("auth.clients"), s, authOpt) },
|
||||
func() error { return addAuthSuperUsers(ctx, log.Named("auth.super-users"), s, authOpt) },
|
||||
}
|
||||
|
||||
for _, fn := range ffn {
|
||||
|
||||
@@ -93,7 +93,7 @@ func Auth(opt AuthOptions) *auth {
|
||||
// 2.3. create credentials for that social login
|
||||
//
|
||||
// External login/signup does not:
|
||||
// - validate provider on profile, only uses it for matching credentials
|
||||
// - validate provider on profile, only uses it for matching credentials
|
||||
func (svc *auth) External(ctx context.Context, profile types.ExternalAuthUser) (u *types.User, err error) {
|
||||
var (
|
||||
authProvider = &types.AuthProvider{Provider: profile.Provider}
|
||||
|
||||
@@ -522,7 +522,7 @@ func (svc *auth) ChangePassword(ctx context.Context, userID uint64, oldPassword,
|
||||
return svc.recordAction(ctx, aam, AuthActionChangePassword, err)
|
||||
}
|
||||
|
||||
func (svc *auth) hashPassword(password string) (hash []byte, err error) {
|
||||
func hashPassword(password string) (hash []byte, err error) {
|
||||
return bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
}
|
||||
|
||||
@@ -532,20 +532,25 @@ func (svc *auth) CheckPasswordStrength(password string) bool {
|
||||
|
||||
// SetPasswordCredentials (soft) deletes old password entry and creates a new entry with new password on every change
|
||||
//
|
||||
// This way we can implement more strict password-change policies in the future
|
||||
// # This way we can implement more strict password-change policies in the future
|
||||
//
|
||||
// This method is used by auth and user procedures to unify password hashing and updating
|
||||
// credentials
|
||||
func (svc *auth) SetPasswordCredentials(ctx context.Context, userID uint64, password string) (err error) {
|
||||
if err = svc.removePasswordCredentials(ctx, userID); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return SetPasswordCredentials(ctx, svc.store, userID, password)
|
||||
}
|
||||
|
||||
// SetPasswordCredentials creates a new password entry
|
||||
func SetPasswordCredentials(ctx context.Context, s store.Storer, userID uint64, password string) (err error) {
|
||||
var (
|
||||
hash []byte
|
||||
)
|
||||
|
||||
if hash, err = svc.hashPassword(password); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = svc.removePasswordCredentials(ctx, userID); err != nil {
|
||||
if hash, err = hashPassword(password); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -558,7 +563,7 @@ func (svc *auth) SetPasswordCredentials(ctx context.Context, userID uint64, pass
|
||||
Credentials: string(hash),
|
||||
}
|
||||
|
||||
return store.CreateCredential(ctx, svc.store, c)
|
||||
return store.CreateCredential(ctx, s, c)
|
||||
}
|
||||
|
||||
//CheckPassword verifies if password matches any of the valid credentials
|
||||
@@ -568,18 +573,17 @@ func (svc *auth) SetPasswordCredentials(ctx context.Context, userID uint64, pass
|
||||
|
||||
// SetPassword sets new password for a user
|
||||
//
|
||||
// This function also records an action
|
||||
// # This function also records an action
|
||||
//
|
||||
// this method is used in 2 scenarios:
|
||||
//
|
||||
// SELF:
|
||||
// user forgot the password and needs to reset it
|
||||
// there should be protocols prior to this point that
|
||||
// authenticate and validate users
|
||||
//
|
||||
// USER MANAGEMENT:
|
||||
// administrator is resetting password for another user
|
||||
// SELF:
|
||||
// user forgot the password and needs to reset it
|
||||
// there should be protocols prior to this point that
|
||||
// authenticate and validate users
|
||||
//
|
||||
// USER MANAGEMENT:
|
||||
// administrator is resetting password for another user
|
||||
func (svc *auth) SetPassword(ctx context.Context, userID uint64, password string) (err error) {
|
||||
var (
|
||||
u *types.User
|
||||
@@ -1066,7 +1070,6 @@ func skipInvalid(c *types.Credential) bool {
|
||||
return c.Valid()
|
||||
}
|
||||
|
||||
//
|
||||
func compareHashedCredentials(password string) func(c *types.Credential) bool {
|
||||
var (
|
||||
p = []byte(password)
|
||||
|
||||
Reference in New Issue
Block a user