Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
119 lines
3.4 KiB
119 lines
3.4 KiB
package openid |
|
|
|
import ( |
|
"errors" |
|
"io" |
|
"io/ioutil" |
|
"strings" |
|
|
|
"golang.org/x/net/html" |
|
) |
|
|
|
var yadisHeaders = map[string]string{ |
|
"Accept": "application/xrds+xml"} |
|
|
|
func yadisDiscovery(id string, getter httpGetter) (opEndpoint string, opLocalID string, err error) { |
|
// Section 6.2.4 of Yadis 1.0 specifications. |
|
// The Yadis Protocol is initiated by the Relying Party Agent |
|
// with an initial HTTP request using the Yadis URL. |
|
|
|
// This request MUST be either a GET or a HEAD request. |
|
|
|
// A GET or HEAD request MAY include an HTTP Accept |
|
// request-header (HTTP 14.1) specifying MIME media type, |
|
// application/xrds+xml. |
|
resp, err := getter.Get(id, yadisHeaders) |
|
if err != nil { |
|
return "", "", err |
|
} |
|
|
|
defer resp.Body.Close() |
|
|
|
// Section 6.2.5 from Yadis 1.0 spec: Response |
|
|
|
contentType := resp.Header.Get("Content-Type") |
|
|
|
// The response MUST be one of: |
|
// (see 6.2.6 for precedence) |
|
if l := resp.Header.Get("X-XRDS-Location"); l != "" { |
|
// 2. HTTP response-headers that include an X-XRDS-Location |
|
// response-header, together with a document |
|
return getYadisResourceDescriptor(l, getter) |
|
} else if strings.Contains(contentType, "text/html") { |
|
// 1. An HTML document with a <head> element that includes a |
|
// <meta> element with http-equiv attribute, X-XRDS-Location, |
|
|
|
metaContent, err := findMetaXrdsLocation(resp.Body) |
|
if err == nil { |
|
return getYadisResourceDescriptor(metaContent, getter) |
|
} |
|
return "", "", err |
|
} else if strings.Contains(contentType, "application/xrds+xml") { |
|
// 4. A document of MIME media type, application/xrds+xml. |
|
body, err := ioutil.ReadAll(resp.Body) |
|
if err == nil { |
|
return parseXrds(body) |
|
} |
|
return "", "", err |
|
} |
|
// 3. HTTP response-headers only, which MAY include an |
|
// X-XRDS-Location response-header, a content-type |
|
// response-header specifying MIME media type, |
|
// application/xrds+xml, or both. |
|
// (this is handled by one of the 2 previous if statements) |
|
return "", "", errors.New("No expected header, or content type") |
|
} |
|
|
|
// Similar as above, but we expect an absolute Yadis document URL. |
|
func getYadisResourceDescriptor(id string, getter httpGetter) (opEndpoint string, opLocalID string, err error) { |
|
resp, err := getter.Get(id, yadisHeaders) |
|
if err != nil { |
|
return "", "", err |
|
} |
|
defer resp.Body.Close() |
|
// 4. A document of MIME media type, application/xrds+xml. |
|
body, err := ioutil.ReadAll(resp.Body) |
|
if err == nil { |
|
return parseXrds(body) |
|
} |
|
return "", "", err |
|
} |
|
|
|
// Search for |
|
// <head> |
|
// <meta http-equiv="X-XRDS-Location" content="...."> |
|
func findMetaXrdsLocation(input io.Reader) (location string, err error) { |
|
tokenizer := html.NewTokenizer(input) |
|
inHead := false |
|
for { |
|
tt := tokenizer.Next() |
|
switch tt { |
|
case html.ErrorToken: |
|
return "", tokenizer.Err() |
|
case html.StartTagToken, html.EndTagToken: |
|
tk := tokenizer.Token() |
|
if tk.Data == "head" { |
|
if tt == html.StartTagToken { |
|
inHead = true |
|
} else { |
|
return "", errors.New("Meta X-XRDS-Location not found") |
|
} |
|
} else if inHead && tk.Data == "meta" { |
|
ok := false |
|
content := "" |
|
for _, attr := range tk.Attr { |
|
if attr.Key == "http-equiv" && |
|
strings.ToLower(attr.Val) == "x-xrds-location" { |
|
ok = true |
|
} else if attr.Key == "content" { |
|
content = attr.Val |
|
} |
|
} |
|
if ok && len(content) > 0 { |
|
return content, nil |
|
} |
|
} |
|
} |
|
} |
|
return "", errors.New("Meta X-XRDS-Location not found") |
|
}
|
|
|