Skip to content

Commit 3d13386

Browse files
committed
Start private key generation vie
1 parent 04b9a4c commit 3d13386

File tree

10 files changed

+199
-23
lines changed

10 files changed

+199
-23
lines changed

.vscode/settings.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
22
"python.formatting.provider": "black",
3-
"python.pythonPath": "~/.virtualenvs/etsd/bin/"
3+
"python.pythonPath": "~/.virtualenvs/etsd/bin/",
4+
"python.linting.pylintEnabled": true
45
}

etsd/core/templatetags/core_tags.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ def get_setting_value(value):
1212
@register.simple_tag(takes_context=True)
1313
def get_user_authority(context):
1414
auth = context.request.user.get_authority()
15-
return auth.name if auth else None
15+
return auth

etsd/keys/forms.py

+13-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class Meta:
1212
fields = ("key", "fingerprint", "confirmation_document")
1313

1414
def __init__(self, *args, **kwargs):
15-
super(PublicKeyCreateForm, self).__init__(*args, **kwargs)
15+
super().__init__(*args, **kwargs)
1616
self.fields["fingerprint"].widget.attrs["readonly"] = True
1717

1818
def clean(self):
@@ -54,4 +54,15 @@ def __init__(self, *args, **kwargs):
5454
self.fields['user_id'].widget.attrs['readonly'] = True
5555
self.fields['creation_time'].widget.attrs['readonly'] = True
5656
#file = forms.FileField()
57-
#passphrase = forms.CharField()
57+
#passphrase = forms.CharField()
58+
59+
60+
class KeyPairCreateForm(forms.ModelForm):
61+
def __init__(self, *args, **kwargs):
62+
super().__init__(*args, **kwargs)
63+
self.fields['fingerprint'].widget.attrs['readonly'] = True
64+
self.fields['key'].widget.attrs.update({'readonly': True, 'rows': 4})
65+
66+
class Meta:
67+
model = models.PublicKey
68+
fields = ("key", "fingerprint", )

