Como consultar associações a grupos

Neste guia, demonstramos como consultar temporariamente as associações a grupos e recuperar o gráfico de associação de um membro.

Além de listar os membros diretos de um grupo, é possível pesquisar transitivamente por assinaturas diretas e indiretas e visualizar o gráfico de associação de um membro específico. Esses recursos abordam os seguintes casos de uso:

  • Os proprietários de recursos podem tomar decisões mais fundamentadas sobre alterações na ACL do recurso. Para isso, é preciso entender quais grupos e membros são afetados pelas alterações.
  • Os proprietários de grupos podem avaliar o impacto de adicionar ou remover um grupo de um grupo relacionado ao controle de ACL e resolver mais facilmente problemas de associação.
  • Os auditores de segurança podem auditar a política de acesso com mais eficácia porque a estrutura de associação expandida da organização inteira fica visível.
  • Os auditores de segurança podem avaliar o risco de segurança de um membro visualizando todas as suas associações a grupos diretas e indiretas ou verificando se um membro pertence a um grupo específico.

Observação: uma associação a um grupo pode pertencer a uma pessoa, uma conta de serviço ou a outro grupo.

A conta de usuário ou de serviço que está fazendo a consulta precisa ter permissão para visualizar as associações de todos os grupos que fazem parte da consulta. Caso contrário, a solicitação falhará. Se a consulta retorna um erro "PERMISSION_DENIED", é provável que você não tenha as permissões corretas para um dos grupos aninhados, especialmente se um deles for de um grupo de outra organização.

Antes de começar

Ative a API Cloud Identity.

Ative a API

Como pesquisar por todas as associações de um grupo

Esse código retorna todas as associações de um grupo. A resposta inclui o tipo de associação (direta, indireta ou ambas) para cada associação.

REST

Para conferir uma lista de todas as associações em um grupo, chame groups.memberships.searchTransitiveMemberships() com o ID do grupo pai.

Python

Para autenticar no Cloud Identity, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento 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()

Pesquisar todas as associações a grupos de um membro

REST

Para encontrar todos os grupos a que um membro pertence, chame groups.memberships.searchTransitiveGroups() com a chave do membro (por exemplo, o endereço de e-mail dele).

Python

Para autenticar no Cloud Identity, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento local.

Esse código retorna todos os grupos a que um membro pertence (exceto grupos mapeados por identidade), direta e indiretamente.

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

Como verificar a participação em um grupo

REST

Para verificar se um membro pertence a um grupo específico (direta ou indiretamente), chame checkTransitiveMembership() com o ID do grupo pai e a chave do membro (por exemplo, o endereço de e-mail do membro).

Python

Para autenticar no Cloud Identity, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento local.

O código a seguir determina se o membro pertence a um 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()

Como recuperar o gráfico de associação de um membro

REST

Para ver o gráfico de associação de um membro (todos os grupos a que um membro pertence e as informações do caminho), chame groups.memberships.getMembershipGraph() com o código do grupo pai e a chave de membro (por exemplo, o endereço de e-mail do membro). O gráfico é retornado como uma lista de adjacências.

Python

Para autenticar no Cloud Identity, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento local.

O código a seguir retorna o gráfico de associação de um membro especificado em um Grupo do Google. Essa consulta é filtrada por tipo de grupo usando o rótulo:

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

Como criar uma representação visual do gráfico de associação

Veja a seguir um exemplo de resposta do código Python acima. Neste exemplo, os grupos 000, 111 e 222 são conectados da seguinte forma (as setas são de pai para filho): 000 -> 111 -> 222. Uma chamada ao código de amostra para recuperar o gráfico completo do grupo 222:

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

resulta na seguinte resposta:

{
  "@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 item na lista de adjacência representa um grupo e seus membros diretos (bordas), e a resposta também inclui detalhes de todos os grupos no gráfico de associação. Ele pode ser analisado para gerar representações alternativas (por exemplo, um gráfico DOT) que pode ser usado para visualizar o gráfico de associação.

Este exemplo de script pode ser usado para converter a resposta em um 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)

Veja a seguir a hierarquia visual resultante para a amostra de resposta:

Gráfico de amostra de associação da conversão de DOT