Implement per-project webhook secret

This commit is contained in:
simon987
2019-02-24 15:30:38 -05:00
parent c736cc3d98
commit 397805f915
22 changed files with 378 additions and 59 deletions

View File

@@ -16,13 +16,6 @@ import (
func (api *WebAPI) ReceiveGitWebHook(r *Request) {
if !signatureValid(r) {
logrus.Error("WebHook signature does not match!")
r.Ctx.SetStatusCode(403)
_, _ = fmt.Fprintf(r.Ctx, "Signature does not match")
return
}
payload := &GitPayload{}
err := json.Unmarshal(r.Ctx.Request.Body(), payload)
if err != nil {
@@ -35,11 +28,27 @@ func (api *WebAPI) ReceiveGitWebHook(r *Request) {
}).Info("Received git WebHook")
if !isProductionBranch(payload) {
r.Ctx.SetStatusCode(400)
return
}
project := api.getAssociatedProject(payload)
if project == nil {
r.Ctx.SetStatusCode(400)
return
}
signature, err := api.Database.GetWebhookSecret(project.Id)
if err != nil {
_, _ = fmt.Fprintf(r.Ctx, err.Error())
r.Ctx.SetStatusCode(400)
return
}
if !signatureValid(r, signature) {
logrus.Error("WebHook signature does not match!")
r.Ctx.SetStatusCode(403)
_, _ = fmt.Fprintf(r.Ctx, "Signature does not match")
return
}
@@ -50,7 +59,7 @@ func (api *WebAPI) ReceiveGitWebHook(r *Request) {
handleErr(err, r)
}
func signatureValid(r *Request) (matches bool) {
func signatureValid(r *Request, webhookSignature string) (matches bool) {
signature := parseSignatureFromRequest(r.Ctx)
@@ -60,7 +69,7 @@ func signatureValid(r *Request) (matches bool) {
body := r.Ctx.PostBody()
mac := hmac.New(getHashFuncFromConfig(), config.Cfg.WebHookSecret)
mac := hmac.New(getHashFuncFromConfig(), []byte(webhookSignature))
mac.Write(body)
expectedMac := hex.EncodeToString(mac.Sum(nil))

View File

@@ -93,6 +93,8 @@ func New() *WebAPI {
api.router.POST("/project/reject_request/:id/:wid", LogRequestMiddleware(api.RejectAccessRequest))
api.router.GET("/project/secret/:id", LogRequestMiddleware(api.GetSecret))
api.router.POST("/project/secret/:id", LogRequestMiddleware(api.SetSecret))
api.router.GET("/project/webhook_secret/:id", LogRequestMiddleware(api.GetWebhookSecret))
api.router.POST("/project/webhook_secret/:id", LogRequestMiddleware(api.SetWebhookSecret))
api.router.POST("/task/submit", LogRequestMiddleware(api.SubmitTask))
api.router.GET("/task/get/:project", LogRequestMiddleware(api.GetTaskFromProject))

View File

@@ -292,3 +292,10 @@ type SetSecretRequest struct {
type GetSecretResponse struct {
Secret string `json:"secret"`
}
type SetWebhookSecretRequest struct {
WebhookSecret string `json:"webhook_secret"`
}
type GetWebhookSecretResponse struct {
WebhookSecret string `json:"webhook_secret"`
}

View File

@@ -3,6 +3,7 @@ package api
import (
"encoding/json"
"github.com/Sirupsen/logrus"
"github.com/google/uuid"
"github.com/simon987/task_tracker/storage"
"strconv"
)
@@ -97,7 +98,9 @@ func (api *WebAPI) CreateProject(r *Request) {
return
}
id, err := api.Database.SaveProject(project)
webhookSecret := makeWebhookSecret()
id, err := api.Database.SaveProject(project, webhookSecret)
if err != nil {
r.Json(JsonResponse{
Ok: false,
@@ -107,7 +110,7 @@ func (api *WebAPI) CreateProject(r *Request) {
}
api.Database.SetManagerRoleOn(manager.(*storage.Manager).Id, id,
storage.ROLE_MANAGE_ACCESS|storage.ROLE_READ|storage.ROLE_EDIT)
storage.RoleManageAccess|storage.RoleRead|storage.RoleEdit|storage.RoleSecret)
r.OkJson(JsonResponse{
Ok: true,
Content: CreateProjectResponse{
@@ -119,6 +122,10 @@ func (api *WebAPI) CreateProject(r *Request) {
}).Debug("Created project")
}
func makeWebhookSecret() string {
return uuid.New().String()
}
func (api *WebAPI) UpdateProject(r *Request) {
id, err := strconv.ParseInt(r.Ctx.UserValue("id").(string), 10, 64)
@@ -163,7 +170,7 @@ func (api *WebAPI) UpdateProject(r *Request) {
sess := api.Session.StartFasthttp(r.Ctx)
manager := sess.Get("manager")
if !isActionOnProjectAuthorized(project.Id, manager, storage.ROLE_EDIT, api.Database) {
if !isActionOnProjectAuthorized(project.Id, manager, storage.RoleEdit, api.Database) {
r.Json(JsonResponse{
Ok: false,
Message: "Unauthorized",
@@ -238,7 +245,7 @@ func isProjectReadAuthorized(project *storage.Project, manager interface{}, db *
return true
}
role := db.GetManagerRoleOn(manager.(*storage.Manager), project.Id)
if role&storage.ROLE_READ == 1 {
if role&storage.RoleRead == 1 {
return true
}
@@ -302,7 +309,7 @@ func (api *WebAPI) GetWorkerAccessListForProject(r *Request) {
return
}
if !isActionOnProjectAuthorized(id, manager, storage.ROLE_MANAGE_ACCESS, api.Database) {
if !isActionOnProjectAuthorized(id, manager, storage.RoleManageAccess, api.Database) {
r.Json(JsonResponse{
Ok: false,
Message: "Unauthorized",
@@ -391,7 +398,7 @@ func (api *WebAPI) AcceptAccessRequest(r *Request) {
sess := api.Session.StartFasthttp(r.Ctx)
manager := sess.Get("manager")
if !isActionOnProjectAuthorized(pid, manager, storage.ROLE_MANAGE_ACCESS, api.Database) {
if !isActionOnProjectAuthorized(pid, manager, storage.RoleManageAccess, api.Database) {
r.Json(JsonResponse{
Message: "Unauthorized",
Ok: false,
@@ -471,7 +478,7 @@ func (api *WebAPI) SetManagerRoleOnProject(r *Request) {
sess := api.Session.StartFasthttp(r.Ctx)
manager := sess.Get("manager")
if !isActionOnProjectAuthorized(pid, manager, storage.ROLE_MANAGE_ACCESS, api.Database) {
if !isActionOnProjectAuthorized(pid, manager, storage.RoleManageAccess, api.Database) {
r.Json(JsonResponse{
Message: "Unauthorized",
Ok: false,
@@ -500,7 +507,7 @@ func (api *WebAPI) SetSecret(r *Request) {
sess := api.Session.StartFasthttp(r.Ctx)
manager := sess.Get("manager")
if !isActionOnProjectAuthorized(pid, manager, storage.ROLE_EDIT, api.Database) {
if !isActionOnProjectAuthorized(pid, manager, storage.RoleSecret, api.Database) {
r.Json(JsonResponse{
Ok: false,
Message: "Unauthorized",
@@ -560,7 +567,7 @@ func (api *WebAPI) GetSecret(r *Request) {
sess := api.Session.StartFasthttp(r.Ctx)
manager := sess.Get("manager")
if !isActionOnProjectAuthorized(pid, manager, storage.ROLE_EDIT, api.Database) {
if !isActionOnProjectAuthorized(pid, manager, storage.RoleSecret, api.Database) {
r.Json(JsonResponse{
Ok: false,
Message: "Unauthorized",
@@ -576,3 +583,79 @@ func (api *WebAPI) GetSecret(r *Request) {
},
})
}
func (api *WebAPI) GetWebhookSecret(r *Request) {
pid, err := strconv.ParseInt(r.Ctx.UserValue("id").(string), 10, 64)
if err != nil || pid <= 0 {
r.Json(JsonResponse{
Ok: false,
Message: "Invalid project id",
}, 400)
return
}
sess := api.Session.StartFasthttp(r.Ctx)
manager := sess.Get("manager")
if !isActionOnProjectAuthorized(pid, manager, storage.RoleSecret, api.Database) {
r.Json(JsonResponse{
Ok: false,
Message: "Unauthorized",
}, 403)
return
}
secret, err := api.Database.GetWebhookSecret(pid)
r.OkJson(JsonResponse{
Ok: true,
Content: GetWebhookSecretResponse{
WebhookSecret: secret,
},
})
}
func (api *WebAPI) SetWebhookSecret(r *Request) {
pid, err := strconv.ParseInt(r.Ctx.UserValue("id").(string), 10, 64)
if err != nil || pid <= 0 {
r.Json(JsonResponse{
Ok: false,
Message: "Invalid project id",
}, 400)
return
}
req := &SetWebhookSecretRequest{}
err = json.Unmarshal(r.Ctx.Request.Body(), req)
if err != nil {
r.Json(JsonResponse{
Ok: false,
Message: "Could not parse request",
}, 400)
return
}
sess := api.Session.StartFasthttp(r.Ctx)
manager := sess.Get("manager")
if !isActionOnProjectAuthorized(pid, manager, storage.RoleSecret, api.Database) {
r.Json(JsonResponse{
Ok: false,
Message: "Unauthorized",
}, 403)
return
}
err = api.Database.SetWebhookSecret(pid, req.WebhookSecret)
if err == nil {
r.OkJson(JsonResponse{
Ok: true,
})
} else {
r.OkJson(JsonResponse{
Ok: false,
Message: err.Error(),
})
}
}