This commit is contained in:
Livio Amstutz 2019-11-28 12:14:14 +01:00
parent 10d671956a
commit 80eeee2de2
19 changed files with 422 additions and 157 deletions

View file

@ -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
}

View file

@ -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)
}

View file

@ -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 {
}

View file

@ -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)
})
}
}

View file

@ -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, "/")
}

View file

@ -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 {

33
pkg/op/discovery.go Normal file
View file

@ -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:
}
}

25
pkg/op/endpoint.go Normal file
View file

@ -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, "/")
}

View file

@ -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

View file

@ -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=

View file

@ -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))
}

View file

@ -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
// }

View file

@ -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

View file

@ -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()

View file

@ -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) {

View file

@ -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)

View file

@ -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
}

9
pkg/op/u/signer.go Normal file
View file

@ -0,0 +1,9 @@
package u
import (
"github.com/caos/oidc/pkg/oidc"
)
type Signer interface {
Sign(claims *oidc.IDTokenClaims) (string, error)
}

View file

@ -1,4 +1,4 @@
package op
package u
import "github.com/caos/oidc/pkg/oidc"