go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/server/encryptedcookies/internal/utils.go (about) 1 // Copyright 2021 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package internal 16 17 import ( 18 "net/http" 19 "net/url" 20 "path" 21 "strings" 22 "time" 23 24 "go.chromium.org/luci/common/errors" 25 ) 26 27 // NormalizeURL verifies URL is parsable and that it is relative. 28 func NormalizeURL(dest string) (string, error) { 29 if dest == "" { 30 return "/", nil 31 } 32 u, err := url.Parse(dest) 33 if err != nil { 34 return "", errors.Annotate(err, "bad destination URL %q", dest).Err() 35 } 36 // Note: '//host/path' is a location on a server named 'host'. 37 if u.IsAbs() || !strings.HasPrefix(u.Path, "/") || strings.HasPrefix(u.Path, "//") { 38 return "", errors.Reason("bad absolute destination URL %q", u).Err() 39 } 40 // path.Clean removes trailing slash. It matters for URLs though. Keep it. 41 keepSlash := strings.HasSuffix(u.Path, "/") 42 u.Path = path.Clean(u.Path) 43 if !strings.HasSuffix(u.Path, "/") && keepSlash { 44 u.Path += "/" 45 } 46 if !strings.HasPrefix(u.Path, "/") { 47 return "", errors.Reason("bad destination URL %q", u).Err() 48 } 49 return u.String(), nil 50 } 51 52 // MakeRedirectURL is used to generate login and logout URLs. 53 func MakeRedirectURL(base, dest string) (string, error) { 54 dest, err := NormalizeURL(dest) 55 if err != nil { 56 return "", err 57 } 58 if dest == "/" { 59 return base, nil 60 } 61 v := url.Values{"r": {dest}} 62 return base + "?" + v.Encode(), nil 63 } 64 65 // RemoveCookie sets a cookie to a past expiration date so that the browser can 66 // remove it. 67 // 68 // It also replaces the value with junk, in unlikely case the browser decides 69 // to ignore the expiration time. 70 func RemoveCookie(rw http.ResponseWriter, r *http.Request, cookie, path string) { 71 http.SetCookie(rw, &http.Cookie{ 72 Name: cookie, 73 Value: "deleted", 74 Path: path, 75 MaxAge: -1, 76 Expires: time.Unix(1, 0), 77 }) 78 }