title: How to implement Django SSO with custom user model and object permission (EX/django-guardian) in multiple projects
body: <p>I tried to search and study some information on the google, but I couldn't find the good solution.</p>
  
  <p>I have several Django projects and want to create "Member" project store user information which implement Single-Sign-On(SSO) feature (ex/"<a href="https://github.com/divio/django-simple-sso" rel="nofollow noreferrer">django-simple-sso</a>"). Then I need to classify user permission, I study the package "<a href="https://github.com/django-guardian/django-guardian" rel="nofollow noreferrer">django-guardian</a>" which maybe can help me do object permission.</p>
  
  <p>I do below things to create custom model in "Member" project. Then I just post the code that I add/change.</p>
  
  <pre class="lang-py prettyprint-override"><code># setting.py of "Member" project
  
  INSTALLED_APPS = [    
      'users',
      'guardian'
  ]
  
  AUTHENTICATION_BACKENDS = (
      'guardian.backends.ObjectPermissionBackend',
  )
  </code></pre>
  
  <p>I add an app which called users in the project named "Member".</p>
  
  <pre class="lang-py prettyprint-override"><code># admin.py in the users app of Member project.
  
  from django.contrib import admin
  from django.contrib.auth.admin import UserAdmin
  from .forms import CustomUserCreationForm, CustomUserChangeForm
  from .models import CustomUser
  
  class CustomUserAdmin(UserAdmin):
      add_form = CustomUserCreationForm
      form = CustomUserChangeForm
      model = CustomUser
      list_display = ('email', 'is_staff', 'is_active', 'employee_account', 'location', 'chinese_name', 'english_name', 'extension', 'last_login', 'date_joined')
      list_filter = ('email', 'is_staff', 'is_active', 'employee_account', 'location', 'chinese_name', 'english_name', 'extension', 'last_login', 'date_joined')
      fieldsets = (
          (None, {'fields': ('employee_account', 'password')}),
          ('Permissions', {'fields': ('is_staff', 'is_active')}),
      )
      add_fieldsets = (
          (None, {
              'classes': ('wide',),
              'fields': ('employee_account', 'password1', 'password2', 'is_staff', 'is_active')}
          ),
      )
      search_fields = ('employee_account',)
      ordering = ('employee_account',)
  
  admin.site.register(CustomUser, CustomUserAdmin)
  </code></pre>
  
  <pre class="lang-py prettyprint-override"><code># apps.py in the users app of Member project.  
  
  from django.apps import AppConfig
  
  class UsersConfig(AppConfig):
      name = 'users'
  </code></pre>
  
  <pre class="lang-py prettyprint-override"><code># forms.py in the users app of Member project.
  
  from django.contrib.auth.forms import UserCreationForm, UserChangeForm
  from .models import CustomUser
  
  class CustomUserCreationForm(UserCreationForm):
  
      class Meta(UserCreationForm):
          model = CustomUser
          fields = ('employee_account',)
  
  
  class CustomUserChangeForm(UserChangeForm):
  
      class Meta:
          model = CustomUser
          fields = ('employee_account',)
  </code></pre>
  
  <pre class="lang-py prettyprint-override"><code># manager.py in the users app of Member project.
  
  from django.contrib.auth.base_user import BaseUserManager
  from django.utils.translation import ugettext_lazy as _
  
  
  class CustomUserManager(BaseUserManager):
      """
      Custom user model manager where email is the unique identifiers
      for authentication instead of usernames.
      """
      def create_user(self, email, password, **extra_fields):
          """
          Create and save a User with the given email and password.
          """
          if not email:
              raise ValueError(_('The Email must be set'))
          email = self.normalize_email(email)
          user = self.model(email=email, **extra_fields)
          user.set_password(password)
          user.save()
          return user
  
      def create_superuser(self, email, password, **extra_fields):
          """
          Create and save a SuperUser with the given email and password.
          """
          extra_fields.setdefault('is_staff', True)
          extra_fields.setdefault('is_superuser', True)
          extra_fields.setdefault('is_active', True)
  
          if extra_fields.get('is_staff') is not True:
              raise ValueError(_('Superuser must have is_staff=True.'))
          if extra_fields.get('is_superuser') is not True:
              raise ValueError(_('Superuser must have is_superuser=True.'))
          return self.create_user(email, password, **extra_fields)
  </code></pre>
  
  <pre class="lang-py prettyprint-override"><code># models.py in the users app of Member project
  
  from django.db import models
  from django.db import models
  from django.contrib.auth.models import AbstractBaseUser
  from django.contrib.auth.models import PermissionsMixin
  from django.utils.translation import gettext_lazy as _
  from django.utils import timezone
  from .managers import CustomUserManager
  
  
  class CustomUser(AbstractBaseUser, PermissionsMixin):
      employee_account = models.CharField(max_length=100, unique=True)
      location = models.CharField(max_length=50)
      chinese_name = models.CharField(max_length=30)
      english_name = models.CharField(max_length=30)
      extension = models.CharField(max_length=30)
      email = models.EmailField(max_length=125)
      is_staff = models.BooleanField(default=False)
      is_active = models.BooleanField(default=True)
      date_joined = models.DateTimeField(default=timezone.now)
  
      USERNAME_FIELD = 'employee_account'
      REQUIRED_FIELDS = ['location','chinese_name','english_name','extension', 'email']
  
      objects = CustomUserManager()
  
      def __str__(self):
          return self.employee_account
  </code></pre>
  
  <p>Then I use django shell to add user and assign permission.</p>
  
  <pre><code>
  &gt;&gt;&gt; from django.contrib.auth.models import  Group
  
  &gt;&gt;&gt; from users.models import CustomUser
  
  &gt;&gt;&gt; jack = CustomUser.objects.create_user('123456789@gamil.com', '123456789')
  
  &gt;&gt;&gt; admins = Group.objects.create(name='admins')
  
  &gt;&gt;&gt; jack.has_perm('change_group', admins)
  False
  
  &gt;&gt;&gt; from guardian.models import UserObjectPermission
  
  &gt;&gt;&gt; UserObjectPermission.objects.assign_perm('change_group', jack, obj=admins)
  
  &lt;UserObjectPermission: admins |  | change_group&gt;
  
  &gt;&gt;&gt; jack.has_perm('change_group', admins)
  True
  
  &gt;&gt;&gt; jack.has_perm('change_group')
  False
  </code></pre>
  
  <p>After that I create an book app in another project named book_web.</p>
  
  <pre><code># model.py in the book app of book_web project
  
  from django.db import models
  
  class Book(models.Model):
  
      sn = models.CharField(max_length=200) 
      book = models.CharField(max_length=100) 
      author = models.CharField(max_length=100) 
      price = models.PositiveIntegerField(null=True, blank=True)
      number = models.PositiveIntegerField(null=True, blank=True)
  </code></pre>
  
  <h3>1. It seems work abnormally when key this command <code>UserObjectPermission.objects.assign_perm('change_group', jack, obj=admins)</code>. I found that response doesn't show the user name which is different with <a href="https://github.com/django-guardian/django-guardian#usage" rel="nofollow noreferrer">django-guardian</a> example code.</h3>
  
  <h3>2. I don't know how to assign Book object permission in Member project such as django-guardian example code. Because there is not same model as Book model in the Member project.</h3>
  
  <p>Does anyone have similar experience about this?</p>
  
  <p>If you have another recommendation package to implement the feature, welcome to share your experience. Thanks for your response.</p>