mirror of
https://github.com/aler9/gortsplib
synced 2025-10-04 23:02:45 +08:00
auth: add Validate() and deprecate Validator{} (#272)
This commit is contained in:
@@ -1286,12 +1286,12 @@ func TestClientPlayAutomaticProtocol(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, base.Describe, req.Method)
|
require.Equal(t, base.Describe, req.Method)
|
||||||
|
|
||||||
v := auth.NewValidator("myuser", "mypass", nil)
|
nonce := auth.GenerateNonce()
|
||||||
|
|
||||||
err = conn.WriteResponse(&base.Response{
|
err = conn.WriteResponse(&base.Response{
|
||||||
StatusCode: base.StatusUnauthorized,
|
StatusCode: base.StatusUnauthorized,
|
||||||
Header: base.Header{
|
Header: base.Header{
|
||||||
"WWW-Authenticate": v.Header(),
|
"WWW-Authenticate": auth.GenerateWWWAuthenticate(nil, "IPCAM", nonce),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -1300,7 +1300,7 @@ func TestClientPlayAutomaticProtocol(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, base.Describe, req.Method)
|
require.Equal(t, base.Describe, req.Method)
|
||||||
|
|
||||||
err = v.ValidateRequest(req, nil)
|
err = auth.Validate(req, "myuser", "mypass", nil, nil, "IPCAM", nonce)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = conn.WriteResponse(&base.Response{
|
err = conn.WriteResponse(&base.Response{
|
||||||
@@ -1399,12 +1399,12 @@ func TestClientPlayAutomaticProtocol(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, base.Setup, req.Method)
|
require.Equal(t, base.Setup, req.Method)
|
||||||
|
|
||||||
v := auth.NewValidator("myuser", "mypass", nil)
|
nonce := auth.GenerateNonce()
|
||||||
|
|
||||||
err = conn.WriteResponse(&base.Response{
|
err = conn.WriteResponse(&base.Response{
|
||||||
StatusCode: base.StatusUnauthorized,
|
StatusCode: base.StatusUnauthorized,
|
||||||
Header: base.Header{
|
Header: base.Header{
|
||||||
"WWW-Authenticate": v.Header(),
|
"WWW-Authenticate": auth.GenerateWWWAuthenticate(nil, "IPCAM", nonce),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -1414,7 +1414,7 @@ func TestClientPlayAutomaticProtocol(t *testing.T) {
|
|||||||
require.Equal(t, base.Setup, req.Method)
|
require.Equal(t, base.Setup, req.Method)
|
||||||
require.Equal(t, mustParseURL("rtsp://localhost:8554/teststream/"+medias[0].Control), req.URL)
|
require.Equal(t, mustParseURL("rtsp://localhost:8554/teststream/"+medias[0].Control), req.URL)
|
||||||
|
|
||||||
err = v.ValidateRequest(req, nil)
|
err = auth.Validate(req, "myuser", "mypass", nil, nil, "IPCAM", nonce)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var inTH headers.Transport
|
var inTH headers.Transport
|
||||||
|
@@ -165,12 +165,12 @@ func TestClientAuth(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, base.Describe, req.Method)
|
require.Equal(t, base.Describe, req.Method)
|
||||||
|
|
||||||
v := auth.NewValidator("myuser", "mypass", nil)
|
nonce := auth.GenerateNonce()
|
||||||
|
|
||||||
err = conn.WriteResponse(&base.Response{
|
err = conn.WriteResponse(&base.Response{
|
||||||
StatusCode: base.StatusUnauthorized,
|
StatusCode: base.StatusUnauthorized,
|
||||||
Header: base.Header{
|
Header: base.Header{
|
||||||
"WWW-Authenticate": v.Header(),
|
"WWW-Authenticate": auth.GenerateWWWAuthenticate(nil, "IPCAM", nonce),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -179,7 +179,7 @@ func TestClientAuth(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, base.Describe, req.Method)
|
require.Equal(t, base.Describe, req.Method)
|
||||||
|
|
||||||
err = v.ValidateRequest(req, nil)
|
err = auth.Validate(req, "myuser", "mypass", nil, nil, "IPCAM", nonce)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
medias := media.Medias{testH264Media}
|
medias := media.Medias{testH264Media}
|
||||||
|
@@ -47,10 +47,10 @@ func TestAuth(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
t.Run(c1.name+"_"+conf, func(t *testing.T) {
|
t.Run(c1.name+"_"+conf, func(t *testing.T) {
|
||||||
va := NewValidator("testuser", "testpass", c1.methods)
|
nonce := GenerateNonce()
|
||||||
wwwAuthenticate := va.Header()
|
|
||||||
|
|
||||||
se, err := NewSender(wwwAuthenticate,
|
se, err := NewSender(
|
||||||
|
GenerateWWWAuthenticate(c1.methods, "IPCAM", nonce),
|
||||||
func() string {
|
func() string {
|
||||||
if conf == "wronguser" {
|
if conf == "wronguser" {
|
||||||
return "test1user"
|
return "test1user"
|
||||||
@@ -78,7 +78,7 @@ func TestAuth(t *testing.T) {
|
|||||||
|
|
||||||
req.URL = mustParseURL("rtsp://myhost/mypath")
|
req.URL = mustParseURL("rtsp://myhost/mypath")
|
||||||
|
|
||||||
err = va.ValidateRequest(req, nil)
|
err = Validate(req, "testuser", "testpass", nil, c1.methods, "IPCAM", nonce)
|
||||||
|
|
||||||
if conf != "nofail" {
|
if conf != "nofail" {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
@@ -104,10 +104,12 @@ func TestAuthVLC(t *testing.T) {
|
|||||||
"rtsp://myhost/mypath/test?testing/trackID=0",
|
"rtsp://myhost/mypath/test?testing/trackID=0",
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
va := NewValidator("testuser", "testpass",
|
nonce := GenerateNonce()
|
||||||
[]headers.AuthMethod{headers.AuthBasic, headers.AuthDigest})
|
|
||||||
|
|
||||||
se, err := NewSender(va.Header(), "testuser", "testpass")
|
se, err := NewSender(
|
||||||
|
GenerateWWWAuthenticate(nil, "IPCAM", nonce),
|
||||||
|
"testuser",
|
||||||
|
"testpass")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
req := &base.Request{
|
req := &base.Request{
|
||||||
@@ -117,53 +119,10 @@ func TestAuthVLC(t *testing.T) {
|
|||||||
se.AddAuthorization(req)
|
se.AddAuthorization(req)
|
||||||
req.URL = mustParseURL(ca.mediaURL)
|
req.URL = mustParseURL(ca.mediaURL)
|
||||||
|
|
||||||
err = va.ValidateRequest(req, mustParseURL(ca.clientURL))
|
err = Validate(req, "testuser", "testpass", mustParseURL(ca.clientURL), nil, "IPCAM", nonce)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = va.ValidateRequest(req, mustParseURL("rtsp://invalid"))
|
err = Validate(req, "testuser", "testpass", mustParseURL("rtsp://invalid"), nil, "IPCAM", nonce)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAuthHashed(t *testing.T) {
|
|
||||||
for _, conf := range []string{
|
|
||||||
"nofail",
|
|
||||||
"wronguser",
|
|
||||||
"wrongpass",
|
|
||||||
} {
|
|
||||||
t.Run(conf, func(t *testing.T) {
|
|
||||||
se := NewValidator("sha256:rl3rgi4NcZkpAEcacZnQ2VuOfJ0FxAqCRaKB/SwdZoQ=",
|
|
||||||
"sha256:E9JJ8stBJ7QM+nV4ZoUCeHk/gU3tPFh/5YieiJp6n2w=",
|
|
||||||
[]headers.AuthMethod{headers.AuthBasic, headers.AuthDigest})
|
|
||||||
|
|
||||||
va, err := NewSender(se.Header(),
|
|
||||||
func() string {
|
|
||||||
if conf == "wronguser" {
|
|
||||||
return "test1user"
|
|
||||||
}
|
|
||||||
return "testuser"
|
|
||||||
}(),
|
|
||||||
func() string {
|
|
||||||
if conf == "wrongpass" {
|
|
||||||
return "test1pass"
|
|
||||||
}
|
|
||||||
return "testpass"
|
|
||||||
}())
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
req := &base.Request{
|
|
||||||
Method: base.Announce,
|
|
||||||
URL: mustParseURL("rtsp://myhost/mypath"),
|
|
||||||
}
|
|
||||||
va.AddAuthorization(req)
|
|
||||||
|
|
||||||
err = se.ValidateRequest(req, nil)
|
|
||||||
|
|
||||||
if conf != "nofail" {
|
|
||||||
require.Error(t, err)
|
|
||||||
} else {
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@@ -8,7 +8,16 @@ import (
|
|||||||
"github.com/bluenviron/gortsplib/v3/pkg/headers"
|
"github.com/bluenviron/gortsplib/v3/pkg/headers"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Sender allows to generate credentials for a Validator.
|
func findHeader(v base.HeaderValue, prefix string) string {
|
||||||
|
for _, vi := range v {
|
||||||
|
if strings.HasPrefix(vi, prefix) {
|
||||||
|
return vi
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sender allows to send credentials.
|
||||||
type Sender struct {
|
type Sender struct {
|
||||||
user string
|
user string
|
||||||
pass string
|
pass string
|
||||||
@@ -17,18 +26,12 @@ type Sender struct {
|
|||||||
nonce string
|
nonce string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSender allocates a Sender with the WWW-Authenticate header provided by
|
// NewSender allocates a Sender.
|
||||||
// a Validator and a set of credentials.
|
// It requires a WWW-Authenticate header (provided by the server)
|
||||||
|
// and a set of credentials.
|
||||||
func NewSender(v base.HeaderValue, user string, pass string) (*Sender, error) {
|
func NewSender(v base.HeaderValue, user string, pass string) (*Sender, error) {
|
||||||
// prefer digest
|
// prefer digest
|
||||||
if v0 := func() string {
|
if v0 := findHeader(v, "Digest"); v0 != "" {
|
||||||
for _, vi := range v {
|
|
||||||
if strings.HasPrefix(vi, "Digest") {
|
|
||||||
return vi
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}(); v0 != "" {
|
|
||||||
var auth headers.Authenticate
|
var auth headers.Authenticate
|
||||||
err := auth.Unmarshal(base.HeaderValue{v0})
|
err := auth.Unmarshal(base.HeaderValue{v0})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -52,14 +55,7 @@ func NewSender(v base.HeaderValue, user string, pass string) (*Sender, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if v0 := func() string {
|
if v0 := findHeader(v, "Basic"); v0 != "" {
|
||||||
for _, vi := range v {
|
|
||||||
if strings.HasPrefix(vi, "Basic") {
|
|
||||||
return vi
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}(); v0 != "" {
|
|
||||||
var auth headers.Authenticate
|
var auth headers.Authenticate
|
||||||
err := auth.Unmarshal(base.HeaderValue{v0})
|
err := auth.Unmarshal(base.HeaderValue{v0})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -1,20 +0,0 @@
|
|||||||
package auth
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/md5"
|
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/hex"
|
|
||||||
)
|
|
||||||
|
|
||||||
func md5Hex(in string) string {
|
|
||||||
h := md5.New()
|
|
||||||
h.Write([]byte(in))
|
|
||||||
return hex.EncodeToString(h.Sum(nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
func sha256Base64(in string) string {
|
|
||||||
h := sha256.New()
|
|
||||||
h.Write([]byte(in))
|
|
||||||
return base64.StdEncoding.EncodeToString(h.Sum(nil))
|
|
||||||
}
|
|
143
pkg/auth/validate.go
Normal file
143
pkg/auth/validate.go
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/bluenviron/gortsplib/v3/pkg/base"
|
||||||
|
"github.com/bluenviron/gortsplib/v3/pkg/headers"
|
||||||
|
"github.com/bluenviron/gortsplib/v3/pkg/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GenerateNonce generates a nonce that can be used in Validate().
|
||||||
|
func GenerateNonce() string {
|
||||||
|
byts := make([]byte, 16)
|
||||||
|
rand.Read(byts)
|
||||||
|
return hex.EncodeToString(byts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateWWWAuthenticate generates a WWW-Authenticate header.
|
||||||
|
func GenerateWWWAuthenticate(methods []headers.AuthMethod, realm string, nonce string) base.HeaderValue {
|
||||||
|
if methods == nil {
|
||||||
|
methods = []headers.AuthMethod{headers.AuthBasic, headers.AuthDigest}
|
||||||
|
}
|
||||||
|
|
||||||
|
var ret base.HeaderValue
|
||||||
|
for _, m := range methods {
|
||||||
|
switch m {
|
||||||
|
case headers.AuthBasic:
|
||||||
|
ret = append(ret, (&headers.Authenticate{
|
||||||
|
Method: headers.AuthBasic,
|
||||||
|
Realm: &realm,
|
||||||
|
}).Marshal()...)
|
||||||
|
|
||||||
|
case headers.AuthDigest:
|
||||||
|
ret = append(ret, headers.Authenticate{
|
||||||
|
Method: headers.AuthDigest,
|
||||||
|
Realm: &realm,
|
||||||
|
Nonce: &nonce,
|
||||||
|
}.Marshal()...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func contains(list []headers.AuthMethod, item headers.AuthMethod) bool {
|
||||||
|
for _, i := range list {
|
||||||
|
if i == item {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates a request sent by a client.
|
||||||
|
func Validate(
|
||||||
|
req *base.Request,
|
||||||
|
user string,
|
||||||
|
pass string,
|
||||||
|
baseURL *url.URL,
|
||||||
|
methods []headers.AuthMethod,
|
||||||
|
realm string,
|
||||||
|
nonce string,
|
||||||
|
) error {
|
||||||
|
if methods == nil {
|
||||||
|
methods = []headers.AuthMethod{headers.AuthBasic, headers.AuthDigest}
|
||||||
|
}
|
||||||
|
|
||||||
|
var auth headers.Authorization
|
||||||
|
err := auth.Unmarshal(req.Header["Authorization"])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case auth.Method == headers.AuthBasic && contains(methods, headers.AuthBasic):
|
||||||
|
if auth.BasicUser != user {
|
||||||
|
return fmt.Errorf("authentication failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
if auth.BasicPass != pass {
|
||||||
|
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 {
|
||||||
|
return fmt.Errorf("wrong nonce")
|
||||||
|
}
|
||||||
|
|
||||||
|
if *auth.DigestValues.Realm != realm {
|
||||||
|
return fmt.Errorf("wrong realm")
|
||||||
|
}
|
||||||
|
|
||||||
|
if *auth.DigestValues.Username != user {
|
||||||
|
return fmt.Errorf("authentication failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
ur := req.URL
|
||||||
|
|
||||||
|
if *auth.DigestValues.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() {
|
||||||
|
return fmt.Errorf("wrong URL")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("wrong URL")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response := md5Hex(md5Hex(user+":"+realm+":"+pass) +
|
||||||
|
":" + nonce + ":" + md5Hex(string(req.Method)+":"+ur.String()))
|
||||||
|
|
||||||
|
if *auth.DigestValues.Response != response {
|
||||||
|
return fmt.Errorf("authentication failed")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("no supported authentication methods found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
77
pkg/auth/validate_test.go
Normal file
77
pkg/auth/validate_test.go
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/bluenviron/gortsplib/v3/pkg/base"
|
||||||
|
)
|
||||||
|
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"myuser",
|
||||||
|
"mypass",
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
"IPCAM",
|
||||||
|
"abcde",
|
||||||
|
)
|
||||||
|
require.EqualError(t, err, ca.err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@@ -1,7 +1,10 @@
|
|||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/md5"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -11,7 +14,20 @@ import (
|
|||||||
"github.com/bluenviron/gortsplib/v3/pkg/url"
|
"github.com/bluenviron/gortsplib/v3/pkg/url"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func md5Hex(in string) string {
|
||||||
|
h := md5.New()
|
||||||
|
h.Write([]byte(in))
|
||||||
|
return hex.EncodeToString(h.Sum(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func sha256Base64(in string) string {
|
||||||
|
h := sha256.New()
|
||||||
|
h.Write([]byte(in))
|
||||||
|
return base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||||
|
}
|
||||||
|
|
||||||
// Validator allows to validate credentials generated by a Sender.
|
// Validator allows to validate credentials generated by a Sender.
|
||||||
|
// Deprecated: Validator{} has been replaced by Validate()
|
||||||
type Validator struct {
|
type Validator struct {
|
||||||
user string
|
user string
|
||||||
userHashed bool
|
userHashed bool
|
||||||
@@ -24,6 +40,7 @@ type Validator struct {
|
|||||||
|
|
||||||
// NewValidator allocates a Validator.
|
// NewValidator allocates a Validator.
|
||||||
// If methods is nil, the Basic and Digest methods are used.
|
// If methods is nil, the Basic and Digest methods are used.
|
||||||
|
// Deprecated: Validator{} has been replaced by Validate()
|
||||||
func NewValidator(user string, pass string, methods []headers.AuthMethod) *Validator {
|
func NewValidator(user string, pass string, methods []headers.AuthMethod) *Validator {
|
||||||
if methods == nil {
|
if methods == nil {
|
||||||
methods = []headers.AuthMethod{headers.AuthBasic, headers.AuthDigest}
|
methods = []headers.AuthMethod{headers.AuthBasic, headers.AuthDigest}
|
||||||
@@ -63,6 +80,7 @@ func NewValidator(user string, pass string, methods []headers.AuthMethod) *Valid
|
|||||||
|
|
||||||
// Header generates the WWW-Authenticate header needed by a client to
|
// Header generates the WWW-Authenticate header needed by a client to
|
||||||
// authenticate.
|
// authenticate.
|
||||||
|
// Deprecated: Validator{} has been replaced by Validate()
|
||||||
func (va *Validator) Header() base.HeaderValue {
|
func (va *Validator) Header() base.HeaderValue {
|
||||||
var ret base.HeaderValue
|
var ret base.HeaderValue
|
||||||
for _, m := range va.methods {
|
for _, m := range va.methods {
|
||||||
@@ -84,8 +102,9 @@ func (va *Validator) Header() base.HeaderValue {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateRequest validates a request sent by a client.
|
// Validate validates a request sent by a client.
|
||||||
func (va *Validator) ValidateRequest(req *base.Request, baseURL *url.URL) error {
|
// Deprecated: Validator{} has been replaced by Validate()
|
||||||
|
func (va *Validator) Validate(req *base.Request, baseURL *url.URL) error {
|
||||||
var auth headers.Authorization
|
var auth headers.Authorization
|
||||||
err := auth.Unmarshal(req.Header["Authorization"])
|
err := auth.Unmarshal(req.Header["Authorization"])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -58,7 +58,7 @@ func TestValidatorErrors(t *testing.T) {
|
|||||||
t.Run(ca.name, func(t *testing.T) {
|
t.Run(ca.name, func(t *testing.T) {
|
||||||
va := NewValidator("myuser", "mypass", nil)
|
va := NewValidator("myuser", "mypass", nil)
|
||||||
va.nonce = "abcde"
|
va.nonce = "abcde"
|
||||||
err := va.ValidateRequest(&base.Request{
|
err := va.Validate(&base.Request{
|
||||||
Method: base.Describe,
|
Method: base.Describe,
|
||||||
URL: nil,
|
URL: nil,
|
||||||
Header: base.Header{
|
Header: base.Header{
|
||||||
|
@@ -1045,17 +1045,17 @@ func TestServerSessionTeardown(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestServerAuth(t *testing.T) {
|
func TestServerAuth(t *testing.T) {
|
||||||
authValidator := auth.NewValidator("myuser", "mypass", nil)
|
nonce := auth.GenerateNonce()
|
||||||
|
|
||||||
s := &Server{
|
s := &Server{
|
||||||
Handler: &testServerHandler{
|
Handler: &testServerHandler{
|
||||||
onAnnounce: func(ctx *ServerHandlerOnAnnounceCtx) (*base.Response, error) {
|
onAnnounce: func(ctx *ServerHandlerOnAnnounceCtx) (*base.Response, error) {
|
||||||
err := authValidator.ValidateRequest(ctx.Request, nil)
|
err := auth.Validate(ctx.Request, "myuser", "mypass", nil, nil, "IPCAM", nonce)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusUnauthorized,
|
StatusCode: base.StatusUnauthorized,
|
||||||
Header: base.Header{
|
Header: base.Header{
|
||||||
"WWW-Authenticate": authValidator.Header(),
|
"WWW-Authenticate": auth.GenerateWWWAuthenticate(nil, "IPCAM", nonce),
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user