flaskbb 配置与 AD 登录

今天花了3个小时把公司内网论坛架好了,同时接入了公司的 Active Directory 服务器账号登录。在此做一点记录。

因为最近用 flask 写了几个网站,所以论坛系统我也选择了 flaskbb,方便平时改着玩儿。它是一个基于 flask 的轻量级论坛,在 Github 上有 800+ follows,它使用了大量的 flask 插件来简化开发,自身也支持插件系统。

依赖

应该使用 python 2.7 来部署 flaskbb。

为了支持 MySQL,我们需要安装一个数据库驱动库,这里我选择 PyMySQL,它使用纯 Python 实现,在 Python2/3 下都可以使用。使用 pip install pymysql 安装它。

为了支持 LDAP 协议,我们需要安装一个 LDAP 驱动。这里我选择 ldap3,我在 实现 LDAP 验证登录 中做过介绍。它也是使用纯 Python 实现, 同时兼容 Python2/3 。使用 pip install ldap3 安装它。

基于 MySQL 安装

默认配置文件中使用的数据库系统是 sqlite。如果要使用 MySQL,需要做一些小小的修改。

首先,需要修改官方配置文件中的 SQLALCHEMY_DATABASE_URI 为如下所示:

SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://zrong:password@localhost/flaskbb'

这里的配置已经指定使用 MySQL 数据库,而且采用 pymysql 作为数据库驱动,数据库名称是 flaskbb。flaskbb 应该是一个已经存在的数据库,而且内容为空。除了基本的 SELECT/INSERT/UPDATE/DELETE 权限外,zrong 这个帐号需要有 ALTER/CREATE/INDEX 权限。

接下来,需要初始化数据库。如果按照官方文档所说执行 make install 多半是不成功的,会提示表不存在。这可能是由于作者没有仔细测试,或者没有更新文档的原因。正确的用法应该是执行下面的命令对数据库进行初始化(建表,填充必要数据):

1python manage.py initdb

然后创建一个管理员,命令行会询问帐号和密码等信息:

1python manage.py create_admin

如果要显示中文翻译(并不完全),还可以编译语言文件:

1python manage.py compile_translations

BTW: flaskbb 采用的是 GNU gettext 实现多语言支持的,这和 Wordpress 一样。我曾经写过一个 lua 封装:在Lua中使用gettext实现多语言支持。在 Python 中,babel 就带有 gettext 的支持

LDAP (AD) 集成

我在 实现 LDAP 验证登录 中已经介绍过 Python 的 LDAP 实现。这里介绍一下应该如何修改 flaskbb 使其支持与公司的 AD 服务器集成。

一开始我准备采用 flaskbb 的 插件系统 来实现这个功能,这样就可以不必修改源码。但我仔细看了官方提供的 Portal 插件的实现后后发现,flaskbb 中的插件机制是通过预埋 event 钩子来实现注入的。这个 event 必须自己埋入代码中才能生效。这就违背了我不愿意改源码的初衷。时间有限,插件的机制后面再深究,先把功能实现再说。

LDAP 服务器的信息需要加入到配置项中,我修改了 configs/production.py.example ,这些项目必须配置才能实现 LDAP 登录:

1# LDAP Settings
2# ------------------------------ #
3# For LDAP authorize
4LDAP_SERVER = 'ldapserver.example.org'
5LDAP_PORT = None
6LDAP_USER_FMT = 'Yourcompany\\%s'
7LDAP_EMAIL_SUFFIX = '@example.org'

我增加了一个 staticmethod User._check_login_password,在这个方法中提供用户名和密码,读取上述配置,去指定的 LDAP 服务器鉴权。

帐号鉴权调用的是 user/models.py 中的 User.authenticate 方法。这是个 classmethod 。我增加了一个 User.authenticate_ldap classmethod 用来替换原始方法,这个修改的方法中会调用 User._check_login_password

这个方法的实现流程为:先检测帐号的合法性,去 AD 服务器鉴权。若鉴权成功,就查询 flashbb 的数据库中有没有该账号,若有则更新登录时间,若无则在 flaskbb 的 users 表中创建该帐号,将密码置为 'password' ,并立即启用该帐号。

1user = User(username=login,
2            email=login+email_suffix,
3            password='password',
4            date_joined=time_utcnow(),
5            primary_group_id=4,
6            language='zh',
7            activated=True)
8user.save()

由于鉴权完全使用 AD 服务器实现,在 users 表中 不应该 保存密码。

完整的方法可查看 User.authenticate_ldap

调用鉴权的位置是 auth/views.py 中的 login 路由。将这个路由的端点注释掉,改名为 login_origin,创建一个新的 login 方法,使其调用 User.authenticate_ldap 方法进行鉴权。

完整的方法可查看 login

2016-11-17 增加:

auth/views.py 中还有一个 reauth 方法需要重新实现。reauth 用来重新验证用户的密码。我们需要取得用户的账号,带上用户输入的密码去 LDAP 服务器进行鉴权。修改后的 reauth 方法直接调用 User.check_password_ldap 来进行鉴权。

完整的方法可查看 reauth

禁用功能

由于鉴权完全使用 AD 服务器实现,注册账号、修改账号、重置密码、修改密码、修改 email 等功能都需要禁用。

注册账号的功能可以直接在 flaskbb 的后台中取消。其他几个功能则需要修改源码实现。

忘记密码和重置密码功能在 auth/views.pyforgot_passwordreset_password 中,abort(403) 即可:

1@auth.route('/reset-password', methods=["GET", "POST"])
2def forgot_password():
3    """Sends a reset password token to the user."""
4    # zrong start 2016-11-16 No permission to reset password when login by LDAP
5    abort(403)
6    # zrong end 2016-11-16

完整的方法可查看 forgot_passwordreset_password

修改密码和修改 email 功能在 user/views.pychange_passwordchange_email 中,处理方法相同。

1@user.route("/settings/password", methods=["POST", "GET"])
2@login_required
3def change_password():
4    # zrong start 2016-11-16 No permission to modify password when login by LDAP
5    abort(403)
6    # zrong end 2016-11-16

完整的方法可查看 change_passwordchange_email

最后的部署,当然是看我之前写的 部署Flask + uWSGI + Nginx 啦!全部搞定!

(全文完)