update
This commit is contained in:
+122
-26
@@ -26,6 +26,11 @@ func (s *Server) handlePortal(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "unable to list authorizations", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
actions, err := s.listActions(r)
|
||||
if err != nil {
|
||||
http.Error(w, "unable to list installer actions", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
auditEvents, err := s.listAuditEvents(r)
|
||||
if err != nil {
|
||||
http.Error(w, "unable to list audit events", http.StatusInternalServerError)
|
||||
@@ -38,6 +43,7 @@ func (s *Server) handlePortal(w http.ResponseWriter, r *http.Request) {
|
||||
Authorizations: authorizations,
|
||||
AuditEvents: auditEvents,
|
||||
Packages: packages,
|
||||
Actions: actions,
|
||||
NewCode: r.URL.Query().Get("code"),
|
||||
DefaultHostLimit: s.cfg.DefaultHostLimit,
|
||||
DefaultDuration: strconv.Itoa(int(s.cfg.DefaultDuration.Hours())),
|
||||
@@ -45,6 +51,29 @@ func (s *Server) handlePortal(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) listActions(r *http.Request) ([]actionRecord, error) {
|
||||
rows, err := s.db.QueryContext(
|
||||
r.Context(),
|
||||
`SELECT slug, display_name, enabled
|
||||
FROM installer_actions
|
||||
ORDER BY sort_order, display_name`,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var records []actionRecord
|
||||
for rows.Next() {
|
||||
var record actionRecord
|
||||
if err := rows.Scan(&record.Slug, &record.DisplayName, &record.Enabled); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
records = append(records, record)
|
||||
}
|
||||
return records, rows.Err()
|
||||
}
|
||||
|
||||
func (s *Server) listAuditEvents(r *http.Request) ([]auditRecord, error) {
|
||||
rows, err := s.db.QueryContext(
|
||||
r.Context(),
|
||||
@@ -113,16 +142,25 @@ func (s *Server) listAuthorizations(r *http.Request) ([]authorizationRecord, err
|
||||
rows, err := s.db.QueryContext(
|
||||
r.Context(),
|
||||
`SELECT a.id, a.code_hint, a.created_by, a.customer_label,
|
||||
a.host_limit, COUNT(DISTINCT h.id), a.created_at,
|
||||
a.expires_at, a.revoked_at,
|
||||
COALESCE(GROUP_CONCAT(DISTINCT p.display_name
|
||||
ORDER BY p.display_name SEPARATOR ', '), '')
|
||||
a.host_limit,
|
||||
(SELECT COUNT(*) FROM authorization_hosts h
|
||||
WHERE h.authorization_id = a.id),
|
||||
a.created_at, a.expires_at, a.revoked_at,
|
||||
COALESCE((
|
||||
SELECT GROUP_CONCAT(ap.display_name
|
||||
ORDER BY ap.display_name SEPARATOR ', ')
|
||||
FROM authorization_packages ap
|
||||
WHERE ap.authorization_id = a.id
|
||||
), ''),
|
||||
COALESCE((
|
||||
SELECT GROUP_CONCAT(ia.display_name
|
||||
ORDER BY ia.sort_order, ia.display_name SEPARATOR ', ')
|
||||
FROM authorization_actions aa
|
||||
JOIN installer_actions ia ON ia.slug = aa.action_slug
|
||||
WHERE aa.authorization_id = a.id
|
||||
), '')
|
||||
FROM authorizations a
|
||||
LEFT JOIN authorization_hosts h ON h.authorization_id = a.id
|
||||
LEFT JOIN authorization_packages ap ON ap.authorization_id = a.id
|
||||
LEFT JOIN packages p ON p.id = ap.package_id
|
||||
WHERE a.created_at > UTC_TIMESTAMP(6) - INTERVAL 30 DAY
|
||||
GROUP BY a.id
|
||||
ORDER BY a.created_at DESC
|
||||
LIMIT 100`,
|
||||
)
|
||||
@@ -144,6 +182,7 @@ func (s *Server) listAuthorizations(r *http.Request) ([]authorizationRecord, err
|
||||
&record.ExpiresAt,
|
||||
&record.RevokedAt,
|
||||
&record.Packages,
|
||||
&record.Actions,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -165,8 +204,9 @@ func (s *Server) handleCreateAuthorization(w http.ResponseWriter, r *http.Reques
|
||||
return
|
||||
}
|
||||
packageIDs := r.Form["package_id"]
|
||||
if len(packageIDs) == 0 {
|
||||
http.Error(w, "select at least one package", http.StatusBadRequest)
|
||||
actionSlugs := r.Form["action_slug"]
|
||||
if len(packageIDs) == 0 && len(actionSlugs) == 0 {
|
||||
http.Error(w, "select at least one package or installer action", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
code, err := deploymentCode()
|
||||
@@ -208,8 +248,13 @@ func (s *Server) handleCreateAuthorization(w http.ResponseWriter, r *http.Reques
|
||||
}
|
||||
result, err := tx.ExecContext(
|
||||
r.Context(),
|
||||
`INSERT INTO authorization_packages (authorization_id, package_id)
|
||||
SELECT ?, id FROM packages WHERE id = ? AND enabled = TRUE`,
|
||||
`INSERT INTO authorization_packages
|
||||
(authorization_id, package_id, package_slug, display_name,
|
||||
package_name, package_version, file_name, sha256)
|
||||
SELECT ?, id, slug, display_name, package_name, package_version,
|
||||
file_name, sha256
|
||||
FROM packages
|
||||
WHERE id = ? AND enabled = TRUE`,
|
||||
authorizationID, packageID,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -222,6 +267,29 @@ func (s *Server) handleCreateAuthorization(w http.ResponseWriter, r *http.Reques
|
||||
return
|
||||
}
|
||||
}
|
||||
for _, actionSlug := range actionSlugs {
|
||||
actionSlug = strings.TrimSpace(actionSlug)
|
||||
if !validSlug(actionSlug) {
|
||||
http.Error(w, "invalid installer action", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
result, err := tx.ExecContext(
|
||||
r.Context(),
|
||||
`INSERT INTO authorization_actions (authorization_id, action_slug)
|
||||
SELECT ?, slug FROM installer_actions
|
||||
WHERE slug = ? AND enabled = TRUE`,
|
||||
authorizationID, actionSlug,
|
||||
)
|
||||
if err != nil {
|
||||
http.Error(w, "unable to authorize installer action", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
affected, _ := result.RowsAffected()
|
||||
if affected != 1 {
|
||||
http.Error(w, "selected installer action is unavailable", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
http.Error(w, "unable to save authorization", http.StatusInternalServerError)
|
||||
return
|
||||
@@ -278,23 +346,18 @@ func (s *Server) handleUpsertPackage(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if !validSlug(values["slug"]) || !validSHA256(values["sha256"]) {
|
||||
http.Error(w, "invalid slug or SHA-256", http.StatusBadRequest)
|
||||
if !validSlug(values["slug"]) ||
|
||||
len(values["display_name"]) > 255 ||
|
||||
!validRegistrySegment(values["package_name"], 255) ||
|
||||
!validRegistrySegment(values["package_version"], 100) ||
|
||||
!validRegistrySegment(values["file_name"], 255) ||
|
||||
!validSHA256(values["sha256"]) {
|
||||
http.Error(w, "invalid package metadata", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
enabled := r.FormValue("enabled") == "1"
|
||||
_, err := s.db.ExecContext(
|
||||
r.Context(),
|
||||
`INSERT INTO packages
|
||||
(slug, display_name, package_name, package_version, file_name, sha256, enabled)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
display_name = VALUES(display_name),
|
||||
package_name = VALUES(package_name),
|
||||
package_version = VALUES(package_version),
|
||||
file_name = VALUES(file_name),
|
||||
sha256 = VALUES(sha256),
|
||||
enabled = VALUES(enabled)`,
|
||||
err := s.savePackage(
|
||||
r,
|
||||
values["slug"],
|
||||
values["display_name"],
|
||||
values["package_name"],
|
||||
@@ -312,6 +375,39 @@ func (s *Server) handleUpsertPackage(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, "/portal?notice=Package+saved", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
func (s *Server) savePackage(
|
||||
r *http.Request,
|
||||
slug string,
|
||||
displayName string,
|
||||
packageName string,
|
||||
packageVersion string,
|
||||
fileName string,
|
||||
sha256 string,
|
||||
enabled bool,
|
||||
) error {
|
||||
_, err := s.db.ExecContext(
|
||||
r.Context(),
|
||||
`INSERT INTO packages
|
||||
(slug, display_name, package_name, package_version, file_name, sha256, enabled)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
display_name = VALUES(display_name),
|
||||
package_name = VALUES(package_name),
|
||||
package_version = VALUES(package_version),
|
||||
file_name = VALUES(file_name),
|
||||
sha256 = VALUES(sha256),
|
||||
enabled = VALUES(enabled)`,
|
||||
slug,
|
||||
displayName,
|
||||
packageName,
|
||||
packageVersion,
|
||||
fileName,
|
||||
sha256,
|
||||
enabled,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func validSlug(value string) bool {
|
||||
if value == "" || len(value) > 100 {
|
||||
return false
|
||||
|
||||
Reference in New Issue
Block a user