mirror of
https://github.com/pocket-id/pocket-id.git
synced 2025-12-09 14:52:57 +03:00
204 lines
7.8 KiB
Go
204 lines
7.8 KiB
Go
package model
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"reflect"
|
|
"slices"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/pocket-id/pocket-id/backend/internal/common"
|
|
)
|
|
|
|
type AppConfigVariable struct {
|
|
Key string `gorm:"primaryKey;not null"`
|
|
Value string
|
|
}
|
|
|
|
// IsTrue returns true if the value is a truthy string, such as "true", "t", "yes", "1", etc.
|
|
func (a *AppConfigVariable) IsTrue() bool {
|
|
ok, _ := strconv.ParseBool(a.Value)
|
|
return ok
|
|
}
|
|
|
|
// AsDurationMinutes returns the value as a time.Duration, interpreting the string as a whole number of minutes.
|
|
func (a *AppConfigVariable) AsDurationMinutes() time.Duration {
|
|
val, err := strconv.Atoi(a.Value)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
return time.Duration(val) * time.Minute
|
|
}
|
|
|
|
type AppConfig struct {
|
|
// General
|
|
AppName AppConfigVariable `key:"appName,public"` // Public
|
|
SessionDuration AppConfigVariable `key:"sessionDuration"`
|
|
EmailsVerified AppConfigVariable `key:"emailsVerified"`
|
|
AccentColor AppConfigVariable `key:"accentColor,public"` // Public
|
|
DisableAnimations AppConfigVariable `key:"disableAnimations,public"` // Public
|
|
AllowOwnAccountEdit AppConfigVariable `key:"allowOwnAccountEdit,public"` // Public
|
|
AllowUserSignups AppConfigVariable `key:"allowUserSignups,public"` // Public
|
|
SignupDefaultUserGroupIDs AppConfigVariable `key:"signupDefaultUserGroupIDs"`
|
|
SignupDefaultCustomClaims AppConfigVariable `key:"signupDefaultCustomClaims"`
|
|
// Internal
|
|
InstanceID AppConfigVariable `key:"instanceId,internal"` // Internal
|
|
// Email
|
|
RequireUserEmail AppConfigVariable `key:"requireUserEmail,public"` // Public
|
|
SmtpHost AppConfigVariable `key:"smtpHost"`
|
|
SmtpPort AppConfigVariable `key:"smtpPort"`
|
|
SmtpFrom AppConfigVariable `key:"smtpFrom"`
|
|
SmtpUser AppConfigVariable `key:"smtpUser"`
|
|
SmtpPassword AppConfigVariable `key:"smtpPassword,sensitive"`
|
|
SmtpTls AppConfigVariable `key:"smtpTls"`
|
|
SmtpSkipCertVerify AppConfigVariable `key:"smtpSkipCertVerify"`
|
|
EmailLoginNotificationEnabled AppConfigVariable `key:"emailLoginNotificationEnabled"`
|
|
EmailOneTimeAccessAsUnauthenticatedEnabled AppConfigVariable `key:"emailOneTimeAccessAsUnauthenticatedEnabled,public"` // Public
|
|
EmailOneTimeAccessAsAdminEnabled AppConfigVariable `key:"emailOneTimeAccessAsAdminEnabled,public"` // Public
|
|
EmailApiKeyExpirationEnabled AppConfigVariable `key:"emailApiKeyExpirationEnabled"`
|
|
// LDAP
|
|
LdapEnabled AppConfigVariable `key:"ldapEnabled,public"` // Public
|
|
LdapUrl AppConfigVariable `key:"ldapUrl"`
|
|
LdapBindDn AppConfigVariable `key:"ldapBindDn"`
|
|
LdapBindPassword AppConfigVariable `key:"ldapBindPassword,sensitive"`
|
|
LdapBase AppConfigVariable `key:"ldapBase"`
|
|
LdapUserSearchFilter AppConfigVariable `key:"ldapUserSearchFilter"`
|
|
LdapUserGroupSearchFilter AppConfigVariable `key:"ldapUserGroupSearchFilter"`
|
|
LdapSkipCertVerify AppConfigVariable `key:"ldapSkipCertVerify"`
|
|
LdapAttributeUserUniqueIdentifier AppConfigVariable `key:"ldapAttributeUserUniqueIdentifier"`
|
|
LdapAttributeUserUsername AppConfigVariable `key:"ldapAttributeUserUsername"`
|
|
LdapAttributeUserEmail AppConfigVariable `key:"ldapAttributeUserEmail"`
|
|
LdapAttributeUserFirstName AppConfigVariable `key:"ldapAttributeUserFirstName"`
|
|
LdapAttributeUserLastName AppConfigVariable `key:"ldapAttributeUserLastName"`
|
|
LdapAttributeUserDisplayName AppConfigVariable `key:"ldapAttributeUserDisplayName"`
|
|
LdapAttributeUserProfilePicture AppConfigVariable `key:"ldapAttributeUserProfilePicture"`
|
|
LdapAttributeGroupMember AppConfigVariable `key:"ldapAttributeGroupMember"`
|
|
LdapAttributeGroupUniqueIdentifier AppConfigVariable `key:"ldapAttributeGroupUniqueIdentifier"`
|
|
LdapAttributeGroupName AppConfigVariable `key:"ldapAttributeGroupName"`
|
|
LdapAttributeAdminGroup AppConfigVariable `key:"ldapAttributeAdminGroup"`
|
|
LdapSoftDeleteUsers AppConfigVariable `key:"ldapSoftDeleteUsers"`
|
|
}
|
|
|
|
func (c *AppConfig) ToAppConfigVariableSlice(showAll bool, redactSensitiveValues bool) []AppConfigVariable {
|
|
// Use reflection to iterate through all fields
|
|
cfgValue := reflect.ValueOf(c).Elem()
|
|
cfgType := cfgValue.Type()
|
|
|
|
var res []AppConfigVariable
|
|
|
|
for i := range cfgType.NumField() {
|
|
field := cfgType.Field(i)
|
|
|
|
key, attrs, _ := strings.Cut(field.Tag.Get("key"), ",")
|
|
if key == "" {
|
|
continue
|
|
}
|
|
|
|
// If we're only showing public variables and this is not public, skip it
|
|
if !showAll && attrs != "public" {
|
|
continue
|
|
}
|
|
|
|
value := cfgValue.Field(i).FieldByName("Value").String()
|
|
|
|
// Redact sensitive values if the value isn't empty, the UI config is disabled, and redactSensitiveValues is true
|
|
if value != "" && common.EnvConfig.UiConfigDisabled && redactSensitiveValues && attrs == "sensitive" {
|
|
value = "XXXXXXXXXX"
|
|
}
|
|
|
|
appConfigVariable := AppConfigVariable{
|
|
Key: key,
|
|
Value: value,
|
|
}
|
|
|
|
res = append(res, appConfigVariable)
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
func (c *AppConfig) FieldByKey(key string) (defaultValue string, isInternal bool, err error) {
|
|
rv := reflect.ValueOf(c).Elem()
|
|
rt := rv.Type()
|
|
|
|
// Find the field in the struct whose "key" tag matches
|
|
for i := range rt.NumField() {
|
|
// Grab only the first part of the key, if there's a comma with additional properties
|
|
tagValue := strings.Split(rt.Field(i).Tag.Get("key"), ",")
|
|
keyFromTag := tagValue[0]
|
|
isInternal = slices.Contains(tagValue, "internal")
|
|
if keyFromTag != key {
|
|
continue
|
|
}
|
|
|
|
valueField := rv.Field(i).FieldByName("Value")
|
|
return valueField.String(), isInternal, nil
|
|
}
|
|
|
|
// If we are here, the config key was not found
|
|
return "", false, AppConfigKeyNotFoundError{field: key}
|
|
}
|
|
|
|
func (c *AppConfig) UpdateField(key string, value string, noInternal bool) error {
|
|
rv := reflect.ValueOf(c).Elem()
|
|
rt := rv.Type()
|
|
|
|
// Find the field in the struct whose "key" tag matches, then update that
|
|
for i := range rt.NumField() {
|
|
// Separate the key (before the comma) from any optional attributes after
|
|
tagValue, attrs, _ := strings.Cut(rt.Field(i).Tag.Get("key"), ",")
|
|
if tagValue != key {
|
|
continue
|
|
}
|
|
|
|
// If the field is internal and noInternal is true, we skip that
|
|
if noInternal && attrs == "internal" {
|
|
return AppConfigInternalForbiddenError{field: key}
|
|
}
|
|
|
|
valueField := rv.Field(i).FieldByName("Value")
|
|
if !valueField.CanSet() {
|
|
return fmt.Errorf("field Value in AppConfigVariable is not settable for config key '%s'", key)
|
|
}
|
|
|
|
// Update the value
|
|
valueField.SetString(value)
|
|
|
|
// Return once updated
|
|
return nil
|
|
}
|
|
|
|
// If we're here, we have not found the right field to update
|
|
return AppConfigKeyNotFoundError{field: key}
|
|
}
|
|
|
|
type AppConfigKeyNotFoundError struct {
|
|
field string
|
|
}
|
|
|
|
func (e AppConfigKeyNotFoundError) Error() string {
|
|
return "cannot find config key '" + e.field + "'"
|
|
}
|
|
|
|
func (e AppConfigKeyNotFoundError) Is(target error) bool {
|
|
// Ignore the field property when checking if an error is of the type AppConfigKeyNotFoundError
|
|
x := AppConfigKeyNotFoundError{}
|
|
return errors.As(target, &x)
|
|
}
|
|
|
|
type AppConfigInternalForbiddenError struct {
|
|
field string
|
|
}
|
|
|
|
func (e AppConfigInternalForbiddenError) Error() string {
|
|
return "field '" + e.field + "' is internal and can't be updated"
|
|
}
|
|
|
|
func (e AppConfigInternalForbiddenError) Is(target error) bool {
|
|
// Ignore the field property when checking if an error is of the type AppConfigInternalForbiddenError
|
|
x := AppConfigInternalForbiddenError{}
|
|
return errors.As(target, &x)
|
|
}
|