Commit 4a6c100b authored by Jose Borreguero's avatar Jose Borreguero
Browse files

switch from username to custodian/executor


Signed-off-by: default avatarJose Borreguero <borreguero@gmail.com>
parent c6c2fbdb
Pipeline #196793 failed with stages
in 6 minutes and 11 seconds
......@@ -25,7 +25,6 @@ import ast
import uuid
from django.db import models
from django.contrib.auth import get_user_model
from django.utils.translation import gettext_lazy as _
from django.conf import settings
from django.core.exceptions import ValidationError, ObjectDoesNotExist, MultipleObjectsReturned
......@@ -36,6 +35,7 @@ from model_utils.models import TimeStampedModel
# standard imports
from pathlib import Path
from typing import Optional
# Thanks http://stackoverflow.com/a/7394475
......@@ -466,33 +466,34 @@ class Result(TimeStampedModel):
return '{self.remote_filename} <{self.job}>'.format(self=self)
class IdentityFileModelError(Exception):
pass
class IdentityFileModelManager(models.Manager):
class MultipleIdentityFileError(MultipleObjectsReturned):
pass
def create_from_username(self, username: str) -> "IdentityFile":
def create_from_custodian(self, custodian: str, executor: Optional[str] = None) -> "IdentityFile":
r"""Instantiate an identity file object and save in the database by passing only a user name.
Avoids creation when a record for `username` is found in the database. In such case, returns the found record.
:param username: login user name
:param custodian: login user name, or Django session. Will be associated to a private/public SSH key pair.
:param executor: user name in charge of opening an SSH tunnel between the app and worker servers.
:raise MultipleObjectsReturned: when more than one identity-file is found in the database
:return: instance of model IdentityFileModel
"""
recipient = get_user_model().objects.get(username=username)
custodian = str(username)
if not executor:
executor = custodian
try:
idf_record = self.get(recipient=custodian)
idf_record = self.get(custodian=custodian)
except ObjectDoesNotExist:
id_file = IdentityFile(persistent=True)
idf_record = super().create(
recipient=custodian,
private=str(id_file.private),
public=str(id_file.public),
custodian=custodian,
executor=custodian,
private=str(id_file.private), public=str(id_file.public), custodian=custodian, executor=executor
)
except MultipleObjectsReturned:
# this should not happen
raise self.MultipleIdentityFileError(f"More than one identity file stored for {recipient}")
raise self.MultipleIdentityFileError(f"More than one identity file stored for {custodian}")
return idf_record
......@@ -517,19 +518,13 @@ class IdentityFileModel(TimeStampedModel):
executor can store more than one public key, meaning one executor can execute jobs
for more than one custodian.
record = IdentityFile.objects.create_from_username(username)
record = IdentityFile.objects.create_from_custodian(username)
"""
recipient = models.OneToOneField(
settings.AUTH_USER_MODEL,
models.PROTECT,
related_name="idfile",
verbose_name=_("User"),
help_text=_("The user receiving the public file"),
)
private = models.CharField(
_("private key file"), help_text=_("The path to the private SSH key file"), max_length=250
)
public = models.CharField(_("public key file"), help_text=_("The path to the public SSH key file"), max_length=250)
custodian = models.CharField(
......@@ -547,11 +542,12 @@ class IdentityFileModel(TimeStampedModel):
def __str__(self):
r"""Convert model to string, e.g. ``"zzz IDF"``"""
return f"{self.recipient} IDF"
return f"{self.custodian} IDF"
def save(self, *args, **kwargs):
r"""Override the save method to prevent updating the record, if extant"""
kwargs["force_insert"] = True
if IdentityFileModel.objects.filter(custodian=self.custodian):
raise IdentityFileModelError(f"An IdentityFile record for {self.custodian} already exists in the database")
kwargs["force_insert"] = True # prevent updating the record, if extante
return super().save(*args, **kwargs)
def delete(self, *args, **kwargs):
......
......@@ -8,12 +8,19 @@ test_django-remote-submission
Tests for `django-remote-submission` models module.
"""
# package imports
from django_remote_submission.models import ListField, IdentityFileModel, Interpreter, Log, Job, Result, Server
from django_remote_submission.models import (
ListField,
IdentityFileModel,
IdentityFileModelError,
Interpreter,
Log,
Job,
Result,
Server,
)
from django_remote_submission.wrapper.remote import IdentityFile
# third party imports
from django.core.exceptions import ObjectDoesNotExist
from django.db.utils import IntegrityError
from django.contrib.auth import get_user_model
import pytest
......@@ -125,70 +132,79 @@ class TestListField:
class TestIdentityFileModel:
r'''
r"""
@pytest.mark.django_db
def test_create(self, user):
with pytest.raises(ObjectDoesNotExist) as e:
record = IdentityFileModel.objects.create_by_username("non-existing-user")
'''
record = IdentityFileModel.objects.create_by_custodian("non-existing-user")
"""
@pytest.mark.django_db
def test_instantiate(self, user):
id_file = IdentityFile(sshdir="/tmp")
record = IdentityFileModel(recipient=user, private=str(id_file.private), public=str(id_file.public))
assert record.recipient == user
username = user.username
record = IdentityFileModel(
private=str(id_file.private), public=str(id_file.public), custodian=username, executor=username
)
assert os.path.exists(record.private)
assert os.path.exists(record.public)
assert record.custodian == username
assert record.executor == username
@pytest.mark.django_db
def test_create(self, user):
id_file = IdentityFile(sshdir="/tmp")
record = IdentityFileModel.objects.create(recipient=user,
private=str(id_file.private),
public=str(id_file.public))
assert record.recipient == user
username = user.username
record = IdentityFileModel.objects.create(
private=str(id_file.private), public=str(id_file.public), custodian=username, executor=username
)
assert os.path.exists(record.private)
assert os.path.exists(record.public)
assert record.custodian == username
assert record.executor == username
record.delete()
assert os.path.exists(record.private) is False
assert os.path.exists(record.public) is False
@pytest.mark.django_db(transaction=True)
def test_create_from_username(self, user):
# error if user does not exist
with pytest.raises(ObjectDoesNotExist) as e:
IdentityFileModel.objects.create_from_username("non-existing-user")
assert str(e.value) == "User matching query does not exist."
def test_create_from_custodian(self, user):
username = user.username
# create an IdentityFileModel record for `user`
record = IdentityFileModel.objects.create_from_username(user.username)
assert record.recipient == user
assert Path(record.private).parent == Path.home() / ".ssh"
record_1 = IdentityFileModel.objects.create_from_custodian(username)
assert record_1.custodian == username
assert Path(record_1.private).parent == Path.home() / ".ssh"
[os.remove(file) for file in [record_1.private, record_1.public]]
# return the newly created IdentityFileModel record, do not instantiate another one
record_2 = IdentityFileModel.objects.create_from_username(user.username)
assert record_2.id == record.id
record_2 = IdentityFileModel.objects.create_from_custodian(username)
assert record_2.id == record_1.id
# we screw up if we try to create & store two records for the same user
# we screw up if we try to save in the database a record that already exists
id_file = IdentityFile(sshdir="/tmp")
with pytest.raises(IntegrityError) as e:
IdentityFileModel.objects.create(recipient=user, private=str(id_file.private), public=str(id_file.public))
assert str(e.value) == "UNIQUE constraint failed: django_remote_submission_identityfilemodel.recipient_id"
record = IdentityFileModel(
private=str(id_file.private), public=str(id_file.public), custodian=username, executor=username
)
with pytest.raises(IdentityFileModelError) as e:
record.save()
assert str(e.value) == f"An IdentityFile record for {username} already exists in the database"
[os.remove(file) for file in [id_file.private, id_file.public]]
# same as before, but using method IdentityFileModel.save()
# same as before, but using Model.objects.create(), which calls Model.save()
id_file = IdentityFile(sshdir="/tmp")
record = IdentityFileModel(recipient=user, private=str(id_file.private), public=str(id_file.public))
with pytest.raises(IntegrityError) as e:
record.save()
assert str(e.value) == "UNIQUE constraint failed: django_remote_submission_identityfilemodel.recipient_id"
with pytest.raises(IdentityFileModelError) as e:
IdentityFileModel.objects.create(
private=str(id_file.private), public=str(id_file.public), custodian=username, executor=username
)
assert str(e.value) == f"An IdentityFile record for {username} already exists in the database"
[os.remove(file) for file in [id_file.private, id_file.public]]
@pytest.mark.django_db(transaction=True)
def test_delete(self, user):
r"""delete the record, along with the SSH key files"""
username = user.username
record = IdentityFileModel.objects.create_from_username(user.username)
record = IdentityFileModel.objects.create_from_custodian(user.username)
record.delete()
assert os.path.exists(record.private) is False
assert os.path.exists(record.public) is False
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment