diff --git a/django_mailbox/admin.py b/django_mailbox/admin.py index c586b40..b953947 100644 --- a/django_mailbox/admin.py +++ b/django_mailbox/admin.py @@ -8,6 +8,7 @@ console. import logging +from django import forms from django.conf import settings from django.contrib import admin from django.utils.translation import ugettext_lazy as _ @@ -39,11 +40,29 @@ resend_message_received_signal.short_description = ( _('Re-send message received signal') ) +class MailboxForm(forms.ModelForm): + uri = forms.CharField(widget=forms.PasswordInput, + help_text="For security, the URI will not be shown and will " + "be encrypted in database " + "
" + "Example: imap+ssl://myusername:mypassword@someserver
" + "
" + "Internet transports include 'imap' and 'pop3'; " + "common local file transports include 'maildir', 'mbox', " + "and less commonly 'babyl', 'mh', and 'mmdf'.
" + "
" + "Be sure to urlencode your username and password should they " + "contain illegal characters (like @, :, etc)." + ) + + class Meta: + model = Mailbox + fields = ('name', 'uri', 'from_email', 'active',) class MailboxAdmin(admin.ModelAdmin): + form = MailboxForm list_display = ( 'name', - 'uri', 'from_email', 'active', 'last_polling', @@ -51,6 +70,10 @@ class MailboxAdmin(admin.ModelAdmin): readonly_fields = ['last_polling', ] actions = [get_new_mail] + def save_model(self, request, obj, form, change): + if request: + obj.uri = obj.encrypt_uri() + obj.save() class MessageAttachmentAdmin(admin.ModelAdmin): raw_id_fields = ('message', ) diff --git a/django_mailbox/models.py b/django_mailbox/models.py index a343989..9d60a78 100644 --- a/django_mailbox/models.py +++ b/django_mailbox/models.py @@ -17,6 +17,7 @@ import os.path import sys import uuid from tempfile import NamedTemporaryFile +from Crypto.Cipher import AES import six from six.moves.urllib.parse import parse_qs, unquote, urlparse @@ -113,8 +114,36 @@ class Mailbox(models.Model): objects = models.Manager() active_mailboxes = ActiveMailboxManager() + def pad(self, key): + length = 32 - (len(key) % 32) + return key + chr(length).encode('utf-8') * length + + def unpad(self, key): + return key[0:-ord(key[-1])] + + def encrypt_uri(self): + secret_key = self.pad(django_settings.SECRET_KEY) + self.uri = unicode(self.pad(self.uri)).encode('utf-8') + + cipher = AES.new(secret_key) + self.uri = cipher.encrypt(self.uri) + self.uri = base64.b64encode(self.uri) + + return self.uri + + def decrypt_uri(self): + secret_key = self.pad(django_settings.SECRET_KEY) + self.uri = self.pad(self.uri) + + cipher = AES.new(secret_key) + self.uri = base64.b64decode(self.uri) + self.uri = cipher.decrypt(self.uri) + + return self.unpad(self.uri) + @property def _protocol_info(self): + self.uri = self.decrypt_uri() return urlparse(self.uri) @property diff --git a/rtd_requirements.txt b/rtd_requirements.txt index 18684b7..8e20bd2 100644 --- a/rtd_requirements.txt +++ b/rtd_requirements.txt @@ -1,2 +1,3 @@ django>=1.9,<1.10 six +pycrypto==2.6.1