enterprise/providers/microsoft_entra: initial account sync to microsoft entra (#9632)
* initial Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add entra mappings Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix some stuff Signed-off-by: Jens Langhammer <jens@goauthentik.io> * make API endpoints more consistent Signed-off-by: Jens Langhammer <jens@goauthentik.io> * implement more things Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add user tests Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix most group tests + fix bugs Signed-off-by: Jens Langhammer <jens@goauthentik.io> * more group tests, fix bugs Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix missing __init__ Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add ui for provisioned users Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix a bunch of bugs Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add `creating` to property mapping env Signed-off-by: Jens Langhammer <jens@goauthentik.io> * always sync group members Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix stuff Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix group membership Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix some types Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix tests Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add group member add test Signed-off-by: Jens Langhammer <jens@goauthentik.io> * create sync status component to dedupe Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix discovery tests Signed-off-by: Jens Langhammer <jens@goauthentik.io> * get rid of more code and fix more issues Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add error handling for auth and transient Signed-off-by: Jens Langhammer <jens@goauthentik.io> * make sure autoretry is on Signed-off-by: Jens Langhammer <jens@goauthentik.io> * format web Signed-off-by: Jens Langhammer <jens@goauthentik.io> * wait for task in signal Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix tests Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add squashed google migration Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
@ -39,26 +39,25 @@ class BaseOutgoingSyncClient[
|
||||
"""Create object in remote destination"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def update(self, obj: TModel, connection: object):
|
||||
def update(self, obj: TModel, connection: TConnection):
|
||||
"""Update object in remote destination"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def write(self, obj: TModel) -> tuple[TConnection, bool]:
|
||||
"""Write object to destination. Uses self.create and self.update, but
|
||||
can be overwritten for further logic"""
|
||||
remote_obj = self.connection_type.objects.filter(
|
||||
connection = self.connection_type.objects.filter(
|
||||
provider=self.provider, **{self.connection_type_query: obj}
|
||||
).first()
|
||||
connection: TConnection | None = None
|
||||
try:
|
||||
if not remote_obj:
|
||||
if not connection:
|
||||
connection = self.create(obj)
|
||||
return connection, True
|
||||
try:
|
||||
self.update(obj, remote_obj)
|
||||
return remote_obj, False
|
||||
self.update(obj, connection)
|
||||
return connection, False
|
||||
except NotFoundSyncException:
|
||||
remote_obj.delete()
|
||||
connection.delete()
|
||||
connection = self.create(obj)
|
||||
return connection, True
|
||||
except DatabaseError as exc:
|
||||
@ -71,7 +70,7 @@ class BaseOutgoingSyncClient[
|
||||
"""Delete object from destination"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def to_schema(self, obj: TModel) -> TSchema:
|
||||
def to_schema(self, obj: TModel, creating: bool) -> TSchema:
|
||||
"""Convert object to destination schema"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
@ -17,6 +17,10 @@ class ObjectExistsSyncException(BaseSyncException):
|
||||
"""Exception when an object already exists in the remote system"""
|
||||
|
||||
|
||||
class BadRequestSyncException(BaseSyncException):
|
||||
"""Exception when invalid data was sent to the remote system"""
|
||||
|
||||
|
||||
class StopSync(BaseSyncException):
|
||||
"""Exception raised when a configuration error should stop the sync process"""
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
from typing import Any, Self
|
||||
|
||||
from django.core.cache import cache
|
||||
from django.db.models import Model, QuerySet
|
||||
from django.db.models import Model, QuerySet, TextChoices
|
||||
from redis.lock import Lock
|
||||
|
||||
from authentik.core.models import Group, User
|
||||
@ -9,6 +9,15 @@ from authentik.lib.sync.outgoing import PAGE_TIMEOUT
|
||||
from authentik.lib.sync.outgoing.base import BaseOutgoingSyncClient
|
||||
|
||||
|
||||
class OutgoingSyncDeleteAction(TextChoices):
|
||||
"""Action taken when a user/group is deleted in authentik. Suspend is not available for groups,
|
||||
and will be treated as `do_nothing`"""
|
||||
|
||||
DO_NOTHING = "do_nothing"
|
||||
DELETE = "delete"
|
||||
SUSPEND = "suspend"
|
||||
|
||||
|
||||
class OutgoingSyncProvider(Model):
|
||||
|
||||
class Meta:
|
||||
|
||||
@ -47,7 +47,7 @@ def register_signals(
|
||||
return
|
||||
task_sync_direct.delay(
|
||||
class_to_path(instance.__class__), instance.pk, Direction.remove.value
|
||||
)
|
||||
).get()
|
||||
|
||||
pre_delete.connect(model_pre_delete, User, dispatch_uid=uid, weak=False)
|
||||
pre_delete.connect(model_pre_delete, Group, dispatch_uid=uid, weak=False)
|
||||
|
||||
@ -14,7 +14,11 @@ from authentik.events.models import TaskStatus
|
||||
from authentik.events.system_tasks import SystemTask
|
||||
from authentik.lib.sync.outgoing import PAGE_SIZE, PAGE_TIMEOUT
|
||||
from authentik.lib.sync.outgoing.base import Direction
|
||||
from authentik.lib.sync.outgoing.exceptions import StopSync, TransientSyncException
|
||||
from authentik.lib.sync.outgoing.exceptions import (
|
||||
BadRequestSyncException,
|
||||
StopSync,
|
||||
TransientSyncException,
|
||||
)
|
||||
from authentik.lib.sync.outgoing.models import OutgoingSyncProvider
|
||||
from authentik.lib.utils.reflection import class_to_path, path_to_class
|
||||
|
||||
@ -121,6 +125,27 @@ class SyncTasks:
|
||||
client.write(obj)
|
||||
except SkipObjectException:
|
||||
continue
|
||||
except BadRequestSyncException as exc:
|
||||
self.logger.warning("failed to sync object", exc=exc, obj=obj)
|
||||
messages.append(
|
||||
LogEvent(
|
||||
_(
|
||||
(
|
||||
"Failed to sync {object_type} {object_name} "
|
||||
"due to error: {error}"
|
||||
).format_map(
|
||||
{
|
||||
"object_type": obj._meta.verbose_name,
|
||||
"object_name": str(obj),
|
||||
"error": str(exc),
|
||||
}
|
||||
)
|
||||
),
|
||||
log_level="warning",
|
||||
logger="",
|
||||
attributes={"arguments": exc.args[1:]},
|
||||
)
|
||||
)
|
||||
except TransientSyncException as exc:
|
||||
self.logger.warning("failed to sync object", exc=exc, user=obj)
|
||||
messages.append(
|
||||
|
||||
Reference in New Issue
Block a user