* Added authentication negotiation

* Added NTLM support
This commit is contained in:
Andreas Schneider 2018-04-03 11:41:51 +02:00
parent 80b454ed31
commit 944b8a356d
3 changed files with 90 additions and 7 deletions

View File

@ -1,6 +1,21 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
branch = "master"
name = "github.com/ThomsonReutersEikon/go-ntlm"
packages = [
"ntlm",
"ntlm/md4"
]
revision = "2a7c173f9e18233a4ae29891da6a0a63637e2d8d"
[[projects]]
branch = "master"
name = "github.com/vadimi/go-http-ntlm"
packages = ["."]
revision = "bc5a8d8d91a12dd386d3fa1019abb8bb681bdd41"
[[projects]] [[projects]]
branch = "master" branch = "master"
name = "golang.org/x/crypto" name = "golang.org/x/crypto"
@ -19,6 +34,6 @@
[solve-meta] [solve-meta]
analyzer-name = "dep" analyzer-name = "dep"
analyzer-version = 1 analyzer-version = 1
inputs-digest = "724028051592219199897d15df4916e95f132caea6497383bbfabbcdb0672123" inputs-digest = "c47823b171022aea87501cabc3a33c2794c9352672de5255805a03a96f191f7d"
solver-name = "gps-cdcl" solver-name = "gps-cdcl"
solver-version = 1 solver-version = 1

View File

@ -32,3 +32,11 @@
[[constraint]] [[constraint]]
branch = "master" branch = "master"
name = "golang.org/x/crypto" name = "golang.org/x/crypto"
[[constraint]]
branch = "master"
name = "github.com/vadimi/go-http-ntlm"
[[constraint]]
branch = "master"
name = "github.com/Azure/go-ntlmssp"

View File

@ -6,10 +6,12 @@ import (
"encoding/hex" "encoding/hex"
"encoding/xml" "encoding/xml"
"fmt" "fmt"
"github.com/vadimi/go-http-ntlm"
"io" "io"
"net/http" "net/http"
"net/http/cookiejar" "net/http/cookiejar"
"strings" "strings"
"sync"
"text/template" "text/template"
"time" "time"
) )
@ -45,8 +47,64 @@ func (ci CalendarItem) Hash() string {
type EWSCalendar struct { type EWSCalendar struct {
httpClient *http.Client httpClient *http.Client
url string url string
username string }
password string
type authType int
const (
authTypeUnknown authType = iota
authTypeBasic
authTypeNTLM
)
type EWSRoundTripper struct {
initMutex sync.Mutex
authType authType
username string
password string
delegate http.RoundTripper
}
func (er EWSRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
if er.authType == authTypeBasic {
r.SetBasicAuth(er.username, er.password)
}
resp, err := er.delegate.RoundTrip(r)
if err == nil && resp.StatusCode == http.StatusUnauthorized {
er.initMutex.Lock()
defer er.initMutex.Unlock()
if er.authType == authTypeUnknown {
// This is a good time to find out what the server prefers.
authHeaders := resp.Header["Www-Authenticate"]
if authHeaders != nil {
for _, h := range authHeaders {
if strings.HasPrefix(h, "BASIC") {
er.authType = authTypeBasic
} else if strings.HasPrefix(h, "NTLM") {
er.authType = authTypeNTLM
break // NTLM is the best we could do
}
}
}
// So, do we know more than before? If so, try again.
if er.authType > authTypeUnknown {
if er.authType == authTypeNTLM {
// We need to replace the delegator.
er.delegate = &httpntlm.NtlmTransport{
Domain: "",
User: er.username,
Password: er.password,
}
}
return er.RoundTrip(r)
}
}
}
return resp, err
} }
func NewEWSCalendar(url, username, password string) *EWSCalendar { func NewEWSCalendar(url, username, password string) *EWSCalendar {
@ -58,10 +116,13 @@ func NewEWSCalendar(url, username, password string) *EWSCalendar {
return &EWSCalendar{ return &EWSCalendar{
httpClient: &http.Client{ httpClient: &http.Client{
Jar: jar, Jar: jar,
Transport: EWSRoundTripper{
username: username,
password: password,
delegate: http.DefaultTransport,
},
}, },
url: url, url: url,
username: username,
password: password,
} }
} }
@ -72,7 +133,6 @@ func (e *EWSCalendar) prepareRequest(body io.Reader) (req *http.Request, err err
req, err = http.NewRequest(http.MethodPost, e.url, body) req, err = http.NewRequest(http.MethodPost, e.url, body)
req.Header.Set("Content-Type", "text/xml") req.Header.Set("Content-Type", "text/xml")
req.Header.Set("Accept", "text/xml") req.Header.Set("Accept", "text/xml")
req.SetBasicAuth(e.username, e.password)
return return
} }