mirror of
https://github.com/documize/community.git
synced 2025-08-08 23:15:29 +02:00
Bump version to 5.11.0
This commit is contained in:
parent
a32510b8e6
commit
510e1bd0bd
370 changed files with 18825 additions and 5454 deletions
12
vendor/gopkg.in/cas.v2/README.md
generated
vendored
12
vendor/gopkg.in/cas.v2/README.md
generated
vendored
|
@ -3,10 +3,18 @@
|
|||
CAS provides a http package compatible client implementation for use with
|
||||
securing http frontends in golang.
|
||||
|
||||
import "gopkg.in/cas.v2"
|
||||
```
|
||||
import "gopkg.in/cas.v2"
|
||||
```
|
||||
|
||||
If you are using go modules, get the library by running:
|
||||
|
||||
```
|
||||
go get gopkg.in/cas.v2@v2.2.1
|
||||
```
|
||||
|
||||
## Examples and Documentation
|
||||
|
||||
Documentation is available at: http://godoc.org/gopkg.in/cas.v1
|
||||
Documentation is available at: https://pkg.go.dev/gopkg.in/cas.v2
|
||||
Examples are included in the documentation but are also available in the
|
||||
`_examples` directory.
|
||||
|
|
286
vendor/gopkg.in/cas.v2/client.go
generated
vendored
286
vendor/gopkg.in/cas.v2/client.go
generated
vendored
|
@ -3,32 +3,34 @@ package cas
|
|||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"sync"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// Client configuration options
|
||||
// Options : Client configuration options
|
||||
type Options struct {
|
||||
URL *url.URL // URL to the CAS service
|
||||
Store TicketStore // Custom TicketStore, if nil a MemoryStore will be used
|
||||
Client *http.Client // Custom http client to allow options for http connections
|
||||
SendService bool // Custom sendService to determine whether you need to send service param
|
||||
URL *url.URL // URL to the CAS service
|
||||
Store TicketStore // Custom TicketStore, if nil a MemoryStore will be used
|
||||
Client *http.Client // Custom http client to allow options for http connections
|
||||
SendService bool // Custom sendService to determine whether you need to send service param
|
||||
URLScheme URLScheme // Custom url scheme, can be used to modify the request urls for the client
|
||||
Cookie *http.Cookie // http.Cookie options, uses Path, Domain, MaxAge, HttpOnly, & Secure
|
||||
SessionStore SessionStore
|
||||
}
|
||||
|
||||
// Client implements the main protocol
|
||||
type Client struct {
|
||||
url *url.URL
|
||||
tickets TicketStore
|
||||
client *http.Client
|
||||
tickets TicketStore
|
||||
client *http.Client
|
||||
urlScheme URLScheme
|
||||
cookie *http.Cookie
|
||||
|
||||
mu sync.Mutex
|
||||
sessions map[string]string
|
||||
sessions SessionStore
|
||||
sendService bool
|
||||
|
||||
stValidator *ServiceTicketValidator
|
||||
}
|
||||
|
||||
// NewClient creates a Client with the provided Options.
|
||||
|
@ -44,6 +46,20 @@ func NewClient(options *Options) *Client {
|
|||
tickets = &MemoryStore{}
|
||||
}
|
||||
|
||||
var sessions SessionStore
|
||||
if options.SessionStore != nil {
|
||||
sessions = options.SessionStore
|
||||
} else {
|
||||
sessions = NewMemorySessionStore()
|
||||
}
|
||||
|
||||
var urlScheme URLScheme
|
||||
if options.URLScheme != nil {
|
||||
urlScheme = options.URLScheme
|
||||
} else {
|
||||
urlScheme = NewDefaultURLScheme(options.URL)
|
||||
}
|
||||
|
||||
var client *http.Client
|
||||
if options.Client != nil {
|
||||
client = options.Client
|
||||
|
@ -51,12 +67,25 @@ func NewClient(options *Options) *Client {
|
|||
client = &http.Client{}
|
||||
}
|
||||
|
||||
var cookie *http.Cookie
|
||||
if options.Cookie != nil {
|
||||
cookie = options.Cookie
|
||||
} else {
|
||||
cookie = &http.Cookie{
|
||||
MaxAge: 86400,
|
||||
HttpOnly: false,
|
||||
Secure: false,
|
||||
}
|
||||
}
|
||||
|
||||
return &Client{
|
||||
url: options.URL,
|
||||
tickets: tickets,
|
||||
client: client,
|
||||
sessions: make(map[string]string),
|
||||
urlScheme: urlScheme,
|
||||
cookie: cookie,
|
||||
sessions: sessions,
|
||||
sendService: options.SendService,
|
||||
stValidator: NewServiceTicketValidator(client, options.URL),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,8 +110,11 @@ func requestURL(r *http.Request) (*url.URL, error) {
|
|||
}
|
||||
|
||||
u.Host = r.Host
|
||||
u.Scheme = "http"
|
||||
if host := r.Header.Get("X-Forwarded-Host"); host != "" {
|
||||
u.Host = host
|
||||
}
|
||||
|
||||
u.Scheme = "http"
|
||||
if scheme := r.Header.Get("X-Forwarded-Proto"); scheme != "" {
|
||||
u.Scheme = scheme
|
||||
} else if r.TLS != nil {
|
||||
|
@ -94,7 +126,7 @@ func requestURL(r *http.Request) (*url.URL, error) {
|
|||
|
||||
// LoginUrlForRequest determines the CAS login URL for the http.Request.
|
||||
func (c *Client) LoginUrlForRequest(r *http.Request) (string, error) {
|
||||
u, err := c.url.Parse(path.Join(c.url.Path, "login"))
|
||||
u, err := c.urlScheme.Login()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -113,7 +145,7 @@ func (c *Client) LoginUrlForRequest(r *http.Request) (string, error) {
|
|||
|
||||
// LogoutUrlForRequest determines the CAS logout URL for the http.Request.
|
||||
func (c *Client) LogoutUrlForRequest(r *http.Request) (string, error) {
|
||||
u, err := c.url.Parse(path.Join(c.url.Path, "logout"))
|
||||
u, err := c.urlScheme.Logout()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -134,42 +166,20 @@ func (c *Client) LogoutUrlForRequest(r *http.Request) (string, error) {
|
|||
|
||||
// ServiceValidateUrlForRequest determines the CAS serviceValidate URL for the ticket and http.Request.
|
||||
func (c *Client) ServiceValidateUrlForRequest(ticket string, r *http.Request) (string, error) {
|
||||
u, err := c.url.Parse(path.Join(c.url.Path, "serviceValidate"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
service, err := requestURL(r)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
q := u.Query()
|
||||
q.Add("service", sanitisedURLString(service))
|
||||
q.Add("ticket", ticket)
|
||||
u.RawQuery = q.Encode()
|
||||
|
||||
return u.String(), nil
|
||||
return c.stValidator.ServiceValidateUrl(service, ticket)
|
||||
}
|
||||
|
||||
// ValidateUrlForRequest determines the CAS validate URL for the ticket and http.Request.
|
||||
func (c *Client) ValidateUrlForRequest(ticket string, r *http.Request) (string, error) {
|
||||
u, err := c.url.Parse(path.Join(c.url.Path, "validate"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
service, err := requestURL(r)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
q := u.Query()
|
||||
q.Add("service", sanitisedURLString(service))
|
||||
q.Add("ticket", ticket)
|
||||
u.RawQuery = q.Encode()
|
||||
|
||||
return u.String(), nil
|
||||
return c.stValidator.ValidateUrl(service, ticket)
|
||||
}
|
||||
|
||||
// RedirectToLogout replies to the request with a redirect URL to log out of CAS.
|
||||
|
@ -181,7 +191,7 @@ func (c *Client) RedirectToLogout(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
if glog.V(2) {
|
||||
glog.Info("Logging out, redirecting client to %v with status %v",
|
||||
glog.Infof("Logging out, redirecting client to %v with status %v",
|
||||
u, http.StatusFound)
|
||||
}
|
||||
|
||||
|
@ -189,7 +199,7 @@ func (c *Client) RedirectToLogout(w http.ResponseWriter, r *http.Request) {
|
|||
http.Redirect(w, r, u, http.StatusFound)
|
||||
}
|
||||
|
||||
// RedirectToLogout replies to the request with a redirect URL to authenticate with CAS.
|
||||
// RedirectToLogin replies to the request with a redirect URL to authenticate with CAS.
|
||||
func (c *Client) RedirectToLogin(w http.ResponseWriter, r *http.Request) {
|
||||
u, err := c.LoginUrlForRequest(r)
|
||||
if err != nil {
|
||||
|
@ -205,134 +215,17 @@ func (c *Client) RedirectToLogin(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
// validateTicket performs CAS ticket validation with the given ticket and service.
|
||||
//
|
||||
// If the request returns a 404 then validateTicketCas1 will be returned.
|
||||
func (c *Client) validateTicket(ticket string, service *http.Request) error {
|
||||
if glog.V(2) {
|
||||
serviceUrl, _ := requestURL(service)
|
||||
glog.Infof("Validating ticket %v for service %v", ticket, serviceUrl)
|
||||
}
|
||||
|
||||
u, err := c.ServiceValidateUrlForRequest(ticket, service)
|
||||
serviceURL, err := requestURL(service)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r, err := http.NewRequest("GET", u, nil)
|
||||
success, err := c.stValidator.ValidateTicket(serviceURL, ticket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.Header.Add("User-Agent", "Golang CAS client gopkg.in/cas")
|
||||
|
||||
if glog.V(2) {
|
||||
glog.Infof("Attempting ticket validation with %v", r.URL)
|
||||
}
|
||||
|
||||
resp, err := c.client.Do(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if glog.V(2) {
|
||||
glog.Infof("Request %v %v returned %v",
|
||||
r.Method, r.URL,
|
||||
resp.Status)
|
||||
}
|
||||
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
return c.validateTicketCas1(ticket, service)
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("cas: validate ticket: %v", string(body))
|
||||
}
|
||||
|
||||
if glog.V(2) {
|
||||
glog.Infof("Received authentication response\n%v", string(body))
|
||||
}
|
||||
|
||||
success, err := ParseServiceResponse(body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if glog.V(2) {
|
||||
glog.Infof("Parsed ServiceResponse: %#v", success)
|
||||
}
|
||||
|
||||
if err := c.tickets.Write(ticket, success); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateTicketCas1 performs CAS protocol 1 ticket validation.
|
||||
func (c *Client) validateTicketCas1(ticket string, service *http.Request) error {
|
||||
u, err := c.ValidateUrlForRequest(ticket, service)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r, err := http.NewRequest("GET", u, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.Header.Add("User-Agent", "Golang CAS client gopkg.in/cas")
|
||||
|
||||
if glog.V(2) {
|
||||
glog.Info("Attempting ticket validation with %v", r.URL)
|
||||
}
|
||||
|
||||
resp, err := c.client.Do(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if glog.V(2) {
|
||||
glog.Info("Request %v %v returned %v",
|
||||
r.Method, r.URL,
|
||||
resp.Status)
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
body := string(data)
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("cas: validate ticket: %v", body)
|
||||
}
|
||||
|
||||
if glog.V(2) {
|
||||
glog.Infof("Received authentication response\n%v", body)
|
||||
}
|
||||
|
||||
if body == "no\n\n" {
|
||||
return nil // not logged in
|
||||
}
|
||||
|
||||
success := &AuthenticationResponse{
|
||||
User: body[4 : len(body)-1],
|
||||
}
|
||||
|
||||
if glog.V(2) {
|
||||
glog.Infof("Parsed ServiceResponse: %#v", success)
|
||||
}
|
||||
|
||||
if err := c.tickets.Write(ticket, success); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -345,9 +238,9 @@ func (c *Client) validateTicketCas1(ticket string, service *http.Request) error
|
|||
// A cookie is set on the response if one is not provided with the request.
|
||||
// Validates the ticket if the URL parameter is provided.
|
||||
func (c *Client) getSession(w http.ResponseWriter, r *http.Request) {
|
||||
cookie := getCookie(w, r)
|
||||
cookie := c.getCookie(w, r)
|
||||
|
||||
if s, ok := c.sessions[cookie.Value]; ok {
|
||||
if s, ok := c.sessions.Get(cookie.Value); ok {
|
||||
if t, err := c.tickets.Read(s); err == nil {
|
||||
if glog.V(1) {
|
||||
glog.Infof("Re-used ticket %s for %s", s, t.User)
|
||||
|
@ -400,31 +293,34 @@ func (c *Client) getSession(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
// getCookie finds or creates the session cookie on the response.
|
||||
func getCookie(w http.ResponseWriter, r *http.Request) *http.Cookie {
|
||||
c, err := r.Cookie(sessionCookieName)
|
||||
func (c *Client) getCookie(w http.ResponseWriter, r *http.Request) *http.Cookie {
|
||||
cookie, err := r.Cookie(sessionCookieName)
|
||||
if err != nil {
|
||||
// NOTE: Intentionally not enabling HttpOnly so the cookie can
|
||||
// still be used by Ajax requests.
|
||||
c = &http.Cookie{
|
||||
cookie = &http.Cookie{
|
||||
Name: sessionCookieName,
|
||||
Value: newSessionId(),
|
||||
MaxAge: 86400,
|
||||
HttpOnly: false,
|
||||
Value: newSessionID(),
|
||||
Path: c.cookie.Path,
|
||||
Domain: c.cookie.Domain,
|
||||
MaxAge: c.cookie.MaxAge,
|
||||
HttpOnly: c.cookie.HttpOnly,
|
||||
Secure: c.cookie.Secure,
|
||||
}
|
||||
|
||||
if glog.V(2) {
|
||||
glog.Infof("Setting %v cookie with value: %v", c.Name, c.Value)
|
||||
glog.Infof("Setting %v cookie with value: %v", cookie.Name, cookie.Value)
|
||||
}
|
||||
|
||||
r.AddCookie(c) // so we can find it later if required
|
||||
http.SetCookie(w, c)
|
||||
r.AddCookie(cookie) // so we can find it later if required
|
||||
http.SetCookie(w, cookie)
|
||||
}
|
||||
|
||||
return c
|
||||
return cookie
|
||||
}
|
||||
|
||||
// newSessionId generates a new opaque session identifier for use in the cookie.
|
||||
func newSessionId() string {
|
||||
func newSessionID() string {
|
||||
const alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
|
||||
// generate 64 character string
|
||||
|
@ -450,24 +346,22 @@ func (c *Client) setSession(id string, ticket string) {
|
|||
glog.Infof("Recording session, %v -> %v", id, ticket)
|
||||
}
|
||||
|
||||
c.mu.Lock()
|
||||
c.sessions[id] = ticket
|
||||
c.mu.Unlock()
|
||||
c.sessions.Set(id, ticket)
|
||||
}
|
||||
|
||||
// clearSession removes the session from the client and clears the cookie.
|
||||
func (c *Client) clearSession(w http.ResponseWriter, r *http.Request) {
|
||||
cookie := getCookie(w, r)
|
||||
cookie := c.getCookie(w, r)
|
||||
|
||||
if s, ok := c.sessions[cookie.Value]; ok {
|
||||
if err := c.tickets.Delete(s); err != nil {
|
||||
if serviceTicket, ok := c.sessions.Get(cookie.Value); ok {
|
||||
if err := c.tickets.Delete(serviceTicket); err != nil {
|
||||
fmt.Printf("Failed to remove %v from %T: %v\n", cookie.Value, c.tickets, err)
|
||||
if glog.V(2) {
|
||||
glog.Errorf("Failed to remove %v from %T: %v", cookie.Value, c.tickets, err)
|
||||
}
|
||||
}
|
||||
|
||||
c.deleteSession(s)
|
||||
c.deleteSession(cookie.Value)
|
||||
}
|
||||
|
||||
clearCookie(w, cookie)
|
||||
|
@ -475,29 +369,5 @@ func (c *Client) clearSession(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// deleteSession removes the session from the client
|
||||
func (c *Client) deleteSession(id string) {
|
||||
c.mu.Lock()
|
||||
delete(c.sessions, id)
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
// findAndDeleteSessionWithTicket removes the session from the client via Single Log Out
|
||||
//
|
||||
// When a Single Log Out request is received we receive the service ticket identidier. This
|
||||
// function loops through the sessions to find the matching session id. Once retrieved the
|
||||
// session is removed from the client. When the session is next requested the getSession
|
||||
// function will notice the session is invalid and revalidate the user.
|
||||
func (c *Client) findAndDeleteSessionWithTicket(ticket string) {
|
||||
var id string
|
||||
for s, t := range c.sessions {
|
||||
if t == ticket {
|
||||
id = s
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if id == "" {
|
||||
return
|
||||
}
|
||||
|
||||
c.deleteSession(id)
|
||||
c.sessions.Delete(id)
|
||||
}
|
||||
|
|
8
vendor/gopkg.in/cas.v2/http_helpers.go
generated
vendored
8
vendor/gopkg.in/cas.v2/http_helpers.go
generated
vendored
|
@ -24,9 +24,9 @@ func setClient(r *http.Request, c *Client) {
|
|||
func getClient(r *http.Request) *Client {
|
||||
if c := r.Context().Value(clientKey); c != nil {
|
||||
return c.(*Client)
|
||||
} else {
|
||||
return nil // explicitly pass along the nil to caller -- conforms to previous impl
|
||||
}
|
||||
|
||||
return nil // explicitly pass along the nil to caller -- conforms to previous impl
|
||||
}
|
||||
|
||||
// RedirectToLogin allows CAS protected handlers to redirect a request
|
||||
|
@ -68,9 +68,9 @@ func setAuthenticationResponse(r *http.Request, a *AuthenticationResponse) {
|
|||
func getAuthenticationResponse(r *http.Request) *AuthenticationResponse {
|
||||
if a := r.Context().Value(authenticationResponseKey); a != nil {
|
||||
return a.(*AuthenticationResponse)
|
||||
} else {
|
||||
return nil // explicitly pass along the nil to caller -- conforms to previous impl
|
||||
}
|
||||
|
||||
return nil // explicitly pass along the nil to caller -- conforms to previous impl
|
||||
}
|
||||
|
||||
// IsAuthenticated indicates whether the request has been authenticated with CAS.
|
||||
|
|
18
vendor/gopkg.in/cas.v2/logout_request.go
generated
vendored
18
vendor/gopkg.in/cas.v2/logout_request.go
generated
vendored
|
@ -24,7 +24,7 @@ func parseLogoutRequest(data []byte) (*logoutRequest, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
t, err := time.Parse(time.RFC1123Z, l.RawIssueInstant)
|
||||
t, err := parseDate(l.RawIssueInstant)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -36,7 +36,19 @@ func parseLogoutRequest(data []byte) (*logoutRequest, error) {
|
|||
return l, nil
|
||||
}
|
||||
|
||||
func newLogoutRequestId() string {
|
||||
func parseDate(raw string) (time.Time, error) {
|
||||
t, err := time.Parse(time.RFC1123Z, raw)
|
||||
if err != nil {
|
||||
// if RFC1123Z does not match, we will try iso8601
|
||||
t, err = time.Parse("2006-01-02T15:04:05Z0700", raw)
|
||||
if err != nil {
|
||||
return t, err
|
||||
}
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func newLogoutRequestID() string {
|
||||
const alphabet = "abcdef0123456789"
|
||||
|
||||
// generate 64 character string
|
||||
|
@ -54,7 +66,7 @@ func xmlLogoutRequest(ticket string) ([]byte, error) {
|
|||
l := &logoutRequest{
|
||||
Version: "2.0",
|
||||
IssueInstant: time.Now().UTC(),
|
||||
ID: newLogoutRequestId(),
|
||||
ID: newLogoutRequestID(),
|
||||
NameID: "@NOT_USED@",
|
||||
SessionIndex: ticket,
|
||||
}
|
||||
|
|
2
vendor/gopkg.in/cas.v2/middleware.go
generated
vendored
2
vendor/gopkg.in/cas.v2/middleware.go
generated
vendored
|
@ -6,6 +6,8 @@ import (
|
|||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// Handler returns a standard http.HandlerFunc, which will check the authenticated status (redirect user go login if needed)
|
||||
// If the user pass the authenticated check, it will call the h's ServeHTTP method
|
||||
func (c *Client) Handler(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if glog.V(2) {
|
||||
|
|
178
vendor/gopkg.in/cas.v2/rest_client.go
generated
vendored
Normal file
178
vendor/gopkg.in/cas.v2/rest_client.go
generated
vendored
Normal file
|
@ -0,0 +1,178 @@
|
|||
package cas
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// https://apereo.github.io/cas/4.2.x/protocol/REST-Protocol.html
|
||||
|
||||
// TicketGrantingTicket represents a SSO session for a user, also known as TGT
|
||||
type TicketGrantingTicket string
|
||||
|
||||
// ServiceTicket stands for the access granted by the CAS server to an application for a specific user, also known as ST
|
||||
type ServiceTicket string
|
||||
|
||||
// RestOptions provide options for the RestClient
|
||||
type RestOptions struct {
|
||||
CasURL *url.URL
|
||||
ServiceURL *url.URL
|
||||
Client *http.Client
|
||||
URLScheme URLScheme
|
||||
}
|
||||
|
||||
// RestClient uses the rest protocol provided by cas
|
||||
type RestClient struct {
|
||||
urlScheme URLScheme
|
||||
serviceURL *url.URL
|
||||
client *http.Client
|
||||
stValidator *ServiceTicketValidator
|
||||
}
|
||||
|
||||
// NewRestClient creates a new client for the cas rest protocol with the provided options
|
||||
func NewRestClient(options *RestOptions) *RestClient {
|
||||
if glog.V(2) {
|
||||
glog.Infof("cas: new rest client with options %v", options)
|
||||
}
|
||||
|
||||
var client *http.Client
|
||||
if options.Client != nil {
|
||||
client = options.Client
|
||||
} else {
|
||||
client = &http.Client{}
|
||||
}
|
||||
|
||||
var urlScheme URLScheme
|
||||
if options.URLScheme != nil {
|
||||
urlScheme = options.URLScheme
|
||||
} else {
|
||||
urlScheme = NewDefaultURLScheme(options.CasURL)
|
||||
}
|
||||
|
||||
return &RestClient{
|
||||
urlScheme: urlScheme,
|
||||
serviceURL: options.ServiceURL,
|
||||
client: client,
|
||||
stValidator: NewServiceTicketValidator(client, options.CasURL),
|
||||
}
|
||||
}
|
||||
|
||||
// Handle wraps a http.Handler to provide CAS Rest authentication for the handler.
|
||||
func (c *RestClient) Handle(h http.Handler) http.Handler {
|
||||
return &restClientHandler{
|
||||
c: c,
|
||||
h: h,
|
||||
}
|
||||
}
|
||||
|
||||
// HandleFunc wraps a function to provide CAS Rest authentication for the handler function.
|
||||
func (c *RestClient) HandleFunc(h func(http.ResponseWriter, *http.Request)) http.Handler {
|
||||
return c.Handle(http.HandlerFunc(h))
|
||||
}
|
||||
|
||||
// RequestGrantingTicket returns a new TGT, if the username and password authentication was successful
|
||||
func (c *RestClient) RequestGrantingTicket(username string, password string) (TicketGrantingTicket, error) {
|
||||
// request:
|
||||
// POST /cas/v1/tickets HTTP/1.0
|
||||
// username=battags&password=password&additionalParam1=paramvalue
|
||||
|
||||
endpoint, err := c.urlScheme.RestGrantingTicket()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
values := url.Values{}
|
||||
values.Set("username", username)
|
||||
values.Set("password", password)
|
||||
|
||||
resp, err := c.client.PostForm(endpoint.String(), values)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// response:
|
||||
// 201 Created
|
||||
// Location: http://www.whatever.com/cas/v1/tickets/{TGT id}
|
||||
|
||||
if resp.StatusCode != 201 {
|
||||
return "", fmt.Errorf("ticket endoint returned status code %v", resp.StatusCode)
|
||||
}
|
||||
|
||||
tgt := path.Base(resp.Header.Get("Location"))
|
||||
if tgt == "" {
|
||||
return "", fmt.Errorf("does not return a valid location header")
|
||||
}
|
||||
|
||||
return TicketGrantingTicket(tgt), nil
|
||||
}
|
||||
|
||||
// RequestServiceTicket requests a service ticket with the TGT for the configured service url
|
||||
func (c *RestClient) RequestServiceTicket(tgt TicketGrantingTicket) (ServiceTicket, error) {
|
||||
// request:
|
||||
// POST /cas/v1/tickets/{TGT id} HTTP/1.0
|
||||
// service={form encoded parameter for the service url}
|
||||
endpoint, err := c.urlScheme.RestServiceTicket(string(tgt))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
values := url.Values{}
|
||||
values.Set("service", c.serviceURL.String())
|
||||
|
||||
resp, err := c.client.PostForm(endpoint.String(), values)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// response:
|
||||
// 200 OK
|
||||
// ST-1-FFDFHDSJKHSDFJKSDHFJKRUEYREWUIFSD2132
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return "", fmt.Errorf("service ticket endoint returned status code %v", resp.StatusCode)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return ServiceTicket(data), nil
|
||||
}
|
||||
|
||||
// ValidateServiceTicket validates the service ticket and returns an AuthenticationResponse
|
||||
func (c *RestClient) ValidateServiceTicket(st ServiceTicket) (*AuthenticationResponse, error) {
|
||||
return c.stValidator.ValidateTicket(c.serviceURL, string(st))
|
||||
}
|
||||
|
||||
// Logout destroys the given granting ticket
|
||||
func (c *RestClient) Logout(tgt TicketGrantingTicket) error {
|
||||
// DELETE /cas/v1/tickets/TGT-fdsjfsdfjkalfewrihfdhfaie HTTP/1.0
|
||||
endpoint, err := c.urlScheme.RestLogout(string(tgt))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("DELETE", endpoint.String(), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 && resp.StatusCode != 204 {
|
||||
return fmt.Errorf("could not destroy granting ticket %v, server returned %v", tgt, resp.StatusCode)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
59
vendor/gopkg.in/cas.v2/rest_handler.go
generated
vendored
Normal file
59
vendor/gopkg.in/cas.v2/rest_handler.go
generated
vendored
Normal file
|
@ -0,0 +1,59 @@
|
|||
package cas
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// restClientHandler handles CAS REST Protocol over HTTP Basic Authentication
|
||||
type restClientHandler struct {
|
||||
c *RestClient
|
||||
h http.Handler
|
||||
}
|
||||
|
||||
// ServeHTTP handles HTTP requests, processes HTTP Basic Authentication over CAS Rest api
|
||||
// and passes requests up to its child http.Handler.
|
||||
func (ch *restClientHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if glog.V(2) {
|
||||
glog.Infof("cas: handling %v request for %v", r.Method, r.URL)
|
||||
}
|
||||
|
||||
username, password, ok := r.BasicAuth()
|
||||
if !ok {
|
||||
w.Header().Set("WWW-Authenticate", "Basic realm=\"CAS Protected Area\"")
|
||||
w.WriteHeader(401)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO we should implement a short cache to avoid hitting cas server on every request
|
||||
// the cache could use the authorization header as key and the authenticationResponse as value
|
||||
|
||||
success, err := ch.authenticate(username, password)
|
||||
if err != nil {
|
||||
if glog.V(1) {
|
||||
glog.Infof("cas: rest authentication failed %v", err)
|
||||
}
|
||||
w.Header().Set("WWW-Authenticate", "Basic realm=\"CAS Protected Area\"")
|
||||
w.WriteHeader(401)
|
||||
return
|
||||
}
|
||||
|
||||
setAuthenticationResponse(r, success)
|
||||
ch.h.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
func (ch *restClientHandler) authenticate(username string, password string) (*AuthenticationResponse, error) {
|
||||
tgt, err := ch.c.RequestGrantingTicket(username, password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
st, err := ch.c.RequestServiceTicket(tgt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ch.c.ValidateServiceTicket(st)
|
||||
}
|
183
vendor/gopkg.in/cas.v2/service_validate.go
generated
vendored
Normal file
183
vendor/gopkg.in/cas.v2/service_validate.go
generated
vendored
Normal file
|
@ -0,0 +1,183 @@
|
|||
package cas
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// NewServiceTicketValidator create a new *ServiceTicketValidator
|
||||
func NewServiceTicketValidator(client *http.Client, casURL *url.URL) *ServiceTicketValidator {
|
||||
return &ServiceTicketValidator{
|
||||
client: client,
|
||||
casURL: casURL,
|
||||
}
|
||||
}
|
||||
|
||||
// ServiceTicketValidator is responsible for the validation of a service ticket
|
||||
type ServiceTicketValidator struct {
|
||||
client *http.Client
|
||||
casURL *url.URL
|
||||
}
|
||||
|
||||
// ValidateTicket validates the service ticket for the given server. The method will try to use the service validate
|
||||
// endpoint of the cas >= 2 protocol, if the service validate endpoint not available, the function will use the cas 1
|
||||
// validate endpoint.
|
||||
func (validator *ServiceTicketValidator) ValidateTicket(serviceURL *url.URL, ticket string) (*AuthenticationResponse, error) {
|
||||
if glog.V(2) {
|
||||
glog.Infof("Validating ticket %v for service %v", ticket, serviceURL)
|
||||
}
|
||||
|
||||
u, err := validator.ServiceValidateUrl(serviceURL, ticket)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r, err := http.NewRequest("GET", u, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r.Header.Add("User-Agent", "Golang CAS client gopkg.in/cas")
|
||||
|
||||
if glog.V(2) {
|
||||
glog.Infof("Attempting ticket validation with %v", r.URL)
|
||||
}
|
||||
|
||||
resp, err := validator.client.Do(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if glog.V(2) {
|
||||
glog.Infof("Request %v %v returned %v",
|
||||
r.Method, r.URL,
|
||||
resp.Status)
|
||||
}
|
||||
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
return validator.validateTicketCas1(serviceURL, ticket)
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("cas: validate ticket: %v", string(body))
|
||||
}
|
||||
|
||||
if glog.V(2) {
|
||||
glog.Infof("Received authentication response\n%v", string(body))
|
||||
}
|
||||
|
||||
success, err := ParseServiceResponse(body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if glog.V(2) {
|
||||
glog.Infof("Parsed ServiceResponse: %#v", success)
|
||||
}
|
||||
|
||||
return success, nil
|
||||
}
|
||||
|
||||
// ServiceValidateUrl creates the service validation url for the cas >= 2 protocol.
|
||||
// TODO the function is only exposed, because of the clients ServiceValidateUrl function
|
||||
func (validator *ServiceTicketValidator) ServiceValidateUrl(serviceURL *url.URL, ticket string) (string, error) {
|
||||
u, err := validator.casURL.Parse(path.Join(validator.casURL.Path, "serviceValidate"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
q := u.Query()
|
||||
q.Add("service", sanitisedURLString(serviceURL))
|
||||
q.Add("ticket", ticket)
|
||||
u.RawQuery = q.Encode()
|
||||
|
||||
return u.String(), nil
|
||||
}
|
||||
|
||||
func (validator *ServiceTicketValidator) validateTicketCas1(serviceURL *url.URL, ticket string) (*AuthenticationResponse, error) {
|
||||
u, err := validator.ValidateUrl(serviceURL, ticket)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r, err := http.NewRequest("GET", u, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r.Header.Add("User-Agent", "Golang CAS client gopkg.in/cas")
|
||||
|
||||
if glog.V(2) {
|
||||
glog.Infof("Attempting ticket validation with %v", r.URL)
|
||||
}
|
||||
|
||||
resp, err := validator.client.Do(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if glog.V(2) {
|
||||
glog.Infof("Request %v %v returned %v",
|
||||
r.Method, r.URL,
|
||||
resp.Status)
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body := string(data)
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("cas: validate ticket: %v", body)
|
||||
}
|
||||
|
||||
if glog.V(2) {
|
||||
glog.Infof("Received authentication response\n%v", body)
|
||||
}
|
||||
|
||||
if body == "no\n\n" {
|
||||
return nil, nil // not logged in
|
||||
}
|
||||
|
||||
success := &AuthenticationResponse{
|
||||
User: body[4 : len(body)-1],
|
||||
}
|
||||
|
||||
if glog.V(2) {
|
||||
glog.Infof("Parsed ServiceResponse: %#v", success)
|
||||
}
|
||||
|
||||
return success, nil
|
||||
}
|
||||
|
||||
// ValidateUrl creates the validation url for the cas >= 1 protocol.
|
||||
// TODO the function is only exposed, because of the clients ValidateUrl function
|
||||
func (validator *ServiceTicketValidator) ValidateUrl(serviceURL *url.URL, ticket string) (string, error) {
|
||||
u, err := validator.casURL.Parse(path.Join(validator.casURL.Path, "validate"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
q := u.Query()
|
||||
q.Add("service", sanitisedURLString(serviceURL))
|
||||
q.Add("ticket", ticket)
|
||||
u.RawQuery = q.Encode()
|
||||
|
||||
return u.String(), nil
|
||||
}
|
52
vendor/gopkg.in/cas.v2/session_store.go
generated
vendored
Normal file
52
vendor/gopkg.in/cas.v2/session_store.go
generated
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
package cas
|
||||
|
||||
import "sync"
|
||||
|
||||
// SessionStore store the session's ticket
|
||||
// SessionID is retrived from cookies
|
||||
type SessionStore interface {
|
||||
// Get the ticket with the session id
|
||||
Get(sessionID string) (string, bool)
|
||||
|
||||
// Set the session with a ticket
|
||||
Set(sessionID, ticket string) error
|
||||
|
||||
// Delete the session
|
||||
Delete(sessionID string) error
|
||||
}
|
||||
|
||||
// NewMemorySessionStore create a default SessionStore that uses memory
|
||||
func NewMemorySessionStore() SessionStore {
|
||||
return &memorySessionStore{
|
||||
sessions: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
type memorySessionStore struct {
|
||||
mu sync.RWMutex
|
||||
sessions map[string]string
|
||||
}
|
||||
|
||||
func (m *memorySessionStore) Get(sessionID string) (string, bool) {
|
||||
m.mu.RLock()
|
||||
ticket, ok := m.sessions[sessionID]
|
||||
m.mu.RUnlock()
|
||||
|
||||
return ticket, ok
|
||||
}
|
||||
|
||||
func (m *memorySessionStore) Set(sessionID, ticket string) error {
|
||||
m.mu.Lock()
|
||||
m.sessions[sessionID] = ticket
|
||||
m.mu.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *memorySessionStore) Delete(sessionID string) error {
|
||||
m.mu.Lock()
|
||||
delete(m.sessions, sessionID)
|
||||
m.mu.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
79
vendor/gopkg.in/cas.v2/url_scheme.go
generated
vendored
Normal file
79
vendor/gopkg.in/cas.v2/url_scheme.go
generated
vendored
Normal file
|
@ -0,0 +1,79 @@
|
|||
package cas
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"path"
|
||||
)
|
||||
|
||||
// URLScheme creates the url which are required to handle the cas protocol.
|
||||
type URLScheme interface {
|
||||
Login() (*url.URL, error)
|
||||
Logout() (*url.URL, error)
|
||||
Validate() (*url.URL, error)
|
||||
ServiceValidate() (*url.URL, error)
|
||||
RestGrantingTicket() (*url.URL, error)
|
||||
RestServiceTicket(tgt string) (*url.URL, error)
|
||||
RestLogout(tgt string) (*url.URL, error)
|
||||
}
|
||||
|
||||
// NewDefaultURLScheme creates a URLScheme which uses the cas default urls
|
||||
func NewDefaultURLScheme(base *url.URL) *DefaultURLScheme {
|
||||
return &DefaultURLScheme{
|
||||
base: base,
|
||||
LoginPath: "login",
|
||||
LogoutPath: "logout",
|
||||
ValidatePath: "validate",
|
||||
ServiceValidatePath: "serviceValidate",
|
||||
RestEndpoint: path.Join("v1", "tickets"),
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultURLScheme is a configurable URLScheme. Use NewDefaultURLScheme to create DefaultURLScheme with the default cas
|
||||
// urls.
|
||||
type DefaultURLScheme struct {
|
||||
base *url.URL
|
||||
LoginPath string
|
||||
LogoutPath string
|
||||
ValidatePath string
|
||||
ServiceValidatePath string
|
||||
RestEndpoint string
|
||||
}
|
||||
|
||||
// Login returns the url for the cas login page
|
||||
func (scheme *DefaultURLScheme) Login() (*url.URL, error) {
|
||||
return scheme.createURL(scheme.LoginPath)
|
||||
}
|
||||
|
||||
// Logout returns the url for the cas logut page
|
||||
func (scheme *DefaultURLScheme) Logout() (*url.URL, error) {
|
||||
return scheme.createURL(scheme.LogoutPath)
|
||||
}
|
||||
|
||||
// Validate returns the url for the request validation endpoint
|
||||
func (scheme *DefaultURLScheme) Validate() (*url.URL, error) {
|
||||
return scheme.createURL(scheme.ValidatePath)
|
||||
}
|
||||
|
||||
// ServiceValidate returns the url for the service validation endpoint
|
||||
func (scheme *DefaultURLScheme) ServiceValidate() (*url.URL, error) {
|
||||
return scheme.createURL(scheme.ServiceValidatePath)
|
||||
}
|
||||
|
||||
// RestGrantingTicket returns the url for requesting an granting ticket via rest api
|
||||
func (scheme *DefaultURLScheme) RestGrantingTicket() (*url.URL, error) {
|
||||
return scheme.createURL(scheme.RestEndpoint)
|
||||
}
|
||||
|
||||
// RestServiceTicket returns the url for requesting an service ticket via rest api
|
||||
func (scheme *DefaultURLScheme) RestServiceTicket(tgt string) (*url.URL, error) {
|
||||
return scheme.createURL(path.Join(scheme.RestEndpoint, tgt))
|
||||
}
|
||||
|
||||
// RestLogout returns the url for destroying an granting ticket via rest api
|
||||
func (scheme *DefaultURLScheme) RestLogout(tgt string) (*url.URL, error) {
|
||||
return scheme.createURL(path.Join(scheme.RestEndpoint, tgt))
|
||||
}
|
||||
|
||||
func (scheme *DefaultURLScheme) createURL(urlPath string) (*url.URL, error) {
|
||||
return scheme.base.Parse(path.Join(scheme.base.Path, urlPath))
|
||||
}
|
19
vendor/gopkg.in/yaml.v2/.travis.yml
generated
vendored
19
vendor/gopkg.in/yaml.v2/.travis.yml
generated
vendored
|
@ -1,12 +1,17 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.4
|
||||
- 1.5
|
||||
- 1.6
|
||||
- 1.7
|
||||
- 1.8
|
||||
- 1.9
|
||||
- tip
|
||||
- "1.4.x"
|
||||
- "1.5.x"
|
||||
- "1.6.x"
|
||||
- "1.7.x"
|
||||
- "1.8.x"
|
||||
- "1.9.x"
|
||||
- "1.10.x"
|
||||
- "1.11.x"
|
||||
- "1.12.x"
|
||||
- "1.13.x"
|
||||
- "1.14.x"
|
||||
- "tip"
|
||||
|
||||
go_import_path: gopkg.in/yaml.v2
|
||||
|
|
5
vendor/gopkg.in/yaml.v2/apic.go
generated
vendored
5
vendor/gopkg.in/yaml.v2/apic.go
generated
vendored
|
@ -79,6 +79,8 @@ func yaml_parser_set_encoding(parser *yaml_parser_t, encoding yaml_encoding_t) {
|
|||
parser.encoding = encoding
|
||||
}
|
||||
|
||||
var disableLineWrapping = false
|
||||
|
||||
// Create a new emitter object.
|
||||
func yaml_emitter_initialize(emitter *yaml_emitter_t) {
|
||||
*emitter = yaml_emitter_t{
|
||||
|
@ -87,6 +89,9 @@ func yaml_emitter_initialize(emitter *yaml_emitter_t) {
|
|||
states: make([]yaml_emitter_state_t, 0, initial_stack_size),
|
||||
events: make([]yaml_event_t, 0, initial_queue_size),
|
||||
}
|
||||
if disableLineWrapping {
|
||||
emitter.best_width = -1
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy an emitter object.
|
||||
|
|
48
vendor/gopkg.in/yaml.v2/decode.go
generated
vendored
48
vendor/gopkg.in/yaml.v2/decode.go
generated
vendored
|
@ -229,6 +229,10 @@ type decoder struct {
|
|||
mapType reflect.Type
|
||||
terrors []string
|
||||
strict bool
|
||||
|
||||
decodeCount int
|
||||
aliasCount int
|
||||
aliasDepth int
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -314,7 +318,43 @@ func (d *decoder) prepare(n *node, out reflect.Value) (newout reflect.Value, unm
|
|||
return out, false, false
|
||||
}
|
||||
|
||||
const (
|
||||
// 400,000 decode operations is ~500kb of dense object declarations, or
|
||||
// ~5kb of dense object declarations with 10000% alias expansion
|
||||
alias_ratio_range_low = 400000
|
||||
|
||||
// 4,000,000 decode operations is ~5MB of dense object declarations, or
|
||||
// ~4.5MB of dense object declarations with 10% alias expansion
|
||||
alias_ratio_range_high = 4000000
|
||||
|
||||
// alias_ratio_range is the range over which we scale allowed alias ratios
|
||||
alias_ratio_range = float64(alias_ratio_range_high - alias_ratio_range_low)
|
||||
)
|
||||
|
||||
func allowedAliasRatio(decodeCount int) float64 {
|
||||
switch {
|
||||
case decodeCount <= alias_ratio_range_low:
|
||||
// allow 99% to come from alias expansion for small-to-medium documents
|
||||
return 0.99
|
||||
case decodeCount >= alias_ratio_range_high:
|
||||
// allow 10% to come from alias expansion for very large documents
|
||||
return 0.10
|
||||
default:
|
||||
// scale smoothly from 99% down to 10% over the range.
|
||||
// this maps to 396,000 - 400,000 allowed alias-driven decodes over the range.
|
||||
// 400,000 decode operations is ~100MB of allocations in worst-case scenarios (single-item maps).
|
||||
return 0.99 - 0.89*(float64(decodeCount-alias_ratio_range_low)/alias_ratio_range)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) {
|
||||
d.decodeCount++
|
||||
if d.aliasDepth > 0 {
|
||||
d.aliasCount++
|
||||
}
|
||||
if d.aliasCount > 100 && d.decodeCount > 1000 && float64(d.aliasCount)/float64(d.decodeCount) > allowedAliasRatio(d.decodeCount) {
|
||||
failf("document contains excessive aliasing")
|
||||
}
|
||||
switch n.kind {
|
||||
case documentNode:
|
||||
return d.document(n, out)
|
||||
|
@ -353,7 +393,9 @@ func (d *decoder) alias(n *node, out reflect.Value) (good bool) {
|
|||
failf("anchor '%s' value contains itself", n.value)
|
||||
}
|
||||
d.aliases[n] = true
|
||||
d.aliasDepth++
|
||||
good = d.unmarshal(n.alias, out)
|
||||
d.aliasDepth--
|
||||
delete(d.aliases, n)
|
||||
return good
|
||||
}
|
||||
|
@ -746,8 +788,7 @@ func (d *decoder) merge(n *node, out reflect.Value) {
|
|||
case mappingNode:
|
||||
d.unmarshal(n, out)
|
||||
case aliasNode:
|
||||
an, ok := d.doc.anchors[n.value]
|
||||
if ok && an.kind != mappingNode {
|
||||
if n.alias != nil && n.alias.kind != mappingNode {
|
||||
failWantMap()
|
||||
}
|
||||
d.unmarshal(n, out)
|
||||
|
@ -756,8 +797,7 @@ func (d *decoder) merge(n *node, out reflect.Value) {
|
|||
for i := len(n.children) - 1; i >= 0; i-- {
|
||||
ni := n.children[i]
|
||||
if ni.kind == aliasNode {
|
||||
an, ok := d.doc.anchors[ni.value]
|
||||
if ok && an.kind != mappingNode {
|
||||
if ni.alias != nil && ni.alias.kind != mappingNode {
|
||||
failWantMap()
|
||||
}
|
||||
} else if ni.kind != mappingNode {
|
||||
|
|
2
vendor/gopkg.in/yaml.v2/resolve.go
generated
vendored
2
vendor/gopkg.in/yaml.v2/resolve.go
generated
vendored
|
@ -81,7 +81,7 @@ func resolvableTag(tag string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
var yamlStyleFloat = regexp.MustCompile(`^[-+]?[0-9]*\.?[0-9]+([eE][-+][0-9]+)?$`)
|
||||
var yamlStyleFloat = regexp.MustCompile(`^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$`)
|
||||
|
||||
func resolve(tag string, in string) (rtag string, out interface{}) {
|
||||
if !resolvableTag(tag) {
|
||||
|
|
125
vendor/gopkg.in/yaml.v2/scannerc.go
generated
vendored
125
vendor/gopkg.in/yaml.v2/scannerc.go
generated
vendored
|
@ -626,30 +626,17 @@ func trace(args ...interface{}) func() {
|
|||
func yaml_parser_fetch_more_tokens(parser *yaml_parser_t) bool {
|
||||
// While we need more tokens to fetch, do it.
|
||||
for {
|
||||
// Check if we really need to fetch more tokens.
|
||||
need_more_tokens := false
|
||||
|
||||
if parser.tokens_head == len(parser.tokens) {
|
||||
// Queue is empty.
|
||||
need_more_tokens = true
|
||||
} else {
|
||||
// Check if any potential simple key may occupy the head position.
|
||||
if !yaml_parser_stale_simple_keys(parser) {
|
||||
if parser.tokens_head != len(parser.tokens) {
|
||||
// If queue is non-empty, check if any potential simple key may
|
||||
// occupy the head position.
|
||||
head_tok_idx, ok := parser.simple_keys_by_tok[parser.tokens_parsed]
|
||||
if !ok {
|
||||
break
|
||||
} else if valid, ok := yaml_simple_key_is_valid(parser, &parser.simple_keys[head_tok_idx]); !ok {
|
||||
return false
|
||||
} else if !valid {
|
||||
break
|
||||
}
|
||||
|
||||
for i := range parser.simple_keys {
|
||||
simple_key := &parser.simple_keys[i]
|
||||
if simple_key.possible && simple_key.token_number == parser.tokens_parsed {
|
||||
need_more_tokens = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We are finished.
|
||||
if !need_more_tokens {
|
||||
break
|
||||
}
|
||||
// Fetch the next token.
|
||||
if !yaml_parser_fetch_next_token(parser) {
|
||||
|
@ -678,11 +665,6 @@ func yaml_parser_fetch_next_token(parser *yaml_parser_t) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// Remove obsolete potential simple keys.
|
||||
if !yaml_parser_stale_simple_keys(parser) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check the indentation level against the current column.
|
||||
if !yaml_parser_unroll_indent(parser, parser.mark.column) {
|
||||
return false
|
||||
|
@ -837,29 +819,30 @@ func yaml_parser_fetch_next_token(parser *yaml_parser_t) bool {
|
|||
"found character that cannot start any token")
|
||||
}
|
||||
|
||||
// Check the list of potential simple keys and remove the positions that
|
||||
// cannot contain simple keys anymore.
|
||||
func yaml_parser_stale_simple_keys(parser *yaml_parser_t) bool {
|
||||
// Check for a potential simple key for each flow level.
|
||||
for i := range parser.simple_keys {
|
||||
simple_key := &parser.simple_keys[i]
|
||||
|
||||
// The specification requires that a simple key
|
||||
//
|
||||
// - is limited to a single line,
|
||||
// - is shorter than 1024 characters.
|
||||
if simple_key.possible && (simple_key.mark.line < parser.mark.line || simple_key.mark.index+1024 < parser.mark.index) {
|
||||
|
||||
// Check if the potential simple key to be removed is required.
|
||||
if simple_key.required {
|
||||
return yaml_parser_set_scanner_error(parser,
|
||||
"while scanning a simple key", simple_key.mark,
|
||||
"could not find expected ':'")
|
||||
}
|
||||
simple_key.possible = false
|
||||
}
|
||||
func yaml_simple_key_is_valid(parser *yaml_parser_t, simple_key *yaml_simple_key_t) (valid, ok bool) {
|
||||
if !simple_key.possible {
|
||||
return false, true
|
||||
}
|
||||
return true
|
||||
|
||||
// The 1.2 specification says:
|
||||
//
|
||||
// "If the ? indicator is omitted, parsing needs to see past the
|
||||
// implicit key to recognize it as such. To limit the amount of
|
||||
// lookahead required, the “:” indicator must appear at most 1024
|
||||
// Unicode characters beyond the start of the key. In addition, the key
|
||||
// is restricted to a single line."
|
||||
//
|
||||
if simple_key.mark.line < parser.mark.line || simple_key.mark.index+1024 < parser.mark.index {
|
||||
// Check if the potential simple key to be removed is required.
|
||||
if simple_key.required {
|
||||
return false, yaml_parser_set_scanner_error(parser,
|
||||
"while scanning a simple key", simple_key.mark,
|
||||
"could not find expected ':'")
|
||||
}
|
||||
simple_key.possible = false
|
||||
return false, true
|
||||
}
|
||||
return true, true
|
||||
}
|
||||
|
||||
// Check if a simple key may start at the current position and add it if
|
||||
|
@ -879,13 +862,14 @@ func yaml_parser_save_simple_key(parser *yaml_parser_t) bool {
|
|||
possible: true,
|
||||
required: required,
|
||||
token_number: parser.tokens_parsed + (len(parser.tokens) - parser.tokens_head),
|
||||
mark: parser.mark,
|
||||
}
|
||||
simple_key.mark = parser.mark
|
||||
|
||||
if !yaml_parser_remove_simple_key(parser) {
|
||||
return false
|
||||
}
|
||||
parser.simple_keys[len(parser.simple_keys)-1] = simple_key
|
||||
parser.simple_keys_by_tok[simple_key.token_number] = len(parser.simple_keys) - 1
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -900,19 +884,33 @@ func yaml_parser_remove_simple_key(parser *yaml_parser_t) bool {
|
|||
"while scanning a simple key", parser.simple_keys[i].mark,
|
||||
"could not find expected ':'")
|
||||
}
|
||||
// Remove the key from the stack.
|
||||
parser.simple_keys[i].possible = false
|
||||
delete(parser.simple_keys_by_tok, parser.simple_keys[i].token_number)
|
||||
}
|
||||
// Remove the key from the stack.
|
||||
parser.simple_keys[i].possible = false
|
||||
return true
|
||||
}
|
||||
|
||||
// max_flow_level limits the flow_level
|
||||
const max_flow_level = 10000
|
||||
|
||||
// Increase the flow level and resize the simple key list if needed.
|
||||
func yaml_parser_increase_flow_level(parser *yaml_parser_t) bool {
|
||||
// Reset the simple key on the next level.
|
||||
parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{})
|
||||
parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{
|
||||
possible: false,
|
||||
required: false,
|
||||
token_number: parser.tokens_parsed + (len(parser.tokens) - parser.tokens_head),
|
||||
mark: parser.mark,
|
||||
})
|
||||
|
||||
// Increase the flow level.
|
||||
parser.flow_level++
|
||||
if parser.flow_level > max_flow_level {
|
||||
return yaml_parser_set_scanner_error(parser,
|
||||
"while increasing flow level", parser.simple_keys[len(parser.simple_keys)-1].mark,
|
||||
fmt.Sprintf("exceeded max depth of %d", max_flow_level))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -920,11 +918,16 @@ func yaml_parser_increase_flow_level(parser *yaml_parser_t) bool {
|
|||
func yaml_parser_decrease_flow_level(parser *yaml_parser_t) bool {
|
||||
if parser.flow_level > 0 {
|
||||
parser.flow_level--
|
||||
parser.simple_keys = parser.simple_keys[:len(parser.simple_keys)-1]
|
||||
last := len(parser.simple_keys) - 1
|
||||
delete(parser.simple_keys_by_tok, parser.simple_keys[last].token_number)
|
||||
parser.simple_keys = parser.simple_keys[:last]
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// max_indents limits the indents stack size
|
||||
const max_indents = 10000
|
||||
|
||||
// Push the current indentation level to the stack and set the new level
|
||||
// the current column is greater than the indentation level. In this case,
|
||||
// append or insert the specified token into the token queue.
|
||||
|
@ -939,6 +942,11 @@ func yaml_parser_roll_indent(parser *yaml_parser_t, column, number int, typ yaml
|
|||
// indentation level.
|
||||
parser.indents = append(parser.indents, parser.indent)
|
||||
parser.indent = column
|
||||
if len(parser.indents) > max_indents {
|
||||
return yaml_parser_set_scanner_error(parser,
|
||||
"while increasing indent level", parser.simple_keys[len(parser.simple_keys)-1].mark,
|
||||
fmt.Sprintf("exceeded max depth of %d", max_indents))
|
||||
}
|
||||
|
||||
// Create a token and insert it into the queue.
|
||||
token := yaml_token_t{
|
||||
|
@ -989,6 +997,8 @@ func yaml_parser_fetch_stream_start(parser *yaml_parser_t) bool {
|
|||
// Initialize the simple key stack.
|
||||
parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{})
|
||||
|
||||
parser.simple_keys_by_tok = make(map[int]int)
|
||||
|
||||
// A simple key is allowed at the beginning of the stream.
|
||||
parser.simple_key_allowed = true
|
||||
|
||||
|
@ -1270,7 +1280,11 @@ func yaml_parser_fetch_value(parser *yaml_parser_t) bool {
|
|||
simple_key := &parser.simple_keys[len(parser.simple_keys)-1]
|
||||
|
||||
// Have we found a simple key?
|
||||
if simple_key.possible {
|
||||
if valid, ok := yaml_simple_key_is_valid(parser, simple_key); !ok {
|
||||
return false
|
||||
|
||||
} else if valid {
|
||||
|
||||
// Create the KEY token and insert it into the queue.
|
||||
token := yaml_token_t{
|
||||
typ: yaml_KEY_TOKEN,
|
||||
|
@ -1288,6 +1302,7 @@ func yaml_parser_fetch_value(parser *yaml_parser_t) bool {
|
|||
|
||||
// Remove the simple key.
|
||||
simple_key.possible = false
|
||||
delete(parser.simple_keys_by_tok, simple_key.token_number)
|
||||
|
||||
// A simple key cannot follow another simple key.
|
||||
parser.simple_key_allowed = false
|
||||
|
|
16
vendor/gopkg.in/yaml.v2/yaml.go
generated
vendored
16
vendor/gopkg.in/yaml.v2/yaml.go
generated
vendored
|
@ -89,7 +89,7 @@ func UnmarshalStrict(in []byte, out interface{}) (err error) {
|
|||
return unmarshal(in, out, true)
|
||||
}
|
||||
|
||||
// A Decorder reads and decodes YAML values from an input stream.
|
||||
// A Decoder reads and decodes YAML values from an input stream.
|
||||
type Decoder struct {
|
||||
strict bool
|
||||
parser *parser
|
||||
|
@ -175,7 +175,7 @@ func unmarshal(in []byte, out interface{}, strict bool) (err error) {
|
|||
// Zero valued structs will be omitted if all their public
|
||||
// fields are zero, unless they implement an IsZero
|
||||
// method (see the IsZeroer interface type), in which
|
||||
// case the field will be included if that method returns true.
|
||||
// case the field will be excluded if IsZero returns true.
|
||||
//
|
||||
// flow Marshal using a flow style (useful for structs,
|
||||
// sequences and maps).
|
||||
|
@ -464,3 +464,15 @@ func isZero(v reflect.Value) bool {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// FutureLineWrap globally disables line wrapping when encoding long strings.
|
||||
// This is a temporary and thus deprecated method introduced to faciliate
|
||||
// migration towards v3, which offers more control of line lengths on
|
||||
// individual encodings, and has a default matching the behavior introduced
|
||||
// by this function.
|
||||
//
|
||||
// The default formatting of v2 was erroneously changed in v2.3.0 and reverted
|
||||
// in v2.4.0, at which point this function was introduced to help migration.
|
||||
func FutureLineWrap() {
|
||||
disableLineWrapping = true
|
||||
}
|
||||
|
|
1
vendor/gopkg.in/yaml.v2/yamlh.go
generated
vendored
1
vendor/gopkg.in/yaml.v2/yamlh.go
generated
vendored
|
@ -579,6 +579,7 @@ type yaml_parser_t struct {
|
|||
|
||||
simple_key_allowed bool // May a simple key occur at the current position?
|
||||
simple_keys []yaml_simple_key_t // The stack of simple keys.
|
||||
simple_keys_by_tok map[int]int // possible simple_key indexes indexed by token_number
|
||||
|
||||
// Parser stuff
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue