| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142 |
- package digest
- import (
- "errors"
- "fmt"
- "strconv"
- "strings"
- "github.com/icholy/digest/internal/param"
- )
- // Credentials is a parsed version of the Authorization header
- type Credentials struct {
- Username string
- Realm string
- Nonce string
- URI string
- Response string
- Algorithm string
- Cnonce string
- Opaque string
- QOP string
- Nc int
- Userhash bool
- }
- // ParseCredentials parses the Authorization header value into credentials
- func ParseCredentials(s string) (*Credentials, error) {
- s, ok := strings.CutPrefix(s, Prefix)
- if !ok {
- return nil, errors.New("digest: invalid credentials prefix")
- }
- pp, err := param.Parse(s)
- if err != nil {
- return nil, fmt.Errorf("digest: invalid credentials: %w", err)
- }
- var c Credentials
- for _, p := range pp {
- switch p.Key {
- case "username":
- c.Username = p.Value
- case "realm":
- c.Realm = p.Value
- case "nonce":
- c.Nonce = p.Value
- case "uri":
- c.URI = p.Value
- case "response":
- c.Response = p.Value
- case "algorithm":
- c.Algorithm = p.Value
- case "cnonce":
- c.Cnonce = p.Value
- case "opaque":
- c.Opaque = p.Value
- case "qop":
- c.QOP = p.Value
- case "nc":
- nc, err := strconv.ParseInt(p.Value, 16, 32)
- if err != nil {
- return nil, fmt.Errorf("digest: invalid nc: %w", err)
- }
- c.Nc = int(nc)
- case "userhash":
- c.Userhash = strings.ToLower(p.Value) == "true"
- }
- }
- return &c, nil
- }
- // String formats the credentials into the header format
- func (c *Credentials) String() string {
- var pp []param.Param
- pp = append(pp,
- param.Param{
- Key: "username",
- Value: c.Username,
- Quote: true,
- },
- param.Param{
- Key: "realm",
- Value: c.Realm,
- Quote: true,
- },
- param.Param{
- Key: "nonce",
- Value: c.Nonce,
- Quote: true,
- },
- param.Param{
- Key: "uri",
- Value: c.URI,
- Quote: true,
- },
- )
- if c.Algorithm != "" {
- pp = append(pp, param.Param{
- Key: "algorithm",
- Value: c.Algorithm,
- })
- }
- if c.QOP != "" {
- pp = append(pp, param.Param{
- Key: "cnonce",
- Value: c.Cnonce,
- Quote: true,
- })
- }
- if c.Opaque != "" {
- pp = append(pp, param.Param{
- Key: "opaque",
- Value: c.Opaque,
- Quote: true,
- })
- }
- if c.QOP != "" {
- pp = append(pp,
- param.Param{
- Key: "qop",
- Value: c.QOP,
- },
- param.Param{
- Key: "nc",
- Value: fmt.Sprintf("%08x", c.Nc),
- },
- )
- }
- if c.Userhash {
- pp = append(pp, param.Param{
- Key: "userhash",
- Value: "true",
- })
- }
- // The RFC does not specify an order, but some implementations expect the response to be at the end.
- // See: https://github.com/icholy/digest/issues/8
- pp = append(pp, param.Param{
- Key: "response",
- Value: c.Response,
- Quote: true,
- })
- return Prefix + param.Format(pp...)
- }
|