Repository: Nerzal/gocloak
Pull request #207 (2020-08-18 19:42:11)

Adding multiple methods for dealing with realm roles
Depends on pull request https://github.com/Nerzal/gocloak/pull/206


Patch:
diff --git a/README.md b/README.md
index a5d3c8b..62c134f 100644
--- a/README.md
+++ b/README.md
@@ -206,7 +206,11 @@ type GoCloak interface {
 	DeleteRealmRoleFromGroup(ctx context.Context, token, realm, groupID string, roles []Role) error
 	AddRealmRoleComposite(ctx context.Context, token, realm, roleName string, roles []Role) error
 	DeleteRealmRoleComposite(ctx context.Context, token, realm, roleName string, roles []Role) error
-
+	GetCompositeRealmRolesByRoleID(ctx context.Context, token, realm, roleID string) ([]*Role, error)
+	GetCompositeRealmRolesByUserID(ctx context.Context, token, realm, userID string) ([]*Role, error)
+	GetCompositeRealmRolesByGroupID(ctx context.Context, token, realm, groupID string) ([]*Role, error)
+	GetAvailableRealmRolesByUserID(ctx context.Context, token, realm, userID string) ([]*Role, error)
+	GetAvailableRealmRolesByGroupID(ctx context.Context, token, realm, groupID string) ([]*Role, error)
 
 	// *** Client Roles ***
 
diff --git a/client.go b/client.go
index 8c639ac..c1c7243 100644
--- a/client.go
+++ b/client.go
@@ -1277,7 +1277,7 @@ func (client *gocloak) GetCompositeClientRolesByUserID(ctx context.Context, toke
 	return result, nil
 }
 
-// GetAvailableClientRolesByUserID returns all available roles to the given user
+// GetAvailableClientRolesByUserID returns all available client roles to the given user
 func (client *gocloak) GetAvailableClientRolesByUserID(ctx context.Context, token, realm, clientID, userID string) ([]*Role, error) {
 	const errMessage = "could not get available client roles by user id"
 
@@ -1541,6 +1541,86 @@ func (client *gocloak) DeleteRealmRoleComposite(ctx context.Context, token, real
 	return checkForError(resp, err, errMessage)
 }
 
+// GetCompositeRealmRolesByRoleID returns all realm composite roles associated with the given client role
+func (client *gocloak) GetCompositeRealmRolesByRoleID(ctx context.Context, token, realm, roleID string) ([]*Role, error) {
+	const errMessage = "could not get composite client roles by role id"
+
+	var result []*Role
+	resp, err := client.getRequestWithBearerAuth(ctx, token).
+		SetResult(&result).
+		Get(client.getAdminRealmURL(realm, "roles-by-id", roleID, "composites", "realm"))
+
+	if err = checkForError(resp, err, errMessage); err != nil {
+		return nil, err
+	}
+
+	return result, nil
+}
+
+// GetCompositeRealmRolesByUserID returns all realm roles and composite roles assigned to the given user
+func (client *gocloak) GetCompositeRealmRolesByUserID(ctx context.Context, token, realm, userID string) ([]*Role, error) {
+	const errMessage = "could not get composite client roles by user id"
+
+	var result []*Role
+	resp, err := client.getRequestWithBearerAuth(ctx, token).
+		SetResult(&result).
+		Get(client.getAdminRealmURL(realm, "users", userID, "role-mappings", "realm", "composite"))
+
+	if err = checkForError(resp, err, errMessage); err != nil {
+		return nil, err
+	}
+
+	return result, nil
+}
+
+// GetCompositeRealmRolesByGroupID returns all realm roles and composite roles assigned to the given group
+func (client *gocloak) GetCompositeRealmRolesByGroupID(ctx context.Context, token, realm, groupID string) ([]*Role, error) {
+	const errMessage = "could not get composite client roles by user id"
+
+	var result []*Role
+	resp, err := client.getRequestWithBearerAuth(ctx, token).
+		SetResult(&result).
+		Get(client.getAdminRealmURL(realm, "groups", groupID, "role-mappings", "realm", "composite"))
+
+	if err = checkForError(resp, err, errMessage); err != nil {
+		return nil, err
+	}
+
+	return result, nil
+}
+
+// GetAvailableRealmRolesByUserID returns all available realm roles to the given user
+func (client *gocloak) GetAvailableRealmRolesByUserID(ctx context.Context, token, realm, userID string) ([]*Role, error) {
+	const errMessage = "could not get available client roles by user id"
+
+	var result []*Role
+	resp, err := client.getRequestWithBearerAuth(ctx, token).
+		SetResult(&result).
+		Get(client.getAdminRealmURL(realm, "users", userID, "role-mappings", "realm", "available"))
+
+	if err = checkForError(resp, err, errMessage); err != nil {
+		return nil, err
+	}
+
+	return result, nil
+}
+
+// GetAvailableRealmRolesByGroupID returns all available realm roles to the given group
+func (client *gocloak) GetAvailableRealmRolesByGroupID(ctx context.Context, token, realm, groupID string) ([]*Role, error) {
+	const errMessage = "could not get available client roles by user id"
+
+	var result []*Role
+	resp, err := client.getRequestWithBearerAuth(ctx, token).
+		SetResult(&result).
+		Get(client.getAdminRealmURL(realm, "groups", groupID, "role-mappings", "realm", "available"))
+
+	if err = checkForError(resp, err, errMessage); err != nil {
+		return nil, err
+	}
+
+	return result, nil
+}
+
 // -----
 // Realm
 // -----
diff --git a/gocloak.go b/gocloak.go
index 0940815..15189ce 100644
--- a/gocloak.go
+++ b/gocloak.go
@@ -179,6 +179,16 @@ type GoCloak interface {
 	AddRealmRoleComposite(ctx context.Context, token, realm, roleName string, roles []Role) error
 	// AddRealmRoleComposite adds roles as composite
 	DeleteRealmRoleComposite(ctx context.Context, token, realm, roleName string, roles []Role) error
+	// GetCompositeRealmRolesByRoleID returns all realm composite roles associated with the given client role
+	GetCompositeRealmRolesByRoleID(ctx context.Context, token, realm, roleID string) ([]*Role, error)
+	// GetCompositeRealmRolesByUserID returns all realm roles and composite roles assigned to the given user
+	GetCompositeRealmRolesByUserID(ctx context.Context, token, realm, userID string) ([]*Role, error)
+	// GetCompositeRealmRolesByGroupID returns all realm roles and composite roles assigned to the given group
+	GetCompositeRealmRolesByGroupID(ctx context.Context, token, realm, groupID string) ([]*Role, error)
+	// GetAvailableRealmRolesByUserID returns all available realm roles to the given user
+	GetAvailableRealmRolesByUserID(ctx context.Context, token, realm, userID string) ([]*Role, error)
+	// GetAvailableRealmRolesByGroupID returns all available realm roles to the given group
+	GetAvailableRealmRolesByGroupID(ctx context.Context, token, realm, groupID string) ([]*Role, error)
 
 	// *** Client Roles ***
 
@@ -206,9 +216,9 @@ type GoCloak interface {
 	GetCompositeClientRolesByUserID(ctx context.Context, token, realm, clientID, userID string) ([]*Role, error)
 	// GetCompositeClientRolesByGroupID returns all client roles and composite roles assigned to the given group
 	GetCompositeClientRolesByGroupID(ctx context.Context, token, realm, clientID, groupID string) ([]*Role, error)
-	// GetAvailableClientRolesByUserID returns all available roles to the given user
+	// GetAvailableClientRolesByUserID returns all available client roles to the given user
 	GetAvailableClientRolesByUserID(ctx context.Context, token, realm, clientID, userID string) ([]*Role, error)
-	// GetAvailableClientRolesByGroupID returns all available roles to the given group
+	// GetAvailableClientRolesByGroupID returns all available client roles to the given group
 	GetAvailableClientRolesByGroupID(ctx context.Context, token, realm, clientID, groupID string) ([]*Role, error)
 
 	// GetClientRole get a role for the given client in a realm by role name


Test patch:
diff --git a/client_test.go b/client_test.go
index d2f1fbc..fa3c3f2 100644
--- a/client_test.go
+++ b/client_test.go
@@ -2059,6 +2059,25 @@ func TestGocloak_GetRealmRolesByUserID(t *testing.T) {
 		userID)
 	require.NoError(t, err)
 	t.Logf("User roles: %+v", roles)
+	var found bool
+	for _, r := range roles {
+		if r.Name == nil {
+			continue
+		}
+		if *r.Name == *role.Name {
+			found = true
+			break
+		}
+	}
+	require.True(t, found, "The role has not been found in the assigned roles. Role: %+v", *role)
+
+	roles, err = client.GetCompositeRealmRolesByUserID(
+		context.Background(),
+		token.AccessToken,
+		cfg.GoCloak.Realm,
+		userID)
+	require.NoError(t, err)
+	t.Logf("User roles: %+v", roles)
 	for _, r := range roles {
 		if r.Name == nil {
 			continue
@@ -2067,7 +2086,7 @@ func TestGocloak_GetRealmRolesByUserID(t *testing.T) {
 			return
 		}
 	}
-	require.Fail(t, "The role has not been found in the assigned roles. Role: %+v", *role)
+	require.Fail(t, "The role has not been found in the assigned composite roles. Role: %+v", *role)
 }
 
 func TestGocloak_GetRealmRolesByGroupID(t *testing.T) {
@@ -3048,6 +3067,79 @@ func TestGoCloak_GetAvailableClientRolesByUserID(t *testing.T) {
 	require.Fail(t, "The role2 has not been found in the assigned composite roles. Role: %+v", *role2)
 }
 
+func TestGoCloak_GetAvailableRealmRolesByUserID(t *testing.T) {
+	t.Parallel()
+	cfg := GetConfig(t)
+	client := NewClientWithDebug(t)
+	token := GetAdminToken(t, client)
+
+	tearDownUser, userID := CreateUser(t, client)
+	defer tearDownUser()
+	tearDownRole, roleName1 := CreateRealmRole(t, client)
+	defer tearDownRole()
+	tearDownRole2, roleName2 := CreateRealmRole(t, client)
+	defer tearDownRole2()
+
+	role1, err := client.GetRealmRole(
+		context.Background(),
+		token.AccessToken,
+		cfg.GoCloak.Realm,
+		roleName1)
+	require.NoError(t, err)
+
+	role2, err := client.GetRealmRole(
+		context.Background(),
+		token.AccessToken,
+		cfg.GoCloak.Realm,
+		roleName2)
+	require.NoError(t, err)
+
+	err = client.AddRealmRoleToUser(
+		context.Background(),
+		token.AccessToken,
+		cfg.GoCloak.Realm,
+		userID,
+		[]gocloak.Role{*role1},
+	)
+	require.NoError(t, err)
+
+	roles, err := client.GetRealmRolesByUserID(
+		context.Background(),
+		token.AccessToken,
+		cfg.GoCloak.Realm,
+		userID)
+	require.NoError(t, err)
+	t.Logf("User roles: %+v", roles)
+	var found bool
+	for _, r := range roles {
+		if r.Name == nil {
+			continue
+		}
+		if *r.Name == *role1.Name {
+			found = true
+			break
+		}
+	}
+	require.True(t, found, "The role1 has not been found in the assigned roles. Role: %+v", *role1)
+
+	roles, err = client.GetAvailableRealmRolesByUserID(
+		context.Background(),
+		token.AccessToken,
+		cfg.GoCloak.Realm,
+		userID)
+	require.NoError(t, err)
+	t.Logf("User roles: %+v", roles)
+	for _, r := range roles {
+		if r.Name == nil {
+			continue
+		}
+		if *r.Name == *role2.Name {
+			return
+		}
+	}
+	require.Fail(t, "The role2 has not been found in the assigned composite roles. Role: %+v", *role2)
+}
+
 func TestGoCloak_GetAvailableClientRolesByGroupID(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
@@ -3126,6 +3218,80 @@ func TestGoCloak_GetAvailableClientRolesByGroupID(t *testing.T) {
 	require.Fail(t, "The role2 has not been found in the assigned composite roles. Role: %+v", *role2)
 }
 
+func TestGoCloak_GetAvailableRealmRolesByGroupID(t *testing.T) {
+	t.Parallel()
+	cfg := GetConfig(t)
+	client := NewClientWithDebug(t)
+	token := GetAdminToken(t, client)
+
+	tearDownGroup, groupID := CreateGroup(t, client)
+	defer tearDownGroup()
+	tearDownRole, roleName1 := CreateRealmRole(t, client)
+	defer tearDownRole()
+	tearDownRole2, roleName2 := CreateRealmRole(t, client)
+	defer tearDownRole2()
+
+	role1, err := client.GetRealmRole(
+		context.Background(),
+		token.AccessToken,
+		cfg.GoCloak.Realm,
+		roleName1)
+	require.NoError(t, err)
+
+	role2, err := client.GetRealmRole(
+		context.Background(),
+		token.AccessToken,
+		cfg.GoCloak.Realm,
+		roleName2)
+	require.NoError(t, err)
+
+	err = client.AddRealmRoleToGroup(
+		context.Background(),
+		token.AccessToken,
+		cfg.GoCloak.Realm,
+		groupID,
+		[]gocloak.Role{*role1},
+	)
+	require.NoError(t, err)
+
+	roles, err := client.GetRealmRolesByGroupID(
+		context.Background(),
+		token.AccessToken,
+		cfg.GoCloak.Realm,
+		groupID)
+	require.NoError(t, err)
+
+	t.Logf("Group roles: %+v", roles)
+	var found bool
+	for _, r := range roles {
+		if r.Name == nil {
+			continue
+		}
+		if *r.Name == *role1.Name {
+			found = true
+			break
+		}
+	}
+	require.True(t, found, "The role1 has not been found in the assigned roles. Role: %+v", *role1)
+
+	roles, err = client.GetAvailableRealmRolesByGroupID(
+		context.Background(),
+		token.AccessToken,
+		cfg.GoCloak.Realm,
+		groupID)
+	require.NoError(t, err)
+	t.Logf("Group roles: %+v", roles)
+	for _, r := range roles {
+		if r.Name == nil {
+			continue
+		}
+		if *r.Name == *role2.Name {
+			return
+		}
+	}
+	require.Fail(t, "The role2 has not been found in the assigned composite roles. Role: %+v", *role2)
+}
+
 func TestGocloak_GetClientRolesByGroupID(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
@@ -3264,6 +3430,63 @@ func TestGocloak_AddDeleteClientRoleComposite(t *testing.T) {
 	require.NoError(t, err, "DeleteClientRoleComposite failed")
 }
 
+func TestGocloak_AddDeleteRealmRoleComposite(t *testing.T) {
+	t.Parallel()
+	cfg := GetConfig(t)
+	client := NewClientWithDebug(t)
+	token := GetAdminToken(t, client)
+
+	tearDown, compositeRole := CreateRealmRole(t, client)
+	defer tearDown()
+
+	tearDown, role := CreateRealmRole(t, client)
+	defer tearDown()
+
+	compositeRoleModel, err := client.GetRealmRole(
+		context.Background(),
+		token.AccessToken,
+		cfg.GoCloak.Realm,
+		compositeRole,
+	)
+	require.NoError(t, err, "Can't get just created role with GetRealmRole")
+
+	roleModel, err := client.GetRealmRole(
+		context.Background(),
+		token.AccessToken,
+		cfg.GoCloak.Realm,
+		role,
+	)
+	require.NoError(t, err, "Can't get just created role with GetRealmRole")
+
+	err = client.AddRealmRoleComposite(
+		context.Background(),
+		token.AccessToken,
+		cfg.GoCloak.Realm,
+		*compositeRoleModel.Name,
+		[]gocloak.Role{*roleModel},
+	)
+	require.NoError(t, err, "AddRealmRoleComposite failed")
+
+	compositeRoles, err := client.GetCompositeRealmRolesByRoleID(
+		context.Background(),
+		token.AccessToken,
+		cfg.GoCloak.Realm,
+		*compositeRoleModel.ID,
+	)
+	require.NoError(t, err, "GetCompositeRealmRolesByRoleID failed")
+	require.GreaterOrEqual(t, len(compositeRoles), 1, "GetCompositeRealmRolesByRoleID didn't return any composite roles")
+	require.Equal(t, *(roleModel.ID), *(compositeRoles[0].ID), "GetCompositeRealmRolesByRoleID returned wrong composite role")
+
+	err = client.DeleteRealmRoleComposite(
+		context.Background(),
+		token.AccessToken,
+		cfg.GoCloak.Realm,
+		*compositeRoleModel.Name,
+		[]gocloak.Role{*roleModel},
+	)
+	require.NoError(t, err, "DeleteRealmRoleComposite failed")
+}
+
 func TestGocloak_CreateGetDeleteUserFederatedIdentity(t *testing.T) {
 	cfg := GetConfig(t)
 	client := NewClientWithDebug(t)