Stores the sanitised list per site, serves it at /sites/{id}/discovered and adds
it to the target picker. Needs a Reef release containing model.DiscoveredService.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
162 lines
9.0 KiB
Go
162 lines
9.0 KiB
Go
package hammerhead
|
|
|
|
import (
|
|
_ "embed"
|
|
"net/http"
|
|
)
|
|
|
|
//go:embed web/index.html
|
|
var indexHTML []byte
|
|
|
|
func page(html []byte) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
h := w.Header()
|
|
h.Set("Content-Type", "text/html; charset=utf-8")
|
|
h.Set("Cache-Control", "no-store")
|
|
h.Set("X-Frame-Options", "DENY")
|
|
h.Set("X-Content-Type-Options", "nosniff")
|
|
h.Set("Referrer-Policy", "no-referrer")
|
|
h.Set("Content-Security-Policy", "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; frame-ancestors 'none'")
|
|
w.Write(html)
|
|
}
|
|
}
|
|
|
|
// dashboardPaths are the addresses the dashboard shows pages at. They all serve the same app,
|
|
// which reads the path to decide what to show. Keep in step with TAB_PATH in web/index.html.
|
|
var dashboardPaths = map[string]bool{
|
|
"/": true, "/apps": true, "/resources": true, "/sites": true, "/domains": true, "/mail-egress": true,
|
|
"/alerts": true, "/access": true, "/logs": true, "/account": true,
|
|
"/server/edges": true, "/server/organizations": true, "/server/users": true, "/server/sso": true,
|
|
"/server/settings": true, "/server/alerts": true, "/server/audit": true,
|
|
}
|
|
|
|
func isDashboardPath(p string) bool { return dashboardPaths[p] }
|
|
|
|
func (s *Server) Handler() http.Handler {
|
|
mux := http.NewServeMux()
|
|
index := page(indexHTML)
|
|
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
if !isDashboardPath(r.URL.Path) {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
index(w, r)
|
|
})
|
|
|
|
s.registerAlertRoutes(mux)
|
|
mux.HandleFunc("GET /metrics", s.metrics)
|
|
|
|
// public
|
|
mux.HandleFunc("GET /api/public", s.publicInfo)
|
|
mux.HandleFunc("POST /api/setup", s.setup)
|
|
mux.HandleFunc("POST /api/login", s.login)
|
|
mux.HandleFunc("POST /api/login/totp", s.loginTOTP)
|
|
mux.HandleFunc("GET /api/invites/{token}", s.inviteInfo)
|
|
mux.HandleFunc("POST /api/invites/accept", s.acceptInvite)
|
|
mux.HandleFunc("GET /auth/oidc/{id}/start", s.oidcStart)
|
|
mux.HandleFunc("GET /auth/oidc/callback", s.oidcCallback)
|
|
|
|
// visitor login for protected ports
|
|
res := page(resourceHTML)
|
|
mux.HandleFunc("GET /auth/r/{id}", res)
|
|
mux.HandleFunc("GET /auth/r/{id}/info", s.resourceInfo)
|
|
mux.HandleFunc("POST /auth/r/{id}/password", s.resourceAuth("password", s.resourcePassword))
|
|
mux.HandleFunc("POST /auth/r/{id}/pin", s.resourceAuth("pin", s.resourcePin))
|
|
mux.HandleFunc("POST /auth/r/{id}/sso", s.resourceAuth("sso", s.resourceSSO))
|
|
mux.HandleFunc("POST /auth/r/{id}/share", s.resourceAuth("share", s.resourceShare))
|
|
mux.HandleFunc("POST /auth/r/{id}/email/start", s.resourceAuth("emailstart", s.resourceEmailStart))
|
|
mux.HandleFunc("POST /auth/r/{id}/email/verify", s.resourceAuth("emailverify", s.resourceEmailVerify))
|
|
|
|
// any signed-in user
|
|
mux.HandleFunc("POST /api/logout", s.withUser(s.logout))
|
|
mux.HandleFunc("GET /api/me", s.withUser(s.me))
|
|
mux.HandleFunc("PUT /api/me", s.withUser(s.updateMe))
|
|
mux.HandleFunc("POST /api/me/password", s.withUser(s.changePassword))
|
|
mux.HandleFunc("POST /api/me/2fa/setup", s.withUser(s.totpSetup))
|
|
mux.HandleFunc("POST /api/me/2fa/enable", s.withUser(s.totpEnable))
|
|
mux.HandleFunc("POST /api/me/2fa/disable", s.withUser(s.totpDisable))
|
|
mux.HandleFunc("GET /api/me/resources", s.withUser(s.myResources))
|
|
mux.HandleFunc("GET /api/me/sessions", s.withUser(s.mySessions))
|
|
mux.HandleFunc("DELETE /api/me/sessions/{id}", s.withUser(s.endSession))
|
|
mux.HandleFunc("POST /api/me/sessions/revoke-others", s.withUser(s.endOtherSessions))
|
|
mux.HandleFunc("GET /api/edges", s.withUser(s.listEdges))
|
|
|
|
// server administrators
|
|
mux.HandleFunc("POST /api/edges", s.withServerAdmin(s.saveEdge))
|
|
mux.HandleFunc("PUT /api/edges/{id}", s.withServerAdmin(s.saveEdge))
|
|
mux.HandleFunc("DELETE /api/edges/{id}", s.withServerAdmin(s.deleteEdge))
|
|
mux.HandleFunc("POST /api/orgs", s.withServerAdmin(s.createOrg))
|
|
mux.HandleFunc("GET /api/admin/users", s.withServerAdmin(s.adminUsers))
|
|
mux.HandleFunc("POST /api/admin/users", s.withServerAdmin(s.adminCreateUser))
|
|
mux.HandleFunc("PUT /api/admin/users/{id}", s.withServerAdmin(s.adminUpdateUser))
|
|
mux.HandleFunc("DELETE /api/admin/users/{id}", s.withServerAdmin(s.adminDeleteUser))
|
|
mux.HandleFunc("GET /api/admin/idps", s.withServerAdmin(s.listIdPs))
|
|
mux.HandleFunc("POST /api/admin/idps", s.withServerAdmin(s.saveIdP))
|
|
mux.HandleFunc("PUT /api/admin/idps/{id}", s.withServerAdmin(s.saveIdP))
|
|
mux.HandleFunc("DELETE /api/admin/idps/{id}", s.withServerAdmin(s.deleteIdP))
|
|
mux.HandleFunc("GET /api/admin/settings", s.withServerAdmin(s.getSettings))
|
|
mux.HandleFunc("PUT /api/admin/settings", s.withServerAdmin(s.putSettings))
|
|
mux.HandleFunc("POST /api/admin/settings/test-mail", s.withServerAdmin(s.testMail))
|
|
mux.HandleFunc("GET /api/admin/update", s.withServerAdmin(s.checkUpdate))
|
|
mux.HandleFunc("POST /api/admin/update/apply", s.withServerAdmin(s.applyUpdate))
|
|
mux.HandleFunc("GET /api/admin/update/status", s.withServerAdmin(s.updateStatus))
|
|
mux.HandleFunc("GET /api/versions", s.withUser(s.latestVersions))
|
|
mux.HandleFunc("GET /api/edges/{id}/update/log", s.withServerAdmin(s.edgeUpdateLog))
|
|
mux.HandleFunc("GET /api/orgs/{org}/sites/{id}/update/log", s.withOrg(true, s.siteUpdateLog))
|
|
mux.HandleFunc("POST /api/edges/{id}/update", s.withServerAdmin(s.updateEdge))
|
|
mux.HandleFunc("POST /api/orgs/{org}/sites/{id}/update", s.withOrg(true, s.updateSite))
|
|
mux.HandleFunc("GET /api/admin/audit", s.withServerAdmin(func(w http.ResponseWriter, r *http.Request, p *principal) {
|
|
writeJSON(w, 200, s.logs.queryAudit("", 300, 0))
|
|
}))
|
|
|
|
// organization scope: members read, admins write
|
|
mux.HandleFunc("GET /api/orgs/{org}/state", s.withOrg(false, s.orgState))
|
|
mux.HandleFunc("PUT /api/orgs/{org}", s.withOrg(true, s.updateOrg))
|
|
mux.HandleFunc("DELETE /api/orgs/{org}", s.withOrg(true, s.deleteOrg))
|
|
mux.HandleFunc("GET /api/orgs/{org}/roles", s.withOrg(false, s.listRoles))
|
|
mux.HandleFunc("POST /api/orgs/{org}/roles", s.withOrg(true, s.saveRole))
|
|
mux.HandleFunc("PUT /api/orgs/{org}/roles/{role}", s.withOrg(true, s.saveRole))
|
|
mux.HandleFunc("DELETE /api/orgs/{org}/roles/{role}", s.withOrg(true, s.deleteRole))
|
|
mux.HandleFunc("GET /api/orgs/{org}/members", s.withOrg(true, s.listMembers))
|
|
mux.HandleFunc("PUT /api/orgs/{org}/members/{user}", s.withOrg(true, s.setMemberRole))
|
|
mux.HandleFunc("DELETE /api/orgs/{org}/members/{user}", s.withOrg(true, s.removeMember))
|
|
mux.HandleFunc("GET /api/orgs/{org}/invites", s.withOrg(true, s.listInvites))
|
|
mux.HandleFunc("POST /api/orgs/{org}/invites", s.withOrg(true, s.createInvite))
|
|
mux.HandleFunc("DELETE /api/orgs/{org}/invites/{id}", s.withOrg(true, s.deleteInvite))
|
|
mux.HandleFunc("GET /api/orgs/{org}/apikeys", s.withOrg(true, s.listKeys))
|
|
mux.HandleFunc("POST /api/orgs/{org}/apikeys", s.withOrg(true, s.createKey))
|
|
mux.HandleFunc("DELETE /api/orgs/{org}/apikeys/{id}", s.withOrg(true, s.deleteKey))
|
|
mux.HandleFunc("POST /api/orgs/{org}/sites", s.withOrg(true, s.addSite))
|
|
mux.HandleFunc("PUT /api/orgs/{org}/sites/{id}", s.withOrg(true, s.putSite))
|
|
mux.HandleFunc("DELETE /api/orgs/{org}/sites/{id}", s.withOrg(true, s.deleteSite))
|
|
mux.HandleFunc("GET /api/orgs/{org}/sites/{id}/containers", s.withOrg(true, s.siteContainers))
|
|
mux.HandleFunc("GET /api/orgs/{org}/sites/{id}/discovered", s.withOrg(true, s.siteDiscovered))
|
|
mux.HandleFunc("POST /api/orgs/{org}/domains", s.withOrg(true, s.addDomain))
|
|
mux.HandleFunc("DELETE /api/orgs/{org}/domains/{id}", s.withOrg(true, s.deleteDomain))
|
|
mux.HandleFunc("POST /api/orgs/{org}/domains/{id}/check", s.withOrg(true, s.checkDomain))
|
|
mux.HandleFunc("POST /api/orgs/{org}/mappings", s.withOrg(true, s.addMapping))
|
|
mux.HandleFunc("PUT /api/orgs/{org}/mappings/{id}", s.withOrg(true, s.putMapping))
|
|
mux.HandleFunc("DELETE /api/orgs/{org}/mappings/{id}", s.withOrg(true, s.deleteMapping))
|
|
mux.HandleFunc("GET /api/orgs/{org}/mappings/{id}/shares", s.withOrg(true, s.listShares))
|
|
mux.HandleFunc("POST /api/orgs/{org}/mappings/{id}/shares", s.withOrg(true, s.createShare))
|
|
mux.HandleFunc("POST /api/orgs/{org}/mappings/{id}/revoke", s.withOrg(true, s.revokeVisitors))
|
|
mux.HandleFunc("DELETE /api/orgs/{org}/mappings/{id}/shares/{share}", s.withOrg(true, s.deleteShare))
|
|
mux.HandleFunc("POST /api/orgs/{org}/mail-egress", s.withOrg(true, s.saveMailEgress))
|
|
mux.HandleFunc("PUT /api/orgs/{org}/mail-egress/{id}", s.withOrg(true, s.saveMailEgress))
|
|
mux.HandleFunc("DELETE /api/orgs/{org}/mail-egress/{id}", s.withOrg(true, s.deleteMailEgress))
|
|
mux.HandleFunc("GET /api/orgs/{org}/dkim", s.withOrg(true, s.listDKIM))
|
|
mux.HandleFunc("POST /api/orgs/{org}/dkim", s.withOrg(true, s.createDKIM))
|
|
mux.HandleFunc("DELETE /api/orgs/{org}/dkim/{id}", s.withOrg(true, s.deleteDKIM))
|
|
mux.HandleFunc("GET /api/orgs/{org}/logs", s.withOrg(true, s.orgLogs))
|
|
mux.HandleFunc("GET /api/orgs/{org}/stats", s.withOrg(false, s.orgStats))
|
|
mux.HandleFunc("GET /api/orgs/{org}/traffic", s.withOrg(false, s.orgTraffic))
|
|
|
|
// agents
|
|
mux.HandleFunc("GET /edge/config", s.edgeConfig)
|
|
mux.HandleFunc("POST /edge/report", s.edgeReport)
|
|
mux.HandleFunc("POST /edge/logs", s.edgeLogs)
|
|
mux.HandleFunc("GET /site/config", s.siteConfig)
|
|
mux.HandleFunc("POST /site/report", s.siteReport)
|
|
return mux
|
|
}
|