From 85814fb69a51cb84aa1abf45dafd31a72449cbe3 Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Tue, 10 Dec 2019 14:50:39 +0100 Subject: [PATCH] userinfo and more --- example/internal/mock/storage.go | 67 ++++++--- pkg/oidc/userinfo.go | 233 ++++++++++++++++++++++++++++++ pkg/op/config.go | 1 - pkg/op/default_op.go | 6 +- pkg/op/discovery.go | 91 ++++++++---- pkg/op/discovery_test.go | 196 ++++++++++++++----------- pkg/op/mock/configuration.mock.go | 132 +++++++++++++++++ pkg/op/mock/generate.go | 2 + pkg/op/mock/signer.mock.go | 64 ++++++++ pkg/op/mock/storage.mock.go | 15 ++ pkg/op/storage.go | 1 + pkg/op/userinfo.go | 28 ++++ 12 files changed, 702 insertions(+), 134 deletions(-) create mode 100644 pkg/oidc/userinfo.go create mode 100644 pkg/op/mock/configuration.mock.go create mode 100644 pkg/op/mock/signer.mock.go create mode 100644 pkg/op/userinfo.go diff --git a/example/internal/mock/storage.go b/example/internal/mock/storage.go index 4e8ab7c..385408a 100644 --- a/example/internal/mock/storage.go +++ b/example/internal/mock/storage.go @@ -75,7 +75,7 @@ func (a *AuthRequest) GetNonce() string { } func (a *AuthRequest) GetRedirectURI() string { - return "https://op.certification.openid.net:62054/authz_cb" + return a.RedirectURI // return "http://localhost:5556/auth/callback" } @@ -104,7 +104,7 @@ var ( ) func (s *AuthStorage) CreateAuthRequest(authReq *oidc.AuthRequest) (op.AuthRequest, error) { - a = &AuthRequest{ID: "id", ClientID: authReq.ClientID, ResponseType: authReq.ResponseType, Nonce: authReq.Nonce} + a = &AuthRequest{ID: "id", ClientID: authReq.ClientID, ResponseType: authReq.ResponseType, Nonce: authReq.Nonce, RedirectURI: authReq.RedirectURI} return a, nil } func (s *OPStorage) GetClientByClientID(id string) (op.Client, error) { @@ -134,21 +134,7 @@ func (s *AuthStorage) DeleteAuthRequestAndCode(string, string) error { return nil } func (s *AuthStorage) AuthRequestByID(id string) (op.AuthRequest, error) { - if id == "none" { - return nil, errors.New("not found") - } - var responseType oidc.ResponseType - if id == "code" { - responseType = oidc.ResponseTypeCode - } else if id == "id" { - responseType = oidc.ResponseTypeIDTokenOnly - } else { - responseType = oidc.ResponseTypeIDToken - } - return &AuthRequest{ - ResponseType: responseType, - RedirectURI: "/callback", - }, nil + return a, nil } func (s *AuthStorage) GetSigningKey() (*jose.SigningKey, error) { @@ -166,6 +152,49 @@ func (s *AuthStorage) GetKeySet() (jose.JSONWebKeySet, error) { }, nil } +func (s *OPStorage) GetUserinfoFromScopes([]string) (interface{}, error) { + return &oidc.Test{ + Userinfo: oidc.Userinfo{ + Subject: a.GetSubject(), + Address: &oidc.UserinfoAddress{ + StreetAddress: "Hjkhkj 789\ndsf", + }, + UserinfoEmail: oidc.UserinfoEmail{ + Email: "test", + EmailVerified: true, + }, + UserinfoPhone: oidc.UserinfoPhone{ + PhoneNumber: "sadsa", + PhoneNumberVerified: true, + }, + UserinfoProfile: oidc.UserinfoProfile{ + UpdatedAt: time.Now(), + }, + // Claims: map[string]interface{}{ + // "test": "test", + // "hkjh": "", + // }, + }, + Add: "jkhnkj", + }, nil +} + +type info struct { + Subject string +} + +func (i *info) GetSubject() string { + return i.Subject +} + +func (i *info) Claims() map[string]interface{} { + return map[string]interface{}{ + "hodor": "hoidoir", + "email": "asdfd", + "emailVerfied": true, + } +} + type ConfClient struct { applicationType op.ApplicationType } @@ -177,8 +206,8 @@ func (c *ConfClient) RedirectURIs() []string { "http://localhost:5556/auth/callback", "custom://callback", "https://localhost:8443/test/a/instructions-example/callback", - "https://op.certification.openid.net:62054/authz_cb", - "https://op.certification.openid.net:62054/authz_post", + "https://op.certification.openid.net:62064/authz_cb", + "https://op.certification.openid.net:62064/authz_post", } } diff --git a/pkg/oidc/userinfo.go b/pkg/oidc/userinfo.go new file mode 100644 index 0000000..f2a399c --- /dev/null +++ b/pkg/oidc/userinfo.go @@ -0,0 +1,233 @@ +package oidc + +import ( + "encoding/json" + "time" +) + +type Test struct { + Userinfo + Add string `json:"add,omitempty"` +} + +type Userinfo struct { + Subject string + Address *UserinfoAddress + UserinfoProfile + UserinfoEmail + UserinfoPhone + + claims map[string]interface{} +} + +// type UserinfoJSON struct { +// Subject string `json:"subject,omitempty"` +// Address *UserinfoAddress `json:"address,omitempty"` +// Email string `json:"email,omitempty"` +// EmailVerified bool `json:"email_verified,omitempty"` +// UserinfoProfileJSON +// PhoneNumber string `json:"phone_number,omitempty"` +// PhoneNumberVerified bool `json:"phone_number_verified,omitempty"` + +// Claims map[string]interface{} `json:",omitempty"` +// } + +// type Claims map[string]interface{} + +type UserinfoPhone struct { + PhoneNumber string + PhoneNumberVerified bool +} +type UserinfoProfile struct { + Name string + GivenName string + FamilyName string + MiddleName string + Nickname string + Profile string + Picture string + Website string + Gender Gender + Birthdate string + Zoneinfo string + // Locale language.Tag + UpdatedAt time.Time + PreferredUsername string +} + +// func (i *UserinfoProfile) MarshalJSON() ([]byte, error) { +// j := new(UserinfoProfileJSON) +// j.UpdatedAt = i.UpdatedAt +// return json.Marshal(j) +// } + +type UserinfoProfileJSON struct { + Name string `json:"name,omitempty"` + GivenName string `json:"given_name,omitempty"` + FamilyName string `json:"family_name,omitempty"` + MiddleName string `json:"middle_name,omitempty"` + Nickname string `json:"nickname,omitempty"` + Profile string `json:"profile,omitempty"` + Picture string `json:"picture,omitempty"` + Website string `json:"website,omitempty"` + Gender Gender `json:"gender,omitempty"` + Birthdate string `json:"birthdate,omitempty"` + Zoneinfo string `json:"zoneinfo,omitempty"` + // Locale language.Tag `json:"locale,omitempty"` + UpdatedAt int64 `json:"updated_at,omitempty"` + PreferredUsername string `json:"preferred_username,omitempty"` +} + +type Gender string + +type UserinfoAddress struct { + Formatted string + StreetAddress string + Locality string + Region string + PostalCode string + Country string +} + +type UserinfoEmail struct { + Email string + EmailVerified bool +} + +func marshalUserinfoProfile(i UserinfoProfile, claims map[string]interface{}) { + claims["updated_at"] = i.UpdatedAt.UTC().Unix() +} + +func marshalUserinfoEmail(i UserinfoEmail, claims map[string]interface{}) { + if i.Email != "" { + claims["email"] = i.Email + } + if i.EmailVerified { + claims["email_verified"] = i.EmailVerified + } +} + +func marshalUserinfoAddress(i *UserinfoAddress, claims map[string]interface{}) { + if i == nil { + return + } + address := make(map[string]interface{}) + if i.Formatted != "" { + address["formatted"] = i.Formatted + } + if i.StreetAddress != "" { + address["street_address"] = i.StreetAddress + } + claims["address"] = address + // claims["email_verified"] = i.EmailVerified +} + +func marshalUserinfoPhone(i UserinfoPhone, claims map[string]interface{}) { + claims["phone_number"] = i.PhoneNumber + claims["phone_number_verified"] = i.PhoneNumberVerified +} + +// func copyClaims(j *Claims, i map[string]interface{}) { +// // *j, _ = json.Marshal(i) +// } + +// func (p Userinfo) MarshalJSON() ([]byte, error) { +// j := new(UserinfoJSON) +// j.Subject = p.Subject +// b, _ := json.Marshal(j) + +// var m map[string]json.RawMessage +// json.Unmarshal(b, &m) + +// // Add tags to the map, possibly overriding struct fields +// // for k, v := range p.Claims { +// // // if overriding struct fields is not acceptable: +// // // if _, ok := m[k]; ok { continue } +// // b, _ = json.Marshal(v) +// // ms[k] = json.RawMessage(b) +// // } + +// return json.Marshal(m) +// } + +func (i *Userinfo) MarshalJSON() ([]byte, error) { + claims := i.claims + if claims == nil { + claims = make(map[string]interface{}) + } + // j := new(UserinfoJSON) + // j.Subject = i.Subject + // j.Address = i.Address + // j.Email = i.Email + // j.EmailVerified = i.EmailVerified + // j.PhoneNumber = i.PhoneNumber + // j.PhoneNumberVerified = i.PhoneNumberVerified + + // j.Claims = make(map[string]interface{}) + // claims := map[string]interface{}{ + // "sdsa": "jajfi", + // "a23r": "", + // } + + // j.Claims["Sdsa"] = "sads" + // j.Claims["3454"] = "" + + // st := new(structpb.Struct) + // b, _ := json.Marshal(j) + // err := jsonpb.Unmarshal(bytes.NewReader(b), st) + // if err != nil { + // return nil, err + // } + // fmt.Println("st", st) + // m := new(jsonpb.Marshaler) + // m.EmitDefaults = false + // s, _ := m.MarshalToString(st) + // return []byte(s), nil + + // claims["phone_number"] = i.PhoneNumber + claims["sub"] = i.Subject + marshalUserinfoAddress(i.Address, claims) + marshalUserinfoEmail(i.UserinfoEmail, claims) + marshalUserinfoPhone(i.UserinfoPhone, claims) + marshalUserinfoProfile(i.UserinfoProfile, claims) + + // for k, v := range claims { + // j.Claims[k] = v + // } + // j.Claims = Claims(m) + // copyClaims(&j.Claims, i.Claims) + // j.Claims, _ = json.Marshal(i.Claims) + + // j.Subject = i.Subject + + // if j.Claims == nil { + // j.Claims = make(map[string]interface{}) + // } + // j.Claims["sub"] = i.Subject + // if i.Address != nil { + // j.Claims["address"] = i.Address + // } + // if i.Email != "" { + // j.Claims["email"] = i.Email + // } + // if i.EmailVerified { + // j.Claims["email_verified"] = i.EmailVerified + // } + // if i.PhoneNumber != "" { + // j.Claims["phone_number"] = i.PhoneNumber + // } + // if i.PhoneNumberVerified { + // j.Claims["phone_number_verified"] = i.PhoneNumberVerified + // } + // if !i.UpdatedAt.IsZero() { + // j.Claims["updated_at"] = i.UpdatedAt.UTC().Unix() + // } + return json.Marshal(claims) +} + +func (i *Userinfo) UnmmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, i); err != nil { + return err + } + return json.Unmarshal(data, i.claims) +} diff --git a/pkg/op/config.go b/pkg/op/config.go index ee9a107..9ebfc1c 100644 --- a/pkg/op/config.go +++ b/pkg/op/config.go @@ -14,7 +14,6 @@ type Configuration interface { KeysEndpoint() Endpoint // SupportedScopes() []string - AuthMethodBasicSupported() bool AuthMethodPostSupported() bool Port() string diff --git a/pkg/op/default_op.go b/pkg/op/default_op.go index 1890620..7f832a5 100644 --- a/pkg/op/default_op.go +++ b/pkg/op/default_op.go @@ -16,8 +16,8 @@ const ( defaultUserinfoEndpoint = "userinfo" defaultKeysEndpoint = "keys" - authMethodBasic = "client_secret_basic" - authMethodPost = "client_secret_post" + AuthMethodBasic = "client_secret_basic" + AuthMethodPost = "client_secret_post" DefaultIDTokenValidity = time.Duration(5 * time.Minute) ) @@ -251,5 +251,5 @@ func (p *DefaultOP) handleTokenExchange(w http.ResponseWriter, r *http.Request) } func (p *DefaultOP) HandleUserinfo(w http.ResponseWriter, r *http.Request) { - w.Write([]byte("ok")) + Userinfo(w, r, p) } diff --git a/pkg/op/discovery.go b/pkg/op/discovery.go index 7c7eae5..9a3d97e 100644 --- a/pkg/op/discovery.go +++ b/pkg/op/discovery.go @@ -20,27 +20,38 @@ func CreateDiscoveryConfig(c Configuration, s Signer) *oidc.DiscoveryConfigurati UserinfoEndpoint: c.UserinfoEndpoint().Absolute(c.Issuer()), // EndSessionEndpoint: c.TokenEndpoint().Absolute(c.Issuer())(c.EndSessionEndpoint), // CheckSessionIframe: c.TokenEndpoint().Absolute(c.Issuer())(c.CheckSessionIframe), - JwksURI: c.KeysEndpoint().Absolute(c.Issuer()), - ScopesSupported: scopes(c), - ResponseTypesSupported: responseTypes(c), - GrantTypesSupported: grantTypes(c), - // ClaimsSupported: oidc.SupportedClaims, - IDTokenSigningAlgValuesSupported: sigAlgorithms(s), - SubjectTypesSupported: subjectTypes(c), - TokenEndpointAuthMethodsSupported: authMethods(c.AuthMethodBasicSupported(), c.AuthMethodPostSupported()), + JwksURI: c.KeysEndpoint().Absolute(c.Issuer()), + ScopesSupported: Scopes(c), + ResponseTypesSupported: ResponseTypes(c), + GrantTypesSupported: GrantTypes(c), + ClaimsSupported: SupportedClaims(c), + IDTokenSigningAlgValuesSupported: SigAlgorithms(s), + SubjectTypesSupported: SubjectTypes(c), + TokenEndpointAuthMethodsSupported: AuthMethods(c), } } -func scopes(c Configuration) []string { - return []string{ - "openid", - "profile", - "email", - "phone", - } //TODO: config +const ( + ScopeOpenID = "openid" + ScopeProfile = "profile" + ScopeEmail = "email" + ScopePhone = "phone" + ScopeAddress = "address" +) + +var DefaultSupportedScopes = []string{ + ScopeOpenID, + ScopeProfile, + ScopeEmail, + ScopePhone, + ScopeAddress, } -func responseTypes(c Configuration) []string { +func Scopes(c Configuration) []string { + return DefaultSupportedScopes //TODO: config +} + +func ResponseTypes(c Configuration) []string { return []string{ "code", "id_token", @@ -51,7 +62,7 @@ func responseTypes(c Configuration) []string { } } -func grantTypes(c Configuration) []string { +func GrantTypes(c Configuration) []string { return []string{ "client_credentials", "authorization_code", @@ -60,23 +71,49 @@ func grantTypes(c Configuration) []string { } } -func sigAlgorithms(s Signer) []string { +func SupportedClaims(c Configuration) []string { + return []string{ //TODO: config + "sub", + "aud", + "exp", + "iat", + "iss", + "auth_time", + "nonce", + "acr", + "amr", + "c_hash", + "at_hash", + "act", + "scopes", + "client_id", + "azp", + "preferred_username", + "name", + "family_name", + "given_name", + "locale", + "email", + "email_verified", + "phone_number", + "phone_number_verified", + } +} + +func SigAlgorithms(s Signer) []string { return []string{string(s.SignatureAlgorithm())} } -func subjectTypes(c Configuration) []string { +func SubjectTypes(c Configuration) []string { return []string{"public"} //TODO: config } -func authMethods(basic, post bool) []string { - authMethods := make([]string, 0, 2) - if basic { - // if c.AuthMethodBasicSupported() { - authMethods = append(authMethods, authMethodBasic) +func AuthMethods(c Configuration) []string { + authMethods := []string{ + AuthMethodBasic, } - if post { - // if c.AuthMethodPostSupported() { - authMethods = append(authMethods, authMethodPost) + if c.AuthMethodPostSupported() { + authMethods = append(authMethods, AuthMethodPost) } return authMethods } diff --git a/pkg/op/discovery_test.go b/pkg/op/discovery_test.go index d4ed4f1..59e9daa 100644 --- a/pkg/op/discovery_test.go +++ b/pkg/op/discovery_test.go @@ -1,4 +1,4 @@ -package op +package op_test import ( "net/http" @@ -6,9 +6,12 @@ import ( "reflect" "testing" - "github.com/stretchr/testify/require" - "github.com/caos/oidc/pkg/oidc" + "github.com/caos/oidc/pkg/op" + "github.com/caos/oidc/pkg/op/mock" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + "gopkg.in/square/go-jose.v2" ) func TestDiscover(t *testing.T) { @@ -30,7 +33,7 @@ func TestDiscover(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - Discover(tt.args.w, tt.args.config) + op.Discover(tt.args.w, tt.args.config) rec := tt.args.w.(*httptest.ResponseRecorder) require.Equal(t, http.StatusOK, rec.Code) require.Equal(t, `{"issuer":"https://issuer.com"}`, rec.Body.String()) @@ -40,8 +43,8 @@ func TestDiscover(t *testing.T) { func TestCreateDiscoveryConfig(t *testing.T) { type args struct { - c Configuration - s Signer + c op.Configuration + s op.Signer } tests := []struct { name string @@ -52,7 +55,7 @@ func TestCreateDiscoveryConfig(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := CreateDiscoveryConfig(tt.args.c, tt.args.s); !reflect.DeepEqual(got, tt.want) { + if got := op.CreateDiscoveryConfig(tt.args.c, tt.args.s); !reflect.DeepEqual(got, tt.want) { t.Errorf("CreateDiscoveryConfig() = %v, want %v", got, tt.want) } }) @@ -61,27 +64,31 @@ func TestCreateDiscoveryConfig(t *testing.T) { func Test_scopes(t *testing.T) { type args struct { - c Configuration + c op.Configuration } tests := []struct { name string args args want []string }{ - // TODO: Add test cases. + { + "default Scopes", + args{}, + op.DefaultSupportedScopes, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := scopes(tt.args.c); !reflect.DeepEqual(got, tt.want) { + if got := op.Scopes(tt.args.c); !reflect.DeepEqual(got, tt.want) { t.Errorf("scopes() = %v, want %v", got, tt.want) } }) } } -func Test_responseTypes(t *testing.T) { +func Test_ResponseTypes(t *testing.T) { type args struct { - c Configuration + c op.Configuration } tests := []struct { name string @@ -92,16 +99,16 @@ func Test_responseTypes(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := responseTypes(tt.args.c); !reflect.DeepEqual(got, tt.want) { + if got := op.ResponseTypes(tt.args.c); !reflect.DeepEqual(got, tt.want) { t.Errorf("responseTypes() = %v, want %v", got, tt.want) } }) } } -func Test_grantTypes(t *testing.T) { +func Test_GrantTypes(t *testing.T) { type args struct { - c Configuration + c op.Configuration } tests := []struct { name string @@ -112,64 +119,64 @@ func Test_grantTypes(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := grantTypes(tt.args.c); !reflect.DeepEqual(got, tt.want) { + if got := op.GrantTypes(tt.args.c); !reflect.DeepEqual(got, tt.want) { t.Errorf("grantTypes() = %v, want %v", got, tt.want) } }) } } -// func Test_sigAlgorithms(t *testing.T) { -// type args struct { -// s Signer -// } -// tests := []struct { -// name string -// args args -// want []string -// }{ -// { -// "", -// args{}, -// []string{"RS256"}, -// }, -// } -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// if got := sigAlgorithms(tt.args.s); !reflect.DeepEqual(got, tt.want) { -// t.Errorf("sigAlgorithms() = %v, want %v", got, tt.want) -// } -// }) -// } -// } - -// func Test_subjectTypes(t *testing.T) { -// type args struct { -// c Configuration -// } -// tests := []struct { -// name string -// args args -// want []string -// }{ -// { -// "none", -// args{func()} -// } -// } -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// if got := subjectTypes(tt.args.c); !reflect.DeepEqual(got, tt.want) { -// t.Errorf("subjectTypes() = %v, want %v", got, tt.want) -// } -// }) -// } -// } - -func Test_authMethods(t *testing.T) { +func TestSupportedClaims(t *testing.T) { type args struct { - basic bool - post bool + c op.Configuration + } + tests := []struct { + name string + args args + want []string + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := op.SupportedClaims(tt.args.c); !reflect.DeepEqual(got, tt.want) { + t.Errorf("SupportedClaims() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_SigAlgorithms(t *testing.T) { + m := mock.NewMockSigner(gomock.NewController((t))) + type args struct { + s op.Signer + } + tests := []struct { + name string + args args + want []string + }{ + { + "", + args{func() op.Signer { + m.EXPECT().SignatureAlgorithm().Return(jose.RS256) + return m + }()}, + []string{"RS256"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := op.SigAlgorithms(tt.args.s); !reflect.DeepEqual(got, tt.want) { + t.Errorf("sigAlgorithms() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_SubjectTypes(t *testing.T) { + type args struct { + c op.Configuration } tests := []struct { name string @@ -178,28 +185,49 @@ func Test_authMethods(t *testing.T) { }{ { "none", - args{false, false}, - []string{}, - }, - { - "basic", - args{true, false}, - []string{authMethodBasic}, - }, - { - "post", - args{false, true}, - []string{authMethodPost}, - }, - { - "basic and post", - args{true, true}, - []string{authMethodBasic, authMethodPost}, + args{}, + []string{"public"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := authMethods(tt.args.basic, tt.args.post); !reflect.DeepEqual(got, tt.want) { + if got := op.SubjectTypes(tt.args.c); !reflect.DeepEqual(got, tt.want) { + t.Errorf("subjectTypes() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_AuthMethods(t *testing.T) { + m := mock.NewMockConfiguration(gomock.NewController((t))) + type args struct { + c op.Configuration + } + tests := []struct { + name string + args args + want []string + }{ + { + "imlicit basic", + args{func() op.Configuration { + m.EXPECT().AuthMethodPostSupported().Return(false) + return m + }()}, + []string{op.AuthMethodBasic}, + }, + { + "basic and post", + args{func() op.Configuration { + m.EXPECT().AuthMethodPostSupported().Return(true) + return m + }()}, + []string{op.AuthMethodBasic, op.AuthMethodPost}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := op.AuthMethods(tt.args.c); !reflect.DeepEqual(got, tt.want) { t.Errorf("authMethods() = %v, want %v", got, tt.want) } }) diff --git a/pkg/op/mock/configuration.mock.go b/pkg/op/mock/configuration.mock.go new file mode 100644 index 0000000..7148c6d --- /dev/null +++ b/pkg/op/mock/configuration.mock.go @@ -0,0 +1,132 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/caos/oidc/pkg/op (interfaces: Configuration) + +// Package mock is a generated GoMock package. +package mock + +import ( + op "github.com/caos/oidc/pkg/op" + gomock "github.com/golang/mock/gomock" + reflect "reflect" +) + +// MockConfiguration is a mock of Configuration interface +type MockConfiguration struct { + ctrl *gomock.Controller + recorder *MockConfigurationMockRecorder +} + +// MockConfigurationMockRecorder is the mock recorder for MockConfiguration +type MockConfigurationMockRecorder struct { + mock *MockConfiguration +} + +// NewMockConfiguration creates a new mock instance +func NewMockConfiguration(ctrl *gomock.Controller) *MockConfiguration { + mock := &MockConfiguration{ctrl: ctrl} + mock.recorder = &MockConfigurationMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockConfiguration) EXPECT() *MockConfigurationMockRecorder { + return m.recorder +} + +// AuthMethodPostSupported mocks base method +func (m *MockConfiguration) AuthMethodPostSupported() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AuthMethodPostSupported") + ret0, _ := ret[0].(bool) + return ret0 +} + +// AuthMethodPostSupported indicates an expected call of AuthMethodPostSupported +func (mr *MockConfigurationMockRecorder) AuthMethodPostSupported() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthMethodPostSupported", reflect.TypeOf((*MockConfiguration)(nil).AuthMethodPostSupported)) +} + +// AuthorizationEndpoint mocks base method +func (m *MockConfiguration) AuthorizationEndpoint() op.Endpoint { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AuthorizationEndpoint") + ret0, _ := ret[0].(op.Endpoint) + return ret0 +} + +// AuthorizationEndpoint indicates an expected call of AuthorizationEndpoint +func (mr *MockConfigurationMockRecorder) AuthorizationEndpoint() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthorizationEndpoint", reflect.TypeOf((*MockConfiguration)(nil).AuthorizationEndpoint)) +} + +// Issuer mocks base method +func (m *MockConfiguration) Issuer() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Issuer") + ret0, _ := ret[0].(string) + return ret0 +} + +// Issuer indicates an expected call of Issuer +func (mr *MockConfigurationMockRecorder) Issuer() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Issuer", reflect.TypeOf((*MockConfiguration)(nil).Issuer)) +} + +// KeysEndpoint mocks base method +func (m *MockConfiguration) KeysEndpoint() op.Endpoint { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "KeysEndpoint") + ret0, _ := ret[0].(op.Endpoint) + return ret0 +} + +// KeysEndpoint indicates an expected call of KeysEndpoint +func (mr *MockConfigurationMockRecorder) KeysEndpoint() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "KeysEndpoint", reflect.TypeOf((*MockConfiguration)(nil).KeysEndpoint)) +} + +// Port mocks base method +func (m *MockConfiguration) Port() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Port") + ret0, _ := ret[0].(string) + return ret0 +} + +// Port indicates an expected call of Port +func (mr *MockConfigurationMockRecorder) Port() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Port", reflect.TypeOf((*MockConfiguration)(nil).Port)) +} + +// TokenEndpoint mocks base method +func (m *MockConfiguration) TokenEndpoint() op.Endpoint { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TokenEndpoint") + ret0, _ := ret[0].(op.Endpoint) + return ret0 +} + +// TokenEndpoint indicates an expected call of TokenEndpoint +func (mr *MockConfigurationMockRecorder) TokenEndpoint() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TokenEndpoint", reflect.TypeOf((*MockConfiguration)(nil).TokenEndpoint)) +} + +// UserinfoEndpoint mocks base method +func (m *MockConfiguration) UserinfoEndpoint() op.Endpoint { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UserinfoEndpoint") + ret0, _ := ret[0].(op.Endpoint) + return ret0 +} + +// UserinfoEndpoint indicates an expected call of UserinfoEndpoint +func (mr *MockConfigurationMockRecorder) UserinfoEndpoint() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserinfoEndpoint", reflect.TypeOf((*MockConfiguration)(nil).UserinfoEndpoint)) +} diff --git a/pkg/op/mock/generate.go b/pkg/op/mock/generate.go index 869be81..beb3132 100644 --- a/pkg/op/mock/generate.go +++ b/pkg/op/mock/generate.go @@ -3,3 +3,5 @@ package mock //go:generate mockgen -package mock -destination ./storage.mock.go github.com/caos/oidc/pkg/op Storage //go:generate mockgen -package mock -destination ./authorizer.mock.go github.com/caos/oidc/pkg/op Authorizer //go:generate mockgen -package mock -destination ./client.mock.go github.com/caos/oidc/pkg/op Client +//go:generate mockgen -package mock -destination ./configuration.mock.go github.com/caos/oidc/pkg/op Configuration +//go:generate mockgen -package mock -destination ./signer.mock.go github.com/caos/oidc/pkg/op Signer diff --git a/pkg/op/mock/signer.mock.go b/pkg/op/mock/signer.mock.go new file mode 100644 index 0000000..d9f6613 --- /dev/null +++ b/pkg/op/mock/signer.mock.go @@ -0,0 +1,64 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/caos/oidc/pkg/op (interfaces: Signer) + +// Package mock is a generated GoMock package. +package mock + +import ( + oidc "github.com/caos/oidc/pkg/oidc" + gomock "github.com/golang/mock/gomock" + go_jose_v2 "gopkg.in/square/go-jose.v2" + reflect "reflect" +) + +// MockSigner is a mock of Signer interface +type MockSigner struct { + ctrl *gomock.Controller + recorder *MockSignerMockRecorder +} + +// MockSignerMockRecorder is the mock recorder for MockSigner +type MockSignerMockRecorder struct { + mock *MockSigner +} + +// NewMockSigner creates a new mock instance +func NewMockSigner(ctrl *gomock.Controller) *MockSigner { + mock := &MockSigner{ctrl: ctrl} + mock.recorder = &MockSignerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockSigner) EXPECT() *MockSignerMockRecorder { + return m.recorder +} + +// SignIDToken mocks base method +func (m *MockSigner) SignIDToken(arg0 *oidc.IDTokenClaims) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SignIDToken", arg0) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SignIDToken indicates an expected call of SignIDToken +func (mr *MockSignerMockRecorder) SignIDToken(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignIDToken", reflect.TypeOf((*MockSigner)(nil).SignIDToken), arg0) +} + +// SignatureAlgorithm mocks base method +func (m *MockSigner) SignatureAlgorithm() go_jose_v2.SignatureAlgorithm { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SignatureAlgorithm") + ret0, _ := ret[0].(go_jose_v2.SignatureAlgorithm) + return ret0 +} + +// SignatureAlgorithm indicates an expected call of SignatureAlgorithm +func (mr *MockSignerMockRecorder) SignatureAlgorithm() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignatureAlgorithm", reflect.TypeOf((*MockSigner)(nil).SignatureAlgorithm)) +} diff --git a/pkg/op/mock/storage.mock.go b/pkg/op/mock/storage.mock.go index 9dfc096..4ebcd82 100644 --- a/pkg/op/mock/storage.mock.go +++ b/pkg/op/mock/storage.mock.go @@ -168,3 +168,18 @@ func (mr *MockStorageMockRecorder) GetSigningKey() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSigningKey", reflect.TypeOf((*MockStorage)(nil).GetSigningKey)) } + +// GetUserinfoFromScopes mocks base method +func (m *MockStorage) GetUserinfoFromScopes(arg0 []string) (*oidc.Userinfo, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetUserinfoFromScopes", arg0) + ret0, _ := ret[0].(*oidc.Userinfo) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetUserinfoFromScopes indicates an expected call of GetUserinfoFromScopes +func (mr *MockStorageMockRecorder) GetUserinfoFromScopes(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserinfoFromScopes", reflect.TypeOf((*MockStorage)(nil).GetUserinfoFromScopes), arg0) +} diff --git a/pkg/op/storage.go b/pkg/op/storage.go index e90d5e7..ef8ac2d 100644 --- a/pkg/op/storage.go +++ b/pkg/op/storage.go @@ -22,6 +22,7 @@ type OPStorage interface { GetClientByClientID(string) (Client, error) AuthorizeClientIDSecret(string, string) (Client, error) AuthorizeClientIDCodeVerifier(string, string) (Client, error) + GetUserinfoFromScopes([]string) (*oidc.Userinfo, error) } type Storage interface { diff --git a/pkg/op/userinfo.go b/pkg/op/userinfo.go new file mode 100644 index 0000000..ad81f69 --- /dev/null +++ b/pkg/op/userinfo.go @@ -0,0 +1,28 @@ +package op + +import ( + "net/http" + + "github.com/caos/oidc/pkg/utils" +) + +type UserinfoProvider interface { + Storage() Storage +} + +func Userinfo(w http.ResponseWriter, r *http.Request, userinfoProvider UserinfoProvider) { + scopes, err := ScopesFromAccessToken(w, r) + if err != nil { + return + } + info, err := userinfoProvider.Storage().GetUserinfoFromScopes(scopes) + if err != nil { + utils.MarshalJSON(w, err) + return + } + utils.MarshalJSON(w, info) +} + +func ScopesFromAccessToken(w http.ResponseWriter, r *http.Request) ([]string, error) { + return []string{}, nil +}