From 80eeee2de200237cb4bea35f335fc53aed71d193 Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Thu, 28 Nov 2019 12:14:14 +0100 Subject: [PATCH] backup --- example/internal/mock/storage.go | 9 +- example/server/default/default.go | 3 +- pkg/op/authrequest.go | 24 +++-- pkg/op/authrequest_test.go | 56 ++++++++--- pkg/op/default_op.go | 144 ++++++---------------------- pkg/op/default_op_test.go | 3 +- pkg/op/discovery.go | 33 +++++++ pkg/op/endpoint.go | 25 +++++ pkg/op/go.mod | 2 + pkg/op/go.sum | 1 + pkg/op/mock/authorizer.mock.go | 107 +++++++++++++++++++++ pkg/op/mock/authorizer.mock.impl.go | 86 +++++++++++++++++ pkg/op/mock/generate.go | 3 +- pkg/op/mock/storage.mock.go | 17 +++- pkg/op/mock/storage.mock.impl.go | 17 ++-- pkg/op/op.go | 25 +++++ pkg/op/tokenrequest.go | 13 +-- pkg/op/u/signer.go | 9 ++ pkg/op/{ => u}/storage.go | 2 +- 19 files changed, 422 insertions(+), 157 deletions(-) create mode 100644 pkg/op/discovery.go create mode 100644 pkg/op/endpoint.go create mode 100644 pkg/op/mock/authorizer.mock.go create mode 100644 pkg/op/mock/authorizer.mock.impl.go create mode 100644 pkg/op/u/signer.go rename pkg/op/{ => u}/storage.go (97%) diff --git a/example/internal/mock/storage.go b/example/internal/mock/storage.go index 0092334..b9828db 100644 --- a/example/internal/mock/storage.go +++ b/example/internal/mock/storage.go @@ -6,6 +6,13 @@ import ( "github.com/caos/oidc/pkg/oidc" ) +type Signer struct { +} + +func (s *Signer) Sign(*oidc.IDTokenClaims) (string, error) { + return "sdsa", nil +} + type Storage struct { } @@ -64,5 +71,5 @@ func (c *ConfClient) LoginURL(id string) string { } func (c *ConfClient) ApplicationType() oidc.ApplicationType { - return oidc.ApplicationTypeWeb + return oidc.ApplicationTypeNative } diff --git a/example/server/default/default.go b/example/server/default/default.go index 23aeb8b..690b38e 100644 --- a/example/server/default/default.go +++ b/example/server/default/default.go @@ -16,7 +16,8 @@ func main() { Port: "9998", } storage := &mock.Storage{} - handler, err := server.NewDefaultOP(config, storage, server.WithCustomTokenEndpoint("test")) + signer := &mock.Signer{} + handler, err := server.NewDefaultOP(config, storage, signer, server.WithCustomTokenEndpoint("test")) if err != nil { log.Fatal(err) } diff --git a/pkg/op/authrequest.go b/pkg/op/authrequest.go index df86df4..e580669 100644 --- a/pkg/op/authrequest.go +++ b/pkg/op/authrequest.go @@ -10,25 +10,35 @@ import ( "github.com/gorilla/schema" "github.com/caos/oidc/pkg/oidc" + "github.com/caos/oidc/pkg/op/u" str_utils "github.com/caos/utils/strings" ) type Authorizer interface { - Storage() Storage + Storage() u.Storage Decoder() *schema.Decoder Encoder() *schema.Encoder - Signer() Signer + Signe() u.Signer + ErrorHandler() func(w http.ResponseWriter, r *http.Request, authReq *oidc.AuthRequest, err error) } +// type Signer interface { +// Sign(claims *oidc.IDTokenClaims) (string, error) +// } + type ValidationAuthorizer interface { Authorizer - ValidateAuthRequest(*oidc.AuthRequest, Storage) error + ValidateAuthRequest(*oidc.AuthRequest, u.Storage) error } +// type errorHandler func(w http.ResponseWriter, r *http.Request, authReq *oidc.AuthRequest, err error) +type callbackHandler func(authReq *oidc.AuthRequest, client oidc.Client, w http.ResponseWriter, r *http.Request) + func Authorize(w http.ResponseWriter, r *http.Request, authorizer Authorizer) { err := r.ParseForm() if err != nil { - AuthRequestError(w, r, nil, ErrInvalidRequest("cannot parse form: %v", err)) + AuthRequestError(w, r, nil, ErrInvalidRequest("cannot parse form")) + // AuthRequestError(w, r, nil, ) return } authReq := new(oidc.AuthRequest) @@ -62,7 +72,7 @@ func Authorize(w http.ResponseWriter, r *http.Request, authorizer Authorizer) { RedirectToLogin(authReq, client, w, r) } -func ValidateAuthRequest(authReq *oidc.AuthRequest, storage Storage) error { +func ValidateAuthRequest(authReq *oidc.AuthRequest, storage u.Storage) error { if err := ValidateAuthReqScopes(authReq.Scopes); err != nil { return err } @@ -90,7 +100,7 @@ func ValidateAuthReqScopes(scopes []string) error { return nil } -func ValidateAuthReqRedirectURI(uri, client_id string, responseType oidc.ResponseType, storage Storage) error { +func ValidateAuthReqRedirectURI(uri, client_id string, responseType oidc.ResponseType, storage u.Storage) error { if uri == "" { return ErrInvalidRequest("redirect_uri must not be empty") } @@ -153,7 +163,7 @@ func AuthResponse(authReq *oidc.AuthRequest, authorizer Authorizer, w http.Respo } } - idToken, err := CreateIDToken(authReq, accessToken, authorizer.Signer()) + idToken, err := CreateIDToken(authReq, accessToken, authorizer.Signe()) if err != nil { } diff --git a/pkg/op/authrequest_test.go b/pkg/op/authrequest_test.go index defe085..93d1c54 100644 --- a/pkg/op/authrequest_test.go +++ b/pkg/op/authrequest_test.go @@ -3,19 +3,18 @@ package op import ( "net/http" "net/http/httptest" + "strings" "testing" - "github.com/gorilla/schema" - "github.com/caos/oidc/pkg/oidc" - "github.com/caos/oidc/pkg/op" "github.com/caos/oidc/pkg/op/mock" + "github.com/caos/oidc/pkg/op/u" ) func TestValidateAuthRequest(t *testing.T) { type args struct { authRequest *oidc.AuthRequest - storage op.Storage + storage u.Storage } tests := []struct { name string @@ -66,7 +65,7 @@ func TestValidateAuthReqRedirectURI(t *testing.T) { uri string clientID string responseType oidc.ResponseType - storage op.Storage + storage u.Storage } tests := []struct { name string @@ -172,23 +171,54 @@ func TestValidateAuthReqScopes(t *testing.T) { } func TestAuthorize(t *testing.T) { + // testCallback := func(t *testing.T, clienID string) callbackHandler { + // return func(authReq *oidc.AuthRequest, client oidc.Client, w http.ResponseWriter, r *http.Request) { + // // require.Equal(t, clientID, client.) + // } + // } + // testErr := func(t *testing.T, expected error) errorHandler { + // return func(w http.ResponseWriter, r *http.Request, authReq *oidc.AuthRequest, err error) { + // require.Equal(t, expected, err) + // } + // } type args struct { - w http.ResponseWriter - r *http.Request - storage Storage - decoder *schema.Decoder + w http.ResponseWriter + r *http.Request + authorizer Authorizer } tests := []struct { name string args args }{ - {"parsing fails", args{httptest.NewRecorder(), &http.Request{Method: "POST", Body: nil}, nil, nil}}, - {"decoding fails", args{httptest.NewRecorder(), &http.Request{}, nil, schema.NewDecoder()}}, - {"decoding fails", args{httptest.NewRecorder(), &http.Request{}, nil, schema.NewDecoder()}}, + { + "parsing fails", + args{ + httptest.NewRecorder(), + &http.Request{Method: "POST", Body: nil}, + mock.NewAuthorizerExpectValid(t, true), + // testCallback(t, ""), + // testErr(t, ErrInvalidRequest("cannot parse form")), + }, + }, + { + "decoding fails", + args{ + httptest.NewRecorder(), + func() *http.Request { + r := httptest.NewRequest("POST", "/authorize", strings.NewReader("client_id=foo")) + r.Header.Set("Content-Type", "application/x-www-form-urlencoded") + return r + }(), + mock.NewAuthorizerExpectValid(t, true), + // testCallback(t, ""), + // testErr(t, ErrInvalidRequest("cannot parse auth request")), + }, + }, + // {"decoding fails", args{httptest.NewRecorder(), &http.Request{}, mock.NewAuthorizerExpectValid(t), nil, testErr(t, nil)}}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - Authorize(tt.args.w, tt.args.r, tt.args.storage, tt.args.decoder) + Authorize(tt.args.w, tt.args.r, tt.args.authorizer) }) } } diff --git a/pkg/op/default_op.go b/pkg/op/default_op.go index 7060d18..b3aa54d 100644 --- a/pkg/op/default_op.go +++ b/pkg/op/default_op.go @@ -1,23 +1,36 @@ package op import ( - "errors" "net/http" - "net/url" - "strings" "github.com/gorilla/schema" - "github.com/caos/oidc/pkg/utils" - "github.com/caos/oidc/pkg/oidc" + "github.com/caos/oidc/pkg/op/u" +) + +const ( + defaultAuthorizationEndpoint = "authorize" + defaulTokenEndpoint = "oauth/token" + defaultIntrospectEndpoint = "introspect" + defaultUserinfoEndpoint = "userinfo" +) + +var ( + DefaultEndpoints = &endpoints{ + Authorization: defaultAuthorizationEndpoint, + Token: defaulTokenEndpoint, + IntrospectionEndpoint: defaultIntrospectEndpoint, + Userinfo: defaultUserinfoEndpoint, + } ) type DefaultOP struct { config *Config endpoints *endpoints discoveryConfig *oidc.DiscoveryConfiguration - storage Storage + storage u.Storage + signer u.Signer http *http.Server decoder *schema.Decoder encoder *schema.Encoder @@ -77,42 +90,7 @@ func WithCustomUserinfoEndpoint(endpoint Endpoint) DefaultOPOpts { } } -const ( - defaultAuthorizationEndpoint = "authorize" - defaulTokenEndpoint = "oauth/token" - defaultIntrospectEndpoint = "introspect" - defaultUserinfoEndpoint = "userinfo" -) - -func CreateDiscoveryConfig(c Configuration) *oidc.DiscoveryConfiguration { - return &oidc.DiscoveryConfiguration{ - Issuer: c.Issuer(), - AuthorizationEndpoint: c.AuthorizationEndpoint().Absolute(c.Issuer()), - TokenEndpoint: c.TokenEndpoint().Absolute(c.Issuer()), - // IntrospectionEndpoint: c.Intro().Absolute(c.Issuer()), - UserinfoEndpoint: c.UserinfoEndpoint().Absolute(c.Issuer()), - // EndSessionEndpoint: c.TokenEndpoint().Absolute(c.Issuer())(c.EndSessionEndpoint), - // CheckSessionIframe: c.TokenEndpoint().Absolute(c.Issuer())(c.CheckSessionIframe), - // JwksURI: c.TokenEndpoint().Absolute(c.Issuer())(c.JwksURI), - // ScopesSupported: oidc.SupportedScopes, - // ResponseTypesSupported: responseTypes, - // GrantTypesSupported: oidc.SupportedGrantTypes, - // ClaimsSupported: oidc.SupportedClaims, - // IdTokenSigningAlgValuesSupported: []string{keys.SigningAlgorithm}, - // SubjectTypesSupported: []string{"public"}, - // TokenEndpointAuthMethodsSupported: - - } -} - -var DefaultEndpoints = &endpoints{ - Authorization: defaultAuthorizationEndpoint, - Token: defaulTokenEndpoint, - IntrospectionEndpoint: defaultIntrospectEndpoint, - Userinfo: defaultUserinfoEndpoint, -} - -func NewDefaultOP(config *Config, storage Storage, opOpts ...DefaultOPOpts) (OpenIDProvider, error) { +func NewDefaultOP(config *Config, storage u.Storage, signer u.Signer, opOpts ...DefaultOPOpts) (OpenIDProvider, error) { if err := ValidateIssuer(config.Issuer); err != nil { return nil, err } @@ -120,6 +98,7 @@ func NewDefaultOP(config *Config, storage Storage, opOpts ...DefaultOPOpts) (Ope p := &DefaultOP{ config: config, storage: storage, + signer: signer, endpoints: DefaultEndpoints, } @@ -148,20 +127,6 @@ func (p *DefaultOP) Issuer() string { return p.config.Issuer } -type Endpoint string - -func (e Endpoint) Relative() string { - return relativeEndpoint(string(e)) -} - -func (e Endpoint) Absolute(host string) string { - return absoluteEndpoint(host, string(e)) -} - -func (e Endpoint) Validate() error { - return nil //TODO: -} - func (p *DefaultOP) AuthorizationEndpoint() Endpoint { return p.endpoints.Authorization } @@ -183,7 +148,7 @@ func (p *DefaultOP) HttpHandler() *http.Server { } func (p *DefaultOP) HandleDiscovery(w http.ResponseWriter, r *http.Request) { - utils.MarshalJSON(w, p.discoveryConfig) + Discover(w, p.discoveryConfig) } func (p *DefaultOP) Decoder() *schema.Decoder { @@ -194,13 +159,17 @@ func (p *DefaultOP) Encoder() *schema.Encoder { return p.encoder } -func (p *DefaultOP) Storage() Storage { +func (p *DefaultOP) Storage() u.Storage { return p.storage } -func (p *DefaultOP) Signer() Signer { - // return p.signer - return nil +func (p *DefaultOP) Signe() u.Signer { + return p.signer + // return +} + +func (p *DefaultOP) ErrorHandler() func(w http.ResponseWriter, r *http.Request, authReq *oidc.AuthRequest, err error) { + return AuthRequestError } func (p *DefaultOP) HandleAuthorize(w http.ResponseWriter, r *http.Request) { @@ -270,56 +239,3 @@ func (p *DefaultOP) handleTokenExchange(w http.ResponseWriter, r *http.Request) func (p *DefaultOP) HandleUserinfo(w http.ResponseWriter, r *http.Request) { } - -// func (c *Config) DefaultAndValidate() error { -// if err := ValidateIssuer(c.Issuer); err != nil { -// return err -// } -// if c.AuthorizationEndpoint == "" { -// c.AuthorizationEndpoint = defaultAuthorizationEndpoint -// } -// if c.TokenEndpoint == "" { -// c.TokenEndpoint = defaulTokenEndpoint -// } -// if c.IntrospectionEndpoint == "" { -// c.IntrospectionEndpoint = defaultIntrospectEndpoint -// } -// if c.UserinfoEndpoint == "" { -// c.UserinfoEndpoint = defaultUserinfoEndpoint -// } -// return nil -// } - -func ValidateIssuer(issuer string) error { - if issuer == "" { - return errors.New("missing issuer") - } - u, err := url.Parse(issuer) - if err != nil { - return errors.New("invalid url for issuer") - } - if u.Host == "" { - return errors.New("host for issuer missing") - } - if u.Scheme != "https" { - if !(u.Scheme == "http" && (u.Host == "localhost" || u.Host == "127.0.0.1" || u.Host == "::1" || strings.HasPrefix(u.Host, "localhost:"))) { //TODO: ? - return errors.New("scheme for issuer must be `https`") - } - } - if u.Fragment != "" || len(u.Query()) > 0 { - return errors.New("no fragments or query allowed for issuer") - } - return nil -} - -func (c *Config) absoluteEndpoint(endpoint string) string { - return strings.TrimSuffix(c.Issuer, "/") + relativeEndpoint(endpoint) -} - -func absoluteEndpoint(host, endpoint string) string { - return strings.TrimSuffix(host, "/") + relativeEndpoint(endpoint) -} - -func relativeEndpoint(endpoint string) string { - return "/" + strings.TrimPrefix(endpoint, "/") -} diff --git a/pkg/op/default_op_test.go b/pkg/op/default_op_test.go index ed359a5..76c1a1f 100644 --- a/pkg/op/default_op_test.go +++ b/pkg/op/default_op_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/require" "github.com/caos/oidc/pkg/oidc" + "github.com/caos/oidc/pkg/op/u" ) func TestDefaultOP_HandleDiscovery(t *testing.T) { @@ -15,7 +16,7 @@ func TestDefaultOP_HandleDiscovery(t *testing.T) { config *Config endpoints *endpoints discoveryConfig *oidc.DiscoveryConfiguration - storage Storage + storage u.Storage http *http.Server } type args struct { diff --git a/pkg/op/discovery.go b/pkg/op/discovery.go new file mode 100644 index 0000000..41ad770 --- /dev/null +++ b/pkg/op/discovery.go @@ -0,0 +1,33 @@ +package op + +import ( + "net/http" + + "github.com/caos/oidc/pkg/oidc" + "github.com/caos/oidc/pkg/utils" +) + +func Discover(w http.ResponseWriter, config *oidc.DiscoveryConfiguration) { + utils.MarshalJSON(w, config) +} + +func CreateDiscoveryConfig(c Configuration) *oidc.DiscoveryConfiguration { + return &oidc.DiscoveryConfiguration{ + Issuer: c.Issuer(), + AuthorizationEndpoint: c.AuthorizationEndpoint().Absolute(c.Issuer()), + TokenEndpoint: c.TokenEndpoint().Absolute(c.Issuer()), + // IntrospectionEndpoint: c.Intro().Absolute(c.Issuer()), + UserinfoEndpoint: c.UserinfoEndpoint().Absolute(c.Issuer()), + // EndSessionEndpoint: c.TokenEndpoint().Absolute(c.Issuer())(c.EndSessionEndpoint), + // CheckSessionIframe: c.TokenEndpoint().Absolute(c.Issuer())(c.CheckSessionIframe), + // JwksURI: c.TokenEndpoint().Absolute(c.Issuer())(c.JwksURI), + // ScopesSupported: oidc.SupportedScopes, + // ResponseTypesSupported: responseTypes, + // GrantTypesSupported: oidc.SupportedGrantTypes, + // ClaimsSupported: oidc.SupportedClaims, + // IdTokenSigningAlgValuesSupported: []string{keys.SigningAlgorithm}, + // SubjectTypesSupported: []string{"public"}, + // TokenEndpointAuthMethodsSupported: + + } +} diff --git a/pkg/op/endpoint.go b/pkg/op/endpoint.go new file mode 100644 index 0000000..cc0419c --- /dev/null +++ b/pkg/op/endpoint.go @@ -0,0 +1,25 @@ +package op + +import "strings" + +type Endpoint string + +func (e Endpoint) Relative() string { + return relativeEndpoint(string(e)) +} + +func (e Endpoint) Absolute(host string) string { + return absoluteEndpoint(host, string(e)) +} + +func (e Endpoint) Validate() error { + return nil //TODO: +} + +func absoluteEndpoint(host, endpoint string) string { + return strings.TrimSuffix(host, "/") + relativeEndpoint(endpoint) +} + +func relativeEndpoint(endpoint string) string { + return "/" + strings.TrimPrefix(endpoint, "/") +} diff --git a/pkg/op/go.mod b/pkg/op/go.mod index 3c73f65..418ba38 100644 --- a/pkg/op/go.mod +++ b/pkg/op/go.mod @@ -10,6 +10,8 @@ replace github.com/caos/oidc/pkg/utils => /Users/livio/workspaces/go/src/github. replace github.com/caos/oidc/pkg/op => /Users/livio/workspaces/go/src/github.com/caos/oidc/pkg/op +replace github.com/caos/oidc/pkg/op/u => /Users/livio/workspaces/go/src/github.com/caos/oidc/pkg/op/u + require ( github.com/caos/oidc v0.0.0-20191119072320-6412f213450c github.com/caos/oidc/pkg/oidc v0.0.0-00010101000000-000000000000 diff --git a/pkg/op/go.sum b/pkg/op/go.sum index 844f1fa..afcf798 100644 --- a/pkg/op/go.sum +++ b/pkg/op/go.sum @@ -55,6 +55,7 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= diff --git a/pkg/op/mock/authorizer.mock.go b/pkg/op/mock/authorizer.mock.go new file mode 100644 index 0000000..5c45d72 --- /dev/null +++ b/pkg/op/mock/authorizer.mock.go @@ -0,0 +1,107 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/caos/oidc/pkg/op (interfaces: Authorizer) + +// Package mock is a generated GoMock package. +package mock + +import ( + oidc "github.com/caos/oidc/pkg/oidc" + u "github.com/caos/oidc/pkg/op/u" + gomock "github.com/golang/mock/gomock" + schema "github.com/gorilla/schema" + http "net/http" + reflect "reflect" +) + +// MockAuthorizer is a mock of Authorizer interface +type MockAuthorizer struct { + ctrl *gomock.Controller + recorder *MockAuthorizerMockRecorder +} + +// MockAuthorizerMockRecorder is the mock recorder for MockAuthorizer +type MockAuthorizerMockRecorder struct { + mock *MockAuthorizer +} + +// NewMockAuthorizer creates a new mock instance +func NewMockAuthorizer(ctrl *gomock.Controller) *MockAuthorizer { + mock := &MockAuthorizer{ctrl: ctrl} + mock.recorder = &MockAuthorizerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockAuthorizer) EXPECT() *MockAuthorizerMockRecorder { + return m.recorder +} + +// Decoder mocks base method +func (m *MockAuthorizer) Decoder() *schema.Decoder { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Decoder") + ret0, _ := ret[0].(*schema.Decoder) + return ret0 +} + +// Decoder indicates an expected call of Decoder +func (mr *MockAuthorizerMockRecorder) Decoder() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Decoder", reflect.TypeOf((*MockAuthorizer)(nil).Decoder)) +} + +// Encoder mocks base method +func (m *MockAuthorizer) Encoder() *schema.Encoder { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Encoder") + ret0, _ := ret[0].(*schema.Encoder) + return ret0 +} + +// Encoder indicates an expected call of Encoder +func (mr *MockAuthorizerMockRecorder) Encoder() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Encoder", reflect.TypeOf((*MockAuthorizer)(nil).Encoder)) +} + +// ErrorHandler mocks base method +func (m *MockAuthorizer) ErrorHandler() func(http.ResponseWriter, *http.Request, *oidc.AuthRequest, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ErrorHandler") + ret0, _ := ret[0].(func(http.ResponseWriter, *http.Request, *oidc.AuthRequest, error)) + return ret0 +} + +// ErrorHandler indicates an expected call of ErrorHandler +func (mr *MockAuthorizerMockRecorder) ErrorHandler() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ErrorHandler", reflect.TypeOf((*MockAuthorizer)(nil).ErrorHandler)) +} + +// Signe mocks base method +func (m *MockAuthorizer) Signe() u.Signer { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Signe") + ret0, _ := ret[0].(u.Signer) + return ret0 +} + +// Signe indicates an expected call of Signe +func (mr *MockAuthorizerMockRecorder) Signe() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Signe", reflect.TypeOf((*MockAuthorizer)(nil).Signe)) +} + +// Storage mocks base method +func (m *MockAuthorizer) Storage() u.Storage { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Storage") + ret0, _ := ret[0].(u.Storage) + return ret0 +} + +// Storage indicates an expected call of Storage +func (mr *MockAuthorizerMockRecorder) Storage() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Storage", reflect.TypeOf((*MockAuthorizer)(nil).Storage)) +} diff --git a/pkg/op/mock/authorizer.mock.impl.go b/pkg/op/mock/authorizer.mock.impl.go new file mode 100644 index 0000000..0275dbf --- /dev/null +++ b/pkg/op/mock/authorizer.mock.impl.go @@ -0,0 +1,86 @@ +package mock + +import ( + http "net/http" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/golang/mock/gomock" + "github.com/gorilla/schema" + + oidc "github.com/caos/oidc/pkg/oidc" + "github.com/caos/oidc/pkg/op" + u "github.com/caos/oidc/pkg/op/u" +) + +func NewAuthorizer(t *testing.T) op.Authorizer { + return NewMockAuthorizer(gomock.NewController(t)) +} + +func NewAuthorizerExpectValid(t *testing.T, wantErr bool) op.Authorizer { + m := NewAuthorizer(t) + ExpectDecoder(m) + ExpectEncoder(m) + ExpectSigner(m, t) + ExpectStorage(m, t) + ExpectErrorHandler(m, t, wantErr) + return m +} + +// func NewAuthorizerExpectDecoderFails(t *testing.T) op.Authorizer { +// m := NewAuthorizer(t) +// ExpectDecoderFails(m) +// ExpectEncoder(m) +// ExpectSigner(m, t) +// ExpectStorage(m, t) +// ExpectErrorHandler(m, t) +// return m +// } + +func ExpectDecoder(a op.Authorizer) { + mockA := a.(*MockAuthorizer) + mockA.EXPECT().Decoder().AnyTimes().Return(schema.NewDecoder()) +} + +func ExpectEncoder(a op.Authorizer) { + mockA := a.(*MockAuthorizer) + mockA.EXPECT().Encoder().AnyTimes().Return(schema.NewEncoder()) +} + +func ExpectSigner(a op.Authorizer, t *testing.T) { + mockA := a.(*MockAuthorizer) + mockA.EXPECT().Signe().DoAndReturn( + func() u.Signer { + return &Sig{} + }) +} + +func ExpectErrorHandler(a op.Authorizer, t *testing.T, wantErr bool) { + mockA := a.(*MockAuthorizer) + mockA.EXPECT().ErrorHandler().AnyTimes(). + Return(func(w http.ResponseWriter, r *http.Request, authReq *oidc.AuthRequest, err error) { + if wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + }) +} + +type Sig struct{} + +func (s *Sig) Sign(*oidc.IDTokenClaims) (string, error) { + return "", nil +} + +func ExpectStorage(a op.Authorizer, t *testing.T) { + mockA := a.(*MockAuthorizer) + mockA.EXPECT().Storage().AnyTimes().Return(NewMockStorageAny(t)) +} + +// func NewMockSignerAny(t *testing.T) op.Signer { +// m := NewMockSigner(gomock.NewController(t)) +// m.EXPECT().Sign(gomock.Any()).AnyTimes().Return("", nil) +// return m +// } diff --git a/pkg/op/mock/generate.go b/pkg/op/mock/generate.go index 57b8351..0cf748b 100644 --- a/pkg/op/mock/generate.go +++ b/pkg/op/mock/generate.go @@ -1,3 +1,4 @@ package mock -//go:generate mockgen -package mock -destination ./storage.mock.go github.com/caos/oidc/pkg/op Storage +//go:generate mockgen -package mock -destination ./storage.mock.go github.com/caos/oidc/pkg/op/u Storage +//go:generate mockgen -package mock -destination ./authorizer.mock.go github.com/caos/oidc/pkg/op Authorizer diff --git a/pkg/op/mock/storage.mock.go b/pkg/op/mock/storage.mock.go index a6276b8..32ec1b0 100644 --- a/pkg/op/mock/storage.mock.go +++ b/pkg/op/mock/storage.mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/caos/oidc/pkg/op (interfaces: Storage) +// Source: github.com/caos/oidc/pkg/op/u (interfaces: Storage) // Package mock is a generated GoMock package. package mock @@ -48,6 +48,21 @@ func (mr *MockStorageMockRecorder) AuthRequestByCode(arg0, arg1, arg2 interface{ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthRequestByCode", reflect.TypeOf((*MockStorage)(nil).AuthRequestByCode), arg0, arg1, arg2) } +// AuthRequestByID mocks base method +func (m *MockStorage) AuthRequestByID(arg0 string) (*oidc.AuthRequest, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AuthRequestByID", arg0) + ret0, _ := ret[0].(*oidc.AuthRequest) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AuthRequestByID indicates an expected call of AuthRequestByID +func (mr *MockStorageMockRecorder) AuthRequestByID(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthRequestByID", reflect.TypeOf((*MockStorage)(nil).AuthRequestByID), arg0) +} + // AuthorizeClientIDCodeVerifier mocks base method func (m *MockStorage) AuthorizeClientIDCodeVerifier(arg0, arg1 string) (oidc.Client, error) { m.ctrl.T.Helper() diff --git a/pkg/op/mock/storage.mock.impl.go b/pkg/op/mock/storage.mock.impl.go index ca21159..811b6e0 100644 --- a/pkg/op/mock/storage.mock.impl.go +++ b/pkg/op/mock/storage.mock.impl.go @@ -4,30 +4,29 @@ import ( "errors" "testing" - "github.com/caos/oidc/pkg/oidc" - "github.com/golang/mock/gomock" - "github.com/caos/oidc/pkg/op" + "github.com/caos/oidc/pkg/oidc" + u "github.com/caos/oidc/pkg/op/u" ) -func NewStorage(t *testing.T) op.Storage { +func NewStorage(t *testing.T) u.Storage { return NewMockStorage(gomock.NewController(t)) } -func NewMockStorageExpectValidClientID(t *testing.T) op.Storage { +func NewMockStorageExpectValidClientID(t *testing.T) u.Storage { m := NewStorage(t) ExpectValidClientID(m) return m } -func NewMockStorageExpectInvalidClientID(t *testing.T) op.Storage { +func NewMockStorageExpectInvalidClientID(t *testing.T) u.Storage { m := NewStorage(t) ExpectInvalidClientID(m) return m } -func NewMockStorageAny(t *testing.T) op.Storage { +func NewMockStorageAny(t *testing.T) u.Storage { m := NewStorage(t) mockS := m.(*MockStorage) mockS.EXPECT().GetClientByClientID(gomock.Any()).AnyTimes().Return(&ConfClient{}, nil) @@ -35,12 +34,12 @@ func NewMockStorageAny(t *testing.T) op.Storage { return m } -func ExpectInvalidClientID(s op.Storage) { +func ExpectInvalidClientID(s u.Storage) { mockS := s.(*MockStorage) mockS.EXPECT().GetClientByClientID(gomock.Any()).Return(nil, errors.New("client not found")) } -func ExpectValidClientID(s op.Storage) { +func ExpectValidClientID(s u.Storage) { mockS := s.(*MockStorage) mockS.EXPECT().GetClientByClientID(gomock.Any()).DoAndReturn( func(id string) (oidc.Client, error) { diff --git a/pkg/op/op.go b/pkg/op/op.go index e3f5f70..8d4c36f 100644 --- a/pkg/op/op.go +++ b/pkg/op/op.go @@ -2,7 +2,10 @@ package op import ( "context" + "errors" "net/http" + "net/url" + "strings" "github.com/gorilla/mux" @@ -22,6 +25,28 @@ type OpenIDProvider interface { HttpHandler() *http.Server } +func ValidateIssuer(issuer string) error { + if issuer == "" { + return errors.New("missing issuer") + } + u, err := url.Parse(issuer) + if err != nil { + return errors.New("invalid url for issuer") + } + if u.Host == "" { + return errors.New("host for issuer missing") + } + if u.Scheme != "https" { + if !(u.Scheme == "http" && (u.Host == "localhost" || u.Host == "127.0.0.1" || u.Host == "::1" || strings.HasPrefix(u.Host, "localhost:"))) { //TODO: ? + return errors.New("scheme for issuer must be `https`") + } + } + if u.Fragment != "" || len(u.Query()) > 0 { + return errors.New("no fragments or query allowed for issuer") + } + return nil +} + func CreateRouter(o OpenIDProvider) *mux.Router { router := mux.NewRouter() router.HandleFunc(oidc.DiscoveryEndpoint, o.HandleDiscovery) diff --git a/pkg/op/tokenrequest.go b/pkg/op/tokenrequest.go index 74594cb..cd99aab 100644 --- a/pkg/op/tokenrequest.go +++ b/pkg/op/tokenrequest.go @@ -5,6 +5,7 @@ import ( "net/http" "time" + "github.com/caos/oidc/pkg/op/u" "github.com/caos/oidc/pkg/utils" "github.com/gorilla/schema" @@ -23,7 +24,7 @@ import ( // return ParseTokenExchangeRequest(w, r) // } -func CodeExchange(w http.ResponseWriter, r *http.Request, storage Storage, decoder *schema.Decoder) { +func CodeExchange(w http.ResponseWriter, r *http.Request, storage u.Storage, decoder *schema.Decoder) { err := r.ParseForm() if err != nil { ExchangeRequestError(w, r, ErrInvalidRequest("error parsing form")) @@ -78,11 +79,7 @@ func CreateAccessToken() (string, error) { return "accessToken", nil } -type Signer interface { - Sign(claims *oidc.IDTokenClaims) (string, error) -} - -func CreateIDToken(authReq *oidc.AuthRequest, atHash string, signer Signer) (string, error) { +func CreateIDToken(authReq *oidc.AuthRequest, atHash string, signer u.Signer) (string, error) { var issuer, sub, acr string var aud, amr []string var exp, iat, authTime time.Time @@ -103,7 +100,7 @@ func CreateIDToken(authReq *oidc.AuthRequest, atHash string, signer Signer) (str return signer.Sign(claims) } -func AuthorizeClient(r *http.Request, tokenReq *oidc.AccessTokenRequest, storage Storage) (oidc.Client, error) { +func AuthorizeClient(r *http.Request, tokenReq *oidc.AccessTokenRequest, storage u.Storage) (oidc.Client, error) { if tokenReq.ClientID == "" { clientID, clientSecret, ok := r.BasicAuth() if ok { @@ -124,7 +121,7 @@ func ParseTokenExchangeRequest(w http.ResponseWriter, r *http.Request) (oidc.Tok return nil, errors.New("Unimplemented") //TODO: impl } -func ValidateTokenExchangeRequest(tokenReq oidc.TokenRequest, storage Storage) error { +func ValidateTokenExchangeRequest(tokenReq oidc.TokenRequest, storage u.Storage) error { return errors.New("Unimplemented") //TODO: impl } diff --git a/pkg/op/u/signer.go b/pkg/op/u/signer.go new file mode 100644 index 0000000..516d768 --- /dev/null +++ b/pkg/op/u/signer.go @@ -0,0 +1,9 @@ +package u + +import ( + "github.com/caos/oidc/pkg/oidc" +) + +type Signer interface { + Sign(claims *oidc.IDTokenClaims) (string, error) +} diff --git a/pkg/op/storage.go b/pkg/op/u/storage.go similarity index 97% rename from pkg/op/storage.go rename to pkg/op/u/storage.go index 5fcf652..a446ce6 100644 --- a/pkg/op/storage.go +++ b/pkg/op/u/storage.go @@ -1,4 +1,4 @@ -package op +package u import "github.com/caos/oidc/pkg/oidc"