Interroger les adhésions aux groupes

Ce guide explique comment interroger les adhésions aux groupes de manière transitoire et récupérer le graphique d'appartenance d'un membre.

En plus de répertorier les membres directs d'un groupe, vous pouvez rechercher les adhésions directes et indirectes de manière transitoire et consulter le graphique d'adhésion d'un membre spécifique. Ces fonctionnalités répondent aux cas d'utilisation suivants :

  • Les propriétaires de ressources peuvent prendre des décisions plus éclairées sur les modifications de la LCA des ressources en analysant les groupes et les membres concernés par les modifications.
  • Les propriétaires de groupe peuvent évaluer l'impact de l'ajout ou de la suppression d'un groupe dans un groupe lié au contrôle de la LCA, et résoudre plus facilement les problèmes d'adhésion.
  • Les auditeurs de sécurité peuvent auditer plus efficacement la stratégie d'accès, car la structure d'adhésion étendue de l'ensemble de l'organisation est visible.
  • Les auditeurs de sécurité peuvent évaluer le risque de sécurité d'un membre en affichant toutes leurs adhésions directes et indirectes à des groupes, ou en vérifiant si un membre appartient à un groupe spécifique.

Une adhésion à un groupe peut concerner une personne, un compte de service ou un autre groupe.

L'utilisateur ou le compte de service qui effectue la requête doit être autorisé à afficher les adhésions de tous les groupes inclus dans la requête, sinon la requête échoue. Si la requête renvoie une erreur "PERMISSION_DENIED" (autorisation refusée), il est probable que vous ne disposiez pas des autorisations appropriées pour l'un des groupes imbriqués, en particulier si l'un d'eux est un groupe appartenant à une autre organisation.

Avant de commencer

Activez l'API Cloud Identity

Activer l'API

Rechercher toutes les adhésions à un groupe

Ce code renvoie toutes les adhésions d'un groupe. La réponse inclut le type d'adhésion (directe, indirecte ou les deux) pour chacune d'elle.

REST

Pour obtenir la liste de tous les membres d'un groupe, appelez groups.memberships.searchTransitiveMemberships() avec l'ID du groupe parent.

Python

Pour vous authentifier auprès de Cloud Identity, configurez les Identifiants par défaut de l'application. Pour en savoir plus, consultez Configurer l'authentification pour un environnement de développement local.

import googleapiclient.discovery
from urllib.parse import urlencode

def search_transitive_memberships(service, parent, page_size):
  try:
    memberships = []
    next_page_token = ''
    while True:
      query_params = urlencode(
        {
          "page_size": page_size,
          "page_token": next_page_token
        }
      )
      request = service.groups().memberships().searchTransitiveMemberships(parent=parent)
      request.uri += "&" + query_params
      response = request.execute()

      if 'memberships' in response:
        memberships += response['memberships']

      if 'nextPageToken' in response:
        next_page_token = response['nextPageToken']
      else:
        next_page_token = ''

      if len(next_page_token) == 0:
        break;

    print(memberships)
  except Exception as e:
    print(e)

def main():

  service = googleapiclient.discovery.build('cloudidentity', 'v1')

  # Return results with a page size of 50
  search_transitive_memberships(service, 'groups/GROUP_ID', 50)

if __name__ == '__main__':
    main()

Rechercher toutes les adhésions d'un membre à des groupes

REST

Pour rechercher tous les groupes auxquels un membre appartient, appelez groups.memberships.searchTransitiveGroups() avec la clé de membre (par exemple, son adresse e-mail).

Python

Pour vous authentifier auprès de Cloud Identity, configurez les Identifiants par défaut de l'application. Pour en savoir plus, consultez Configurer l'authentification pour un environnement de développement local.

Ce code renvoie tous les groupes auxquels un membre appartient (à l'exception des groupe de mappage d'identité), à la fois directement et indirectement.

import googleapiclient.discovery
from urllib.parse import urlencode

