class User(AbstractUser):
    """
        Project's base user model
    """
    LANGUAGE_CHOICES = (
        ('en-us', 'English'),
        ('pt-pt', _('Portuguese')),
    )

    organization = models.CharField(
        null=True, blank=True, max_length=80, verbose_name=_('Organization'))
    public_key = models.TextField(
        blank=True, null=True, verbose_name=_('Public key'))
    fingerprint = models.CharField(
        null=True, blank=True, max_length=50, verbose_name=_('Fingerprint'))
    keyserver_url = models.URLField(
        null=True, blank=True, verbose_name=_('Key server URL'))
    timezone = TimeZoneField(default='UTC', verbose_name=_('Timezone'))
    language = models.CharField(
        default="en-us", max_length=16, choices=LANGUAGE_CHOICES, verbose_name=_('Language'))

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.base_fingerprint = self.fingerprint

    def save(self, *args, **kwargs):
        ip = kwargs.pop('ip', None)
        agent = kwargs.pop('agent', '')
        with transaction.atomic():
            super().save(*args, **kwargs)
            if self.base_fingerprint != self.fingerprint:
                self.keychanges.create(user=self,
                                       prev_fingerprint=self.base_fingerprint,
                                       to_fingerprint=self.fingerprint,
                                       ip_address=ip,
                                       agent=agent)
                self.base_fingerprint = self.fingerprint

    def has_setup_complete(self):
        if self.public_key and self.fingerprint:
            return True
        return False

    @property
    def has_github_login(self):
        return self.socialaccount_set.filter(provider='github').count() >= 1

    @property
    def has_public_key(self):
        return True if self.public_key else False

    @property
    def has_keyserver_url(self):
        return True if self.keyserver_url else False class Box(models.Model):

    OPEN = 10
    EXPIRED = 20
    DONE = 30
    CLOSED = 40

    STATUSES = (
        (OPEN, _('Open')),
        (EXPIRED, _('Expired')),
        (DONE, _('Done')),
        (CLOSED, _('Closed'))
    )

    name = models.CharField(max_length=128, verbose_name=_('Name'))
    description = models.TextField(null=True, blank=True,
                                   verbose_name=_('Description'))
    uuid = models.UUIDField(default=uuid.uuid4, editable=False,
                            verbose_name=_('Unique ID'))

    owner = models.ForeignKey(settings.AUTH_USER_MODEL,
                              on_delete=models.CASCADE,
                              related_name='own_boxes',
                              verbose_name=_('Owner'))

    recipients = models.ManyToManyField(settings.AUTH_USER_MODEL,
                                        related_name='boxes',
                                        through='Membership',
                                        through_fields=('box', 'user'),
                                        verbose_name=_('Recipients'))
    created_at = models.DateTimeField(auto_now_add=True,
                                      verbose_name=_('Created at'))
    updated_at = models.DateTimeField(auto_now=True,
                                      verbose_name=_('Updated at'))
    expires_at = models.DateTimeField(null=True, blank=True,
                                      verbose_name=_('Expires at'))

    status = models.IntegerField(choices=STATUSES, default=OPEN,
                                 verbose_name=_('Status'))
    max_messages = models.PositiveIntegerField(default=1, validators=[
                                               MinValueValidator(1)],
                                               verbose_name=_('Max. messages'))

    last_sent_at = models.DateTimeField(null=True,
                                        verbose_name=_('Last sent at'))

    class Meta:
        verbose_name = _('Box')
        verbose_name_plural = _('Boxes')

    def __str__(self):
        return self.name

    @staticmethod
    def get_status(name):
        return {
            "Open": Box.OPEN,
            "Expired": Box.EXPIRED,
            "Done": Box.DONE,
            "Closed": Box.CLOSED
        }.get(name, Box.OPEN) class Message(models.Model):

    ONQUEUE = 10
    SENT = 20
    FAILED = 30

    STATUSES = (
        (ONQUEUE, _('OnQueue')),
        (SENT, _('Sent')),
        (FAILED, _('Failed'))
    )

    box = models.ForeignKey(
        "Box", on_delete=models.CASCADE, related_name='messages')
    status = models.IntegerField(choices=STATUSES, default=ONQUEUE,
                                 verbose_name=_('Status'))
    created_at = models.DateTimeField(auto_now_add=True,
                                      verbose_name=_('Created at'))
    updated_at = models.DateTimeField(auto_now=True,
                                      verbose_name=_('Updated at'))
    sent_at = models.DateTimeField(null=True, blank=True,
                                   verbose_name=_('Sent at')) class CreateBoxForm(ModelForm):
    never_expires = BooleanField(required=False)

    field_order = [
        "name",
        "never_expires",
        "expires_at",
        "max_messages",
        "description"]

    class Meta:
        model = Box
        fields = [
            "name",
            "description",
            "expires_at",
            "max_messages"]

    def clean_expires_at(self):
        # Validate the expiration date
        expires_at = self.cleaned_data.get("expires_at", "")
        never_expires = self.cleaned_data.get("never_expires", "")
        current_tz = timezone.get_current_timezone()
        if never_expires:
            expires_at = None
            self.cleaned_data["expires_at"] = expires_at
        if expires_at:
            # Check if the expiration date is a past date
            if timezone.localtime(timezone.now(), current_tz) > expires_at:
                self.add_error('expires_at', _('The date must be on the future.'))
        if not expires_at and not never_expires:
            self.add_error('expires_at',
                           _('This field is required, unless box is set to '
                             'never expire.'))
        return expires_at

    def clean(self):
        cleaned_data = super().clean()
        cleaned_data.pop("never_expires")
        return cleaned_data class SubmitBoxForm(Form):
    message = CharField(widget=Textarea, required=True)
    file_name = CharField(required=False)
    add_reply_to = BooleanField(required=False)

    def clean_message(self):
        # Quick check if the message really came encrypted
        message = self.cleaned_data.get("message")
        lines = message.split("\r\n")

        if getsizeof(message) > MAX_MESSAGE_SIZE:
            self.add_error(
                "message",
                _('The message or file exceeds your allowed size limit.')
            )

        begin = "-----BEGIN PGP MESSAGE-----"
        end = "-----END PGP MESSAGE-----"

        try:
            if lines[0] != begin or lines[-1] != end:
                self.add_error("message", _('Invalid PGP message'))
        except IndexError:
            self.add_error("message", _('Invalid PGP message'))
        return message MAX_MESSAGE_SIZE = 10485760  # Bytes = 10 Mb @shared_task
