Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The LUCI Authors. All rights reserved. | 1 // Copyright 2016 The LUCI Authors. All rights reserved. |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
| 3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
| 4 | 4 |
| 5 // Package admin implements HTTP routes for settings UI. | 5 // Package admin implements HTTP routes for settings UI. |
| 6 package admin | 6 package admin |
| 7 | 7 |
| 8 import ( | 8 import ( |
| 9 "html/template" | 9 "html/template" |
| 10 "net" | 10 "net" |
| 11 "net/http" | 11 "net/http" |
| 12 | 12 |
| 13 "github.com/julienschmidt/httprouter" | |
| 14 "golang.org/x/net/context" | 13 "golang.org/x/net/context" |
| 15 | 14 |
| 16 "github.com/luci/luci-go/common/errors" | 15 "github.com/luci/luci-go/common/errors" |
| 17 | 16 |
| 18 "github.com/luci/luci-go/server/auth" | 17 "github.com/luci/luci-go/server/auth" |
| 19 "github.com/luci/luci-go/server/auth/identity" | 18 "github.com/luci/luci-go/server/auth/identity" |
| 20 "github.com/luci/luci-go/server/auth/xsrf" | 19 "github.com/luci/luci-go/server/auth/xsrf" |
| 21 » "github.com/luci/luci-go/server/middleware" | 20 » "github.com/luci/luci-go/server/router" |
| 22 "github.com/luci/luci-go/server/templates" | 21 "github.com/luci/luci-go/server/templates" |
| 23 | 22 |
| 24 "github.com/luci/luci-go/server/settings/admin/internal/assets" | 23 "github.com/luci/luci-go/server/settings/admin/internal/assets" |
| 25 ) | 24 ) |
| 26 | 25 |
| 27 // InstallHandlers installs HTTP handlers that implement admin UI. | 26 // InstallHandlers installs HTTP handlers that implement admin UI. |
| 28 // | 27 // |
| 29 // `adminAuth` is the method that will be used to authenticate the access | 28 // `adminAuth` is the method that will be used to authenticate the access |
| 30 // (regardless of what's installed in the base context). It must be able to | 29 // (regardless of what's installed in the base context). It must be able to |
| 31 // distinguish admins (aka superusers) from non-admins. It is needed because | 30 // distinguish admins (aka superusers) from non-admins. It is needed because |
| 32 // settings UI must be usable even before auth system is configured. | 31 // settings UI must be usable even before auth system is configured. |
| 33 func InstallHandlers(r *httprouter.Router, base middleware.Base, adminAuth auth. Method) { | 32 func InstallHandlers(r *router.Router, base router.MiddlewareChain, adminAuth au th.Method) { |
| 34 tmpl := &templates.Bundle{ | 33 tmpl := &templates.Bundle{ |
| 35 Loader: templates.AssetsLoader(assets.Assets()), | 34 Loader: templates.AssetsLoader(assets.Assets()), |
| 36 DefaultTemplate: "base", | 35 DefaultTemplate: "base", |
| 37 FuncMap: template.FuncMap{ | 36 FuncMap: template.FuncMap{ |
| 38 "includeCSS": func(name string) template.CSS { | 37 "includeCSS": func(name string) template.CSS { |
| 39 return template.CSS(assets.GetAsset(name)) | 38 return template.CSS(assets.GetAsset(name)) |
| 40 }, | 39 }, |
| 41 }, | 40 }, |
| 42 DefaultArgs: func(c context.Context) (templates.Args, error) { | 41 DefaultArgs: func(c context.Context) (templates.Args, error) { |
| 43 logoutURL, err := auth.LogoutURL(c, "/") | 42 logoutURL, err := auth.LogoutURL(c, "/") |
| 44 if err != nil { | 43 if err != nil { |
| 45 return nil, err | 44 return nil, err |
| 46 } | 45 } |
| 47 return templates.Args{ | 46 return templates.Args{ |
| 48 "Email": auth.CurrentUser(c).Email, | 47 "Email": auth.CurrentUser(c).Email, |
| 49 "LogoutURL": logoutURL, | 48 "LogoutURL": logoutURL, |
| 50 }, nil | 49 }, nil |
| 51 }, | 50 }, |
| 52 } | 51 } |
| 53 | 52 |
| 54 adminDB := adminBypassDB{ | 53 adminDB := adminBypassDB{ |
| 55 auth.ErroringDB{ | 54 auth.ErroringDB{ |
| 56 Error: errors.New("admin: unexpected call to auth.DB on admin page"), | 55 Error: errors.New("admin: unexpected call to auth.DB on admin page"), |
| 57 }, | 56 }, |
| 58 } | 57 } |
| 59 | 58 |
| 60 » wrap := func(h middleware.Handler) httprouter.Handle { | 59 » rr := r.Subrouter("/admin/settings") |
| 61 » » h = adminOnly(h) | 60 » rr.Use(append( |
|
Vadim Sh.
2016/06/18 16:57:20
I think it should be in reverse now?
base, // bas
nishanths
2016/06/19 02:47:02
You're right. Done.
| |
| 62 » » h = auth.WithDB(h, func(c context.Context) (auth.DB, error) { | 61 » » base, |
| 62 » » auth.Autologin, | |
| 63 » » adminOnly, | |
| 64 » » auth.WithDB(func(c context.Context) (auth.DB, error) { | |
| 63 return adminDB, nil | 65 return adminDB, nil |
| 64 » » }) | 66 » » }), |
| 65 » » h = auth.Use(h, auth.Authenticator{adminAuth}) | 67 » » auth.Use(auth.Authenticator{adminAuth}), |
| 66 » » h = templates.WithTemplates(h, tmpl) | 68 » » templates.WithTemplates(tmpl), |
| 67 » » return base(h) | 69 » )) |
| 68 » } | |
| 69 | 70 |
| 70 » r.GET("/admin/settings", wrap(indexPage)) | 71 » rr.GET("/", nil, indexPage) |
|
nishanths
2016/06/19 02:47:02
This change expects that clients follow redirects.
nishanths
2016/06/20 15:14:29
Changed in Patch Set 33.
| |
| 71 » r.GET("/admin/settings/:SettingsKey", wrap(settingsPageGET)) | 72 » rr.GET("/:SettingsKey", nil, settingsPageGET) |
| 72 » r.POST("/admin/settings/:SettingsKey", wrap(xsrf.WithTokenCheck(settings PagePOST))) | 73 » rr.POST("/:SettingsKey", router.MiddlewareChain{xsrf.WithTokenCheck}, se ttingsPagePOST) |
| 73 } | 74 } |
| 74 | 75 |
| 75 // replyError sends HTML error page with status 500 on transient errors or 400 | 76 // replyError sends HTML error page with status 500 on transient errors or 400 |
| 76 // on fatal ones. | 77 // on fatal ones. |
| 77 func replyError(c context.Context, rw http.ResponseWriter, err error) { | 78 func replyError(c context.Context, rw http.ResponseWriter, err error) { |
| 78 if errors.IsTransient(err) { | 79 if errors.IsTransient(err) { |
| 79 rw.WriteHeader(http.StatusInternalServerError) | 80 rw.WriteHeader(http.StatusInternalServerError) |
| 80 } else { | 81 } else { |
| 81 rw.WriteHeader(http.StatusBadRequest) | 82 rw.WriteHeader(http.StatusBadRequest) |
| 82 } | 83 } |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 101 return "", nil | 102 return "", nil |
| 102 } | 103 } |
| 103 | 104 |
| 104 func (adminBypassDB) IsInWhitelist(c context.Context, ip net.IP, whitelist strin g) (bool, error) { | 105 func (adminBypassDB) IsInWhitelist(c context.Context, ip net.IP, whitelist strin g) (bool, error) { |
| 105 return false, nil | 106 return false, nil |
| 106 } | 107 } |
| 107 | 108 |
| 108 // adminOnly is middleware that ensures authenticated user is local site admin | 109 // adminOnly is middleware that ensures authenticated user is local site admin |
| 109 // aka superuser. On GAE it grants access only to users that have Editor or | 110 // aka superuser. On GAE it grants access only to users that have Editor or |
| 110 // Owner roles in the Cloud Project. | 111 // Owner roles in the Cloud Project. |
| 111 func adminOnly(h middleware.Handler) middleware.Handler { | 112 func adminOnly(c *router.Context, next router.Handler) { |
| 112 » return auth.Autologin(func(c context.Context, rw http.ResponseWriter, r *http.Request, p httprouter.Params) { | 113 » if !auth.CurrentUser(c.Context).Superuser { |
|
nishanths
2016/06/19 02:47:02
Note: auth.Autologin is not coupled with adminOnly
| |
| 113 » » if !auth.CurrentUser(c).Superuser { | 114 » » c.Writer.WriteHeader(http.StatusForbidden) |
| 114 » » » rw.WriteHeader(http.StatusForbidden) | 115 » » templates.MustRender(c.Context, c.Writer, "pages/access_denied.h tml", nil) |
| 115 » » » templates.MustRender(c, rw, "pages/access_denied.html", nil) | 116 » » return |
| 116 » » » return | 117 » } |
| 117 » » } | 118 » next(c) |
| 118 » » h(c, rw, r, p) | |
| 119 » }) | |
| 120 } | 119 } |
| OLD | NEW |