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:
@@ -7,6 +7,7 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.wntrmute.dev/kyle/mcias/internal/db"
|
||||
"git.wntrmute.dev/kyle/mcias/internal/model"
|
||||
@@ -70,7 +71,7 @@ func (u *UIServer) handlePoliciesPage(w http.ResponseWriter, r *http.Request) {
|
||||
// policyRuleToView converts a DB record to a template-friendly view.
|
||||
func policyRuleToView(rec *model.PolicyRuleRecord) *PolicyRuleView {
|
||||
pretty := prettyJSONStr(rec.RuleJSON)
|
||||
return &PolicyRuleView{
|
||||
v := &PolicyRuleView{
|
||||
ID: rec.ID,
|
||||
Priority: rec.Priority,
|
||||
Description: rec.Description,
|
||||
@@ -79,6 +80,16 @@ func policyRuleToView(rec *model.PolicyRuleRecord) *PolicyRuleView {
|
||||
CreatedAt: rec.CreatedAt.Format("2006-01-02 15:04 UTC"),
|
||||
UpdatedAt: rec.UpdatedAt.Format("2006-01-02 15:04 UTC"),
|
||||
}
|
||||
now := time.Now()
|
||||
if rec.NotBefore != nil {
|
||||
v.NotBefore = rec.NotBefore.UTC().Format("2006-01-02 15:04 UTC")
|
||||
v.IsPending = now.Before(*rec.NotBefore)
|
||||
}
|
||||
if rec.ExpiresAt != nil {
|
||||
v.ExpiresAt = rec.ExpiresAt.UTC().Format("2006-01-02 15:04 UTC")
|
||||
v.IsExpired = now.After(*rec.ExpiresAt)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func prettyJSONStr(s string) string {
|
||||
@@ -160,6 +171,29 @@ func (u *UIServer) handleCreatePolicyRule(w http.ResponseWriter, r *http.Request
|
||||
return
|
||||
}
|
||||
|
||||
// Parse optional time-scoped validity window from datetime-local inputs.
|
||||
var notBefore, expiresAt *time.Time
|
||||
if nbStr := strings.TrimSpace(r.FormValue("not_before")); nbStr != "" {
|
||||
t, err := time.Parse("2006-01-02T15:04", nbStr)
|
||||
if err != nil {
|
||||
u.renderError(w, r, http.StatusBadRequest, "invalid not_before time format")
|
||||
return
|
||||
}
|
||||
notBefore = &t
|
||||
}
|
||||
if eaStr := strings.TrimSpace(r.FormValue("expires_at")); eaStr != "" {
|
||||
t, err := time.Parse("2006-01-02T15:04", eaStr)
|
||||
if err != nil {
|
||||
u.renderError(w, r, http.StatusBadRequest, "invalid expires_at time format")
|
||||
return
|
||||
}
|
||||
expiresAt = &t
|
||||
}
|
||||
if notBefore != nil && expiresAt != nil && !expiresAt.After(*notBefore) {
|
||||
u.renderError(w, r, http.StatusBadRequest, "expires_at must be after not_before")
|
||||
return
|
||||
}
|
||||
|
||||
claims := claimsFromContext(r.Context())
|
||||
var actorID *int64
|
||||
if claims != nil {
|
||||
@@ -168,7 +202,7 @@ func (u *UIServer) handleCreatePolicyRule(w http.ResponseWriter, r *http.Request
|
||||
}
|
||||
}
|
||||
|
||||
rec, err := u.db.CreatePolicyRule(description, priority, string(ruleJSON), actorID)
|
||||
rec, err := u.db.CreatePolicyRule(description, priority, string(ruleJSON), actorID, notBefore, expiresAt)
|
||||
if err != nil {
|
||||
u.renderError(w, r, http.StatusInternalServerError, fmt.Sprintf("create policy rule: %v", err))
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user