» » » Использование учетных записей Joomla в проекте на Django

 

Использование учетных записей Joomla в проекте на Django

Автор: admin от 4-03-2019, 19:10, посмотрело: 156

Допустим что сайт, которым пользуются ваши пользователи, написан на Joomla, но для создания нового продукта для вашей аудитории вы выбрали связку Python/Django.



Как следствие, возникает необходимость использовать в Django учетные записи пользователей из базы данных Joomla.



Проблема однако в том, что Joomla и Django используют разные алгоритмы хэширования паролей, поэтому просто скопировать учетные записи не получится.



Почитав документацию Django, stack overflow и потратив некоторое время, получилось нижеописанное решение, которое по максимуму использует рекомендуемые практики разработки под Django.

Прочитайте, как Django работает с несколькими базами данных
  • для подключения базы данных Joomla в наш Django проект, добавьте следующий код в файл с настройками проекта /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',
    }
    }



  • Далее, в этом же файле, добавляем роутер для БД, который будет перенаправлять запросы от модели JoomlaUser к правильной БД:


    # ensure that Joomla users are populated from the right database:
    DATABASE_ROUTERS = ['manager.router.DatabaseAppsRouter']
    DATABASE_APPS_MAPPING = {'joomla_users': 'joomla_db'}
    
    # you also can create your own database router:
    # https://docs.djangoproject.com/en/dev/topics/db/multi-db/#automatic-database-routing

    При необходимости, в этом же файле с настройками проекта, можно включить логирование запросов к БД:


    # 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. создайте модель JoomlaUser



    • Прочитайте, как модель Django может использовать существующую БД

    • Подумайте, где расположить новую модель "JoomlaUser". В моем проекте я создал application с именем "users" (manage.py startapp users). В ней будет лежать бекэнд авторизации и модель пользователя Joomla

    • запустите python manage.py inspectdb --database="joomla_db" и исследуйте существующую базу данных Joomla.

    • в частности нас интересует какие поля содержит таблица учетных записей и их репрезентация в виде полей модели Django

    • добавьте вашу модель в 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, see "settings.DATABASE_APPS_MAPPING"
          app_label = "joomla_users"  



    Запускайте терминал Django и попробуйте вытащить существующего пользователя: python manage.py shell


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

    Если все работает (вы видите пользователя), то переходим к слудующему шагу. Иначе смотрите на вывод ошибок и исправляйте настройки.


    3. Проверка пароля учетной записи Joomla


    Joomla не хранит пароли пользователей, но их хэш, например
    $2y$10$aoZ4/bA7pe.QvjTU0R5.IeFGYrGag/THGvgKpoTk6bTz6XNkY0F2e


    Начиная с версии Joomla v3.2, пароли позльзователей зашифрованы с помощью алгоритма BLOWFISH .


    Так что я загрузил python код с этим алгоритмом:


    pip install bcrypt
    echo bcrypt  requirements.txt

    И создал функцию для проверки паролей в файле 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)

    Внимание! Joomla версии ниже чем 3.2 использует другой метод хеширования (md5+salt), так что эта функция не будет работать. В таком случае почитайте
    обсуждение на Stackoverflow и создайте функцию для проверки хэша, которая будет выглядеть примерно так:


    # 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()

    К сожалению у меня сейчас нет под рукой базы пользователей из старой версии Joomla, поэтому я не смогу протестировать эту функцию для вас.


    4. Бэкенд авторизации пользователей Joomla


    Теперь вы готовы создать Django бэкенд для авторизации пользователей из Joomla проекта.



    1. прочитайте, как модифицировать систему авторизации Django



    2. Зарегистрируйте новый бэкенд (еще не существующий) в 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. Создайте бэкенд авторизации пользователей Joomla в 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


    Итог



    Поздравляю — теперь пользователи вашего существующего Joomla сайта могут использовать свои учётные данные на новом сайте/приложении.



    По мере авторизации активных пользователей через новый интерфейс, они будут по одному скопированы в новую базу данных.



    Как вариант, вы можете не захотеть копировать сущности пользователей из старой системы в новую.



    В таком случае вот вам ссылка на статью, в которой описывается, как в Django заменить модель пользователя по умолчанию на свою (вышеописанную модель JoomlaUser).



    Конечное решение, переносить или не переносить пользователей, принимайте на основе того, в каких взаимоотношениях будут новый и старый проект. Например, где будет происходить регистрация новых пользователей, какой сайт/приложение будет основным, и т.д.



    Тестирование и документация



    Теперь пожалуйста добавьте соответствующие тесты и документацию, покрывающие новый код. Логика данного решения тесно переплетена с архитектурой Django и не очень очевидна, поэтому если вы не сделаете тесты/документацию сейчас, поддержка проекта в будущем усложнится.



    Источник: Хабр / Интересные публикации

    Категория: Веб-разработка

    Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь.
    Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.

    Добавление комментария

    Имя:*
    E-Mail:
    Комментарий:
    Полужирный Наклонный текст Подчеркнутый текст Зачеркнутый текст | Выравнивание по левому краю По центру Выравнивание по правому краю | Вставка смайликов Выбор цвета | Скрытый текст Вставка цитаты Преобразовать выбранный текст из транслитерации в кириллицу Вставка спойлера
    Введите два слова, показанных на изображении: *