Skip to content

Commit

Permalink
Merge branch 'main' into dev
Browse files Browse the repository at this point in the history
* main:
  website/docs: release notes for 2024.6 (#9812)
  policies/reputation: save to database directly (#10059)
  providers/enterprise: import user/group data when manually linking objects (#10089)
  core, web: update translations (#10108)
  web: Add enterprise / FIPS notification to the AdminOverviewPage (#10090)
  core: bump github.com/getsentry/sentry-go from 0.28.0 to 0.28.1 (#10095)
  web: bump API Client version (#10107)
  admin: system api: do not show FIPS status if no valid license (#10091)
  root: add configuration option to enable fips (#10088)
  web: bump the sentry group across 1 directory with 2 updates (#10101)
  web: bump ts-pattern from 5.1.2 to 5.2.0 in /web (#10098)
  web: bump the storybook group across 1 directory with 7 updates (#10102)
  core: bump github.com/gorilla/websocket from 1.5.2 to 1.5.3 (#10103)
  core: bump pydantic from 2.7.3 to 2.7.4 (#10093)
  core: bump bandit from 1.7.8 to 1.7.9 (#10094)
  • Loading branch information
kensternberg-authentik committed Jun 14, 2024
2 parents 10bfc4e + 0c604ce commit c49185d
Show file tree
Hide file tree
Showing 52 changed files with 2,468 additions and 943 deletions.
7 changes: 5 additions & 2 deletions authentik/admin/api/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

from authentik import get_full_version
from authentik.core.api.utils import PassiveSerializer
from authentik.enterprise.license import LicenseKey
from authentik.lib.config import CONFIG
from authentik.lib.utils.reflection import get_env
from authentik.outposts.apps import MANAGED_OUTPOST
Expand All @@ -32,7 +33,7 @@ class RuntimeDict(TypedDict):
platform: str
uname: str
openssl_version: str
openssl_fips_mode: bool
openssl_fips_mode: bool | None
authentik_version: str


Expand Down Expand Up @@ -71,7 +72,9 @@ def get_runtime(self, request: Request) -> RuntimeDict:
"architecture": platform.machine(),
"authentik_version": get_full_version(),
"environment": get_env(),
"openssl_fips_enabled": backend._fips_enabled,
"openssl_fips_enabled": (
backend._fips_enabled if LicenseKey.get_total().is_valid() else None
),
"openssl_version": OPENSSL_VERSION,
"platform": platform.platform(),
"python_version": python_version,
Expand Down
2 changes: 2 additions & 0 deletions authentik/enterprise/providers/google_workspace/api/groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.users import UserGroupSerializer
from authentik.enterprise.providers.google_workspace.models import GoogleWorkspaceProviderGroup
from authentik.lib.sync.outgoing.api import OutgoingSyncConnectionCreateMixin


class GoogleWorkspaceProviderGroupSerializer(ModelSerializer):
Expand All @@ -30,6 +31,7 @@ class Meta:

class GoogleWorkspaceProviderGroupViewSet(
mixins.CreateModelMixin,
OutgoingSyncConnectionCreateMixin,
mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
UsedByMixin,
Expand Down
2 changes: 2 additions & 0 deletions authentik/enterprise/providers/google_workspace/api/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from authentik.core.api.groups import GroupMemberSerializer
from authentik.core.api.used_by import UsedByMixin
from authentik.enterprise.providers.google_workspace.models import GoogleWorkspaceProviderUser
from authentik.lib.sync.outgoing.api import OutgoingSyncConnectionCreateMixin


class GoogleWorkspaceProviderUserSerializer(ModelSerializer):
Expand All @@ -30,6 +31,7 @@ class Meta:

class GoogleWorkspaceProviderUserViewSet(
mixins.CreateModelMixin,
OutgoingSyncConnectionCreateMixin,
mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
UsedByMixin,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,3 +214,7 @@ def _discover_single_group(self, group: dict):
google_id=google_id,
attributes=group,
)

def update_single_attribute(self, connection: GoogleWorkspaceProviderUser):
group = self.directory_service.groups().get(connection.google_id)
connection.attributes = group
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,7 @@ def _discover_single_user(self, user: dict):
google_id=email,
attributes=user,
)

def update_single_attribute(self, connection: GoogleWorkspaceProviderUser):
user = self.directory_service.users().get(connection.google_id)
connection.attributes = user
111 changes: 56 additions & 55 deletions authentik/enterprise/providers/google_workspace/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,58 @@ def default_scopes() -> list[str]:
]


class GoogleWorkspaceProviderUser(SerializerModel):
"""Mapping of a user and provider to a Google user ID"""

id = models.UUIDField(primary_key=True, editable=False, default=uuid4)
google_id = models.TextField()
user = models.ForeignKey(User, on_delete=models.CASCADE)
provider = models.ForeignKey("GoogleWorkspaceProvider", on_delete=models.CASCADE)
attributes = models.JSONField(default=dict)

@property
def serializer(self) -> type[Serializer]:
from authentik.enterprise.providers.google_workspace.api.users import (
GoogleWorkspaceProviderUserSerializer,
)

return GoogleWorkspaceProviderUserSerializer

class Meta:
verbose_name = _("Google Workspace Provider User")
verbose_name_plural = _("Google Workspace Provider Users")
unique_together = (("google_id", "user", "provider"),)

def __str__(self) -> str:
return f"Google Workspace Provider User {self.user_id} to {self.provider_id}"


class GoogleWorkspaceProviderGroup(SerializerModel):
"""Mapping of a group and provider to a Google group ID"""

id = models.UUIDField(primary_key=True, editable=False, default=uuid4)
google_id = models.TextField()
group = models.ForeignKey(Group, on_delete=models.CASCADE)
provider = models.ForeignKey("GoogleWorkspaceProvider", on_delete=models.CASCADE)
attributes = models.JSONField(default=dict)

@property
def serializer(self) -> type[Serializer]:
from authentik.enterprise.providers.google_workspace.api.groups import (
GoogleWorkspaceProviderGroupSerializer,
)

return GoogleWorkspaceProviderGroupSerializer

class Meta:
verbose_name = _("Google Workspace Provider Group")
verbose_name_plural = _("Google Workspace Provider Groups")
unique_together = (("google_id", "group", "provider"),)

def __str__(self) -> str:
return f"Google Workspace Provider Group {self.group_id} to {self.provider_id}"


class GoogleWorkspaceProvider(OutgoingSyncProvider, BackchannelProvider):
"""Sync users from authentik into Google Workspace."""

Expand Down Expand Up @@ -59,15 +111,16 @@ class GoogleWorkspaceProvider(OutgoingSyncProvider, BackchannelProvider):
)

def client_for_model(
self, model: type[User | Group]
self,
model: type[User | Group | GoogleWorkspaceProviderUser | GoogleWorkspaceProviderGroup],
) -> BaseOutgoingSyncClient[User | Group, Any, Any, Self]:
if issubclass(model, User):
if issubclass(model, User | GoogleWorkspaceProviderUser):
from authentik.enterprise.providers.google_workspace.clients.users import (
GoogleWorkspaceUserClient,
)

return GoogleWorkspaceUserClient(self)
if issubclass(model, Group):
if issubclass(model, Group | GoogleWorkspaceProviderGroup):
from authentik.enterprise.providers.google_workspace.clients.groups import (
GoogleWorkspaceGroupClient,
)
Expand Down Expand Up @@ -144,55 +197,3 @@ def __str__(self):
class Meta:
verbose_name = _("Google Workspace Provider Mapping")
verbose_name_plural = _("Google Workspace Provider Mappings")


class GoogleWorkspaceProviderUser(SerializerModel):
"""Mapping of a user and provider to a Google user ID"""

id = models.UUIDField(primary_key=True, editable=False, default=uuid4)
google_id = models.TextField()
user = models.ForeignKey(User, on_delete=models.CASCADE)
provider = models.ForeignKey(GoogleWorkspaceProvider, on_delete=models.CASCADE)
attributes = models.JSONField(default=dict)

@property
def serializer(self) -> type[Serializer]:
from authentik.enterprise.providers.google_workspace.api.users import (
GoogleWorkspaceProviderUserSerializer,
)

return GoogleWorkspaceProviderUserSerializer

class Meta:
verbose_name = _("Google Workspace Provider User")
verbose_name_plural = _("Google Workspace Provider Users")
unique_together = (("google_id", "user", "provider"),)

def __str__(self) -> str:
return f"Google Workspace Provider User {self.user_id} to {self.provider_id}"


class GoogleWorkspaceProviderGroup(SerializerModel):
"""Mapping of a group and provider to a Google group ID"""

id = models.UUIDField(primary_key=True, editable=False, default=uuid4)
google_id = models.TextField()
group = models.ForeignKey(Group, on_delete=models.CASCADE)
provider = models.ForeignKey(GoogleWorkspaceProvider, on_delete=models.CASCADE)
attributes = models.JSONField(default=dict)

@property
def serializer(self) -> type[Serializer]:
from authentik.enterprise.providers.google_workspace.api.groups import (
GoogleWorkspaceProviderGroupSerializer,
)

return GoogleWorkspaceProviderGroupSerializer

class Meta:
verbose_name = _("Google Workspace Provider Group")
verbose_name_plural = _("Google Workspace Provider Groups")
unique_together = (("google_id", "group", "provider"),)

def __str__(self) -> str:
return f"Google Workspace Provider Group {self.group_id} to {self.provider_id}"
2 changes: 2 additions & 0 deletions authentik/enterprise/providers/microsoft_entra/api/groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.users import UserGroupSerializer
from authentik.enterprise.providers.microsoft_entra.models import MicrosoftEntraProviderGroup
from authentik.lib.sync.outgoing.api import OutgoingSyncConnectionCreateMixin


class MicrosoftEntraProviderGroupSerializer(ModelSerializer):
Expand All @@ -30,6 +31,7 @@ class Meta:

class MicrosoftEntraProviderGroupViewSet(
mixins.CreateModelMixin,
OutgoingSyncConnectionCreateMixin,
mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
UsedByMixin,
Expand Down
2 changes: 2 additions & 0 deletions authentik/enterprise/providers/microsoft_entra/api/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from authentik.core.api.groups import GroupMemberSerializer
from authentik.core.api.used_by import UsedByMixin
from authentik.enterprise.providers.microsoft_entra.models import MicrosoftEntraProviderUser
from authentik.lib.sync.outgoing.api import OutgoingSyncConnectionCreateMixin


class MicrosoftEntraProviderUserSerializer(ModelSerializer):
Expand All @@ -29,6 +30,7 @@ class Meta:


class MicrosoftEntraProviderUserViewSet(
OutgoingSyncConnectionCreateMixin,
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,3 +226,7 @@ def _discover_single_group(self, group: MSGroup):
microsoft_id=group.id,
attributes=self.entity_as_dict(group),
)

def update_single_attribute(self, connection: MicrosoftEntraProviderGroup):
data = self._request(self.client.groups.by_group_id(connection.microsoft_id).get())
connection.attributes = self.entity_as_dict(data)
47 changes: 41 additions & 6 deletions authentik/enterprise/providers/microsoft_entra/clients/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,26 @@ def delete(self, obj: User):
microsoft_user.delete()
return response

def get_select_fields(self) -> list[str]:
"""All fields that should be selected when we fetch user data."""
# TODO: Make this customizable in the future
return [
# Default fields
"businessPhones",
"displayName",
"givenName",
"jobTitle",
"mail",
"mobilePhone",
"officeLocation",
"preferredLanguage",
"surname",
"userPrincipalName",
"id",
# Required for logging into M365 using authentik
"onPremisesImmutableId",
]

def create(self, user: User):
"""Create user from scratch and create a connection object"""
microsoft_user = self.to_schema(user, None)
Expand All @@ -75,12 +95,12 @@ def create(self, user: User):
response = self._request(self.client.users.post(microsoft_user))
except ObjectExistsSyncException:
# user already exists in microsoft entra, so we can connect them manually
query_params = UsersRequestBuilder.UsersRequestBuilderGetQueryParameters()(
filter=f"mail eq '{microsoft_user.mail}'",
)
request_configuration = (
UsersRequestBuilder.UsersRequestBuilderGetRequestConfiguration(
query_parameters=query_params,
query_parameters=UsersRequestBuilder.UsersRequestBuilderGetQueryParameters(
filter=f"mail eq '{microsoft_user.mail}'",
select=self.get_select_fields(),
),
)
)
user_data = self._request(self.client.users.get(request_configuration))
Expand All @@ -99,7 +119,6 @@ def create(self, user: User):
except TransientSyncException as exc:
raise exc
else:
print(self.entity_as_dict(response))
return MicrosoftEntraProviderUser.objects.create(
provider=self.provider,
user=user,
Expand All @@ -120,7 +139,12 @@ def update(self, user: User, connection: MicrosoftEntraProviderUser):

def discover(self):
"""Iterate through all users and connect them with authentik users if possible"""
users = self._request(self.client.users.get())
request_configuration = UsersRequestBuilder.UsersRequestBuilderGetRequestConfiguration(
query_parameters=UsersRequestBuilder.UsersRequestBuilderGetQueryParameters(
select=self.get_select_fields(),
),
)
users = self._request(self.client.users.get(request_configuration))
next_link = True
while next_link:
for user in users.value:
Expand All @@ -141,3 +165,14 @@ def _discover_single_user(self, user: MSUser):
microsoft_id=user.id,
attributes=self.entity_as_dict(user),
)

def update_single_attribute(self, connection: MicrosoftEntraProviderUser):
request_configuration = UsersRequestBuilder.UsersRequestBuilderGetRequestConfiguration(
query_parameters=UsersRequestBuilder.UsersRequestBuilderGetQueryParameters(
select=self.get_select_fields(),
),
)
data = self._request(
self.client.users.by_user_id(connection.microsoft_id).get(request_configuration)
)
connection.attributes = self.entity_as_dict(data)
Loading

0 comments on commit c49185d

Please sign in to comment.