forked from gitea/gitea
120 lines
3.4 KiB
Go
120 lines
3.4 KiB
Go
|
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")
|
||
|
}
|