Skip to content

Commit 78d26a6

Browse files
committed
Add: KeycloakRepository; Add KeycloakJWTReceiverCached
1 parent fcfe402 commit 78d26a6

File tree

4 files changed

+93
-133
lines changed

4 files changed

+93
-133
lines changed

client/keycloakJWTClient.go

Lines changed: 0 additions & 110 deletions
This file was deleted.
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package client
2+
3+
import (
4+
"github.com/Nerzal/gocloak/v12"
5+
"github.com/golang-jwt/jwt/v4"
6+
"github.com/rs/zerolog/log"
7+
)
8+
9+
type ITokenReceiver interface {
10+
GetClientAccessToken(clientName, clientSecret string) (string, error)
11+
getClientToken(clientName, clientSecret string) (*gocloak.JWT, error)
12+
}
13+
14+
type KeycloakJWTReceiverCachedInMemory struct {
15+
keycloakRepository IKeycloakRepository
16+
cachedToken *gocloak.JWT
17+
}
18+
19+
var _ ITokenReceiver = &KeycloakJWTReceiverCachedInMemory{}
20+
21+
func NewKeycloakJWTReceiverCachedInMemory(keycloakRepository IKeycloakRepository) *KeycloakJWTReceiverCachedInMemory {
22+
return &KeycloakJWTReceiverCachedInMemory{
23+
keycloakRepository: keycloakRepository,
24+
}
25+
}
26+
27+
func isTokenValid(token *gocloak.JWT) bool {
28+
if token == nil {
29+
return false
30+
}
31+
32+
parser := jwt.NewParser()
33+
claims := &jwt.MapClaims{}
34+
35+
_, _, err := parser.ParseUnverified(token.AccessToken, claims)
36+
if err != nil {
37+
log.Error().Msgf("couldn't parse JWT access token: %v", err)
38+
return false
39+
}
40+
41+
err = claims.Valid()
42+
if err != nil {
43+
log.Debug().Msgf("Token is invalid: %v", err)
44+
return false
45+
}
46+
47+
return true
48+
}
49+
50+
func (k *KeycloakJWTReceiverCachedInMemory) getClientToken(clientName, clientSecret string) (*gocloak.JWT, error) {
51+
if k.cachedToken == nil || !isTokenValid(k.cachedToken) {
52+
token, err := k.keycloakRepository.getClientToken(clientName, clientSecret)
53+
if err != nil {
54+
return nil, err
55+
}
56+
k.cachedToken = token
57+
log.Debug().Msgf("updated token: %s", token.AccessToken)
58+
} else {
59+
log.Debug().Msgf("Using cached token: %s", k.cachedToken.AccessToken)
60+
}
61+
62+
return k.cachedToken, nil
63+
}
64+
65+
func (k *KeycloakJWTReceiverCachedInMemory) GetClientAccessToken(clientName, clientSecret string) (string, error) {
66+
token, err := k.getClientToken(clientName, clientSecret)
67+
if err != nil {
68+
return "", err
69+
}
70+
71+
return token.AccessToken, nil
72+
}

client/keycloakJWTClient_test.go renamed to client/keycloakJWTReceiverCachedInMemory_test.go

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,16 @@ import (
88
"github.com/stretchr/testify/mock"
99
)
1010

