Repository: Nerzal/gocloak
Pull request #87 (2019-06-27 16:40:28)

Adding a GetClientServiceAccount method
Retrieving User Representations of Client Service Accounts

Closes Issue #86 



Patch:
diff --git a/README.md b/README.md
index e777b52..bbca7ed 100644
--- a/README.md
+++ b/README.md
@@ -139,6 +139,7 @@ type GoCloak interface {
 	GetClient(accessToken string, realm string, clientID string) (*Client, error)
 	GetClientScope(token string, realm string, scopeID string) (*ClientScope, error)
 	GetClientSecret(token string, realm string, clientID string) (*CredentialRepresentation, error)
+	GetClientServiceAccount(token string, realm string, clientID string) (*User, error)
 	RegenerateClientSecret(token string, realm string, clientID string) (*CredentialRepresentation, error)
 	GetKeyStoreConfig(accessToken string, realm string) (*KeyStoreConfig, error)
 	GetUserByID(accessToken string, realm string, userID string) (*User, error)
diff --git a/client.go b/client.go
index 86fb4ab..fa20263 100644
--- a/client.go
+++ b/client.go
@@ -514,6 +514,20 @@ func (client *gocloak) GetClientSecret(token string, realm string, clientID stri
 	return &result, nil
 }
 
+// GetClientServiceAccount retrieves the service account "user" for a client if enabled
+func (client *gocloak) GetClientServiceAccount(token string, realm string, clientID string) (*User, error) {
+	var result User
+	resp, err := client.getRequestWithBearerAuth(token).
+		SetResult(&result).
+		Get(client.getAdminRealmURL(realm, "clients", clientID, "service-account-user"))
+
+	if err := checkForError(resp, err); err != nil {
+		return nil, err
+	}
+
+	return &result, nil
+}
+
 func (client *gocloak) RegenerateClientSecret(token string, realm string, clientID string) (*CredentialRepresentation, error) {
 	var result CredentialRepresentation
 	resp, err := client.getRequestWithBearerAuth(token).
diff --git a/gocloak.go b/gocloak.go
index f7f8af5..31dd3c6 100644
--- a/gocloak.go
+++ b/gocloak.go
@@ -79,6 +79,8 @@ type GoCloak interface {
 	GetClientScope(token string, realm string, scopeID string) (*ClientScope, error)
 	// GetClientSecret returns a client's secret
 	GetClientSecret(token string, realm string, clientID string) (*CredentialRepresentation, error)
+	// GetClientServiceAccount retrieves the service account "user" for a client if enabled
+	GetClientServiceAccount(token string, realm string, clientID string) (*User, error)
 	// RegenerateClientSecret creates a new client secret returning the updated CredentialRepresentation
 	RegenerateClientSecret(token string, realm string, clientID string) (*CredentialRepresentation, error)
 	// GetKeyStoreConfig gets the keyStoreConfig
diff --git a/models.go b/models.go
index 2ecbe15..adafdb5 100644
--- a/models.go
+++ b/models.go
@@ -74,22 +74,24 @@ type RetrospecTokenResult struct {
 
 // User represents the Keycloak User Structure
 type User struct {
-	ID                         string              `json:"id,omitempty"`
-	CreatedTimestamp           int64               `json:"createdTimestamp,omitempty"`
-	Username                   string              `json:"username,omitempty"`
-	Enabled                    bool                `json:"enabled"`
-	Totp                       bool                `json:"totp"`
-	EmailVerified              bool                `json:"emailVerified"`
-	FirstName                  string              `json:"firstName,omitempty"`
-	LastName                   string              `json:"lastName,omitempty"`
-	Email                      string              `json:"email,omitempty"`
-	FederationLink             string              `json:"federationLink,omitempty"`
-	Attributes                 map[string][]string `json:"attributes,omitempty"`
-	DisableableCredentialTypes []interface{}       `json:"disableableCredentialTypes,omitempty"`
-	RequiredActions            []string            `json:"requiredActions,omitempty"`
-	Access                     map[string]bool     `json:"access"`
-	ClientRoles                map[string][]string `json:"clientRoles,omitempty"`
-	RealmRoles                 []string            `json:"realmRoles,omitempty"`
+	ID                         string                      `json:"id,omitempty"`
+	CreatedTimestamp           int64                       `json:"createdTimestamp,omitempty"`
+	Username                   string                      `json:"username,omitempty"`
+	Enabled                    bool                        `json:"enabled"`
+	Totp                       bool                        `json:"totp"`
+	EmailVerified              bool                        `json:"emailVerified"`
+	FirstName                  string                      `json:"firstName,omitempty"`
+	LastName                   string                      `json:"lastName,omitempty"`
+	Email                      string                      `json:"email,omitempty"`
+	FederationLink             string                      `json:"federationLink,omitempty"`
+	Attributes                 map[string][]string         `json:"attributes,omitempty"`
+	DisableableCredentialTypes []interface{}               `json:"disableableCredentialTypes,omitempty"`
+	RequiredActions            []string                    `json:"requiredActions,omitempty"`
+	Access                     map[string]bool             `json:"access"`
+	ClientRoles                map[string][]string         `json:"clientRoles,omitempty"`
+	RealmRoles                 []string                    `json:"realmRoles,omitempty"`
+	ServiceAccountClientID     string                      `json:"serviceAccountClientId,omitempty"`
+	Credentials                []*CredentialRepresentation `json:"credentials,omitempty"`
 }
 
 // SetPasswordRequest sets a new password


Test patch:
diff --git a/client_test.go b/client_test.go
index 89816ec..c715925 100644
--- a/client_test.go
+++ b/client_test.go
@@ -1717,3 +1717,17 @@ func TestGoCloak_ClientSecret(t *testing.T) {
 
 	AssertNotEquals(t, oldCreds.Value, regeneratedCreds.Value)
 }
+
+func TestGoCloak_ClientServiceAccount(t *testing.T) {
+	t.Parallel()
+	cfg := GetConfig(t)
+	client := NewClientWithDebug(t)
+	token := GetAdminToken(t, client)
+
+	serviceAccount, err := client.GetClientServiceAccount(token.AccessToken, cfg.GoCloak.Realm, gocloakClientID)
+	FailIfErr(t, err, "GetClientServiceAccount failed")
+
+	AssertNotEquals(t, "", serviceAccount.ID)
+	AssertNotEquals(t, gocloakClientID, serviceAccount.ID)
+	AssertEquals(t, "service-account-gocloak", serviceAccount.Username)
+}