自宅サーバ用でProFTPDを使っていたのだが、ユーザー管理が面倒なのでFTPサーバを自作してみた。
Djangoのユーザー管理機能に結びつけたFtpUserモデルを作成し、pyftpdlibのauthorizerを書いただけのシンプルなもの。
settings.py
# Django settings for unboxftpd project. import os BASE_DIR = os.path.dirname(os.path.abspath(__file__)) DEBUG = True TEMPLATE_DEBUG = DEBUG ADMINS = ( ('admin', 'admin@hoge.hoge') ) MANAGERS = ADMINS DATABASE_ENGINE = 'sqlite3' DATABASE_NAME = os.path.join(BASE_DIR, "unboxftpd.db") DATABASE_USER = '' DATABASE_PASSWORD = '' DATABASE_HOST = '' DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3. TIME_ZONE = 'Asia/Tokyo' LANGUAGE_CODE = 'ja' SITE_ID = 1 USE_I18N = True MEDIA_ROOT = '' MEDIA_URL = '' ADMIN_MEDIA_PREFIX = '/media/' SECRET_KEY = 'secret_key..' TEMPLATE_LOADERS = ( 'django.template.loaders.filesystem.load_template_source', 'django.template.loaders.app_directories.load_template_source', ) MIDDLEWARE_CLASSES = ( 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.middleware.doc.XViewMiddleware', ) ROOT_URLCONF = 'unboxftpd.urls' TEMPLATE_DIRS = ( ) INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'django.contrib.admin', 'unboxftpd.ftpd', )
urls.py
from django.conf.urls.defaults import * urlpatterns = patterns('', (r'', include('django.contrib.admin.urls')), )
ftpd/models.py
# -*- coding: utf8 -*- from django.db import models from django.contrib.auth.models import User import datetime class FtpUser(models.Model): """ unboxftp users model """ user = models.ForeignKey(User) last_login = models.DateTimeField(default=datetime.datetime.now(), editable=False) root_dir = models.CharField(null=False, maxlength=250) readable = models.BooleanField(default=True) writable = models.BooleanField(default=False) class Admin: list_display = ('user', 'root_dir', 'last_login', 'readable', 'writable',) search_fields = ('user',) fields = ( (None, {'fields': ('user', 'root_dir',)}), ('Permission', {'fields': ('readable', 'writable',)}), ) def __str__(self): return self.user.username def login_update(self): self.last_login = datetime.datetime.now()
ftpd/ftpdconf.py
# -*- coding: utf8 -*- ADDRESS = '127.0.0.1' PORT = 21
ftpd/ftpd.py
# -*- coding: utf8 -*- import os import sys BASE_DIR = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '../../')) if not BASE_DIR in sys.path: sys.path.append(BASE_DIR) os.environ['DJANGO_SETTINGS_MODULE'] = 'unboxftpd.settings' from django.contrib.auth.models import User from pyftpdlib import ftpserver import ftpdconf class UnboxFtpdAuthorizer: def __init__(self): pass def validate_authentication(self, username, password): try: u = self.get_user(username=username) return u.check_password(password) except: return False def has_user(self, username): try: u = self.get_user(username=username) return True except: return False def get_home_dir(self, username): try: u = self.get_ftpuser(username=username) return u.root_dir except: return None def get_msg_login(self, username): u = self.get_ftpuser(username=username) u.login_update() u.save() return 'welcome to unboxftp server' def get_msg_quit(self, username): return 'see you next time' def r_perm(self, username, obj=None): try: u = self.get_ftpuser(username=username) return u.readable except: return False def w_perm(self, username, obj=None): try: u = self.get_ftpuser(username=username) return u.writable except: return False def get_user(self, username): try: return User.objects.get(username=username) except: raise def get_ftpuser(self, username): try: return User.objects.get(username=username).ftpuser_set.all()[0] except: raise def runftpd(): authorizer = UnboxFtpdAuthorizer() ftp_handler = ftpserver.FTPHandler ftp_handler.authorizer = authorizer address=(ftpdconf.ADDRESS,ftpdconf.PORT) ftpd=ftpserver.FTPServer(address,ftp_handler) ftpd.serve_forever() if __name__ == '__main__': runftpd()
http://localhost:8000/ にアクセスするとおなじみのadmin画面。ftpdは、クライアントがログインするときに毎回DBを参照しに行くので、ユーザーの追加・削除を行っても再起動の必要はない。
ソースのアーカイブは以下に。
unboxftpd20071101.zip
2008/1/23追記
この記事はpyftpdlib 0.2.0の時点で書いたものです。
pyftpdlib 0.3.0の場合はパーミッションの実装に変更があるので注意してください。
2016/4/13追記
もろもろ整理してライブラリ化したものをdjango-ftpserverとして公開しています。
GitHub - tokibito/django-ftpserver: FTP server application that used user authentication of Django.