From b24be52638ae2f41175a5885dfa3285661301d30 Mon Sep 17 00:00:00 2001 From: Fabian Fischer Date: Wed, 27 Apr 2022 13:22:26 +0200 Subject: [PATCH] Fix logout call to not use privileged endpoint The LogoutUserSession uses a privileged endpoint that can logout any session. This means service accounts usually will not be able to call this. --- keycloak/ZZ_mock_gocloak_test.go | 12 ++++++------ keycloak/client.go | 5 +++-- keycloak/client_login_test.go | 6 ++++-- keycloak/suite_test.go | 3 ++- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/keycloak/ZZ_mock_gocloak_test.go b/keycloak/ZZ_mock_gocloak_test.go index 8c0f90a..f7d6af4 100644 --- a/keycloak/ZZ_mock_gocloak_test.go +++ b/keycloak/ZZ_mock_gocloak_test.go @@ -167,18 +167,18 @@ func (mr *MockGoCloakMockRecorder) LoginAdmin(ctx, username, password, realm int return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoginAdmin", reflect.TypeOf((*MockGoCloak)(nil).LoginAdmin), ctx, username, password, realm) } -// LogoutUserSession mocks base method. -func (m *MockGoCloak) LogoutUserSession(ctx context.Context, accessToken, realm, session string) error { +// LogoutPublicClient mocks base method. +func (m *MockGoCloak) LogoutPublicClient(ctx context.Context, clientID, realm, accessToken, refreshToken string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "LogoutUserSession", ctx, accessToken, realm, session) + ret := m.ctrl.Call(m, "LogoutPublicClient", ctx, clientID, realm, accessToken, refreshToken) ret0, _ := ret[0].(error) return ret0 } -// LogoutUserSession indicates an expected call of LogoutUserSession. -func (mr *MockGoCloakMockRecorder) LogoutUserSession(ctx, accessToken, realm, session interface{}) *gomock.Call { +// LogoutPublicClient indicates an expected call of LogoutPublicClient. +func (mr *MockGoCloakMockRecorder) LogoutPublicClient(ctx, clientID, realm, accessToken, refreshToken interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LogoutUserSession", reflect.TypeOf((*MockGoCloak)(nil).LogoutUserSession), ctx, accessToken, realm, session) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LogoutPublicClient", reflect.TypeOf((*MockGoCloak)(nil).LogoutPublicClient), ctx, clientID, realm, accessToken, refreshToken) } // UpdateUser mocks base method. diff --git a/keycloak/client.go b/keycloak/client.go index 03f9d60..dcd774c 100644 --- a/keycloak/client.go +++ b/keycloak/client.go @@ -114,7 +114,7 @@ func (errs *MembershipSyncErrors) Error() string { // This keeps the mock at a more reasonable size type GoCloak interface { LoginAdmin(ctx context.Context, username, password, realm string) (*gocloak.JWT, error) - LogoutUserSession(ctx context.Context, accessToken, realm, session string) error + LogoutPublicClient(ctx context.Context, clientID, realm, accessToken, refreshToken string) error CreateGroup(ctx context.Context, accessToken, realm string, group gocloak.Group) (string, error) CreateChildGroup(ctx context.Context, accessToken, realm, groupID string, group gocloak.Group) (string, error) @@ -302,7 +302,8 @@ func (c Client) login(ctx context.Context) (*gocloak.JWT, error) { } func (c Client) logout(ctx context.Context, token *gocloak.JWT) error { - return c.Client.LogoutUserSession(ctx, token.AccessToken, c.loginRealm(), token.SessionState) + // `admin-cli` is the client used when authenticating to the admin API + return c.Client.LogoutPublicClient(ctx, "admin-cli", c.loginRealm(), token.AccessToken, token.RefreshToken) } func (c Client) getGroup(ctx context.Context, token *gocloak.JWT, toSearch Group) (*gocloak.Group, error) { diff --git a/keycloak/client_login_test.go b/keycloak/client_login_test.go index a597068..0188582 100644 --- a/keycloak/client_login_test.go +++ b/keycloak/client_login_test.go @@ -26,10 +26,11 @@ func TestLogin(t *testing.T) { Return(&gocloak.JWT{ SessionState: "session", AccessToken: "token", + RefreshToken: "refresh", }, nil). AnyTimes() mKeycloak.EXPECT(). - LogoutUserSession(gomock.Any(), "token", "target-realm", "session"). + LogoutPublicClient(gomock.Any(), "admin-cli", "target-realm", "token", "refresh"). Return(nil). AnyTimes() @@ -55,10 +56,11 @@ func TestLogin_WithLoginRealm(t *testing.T) { Return(&gocloak.JWT{ SessionState: "session", AccessToken: "token", + RefreshToken: "refresh", }, nil). AnyTimes() mKeycloak.EXPECT(). - LogoutUserSession(gomock.Any(), "token", "login-realm", "session"). + LogoutPublicClient(gomock.Any(), "admin-cli", "login-realm", "token", "refresh"). Return(nil). AnyTimes() diff --git a/keycloak/suite_test.go b/keycloak/suite_test.go index 33d988b..8049c37 100644 --- a/keycloak/suite_test.go +++ b/keycloak/suite_test.go @@ -15,10 +15,11 @@ func mockLogin(mgc *MockGoCloak, c Client) { Return(&gocloak.JWT{ SessionState: "session", AccessToken: "token", + RefreshToken: "refresh", }, nil). AnyTimes() mgc.EXPECT(). - LogoutUserSession(gomock.Any(), "token", c.Realm, "session"). + LogoutPublicClient(gomock.Any(), "admin-cli", c.Realm, "token", "refresh"). Return(nil). AnyTimes() }