package consoleauth import ( "context" "errors" "git.yuntuo.hk/k2safe/orbit/platform/auth" "net/netip" "time" ) // LoginPolicy 是所有节点统一配置的登录保护策略;计数及锁定保存在数据库。 type LoginPolicy struct { FailureLimit int // FailureLimit 失败窗口内允许的连续失败次数,达到后锁定。 FailureWindow time.Duration // FailureWindow 连续失败的统计窗口。 LockDuration time.Duration // LockDuration 临时锁定时长,锁定期间请求不续期。 AccountLimit int // AccountLimit 每个限流窗口最多接受的账号登录尝试数。 IPLimit int // IPLimit 每个限流窗口最多接受的来源 IP 登录尝试数。 RateWindow time.Duration // RateWindow 固定限流窗口;成功登录不清空流量计数。 AuditDays int // AuditDays 登录审计保留天数,与业务数据策略独立。 TrustedProxies []netip.Prefix // TrustedProxies 明确信任的反向代理网段;空时忽略所有转发头。 } func DefaultLoginPolicy() LoginPolicy { return LoginPolicy{FailureLimit: 5, FailureWindow: 15 * time.Minute, LockDuration: 15 * time.Minute, AccountLimit: 10, IPLimit: 30, RateWindow: time.Minute, AuditDays: 90} } func (p LoginPolicy) Validate() error { if p.FailureLimit < 1 || p.FailureLimit > 100 || p.AccountLimit < 1 || p.AccountLimit > 1000 || p.IPLimit < 1 || p.IPLimit > 10000 { return errors.New("console login limits out of range") } for _, d := range []time.Duration{p.FailureWindow, p.LockDuration, p.RateWindow} { if d < time.Second || d > 24*time.Hour { return errors.New("console login durations must be 1s–24h") } } if p.AuditDays < 1 || p.AuditDays > 3650 { return errors.New("console login audit retention must be 1–3650 days") } for _, n := range p.TrustedProxies { if !n.IsValid() { return errors.New("invalid console trusted proxy CIDR") } } return nil } // LoginBlocked 对外不区分账号是否存在,只给出拒绝类型和最短等待时间。 type LoginBlocked struct { Locked bool // Locked 表示临时锁定,否则为请求限流。 RetryAfter time.Duration // RetryAfter 按数据库时钟计算的剩余等待时间。 } func (*LoginBlocked) Error() string { return "console login temporarily blocked" } // LoginAuditEntry 是脱敏的登录审计;从不记录密码、Cookie、Token 或它们的摘要。 type LoginAuditEntry struct { ID uint64 `json:"id,string"` // ID 审计游标,按插入序号倒序分页。 Username string `json:"username"` // Username 提交的有界登录名,不证明账号存在。 RemoteIP string `json:"remote_ip"` // RemoteIP 经可信代理规则解析的来源地址。 UserAgent string `json:"user_agent"` // UserAgent 截断为最多 512 字节的浏览器信息。 Outcome string `json:"outcome"` // Outcome PENDING、SUCCESS、INVALID、LOCKED、ACCOUNT_LIMIT、IP_LIMIT 或 ERROR。 CreatedAt time.Time `json:"created_at"` // CreatedAt 尝试时间,UTC。 FinishedAt *time.Time `json:"finished_at,omitempty"` // FinishedAt 完成时间;空表示尚未确认结果。 RetryAt *time.Time `json:"retry_at,omitempty"` // RetryAt 拒绝时建议最早重试时间,UTC。 } // LoginAuditPage 固定每页 10 条,Next 是下一页排他上界;不承诺跨页快照。 type LoginAuditPage struct { Items []LoginAuditEntry `json:"items"` // Items 当前页脱敏记录。 Next string `json:"next,omitempty"` // Next 下一页游标,无更多时为空。 } type LoginAuditor interface { ListLoginAudit(context.Context, auth.Token, string) (LoginAuditPage, error) }