Como consultar associações a grupos

Neste guia, você verá como consultar as assinaturas de grupo de maneira transitiva 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.

Uma associação a um grupo pode pertencer a um indivíduo, uma conta de serviço ou outro grupo.

O usuário ou a conta 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 retornar 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 um grupo de outra organização.

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 ver uma lista de todas as assinaturas de um grupo, chame groups.memberships.searchTransitiveMemberships() com o ID do grupo pai.

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)

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 de membro (por exemplo, o endereço de e-mail do membro).

Python

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

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)

Como verificar a participação em um grupo

REST

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

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

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

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

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}')

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