etsd/keys/models.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@ class PublicKey(UserDateAbstractModel):
1616
authority = models.ForeignKey("authorities.Authority", on_delete=models.PROTECT)
1717
key = models.TextField(
1818
verbose_name=_("Key text"),
19-
help_text=_("Please paste the key text in armored format ASCII"),
19+
help_text=_("The key text in armored ASCII format"),
2020
)
2121
fingerprint = models.CharField(
2222
max_length=128,
2323
verbose_name=_("Key fingerprint"),
2424
unique=True,
2525
help_text=_(
26-
"The fingerprint of the key will be automatically generated after the key is validated"
26+
"The fingerprint of the key"
2727
),
2828
)
2929
status = models.CharField(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
{% extends "site_base.html" %}
2+
{% load render_table from django_tables2 %}
3+
{% load crispy_forms_tags %}
4+
{% load core_tags %}
5+
{% load i18n static %}
6+
7+
8+
{% block head_title %}Create new key pair{% endblock %}
9+
{% block page_title %}Create new key pair{% endblock %}
10+
{% block page_content %}
11+
{% get_user_authority as auth %}
12+
{% trans "Authority:" %} {{ auth.name }} {{ auth.email }}
13+
<div class="row">
14+
<div class="col-md-12">
15+
<div class='alert alert-info'>
16+
17+
{% blocktrans %}
18+
<p>
19+
You can submit a new public key through this form. The publickey must be in the ASCII armored format i.e it
20+
should start with <code>-----BEGIN PGP PUBLIC KEY BLOCK-----</code> contain the key data in printable ASCII
21+
format (base64 encoded) and end with <code>-----END PGP PUBLIC KEY BLOCK-----</code>.
22+
</p>
23+
24+
</p>
25+
{% endblocktrans %}
26+
</div>
27+
</div>
28+
</div>
29+
30+
<div class="row">
31+
<div class="col-md-12">
32+
<div class="mb-3">
33+
<label for="passphrase" class="form-label requiredField">
34+
{% trans "Passphrase" %}<span class="asteriskField">*</span>
35+
</label>
36+
<input type="password" name="passphrase" maxlength="512" class="textinput textInput form-control" required="" id="passphrase">
37+
<small class="form-text text-muted">
38+
Please use a secure enough passphrase to enrypt your private key. Rules: Must be > <b id='passphraseLength'>16</b> characters. Should be a passphrase
39+
ie a combination of multiple words and contain lower and capital letters, numbers, punctuation and special characters.
40+
41+
</small>
42+
</div>
43+
</div>
44+
</div>
45+
46+
<div class="row">
47+
<div class="col-md-12">
48+
<form method="POST" id='keyform' enctype="multipart/form-data" >
49+
{% csrf_token %}
50+
{{ form|crispy }}
51+
<button id='generateButton' type='button' class='btn btn-warning' >{% trans "Generate Key Pair" %}</button>
52+
<button id='saveButton' type='button' disabled class='btn btn-danger' >{% trans "Download Private Key" %}</button>
53+
<button id='submitButton' type='button' disabled class='btn btn-success' >{% trans "Submit" %}</button>
54+
<a class='btn btn-secondary' href='{% url "public_key_list" %}'>{% trans "Return" %}</a>
55+
</form>
56+
</div>
57+
</div>
58+
59+
{% endblock %}
60+
61+
{% block extra_script %}
62+
{% get_user_authority as auth %}
63+
<script>
64+
const authority = '{{ auth.name }}';
65+
const authority_email = '{{ auth.email }}';
66+
</script>
67+
68+
<script>
69+
const passphraseLength = 4
70+
71+
72+
const loadPK = (async(key_data) => {
73+
74+
let publicKey = await openpgp.readKey({
75+
armoredKey: key_data
76+
});
77+
let fingerprint = publicKey.keyPacket.getFingerprint()
78+
79+
return {key_data, fingerprint}
80+
81+
})
82+
83+
$(function() {
84+
$('#passphraseLength').html(passphraseLength)
85+
$('#generateButton').click(function() {
86+
87+
const passphrase = $('#passphrase').val()
88+
if(!passphrase) {
89+
bootstrap5Alert({'message': "Please enter a passphrase", 'title': "ERROR"})
90+
return
91+
}
92+
if (passphrase.length < passphraseLength) {
93+
bootstrap5Alert({'message': `Passphrase must be at least ${passphraseLength} characters`, 'title': "ERROR"})
94+
return
95+
}
96+
97+
(async () => {
98+
const { privateKey, publicKey, revocationCertificate } = await openpgp.generateKey({
99+
type: 'ecc',
100+
curve: 'curve25519',
101+
userIDs: [{ name: authority, email: authority_email, comment: "ETSD Generated Key" }],
102+
passphrase,
103+
format: 'armored'
104+
});
105+
106+
return {
107+
privateKey,
108+
publicKey
109+
}
110+
})().then(({ privateKey, publicKey }) => {
111+
console.log("OK")
112+
$('#id_key').val(publicKey)
113+
console.log(privateKey, publicKey)
114+
}).catch(err => {
115+
console.log(err)
116+
})
117+
118+
119+
})
120+
121+
$('#submitButton').click(function() {
122+
$(this).closest('form').submit()
123+
})
124+
})
125+
</script>
126+
{% endblock %}

etsd/keys/templates/keys/publickey_list.html

+3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
<path d="M8 0a1 1 0 0 1 1 1v6h6a1 1 0 1 1 0 2H9v6a1 1 0 1 1-2 0V9H1a1 1 0 0 1 0-2h6V1a1 1 0 0 1 1-1z"/>
1010
</svg>
1111
</a>
12+
<a class='btn btn-warning' href='{% url "key_pair_create" %}'>
13+
New key pair
14+
</a>
1215
{% endblock %}
1316
{% block page_content %}
1417

etsd/keys/urls.py

+7
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@ def any_permission_required(*args):
3939
),
4040
name="public_key_create",
4141
),
42+
path(
43+
"new_key_pair/",
44+
any_permission_required("core.admin", "core.user")(
45+
views.KeyPairCreateView.as_view()
46+
),
47+
name="key_pair_create",
48+
),
4249
path(
4350
"detail/<int:pk>/",
4451
any_permission_required("core.admin", "core.user")(

etsd/keys/views.py

+37-13
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
from django.http.response import HttpResponseRedirect
22
from django.contrib import messages
33
from django.views.generic import ListView, DetailView, CreateView, FormView
4-
from django_tables2 import RequestConfig
54
from django.urls import reverse
65
from django.utils.translation import ugettext as _
76

7+
from django_tables2 import RequestConfig
88
from django_tables2.export.views import ExportMixin
99

1010
from . import models, tables, filters, forms
@@ -45,6 +45,26 @@ class PublicKeyDetailView(AdminOrAuthorityQsMixin, DetailView):
4545
model = models.PublicKey
4646

4747

48+
class KeyPairCreateView(CreateView):
49+
model = models.PublicKey
50+
form_class = forms.KeyPairCreateForm
51+
template_name = 'keys/key_pair_create.html'
52+
53+
def dispatch(self, request, *args, **kwargs):
54+
self.user_authority = self.request.user.get_authority()
55+
if not self.user_authority:
56+
messages.error(
57+
self.request,
58+
"Key pair creation is not allowed from users without an authority!",
59+
)
60+
return HttpResponseRedirect(reverse("home"))
61+
return super().dispatch(request, *args, **kwargs)
62+
63+
64+
def form_valid(self, form):
65+
a+=1
66+
67+
4868
class PublicKeyCreateView(CreateView):
4969
model = models.PublicKey
5070
form_class = forms.PublicKeyCreateForm
@@ -58,7 +78,7 @@ def form_valid(self, form):
5878
)
5979
return HttpResponseRedirect(reverse("home"))
6080
form.instance.authority = user_authority
61-
obj = form.save()
81+
_obj = form.save()
6282

6383
messages.add_message(
6484
self.request,
@@ -72,18 +92,22 @@ def form_valid(self, form):
7292

7393
class LoadPrivateKey(FormView):
7494
form_class = forms.LoadPrivateKeyForm
75-
template_name = 'keys/load_private_key.html'
95+
template_name = "keys/load_private_key.html"
7696

7797
def form_valid(self, form):
78-
fingerprint = form.cleaned_data['fingerprint']
79-
user_id = form.cleaned_data['user_id']
80-
self.request.session['private_key_data'] = {
81-
'fingerprint': fingerprint,
82-
'user_id': user_id,
98+
fingerprint = form.cleaned_data["fingerprint"]
99+
user_id = form.cleaned_data["user_id"]
100+
self.request.session["private_key_data"] = {
101+
"fingerprint": fingerprint,
102+
"user_id": user_id,
83103
}
84-
messages.add_message(self.request, messages.SUCCESS, _(
85-
"Private Key has been loaded. User id: {0}, fingerprint {1}".format(
86-
user_id, fingerprint
87-
)
88-
))
104+
messages.add_message(
105+
self.request,
106+
messages.SUCCESS,
107+
_(
108+
"Private Key has been loaded. User id: {0}, fingerprint {1}".format(
109+
user_id, fingerprint
110+
)
111+
),
112+
)
89113
return HttpResponseRedirect(reverse("home"))

pylintrc

+7-3
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ limit-inference-results=100
4545

4646
# List of plugins (as comma separated values of python module names) to load,
4747
# usually to register additional checkers.
48-
load-plugins=
48+
load-plugins=pylint_django
4949

5050
# Pickle collected data for later comparisons.
5151
persistent=yes
@@ -155,7 +155,11 @@ disable=print-statement,
155155
comprehension-escape,
156156
missing-class-docstring,
157157
missing-function-docstring,
158-
missing-module-docstring
158+
missing-module-docstring,
159+
too-few-public-methods,
160+
too-many-ancestors,
161+
no-self-use,
162+
attribute-defined-outside-init
159163

160164
# Enable the message, report, category or checker with the given id(s). You can
161165
# either give multiple identifier separated by comma (,) or put this option
@@ -290,7 +294,7 @@ indent-after-paren=4
290294
indent-string=' '
291295

292296
# Maximum number of characters on a single line.
293-
max-line-length=100
297+
max-line-length=120
294298

295299
# Maximum number of lines in a module.
296300
max-module-lines=1000

templates/partials/_filter.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<div class="col-md-12">
66
<form class="row row-cols-md-auto g-3 align-items-center" method='GET' action=''>
77
{{ filter.form|crispy }}
8-
<div class="col-12 d-flex align-self-end">
8+
<div class="col mb-3 d-flex align-self-end">
99
<input class='btn btn-info' type='submit' value='Φίλτρο'>
1010
<a href='{{ request.path }}' class='btn btn-secondary'>Επαναφορά</a>
1111

0 commit comments

Comments
 (0)