feat: isolate release source for installers and provisioning
Release / Build & Release (push) Canceled after 0s

This commit is contained in:
kory
2026-10-02 15:25:19 +08:00
parent ea7aebd276
commit f084ac5bae
10 changed files with 144 additions and 39 deletions
+24 -14
View File
@@ -39,17 +39,18 @@ const (
)
type Server struct {
Auth *auth.Manager
Exec executor.Local
Cluster *cluster.Manager
Store *storage.Store
LocalName string
Notify *notify.Manager
Policy *security.Snapshot
AdminToken string
ExecLimiter chan struct{}
SearchLimiter chan struct{}
LocalSessions *executor.SessionManager
Auth *auth.Manager
Exec executor.Local
Cluster *cluster.Manager
Store *storage.Store
LocalName string
Notify *notify.Manager
Policy *security.Snapshot
AdminToken string
InstallCommand string
ExecLimiter chan struct{}
SearchLimiter chan struct{}
LocalSessions *executor.SessionManager
}
const (
@@ -254,8 +255,17 @@ func (s *Server) ProvisionDeviceCredentialJSON(w http.ResponseWriter, r *http.Re
http.Error(w, "local control node does not use agent credential", 400)
return
}
if s.Cluster == nil || !s.Cluster.Online(q.Device) {
http.Error(w, "device offline", 409)
if strings.ContainsAny(q.Device, "/\\?&=#%") {
http.Error(w, "invalid device name", 400)
return
}
configured, err := s.Store.HasDeviceCredential(q.Device)
if err != nil {
http.Error(w, "credential lookup failed", 500)
return
}
if configured {
http.Error(w, "device credential already exists; revoke/rotate it before provisioning again", 409)
return
}
b := make([]byte, 32)
@@ -269,7 +279,7 @@ func (s *Server) ProvisionDeviceCredentialJSON(w http.ResponseWriter, r *http.Re
return
}
_ = s.Store.Audit(clientIP(r), q.Device, "device_credential_provision", "device="+q.Device, 0, 0, "credential provisioned")
jsonOut(w, map[string]any{"ok": true, "device": q.Device, "token": token})
jsonOut(w, map[string]any{"ok": true, "device": q.Device, "token": token, "install_command": s.InstallCommand})
}
func (s *Server) UpdateSecuritySettingsJSON(w http.ResponseWriter, r *http.Request) {
+41
View File
@@ -167,3 +167,44 @@ func TestEditBlockSecurityAndReplacement(t *testing.T) {
t.Fatalf("low mode status=%d body=%s", w.Code, w.Body.String())
}
}
func TestProvisionDeviceCredentialForNewOfflineDevice(t *testing.T) {
store, err := storage.Open(filepath.Join(t.TempDir(), "commander.db"))
if err != nil {
t.Fatal(err)
}
defer store.DB.Close()
s := &Server{
Store: store,
LocalName: "wjyhk",
AdminToken: "admin-test-token",
}
req := httptest.NewRequest(http.MethodPost, "/api/v1/security/device-credentials/provision", strings.NewReader("{\"device\":\"new-node-01\"}"))
req.Header.Set("X-Admin-Token", "admin-test-token")
w := httptest.NewRecorder()
s.ProvisionDeviceCredentialJSON(w, req)
if w.Code != http.StatusOK {
t.Fatalf("provision status=%d body=%s", w.Code, w.Body.String())
}
if !strings.Contains(w.Body.String(), "\"token\"") {
t.Fatalf("missing token: %s", w.Body.String())
}
ok, err := store.HasDeviceCredential("new-node-01")
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("credential was not persisted")
}
req = httptest.NewRequest(http.MethodPost, "/api/v1/security/device-credentials/provision", strings.NewReader("{\"device\":\"new-node-01\"}"))
req.Header.Set("X-Admin-Token", "admin-test-token")
w = httptest.NewRecorder()
s.ProvisionDeviceCredentialJSON(w, req)
if w.Code != http.StatusConflict {
t.Fatalf("duplicate provision status=%d body=%s", w.Code, w.Body.String())
}
}