move decoding of more configuration into JSON decoding
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
package conf
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var reCredential = regexp.MustCompile(`^[a-zA-Z0-9!\$\(\)\*\+\.;<=>\[\]\^_\-\{\}]+$`)
|
||||
|
||||
const credentialSupportedChars = "A-Z,0-9,!,$,(,),*,+,.,;,<,=,>,[,],^,_,-,{,}"
|
||||
|
||||
// Credential is a parameter that is used as username or password.
|
||||
type Credential string
|
||||
|
||||
// MarshalJSON marshals a Credential into JSON.
|
||||
func (d Credential) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(string(d))
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals a Credential from JSON.
|
||||
func (d *Credential) UnmarshalJSON(b []byte) error {
|
||||
var in string
|
||||
if err := json.Unmarshal(b, &in); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if in != "" &&
|
||||
!strings.HasPrefix(in, "sha256:") &&
|
||||
!reCredential.MatchString(in) {
|
||||
return fmt.Errorf("contains unsupported characters (supported are %s)", credentialSupportedChars)
|
||||
}
|
||||
|
||||
*d = Credential(in)
|
||||
return nil
|
||||
}
|
||||
+9
-41
@@ -11,10 +11,6 @@ import (
|
||||
"github.com/aler9/gortsplib/pkg/base"
|
||||
)
|
||||
|
||||
const userPassSupportedChars = "A-Z,0-9,!,$,(,),*,+,.,;,<,=,>,[,],^,_,-,{,}"
|
||||
|
||||
var reUserPass = regexp.MustCompile(`^[a-zA-Z0-9!\$\(\)\*\+\.;<=>\[\]\^_\-\{\}]+$`)
|
||||
|
||||
var rePathName = regexp.MustCompile(`^[0-9a-zA-Z_\-/\.~]+$`)
|
||||
|
||||
// IsValidPathName checks if a path name is valid.
|
||||
@@ -55,12 +51,12 @@ type PathConf struct {
|
||||
Fallback string `json:"fallback"`
|
||||
|
||||
// authentication
|
||||
PublishUser string `json:"publishUser"`
|
||||
PublishPass string `json:"publishPass"`
|
||||
PublishIPs IPsOrNets `json:"publishIPs"`
|
||||
ReadUser string `json:"readUser"`
|
||||
ReadPass string `json:"readPass"`
|
||||
ReadIPs IPsOrNets `json:"readIPs"`
|
||||
PublishUser Credential `json:"publishUser"`
|
||||
PublishPass Credential `json:"publishPass"`
|
||||
PublishIPs IPsOrNets `json:"publishIPs"`
|
||||
ReadUser Credential `json:"readUser"`
|
||||
ReadPass Credential `json:"readPass"`
|
||||
ReadIPs IPsOrNets `json:"readIPs"`
|
||||
|
||||
// custom commands
|
||||
RunOnInit string `json:"runOnInit"`
|
||||
@@ -211,25 +207,9 @@ func (pconf *PathConf) checkAndFillMissing(name string) error {
|
||||
return fmt.Errorf("read username and password must be both filled")
|
||||
}
|
||||
|
||||
if pconf.PublishUser != "" {
|
||||
if pconf.Source != "publisher" {
|
||||
return fmt.Errorf("'publishUser' is useless when source is not 'publisher'")
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(pconf.PublishUser, "sha256:") && !reUserPass.MatchString(pconf.PublishUser) {
|
||||
return fmt.Errorf("publish username contains unsupported characters (supported are %s)", userPassSupportedChars)
|
||||
}
|
||||
}
|
||||
|
||||
if pconf.PublishPass != "" {
|
||||
if pconf.Source != "publisher" {
|
||||
return fmt.Errorf("'publishPass' is useless when source is not 'publisher', since " +
|
||||
"the stream is not provided by a publisher, but by a fixed source")
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(pconf.PublishPass, "sha256:") && !reUserPass.MatchString(pconf.PublishPass) {
|
||||
return fmt.Errorf("publish password contains unsupported characters (supported are %s)", userPassSupportedChars)
|
||||
}
|
||||
if pconf.PublishUser != "" && pconf.Source != "publisher" {
|
||||
return fmt.Errorf("'publishUser' is useless when source is not 'publisher', since " +
|
||||
"the stream is not provided by a publisher, but by a fixed source")
|
||||
}
|
||||
|
||||
if len(pconf.PublishIPs) > 0 && pconf.Source != "publisher" {
|
||||
@@ -242,18 +222,6 @@ func (pconf *PathConf) checkAndFillMissing(name string) error {
|
||||
return fmt.Errorf("read username and password must be both filled")
|
||||
}
|
||||
|
||||
if pconf.ReadUser != "" {
|
||||
if !strings.HasPrefix(pconf.ReadUser, "sha256:") && !reUserPass.MatchString(pconf.ReadUser) {
|
||||
return fmt.Errorf("read username contains unsupported characters (supported are %s)", userPassSupportedChars)
|
||||
}
|
||||
}
|
||||
|
||||
if pconf.ReadPass != "" {
|
||||
if !strings.HasPrefix(pconf.ReadPass, "sha256:") && !reUserPass.MatchString(pconf.ReadPass) {
|
||||
return fmt.Errorf("read password contains unsupported characters (supported are %s)", userPassSupportedChars)
|
||||
}
|
||||
}
|
||||
|
||||
if pconf.RunOnInit != "" && pconf.Regexp != nil {
|
||||
return fmt.Errorf("a path with a regular expression does not support option 'runOnInit'; use another path")
|
||||
}
|
||||
|
||||
@@ -112,12 +112,12 @@ func loadConfPathData(ctx *gin.Context) (interface{}, error) {
|
||||
Fallback *string `json:"fallback"`
|
||||
|
||||
// authentication
|
||||
PublishUser *string `json:"publishUser"`
|
||||
PublishPass *string `json:"publishPass"`
|
||||
PublishIPs *conf.IPsOrNets `json:"publishIPs"`
|
||||
ReadUser *string `json:"readUser"`
|
||||
ReadPass *string `json:"readPass"`
|
||||
ReadIPs *conf.IPsOrNets `json:"readIPs"`
|
||||
PublishUser *conf.Credential `json:"publishUser"`
|
||||
PublishPass *conf.Credential `json:"publishPass"`
|
||||
PublishIPs *conf.IPsOrNets `json:"publishIPs"`
|
||||
ReadUser *conf.Credential `json:"readUser"`
|
||||
ReadPass *conf.Credential `json:"readPass"`
|
||||
ReadIPs *conf.IPsOrNets `json:"readIPs"`
|
||||
|
||||
// custom commands
|
||||
RunOnInit *string `json:"runOnInit"`
|
||||
|
||||
@@ -424,7 +424,7 @@ func (r *hlsMuxer) handleRequest(req hlsMuxerRequest) {
|
||||
|
||||
if conf.ReadUser != "" {
|
||||
user, pass, ok := req.Req.BasicAuth()
|
||||
if !ok || user != conf.ReadUser || pass != conf.ReadPass {
|
||||
if !ok || user != string(conf.ReadUser) || pass != string(conf.ReadPass) {
|
||||
req.W.Header().Set("WWW-Authenticate", `Basic realm="rtsp-simple-server"`)
|
||||
req.W.WriteHeader(http.StatusUnauthorized)
|
||||
req.Res <- nil
|
||||
|
||||
@@ -123,7 +123,7 @@ type pathDescribeReq struct {
|
||||
PathName string
|
||||
URL *base.URL
|
||||
IP net.IP
|
||||
ValidateCredentials func(pathUser string, pathPass string) error
|
||||
ValidateCredentials func(pathUser conf.Credential, pathPass conf.Credential) error
|
||||
Res chan pathDescribeRes
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ type pathReaderSetupPlayReq struct {
|
||||
Author reader
|
||||
PathName string
|
||||
IP net.IP
|
||||
ValidateCredentials func(pathUser string, pathPass string) error
|
||||
ValidateCredentials func(pathUser conf.Credential, pathPass conf.Credential) error
|
||||
Res chan pathReaderSetupPlayRes
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ type pathPublisherAnnounceReq struct {
|
||||
Author publisher
|
||||
PathName string
|
||||
IP net.IP
|
||||
ValidateCredentials func(pathUser string, pathPass string) error
|
||||
ValidateCredentials func(pathUser conf.Credential, pathPass conf.Credential) error
|
||||
Res chan pathPublisherAnnounceRes
|
||||
}
|
||||
|
||||
|
||||
@@ -312,11 +312,11 @@ func (pm *pathManager) findPathConf(name string) (string, *conf.PathConf, error)
|
||||
|
||||
func (pm *pathManager) authenticate(
|
||||
ip net.IP,
|
||||
validateCredentials func(pathUser string, pathPass string) error,
|
||||
validateCredentials func(pathUser conf.Credential, pathPass conf.Credential) error,
|
||||
pathName string,
|
||||
pathIPs []interface{},
|
||||
pathUser string,
|
||||
pathPass string,
|
||||
pathUser conf.Credential,
|
||||
pathPass conf.Credential,
|
||||
) error {
|
||||
// validate ip
|
||||
if pathIPs != nil && ip != nil {
|
||||
|
||||
@@ -205,7 +205,7 @@ func (c *rtmpConn) runRead(ctx context.Context) error {
|
||||
Author: c,
|
||||
PathName: pathName,
|
||||
IP: c.ip(),
|
||||
ValidateCredentials: func(pathUser string, pathPass string) error {
|
||||
ValidateCredentials: func(pathUser conf.Credential, pathPass conf.Credential) error {
|
||||
return c.validateCredentials(pathUser, pathPass, query)
|
||||
},
|
||||
})
|
||||
@@ -440,7 +440,7 @@ func (c *rtmpConn) runPublish(ctx context.Context) error {
|
||||
Author: c,
|
||||
PathName: pathName,
|
||||
IP: c.ip(),
|
||||
ValidateCredentials: func(pathUser string, pathPass string) error {
|
||||
ValidateCredentials: func(pathUser conf.Credential, pathPass conf.Credential) error {
|
||||
return c.validateCredentials(pathUser, pathPass, query)
|
||||
},
|
||||
})
|
||||
@@ -548,12 +548,12 @@ func (c *rtmpConn) runPublish(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (c *rtmpConn) validateCredentials(
|
||||
pathUser string,
|
||||
pathPass string,
|
||||
pathUser conf.Credential,
|
||||
pathPass conf.Credential,
|
||||
query url.Values,
|
||||
) error {
|
||||
if query.Get("user") != pathUser ||
|
||||
query.Get("pass") != pathPass {
|
||||
if query.Get("user") != string(pathUser) ||
|
||||
query.Get("pass") != string(pathPass) {
|
||||
return pathErrAuthCritical{
|
||||
Message: "wrong username or password",
|
||||
}
|
||||
|
||||
@@ -99,16 +99,16 @@ func (c *rtspConn) ip() net.IP {
|
||||
}
|
||||
|
||||
func (c *rtspConn) validateCredentials(
|
||||
pathUser string,
|
||||
pathPass string,
|
||||
pathUser conf.Credential,
|
||||
pathPass conf.Credential,
|
||||
pathName string,
|
||||
req *base.Request,
|
||||
) error {
|
||||
// reset authValidator every time the credentials change
|
||||
if c.authValidator == nil || c.authUser != pathUser || c.authPass != pathPass {
|
||||
c.authUser = pathUser
|
||||
c.authPass = pathPass
|
||||
c.authValidator = auth.NewValidator(pathUser, pathPass, c.authMethods)
|
||||
if c.authValidator == nil || c.authUser != string(pathUser) || c.authPass != string(pathPass) {
|
||||
c.authUser = string(pathUser)
|
||||
c.authPass = string(pathPass)
|
||||
c.authValidator = auth.NewValidator(string(pathUser), string(pathPass), c.authMethods)
|
||||
}
|
||||
|
||||
// VLC strips the control attribute
|
||||
@@ -193,7 +193,7 @@ func (c *rtspConn) OnDescribe(ctx *gortsplib.ServerHandlerOnDescribeCtx,
|
||||
PathName: ctx.Path,
|
||||
URL: ctx.Req.URL,
|
||||
IP: c.ip(),
|
||||
ValidateCredentials: func(pathUser string, pathPass string) error {
|
||||
ValidateCredentials: func(pathUser conf.Credential, pathPass conf.Credential) error {
|
||||
return c.validateCredentials(pathUser, pathPass, ctx.Path, ctx.Req)
|
||||
},
|
||||
})
|
||||
|
||||
@@ -134,7 +134,7 @@ func (s *rtspSession) OnAnnounce(c *rtspConn, ctx *gortsplib.ServerHandlerOnAnno
|
||||
Author: s,
|
||||
PathName: ctx.Path,
|
||||
IP: ctx.Conn.NetConn().RemoteAddr().(*net.TCPAddr).IP,
|
||||
ValidateCredentials: func(pathUser string, pathPass string) error {
|
||||
ValidateCredentials: func(pathUser conf.Credential, pathPass conf.Credential) error {
|
||||
return c.validateCredentials(pathUser, pathPass, ctx.Path, ctx.Req)
|
||||
},
|
||||
})
|
||||
@@ -198,7 +198,7 @@ func (s *rtspSession) OnSetup(c *rtspConn, ctx *gortsplib.ServerHandlerOnSetupCt
|
||||
Author: s,
|
||||
PathName: ctx.Path,
|
||||
IP: ctx.Conn.NetConn().RemoteAddr().(*net.TCPAddr).IP,
|
||||
ValidateCredentials: func(pathUser string, pathPass string) error {
|
||||
ValidateCredentials: func(pathUser conf.Credential, pathPass conf.Credential) error {
|
||||
return c.validateCredentials(pathUser, pathPass, ctx.Path, ctx.Req)
|
||||
},
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user