ユーザーが使用するサイトがJoomlaで記述されているとしましょう。ただし、視聴者向けの新しい製品を作成するには、Python / Djangoバンドルを選択しました。
そのため、DjangoのJoomlaデータベースのユーザーアカウントを使用する必要があります。
ただし、問題は、JoomlaとDjangoが異なるパスワードハッシュアルゴリズムを使用するため、アカウントのコピーだけが失敗することです。
Djangoのドキュメントを読み、スタックオーバーフローを起こし、しばらく時間を費やした後、以下に説明するソリューションを得ました。これは、Djangoの推奨開発プラクティスを最大限に活用しています。
警告
このアーキテクチャ上の解決策はあなたに合わないかもしれません。 コメントの議論を参照してください。
以下の例で何が起こるかを理解するには、Djangoアーキテクチャについてある程度理解している必要があります。
また、Djangoプロジェクトをデプロイする方法を知っていると想定しているため、このプロセスについては説明しません。
コードは作業中のドラフトからコピーされますが、最小限の変更でプロジェクトに簡単に調整できます。
おそらく、Djangoの次のメジャーバージョンでは、このコードが破損する可能性がありますが、ソリューションの原則は変わりません。
このガイドでは、承認システムのフロントエンドについては説明しません。
- どのフロントエンドを使用するかは、プロジェクトのニーズに依存します(たとえば、Json APIエンドポイントである場合もあります)
- この情報は、公式のDjangoチュートリアルとさまざまなスターター記事で既に説明されています
アルゴリズム
- Joomlaデータベース(DB)をDjangoプロジェクトに接続します
- Joomlaデータベースからユーザーを表すJoomlaUserモデルを作成します
- 入力したパスワードがユーザーの元のパスワードと一致することを確認する
check_joomla_password()
関数を作成します。 - 新しい認証バックエンド「Joomla Auth Backend」をプロジェクトに追加します。これは、Djangoでクライアントを認証するときに、Joomlaデータベースからユーザーアカウントを取得します
1. Joomlaデータベースへの接続:
- 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', } }
必要に応じて、プロジェクト設定と同じファイルで、データベースクエリのログを有効にできます。
# 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を配置する場所について考えます。
私のプロジェクトでは、「users」(manage.py startapp users
)というアプリケーションを作成しました。 承認バックエンドとJoomlaユーザーモデルが含まれます。 - inspectdbを使用してモデルを自動的に生成します。
python manage.py inspectdb live_users --database="joomla_db"
joomla_db-settings.py/DATABASESで指定したデータベースの名前。
live_users-アカウントを持つテーブルの名前。
モデルを
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"
次に、モデルが正しいデータベースにアクセスすることを確認する必要があります。 これを行うには、 異なるデータベースへのクエリ用のルーターをプロジェクトに追加します。これにより、リクエストがJoomlaUserモデルからネイティブデータベースにリダイレクトされます。
プロジェクトのメインフォルダー( "settings.py"と同じ場所)にファイル "db_routers.py"を作成します。
# 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
settings.py
新しいルーターを登録しsettings.py
。
# ensure that Joomla users are populated from the right database: DATABASE_ROUTERS = ['project_name.db_routers.DbRouter']
これで、古いデータベースからアカウントを取得できます。
Djangoターミナルを起動し、既存のユーザーをpython manage.py shell
みます: 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)
注意! 3.2より前のJoomlaバージョンは異なるハッシュ方式(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
これで、Joomlaプロジェクトからユーザーを承認するためのDjangoバックエンドを作成する準備が整いました。
-
project/settings.py
新しいバックエンド(まだ存在しない)を登録し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', ]
users/backend.py
でJoomlaユーザー認証バックエンドを作成します
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サイトのユーザーは、新しいサイト/アプリケーションで資格情報を使用できるようになりました。
新しいインターフェイスを介したアクティブユーザーの承認として、ユーザーは1つずつ新しいデータベースにコピーされます。
または、ユーザーエンティティを古いシステムから新しいシステムにコピーしたくない場合があります。
この場合、 Djangoでデフォルトのユーザーモデルを独自のモデル(上記のJoomlaUserモデル) に置き換える方法を説明した記事へのリンクがあります 。
ユーザーを転送するかどうかの最終決定は、新しいプロジェクトと古いプロジェクトの関係に基づいて行われます。 たとえば、新しいユーザーの登録が行われる場所、メインのサイト/アプリケーションなど。
テストとドキュメント
次に、新しいコードを対象とした適切なテストとドキュメントを追加してください。 このソリューションのロジックはDjangoアーキテクチャと密接に関連しており、あまり明確ではないため、テスト/ドキュメントを今すぐ行わないと、プロジェクトのサポートは将来より複雑になります。