mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/yunionio/cloudpods.git
synced 2026-09-20 08:03:53 +08:00
156 lines
3.1 KiB
Go
156 lines
3.1 KiB
Go
package digest
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/icholy/digest/internal/param"
|
|
)
|
|
|
|
// Challenge is a challenge sent in the WWW-Authenticate header
|
|
type Challenge struct {
|
|
Realm string
|
|
Domain []string
|
|
Nonce string
|
|
Opaque string
|
|
Stale bool
|
|
Algorithm string
|
|
QOP []string
|
|
Charset string
|
|
Userhash bool
|
|
}
|
|
|
|
// SupportsQOP returns true if the challenge advertises support
|
|
// for the provided qop value
|
|
func (c *Challenge) SupportsQOP(qop string) bool {
|
|
for _, v := range c.QOP {
|
|
if v == qop {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// ParseChallenge parses the WWW-Authenticate header challenge
|
|
func ParseChallenge(s string) (*Challenge, error) {
|
|
s, ok := strings.CutPrefix(s, Prefix)
|
|
if !ok {
|
|
return nil, errors.New("digest: invalid challenge prefix")
|
|
}
|
|
pp, err := param.Parse(s)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("digest: invalid challenge: %w", err)
|
|
}
|
|
var c Challenge
|
|
for _, p := range pp {
|
|
switch p.Key {
|
|
case "realm":
|
|
c.Realm = p.Value
|
|
case "domain":
|
|
c.Domain = strings.Fields(p.Value)
|
|
case "nonce":
|
|
c.Nonce = p.Value
|
|
case "algorithm":
|
|
c.Algorithm = p.Value
|
|
case "stale":
|
|
c.Stale = strings.ToLower(p.Value) == "true"
|
|
case "opaque":
|
|
c.Opaque = p.Value
|
|
case "qop":
|
|
c.QOP = strings.Split(p.Value, ",")
|
|
case "charset":
|
|
c.Charset = p.Value
|
|
case "userhash":
|
|
c.Userhash = strings.ToLower(p.Value) == "true"
|
|
}
|
|
}
|
|
return &c, nil
|
|
}
|
|
|
|
// String returns the foramtted header value
|
|
func (c *Challenge) String() string {
|
|
var pp []param.Param
|
|
pp = append(pp, param.Param{
|
|
Key: "realm",
|
|
Value: c.Realm,
|
|
Quote: true,
|
|
})
|
|
if len(c.Domain) != 0 {
|
|
pp = append(pp, param.Param{
|
|
Key: "domain",
|
|
Value: strings.Join(c.Domain, " "),
|
|
Quote: true,
|
|
})
|
|
}
|
|
pp = append(pp, param.Param{
|
|
Key: "nonce",
|
|
Value: c.Nonce,
|
|
Quote: true,
|
|
})
|
|
if c.Opaque != "" {
|
|
pp = append(pp, param.Param{
|
|
Key: "opaque",
|
|
Value: c.Opaque,
|
|
Quote: true,
|
|
})
|
|
}
|
|
if c.Stale {
|
|
pp = append(pp, param.Param{
|
|
Key: "stale",
|
|
Value: "true",
|
|
})
|
|
}
|
|
if c.Algorithm != "" {
|
|
pp = append(pp, param.Param{
|
|
Key: "algorithm",
|
|
Value: c.Algorithm,
|
|
})
|
|
}
|
|
if len(c.QOP) != 0 {
|
|
pp = append(pp, param.Param{
|
|
Key: "qop",
|
|
Value: strings.Join(c.QOP, ","),
|
|
Quote: true,
|
|
})
|
|
}
|
|
if c.Charset != "" {
|
|
pp = append(pp, param.Param{
|
|
Key: "charset",
|
|
Value: c.Charset,
|
|
})
|
|
}
|
|
if c.Userhash {
|
|
pp = append(pp, param.Param{
|
|
Key: "userhash",
|
|
Value: "true",
|
|
})
|
|
}
|
|
return Prefix + param.Format(pp...)
|
|
}
|
|
|
|
// ErrNoChallenge indicates that no WWW-Authenticate headers were found.
|
|
var ErrNoChallenge = errors.New("digest: no challenge found")
|
|
|
|
// FindChallenge returns the first supported challenge in the headers
|
|
func FindChallenge(h http.Header) (*Challenge, error) {
|
|
var last error
|
|
for _, header := range h.Values("WWW-Authenticate") {
|
|
if !IsDigest(header) {
|
|
continue
|
|
}
|
|
chal, err := ParseChallenge(header)
|
|
if err == nil && CanDigest(chal) {
|
|
return chal, nil
|
|
}
|
|
if err != nil {
|
|
last = err
|
|
}
|
|
}
|
|
if last != nil {
|
|
return nil, last
|
|
}
|
|
return nil, ErrNoChallenge
|
|
}
|