add automatic authentication

This commit is contained in:
aler9
2020-05-17 13:46:14 +02:00
parent f2060cfd21
commit 59e01c7609
2 changed files with 33 additions and 17 deletions

View File

@@ -13,6 +13,12 @@ type ConnClientConf struct {
// pre-existing TCP connection that will be wrapped // pre-existing TCP connection that will be wrapped
NConn net.Conn NConn net.Conn
// (optional) a username to authenticate with the server
Username string
// (optional) a password to authenticate with the server
Password string
// (optional) timeout for read requests. // (optional) timeout for read requests.
// It defaults to 5 seconds // It defaults to 5 seconds
ReadTimeout time.Duration ReadTimeout time.Duration
@@ -41,7 +47,7 @@ type ConnClient struct {
} }
// NewConnClient allocates a ConnClient. See ConnClientConf for the options. // NewConnClient allocates a ConnClient. See ConnClientConf for the options.
func NewConnClient(conf ConnClientConf) *ConnClient { func NewConnClient(conf ConnClientConf) (*ConnClient, error) {
if conf.ReadTimeout == time.Duration(0) { if conf.ReadTimeout == time.Duration(0) {
conf.ReadTimeout = 5 * time.Second conf.ReadTimeout = 5 * time.Second
} }
@@ -55,11 +61,16 @@ func NewConnClient(conf ConnClientConf) *ConnClient {
conf.WriteBufferSize = 4096 conf.WriteBufferSize = 4096
} }
if conf.Username != "" && conf.Password == "" ||
conf.Username == "" && conf.Password != "" {
return nil, fmt.Errorf("both username and password must be provided")
}
return &ConnClient{ return &ConnClient{
conf: conf, conf: conf,
br: bufio.NewReaderSize(conf.NConn, conf.ReadBufferSize), br: bufio.NewReaderSize(conf.NConn, conf.ReadBufferSize),
bw: bufio.NewWriterSize(conf.NConn, conf.WriteBufferSize), bw: bufio.NewWriterSize(conf.NConn, conf.WriteBufferSize),
} }, nil
} }
// NetConn returns the underlying net.Conn. // NetConn returns the underlying net.Conn.
@@ -67,14 +78,6 @@ func (c *ConnClient) NetConn() net.Conn {
return c.conf.NConn return c.conf.NConn
} }
// SetCredentials allows to automatically insert the Authenticate header into every outgoing request.
// The content of the header is computed with the given user, password, realm and nonce.
func (c *ConnClient) SetCredentials(wwwAuthenticateHeader []string, user string, pass string) error {
var err error
c.auth, err = NewAuthClient(wwwAuthenticateHeader, user, pass)
return err
}
// WriteRequest writes a request and reads a response. // WriteRequest writes a request and reads a response.
func (c *ConnClient) WriteRequest(req *Request) (*Response, error) { func (c *ConnClient) WriteRequest(req *Request) (*Response, error) {
if req.Header == nil { if req.Header == nil {
@@ -108,14 +111,24 @@ func (c *ConnClient) WriteRequest(req *Request) (*Response, error) {
} }
// get session from response // get session from response
if res.StatusCode == StatusOK { if sxRaw, ok := res.Header["Session"]; ok && len(sxRaw) == 1 {
if sxRaw, ok := res.Header["Session"]; ok && len(sxRaw) == 1 { sx, err := ReadHeaderSession(sxRaw[0])
sx, err := ReadHeaderSession(sxRaw[0]) if err != nil {
if err != nil { return nil, fmt.Errorf("unable to parse session header: %s", err)
return nil, fmt.Errorf("unable to parse session header: %s", err)
}
c.session = sx.Session
} }
c.session = sx.Session
}
// setup authentication
if res.StatusCode == StatusUnauthorized && c.conf.Username != "" && c.auth == nil {
auth, err := NewAuthClient(res.Header["WWW-Authenticate"], c.conf.Username, c.conf.Password)
if err != nil {
return nil, fmt.Errorf("unable to setup authentication: %s", err)
}
c.auth = auth
// send request again
return c.WriteRequest(req)
} }
return res, nil return res, nil

View File

@@ -59,6 +59,9 @@ const (
StatusProxyUnavailable StatusCode = 553 StatusProxyUnavailable StatusCode = 553
) )
// StatusMessages contains the status messages associated with each status code.
var StatusMessages = statusMessages
var statusMessages = map[StatusCode]string{ var statusMessages = map[StatusCode]string{
StatusContinue: "Continue", StatusContinue: "Continue",