authentik.enterprise.providers.google_workspace.clients.users

  1from django.db import transaction
  2
  3from authentik.core.models import User
  4from authentik.enterprise.providers.google_workspace.clients.base import GoogleWorkspaceSyncClient
  5from authentik.enterprise.providers.google_workspace.models import (
  6    GoogleWorkspaceProvider,
  7    GoogleWorkspaceProviderMapping,
  8    GoogleWorkspaceProviderUser,
  9)
 10from authentik.lib.sync.mapper import PropertyMappingManager
 11from authentik.lib.sync.outgoing.exceptions import (
 12    ObjectExistsSyncException,
 13    TransientSyncException,
 14)
 15from authentik.lib.sync.outgoing.models import OutgoingSyncDeleteAction
 16from authentik.policies.utils import delete_none_values
 17
 18
 19class GoogleWorkspaceUserClient(GoogleWorkspaceSyncClient[User, GoogleWorkspaceProviderUser, dict]):
 20    """Sync authentik users into google workspace"""
 21
 22    connection_type = GoogleWorkspaceProviderUser
 23    connection_type_query = "user"
 24    can_discover = True
 25
 26    def __init__(self, provider: GoogleWorkspaceProvider) -> None:
 27        super().__init__(provider)
 28        self.mapper = PropertyMappingManager(
 29            self.provider.property_mappings.all().order_by("name").select_subclasses(),
 30            GoogleWorkspaceProviderMapping,
 31            ["provider", "connection"],
 32        )
 33
 34    def to_schema(self, obj: User, connection: GoogleWorkspaceProviderUser) -> dict:
 35        """Convert authentik user"""
 36        return delete_none_values(super().to_schema(obj, connection, primaryEmail=obj.email))
 37
 38    def delete(self, identifier: str):
 39        """Delete user"""
 40        GoogleWorkspaceProviderUser.objects.filter(
 41            provider=self.provider, google_id=identifier
 42        ).delete()
 43        if self.provider.user_delete_action == OutgoingSyncDeleteAction.DELETE:
 44            return self._request(self.directory_service.users().delete(userKey=identifier))
 45        if self.provider.user_delete_action == OutgoingSyncDeleteAction.SUSPEND:
 46            return self._request(
 47                self.directory_service.users().update(userKey=identifier, body={"suspended": True})
 48            )
 49
 50    def create(self, user: User):
 51        """Create user from scratch and create a connection object"""
 52        google_user = self.to_schema(user, None)
 53        self.check_email_valid(
 54            google_user["primaryEmail"], *[x["address"] for x in google_user.get("emails", [])]
 55        )
 56        with transaction.atomic():
 57            try:
 58                response = self._request(self.directory_service.users().insert(body=google_user))
 59            except ObjectExistsSyncException:
 60                # user already exists in google workspace, so we can connect them manually
 61                return GoogleWorkspaceProviderUser.objects.create(
 62                    provider=self.provider, user=user, google_id=user.email, attributes={}
 63                )
 64            except TransientSyncException as exc:
 65                raise exc
 66            else:
 67                return GoogleWorkspaceProviderUser.objects.create(
 68                    provider=self.provider,
 69                    user=user,
 70                    google_id=response["primaryEmail"],
 71                    attributes=response,
 72                )
 73
 74    def update(self, user: User, connection: GoogleWorkspaceProviderUser):
 75        """Update existing user"""
 76        google_user = self.to_schema(user, connection)
 77        self.check_email_valid(
 78            google_user["primaryEmail"], *[x["address"] for x in google_user.get("emails", [])]
 79        )
 80        response = self._request(
 81            self.directory_service.users().update(userKey=connection.google_id, body=google_user)
 82        )
 83        connection.attributes = response
 84        connection.save()
 85
 86    def discover(self):
 87        """Iterate through all users and connect them with authentik users if possible"""
 88        request = self.directory_service.users().list(
 89            customer="my_customer", maxResults=500, orderBy="email"
 90        )
 91        while request:
 92            response = request.execute()
 93            for user in response.get("users", []):
 94                self._discover_single_user(user)
 95            request = self.directory_service.users().list_next(
 96                previous_request=request, previous_response=response
 97            )
 98
 99    def _discover_single_user(self, user: dict):