def process_email(message_id, form_data, sent_by=None):
    message = Message.objects.get(id=message_id)
    box = message.box
    msg = form_data["message"]
    file_name = form_data.get("file_name", "")
    subject = _('New submission to your box: {}').format(box)
    reply_to = [sent_by] if sent_by else []

    if file_name:
        body = _("The submitted file can be found in the attachments.")
        email = EmailMultiAlternatives(
            subject,
            body,
            settings.DEFAULT_FROM_EMAIL,
            [box.owner.email],
            reply_to=reply_to,
            attachments=[(file_name, msg, "application/octet-stream")]
        )
    else:
        email = EmailMultiAlternatives(subject,
                                       msg,
                                       settings.DEFAULT_FROM_EMAIL,
                                       [box.owner.email],
                                       reply_to=reply_to)

    # Output e-mail message for debug purposes
    # with open('email.mbox', 'w') as f:
    #     f.write(email.message().as_string(unixfrom=True))

    email.send()
    now = timezone.now()
    box.last_sent_at = now
    box.save()
    message.sent_at = now
    message.status = Message.SENT
    message.save() ENCRYPTED_MESSAGE = """-----BEGIN PGP MESSAGE-----\r
Version: OpenPGP.js v2.2.0\r
Comment: http://openpgpjs.org\r
\r
wcFMA9K5fTFi5lXtAQ//WLWrSXgc5YRgL8sGCLPFVoFb5ootYlCphhk+hWSD\r
Wr7U4IXYr+mHvhX3twigK+io4EVoBtSg5+e+Nkvbj4HTIq/XH2DGHLFDMKTc\r
zeY7kNHxKuWB4ZCDJPTlwQHfY5hyBZQTkhRDXsA4Y79OZo9gN9FwvbKjSwUM\r
AG+fhorLT/nEoHexZt1vohhobAQkRaVLh+NMKRLgHXdhPhbO92JIvel/TwZs\r
5Iub8S11bOOsbT4a6y9yxQk2rL8lct77nygjlrK6ejlwdHWzT6OG3yS1YoaK\r
jpx3frXnITCgy8oNPn1Pxn4S7Pmmq4xl4JmH4inmlzRMZDzKG/5kVesh6pwH\r
nJyJCMuyIj60gNhxBF91ndLgg+PJWwjZ3I+E/M5mo0ZscCpAnxKh/qmt4I6j\r
rn/jEp1djY6nFGmuzmHIGYjhvxkGFfFEIwsqkHfGTBOkWIjK5T4WzpGNa4Dr\r
k285x1R+3lzUBzl674Rl74+8wLm9DESd3k6+yOhibkv29kVxEcaCsOOzpNOx\r
q/1vWCRjqoOnVXrx3tbzpjLjb647Hf/+DYA6ENpNyohAZv9bp3J1ZTrcpMXg\r
/6mOxDt+C3G5ARmH/FG6JYtge1ck4GQh02CQxiYG8psaqAntd+VzaRUl+lVC\r
1pbvd9ToxWD3HaVdoGsZBEjIWt9gEtCv+RTvPY6EmrbSPQGI40HjcYG8sbY+\r
OZ/Lsv1Kz9Rg/VSvCxknTCncdju07kD5eiLJUAt1u6JYd4mn/TGZBlmtRo/J\r
CIuqkcg=\r
=6WtN\r
-----END PGP MESSAGE-----""" EXPIRED_KEY = """-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: SKS 1.1.5
Comment: Hostname: pgp.mit.edu

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
8e6k2TpLNf1VrR1+IUmD+hbFmeSR
=3qZO
-----END PGP PUBLIC KEY BLOCK-----
""" EXPIRED_KEY_FINGERPRINT = "84009091C9F92152D34BF0919841A6E703BA514A" REVOKED_KEY = """-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: SKS 1.1.5
Comment: Hostname: pgp.mit.edu

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
=+LpS
-----END PGP PUBLIC KEY BLOCK-----
""" REVOKED_KEY_FINGERPRINT = "C341AE04573A4D7CCBDE594C83AFB921E7A24CCA" VALID_KEY = """-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: SKS 1.1.6
Comment: Hostname: keys2.kfwebs.net

mQENBFoUymwBCADE0pq/ZTgyzV2mb/TySnZ2qt/wHqzv8vBrLi4ymqTHtMi/ZIPfcrsisdZE
N3l+hfO6DGkB1r/50IM4CiZB3qAuhRlzfAi3MKygw0dLQjQqlz4pShkdr+wFWSDMfY6uN+sk
Fon8hq5k5v4c7kKUErTKwm9eT19NuKU7DWHvAN7aFtHstN3mUd1hizizyIl7xcxvA01uoMgP
nMw6mqg/DbB7TwQ1E6zdRAnP6qwUyy9dDLO6Pe34uIpsmWr1qgpZQ6jXIRG0K/tHSXBrVwcy
7rEJU01pCUBdZWnQ8WWegjqwPGpOMplqSdIUW3W6MBZ74ly0NPi3mILrW1R3Ahj9SSDZABEB
AAG0ZUhhd2twb3N0IFRlc3QgS2V5IChUaGlzIGtleSBpcyBub3QgdXNlZC4gT25seSBmb3Ig
dGVzdGluZyBwdXJwb3Nlcy4pIDxmYWtlLnRlc3RpbmcuZW1haWxAaGF3a3Bvc3QuY28+iQE3
BBMBCAAhBQJaFMpsAhsDBQsJCAcCBhUICQoLAgQWAgMBAh4BAheAAAoJEK0OUjfZhr11RZ8H
+wblJeH1cjQpymqW9ml0lVQlhpold6aTecQ5ncMf+Ax1xtexYx6Lm1kbDbKDjVJizccikpiE
rPniLGUJwWp/kvZegpiUv2ha25k2fuWmSPRbT32MKa2pNzzhDqXvOVujytDOAGqWxPaexVak
JTt8mYWrLhTn0DRdX7KK/8y019pfeE/UD0ND3diRFtS/GowFY+KRut5Yl8Fu8hucpSXedeEM
UxRG9AnYxS4oosVwpg8AxyZ+llFhNmth8CEyFXt5RL29F8GkKxz+9wvZNGgwlB/vEkzm2XAJ
ckPVGLSndIClNHLcpGtS+lcw2uJaQllg70W1prFh/EI+tsyMnGV0n4m5AQ0EWhTKbAEIAMoa
vGOmcwcvB8v++3qoj2JqyDcuZp4WgGCxX/wZbo18ICg5FtcweP6Vm9/2sauVHsEyt82Udpf7
B4zCmAHUTI35UQpb2GZq6UoFLXH6QXR5Pooft/rJpgtmMiJlxO4oqErabZwYYK2+5p718dcQ
CexwJxdFuMRMhMGpo7uc/Z/R136lTh2gNNRcfz0X8mFYRe8W3fXl1oWlUo5hkNNPD+05yd9W
++cQ8zvuYNSFuyTfb4cff8E0WGns9zRkJ4ppspVB4pzOIPBoYasswsm7HcXtGQ6c+iYb87YW
uSe24bo/MlQMEsSaIPGZ0oC3IugoMD9u9a2H8j/PKXEK1kp+nlsAEQEAAYkBHwQYAQgACQUC
WhTKbAIbDAAKCRCtDlI32Ya9dSDeCACvnhxY2zesbsJbjeOhBQGmWRYz3UtBT//vuguNcCEI
U10Y8lsjuwmuN9n55d2vKyIAIN1m7Ixt95svHWs51bbZUNjzHB5OMyoVwTY53itbm736dmk4
TVUzKyeCmGxFzipQQHPQljrkEy29hFXhSvRsghrFlYB/liNTWxHYdpaEXzsIOFIOY8rCLqV5
6xu/ngxTQAEPTnJJ0NSJLSVk4vGRo00i/tljvRfTgC2vLOUsnI5/TGVi3QZJOyZsCesNTl9s
cdUsTGXAGrbCtPdqs7qusZGwh9VuePwa+g+mI/vagxNeLQSqB8uqLzQ3YaqkgnH4FR1u+819
f3+dc1sYd68l
=j/j2
-----END PGP PUBLIC KEY BLOCK-----
""" VALID_KEY_FINGERPRINT = "3DA4 A7C5 49BF F7EB 17A7 00F8 AD0E 5237 D986 BD75"
from django.test import TestCase
from django.utils import timezone
from django.urls import reverse
from django.core import mail
from humans.models import User
from datetime import timedelta
from .models import Box, Message
from .forms import CreateBoxForm, SubmitBoxForm, MAX_MESSAGE_SIZE
from .tasks import process_email
from .test_constants import ENCRYPTED_MESSAGE
from humans.test_constants import EXPIRED_KEY, EXPIRED_KEY_FINGERPRINT, \
    REVOKED_KEY, REVOKED_KEY_FINGERPRINT, VALID_KEY, VALID_KEY_FINGERPRINT
