-
Notifications
You must be signed in to change notification settings - Fork 24
/
auth_client.go
114 lines (103 loc) · 2.86 KB
/
auth_client.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package cwmp
import (
// "io/ioutil"
"crypto/md5"
"crypto/rand"
"encoding/base64"
"fmt"
"io"
"net/http"
"net/url"
"strings"
)
type myjar struct {
jar map[string][]*http.Cookie
}
func (p *myjar) SetCookies(u *url.URL, cookies []*http.Cookie) {
p.jar[u.Host] = cookies
}
func (p *myjar) Cookies(u *url.URL) []*http.Cookie {
return p.jar[u.Host]
}
func ConnectionRequestAuth(username string, password string, uri string) (bool, error) {
client := &http.Client{}
jar := &myjar{}
jar.jar = make(map[string][]*http.Cookie)
client.Jar = jar
var req *http.Request
var resp *http.Response
var err error
req, err = http.NewRequest("GET", uri, nil)
resp, err = client.Do(req)
if err != nil {
return false, err
}
if resp.StatusCode == 401 {
var authorization map[string]string = DigestAuthParams(resp)
realmHeader := authorization["realm"]
qopHeader := authorization["qop"]
nonceHeader := authorization["nonce"]
opaqueHeader := authorization["opaque"]
realm := realmHeader
// A1
h := md5.New()
A1 := fmt.Sprintf("%s:%s:%s", username, realm, password)
io.WriteString(h, A1)
HA1 := fmt.Sprintf("%x", h.Sum(nil))
// A2
h = md5.New()
A2 := fmt.Sprintf("GET:%s", "/auth")
io.WriteString(h, A2)
HA2 := fmt.Sprintf("%x", h.Sum(nil))
// response
cnonce := RandomKey()
response := H(strings.Join([]string{HA1, nonceHeader, "00000001", cnonce, qopHeader, HA2}, ":"))
// now make header
AuthHeader := fmt.Sprintf(`Digest username="%s", realm="%s", nonce="%s", uri="%s", cnonce="%s", nc=00000001, qop=%s, response="%s", opaque="%s", algorithm=MD5`,
username, realmHeader, nonceHeader, "/auth", cnonce, qopHeader, response, opaqueHeader)
req.Header.Set("Authorization", AuthHeader)
resp, err = client.Do(req)
} else {
return false, fmt.Errorf("response status code should have been 401, it was %v", resp.StatusCode)
}
return resp.StatusCode == 200, err
}
/*
Parse Authorization header from the http.Request. Returns a map of
auth parameters or nil if the header is not a valid parsable Digest
auth header.
*/
func DigestAuthParams(r *http.Response) map[string]string {
s := strings.SplitN(r.Header.Get("Www-Authenticate"), " ", 2)
if len(s) != 2 || s[0] != "Digest" {
return nil
}
result := map[string]string{}
for _, kv := range strings.Split(s[1], ",") {
parts := strings.SplitN(kv, "=", 2)
if len(parts) != 2 {
continue
}
result[strings.Trim(parts[0], "\" ")] = strings.Trim(parts[1], "\" ")
}
return result
}
func RandomKey() string {
k := make([]byte, 12)
for bytes := 0; bytes < len(k); {
n, err := rand.Read(k[bytes:])
if err != nil {
panic("rand.Read() failed")
}
bytes += n
}
return base64.StdEncoding.EncodeToString(k)
}
/*
H function for MD5 algorithm (returns a lower-case hex MD5 digest)
*/
func H(data string) string {
digest := md5.New()
digest.Write([]byte(data))
return fmt.Sprintf("%x", digest.Sum(nil))
}