11-
type MockTokenReceiver struct {
11+
type MockKeycloakRepository struct {
1212
mock.Mock
1313
}
1414

15-
func (m *MockTokenReceiver) GetAccessToken() (string, error) {
16-
args := m.Called()
17-
return args.String(0), args.Error(1)
18-
}
19-
20-
func (m *MockTokenReceiver) getToken() (*gocloak.JWT, error) {
15+
func (m *MockKeycloakRepository) getClientToken(clientName, clientSecret string) (*gocloak.JWT, error) {
2116
args := m.Called()
2217
return args.Get(0).(*gocloak.JWT), args.Error(1)
2318
}
2419

25-
func TestKeycloakJWTCacheInMemory_GetToken(t *testing.T) {
20+
func TestKeycloakJWTCacheInMemory_GetClientToken(t *testing.T) {
2621

2722
tests := []struct {
2823
name string
@@ -78,14 +73,14 @@ func TestKeycloakJWTCacheInMemory_GetToken(t *testing.T) {
7873
for _, tt := range tests {
7974
t.Run(tt.name, func(t *testing.T) {
8075

81-
mockTokenReceiver := new(MockTokenReceiver)
82-
cache := NewKeycloakJWTCacheInMemory(mockTokenReceiver)
76+
mockTokenReceiver := new(MockKeycloakRepository)
77+
cache := NewKeycloakJWTReceiverCachedInMemory(mockTokenReceiver)
8378

8479
cache.cachedToken = tt.cachedToken
8580

86-
mockTokenReceiver.On("getToken").Return(tt.mockToken, tt.mockError)
81+
mockTokenReceiver.On("getClientToken").Return(tt.mockToken, tt.mockError)
8782

88-
token, err := cache.getToken()
83+
token, err := cache.getClientToken("testClient", "testSecret")
8984

9085
if tt.expectedError != nil {
9186
assert.ErrorIs(t, err, tt.expectedError)
@@ -95,26 +90,26 @@ func TestKeycloakJWTCacheInMemory_GetToken(t *testing.T) {
9590

9691
assert.Equal(t, tt.expectedToken, token)
9792
if tt.shouldFetchToken {
98-
mockTokenReceiver.AssertCalled(t, "getToken")
93+
mockTokenReceiver.AssertCalled(t, "getClientToken")
9994
}
10095
})
10196
}
10297
}
10398

104-
func TestKeycloakJWTCacheInMemory_GetAccessToken(t *testing.T) {
105-
mockTokenReceiver := new(MockTokenReceiver)
99+
func TestKeycloakJWTCacheInMemory_GetClientAccessToken(t *testing.T) {
100+
mockKeycloakRepository := new(MockKeycloakRepository)
106101
mockToken := &gocloak.JWT{
107102
AccessToken: "test_token",
108103
ExpiresIn: 3600,
109104
}
110105

111-
mockTokenReceiver.On("getToken").Return(mockToken, nil)
106+
mockKeycloakRepository.On("getClientToken").Return(mockToken, nil)
112107

113-
cache := NewKeycloakJWTCacheInMemory(mockTokenReceiver)
108+
cache := NewKeycloakJWTReceiverCachedInMemory(mockKeycloakRepository)
114109

115-
accessToken, err := cache.GetAccessToken()
110+
accessToken, err := cache.GetClientAccessToken("testClient", "testSecret")
116111

117112
assert.NoError(t, err)
118113
assert.Equal(t, "test_token", accessToken)
119-
mockTokenReceiver.AssertCalled(t, "getToken")
114+
mockKeycloakRepository.AssertCalled(t, "getClientToken")
120115
}

client/keycloakRepository.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,24 @@ import (
66
)
77

88
type IKeycloakRepository interface {
9-
GetClientAccessToken(clientName, clientSecret, realm string) (*gocloak.JWT, error)
9+
getClientToken(clientName, clientSecret string) (*gocloak.JWT, error)
10+
// Append keycloak functions here
1011
}
1112

1213
type KeycloakRepository struct {
1314
client *gocloak.GoCloak
15+
realm string
1416
}
1517

1618
var _ IKeycloakRepository = &KeycloakRepository{}
1719

18-
func NewKeycloakRepository(basePath string) *KeycloakRepository {
20+
func NewKeycloakRepository(basePath, realm string) *KeycloakRepository {
1921
return &KeycloakRepository{
2022
client: gocloak.NewClient(basePath),
23+
realm: realm,
2124
}
2225
}
2326

24-
func (r *KeycloakRepository) GetClientAccessToken(clientName, clientSecret, realm string) (*gocloak.JWT, error) {
25-
return r.client.LoginClient(context.Background(), clientName, clientSecret, realm)
27+
func (r *KeycloakRepository) getClientToken(clientName, clientSecret string) (*gocloak.JWT, error) {
28+
return r.client.LoginClient(context.Background(), clientName, clientSecret, r.realm)
2629
}

0 commit comments

Comments
 (0)