100        """handle discovery of a single user"""
101        email = user["primaryEmail"]
102        matching_authentik_user = self.provider.get_object_qs(User).filter(email=email).first()
103        if not matching_authentik_user:
104            return
105        GoogleWorkspaceProviderUser.objects.update_or_create(
106            provider=self.provider,
107            user=matching_authentik_user,
108            google_id=email,
109            defaults={"attributes": user},
110        )
111
112    def update_single_attribute(self, connection: GoogleWorkspaceProviderUser):
113        user = self.directory_service.users().get(connection.google_id)
114        connection.attributes = user
 20class GoogleWorkspaceUserClient(GoogleWorkspaceSyncClient[User, GoogleWorkspaceProviderUser, dict]):
 21    """Sync authentik users into google workspace"""
 22
 23    connection_type = GoogleWorkspaceProviderUser
 24    connection_type_query = "user"
 25    can_discover = True
 26
 27    def __init__(self, provider: GoogleWorkspaceProvider) -> None:
 28        super().__init__(provider)
 29        self.mapper = PropertyMappingManager(
 30            self.provider.property_mappings.all().order_by("name").select_subclasses(),
 31            GoogleWorkspaceProviderMapping,
 32            ["provider", "connection"],
 33        )
 34
 35    def to_schema(self, obj: User, connection: GoogleWorkspaceProviderUser) -> dict:
 36        """Convert authentik user"""
 37        return delete_none_values(super().to_schema(obj, connection, primaryEmail=obj.email))
 38
 39    def delete(self, identifier: str):
 40        """Delete user"""
 41        GoogleWorkspaceProviderUser.objects.filter(
 42            provider=self.provider, google_id=identifier
 43        ).delete()
 44        if self.provider.user_delete_action == OutgoingSyncDeleteAction.DELETE:
 45            return self._request(self.directory_service.users().delete(userKey=identifier))
 46        if self.provider.user_delete_action == OutgoingSyncDeleteAction.SUSPEND:
 47            return self._request(
 48                self.directory_service.users().update(userKey=identifier, body={"suspended": True})
 49            )
 50
 51    def create(self, user: User):
 52        """Create user from scratch and create a connection object"""
 53        google_user = self.to_schema(user, None)
 54        self.check_email_valid(
 55            google_user["primaryEmail"], *[x["address"] for x in google_user.get("emails", [])]
 56        )
 57        with transaction.atomic():
 58            try:
 59                response = self._request(self.directory_service.users().insert(body=google_user))
 60            except ObjectExistsSyncException:
 61                # user already exists in google workspace, so we can connect them manually
 62                return GoogleWorkspaceProviderUser.objects.create(
 63                    provider=self.provider, user=user, google_id=user.email, attributes={}
 64                )
 65            except TransientSyncException as exc:
 66                raise exc
 67            else:
 68                return GoogleWorkspaceProviderUser.objects.create(
 69                    provider=self.provider,
 70                    user=user,
 71                    google_id=response["primaryEmail"],
 72                    attributes=response,
 73                )
 74
 75    def update(self, user: User, connection: GoogleWorkspaceProviderUser):
 76        """Update existing user"""
 77        google_user = self.to_schema(user, connection)
 78        self.check_email_valid(
 79            google_user["primaryEmail"], *[x["address"] for x in google_user.get("emails", [])]
 80        )
 81        response = self._request(
 82            self.directory_service.users().update(userKey=connection.google_id, body=google_user)
 83        )
 84        connection.attributes = response
 85        connection.save()
 86
 87    def discover(self):
 88        """Iterate through all users and connect them with authentik users if possible"""
 89        request = self.directory_service.users().list(
 90            customer="my_customer", maxResults=500, orderBy="email"
 91        )
 92        while request:
 93            response = request.execute()
 94            for user in response.get("users", []):
 95                self._discover_single_user(user)
 96            request = self.directory_service.users().list_next(
 97                previous_request=request, previous_response=response
 98            )
 99