import random
import string
        user = create_and_login_user(self.client)
        create_boxes(user)
        response = self.client.get(reverse("boxes_list"),
                                   {'display': 'Done'})
        for box in response.context["object_list"]:
            self.assertEqual(box.status, Box.DONE)


class BoxSubmitViewTests(TestCase):

    def test_valid_owner_key(self):
        user = create_and_login_user(self.client)
        user.public_key = VALID_KEY
        user.fingerprint = VALID_KEY_FINGERPRINT
        user.save()
        box = create_open_box(user)
        response = self.client.get(reverse("boxes_show", args=(box.uuid,)))
        self.assertEqual(response.template_name[0], 'boxes/box_submit.html')

    def test_revoked_owner_key(self):
        user = create_and_login_user(self.client)
        user.public_key = REVOKED_KEY
        user.fingerprint = REVOKED_KEY_FINGERPRINT
        user.save()
        box = create_open_box(user)
        response = self.client.get(reverse("boxes_show", args=(box.uuid,)))
        self.assertEqual(response.template_name, 'boxes/closed.html')

    def test_expired_owner_key(self):
        user = create_and_login_user(self.client)
        user.public_key = EXPIRED_KEY
        user.fingerprint = EXPIRED_KEY_FINGERPRINT
        user.save()
        box = create_open_box(user)
        response = self.client.get(reverse("boxes_show", args=(box.uuid,)))
        self.assertEqual(response.template_name, 'boxes/closed.html')

    def test_no_owner_key(self):
        user = create_and_login_user(self.client)
        box = create_open_box(user)
        user.save()
        response = self.client.get(reverse("boxes_show", args=(box.uuid,)))
        self.assertEqual(response.template_name, 'boxes/closed.html')

    def test_box_not_found(self):
        user = create_and_login_user(self.client)
        user.public_key = VALID_KEY
        user.fingerprint = VALID_KEY_FINGERPRINT
        user.save()
        box = create_open_box(user)
        box.delete()
        response = self.client.get(reverse("boxes_show", args=(box.uuid,)))
        self.assertEqual(response.status_code, 404)


