Realizar consultas a suscripciones de grupos

En esta guía se muestra cómo consultar de forma transitiva las pertenencias a grupos y cómo obtener el gráfico de pertenencia de un miembro.

Además de enumerar los miembros directos de un grupo, puedes buscar de forma transitiva las pertenencias directas e indirectas y ver el gráfico de pertenencia de un miembro específico. Estas funciones se aplican a los siguientes casos prácticos:

  • Los propietarios de los recursos pueden tomar decisiones más fundamentadas sobre los cambios en las listas de control de acceso de los recursos si saben qué grupos y miembros se ven afectados por los cambios.
  • Los propietarios de grupos pueden evaluar el impacto de añadir o quitar un grupo de otro relacionado con el control de acceso y resolver más fácilmente los problemas de pertenencia.
  • Los auditores de seguridad pueden auditar las políticas de acceso de forma más eficaz porque pueden ver la estructura de pertenencia ampliada de toda su organización.
  • Los auditores de seguridad pueden evaluar el riesgo de seguridad de un miembro viendo todas sus pertenencias a grupos directas e indirectas, o comprobando si un miembro pertenece a un grupo específico.

Una pertenencia a un grupo puede ser de un usuario, una cuenta de servicio u otro grupo.

El usuario o la cuenta de servicio que realiza la consulta debe tener permiso para ver las pertenencias de todos los grupos que forman parte de la consulta. De lo contrario, la solicitud fallará. Si la consulta devuelve un error "PERMISSION_DENIED", es probable que no tengas los permisos correctos para uno de los grupos anidados, sobre todo si uno de ellos es un grupo propiedad de otra organización.

.

Antes de empezar

Enable the Cloud Identity API.

Roles required to enable APIs

To enable APIs, you need the Service Usage Admin IAM role (roles/serviceusage.serviceUsageAdmin), which contains the serviceusage.services.enable permission. Learn how to grant roles.

Enable the API

Buscar todas las suscripciones de un grupo

Este código devuelve todas las pertenencias de un grupo. La respuesta incluye el tipo de suscripción (directa, indirecta o ambas) de cada suscripción.

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. Para obtener más información, consulta el artículo Configurar la autenticación en 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()

Buscar todas las pertenencias a grupos 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, la dirección de correo electrónico del miembro).

Python

Para autenticarte en Cloud Identity, configura las credenciales predeterminadas de la aplicación. Para obtener más información, consulta el artículo Configurar la autenticación en un entorno de desarrollo local.

Este código devuelve todos los grupos a los que pertenece un miembro (excepto los grupos asignados por identidad), tanto directa como indirectamente.

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

Comprobar la pertenencia a un grupo

REST

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

Python

Para autenticarte en Cloud Identity, configura las credenciales predeterminadas de la aplicación. Para obtener más información, consulta el artículo Configurar la autenticación en 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()

Obtener el gráfico de pertenencia de un miembro

REST

Para obtener el gráfico de pertenencia 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 del miembro (por ejemplo, la dirección de correo del miembro). El gráfico se devuelve como una lista de adyacencia.

Python

Para autenticarte en Cloud Identity, configura las credenciales predeterminadas de la aplicación. Para obtener más información, consulta el artículo Configurar la autenticación en un entorno de desarrollo local.

El siguiente código devuelve el gráfico de pertenencia de un miembro especificado en un grupo de Google (esta consulta se filtra por tipo de grupo mediante 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()

Crear una representación visual del gráfico de pertenencia

A continuación se muestra una respuesta de ejemplo del código Python anterior. En este ejemplo, los grupos 000, 111 y 222 están conectados de la siguiente manera (las flechas van del elemento superior al inferior): 000 -> 111 -> 222. Una llamada al código de ejemplo para obtener el gráfico completo del grupo 222:

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

El resultado es 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 adyacencia representa un grupo y sus miembros directos (aristas), y la respuesta también incluye detalles de todos los grupos del gráfico de pertenencia. Se puede analizar para generar representaciones alternativas (por ejemplo, un gráfico DOT) que se pueden usar para visualizar el gráfico de pertenencia.

Esta secuencia de comandos de ejemplo se puede usar para convertir la respuesta en un gráfico 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 de la respuesta de ejemplo:

Gráfico de membresía de ejemplo de la conversión de DOT