将配置文件模型连接到远程用户(自定义验证后端)



我正在编写自定义身份验证后端,以对其进行身份验证。

我不知道如何将Profile模型(将包含远程数据库中不包含信息的信息)连接到那些用户,以不受用户名的更改等的方式等。例如,如果我想要为这些用户提供" Bio"字段,然后我通常会这样做:

from django.contrib.auth.models import User
class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    bio = models.TextField(blank=True)
    def __str__(self):
        return self.user.email

这是否可以使用基于自定义API的身份验证后端?如果是这样,我将把它连接到远程用户的OneToOneField中?

我是否只需要确保身份验证后端更新用户的本地数据库,然后配置文件模型将连接到该数据库?这就是我要尝试的方式,但我认为我会在其他地方询问社区。

希望将来对某人有所帮助。我解决此问题的方式是在我的身份验证后端中查询API,然后找到(或创建)本地用户对象并更新该对象的相关属性。它有点奇怪,因为本地数据库的主要键是远程用户对象的ID(和PK)(以使get_user()快速查找查找),但是我将用户名作为查找密钥进行认证。

so,总结:

  • 进行后端查询API,然后将本地DB与一个用户同步(本地DB仅用于保留用户对象的远程数据)。
  • 为了确保您的系统即使其远程用户名更改,请确保您使用远程ID(可能永远不会更改)来同步本地DB。
  • 确保本地用户模型的主要键是远程数据库的主要键,以帮助您自己使用数据完整性。

另外,在我的示例中,我必须下载整个帐户列表并为合适的用户求助。在我的现实示例中,我使用的是一个API,该API允许我通过用户名查找。下载整个帐户列表是一个疯狂的坏主意,我只是在这里这样做,因为我的测试API不支持它。

models.py

class RemoteUserManager(BaseUserManager):
    def create_user(self, remote_id, remote_username=None, remote_first_name=None, remote_last_name=None, remote_email=None):
        if not remote_id:
            raise ValueError('Users must have a remote_id')
        user = self.model(
            remote_id=remote_id,
            remote_username=remote_username,
            remote_first_name=remote_first_name,
            remote_last_name=remote_last_name,
            remote_email=remote_email,
        )
        user.save(using=self._db)
        return user
    def create_superuser(self, remote_id, remote_username=None, remote_first_name=None, remote_last_name=None, remote_email=None):
        user = self.create_user(
            remote_id=remote_id,
            remote_username=remote_username,
            remote_first_name=remote_first_name,
            remote_last_name=remote_last_name,
            remote_email=remote_email,
        )
        user.is_staff = True
        user.is_superuser = True
        user.save(using=self._db)
        return user
class RemoteUser(AbstractBaseUser):
    remote_id = models.IntegerField(primary_key=True)
    remote_username = models.CharField(max_length=255, blank=True)
    remote_first_name = models.CharField(max_length=255, blank=True)
    remote_last_name = models.CharField(max_length=255, blank=True)
    remote_email = models.EmailField(blank=True)
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=True)     # testing login to admin interface
    is_superuser = models.BooleanField(default=True) # testing login to admin interface
    created = models.DateTimeField(auto_now_add=True)
    last_login = models.DateTimeField(auto_now=True)
    objects = RemoteUserManager()
    USERNAME_FIELD = 'remote_username'
    REQUIRED_FIELDS = []
    def has_perm(self, perm, obj=None):
        return True
    def has_module_perms(self, app_label):
        return True
    def get_full_name(self):
        return str(self.remote_first_name + ' ' + self.remote_last_name).strip()
    def get_short_name(self):
        return self.remote_first_name
    def get_display_name(self):
        if (self.remote_first_name):
            return self.remote_first_name
        else:
            return self.remote_email
    def __str__(self):
        return self.remote_username

backends.py

class RemoteAuthBackend(object):
    apikey = 'yourlongandsecureapikeygoeshere'
    target = 'https://remote.domain.com'
    list_call = '/admin/scaffolds/accounts/list.json'
    show_call = '/admin/scaffolds/accounts/show/'
    def authenticate(self, username=None, password=None):
        username = username.strip()
        # run API call and find user by username
        request = urllib.request.Request(self.target + self.list_call + '?api_key=' + self.apikey)
        response = urllib.request.urlopen(request)
        resp_parsed = json.loads(response.read().decode('utf-8'))
        # go through list of dicts and find the matching username
        match_user = None
        for user_record in resp_parsed:
            if user_record.get('login', None) == username:
                match_user = user_record
                print('testy')
                break
        if not match_user: return None
        # get password-crypted and salt
        crypted_password = match_user.get('crypted_password', None)
        salt = match_user.get('salt', None)
        # hash password and see if the digests match (base64 encoded sha1 digest)
        hash = b64encode(sha1((salt + password).encode('utf-8')).digest()).decode('utf-8')
        if hash == match_user.get('crypted_password'):
            # update user object that matches remote_id and return local user object
            try: local_user = models.RemoteUser.objects.get(pk=match_user['id'])
            except: local_user = None
            if not local_user:
                try: local_user = models.RemoteUser(remote_id=match_user['id'])
                except: return None   # This should never happen, ever
            local_user.remote_username = match_user.get('login', None)
            local_user.remote_first_name = match_user.get('first_name', None)
            local_user.remote_last_name = match_user.get('last_name', None)
            local_user.remote_email = match_user.get('email', None)
            try: local_user.save()
            except: return None
            return local_user
        else:
            # failed auth
            return None
    def get_user(self, user_id):
        # get user from remote and sync up local object properties based on remote_id
        request = urllib.request.Request(self.target + self.show_call + str(user_id) + '.json?api_key=' + self.apikey)
        response = urllib.request.urlopen(request)
        match_user = json.loads(response.read().decode('utf-8'))
        if not match_user: return None
        try: local_user = models.RemoteUser.objects.get(pk=match_user['id'])
        except: local_user = None
        if not local_user:
            try: local_user = models.RemoteUser(remote_id=match_user['id'])
            except: return None   # This should never happen, ever
        local_user.remote_username = match_user.get('login', None)
        local_user.remote_first_name = match_user.get('first_name', None)
        local_user.remote_last_name = match_user.get('last_name', None)
        local_user.remote_email = match_user.get('email', None)
        try: local_user.save()
        except: return None
        return local_user

,您仍然必须像使用自定义用户模型时一样修改admin.py

最新更新