This commit is contained in:
David Schroeder
2026-07-25 14:51:48 -05:00
parent 1ea21c065e
commit 69cc236635
18 changed files with 770 additions and 106 deletions
+89 -7
View File
@@ -16,6 +16,8 @@ type exchangeRequest struct {
Code string `json:"code"`
HostFingerprint string `json:"host_fingerprint"`
Hostname string `json:"hostname"`
RequestedAction string `json:"requested_action,omitempty"`
RequestedPackage string `json:"requested_package,omitempty"`
}
type exchangePackage struct {
@@ -30,6 +32,7 @@ type exchangeResponse struct {
SessionToken string `json:"session_token"`
ExpiresAt time.Time `json:"expires_at"`
Packages []exchangePackage `json:"packages"`
Actions []string `json:"actions"`
}
func (s *Server) handleExchange(w http.ResponseWriter, r *http.Request) {
@@ -54,8 +57,12 @@ func (s *Server) handleExchange(w http.ResponseWriter, r *http.Request) {
request.Code = normalizeCode(request.Code)
request.HostFingerprint = strings.TrimSpace(request.HostFingerprint)
request.Hostname = strings.TrimSpace(request.Hostname)
request.RequestedAction = strings.TrimSpace(request.RequestedAction)
request.RequestedPackage = strings.TrimSpace(request.RequestedPackage)
if request.Code == "" || len(request.HostFingerprint) < 16 ||
request.Hostname == "" || len(request.Hostname) > 255 {
request.Hostname == "" || len(request.Hostname) > 255 ||
(request.RequestedAction != "" && !validSlug(request.RequestedAction)) ||
(request.RequestedPackage != "" && !validSlug(request.RequestedPackage)) {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "code and host identity are required"})
return
}
@@ -72,7 +79,16 @@ func (s *Server) handleExchange(w http.ResponseWriter, r *http.Request) {
writeJSON(w, status, map[string]string{"error": message})
return
}
_ = s.audit(r.Context(), "code_exchanged", "", &authorizationID, request.Hostname, "", sourceIP, "")
_ = s.audit(
r.Context(),
"code_exchanged",
"",
&authorizationID,
request.Hostname,
request.RequestedPackage,
sourceIP,
"requested_action="+request.RequestedAction,
)
writeJSON(w, http.StatusOK, response)
}
@@ -124,6 +140,43 @@ func (s *Server) exchangeDeploymentCode(
return response, 0, err
}
if request.RequestedAction != "" {
var authorized int
if err := tx.QueryRowContext(
r.Context(),
`SELECT COUNT(*)
FROM authorization_actions aa
JOIN installer_actions ia ON ia.slug = aa.action_slug
WHERE aa.authorization_id = ?
AND aa.action_slug = ?
AND ia.enabled = TRUE`,
authorizationID, request.RequestedAction,
).Scan(&authorized); err != nil {
return response, 0, err
}
if authorized != 1 {
return response, 0, errAuthorizationDenied
}
}
if request.RequestedPackage != "" {
var authorized int
if err := tx.QueryRowContext(
r.Context(),
`SELECT COUNT(*)
FROM authorization_packages ap
JOIN packages p ON p.id = ap.package_id
WHERE ap.authorization_id = ?
AND ap.package_slug = ?
AND p.enabled = TRUE`,
authorizationID, request.RequestedPackage,
).Scan(&authorized); err != nil {
return response, 0, err
}
if authorized != 1 {
return response, 0, errAuthorizationDenied
}
}
var hostID uint64
err = tx.QueryRowContext(
r.Context(),
@@ -188,7 +241,7 @@ func (s *Server) exchangeDeploymentCode(
rows, err := tx.QueryContext(
r.Context(),
`SELECT p.slug, p.display_name, p.package_version, p.sha256
`SELECT ap.package_slug, ap.display_name, ap.package_version, ap.sha256
FROM authorization_packages ap
JOIN packages p ON p.id = ap.package_id
WHERE ap.authorization_id = ? AND p.enabled = TRUE
@@ -216,7 +269,36 @@ func (s *Server) exchangeDeploymentCode(
if err := rows.Err(); err != nil {
return response, 0, err
}
if len(response.Packages) == 0 {
if err := rows.Close(); err != nil {
return response, 0, err
}
actionRows, err := tx.QueryContext(
r.Context(),
`SELECT ia.slug
FROM authorization_actions aa
JOIN installer_actions ia ON ia.slug = aa.action_slug
WHERE aa.authorization_id = ? AND ia.enabled = TRUE
ORDER BY ia.sort_order, ia.display_name`,
authorizationID,
)
if err != nil {
return response, 0, err
}
defer actionRows.Close()
for actionRows.Next() {
var slug string
if err := actionRows.Scan(&slug); err != nil {
return response, 0, err
}
response.Actions = append(response.Actions, slug)
}
if err := actionRows.Err(); err != nil {
return response, 0, err
}
if err := actionRows.Close(); err != nil {
return response, 0, err
}
if len(response.Packages) == 0 && len(response.Actions) == 0 {
return response, 0, errAuthorizationDenied
}
@@ -248,8 +330,8 @@ func (s *Server) handlePackageDownload(w http.ResponseWriter, r *http.Request) {
var hostname string
err := s.db.QueryRowContext(
r.Context(),
`SELECT p.id, p.slug, p.display_name, p.package_name,
p.package_version, p.file_name, p.sha256, p.enabled,
`SELECT p.id, ap.package_slug, ap.display_name, ap.package_name,
ap.package_version, ap.file_name, ap.sha256, p.enabled,
ds.authorization_id, ah.hostname
FROM download_sessions ds
JOIN authorizations a ON a.id = ds.authorization_id
@@ -261,7 +343,7 @@ func (s *Server) handlePackageDownload(w http.ResponseWriter, r *http.Request) {
AND ds.expires_at > UTC_TIMESTAMP(6)
AND a.revoked_at IS NULL
AND a.expires_at > UTC_TIMESTAMP(6)
AND p.slug = ?
AND ap.package_slug = ?
AND p.enabled = TRUE`,
sessionHash[:], slug,
).Scan(