100    def _discover_single_user(self, user: dict):
101        """handle discovery of a single user"""
102        email = user["primaryEmail"]
103        matching_authentik_user = self.provider.get_object_qs(User).filter(email=email).first()
104        if not matching_authentik_user:
105            return
106        GoogleWorkspaceProviderUser.objects.update_or_create(
107            provider=self.provider,
108            user=matching_authentik_user,
109            google_id=email,
110            defaults={"attributes": user},
111        )
112
113    def update_single_attribute(self, connection: GoogleWorkspaceProviderUser):
114        user = self.directory_service.users().get(connection.google_id)
115        connection.attributes = user

Sync authentik users into google workspace

GoogleWorkspaceUserClient( provider: authentik.enterprise.providers.google_workspace.models.GoogleWorkspaceProvider)
27    def __init__(self, provider: GoogleWorkspaceProvider) -> None:
28        super().__init__(provider)
29        self.mapper = PropertyMappingManager(
30            self.provider.property_mappings.all().order_by("name").select_subclasses(),
31            GoogleWorkspaceProviderMapping,
32            ["provider", "connection"],
33        )
connection_type_query = 'user'
can_discover = True
mapper
35    def to_schema(self, obj: User, connection: GoogleWorkspaceProviderUser) -> dict:
36        """Convert authentik user"""
37        return delete_none_values(super().to_schema(obj, connection, primaryEmail=obj.email))

Convert authentik user

def delete(self, identifier: str):
39    def delete(self, identifier: str):
40        """Delete user"""
41        GoogleWorkspaceProviderUser.objects.filter(
42            provider=self.provider, google_id=identifier
43        ).delete()
44        if self.provider.user_delete_action == OutgoingSyncDeleteAction.DELETE:
45            return self._request(self.directory_service.users().delete(userKey=identifier))
46        if self.provider.user_delete_action == OutgoingSyncDeleteAction.SUSPEND:
47            return self._request(
48                self.directory_service.users().update(userKey=identifier, body={"suspended": True})
49            )

Delete user

def create(self, user: authentik.core.models.User):
51    def create(self, user: User):
52        """Create user from scratch and create a connection object"""
53        google_user = self.to_schema(user, None)
54        self.check_email_valid(
55            google_user["primaryEmail"], *[x["address"] for x in google_user.get("emails", [])]
56        )
57        with transaction.atomic():
58            try:
59                response = self._request(self.directory_service.users().insert(body=google_user))
60            except ObjectExistsSyncException:
61                # user already exists in google workspace, so we can connect them manually
62                return GoogleWorkspaceProviderUser.objects.create(
63                    provider=self.provider, user=user, google_id=user.email, attributes={}
64                )
65            except TransientSyncException as exc:
66                raise exc
67            else:
68                return GoogleWorkspaceProviderUser.objects.create(
69                    provider=self.provider,
70                    user=user,
71                    google_id=response["primaryEmail"],
72                    attributes=response,
73                )

Create user from scratch and create a connection object

75    def update(self, user: User, connection: GoogleWorkspaceProviderUser):
76        """Update existing user"""
77        google_user = self.to_schema(user, connection)
78        self.check_email_valid(
79            google_user["primaryEmail"], *[x["address"] for x in google_user.get("emails", [])]
80        )
81        response = self._request(
82            self.directory_service.users().update(userKey=connection.google_id, body=google_user)
83        )
84        connection.attributes = response
85        connection.save()

Update existing user

def discover(self):
87    def discover(self):
88        """Iterate through all users and connect them with authentik users if possible"""
89        request = self.directory_service.users().list(
90            customer="my_customer", maxResults=500, orderBy="email"
91        )
92        while request:
93            response = request.execute()
94            for user in response.get("users", []):
95                self._discover_single_user(user)
96            request = self.directory_service.users().list_next(
97                previous_request=request, previous_response=response
98            )

Iterate through all users and connect them with authentik users if possible

def update_single_attribute( self, connection: authentik.enterprise.providers.google_workspace.models.GoogleWorkspaceProviderUser):
113    def update_single_attribute(self, connection: GoogleWorkspaceProviderUser):
114        user = self.directory_service.users().get(connection.google_id)
115        connection.attributes = user

Update connection attributes on a connection object, when the connection is manually created