日々の生活メモ帳

調べたことをたまーにつぶやきます コメント、指摘大歓迎

PythonのDjangoでOpenIDのOPを実装してSSO実現

OpenIDによるSSOを試したのでまとめました

OpenIDの仕様等に関する説明は一切しないので注意!

使ったもの

 ざっくり説明すると

django_openid_providerはOP用のプラグイン

OpenIDRequestを受けてログイン状態を確認して、OpenIDResponseを返します

django-openid-authはRP用のプラグイン

djangoの認証バックエンドとしてOpenIDが使えるようになります

今回の構成

それぞれのURLがやっていることはこんな感じ。適宜自分のURLに置き換えてください

あと、OPとRPは別プロジェクトじゃないと動きません。(たぶん)

OP側(django_openid_provider)

/provider/ OpenIDRequestの受け取りとOpenIDResponseの返し

/provider/xrds/ XRDS文書を返します

/provider/decide/ 確認ページを表示

/accounts/login/ 自分で実装する、OPでのログインをする部分。ログイン出来れば何でもいいです

 RP側(django-openid-auth)

/consumer/login/ IdentityURLを使ってDiscovery,OpenIDRequestの送信

/consumer/complate/ OpenIDResponseの受け取りとRPでのログイン

/app/ OpenID認証させたいプログラム(なんでもいいです)

 準備

両方共リンクからダウンロードしてきて各プロジェクトフォルダに入れた後、

必要な設定をしていきます(INSTALLED_APPSへの追加とルートのurls.pyへの追加は省略)

 OP側

settings.py

LOGIN_URL='/accounts/login/'

LOGIN_REDIRECT_URL = '/provider/'

 base.htmlをtemplates/openid_providerの中に追加します

 blocktagが'content'のものさえ入っていれば良さそう

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>OpenIDSSO</title>

</head>
<body>

{% block content %}

{% endblock %}
</body>
</html>

 django1.8で動くようにプラグインのコードを少し修正

views.py(127行目)

#context_instance=RequestContext(request), mimetype=YADIS_CONTENT_TYPE)

context_instance=RequestContext(request), content_type=YADIS_CONTENT_TYPE)

 

RP側

ファイルを解凍するとREADMEにいろいろ書いてあるのでそこを見て設定します

settings.py

AUTHENTICATION_BACKENDS = (
'django_openid_auth.auth.OpenIDBackend',
'django.contrib.auth.backends.ModelBackend',
)

OPENID_CREATE_USERS=True

LOGIN_URL='/consumer/login/'

LOGIN_REDIRECT_URL='/app/'

OPENID_SSO_SERVERL_URL = 'http://localhost:8000/provider/xrds/'

SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer' #READMEには書いてないですが、ないとエラーが出ます

 同様にコードを修正

view.py(302行目)

#OPENID_LOGO_BASE_64.decode('base64'), mimetype='image/gif'

OPENID_LOGO_BASE_64.decode('base64'), content_type='image/gif'

urls.py(30行目)

#from django.conf.urls.defaults import *
from django.conf.urls import patterns, include, url

templates/openid/login.html(8行目)

background: url({% url openid-logo %}) no-repeat;←この行を削除

以上

 

 

mod_auth_openidのAX属性の罠

apacheOpenID用モジュールmod_auth_openidで詰まった部分があったので紹介

インストールはここ参照です。

 モジュールの設定

apacheを使って提供するWEBサービス上でOPからユーザー名を得るためにAX属性を使う事になります。

mod_auth_openidのAX属性の設定を追加します

AuthOpenIDAXReuire <alias> <type_url> <regex>

AuthOpenIDAXUsername <alias>

aliasは欲しい属性名、type_urlはOPが提供している属性に対応したURIregexは取得した属性値の正規表現が入ります。AXRequireがOPへの属性値要求を行い、AXUsernameで指定した属性値がREMOTE_USERとしてapacheに渡されます

AuthOpenIDAXRequire email https://axschema.org/person/email @my\.email\.jp$

AuthOpenIDAXUsername email

検証

pythonのライブラリを使って試していたんですが、どうにもmod_auth_openidが属性パラメータを受け取ってくれない

 

原因はmod_auth_openid側がopenid.ax.count.<alias>が来ているかどうかにかかわらず、常にAX属性値を一つしか想定していないせいでした。(ちなみにpythonのライブラリは個数に拘らず複数の形で渡します)

つまり

OPが渡すパラメータ:openid.ax.value.email.1

mod_auth_openidが期待するパラメータ:openid.ax.value.email

となっており、mod_auth_openidがパラメータを見つけられずエラーを出してました。

とりあえずAX属性値を単数の形でOP側から渡してみたらちゃんと動いたのでよしとします。

        

 

【孔明の罠】ConfigParserのValueError

pythonでiniファイルの操作をするときに使うConfigParserモジュール

何故かvalueの中に'%'が入っているとエラーを出す

>>> from configparser import SafeConfigParser
>>> ini = SafeConfigParser()
>>> ini.read("file.ini")
[]
>>> ini.add_section("section")
>>> ini.set("section","option","%")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.4/configparser.py", line 1167, in set
super().set(section, option, value)
File "/usr/lib/python3.4/configparser.py", line 872, in set
value)
File "/usr/lib/python3.4/configparser.py", line 382, in before_set
"position %d" % (value, tmp_value.find('%')))
ValueError: invalid interpolation syntax in '%' at position 0

 ここで中身を見てみると、'%'を'%%'に置換してから渡せばいいらしい

