| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 The LUCI Authors. All rights reserved. | |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | |
| 3 // that can be found in the LICENSE file. | |
| 4 | |
| 5 package settings | |
| 6 | |
| 7 import ( | |
| 8 "encoding/base64" | |
| 9 "encoding/json" | |
| 10 "fmt" | |
| 11 "net/http" | |
| 12 "strconv" | |
| 13 | |
| 14 ds "github.com/luci/gae/service/datastore" | |
| 15 "github.com/luci/luci-go/milo/api/resp" | |
| 16 "github.com/luci/luci-go/milo/appengine/model" | |
| 17 "github.com/luci/luci-go/milo/common/miloerror" | |
| 18 "github.com/luci/luci-go/server/auth" | |
| 19 "github.com/luci/luci-go/server/auth/identity" | |
| 20 "github.com/luci/luci-go/server/auth/xsrf" | |
| 21 "github.com/luci/luci-go/server/router" | |
| 22 | |
| 23 "golang.org/x/net/context" | |
| 24 ) | |
| 25 | |
| 26 type updateReq struct { | |
| 27 Theme string | |
| 28 } | |
| 29 | |
| 30 // GetTheme returns the chosen theme based on the current user. | |
| 31 func GetTheme(c context.Context, r *http.Request) Theme { | |
| 32 cfg := getUserSettings(c) | |
| 33 if cfg == nil { | |
| 34 cfg = getCookieSettings(c, r) | |
| 35 } | |
| 36 if t, ok := Themes[cfg.Theme]; ok { | |
| 37 return t | |
| 38 } | |
| 39 return Default | |
| 40 } | |
| 41 | |
| 42 func getUserSettings(c context.Context) *model.UserConfig { | |
| 43 // First get settings | |
| 44 cu := auth.CurrentUser(c) | |
| 45 if cu.Identity == identity.AnonymousIdentity { | |
| 46 return nil | |
| 47 } | |
| 48 userSettings := &model.UserConfig{UserID: cu.Identity} | |
| 49 ds.Get(c, userSettings) | |
| 50 // Even if the get fails (No user found) we still want to return an empt
y | |
| 51 // UserConfig with defaults. | |
| 52 return userSettings | |
| 53 } | |
| 54 | |
| 55 // getCookieSettings returns user settings from a cookie, or a blank slate with | |
| 56 // defaults if no settings were found. | |
| 57 func getCookieSettings(c context.Context, r *http.Request) *model.UserConfig { | |
| 58 cookie, err := r.Cookie("luci-milo") | |
| 59 config := model.UserConfig{ | |
| 60 UserID: identity.AnonymousIdentity, | |
| 61 Theme: Default.Name, | |
| 62 } | |
| 63 if err != nil { | |
| 64 return &config | |
| 65 } | |
| 66 // If this errors, then just return the default. | |
| 67 s, err := base64.StdEncoding.DecodeString(cookie.Value) | |
| 68 if err != nil { | |
| 69 panic(err) | |
| 70 } | |
| 71 err = json.Unmarshal([]byte(s), &config) | |
| 72 if err != nil { | |
| 73 panic(err) | |
| 74 } | |
| 75 return &config | |
| 76 } | |
| 77 | |
| 78 // ChangeSettings is invoked in a POST request to settings and changes either | |
| 79 // the user settings in the datastore, or the cookies if user is anon. | |
| 80 func ChangeSettings(ctx *router.Context) { | |
| 81 c, h, r := ctx.Context, ctx.Writer, ctx.Request | |
| 82 | |
| 83 // First, check XSRF token. | |
| 84 err := xsrf.Check(c, r.FormValue("xsrf_token")) | |
| 85 if err != nil { | |
| 86 h.WriteHeader(http.StatusUnauthorized) | |
| 87 h.Write([]byte("Failed XSRF check.")) | |
| 88 return | |
| 89 } | |
| 90 | |
| 91 u := &updateReq{ | |
| 92 Theme: r.FormValue("theme"), | |
| 93 } | |
| 94 validateUpdate(u) | |
| 95 s := getUserSettings(c) | |
| 96 if s == nil { | |
| 97 // User doesn't exist, just respond with a cookie. | |
| 98 s = getCookieSettings(c, r) | |
| 99 s.Theme = u.Theme | |
| 100 setCookieSettings(h, s) | |
| 101 } else { | |
| 102 changeUserSettings(c, u) | |
| 103 } | |
| 104 | |
| 105 // Redirect to the GET endpoint. | |
| 106 http.Redirect(h, r, r.URL.String(), http.StatusSeeOther) | |
| 107 } | |
| 108 | |
| 109 // setCookieSettings sets the cfg object as a base64 json serialized string. | |
| 110 func setCookieSettings(h http.ResponseWriter, cfg *model.UserConfig) { | |
| 111 s, err := json.Marshal(cfg) | |
| 112 if err != nil { | |
| 113 panic(err) | |
| 114 } | |
| 115 bs := base64.StdEncoding.EncodeToString(s) | |
| 116 cookie := http.Cookie{ | |
| 117 Name: "luci-milo", | |
| 118 Value: bs, | |
| 119 } | |
| 120 http.SetCookie(h, &cookie) | |
| 121 } | |
| 122 | |
| 123 func validateUpdate(u *updateReq) error { | |
| 124 if _, ok := Themes[u.Theme]; ok { | |
| 125 return nil | |
| 126 } | |
| 127 return fmt.Errorf("Invalid theme %s", u.Theme) | |
| 128 } | |
| 129 | |
| 130 func changeUserSettings(c context.Context, u *updateReq) error { | |
| 131 cfg := getUserSettings(c) | |
| 132 err := validateUpdate(u) | |
| 133 if err != nil { | |
| 134 return err | |
| 135 } | |
| 136 return ds.Put(c, cfg) | |
| 137 } | |
| 138 | |
| 139 func getSettings(c context.Context, r *http.Request) (*resp.Settings, error) { | |
| 140 userSettings := getUserSettings(c) | |
| 141 if userSettings == nil { | |
| 142 userSettings = getCookieSettings(c, r) | |
| 143 } | |
| 144 | |
| 145 result := &resp.Settings{} | |
| 146 result.ActionURL = r.URL.String() | |
| 147 result.Theme = &resp.Choices{ | |
| 148 Choices: GetAllThemes(), | |
| 149 Selected: userSettings.Theme, | |
| 150 } | |
| 151 | |
| 152 return result, nil | |
| 153 } | |
| 154 | |
| 155 // GetLimit extracts the "limit", "numbuilds", or "num_builds" http param from | |
| 156 // the request, or returns "-1" implying no limit was specified. | |
| 157 func GetLimit(r *http.Request) (int, error) { | |
| 158 sLimit := r.FormValue("limit") | |
| 159 if sLimit == "" { | |
| 160 sLimit = r.FormValue("numbuilds") | |
| 161 if sLimit == "" { | |
| 162 sLimit = r.FormValue("num_builds") | |
| 163 if sLimit == "" { | |
| 164 return -1, nil | |
| 165 } | |
| 166 } | |
| 167 } | |
| 168 limit, err := strconv.Atoi(sLimit) | |
| 169 if err != nil { | |
| 170 return -1, &miloerror.Error{ | |
| 171 Message: fmt.Sprintf("limit parameter value %q is not a
number: %s", sLimit, err), | |
| 172 Code: http.StatusBadRequest, | |
| 173 } | |
| 174 } | |
| 175 if limit < 0 { | |
| 176 return -1, &miloerror.Error{ | |
| 177 Message: fmt.Sprintf("limit parameter value %q is less t
han 0", sLimit), | |
| 178 Code: http.StatusBadRequest, | |
| 179 } | |
| 180 } | |
| 181 return limit, nil | |
| 182 } | |
| OLD | NEW |