mirror of
https://github.com/miniflux/v2.git
synced 2025-08-11 17:51:01 +00:00
Refactor user validation
Validate each user field for creation/modification via API and web UI
This commit is contained in:
parent
291bf96d15
commit
e45cc2d2aa
40 changed files with 567 additions and 400 deletions
21
model/model.go
Normal file
21
model/model.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
// Copyright 2021 Frédéric Guillot. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package model // import "miniflux.app/model"
|
||||
|
||||
// OptionalString populates an optional string field.
|
||||
func OptionalString(value string) *string {
|
||||
if value != "" {
|
||||
return &value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// OptionalInt populates an optional int field.
|
||||
func OptionalInt(value int) *int {
|
||||
if value > 0 {
|
||||
return &value
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -4,8 +4,6 @@
|
|||
|
||||
package model // import "miniflux.app/model"
|
||||
|
||||
import "miniflux.app/errors"
|
||||
|
||||
// Themes returns the list of available themes.
|
||||
func Themes() map[string]string {
|
||||
return map[string]string{
|
||||
|
@ -29,14 +27,3 @@ func ThemeColor(theme string) string {
|
|||
return "#fff"
|
||||
}
|
||||
}
|
||||
|
||||
// ValidateTheme validates theme value.
|
||||
func ValidateTheme(theme string) error {
|
||||
for key := range Themes() {
|
||||
if key == theme {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return errors.NewLocalizedError("Invalid theme")
|
||||
}
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
// Copyright 2017 Frédéric Guillot. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package model // import "miniflux.app/model"
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestValidateTheme(t *testing.T) {
|
||||
for _, status := range []string{"light_serif", "dark_sans_serif", "system_serif"} {
|
||||
if err := ValidateTheme(status); err != nil {
|
||||
t.Error(`A valid theme should not generate any error`)
|
||||
}
|
||||
}
|
||||
|
||||
if err := ValidateTheme("invalid"); err == nil {
|
||||
t.Error(`An invalid theme should generate a error`)
|
||||
}
|
||||
}
|
108
model/user.go
108
model/user.go
|
@ -5,7 +5,6 @@
|
|||
package model // import "miniflux.app/model"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"miniflux.app/timezone"
|
||||
|
@ -15,7 +14,7 @@ import (
|
|||
type User struct {
|
||||
ID int64 `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Password string `json:"-"`
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
Theme string `json:"theme"`
|
||||
Language string `json:"language"`
|
||||
|
@ -28,56 +27,93 @@ type User struct {
|
|||
KeyboardShortcuts bool `json:"keyboard_shortcuts"`
|
||||
ShowReadingTime bool `json:"show_reading_time"`
|
||||
EntrySwipe bool `json:"entry_swipe"`
|
||||
LastLoginAt *time.Time `json:"last_login_at,omitempty"`
|
||||
LastLoginAt *time.Time `json:"last_login_at"`
|
||||
}
|
||||
|
||||
// NewUser returns a new User.
|
||||
func NewUser() *User {
|
||||
return &User{}
|
||||
// UserCreationRequest represents the request to create a user.
|
||||
type UserCreationRequest struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
GoogleID string `json:"google_id"`
|
||||
OpenIDConnectID string `json:"openid_connect_id"`
|
||||
}
|
||||
|
||||
// ValidateUserCreation validates new user.
|
||||
func (u User) ValidateUserCreation() error {
|
||||
if err := u.ValidateUserLogin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return u.ValidatePassword()
|
||||
// UserModificationRequest represents the request to update a user.
|
||||
type UserModificationRequest struct {
|
||||
Username *string `json:"username"`
|
||||
Password *string `json:"password"`
|
||||
Theme *string `json:"theme"`
|
||||
Language *string `json:"language"`
|
||||
Timezone *string `json:"timezone"`
|
||||
EntryDirection *string `json:"entry_sorting_direction"`
|
||||
Stylesheet *string `json:"stylesheet"`
|
||||
GoogleID *string `json:"google_id"`
|
||||
OpenIDConnectID *string `json:"openid_connect_id"`
|
||||
EntriesPerPage *int `json:"entries_per_page"`
|
||||
IsAdmin *bool `json:"is_admin"`
|
||||
KeyboardShortcuts *bool `json:"keyboard_shortcuts"`
|
||||
ShowReadingTime *bool `json:"show_reading_time"`
|
||||
EntrySwipe *bool `json:"entry_swipe"`
|
||||
}
|
||||
|
||||
// ValidateUserModification validates user modification payload.
|
||||
func (u User) ValidateUserModification() error {
|
||||
if u.Theme != "" {
|
||||
return ValidateTheme(u.Theme)
|
||||
// Patch updates the User object with the modification request.
|
||||
func (u *UserModificationRequest) Patch(user *User) {
|
||||
if u.Username != nil {
|
||||
user.Username = *u.Username
|
||||
}
|
||||
|
||||
if u.Password != "" {
|
||||
return u.ValidatePassword()
|
||||
if u.Password != nil {
|
||||
user.Password = *u.Password
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateUserLogin validates user credential requirements.
|
||||
func (u User) ValidateUserLogin() error {
|
||||
if u.Username == "" {
|
||||
return errors.New("The username is mandatory")
|
||||
if u.IsAdmin != nil {
|
||||
user.IsAdmin = *u.IsAdmin
|
||||
}
|
||||
|
||||
if u.Password == "" {
|
||||
return errors.New("The password is mandatory")
|
||||
if u.Theme != nil {
|
||||
user.Theme = *u.Theme
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidatePassword validates user password requirements.
|
||||
func (u User) ValidatePassword() error {
|
||||
if u.Password != "" && len(u.Password) < 6 {
|
||||
return errors.New("The password must have at least 6 characters")
|
||||
if u.Language != nil {
|
||||
user.Language = *u.Language
|
||||
}
|
||||
|
||||
return nil
|
||||
if u.Timezone != nil {
|
||||
user.Timezone = *u.Timezone
|
||||
}
|
||||
|
||||
if u.EntryDirection != nil {
|
||||
user.EntryDirection = *u.EntryDirection
|
||||
}
|
||||
|
||||
if u.Stylesheet != nil {
|
||||
user.Stylesheet = *u.Stylesheet
|
||||
}
|
||||
|
||||
if u.GoogleID != nil {
|
||||
user.GoogleID = *u.GoogleID
|
||||
}
|
||||
|
||||
if u.OpenIDConnectID != nil {
|
||||
user.OpenIDConnectID = *u.OpenIDConnectID
|
||||
}
|
||||
|
||||
if u.EntriesPerPage != nil {
|
||||
user.EntriesPerPage = *u.EntriesPerPage
|
||||
}
|
||||
|
||||
if u.KeyboardShortcuts != nil {
|
||||
user.KeyboardShortcuts = *u.KeyboardShortcuts
|
||||
}
|
||||
|
||||
if u.ShowReadingTime != nil {
|
||||
user.ShowReadingTime = *u.ShowReadingTime
|
||||
}
|
||||
|
||||
if u.EntrySwipe != nil {
|
||||
user.EntrySwipe = *u.EntrySwipe
|
||||
}
|
||||
}
|
||||
|
||||
// UseTimezone converts last login date to the given timezone.
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
// Copyright 2017 Frédéric Guillot. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package model // import "miniflux.app/model"
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestValidateUserCreation(t *testing.T) {
|
||||
user := &User{}
|
||||
if err := user.ValidateUserCreation(); err == nil {
|
||||
t.Error(`An empty user should generate an error`)
|
||||
}
|
||||
|
||||
user = &User{Username: "test", Password: ""}
|
||||
if err := user.ValidateUserCreation(); err == nil {
|
||||
t.Error(`User without password should generate an error`)
|
||||
}
|
||||
|
||||
user = &User{Username: "test", Password: "a"}
|
||||
if err := user.ValidateUserCreation(); err == nil {
|
||||
t.Error(`Passwords shorter than 6 characters should generate an error`)
|
||||
}
|
||||
|
||||
user = &User{Username: "", Password: "secret"}
|
||||
if err := user.ValidateUserCreation(); err == nil {
|
||||
t.Error(`An empty username should generate an error`)
|
||||
}
|
||||
|
||||
user = &User{Username: "test", Password: "secret"}
|
||||
if err := user.ValidateUserCreation(); err != nil {
|
||||
t.Error(`A valid user should not generate any error`)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateUserModification(t *testing.T) {
|
||||
user := &User{}
|
||||
if err := user.ValidateUserModification(); err != nil {
|
||||
t.Error(`There is no changes, so we should not have an error`)
|
||||
}
|
||||
|
||||
user = &User{Theme: "system_serif"}
|
||||
if err := user.ValidateUserModification(); err != nil {
|
||||
t.Error(`A valid theme should not generate any errors`)
|
||||
}
|
||||
|
||||
user = &User{Theme: "invalid theme"}
|
||||
if err := user.ValidateUserModification(); err == nil {
|
||||
t.Error(`An invalid theme should generate an error`)
|
||||
}
|
||||
|
||||
user = &User{Password: "test123"}
|
||||
if err := user.ValidateUserModification(); err != nil {
|
||||
t.Error(`A valid password should not generate any errors`)
|
||||
}
|
||||
|
||||
user = &User{Password: "a"}
|
||||
if err := user.ValidateUserModification(); err == nil {
|
||||
t.Error(`An invalid password should generate an error`)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue