Skip to content

Commit bdc94c8

Browse files
committed
Core Interfaces
1 parent 9b0b345 commit bdc94c8

File tree

2 files changed

+381
-0
lines changed

2 files changed

+381
-0
lines changed

shared/identity_provider_response.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package shared
2+
3+
import (
4+
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
5+
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/public"
6+
"github.com/redis-developer/go-redis-entraid/internal"
7+
"github.com/redis-developer/go-redis-entraid/token"
8+
)
9+
10+
const (
11+
// ResponseTypeAuthResult is the type of the auth result.
12+
ResponseTypeAuthResult = "AuthResult"
13+
// ResponseTypeAccessToken is the type of the access token.
14+
ResponseTypeAccessToken = "AccessToken"
15+
// ResponseTypeRawToken is the type of the response when you have a raw string.
16+
ResponseTypeRawToken = "RawToken"
17+
)
18+
19+
// IdentityProviderResponseParser is an interface that defines the methods for parsing the identity provider response.
20+
// It is used to parse the response from the identity provider and extract the token.
21+
// If not provided, the default implementation will be used.
22+
type IdentityProviderResponseParser interface {
23+
ParseResponse(response IdentityProviderResponse) (*token.Token, error)
24+
}
25+
26+
// IdentityProviderResponse is an interface that defines the methods for an identity provider authentication result.
27+
// It is used to get the type of the authentication result, the authentication result itself (can be AuthResult or AccessToken),
28+
type IdentityProviderResponse interface {
29+
// Type returns the type of the auth result
30+
Type() string
31+
AuthResult() public.AuthResult
32+
AccessToken() azcore.AccessToken
33+
RawToken() string
34+
}
35+
36+
// IdentityProvider is an interface that defines the methods for an identity provider.
37+
// It is used to request a token for authentication.
38+
// The identity provider is responsible for providing the raw authentication token.
39+
type IdentityProvider interface {
40+
// RequestToken requests a token from the identity provider.
41+
// It returns the token, the expiration time, and an error if any.
42+
RequestToken() (IdentityProviderResponse, error)
43+
}
44+
45+
// NewIDPResponse creates a new auth result based on the type provided.
46+
// It returns an IdentityProviderResponse interface.
47+
// Type can be either AuthResult, AccessToken, or RawToken.
48+
// Second argument is the result of the type provided in the first argument.
49+
func NewIDPResponse(responseType string, result interface{}) (IdentityProviderResponse, error) {
50+
return internal.NewIDPResp(responseType, result)
51+
}
Lines changed: 330 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
1+
package shared
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
8+
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/public"
9+
"github.com/redis-developer/go-redis-entraid/token"
10+
"github.com/stretchr/testify/assert"
11+
)
12+
13+
// Mock implementations for testing
14+
type mockIDPResponse struct {
15+
responseType string
16+
authResult *public.AuthResult
17+
accessToken *azcore.AccessToken
18+
rawToken string
19+
}
20+
21+
func (m *mockIDPResponse) Type() string {
22+
return m.responseType
23+
}
24+
25+
func (m *mockIDPResponse) AuthResult() public.AuthResult {
26+
if m.authResult == nil {
27+
return public.AuthResult{}
28+
}
29+
return *m.authResult
30+
}
31+
32+
func (m *mockIDPResponse) AccessToken() azcore.AccessToken {
33+
if m.accessToken == nil {
34+
return azcore.AccessToken{}
35+
}
36+
return *m.accessToken
37+
}
38+
39+
func (m *mockIDPResponse) RawToken() string {
40+
return m.rawToken
41+
}
42+
43+
type mockIDPParser struct {
44+
parseError error
45+
token *token.Token
46+
}
47+
48+
func (m *mockIDPParser) ParseResponse(response IdentityProviderResponse) (*token.Token, error) {
49+
if m.parseError != nil {
50+
return nil, m.parseError
51+
}
52+
return m.token, nil
53+
}
54+
55+
type mockIDP struct {
56+
response IdentityProviderResponse
57+
err error
58+
}
59+
60+
func (m *mockIDP) RequestToken() (IdentityProviderResponse, error) {
61+
if m.err != nil {
62+
return nil, m.err
63+
}
64+
return m.response, nil
65+
}
66+
67+
func TestNewIDPResponse(t *testing.T) {
68+
tests := []struct {
69+
name string
70+
responseType string
71+
result interface{}
72+
expectedError string
73+
}{
74+
{
75+
name: "Valid AuthResult pointer",
76+
responseType: ResponseTypeAuthResult,
77+
result: &public.AuthResult{},
78+
},
79+
{
80+
name: "Valid AuthResult value",
81+
responseType: ResponseTypeAuthResult,
82+
result: public.AuthResult{},
83+
},
84+
{
85+
name: "Valid AccessToken pointer",
86+
responseType: ResponseTypeAccessToken,
87+
result: &azcore.AccessToken{Token: "test-token"},
88+
},
89+
{
90+
name: "Valid AccessToken value",
91+
responseType: ResponseTypeAccessToken,
92+
result: azcore.AccessToken{Token: "test-token"},
93+
},
94+
{
95+
name: "Valid RawToken string",
96+
responseType: ResponseTypeRawToken,
97+
result: "test-token",
98+
},
99+
{
100+
name: "Valid RawToken string pointer",
101+
responseType: ResponseTypeRawToken,
102+
result: stringPtr("test-token"),
103+
},
104+
{
105+
name: "Nil result",
106+
responseType: ResponseTypeAuthResult,
107+
result: nil,
108+
expectedError: "result cannot be nil",
109+
},
110+
{
111+
name: "Nil string pointer",
112+
responseType: ResponseTypeRawToken,
113+
result: (*string)(nil),
114+
expectedError: "raw token cannot be nil",
115+
},
116+
{
117+
name: "Invalid AuthResult type",
118+
responseType: ResponseTypeAuthResult,
119+
result: "not-an-auth-result",
120+
expectedError: "invalid auth result type: expected public.AuthResult or *public.AuthResult",
121+
},
122+
{
123+
name: "Invalid AccessToken type",
124+
responseType: ResponseTypeAccessToken,
125+
result: "not-an-access-token",
126+
expectedError: "invalid access token type: expected azcore.AccessToken or *azcore.AccessToken",
127+
},
128+
{
129+
name: "Invalid RawToken type",
130+
responseType: ResponseTypeRawToken,
131+
result: 123,
132+
expectedError: "invalid raw token type: expected string or *string",
133+
},
134+
{
135+
name: "Invalid response type",
136+
responseType: "InvalidType",
137+
result: "test",
138+
expectedError: "unsupported identity provider response type: InvalidType",
139+
},
140+
}
141+
142+
for _, tt := range tests {
143+
t.Run(tt.name, func(t *testing.T) {
144+
resp, err := NewIDPResponse(tt.responseType, tt.result)
145+
146+
if tt.expectedError != "" {
147+
assert.Error(t, err)
148+
assert.Contains(t, err.Error(), tt.expectedError)
149+
assert.Nil(t, resp)
150+
return
151+
}
152+
153+
assert.NoError(t, err)
154+
assert.NotNil(t, resp)
155+
assert.Equal(t, tt.responseType, resp.Type())
156+
157+
switch tt.responseType {
158+
case ResponseTypeAuthResult:
159+
assert.NotNil(t, resp.AuthResult())
160+
case ResponseTypeAccessToken:
161+
assert.NotNil(t, resp.AccessToken())
162+
assert.NotEmpty(t, resp.AccessToken().Token)
163+
case ResponseTypeRawToken:
164+
assert.NotEmpty(t, resp.RawToken())
165+
}
166+
})
167+
}
168+
}
169+
170+
func stringPtr(s string) *string {
171+
return &s
172+
}
173+
174+
func TestIdentityProviderResponse(t *testing.T) {
175+
now := time.Now()
176+
expires := now.Add(time.Hour)
177+
178+
authResult := &public.AuthResult{
179+
AccessToken: "test-access-token",
180+
ExpiresOn: expires,
181+
}
182+
183+
accessToken := &azcore.AccessToken{
184+
Token: "test-access-token",
185+
ExpiresOn: expires,
186+
}
187+
188+
tests := []struct {
189+
name string
190+
response *mockIDPResponse
191+
expectedType string
192+
}{
193+
{
194+
name: "AuthResult response",
195+
response: &mockIDPResponse{
196+
responseType: ResponseTypeAuthResult,
197+
authResult: authResult,
198+
},
199+
expectedType: ResponseTypeAuthResult,
200+
},
201+
{
202+
name: "AccessToken response",
203+
response: &mockIDPResponse{
204+
responseType: ResponseTypeAccessToken,
205+
accessToken: accessToken,
206+
},
207+
expectedType: ResponseTypeAccessToken,
208+
},
209+
{
210+
name: "RawToken response",
211+
response: &mockIDPResponse{
212+
responseType: ResponseTypeRawToken,
213+
rawToken: "test-raw-token",
214+
},
215+
expectedType: ResponseTypeRawToken,
216+
},
217+
}
218+
219+
for _, tt := range tests {
220+
t.Run(tt.name, func(t *testing.T) {
221+
assert.Equal(t, tt.expectedType, tt.response.Type())
222+
223+
switch tt.expectedType {
224+
case ResponseTypeAuthResult:
225+
result := tt.response.AuthResult()
226+
assert.Equal(t, authResult.AccessToken, result.AccessToken)
227+
assert.Equal(t, authResult.ExpiresOn, result.ExpiresOn)
228+
case ResponseTypeAccessToken:
229+
token := tt.response.AccessToken()
230+
assert.Equal(t, accessToken.Token, token.Token)
231+
assert.Equal(t, accessToken.ExpiresOn, token.ExpiresOn)
232+
case ResponseTypeRawToken:
233+
assert.Equal(t, "test-raw-token", tt.response.RawToken())
234+
}
235+
})
236+
}
237+
}
238+
239+
func TestIdentityProvider(t *testing.T) {
240+
tests := []struct {
241+
name string
242+
provider *mockIDP
243+
wantErr bool
244+
}{
245+
{
246+
name: "Successful token request",
247+
provider: &mockIDP{
248+
response: &mockIDPResponse{
249+
responseType: ResponseTypeRawToken,
250+
rawToken: "test-token",
251+
},
252+
},
253+
wantErr: false,
254+
},
255+
{
256+
name: "Failed token request",
257+
provider: &mockIDP{
258+
err: assert.AnError,
259+
},
260+
wantErr: true,
261+
},
262+
}
263+
264+
for _, tt := range tests {
265+
t.Run(tt.name, func(t *testing.T) {
266+
response, err := tt.provider.RequestToken()
267+
if tt.wantErr {
268+
assert.Error(t, err)
269+
assert.Nil(t, response)
270+
} else {
271+
assert.NoError(t, err)
272+
assert.NotNil(t, response)
273+
assert.Equal(t, ResponseTypeRawToken, response.Type())
274+
assert.Equal(t, "test-token", response.RawToken())
275+
}
276+
})
277+
}
278+
}
279+
280+
func TestIdentityProviderResponseParser(t *testing.T) {
281+
now := time.Now()
282+
expires := now.Add(time.Hour)
283+
testToken := token.New("test-user", "test-password", "test-token", expires, now, int64(time.Hour.Seconds()))
284+
285+
tests := []struct {
286+
name string
287+
parser *mockIDPParser
288+
response IdentityProviderResponse
289+
wantErr bool
290+
wantToken *token.Token
291+
}{
292+
{
293+
name: "Successful parse",
294+
parser: &mockIDPParser{
295+
token: testToken,
296+
},
297+
response: &mockIDPResponse{
298+
responseType: ResponseTypeRawToken,
299+
rawToken: "test-token",
300+
},
301+
wantErr: false,
302+
wantToken: testToken,
303+
},
304+
{
305+
name: "Failed parse",
306+
parser: &mockIDPParser{
307+
parseError: assert.AnError,
308+
},
309+
response: &mockIDPResponse{
310+
responseType: ResponseTypeRawToken,
311+
rawToken: "test-token",
312+
},
313+
wantErr: true,
314+
wantToken: nil,
315+
},
316+
}
317+
318+
for _, tt := range tests {
319+
t.Run(tt.name, func(t *testing.T) {
320+
token, err := tt.parser.ParseResponse(tt.response)
321+
if tt.wantErr {
322+
assert.Error(t, err)
323+
assert.Nil(t, token)
324+
} else {
325+
assert.NoError(t, err)
326+
assert.Equal(t, tt.wantToken, token)
327+
}
328+
})
329+
}
330+
}

0 commit comments

Comments
 (0)