From 2c2574a72d23a70a66f480c5ab693994dbc136f7 Mon Sep 17 00:00:00 2001
From: Paul Marillonnet <pmarillonnet@entrouvert.com>
Date: Thu, 21 Feb 2019 17:32:53 +0100
Subject: [PATCH] python3: py2 unicode compatible __str__ magid methods
 (#31184)

---
 src/authentic2/a2_rbac/admin.py               |  2 +-
 src/authentic2/a2_rbac/models.py              |  8 +++++++
 src/authentic2/admin.py                       |  2 +-
 src/authentic2/auth2_auth/auth2_ssl/models.py |  4 +++-
 src/authentic2/custom_user/models.py          |  3 ++-
 src/authentic2/models.py                      | 15 ++++++++-----
 src/authentic2/nonce/models.py                |  5 +++--
 src/authentic2/saml/models.py                 | 21 ++++++++++++-------
 src/authentic2_auth_oidc/models.py            | 10 ++++++---
 src/authentic2_idp_cas/models.py              | 10 ++++++---
 src/authentic2_idp_oidc/models.py             |  3 ++-
 src/django_rbac/models.py                     |  9 +++++---
 12 files changed, 64 insertions(+), 28 deletions(-)

diff --git a/src/authentic2/a2_rbac/admin.py b/src/authentic2/a2_rbac/admin.py
index ce9d5ee2..9f957151 100644
--- a/src/authentic2/a2_rbac/admin.py
+++ b/src/authentic2/a2_rbac/admin.py
@@ -36,7 +36,7 @@ class RoleAdmin(admin.ModelAdmin):
     readonly_fields = ('uuid',)
     prepopulated_fields = {"slug": ("name",)}
     filter_horizontal = ('members', 'permissions')
-    list_display = ('__unicode__', 'slug', 'ou', 'service', 'admin_scope')
+    list_display = ('__str__', 'slug', 'ou', 'service', 'admin_scope')
     list_select_related = True
     list_filter = ['ou', 'service']
     inlines = [RoleAttributeInline]
diff --git a/src/authentic2/a2_rbac/models.py b/src/authentic2/a2_rbac/models.py
index c6eec1ed..8c7717ff 100644
--- a/src/authentic2/a2_rbac/models.py
+++ b/src/authentic2/a2_rbac/models.py
@@ -25,6 +25,7 @@ from authentic2.decorators import GlobalCache
 from . import managers, fields
 
 
+@six.python_2_unicode_compatible
 class OrganizationalUnit(OrganizationalUnitAbstractBase):
 
     RESET_LINK_POLICY = 0
@@ -134,6 +135,9 @@ class OrganizationalUnit(OrganizationalUnitAbstractBase):
             'validate_emails': self.validate_emails
         }
 
+    def __str__(self):
+        return self.name
+
 
 OrganizationalUnit._meta.natural_key = [['uuid'], ['slug'], ['name']]
 
@@ -293,11 +297,15 @@ Role._meta.natural_key = [
 ]
 
 
+@six.python_2_unicode_compatible
 class RoleParenting(RoleParentingAbstractBase):
     class Meta(RoleParentingAbstractBase.Meta):
         verbose_name = _('role parenting relation')
         verbose_name_plural = _('role parenting relations')
 
+    def __str__(self):
+        return self.name
+
 
 class RoleAttribute(models.Model):
     KINDS = (
diff --git a/src/authentic2/admin.py b/src/authentic2/admin.py
index 322cd840..18e47dd6 100644
--- a/src/authentic2/admin.py
+++ b/src/authentic2/admin.py
@@ -251,7 +251,7 @@ class AuthenticUserAdmin(UserAdmin):
         )
     readonly_fields = ('uuid',)
     list_filter = UserAdmin.list_filter + (UserRealmListFilter,ExternalUserListFilter)
-    list_display = ['__unicode__', 'ou', 'first_name', 'last_name', 'email']
+    list_display = ['__str__', 'ou', 'first_name', 'last_name', 'email']
 
     def get_fieldsets(self, request, obj=None):
         fieldsets = deepcopy(super(AuthenticUserAdmin, self).get_fieldsets(request, obj))
diff --git a/src/authentic2/auth2_auth/auth2_ssl/models.py b/src/authentic2/auth2_auth/auth2_ssl/models.py
index f180a1f6..c6b582fc 100644
--- a/src/authentic2/auth2_auth/auth2_ssl/models.py
+++ b/src/authentic2/auth2_auth/auth2_ssl/models.py
@@ -1,8 +1,10 @@
 from django.db import models
 from django.conf import settings
