forked from gaia-pipeline/gaia
/
handler.go
164 lines (134 loc) · 4.98 KB
/
handler.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
package handlers
import (
"crypto/rand"
"errors"
"fmt"
"net/http"
"strings"
"github.com/GeertJohan/go.rice"
jwt "github.com/dgrijalva/jwt-go"
"github.com/gaia-pipeline/gaia"
scheduler "github.com/gaia-pipeline/gaia/scheduler"
"github.com/gaia-pipeline/gaia/store"
"github.com/labstack/echo"
"github.com/labstack/echo/middleware"
)
const (
// apiVersion represents the current API version
apiVersion = "v1"
)
var (
// errNotAuthorized is thrown when user wants to access resource which is protected
errNotAuthorized = errors.New("no or invalid jwt token provided. You are not authorized")
// errPathLength is a validation error during pipeline name input
errPathLength = errors.New("name of pipeline is empty or one of the path elements length exceeds 50 characters")
// errPipelineNotFound is thrown when a pipeline was not found with the given id
errPipelineNotFound = errors.New("pipeline not found with the given id")
// errInvalidPipelineID is thrown when the given pipeline id is not valid
errInvalidPipelineID = errors.New("the given pipeline id is not valid")
// errPipelineRunNotFound is thrown when a pipeline run was not found with the given id
errPipelineRunNotFound = errors.New("pipeline run not found with the given id")
// errLogNotFound is thrown when a job log file was not found
errLogNotFound = errors.New("job log file not found")
)
// storeService is an instance of store.
// Use this to talk to the store.
var storeService *store.Store
var schedulerService *scheduler.Scheduler
// jwtKey is a random generated key for jwt signing
var jwtKey []byte
// InitHandlers initializes(registers) all handlers
func InitHandlers(e *echo.Echo, store *store.Store, scheduler *scheduler.Scheduler) error {
// Set instances
storeService = store
schedulerService = scheduler
// Generate signing key for jwt
jwtKey = make([]byte, 64)
_, err := rand.Read(jwtKey)
if err != nil {
return err
}
// Define prefix
p := "/api/" + apiVersion + "/"
// --- Register handlers at echo instance ---
// Users
e.POST(p+"login", UserLogin)
e.GET(p+"users", UserGetAll)
e.POST(p+"user/password", UserChangePassword)
e.DELETE(p+"user/:username", UserDelete)
e.POST(p+"user", UserAdd)
// Pipelines
e.POST(p+"pipeline", CreatePipeline)
e.POST(p+"pipeline/gitlsremote", PipelineGitLSRemote)
e.GET(p+"pipeline/created", CreatePipelineGetAll)
e.GET(p+"pipeline/name", PipelineNameAvailable)
e.GET(p+"pipeline", PipelineGetAll)
e.GET(p+"pipeline/:pipelineid", PipelineGet)
e.POST(p+"pipeline/:pipelineid/start", PipelineStart)
e.GET(p+"pipeline/latest", PipelineGetAllWithLatestRun)
// PipelineRun
e.GET(p+"pipelinerun/:pipelineid/:runid", PipelineRunGet)
e.GET(p+"pipelinerun/:pipelineid", PipelineGetAllRuns)
e.GET(p+"pipelinerun/:pipelineid/latest", PipelineGetLatestRun)
e.GET(p+"pipelinerun/:pipelineid/:runid/log", GetJobLogs)
// Middleware
e.Use(middleware.Recover())
//e.Use(middleware.Logger())
e.Use(middleware.BodyLimit("32M"))
e.Use(authBarrier)
// Extra options
e.HideBanner = true
// Are we in production mode?
if !gaia.Cfg.DevMode {
staticAssets, err := rice.FindBox("../frontend/dist")
if err != nil {
gaia.Cfg.Logger.Error("Cannot find assets in production mode.")
return err
}
// Register handler for static assets
assetHandler := http.FileServer(staticAssets.HTTPBox())
e.GET("/", echo.WrapHandler(assetHandler))
e.GET("/favicon.ico", echo.WrapHandler(assetHandler))
e.GET("/assets/css/*", echo.WrapHandler(http.StripPrefix("/", assetHandler)))
e.GET("/assets/js/*", echo.WrapHandler(http.StripPrefix("/", assetHandler)))
e.GET("/assets/fonts/*", echo.WrapHandler(http.StripPrefix("/", assetHandler)))
e.GET("/assets/img/*", echo.WrapHandler(http.StripPrefix("/", assetHandler)))
}
return nil
}
// authBarrier is the middleware which prevents user exploits.
// It makes sure that the request contains a valid jwt token.
// TODO: Role based access
func authBarrier(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// Login and static resources are open
if strings.Contains(c.Path(), "/login") || c.Path() == "/" || strings.Contains(c.Path(), "/assets/") || c.Path() == "/favicon.ico" {
return next(c)
}
// Get JWT token
jwtRaw := c.Request().Header.Get("Authorization")
split := strings.Split(jwtRaw, " ")
if len(split) != 2 {
return c.String(http.StatusForbidden, errNotAuthorized.Error())
}
jwtString := split[1]
// Parse token
token, err := jwt.Parse(jwtString, func(token *jwt.Token) (interface{}, error) {
// Validate signing method
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
// return secret
return jwtKey, nil
})
if err != nil {
return c.String(http.StatusForbidden, err.Error())
}
// Validate token
if _, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
// All ok, continue
return next(c)
}
return c.String(http.StatusForbidden, errNotAuthorized.Error())
}
}