feature (htpasswd): new authentication middleware

This commit is contained in:
Mickael Kerjean 2022-11-09 01:40:55 +11:00
parent 92219db637
commit e0f2f29307
21 changed files with 1352 additions and 19 deletions

7
go.mod
View file

@ -3,12 +3,13 @@ module github.com/mickael-kerjean/filestash
go 1.16
require (
github.com/abbot/go-http-auth v0.4.1-0.20220112235402-e1cee1c72f2f
github.com/aws/aws-sdk-go v1.40.41
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 // indirect
github.com/creack/pty v1.1.18
github.com/cretz/bine v0.1.0
github.com/go-sql-driver/mysql v1.5.0
github.com/google/go-cmp v0.5.6 // indirect
github.com/google/go-cmp v0.5.8 // indirect
github.com/gorilla/mux v1.7.3
github.com/gorilla/websocket v1.4.1
github.com/h2non/bimg v1.1.5
@ -28,12 +29,14 @@ require (
github.com/tidwall/gjson v1.13.0
github.com/tidwall/sjson v1.0.4
github.com/wayneashleyberry/terminal-dimensions v1.1.0 // indirect
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871
golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed
golang.org/x/image v0.0.0-20210622092929-e6eecd499c2c
golang.org/x/mod v0.4.1 // indirect
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9
golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963 // indirect
google.golang.org/api v0.15.0
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect

18
go.sum
View file

@ -3,6 +3,8 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/abbot/go-http-auth v0.4.1-0.20220112235402-e1cee1c72f2f h1:R2ZVGCZzU95oXFJxncosHS9LsX8N4/MYUdGGWOb2cFk=
github.com/abbot/go-http-auth v0.4.1-0.20220112235402-e1cee1c72f2f/go.mod h1:l2P3JyHa+fjy5Bxol6y1u2o4DV/mv3QMBdBu2cNR53w=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
@ -43,8 +45,8 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
@ -155,8 +157,9 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 h1:/pEO3GD/ABYAjuakUS6xSEmmlyVS4kxBNkeA9tLJiTI=
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed h1:YoWVYYAfvQ4ddHv3OKmIvX7NCAhFGTj62VP2l2kfBbA=
golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/image v0.0.0-20210622092929-e6eecd499c2c h1:FRR4fGZm/CMwZka5baQ4z8c8StbxJOMjS/45e0BAxK0=
golang.org/x/image v0.0.0-20210622092929-e6eecd499c2c/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
@ -165,8 +168,9 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1 h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -177,6 +181,7 @@ golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@ -225,8 +230,9 @@ golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78 h1:M8tBwCtWD/cZV9DZpFYRUgaymAYAr+aIUTWzDaM3uPs=
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963 h1:K+NlvTLy0oONtRtkl1jRD9xIhnItbG2PiE7YOdjPb+k=
golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View file

@ -133,17 +133,22 @@ func Page(stuff string) string {
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<style>
html { background: #f4f4f4; color: #455164; font-size: 16px; font-family: -apple-system,system-ui,BlinkMacSystemFont,Roboto,"Helvetica Neue",Arial,sans-serif; }
html { background: #f4f4f4; font-size: 16px; font-family: -apple-system,system-ui,BlinkMacSystemFont,Roboto,"Helvetica Neue",Arial,sans-serif; }
body { text-align: center; padding-top: 50px; text-align: center; margin: 0; }
h1 { font-weight: 200; line-height: 1em; font-size: 40px; }
p { opacity: 0.8; font-size: 1.05em; }
form { max-width: 500px; margin: 0 auto; padding: 0 10px; text-align: left; }
button { padding: 7px 0px; width: 100%; margin-top: 5px; cursor: pointer; }
form { max-width: 450px; margin: 0 auto; padding: 0 10px; text-align: left; }
button { padding: 7px 0px; width: 100%; margin-top: 5px; cursor: pointer; font-weight: bold; background: rgba(0,0,0,0.2); border-color: rgba(0,0,0,0.2); border-radius: 3px; }
input, textarea { display: block; margin: 5px 0; border-radius: 3px; border: 2px solid rgba(0,0,0,0.1); outline: none; padding: 8px 10px; min-width: 100%; max-width: 100%; max-height: 80px; box-sizing: border-box; }
input, textarea, body, button { color: #313538; }
</style>
</head>
<body>
<body class="common_response_page">
` + stuff + `
<style>
` + Hooks.Get.CSS() + `
` + Config.Get("general.custom_css").String() + `
</style>
</body>
</html>`
}

View file

@ -302,11 +302,17 @@ func SessionAuthMiddleware(ctx *App, res http.ResponseWriter, req *http.Request)
// - target of a html form. eg: ldap, mysql, ...
// - identity provider redirection uri. eg: oauth2, openid, ...
templateBind, err := plugin.Callback(formData, idpParams, res)
if err != nil {
if err == ErrAuthenticationFailed {
http.Redirect(
res, req,
req.URL.Path+"?action=redirect",
http.StatusSeeOther,
)
return
} else if err != nil {
Log.Error("session::authMiddleware 'callback error - %s'", err.Error())
http.Redirect(
res,
req,
res, req,
"/?error="+ErrNotAllowed.Error()+"&trace=redirect request failed - "+err.Error(),
http.StatusSeeOther,
)

View file

@ -3,6 +3,7 @@ package plugin
import (
. "github.com/mickael-kerjean/filestash/server/common"
_ "github.com/mickael-kerjean/filestash/server/plugin/plg_authenticate_admin"
_ "github.com/mickael-kerjean/filestash/server/plugin/plg_authenticate_htpasswd"
_ "github.com/mickael-kerjean/filestash/server/plugin/plg_authenticate_ldap"
_ "github.com/mickael-kerjean/filestash/server/plugin/plg_authenticate_openid"
_ "github.com/mickael-kerjean/filestash/server/plugin/plg_authenticate_passthrough"

View file

@ -0,0 +1,94 @@
package plg_authenticate_passthrough
import (
"fmt"
auth "github.com/abbot/go-http-auth"
. "github.com/mickael-kerjean/filestash/server/common"
"net/http"
"strings"
)
func init() {
Hooks.Register.AuthenticationMiddleware("htpasswd", Htpasswd{})
}
type Htpasswd struct{}
func (this Htpasswd) Setup() Form {
return Form{
Elmnts: []FormElement{
{
Name: "type",
Type: "hidden",
Value: "htpasswd",
},
{
Name: "users",
Type: "long_text",
Placeholder: "eg:\nbob123:$apr1$FaPCZHMe$jYiw5.9UevKx25pBH4AsT/\nnancy456:$apr1$mrCHcVhc$oNdJeRcWKPk2z8dlzQI0x/",
Default: "",
Description: "The list of users that will be granted access using the htpasswd file format. This plugin exposes the following variables which you can use from the attribute mapping: {{ .user }}, {{ .password }}",
},
},
}
}
func (this Htpasswd) EntryPoint(idpParams map[string]string, req *http.Request, res http.ResponseWriter) error {
getFlash := func() string {
c, err := req.Cookie("flash")
if err != nil {
return ""
}
http.SetCookie(res, &http.Cookie{
Name: "flash",
MaxAge: -1,
Path: "/",
})
return fmt.Sprintf(`<p class="flash">%s</p>`, c.Value)
}
res.Header().Set("Content-Type", "text/html; charset=utf-8")
res.WriteHeader(http.StatusOK)
res.Write([]byte(Page(`
<form action="/api/session/auth/" method="post" class="component_middleware">
<label>
<input type="text" name="user" value="" placeholder="User" />
</label>
<label>
<input type="password" name="password" value="" placeholder="Password" />
</label>
<button>CONNECT</button>
` + getFlash() + `
<style>.flash{ color: #f26d6d; font-weight: bold; }</style>
</form>`)))
return nil
}
func (this Htpasswd) Callback(formData map[string]string, idpParams map[string]string, res http.ResponseWriter) (map[string]string, error) {
lines := strings.Split(idpParams["users"], "\n")
if len(lines) == 0 {
Log.Error("plg_authenticate_htpasswd::callback there is no user configured")
return nil, ErrAuthenticationFailed
}
for _, line := range lines {
pair := strings.SplitN(line, ":", 2)
if len(pair) != 2 {
continue
} else if formData["user"] != pair[0] {
continue
} else if auth.CheckSecret(formData["password"], pair[1]) == false {
continue
}
return map[string]string{
"user": formData["user"],
"password": formData["password"],
}, nil
}
http.SetCookie(res, &http.Cookie{
Name: "flash",
Value: "Invalid username or password",
MaxAge: 1,
Path: "/",
})
return nil, ErrAuthenticationFailed
}

5
vendor/github.com/abbot/go-http-auth/.gitignore generated vendored Normal file
View file

@ -0,0 +1,5 @@
*~
*.a
*.6
*.out
_testmain.go

178
vendor/github.com/abbot/go-http-auth/LICENSE generated vendored Normal file
View file

@ -0,0 +1,178 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

12
vendor/github.com/abbot/go-http-auth/Makefile generated vendored Normal file
View file

@ -0,0 +1,12 @@
include $(GOROOT)/src/Make.inc
TARG=auth_digest
GOFILES=\
auth.go\
digest.go\
basic.go\
misc.go\
md5crypt.go\
users.go\
include $(GOROOT)/src/Make.pkg

71
vendor/github.com/abbot/go-http-auth/README.md generated vendored Normal file
View file

@ -0,0 +1,71 @@
HTTP Authentication implementation in Go
========================================
This is an implementation of HTTP Basic and HTTP Digest authentication
in Go language. It is designed as a simple wrapper for
http.RequestHandler functions.
Features
--------
* Supports HTTP Basic and HTTP Digest authentication.
* Supports htpasswd and htdigest formatted files.
* Automatic reloading of password files.
* Pluggable interface for user/password storage.
* Supports MD5, SHA1 and BCrypt for Basic authentication password storage.
* Configurable Digest nonce cache size with expiration.
* Wrapper for legacy http handlers (http.HandlerFunc interface)
Example usage
-------------
This is a complete working example for Basic auth:
package main
import (
"fmt"
"net/http"
auth "github.com/abbot/go-http-auth"
)
func Secret(user, realm string) string {
if user == "john" {
// password is "hello"
return "$1$dlPL2MqE$oQmn16q49SqdmhenQuNgs1"
}
return ""
}
func handle(w http.ResponseWriter, r *auth.AuthenticatedRequest) {
fmt.Fprintf(w, "<html><body><h1>Hello, %s!</h1></body></html>", r.Username)
}
func main() {
authenticator := auth.NewBasicAuthenticator("example.com", Secret)
http.HandleFunc("/", authenticator.Wrap(handle))
http.ListenAndServe(":8080", nil)
}
See more examples in the "examples" directory.
Legal
-----
This module is developed under Apache 2.0 license, and can be used for
open and proprietary projects.
Copyright 2012-2013 Lev Shamardin
Licensed under the Apache License, Version 2.0 (the "License"); you
may not use this file or any other part of this project except in
compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied. See the License for the specific language governing
permissions and limitations under the License.

110
vendor/github.com/abbot/go-http-auth/auth.go generated vendored Normal file
View file

@ -0,0 +1,110 @@
// Package auth is an implementation of HTTP Basic and HTTP Digest authentication.
package auth
import (
"context"
"net/http"
)
// AuthenticatedRequest is passed to AuthenticatedHandlerFunc instead
// of *http.Request.
type AuthenticatedRequest struct {
http.Request
// Username is the authenticated user name. Current API implies that
// Username is never empty, which means that authentication is
// always done before calling the request handler.
Username string
}
// AuthenticatedHandlerFunc is like http.HandlerFunc, but takes
// AuthenticatedRequest instead of http.Request
type AuthenticatedHandlerFunc func(http.ResponseWriter, *AuthenticatedRequest)
// Authenticator wraps an AuthenticatedHandlerFunc with
// authentication-checking code.
//
// Typical Authenticator usage is something like:
//
// authenticator := SomeAuthenticator(...)
// http.HandleFunc("/", authenticator(my_handler))
//
// Authenticator wrapper checks the user authentication and calls the
// wrapped function only after authentication has succeeded. Otherwise,
// it returns a handler which initiates the authentication procedure.
type Authenticator func(AuthenticatedHandlerFunc) http.HandlerFunc
// Info contains authentication information for the request.
type Info struct {
// Authenticated is set to true when request was authenticated
// successfully, i.e. username and password passed in request did
// pass the check.
Authenticated bool
// Username contains a user name passed in the request when
// Authenticated is true. It's value is undefined if Authenticated
// is false.
Username string
// ResponseHeaders contains extra headers that must be set by server
// when sending back HTTP response.
ResponseHeaders http.Header
}
// UpdateHeaders updates headers with this Info's ResponseHeaders. It is
// safe to call this function on nil Info.
func (i *Info) UpdateHeaders(headers http.Header) {
if i == nil {
return
}
for k, values := range i.ResponseHeaders {
for _, v := range values {
headers.Add(k, v)
}
}
}
type key int // used for context keys
var infoKey key
// AuthenticatorInterface is the interface implemented by BasicAuth
// and DigestAuth authenticators.
//
// Deprecated: this interface is not coherent. New code should define
// and use your own interfaces with a required subset of authenticator
// methods.
type AuthenticatorInterface interface {
// NewContext returns a new context carrying authentication
// information extracted from the request.
NewContext(ctx context.Context, r *http.Request) context.Context
// Wrap returns an http.HandlerFunc which wraps
// AuthenticatedHandlerFunc with this authenticator's
// authentication checks.
Wrap(AuthenticatedHandlerFunc) http.HandlerFunc
}
// FromContext returns authentication information from the context or
// nil if no such information present.
func FromContext(ctx context.Context) *Info {
info, ok := ctx.Value(infoKey).(*Info)
if !ok {
return nil
}
return info
}
// AuthUsernameHeader is the header set by JustCheck functions. It
// contains an authenticated username (if authentication was
// successful).
const AuthUsernameHeader = "X-Authenticated-Username"
// JustCheck returns a new http.HandlerFunc, which requires
// authenticator to successfully authenticate a user before calling
// wrapped http.HandlerFunc.
func JustCheck(auth AuthenticatorInterface, wrapped http.HandlerFunc) http.HandlerFunc {
return auth.Wrap(func(w http.ResponseWriter, ar *AuthenticatedRequest) {
ar.Header.Set(AuthUsernameHeader, ar.Username)
wrapped(w, &ar.Request)
})
}

161
vendor/github.com/abbot/go-http-auth/basic.go generated vendored Normal file
View file

@ -0,0 +1,161 @@
package auth
import (
"bytes"
"context"
"crypto/sha1"
"crypto/subtle"
"encoding/base64"
"errors"
"net/http"
"strings"
"golang.org/x/crypto/bcrypt"
)
type compareFunc func(hashedPassword, password []byte) error
var (
errMismatchedHashAndPassword = errors.New("mismatched hash and password")
compareFuncs = []struct {
prefix string
compare compareFunc
}{
{"", compareMD5HashAndPassword}, // default compareFunc
{"{SHA}", compareShaHashAndPassword},
// Bcrypt is complicated. According to crypt(3) from
// crypt_blowfish version 1.3 (fetched from
// http://www.openwall.com/crypt/crypt_blowfish-1.3.tar.gz), there
// are three different has prefixes: "$2a$", used by versions up
// to 1.0.4, and "$2x$" and "$2y$", used in all later
// versions. "$2a$" has a known bug, "$2x$" was added as a
// migration path for systems with "$2a$" prefix and still has a
// bug, and only "$2y$" should be used by modern systems. The bug
// has something to do with handling of 8-bit characters. Since
// both "$2a$" and "$2x$" are deprecated, we are handling them the
// same way as "$2y$", which will yield correct results for 7-bit
// character passwords, but is wrong for 8-bit character
// passwords. You have to upgrade to "$2y$" if you want sant 8-bit
// character password support with bcrypt. To add to the mess,
// OpenBSD 5.5. introduced "$2b$" prefix, which behaves exactly
// like "$2y$" according to the same source.
{"$2a$", bcrypt.CompareHashAndPassword},
{"$2b$", bcrypt.CompareHashAndPassword},
{"$2x$", bcrypt.CompareHashAndPassword},
{"$2y$", bcrypt.CompareHashAndPassword},
}
)
// BasicAuth is an authenticator implementation for 'Basic' HTTP
// Authentication scheme (RFC 7617).
type BasicAuth struct {
Realm string
Secrets SecretProvider
// Headers used by authenticator. Set to ProxyHeaders to use with
// proxy server. When nil, NormalHeaders are used.
Headers *Headers
}
// check that BasicAuth implements AuthenticatorInterface
var _ = (AuthenticatorInterface)((*BasicAuth)(nil))
// CheckAuth checks the username/password combination from the
// request. Returns either an empty string (authentication failed) or
// the name of the authenticated user.
func (a *BasicAuth) CheckAuth(r *http.Request) string {
user, password, ok := r.BasicAuth()
if !ok {
return ""
}
secret := a.Secrets(user, a.Realm)
if secret == "" {
return ""
}
if !CheckSecret(password, secret) {
return ""
}
return user
}
// CheckSecret returns true if the password matches the encrypted
// secret.
func CheckSecret(password, secret string) bool {
compare := compareFuncs[0].compare
for _, cmp := range compareFuncs[1:] {
if strings.HasPrefix(secret, cmp.prefix) {
compare = cmp.compare
break
}
}
return compare([]byte(secret), []byte(password)) == nil
}
func compareShaHashAndPassword(hashedPassword, password []byte) error {
d := sha1.New()
d.Write(password)
if subtle.ConstantTimeCompare(hashedPassword[5:], []byte(base64.StdEncoding.EncodeToString(d.Sum(nil)))) != 1 {
return errMismatchedHashAndPassword
}
return nil
}
func compareMD5HashAndPassword(hashedPassword, password []byte) error {
parts := bytes.SplitN(hashedPassword, []byte("$"), 4)
if len(parts) != 4 {
return errMismatchedHashAndPassword
}
magic := []byte("$" + string(parts[1]) + "$")
salt := parts[2]
if subtle.ConstantTimeCompare(hashedPassword, MD5Crypt(password, salt, magic)) != 1 {
return errMismatchedHashAndPassword
}
return nil
}
// RequireAuth is an http.HandlerFunc for BasicAuth which initiates
// the authentication process (or requires reauthentication).
func (a *BasicAuth) RequireAuth(w http.ResponseWriter, r *http.Request) {
w.Header().Set(contentType, a.Headers.V().UnauthContentType)
w.Header().Set(a.Headers.V().Authenticate, `Basic realm="`+a.Realm+`"`)
w.WriteHeader(a.Headers.V().UnauthCode)
w.Write([]byte(a.Headers.V().UnauthResponse))
}
// Wrap returns an http.HandlerFunc, which wraps
// AuthenticatedHandlerFunc with this BasicAuth authenticator's
// authentication checks. Once the request contains valid credentials,
// it calls wrapped AuthenticatedHandlerFunc.
//
// Deprecated: new code should use NewContext instead.
func (a *BasicAuth) Wrap(wrapped AuthenticatedHandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if username := a.CheckAuth(r); username == "" {
a.RequireAuth(w, r)
} else {
ar := &AuthenticatedRequest{Request: *r, Username: username}
wrapped(w, ar)
}
}
}
// NewContext returns a context carrying authentication information for the request.
func (a *BasicAuth) NewContext(ctx context.Context, r *http.Request) context.Context {
info := &Info{Username: a.CheckAuth(r), ResponseHeaders: make(http.Header)}
info.Authenticated = (info.Username != "")
if !info.Authenticated {
info.ResponseHeaders.Set(a.Headers.V().Authenticate, `Basic realm="`+a.Realm+`"`)
}
return context.WithValue(ctx, infoKey, info)
}
// NewBasicAuthenticator returns a BasicAuth initialized with provided
// realm and secrets.
//
// Deprecated: new code should construct BasicAuth values directly.
func NewBasicAuthenticator(realm string, secrets SecretProvider) *BasicAuth {
return &BasicAuth{Realm: realm, Secrets: secrets}
}

288
vendor/github.com/abbot/go-http-auth/digest.go generated vendored Normal file
View file

@ -0,0 +1,288 @@
package auth
import (
"context"
"crypto/subtle"
"fmt"
"net/http"
"net/url"
"sort"
"strconv"
"strings"
"sync"
"time"
)
type digestClient struct {
nc uint64
lastSeen int64
}
// DigestAuth is an authenticator implementation for 'Digest' HTTP Authentication scheme (RFC 7616).
//
// Note: this implementation was written following now deprecated RFC
// 2617, and supports only MD5 algorithm.
//
// TODO: Add support for SHA-256 and SHA-512/256 algorithms.
type DigestAuth struct {
Realm string
Opaque string
Secrets SecretProvider
PlainTextSecrets bool
IgnoreNonceCount bool
// Headers used by authenticator. Set to ProxyHeaders to use with
// proxy server. When nil, NormalHeaders are used.
Headers *Headers
/*
Approximate size of Client's Cache. When actual number of
tracked client nonces exceeds
ClientCacheSize+ClientCacheTolerance, ClientCacheTolerance*2
older entries are purged.
*/
ClientCacheSize int
ClientCacheTolerance int
clients map[string]*digestClient
mutex sync.RWMutex
}
// check that DigestAuth implements AuthenticatorInterface
var _ = (AuthenticatorInterface)((*DigestAuth)(nil))
type digestCacheEntry struct {
nonce string
lastSeen int64
}
type digestCache []digestCacheEntry
func (c digestCache) Less(i, j int) bool {
return c[i].lastSeen < c[j].lastSeen
}
func (c digestCache) Len() int {
return len(c)
}
func (c digestCache) Swap(i, j int) {
c[i], c[j] = c[j], c[i]
}
// Purge removes count oldest entries from DigestAuth.clients
func (da *DigestAuth) Purge(count int) {
da.mutex.Lock()
da.purgeLocked(count)
da.mutex.Unlock()
}
func (da *DigestAuth) purgeLocked(count int) {
entries := make([]digestCacheEntry, 0, len(da.clients))
for nonce, client := range da.clients {
entries = append(entries, digestCacheEntry{nonce, client.lastSeen})
}
cache := digestCache(entries)
sort.Sort(cache)
for _, client := range cache[:count] {
delete(da.clients, client.nonce)
}
}
// RequireAuth is an http.HandlerFunc which initiates the
// authentication process (or requires reauthentication).
func (da *DigestAuth) RequireAuth(w http.ResponseWriter, r *http.Request) {
da.mutex.RLock()
clientsLen := len(da.clients)
da.mutex.RUnlock()
if clientsLen > da.ClientCacheSize+da.ClientCacheTolerance {
da.Purge(da.ClientCacheTolerance * 2)
}
nonce := RandomKey()
da.mutex.Lock()
da.clients[nonce] = &digestClient{nc: 0, lastSeen: time.Now().UnixNano()}
da.mutex.Unlock()
da.mutex.RLock()
w.Header().Set(contentType, da.Headers.V().UnauthContentType)
w.Header().Set(da.Headers.V().Authenticate,
fmt.Sprintf(`Digest realm="%s", nonce="%s", opaque="%s", algorithm=MD5, qop="auth"`,
da.Realm, nonce, da.Opaque))
w.WriteHeader(da.Headers.V().UnauthCode)
w.Write([]byte(da.Headers.V().UnauthResponse))
da.mutex.RUnlock()
}
// DigestAuthParams parses Authorization header from the
// http.Request. Returns a map of auth parameters or nil if the header
// is not a valid parsable Digest auth header.
func DigestAuthParams(authorization string) map[string]string {
s := strings.SplitN(authorization, " ", 2)
if len(s) != 2 || s[0] != "Digest" {
return nil
}
return ParsePairs(s[1])
}
// CheckAuth checks whether the request contains valid authentication
// data. Returns a pair of username, authinfo, where username is the
// name of the authenticated user or an empty string and authinfo is
// the contents for the optional Authentication-Info response header.
func (da *DigestAuth) CheckAuth(r *http.Request) (username string, authinfo *string) {
da.mutex.RLock()
defer da.mutex.RUnlock()
username = ""
authinfo = nil
auth := DigestAuthParams(r.Header.Get(da.Headers.V().Authorization))
if auth == nil {
return "", nil
}
// RFC2617 Section 3.2.1 specifies that unset value of algorithm in
// WWW-Authenticate Response header should be treated as
// "MD5". According to section 3.2.2 the "algorithm" value in
// subsequent Request Authorization header must be set to whatever
// was supplied in the WWW-Authenticate Response header. This
// implementation always returns an algorithm in WWW-Authenticate
// header, however there seems to be broken clients in the wild
// which do not set the algorithm. Assume the unset algorithm in
// Authorization header to be equal to MD5.
if _, ok := auth["algorithm"]; !ok {
auth["algorithm"] = "MD5"
}
if da.Opaque != auth["opaque"] || auth["algorithm"] != "MD5" || auth["qop"] != "auth" {
return "", nil
}
// Check if the requested URI matches auth header
if r.RequestURI != auth["uri"] {
// We allow auth["uri"] to be a full path prefix of request-uri
// for some reason lost in history, which is probably wrong, but
// used to be like that for quite some time
// (https://tools.ietf.org/html/rfc2617#section-3.2.2 explicitly
// says that auth["uri"] is the request-uri).
//
// TODO: make an option to allow only strict checking.
switch u, err := url.Parse(auth["uri"]); {
case err != nil:
return "", nil
case r.URL == nil:
return "", nil
case len(u.Path) > len(r.URL.Path):
return "", nil
case !strings.HasPrefix(r.URL.Path, u.Path):
return "", nil
}
}
HA1 := da.Secrets(auth["username"], da.Realm)
if da.PlainTextSecrets {
HA1 = H(auth["username"] + ":" + da.Realm + ":" + HA1)
}
HA2 := H(r.Method + ":" + auth["uri"])
KD := H(strings.Join([]string{HA1, auth["nonce"], auth["nc"], auth["cnonce"], auth["qop"], HA2}, ":"))
if subtle.ConstantTimeCompare([]byte(KD), []byte(auth["response"])) != 1 {
return "", nil
}
// At this point crypto checks are completed and validated.
// Now check if the session is valid.
nc, err := strconv.ParseUint(auth["nc"], 16, 64)
if err != nil {
return "", nil
}
client, ok := da.clients[auth["nonce"]]
if !ok {
return "", nil
}
if client.nc != 0 && client.nc >= nc && !da.IgnoreNonceCount {
return "", nil
}
client.nc = nc
client.lastSeen = time.Now().UnixNano()
respHA2 := H(":" + auth["uri"])
rspauth := H(strings.Join([]string{HA1, auth["nonce"], auth["nc"], auth["cnonce"], auth["qop"], respHA2}, ":"))
info := fmt.Sprintf(`qop="auth", rspauth="%s", cnonce="%s", nc="%s"`, rspauth, auth["cnonce"], auth["nc"])
return auth["username"], &info
}
// Default values for ClientCacheSize and ClientCacheTolerance for DigestAuth
const (
DefaultClientCacheSize = 1000
DefaultClientCacheTolerance = 100
)
// Wrap returns an http.HandlerFunc wraps AuthenticatedHandlerFunc
// with this DigestAuth authentication checks. Once the request
// contains valid credentials, it calls wrapped
// AuthenticatedHandlerFunc.
//
// Deprecated: new code should use NewContext instead.
func (da *DigestAuth) Wrap(wrapped AuthenticatedHandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if username, authinfo := da.CheckAuth(r); username == "" {
da.RequireAuth(w, r)
} else {
ar := &AuthenticatedRequest{Request: *r, Username: username}
if authinfo != nil {
w.Header().Set(da.Headers.V().AuthInfo, *authinfo)
}
wrapped(w, ar)
}
}
}
// JustCheck returns a new http.HandlerFunc, which requires
// DigestAuth to successfully authenticate a user before calling
// wrapped http.HandlerFunc.
//
// Authenticated Username is passed as an extra
// X-Authenticated-Username header to the wrapped HandlerFunc.
func (da *DigestAuth) JustCheck(wrapped http.HandlerFunc) http.HandlerFunc {
return da.Wrap(func(w http.ResponseWriter, ar *AuthenticatedRequest) {
ar.Header.Set(AuthUsernameHeader, ar.Username)
wrapped(w, &ar.Request)
})
}
// NewContext returns a context carrying authentication information for the request.
func (da *DigestAuth) NewContext(ctx context.Context, r *http.Request) context.Context {
username, authinfo := da.CheckAuth(r)
da.mutex.Lock()
defer da.mutex.Unlock()
info := &Info{Username: username, ResponseHeaders: make(http.Header)}
if username != "" {
info.Authenticated = true
info.ResponseHeaders.Set(da.Headers.V().AuthInfo, *authinfo)
} else {
// return back digest WWW-Authenticate header
if len(da.clients) > da.ClientCacheSize+da.ClientCacheTolerance {
da.purgeLocked(da.ClientCacheTolerance * 2)
}
nonce := RandomKey()
da.clients[nonce] = &digestClient{nc: 0, lastSeen: time.Now().UnixNano()}
info.ResponseHeaders.Set(da.Headers.V().Authenticate,
fmt.Sprintf(`Digest realm="%s", nonce="%s", opaque="%s", algorithm=MD5, qop="auth"`,
da.Realm, nonce, da.Opaque))
}
return context.WithValue(ctx, infoKey, info)
}
// NewDigestAuthenticator generates a new DigestAuth object
func NewDigestAuthenticator(realm string, secrets SecretProvider) *DigestAuth {
da := &DigestAuth{
Opaque: RandomKey(),
Realm: realm,
Secrets: secrets,
PlainTextSecrets: false,
ClientCacheSize: DefaultClientCacheSize,
ClientCacheTolerance: DefaultClientCacheTolerance,
clients: map[string]*digestClient{}}
return da
}

5
vendor/github.com/abbot/go-http-auth/go.mod generated vendored Normal file
View file

@ -0,0 +1,5 @@
module github.com/abbot/go-http-auth
go 1.14
require golang.org/x/crypto v0.0.0-20210921155107-089bfa567519

8
vendor/github.com/abbot/go-http-auth/go.sum generated vendored Normal file
View file

@ -0,0 +1,8 @@
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

73
vendor/github.com/abbot/go-http-auth/md5crypt.go generated vendored Normal file
View file

@ -0,0 +1,73 @@
package auth
import "crypto/md5"
const itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
var md5CryptSwaps = [16]int{12, 6, 0, 13, 7, 1, 14, 8, 2, 15, 9, 3, 5, 10, 4, 11}
// MD5Crypt is the MD5 password crypt implementation.
func MD5Crypt(password, salt, magic []byte) []byte {
d := md5.New()
d.Write(password)
d.Write(magic)
d.Write(salt)
d2 := md5.New()
d2.Write(password)
d2.Write(salt)
d2.Write(password)
for i, mixin := 0, d2.Sum(nil); i < len(password); i++ {
d.Write([]byte{mixin[i%16]})
}
for i := len(password); i != 0; i >>= 1 {
if i&1 == 0 {
d.Write([]byte{password[0]})
} else {
d.Write([]byte{0})
}
}
final := d.Sum(nil)
for i := 0; i < 1000; i++ {
d2 := md5.New()
if i&1 == 0 {
d2.Write(final)
} else {
d2.Write(password)
}
if i%3 != 0 {
d2.Write(salt)
}
if i%7 != 0 {
d2.Write(password)
}
if i&1 == 0 {
d2.Write(password)
} else {
d2.Write(final)
}
final = d2.Sum(nil)
}
result := make([]byte, 0, 22)
v := uint(0)
bits := uint(0)
for _, i := range md5CryptSwaps {
v |= (uint(final[i]) << bits)
for bits = bits + 8; bits > 6; bits -= 6 {
result = append(result, itoa64[v&0x3f])
v >>= 6
}
}
result = append(result, itoa64[v&0x3f])
return append(append(append(magic, salt...), '$'), result...)
}

146
vendor/github.com/abbot/go-http-auth/misc.go generated vendored Normal file
View file

@ -0,0 +1,146 @@
package auth
import (
"bytes"
"crypto/md5"
"crypto/rand"
"encoding/base64"
"fmt"
"net/http"
"strings"
)
// RandomKey returns a random 16-byte base64 alphabet string
func RandomKey() string {
k := make([]byte, 12)
for bytes := 0; bytes < len(k); {
n, err := rand.Read(k[bytes:])
if err != nil {
panic("rand.Read() failed")
}
bytes += n
}
return base64.StdEncoding.EncodeToString(k)
}
// H function for MD5 algorithm (returns a lower-case hex MD5 digest)
func H(data string) string {
digest := md5.New()
digest.Write([]byte(data))
return fmt.Sprintf("%x", digest.Sum(nil))
}
// ParseList parses a comma-separated list of values as described by
// RFC 2068 and returns list elements.
//
// Lifted from https://code.google.com/p/gorilla/source/browse/http/parser/parser.go
// which was ported from urllib2.parse_http_list, from the Python
// standard library.
func ParseList(value string) []string {
var list []string
var escape, quote bool
b := new(bytes.Buffer)
for _, r := range value {
switch {
case escape:
b.WriteRune(r)
escape = false
case quote:
if r == '\\' {
escape = true
} else {
if r == '"' {
quote = false
}
b.WriteRune(r)
}
case r == ',':
list = append(list, strings.TrimSpace(b.String()))
b.Reset()
case r == '"':
quote = true
b.WriteRune(r)
default:
b.WriteRune(r)
}
}
// Append last part.
if s := b.String(); s != "" {
list = append(list, strings.TrimSpace(s))
}
return list
}
// ParsePairs extracts key/value pairs from a comma-separated list of
// values as described by RFC 2068 and returns a map[key]value. The
// resulting values are unquoted. If a list element doesn't contain a
// "=", the key is the element itself and the value is an empty
// string.
//
// Lifted from https://code.google.com/p/gorilla/source/browse/http/parser/parser.go
func ParsePairs(value string) map[string]string {
m := make(map[string]string)
for _, pair := range ParseList(strings.TrimSpace(value)) {
switch i := strings.Index(pair, "="); {
case i < 0:
// No '=' in pair, treat whole string as a 'key'.
m[pair] = ""
case i == len(pair)-1:
// Malformed pair ('key=' with no value), keep key with empty value.
m[pair[:i]] = ""
default:
v := pair[i+1:]
if v[0] == '"' && v[len(v)-1] == '"' {
// Unquote it.
v = v[1 : len(v)-1]
}
m[pair[:i]] = v
}
}
return m
}
// Headers contains header and error codes used by authenticator.
type Headers struct {
Authenticate string // WWW-Authenticate
Authorization string // Authorization
AuthInfo string // Authentication-Info
UnauthCode int // 401
UnauthContentType string // text/plain
UnauthResponse string // Unauthorized.
}
// V returns NormalHeaders when h is nil, or h otherwise. Allows to
// use uninitialized *Headers values in structs.
func (h *Headers) V() *Headers {
if h == nil {
return NormalHeaders
}
return h
}
var (
// NormalHeaders are the regular Headers used by an HTTP Server for
// request authentication.
NormalHeaders = &Headers{
Authenticate: "WWW-Authenticate",
Authorization: "Authorization",
AuthInfo: "Authentication-Info",
UnauthCode: http.StatusUnauthorized,
UnauthContentType: "text/plain",
UnauthResponse: fmt.Sprintf("%d %s\n", http.StatusUnauthorized, http.StatusText(http.StatusUnauthorized)),
}
// ProxyHeaders are Headers used by an HTTP Proxy server for proxy
// access authentication.
ProxyHeaders = &Headers{
Authenticate: "Proxy-Authenticate",
Authorization: "Proxy-Authorization",
AuthInfo: "Proxy-Authentication-Info",
UnauthCode: http.StatusProxyAuthRequired,
UnauthContentType: "text/plain",
UnauthResponse: fmt.Sprintf("%d %s\n", http.StatusProxyAuthRequired, http.StatusText(http.StatusProxyAuthRequired)),
}
)
const contentType = "Content-Type"

1
vendor/github.com/abbot/go-http-auth/test.htdigest generated vendored Normal file
View file

@ -0,0 +1 @@
test:example.com:aa78524fceb0e50fd8ca96dd818b8cf9

4
vendor/github.com/abbot/go-http-auth/test.htpasswd generated vendored Normal file
View file

@ -0,0 +1,4 @@
test:{SHA}qvTGHdzF6KLavt4PO0gs2a6pQ00=
test2:$apr1$a0j62R97$mYqFkloXH0/UOaUnAiV2b0
test16:$apr1$JI4wh3am$AmhephVqLTUyAVpFQeHZC0
test3:$2y$05$ih3C91zUBSTFcAh2mQnZYuob0UOZVEf16wl/ukgjDhjvj.xgM1WwS

151
vendor/github.com/abbot/go-http-auth/users.go generated vendored Normal file
View file

@ -0,0 +1,151 @@
package auth
import (
"encoding/csv"
"os"
"sync"
)
// SecretProvider is used by authenticators. Takes user name and realm
// as an argument, returns secret required for authentication (HA1 for
// digest authentication, properly encrypted password for basic).
//
// Returning an empty string means failing the authentication.
type SecretProvider func(user, realm string) string
// File handles automatic file reloading on changes.
type File struct {
Path string
Info os.FileInfo
/* must be set in inherited types during initialization */
Reload func()
mu sync.Mutex
}
// ReloadIfNeeded checks file Stat and calls Reload() if any changes
// were detected. File mutex is Locked for the duration of Reload()
// call.
//
// This function will panic() if Stat fails.
func (f *File) ReloadIfNeeded() {
info, err := os.Stat(f.Path)
if err != nil {
panic(err)
}
f.mu.Lock()
defer f.mu.Unlock()
if f.Info == nil || f.Info.ModTime() != info.ModTime() {
f.Info = info
f.Reload()
}
}
// HtdigestFile is a File holding htdigest authentication data.
type HtdigestFile struct {
// File is used for automatic reloading of the authentication data.
File
// Users is a map of realms to users to HA1 digests.
Users map[string]map[string]string
mu sync.RWMutex
}
func reloadHTDigest(hf *HtdigestFile) {
r, err := os.Open(hf.Path)
if err != nil {
panic(err)
}
reader := csv.NewReader(r)
reader.Comma = ':'
reader.Comment = '#'
reader.TrimLeadingSpace = true
records, err := reader.ReadAll()
if err != nil {
panic(err)
}
hf.mu.Lock()
defer hf.mu.Unlock()
hf.Users = make(map[string]map[string]string)
for _, record := range records {
_, exists := hf.Users[record[1]]
if !exists {
hf.Users[record[1]] = make(map[string]string)
}
hf.Users[record[1]][record[0]] = record[2]
}
}
// HtdigestFileProvider is a SecretProvider implementation based on
// htdigest-formated files. It will automatically reload htdigest file
// on changes. It panics on syntax errors in htdigest files.
func HtdigestFileProvider(filename string) SecretProvider {
hf := &HtdigestFile{File: File{Path: filename}}
hf.Reload = func() { reloadHTDigest(hf) }
return func(user, realm string) string {
hf.ReloadIfNeeded()
hf.mu.RLock()
defer hf.mu.RUnlock()
_, exists := hf.Users[realm]
if !exists {
return ""
}
digest, exists := hf.Users[realm][user]
if !exists {
return ""
}
return digest
}
}
// HtpasswdFile is a File holding basic authentication data.
type HtpasswdFile struct {
// File is used for automatic reloading of the authentication data.
File
// Users is a map of users to their secrets (salted encrypted
// passwords).
Users map[string]string
mu sync.RWMutex
}
func reloadHTPasswd(h *HtpasswdFile) {
r, err := os.Open(h.Path)
if err != nil {
panic(err)
}
reader := csv.NewReader(r)
reader.Comma = ':'
reader.Comment = '#'
reader.TrimLeadingSpace = true
records, err := reader.ReadAll()
if err != nil {
panic(err)
}
h.mu.Lock()
defer h.mu.Unlock()
h.Users = make(map[string]string)
for _, record := range records {
h.Users[record[0]] = record[1]
}
}
// HtpasswdFileProvider is a SecretProvider implementation based on
// htpasswd-formated files. It will automatically reload htpasswd file
// on changes. It panics on syntax errors in htpasswd files. Realm
// argument of the SecretProvider is ignored.
func HtpasswdFileProvider(filename string) SecretProvider {
h := &HtpasswdFile{File: File{Path: filename}}
h.Reload = func() { reloadHTPasswd(h) }
return func(user, realm string) string {
h.ReloadIfNeeded()
h.mu.RLock()
password, exists := h.Users[user]
h.mu.RUnlock()
if !exists {
return ""
}
return password
}
}

13
vendor/modules.txt vendored
View file

@ -1,5 +1,8 @@
# cloud.google.com/go v0.38.0
cloud.google.com/go/compute/metadata
# github.com/abbot/go-http-auth v0.4.1-0.20220112235402-e1cee1c72f2f
## explicit
github.com/abbot/go-http-auth
# github.com/aws/aws-sdk-go v1.40.41
## explicit
github.com/aws/aws-sdk-go/aws
@ -85,7 +88,7 @@ github.com/golang/protobuf/ptypes
github.com/golang/protobuf/ptypes/any
github.com/golang/protobuf/ptypes/duration
github.com/golang/protobuf/ptypes/timestamp
# github.com/google/go-cmp v0.5.6
# github.com/google/go-cmp v0.5.8
## explicit
# github.com/google/uuid v1.3.0
github.com/google/uuid
@ -208,7 +211,7 @@ go.opencensus.io/trace
go.opencensus.io/trace/internal
go.opencensus.io/trace/propagation
go.opencensus.io/trace/tracestate
# golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871
# golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed
## explicit
golang.org/x/crypto/acme
golang.org/x/crypto/acme/autocert
@ -238,7 +241,8 @@ golang.org/x/crypto/ssh/knownhosts
## explicit
golang.org/x/image/draw
golang.org/x/image/math/f64
# golang.org/x/mod v0.3.0
# golang.org/x/mod v0.4.1
## explicit
golang.org/x/mod/semver
# golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2
## explicit
@ -275,7 +279,8 @@ golang.org/x/text/unicode/norm
# golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9
## explicit
golang.org/x/time/rate
# golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78
# golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963
## explicit
golang.org/x/tools/go/gcexportdata
golang.org/x/tools/go/internal/gcimporter
golang.org/x/tools/go/internal/packagesdriver