Usando contas Joomla em um projeto Django

Digamos que o site usado por seus usuários seja escrito em Joomla, mas para criar um novo produto para o seu público, você escolheu o pacote Python / Django.


Como resultado, você precisa usar contas de usuário do banco de dados Joomla no Django.


O problema, no entanto, é que o Joomla e o Django usam algoritmos diferentes de hash de senha, então apenas copiar as contas falha.


Depois de ler a documentação do Django, sobrecarregar a pilha e passar algum tempo, obtive a solução descrita abaixo, que usa as práticas de desenvolvimento recomendadas para o Django ao máximo.


Advertências


Essa solução arquitetônica pode não ser sua, veja a discussão nos comentários .


Para entender o que acontece nos exemplos abaixo, você deve ter algum entendimento da arquitetura do Django.


Eu também presumo que você sabe como implantar um projeto Django, então não estou descrevendo esse processo.


O código é copiado de um projeto em funcionamento, mas será fácil se ajustar ao seu projeto com um mínimo de alterações.


Provavelmente, na próxima versão principal do Django, esse código pode quebrar, no entanto, o princípio da solução permanecerá o mesmo.


Neste guia, não descrevo o front end do sistema de autorização, pois:


  • qual front-end você dependerá das necessidades do seu projeto (pode até ser um terminal da API Json, por exemplo)
  • esta informação já está descrita nos tutoriais oficiais do Django e em vários artigos para iniciantes

Algoritmo


  • conectar o banco de dados Joomla (DB) ao projeto Django
  • crie um modelo JoomlaUser representando um usuário do banco de dados Joomla
  • escreva uma função check_joomla_password() que verifique se a senha digitada corresponde à senha original do usuário.
  • adicione um novo backend de autorização "Joomla Auth Backend" ao projeto, que, ao autorizar o cliente no Django, obterá a conta do usuário no banco de dados Joomla

