mirror of
https://github.com/simon987/task_tracker.git
synced 2025-04-16 08:56:45 +00:00
Implement per-project webhook secret
This commit is contained in:
parent
c736cc3d98
commit
397805f915
27
api/git.go
27
api/git.go
@ -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))
|
||||
|
@ -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))
|
||||
|
@ -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"`
|
||||
}
|
||||
|
101
api/project.go
101
api/project.go
@ -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(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ database:
|
||||
log_levels: ["error", "info", "warn"]
|
||||
|
||||
git:
|
||||
webhook_secret: "very_secret_secret"
|
||||
# Github: sha1, Gogs: sha256
|
||||
webhook_hash: "sha256"
|
||||
# Github: 'X-Hub-Signature', Gogs: 'X-Gogs-Signature'
|
||||
|
@ -11,10 +11,11 @@ import (
|
||||
type ManagerRole int
|
||||
|
||||
const (
|
||||
ROLE_NONE ManagerRole = 0
|
||||
ROLE_READ ManagerRole = 1
|
||||
ROLE_EDIT ManagerRole = 2
|
||||
ROLE_MANAGE_ACCESS ManagerRole = 4
|
||||
RoleNone ManagerRole = 0
|
||||
RoleRead ManagerRole = 1
|
||||
RoleEdit ManagerRole = 2
|
||||
RoleManageAccess ManagerRole = 4
|
||||
RoleSecret ManagerRole = 8
|
||||
)
|
||||
|
||||
type Manager struct {
|
||||
@ -142,7 +143,7 @@ func (database *Database) GetManagerRoleOn(manager *Manager, projectId int64) Ma
|
||||
var role ManagerRole
|
||||
err := row.Scan(&role)
|
||||
if err != nil {
|
||||
return ROLE_NONE
|
||||
return RoleNone
|
||||
}
|
||||
|
||||
return role
|
||||
|
@ -25,14 +25,14 @@ type AssignedTasks struct {
|
||||
TaskCount int64 `json:"task_count"`
|
||||
}
|
||||
|
||||
func (database *Database) SaveProject(project *Project) (int64, error) {
|
||||
func (database *Database) SaveProject(project *Project, webhookSecret string) (int64, error) {
|
||||
db := database.getDB()
|
||||
|
||||
row := db.QueryRow(`INSERT INTO project (name, git_repo, clone_url, version, priority,
|
||||
motd, public, hidden, chain, paused)
|
||||
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,NULLIF($9, 0),$10) RETURNING id`,
|
||||
motd, public, hidden, chain, paused, webhook_secret)
|
||||
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,NULLIF($9, 0),$10,$11) RETURNING id`,
|
||||
project.Name, project.GitRepo, project.CloneUrl, project.Version, project.Priority, project.Motd,
|
||||
project.Public, project.Hidden, project.Chain, project.Paused)
|
||||
project.Public, project.Hidden, project.Chain, project.Paused, webhookSecret)
|
||||
|
||||
var id int64
|
||||
err := row.Scan(&id)
|
||||
@ -225,3 +225,23 @@ func (database *Database) SetSecret(pid int64, secret string) {
|
||||
"project": pid,
|
||||
}).Info("Set secret")
|
||||
}
|
||||
|
||||
func (database *Database) GetWebhookSecret(pid int64) (secret string, err error) {
|
||||
db := database.getDB()
|
||||
row := db.QueryRow(`SELECT webhook_secret FROM project WHERE id=$1`, pid)
|
||||
err = row.Scan(&secret)
|
||||
return
|
||||
}
|
||||
|
||||
func (database *Database) SetWebhookSecret(pid int64, secret string) (err error) {
|
||||
db := database.getDB()
|
||||
res, err := db.Exec(`UPDATE project SET webhook_secret=$1 WHERE id=$2`, secret, pid)
|
||||
handleErr(err)
|
||||
|
||||
rowsAffected, _ := res.RowsAffected()
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"project": pid,
|
||||
"rowsAffected": rowsAffected,
|
||||
}).Trace("Update webhook secret")
|
||||
return
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package test
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/simon987/task_tracker/api"
|
||||
"github.com/simon987/task_tracker/config"
|
||||
"golang.org/x/net/publicsuffix"
|
||||
@ -177,3 +178,15 @@ func getSessionCtx(username string, password string, admin bool) *http.Client {
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
func setRoleOnProject(req api.SetManagerRoleOnProjectRequest, pid int64, s *http.Client) (ar api.JsonResponse) {
|
||||
r := Post(fmt.Sprintf("/manager/set_role_for_project/%d", pid), req, nil, s)
|
||||
UnmarshalResponse(r, &ar)
|
||||
return
|
||||
}
|
||||
|
||||
func getAccountDetails(s *http.Client) (ar AccountAR) {
|
||||
r := Get("/account", nil, s)
|
||||
UnmarshalResponse(r, &ar)
|
||||
return
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ func TestWebHookNoSignature(t *testing.T) {
|
||||
|
||||
r := Post("/git/receivehook", api.GitPayload{}, nil, nil)
|
||||
|
||||
if r.StatusCode != 403 {
|
||||
if r.StatusCode == 200 {
|
||||
t.Error()
|
||||
}
|
||||
}
|
||||
@ -28,7 +28,7 @@ func TestWebHookInvalidSignature(t *testing.T) {
|
||||
client := http.Client{}
|
||||
r, _ := client.Do(req)
|
||||
|
||||
if r.StatusCode != 403 {
|
||||
if r.StatusCode == 200 {
|
||||
t.Error()
|
||||
}
|
||||
}
|
||||
@ -41,10 +41,12 @@ func TestWebHookDontUpdateVersion(t *testing.T) {
|
||||
GitRepo: "username/not_this_one",
|
||||
}).Content
|
||||
|
||||
webhookSecret := getWebhookSecret(resp.Id, testAdminCtx).Content.WebhookSecret
|
||||
|
||||
body := []byte(`{"ref": "refs/heads/master", "after": "new", "repository": {"full_name": "username/repo_name"}}`)
|
||||
bodyReader := bytes.NewReader(body)
|
||||
|
||||
mac := hmac.New(crypto.SHA1.New, config.Cfg.WebHookSecret)
|
||||
mac := hmac.New(crypto.SHA1.New, []byte(webhookSecret))
|
||||
mac.Write(body)
|
||||
signature := hex.EncodeToString(mac.Sum(nil))
|
||||
signature = "sha1=" + signature
|
||||
@ -53,11 +55,7 @@ func TestWebHookDontUpdateVersion(t *testing.T) {
|
||||
req.Header.Add("X-Hub-Signature", signature)
|
||||
|
||||
client := http.Client{}
|
||||
r, _ := client.Do(req)
|
||||
|
||||
if r.StatusCode != 200 {
|
||||
t.Error()
|
||||
}
|
||||
_, _ = client.Do(req)
|
||||
|
||||
getResp := getProjectAsAdmin(resp.Id).Content
|
||||
|
||||
@ -76,7 +74,9 @@ func TestWebHookUpdateVersion(t *testing.T) {
|
||||
body := []byte(`{"ref": "refs/heads/master", "after": "new", "repository": {"full_name": "username/repo_name"}}`)
|
||||
bodyReader := bytes.NewReader(body)
|
||||
|
||||
mac := hmac.New(crypto.SHA1.New, config.Cfg.WebHookSecret)
|
||||
webhookSecret := getWebhookSecret(resp.Id, testAdminCtx).Content.WebhookSecret
|
||||
|
||||
mac := hmac.New(crypto.SHA1.New, []byte(webhookSecret))
|
||||
mac.Write(body)
|
||||
signature := hex.EncodeToString(mac.Sum(nil))
|
||||
signature = "sha1=" + signature
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/simon987/task_tracker/api"
|
||||
"github.com/simon987/task_tracker/storage"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"testing"
|
||||
@ -465,6 +466,82 @@ func TestPausedProjectShouldNotDispatchTasks(t *testing.T) {
|
||||
}, testProject, testAdminCtx)
|
||||
}
|
||||
|
||||
func TestGetWebhookSecret(t *testing.T) {
|
||||
|
||||
resp := getWebhookSecret(testProject, testAdminCtx)
|
||||
|
||||
if resp.Ok != true {
|
||||
t.Error()
|
||||
}
|
||||
if len(resp.Content.WebhookSecret) <= 0 {
|
||||
t.Error()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetWebhookSecret(t *testing.T) {
|
||||
|
||||
resp1 := setWebhookSecret(api.SetWebhookSecretRequest{
|
||||
WebhookSecret: "new",
|
||||
}, testProject, testAdminCtx)
|
||||
|
||||
if resp1.Ok != true {
|
||||
t.Error()
|
||||
}
|
||||
|
||||
resp := getWebhookSecret(testProject, testAdminCtx)
|
||||
|
||||
if resp.Ok != true {
|
||||
t.Error()
|
||||
}
|
||||
if resp.Content.WebhookSecret != "new" {
|
||||
t.Error()
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetWebhookRequiresRole(t *testing.T) {
|
||||
|
||||
otherUser := getSessionCtx("testwebhookrole", "testwebhookrole", false)
|
||||
otherUserId := getAccountDetails(otherUser).Content.Id
|
||||
|
||||
user := getSessionCtx("testwebhookroleu", "testwebhookroleu", false)
|
||||
userId := getAccountDetails(user).Content.Id
|
||||
|
||||
resp := setRoleOnProject(api.SetManagerRoleOnProjectRequest{
|
||||
Role: storage.RoleEdit | storage.RoleManageAccess | storage.RoleRead,
|
||||
Manager: otherUserId,
|
||||
}, testProject, testAdminCtx)
|
||||
if resp.Ok != true {
|
||||
t.Fail()
|
||||
}
|
||||
resp = setRoleOnProject(api.SetManagerRoleOnProjectRequest{
|
||||
Role: storage.RoleSecret,
|
||||
Manager: userId,
|
||||
}, testProject, testAdminCtx)
|
||||
if resp.Ok != true {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
rUser := setWebhookSecret(api.SetWebhookSecretRequest{
|
||||
WebhookSecret: "test",
|
||||
}, testProject, user)
|
||||
rOther := setWebhookSecret(api.SetWebhookSecretRequest{
|
||||
WebhookSecret: "test",
|
||||
}, testProject, otherUser)
|
||||
rGuest := setWebhookSecret(api.SetWebhookSecretRequest{
|
||||
WebhookSecret: "test",
|
||||
}, testProject, nil)
|
||||
|
||||
if rUser.Ok != true {
|
||||
t.Error()
|
||||
}
|
||||
if rOther.Ok != false {
|
||||
t.Error()
|
||||
}
|
||||
if rGuest.Ok != false {
|
||||
t.Error()
|
||||
}
|
||||
}
|
||||
|
||||
func createProjectAsAdmin(req api.CreateProjectRequest) CreateProjectAR {
|
||||
return createProject(req, testAdminCtx)
|
||||
}
|
||||
@ -502,3 +579,15 @@ func getProjectList(s *http.Client) (ar ProjectListAR) {
|
||||
UnmarshalResponse(r, &ar)
|
||||
return
|
||||
}
|
||||
|
||||
func getWebhookSecret(pid int64, s *http.Client) (ar WebhookSecretAR) {
|
||||
r := Get(fmt.Sprintf("/project/webhook_secret/%d", pid), nil, s)
|
||||
UnmarshalResponse(r, &ar)
|
||||
return
|
||||
}
|
||||
|
||||
func setWebhookSecret(req api.SetWebhookSecretRequest, pid int64, s *http.Client) (ar api.JsonResponse) {
|
||||
r := Post(fmt.Sprintf("/project/webhook_secret/%d", pid), req, nil, s)
|
||||
UnmarshalResponse(r, &ar)
|
||||
return
|
||||
}
|
||||
|
@ -172,3 +172,19 @@ type ReleaseAR struct {
|
||||
Updated bool `json:"updated"`
|
||||
} `json:"content"`
|
||||
}
|
||||
|
||||
type WebhookSecretAR struct {
|
||||
Ok bool `json:"ok"`
|
||||
Message string `json:"message"`
|
||||
Content struct {
|
||||
WebhookSecret string `json:"webhook_secret"`
|
||||
} `json:"content"`
|
||||
}
|
||||
|
||||
type AccountAR struct {
|
||||
Ok bool `json:"ok"`
|
||||
Message string `json:"message"`
|
||||
Content struct {
|
||||
*storage.Manager `json:"manager"`
|
||||
} `json:"content"`
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ database:
|
||||
log_levels: ["debug", "error", "trace", "info", "warn"]
|
||||
|
||||
git:
|
||||
webhook_secret: "very_secret_secret"
|
||||
webhook_hash: "sha1"
|
||||
webhook_sig_header: "X-Hub-Signature"
|
||||
|
||||
|
@ -25,7 +25,8 @@ CREATE TABLE project
|
||||
git_repo TEXT NOT NULL,
|
||||
version TEXT NOT NULL,
|
||||
motd TEXT NOT NULL,
|
||||
secret TEXT NOT NULL DEFAULT '{}'
|
||||
secret TEXT NOT NULL DEFAULT '{}',
|
||||
webhook_secret TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE worker_access
|
||||
|
@ -106,4 +106,12 @@ export class ApiService {
|
||||
return this.http.post(this.url + `/project/secret/${pid}`, {"secret": secret})
|
||||
}
|
||||
|
||||
getWebhookSecret(pid: number) {
|
||||
return this.http.get(this.url + `/project/webhook_secret/${pid}`,)
|
||||
}
|
||||
|
||||
setWebhookSecret(pid: number, secret: string) {
|
||||
return this.http.post(this.url + `/project/webhook_secret/${pid}`, {"webhook_secret": secret})
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -51,4 +51,16 @@ export class ManagerRoleOnProject {
|
||||
this.role &= ~4
|
||||
}
|
||||
}
|
||||
|
||||
get secretRole(): boolean {
|
||||
return (this.role & 8) != 0
|
||||
}
|
||||
|
||||
set secretRole(role: boolean) {
|
||||
if (role) {
|
||||
this.role |= 8
|
||||
} else {
|
||||
this.role &= ~8
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -366,13 +366,14 @@ export class ProjectDashboardComponent implements OnInit {
|
||||
this.dialog.open(AreYouSureComponent, {
|
||||
width: '250px',
|
||||
}).afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this.project.paused = paused;
|
||||
this.apiService.updateProject(this.project).subscribe(() => {
|
||||
this.translate.get("messenger.acknowledged").subscribe(t =>
|
||||
this.messenger.show(t))
|
||||
})
|
||||
}
|
||||
this.project.paused = paused;
|
||||
this.apiService.updateProject(this.project).subscribe(() => {
|
||||
this.translate.get("messenger.acknowledged").subscribe(t =>
|
||||
this.messenger.show(t))
|
||||
}, error => {
|
||||
this.translate.get("messenger.unauthorized").subscribe(t =>
|
||||
this.messenger.show(t))
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,7 @@
|
||||
<button mat-raised-button color="primary"
|
||||
*ngIf="authService.logged"
|
||||
[routerLink]="'/project/' + project.id + '/secret'">
|
||||
<mat-icon>security</mat-icon>
|
||||
{{"project.secret" | translate}}</button>
|
||||
</div>
|
||||
</mat-expansion-panel>
|
||||
|
@ -55,6 +55,10 @@
|
||||
(change)="onRoleChange(m)"
|
||||
[disabled]="m.manager.id==auth.account.id"
|
||||
>{{"perms.manage"|translate}}</mat-checkbox>
|
||||
<mat-checkbox [(ngModel)]="m.secretRole"
|
||||
(change)="onRoleChange(m)"
|
||||
[disabled]="m.manager.id==auth.account.id"
|
||||
>{{"perms.secret"|translate}}</mat-checkbox>
|
||||
</mat-list-item>
|
||||
</mat-list>
|
||||
</mat-card-content>
|
||||
|
@ -1,22 +1,47 @@
|
||||
<div class="container">
|
||||
<mat-card>
|
||||
<mat-card class="mat-elevation-z8">
|
||||
<button mat-button [title]="'perms.refresh' | translate" style="float:right"
|
||||
(click)="refresh()">
|
||||
<mat-icon>refresh</mat-icon>
|
||||
</button>
|
||||
<mat-card-header>
|
||||
<mat-card-title>{{"secret.title" | translate}}</mat-card-title>
|
||||
<mat-card-subtitle>{{"secret.subtitle" | translate}}</mat-card-subtitle>
|
||||
</mat-card-header>
|
||||
|
||||
<mat-card-content>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>{{"secret.secret" | translate}}</mat-label>
|
||||
<textarea matInput [(ngModel)]="secret"
|
||||
[placeholder]="'secret.secret'|translate"
|
||||
name="secret"></textarea>
|
||||
</mat-form-field>
|
||||
</mat-card-content>
|
||||
<mat-card-actions>
|
||||
<button mat-raised-button color="primary"
|
||||
(click)="onUpdate()">{{"secret.update" | translate}}
|
||||
</button>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>{{"secret.secret" | translate}}</mat-label>
|
||||
<textarea matInput [(ngModel)]="secret"
|
||||
[placeholder]="'secret.secret'|translate"
|
||||
name="secret"></textarea>
|
||||
</mat-form-field>
|
||||
<mat-card style="margin-top: 2em" class="mat-elevation-z8">
|
||||
<mat-card-header>
|
||||
<mat-card-title>{{"secret.webhook_title" | translate}}</mat-card-title>
|
||||
<mat-card-subtitle>{{"secret.webhook_subtitle" | translate}}</mat-card-subtitle>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>{{"secret.webhook_secret" | translate}}</mat-label>
|
||||
<textarea matInput [(ngModel)]="webhookSecret"
|
||||
[placeholder]="'secret.webhook_secret'|translate"
|
||||
name="webhook_secret"></textarea>
|
||||
</mat-form-field>
|
||||
</mat-card-content>
|
||||
|
||||
<mat-card-actions>
|
||||
<button mat-raised-button [routerLink]="'../'">Back</button>
|
||||
<button mat-raised-button color="primary"
|
||||
(click)="onUpdate()">{{"secret.update" | translate}}</button>
|
||||
(click)="onWebhookUpdate()">{{"secret.update" | translate}}</button>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
</div>
|
||||
|
@ -13,6 +13,7 @@ import {MessengerService} from "../messenger.service";
|
||||
export class ProjectSecretComponent implements OnInit {
|
||||
|
||||
secret: string;
|
||||
webhookSecret: string;
|
||||
projectId: number;
|
||||
|
||||
constructor(private auth: AuthService,
|
||||
@ -26,6 +27,7 @@ export class ProjectSecretComponent implements OnInit {
|
||||
this.route.params.subscribe(params => {
|
||||
this.projectId = params["id"];
|
||||
this.getSecret();
|
||||
this.getWebhookSecret();
|
||||
});
|
||||
}
|
||||
|
||||
@ -37,10 +39,28 @@ export class ProjectSecretComponent implements OnInit {
|
||||
})
|
||||
}
|
||||
|
||||
getWebhookSecret() {
|
||||
this.apiService.getWebhookSecret(this.projectId).subscribe(data => {
|
||||
this.webhookSecret = data["content"]["webhook_secret"]
|
||||
}, error => {
|
||||
this.translate.get("messenger.unauthorized").subscribe(t => this.messenger.show(t))
|
||||
})
|
||||
}
|
||||
|
||||
onUpdate() {
|
||||
this.apiService.setSecret(this.projectId, this.secret).subscribe(data => {
|
||||
this.translate.get("secret.ok").subscribe(t => this.messenger.show(t))
|
||||
})
|
||||
}
|
||||
|
||||
onWebhookUpdate() {
|
||||
this.apiService.setWebhookSecret(this.projectId, this.webhookSecret).subscribe(data => {
|
||||
this.translate.get("secret.ok").subscribe(t => this.messenger.show(t))
|
||||
})
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this.getWebhookSecret();
|
||||
this.getSecret();
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,8 @@
|
||||
"login": "Login",
|
||||
"worker_dashboard": "Workers",
|
||||
"account": "Account",
|
||||
"manager_list": "Managers"
|
||||
"manager_list": "Managers",
|
||||
"back": "back"
|
||||
},
|
||||
"logs": {
|
||||
"title": "Logs",
|
||||
@ -119,6 +120,7 @@
|
||||
"read": "Read",
|
||||
"edit": "Edit",
|
||||
"manage": "Manage permissions",
|
||||
"secret": "Configure secrets",
|
||||
"set": "Changes saved",
|
||||
"workers": "Workers",
|
||||
"no_workers": "No workers have explicit access to this project",
|
||||
@ -152,6 +154,9 @@
|
||||
"secret": {
|
||||
"title": "Project secret",
|
||||
"subtitle": "You can set project configuration here. It is only accessible by workers with ASSIGN access to this project",
|
||||
"webhook_title": "Webhook secret",
|
||||
"webhook_subtitle": "You need to supply this to Github/Gogs for delivery to work",
|
||||
"webhook_secret": "Webhook secret",
|
||||
"secret": "Secret",
|
||||
"update": "Update",
|
||||
"ok": "Updated"
|
||||
|
@ -8,7 +8,8 @@
|
||||
"login": "Ouvrir un session",
|
||||
"worker_dashboard": "Workers",
|
||||
"account": "Compte",
|
||||
"manager_list": "Managers"
|
||||
"manager_list": "Managers",
|
||||
"back": "Arrière"
|
||||
},
|
||||
"logs": {
|
||||
"title": "Journaux",
|
||||
@ -116,11 +117,11 @@
|
||||
"read": "Lecture",
|
||||
"edit": "Modification",
|
||||
"manage": "Gestion des permissions",
|
||||
"secret": "Gestion des secrets",
|
||||
"set": "Changements enregistrés",
|
||||
"workers": "Workers",
|
||||
"no_workers": "Aucun Worker n'a explicitement accès à ce projet",
|
||||
"managers": "Managers",
|
||||
"secret": "Secret"
|
||||
"managers": "Managers"
|
||||
},
|
||||
"messenger": {
|
||||
"close": "Fermer",
|
||||
@ -148,8 +149,11 @@
|
||||
"alias": "Alias pertinent"
|
||||
},
|
||||
"secret": {
|
||||
"title": "Secret",
|
||||
"title": "Secret du projet",
|
||||
"subtitle": "Vous pouvez définir la configuration du projet ici. Ce n'est accessible que par les Workers ayant accès au projet",
|
||||
"webhook_title": "Secret (Webhook)",
|
||||
"webhook_subtitle": "Vous devez le donner à Github/Gogs.",
|
||||
"webhook_secret": "Secret (Webhook)",
|
||||
"secret": "Secret",
|
||||
"update": "Mettre à jour",
|
||||
"ok": "Mis à jour"
|
||||
|
Loading…
x
Reference in New Issue
Block a user