Files
phishingclub/backend/service/option.go
Ronni Skansing 311df47ee1 fix update options in settings
Signed-off-by: Ronni Skansing <rskansing@gmail.com>
2026-01-22 16:58:17 +01:00

234 lines
5.8 KiB
Go

package service
import (
"context"
"strconv"
"github.com/go-errors/errors"
"github.com/phishingclub/phishingclub/data"
"github.com/phishingclub/phishingclub/errs"
"github.com/phishingclub/phishingclub/model"
"github.com/phishingclub/phishingclub/repository"
"github.com/phishingclub/phishingclub/validate"
"github.com/phishingclub/phishingclub/vo"
"gorm.io/gorm"
)
// Option is a service for Option
type Option struct {
Common
OptionRepository *repository.Option
}
// GetOption gets an option
func (o *Option) GetOption(
ctx context.Context,
session *model.Session,
key string,
) (*model.Option, error) {
ae := NewAuditEvent("Option.GetOption", session)
// check permissions
isAuthorized, err := IsAuthorized(session, data.PERMISSION_ALLOW_GLOBAL)
if err != nil && !errors.Is(err, errs.ErrAuthorizationFailed) {
o.LogAuthError(err)
return nil, errs.Wrap(err)
}
if !isAuthorized {
o.AuditLogNotAuthorized(ae)
return nil, errs.ErrAuthorizationFailed
}
opt, err := o.OptionRepository.GetByKey(
ctx,
key,
)
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errs.Wrap(err)
}
if err != nil {
o.Logger.Errorw("failed to get option with key",
"key", key,
"error", err,
)
return nil, errs.Wrap(err)
}
return opt, nil
}
// GetOption gets an option
func (o *Option) GetOptionWithoutAuth(
ctx context.Context,
key string,
) (*model.Option, error) {
opt, err := o.OptionRepository.GetByKey(
ctx,
key,
)
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errs.Wrap(err)
}
if err != nil {
o.Logger.Errorw("failed to get option with key",
"key", key,
"error", err,
)
return nil, errs.Wrap(err)
}
return opt, nil
}
// MaskSSOSecret masks the sso secret
func (o *Option) MaskSSOSecret(opt *model.Option) (*model.Option, error) {
a, err := model.NewSSOOptionFromJSON([]byte(opt.Value.String()))
if err != nil {
o.Logger.Errorw("failed to read sso option", "error", err)
return nil, errs.Wrap(err)
}
// mask the key
a.ClientSecret = *vo.NewOptionalString1024Must("")
b, err := a.ToJSON()
if err != nil {
o.Logger.Errorw("failed to mask sso secret", "error", err)
return nil, errs.Wrap(err)
}
c, err := vo.NewOptionalString1MB(string(b))
if err != nil {
o.Logger.Errorw("failed to mask secret option", "error", err)
return nil, errs.Wrap(err)
}
opt.Value = *c
return opt, nil
}
// SetOptionByKey sets an option
func (o *Option) SetOptionByKey(
ctx context.Context,
session *model.Session,
option *model.Option,
) error {
ae := NewAuditEvent("Option.SetOptionByKey", session)
ae.Details["key"] = option.Key.String()
ae.Details["value"] = option.Value.String()
// check permissions
isAuthorized, err := IsAuthorized(session, data.PERMISSION_ALLOW_GLOBAL)
if err != nil && !errors.Is(err, errs.ErrAuthorizationFailed) {
o.LogAuthError(err)
return err
}
if !isAuthorized {
o.AuditLogNotAuthorized(ae)
return errs.ErrAuthorizationFailed
}
k := option.Key.String()
v := option.Value.String()
switch k {
case data.OptionKeyMaxFileUploadSizeMB:
if n, err := strconv.Atoi(v); err != nil || n <= 0 {
o.Logger.Debugw("invalid max file size",
"n", n,
"error", err,
)
return validate.WrapErrorWithField(
errs.NewValidationError(
errors.New("invalid max"),
),
"max file size",
)
}
case data.OptionKeyRepeatOffenderMonths:
if n, err := strconv.Atoi(v); err != nil || n <= 0 {
o.Logger.Debugw("invalid repeat offender months",
"n", n,
"error", err,
)
return validate.WrapErrorWithField(
errs.NewValidationError(
errors.New("invalid months"),
),
"repeat offender months",
)
}
case data.OptionKeyLogLevel:
// validate log level value
if v != "debug" && v != "info" && v != "warn" && v != "error" {
o.Logger.Debugw("invalid log level value",
"value", v,
)
return validate.WrapErrorWithField(
errs.NewValidationError(
errors.New("invalid log level"),
),
"log level",
)
}
case data.OptionKeyDBLogLevel:
// validate db log level value
if v != "silent" && v != "info" && v != "warn" && v != "error" {
o.Logger.Debugw("invalid db log level value",
"value", v,
)
return validate.WrapErrorWithField(
errs.NewValidationError(
errors.New("invalid db log level"),
),
"db log level",
)
}
case data.OptionKeyAdminSSOLogin:
// is allow listed
case data.OptionKeyDisplayMode:
// validate display mode value
if v != data.OptionValueDisplayModeWhitebox && v != data.OptionValueDisplayModeBlackbox {
o.Logger.Debugw("invalid display mode value",
"value", v,
)
return validate.WrapErrorWithField(
errs.NewValidationError(
errors.New("invalid display mode"),
),
"display mode",
)
}
case data.OptionKeyObfuscationTemplate:
// is allow listed
default:
o.Logger.Debugw("invalid settings key", "key", k)
return validate.WrapErrorWithField(
errs.NewValidationError(
errors.New("invalid option"),
),
"key",
)
}
// update options
err = o.OptionRepository.UpdateByKey(
ctx,
option,
)
if err != nil {
o.Logger.Errorw("failed to update option by key", "error", err)
return err
}
o.AuditLogAuthorized(ae)
return nil
}
// GetObfuscationTemplate gets the obfuscation template from options or returns default
func (o *Option) GetObfuscationTemplate(ctx context.Context) (string, error) {
opt, err := o.OptionRepository.GetByKey(ctx, data.OptionKeyObfuscationTemplate)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
// return default template if not found
return data.OptionValueObfuscationTemplateDefault, nil
}
o.Logger.Errorw("failed to get obfuscation template option", "error", err)
return "", errs.Wrap(err)
}
template := opt.Value.String()
if template == "" {
// return default if empty
return data.OptionValueObfuscationTemplateDefault, nil
}
return template, nil
}