mirror of
https://github.com/pomerium/pomerium.git
synced 2025-04-29 18:36:30 +02:00
- all: prefer `FormValues` to `ParseForm` with subsequent `Form.Get`s - all: refactor authentication stack to be checked by middleware, and accessible via request context. - all: replace http.ServeMux with gorilla/mux’s router - all: replace custom CSRF checks with gorilla/csrf middleware - authenticate: extract callback path as constant. - internal/config: implement stringer interface for policy - internal/cryptutil: add helper func `NewBase64Key` - internal/cryptutil: rename `GenerateKey` to `NewKey` - internal/cryptutil: rename `GenerateRandomString` to `NewRandomStringN` - internal/middleware: removed alice in favor of gorilla/mux - internal/sessions: remove unused `ValidateRedirectURI` and `ValidateClientSecret` - internal/sessions: replace custom CSRF with gorilla/csrf fork that supports custom handler protection - internal/urlutil: add `SignedRedirectURL` to create hmac'd URLs - internal/urlutil: add `ValidateURL` helper to parse URL options - internal/urlutil: add `GetAbsoluteURL` which takes a request and returns its absolute URL. - proxy: remove holdover state verification checks; we no longer are setting sessions in any proxy routes so we don’t need them. - proxy: replace un-named http.ServeMux with named domain routes. Signed-off-by: Bobby DeSimone <bobbydesimone@gmail.com>
109 lines
3 KiB
Go
109 lines
3 KiB
Go
package urlutil // import "github.com/pomerium/pomerium/internal/urlutil"
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/pomerium/pomerium/internal/cryptutil"
|
|
)
|
|
|
|
// StripPort returns a host, without any port number.
|
|
//
|
|
// If Host is an IPv6 literal with a port number, Hostname returns the
|
|
// IPv6 literal without the square brackets. IPv6 literals may include
|
|
// a zone identifier.
|
|
func StripPort(hostport string) string {
|
|
colon := strings.IndexByte(hostport, ':')
|
|
if colon == -1 {
|
|
return hostport
|
|
}
|
|
if i := strings.IndexByte(hostport, ']'); i != -1 {
|
|
return strings.TrimPrefix(hostport[:i], "[")
|
|
}
|
|
return hostport[:colon]
|
|
}
|
|
|
|
// ParseAndValidateURL wraps standard library's default url.Parse because
|
|
// it's much more lenient about what type of urls it accepts than pomerium.
|
|
func ParseAndValidateURL(rawurl string) (*url.URL, error) {
|
|
if rawurl == "" {
|
|
return nil, fmt.Errorf("url cannot be empty")
|
|
}
|
|
u, err := url.Parse(rawurl)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := ValidateURL(u); err != nil {
|
|
return nil, err
|
|
}
|
|
return u, nil
|
|
}
|
|
|
|
// ValidateURL wraps standard library's default url.Parse because
|
|
// it's much more lenient about what type of urls it accepts than pomerium.
|
|
func ValidateURL(u *url.URL) error {
|
|
if u == nil {
|
|
return fmt.Errorf("nil url")
|
|
}
|
|
if u.Scheme == "" {
|
|
return fmt.Errorf("%s url does contain a valid scheme. Did you mean https://%s?", u.String(), u.String())
|
|
}
|
|
if u.Host == "" {
|
|
return fmt.Errorf("%s url does contain a valid hostname", u.String())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DeepCopy(u *url.URL) (*url.URL, error) {
|
|
if u == nil {
|
|
return nil, nil
|
|
}
|
|
return ParseAndValidateURL(u.String())
|
|
}
|
|
|
|
// testTimeNow can be used in tests to set a specific int64 time
|
|
var testTimeNow int64
|
|
|
|
// timestamp returns the current timestamp, in seconds.
|
|
//
|
|
// For testing purposes, the function that generates the timestamp can be
|
|
// overridden. If not set, it will return time.Now().UTC().Unix().
|
|
func timestamp() int64 {
|
|
if testTimeNow == 0 {
|
|
return time.Now().UTC().Unix()
|
|
}
|
|
return testTimeNow
|
|
}
|
|
|
|
// SignedRedirectURL takes a destination URL and adds redirect_uri to it's
|
|
// query params, along with a timestamp and an keyed signature.
|
|
func SignedRedirectURL(key string, destination, urlToSign *url.URL) *url.URL {
|
|
now := timestamp()
|
|
rawURL := urlToSign.String()
|
|
params, _ := url.ParseQuery(destination.RawQuery) // handled by incoming mux
|
|
params.Set("redirect_uri", rawURL)
|
|
params.Set("ts", fmt.Sprint(now))
|
|
params.Set("sig", hmacURL(key, rawURL, now))
|
|
destination.RawQuery = params.Encode()
|
|
return destination
|
|
}
|
|
|
|
// hmacURL takes a redirect url string and timestamp and returns the base64
|
|
// encoded HMAC result.
|
|
func hmacURL(key, data string, timestamp int64) string {
|
|
h := cryptutil.Hash(key, []byte(fmt.Sprint(data, timestamp)))
|
|
return base64.URLEncoding.EncodeToString(h)
|
|
}
|
|
|
|
// GetAbsoluteURL returns the current handler's absolute url.
|
|
// https://stackoverflow.com/a/23152483
|
|
func GetAbsoluteURL(r *http.Request) *url.URL {
|
|
u := r.URL
|
|
u.Scheme = "https"
|
|
u.Host = r.Host
|
|
return u
|
|
}
|