providers/scim: default to None for fields instead of empty list (#5642)
* providers/scim: default to None for fields instead of empty list Signed-off-by: Jens Langhammer <jens@goauthentik.io> * make name of delete_none_keys clearer Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
parent
daa3c91afc
commit
f4b0d6e85c
|
@ -28,7 +28,7 @@ from authentik.flows.views.executor import NEXT_ARG_NAME, SESSION_KEY_GET, SESSI
|
||||||
from authentik.lib.utils.urls import redirect_with_qs
|
from authentik.lib.utils.urls import redirect_with_qs
|
||||||
from authentik.lib.views import bad_request_message
|
from authentik.lib.views import bad_request_message
|
||||||
from authentik.policies.denied import AccessDeniedResponse
|
from authentik.policies.denied import AccessDeniedResponse
|
||||||
from authentik.policies.utils import delete_none_keys
|
from authentik.policies.utils import delete_none_values
|
||||||
from authentik.stages.password import BACKEND_INBUILT
|
from authentik.stages.password import BACKEND_INBUILT
|
||||||
from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND
|
from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND
|
||||||
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
|
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
|
||||||
|
@ -329,7 +329,7 @@ class SourceFlowManager:
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
**{
|
**{
|
||||||
PLAN_CONTEXT_PROMPT: delete_none_keys(self.enroll_info),
|
PLAN_CONTEXT_PROMPT: delete_none_values(self.enroll_info),
|
||||||
PLAN_CONTEXT_USER_PATH: self.source.get_user_path(),
|
PLAN_CONTEXT_USER_PATH: self.source.get_user_path(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
def delete_none_keys(dict_: dict[Any, Any]) -> dict[Any, Any]:
|
def delete_none_values(dict_: dict[Any, Any]) -> dict[Any, Any]:
|
||||||
"""Remove any keys from `dict_` that are None."""
|
"""Remove any keys from `dict_` that are None."""
|
||||||
new_dict = {}
|
new_dict = {}
|
||||||
for key, value in dict_.items():
|
for key, value in dict_.items():
|
||||||
|
|
|
@ -8,7 +8,7 @@ from authentik.core.exceptions import PropertyMappingExpressionException
|
||||||
from authentik.core.models import Group
|
from authentik.core.models import Group
|
||||||
from authentik.events.models import Event, EventAction
|
from authentik.events.models import Event, EventAction
|
||||||
from authentik.lib.utils.errors import exception_to_string
|
from authentik.lib.utils.errors import exception_to_string
|
||||||
from authentik.policies.utils import delete_none_keys
|
from authentik.policies.utils import delete_none_values
|
||||||
from authentik.providers.scim.clients.base import SCIMClient
|
from authentik.providers.scim.clients.base import SCIMClient
|
||||||
from authentik.providers.scim.clients.exceptions import (
|
from authentik.providers.scim.clients.exceptions import (
|
||||||
ResourceMissing,
|
ResourceMissing,
|
||||||
|
@ -74,7 +74,7 @@ class SCIMGroupClient(SCIMClient[Group, SCIMGroupSchema]):
|
||||||
if not raw_scim_group:
|
if not raw_scim_group:
|
||||||
raise StopSync(ValueError("No group mappings configured"), obj)
|
raise StopSync(ValueError("No group mappings configured"), obj)
|
||||||
try:
|
try:
|
||||||
scim_group = SCIMGroupSchema.parse_obj(delete_none_keys(raw_scim_group))
|
scim_group = SCIMGroupSchema.parse_obj(delete_none_values(raw_scim_group))
|
||||||
except ValidationError as exc:
|
except ValidationError as exc:
|
||||||
raise StopSync(exc, obj) from exc
|
raise StopSync(exc, obj) from exc
|
||||||
if not scim_group.externalId:
|
if not scim_group.externalId:
|
||||||
|
|
|
@ -6,7 +6,7 @@ from authentik.core.exceptions import PropertyMappingExpressionException
|
||||||
from authentik.core.models import User
|
from authentik.core.models import User
|
||||||
from authentik.events.models import Event, EventAction
|
from authentik.events.models import Event, EventAction
|
||||||
from authentik.lib.utils.errors import exception_to_string
|
from authentik.lib.utils.errors import exception_to_string
|
||||||
from authentik.policies.utils import delete_none_keys
|
from authentik.policies.utils import delete_none_values
|
||||||
from authentik.providers.scim.clients.base import SCIMClient
|
from authentik.providers.scim.clients.base import SCIMClient
|
||||||
from authentik.providers.scim.clients.exceptions import ResourceMissing, StopSync
|
from authentik.providers.scim.clients.exceptions import ResourceMissing, StopSync
|
||||||
from authentik.providers.scim.clients.schema import User as SCIMUserSchema
|
from authentik.providers.scim.clients.schema import User as SCIMUserSchema
|
||||||
|
@ -64,7 +64,7 @@ class SCIMUserClient(SCIMClient[User, SCIMUserSchema]):
|
||||||
if not raw_scim_user:
|
if not raw_scim_user:
|
||||||
raise StopSync(ValueError("No user mappings configured"), obj)
|
raise StopSync(ValueError("No user mappings configured"), obj)
|
||||||
try:
|
try:
|
||||||
scim_user = SCIMUserSchema.parse_obj(delete_none_keys(raw_scim_user))
|
scim_user = SCIMUserSchema.parse_obj(delete_none_values(raw_scim_user))
|
||||||
except ValidationError as exc:
|
except ValidationError as exc:
|
||||||
raise StopSync(exc, obj) from exc
|
raise StopSync(exc, obj) from exc
|
||||||
if not scim_user.externalId:
|
if not scim_user.externalId:
|
||||||
|
|
|
@ -91,7 +91,6 @@ class SCIMMembershipTests(TestCase):
|
||||||
"active": True,
|
"active": True,
|
||||||
"externalId": user.uid,
|
"externalId": user.uid,
|
||||||
"name": {"familyName": "", "formatted": "", "givenName": ""},
|
"name": {"familyName": "", "formatted": "", "givenName": ""},
|
||||||
"photos": [],
|
|
||||||
"displayName": "",
|
"displayName": "",
|
||||||
"userName": user.username,
|
"userName": user.username,
|
||||||
},
|
},
|
||||||
|
@ -177,7 +176,6 @@ class SCIMMembershipTests(TestCase):
|
||||||
"emails": [],
|
"emails": [],
|
||||||
"externalId": user.uid,
|
"externalId": user.uid,
|
||||||
"name": {"familyName": "", "formatted": "", "givenName": ""},
|
"name": {"familyName": "", "formatted": "", "givenName": ""},
|
||||||
"photos": [],
|
|
||||||
"userName": user.username,
|
"userName": user.username,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -81,7 +81,6 @@ class SCIMUserTests(TestCase):
|
||||||
"givenName": uid,
|
"givenName": uid,
|
||||||
},
|
},
|
||||||
"displayName": uid,
|
"displayName": uid,
|
||||||
"photos": [],
|
|
||||||
"userName": uid,
|
"userName": uid,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -137,7 +136,6 @@ class SCIMUserTests(TestCase):
|
||||||
"formatted": uid,
|
"formatted": uid,
|
||||||
"givenName": uid,
|
"givenName": uid,
|
||||||
},
|
},
|
||||||
"photos": [],
|
|
||||||
"userName": uid,
|
"userName": uid,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -190,7 +188,6 @@ class SCIMUserTests(TestCase):
|
||||||
"givenName": uid,
|
"givenName": uid,
|
||||||
},
|
},
|
||||||
"displayName": uid,
|
"displayName": uid,
|
||||||
"photos": [],
|
|
||||||
"userName": uid,
|
"userName": uid,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -258,7 +255,6 @@ class SCIMUserTests(TestCase):
|
||||||
"givenName": uid,
|
"givenName": uid,
|
||||||
},
|
},
|
||||||
"displayName": uid,
|
"displayName": uid,
|
||||||
"photos": [],
|
|
||||||
"userName": uid,
|
"userName": uid,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -21,7 +21,7 @@ from authentik.core.models import (
|
||||||
from authentik.core.sources.flow_manager import SourceFlowManager
|
from authentik.core.sources.flow_manager import SourceFlowManager
|
||||||
from authentik.lib.expression.evaluator import BaseEvaluator
|
from authentik.lib.expression.evaluator import BaseEvaluator
|
||||||
from authentik.lib.utils.time import timedelta_from_string
|
from authentik.lib.utils.time import timedelta_from_string
|
||||||
from authentik.policies.utils import delete_none_keys
|
from authentik.policies.utils import delete_none_values
|
||||||
from authentik.sources.saml.exceptions import (
|
from authentik.sources.saml.exceptions import (
|
||||||
InvalidSignature,
|
InvalidSignature,
|
||||||
MismatchedRequestID,
|
MismatchedRequestID,
|
||||||
|
@ -160,7 +160,7 @@ class ResponseProcessor:
|
||||||
self._source,
|
self._source,
|
||||||
self._http_request,
|
self._http_request,
|
||||||
name_id,
|
name_id,
|
||||||
delete_none_keys(self.get_attributes()),
|
delete_none_values(self.get_attributes()),
|
||||||
)
|
)
|
||||||
|
|
||||||
def _get_name_id(self) -> "Element":
|
def _get_name_id(self) -> "Element":
|
||||||
|
@ -237,7 +237,7 @@ class ResponseProcessor:
|
||||||
self._source,
|
self._source,
|
||||||
self._http_request,
|
self._http_request,
|
||||||
name_id.text,
|
name_id.text,
|
||||||
delete_none_keys(self.get_attributes()),
|
delete_none_values(self.get_attributes()),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ entries:
|
||||||
|
|
||||||
# photos supports URLs to images, however authentik might return data URIs
|
# photos supports URLs to images, however authentik might return data URIs
|
||||||
avatar = request.user.avatar
|
avatar = request.user.avatar
|
||||||
photos = []
|
photos = None
|
||||||
if "://" in avatar:
|
if "://" in avatar:
|
||||||
photos = [{"value": avatar, "type": "photo"}]
|
photos = [{"value": avatar, "type": "photo"}]
|
||||||
|
|
||||||
|
@ -31,11 +31,11 @@ entries:
|
||||||
|
|
||||||
emails = []
|
emails = []
|
||||||
if request.user.email != "":
|
if request.user.email != "":
|
||||||
emails.append({
|
emails = [{
|
||||||
"value": request.user.email,
|
"value": request.user.email,
|
||||||
"type": "other",
|
"type": "other",
|
||||||
"primary": True,
|
"primary": True,
|
||||||
})
|
}]
|
||||||
return {
|
return {
|
||||||
"userName": request.user.username,
|
"userName": request.user.username,
|
||||||
"name": {
|
"name": {
|
||||||
|
|
Reference in a new issue