Pulse/internal/api/security_first_run_reset.go

88 lines
2.4 KiB
Go

package api
import (
"encoding/json"
"net/http"
"os"
"strings"
"github.com/rcourtman/pulse-go-rewrite/internal/config"
internalauth "github.com/rcourtman/pulse-go-rewrite/pkg/auth"
"github.com/rs/zerolog/log"
)
type firstRunResetResponse struct {
BootstrapToken string `json:"bootstrapToken"`
BootstrapTokenPath string `json:"bootstrapTokenPath,omitempty"`
}
func devModeEnabled() bool {
return os.Getenv("PULSE_DEV") == "true" || strings.EqualFold(os.Getenv("NODE_ENV"), "development")
}
func (r *Router) handleResetFirstRunSecurity(w http.ResponseWriter, req *http.Request) {
if req.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
if !devModeEnabled() {
http.Error(w, "Development mode required", http.StatusForbidden)
return
}
if !CheckAuth(r.config, w, req) {
return
}
if !ensureSettingsWriteScope(r.config, w, req) {
return
}
config.Mu.Lock()
previousAuthUser := strings.TrimSpace(r.config.AuthUser)
r.config.AuthUser = ""
r.config.AuthPass = ""
r.config.APIToken = ""
r.config.APITokens = nil
config.Mu.Unlock()
for _, key := range []string{
"PULSE_AUTH_USER",
"PULSE_AUTH_PASS",
"REQUIRE_AUTH",
} {
_ = os.Unsetenv(key)
}
if previousAuthUser != "" {
InvalidateUserSessions(previousAuthUser)
}
r.clearSession(w, req)
r.clearBootstrapToken()
if err := removeAuthEnvFiles(r.config.ConfigPath, r.config.DataPath); err != nil {
log.Warn().Err(err).Msg("Failed to remove auth env files during first-run reset")
http.Error(w, "Failed to remove persisted auth configuration", http.StatusInternalServerError)
return
}
if r.persistence != nil {
if err := r.persistence.SaveAPITokens([]config.APITokenRecord{}); err != nil {
log.Warn().Err(err).Msg("Failed to clear persisted API tokens during first-run reset")
http.Error(w, "Failed to clear persisted API tokens", http.StatusInternalServerError)
return
}
}
token, _, path, err := loadOrCreateBootstrapToken(r.config.DataPath)
if err != nil {
log.Error().Err(err).Msg("Failed to recreate bootstrap token during first-run reset")
http.Error(w, "Failed to recreate bootstrap token", http.StatusInternalServerError)
return
}
r.bootstrapTokenHash = internalauth.HashAPIToken(token)
r.bootstrapTokenPath = path
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(firstRunResetResponse{
BootstrapToken: token,
BootstrapTokenPath: path,
})
}