Gruppenmitgliedschaften abfragen

In diesem Leitfaden wird gezeigt, wie Sie Gruppenmitgliedschaften transitiv abfragen und die Mitgliedschaftsgrafik eines Mitglieds abrufen.

Zusätzlich zur Auflistung der direkten Mitglieder einer Gruppe können Sie auch transitiv sowohl direkte als auch indirekte Mitgliedschaften suchen und sich das Mitgliedschaftsdiagramm eines bestimmten Mitglieds anzeigen lassen. Diese Funktionen sind für folgende Anwendungsfälle relevant:

  • Ressourceninhaber können fundierte Entscheidungen über Änderungen an Ressourcen-ACLs treffen, wenn sie verstehen, welche Gruppen und Mitglieder von Änderungen betroffen sind.
  • Gruppeninhaber können die Auswirkungen des Hinzufügens oder Entfernens einer Gruppe zu/von einer Gruppe mit ACL-Kontrolle besser bewerten und Mitgliedschaftsprobleme einfacher lösen.
  • Sicherheitsprüfer können Zugriffsrichtlinien effektiver prüfen, da die erweiterte Mitgliedschaftsstruktur ihrer gesamten Organisation sichtbar ist.
  • Sicherheitsprüfer können das Sicherheitsrisiko eines Mitglieds bewerten. Dazu müssen sie sich ihre direkten und indirekten Gruppenmitgliedschaften ansehen oder prüfen, ob ein Mitglied zu einer bestimmten Gruppe gehört.

Eine Gruppenmitgliedschaft kann zu einer einzelnen Person, einem Dienstkonto oder einer anderen Gruppe gehören.

Das Nutzer- oder Dienstkonto, von dem die Abfrage gesendet wird, muss berechtigt sein, sich die Mitgliedschaften aller Gruppen anzusehen, die Teil der Abfrage sind. Andernfalls schlägt die Anfrage fehl. Wenn die Abfrage den Fehler "PERMISSION_DENIED" zurückgibt, haben Sie wahrscheinlich nicht die erforderlichen Berechtigungen für eine der verschachtelten Gruppen, insbesondere wenn es sich um eine Gruppe handelt, die zu einer anderen Organisation gehört.

Alle Mitgliedschaften in einer Gruppe suchen

Dieser Code gibt alle Mitgliedschaften einer Gruppe zurück. Die Antwort enthält die Art der Mitgliedschaft (direkt, indirekt oder beides) für jede Mitgliedschaft.

REST

Um eine Liste aller Mitgliedschaften in einer Gruppe zu erhalten, rufen Sie groups.memberships.searchTransitiveMemberships() mit der ID der übergeordneten Gruppe auf.

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)

Alle Gruppenmitgliedschaften eines Mitglieds suchen

REST

Um alle Gruppen zu finden, zu denen ein Mitglied gehört, rufen Sie groups.memberships.searchTransitiveGroups() mit dem Mitgliedsschlüssel auf (z. B. die E-Mail-Adresse des Mitglieds).

Python

Dieser Code gibt alle Gruppen zurück, zu denen ein Mitglied gehört (mit Ausnahme von Gruppen mit Identitätszuweisung), sowohl direkt als auch indirekt.

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)

Mitgliedschaft in einer Gruppe prüfen

REST

Um zu prüfen, ob ein Mitglied zu einer bestimmten Gruppe gehört (entweder direkt oder indirekt), rufen Sie checkTransitiveMembership() mit der ID der übergeordneten Gruppe und dem Mitgliedsschlüssel auf (beispielsweise die E-Mail-Adresse des Mitglieds).

Folgender Code bestimmt, ob das Mitglied zu einer bestimmten Gruppe gehört:

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

Mitgliederdiagramm für ein Mitglied abrufen

REST

Rufen Sie groups.memberships.getMembershipGraph() mit der ID die übergeordnete Gruppe und den Mitgliedsschlüssel (z. B. die E-Mail-Adresse des Mitglieds) auf, um das Mitgliedschaftsdiagramm (alle Gruppen, zu denen ein Mitglied gehört, zusammen mit den Pfadinformationen) eines Mitglieds zu erhalten. Das Diagramm wird als Adjazenzliste zurückgegeben.

Python

Folgender Code gibt das Mitgliedschaftsdiagramm eines angegebenen Mitglieds in einer Google-Gruppe zurück (diese Abfrage wird unter Verwendung des Labels nach Gruppentyp gefiltert):

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

Eine visuelle Darstellung des Mitgliedsdiagramms erstellen

Im Folgenden finden Sie eine Beispielantwort aus dem obigen Python-Code. In diesem Beispiel sind die Gruppen 000, 111 und 222 miteinander verbunden (Pfeile stammen von übergeordnetem Element zu übergeordnetem Element): 000 -> 111 -> 222. Einen Aufruf zum Beispielcode, um die vollständige Grafik für die Gruppe 222 abzurufen:

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

führt zur folgenden Antwort:

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

Jedes Element in der Adjazenz-Liste stellt eine Gruppe und deren direkte Mitglieder (Edges) dar. Die Antwort enthält auch Details zu allen Gruppen im Mitgliedschaftsdiagramm. Sie kann geparst werden, um alternative Darstellungen (z. B. ein DOT-Diagramm) zu generieren, die zur Visualisierung des Mitgliedsdiagramms dienen können.

Mit diesem Beispielskript kann die Antwort in eine DOT-Grafik konvertiert werden:

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

Im Folgenden ist die resultierende visuelle Hierarchie der Beispielantwort dargestellt:

Beispiel eines Mitgliedschaftsdiagramms per DOT-Konvertierung