support default feature restrictions after user registration

This commit is contained in:
MaysWind
2024-11-10 15:24:09 +08:00
parent 1f159bf826
commit 34b0b793ba
14 changed files with 275 additions and 195 deletions
+1
View File
@@ -769,6 +769,7 @@ func printUserInfo(user *models.User) {
fmt.Printf("[CurrencyDisplayType] %s (%d)\n", user.CurrencyDisplayType, user.CurrencyDisplayType) fmt.Printf("[CurrencyDisplayType] %s (%d)\n", user.CurrencyDisplayType, user.CurrencyDisplayType)
fmt.Printf("[ExpenseAmountColor] %s (%d)\n", user.ExpenseAmountColor, user.ExpenseAmountColor) fmt.Printf("[ExpenseAmountColor] %s (%d)\n", user.ExpenseAmountColor, user.ExpenseAmountColor)
fmt.Printf("[IncomeAmountColor] %s (%d)\n", user.IncomeAmountColor, user.IncomeAmountColor) fmt.Printf("[IncomeAmountColor] %s (%d)\n", user.IncomeAmountColor, user.IncomeAmountColor)
fmt.Printf("[FeatureRestriction] %s (%d)\n", user.FeatureRestriction, user.FeatureRestriction)
fmt.Printf("[Deleted] %t\n", user.Deleted) fmt.Printf("[Deleted] %t\n", user.Deleted)
fmt.Printf("[EmailVerified] %t\n", user.EmailVerified) fmt.Printf("[EmailVerified] %t\n", user.EmailVerified)
fmt.Printf("[CreatedAt] %s (%d)\n", utils.FormatUnixTimeToLongDateTimeInServerTimezone(user.CreatedUnixTime), user.CreatedUnixTime) fmt.Printf("[CreatedAt] %s (%d)\n", utils.FormatUnixTimeToLongDateTimeInServerTimezone(user.CreatedUnixTime), user.CreatedUnixTime)
+15
View File
@@ -217,6 +217,21 @@ avatar_provider = internal
# For "internal" avatar provider only, maximum allowed user avatar file size (1 - 4294967295 bytes) # For "internal" avatar provider only, maximum allowed user avatar file size (1 - 4294967295 bytes)
max_user_avatar_size = 1048576 max_user_avatar_size = 1048576
# The default feature restrictions after user registration (feature types separated by commas), leave blank for no restrictions
# Supports the following feature types:
# 1: Update Password
# 2: Update Email
# 3: Update Profile Basic Info
# 4: Update Avatar
# 5: Logout Other Session
# 6: Enable Two-Factor Authentication
# 7: Disable Enable Two-Factor Authentication
# 8: Forget Password
# 9: Import Transactions
# 10: Export Transactions
# 11: Clear All Data
default_feature_restrictions =
[data] [data]
# Set to true to allow users to export their data # Set to true to allow users to export their data
enable_export = true enable_export = true
+2 -2
View File
@@ -147,7 +147,7 @@ func (a *DataManagementsApi) ClearDataHandler(c *core.WebContext) (any, *errs.Er
return nil, errs.ErrUserPasswordWrong return nil, errs.ErrUserPasswordWrong
} }
if user.FeatureRestriction.Contains(models.USER_FEATURE_RESTRICTION_TYPE_CLEAR_ALL_DATA) { if user.FeatureRestriction.Contains(core.USER_FEATURE_RESTRICTION_TYPE_CLEAR_ALL_DATA) {
return nil, errs.ErrNotPermittedToPerformThisAction return nil, errs.ErrNotPermittedToPerformThisAction
} }
@@ -208,7 +208,7 @@ func (a *DataManagementsApi) getExportedFileContent(c *core.WebContext, fileType
return nil, "", errs.ErrUserNotFound return nil, "", errs.ErrUserNotFound
} }
if user.FeatureRestriction.Contains(models.USER_FEATURE_RESTRICTION_TYPE_EXPORT_TRANSACTION) { if user.FeatureRestriction.Contains(core.USER_FEATURE_RESTRICTION_TYPE_EXPORT_TRANSACTION) {
return nil, "", errs.ErrNotPermittedToPerformThisAction return nil, "", errs.ErrNotPermittedToPerformThisAction
} }
+2 -2
View File
@@ -56,7 +56,7 @@ func (a *ForgetPasswordsApi) UserForgetPasswordRequestHandler(c *core.WebContext
return nil, errs.ErrUserIsDisabled return nil, errs.ErrUserIsDisabled
} }
if user.FeatureRestriction.Contains(models.USER_FEATURE_RESTRICTION_TYPE_FORGET_PASSWORD) { if user.FeatureRestriction.Contains(core.USER_FEATURE_RESTRICTION_TYPE_FORGET_PASSWORD) {
return nil, errs.ErrNotPermittedToPerformThisAction return nil, errs.ErrNotPermittedToPerformThisAction
} }
@@ -113,7 +113,7 @@ func (a *ForgetPasswordsApi) UserResetPasswordHandler(c *core.WebContext) (any,
return nil, errs.ErrUserIsDisabled return nil, errs.ErrUserIsDisabled
} }
if user.FeatureRestriction.Contains(models.USER_FEATURE_RESTRICTION_TYPE_FORGET_PASSWORD) { if user.FeatureRestriction.Contains(core.USER_FEATURE_RESTRICTION_TYPE_FORGET_PASSWORD) {
return nil, errs.ErrNotPermittedToPerformThisAction return nil, errs.ErrNotPermittedToPerformThisAction
} }
+2 -2
View File
@@ -146,7 +146,7 @@ func (a *TokensApi) TokenRevokeHandler(c *core.WebContext) (any, *errs.Error) {
return nil, errs.ErrUserNotFound return nil, errs.ErrUserNotFound
} }
if user.FeatureRestriction.Contains(models.USER_FEATURE_RESTRICTION_TYPE_REVOKE_OTHER_SESSION) { if user.FeatureRestriction.Contains(core.USER_FEATURE_RESTRICTION_TYPE_REVOKE_OTHER_SESSION) {
return nil, errs.ErrNotPermittedToPerformThisAction return nil, errs.ErrNotPermittedToPerformThisAction
} }
} }
@@ -200,7 +200,7 @@ func (a *TokensApi) TokenRevokeAllHandler(c *core.WebContext) (any, *errs.Error)
return nil, errs.ErrUserNotFound return nil, errs.ErrUserNotFound
} }
if user.FeatureRestriction.Contains(models.USER_FEATURE_RESTRICTION_TYPE_REVOKE_OTHER_SESSION) { if user.FeatureRestriction.Contains(core.USER_FEATURE_RESTRICTION_TYPE_REVOKE_OTHER_SESSION) {
return nil, errs.ErrNotPermittedToPerformThisAction return nil, errs.ErrNotPermittedToPerformThisAction
} }
+2 -2
View File
@@ -1077,7 +1077,7 @@ func (a *TransactionsApi) TransactionParseImportFileHandler(c *core.WebContext)
return nil, errs.ErrUserNotFound return nil, errs.ErrUserNotFound
} }
if user.FeatureRestriction.Contains(models.USER_FEATURE_RESTRICTION_TYPE_IMPORT_TRANSACTION) { if user.FeatureRestriction.Contains(core.USER_FEATURE_RESTRICTION_TYPE_IMPORT_TRANSACTION) {
return nil, errs.ErrNotPermittedToPerformThisAction return nil, errs.ErrNotPermittedToPerformThisAction
} }
@@ -1205,7 +1205,7 @@ func (a *TransactionsApi) TransactionImportHandler(c *core.WebContext) (any, *er
return nil, errs.ErrUserNotFound return nil, errs.ErrUserNotFound
} }
if user.FeatureRestriction.Contains(models.USER_FEATURE_RESTRICTION_TYPE_IMPORT_TRANSACTION) { if user.FeatureRestriction.Contains(core.USER_FEATURE_RESTRICTION_TYPE_IMPORT_TRANSACTION) {
return nil, errs.ErrNotPermittedToPerformThisAction return nil, errs.ErrNotPermittedToPerformThisAction
} }
+3 -3
View File
@@ -81,7 +81,7 @@ func (a *TwoFactorAuthorizationsApi) TwoFactorEnableRequestHandler(c *core.WebCo
return nil, errs.ErrUserNotFound return nil, errs.ErrUserNotFound
} }
if user.FeatureRestriction.Contains(models.USER_FEATURE_RESTRICTION_TYPE_ENABLE_2FA) { if user.FeatureRestriction.Contains(core.USER_FEATURE_RESTRICTION_TYPE_ENABLE_2FA) {
return nil, errs.ErrNotPermittedToPerformThisAction return nil, errs.ErrNotPermittedToPerformThisAction
} }
@@ -145,7 +145,7 @@ func (a *TwoFactorAuthorizationsApi) TwoFactorEnableConfirmHandler(c *core.WebCo
return nil, errs.ErrUserNotFound return nil, errs.ErrUserNotFound
} }
if user.FeatureRestriction.Contains(models.USER_FEATURE_RESTRICTION_TYPE_ENABLE_2FA) { if user.FeatureRestriction.Contains(core.USER_FEATURE_RESTRICTION_TYPE_ENABLE_2FA) {
return nil, errs.ErrNotPermittedToPerformThisAction return nil, errs.ErrNotPermittedToPerformThisAction
} }
@@ -237,7 +237,7 @@ func (a *TwoFactorAuthorizationsApi) TwoFactorDisableHandler(c *core.WebContext)
return nil, errs.ErrUserNotFound return nil, errs.ErrUserNotFound
} }
if user.FeatureRestriction.Contains(models.USER_FEATURE_RESTRICTION_TYPE_DISABLE_2FA) { if user.FeatureRestriction.Contains(core.USER_FEATURE_RESTRICTION_TYPE_DISABLE_2FA) {
return nil, errs.ErrNotPermittedToPerformThisAction return nil, errs.ErrNotPermittedToPerformThisAction
} }
+6 -5
View File
@@ -79,6 +79,7 @@ func (a *UsersApi) UserRegisterHandler(c *core.WebContext) (any, *errs.Error) {
DefaultCurrency: userRegisterReq.DefaultCurrency, DefaultCurrency: userRegisterReq.DefaultCurrency,
FirstDayOfWeek: userRegisterReq.FirstDayOfWeek, FirstDayOfWeek: userRegisterReq.FirstDayOfWeek,
TransactionEditScope: models.TRANSACTION_EDIT_SCOPE_ALL, TransactionEditScope: models.TRANSACTION_EDIT_SCOPE_ALL,
FeatureRestriction: a.CurrentConfig().DefaultFeatureRestrictions,
} }
err = a.users.CreateUser(c, user) err = a.users.CreateUser(c, user)
@@ -259,7 +260,7 @@ func (a *UsersApi) UserUpdateProfileHandler(c *core.WebContext) (any, *errs.Erro
} }
if userUpdateReq.Email != "" && userUpdateReq.Email != user.Email { if userUpdateReq.Email != "" && userUpdateReq.Email != user.Email {
if user.FeatureRestriction.Contains(models.USER_FEATURE_RESTRICTION_TYPE_UPDATE_EMAIL) { if user.FeatureRestriction.Contains(core.USER_FEATURE_RESTRICTION_TYPE_UPDATE_EMAIL) {
return nil, errs.ErrNotPermittedToPerformThisAction return nil, errs.ErrNotPermittedToPerformThisAction
} }
@@ -269,7 +270,7 @@ func (a *UsersApi) UserUpdateProfileHandler(c *core.WebContext) (any, *errs.Erro
} }
if userUpdateReq.Password != "" { if userUpdateReq.Password != "" {
if user.FeatureRestriction.Contains(models.USER_FEATURE_RESTRICTION_TYPE_UPDATE_PASSWORD) { if user.FeatureRestriction.Contains(core.USER_FEATURE_RESTRICTION_TYPE_UPDATE_PASSWORD) {
return nil, errs.ErrNotPermittedToPerformThisAction return nil, errs.ErrNotPermittedToPerformThisAction
} }
@@ -438,7 +439,7 @@ func (a *UsersApi) UserUpdateProfileHandler(c *core.WebContext) (any, *errs.Erro
userNew.IncomeAmountColor = models.AMOUNT_COLOR_TYPE_INVALID userNew.IncomeAmountColor = models.AMOUNT_COLOR_TYPE_INVALID
} }
if modifyProfileBasicInfo && user.FeatureRestriction.Contains(models.USER_FEATURE_RESTRICTION_TYPE_UPDATE_PROFILE_BASIC_INFO) { if modifyProfileBasicInfo && user.FeatureRestriction.Contains(core.USER_FEATURE_RESTRICTION_TYPE_UPDATE_PROFILE_BASIC_INFO) {
return nil, errs.ErrNotPermittedToPerformThisAction return nil, errs.ErrNotPermittedToPerformThisAction
} }
@@ -554,7 +555,7 @@ func (a *UsersApi) UserUpdateAvatarHandler(c *core.WebContext) (any, *errs.Error
return nil, errs.ErrUserNotFound return nil, errs.ErrUserNotFound
} }
if user.FeatureRestriction.Contains(models.USER_FEATURE_RESTRICTION_TYPE_UPDATE_AVATAR) { if user.FeatureRestriction.Contains(core.USER_FEATURE_RESTRICTION_TYPE_UPDATE_AVATAR) {
return nil, errs.ErrNotPermittedToPerformThisAction return nil, errs.ErrNotPermittedToPerformThisAction
} }
@@ -621,7 +622,7 @@ func (a *UsersApi) UserRemoveAvatarHandler(c *core.WebContext) (any, *errs.Error
return nil, errs.ErrUserNotFound return nil, errs.ErrUserNotFound
} }
if user.FeatureRestriction.Contains(models.USER_FEATURE_RESTRICTION_TYPE_UPDATE_AVATAR) { if user.FeatureRestriction.Contains(core.USER_FEATURE_RESTRICTION_TYPE_UPDATE_AVATAR) {
return nil, errs.ErrNotPermittedToPerformThisAction return nil, errs.ErrNotPermittedToPerformThisAction
} }
+1
View File
@@ -88,6 +88,7 @@ func (l *UserDataCli) AddNewUser(c *core.CliContext, username string, email stri
DefaultCurrency: defaultCurrency, DefaultCurrency: defaultCurrency,
FirstDayOfWeek: core.WEEKDAY_SUNDAY, FirstDayOfWeek: core.WEEKDAY_SUNDAY,
TransactionEditScope: models.TRANSACTION_EDIT_SCOPE_ALL, TransactionEditScope: models.TRANSACTION_EDIT_SCOPE_ALL,
FeatureRestriction: l.CurrentConfig().DefaultFeatureRestrictions,
} }
err := l.users.CreateUser(c, user) err := l.users.CreateUser(c, user)
+120
View File
@@ -0,0 +1,120 @@
package core
import (
"fmt"
"strconv"
"strings"
)
// UserFeatureRestrictions represents all the restrictions of user features
type UserFeatureRestrictions uint64
// Add returns a new feature restrictions with the specified feature
func (r UserFeatureRestrictions) Add(featureRestrictionType UserFeatureRestrictionType) UserFeatureRestrictions {
typeValue := uint64(1 << (featureRestrictionType - 1))
return UserFeatureRestrictions(uint64(r) | typeValue)
}
// Remove returns a new feature restrictions without the specified feature
func (r UserFeatureRestrictions) Remove(featureRestrictionType UserFeatureRestrictionType) UserFeatureRestrictions {
typeValue := uint64(1 << (featureRestrictionType - 1))
return UserFeatureRestrictions(uint64(r) & (^typeValue))
}
// Contains returns whether contains the specified feature
func (r UserFeatureRestrictions) Contains(featureRestrictionType UserFeatureRestrictionType) bool {
typeValue := uint64(1 << (featureRestrictionType - 1))
return uint64(r)&typeValue == typeValue
}
// String returns a textual representation of all the restrictions of user features
func (r UserFeatureRestrictions) String() string {
builder := strings.Builder{}
for restrictionType := USER_FEATURE_RESTRICTION_TYPE_UPDATE_PASSWORD; restrictionType <= USER_FEATURE_RESTRICTION_TYPE_CLEAR_ALL_DATA; restrictionType++ {
if !r.Contains(restrictionType) {
continue
}
if builder.Len() > 0 {
builder.WriteRune(',')
}
builder.WriteString(restrictionType.String())
}
return builder.String()
}
// ParseUserFeatureRestrictions returns restrictions of user features according to the textual restrictions of user features separated by commas
func ParseUserFeatureRestrictions(featureRestrictions string) UserFeatureRestrictions {
if len(featureRestrictions) < 1 {
return 0
}
restrictions := uint64(0)
typeValues := strings.Split(featureRestrictions, ",")
for i := 0; i < len(typeValues); i++ {
value, err := strconv.ParseInt(typeValues[i], 10, 64)
if err != nil {
continue
}
if uint64(USER_FEATURE_RESTRICTION_TYPE_UPDATE_PASSWORD) <= uint64(value) && uint64(value) <= uint64(USER_FEATURE_RESTRICTION_TYPE_CLEAR_ALL_DATA) {
typeValue := uint64(1 << (value - 1))
restrictions = restrictions | typeValue
}
}
return UserFeatureRestrictions(restrictions)
}
// UserFeatureRestrictionType represents the restriction type of user features
type UserFeatureRestrictionType uint64
// User Feature Restriction Type
const (
USER_FEATURE_RESTRICTION_TYPE_UPDATE_PASSWORD UserFeatureRestrictionType = 1
USER_FEATURE_RESTRICTION_TYPE_UPDATE_EMAIL UserFeatureRestrictionType = 2
USER_FEATURE_RESTRICTION_TYPE_UPDATE_PROFILE_BASIC_INFO UserFeatureRestrictionType = 3
USER_FEATURE_RESTRICTION_TYPE_UPDATE_AVATAR UserFeatureRestrictionType = 4
USER_FEATURE_RESTRICTION_TYPE_REVOKE_OTHER_SESSION UserFeatureRestrictionType = 5
USER_FEATURE_RESTRICTION_TYPE_ENABLE_2FA UserFeatureRestrictionType = 6
USER_FEATURE_RESTRICTION_TYPE_DISABLE_2FA UserFeatureRestrictionType = 7
USER_FEATURE_RESTRICTION_TYPE_FORGET_PASSWORD UserFeatureRestrictionType = 8
USER_FEATURE_RESTRICTION_TYPE_IMPORT_TRANSACTION UserFeatureRestrictionType = 9
USER_FEATURE_RESTRICTION_TYPE_EXPORT_TRANSACTION UserFeatureRestrictionType = 10
USER_FEATURE_RESTRICTION_TYPE_CLEAR_ALL_DATA UserFeatureRestrictionType = 11
)
// String returns a textual representation of the restriction type of user features
func (t UserFeatureRestrictionType) String() string {
switch t {
case USER_FEATURE_RESTRICTION_TYPE_UPDATE_PASSWORD:
return "Update Password"
case USER_FEATURE_RESTRICTION_TYPE_UPDATE_EMAIL:
return "Update Email"
case USER_FEATURE_RESTRICTION_TYPE_UPDATE_PROFILE_BASIC_INFO:
return "Update Profile Basic Info"
case USER_FEATURE_RESTRICTION_TYPE_UPDATE_AVATAR:
return "Update Avatar"
case USER_FEATURE_RESTRICTION_TYPE_REVOKE_OTHER_SESSION:
return "Logout Other Session"
case USER_FEATURE_RESTRICTION_TYPE_ENABLE_2FA:
return "Enable Two-Factor Authentication"
case USER_FEATURE_RESTRICTION_TYPE_DISABLE_2FA:
return "Disable Enable Two-Factor Authentication"
case USER_FEATURE_RESTRICTION_TYPE_FORGET_PASSWORD:
return "Forget Password"
case USER_FEATURE_RESTRICTION_TYPE_IMPORT_TRANSACTION:
return "Import Transactions"
case USER_FEATURE_RESTRICTION_TYPE_EXPORT_TRANSACTION:
return "Export Transactions"
case USER_FEATURE_RESTRICTION_TYPE_CLEAR_ALL_DATA:
return "Clear All Data"
default:
return fmt.Sprintf("Invalid(%d)", int(t))
}
}
+118
View File
@@ -0,0 +1,118 @@
package core
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestUserFeatureRestrictionsAdd(t *testing.T) {
var featureRestrictions UserFeatureRestrictions
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_UPDATE_PASSWORD)
expectedValue := UserFeatureRestrictions(1)
assert.Equal(t, expectedValue, featureRestrictions)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_UPDATE_EMAIL)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_UPDATE_PROFILE_BASIC_INFO)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_UPDATE_AVATAR)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_REVOKE_OTHER_SESSION)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_ENABLE_2FA)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_DISABLE_2FA)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_FORGET_PASSWORD)
expectedValue = UserFeatureRestrictions(255)
assert.Equal(t, expectedValue, featureRestrictions)
}
func TestUserFeatureRestrictionsRemove(t *testing.T) {
var featureRestrictions UserFeatureRestrictions
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_UPDATE_PASSWORD)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_UPDATE_EMAIL)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_UPDATE_PROFILE_BASIC_INFO)
featureRestrictions = featureRestrictions.Remove(USER_FEATURE_RESTRICTION_TYPE_UPDATE_EMAIL)
featureRestrictions = featureRestrictions.Remove(USER_FEATURE_RESTRICTION_TYPE_UPDATE_PROFILE_BASIC_INFO)
featureRestrictions = featureRestrictions.Remove(USER_FEATURE_RESTRICTION_TYPE_UPDATE_AVATAR)
featureRestrictions = featureRestrictions.Remove(USER_FEATURE_RESTRICTION_TYPE_REVOKE_OTHER_SESSION)
expectedValue := UserFeatureRestrictions(1)
assert.Equal(t, expectedValue, featureRestrictions)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_UPDATE_AVATAR)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_REVOKE_OTHER_SESSION)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_ENABLE_2FA)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_DISABLE_2FA)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_FORGET_PASSWORD)
featureRestrictions = featureRestrictions.Remove(USER_FEATURE_RESTRICTION_TYPE_ENABLE_2FA)
featureRestrictions = featureRestrictions.Remove(USER_FEATURE_RESTRICTION_TYPE_DISABLE_2FA)
expectedValue = UserFeatureRestrictions(153)
assert.Equal(t, expectedValue, featureRestrictions)
}
func TestUserFeatureRestrictionsContains(t *testing.T) {
var featureRestrictions UserFeatureRestrictions
assert.False(t, featureRestrictions.Contains(USER_FEATURE_RESTRICTION_TYPE_UPDATE_PASSWORD))
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_UPDATE_PASSWORD)
assert.True(t, featureRestrictions.Contains(USER_FEATURE_RESTRICTION_TYPE_UPDATE_PASSWORD))
assert.False(t, featureRestrictions.Contains(USER_FEATURE_RESTRICTION_TYPE_UPDATE_PROFILE_BASIC_INFO))
}
func TestUserFeatureRestrictionsString(t *testing.T) {
var featureRestrictions UserFeatureRestrictions
expectedValue := ""
actualValue := featureRestrictions.String()
assert.Equal(t, expectedValue, actualValue)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_UPDATE_PASSWORD)
expectedValue = "Update Password"
actualValue = featureRestrictions.String()
assert.Equal(t, expectedValue, actualValue)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_FORGET_PASSWORD)
expectedValue = "Update Password,Forget Password"
actualValue = featureRestrictions.String()
assert.Equal(t, expectedValue, actualValue)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_UPDATE_EMAIL)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_UPDATE_PROFILE_BASIC_INFO)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_UPDATE_AVATAR)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_REVOKE_OTHER_SESSION)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_ENABLE_2FA)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_DISABLE_2FA)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_IMPORT_TRANSACTION)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_EXPORT_TRANSACTION)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_CLEAR_ALL_DATA)
expectedValue = "Update Password," +
"Update Email," +
"Update Profile Basic Info," +
"Update Avatar," +
"Logout Other Session," +
"Enable Two-Factor Authentication," +
"Disable Enable Two-Factor Authentication," +
"Forget Password," +
"Import Transactions," +
"Export Transactions," +
"Clear All Data"
actualValue = featureRestrictions.String()
assert.Equal(t, expectedValue, actualValue)
}
func TestParseUserFeatureRestrictions(t *testing.T) {
expectedValue := UserFeatureRestrictions(0)
actualValue := ParseUserFeatureRestrictions("")
assert.Equal(t, expectedValue, actualValue)
expectedValue = UserFeatureRestrictions(1)
actualValue = ParseUserFeatureRestrictions("1")
assert.Equal(t, expectedValue, actualValue)
expectedValue = UserFeatureRestrictions(1)
actualValue = ParseUserFeatureRestrictions("1,20")
assert.Equal(t, expectedValue, actualValue)
expectedValue = UserFeatureRestrictions(255)
actualValue = ParseUserFeatureRestrictions("1,2,3,4,5,6,7,8,20,21,22")
assert.Equal(t, expectedValue, actualValue)
expectedValue = UserFeatureRestrictions(255)
actualValue = ParseUserFeatureRestrictions("1,2,3,4,5,6,7,8,a,b,20")
assert.Equal(t, expectedValue, actualValue)
}
+1 -90
View File
@@ -2,7 +2,6 @@ package models
import ( import (
"fmt" "fmt"
"strings"
"time" "time"
"github.com/mayswind/ezbookkeeping/pkg/core" "github.com/mayswind/ezbookkeeping/pkg/core"
@@ -81,94 +80,6 @@ func (s AmountColorType) String() string {
} }
} }
// UserFeatureRestrictions represents all the restrictions of user features
type UserFeatureRestrictions int64
// Add returns a new feature restrictions with the specified feature
func (r UserFeatureRestrictions) Add(featureRestrictionType UserFeatureRestrictionType) UserFeatureRestrictions {
typeValue := int64(1 << (featureRestrictionType - 1))
return UserFeatureRestrictions(int64(r) | typeValue)
}
// Remove returns a new feature restrictions without the specified feature
func (r UserFeatureRestrictions) Remove(featureRestrictionType UserFeatureRestrictionType) UserFeatureRestrictions {
typeValue := int64(1 << (featureRestrictionType - 1))
return UserFeatureRestrictions(int64(r) & (^typeValue))
}
// Contains returns whether contains the specified feature
func (r UserFeatureRestrictions) Contains(featureRestrictionType UserFeatureRestrictionType) bool {
typeValue := int64(1 << (featureRestrictionType - 1))
return int64(r)&typeValue == typeValue
}
// String returns a textual representation of all the restrictions of user features
func (r UserFeatureRestrictions) String() string {
builder := strings.Builder{}
for restrictionType := USER_FEATURE_RESTRICTION_TYPE_UPDATE_PASSWORD; restrictionType <= USER_FEATURE_RESTRICTION_TYPE_CLEAR_ALL_DATA; restrictionType++ {
if !r.Contains(restrictionType) {
continue
}
if builder.Len() > 0 {
builder.WriteRune(',')
}
builder.WriteString(restrictionType.String())
}
return builder.String()
}
// UserFeatureRestrictionType represents the restriction type of user features
type UserFeatureRestrictionType int64
// User Feature Restriction Type
const (
USER_FEATURE_RESTRICTION_TYPE_UPDATE_PASSWORD UserFeatureRestrictionType = 1
USER_FEATURE_RESTRICTION_TYPE_UPDATE_EMAIL UserFeatureRestrictionType = 2
USER_FEATURE_RESTRICTION_TYPE_UPDATE_PROFILE_BASIC_INFO UserFeatureRestrictionType = 3
USER_FEATURE_RESTRICTION_TYPE_UPDATE_AVATAR UserFeatureRestrictionType = 4
USER_FEATURE_RESTRICTION_TYPE_REVOKE_OTHER_SESSION UserFeatureRestrictionType = 5
USER_FEATURE_RESTRICTION_TYPE_ENABLE_2FA UserFeatureRestrictionType = 6
USER_FEATURE_RESTRICTION_TYPE_DISABLE_2FA UserFeatureRestrictionType = 7
USER_FEATURE_RESTRICTION_TYPE_FORGET_PASSWORD UserFeatureRestrictionType = 8
USER_FEATURE_RESTRICTION_TYPE_IMPORT_TRANSACTION UserFeatureRestrictionType = 9
USER_FEATURE_RESTRICTION_TYPE_EXPORT_TRANSACTION UserFeatureRestrictionType = 10
USER_FEATURE_RESTRICTION_TYPE_CLEAR_ALL_DATA UserFeatureRestrictionType = 11
)
// String returns a textual representation of the restriction type of user features
func (t UserFeatureRestrictionType) String() string {
switch t {
case USER_FEATURE_RESTRICTION_TYPE_UPDATE_PASSWORD:
return "Update Password"
case USER_FEATURE_RESTRICTION_TYPE_UPDATE_EMAIL:
return "Update Email"
case USER_FEATURE_RESTRICTION_TYPE_UPDATE_PROFILE_BASIC_INFO:
return "Update Profile Basic Info"
case USER_FEATURE_RESTRICTION_TYPE_UPDATE_AVATAR:
return "Update Avatar"
case USER_FEATURE_RESTRICTION_TYPE_REVOKE_OTHER_SESSION:
return "Logout Other Session"
case USER_FEATURE_RESTRICTION_TYPE_ENABLE_2FA:
return "Enable Two-Factor Authentication"
case USER_FEATURE_RESTRICTION_TYPE_DISABLE_2FA:
return "Disable Enable Two-Factor Authentication"
case USER_FEATURE_RESTRICTION_TYPE_FORGET_PASSWORD:
return "Forget Password"
case USER_FEATURE_RESTRICTION_TYPE_IMPORT_TRANSACTION:
return "Import Transactions"
case USER_FEATURE_RESTRICTION_TYPE_EXPORT_TRANSACTION:
return "Export Transactions"
case USER_FEATURE_RESTRICTION_TYPE_CLEAR_ALL_DATA:
return "Clear All Data"
default:
return fmt.Sprintf("Invalid(%d)", int(t))
}
}
// User represents user data stored in database // User represents user data stored in database
type User struct { type User struct {
Uid int64 `xorm:"PK"` Uid int64 `xorm:"PK"`
@@ -193,7 +104,7 @@ type User struct {
CurrencyDisplayType core.CurrencyDisplayType `xorm:"TINYINT"` CurrencyDisplayType core.CurrencyDisplayType `xorm:"TINYINT"`
ExpenseAmountColor AmountColorType `xorm:"TINYINT"` ExpenseAmountColor AmountColorType `xorm:"TINYINT"`
IncomeAmountColor AmountColorType `xorm:"TINYINT"` IncomeAmountColor AmountColorType `xorm:"TINYINT"`
FeatureRestriction UserFeatureRestrictions FeatureRestriction core.UserFeatureRestrictions
Disabled bool Disabled bool
Deleted bool `xorm:"NOT NULL"` Deleted bool `xorm:"NOT NULL"`
EmailVerified bool `xorm:"NOT NULL"` EmailVerified bool `xorm:"NOT NULL"`
-89
View File
@@ -10,95 +10,6 @@ import (
"github.com/mayswind/ezbookkeeping/pkg/utils" "github.com/mayswind/ezbookkeeping/pkg/utils"
) )
func TestUserFeatureRestrictionsAdd(t *testing.T) {
var featureRestrictions UserFeatureRestrictions
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_UPDATE_PASSWORD)
expectedValue := UserFeatureRestrictions(1)
assert.Equal(t, expectedValue, featureRestrictions)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_UPDATE_EMAIL)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_UPDATE_PROFILE_BASIC_INFO)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_UPDATE_AVATAR)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_REVOKE_OTHER_SESSION)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_ENABLE_2FA)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_DISABLE_2FA)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_FORGET_PASSWORD)
expectedValue = UserFeatureRestrictions(255)
assert.Equal(t, expectedValue, featureRestrictions)
}
func TestUserFeatureRestrictionsRemove(t *testing.T) {
var featureRestrictions UserFeatureRestrictions
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_UPDATE_PASSWORD)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_UPDATE_EMAIL)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_UPDATE_PROFILE_BASIC_INFO)
featureRestrictions = featureRestrictions.Remove(USER_FEATURE_RESTRICTION_TYPE_UPDATE_EMAIL)
featureRestrictions = featureRestrictions.Remove(USER_FEATURE_RESTRICTION_TYPE_UPDATE_PROFILE_BASIC_INFO)
featureRestrictions = featureRestrictions.Remove(USER_FEATURE_RESTRICTION_TYPE_UPDATE_AVATAR)
featureRestrictions = featureRestrictions.Remove(USER_FEATURE_RESTRICTION_TYPE_REVOKE_OTHER_SESSION)
expectedValue := UserFeatureRestrictions(1)
assert.Equal(t, expectedValue, featureRestrictions)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_UPDATE_AVATAR)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_REVOKE_OTHER_SESSION)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_ENABLE_2FA)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_DISABLE_2FA)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_FORGET_PASSWORD)
featureRestrictions = featureRestrictions.Remove(USER_FEATURE_RESTRICTION_TYPE_ENABLE_2FA)
featureRestrictions = featureRestrictions.Remove(USER_FEATURE_RESTRICTION_TYPE_DISABLE_2FA)
expectedValue = UserFeatureRestrictions(153)
assert.Equal(t, expectedValue, featureRestrictions)
}
func TestUserFeatureRestrictionsContains(t *testing.T) {
var featureRestrictions UserFeatureRestrictions
assert.False(t, featureRestrictions.Contains(USER_FEATURE_RESTRICTION_TYPE_UPDATE_PASSWORD))
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_UPDATE_PASSWORD)
assert.True(t, featureRestrictions.Contains(USER_FEATURE_RESTRICTION_TYPE_UPDATE_PASSWORD))
assert.False(t, featureRestrictions.Contains(USER_FEATURE_RESTRICTION_TYPE_UPDATE_PROFILE_BASIC_INFO))
}
func TestUserFeatureRestrictionsString(t *testing.T) {
var featureRestrictions UserFeatureRestrictions
expectedValue := ""
actualValue := featureRestrictions.String()
assert.Equal(t, expectedValue, actualValue)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_UPDATE_PASSWORD)
expectedValue = "Update Password"
actualValue = featureRestrictions.String()
assert.Equal(t, expectedValue, actualValue)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_FORGET_PASSWORD)
expectedValue = "Update Password,Forget Password"
actualValue = featureRestrictions.String()
assert.Equal(t, expectedValue, actualValue)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_UPDATE_EMAIL)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_UPDATE_PROFILE_BASIC_INFO)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_UPDATE_AVATAR)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_REVOKE_OTHER_SESSION)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_ENABLE_2FA)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_DISABLE_2FA)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_IMPORT_TRANSACTION)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_EXPORT_TRANSACTION)
featureRestrictions = featureRestrictions.Add(USER_FEATURE_RESTRICTION_TYPE_CLEAR_ALL_DATA)
expectedValue = "Update Password," +
"Update Email," +
"Update Profile Basic Info," +
"Update Avatar," +
"Logout Other Session," +
"Enable Two-Factor Authentication," +
"Disable Enable Two-Factor Authentication," +
"Forget Password," +
"Import Transactions," +
"Export Transactions," +
"Clear All Data"
actualValue = featureRestrictions.String()
assert.Equal(t, expectedValue, actualValue)
}
func TestUserCanEditTransactionByTransactionTime_ScopeIsNone(t *testing.T) { func TestUserCanEditTransactionByTransactionTime_ScopeIsNone(t *testing.T) {
user := &User{ user := &User{
TransactionEditScope: TRANSACTION_EDIT_SCOPE_NONE, TransactionEditScope: TRANSACTION_EDIT_SCOPE_NONE,
+2
View File
@@ -282,6 +282,7 @@ type Config struct {
EnableScheduledTransaction bool EnableScheduledTransaction bool
AvatarProvider core.UserAvatarProviderType AvatarProvider core.UserAvatarProviderType
MaxAvatarFileSize uint32 MaxAvatarFileSize uint32
DefaultFeatureRestrictions core.UserFeatureRestrictions
// Data // Data
EnableDataExport bool EnableDataExport bool
@@ -766,6 +767,7 @@ func loadUserConfiguration(config *Config, configFile *ini.File, sectionName str
} }
config.MaxAvatarFileSize = getConfigItemUint32Value(configFile, sectionName, "max_user_avatar_size", defaultUserAvatarFileMaxSize) config.MaxAvatarFileSize = getConfigItemUint32Value(configFile, sectionName, "max_user_avatar_size", defaultUserAvatarFileMaxSize)
config.DefaultFeatureRestrictions = core.ParseUserFeatureRestrictions(getConfigItemStringValue(configFile, sectionName, "default_feature_restrictions", ""))
return nil return nil
} }