Semaphore/api/router.go

385 lines
15 KiB
Go
Raw Normal View History

2016-05-24 11:55:48 +02:00
package api
2016-01-05 00:32:53 +01:00
import (
"fmt"
"github.com/ansible-semaphore/semaphore/api/runners"
2017-02-23 00:21:49 +01:00
"net/http"
2019-09-08 09:19:46 +02:00
"os"
2016-01-06 12:20:07 +01:00
"strings"
2016-03-16 22:49:43 +01:00
2023-08-16 16:04:49 +02:00
"github.com/ansible-semaphore/semaphore/api/helpers"
2016-05-24 11:55:48 +02:00
"github.com/ansible-semaphore/semaphore/api/projects"
"github.com/ansible-semaphore/semaphore/api/sockets"
2023-08-16 16:04:49 +02:00
"github.com/ansible-semaphore/semaphore/db"
2016-03-16 22:49:43 +01:00
"github.com/ansible-semaphore/semaphore/util"
"github.com/gobuffalo/packr"
2017-02-23 00:21:49 +01:00
"github.com/gorilla/mux"
2016-01-05 00:32:53 +01:00
)
2022-10-29 15:37:20 +02:00
var publicAssets2 = packr.NewBox("../web/dist")
// StoreMiddleware WTF?
func StoreMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
store := helpers.Store(r)
//var url = r.URL.String()
db.StoreSession(store, util.RandString(12), func() {
next.ServeHTTP(w, r)
})
})
}
// JSONMiddleware ensures that all the routes respond with Json, this is added by default to all routes
func JSONMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("content-type", "application/json")
next.ServeHTTP(w, r)
})
}
// plainTextMiddleware resets headers to Plain Text if needed
func plainTextMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("content-type", "text/plain; charset=utf-8")
next.ServeHTTP(w, r)
})
}
2018-10-23 05:22:49 +02:00
func pongHandler(w http.ResponseWriter, r *http.Request) {
2020-02-09 14:48:24 +01:00
//nolint: errcheck
w.Write([]byte("pong"))
}
func notFoundHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin"))
w.Header().Set("Access-Control-Allow-Credentials", "true")
w.WriteHeader(http.StatusNotFound)
2020-02-09 14:48:24 +01:00
//nolint: errcheck
w.Write([]byte("404 not found"))
fmt.Println(r.Method, ":", r.URL.String(), "--> 404 Not Found")
}
// Route declares all routes
func Route() *mux.Router {
r := mux.NewRouter()
r.NotFoundHandler = http.HandlerFunc(servePublic)
2016-01-05 00:32:53 +01:00
webPath := "/"
2020-11-24 13:26:02 +01:00
if util.WebHostURL != nil {
2019-09-08 09:19:46 +02:00
webPath = util.WebHostURL.Path
2020-11-24 13:26:02 +01:00
if !strings.HasSuffix(webPath, "/") {
webPath += "/"
}
}
r.Use(mux.CORSMethodMiddleware(r))
2019-09-08 09:19:46 +02:00
pingRouter := r.Path(webPath + "api/ping").Subrouter()
2019-09-08 09:19:46 +02:00
pingRouter.Use(plainTextMiddleware)
pingRouter.Methods("GET", "HEAD").HandlerFunc(pongHandler)
publicAPIRouter := r.PathPrefix(webPath + "api").Subrouter()
publicAPIRouter.Use(StoreMiddleware, JSONMiddleware)
2019-09-08 09:19:46 +02:00
publicAPIRouter.HandleFunc("/runners", runners.RegisterRunner).Methods("POST")
2023-04-16 23:57:56 +02:00
publicAPIRouter.HandleFunc("/auth/login", login).Methods("GET", "POST")
publicAPIRouter.HandleFunc("/auth/logout", logout).Methods("POST")
2023-04-16 23:57:56 +02:00
publicAPIRouter.HandleFunc("/auth/oidc/{provider}/login", oidcLogin).Methods("GET")
publicAPIRouter.HandleFunc("/auth/oidc/{provider}/redirect", oidcRedirect).Methods("GET")
routersAPI := r.PathPrefix(webPath + "api").Subrouter()
routersAPI.Use(StoreMiddleware, JSONMiddleware, runners.RunnerMiddleware)
routersAPI.Path("/runners/{runner_id}").HandlerFunc(runners.GetRunner).Methods("GET", "HEAD")
routersAPI.Path("/runners/{runner_id}").HandlerFunc(runners.UpdateRunner).Methods("PUT")
authenticatedWS := r.PathPrefix(webPath + "api").Subrouter()
authenticatedWS.Use(JSONMiddleware, authenticationWithStore)
authenticatedWS.Path("/ws").HandlerFunc(sockets.Handler).Methods("GET", "HEAD")
authenticatedAPI := r.PathPrefix(webPath + "api").Subrouter()
2019-09-08 09:19:46 +02:00
authenticatedAPI.Use(StoreMiddleware, JSONMiddleware, authentication)
2019-09-08 09:19:46 +02:00
authenticatedAPI.Path("/info").HandlerFunc(getSystemInfo).Methods("GET", "HEAD")
authenticatedAPI.Path("/projects").HandlerFunc(projects.GetProjects).Methods("GET", "HEAD")
authenticatedAPI.Path("/projects").HandlerFunc(projects.AddProject).Methods("POST")
authenticatedAPI.Path("/events").HandlerFunc(getAllEvents).Methods("GET", "HEAD")
authenticatedAPI.HandleFunc("/events/last", getLastEvents).Methods("GET", "HEAD")
authenticatedAPI.Path("/users").HandlerFunc(getUsers).Methods("GET", "HEAD")
authenticatedAPI.Path("/users").HandlerFunc(addUser).Methods("POST")
authenticatedAPI.Path("/user").HandlerFunc(getUser).Methods("GET", "HEAD")
2019-09-08 09:19:46 +02:00
tokenAPI := authenticatedAPI.PathPrefix("/user").Subrouter()
2019-09-08 09:19:46 +02:00
tokenAPI.Path("/tokens").HandlerFunc(getAPITokens).Methods("GET", "HEAD")
tokenAPI.Path("/tokens").HandlerFunc(createAPIToken).Methods("POST")
tokenAPI.HandleFunc("/tokens/{token_id}", expireAPIToken).Methods("DELETE")
userAPI := authenticatedAPI.Path("/users/{user_id}").Subrouter()
2019-09-08 09:19:46 +02:00
userAPI.Use(getUserMiddleware)
userAPI.Methods("GET", "HEAD").HandlerFunc(getUser)
userAPI.Methods("PUT").HandlerFunc(updateUser)
userAPI.Methods("DELETE").HandlerFunc(deleteUser)
userPasswordAPI := authenticatedAPI.PathPrefix("/users/{user_id}").Subrouter()
userPasswordAPI.Use(getUserMiddleware)
userPasswordAPI.Path("/password").HandlerFunc(updateUserPassword).Methods("POST")
projectGet := authenticatedAPI.Path("/project/{project_id}").Subrouter()
projectGet.Use(projects.ProjectMiddleware)
projectGet.Methods("GET", "HEAD").HandlerFunc(projects.GetProject)
2016-01-05 00:32:53 +01:00
//
// Start and Stop tasks
projectTaskStart := authenticatedAPI.PathPrefix("/project/{project_id}").Subrouter()
2023-08-26 20:43:42 +02:00
projectTaskStart.Use(projects.ProjectMiddleware, projects.GetMustCanMiddleware(db.CanRunProjectTasks))
projectTaskStart.Path("/tasks").HandlerFunc(projects.AddTask).Methods("POST")
2023-08-27 00:10:02 +02:00
projectTaskStop := authenticatedAPI.PathPrefix("/project/{project_id}").Subrouter()
2023-08-26 20:43:42 +02:00
projectTaskStop.Use(projects.ProjectMiddleware, projects.GetTaskMiddleware, projects.GetMustCanMiddleware(db.CanRunProjectTasks))
2023-08-27 00:10:02 +02:00
projectTaskStop.HandleFunc("/tasks/{task_id}/stop", projects.StopTask).Methods("POST")
//
// Project resources CRUD
2019-09-08 09:19:46 +02:00
projectUserAPI := authenticatedAPI.PathPrefix("/project/{project_id}").Subrouter()
2023-08-26 20:43:42 +02:00
projectUserAPI.Use(projects.ProjectMiddleware, projects.GetMustCanMiddleware(db.CanManageProjectResources))
projectUserAPI.Path("/role").HandlerFunc(projects.GetUserRole).Methods("GET", "HEAD")
2019-09-08 09:19:46 +02:00
projectUserAPI.Path("/events").HandlerFunc(getAllEvents).Methods("GET", "HEAD")
projectUserAPI.HandleFunc("/events/last", getLastEvents).Methods("GET", "HEAD")
2016-01-05 00:32:53 +01:00
2019-09-08 09:19:46 +02:00
projectUserAPI.Path("/users").HandlerFunc(projects.GetUsers).Methods("GET", "HEAD")
2016-01-05 00:32:53 +01:00
2019-09-08 09:19:46 +02:00
projectUserAPI.Path("/keys").HandlerFunc(projects.GetKeys).Methods("GET", "HEAD")
projectUserAPI.Path("/keys").HandlerFunc(projects.AddKey).Methods("POST")
2019-09-08 09:19:46 +02:00
projectUserAPI.Path("/repositories").HandlerFunc(projects.GetRepositories).Methods("GET", "HEAD")
projectUserAPI.Path("/repositories").HandlerFunc(projects.AddRepository).Methods("POST")
2019-09-08 09:19:46 +02:00
projectUserAPI.Path("/inventory").HandlerFunc(projects.GetInventory).Methods("GET", "HEAD")
projectUserAPI.Path("/inventory").HandlerFunc(projects.AddInventory).Methods("POST")
2019-09-08 09:19:46 +02:00
projectUserAPI.Path("/environment").HandlerFunc(projects.GetEnvironment).Methods("GET", "HEAD")
projectUserAPI.Path("/environment").HandlerFunc(projects.AddEnvironment).Methods("POST")
projectUserAPI.Path("/tasks").HandlerFunc(projects.GetAllTasks).Methods("GET", "HEAD")
projectUserAPI.HandleFunc("/tasks/last", projects.GetLastTasks).Methods("GET", "HEAD")
2019-09-08 09:19:46 +02:00
projectUserAPI.Path("/templates").HandlerFunc(projects.GetTemplates).Methods("GET", "HEAD")
projectUserAPI.Path("/templates").HandlerFunc(projects.AddTemplate).Methods("POST")
projectUserAPI.Path("/schedules").HandlerFunc(projects.AddSchedule).Methods("POST")
projectUserAPI.Path("/schedules/validate").HandlerFunc(projects.ValidateScheduleCronFormat).Methods("POST")
projectUserAPI.Path("/views").HandlerFunc(projects.GetViews).Methods("GET", "HEAD")
projectUserAPI.Path("/views").HandlerFunc(projects.AddView).Methods("POST")
2021-10-27 20:05:54 +02:00
projectUserAPI.Path("/views/positions").HandlerFunc(projects.SetViewPositions).Methods("POST")
2021-10-26 20:19:12 +02:00
//
// Updating and deleting project
projectAdminAPI := authenticatedAPI.Path("/project/{project_id}").Subrouter()
2023-08-26 20:43:42 +02:00
projectAdminAPI.Use(projects.ProjectMiddleware, projects.GetMustCanMiddleware(db.CanUpdateProject))
projectAdminAPI.Methods("PUT").HandlerFunc(projects.UpdateProject)
projectAdminAPI.Methods("DELETE").HandlerFunc(projects.DeleteProject)
//
// Manage project users
projectAdminUsersAPI := authenticatedAPI.PathPrefix("/project/{project_id}").Subrouter()
2023-08-26 20:43:42 +02:00
projectAdminUsersAPI.Use(projects.ProjectMiddleware, projects.GetMustCanMiddleware(db.CanManageProjectUsers))
projectAdminUsersAPI.Path("/users").HandlerFunc(projects.AddUser).Methods("POST")
2020-12-01 18:16:29 +01:00
projectUserManagement := projectAdminUsersAPI.PathPrefix("/users").Subrouter()
2019-09-08 09:19:46 +02:00
projectUserManagement.Use(projects.UserMiddleware)
2020-11-03 21:56:22 +01:00
projectUserManagement.HandleFunc("/{user_id}", projects.GetUsers).Methods("GET", "HEAD")
projectUserManagement.HandleFunc("/{user_id}", projects.UpdateUser).Methods("PUT")
2019-09-08 09:19:46 +02:00
projectUserManagement.HandleFunc("/{user_id}", projects.RemoveUser).Methods("DELETE")
//
// Project resources CRUD (continue)
projectKeyManagement := projectUserAPI.PathPrefix("/keys").Subrouter()
2019-09-08 09:19:46 +02:00
projectKeyManagement.Use(projects.KeyMiddleware)
2020-11-04 20:30:36 +01:00
projectKeyManagement.HandleFunc("/{key_id}", projects.GetKeys).Methods("GET", "HEAD")
2022-02-03 08:05:13 +01:00
projectKeyManagement.HandleFunc("/{key_id}/refs", projects.GetKeyRefs).Methods("GET", "HEAD")
2020-02-08 08:44:33 +01:00
projectKeyManagement.HandleFunc("/{key_id}", projects.UpdateKey).Methods("PUT")
projectKeyManagement.HandleFunc("/{key_id}", projects.RemoveKey).Methods("DELETE")
2019-09-08 09:19:46 +02:00
projectRepoManagement := projectUserAPI.PathPrefix("/repositories").Subrouter()
projectRepoManagement.Use(projects.RepositoryMiddleware)
2020-11-04 20:30:36 +01:00
projectRepoManagement.HandleFunc("/{repository_id}", projects.GetRepositories).Methods("GET", "HEAD")
2022-02-03 08:05:13 +01:00
projectRepoManagement.HandleFunc("/{repository_id}/refs", projects.GetRepositoryRefs).Methods("GET", "HEAD")
2020-02-08 08:44:33 +01:00
projectRepoManagement.HandleFunc("/{repository_id}", projects.UpdateRepository).Methods("PUT")
projectRepoManagement.HandleFunc("/{repository_id}", projects.RemoveRepository).Methods("DELETE")
2019-09-08 09:19:46 +02:00
projectInventoryManagement := projectUserAPI.PathPrefix("/inventory").Subrouter()
projectInventoryManagement.Use(projects.InventoryMiddleware)
2020-11-03 20:32:24 +01:00
projectInventoryManagement.HandleFunc("/{inventory_id}", projects.GetInventory).Methods("GET", "HEAD")
2022-02-03 08:05:13 +01:00
projectInventoryManagement.HandleFunc("/{inventory_id}/refs", projects.GetInventoryRefs).Methods("GET", "HEAD")
2020-02-08 08:44:33 +01:00
projectInventoryManagement.HandleFunc("/{inventory_id}", projects.UpdateInventory).Methods("PUT")
projectInventoryManagement.HandleFunc("/{inventory_id}", projects.RemoveInventory).Methods("DELETE")
2019-09-08 09:19:46 +02:00
projectEnvManagement := projectUserAPI.PathPrefix("/environment").Subrouter()
projectEnvManagement.Use(projects.EnvironmentMiddleware)
2020-11-03 20:32:24 +01:00
projectEnvManagement.HandleFunc("/{environment_id}", projects.GetEnvironment).Methods("GET", "HEAD")
2022-02-03 08:05:13 +01:00
projectEnvManagement.HandleFunc("/{environment_id}/refs", projects.GetEnvironmentRefs).Methods("GET", "HEAD")
2020-02-08 08:44:33 +01:00
projectEnvManagement.HandleFunc("/{environment_id}", projects.UpdateEnvironment).Methods("PUT")
projectEnvManagement.HandleFunc("/{environment_id}", projects.RemoveEnvironment).Methods("DELETE")
2019-09-08 09:19:46 +02:00
projectTmplManagement := projectUserAPI.PathPrefix("/templates").Subrouter()
projectTmplManagement.Use(projects.TemplatesMiddleware)
2020-02-08 08:44:33 +01:00
projectTmplManagement.HandleFunc("/{template_id}", projects.UpdateTemplate).Methods("PUT")
projectTmplManagement.HandleFunc("/{template_id}", projects.RemoveTemplate).Methods("DELETE")
2020-10-05 00:29:02 +02:00
projectTmplManagement.HandleFunc("/{template_id}", projects.GetTemplate).Methods("GET")
projectTmplManagement.HandleFunc("/{template_id}/refs", projects.GetTemplateRefs).Methods("GET", "HEAD")
projectTmplManagement.HandleFunc("/{template_id}/tasks", projects.GetAllTasks).Methods("GET")
projectTmplManagement.HandleFunc("/{template_id}/tasks/last", projects.GetLastTasks).Methods("GET")
projectTmplManagement.HandleFunc("/{template_id}/schedules", projects.GetTemplateSchedules).Methods("GET")
2019-09-08 09:19:46 +02:00
projectTaskManagement := projectUserAPI.PathPrefix("/tasks").Subrouter()
projectTaskManagement.Use(projects.GetTaskMiddleware)
2019-09-08 09:19:46 +02:00
projectTaskManagement.HandleFunc("/{task_id}/output", projects.GetTaskOutput).Methods("GET", "HEAD")
projectTaskManagement.HandleFunc("/{task_id}", projects.GetTask).Methods("GET", "HEAD")
projectTaskManagement.HandleFunc("/{task_id}", projects.RemoveTask).Methods("DELETE")
2021-09-06 13:05:10 +02:00
projectScheduleManagement := projectUserAPI.PathPrefix("/schedules").Subrouter()
projectScheduleManagement.Use(projects.SchedulesMiddleware)
projectScheduleManagement.HandleFunc("/{schedule_id}", projects.GetSchedule).Methods("GET", "HEAD")
projectScheduleManagement.HandleFunc("/{schedule_id}", projects.UpdateSchedule).Methods("PUT")
projectScheduleManagement.HandleFunc("/{schedule_id}", projects.RemoveSchedule).Methods("DELETE")
2019-09-08 09:19:46 +02:00
2021-10-27 18:22:52 +02:00
projectViewManagement := projectUserAPI.PathPrefix("/views").Subrouter()
2021-10-26 20:19:12 +02:00
projectViewManagement.Use(projects.ViewMiddleware)
projectViewManagement.HandleFunc("/{view_id}", projects.GetViews).Methods("GET", "HEAD")
projectViewManagement.HandleFunc("/{view_id}", projects.UpdateView).Methods("PUT")
projectViewManagement.HandleFunc("/{view_id}", projects.RemoveView).Methods("DELETE")
2021-10-27 21:48:51 +02:00
projectViewManagement.HandleFunc("/{view_id}/templates", projects.GetViewTemplates).Methods("GET", "HEAD")
2021-10-26 20:19:12 +02:00
2019-09-08 09:19:46 +02:00
if os.Getenv("DEBUG") == "1" {
defer debugPrintRoutes(r)
}
2017-02-23 00:21:49 +01:00
return r
2016-01-05 00:32:53 +01:00
}
2019-09-08 09:19:46 +02:00
func debugPrintRoutes(r *mux.Router) {
err := r.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error {
pathTemplate, err := route.GetPathTemplate()
if err == nil {
fmt.Println("ROUTE:", pathTemplate)
}
pathRegexp, err := route.GetPathRegexp()
if err == nil {
fmt.Println("Path regexp:", pathRegexp)
}
queriesTemplates, err := route.GetQueriesTemplates()
if err == nil {
fmt.Println("Queries templates:", strings.Join(queriesTemplates, ","))
}
queriesRegexps, err := route.GetQueriesRegexp()
if err == nil {
fmt.Println("Queries regexps:", strings.Join(queriesRegexps, ","))
}
methods, err := route.GetMethods()
if err == nil {
fmt.Println("Methods:", strings.Join(methods, ","))
}
fmt.Println()
return nil
})
if err != nil {
fmt.Println(err)
}
}
// nolint: gocyclo
func servePublic(w http.ResponseWriter, r *http.Request) {
webPath := "/"
if util.WebHostURL != nil {
webPath = util.WebHostURL.RequestURI()
}
path := r.URL.Path
2016-04-04 15:44:34 +02:00
if path == webPath+"api" || strings.HasPrefix(path, webPath+"api/") {
w.WriteHeader(http.StatusNotFound)
return
}
2021-05-15 14:59:06 +02:00
if !strings.Contains(path, ".") {
path = "/index.html"
}
2021-05-15 14:59:06 +02:00
path = strings.Replace(path, webPath+"/", "", 1)
split := strings.Split(path, ".")
suffix := split[len(split)-1]
2016-01-05 00:32:53 +01:00
2020-11-23 21:57:03 +01:00
var res []byte
var err error
2021-05-15 14:59:06 +02:00
res, err = publicAssets2.MustBytes(path)
2020-11-23 21:57:03 +01:00
if err != nil {
notFoundHandler(w, r)
return
}
// replace base path
2021-05-15 14:59:06 +02:00
if util.WebHostURL != nil && path == "/index.html" {
2020-11-24 13:26:02 +01:00
baseURL := util.WebHostURL.String()
if !strings.HasSuffix(baseURL, "/") {
baseURL += "/"
}
res = []byte(strings.Replace(string(res),
"<base href=\"/\">",
2020-11-24 13:26:02 +01:00
"<base href=\""+baseURL+"\">",
1))
}
contentType := "text/plain"
switch suffix {
case "png":
contentType = "image/png"
case "jpg", "jpeg":
contentType = "image/jpeg"
case "gif":
contentType = "image/gif"
case "js":
contentType = "application/javascript"
case "css":
contentType = "text/css"
case "woff":
contentType = "application/x-font-woff"
case "ttf":
contentType = "application/x-font-ttf"
case "otf":
contentType = "application/x-font-otf"
case "html":
contentType = "text/html"
}
w.Header().Set("content-type", contentType)
_, err = w.Write(res)
util.LogWarning(err)
2016-01-05 00:32:53 +01:00
}
2016-05-17 17:18:26 +02:00
func getSystemInfo(w http.ResponseWriter, r *http.Request) {
body := map[string]interface{}{
"version": util.Version,
2021-12-18 14:16:34 +01:00
"ansible": util.AnsibleVersion(),
}
2016-05-17 17:18:26 +02:00
helpers.WriteJSON(w, http.StatusOK, body)
2021-09-22 06:01:53 +02:00
}