1. Conexão ao banco de dados Joomla:


  • Leia como o Django trabalha com vários bancos de dados
  • Para conectar o banco de dados Joomla ao nosso projeto Django, adicione o seguinte código ao arquivo de configurações do projeto /project_name/settings.py :


     DATABASES = { #    'default': { ... }, 'joomla_db': { 'ENGINE': 'django.db.backends.mysql', 'OPTIONS': {}, 'NAME': 'joomla_database_name', # Don't store passwords in the code, instead use env vars: 'USER': os.environ['joomla_db_user'], 'PASSWORD': os.environ['joomla_db_pass'], 'HOST': 'joomla_db_host, can be localhost or remote IP', 'PORT': '3306', } } 


Se necessário, no mesmo arquivo com as configurações do projeto, você pode ativar o log de consultas do banco de dados:


 # add logging to see DB requests: LOGGING = { 'version': 1, 'handlers': { 'console': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'level': 'DEBUG', 'handlers': ['console'], }, }, } 

2. crie um modelo JoomlaUser


  • Leia como um modelo Django pode usar um banco de dados existente
  • Pense em onde colocar o novo JoomlaUser.
    No meu projeto, criei um aplicativo chamado "users" ( manage.py startapp users ). Ele conterá o back-end de autorização e o modelo de usuário do Joomla.
  • gere o modelo automaticamente usando o inspectdb:
    python manage.py inspectdb live_users --database="joomla_db"
    joomla_db - o nome do banco de dados que você especificou em settings.py/DATABASES ;
    live_users - nome da tabela com contas.
  • adicione seu modelo a users/models.py :


     class JoomlaUser(models.Model): """ Represents our customer from the legacy Joomla database. """ username = models.CharField(max_length=150, primary_key=True) email = models.CharField(max_length=100) password = models.CharField(max_length=100) # you can copy more fields from `inspectdb` output, # but it's enough for the example class Meta: # joomla db user table. WARNING, your case can differs. db_table = 'live_users' # readonly managed = False # tip for the database router app_label = "joomla_users" 


Em seguida, precisamos garantir que o modelo acesse o banco de dados correto. Para fazer isso, adicione ao projeto um roteador para consultas a diferentes bancos de dados , que redirecionará solicitações do modelo JoomlaUser para seu banco de dados nativo.


  1. Crie o arquivo "db_routers.py" na pasta principal do projeto (no mesmo local em que seu "settings.py" está):


     # project_name/db_routers.py class DbRouter: """this router makes sure that django uses legacy 'Joomla' database for models, that are stored there (JoomlaUser)""" def db_for_read(self, model, **kwargs): if model._meta.app_label == 'joomla_user': return 'joomla_db' return None def db_for_write(self, model, **kwargs): if model._meta.app_label == 'joomla_user': return 'joomla_db' return None 

  2. registre um novo roteador em settings.py :


     # ensure that Joomla users are populated from the right database: DATABASE_ROUTERS = ['project_name.db_routers.DbRouter'] 


Agora você pode obter uma conta do banco de dados antigo.
Inicie um terminal do Django e tente puxar um usuário existente: python manage.py shell


 >>> from users.models import JoomlaUser >>> print(JoomlaUser.objects.get(username='someuser')) JoomlaUser object (someusername) >>> 

Se tudo funcionar (você vê o usuário), vá para a próxima etapa. Caso contrário, observe a saída de erro e corrija as configurações.


3. Verifique a senha da conta Joomla


O Joomla não armazena senhas de usuários, mas seu hash, por exemplo
$2y$10$aoZ4/bA7pe.QvjTU0R5.IeFGYrGag/THGvgKpoTk6bTz6XNkY0F2e


A partir do Joomla v3.2, as senhas dos usuários são criptografadas usando o algoritmo BLOWFISH .


Então eu baixei o código python com este algoritmo:


 pip install bcrypt echo bcrypt >> requirements.txt 

E criou uma função para verificar senhas no users/backend.py :


 def check_joomla_password(password, hashed): """ Check if password matches the hashed password, using same hashing method (Blowfish) as Joomla >= 3.2 If you get wrong results with this function, check that the Hash starts from prefix "$2y", otherwise it is probably not a blowfish hash :return: True/False """ import bcrypt if password is None: return False # bcrypt requires byte strings password = password.encode('utf-8') hashed = hashed.encode('utf-8') return hashed == bcrypt.hashpw(password, hashed) 

Atenção! As versões do Joomla inferiores a 3.2 usam um método de hash diferente (md5 + salt), portanto esta função não funciona. Nesse caso, leia
discussão no Stackoverflow e crie uma função de verificação de hash que se parece com isso:


 # WARNING - THIS FUNCTION WAS NOT TESTED WITH REAL JOOMLA USERS # and definitely has some errors def check_old_joomla_password(password, hashed): from hashlib import md5 password = password.encode('utf-8') hashed = hashed.encode('utf-8') if password is None: return False # check carefully this part: hash, salt = hashed.split(':') return hash == md5(password+salt).hexdigest() 

Infelizmente, eu não tenho uma base de usuários da versão antiga do Joomla em mãos, então não posso testar esse recurso para você.


4. Autorização de usuário back-end Joomla


Agora você está pronto para criar um back-end do Django para autorizar usuários do projeto Joomla.


  1. leia como modificar o sistema de autorização do Django


  2. Registre um novo back-end (ainda não existente) em project/settings.py :


     AUTHENTICATION_BACKENDS = [ # Check if user already in the local DB # by using default django users backend 'django.contrib.auth.backends.ModelBackend', # If user was not found among django users, # use Joomla backend, which: # - search for user in Joomla DB # - check joomla user password # - copy joomla user into Django user. 'users.backend.JoomlaBackend', ] 

  3. Crie um back-end de autorização de usuário do Joomla em users/backend.py



 from django.contrib.auth.models import User from .models import JoomlaUser def check_joomla_password(password, hashed): # this is a fuction, that we wrote before ... class JoomlaBackend: """ authorize users against Joomla user records """ def authenticate(self, request, username=None, password=None): """ IF joomla user exists AND password is correct: create django user return user object ELSE: return None """ try: joomla_user = JoomlaUser.objects.get(username=username) except JoomlaUser.DoesNotExist: return None if check_joomla_password(password, joomla_user.password): # Password is correct, let's create and return Django user, # identical to Joomla user: # but before let's ensure there is no same username # in DB. That could happen, when user changed password # in Joomla, but Django doesn't know that User.objects.filter(username=username).delete() return User.objects.create_user( username=username, email=joomla_user.email, password=password, # any additional fields from the Joomla user: ... ) # this method is required to match Django Auth Backend interface def get_user(self, user_id): try: return User.objects.get(pk=user_id) except User.DoesNotExist: return None 

Sumário


Parabéns - agora os usuários do seu site Joomla existente podem usar suas credenciais em um novo site / aplicativo.


Como autorização de usuários ativos por meio da nova interface, eles serão copiados um a um no novo banco de dados.


Como alternativa, você pode não querer copiar entidades do usuário do sistema antigo para o novo.


Neste caso, aqui está um link para um artigo que descreve como substituir o modelo de usuário padrão no Django pelo seu (o modelo JoomlaUser descrito acima).


A decisão final, de transferir ou não usuários, é tomada com base no relacionamento em que os projetos novos e antigos serão. Por exemplo, onde será realizado o registro de novos usuários, qual site / aplicativo será o principal, etc.


Teste e documentação


Agora, adicione os testes e a documentação adequados que cobrem o novo código. A lógica desta solução está intimamente ligada à arquitetura do Django e não é muito óbvia; portanto, se você não fizer testes / documentação agora, o suporte ao projeto se tornará mais complicado no futuro.

Source: https://habr.com/ru/post/pt442592/


All Articles