というわけで試してみると無事回避出来ました。めでたしめでたし

>>> a = '%'
>>> ini.set("section","option",a.replace('%','%%'))
>>> ini.get("section","option")
'%'

 

apacheのモジュールを使って簡単OpenID対応

今更感ありますが、apacheにモジュールをインストールして手間をかけずにOpenIDに対応する方法の紹介です

ここではローカルにOpenIDのOP(認証サーバのこと)も立てて動作の確認までしていきます

 

モジュールのインストールに関しては、ほとんどここを参考にしました

使ったもの

OS:CentoOS6.7

WEBサーバー:apache2.2

使うモジュール:mod_auth_openid

 

まずはリポジトリの登録

rpm --import http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-6

rpm -ivh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm

rpm --import http://rpms.famillecollet.com/RPM-GPG-KEY-remi

rpm -ivh http://rpms.famillecollet.com/enterprise/remi-release-6.rpm

 

 

 そしてモジュールインストール、yumで一発(ソースコードからコンパイルしてたときの苦労はなんだったのか)

yum install -y mod_auth_openid

/etc/httpd/conf.dに mod_auth_openid.confができるのでそこに設定を入れていきます

 LoadModule authopenid_module modules/mod_auth_openid.so

<Location "/">

 AuthType OpenID

 require valid-user

 AuthOpenIDDBLocation /var/lib/mod_auth_openid/database.db

 AuthOpenIDTrusted ^http://localhost:8000

 AuthOpenIDSingleIdP http://localhost:8000/yadis

</Location>

一応説明しておくと、それぞれの意味はこんな感じです

AuthOpenIDDBLocation=セッション管理にsqliteを使用しているので、そのdbファイルの位置です

AuthOpenIDTrusted=認証先として信用するOPのURLです

AuthOpenIDSingleIdp=使用するOPのIdentifyURL(XRDS文書(XML)を渡してくるURLもしくはXRI) 設定しない場合は認証前にIdentifyURLを入力する画面が表示されます

設定が終わったので起動し、自動的に起動するように設定変更

service httpd start

chkconfig httpd on

 これでapacheOpenID認証を設定出来ました

 次にpython-openidにおいてあるOPのサンプルで認証動作を確認したいと思います

まずpipで必要なライブラリをインストールした後、ソースコードを取って来て実行

pip install python-openid

git clone https://github.com/openid/python-openid.git

cd python-openid/examples

python server.py

 試しにブラウザでapacheのURLにアクセスして見ると今さっき立てたOPにリダイレクトされます

 

おまけ

mod_auth_openidcの場合

yum groupinstall 'Development tools' -y

rpm -ivh http://download.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm

yum install httpd mod_ssl httpd-devel curl hiredis jansson -y

rpm -ivh https://github.com/pingidentity/mod_auth_openidc/releases/download/v1.8.5/mod_auth_openidc-1.8.5-1.el6.x86_64.rpm

yum install mod_auth_openidc -y

 

でインストールできます

 

windows7からwindows10へのアップグレードでつまづいたこと

今日Windows7を10にアップグレードしようとした時のこと

 

予約してwindows10へのアップグレードがデータのダウンロードが終わった後

いつまでたっても進みませんでした。5時間くらい待っても何も始まらない笑

 

ネットで調べるとどうにも

アップグレード予約のとこからやると再起動のたびに必要なドライバ等が消えてしまうため、普通にコントロールパネルを開いて直接WindowsUpdateを使ってやる方がいいらしい。

というわけで一旦再起動してから、WindowsUpdateから実行したら無事動きました。

それだけの話し

 

あと、当たり前ですけどウイルス対策ソフトは切っておきましょう

 

pythonでsvnをcommitしよう

pythonでローカルにインストールしたSubversionを操作してcommitとかcopyをする方法まとめ

使うもの

  • python 2.7以上
  • subversion 1.6以上
  • svn 0.3.36  ←これを使って操作していきます

準備

pipでsvnをインストール、以上

$pip install svn

 使い方

ここここを参考

基本的に、操作したいリポジトリがローカルにある場合はsvn.local.Localclient、httpなりhttpsなりを使ってアクセスしたい場合はsvn.remote.Remoteclientを使うみたいです

しかし問題はupdate,commitのサブコマンド用メソッドが用意されていない!

というわけで代わりにrun_commandというメソッドを使っていきます

def run_command(self, subcommand, args, success_code=0,
return_stderr=False, combine=False, return_binary=False):

引数としてsubversionのサブコマンドと、オプションをリストで渡して使う感じです

 

checkoutだとメソッドがあるから

r = svn.local.LocalClient("hogehoge/fugafuga",username="hogehoge",password="fugafuga")
r.checkout('') #カレントディレクトリにcheckout

commitだと

r=svn.remote.RemoteClient("https://hogehoge.fugafuga")

r.run_command("commit",["-m  hogehoge"]) 

updateだと

r.run_command("update",[])

 copyだと

r.run_command("copy",["https://hogehoge.com/trunk/fugafuga1","https://hogehoge.com/trunk/fugafuga2","-m hogehoge"])

 

こんな感じです