Nerzal / gocloak

golang keycloak client
Apache License 2.0
1.01k stars 275 forks source link

The function "GetGroups" doesn't return the list of all sub-groups #456

Open hosseinjdk opened 8 months ago

hosseinjdk commented 8 months ago

Describe the bug After upgrading the Keycloak from "22.0.4" to "23.0.3", I found out that the gocloak function "GetGroups" only returns the parent groups of a realm. The following code is working perfect with Keycloak-v22.0.4:

func (kcClient *KeyCloakClient) getKeyCloakGroupByPath(realm, path string) (*gocloak.Group, error) 

    groups, err := kcClient.client.GetGroups(context.Background(), kcClient.token.AccessToken, realm, gocloak.GetGroupsParams{})
    if err != nil {
        return nil, err
    }

    for _, group := range groups {
        found := traverseGroup(group, path)
        if found != nil {
            return found, nil
        }
    }
    return nil, errors.New("No Group found")
}

However we upgraded the Keycloak to "23.0.3" and after that the "GetGroups" returns only the parent groups of a realm.

Expected behavior "GetGroups" should recursively return all groups including all the sub-groups in tree just like before.

Environment:

osmian commented 8 months ago

@hosseinjdk Does it return the subgroups if you specify the params in the call you make gocloak.GetGroupsParams, specifically making BriefRepresentation false or Full as true.

hosseinjdk commented 8 months ago

@hosseinjdk Does it return the subgroups if you specify the params in the call you make gocloak.GetGroupsParams, specifically making BriefRepresentation false or Full as true.

@osmian Thanks for your answer. I tested my function with these 3 variables as an input for "kcClient.client.GetGroups":

        grpParams := gocloak.GetGroupsParams{
            Full: gocloak.BoolP(true),
        }

        grpParams := gocloak.GetGroupsParams{
            BriefRepresentation: gocloak.BoolP(false),
        }

        grpParams := gocloak.GetGroupsParams{
            BriefRepresentation: gocloak.BoolP(false),
            Full: gocloak.BoolP(true),
        }

All return the same group as a result, I mean only the parent and no subgroups.

hosseinjdk commented 8 months ago

@osmian

The following api is new in keycloak 23.0.0 for this purpose maybe. GET /admin/realms/{realm}/groups/{id}/children

Keycloak Admin REST API

osmian commented 8 months ago

@hosseinjdk @Nerzal it may be useful to make an MR and add this to the client.go so its available in future releases?

Nerzal commented 8 months ago

Yes, that would make sense. Keycloak loves to change the behaviour of stuff. It's hard to keep up with all these changes

robson90 commented 8 months ago

As already mentioned here, this workaround will work for getting "all" Groups with their SubGroups

client.GetGroups(ctx, token.AccessToken, realm, gocloak.GetGroupsParams{Search: gocloak.StringP("%")})
tduong2049 commented 7 months ago

Initial method for getting the child groups of a given group, using documentation from https://github.com/Nerzal/gocloak/issues/456#issuecomment-1874185851

// client.go

// GetChildGroups get child groups of group with id in realm
func (g *GoCloak) GetChildGroups(ctx context.Context, token, realm, groupID string) ([]*Group, error) {
    const errMessage = "could not get child groups"

    var result []*Group

    resp, err := g.getRequestWithBearerAuth(ctx, token).
        SetResult(&result).
        Get(g.getAdminRealmURL(realm, "groups", groupID, "children"))

    if err := checkForError(resp, err, errMessage); err != nil {
        return nil, err
    }

    return result, nil
}

This method could be modified in the future to include a GetChildGroupsParams parameter

// client.go

// GetChildGroups get child groups of group with id in realm
func (g *GoCloak) GetChildGroups(ctx context.Context, token, realm, groupID string, params GetChildGroupsParams) ([]*Group, error) {
    const errMessage = "could not get child groups"

    var result []*Group
    queryParams, err := GetQueryParams(params)
    if err != nil {
        return nil, errors.Wrap(err, errMessage)
    }

    resp, err := g.getRequestWithBearerAuth(ctx, token).
        SetResult(&result).
        SetQueryParams(queryParams).
        Get(g.getAdminRealmURL(realm, "groups", groupID, "children"))

    if err := checkForError(resp, err, errMessage); err != nil {
        return nil, err
    }

    return result, nil
}
// models.go

// GetChildGroupsParams represents the optional parameters for getting child groups
type GetChildGroupsParams struct {
    First               *int    `json:"first,string,omitempty"`
    Max                 *int    `json:"max,string,omitempty"`
    BriefRepresentation *bool   `json:"briefRepresentation,string,omitempty"`
}
hosseinjdk commented 7 months ago

As already mentioned here, this workaround will work for getting "all" Groups with their SubGroups

client.GetGroups(ctx, token.AccessToken, realm, gocloak.GetGroupsParams{Search: gocloak.StringP("%")})

Thank you very much. This workaround works.