def search_transitive_groups(service, member, page_size):
  try:
    groups = []
    next_page_token = ''
    while True:
      query_params = urlencode(
        {
          "query": "member_key_id == '{}' && 'cloudidentity.googleapis.com/groups.discussion_forum' in labels".format(member),
          "page_size": page_size,
          "page_token": next_page_token
        }
      )
      request = service.groups().memberships().searchTransitiveGroups(parent='groups/-')
      request.uri += "&" + query_params
      response = request.execute()

      if 'memberships' in response:
        groups += response['memberships']

      if 'nextPageToken' in response:
        next_page_token = response['nextPageToken']
      else:
        next_page_token = ''

      if len(next_page_token) == 0:
        break;

    print(groups)
  except Exception as e:
    print(e)

def main():

  service = googleapiclient.discovery.build('cloudidentity', 'v1')

  # Return results with a page size of 50
  search_transitive_groups(service, 'MEMBER_EMAIL_ADDRESS', 50)

if __name__ == '__main__':
    main()

Vérifier l'adhésion à un groupe

REST

Pour vérifier si un membre appartient à un groupe spécifique (directement ou indirectement), appelez checkTransitiveMembership() avec l'ID du groupe parent et la clé du membre (par exemple, son adresse e-mail).

Python

Pour vous authentifier auprès de Cloud Identity, configurez les Identifiants par défaut de l'application. Pour en savoir plus, consultez Configurer l'authentification pour un environnement de développement local.

Le code suivant détermine si le membre appartient à un groupe spécifique :

import googleapiclient.discovery
from urllib.parse import urlencode

def check_transitive_membership(service, parent, member):
  try:
    query_params = urlencode(
      {
        "query": "member_key_id == '{}'".format(member)
      }
    )
    request = service.groups().memberships().checkTransitiveMembership(parent=parent)
    request.uri += "&" + query_params
    response = request.execute()
    print(response['hasMembership'])
  except Exception as e:
    print(e)

def main():

  service = googleapiclient.discovery.build('cloudidentity', 'v1')

  check_transitive_membership(service, 'groups/GROUP_ID', 'MEMBER_EMAIL_ADDRESS')

if __name__ == '__main__':
    main()

Récupérer le graphique d'adhésion d'un membre

REST

