Consulta las membresías de grupos

En esta guía, se muestra cómo consultar de forma transitiva las membresías grupales y recuperar el gráfico de membresía de un miembro.

Además de enumerar los miembros directos de un grupo, puedes buscar de forma transitiva y directa las membresías indirectas y directas, y ver el grafo de membresía de un miembro específico. Estas capacidades abordan los siguientes caso de uso:

  • Los propietarios de los recursos pueden tomar decisiones más fundamentadas sobre los cambios en la LCA del recurso, ya que comprenden qué grupos y miembros se ven afectados por ellos.
  • Los propietarios de grupos pueden evaluar el impacto de agregar o quitar un grupo de un grupo relacionado con el control de la LCA, y resolver más fácilmente los problemas de membresías.
  • Los auditores de seguridad pueden auditar de manera más eficiente la política de acceso, ya que la estructura de membresía expandida de toda la organización es visible.
  • Los auditores de seguridad pueden evaluar el riesgo de seguridad de un miembro si ven todas sus membresías grupales indirectas y directas, o si un miembro pertenece a un grupo específico.

Una membresía grupal puede pertenecer a una persona, a una cuenta de servicio o a otro grupo.

La cuenta de servicio o usuario que realiza la consulta debe tener permiso para ver las membresías de todos los grupos que forman parte de la consulta; de lo contrario, la solicitud fallará. Si la consulta muestra el error "PERMISSION_DENIED", es probable que no tengas los permisos correctos para uno de los grupos anidados, en especial, si uno de ellos es propiedad de otra organización.

Antes de comenzar

Habilita la API de Cloud Identity.

Habilita la API

Busca todas las membresías en un grupo

En este código, se muestran todas las membresías de un grupo. La respuesta incluye el tipo de membresía (directa, indirecta o ambas) para cada membresía.

REST

Para obtener una lista de todas las membresías de un grupo, llama a groups.memberships.searchTransitiveMemberships() con el ID del grupo superior.

Python

Para autenticarte en Cloud Identity, configura las credenciales predeterminadas de la aplicación. Si deseas obtener más información, consulta Configura la autenticación para un entorno de desarrollo 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()

Busca todas las membresías grupales de un miembro

REST

Para encontrar todos los grupos a los que pertenece un miembro, llama a groups.memberships.searchTransitiveGroups() con la clave del miembro (por ejemplo, su dirección de correo electrónico).

Python

Para autenticarte en Cloud Identity, configura las credenciales predeterminadas de la aplicación. Si deseas obtener más información, consulta Configura la autenticación para un entorno de desarrollo local.

Este código muestra todos los grupos a los que pertenece un miembro (excepto los grupos con identidades asignadas), indirecta y directamente.

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()

Verifica la membresía en un grupo

REST

Para verificar si un miembro pertenece a un grupo específico (ya sea de forma directa o indirecta), llama a checkTransitiveMembership() con el ID del grupo superior y la clave del miembro (por ejemplo, su dirección de correo electrónico).

Python

Para autenticarte en Cloud Identity, configura las credenciales predeterminadas de la aplicación. Si deseas obtener más información, consulta Configura la autenticación para un entorno de desarrollo local.

El siguiente código determina si el miembro pertenece a un grupo específico:

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()

Recupera el grafo de membresía de un miembro

REST

Para obtener el grafo de membresía de un miembro (todos los grupos a los que pertenece un miembro, junto con la información de la ruta), llama a groups.memberships.getMembershipGraph() con el ID del grupo superior y la clave de miembro (por ejemplo, la dirección de correo electrónico del miembro) El grafo se muestra como una lista de ajustes.

Python

Para autenticarte en Cloud Identity, configura las credenciales predeterminadas de la aplicación. Si deseas obtener más información, consulta Configura la autenticación para un entorno de desarrollo local.

El siguiente código muestra el gráfico de membresía de un miembro especificado en un Grupo de Google (esta consulta se filtra por tipo de grupo con la etiqueta):

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()

Crea una representación visual del grafo de membresía

A continuación, se muestra una respuesta de ejemplo del código de Python anterior. En este ejemplo, los grupos 000, 111 y 222 se conectan de la siguiente manera (las flechas son de superior a secundaria): 000 -> 111 -> 222. Una llamada al código de muestra a fin de recuperar el grafo completo para el grupo 222:

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

genera la siguiente respuesta:

{
  "@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": ""
      }
    }
  ]
}

Cada elemento de la lista de ajustes representa un grupo y sus miembros directos (bordes) y la respuesta también incluye detalles de todos los grupos en el grafo de membresía. Se puede analizar a fin de generar representaciones alternativas (por ejemplo, un grafo DOT) que se puede usar para visualizar el gráfico de membresía.

Esta secuencia de comandos de ejemplo puede usarse para convertir la respuesta en un grafo DOT:

#
# 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)

A continuación, se muestra la jerarquía visual resultante para la respuesta de muestra:

Grafo de membresía de muestra de conversión de la conversión de DOT