diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2019-04-05 17:59:52 +0200 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2019-04-08 14:32:32 +0200 |
| commit | 3ef496b7ba14f5099f3d09f9da0e931411c2afc0 (patch) | |
| tree | a8be358be459834235232c96bbb6bdd9d0a2e1ba /dashboard/app/handler.go | |
| parent | c34fde03ec2b778c7cb3f4463dac2e6b9c7934c9 (diff) | |
dashboard/app: split dashboard per-namespace
We now have too many namespaces and bugs.
Main page takes infinity to load.
Also almost nobody is interested in more than 1 namespace.
So split main page per-namespaces.
Diffstat (limited to 'dashboard/app/handler.go')
| -rw-r--r-- | dashboard/app/handler.go | 142 |
1 files changed, 137 insertions, 5 deletions
diff --git a/dashboard/app/handler.go b/dashboard/app/handler.go index 7fc133bee..8e2d66ad7 100644 --- a/dashboard/app/handler.go +++ b/dashboard/app/handler.go @@ -5,7 +5,12 @@ package dash import ( "bytes" + "encoding/base64" + "encoding/json" + "fmt" "net/http" + "sort" + "strings" "github.com/google/syzkaller/pkg/html" "golang.org/x/net/context" @@ -26,11 +31,12 @@ func handleContext(fn contextHandler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) if err := fn(c, w, r); err != nil { + hdr, _ := commonHeader(c, r, w, "") data := &struct { Header *uiHeader Error string }{ - Header: commonHeader(c, r), + Header: hdr, Error: err.Error(), } if err == ErrAccess { @@ -41,6 +47,10 @@ func handleContext(fn contextHandler) http.Handler { } return } + if redir, ok := err.(ErrRedirect); ok { + http.Redirect(w, r, redir.Error(), http.StatusMovedPermanently) + return + } if _, dontlog := err.(ErrDontLog); !dontlog { log.Errorf(c, "%v", err) } @@ -52,7 +62,10 @@ func handleContext(fn contextHandler) http.Handler { }) } -type ErrDontLog error +type ( + ErrDontLog error + ErrRedirect error +) func handleAuth(fn contextHandler) contextHandler { return func(c context.Context, w http.ResponseWriter, r *http.Request) error { @@ -76,17 +89,136 @@ type uiHeader struct { Admin bool LoginLink string AnalyticsTrackingID string + Subpage string + Namespace string + Namespaces []uiNamespace + Redirects []uiRedirect +} + +type uiNamespace struct { + Name string + Caption string +} + +type uiRedirect struct { + From string + To string +} + +type cookieData struct { + Namespace string `json:"namespace"` } -func commonHeader(c context.Context, r *http.Request) *uiHeader { +func commonHeader(c context.Context, r *http.Request, w http.ResponseWriter, ns string) (*uiHeader, error) { + accessLevel := accessLevel(c, r) + if ns == "" { + ns = strings.ToLower(r.URL.Path) + if ns != "" && ns[0] == '/' { + ns = ns[1:] + } + if pos := strings.IndexByte(ns, '/'); pos != -1 { + ns = ns[:pos] + } + } h := &uiHeader{ - Admin: accessLevel(c, r) == AccessAdmin, + Admin: accessLevel == AccessAdmin, AnalyticsTrackingID: config.AnalyticsTrackingID, } + const adminPage = "admin" + isAdminPage := r.URL.Path == "/"+adminPage + isBugPage := r.URL.Path == "/bug" + found := false + for ns1, cfg := range config.Namespaces { + if accessLevel < cfg.AccessLevel { + if ns1 == ns { + return nil, ErrAccess + } + continue + } + if ns1 == ns { + found = true + } + h.Namespaces = append(h.Namespaces, uiNamespace{ + Name: ns1, + Caption: cfg.DisplayTitle, + }) + // This handles redirects from old URL scheme to new scheme. + // This this should be removed at some point (Apr 5, 2019). + // Also see handling of "fixed" parameter in handleMain. + if isBugPage { + continue + } + h.Redirects = append(h.Redirects, uiRedirect{ + From: "#" + ns1, + To: "/" + ns1, + }) + fragments := []string{"managers", "open", "pending"} + for _, reporting := range cfg.Reporting { + if !reporting.moderation || accessLevel < reporting.AccessLevel { + continue + } + fragments = append(fragments, reporting.Name) + } + for _, frag := range fragments { + h.Redirects = append(h.Redirects, uiRedirect{ + From: "#" + ns1 + "-" + frag, + To: "/" + ns1 + "#" + frag, + }) + } + } + sort.Slice(h.Namespaces, func(i, j int) bool { + return h.Namespaces[i].Caption < h.Namespaces[j].Caption + }) + cookie := decodeCookie(r) + if !found { + ns = config.DefaultNamespace + if cfg := config.Namespaces[cookie.Namespace]; cfg != nil && cfg.AccessLevel <= accessLevel { + ns = cookie.Namespace + } + if accessLevel == AccessAdmin { + ns = adminPage + } + if ns != adminPage || !isAdminPage { + return nil, ErrRedirect(fmt.Errorf("/%v", ns)) + } + } + if ns != adminPage { + h.Namespace = ns + cookie.Namespace = ns + encodeCookie(w, cookie) + } if user.Current(c) == nil { h.LoginLink, _ = user.LoginURL(c, r.URL.String()) } - return h + return h, nil +} + +const cookieName = "syzkaller" + +func decodeCookie(r *http.Request) *cookieData { + cd := new(cookieData) + cookie, err := r.Cookie(cookieName) + if err != nil { + return cd + } + decoded, err := base64.StdEncoding.DecodeString(cookie.Value) + if err != nil { + return cd + } + json.Unmarshal(decoded, cd) + return cd +} + +func encodeCookie(w http.ResponseWriter, cd *cookieData) { + data, err := json.Marshal(cd) + if err != nil { + return + } + cookie := &http.Cookie{ + Name: cookieName, + Value: base64.StdEncoding.EncodeToString(data), + } + http.SetCookie(w, cookie) } var templates = html.CreateGlob("*.html") |
