使用djangosaml2实现SSO时,出现验证错误'is_staff':[“'true'值必须为True或False。”]

时间:2019-10-09 17:47:28

标签: django postgresql single-sign-on python-3.6 saml-2.0

我尝试为我的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
djangosaml2 SP的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后端时,但是当我为自定义用户模型覆盖User模型时,抛出了以上错误。 我认为在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)

1 个答案:

答案 0 :(得分:0)

遇到相同的问题,似乎与djangosaml2idp实现有关。

在源代码中的示例代码中,应将以下代码添加到User应用程序的view.py文件中(如果未自定义User模型,则应添加到主项目文件夹中)。

@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

来源: custom_update_user