go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/milo/frontend/middleware_test.go (about) 1 // Copyright 2016 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 frontend 16 17 import ( 18 "bytes" 19 "html/template" 20 "net/http/httptest" 21 "testing" 22 23 "github.com/julienschmidt/httprouter" 24 "go.chromium.org/luci/appengine/gaetesting" 25 "go.chromium.org/luci/auth/identity" 26 buildbucketpb "go.chromium.org/luci/buildbucket/proto" 27 "go.chromium.org/luci/config" 28 "go.chromium.org/luci/config/cfgclient" 29 "go.chromium.org/luci/config/impl/memory" 30 "go.chromium.org/luci/milo/internal/git" 31 "go.chromium.org/luci/milo/internal/projectconfig" 32 "go.chromium.org/luci/server/auth" 33 "go.chromium.org/luci/server/auth/authtest" 34 "go.chromium.org/luci/server/router" 35 36 . "github.com/smartystreets/goconvey/convey" 37 ) 38 39 func TestFuncs(t *testing.T) { 40 t.Parallel() 41 42 Convey("Middleware Tests", t, func() { 43 Convey("Format Commit Description", func() { 44 Convey("linkify https://", func() { 45 So(formatCommitDesc("https://foo.com"), 46 ShouldEqual, 47 template.HTML("<a href=\"https://foo.com\">https://foo.com</a>")) 48 Convey("but not http://", func() { 49 So(formatCommitDesc("http://foo.com"), ShouldEqual, template.HTML("http://foo.com")) 50 }) 51 }) 52 Convey("linkify b/ and crbug/", func() { 53 So(formatCommitDesc("blah blah b/123456 blah"), ShouldEqual, template.HTML("blah blah <a href=\"http://b/123456\">b/123456</a> blah")) 54 So(formatCommitDesc("crbug:foo/123456"), ShouldEqual, template.HTML("<a href=\"https://crbug.com/foo/123456\">crbug:foo/123456</a>")) 55 }) 56 Convey("linkify Bug: lines", func() { 57 So(formatCommitDesc("\nBug: 12345\n"), ShouldEqual, template.HTML("\nBug: <a href=\"https://crbug.com/12345\">12345</a>\n")) 58 So( 59 formatCommitDesc(" > > BugS= 12345, butter:12345"), 60 ShouldEqual, 61 template.HTML(" > > BugS= <a href=\"https://crbug.com/12345\">12345</a>, "+ 62 "<a href=\"https://crbug.com/butter/12345\">butter:12345</a>")) 63 }) 64 Convey("linkify rules should not collide", func() { 65 So( 66 formatCommitDesc("I \"fixed\" https://crbug.com/123456 <today>"), 67 ShouldEqual, 68 template.HTML("I "fixed" <a href=\"https://crbug.com/123456\">https://crbug.com/123456</a> <today>")) 69 So( 70 formatCommitDesc("Bug: 12, crbug/34, https://crbug.com/56, 78"), 71 ShouldEqual, 72 template.HTML("Bug: <a href=\"https://crbug.com/12\">12</a>, <a href=\"https://crbug.com/34\">crbug/34</a>, <a href=\"https://crbug.com/56\">https://crbug.com/56</a>, <a href=\"https://crbug.com/78\">78</a>")) 73 }) 74 Convey("linkify rules interact correctly with escaping", func() { 75 So( 76 formatCommitDesc("\"https://example.com\""), 77 ShouldEqual, 78 template.HTML(""<a href=\"https://example.com\">https://example.com</a>"")) 79 So( 80 formatCommitDesc("Bug: <not a bug number, sorry>"), 81 ShouldEqual, 82 template.HTML("Bug: <not a bug number, sorry>")) 83 // This is not remotely valid of a URL, but exists to test that 84 // the linking template correctly escapes the URL, both as an 85 // attribute and as a value. 86 So( 87 formatCommitDesc("https://foo&bar<baz\"aaa>bbb"), 88 ShouldEqual, 89 template.HTML("<a href=\"https://foo&bar%3cbaz%22aaa%3ebbb\">https://foo&bar<baz"aaa>bbb</a>")) 90 }) 91 92 Convey("trimLongString", func() { 93 Convey("short", func() { 94 So(trimLongString(4, "😀😀😀😀"), ShouldEqual, "😀😀😀😀") 95 }) 96 Convey("long", func() { 97 So(trimLongString(4, "😀😀😀😀😀"), ShouldEqual, "😀😀😀…") 98 }) 99 }) 100 }) 101 102 Convey("Redirect unauthorized users to login page for projects with access restrictions", func() { 103 projectACLMiddleware := buildProjectACLMiddleware(false) 104 r := httptest.NewRecorder() 105 c := gaetesting.TestingContextWithAppID("luci-milo-dev") 106 107 // Fake user to be anonymous. 108 c = auth.WithState(c, &authtest.FakeState{Identity: identity.AnonymousIdentity}) 109 110 // Create fake internal project named "secret". 111 c = cfgclient.Use(c, memory.New(map[config.Set]memory.Files{ 112 "projects/secret": { 113 "project.cfg": "name: \"secret\"\naccess: \"group:googlers\"", 114 }, 115 })) 116 So(projectconfig.UpdateProjects(c), ShouldBeNil) 117 118 ctx := &router.Context{ 119 Writer: r, 120 Request: httptest.NewRequest("GET", "/p/secret", bytes.NewReader(nil)).WithContext(c), 121 Params: httprouter.Params{{Key: "project", Value: "secret"}}, 122 } 123 projectACLMiddleware(ctx, nil) 124 project, ok := git.ProjectFromContext(ctx.Request.Context()) 125 So(ok, ShouldBeFalse) 126 So(project, ShouldEqual, "") 127 So(r.Code, ShouldEqual, 302) 128 So(r.Result().Header["Location"], ShouldResemble, []string{"http://fake.example.com/login?dest=%2Fp%2Fsecret"}) 129 }) 130 131 Convey("Install git project to context when the user has access to the project", func() { 132 optionalProjectACLMiddleware := buildProjectACLMiddleware(true) 133 r := httptest.NewRecorder() 134 c := gaetesting.TestingContextWithAppID("luci-milo-dev") 135 136 // Fake user to be anonymous. 137 c = auth.WithState(c, &authtest.FakeState{Identity: identity.AnonymousIdentity, IdentityGroups: []string{"all"}}) 138 139 // Create fake public project named "public". 140 c = cfgclient.Use(c, memory.New(map[config.Set]memory.Files{ 141 "projects/public": { 142 "project.cfg": "name: \"public\"\naccess: \"group:all\"", 143 }, 144 })) 145 So(projectconfig.UpdateProjects(c), ShouldBeNil) 146 147 ctx := &router.Context{ 148 Writer: r, 149 Request: httptest.NewRequest("GET", "/p/public", bytes.NewReader(nil)).WithContext(c), 150 Params: httprouter.Params{{Key: "project", Value: "public"}}, 151 } 152 nextCalled := false 153 next := func(*router.Context) { 154 nextCalled = true 155 } 156 optionalProjectACLMiddleware(ctx, next) 157 project, ok := git.ProjectFromContext(ctx.Request.Context()) 158 So(project, ShouldEqual, "public") 159 So(ok, ShouldBeTrue) 160 So(nextCalled, ShouldBeTrue) 161 So(r.Code, ShouldEqual, 200) 162 }) 163 164 Convey("Don't install git project to context when the user doesn't have access to the project", func() { 165 optionalProjectACLMiddleware := buildProjectACLMiddleware(true) 166 r := httptest.NewRecorder() 167 c := gaetesting.TestingContextWithAppID("luci-milo-dev") 168 169 // Fake user to be anonymous. 170 c = auth.WithState(c, &authtest.FakeState{Identity: identity.AnonymousIdentity}) 171 172 // Create fake internal project named "secret". 173 c = cfgclient.Use(c, memory.New(map[config.Set]memory.Files{ 174 "projects/secret": { 175 "project.cfg": "name: \"secret\"\naccess: \"group:googlers\"", 176 }, 177 })) 178 So(projectconfig.UpdateProjects(c), ShouldBeNil) 179 180 ctx := &router.Context{ 181 Writer: r, 182 Request: httptest.NewRequest("GET", "/p/secret", bytes.NewReader(nil)).WithContext(c), 183 Params: httprouter.Params{{Key: "project", Value: "secret"}}, 184 } 185 nextCalled := false 186 next := func(*router.Context) { 187 nextCalled = true 188 } 189 optionalProjectACLMiddleware(ctx, next) 190 project, ok := git.ProjectFromContext(ctx.Request.Context()) 191 So(ok, ShouldBeFalse) 192 So(project, ShouldEqual, "") 193 So(nextCalled, ShouldBeTrue) 194 So(r.Code, ShouldEqual, 200) 195 }) 196 197 Convey("Convert LogDog URLs", func() { 198 So( 199 logdogLink(buildbucketpb.Log{Name: "foo", Url: "logdog://www.example.com:1234/foo/bar/+/baz"}, true), 200 ShouldEqual, 201 template.HTML(`<a href="https://www.example.com:1234/logs/foo/bar/+/baz?format=raw" aria-label="raw log foo">raw</a>`)) 202 So( 203 logdogLink(buildbucketpb.Log{Name: "foo", Url: "%zzzzz"}, true), 204 ShouldEqual, 205 template.HTML(`<a href="#invalid-logdog-link" aria-label="raw log foo">raw</a>`)) 206 So( 207 logdogLink(buildbucketpb.Log{Name: "foo", Url: "logdog://logs.chromium.org/foo/bar/+/baz"}, false), 208 ShouldEqual, 209 template.HTML(`<a href="https://logs.chromium.org/logs/foo/bar/+/baz" aria-label="raw log foo">foo</a>`)) 210 }) 211 }) 212 }