Checkpoint: password reset, rule expiry, migrations
- Self-service and admin password-change endpoints
(PUT /v1/auth/password, PUT /v1/accounts/{id}/password)
- Policy rule time-scoped expiry (not_before / expires_at)
with migration 000006 and engine filtering
- golang-migrate integration; embedded SQL migrations
- PolicyRecord fieldalignment lint fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Engine wraps the stateless Evaluate function with an in-memory cache of
|
||||
@@ -31,11 +32,19 @@ func NewEngine() *Engine {
|
||||
// into a Rule. This prevents the database from injecting values into the ID or
|
||||
// Description fields that are stored as dedicated columns.
|
||||
func (e *Engine) SetRules(records []PolicyRecord) error {
|
||||
now := time.Now()
|
||||
rules := make([]Rule, 0, len(records))
|
||||
for _, rec := range records {
|
||||
if !rec.Enabled {
|
||||
continue
|
||||
}
|
||||
// Skip rules outside their validity window.
|
||||
if rec.NotBefore != nil && now.Before(*rec.NotBefore) {
|
||||
continue
|
||||
}
|
||||
if rec.ExpiresAt != nil && now.After(*rec.ExpiresAt) {
|
||||
continue
|
||||
}
|
||||
var body RuleBody
|
||||
if err := json.Unmarshal([]byte(rec.RuleJSON), &body); err != nil {
|
||||
return fmt.Errorf("policy: decode rule %d %q: %w", rec.ID, rec.Description, err)
|
||||
@@ -75,6 +84,8 @@ func (e *Engine) Evaluate(input PolicyInput) (Effect, *Rule) {
|
||||
// Using a local struct avoids importing the db or model packages from policy,
|
||||
// which would create a dependency cycle.
|
||||
type PolicyRecord struct {
|
||||
NotBefore *time.Time
|
||||
ExpiresAt *time.Time
|
||||
Description string
|
||||
RuleJSON string
|
||||
ID int64
|
||||
|
||||
Reference in New Issue
Block a user