diff --git a/prow/cmd/deck/main.go b/prow/cmd/deck/main.go
index dec4d42c7e4a5..4ef665a6acf01 100644
--- a/prow/cmd/deck/main.go
+++ b/prow/cmd/deck/main.go
@@ -394,7 +394,7 @@ func prodOnlyMain(cfg config.Getter, o options, mux *http.ServeMux) *http.ServeM
mux.Handle("/prowjobs.js", gziphandler.GzipHandler(handleProwJobs(ja)))
mux.Handle("/badge.svg", gziphandler.GzipHandler(handleBadge(ja)))
mux.Handle("/log", gziphandler.GzipHandler(handleLog(ja)))
- mux.Handle("/rerun", gziphandler.GzipHandler(handleRerun(prowJobClient, o.rerunCreatesJob)))
+ mux.Handle("/rerun", gziphandler.GzipHandler(handleRerun(prowJobClient, o.rerunCreatesJob, cfg)))
mux.Handle("/prowjob", gziphandler.GzipHandler(handleProwJob(prowJobClient)))
if o.spyglass {
@@ -1150,7 +1150,22 @@ func handleProwJob(prowJobClient prowv1.ProwJobInterface) http.HandlerFunc {
}
}
-func handleRerun(prowJobClient prowv1.ProwJobInterface, createProwJob bool) http.HandlerFunc {
+// canTriggerJob determines whether the given user can trigger any job.
+func canTriggerJob(user string, cfg config.Getter) bool {
+ if cfg().Deck.RerunAuthConfig.AllowAnyone {
+ return true
+ }
+ if cfg().Deck.RerunAuthConfig.AuthorizedUsers != nil {
+ for _, s := range cfg().Deck.RerunAuthConfig.AuthorizedUsers {
+ if user == s {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+func handleRerun(prowJobClient prowv1.ProwJobInterface, createProwJob bool, cfg config.Getter) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("prowjob")
if name == "" {
@@ -1177,13 +1192,25 @@ func handleRerun(prowJobClient prowv1.ProwJobInterface, createProwJob bool) http
http.Error(w, "request must be of type POST", http.StatusMethodNotAllowed)
return
}
- if _, err := prowJobClient.Create(&newPJ); err != nil {
- logrus.WithError(err).Error("Error creating job")
- http.Error(w, fmt.Sprintf("Error creating job: %v", err), http.StatusInternalServerError)
+ githubCookie, err := r.Cookie("github_login")
+ if err == http.ErrNoCookie {
+ http.Redirect(w, r, "/github-login", http.StatusFound)
+ } else if err != nil {
+ http.Error(w, fmt.Sprintf("Error retrieving GitHub cookie: %v", err), http.StatusInternalServerError)
+ return
+ }
+ if canTriggerJob(githubCookie.Value, cfg) {
+ if _, err := prowJobClient.Create(&newPJ); err != nil {
+ logrus.WithError(err).Error("Error creating job")
+ http.Error(w, fmt.Sprintf("Error creating job: %v", err), http.StatusInternalServerError)
+ return
+ }
+ http.Redirect(w, r, "/?rerun=success", http.StatusFound)
+ return
+ } else {
+ http.Redirect(w, r, "/?rerun=denied", http.StatusFound)
return
}
- w.WriteHeader(http.StatusNoContent)
- return
}
b, err := yaml.Marshal(&newPJ)
if err != nil {
diff --git a/prow/cmd/deck/main_test.go b/prow/cmd/deck/main_test.go
index 2e5e5d5096e73..8e88e85032431 100644
--- a/prow/cmd/deck/main_test.go
+++ b/prow/cmd/deck/main_test.go
@@ -258,15 +258,43 @@ func TestProwJob(t *testing.T) {
func TestRerun(t *testing.T) {
testCases := []struct {
name string
+ login string
+ allowAnyone bool
+ rerunCreatesJob bool
shouldCreateProwjob bool
+ httpCode int
}{
{
name: "Handler returns ProwJob",
+ login: "hello",
+ allowAnyone: false,
+ rerunCreatesJob: true,
+ shouldCreateProwjob: true,
+ httpCode: http.StatusFound,
+ },
+ {
+ name: "User not authorized to create prow job",
+ login: "malicious",
+ allowAnyone: false,
+ rerunCreatesJob: true,
+ shouldCreateProwjob: false,
+ httpCode: http.StatusFound,
+ },
+ {
+ name: "RerunCreatesJob set to false, should not create prow job",
+ login: "hello",
+ allowAnyone: true,
+ rerunCreatesJob: false,
shouldCreateProwjob: false,
+ httpCode: http.StatusOK,
},
{
- name: "Handler creates ProwJob",
+ name: "Allow anyone set to true, creates job",
+ login: "ugh",
+ allowAnyone: true,
+ rerunCreatesJob: true,
shouldCreateProwjob: true,
+ httpCode: http.StatusFound,
},
}
@@ -292,18 +320,37 @@ func TestRerun(t *testing.T) {
State: prowapi.PendingState,
},
})
- handler := handleRerun(fakeProwJobClient.ProwV1().ProwJobs("prowjobs"), tc.shouldCreateProwjob)
+ configGetter := func() *config.Config {
+ return &config.Config{
+ ProwConfig: config.ProwConfig{
+ Deck: config.Deck{
+ RerunAuthConfig: config.RerunAuthConfig{
+ AllowAnyone: tc.allowAnyone,
+ AuthorizedUsers: []string{"hello", "world"},
+ },
+ },
+ },
+ }
+ }
+ handler := handleRerun(fakeProwJobClient.ProwV1().ProwJobs("prowjobs"), tc.rerunCreatesJob, configGetter)
req, err := http.NewRequest(http.MethodPost, "/rerun?prowjob=wowsuch", nil)
+ req.AddCookie(&http.Cookie{
+ Name: "github_login",
+ Value: tc.login,
+ Path: "/",
+ Expires: time.Now().Add(time.Hour * 24 * 30),
+ Secure: true,
+ })
if err != nil {
t.Fatalf("Error making request: %v", err)
}
rr := httptest.NewRecorder()
handler.ServeHTTP(rr, req)
+ if rr.Code != tc.httpCode {
+ t.Fatalf("Bad error code: %d", rr.Code)
+ }
if tc.shouldCreateProwjob {
- if rr.Code != http.StatusNoContent {
- t.Fatalf("Unexpected http status code: %d, expected 204", rr.Code)
- }
pjs, err := fakeProwJobClient.ProwV1().ProwJobs("prowjobs").List(metav1.ListOptions{})
if err != nil {
t.Fatalf("failed to list prowjobs: %v", err)
@@ -312,10 +359,7 @@ func TestRerun(t *testing.T) {
t.Errorf("expected to get two prowjobs, got %d", numPJs)
}
- } else {
- if rr.Code != http.StatusOK {
- t.Fatalf("Bad error code: %d", rr.Code)
- }
+ } else if !tc.rerunCreatesJob {
resp := rr.Result()
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
diff --git a/prow/cmd/deck/static/prow/prow.ts b/prow/cmd/deck/static/prow/prow.ts
index 198ba0d1fe63d..f95373790fe17 100644
--- a/prow/cmd/deck/static/prow/prow.ts
+++ b/prow/cmd/deck/static/prow/prow.ts
@@ -650,7 +650,13 @@ function redraw(fz: FuzzySearch): void {
max = 2 * 3600;
}
drawJobHistogram(totalJob, jobHistogram, now - (12 * 3600), now, max);
- if (rerunStatus != null) {
+ if (rerunStatus === "denied") {
+ modal.style.display = "block";
+ rerunCommand.innerHTML = `You don't have permission to rerun that job. Try asking @cjwagner.`;
+ } else if (rerunStatus === "success") {
+ modal.style.display = "block";
+ rerunCommand.innerHTML = `Job successfully triggered`;
+ } else if (rerunStatus != null) {
modal.style.display = "block";
rerunCommand.innerHTML = `Nice try! The direct rerun feature hasn't been implemented yet, so that button does nothing.`;
}
@@ -671,7 +677,7 @@ function createRerunCell(modal: HTMLElement, rerunElement: HTMLElement, prowjob:
copyButton.innerHTML = "file_copy";
rerunElement.appendChild(copyButton);
const runButton = document.createElement('a');
- runButton.innerHTML = "";
+ runButton.innerHTML = "";
if (login === "") {
runButton.href = `/github-login?dest=%2F?rerun=work_in_progress`;
} else {