class BoxCreateViewTests(TestCase):

    def test_create_new_box(self):
        user = create_and_login_user(self.client)
        user.public_key = VALID_KEY
        user.fingerprint = VALID_KEY_FINGERPRINT
        user.save()
        response = self.client.post(reverse("boxes_create"), {
                                    "name": "test",
                                    "never_expires": True,
                                    "max_messages": 1})
        self.assertEqual(response.status_code, 200)
        self.assertEqual(user.boxes.count(), 1)
        self.assertEqual(user.boxes.first().name, "test")


class BoxDeleteViewTests(TestCase):

    def test_delete_existing_box(self):
        user = create_and_login_user(self.client)
        create_boxes(user)
        box = user.own_boxes.filter(name="open").first()
        response = self.client.post(reverse("boxes_delete", args=(box.id,)))
        self.assertEqual(response.status_code, 302)
        self.assertEqual(user.own_boxes.count(), 3)

    def test_cannot_delete_box_that_are_not_open(self):
        user = create_and_login_user(self.client)
        create_boxes(user)
        box = user.own_boxes.filter(name="closed").first()
        response = self.client.post(reverse("boxes_delete", args=(box.id,)))
        self.assertEqual(response.status_code, 302)
        self.assertEqual(user.own_boxes.count(), 4)


class BoxCloseView(TestCase):

    def test_close_an_open_box(self):
        user = create_and_login_user(self.client)
        create_boxes(user)
        box = user.own_boxes.filter(name="open").first()
        response = self.client.post(reverse("boxes_close", args=(box.id,)))
        self.assertEqual(response.status_code, 302)
        self.assertEqual(user.own_boxes.filter(status=Box.CLOSED).count(), 2)

    def test_close_box_that_is_not_open(self):
        user = create_and_login_user(self.client)
        create_boxes(user)
        box = user.own_boxes.filter(name="sent").first()
        response = self.client.post(reverse("boxes_close", args=(box.id,)))
        self.assertEqual(response.status_code, 302)
        self.assertEqual(user.own_boxes.filter(status=Box.CLOSED).count(), 1)


class MailTaskTests(TestCase):

    def test_email_sending(self):
        """
            With a valid message_id an email is sent and the Message status
            is changed to sent
        """
        user = create_and_login_user(self.client)
        create_boxes(user)
        initial_box = user.own_boxes.all()[0]
        message = initial_box.messages.create()

        process_email(message.id, {"message": ENCRYPTED_MESSAGE})