update login test to do the csrf dance

also: use pre-release of http/tester with better header handling
This commit is contained in:
Henry 2021-02-16 13:10:32 +01:00
parent 56daccbb4b
commit aefa2a266c
7 changed files with 101 additions and 60 deletions

2
go.mod
View File

@ -26,7 +26,7 @@ require (
go.cryptoscope.co/muxrpc/v2 v2.0.0-20210202162901-fe642d405dc6
go.cryptoscope.co/netwrap v0.1.1
go.cryptoscope.co/secretstream v1.2.2
go.mindeco.de v1.7.2
go.mindeco.de v1.7.3-0.20210216124941-2da08c6b0080
go.mindeco.de/ssb-refs v0.1.1-0.20210108133850-cf1f44fea870
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9

2
go.sum
View File

@ -443,6 +443,8 @@ go.mindeco.de v1.7.1 h1:OQmYUis1G5kOZdXbYyL5q4s2ywJZgzGyPJvKbYsc1AY=
go.mindeco.de v1.7.1/go.mod h1:ePOcyktbpqzhMPRBDv2gUaDd3h8QtT+DUU1DK+VbQZE=
go.mindeco.de v1.7.2 h1:nuiz9ZJbRx+HHIobKi/J8rSE4A3cf8KArzQsjlF6f1o=
go.mindeco.de v1.7.2/go.mod h1:ePOcyktbpqzhMPRBDv2gUaDd3h8QtT+DUU1DK+VbQZE=
go.mindeco.de v1.7.3-0.20210216124941-2da08c6b0080 h1:wbrGjnr0dZetkGZURd9RhsXhYW0MC3cwovcy3kXXeKw=
go.mindeco.de v1.7.3-0.20210216124941-2da08c6b0080/go.mod h1:ePOcyktbpqzhMPRBDv2gUaDd3h8QtT+DUU1DK+VbQZE=
go.mindeco.de/ssb-refs v0.1.1-0.20210108133850-cf1f44fea870 h1:TCI3AefMAaOYECvppn30+CfEB0Fn8IES1SKvvacc3/c=
go.mindeco.de/ssb-refs v0.1.1-0.20210108133850-cf1f44fea870/go.mod h1:OnBnV02ux4lLsZ39LID6yYLqSDp+dqTHb/3miYPkQFs=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=

View File

@ -21,7 +21,7 @@ func TestAllowListEmpty(t *testing.T) {
url, err := ts.Router.Get(router.AdminAllowListOverview).URL()
a.Nil(err)
html, resp := ts.Client.GetHTML(url.String(), nil)
html, resp := ts.Client.GetHTML(url.String())
a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code")
assertLocalized(t, html, []localizedElement{
@ -38,7 +38,7 @@ func TestAllowListAdd(t *testing.T) {
listURL, err := ts.Router.Get(router.AdminAllowListOverview).URL()
a.NoError(err)
html, resp := ts.Client.GetHTML(listURL.String(), nil)
html, resp := ts.Client.GetHTML(listURL.String())
a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code")
formSelection := html.Find("form#add-entry")
@ -86,7 +86,7 @@ func TestAllowList(t *testing.T) {
}
ts.AllowListDB.ListReturns(lst, nil)
html, resp := ts.Client.GetHTML("/members", nil)
html, resp := ts.Client.GetHTML("/members")
a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code")
assertLocalized(t, html, []localizedElement{
@ -102,7 +102,7 @@ func TestAllowList(t *testing.T) {
}
ts.AllowListDB.ListReturns(lst, nil)
html, resp = ts.Client.GetHTML("/members", nil)
html, resp = ts.Client.GetHTML("/members")
a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code")
assertLocalized(t, html, []localizedElement{
@ -132,7 +132,7 @@ func TestAllowListRemoveConfirmation(t *testing.T) {
urlTo := web.NewURLTo(ts.Router)
urlRemoveConfirm := urlTo(router.AdminAllowListRemoveConfirm, "id", 3)
html, resp := ts.Client.GetHTML(urlRemoveConfirm.String(), nil)
html, resp := ts.Client.GetHTML(urlRemoveConfirm.String())
a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code")
a.Equal(testKey.Ref(), html.Find("pre#verify").Text(), "has the key for verification")

View File

@ -16,7 +16,7 @@ func TestDashoard(t *testing.T) {
url, err := ts.Router.Get(router.AdminDashboard).URL()
a.Nil(err)
html, resp := ts.Client.GetHTML(url.String(), nil)
html, resp := ts.Client.GetHTML(url.String())
a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code")
assertLocalized(t, html, []localizedElement{

View File

@ -25,7 +25,7 @@ func TestIndex(t *testing.T) {
url, err := ts.Router.Get(router.CompleteIndex).URL()
r.Nil(err)
html, resp := ts.Client.GetHTML(url.String(), nil)
html, resp := ts.Client.GetHTML(url.String())
a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code")
assertLocalized(t, html, []localizedElement{
{"h1", "LandingWelcome"},
@ -46,7 +46,7 @@ func TestAbout(t *testing.T) {
url, err := ts.Router.Get(router.CompleteAbout).URL()
r.Nil(err)
html, resp := ts.Client.GetHTML(url.String(), nil)
html, resp := ts.Client.GetHTML(url.String())
a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code")
found := html.Find("h1").Text()
a.Equal("The about page", found)
@ -57,7 +57,7 @@ func TestNotFound(t *testing.T) {
a := assert.New(t)
html, resp := ts.Client.GetHTML("/some/random/ASDKLANZXC", nil)
html, resp := ts.Client.GetHTML("/some/random/ASDKLANZXC")
a.Equal(http.StatusNotFound, resp.Code, "wrong HTTP status code")
found := html.Find("h1").Text()
a.Equal("Error #404 - Not Found", found)
@ -68,7 +68,7 @@ func TestNewsRegisterd(t *testing.T) {
a := assert.New(t)
html, resp := ts.Client.GetHTML("/news/", nil)
html, resp := ts.Client.GetHTML("/news/")
a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code")
assertLocalized(t, html, []localizedElement{
{"#welcome", "NewsWelcome"},
@ -88,7 +88,7 @@ func TestRestricted(t *testing.T) {
}
for _, turl := range testURLs {
html, resp := ts.Client.GetHTML(turl, nil)
html, resp := ts.Client.GetHTML(turl)
a.Equal(http.StatusUnauthorized, resp.Code, "wrong HTTP status code for %q", turl)
found := html.Find("h1").Text()
a.Equal("Error #401 - Unauthorized", found, "wrong error message code for %q", turl)
@ -102,7 +102,7 @@ func TestLoginForm(t *testing.T) {
url, err := ts.Router.Get(router.AuthFallbackSignInForm).URL()
r.Nil(err)
html, resp := ts.Client.GetHTML(url.String(), nil)
html, resp := ts.Client.GetHTML(url.String())
a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code")
assertLocalized(t, html, []localizedElement{
@ -113,51 +113,90 @@ func TestLoginForm(t *testing.T) {
func TestFallbackAuth(t *testing.T) {
ts := setup(t)
a, r := assert.New(t), require.New(t)
loginVals := url.Values{
"user": []string{"test"},
"pass": []string{"test"},
}
ts.AuthFallbackDB.CheckReturns(int64(23), nil)
url, err := ts.Router.Get(router.AuthFallbackSignIn).URL()
r.Nil(err)
url.Host = "localhost"
url.Scheme = "http"
resp := ts.Client.PostForm(url.String(), loginVals)
a.Equal(http.StatusSeeOther, resp.Code, "wrong HTTP status code for sign in")
a.Equal(1, ts.AuthFallbackDB.CheckCallCount())
// very cheap client session
jar, err := cookiejar.New(nil)
r.NoError(err)
c := resp.Result().Cookies()
jar.SetCookies(url, c)
signInFormURL, err := ts.Router.Get(router.AuthFallbackSignInForm).URL()
r.Nil(err)
signInFormURL.Host = "localhost"
signInFormURL.Scheme = "https"
var h = http.Header(map[string][]string{})
doc, resp := ts.Client.GetHTML(signInFormURL.String())
a.Equal(http.StatusOK, resp.Code)
csrfCookie := resp.Result().Cookies()
a.Len(csrfCookie, 1, "should have one cookie for CSRF protection validation")
t.Log(csrfCookie)
jar.SetCookies(signInFormURL, csrfCookie)
csrfTokenElem := doc.Find("input[type=hidden]")
a.Equal(1, csrfTokenElem.Length())
csrfName, has := csrfTokenElem.Attr("name")
a.True(has, "should have a name attribute")
csrfValue, has := csrfTokenElem.Attr("value")
a.True(has, "should have value attribute")
loginVals := url.Values{
"user": []string{"test"},
"pass": []string{"test"},
csrfName: []string{csrfValue},
}
ts.AuthFallbackDB.CheckReturns(int64(23), nil)
t.Log(loginVals)
signInURL, err := ts.Router.Get(router.AuthFallbackSignIn).URL()
r.Nil(err)
signInURL.Host = "localhost"
signInURL.Scheme = "https"
var csrfCookieHeader = http.Header(map[string][]string{})
csrfCookieHeader.Set("Referer", "https://localhost")
cs := jar.Cookies(signInURL)
r.Len(cs, 1, "expecting one cookie for csrf")
theCookie := cs[0].String()
a.NotEqual("", theCookie, "should have a new cookie")
csrfCookieHeader.Set("Cookie", theCookie)
ts.Client.SetHeaders(csrfCookieHeader)
resp = ts.Client.PostForm(signInURL.String(), loginVals)
a.Equal(http.StatusSeeOther, resp.Code, "wrong HTTP status code for sign in")
a.Equal(1, ts.AuthFallbackDB.CheckCallCount())
sessionCookie := resp.Result().Cookies()
jar.SetCookies(signInURL, sessionCookie)
dashboardURL, err := ts.Router.Get(router.AdminDashboard).URL()
r.Nil(err)
dashboardURL.Host = "localhost"
dashboardURL.Scheme = "http"
dashboardURL.Scheme = "https"
var sessionHeader = http.Header(map[string][]string{})
cs = jar.Cookies(dashboardURL)
// TODO: why doesnt this return the csrf cookie?!
// r.Len(cs, 2, "expecting one cookie!")
for _, c := range cs {
theCookie := c.String()
a.NotEqual("", theCookie, "should have a new cookie")
sessionHeader.Add("Cookie", theCookie)
}
cs := jar.Cookies(dashboardURL)
r.Len(cs, 1, "expecting one cookie!")
theCookie := cs[0].String()
a.NotEqual("", theCookie, "should have a new cookie")
h.Set("Cookie", theCookie)
// t.Log(h)
durl := dashboardURL.String()
t.Log(durl)
// durl = "http://localhost/admin/dashboard"
// durl := "/admin"
// t.Log(durl)
html, resp := ts.Client.GetHTML(durl, &h)
// update headers
ts.Client.ClearHeaders()
ts.Client.SetHeaders(sessionHeader)
html, resp := ts.Client.GetHTML(durl)
if !a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code for dashboard") {
t.Log(html.Find("body").Text())
}
@ -170,7 +209,7 @@ func TestFallbackAuth(t *testing.T) {
testRef := refs.FeedRef{Algo: "test", ID: bytes.Repeat([]byte{0}, 16)}
ts.RoomState.AddEndpoint(testRef, nil)
html, resp = ts.Client.GetHTML(durl, &h)
html, resp = ts.Client.GetHTML(durl)
if !a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code") {
t.Log(html.Find("body").Text())
}
@ -184,7 +223,7 @@ func TestFallbackAuth(t *testing.T) {
testRef2 := refs.FeedRef{Algo: "test", ID: bytes.Repeat([]byte{1}, 16)}
ts.RoomState.AddEndpoint(testRef2, nil)
html, resp = ts.Client.GetHTML(durl, &h)
html, resp = ts.Client.GetHTML(durl)
a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code")
t.Log(html.Find("body").Text())

View File

@ -16,7 +16,7 @@ func TestOverview(t *testing.T) {
a := assert.New(t)
url, err := router.News(nil).Get(router.NewsOverview).URL()
a.Nil(err)
html, resp := ts.Client.GetHTML(url.String(), nil)
html, resp := ts.Client.GetHTML(url.String())
a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code")
// we dont test for the text values, just the i18n placeholders
a.Equal(html.Find("#welcome").Text(), "NewsWelcome")
@ -28,7 +28,7 @@ func TestPost(t *testing.T) {
url, err := router.News(nil).Get(router.NewsPost).URL()
a.Nil(err)
url.RawQuery = "id=1"
html, resp := ts.Client.GetHTML(url.String(), nil)
html, resp := ts.Client.GetHTML(url.String())
a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code")
a.Equal(html.Find("h1").Text(), db[1].Name)
}
@ -39,7 +39,7 @@ func TestURLTo(t *testing.T) {
url, err := router.News(nil).Get(router.NewsPost).URL()
a.Nil(err)
url.RawQuery = "id=1"
html, resp := ts.Client.GetHTML(url.String(), nil)
html, resp := ts.Client.GetHTML(url.String())
a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code")
a.Equal(html.Find("h1").Text(), db[1].Name)
lnk, ok := html.Find("#overview").Attr("href")

View File

@ -25,21 +25,21 @@ var Templates = func() http.FileSystem {
},
"/admin": &vfsgen۰DirInfo{
name: "admin",
modTime: time.Date(2021, 2, 15, 13, 43, 55, 578838236, time.UTC),
modTime: time.Date(2021, 2, 16, 11, 35, 6, 170573880, time.UTC),
},
"/admin/allow-list-remove-confirm.tmpl": &vfsgen۰CompressedFileInfo{
name: "allow-list-remove-confirm.tmpl",
modTime: time.Date(2021, 2, 15, 17, 42, 2, 266073953, time.UTC),
uncompressedSize: 413,
modTime: time.Date(2021, 2, 16, 11, 35, 6, 170573880, time.UTC),
uncompressedSize: 432,
compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x8c\x90\xc1\x6a\xf3\x30\x10\x06\xef\x7a\x8a\x65\xef\x7f\x42\x6e\x3f\xc1\x36\x84\xb6\x87\xd2\x42\x43\x1a\xe8\xd9\xb1\xd6\x78\x41\x5a\x19\x45\x4e\x30\x62\xdf\xbd\xc8\x4d\x4a\xa1\x97\xde\x47\xf3\x8d\x36\x67\xb0\xd4\xb3\x10\x60\xe2\xe4\x08\x41\x35\x67\xde\xfc\x17\xc0\x9d\xf5\x2c\x3b\xe7\xc2\xf5\x95\xcf\xe9\x40\x3e\x5c\xe8\x21\x48\xcf\xd1\x1f\x17\xb6\xa0\x40\x62\x41\xd5\xfc\x10\x75\x41\x12\x49\x2a\x2a\x53\x0d\x1b\x60\x5b\xe3\x95\x5c\x17\x3c\x61\xf3\x07\xf9\xc7\x8d\x55\xad\xd6\xc3\xa6\x31\xa6\x1a\x23\x2d\x96\x0b\x45\xee\xe7\x22\x59\x3d\x49\x8a\xf3\x6a\x3f\x9d\x5e\x68\x5e\x1d\xa8\x2f\xf0\x18\xa9\x31\xc6\x54\x7d\x88\x7e\xe1\xbb\x2f\x21\x42\xdb\x25\x0e\x52\x63\xce\x53\x74\xc7\x00\xd8\x96\xf9\x6d\x5b\xf6\xff\x39\x3e\xa7\x6d\x5c\x0a\x50\x15\xc1\x53\x1a\x82\xad\x71\xff\xf6\x7e\xc4\xc6\x00\x54\x2c\xe3\x94\x20\xcd\x23\xd5\x38\xb0\xb5\x24\x08\xd2\x7a\xaa\x91\x2d\xc2\xa5\x75\x13\xd5\xdf\x4d\xcf\x8f\xaa\xbf\x5e\x9d\xa7\x93\xe7\x74\x67\xf1\x7e\x85\xdb\x8f\xcb\x6c\x63\xaa\x75\x09\x6f\x8c\xc9\x99\xc4\xaa\x9a\xcf\x00\x00\x00\xff\xff\x2d\xb2\xea\x04\x9d\x01\x00\x00"),
compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x8c\x90\x41\x6b\xe3\x30\x10\x85\xef\xfa\x15\xc3\xdc\xd7\x21\xb7\x25\xd8\x82\xb0\xdb\x42\x69\xa1\x21\x0d\xf4\xec\x58\x63\x3c\x20\x8d\x8c\x2c\x27\x18\xa1\xff\x5e\xe4\x26\xa5\xf4\xd4\xfb\x37\xdf\xbc\xf7\x52\x02\x43\x3d\x0b\x01\x46\x8e\x96\x10\x72\x4e\x89\xb7\x7f\x05\x70\x6f\x1c\xcb\xde\x5a\x7f\x7d\xe1\x29\x1e\xc9\xf9\x0b\xfd\xf3\xd2\x73\x70\xa7\x95\x2d\x28\x90\x18\xc8\x59\x7d\x13\x75\x5e\x22\x49\x2c\x2a\x55\x0f\x5b\x60\xd3\xe0\x95\x6c\xe7\x1d\xa1\xfe\x85\xfc\xfd\xc6\xe6\x5c\x6f\x86\xad\x56\xaa\x1e\x03\xad\x96\x0b\x05\xee\x97\x22\xa9\x1e\x24\x86\xa5\x3a\xcc\xe7\x67\x5a\xaa\x23\xf5\x05\x1e\x03\x69\xa5\x54\xdd\xfb\xe0\x56\xbe\xfb\x14\x22\xb4\x5d\x64\x2f\x0d\xa6\x34\x07\x7b\xf2\x80\x6d\x79\xbf\x6b\xcb\xff\x3f\x96\xa7\xb8\x0b\x6b\x02\xcc\x19\xc1\x51\x1c\xbc\x69\xf0\xf0\xfa\x76\x42\xad\x00\x52\x82\xaa\x9b\x42\xff\xc8\x64\xd7\xb2\x00\x35\xcb\x38\x47\x88\xcb\x48\x0d\x0e\x6c\x0c\x09\x82\xb4\x8e\x1a\x64\x83\x70\x69\xed\x4c\xcd\x57\xcc\xa7\xff\x39\xeb\x9f\x57\xd3\x7c\x76\x1c\xef\x2c\xde\x87\xb9\x8d\x50\x92\x68\x55\x6f\x4a\x17\xad\x54\x4a\x24\x26\x67\xf5\x11\x00\x00\xff\xff\x4f\xe5\x80\x6e\xb0\x01\x00\x00"),
},
"/admin/allow-list.tmpl": &vfsgen۰CompressedFileInfo{
name: "allow-list.tmpl",
modTime: time.Date(2021, 2, 15, 17, 14, 16, 230167877, time.UTC),
uncompressedSize: 742,
modTime: time.Date(2021, 2, 16, 11, 35, 6, 170573880, time.UTC),
uncompressedSize: 765,
compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x7c\x52\x4d\x8b\xdb\x30\x10\xbd\xfb\x57\x4c\xe7\xd4\x1e\x64\x93\xee\xa5\x04\xd9\x10\xda\x1e\x4a\x0b\x5d\xb6\x81\x1e\x8b\x62\x8d\xd7\xa2\xfa\x30\xb2\x94\x34\x08\xfd\xf7\x62\xc5\xa6\x5b\xd8\xdd\x93\xed\x79\xf3\x3e\xfc\xa4\x94\x40\xd2\xa0\x2c\x01\x06\x15\x34\x21\xe4\x9c\x92\xda\x7d\xb0\x80\x07\x69\x94\x3d\x68\xed\x2e\xdf\xd4\x1c\x8e\x05\x5e\x50\x20\x2b\x21\xe7\xea\x09\xb7\x77\x36\x90\x0d\x0b\xbb\xe2\x52\x9d\xa1\xd7\x62\x9e\x5b\x9c\xc4\x23\xb1\x91\x84\x24\x8f\x5d\x05\xc0\xc7\x1d\x28\xd9\xe2\x85\x74\xef\x0c\x61\xf7\xbc\xd7\xcf\x15\xce\x99\x37\xe3\xae\xab\x78\x23\xd5\xb9\xfb\x4f\xd9\xbb\x4b\x51\x04\x78\x3a\xed\x9d\x66\x46\xb2\xdd\xfb\x0d\x9b\x8a\x9d\xd8\x84\x3f\xba\x68\xc3\xe6\x3a\x69\xc0\x7f\x43\xa8\xcb\x73\xb1\x9c\x56\x72\xd4\x85\x1d\x46\x5a\xd6\x56\xc9\x94\xbc\xb0\x8f\x04\xf5\x67\x1b\xbc\xa2\x39\xe7\x32\x06\xe0\x5a\x75\x29\xd5\xf7\xf1\xf4\x95\xae\xf5\x03\x0d\x39\xc3\x5b\x2e\x60\xf4\x34\xb4\x98\x52\xf4\xfa\xe8\x00\xc5\xf2\xa7\xfb\x92\x88\x69\x35\x87\xbd\x27\xe3\xce\xb4\xef\x9d\x1d\x94\x37\x08\xa8\x24\x42\xfd\xe5\x53\xce\xd8\x3d\x14\x8c\x37\xa2\x7b\x07\xbc\xd1\x6a\x8b\x40\x56\xae\xbe\xbc\x89\x7a\x8d\x7b\x2b\x69\x79\xbd\x7d\x8f\x77\x2f\xd4\x7b\x90\xf2\x56\xed\xdd\xca\x1c\x9c\x37\xb7\xa2\xa4\x64\x64\x83\xbf\x22\x88\x3e\x28\x67\x5f\x0b\x2e\x8a\x0c\x82\xa1\x30\x3a\xd9\xe2\xfd\xf7\x1f\xc7\xb5\x24\x00\xae\xec\x14\x03\x84\xeb\x44\x2d\x06\xfa\x13\x10\xac\x30\xd4\xe2\x14\x4f\xbf\x7e\xd3\xf5\xf9\xc5\x39\x9e\x8c\x0a\x08\x67\xa1\x23\x2d\xde\x2f\xc7\xdf\x4e\xb8\x59\xc2\x6f\x57\x04\xf8\x1b\xc6\xa0\xf1\xee\x02\x8c\x75\xd5\x56\xd4\xdf\x00\x00\x00\xff\xff\xa6\x94\x91\xce\xe6\x02\x00\x00"),
compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x7c\x52\x4d\x8b\xdb\x3c\x10\xbe\xfb\x57\xcc\x3b\xa7\xb7\x07\xdb\xa4\x7b\x29\x41\x36\x84\x7e\x40\x69\xa1\xcb\x36\xd0\x63\x51\xac\xf1\x7a\xa8\x2c\x19\x59\x4e\x1a\x84\xfe\x7b\x91\x62\xb7\xbb\xd0\xed\xc9\xf6\x3c\x7a\x3e\xe6\xb1\x42\x00\x45\x3d\x1b\x02\xf4\xec\x35\x21\xc4\x18\x02\xef\xde\x18\xc0\x83\x1a\xd9\x1c\xb4\xb6\x97\xcf\x3c\xfb\x63\x86\x13\x0a\x64\x14\xc4\x58\x3c\xe1\x76\xd6\x78\x32\x3e\xb1\x0b\xa1\xf8\x0c\x9d\x96\xf3\xdc\xe0\x24\x1f\xa9\x1c\x48\x2a\x72\xd8\x16\x00\x62\xd8\x01\xab\x06\x2f\xa4\x3b\x3b\x12\xb6\x7f\xf7\xfa\xb6\xc2\x31\x8a\x7a\xd8\xb5\x85\xa8\x15\x9f\xdb\x67\xca\xce\x5e\xb2\x22\xc0\xd3\x69\x67\x75\x39\xaa\x72\xf7\x7a\xc3\xa6\x6c\x27\x37\xe1\xb7\x76\x31\x7e\x73\x9d\x34\xe0\x9f\x21\x54\xf9\x99\x2c\xa7\x95\xbc\xe8\xcc\xf6\x03\xa5\x63\xab\x64\x08\x4e\x9a\x47\x82\xea\xbd\xf1\x8e\x69\x8e\x31\x8f\x01\x84\xe6\x36\x84\xea\x7e\x39\x7d\xa2\x6b\xf5\x40\x7d\x8c\xf0\xbf\x90\x30\x38\xea\x1b\x0c\x61\x71\xfa\x68\x01\x65\xda\x74\x9f\x13\x95\x9a\x67\xbf\x77\x34\xda\x33\xed\x3b\x6b\x7a\x76\x23\x02\xb2\x42\xa8\x3e\xbe\x8b\x11\xdb\x87\x8c\x89\x5a\xb6\xaf\x40\xd4\x9a\xb7\x08\x64\xd4\xea\x2b\xea\x45\xaf\x71\x6f\x25\xa5\xd7\xdb\xf7\x70\xf7\x42\xbd\x07\xa5\x6e\xd5\xde\xad\xcc\xde\xba\xf1\x56\x94\x52\x25\x19\xef\xae\x08\xb2\xf3\x6c\xcd\xbf\x82\xcb\x2c\x83\x30\x92\x1f\xac\x6a\xf0\xfe\xcb\xd7\xe3\x5a\x52\xca\x08\x55\x37\xbb\xfe\x03\x93\xce\xb7\x65\x2d\x89\xcd\xb4\x78\xf0\xd7\x89\x1a\xf4\xf4\xd3\x23\x18\x39\x52\x83\xd3\x72\xfa\xfe\x83\xae\xbf\xf9\xcf\x0e\xce\xcb\x69\x64\x8f\x70\x96\x7a\xa1\x14\xe9\xe5\xad\xb6\x1f\x5f\xa7\x9d\xb6\x9b\x03\xe2\xbf\xb2\x84\xda\xd9\x0b\x94\x65\x5b\x6c\xfd\xfd\x0a\x00\x00\xff\xff\x2d\xc0\x7a\xf4\xfd\x02\x00\x00"),
},
"/admin/dashboard.tmpl": &vfsgen۰CompressedFileInfo{
name: "dashboard.tmpl",
@ -50,14 +50,14 @@ var Templates = func() http.FileSystem {
},
"/auth": &vfsgen۰DirInfo{
name: "auth",
modTime: time.Date(2021, 2, 10, 16, 34, 42, 221227039, time.UTC),
modTime: time.Date(2021, 2, 16, 11, 35, 6, 170573880, time.UTC),
},
"/auth/fallback_sign_in.tmpl": &vfsgen۰CompressedFileInfo{
name: "fallback_sign_in.tmpl",
modTime: time.Date(2021, 2, 10, 16, 34, 42, 221227039, time.UTC),
uncompressedSize: 445,
modTime: time.Date(2021, 2, 16, 11, 35, 6, 170573880, time.UTC),
uncompressedSize: 470,
compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x74\x8f\xcb\x6a\xeb\x30\x10\x86\xf7\x7e\x8a\x39\xb3\x17\xc6\x67\x55\x82\x6d\xe8\xa6\xdb\x16\x1a\xe8\x5a\xb1\x26\xb1\xa8\x2e\x46\x1a\xc5\x2d\x42\xef\x5e\xec\xd8\x90\x42\xba\xd2\xe5\xfb\xe7\x9b\x99\x9c\x41\xd1\x59\x3b\x02\x64\xcd\x86\x10\x4a\xc9\x59\x37\x4f\x0e\xf0\x39\xf1\xf8\x22\x8d\x39\xc9\xe1\xf3\xb8\xc2\x85\x01\x39\x05\xa5\x54\x77\x95\x83\x77\x4c\x8e\x97\xda\xaa\x55\xfa\x0a\x83\x91\x31\x76\x38\xc9\x0b\x89\x91\xa4\xa2\x80\x7d\x05\xd0\x8e\x0d\x68\xd5\xe1\x4c\x66\xf0\x96\xb0\x7f\xd4\xe9\x63\x83\xa5\xb4\xf5\xd8\xf4\x55\x5b\x2b\x7d\xed\x7f\x79\x83\x9f\x57\x1f\xc0\xfd\xef\xe0\x8d\xb0\x4a\x34\xff\x77\x76\xf6\xc1\x82\x25\x1e\xbd\xea\xf0\xed\xf5\xfd\x88\x20\x07\xd6\xde\x75\x39\xa7\x60\x8e\x1e\x50\x26\x1e\x0f\xe7\xad\xf3\x21\xea\x8b\xd3\x0e\x4b\xb9\x09\x56\x89\x76\x53\x62\xe0\xef\x89\x3a\x64\xfa\x62\x04\x27\x2d\x75\x98\xe2\xb6\xd4\x83\xdc\x24\x63\x9c\x7d\x50\x7b\x76\x79\xff\x95\x8d\xe9\x64\x35\xef\x23\xd7\xcb\xcc\xfb\xfd\xb6\xf7\x7a\x40\xfb\x4f\x08\xa8\x83\x9f\x41\x88\xbe\xca\x99\x9c\x2a\xe5\x27\x00\x00\xff\xff\x62\x75\x88\x18\xbd\x01\x00\x00"),
compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x74\x90\xcf\x6a\xf3\x30\x10\xc4\xef\x7e\x8a\xfd\xf6\xae\xcf\xb8\xa7\x12\x6c\x43\x2f\xb9\xb6\xd0\x40\xcf\x8a\xb4\x8e\x45\x65\xc9\x48\xeb\xb8\x45\xe8\xdd\x8b\x1d\x1b\x52\x48\x75\xd1\x9f\xdf\xec\x0c\xa3\x94\x40\x53\x67\x1c\x01\xb2\x61\x4b\x08\x39\xa7\x64\xaa\x67\x07\xf8\x32\x71\x7f\x94\xd6\x9e\xa5\xfa\x3c\xad\x70\x61\x40\x4e\x43\xce\xc5\xdd\xa4\xf2\x8e\xc9\xf1\x32\x5b\xd4\xda\x5c\x41\x59\x19\x63\x83\xa3\xbc\x90\xe8\x49\x6a\x0a\xd8\x16\x00\x75\x5f\x81\xd1\x0d\xce\x64\x95\x1f\x08\xdb\x47\x49\x1f\x1b\xcc\xb9\x2e\xfb\xaa\x2d\xea\x52\x9b\x6b\xfb\xcb\x37\xf8\x79\xf5\x03\xb8\x7f\x55\xde\x8a\x41\x8b\xea\x69\x67\x9d\x0f\x03\x0c\xc4\xbd\xd7\x0d\xbe\xbd\xbe\x9f\x10\xa4\x62\xe3\x5d\x93\xd2\x14\xec\xc9\x03\xca\x89\xfb\x43\xb7\x25\x1f\xa2\xb9\x38\xe3\x30\xe7\x9b\xc1\xb2\x52\x82\xff\x2a\x86\xee\x68\xc8\xae\xb5\x77\x50\x1b\x37\x4e\x0c\xfc\x3d\x52\x83\x4c\x5f\x8c\xe0\xe4\x40\x0d\x4e\x71\x6b\xfb\x40\x37\xca\x18\x67\x1f\xf4\xae\x5d\xee\x7f\x69\xe3\x74\x1e\x0c\xef\x5d\xca\xa5\xcc\x7e\xbe\x7d\xc8\xba\x41\xfd\x4f\x08\x28\x83\x9f\x41\x88\xb6\x48\x89\x9c\xce\xf9\x27\x00\x00\xff\xff\x06\x74\xd3\xeb\xd6\x01\x00\x00"),
},
"/base.tmpl": &vfsgen۰CompressedFileInfo{
name: "base.tmpl",