グループ メンバーシップのクエリ
このガイドでは、グループ メンバーシップの横断的なクエリ方法と、メンバーのメンバーシップ グラフを取得する方法について説明します。
グループの直接的なメンバーを一覧表示するだけでなく、直接的なメンバーと間接的なメンバーの両方を横断して検索し、特定のメンバーのメンバーシップ グラフを表示できます。これらの機能は、次のようなユースケースに対応するものです。
- リソースのオーナーは、変更の影響を受けるグループとメンバーを把握することで、リソース ACL の変更について、十分な情報に基づく意思決定を行えます。
- グループ オーナーは、ACL 管理に関連するグループに対してグループを追加または削除した場合の影響を評価でき、メンバーシップの問題をより簡単に解決できます。
- 組織全体の詳細なメンバーシップ構造を把握できるため、セキュリティ監査者はアクセス ポリシーをより効果的に監査できます。
- セキュリティ監査者は、メンバーの直接的および間接的なグループ メンバーをすべて表示するか、メンバーが特定のグループに属しているかどうかを確認することで、メンバーのセキュリティ リスクを評価できます。
グループ メンバーシップは、個人、サービス アカウント、別のグループにより所有されます。
クエリを行うユーザーまたはサービス アカウントには、クエリの一部であるすべてのグループのメンバーシップを表示する権限が必要です。そうでない場合、リクエストは失敗します。クエリから「PERMISSION_DENIED」エラーが返された場合は、ネストされたグループ(特に、あるグループが別の組織が所有するグループである場合)の 1 つに対して、適切な権限がない可能性があります。
始める前に
Enable the Cloud Identity API.
グループ内の全メンバーを検索する
このコードは、グループの全メンバーを返します。そのレスポンスは、各メンバーのメンバーシップの種類(直接、間接、またはその両方)を含んでいます。
REST
グループ内の全メンバーのリストを取得するには、親グループの ID を指定して groups.memberships.searchTransitiveMemberships()
を呼び出します。
Python
Cloud Identity で認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証の設定をご覧ください。
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()
メンバーのグループ メンバーシップをすべて検索する
REST
あるメンバーが所属するグループをすべて見つけるには、メンバーキー(たとえば、メンバーのメールアドレス)を指定して groups.memberships.searchTransitiveGroups()
を呼び出します。
Python
Cloud Identity で認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証の設定をご覧ください。
このコードは、メンバーが直接的または間接的に所属しているすべてのグループ(ID マッピング グループを除く)を返します。
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()
グループのメンバーシップを確認する
REST
メンバーが特定のグループに(直接または間接的に)所属しているかどうかを確認するには、親グループの ID とメンバーキー(メンバーのメールアドレスなど)を指定して checkTransitiveMembership()
を呼び出します。
Python
Cloud Identity で認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証の設定をご覧ください。
次のコードは、メンバーが特定のグループに所属しているかどうかを判定します。
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()
メンバーのメンバーシップ グラフを取得する
REST
メンバーのメンバーシップ グラフ(メンバーが所属するすべてのグループとそのパスの情報)を取得するには、親グループの ID とメンバーキー(メンバーのメールアドレスなど)を指定して groups.memberships.getMembershipGraph()
を呼び出します。グラフは、隣接リストとして返されます。
Python
Cloud Identity で認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証の設定をご覧ください。
次のコードは、指定したメンバーの、ある Google グループ内におけるメンバーシップ グラフを返します(このクエリは、ラベルを使用してグループタイプでフィルタリングされています)。
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()
メンバーシップ グラフの視覚的な表示を作成する
上記 Python コードのレスポンスの例を、次に示します。この例では、グループ 000、111、222 が、000 -> 111 -> 222 のようにつながっています(矢印は親から子に向きます)。グループ 222 の完全なグラフを取得するサンプルコードは、次のとおりです。
get_membership_graph(service, 'groups/-', '[email protected]')
次のレスポンスが得られます。
{
"@type": "type.googleapis.com/google.apps.cloudidentity.groups.v1.GetMembershipGraphResponse",
"adjacencyList": [
{
"edges": [
{
"name": "groups/000/memberships/111",
"preferredMemberKey": {
"id": "[email protected]"
},
"roles": [
{
"name": "MEMBER"
}
]
}
],
"group": "groups/000"
},
{
"edges": [
{
"name": "groups/111/memberships/222",
"preferredMemberKey": {
"id": "[email protected]"
},
"roles": [
{
"name": "MEMBER"
}
]
}
],
"group": "groups/111"
}
],
"groups": [
{
"name": "groups/000",
"groupKey": {
"id": "[email protected]"
},
"displayName": "Group - 0",
"description": "Group - 0",
"labels": {
"cloudidentity.googleapis.com/groups.discussion_forum": ""
}
},
{
"name": "groups/111",
"groupKey": {
"id": "[email protected]"
},
"displayName": "Group - 1",
"description": "Group - 1",
"labels": {
"cloudidentity.googleapis.com/groups.discussion_forum": ""
}
},
{
"name": "groups/222",
"groupKey": {
"id": "[email protected]"
},
"displayName": "Group - 2",
"description": "Group - 2",
"labels": {
"cloudidentity.googleapis.com/groups.discussion_forum": ""
}
}
]
}
隣接リスト内の各項目は、グループとその直接のメンバー(エッジ)を表し、レスポンスには、メンバーシップ グラフ内の全グループの詳細も含まれます。これを解析して、メンバーシップ グラフの可視化に使用できる別の表現(DOT グラフなど)を生成できます。
次のサンプル スクリプトを使用すると、レスポンスを 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='[email protected] (MEMBER)'];
# 'group0' -> 'group2' [label='[email protected] (MEMBER)'];
# 'group1' -> 'group3' [label='[email protected] (MEMBER)'];
# 'group3' -> 'group4' [label='[email protected] (MEMBER)'];
# 'group2' -> 'group3' [label='[email protected] (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)
サンプル レスポンスを変換した視覚的な階層は、次のようになります。