Pour obtenir le graphique d'adhésion d'un membre (tous les groupes auxquels un membre appartient, ainsi que les informations de chemin d'accès), appelez groups.memberships.getMembershipGraph() avec l'ID du groupe parent et la clé de membre (par exemple, l'adresse e-mail du membre). Le graphique est renvoyé sous la forme d'une liste de contiguïté.

Python

Pour vous authentifier auprès de Cloud Identity, configurez les Identifiants par défaut de l'application. Pour en savoir plus, consultez Configurer l'authentification pour un environnement de développement local.

Le code suivant renvoie le graphique des membres d'un membre spécifié dans un groupe Google (cette requête est filtrée par type de groupe à l'aide du libellé):

import googleapiclient.discovery
from urllib.parse import urlencode

def get_membership_graph(service, parent, member):
  try:
    query_params = urlencode(
      {
        "query": "member_key_id == '{}' && 'cloudidentity.googleapis.com/groups.discussion_forum' in labels".format(member)
      }
    )
    request = service.groups().memberships().getMembershipGraph(parent=parent)
    request.uri += "&" + query_params
    response = request.execute()
    print(response['response'])
  except Exception as e:
    print(e)

def main()

  service = googleapiclient.discovery.build('cloudidentity', 'v1')

  # Specify parent group as 'groups/-' to get ALL the groups of a member
  # along with path information
  get_membership_graph(service, 'groups/GROUP_ID', 'MEMBER_KEY')

if __name__ == '__main__':
    main()

Créer une représentation visuelle du graphique d'adhésion

Voici un exemple de réponse généré à partir du code Python ci-dessus. Dans cet exemple, les groupes 000, 111 et 222 sont connectés comme suit (les flèches vont du parent vers l'enfant) : 000 -> 111 -> 222. Un appel à l'exemple de code pour récupérer le graphique complet pour le groupe 222 :

get_membership_graph(service, 'groups/-', 'group-2@example.com')

génère la réponse suivante :

{
  "@type": "type.googleapis.com/google.apps.cloudidentity.groups.v1.GetMembershipGraphResponse",
  "adjacencyList": [
    {
      "edges": [
        {
          "name": "groups/000/memberships/111",
          "preferredMemberKey": {
            "id": "group-1@example.com"
          },
          "roles": [
            {
              "name": "MEMBER"
            }
          ]
        }
      ],
      "group": "groups/000"
    },
    {
      "edges": [
        {
          "name": "groups/111/memberships/222",
          "preferredMemberKey": {
            "id": "group-2@example.com"
          },
          "roles": [
            {
              "name": "MEMBER"
            }
          ]
        }
      ],
      "group": "groups/111"
    }
  ],
  "groups": [
    {
      "name": "groups/000",
      "groupKey": {
        "id": "group-0@example.com"
      },
      "displayName": "Group - 0",
      "description": "Group - 0",
      "labels": {
        "cloudidentity.googleapis.com/groups.discussion_forum": ""
      }
    },
    {
      "name": "groups/111",
      "groupKey": {
        "id": "group-1@example.com"
      },
      "displayName": "Group - 1",
      "description": "Group - 1",
      "labels": {
        "cloudidentity.googleapis.com/groups.discussion_forum": ""
      }
    },
    {
      "name": "groups/222",
      "groupKey": {
        "id": "group-2@example.com"
      },
      "displayName": "Group - 2",
      "description": "Group - 2",
      "labels": {
        "cloudidentity.googleapis.com/groups.discussion_forum": ""
      }
    }
  ]
}

Chaque élément de la liste de contiguïté représente un groupe et ses membres directs (Edges), et la réponse inclut également des détails sur tous les groupes du graphique d'adhésion. Vous pouvez l'analyser pour générer d'autres représentations (par exemple, un nuage de points) qui peuvent être utilisées pour visualiser le graphique d'adhésion.

Cet exemple de script peut être utilisé pour convertir la réponse en nuage de points :

#
# Generates output in a dot format. Invoke this method using
# response['response'] from get_membership_graph()
#
# Save the output to a .dot file (say graph.dot)
# Use the dot tool to generate a visualization of the graph
# Example:
# dot -Tpng -o graph.png graph.dot
#
# Generates output like below:
#
# digraph {
#   'group0' [label='groups/000 (GROUP 0)'];
#   'group1' [label='groups/111 (GROUP 1)'];
#   'group2' [label='groups/222 (GROUP 2)'];
#   'group3' [label='groups/333 (GROUP 3)'];
#   'group4' [label='groups/444 (GROUP 4)'];
#
#   'group0' -> 'group1' [label='group-1@example.com (MEMBER)'];
#   'group0' -> 'group2' [label='group-2@example.com (MEMBER)'];
#   'group1' -> 'group3' [label='group-3@example.com (MEMBER)'];
#   'group3' -> 'group4' [label='group-4@example.com (MEMBER)'];
#   'group2' -> 'group3' [label='group-3@example.com (MEMBER)'];
# }
#
def convert_to_dot_format(graph):
  output = "digraph {\n"
  try:
    # Generate labels for the group nodes
    for group in graph['groups']:
      if 'displayName' in group:
        label = '{} ({})'.format(group['name'], group['displayName'])
      else:
        label = group['name']
      output += '  "{}" [label="{}"];\n'.format(group['name'].split('/')[1], label)

    output += '\n'

    # Generate edges
    for item in graph['adjacencyList']:
      group_id = item['group'].split('/')[1]
      for edge in item['edges']:
        edge_to = edge['name'].split('/')[3]
        edge_key = edge['preferredMemberKey']['id']
        # Collect the roles
        roles = []
        for role in edge['roles']:
          roles.append(role['name'])
        output += '  "{}" -> "{}" [label="{} ({})"];\n'.format(group_id,
                                                               edge_to,
                                                               edge_key,
                                                               ','.join(roles))

    output += "}\n"
    print(output)
  except Exception as e:
    print(e)

Voici la hiérarchie visuelle obtenue pour l'exemple de réponse :

Exemple de graphique d'adhésion issu d'une conversion en nuage de points