mirror of
https://github.com/h44z/wg-portal.git
synced 2025-11-09 10:36:17 +00:00
add minimum password length check
This commit is contained in:
@@ -99,6 +99,8 @@ type Authenticator interface {
|
||||
LoggedIn(scopes ...Scope) func(next http.Handler) http.Handler
|
||||
// UserIdMatch checks if the user id in the session matches the user id in the request. If not, the request is aborted.
|
||||
UserIdMatch(idParameter string) func(next http.Handler) http.Handler
|
||||
// InfoOnly only add user info to the request context. No login check is performed.
|
||||
InfoOnly() func(next http.Handler) http.Handler
|
||||
}
|
||||
|
||||
type Session interface {
|
||||
|
||||
@@ -47,7 +47,7 @@ func (e ConfigEndpoint) RegisterRoutes(g *routegroup.Bundle) {
|
||||
apiGroup := g.Mount("/config")
|
||||
|
||||
apiGroup.HandleFunc("GET /frontend.js", e.handleConfigJsGet())
|
||||
apiGroup.HandleFunc("GET /settings", e.handleSettingsGet())
|
||||
apiGroup.With(e.authenticator.InfoOnly()).HandleFunc("GET /settings", e.handleSettingsGet())
|
||||
}
|
||||
|
||||
// handleConfigJsGet returns a gorm Handler function.
|
||||
@@ -108,6 +108,7 @@ func (e ConfigEndpoint) handleSettingsGet() http.HandlerFunc {
|
||||
SelfProvisioning: e.cfg.Core.SelfProvisioningAllowed,
|
||||
ApiAdminOnly: e.cfg.Advanced.ApiAdminOnly,
|
||||
WebAuthnEnabled: e.cfg.Auth.WebAuthn.Enabled,
|
||||
MinPasswordLength: e.cfg.Auth.MinPasswordLength,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +72,32 @@ func (h AuthenticationHandler) LoggedIn(scopes ...Scope) func(next http.Handler)
|
||||
}
|
||||
}
|
||||
|
||||
// InfoOnly only checks if the user is logged in and adds the user id to the context.
|
||||
// If the user is not logged in, the context user id is set to domain.CtxUnknownUserId.
|
||||
func (h AuthenticationHandler) InfoOnly() func(next http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
session := h.session.GetData(r.Context())
|
||||
|
||||
var newContext context.Context
|
||||
|
||||
if !session.LoggedIn {
|
||||
newContext = domain.SetUserInfo(r.Context(), domain.DefaultContextUserInfo())
|
||||
} else {
|
||||
newContext = domain.SetUserInfo(r.Context(), &domain.ContextUserInfo{
|
||||
Id: domain.UserIdentifier(session.UserIdentifier),
|
||||
IsAdmin: session.IsAdmin,
|
||||
})
|
||||
}
|
||||
|
||||
r = r.WithContext(newContext)
|
||||
|
||||
// Continue down the chain to Handler etc
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// UserIdMatch checks if the user id in the session matches the user id in the request. If not, the request is aborted.
|
||||
func (h AuthenticationHandler) UserIdMatch(idParameter string) func(next http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
|
||||
@@ -11,4 +11,5 @@ type Settings struct {
|
||||
SelfProvisioning bool `json:"SelfProvisioning"`
|
||||
ApiAdminOnly bool `json:"ApiAdminOnly"`
|
||||
WebAuthnEnabled bool `json:"WebAuthnEnabled"`
|
||||
MinPasswordLength int `json:"MinPasswordLength"`
|
||||
}
|
||||
|
||||
@@ -364,6 +364,10 @@ func (m Manager) validateModifications(ctx context.Context, old, new *domain.Use
|
||||
return errors.Join(fmt.Errorf("no access: %w", err), domain.ErrInvalidData)
|
||||
}
|
||||
|
||||
if err := new.HasWeakPassword(m.cfg.Auth.MinPasswordLength); err != nil {
|
||||
return errors.Join(fmt.Errorf("password too weak: %w", err), domain.ErrInvalidData)
|
||||
}
|
||||
|
||||
if currentUser.Id == old.Identifier && old.IsAdmin && !new.IsAdmin {
|
||||
return fmt.Errorf("cannot remove own admin rights: %w", domain.ErrInvalidData)
|
||||
}
|
||||
@@ -418,7 +422,11 @@ func (m Manager) validateCreation(ctx context.Context, new *domain.User) error {
|
||||
|
||||
// database users must have a password
|
||||
if new.Source == domain.UserSourceDatabase && string(new.Password) == "" {
|
||||
return fmt.Errorf("invalid password: %w", domain.ErrInvalidData)
|
||||
return fmt.Errorf("missing password: %w", domain.ErrInvalidData)
|
||||
}
|
||||
|
||||
if err := new.HasWeakPassword(m.cfg.Auth.MinPasswordLength); err != nil {
|
||||
return errors.Join(fmt.Errorf("password too weak: %w", err), domain.ErrInvalidData)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -18,6 +18,9 @@ type Auth struct {
|
||||
Ldap []LdapProvider `yaml:"ldap"`
|
||||
// Webauthn contains the configuration for the WebAuthn authenticator.
|
||||
WebAuthn WebauthnConfig `yaml:"webauthn"`
|
||||
// MinPasswordLength is the minimum password length for user accounts. This also applies to the admin user.
|
||||
// It is encouraged to set this value to at least 16 characters.
|
||||
MinPasswordLength int `yaml:"min_password_length"`
|
||||
}
|
||||
|
||||
// BaseFields contains the basic fields that are used to map user information from the authentication providers.
|
||||
|
||||
@@ -101,7 +101,7 @@ func defaultConfig() *Config {
|
||||
cfg := &Config{}
|
||||
|
||||
cfg.Core.AdminUser = "admin@wgportal.local"
|
||||
cfg.Core.AdminPassword = "wgportal"
|
||||
cfg.Core.AdminPassword = "wgportal-default"
|
||||
cfg.Core.AdminApiToken = "" // by default, the API access is disabled
|
||||
cfg.Core.ImportExisting = true
|
||||
cfg.Core.RestoreState = true
|
||||
@@ -165,6 +165,7 @@ func defaultConfig() *Config {
|
||||
cfg.Webhook.Timeout = 10 * time.Second
|
||||
|
||||
cfg.Auth.WebAuthn.Enabled = true
|
||||
cfg.Auth.MinPasswordLength = 16
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
@@ -88,6 +88,22 @@ func (u *User) CanChangePassword() error {
|
||||
return errors.New("password change only allowed for database source")
|
||||
}
|
||||
|
||||
func (u *User) HasWeakPassword(minLength int) error {
|
||||
if u.Source != UserSourceDatabase {
|
||||
return nil // password is not required for non-database users, so no check needed
|
||||
}
|
||||
|
||||
if u.Password == "" {
|
||||
return nil // password is not set, so no check needed
|
||||
}
|
||||
|
||||
if len(u.Password) < minLength {
|
||||
return fmt.Errorf("password is too short, minimum length is %d", minLength)
|
||||
}
|
||||
|
||||
return nil // password is strong enough
|
||||
}
|
||||
|
||||
func (u *User) EditAllowed(new *User) error {
|
||||
if u.Source == UserSourceDatabase {
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user