Skip to content

Commit

Permalink
proxy: Validate handler configurations
Browse files Browse the repository at this point in the history
Signed-off-by: arekkas <[email protected]>
  • Loading branch information
arekkas committed Aug 31, 2018
1 parent 8b2dc13 commit 12d632a
Show file tree
Hide file tree
Showing 9 changed files with 142 additions and 38 deletions.
87 changes: 66 additions & 21 deletions cmd/helper_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import (
"strings"
"time"

"net/url"

"github.com/ory/fosite"
"github.com/ory/hydra/sdk/go/hydra"
"github.com/ory/keto/sdk/go/keto"
Expand Down Expand Up @@ -178,41 +180,84 @@ func getScopeStrategy(key string) fosite.ScopeStrategy {
return nil
}

func authenticatorFactory(f func() (proxy.Authenticator, error)) proxy.Authenticator {
a, err := f()
if err != nil {
logger.WithError(err).Fatalf("Unable to initialize authenticator \"%s\" because an environment variable is missing or misconfigured.", a.GetID())
}
return a
}
func credentialsIssuerFactory(f func() (proxy.CredentialsIssuer, error)) proxy.CredentialsIssuer {
a, err := f()
if err != nil {
logger.WithError(err).Fatalf("Unable to initialize authenticator \"%s\" because an environment variable is missing or misconfigured.", a.GetID())
}
return a
}

func handlerFactories(keyManager rsakey.Manager) ([]proxy.Authenticator, []proxy.Authorizer, []proxy.CredentialsIssuer) {
var authorizers = []proxy.Authorizer{
proxy.NewAuthorizerAllow(),
proxy.NewAuthorizerDeny(),
}

if u := viper.GetString("AUTHORIZER_KETO_WARDEN_KETO_URL"); len(u) > 0 {
ketoSdk, err := keto.NewCodeGenSDK(&keto.Configuration{
EndpointURL: viper.GetString("AUTHORIZER_KETO_WARDEN_KETO_URL"),
})
if err != nil {
logger.WithError(err).Fatal("Unable to initialize the ORY Keto SDK")
}
authorizers = append(authorizers, proxy.NewAuthorizerKetoWarden(ketoSdk))
var authenticators = []proxy.Authenticator{
proxy.NewAuthenticatorNoOp(),
proxy.NewAuthenticatorAnonymous(viper.GetString("AUTHENTICATOR_ANONYMOUS_USERNAME")),
}

return []proxy.Authenticator{
proxy.NewAuthenticatorNoOp(),
proxy.NewAuthenticatorAnonymous(viper.GetString("AUTHENTICATOR_ANONYMOUS_USERNAME")),
proxy.NewAuthenticatorOAuth2Introspection(
if u := viper.GetString("AUTHENTICATOR_OAUTH2_INTROSPECTION_URL"); len(u) > 0 {
authenticators = append(authenticators, authenticatorFactory(func() (proxy.Authenticator, error) {
return proxy.NewAuthenticatorOAuth2Introspection(
viper.GetString("AUTHENTICATOR_OAUTH2_INTROSPECTION_AUTHORIZATION_CLIENT_ID"),
viper.GetString("AUTHENTICATOR_OAUTH2_INTROSPECTION_AUTHORIZATION_CLIENT_SECRET"),
viper.GetString("AUTHENTICATOR_OAUTH2_INTROSPECTION_AUTHORIZATION_TOKEN_URL"),
viper.GetString("AUTHENTICATOR_OAUTH2_INTROSPECTION_URL"),
u,
strings.Split(viper.GetString("AUTHENTICATOR_OAUTH2_INTROSPECTION_AUTHORIZATION_SCOPE"), ","),
getScopeStrategy("AUTHENTICATOR_OAUTH2_INTROSPECTION_SCOPE_STRATEGY"),
),
proxy.NewAuthenticatorOAuth2ClientCredentials(
viper.GetString("AUTHENTICATOR_OAUTH2_CLIENT_CREDENTIALS_TOKEN_URL"),
),
proxy.NewAuthenticatorJWT(
)
}))
logger.Info("Authenticator \"oauth2_client_credentials\" was configured and enabled successfully.")
} else {
logger.Warn("Authenticator \"oauth2_client_credentials\" is not configured and thus disabled.")
}

if u := viper.GetString("AUTHENTICATOR_OAUTH2_CLIENT_CREDENTIALS_TOKEN_URL"); len(u) > 0 {
authenticators = append(authenticators, authenticatorFactory(func() (proxy.Authenticator, error) {
return proxy.NewAuthenticatorOAuth2ClientCredentials(u)
}))
logger.Info("Authenticator \"oauth2_client_credentials\" was configured and enabled successfully.")
} else {
logger.Warn("Authenticator \"oauth2_client_credentials\" is not configured and thus disabled.")
}

if u := viper.GetString("AUTHENTICATOR_JWT_JWKS_URL"); len(u) > 0 {
authenticators = append(authenticators, authenticatorFactory(func() (proxy.Authenticator, error) {
return proxy.NewAuthenticatorJWT(
viper.GetString("AUTHENTICATOR_JWT_JWKS_URL"),
getScopeStrategy("AUTHENTICATOR_JWT_SCOPE_STRATEGY"),
),
},
)
}))
logger.Info("Authenticator \"jwt\" was configured and enabled successfully.")
} else {
logger.Warn("Authenticator \"jwt\" is not configured and thus disabled.")
}

if u := viper.GetString("AUTHORIZER_KETO_WARDEN_KETO_URL"); len(u) > 0 {
if _, err := url.ParseRequestURI(u); err != nil {
logger.WithError(err).Fatalf("Value \"%s\" from environment variable \"AUTHORIZER_KETO_WARDEN_KETO_URL\" is not a valid URL.", u)
}
ketoSdk, err := keto.NewCodeGenSDK(&keto.Configuration{
EndpointURL: u,
})
if err != nil {
logger.WithError(err).Fatal("Unable to initialize the ORY Keto SDK.")
}
authorizers = append(authorizers, proxy.NewAuthorizerKetoWarden(ketoSdk))
} else {
logger.Warn("Authorizer \"ory-keto\" is not configured and thus disabled.")
}

return authenticators,
authorizers,
[]proxy.CredentialsIssuer{
proxy.NewCredentialsIssuerNoOp(),
Expand Down
10 changes: 8 additions & 2 deletions proxy/authenticator_jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"crypto/rsa"
"fmt"

"net/url"

"github.com/dgrijalva/jwt-go"
"github.com/ory/fosite"
"github.com/ory/go-convenience/jwtx"
Expand Down Expand Up @@ -45,12 +47,16 @@ type AuthenticatorJWT struct {
scopeStrategy fosite.ScopeStrategy
}

func NewAuthenticatorJWT(jwksURL string, scopeStrategy fosite.ScopeStrategy) *AuthenticatorJWT {
func NewAuthenticatorJWT(jwksURL string, scopeStrategy fosite.ScopeStrategy) (*AuthenticatorJWT, error) {
if _, err := url.ParseRequestURI(jwksURL); err != nil {
return new(AuthenticatorJWT), errors.Errorf(`unable to validate the JSON Web Token Authenticator's JWKs URL "%s" because %s`, jwksURL, err)
}

return &AuthenticatorJWT{
jwksURL: jwksURL,
fetcher: fosite.NewDefaultJWKSFetcherStrategy(),
scopeStrategy: scopeStrategy,
}
}, nil
}

func (a *AuthenticatorJWT) GetID() string {
Expand Down
10 changes: 9 additions & 1 deletion proxy/authenticator_jwt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,13 @@ func generateJWT(t *testing.T, claims jwt.Claims, method string) string {
return fmt.Sprintf("%s.%s", sign, j)
}

func TestNewAuthenticatorJWT(t *testing.T) {
_, err := NewAuthenticatorJWT("", fosite.ExactScopeStrategy)
require.Error(t, err)
_, err = NewAuthenticatorJWT("foo", fosite.ExactScopeStrategy)
require.Error(t, err)
}

func TestAuthenticatorJWT(t *testing.T) {
generateKeys(t)

Expand All @@ -97,7 +104,8 @@ func TestAuthenticatorJWT(t *testing.T) {
}))
defer ts.Close()

authenticator := NewAuthenticatorJWT(ts.URL, fosite.ExactScopeStrategy)
authenticator, err := NewAuthenticatorJWT(ts.URL, fosite.ExactScopeStrategy)
require.NoError(t, err)
assert.NotEmpty(t, authenticator.GetID())
now := time.Now().Round(time.Second)

Expand Down
8 changes: 6 additions & 2 deletions proxy/authenticator_oauth2_client_credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,14 @@ type AuthenticatorOAuth2ClientCredentials struct {
tokenURL string
}

func NewAuthenticatorOAuth2ClientCredentials(tokenURL string) *AuthenticatorOAuth2ClientCredentials {
func NewAuthenticatorOAuth2ClientCredentials(tokenURL string) (*AuthenticatorOAuth2ClientCredentials, error) {
if _, err := url.ParseRequestURI(tokenURL); err != nil {
return new(AuthenticatorOAuth2ClientCredentials), errors.Errorf(`unable to validate the OAuth 2.0 Client Credentials Authenticator's Token Introspection URL "%s" because %s`, tokenURL, err)
}

return &AuthenticatorOAuth2ClientCredentials{
tokenURL: tokenURL,
}
}, nil
}

func (a *AuthenticatorOAuth2ClientCredentials) GetID() string {
Expand Down
12 changes: 10 additions & 2 deletions proxy/authenticator_oauth2_client_credentials_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ import (
"github.com/stretchr/testify/require"
)

func TestNewAuthenticatorOAuth2ClientCredentials(t *testing.T) {
_, err := NewAuthenticatorOAuth2ClientCredentials("")
require.Error(t, err)
_, err = NewAuthenticatorOAuth2ClientCredentials("oauth2/token")
require.Error(t, err)
}

func TestAuthenticatorOAuth2ClientCredentials(t *testing.T) {
h := httprouter.New()
h.POST("/oauth2/token", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
Expand All @@ -49,12 +56,13 @@ func TestAuthenticatorOAuth2ClientCredentials(t *testing.T) {
})
ts := httptest.NewServer(h)

a := NewAuthenticatorOAuth2ClientCredentials(ts.URL + "/oauth2/token")
a, err := NewAuthenticatorOAuth2ClientCredentials(ts.URL + "/oauth2/token")
require.NoError(t, err)
// "client",
// "secret",
//,
//[]string{"foo-scope"},)
assert.NotEmpty(t, NewAuthenticatorNoOp().GetID())
assert.NotEmpty(t, a.GetID())

authOk := &http.Request{Header: http.Header{}}
authOk.SetBasicAuth("client", "secret")
Expand Down
22 changes: 20 additions & 2 deletions proxy/authenticator_oauth2_introspection.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,26 @@ type AuthenticatorOAuth2Introspection struct {
scopeStrategy fosite.ScopeStrategy
}

func NewAuthenticatorOAuth2Introspection(clientID, clientSecret, tokenURL, introspectionURL string, scopes []string, strategy fosite.ScopeStrategy) *AuthenticatorOAuth2Introspection {
func NewAuthenticatorOAuth2Introspection(
clientID, clientSecret, tokenURL, introspectionURL string,
scopes []string, strategy fosite.ScopeStrategy,
) (*AuthenticatorOAuth2Introspection, error) {
if _, err := url.ParseRequestURI(introspectionURL); err != nil {
return new(AuthenticatorOAuth2Introspection), errors.Errorf(`unable to validate the OAuth 2.0 Introspection Authenticator's Token Introspection URL "%s" because %s`, introspectionURL, err)
}

c := http.DefaultClient
if len(clientID)+len(clientSecret)+len(tokenURL)+len(scopes) > 0 {
if len(clientID) == 0 {
return new(AuthenticatorOAuth2Introspection), errors.Errorf("if OAuth 2.0 Authorization is used in the OAuth 2.0 Introspection Authenticator, the OAuth 2.0 Client ID must be set but was not")
}
if len(clientSecret) == 0 {
return new(AuthenticatorOAuth2Introspection), errors.Errorf("if OAuth 2.0 Authorization is used in the OAuth 2.0 Introspection Authenticator, the OAuth 2.0 Client ID must be set but was not")
}
if _, err := url.ParseRequestURI(tokenURL); err != nil {
return new(AuthenticatorOAuth2Introspection), errors.Errorf(`if OAuth 2.0 Authorization is used in the OAuth 2.0 Introspection Authenticator, the OAuth 2.0 Token URL must be set but validating URL "%s" failed because %s`, tokenURL, err)
}

c = (&clientcredentials.Config{
ClientID: clientID,
ClientSecret: clientSecret,
Expand All @@ -52,7 +69,8 @@ func NewAuthenticatorOAuth2Introspection(clientID, clientSecret, tokenURL, intro
return &AuthenticatorOAuth2Introspection{
client: c,
introspectionURL: introspectionURL,
}
scopeStrategy: strategy,
}, nil
}

func (a *AuthenticatorOAuth2Introspection) GetID() string {
Expand Down
25 changes: 20 additions & 5 deletions proxy/authenticator_oauth2_introspection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,22 @@ import (
"github.com/stretchr/testify/require"
)

func TestAuthenticatorOAuth2Introspection(t *testing.T) {
a := NewAuthenticatorOAuth2Introspection("", "", "", "", []string{}, fosite.ExactScopeStrategy)
assert.NotEmpty(t, a.GetID())
func TestNewAuthenticatorOAuth2Introspection(t *testing.T) {
_, err := NewAuthenticatorOAuth2Introspection("", "", "", "", []string{}, fosite.ExactScopeStrategy)
require.Error(t, err)
_, err = NewAuthenticatorOAuth2Introspection("", "", "", "http://localhost:1234/oauth2/introspect", []string{}, fosite.ExactScopeStrategy)
require.NoError(t, err)
_, err = NewAuthenticatorOAuth2Introspection("foo", "", "", "http://localhost:1234/oauth2/introspect", []string{}, fosite.ExactScopeStrategy)
require.Error(t, err)
_, err = NewAuthenticatorOAuth2Introspection("", "foo", "", "http://localhost:1234/oauth2/introspect", []string{}, fosite.ExactScopeStrategy)
require.Error(t, err)
_, err = NewAuthenticatorOAuth2Introspection("foo", "foo", "foo", "http://localhost:1234/oauth2/introspect", []string{}, fosite.ExactScopeStrategy)
require.Error(t, err)
_, err = NewAuthenticatorOAuth2Introspection("foo", "foo", "http://localhost:1234/oauth2/token", "http://localhost:1234/oauth2/introspect", []string{}, fosite.ExactScopeStrategy)
require.NoError(t, err)
}

func TestAuthenticatorOAuth2Introspection(t *testing.T) {
for k, tc := range []struct {
d string
setup func(*testing.T, *httprouter.Router)
Expand Down Expand Up @@ -148,7 +160,7 @@ func TestAuthenticatorOAuth2Introspection(t *testing.T) {
}))
})
},
expectErr: false,
expectErr: true,
},
{
d: "should fail because active but issuer not matching",
Expand Down Expand Up @@ -238,7 +250,10 @@ func TestAuthenticatorOAuth2Introspection(t *testing.T) {
}
ts := httptest.NewServer(router)
defer ts.Close()
a.introspectionURL = ts.URL + "/oauth2/introspect"

a, err := NewAuthenticatorOAuth2Introspection("", "", "", ts.URL+"/oauth2/introspect", []string{}, fosite.ExactScopeStrategy)
require.NoError(t, err)
assert.NotEmpty(t, a.GetID())

sess, err := a.Authenticate(tc.r, tc.config, nil)
if tc.expectErr {
Expand Down
2 changes: 1 addition & 1 deletion rule/matcher_cached_http.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func (m *HTTPMatcher) Refresh() error {
return errors.WithStack(err)
}
if response.StatusCode != http.StatusOK {
return errors.Errorf("Unable to fetch rules from backend, got status code %d but expected %s", response.StatusCode, http.StatusOK)
return errors.Errorf("unable to fetch rules from backend, got status code %d but expected %d", response.StatusCode, http.StatusOK)
}

inserted := map[string]bool{}
Expand Down
4 changes: 2 additions & 2 deletions rule/rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ type Upstream struct {
// IsMatching returns an error if the provided method and URL do not match the rule.
func (r *Rule) IsMatching(method string, u *url.URL) error {
if !stringInSlice(method, r.Match.Methods) {
return errors.Errorf("Rule %s does not match URL %s", u)
return errors.Errorf("rule %s does not match URL %s", r.ID, u)
}

c, err := r.CompileURL()
Expand All @@ -128,7 +128,7 @@ func (r *Rule) IsMatching(method string, u *url.URL) error {
}

if !c.MatchString(u.String()) {
return errors.Errorf("Rule %s does not match URL %s", u)
return errors.Errorf("rule %s does not match URL %s", r.ID, u)
}

return nil
Expand Down

0 comments on commit 12d632a

Please sign in to comment.