Consulta las membresías de grupos

En esta guía, se demuestra cómo consultar de forma transitiva las membresías de un grupo y recuperar el gráfico de membresías 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 del grupo 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 un error "PERMISSION_DENIED", es probable que no tengas los permisos correctos para uno de los grupos anidados, en especial si uno de ellos es un grupo que pertenece a otra organización.

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 en un grupo, llama a groups.memberships.searchTransitiveMemberships() con el ID del grupo superior.

Python

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)

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

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 de miembro (por ejemplo, la dirección de correo electrónico del miembro).

Python

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

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)

# Get credentials and a handle to the service definition
# Return results with a page size of 50
search_transitive_groups(service, 'joe@example.com', 50)

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 directa), llama a checkTransitiveMembership() con el ID del grupo superior y la clave de miembro (por ejemplo, la dirección de correo electrónico del miembro).

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

Python

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)

# Get credentials and a handle to the service definition
check_transitive_membership(service, 'groups/{group_id}', 'joe@example.com')

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

Con el siguiente código, se muestra el grafo de membresía de un miembro especificado en un Grupo de Google (esta consulta se filtra por tipo de grupo mediante la etiqueta):

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)

# Get credentials and a handle to the service definition
# 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}')

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