Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(242)

Side by Side Diff: tools/bug_chomper/src/server/server.go

Issue 731123003: Remove bug_chomper (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 /*
6 Serves a webpage for easy management of Skia bugs.
7
8 WARNING: This server is NOT secure and should not be made publicly
9 accessible.
10 */
11
12 package main
13
14 import (
15 "encoding/json"
16 "flag"
17 "fmt"
18 "html/template"
19 "issue_tracker"
20 "log"
21 "net/http"
22 "net/url"
23 "os"
24 "path/filepath"
25 "runtime"
26 "strconv"
27 "strings"
28 "time"
29 )
30
31 import "github.com/gorilla/securecookie"
32
33 const (
34 certFile = "certs/cert.pem"
35 keyFile = "certs/key.pem"
36 issueComment = "Edited by BugChomper"
37 oauthCallbackPath = "/oauth2callback"
38 oauthConfigFile = "oauth_client_secret.json"
39 localHost = "127.0.0.1"
40 maxSessionLen = time.Duration(3600 * time.Second)
41 priorityPrefix = "Priority-"
42 project = "skia"
43 cookieName = "BugChomperCookie"
44 )
45
46 // Flags:
47 var (
48 port = flag.String("port", ":8000", "HTTP service address (e.g., ':800 0')")
49 public = flag.Bool("public", false, "Make this server publicly accessibl e.")
50 )
51
52 var (
53 // templates is the list of html templates used by bug_chomper.
54 templates *template.Template = nil
55
56 scheme = "http"
57 hashKey = securecookie.GenerateRandomKey(32)
58 blockKey = securecookie.GenerateRandomKey(32)
59 secureCookie = securecookie.New(hashKey, blockKey)
60 )
61
62 func init() {
63 // Change the current working directory to two directories up from this
64 // source file so that we can read templates.
65 _, filename, _, _ := runtime.Caller(0)
66 cwd := filepath.Join(filepath.Dir(filename), "../..")
67 if err := os.Chdir(cwd); err != nil {
68 log.Fatal(err)
69 }
70
71 templates = template.Must(template.ParseFiles(
72 filepath.Join(cwd, "templates/bug_chomper.html"),
73 filepath.Join(cwd, "templates/submitted.html"),
74 filepath.Join(cwd, "templates/error.html"),
75 ))
76 }
77
78 // SessionState contains data for a given session.
79 type SessionState struct {
80 IssueTracker *issue_tracker.IssueTracker
81 OrigRequestURL string
82 SessionStart time.Time
83 }
84
85 // getAbsoluteURL returns the absolute URL of the given Request.
86 func getAbsoluteURL(r *http.Request) string {
87 return scheme + "://" + r.Host + r.URL.Path
88 }
89
90 // getOAuth2CallbackURL returns a callback URL to be used by the OAuth2 login
91 // page.
92 func getOAuth2CallbackURL(r *http.Request) string {
93 return scheme + "://" + r.Host + oauthCallbackPath
94 }
95
96 func saveSession(session *SessionState, w http.ResponseWriter, r *http.Request) error {
97 encodedSession, err := secureCookie.Encode(cookieName, session)
98 if err != nil {
99 return fmt.Errorf("unable to encode session state: %s", err)
100 }
101 cookie := &http.Cookie{
102 Name: cookieName,
103 Value: encodedSession,
104 Domain: strings.Split(r.Host, ":")[0],
105 Path: "/",
106 HttpOnly: true,
107 }
108 http.SetCookie(w, cookie)
109 return nil
110 }
111
112 // makeSession creates a new session for the Request.
113 func makeSession(w http.ResponseWriter, r *http.Request) (*SessionState, error) {
114 log.Println("Creating new session.")
115 // Create the session state.
116 issueTracker, err := issue_tracker.MakeIssueTracker(
117 oauthConfigFile, getOAuth2CallbackURL(r))
118 if err != nil {
119 return nil, fmt.Errorf("unable to create IssueTracker for sessio n: %s", err)
120 }
121 session := SessionState{
122 IssueTracker: issueTracker,
123 OrigRequestURL: getAbsoluteURL(r),
124 SessionStart: time.Now(),
125 }
126
127 // Encode and store the session state.
128 if err := saveSession(&session, w, r); err != nil {
129 return nil, err
130 }
131
132 return &session, nil
133 }
134
135 // getSession retrieves the active SessionState or creates and returns a new
136 // SessionState.
137 func getSession(w http.ResponseWriter, r *http.Request) (*SessionState, error) {
138 cookie, err := r.Cookie(cookieName)
139 if err != nil {
140 log.Println("No cookie found! Starting new session.")
141 return makeSession(w, r)
142 }
143 var session SessionState
144 if err := secureCookie.Decode(cookieName, cookie.Value, &session); err ! = nil {
145 log.Printf("Invalid or corrupted session. Starting another: %s", err.Error())
146 return makeSession(w, r)
147 }
148
149 currentTime := time.Now()
150 if currentTime.Sub(session.SessionStart) > maxSessionLen {
151 log.Printf("Session starting at %s is expired. Starting another. ",
152 session.SessionStart.Format(time.RFC822))
153 return makeSession(w, r)
154 }
155 saveSession(&session, w, r)
156 return &session, nil
157 }
158
159 // reportError serves the error page with the given message.
160 func reportError(w http.ResponseWriter, msg string, code int) {
161 errData := struct {
162 Code int
163 CodeString string
164 Message string
165 }{
166 Code: code,
167 CodeString: http.StatusText(code),
168 Message: msg,
169 }
170 w.WriteHeader(code)
171 err := templates.ExecuteTemplate(w, "error.html", errData)
172 if err != nil {
173 log.Println("Failed to display error.html!!")
174 }
175 }
176
177 // makeBugChomperPage builds and serves the BugChomper page.
178 func makeBugChomperPage(w http.ResponseWriter, r *http.Request) {
179 session, err := getSession(w, r)
180 if err != nil {
181 reportError(w, err.Error(), http.StatusInternalServerError)
182 return
183 }
184 issueTracker := session.IssueTracker
185 user, err := issueTracker.GetLoggedInUser()
186 if err != nil {
187 reportError(w, err.Error(), http.StatusInternalServerError)
188 return
189 }
190 log.Println("Loading bugs for " + user)
191 bugList, err := issueTracker.GetBugs(project, user)
192 if err != nil {
193 reportError(w, err.Error(), http.StatusInternalServerError)
194 return
195 }
196 bugsById := make(map[string]*issue_tracker.Issue)
197 bugsByPriority := make(map[string][]*issue_tracker.Issue)
198 for _, bug := range bugList.Items {
199 bugsById[strconv.Itoa(bug.Id)] = bug
200 var bugPriority string
201 for _, label := range bug.Labels {
202 if strings.HasPrefix(label, priorityPrefix) {
203 bugPriority = label[len(priorityPrefix):]
204 }
205 }
206 if _, ok := bugsByPriority[bugPriority]; !ok {
207 bugsByPriority[bugPriority] = make(
208 []*issue_tracker.Issue, 0)
209 }
210 bugsByPriority[bugPriority] = append(
211 bugsByPriority[bugPriority], bug)
212 }
213 bugsJson, err := json.Marshal(bugsById)
214 if err != nil {
215 reportError(w, err.Error(), http.StatusInternalServerError)
216 return
217 }
218 data := struct {
219 Title string
220 User string
221 BugsJson template.JS
222 BugsByPriority *map[string][]*issue_tracker.Issue
223 Priorities []string
224 PriorityPrefix string
225 }{
226 Title: "BugChomper",
227 User: user,
228 BugsJson: template.JS(string(bugsJson)),
229 BugsByPriority: &bugsByPriority,
230 Priorities: issue_tracker.BugPriorities,
231 PriorityPrefix: priorityPrefix,
232 }
233
234 if err := templates.ExecuteTemplate(w, "bug_chomper.html", data); err != nil {
235 reportError(w, err.Error(), http.StatusInternalServerError)
236 return
237 }
238 }
239
240 // authIfNeeded determines whether the current user is logged in. If not, it
241 // redirects to a login page. Returns true if the user is redirected and false
242 // otherwise.
243 func authIfNeeded(w http.ResponseWriter, r *http.Request) bool {
244 session, err := getSession(w, r)
245 if err != nil {
246 reportError(w, err.Error(), http.StatusInternalServerError)
247 return false
248 }
249 issueTracker := session.IssueTracker
250 if !issueTracker.IsAuthenticated() {
251 loginURL := issueTracker.MakeAuthRequestURL()
252 log.Println("Redirecting for login:", loginURL)
253 http.Redirect(w, r, loginURL, http.StatusTemporaryRedirect)
254 return true
255 }
256 return false
257 }
258
259 // submitData attempts to submit data from a POST request to the IssueTracker.
260 func submitData(w http.ResponseWriter, r *http.Request) {
261 session, err := getSession(w, r)
262 if err != nil {
263 reportError(w, err.Error(), http.StatusInternalServerError)
264 return
265 }
266 issueTracker := session.IssueTracker
267 edits := r.FormValue("all_edits")
268 var editsMap map[string]*issue_tracker.Issue
269 if err := json.Unmarshal([]byte(edits), &editsMap); err != nil {
270 errMsg := "Could not parse edits from form response: " + err.Err or()
271 reportError(w, errMsg, http.StatusInternalServerError)
272 return
273 }
274 data := struct {
275 Title string
276 Message string
277 BackLink string
278 }{}
279 if len(editsMap) == 0 {
280 data.Title = "No Changes Submitted"
281 data.Message = "You didn't change anything!"
282 data.BackLink = ""
283 if err := templates.ExecuteTemplate(w, "submitted.html", data); err != nil {
284 reportError(w, err.Error(), http.StatusInternalServerErr or)
285 return
286 }
287 return
288 }
289 errorList := make([]error, 0)
290 for issueId, newIssue := range editsMap {
291 log.Println("Editing issue " + issueId)
292 if err := issueTracker.SubmitIssueChanges(newIssue, issueComment ); err != nil {
293 errorList = append(errorList, err)
294 }
295 }
296 if len(errorList) > 0 {
297 errorStrings := ""
298 for _, err := range errorList {
299 errorStrings += err.Error() + "\n"
300 }
301 errMsg := "Not all changes could be submitted: \n" + errorString s
302 reportError(w, errMsg, http.StatusInternalServerError)
303 return
304 }
305 data.Title = "Submitted Changes"
306 data.Message = "Your changes were submitted to the issue tracker."
307 data.BackLink = ""
308 if err := templates.ExecuteTemplate(w, "submitted.html", data); err != n il {
309 reportError(w, err.Error(), http.StatusInternalServerError)
310 return
311 }
312 return
313 }
314
315 // handleBugChomper handles HTTP requests for the bug_chomper page.
316 func handleBugChomper(w http.ResponseWriter, r *http.Request) {
317 if authIfNeeded(w, r) {
318 return
319 }
320 switch r.Method {
321 case "GET":
322 makeBugChomperPage(w, r)
323 case "POST":
324 submitData(w, r)
325 }
326 }
327
328 // handleOAuth2Callback handles callbacks from the OAuth2 sign-in.
329 func handleOAuth2Callback(w http.ResponseWriter, r *http.Request) {
330 session, err := getSession(w, r)
331 if err != nil {
332 reportError(w, err.Error(), http.StatusInternalServerError)
333 }
334 issueTracker := session.IssueTracker
335 invalidLogin := "Invalid login credentials"
336 params, err := url.ParseQuery(r.URL.RawQuery)
337 if err != nil {
338 reportError(w, invalidLogin+": "+err.Error(), http.StatusForbidd en)
339 return
340 }
341 code, ok := params["code"]
342 if !ok {
343 reportError(w, invalidLogin+": redirect did not include auth cod e.",
344 http.StatusForbidden)
345 return
346 }
347 log.Println("Upgrading auth token:", code[0])
348 if err := issueTracker.UpgradeCode(code[0]); err != nil {
349 errMsg := "failed to upgrade token: " + err.Error()
350 reportError(w, errMsg, http.StatusForbidden)
351 return
352 }
353 if err := saveSession(session, w, r); err != nil {
354 reportError(w, "failed to save session: "+err.Error(),
355 http.StatusInternalServerError)
356 return
357 }
358 http.Redirect(w, r, session.OrigRequestURL, http.StatusTemporaryRedirect )
359 return
360 }
361
362 // handleRoot is the handler function for all HTTP requests at the root level.
363 func handleRoot(w http.ResponseWriter, r *http.Request) {
364 log.Println("Fetching " + r.URL.Path)
365 if r.URL.Path == "/" || r.URL.Path == "/index.html" {
366 handleBugChomper(w, r)
367 return
368 }
369 http.NotFound(w, r)
370 }
371
372 // Run the BugChomper server.
373 func main() {
374 flag.Parse()
375
376 http.HandleFunc("/", handleRoot)
377 http.HandleFunc(oauthCallbackPath, handleOAuth2Callback)
378 http.Handle("/res/", http.FileServer(http.Dir("./")))
379 log.Println("Server is running at " + scheme + "://" + localHost + *port )
380 var err error
381 if *public {
382 log.Println("WARNING: This server is not secure and should not b e made " +
383 "publicly accessible.")
384 scheme = "https"
385 err = http.ListenAndServeTLS(*port, certFile, keyFile, nil)
386 } else {
387 scheme = "http"
388 err = http.ListenAndServe(localHost+*port, nil)
389 }
390 if err != nil {
391 log.Println(err.Error())
392 }
393 }
OLDNEW
« no previous file with comments | « tools/bug_chomper/src/issue_tracker/issue_tracker.go ('k') | tools/bug_chomper/templates/bug_chomper.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698