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(" &gt; &gt; 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 &#34;fixed&#34; <a href=\"https://crbug.com/123456\">https://crbug.com/123456</a> &lt;today&gt;"))
    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("&#34;<a href=\"https://example.com\">https://example.com</a>&#34;"))
    79  				So(
    80  					formatCommitDesc("Bug: <not a bug number, sorry>"),
    81  					ShouldEqual,
    82  					template.HTML("Bug: &lt;not a bug number, sorry&gt;"))
    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&amp;bar%3cbaz%22aaa%3ebbb\">https://foo&amp;bar&lt;baz&#34;aaa&gt;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/&#43;/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/&#43;/baz" aria-label="raw log foo">foo</a>`))
   210  		})
   211  	})
   212  }