split headers.Authenticate and headers.Authorization (#523)

This commit is contained in:
Alessandro Ros
2024-02-22 13:19:31 +01:00
committed by GitHub
parent c93d5c54d9
commit c10f7aaedb
21 changed files with 369 additions and 424 deletions

View File

@@ -1691,12 +1691,13 @@ func TestClientPlayRedirect(t *testing.T) {
authOpaque := "exampleOpaque"
authStale := "FALSE"
authAlg := "MD5"
err = conn.WriteResponse(&base.Response{
Header: base.Header{
"WWW-Authenticate": headers.Authenticate{
Method: headers.AuthDigest,
Realm: &authRealm,
Nonce: &authNonce,
Realm: authRealm,
Nonce: authNonce,
Opaque: &authOpaque,
Stale: &authStale,
Algorithm: &authAlg,
@@ -1706,13 +1707,16 @@ func TestClientPlayRedirect(t *testing.T) {
})
require.NoError(t, err)
}
req, err = conn.ReadRequest()
require.NoError(t, err)
authHeaderVal, exists := req.Header["Authorization"]
require.True(t, exists)
var authHeader headers.Authenticate
var authHeader headers.Authorization
require.NoError(t, authHeader.Unmarshal(authHeaderVal))
require.Equal(t, *authHeader.Username, "testusr")
require.Equal(t, authHeader.Username, "testusr")
require.Equal(t, base.Describe, req.Method)
}

View File

@@ -19,11 +19,9 @@ func findHeader(v base.HeaderValue, prefix string) string {
// Sender allows to send credentials.
type Sender struct {
user string
pass string
method headers.AuthMethod
realm string
nonce string
user string
pass string
authenticateHeader *headers.Authenticate
}
// NewSender allocates a Sender.
@@ -38,20 +36,10 @@ func NewSender(v base.HeaderValue, user string, pass string) (*Sender, error) {
return nil, err
}
if auth.Realm == nil {
return nil, fmt.Errorf("realm is missing")
}
if auth.Nonce == nil {
return nil, fmt.Errorf("nonce is missing")
}
return &Sender{
user: user,
pass: pass,
method: headers.AuthDigest,
realm: *auth.Realm,
nonce: *auth.Nonce,
user: user,
pass: pass,
authenticateHeader: &auth,
}, nil
}
@@ -62,15 +50,10 @@ func NewSender(v base.HeaderValue, user string, pass string) (*Sender, error) {
return nil, err
}
if auth.Realm == nil {
return nil, fmt.Errorf("realm is missing")
}
return &Sender{
user: user,
pass: pass,
method: headers.AuthBasic,
realm: *auth.Realm,
user: user,
pass: pass,
authenticateHeader: &auth,
}, nil
}
@@ -82,26 +65,19 @@ func (se *Sender) AddAuthorization(req *base.Request) {
urStr := req.URL.CloneWithoutCredentials().String()
h := headers.Authorization{
Method: se.method,
Method: se.authenticateHeader.Method,
}
switch se.method {
case headers.AuthBasic:
if se.authenticateHeader.Method == headers.AuthBasic {
h.BasicUser = se.user
h.BasicPass = se.pass
default: // headers.AuthDigest
response := md5Hex(md5Hex(se.user+":"+se.realm+":"+se.pass) + ":" +
se.nonce + ":" + md5Hex(string(req.Method)+":"+urStr))
h.DigestValues = headers.Authenticate{
Method: headers.AuthDigest,
Username: &se.user,
Realm: &se.realm,
Nonce: &se.nonce,
URI: &urStr,
Response: &response,
}
} else { // digest
h.Username = se.user
h.Realm = se.authenticateHeader.Realm
h.Nonce = se.authenticateHeader.Nonce
h.URI = urStr
h.Response = md5Hex(md5Hex(se.user+":"+se.authenticateHeader.Realm+":"+se.pass) + ":" +
se.authenticateHeader.Nonce + ":" + md5Hex(string(req.Method)+":"+urStr))
}
if req.Header == nil {

View File

@@ -3,51 +3,18 @@ package auth
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/bluenviron/gortsplib/v4/pkg/base"
)
func TestSenderErrors(t *testing.T) {
for _, ca := range []struct {
name string
hv base.HeaderValue
err string
}{
{
"invalid method",
base.HeaderValue{`Invalid`},
"no authentication methods available",
},
{
"digest invalid",
base.HeaderValue{`Digest`},
"unable to split between method and keys (Digest)",
},
{
"digest, missing realm",
base.HeaderValue{`Digest nonce=123`},
"realm is missing",
},
{
"digest, missing nonce",
base.HeaderValue{`Digest realm=123`},
"nonce is missing",
},
{
"basic invalid",
base.HeaderValue{`Basic`},
"unable to split between method and keys (Basic)",
},
{
"basic, missing realm",
base.HeaderValue{`Basic nonce=123`},
"realm is missing",
},
} {
t.Run(ca.name, func(t *testing.T) {
_, err := NewSender(ca.hv, "myuser", "mypass")
require.EqualError(t, err, ca.err)
})
}
func FuzzSender(f *testing.F) {
f.Add(`Invalid`)
f.Add(`Digest`)
f.Add(`Digest nonce=123`)
f.Add(`Digest realm=123`)
f.Add(`Basic`)
f.Add(`Basic nonce=123`)
f.Fuzz(func(t *testing.T, a string) {
NewSender(base.HeaderValue{a}, "myuser", "mypass") //nolint:errcheck
})
}

View File

@@ -39,14 +39,14 @@ func GenerateWWWAuthenticate(methods []headers.AuthMethod, realm string, nonce s
case headers.AuthBasic:
ret = append(ret, (&headers.Authenticate{
Method: headers.AuthBasic,
Realm: &realm,
Realm: realm,
}).Marshal()...)
case headers.AuthDigest:
ret = append(ret, headers.Authenticate{
Method: headers.AuthDigest,
Realm: &realm,
Nonce: &nonce,
Realm: realm,
Nonce: nonce,
}.Marshal()...)
}
}
@@ -92,46 +92,26 @@ func Validate(
return fmt.Errorf("authentication failed")
}
case auth.Method == headers.AuthDigest && contains(methods, headers.AuthDigest):
if auth.DigestValues.Realm == nil {
return fmt.Errorf("realm is missing")
}
if auth.DigestValues.Nonce == nil {
return fmt.Errorf("nonce is missing")
}
if auth.DigestValues.Username == nil {
return fmt.Errorf("username is missing")
}
if auth.DigestValues.URI == nil {
return fmt.Errorf("uri is missing")
}
if auth.DigestValues.Response == nil {
return fmt.Errorf("response is missing")
}
if *auth.DigestValues.Nonce != nonce {
if auth.Nonce != nonce {
return fmt.Errorf("wrong nonce")
}
if *auth.DigestValues.Realm != realm {
if auth.Realm != realm {
return fmt.Errorf("wrong realm")
}
if *auth.DigestValues.Username != user {
if auth.Username != user {
return fmt.Errorf("authentication failed")
}
ur := req.URL
if *auth.DigestValues.URI != ur.String() {
if auth.URI != ur.String() {
// in SETUP requests, VLC strips the control attribute.
// try again with the base URL.
if baseURL != nil {
ur = baseURL
if *auth.DigestValues.URI != ur.String() {
if auth.URI != ur.String() {
return fmt.Errorf("wrong URL")
}
} else {
@@ -142,7 +122,7 @@ func Validate(
response := md5Hex(md5Hex(user+":"+realm+":"+pass) +
":" + nonce + ":" + md5Hex(string(req.Method)+":"+ur.String()))
if *auth.DigestValues.Response != response {
if auth.Response != response {
return fmt.Errorf("authentication failed")
}
default:

View File

@@ -3,75 +3,55 @@ package auth
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/bluenviron/gortsplib/v4/pkg/base"
"github.com/bluenviron/gortsplib/v4/pkg/headers"
"github.com/stretchr/testify/require"
)
func TestValidateErrors(t *testing.T) {
for _, ca := range []struct {
name string
hv base.HeaderValue
err string
}{
{
"invalid auth",
base.HeaderValue{`Invalid`},
"invalid authorization header",
},
{
"digest missing realm",
base.HeaderValue{`Digest `},
"realm is missing",
},
{
"digest missing nonce",
base.HeaderValue{`Digest realm=123`},
"nonce is missing",
},
{
"digest missing username",
base.HeaderValue{`Digest realm=123,nonce=123`},
"username is missing",
},
{
"digest missing uri",
base.HeaderValue{`Digest realm=123,nonce=123,username=123`},
"uri is missing",
},
{
"digest missing response",
base.HeaderValue{`Digest realm=123,nonce=123,username=123,uri=123`},
"response is missing",
},
{
"digest wrong nonce",
base.HeaderValue{`Digest realm=123,nonce=123,username=123,uri=123,response=123`},
"wrong nonce",
},
{
"digest wrong realm",
base.HeaderValue{`Digest realm=123,nonce=abcde,username=123,uri=123,response=123`},
"wrong realm",
},
} {
t.Run(ca.name, func(t *testing.T) {
err := Validate(
&base.Request{
Method: base.Describe,
URL: nil,
Header: base.Header{
"Authorization": ca.hv,
},
func FuzzValidate(f *testing.F) {
f.Add(`Invalid`)
f.Add(`Digest `)
f.Add(`Digest realm=123`)
f.Add(`Digest realm=123,nonce=123`)
f.Add(`Digest realm=123,nonce=123,username=123`)
f.Add(`Digest realm=123,nonce=123,username=123,uri=123`)
f.Add(`Digest realm=123,nonce=123,username=123,uri=123,response=123`)
f.Add(`Digest realm=123,nonce=abcde,username=123,uri=123,response=123`)
f.Fuzz(func(t *testing.T, a string) {
Validate( //nolint:errcheck
&base.Request{
Method: base.Describe,
URL: nil,
Header: base.Header{
"Authorization": base.HeaderValue{a},
},
"myuser",
"mypass",
nil,
nil,
"IPCAM",
"abcde",
)
require.EqualError(t, err, ca.err)
})
}
},
"myuser",
"mypass",
nil,
nil,
"IPCAM",
"abcde",
)
})
}
func TestValidateAdditionalErrors(t *testing.T) {
err := Validate(
&base.Request{
Method: base.Describe,
URL: nil,
Header: base.Header{
"Authorization": base.HeaderValue{"Basic bXl1c2VyOm15cGFzcw=="},
},
},
"myuser",
"mypass",
nil,
[]headers.AuthMethod{headers.AuthDigest},
"IPCAM",
"abcde",
)
require.Error(t, err)
}

View File

@@ -19,37 +19,32 @@ const (
AuthDigest
)
// Authenticate is an Authenticate or a WWW-Authenticate header.
// Authenticate is a WWW-Authenticate header.
type Authenticate struct {
// authentication method
Method AuthMethod
// (optional) username
Username *string
// realm
Realm string
// (optional) realm
Realm *string
//
// Digest authentication fields
//
// (optional) nonce
Nonce *string
// nonce
Nonce string
// (optional) uri
URI *string
// (optional) response
Response *string
// (optional) opaque
// opaque
Opaque *string
// (optional) stale
// stale
Stale *string
// (optional) algorithm
// algorithm
Algorithm *string
}
// Unmarshal decodes an Authenticate or a WWW-Authenticate header.
// Unmarshal decodes a WWW-Authenticate header.
func (h *Authenticate) Unmarshal(v base.HeaderValue) error {
if len(v) == 0 {
return fmt.Errorf("value not provided")
@@ -78,93 +73,86 @@ func (h *Authenticate) Unmarshal(v base.HeaderValue) error {
return fmt.Errorf("invalid method (%s)", method)
}
kvs, err := keyValParse(v0, ',')
if err != nil {
return err
}
if h.Method == AuthBasic {
kvs, err := keyValParse(v0, ',')
if err != nil {
return err
}
for k, rv := range kvs {
v := rv
realmReceived := false
switch k {
case "username":
h.Username = &v
for k, rv := range kvs {
v := rv
case "realm":
h.Realm = &v
if k == "realm" {
h.Realm = v
realmReceived = true
}
}
case "nonce":
h.Nonce = &v
if !realmReceived {
return fmt.Errorf("realm is missing")
}
} else { // digest
kvs, err := keyValParse(v0, ',')
if err != nil {
return err
}
case "uri":
h.URI = &v
realmReceived := false
nonceReceived := false
case "response":
h.Response = &v
for k, rv := range kvs {
v := rv
case "opaque":
h.Opaque = &v
switch k {
case "realm":
h.Realm = v
realmReceived = true
case "stale":
h.Stale = &v
case "nonce":
h.Nonce = v
nonceReceived = true
case "algorithm":
h.Algorithm = &v
case "opaque":
h.Opaque = &v
case "stale":
h.Stale = &v
case "algorithm":
h.Algorithm = &v
}
}
if !realmReceived || !nonceReceived {
return fmt.Errorf("one or more digest fields are missing")
}
}
return nil
}
// Marshal encodes an Authenticate or a WWW-Authenticate header.
// Marshal encodes a WWW-Authenticate header.
func (h Authenticate) Marshal() base.HeaderValue {
ret := ""
switch h.Method {
case AuthBasic:
ret += "Basic"
case AuthDigest:
ret += "Digest"
if h.Method == AuthBasic {
return base.HeaderValue{"Basic " +
"realm=\"" + h.Realm + "\""}
}
ret += " "
var rets []string
if h.Username != nil {
rets = append(rets, "username=\""+*h.Username+"\"")
}
if h.Realm != nil {
rets = append(rets, "realm=\""+*h.Realm+"\"")
}
if h.Nonce != nil {
rets = append(rets, "nonce=\""+*h.Nonce+"\"")
}
if h.URI != nil {
rets = append(rets, "uri=\""+*h.URI+"\"")
}
if h.Response != nil {
rets = append(rets, "response=\""+*h.Response+"\"")
}
ret := "Digest realm=\"" + h.Realm + "\", nonce=\"" + h.Nonce + "\""
if h.Opaque != nil {
rets = append(rets, "opaque=\""+*h.Opaque+"\"")
ret += ", opaque=\"" + *h.Opaque + "\""
}
if h.Stale != nil {
rets = append(rets, "stale=\""+*h.Stale+"\"")
ret += ", stale=\"" + *h.Stale + "\""
}
if h.Algorithm != nil {
rets = append(rets, "algorithm=\""+*h.Algorithm+"\"")
ret += ", algorithm=\"" + *h.Algorithm + "\""
}
ret += strings.Join(rets, ", ")
return base.HeaderValue{ret}
}

View File

@@ -24,82 +24,52 @@ var casesAuthenticate = []struct {
base.HeaderValue{`Basic realm="4419b63f5e51"`},
Authenticate{
Method: AuthBasic,
Realm: stringPtr("4419b63f5e51"),
Realm: "4419b63f5e51",
},
},
{
"digest request 1",
"digest 1",
base.HeaderValue{`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", stale="FALSE"`},
base.HeaderValue{`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", stale="FALSE"`},
Authenticate{
Method: AuthDigest,
Realm: stringPtr("4419b63f5e51"),
Nonce: stringPtr("8b84a3b789283a8bea8da7fa7d41f08b"),
Realm: "4419b63f5e51",
Nonce: "8b84a3b789283a8bea8da7fa7d41f08b",
Stale: stringPtr("FALSE"),
},
},
{
"digest request 2",
"digest 2",
base.HeaderValue{`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", stale=FALSE`},
base.HeaderValue{`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", stale="FALSE"`},
Authenticate{
Method: AuthDigest,
Realm: stringPtr("4419b63f5e51"),
Nonce: stringPtr("8b84a3b789283a8bea8da7fa7d41f08b"),
Realm: "4419b63f5e51",
Nonce: "8b84a3b789283a8bea8da7fa7d41f08b",
Stale: stringPtr("FALSE"),
},
},
{
"digest request 3",
"digest 3",
base.HeaderValue{`Digest realm="4419b63f5e51",nonce="133767111917411116111311118211673010032", stale="FALSE"`},
base.HeaderValue{`Digest realm="4419b63f5e51", nonce="133767111917411116111311118211673010032", stale="FALSE"`},
Authenticate{
Method: AuthDigest,
Realm: stringPtr("4419b63f5e51"),
Nonce: stringPtr("133767111917411116111311118211673010032"),
Realm: "4419b63f5e51",
Nonce: "133767111917411116111311118211673010032",
Stale: stringPtr("FALSE"),
},
},
{
"digest response generic",
base.HeaderValue{`Digest username="aa", realm="bb", nonce="cc", uri="dd", response="ee"`},
base.HeaderValue{`Digest username="aa", realm="bb", nonce="cc", uri="dd", response="ee"`},
Authenticate{
Method: AuthDigest,
Username: stringPtr("aa"),
Realm: stringPtr("bb"),
Nonce: stringPtr("cc"),
URI: stringPtr("dd"),
Response: stringPtr("ee"),
},
},
{
"digest response with empty field",
base.HeaderValue{`Digest username="", realm="IPCAM", ` +
`nonce="5d17cd12b9fa8a85ac5ceef0926ea5a6", uri="rtsp://localhost:8554/mystream", ` +
`response="c072ae90eb4a27f4cdcb90d62266b2a1"`},
base.HeaderValue{`Digest username="", realm="IPCAM", ` +
`nonce="5d17cd12b9fa8a85ac5ceef0926ea5a6", uri="rtsp://localhost:8554/mystream", ` +
`response="c072ae90eb4a27f4cdcb90d62266b2a1"`},
Authenticate{
Method: AuthDigest,
Username: stringPtr(""),
Realm: stringPtr("IPCAM"),
Nonce: stringPtr("5d17cd12b9fa8a85ac5ceef0926ea5a6"),
URI: stringPtr("rtsp://localhost:8554/mystream"),
Response: stringPtr("c072ae90eb4a27f4cdcb90d62266b2a1"),
},
},
{
"digest response with no spaces and additional fields",
"digest after failed auth",
base.HeaderValue{`Digest realm="Please log in with a valid username",` +
`nonce="752a62306daf32b401a41004555c7663",opaque="",stale=FALSE,algorithm=MD5`},
base.HeaderValue{`Digest realm="Please log in with a valid username", ` +
`nonce="752a62306daf32b401a41004555c7663", opaque="", stale="FALSE", algorithm="MD5"`},
Authenticate{
Method: AuthDigest,
Realm: stringPtr("Please log in with a valid username"),
Nonce: stringPtr("752a62306daf32b401a41004555c7663"),
Realm: "Please log in with a valid username",
Nonce: "752a62306daf32b401a41004555c7663",
Opaque: stringPtr(""),
Stale: stringPtr("FALSE"),
Algorithm: stringPtr("MD5"),
@@ -118,46 +88,6 @@ func TestAuthenticateUnmarshal(t *testing.T) {
}
}
func TestAutenticatehUnmarshalErrors(t *testing.T) {
for _, ca := range []struct {
name string
hv base.HeaderValue
err string
}{
{
"empty",
base.HeaderValue{},
"value not provided",
},
{
"2 values",
base.HeaderValue{"a", "b"},
"value provided multiple times ([a b])",
},
{
"no keys",
base.HeaderValue{"Basic"},
"unable to split between method and keys (Basic)",
},
{
"invalid keys",
base.HeaderValue{`Basic key1="k`},
"apexes not closed (key1=\"k)",
},
{
"invalid method",
base.HeaderValue{"Testing key1=val1"},
"invalid method (Testing)",
},
} {
t.Run(ca.name, func(t *testing.T) {
var h Authenticate
err := h.Unmarshal(ca.hv)
require.EqualError(t, err, ca.err)
})
}
}
func TestAuthenticateMarshal(t *testing.T) {
for _, ca := range casesAuthenticate {
t.Run(ca.name, func(t *testing.T) {
@@ -166,3 +96,28 @@ func TestAuthenticateMarshal(t *testing.T) {
})
}
}
func FuzzAuthenticateUnmarshal(f *testing.F) {
for _, ca := range casesAuthenticate {
f.Add(ca.vin[0])
}
f.Fuzz(func(t *testing.T, b string) {
var h Authenticate
h.Unmarshal(base.HeaderValue{b}) //nolint:errcheck
})
}
func TestAuthenticateAdditionalErrors(t *testing.T) {
func() {
var h Authenticate
err := h.Unmarshal(base.HeaderValue{})
require.Error(t, err)
}()
func() {
var h Authenticate
err := h.Unmarshal(base.HeaderValue{"a", "b"})
require.Error(t, err)
}()
}

View File

@@ -13,14 +13,37 @@ type Authorization struct {
// authentication method
Method AuthMethod
// basic user
//
// Basic authentication fields
//
// user
BasicUser string
// basic password
// password
BasicPass string
// digest values
DigestValues Authenticate
//
// Digest authentication fields
//
// username
Username string
// realm
Realm string
// nonce
Nonce string
// URI
URI string
// response
Response string
// response
Opaque *string
}
// Unmarshal decodes an Authorization header.
@@ -35,12 +58,24 @@ func (h *Authorization) Unmarshal(v base.HeaderValue) error {
v0 := v[0]
switch {
case strings.HasPrefix(v0, "Basic "):
i := strings.IndexByte(v0, ' ')
if i < 0 {
return fmt.Errorf("unable to split between method and keys (%v)", v0)
}
method, v0 := v0[:i], v0[i+1:]
switch method {
case "Basic":
h.Method = AuthBasic
v0 = v0[len("Basic "):]
case "Digest":
h.Method = AuthDigest
default:
return fmt.Errorf("invalid method (%s)", method)
}
if h.Method == AuthBasic {
tmp, err := base64.StdEncoding.DecodeString(v0)
if err != nil {
return fmt.Errorf("invalid value")
@@ -52,20 +87,50 @@ func (h *Authorization) Unmarshal(v base.HeaderValue) error {
}
h.BasicUser, h.BasicPass = tmp2[0], tmp2[1]
case strings.HasPrefix(v0, "Digest "):
h.Method = AuthDigest
var vals Authenticate
err := vals.Unmarshal(base.HeaderValue{v0})
} else { // digest
kvs, err := keyValParse(v0, ',')
if err != nil {
return err
}
h.DigestValues = vals
realmReceived := false
usernameReceived := false
nonceReceived := false
uriReceived := false
responseReceived := false
default:
return fmt.Errorf("invalid authorization header")
for k, rv := range kvs {
v := rv
switch k {
case "realm":
h.Realm = v
realmReceived = true
case "username":
h.Username = v
usernameReceived = true
case "nonce":
h.Nonce = v
nonceReceived = true
case "uri":
h.URI = v
uriReceived = true
case "response":
h.Response = v
responseReceived = true
case "opaque":
h.Opaque = &v
}
}
if !realmReceived || !usernameReceived || !nonceReceived || !uriReceived || !responseReceived {
return fmt.Errorf("one or more digest fields are missing")
}
}
return nil
@@ -73,13 +138,18 @@ func (h *Authorization) Unmarshal(v base.HeaderValue) error {
// Marshal encodes an Authorization header.
func (h Authorization) Marshal() base.HeaderValue {
switch h.Method {
case AuthBasic:
response := base64.StdEncoding.EncodeToString([]byte(h.BasicUser + ":" + h.BasicPass))
return base.HeaderValue{"Basic " + response}
default: // AuthDigest
return h.DigestValues.Marshal()
if h.Method == AuthBasic {
return base.HeaderValue{"Basic " +
base64.StdEncoding.EncodeToString([]byte(h.BasicUser+":"+h.BasicPass))}
}
ret := "Digest " +
"username=\"" + h.Username + "\", realm=\"" + h.Realm + "\", " +
"nonce=\"" + h.Nonce + "\", uri=\"" + h.URI + "\", response=\"" + h.Response + "\""
if h.Opaque != nil {
ret += ", opaque=\"" + *h.Opaque + "\""
}
return base.HeaderValue{ret}
}

View File

@@ -26,16 +26,37 @@ var casesAuthorization = []struct {
},
{
"digest",
base.HeaderValue{"Digest realm=\"4419b63f5e51\", nonce=\"8b84a3b789283a8bea8da7fa7d41f08b\", stale=\"FALSE\""},
base.HeaderValue{"Digest realm=\"4419b63f5e51\", nonce=\"8b84a3b789283a8bea8da7fa7d41f08b\", stale=\"FALSE\""},
base.HeaderValue{`Digest username="Mufasa", realm="testrealm@host.com", ` +
`nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", ` +
`uri="/dir/index.html", response="e966c932a9242554e42c8ee200cec7f6", opaque="5ccc069c403ebaf9f0171e9517f40e41"`},
base.HeaderValue{`Digest username="Mufasa", realm="testrealm@host.com", ` +
`nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", ` +
`uri="/dir/index.html", response="e966c932a9242554e42c8ee200cec7f6", opaque="5ccc069c403ebaf9f0171e9517f40e41"`},
Authorization{
Method: AuthDigest,
DigestValues: Authenticate{
Method: AuthDigest,
Realm: stringPtr("4419b63f5e51"),
Nonce: stringPtr("8b84a3b789283a8bea8da7fa7d41f08b"),
Stale: stringPtr("FALSE"),
},
Method: AuthDigest,
Username: "Mufasa",
Realm: "testrealm@host.com",
Nonce: "dcd98b7102dd2f0e8b11d0f600bfb0c093",
URI: "/dir/index.html",
Response: "e966c932a9242554e42c8ee200cec7f6",
Opaque: stringPtr("5ccc069c403ebaf9f0171e9517f40e41"),
},
},
{
"digest with empty field",
base.HeaderValue{`Digest username="", realm="IPCAM", ` +
`nonce="5d17cd12b9fa8a85ac5ceef0926ea5a6", uri="rtsp://localhost:8554/mystream", ` +
`response="c072ae90eb4a27f4cdcb90d62266b2a1"`},
base.HeaderValue{`Digest username="", realm="IPCAM", ` +
`nonce="5d17cd12b9fa8a85ac5ceef0926ea5a6", uri="rtsp://localhost:8554/mystream", ` +
`response="c072ae90eb4a27f4cdcb90d62266b2a1"`},
Authorization{
Method: AuthDigest,
Username: "",
Realm: "IPCAM",
Nonce: "5d17cd12b9fa8a85ac5ceef0926ea5a6",
URI: "rtsp://localhost:8554/mystream",
Response: "c072ae90eb4a27f4cdcb90d62266b2a1",
},
},
}
@@ -51,51 +72,6 @@ func TestAuthorizationUnmarshal(t *testing.T) {
}
}
func TestAuthorizationUnmarshalErrors(t *testing.T) {
for _, ca := range []struct {
name string
hv base.HeaderValue
err string
}{
{
"empty",
base.HeaderValue{},
"value not provided",
},
{
"2 values",
base.HeaderValue{"a", "b"},
"value provided multiple times ([a b])",
},
{
"invalid",
base.HeaderValue{`Invalid`},
"invalid authorization header",
},
{
"basic invalid 1",
base.HeaderValue{`Basic aaa`},
"invalid value",
},
{
"basic invalid 2",
base.HeaderValue{`Basic aW52YWxpZA==`},
"invalid value",
},
{
"digest invalid",
base.HeaderValue{`Digest test="v`},
"apexes not closed (test=\"v)",
},
} {
t.Run(ca.name, func(t *testing.T) {
var h Authorization
err := h.Unmarshal(ca.hv)
require.EqualError(t, err, ca.err)
})
}
}
func TestAuthorizationMarshal(t *testing.T) {
for _, ca := range casesAuthorization {
t.Run(ca.name, func(t *testing.T) {
@@ -104,3 +80,28 @@ func TestAuthorizationMarshal(t *testing.T) {
})
}
}
func FuzzAuthorizationUnmarshal(f *testing.F) {
for _, ca := range casesAuthorization {
f.Add(ca.vin[0])
}
f.Fuzz(func(t *testing.T, b string) {
var h Authorization
h.Unmarshal(base.HeaderValue{b}) //nolint:errcheck
})
}
func TestAuthorizationAdditionalErrors(t *testing.T) {
func() {
var h Authorization
err := h.Unmarshal(base.HeaderValue{})
require.Error(t, err)
}()
func() {
var h Authorization
err := h.Unmarshal(base.HeaderValue{"a", "b"})
require.Error(t, err)
}()
}

View File

@@ -0,0 +1,2 @@
go test fuzz v1
string("Basic =\"")

View File

@@ -0,0 +1,2 @@
go test fuzz v1
string("Digest ")

View File

@@ -0,0 +1,2 @@
go test fuzz v1
string("0")

View File

@@ -0,0 +1,2 @@
go test fuzz v1
string(" ")

View File

@@ -0,0 +1,2 @@
go test fuzz v1
string("Digest =\"")

View File

@@ -0,0 +1,2 @@
go test fuzz v1
string("Basic ")

View File

@@ -0,0 +1,2 @@
go test fuzz v1
string("Digest ")

View File

@@ -0,0 +1,2 @@
go test fuzz v1
string("0")

View File

@@ -0,0 +1,2 @@
go test fuzz v1
string(" ")

View File

@@ -0,0 +1,2 @@
go test fuzz v1
string("Digest =\"")

View File

@@ -0,0 +1,2 @@
go test fuzz v1
string("Basic ")

View File

@@ -0,0 +1,2 @@
go test fuzz v1
string("Basic 0")