+from django.utils import six
 
 from . import util
 
+@six.python_2_unicode_compatible
 class ClientCertificate(models.Model):
     serial = models.CharField(max_length=255, blank=True)
     subject_dn = models.CharField(max_length=255)
@@ -10,7 +12,7 @@ class ClientCertificate(models.Model):
     cert = models.TextField()
     user = models.ForeignKey(settings.AUTH_USER_MODEL)
 
-    def __unicode__(self):
+    def __str__(self):
         return self.subject_dn
 
     def explode_subject_dn(self):
diff --git a/src/authentic2/custom_user/models.py b/src/authentic2/custom_user/models.py
index 25d81952..55a8cd77 100644
--- a/src/authentic2/custom_user/models.py
+++ b/src/authentic2/custom_user/models.py
@@ -95,6 +95,7 @@ class IsVerifiedDescriptor(object):
         return IsVerified(obj)
 
 
+@six.python_2_unicode_compatible
 class User(AbstractBaseUser, PermissionMixin):
     """
     An abstract base class implementing a fully featured User model with
@@ -182,7 +183,7 @@ class User(AbstractBaseUser, PermissionMixin):
             'members', queryset=self.__class__.objects.filter(pk=self.pk), to_attr='member'))
         return qs
 
-    def __unicode__(self):
+    def __str__(self):
         human_name = self.username or self.email or self.get_full_name()
         short_id = self.uuid[:6]
         return u'%s (%s)' % (human_name, short_id)
diff --git a/src/authentic2/models.py b/src/authentic2/models.py
index 2621c17e..dcf0a911 100644
--- a/src/authentic2/models.py
+++ b/src/authentic2/models.py
@@ -42,6 +42,7 @@ class DeletedUser(models.Model):
         verbose_name = _('user to delete')
         verbose_name_plural = _('users to delete')
 
+@six.python_2_unicode_compatible
 class UserExternalId(models.Model):
     user = models.ForeignKey(settings.AUTH_USER_MODEL,
             verbose_name=_('user'))
@@ -54,7 +55,7 @@ class UserExternalId(models.Model):
     updated = models.DateTimeField(auto_now=True,
             verbose_name=_('last update date'))
 
-    def __unicode__(self):
+    def __str__(self):
         return u'{0} is {1} on {2}'.format(
                 self.user, self.external_id, self.source)
 
@@ -68,6 +69,7 @@ class UserExternalId(models.Model):
         verbose_name = _('user external id')
         verbose_name_plural = _('user external ids')
 
+@six.python_2_unicode_compatible
 class AuthenticationEvent(models.Model):
     '''Record authentication events whatever the source'''
     when = models.DateTimeField(auto_now=True,
@@ -85,7 +87,7 @@ class AuthenticationEvent(models.Model):
         verbose_name = _('authentication log')
         verbose_name_plural = _('authentication logs')
 
-    def __unicode__(self):
+    def __str__(self):
         return _('Authentication of %(who)s by %(how)s at %(when)s') % \
             self.__dict__
 
@@ -124,6 +126,7 @@ class LogoutUrl(LogoutUrlAbstract):
         verbose_name_plural = _('logout URL')
 
 
+@six.python_2_unicode_compatible
 class Attribute(models.Model):
     label = models.CharField(verbose_name=_('label'), max_length=63,
             unique=True)
@@ -248,7 +251,7 @@ class Attribute(models.Model):
     def natural_key(self):
         return (self.name,)
 
-    def __unicode__(self):
+    def __str__(self):
         return self.label
 
     class Meta:
@@ -292,6 +295,7 @@ class AttributeValue(models.Model):
         )
 
 
+@six.python_2_unicode_compatible
 class PasswordReset(models.Model):
     user = models.OneToOneField(settings.AUTH_USER_MODEL,
             verbose_name=_('user'))
@@ -306,10 +310,11 @@ class PasswordReset(models.Model):
         verbose_name = _('password reset')
         verbose_name_plural = _('password reset')
 
-    def __unicode__(self):
+    def __str__(self):
         return six.text_type(self.user)
 
 
+@six.python_2_unicode_compatible
 class Service(models.Model):
     name = models.CharField(
         verbose_name=_('name'),
@@ -359,7 +364,7 @@ class Service(models.Model):
     def natural_key(self):
         return [self.ou and self.ou.natural_key(), self.slug]
 
-    def __unicode__(self):
+    def __str__(self):
         return self.name
 
     def __repr__(self):
diff --git a/src/authentic2/nonce/models.py b/src/authentic2/nonce/models.py
index ae135e1e..e9b6d866 100644
--- a/src/authentic2/nonce/models.py
+++ b/src/authentic2/nonce/models.py
@@ -1,7 +1,7 @@
 import datetime as dt
 
 from django.db import models
-from django.utils import timezone
+from django.utils import timezone, six
 
 __all__ = ('Nonce',)
 
@@ -12,6 +12,7 @@ class NonceManager(models.Manager):
         now = now or timezone.now()
         self.filter(not_on_or_after__lt=now).delete()
 
+@six.python_2_unicode_compatible
 class Nonce(models.Model):
     value = models.CharField(max_length=_NONCE_LENGTH_CONSTANT)
     context = models.CharField(max_length=_NONCE_LENGTH_CONSTANT, blank=True,
@@ -20,5 +21,5 @@ class Nonce(models.Model):
 
     objects  = NonceManager()
 
-    def __unicode__(self):
+    def __str__(self):
         return self.value
diff --git a/src/authentic2/saml/models.py b/src/authentic2/saml/models.py
index 43d5e9f8..4c1049b1 100644
--- a/src/authentic2/saml/models.py
+++ b/src/authentic2/saml/models.py
@@ -161,6 +161,7 @@ AUTHSAML2_UNAUTH_TRANSIENT = (
 )
 
 
+@six.python_2_unicode_compatible
 class SPOptionsIdPPolicy(models.Model):
     '''
         Policies configured as a SAML2 identity provider.
@@ -232,9 +233,10 @@ class SPOptionsIdPPolicy(models.Model):
         verbose_name = _('service provider options policy')
         verbose_name_plural = _('service provider options policies')
 
-    def __unicode__(self):
+    def __str__(self):
         return self.name
 
+@six.python_2_unicode_compatible
 class SAMLAttribute(models.Model):
     ATTRIBUTE_NAME_FORMATS = (
             ('basic', 'Basic'),
@@ -294,7 +296,7 @@ class SAMLAttribute(models.Model):
         for text_value in normalize_attribute_values(values):
             yield (name, name_format, friendly_name, text_value)
 
-    def __unicode__(self):
+    def __str__(self):
         return u'%s %s %s' % (self.name, self.name_format_uri(), self.attribute_name)
 
     def natural_key(self):
@@ -307,6 +309,7 @@ class SAMLAttribute(models.Model):
             'friendly_name', 'attribute_name'),)
 
 
+@six.python_2_unicode_compatible
 class LibertyProvider(Service):
     entity_id = models.URLField(max_length=256, unique=True,
             verbose_name=_('Entity ID'))
@@ -329,7 +332,7 @@ class LibertyProvider(Service):
 
     objects = managers.LibertyProviderManager()
 
-    def __unicode__(self):
+    def __str__(self):
         return self.name
 
     def save(self, *args, **kwargs):
@@ -387,6 +390,7 @@ def get_all_custom_or_default(instance, name):
 
 # TODO: The IdP must look to the preferred binding order for sso in the SP metadata (AssertionConsumerService)
 # expect if the protocol for response is defined in the request (ProtocolBinding attribute)
+@six.python_2_unicode_compatible
 class LibertyServiceProvider(models.Model):
     liberty_provider = models.OneToOneField(LibertyProvider,
             primary_key = True, related_name = 'service_provider')
@@ -411,7 +415,7 @@ class LibertyServiceProvider(models.Model):
     def natural_key(self):
         return (self.liberty_provider.slug,)
 
-    def __unicode__(self):
+    def __str__(self):
         return six.text_type(self.liberty_provider)
 
     class Meta:
@@ -463,6 +467,7 @@ def nameid2kwargs(name_id):
 # XXX: for retrocompatibility
 federation_delete = managers.federation_delete
 
+@six.python_2_unicode_compatible
 class LibertyFederation(models.Model):
     """Store a federation, i.e. an identifier shared with another provider, be
        it IdP or SP"""
@@ -519,10 +524,11 @@ class LibertyFederation(models.Model):
         verbose_name = _("SAML federation")
         verbose_name_plural = _("SAML federations")
 
-    def __unicode__(self):
+    def __str__(self):
         return self.name_id_content
 
 
+@six.python_2_unicode_compatible
 class LibertySession(models.Model):
     """Store the link between a Django session and a SAML session"""
     django_session_key = models.CharField(max_length = 128)
@@ -572,13 +578,14 @@ class LibertySession(models.Model):
         qs = qs.filter(Q(name_id_sp_name_qualifier__isnull=True)|Q(name_id_sp_name_qualifier=provider_id))
         return qs
 
-    def __unicode__(self):
+    def __str__(self):
         return '<LibertySession %s>' % self.__dict__
 
     class Meta:
         verbose_name = _("SAML session")
         verbose_name_plural = _("SAML sessions")
 
+@six.python_2_unicode_compatible
 class KeyValue(models.Model):
     key = models.CharField(max_length=128, primary_key=True)
     value = PickledObjectField()
@@ -586,7 +593,7 @@ class KeyValue(models.Model):
 
     objects = a2_managers.ExpireManager()
 
-    def __unicode__(self):
+    def __str__(self):
         return self.key
 
     class Meta:
diff --git a/src/authentic2_auth_oidc/models.py b/src/authentic2_auth_oidc/models.py
index 28ada5da..ee2dbbae 100644
--- a/src/authentic2_auth_oidc/models.py
+++ b/src/authentic2_auth_oidc/models.py
@@ -2,6 +2,7 @@ import uuid
 import json
 
 from django.db import models
+from django.utils import six
 from django.utils.translation import ugettext_lazy as _
 from django.conf import settings
 from django.core.exceptions import ValidationError
@@ -24,6 +25,7 @@ def validate_jwkset(data):
         raise ValidationError(_('Invalid JWKSet: %s') % e)
 
 
+@six.python_2_unicode_compatible
 class OIDCProvider(models.Model):
     STRATEGY_CREATE = 'create'
     STRATEGY_FIND_UUID = 'find-uuid'
@@ -145,7 +147,7 @@ class OIDCProvider(models.Model):
             return JWK(kty='oct', k=base64url_encode(self.client_secret.encode('utf-8')))
         return None
 
-    def __unicode__(self):
+    def __str__(self):
         return self.name
 
     def authorization_claims_parameter(self):
@@ -166,6 +168,7 @@ class OIDCProvider(models.Model):
         return '<OIDCProvider %r>' % self.issuer
 
 
+@six.python_2_unicode_compatible
 class OIDCClaimMapping(models.Model):
     NOT_VERIFIED = 0
     VERIFIED_CLAIM = 1
@@ -210,7 +213,7 @@ class OIDCClaimMapping(models.Model):
     def natural_key(self):
         return (self.claim, self.attribute, self.verified, self.required)
 
-    def __unicode__(self):
+    def __str__(self):
         s = u'{0} -> {1}'.format(self.claim, self.attribute)
         if self.verified:
             s += u', verified'
@@ -226,6 +229,7 @@ class OIDCClaimMapping(models.Model):
             self.verified, self.required)
 
 
+@six.python_2_unicode_compatible
 class OIDCAccount(models.Model):
     created = models.DateTimeField(
         verbose_name=_('created'),
@@ -246,7 +250,7 @@ class OIDCAccount(models.Model):
         max_length=256,
         unique=True)
 
-    def __unicode__(self):
+    def __str__(self):
         return u'{0} on {1} linked to {2}'.format(self.sub, self.provider and self.provider.issuer,
                                                   self.user)
 
diff --git a/src/authentic2_idp_cas/models.py b/src/authentic2_idp_cas/models.py
index d3c0305c..794e06cb 100644
--- a/src/authentic2_idp_cas/models.py
+++ b/src/authentic2_idp_cas/models.py
@@ -1,4 +1,5 @@
 from django.db import models
+from django.utils import six
 from django.utils.translation import ugettext_lazy as _
 from django.utils.timezone import now
 from django.core.validators import URLValidator
@@ -12,6 +13,7 @@ from . import managers, utils, constants
 
 url_validator = URLValidator(schemes=['http', 'https', 'ftp', 'ftps', 'imap', 'imaps', 'sieve', 'smtp', 'smtps', 'ssh'])
 
+@six.python_2_unicode_compatible
 class Service(LogoutUrlAbstract, Service):
     urls = models.TextField(max_length=128,
             verbose_name=_('urls'))
@@ -53,7 +55,7 @@ class Service(LogoutUrlAbstract, Service):
             wanted.add(attribute.attribute_name)
         return list(wanted)
 
-    def __unicode__(self):
+    def __str__(self):
         return self.name
 
     class Meta:
@@ -61,6 +63,7 @@ class Service(LogoutUrlAbstract, Service):
         verbose_name_plural = _('services')
 
 
+@six.python_2_unicode_compatible
 class Attribute(models.Model):
     service = models.ForeignKey(Service, verbose_name=_('service'))
     slug    = models.SlugField(verbose_name=_('slug'))
@@ -70,7 +73,7 @@ class Attribute(models.Model):
             verbose_name=_('enabled'),
             default=True)
 
-    def __unicode__(self):
+    def __str__(self):
         return u'%s <- %s' % (self.slug, self.attribute_name)
 
     class Meta:
@@ -81,6 +84,7 @@ class Attribute(models.Model):
 def make_uuid():
     return utils.make_id(constants.SERVICE_TICKET_PREFIX)
 
+@six.python_2_unicode_compatible
 class Ticket(models.Model):
     '''Session ticket with a CAS 1.0 or 2.0 consumer'''
 
@@ -107,7 +111,7 @@ class Ticket(models.Model):
 
     objects = managers.TicketManager()
 
-    def __unicode__(self):
+    def __str__(self):
         return self.ticket_id
 
     def valid(self):
diff --git a/src/authentic2_idp_oidc/models.py b/src/authentic2_idp_oidc/models.py
index 0198ef68..943b1f6d 100644
--- a/src/authentic2_idp_oidc/models.py
+++ b/src/authentic2_idp_oidc/models.py
@@ -312,6 +312,7 @@ GenericRelation('authentic2_idp_oidc.OIDCAuthorization',
                     OrganizationalUnit, 'oidc_authorizations')
 
 
+@six.python_2_unicode_compatible
 class OIDCClaim(models.Model):
     client = models.ForeignKey(
         to=OIDCClient, verbose_name=_('client'))
@@ -325,7 +326,7 @@ class OIDCClaim(models.Model):
         max_length=128, blank=True,
         verbose_name=_('attribute scopes'))
 
-    def __unicode__(self):
+    def __str__(self):
         return u'%s - %s - %s' % (self.name, self.value, self.scopes)
 
     def get_scopes(self):
diff --git a/src/django_rbac/models.py b/src/django_rbac/models.py
index 2a0e40d3..d50fd83a 100644
--- a/src/django_rbac/models.py
+++ b/src/django_rbac/models.py
@@ -23,6 +23,7 @@ from django.contrib import auth
 from . import utils, constants, managers, backends
 
 
+@six.python_2_unicode_compatible
 class AbstractBase(models.Model):
     '''Abstract base model for all models having a name and uuid and a
        slug
@@ -44,7 +45,7 @@ class AbstractBase(models.Model):
 
     objects = managers.AbstractBaseManager()
 
-    def __unicode__(self):
+    def __str__(self):
         return self.name
 
     def __repr__(self):
@@ -100,6 +101,7 @@ class OrganizationalUnit(OrganizationalUnitAbstractBase):
         swappable = constants.RBAC_OU_MODEL_SETTING
 
 
+@six.python_2_unicode_compatible
 class Operation(models.Model):
     name = models.CharField(
         max_length=128,
@@ -112,7 +114,7 @@ class Operation(models.Model):
     def natural_key(self):
         return [self.slug]
 
-    def __unicode__(self):
+    def __str__(self):
         return six.text_type(_(self.name))
 
     def export_json(self):
@@ -124,6 +126,7 @@ class Operation(models.Model):
 Operation._meta.natural_key = ['slug']
 
 
+@six.python_2_unicode_compatible
 class PermissionAbstractBase(models.Model):
     operation = models.ForeignKey(
         to=Operation,
@@ -157,7 +160,7 @@ class PermissionAbstractBase(models.Model):
             "target": self.target.natural_key_json()
         }
 
-    def __unicode__(self):
+    def __str__(self):
         ct = ContentType.objects.get_for_id(self.target_ct_id)
         ct_ct = ContentType.objects.get_for_model(ContentType)
         if ct == ct_ct:
-- 
2.20.1

