使用 djangosaml2 实现 SSO 时出现验证错误 'is_staff': ["'true' value must be True or False."]
Getting validation error 'is_staff': ["'true' value must be either True or False."] while implementing SSO using djangosaml2
我尝试为我的 Django 项目实施 SSO。
当我尝试登录表单 SP ('/sso/login') 时,我从我的 IDP 那里得到了正确的响应,但是在 SP 端 'sso/acs' 它抛出了一个验证错误。
{'is_superuser': ["'true' value must be either True or False."], 'is_staff': ["'true' value must be either True or False."]}
当我从 SAML_ATTRIBUTE_MAPPING 中删除 is_staff 和 is_superuser 时。
SSO 正在运行。
项目包——
django 1.11,
后处理,
djangosaml2idp 0.5.0,用于 IDP
用于 SP
的 djangosaml2 0.17.2
My setting for SP:
SAML_USE_NAME_ID_AS_USERNAME = True
SAML_DJANGO_USER_MAIN_ATTRIBUTE = 'email'
SAML_DJANGO_USER_MAIN_ATTRIBUTE_LOOKUP = '__iexact'
SAML_CREATE_UNKNOWN_USER = False
SAML_LOGOUT_REQUEST_PREFERRED_BINDING = saml2.BINDING_HTTP_POST
SAML_ATTRIBUTE_MAPPING = {
# SAML : DJANGO
# Must also be present in attribute-maps!
'email': ('email', ),
'username': ('username', ),
'account_id': ('account_id', ),
'is_staff': ('is_staff', ),
'is_superuser': ('is_superuser', ),
}
错误回溯:
Traceback (most recent call last):
File "/Users/lite/Virtual_Envs/spEnv/lib/python3.6/site-packages/django/core/handlers/exception.py", line 34, in inner
response = get_response(request)
File "/Users/lite/Virtual_Envs/spEnv/lib/python3.6/site-packages/django/core/handlers/base.py", line 115, in _get_response
response = self.process_exception_by_middleware(e, request)
File "/Users/lite/Virtual_Envs/spEnv/lib/python3.6/site-packages/django/core/handlers/base.py", line 113, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/Users/lite/Virtual_Envs/spEnv/lib/python3.6/site-packages/django/views/decorators/http.py", line 40, in inner
return func(request, *args, **kwargs)
File "/Users/lite/Virtual_Envs/spEnv/lib/python3.6/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
return view_func(*args, **kwargs)
File "/Users/lite/Virtual_Envs/spEnv/lib/python3.6/site-packages/djangosaml2/views.py", line 316, in assertion_consumer_service
create_unknown_user=create_unknown_user)
File "/Users/lite/Virtual_Envs/spEnv/lib/python3.6/site-packages/django/contrib/auth/__init__.py", line 73, in authenticate
user = backend.authenticate(request, **credentials)
File "/Users/lite/Virtual_Envs/spEnv/lib/python3.6/site-packages/djangosaml2/backends.py", line 107, in authenticate
create_unknown_user, main_attribute, attributes, attribute_mapping)
File "/Users/lite/Virtual_Envs/spEnv/lib/python3.6/site-packages/djangosaml2/backends.py", line 154, in get_saml2_user
return self._get_saml2_user(main_attribute, attributes, attribute_mapping)
File "/Users/lite/Virtual_Envs/spEnv/lib/python3.6/site-packages/djangosaml2/backends.py", line 188, in _get_saml2_user
user = self.update_user(user, attributes, attribute_mapping)
File "/Users/lite/Virtual_Envs/spEnv/lib/python3.6/site-packages/djangosaml2/backends.py", line 250, in update_user
user.save()
File "/Users/lite/projects/djangosaml2/sample-sp/sp/models.py", line 105, in save
self.full_clean()
File "/Users/lite/Virtual_Envs/spEnv/lib/python3.6/site-packages/django/db/models/base.py", line 1203, in full_clean
raise ValidationError(errors)
django.core.exceptions.ValidationError: {'is_superuser': ["'true' value must be either True or False."], 'is_staff': ["'true' value must be either True or False."]}
我尝试使用不同的数据库,无论是否覆盖 Django 用户模型。
当我尝试使用默认 (auth.models) 时,用户模型 sso 正在使用 Postgres 和 sqlite 后端,但是当我为我的自定义用户模型覆盖用户模型时,它会抛出上述错误。
我认为 SP 不是对 IDP 响应进行类型转换,它只是将所有属性解析并作为字符串传递,同时在 DB 上保存它会导致验证错误。但为什么它使用默认的 Django 用户模型?
应用程序用户模型
class User(AbstractBaseUser, PermissionsMixin):
username = models.CharField(max_length=15, unique=True,)
email = models.EmailField(max_length=123, blank=False, null=False, unique=True)
is_staff = models.BooleanField(default=False, db_index=True)
objects = UserManager()
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email']
def clean(self):
errors = {}
users = get_user_model().objects.filter(username__iexact=self.username)
if len(users) > 1:
errors.setdefault('username', []).append(f'{self.username} not available')
if errors:
raise ValidationError(errors)
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
self.full_clean()
return super().save(force_insert, force_update, using, update_fields)
有同样的问题,这似乎与 djangosaml2idp
实施有关。
根据源代码中的示例代码,您应该将以下代码添加到用户应用程序 view.py 文件中(或者在主项目文件夹中,如果您没有自定义用户模型)。
@receiver(pre_user_save, sender=User)
def custom_update_user(sender, instance, attributes, user_modified, **kargs):
""" Default behaviour does not play nice with booleans encoded in SAML as u'true'/u'false'.
This will convert those attributes to real booleans when saving.
"""
for k, v in attributes.items():
u = set.intersection(set(v), set([u'true', u'false']))
if u:
setattr(instance, k, u.pop() == u'true')
return True
我尝试为我的 Django 项目实施 SSO。 当我尝试登录表单 SP ('/sso/login') 时,我从我的 IDP 那里得到了正确的响应,但是在 SP 端 'sso/acs' 它抛出了一个验证错误。
{'is_superuser': ["'true' value must be either True or False."], 'is_staff': ["'true' value must be either True or False."]}
当我从 SAML_ATTRIBUTE_MAPPING 中删除 is_staff 和 is_superuser 时。
SSO 正在运行。
项目包——
django 1.11,
后处理,
djangosaml2idp 0.5.0,用于 IDP
用于 SP
My setting for SP:
SAML_USE_NAME_ID_AS_USERNAME = True
SAML_DJANGO_USER_MAIN_ATTRIBUTE = 'email'
SAML_DJANGO_USER_MAIN_ATTRIBUTE_LOOKUP = '__iexact'
SAML_CREATE_UNKNOWN_USER = False
SAML_LOGOUT_REQUEST_PREFERRED_BINDING = saml2.BINDING_HTTP_POST
SAML_ATTRIBUTE_MAPPING = {
# SAML : DJANGO
# Must also be present in attribute-maps!
'email': ('email', ),
'username': ('username', ),
'account_id': ('account_id', ),
'is_staff': ('is_staff', ),
'is_superuser': ('is_superuser', ),
}
错误回溯:
Traceback (most recent call last):
File "/Users/lite/Virtual_Envs/spEnv/lib/python3.6/site-packages/django/core/handlers/exception.py", line 34, in inner
response = get_response(request)
File "/Users/lite/Virtual_Envs/spEnv/lib/python3.6/site-packages/django/core/handlers/base.py", line 115, in _get_response
response = self.process_exception_by_middleware(e, request)
File "/Users/lite/Virtual_Envs/spEnv/lib/python3.6/site-packages/django/core/handlers/base.py", line 113, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/Users/lite/Virtual_Envs/spEnv/lib/python3.6/site-packages/django/views/decorators/http.py", line 40, in inner
return func(request, *args, **kwargs)
File "/Users/lite/Virtual_Envs/spEnv/lib/python3.6/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
return view_func(*args, **kwargs)
File "/Users/lite/Virtual_Envs/spEnv/lib/python3.6/site-packages/djangosaml2/views.py", line 316, in assertion_consumer_service
create_unknown_user=create_unknown_user)
File "/Users/lite/Virtual_Envs/spEnv/lib/python3.6/site-packages/django/contrib/auth/__init__.py", line 73, in authenticate
user = backend.authenticate(request, **credentials)
File "/Users/lite/Virtual_Envs/spEnv/lib/python3.6/site-packages/djangosaml2/backends.py", line 107, in authenticate
create_unknown_user, main_attribute, attributes, attribute_mapping)
File "/Users/lite/Virtual_Envs/spEnv/lib/python3.6/site-packages/djangosaml2/backends.py", line 154, in get_saml2_user
return self._get_saml2_user(main_attribute, attributes, attribute_mapping)
File "/Users/lite/Virtual_Envs/spEnv/lib/python3.6/site-packages/djangosaml2/backends.py", line 188, in _get_saml2_user
user = self.update_user(user, attributes, attribute_mapping)
File "/Users/lite/Virtual_Envs/spEnv/lib/python3.6/site-packages/djangosaml2/backends.py", line 250, in update_user
user.save()
File "/Users/lite/projects/djangosaml2/sample-sp/sp/models.py", line 105, in save
self.full_clean()
File "/Users/lite/Virtual_Envs/spEnv/lib/python3.6/site-packages/django/db/models/base.py", line 1203, in full_clean
raise ValidationError(errors)
django.core.exceptions.ValidationError: {'is_superuser': ["'true' value must be either True or False."], 'is_staff': ["'true' value must be either True or False."]}
我尝试使用不同的数据库,无论是否覆盖 Django 用户模型。 当我尝试使用默认 (auth.models) 时,用户模型 sso 正在使用 Postgres 和 sqlite 后端,但是当我为我的自定义用户模型覆盖用户模型时,它会抛出上述错误。 我认为 SP 不是对 IDP 响应进行类型转换,它只是将所有属性解析并作为字符串传递,同时在 DB 上保存它会导致验证错误。但为什么它使用默认的 Django 用户模型?
应用程序用户模型
class User(AbstractBaseUser, PermissionsMixin):
username = models.CharField(max_length=15, unique=True,)
email = models.EmailField(max_length=123, blank=False, null=False, unique=True)
is_staff = models.BooleanField(default=False, db_index=True)
objects = UserManager()
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email']
def clean(self):
errors = {}
users = get_user_model().objects.filter(username__iexact=self.username)
if len(users) > 1:
errors.setdefault('username', []).append(f'{self.username} not available')
if errors:
raise ValidationError(errors)
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
self.full_clean()
return super().save(force_insert, force_update, using, update_fields)
有同样的问题,这似乎与 djangosaml2idp
实施有关。
根据源代码中的示例代码,您应该将以下代码添加到用户应用程序 view.py 文件中(或者在主项目文件夹中,如果您没有自定义用户模型)。
@receiver(pre_user_save, sender=User)
def custom_update_user(sender, instance, attributes, user_modified, **kargs):
""" Default behaviour does not play nice with booleans encoded in SAML as u'true'/u'false'.
This will convert those attributes to real booleans when saving.
"""
for k, v in attributes.items():
u = set.intersection(set(v), set([u'true', u'false']))
if u:
setattr(instance, k, u.pop() == u'true')
return True