Skip to content

Commit

Permalink
Add: KeycloakRepository; Add KeycloakJWTReceiverCached
Browse files Browse the repository at this point in the history
  • Loading branch information
greenMika committed May 31, 2024
1 parent fcfe402 commit 78d26a6
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 133 deletions.
110 changes: 0 additions & 110 deletions client/keycloakJWTClient.go

This file was deleted.

72 changes: 72 additions & 0 deletions client/keycloakJWTReceiverCachedInMemory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package client

import (
"github.com/Nerzal/gocloak/v12"
"github.com/golang-jwt/jwt/v4"
"github.com/rs/zerolog/log"
)

type ITokenReceiver interface {
GetClientAccessToken(clientName, clientSecret string) (string, error)
getClientToken(clientName, clientSecret string) (*gocloak.JWT, error)
}

type KeycloakJWTReceiverCachedInMemory struct {
keycloakRepository IKeycloakRepository
cachedToken *gocloak.JWT
}

var _ ITokenReceiver = &KeycloakJWTReceiverCachedInMemory{}

func NewKeycloakJWTReceiverCachedInMemory(keycloakRepository IKeycloakRepository) *KeycloakJWTReceiverCachedInMemory {
return &KeycloakJWTReceiverCachedInMemory{
keycloakRepository: keycloakRepository,
}
}

func isTokenValid(token *gocloak.JWT) bool {
if token == nil {
return false
}

parser := jwt.NewParser()
claims := &jwt.MapClaims{}

_, _, err := parser.ParseUnverified(token.AccessToken, claims)
if err != nil {
log.Error().Msgf("couldn't parse JWT access token: %v", err)
return false
}

err = claims.Valid()
if err != nil {
log.Debug().Msgf("Token is invalid: %v", err)
return false
}

return true
}

func (k *KeycloakJWTReceiverCachedInMemory) getClientToken(clientName, clientSecret string) (*gocloak.JWT, error) {
if k.cachedToken == nil || !isTokenValid(k.cachedToken) {
token, err := k.keycloakRepository.getClientToken(clientName, clientSecret)
if err != nil {
return nil, err

Check failure on line 54 in client/keycloakJWTReceiverCachedInMemory.go

View workflow job for this annotation

GitHub Actions / lint

error returned from interface method should be wrapped: sig: func (github.com/greenbone/keycloak-client-golang/client.IKeycloakRepository).getClientToken(clientName string, clientSecret string) (*github.com/Nerzal/gocloak/v12.JWT, error) (wrapcheck)
}
k.cachedToken = token
log.Debug().Msgf("updated token: %s", token.AccessToken)
} else {
log.Debug().Msgf("Using cached token: %s", k.cachedToken.AccessToken)
}

return k.cachedToken, nil
}

func (k *KeycloakJWTReceiverCachedInMemory) GetClientAccessToken(clientName, clientSecret string) (string, error) {
token, err := k.getClientToken(clientName, clientSecret)
if err != nil {
return "", err
}

return token.AccessToken, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,16 @@ import (
"github.com/stretchr/testify/mock"
)

type MockTokenReceiver struct {
type MockKeycloakRepository struct {
mock.Mock
}

func (m *MockTokenReceiver) GetAccessToken() (string, error) {
args := m.Called()
return args.String(0), args.Error(1)
}

func (m *MockTokenReceiver) getToken() (*gocloak.JWT, error) {
func (m *MockKeycloakRepository) getClientToken(clientName, clientSecret string) (*gocloak.JWT, error) {
args := m.Called()
return args.Get(0).(*gocloak.JWT), args.Error(1)

Check failure on line 17 in client/keycloakJWTReceiverCachedInMemory_test.go

View workflow job for this annotation

GitHub Actions / lint

error returned from external package is unwrapped: sig: func (github.com/stretchr/testify/mock.Arguments).Error(index int) error (wrapcheck)
}

func TestKeycloakJWTCacheInMemory_GetToken(t *testing.T) {
func TestKeycloakJWTCacheInMemory_GetClientToken(t *testing.T) {

tests := []struct {
name string
Expand Down Expand Up @@ -78,14 +73,14 @@ func TestKeycloakJWTCacheInMemory_GetToken(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

mockTokenReceiver := new(MockTokenReceiver)
cache := NewKeycloakJWTCacheInMemory(mockTokenReceiver)
mockTokenReceiver := new(MockKeycloakRepository)
cache := NewKeycloakJWTReceiverCachedInMemory(mockTokenReceiver)

cache.cachedToken = tt.cachedToken

mockTokenReceiver.On("getToken").Return(tt.mockToken, tt.mockError)
mockTokenReceiver.On("getClientToken").Return(tt.mockToken, tt.mockError)

token, err := cache.getToken()
token, err := cache.getClientToken("testClient", "testSecret")

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

assert.Equal(t, tt.expectedToken, token)
if tt.shouldFetchToken {
mockTokenReceiver.AssertCalled(t, "getToken")
mockTokenReceiver.AssertCalled(t, "getClientToken")
}
})
}
}

func TestKeycloakJWTCacheInMemory_GetAccessToken(t *testing.T) {
mockTokenReceiver := new(MockTokenReceiver)
func TestKeycloakJWTCacheInMemory_GetClientAccessToken(t *testing.T) {
mockKeycloakRepository := new(MockKeycloakRepository)
mockToken := &gocloak.JWT{
AccessToken: "test_token",
ExpiresIn: 3600,
}

mockTokenReceiver.On("getToken").Return(mockToken, nil)
mockKeycloakRepository.On("getClientToken").Return(mockToken, nil)

cache := NewKeycloakJWTCacheInMemory(mockTokenReceiver)
cache := NewKeycloakJWTReceiverCachedInMemory(mockKeycloakRepository)

accessToken, err := cache.GetAccessToken()
accessToken, err := cache.GetClientAccessToken("testClient", "testSecret")

assert.NoError(t, err)
assert.Equal(t, "test_token", accessToken)
mockTokenReceiver.AssertCalled(t, "getToken")
mockKeycloakRepository.AssertCalled(t, "getClientToken")
}
11 changes: 7 additions & 4 deletions client/keycloakRepository.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,24 @@ import (
)

type IKeycloakRepository interface {
GetClientAccessToken(clientName, clientSecret, realm string) (*gocloak.JWT, error)
getClientToken(clientName, clientSecret string) (*gocloak.JWT, error)
// Append keycloak functions here
}

type KeycloakRepository struct {
client *gocloak.GoCloak
realm string
}

var _ IKeycloakRepository = &KeycloakRepository{}

func NewKeycloakRepository(basePath string) *KeycloakRepository {
func NewKeycloakRepository(basePath, realm string) *KeycloakRepository {
return &KeycloakRepository{
client: gocloak.NewClient(basePath),
realm: realm,
}
}

func (r *KeycloakRepository) GetClientAccessToken(clientName, clientSecret, realm string) (*gocloak.JWT, error) {
return r.client.LoginClient(context.Background(), clientName, clientSecret, realm)
func (r *KeycloakRepository) getClientToken(clientName, clientSecret string) (*gocloak.JWT, error) {
return r.client.LoginClient(context.Background(), clientName, clientSecret, r.realm)

Check failure on line 28 in client/keycloakRepository.go

View workflow job for this annotation

GitHub Actions / lint

error returned from external package is unwrapped: sig: func (*github.com/Nerzal/gocloak/v12.GoCloak).LoginClient(ctx context.Context, clientID string, clientSecret string, realm string) (*github.com/Nerzal/gocloak/v12.JWT, error) (wrapcheck)
}

0 comments on commit 78d26a6

Please sign in to comment.