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)) { if i := strings.Index(pair, "="); i < 0 { m[pair] = "" } else { 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"