add unit tests for oidc.IntrospectionResponse

- Changed UserInfoAddress to pointer in UserInfo and
IntrospectionResponse.
This was needed to make omitempty work correctly.

- Copy or merge maps in IntrospectionResponse GetUserInfo
and SetUserInfo
This commit is contained in:
Tim Möhlmann 2023-03-02 15:44:27 +02:00
parent 3940b520a8
commit 72a108a33b
8 changed files with 99 additions and 14 deletions

1
go.mod
View file

@ -10,6 +10,7 @@ require (
github.com/gorilla/schema v1.2.0
github.com/gorilla/securecookie v1.1.1
github.com/jeremija/gosubmit v0.2.7
github.com/muhlemmer/gu v0.2.0
github.com/rs/cors v1.8.3
github.com/sirupsen/logrus v1.9.0
github.com/stretchr/testify v1.8.1

2
go.sum
View file

@ -123,6 +123,8 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/muhlemmer/gu v0.2.0 h1:vnIYZccxWVSPXbCPTuB1J7HWimMeZLlkfDody5qLUEI=
github.com/muhlemmer/gu v0.2.0/go.mod h1:YHtHR+gxM+bKEIIs7Hmi9sPT3ZDUvTN/i88wQpZkrdM=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=

View file

@ -1,5 +1,7 @@
package oidc
import "github.com/muhlemmer/gu"
type IntrospectionRequest struct {
Token string `schema:"token"`
}
@ -26,8 +28,8 @@ type IntrospectionResponse struct {
UserInfoEmail
UserInfoPhone
Address UserInfoAddress `json:"address,omitempty"`
Claims map[string]any `json:"-"`
Address *UserInfoAddress `json:"address,omitempty"`
Claims map[string]any `json:"-"`
}
// GetUserInfo copies all user related fields into a new UserInfo.
@ -38,6 +40,7 @@ func (i *IntrospectionResponse) GetUserInfo() *UserInfo {
UserInfoProfile: i.UserInfoProfile,
UserInfoEmail: i.UserInfoEmail,
UserInfoPhone: i.UserInfoPhone,
Claims: gu.MapCopy(i.Claims),
}
}
@ -45,20 +48,25 @@ func (i *IntrospectionResponse) GetUserInfo() *UserInfo {
// into the IntroSpectionResponse.
func (i *IntrospectionResponse) SetUserInfo(u *UserInfo) {
i.Subject = u.Subject
i.Username = i.PreferredUsername
i.Username = u.PreferredUsername
i.Address = u.Address
i.UserInfoProfile = u.UserInfoProfile
i.UserInfoEmail = u.UserInfoEmail
i.UserInfoPhone = u.UserInfoPhone
if i.Claims == nil {
i.Claims = gu.MapCopy(u.Claims)
} else {
gu.MapMerge(u.Claims, i.Claims)
}
}
// introspectionResponseAlias prevents loops on the JSON methods
type introspectionResponseAlias IntrospectionResponse
func (i *IntrospectionResponse) MarshalJSON() ([]byte, error) {
//TODO: set the username directly where the IntrospectionResponse is created
// a.Username = i.PreferredUsername
if i.Username == "" {
i.Username = i.PreferredUsername
}
return mergeAndMarshalClaims((*introspectionResponseAlias)(i), i.Claims)
}

View file

@ -0,0 +1,74 @@
package oidc
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestIntrospectionResponse_GetUserInfo(t *testing.T) {
got := introspectionResponseData.GetUserInfo()
assert.Equal(t, userInfoData, got)
}
func TestIntrospectionResponse_SetUserInfo(t *testing.T) {
tests := []struct {
name string
start *IntrospectionResponse
want *IntrospectionResponse
}{
{
name: "nil claims",
start: &IntrospectionResponse{},
want: &IntrospectionResponse{
Subject: userInfoData.Subject,
Username: userInfoData.PreferredUsername,
Address: userInfoData.Address,
UserInfoProfile: userInfoData.UserInfoProfile,
UserInfoEmail: userInfoData.UserInfoEmail,
UserInfoPhone: userInfoData.UserInfoPhone,
Claims: userInfoData.Claims,
},
},
{
name: "merge claims",
start: &IntrospectionResponse{
Claims: map[string]any{
"hello": "world",
},
},
want: &IntrospectionResponse{
Subject: userInfoData.Subject,
Username: userInfoData.PreferredUsername,
Address: userInfoData.Address,
UserInfoProfile: userInfoData.UserInfoProfile,
UserInfoEmail: userInfoData.UserInfoEmail,
UserInfoPhone: userInfoData.UserInfoPhone,
Claims: map[string]any{
"foo": "bar",
"hello": "world",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.start.SetUserInfo(userInfoData)
assert.Equal(t, tt.want, tt.start)
})
}
}
func TestIntrospectionResponse_MarshalJSON(t *testing.T) {
got, err := json.Marshal(&IntrospectionResponse{
UserInfoProfile: UserInfoProfile{
PreferredUsername: "muhlemmer",
},
})
require.NoError(t, err)
assert.Equal(t, string(got), `{"active":false,"username":"muhlemmer","preferred_username":"muhlemmer"}`)
}

View file

@ -113,8 +113,8 @@ type IDTokenClaims struct {
UserInfoProfile
UserInfoEmail
UserInfoPhone
Address UserInfoAddress `json:"address,omitempty"`
Claims map[string]any `json:"-"`
Address *UserInfoAddress `json:"address,omitempty"`
Claims map[string]any `json:"-"`
}
// GetAccessTokenHash implements the IDTokenClaims interface

View file

@ -96,7 +96,7 @@ var (
PhoneNumber: "+1234567890",
PhoneNumberVerified: true,
},
Address: UserInfoAddress{
Address: &UserInfoAddress{
Formatted: "Sesame street 666\n666-666, Smallvile\nMoon",
StreetAddress: "Sesame street 666",
Locality: "Smallvile",

View file

@ -5,7 +5,7 @@ type UserInfo struct {
UserInfoProfile
UserInfoEmail
UserInfoPhone
Address UserInfoAddress `json:"address,omitempty"`
Address *UserInfoAddress `json:"address,omitempty"`
Claims map[string]any `json:"-"`
}

View file

@ -10,7 +10,7 @@ import (
func TestUserInfoMarshal(t *testing.T) {
userinfo := &UserInfo{
Subject: "test",
Address: UserInfoAddress{
Address: &UserInfoAddress{
StreetAddress: "Test 789\nPostfach 2",
},
UserInfoEmail: UserInfoEmail{
@ -55,7 +55,7 @@ func TestUserInfoEmailVerifiedUnmarshal(t *testing.T) {
}, uie)
})
t.Run("unmarsha email_verified from json string true", func(t *testing.T) {
t.Run("unmarshal email_verified from json string true", func(t *testing.T) {
jsonBool := []byte(`{"email": "my@email.com", "email_verified": "true"}`)
var uie UserInfoEmail
@ -68,7 +68,7 @@ func TestUserInfoEmailVerifiedUnmarshal(t *testing.T) {
}, uie)
})
t.Run("unmarsha email_verified from json bool false", func(t *testing.T) {
t.Run("unmarshal email_verified from json bool false", func(t *testing.T) {
jsonBool := []byte(`{"email": "my@email.com", "email_verified": false}`)
var uie UserInfoEmail
@ -81,7 +81,7 @@ func TestUserInfoEmailVerifiedUnmarshal(t *testing.T) {
}, uie)
})
t.Run("unmarsha email_verified from json string false", func(t *testing.T) {
t.Run("unmarshal email_verified from json string false", func(t *testing.T) {
jsonBool := []byte(`{"email": "my@email.com", "email_verified": "false"}`)
var uie UserInfoEmail