Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/provisioning module #486

Merged
merged 18 commits into from
Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 42 additions & 1 deletion app/integrations/google_workspace/google_directory.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def list_groups(
customer=None,
**kwargs,
):
"""List all groups in the Google Workspace domain.
"""List all groups in the Google Workspace domain. A query can be provided to filter the results (e.g. query="email:prefix-*" will filter for all groups where the email starts with 'prefix-').

Returns:
list: A list of group objects.
Expand Down Expand Up @@ -126,3 +126,44 @@ def list_group_members(group_key, delegated_user_email=None):
groupKey=group_key,
maxResults=200,
)


def add_users_to_group(group, group_key):
"""Add users to a group in the Google Workspace domain.

Args:
group_key (str): The group's email address or unique group ID.

Returns:
list: A list of user objects.

Ref: https://developers.google.com/admin-sdk/directory/reference/rest/v1/members/insert
"""
result = list_group_members(group_key)
if result:
group["members"] = result
return group


def list_groups_with_members(**kwargs):
"""List all groups in the Google Workspace domain with their members.

Returns:
list: A list of group objects with members.
"""
members_details = kwargs.get("members_details", True)
kwargs.pop("members_details", None)
groups = list_groups(**kwargs)
if not groups:
return []
for group in range(len(groups)):
members = list_group_members(groups[group]["email"])
if members and members_details:
groups[group]["members"] = members

for member in range(len(groups[group]["members"])):
groups[group]["members"][member] = get_user(
groups[group]["members"][member]["email"]
)

return groups
Empty file.
42 changes: 42 additions & 0 deletions app/modules/provisioning/groups.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from integrations.google_workspace import google_directory
from integrations.aws import identity_store
from utils import filters as filter_tools


def get_groups_with_members_from_integration(integration_source, **kwargs):
"""Retrieve the users from an integration group source.
Supported sources are:
- Google Groups
- AWS Identity Center (Identity Store)

Args:
integration_source (str): The source of the groups.
**kwargs: Additional keyword arguments. Supported arguments are:

- `filters` (list): List of filters to apply to the groups.
- `query` (str): The query to search for groups.
- `members_details` (bool): Include the members details in the groups.

Returns:
list: A list of groups with members, empty list if no groups are found.
"""
filters = kwargs.get("filters", [])
query = kwargs.get("query", None)
members_details = kwargs.get("members_details", True)

groups = []
match integration_source:
Copy link
Contributor

@sylviamclaughlin sylviamclaughlin Apr 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So much better than several if statements!

case "google_groups":
groups = google_directory.list_groups_with_members(
query=query, members_details=members_details
)
case "aws_identity_center":
groups = identity_store.list_groups_with_memberships(
members_details=members_details
)
case _:
return groups

for filter in filters:
groups = filter_tools.filter_by_condition(groups, filter)
return groups
31 changes: 31 additions & 0 deletions app/modules/provisioning/users.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from logging import getLogger

from utils import filters as filter_tools


logger = getLogger(__name__)


def get_unique_users_from_groups(groups, key):
"""Get the unique users from a list of groups with the same data schema or a single group dict.
Considers the whole object for uniqueness, not specific keys.

Args:
groups (list or dict): A list of groups or a single group.
key (str): The key to get the users from the groups.

Returns:
list: A list of unique users from the groups
"""
users_dict = {}
if isinstance(groups, list):
for group in groups:
for user in filter_tools.get_nested_value(group, key):
if user:
users_dict[str(user)] = user
elif isinstance(groups, dict):
for user in filter_tools.get_nested_value(groups, key):
if user:
users_dict[str(user)] = user

