优化结构
This commit is contained in:
@@ -57,6 +57,9 @@ func NewAuthenticationUsecase(users *UserUsecase, security *SecurityUsecase, iss
|
||||
}
|
||||
|
||||
func (uc *AuthenticationUsecase) recordLogin(ctx context.Context, attempt *LoginAttempt, status bool, message string, userID uint) {
|
||||
if uc.audit == nil || attempt == nil {
|
||||
return
|
||||
}
|
||||
_ = uc.audit.RecordLogin(ctx, &LoginLog{Username: attempt.Username, IP: attempt.IP, Status: status, ErrorMessage: message, Agent: attempt.Agent, UserID: userID})
|
||||
}
|
||||
|
||||
@@ -65,7 +68,7 @@ func (uc *AuthenticationUsecase) Login(ctx context.Context, attempt *LoginAttemp
|
||||
if err != nil || config == nil {
|
||||
return nil, fmt.Errorf("%w: security config unavailable", ErrLoginState)
|
||||
}
|
||||
if config != nil && config.LockEnable {
|
||||
if config.LockEnable {
|
||||
locked, lockErr := uc.security.LoginLocked(ctx, attempt.Username)
|
||||
if lockErr != nil {
|
||||
return nil, fmt.Errorf("%w: %v", ErrLoginState, lockErr)
|
||||
@@ -77,17 +80,14 @@ func (uc *AuthenticationUsecase) Login(ctx context.Context, attempt *LoginAttemp
|
||||
}
|
||||
|
||||
ipTTL := time.Hour
|
||||
requireCaptcha := config == nil || config.CaptchaOpen == 0
|
||||
if config != nil {
|
||||
if config.CaptchaTimeout > 0 {
|
||||
ipTTL = time.Duration(config.CaptchaTimeout) * time.Second
|
||||
}
|
||||
failures, counterErr := uc.security.EnsureLoginIPCounter(ctx, attempt.IP, ipTTL)
|
||||
if counterErr != nil {
|
||||
return nil, fmt.Errorf("%w: %v", ErrLoginState, counterErr)
|
||||
}
|
||||
requireCaptcha = config.CaptchaOpen == 0 || failures > config.CaptchaOpen
|
||||
if config.CaptchaTimeout > 0 {
|
||||
ipTTL = time.Duration(config.CaptchaTimeout) * time.Second
|
||||
}
|
||||
failures, counterErr := uc.security.EnsureLoginIPCounter(ctx, attempt.IP, ipTTL)
|
||||
if counterErr != nil {
|
||||
return nil, fmt.Errorf("%w: %v", ErrLoginState, counterErr)
|
||||
}
|
||||
requireCaptcha := config.CaptchaOpen == 0 || failures > config.CaptchaOpen
|
||||
if requireCaptcha && !uc.security.VerifyCaptcha(ctx, attempt.CaptchaID, attempt.Captcha, true) {
|
||||
_, _ = uc.security.IncrementLoginIP(ctx, attempt.IP, ipTTL)
|
||||
uc.recordLogin(ctx, attempt, false, "验证码错误", 0)
|
||||
@@ -97,7 +97,7 @@ func (uc *AuthenticationUsecase) Login(ctx context.Context, attempt *LoginAttemp
|
||||
user, err := uc.users.Login(ctx, attempt.Username, attempt.Password)
|
||||
if err != nil {
|
||||
_, _ = uc.security.IncrementLoginIP(ctx, attempt.IP, ipTTL)
|
||||
if config != nil && config.LockEnable {
|
||||
if config.LockEnable {
|
||||
lockTTL := time.Duration(config.LockDuration) * time.Minute
|
||||
failures, stateErr := uc.security.IncrementLoginFailure(ctx, attempt.Username, lockTTL)
|
||||
if stateErr != nil {
|
||||
@@ -119,7 +119,7 @@ func (uc *AuthenticationUsecase) Login(ctx context.Context, attempt *LoginAttemp
|
||||
}
|
||||
|
||||
uc.security.ClearLoginState(ctx, attempt.Username)
|
||||
if config != nil && config.PwdExpireEnable && config.PwdExpireDays > 0 && user.PasswordUpdatedAt != nil && time.Now().After((*user.PasswordUpdatedAt).AddDate(0, 0, config.PwdExpireDays)) {
|
||||
if config.PwdExpireEnable && config.PwdExpireDays > 0 && user.PasswordUpdatedAt != nil && time.Now().After((*user.PasswordUpdatedAt).AddDate(0, 0, config.PwdExpireDays)) {
|
||||
user.MustChangePassword = true
|
||||
}
|
||||
issued, err := uc.issuer.IssueToken(user, user.AuthorityID, user.MustChangePassword, 0)
|
||||
|
||||
@@ -140,6 +140,11 @@ func TestLoginRecordsSuccessBeforeMultipointCacheFailure(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecordLoginAllowsMissingAuditRecorder(t *testing.T) {
|
||||
uc := &AuthenticationUsecase{}
|
||||
uc.recordLogin(context.Background(), &LoginAttempt{Username: "admin"}, false, "failed", 0)
|
||||
}
|
||||
|
||||
func TestSwitchAuthorityReissuesCurrentClaimsWithoutReloadingUser(t *testing.T) {
|
||||
repo := &switchAuthorityUserRepo{}
|
||||
issuer := &switchAuthorityIssuer{}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"go.einride.tech/aip/filtering"
|
||||
"go.einride.tech/aip/ordering"
|
||||
)
|
||||
|
||||
type ListOption func(*ListOptions)
|
||||
|
||||
type ListOptions struct {
|
||||
Filter filtering.Filter
|
||||
OrderBy ordering.OrderBy
|
||||
Offset int
|
||||
Limit int
|
||||
}
|
||||
|
||||
func ListFilter(filter filtering.Filter) ListOption {
|
||||
return func(o *ListOptions) {
|
||||
o.Filter = filter
|
||||
}
|
||||
}
|
||||
|
||||
func ListOrderBy(orderBy ordering.OrderBy) ListOption {
|
||||
return func(o *ListOptions) {
|
||||
o.OrderBy = orderBy
|
||||
}
|
||||
}
|
||||
|
||||
func ListOffset(offset int) ListOption {
|
||||
return func(o *ListOptions) {
|
||||
o.Offset = offset
|
||||
}
|
||||
}
|
||||
|
||||
func ListLimit(limit int) ListOption {
|
||||
return func(o *ListOptions) {
|
||||
o.Limit = limit
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package system
|
||||
|
||||
import "github.com/google/wire"
|
||||
|
||||
// ProviderSet wires the system domain usecases. The parent biz package keeps
|
||||
// the aggregate entry point used by Wire while each domain owns its providers.
|
||||
var ProviderSet = wire.NewSet(
|
||||
NewUserUsecase,
|
||||
NewAuthenticationUsecase,
|
||||
NewSystemConfigUsecase,
|
||||
NewAuthorityUsecase,
|
||||
NewAPIUsecase,
|
||||
NewPermissionUsecase,
|
||||
NewAccessControlUsecase,
|
||||
NewMenuUsecase,
|
||||
NewDepartmentUsecase,
|
||||
NewPositionUsecase,
|
||||
NewDictionaryUsecase,
|
||||
NewParameterUsecase,
|
||||
NewTokenUsecase,
|
||||
NewSecurityUsecase,
|
||||
NewVersionUsecase,
|
||||
NewExportUsecase,
|
||||
NewAuditUsecase,
|
||||
NewAuditRecorderUsecase,
|
||||
NewLogViewerUsecase,
|
||||
NewTaskUsecaseWithRegistry,
|
||||
NewTaskApplicationUsecase,
|
||||
NewMediaUsecase,
|
||||
NewAnnouncementUsecase,
|
||||
NewEmailUsecase,
|
||||
NewPaymentUsecase,
|
||||
NewIntegrationConfigUsecase,
|
||||
)
|
||||
@@ -30,12 +30,16 @@ type InitializationRepo interface {
|
||||
DiskMountPoints() []string
|
||||
}
|
||||
|
||||
type SystemConfigUsecase struct {
|
||||
repo InitializationRepo
|
||||
tasks TaskRuntime
|
||||
type TaskReloader interface {
|
||||
Reload(context.Context) error
|
||||
}
|
||||
|
||||
func NewSystemConfigUsecase(repo InitializationRepo, tasks TaskRuntime) *SystemConfigUsecase {
|
||||
type SystemConfigUsecase struct {
|
||||
repo InitializationRepo
|
||||
tasks TaskReloader
|
||||
}
|
||||
|
||||
func NewSystemConfigUsecase(repo InitializationRepo, tasks TaskReloader) *SystemConfigUsecase {
|
||||
return &SystemConfigUsecase{repo: repo, tasks: tasks}
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ func NewUserUsecase(repo UserRepo) *UserUsecase { return &UserUsecase{repo: repo
|
||||
|
||||
func (uc *UserUsecase) Login(ctx context.Context, username, password string) (*User, error) {
|
||||
u, err := uc.repo.FindUserByUsername(ctx, username)
|
||||
if err != nil || bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password)) != nil {
|
||||
if err != nil || u == nil || bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password)) != nil {
|
||||
return nil, ErrInvalidCredentials
|
||||
}
|
||||
uc.fallbackDefaultRouter(ctx, u)
|
||||
|
||||
@@ -12,6 +12,16 @@ type defaultRouterUserRepo struct {
|
||||
err error
|
||||
}
|
||||
|
||||
type loginUserRepo struct {
|
||||
UserRepo
|
||||
user *User
|
||||
err error
|
||||
}
|
||||
|
||||
func (r *loginUserRepo) FindUserByUsername(context.Context, string) (*User, error) {
|
||||
return r.user, r.err
|
||||
}
|
||||
|
||||
func (r *defaultRouterUserRepo) HasAuthorityMenu(context.Context, uint, string) (bool, error) {
|
||||
return r.hasMenu, r.err
|
||||
}
|
||||
@@ -35,3 +45,10 @@ func TestFallbackDefaultRouterMatchesMenuLookupOutcome(t *testing.T) {
|
||||
t.Fatalf("empty missing default route = %q", user.Authority.DefaultRouter)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoginRejectsMissingUserWithoutPanic(t *testing.T) {
|
||||
user, err := NewUserUsecase(&loginUserRepo{}).Login(context.Background(), "missing", "password")
|
||||
if !errors.Is(err, ErrInvalidCredentials) || user != nil {
|
||||
t.Fatalf("Login() = %#v, %v; want nil, ErrInvalidCredentials", user, err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user