FAQ | This is a LIVE service | Changelog

Skip to content
Snippets Groups Projects
auth_request.go 3.44 KiB
Newer Older
Dr Rich Wareham's avatar
Dr Rich Wareham committed
package ucamwls

import (
	"fmt"
	"net/http"
	"net/url"
)

Dr Rich Wareham's avatar
Dr Rich Wareham committed
// requiredParams is an array of query parameters which are required in valid
// authentication requests.
Dr Rich Wareham's avatar
Dr Rich Wareham committed
var requiredParams = []string{"ver", "url"}

Dr Rich Wareham's avatar
Dr Rich Wareham committed
// allowedParams is a map of allowed parameter names to the value "true". It
// is used as an O(1) lookup to determine if a given parameter name is valid.
Dr Rich Wareham's avatar
Dr Rich Wareham committed
var allowedParams = map[string]bool{}

// knownVersions is a list of known versions of the protocol. An authetication
// request is invalid if the version does not match one of these versions.
var knownVersions = []string{"1", "2", "3"}

Dr Rich Wareham's avatar
Dr Rich Wareham committed
// AuthRequest represents the parsed value of an authentication request.
Dr Rich Wareham's avatar
Dr Rich Wareham committed
type AuthRequest struct {
Dr Rich Wareham's avatar
Dr Rich Wareham committed
	// The version of the protocol being requested. Must be "1", "2" or "3".
	Version string

	// The parsed URL to redirect to after authentication has been performed.
	URL *url.URL

	// A human-readable description of the application and a message indicating
	// the application wants authentication. Both are optional and are blank if
	// they are not present in the original request.
	Description string
	Message     string

	// The list of authentication types acceptable to the WAA.
Dr Rich Wareham's avatar
Dr Rich Wareham committed
	AcceptableAuthTypes []string
Dr Rich Wareham's avatar
Dr Rich Wareham committed

	// Should the WLS always seek to re-authenticate the user even if it can
	// use persistent session information to avoid this?
	RequireReAuth bool

	// If true, the WLS should fail any authentication request which cannot be
	// satisfied without requiring user interaction.
	NonInteractiveAuth bool

	// Parameters which should be passed back to the WAA unchanged from the WLS.
	Params string
Dr Rich Wareham's avatar
Dr Rich Wareham committed
}

func init() {
	allowedParamsArray := []string{
		"ver", "url", "desc", "aauth", "iact", "msg", "params", "date", "skew", "fail",
	}
	for _, k := range allowedParamsArray {
		allowedParams[k] = true
	}
}

// NewAuthRequest is
func NewAuthRequest(request *http.Request) (*AuthRequest, error) {
	// Parse query parameters
	vs, err := url.ParseQuery(request.URL.RawQuery)
	if err != nil {
		return nil, err
	}

	// Check for multiple values
	for param, values := range vs {
		if len(values) != 1 {
			return nil, fmt.Errorf("Parameter '%s' has multiple values", param)
		}
	}

	// Check required parameters are present
	for _, param := range requiredParams {
		if vs.Get(param) == "" {
			return nil, fmt.Errorf("Parameter '%s' missing", param)
		}
	}

	// Check unknown parameters are not present
	for k := range vs {
		if _, ok := allowedParams[k]; !ok {
			return nil, fmt.Errorf("Unknown parameter: %s", k)
		}
	}

	// Check safe text parameters
	for _, param := range []string{"desc", "msg"} {
		if v := vs.Get(param); v != "" {
			// The parameter is in the request
			if !isSafeText(v) {
				return nil, fmt.Errorf(
					"Parameter '%s' contains forbidden characters", param)
			}
		}
	}

	// Parse the URL in the request
	url, err := url.Parse(vs.Get("url"))
	if err != nil {
		return nil, err
	}

	if url.Scheme != "http" && url.Scheme != "https" {
		return nil, fmt.Errorf("Bad redirect URL scheme: %s", url.Scheme)
	}

	ar := &AuthRequest{
		Version: vs.Get("ver"),
		URL:     url,
	}

	for _, ver := range knownVersions {
		if ver == ar.Version {
			return ar, nil
		}
	}

	return nil, fmt.Errorf("Unkown protocol version: %v", ar.Version)
Dr Rich Wareham's avatar
Dr Rich Wareham committed
}

// isSafeText returns true if the passed string is ASCII and does not
// contain any non-printable characters.
func isSafeText(s string) bool {
	for _, r := range s {
		if r < 0x20 {
			return false
		}
		if r >= 0x7f {
			return false
		}
	}
	return true
}