return list(users_dict.values())
46 changes: 23 additions & 23 deletions app/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ def google_groups():
def _google_groups(n=3, prefix="", domain="test.com"):
return [
{
"id": f"{prefix}_google_group_id{i+1}",
"name": f"AWS-group{i+1}",
"email": f"{prefix}_aws-group{i+1}@{domain}",
"id": f"{prefix}google_group_id{i+1}",
"name": f"{prefix}group-name{i+1}",
"email": f"{prefix}group-name{i+1}@{domain}",
}
for i in range(n)
]
Expand All @@ -26,21 +26,21 @@ def _google_users(n=3, prefix="", domain="test.com"):
users = []
for i in range(n):
user = {
"id": f"{prefix}_id_{i}",
"primaryEmail": f"{prefix}_email_{i}@{domain}",
"id": f"{prefix}user_id{i+1}",
"primaryEmail": f"{prefix}user-email{i+1}@{domain}",
"emails": [
{
"address": f"{prefix}_email_{i}@{domain}",
"address": f"{prefix}user-email{i+1}@{domain}",
"primary": True,
"type": "work",
}
],
"suspended": False,
"name": {
"fullName": f"Given_name_{i} Family_name_{i}",
"familyName": f"Family_name_{i}",
"givenName": f"Given_name_{i}",
"displayName": f"Given_name_{i} Family_name_{i}",
"fullName": f"Given_name_{i+1} Family_name_{i+1}",
"familyName": f"Family_name_{i+1}",
"givenName": f"Given_name_{i+1}",
"displayName": f"Given_name_{i+1} Family_name_{i+1}",
},
}
users.append(user)
Expand Down Expand Up @@ -90,16 +90,16 @@ def _aws_users(n=3, prefix="", domain="test.com", store_id="d-123412341234"):
users = []
for i in range(n):
user = {
"UserName": f"{prefix}_email_{i}@{domain}",
"UserId": f"{prefix}_id_{i}",
"UserName": f"{prefix}user-email{i+1}@{domain}",
"UserId": f"{prefix}user_id{i+1}",
"Name": {
"FamilyName": f"Family_name_{i}",
"GivenName": f"Given_name_{i}",
"FamilyName": f"Family_name_{i+1}",
"GivenName": f"Given_name_{i+1}",
},
"DisplayName": f"Given_name_{i} Family_name_{i}",
"DisplayName": f"Given_name_{i+1} Family_name_{i+1}",
"Emails": [
{
"Value": f"{prefix}_email_{i}@{domain}",
"Value": f"{prefix}user-email{i+1}@{domain}",
"Type": "work",
"Primary": True,
}
Expand All @@ -118,8 +118,8 @@ def _aws_groups(n=3, prefix="", store_id="d-123412341234"):
return {
"Groups": [
{
"GroupId": f"{prefix}_aws-group_id{i+1}",
"DisplayName": f"AWS-group{i+1}",
"GroupId": f"{prefix}aws-group_id{i+1}",
"DisplayName": f"{prefix}group-name{i+1}",
"Description": f"A group to test resolving AWS-group{i+1} memberships",
"IdentityStoreId": f"{store_id}",
}
Expand All @@ -137,10 +137,10 @@ def _aws_groups_memberships(n=3, prefix="", store_id="d-123412341234"):
"GroupMemberships": [
{
"IdentityStoreId": f"{store_id}",
"MembershipId": f"{prefix}_membership_id_{i+1}",
"GroupId": f"{prefix}_aws-group_id{i+1}",
"MembershipId": f"{prefix}membership_id_{i+1}",
"GroupId": f"{prefix}aws-group_id{i+1}",
"MemberId": {
"UserId": f"{prefix}_id_{i}",
"UserId": f"{prefix}user_id{i+1}",
},
}
for i in range(n)
Expand All @@ -155,9 +155,9 @@ def aws_groups_w_users(aws_groups, aws_users, aws_groups_memberships):
def _aws_groups_w_users(
n_groups=1, n_users=3, prefix="", domain="test.com", store_id="d-123412341234"
):
groups = aws_groups(n_groups, prefix, domain, store_id)["Groups"]
groups = aws_groups(n_groups, prefix, store_id)["Groups"]
users = aws_users(n_users, prefix, domain, store_id)
memberships = aws_groups_memberships(n_groups, prefix, domain, store_id)[
memberships = aws_groups_memberships(n_groups, prefix, store_id)[
"GroupMemberships"
]
for group, membership in zip(groups, memberships):
Expand Down
62 changes: 31 additions & 31 deletions app/tests/integrations/aws/test_identity_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,16 +156,16 @@ def test_describe_user(
user_id = "test_user_id1"

expected = {
"UserName": "_email_0@test.com",
"UserId": "_id_0",
"UserName": "user-email1@test.com",
"UserId": "user_id1",
"Name": {
"FamilyName": "Family_name_0",
"GivenName": "Given_name_0",
"FamilyName": "Family_name_1",
"GivenName": "Given_name_1",
},
"DisplayName": "Given_name_0 Family_name_0",
"DisplayName": "Given_name_1 Family_name_1",
"Emails": [
{
"Value": "_email_0@test.com",
"Value": "user-email1@test.com",
"Type": "work",
"Primary": True,
}
Expand Down Expand Up @@ -683,37 +683,37 @@ def test_list_groups_with_memberships(
):
# groups = aws_groups_w_users(2, 3, prefix="test", domain="test.com")
groups = aws_groups(2, prefix="test")["Groups"]
memberships = [[], aws_groups_memberships(2, prefix="test")["GroupMemberships"]]
users = aws_users(2, prefix="test", domain="test.com")
memberships = [[], aws_groups_memberships(2, prefix="test-")["GroupMemberships"]]
users = aws_users(2, prefix="test-", domain="test.com")
expected_output = [
{
"IdentityStoreId": "d-123412341234",
"GroupId": "test_aws-group_id1",
"DisplayName": "AWS-group1",
"GroupId": "testaws-group_id1",
"DisplayName": "testgroup-name1",
"Description": "A group to test resolving AWS-group1 memberships",
"IdentityStoreId": "d-123412341234",
"GroupMemberships": [],
},
{
"IdentityStoreId": "d-123412341234",
"GroupId": "test_aws-group_id2",
"DisplayName": "AWS-group2",
"GroupId": "testaws-group_id2",
"DisplayName": "testgroup-name2",
"Description": "A group to test resolving AWS-group2 memberships",
"IdentityStoreId": "d-123412341234",
"GroupMemberships": [
{
"IdentityStoreId": "d-123412341234",
"MembershipId": "test_membership_id_1",
"GroupId": "test_aws-group_id1",
"MembershipId": "test-membership_id_1",
"GroupId": "test-aws-group_id1",
"MemberId": {
"UserName": "test_email_0@test.com",
"UserId": "test_id_0",
"UserName": "test-user-email1@test.com",
"UserId": "test-user_id1",
"Name": {
"FamilyName": "Family_name_0",
"GivenName": "Given_name_0",
"FamilyName": "Family_name_1",
"GivenName": "Given_name_1",
},
"DisplayName": "Given_name_0 Family_name_0",
"DisplayName": "Given_name_1 Family_name_1",
"Emails": [
{
"Value": "test_email_0@test.com",
"Value": "test-user-email1@test.com",
"Type": "work",
"Primary": True,
}
Expand All @@ -723,19 +723,19 @@ def test_list_groups_with_memberships(
},
{
"IdentityStoreId": "d-123412341234",
"MembershipId": "test_membership_id_2",
"GroupId": "test_aws-group_id2",
"MembershipId": "test-membership_id_2",
"GroupId": "test-aws-group_id2",
"MemberId": {
"UserName": "test_email_1@test.com",
"UserId": "test_id_1",
"UserName": "test-user-email2@test.com",
"UserId": "test-user_id2",
"Name": {
"FamilyName": "Family_name_1",
"GivenName": "Given_name_1",
"FamilyName": "Family_name_2",
"GivenName": "Given_name_2",
},
"DisplayName": "Given_name_1 Family_name_1",
"DisplayName": "Given_name_2 Family_name_2",
"Emails": [
{
"Value": "test_email_1@test.com",
"Value": "test-user-email2@test.com",
"Type": "work",
"Primary": True,
}
Expand All @@ -757,5 +757,5 @@ def test_list_groups_with_memberships(
mock_describe_user.side_effect = user_side_effect

result = identity_store.list_groups_with_memberships()

# print(json.dumps(result, indent=4))
assert result == expected_output
Loading
Loading