New file |
| | |
| | | package auth |
| | | |
| | | import ( |
| | | "bytes" |
| | | "crypto/md5" |
| | | "crypto/rand" |
| | | "encoding/base64" |
| | | "fmt" |
| | | "net/http" |
| | | "strings" |
| | | ) |
| | | |
| | | // RandomKey returns a random 16-byte base64 alphabet string |
| | | 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)) |
| | | } |
| | | |
| | | // ParseList parses a comma-separated list of values as described by |
| | | // RFC 2068 and returns list elements. |
| | | // |
| | | // Lifted from https://code.google.com/p/gorilla/source/browse/http/parser/parser.go |
| | | // which was ported from urllib2.parse_http_list, from the Python |
| | | // standard library. |
| | | func ParseList(value string) []string { |
| | | var list []string |
| | | var escape, quote bool |
| | | b := new(bytes.Buffer) |
| | | for _, r := range value { |
| | | switch { |
| | | case escape: |
| | | b.WriteRune(r) |
| | | escape = false |
| | | case quote: |
| | | if r == '\\' { |
| | | escape = true |
| | | } else { |
| | | if r == '"' { |
| | | quote = false |
| | | } |
| | | b.WriteRune(r) |
| | | } |
| | | case r == ',': |
| | | list = append(list, strings.TrimSpace(b.String())) |
| | | b.Reset() |
| | | case r == '"': |
| | | quote = true |
| | | b.WriteRune(r) |
| | | default: |
| | | b.WriteRune(r) |
| | | } |
| | | } |
| | | // Append last part. |
| | | if s := b.String(); s != "" { |
| | | list = append(list, strings.TrimSpace(s)) |
| | | } |
| | | return list |
| | | } |
| | | |
| | | // ParsePairs extracts key/value pairs from a comma-separated list of |
| | | // values as described by RFC 2068 and returns a map[key]value. The |
| | | // resulting values are unquoted. If a list element doesn't contain a |
| | | // "=", the key is the element itself and the value is an empty |
| | | // string. |
| | | // |
| | | // Lifted from https://code.google.com/p/gorilla/source/browse/http/parser/parser.go |
| | | func ParsePairs(value string) map[string]string { |
| | | m := make(map[string]string) |
| | | for _, pair := range ParseList(strings.TrimSpace(value)) { |
| | | switch i := strings.Index(pair, "="); { |
| | | case i < 0: |
| | | // No '=' in pair, treat whole string as a 'key'. |
| | | m[pair] = "" |
| | | case i == len(pair)-1: |
| | | // Malformed pair ('key=' with no value), keep key with empty value. |
| | | m[pair[:i]] = "" |
| | | default: |
| | | v := pair[i+1:] |
| | | if v[0] == '"' && v[len(v)-1] == '"' { |
| | | // Unquote it. |
| | | v = v[1 : len(v)-1] |
| | | } |
| | | m[pair[:i]] = v |
| | | } |
| | | } |
| | | return m |
| | | } |
| | | |
| | | // Headers contains header and error codes used by authenticator. |
| | | type Headers struct { |
| | | Authenticate string // WWW-Authenticate |
| | | Authorization string // Authorization |
| | | AuthInfo string // Authentication-Info |
| | | UnauthCode int // 401 |
| | | UnauthContentType string // text/plain |
| | | UnauthResponse string // Unauthorized. |
| | | } |
| | | |
| | | // V returns NormalHeaders when h is nil, or h otherwise. Allows to |
| | | // use uninitialized *Headers values in structs. |
| | | func (h *Headers) V() *Headers { |
| | | if h == nil { |
| | | return NormalHeaders |
| | | } |
| | | return h |
| | | } |
| | | |
| | | var ( |
| | | // NormalHeaders are the regular Headers used by an HTTP Server for |
| | | // request authentication. |
| | | NormalHeaders = &Headers{ |
| | | Authenticate: "WWW-Authenticate", |
| | | Authorization: "Authorization", |
| | | AuthInfo: "Authentication-Info", |
| | | UnauthCode: http.StatusUnauthorized, |
| | | UnauthContentType: "text/plain", |
| | | UnauthResponse: fmt.Sprintf("%d %s\n", http.StatusUnauthorized, http.StatusText(http.StatusUnauthorized)), |
| | | } |
| | | |
| | | // ProxyHeaders are Headers used by an HTTP Proxy server for proxy |
| | | // access authentication. |
| | | ProxyHeaders = &Headers{ |
| | | Authenticate: "Proxy-Authenticate", |
| | | Authorization: "Proxy-Authorization", |
| | | AuthInfo: "Proxy-Authentication-Info", |
| | | UnauthCode: http.StatusProxyAuthRequired, |
| | | UnauthContentType: "text/plain", |
| | | UnauthResponse: fmt.Sprintf("%d %s\n", http.StatusProxyAuthRequired, http.StatusText(http.StatusProxyAuthRequired)), |
| | | } |
| | | ) |
| | | |
| | | const contentType = "Content-Type" |