Skip to content

Commit 286ea11

Browse files
committed
add api key management UI in profile view
Signed-off-by: tdruez <tdruez@aboutcode.org>
1 parent 50c95e0 commit 286ea11

4 files changed

Lines changed: 83 additions & 21 deletions

File tree

scanpipe/templates/account/profile.html

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,18 @@
1313
</ul>
1414
</nav>
1515
</div>
16-
1716
<div class="columns">
1817
<div class="column is-7">
1918
<div class="content">
20-
<p>
21-
Your personal API key provides access to the <a href="{% url 'project-list' %}" target="_blank">REST API</a>.<br>
22-
<strong>Treat it like a password and keep it secure.</strong>
23-
</p>
19+
<div class="mb-2">
20+
Your personal API key provides access to the
21+
<a href="{% url 'project-list' %}" target="_blank">REST API</a>
22+
<div class="has-text-weight-semibold">
23+
Treat it like a password and keep it secure.
24+
</div>
25+
</div>
2426
{% if request.user.api_token %}
25-
<div class="notification is-info is-light mb-4">
27+
<div class="notification is-grey mb-4">
2628
Your API key <strong>{{ request.user.api_token.prefix }}...</strong>
2729
was generated on {{ request.user.api_token.created }}<br>
2830
For security reasons, the full key is only shown once at generation time.<br>
@@ -36,26 +38,21 @@
3638
{% endif %}
3739
</div>
3840
<div class="buttons">
39-
<button type="button" class="button is-link is-outlined modal-button">
41+
<button type="button" class="button is-success is-outlined modal-button" data-target="modal-generate-api-key" aria-haspopup="true">
4042
Generate API key
4143
</button>
4244
{% if request.user.api_token %}
43-
<button type="button" class="button is-danger is-outlined modal-button">
45+
<button type="button" class="button is-danger is-outlined modal-button" data-target="modal-revoke-api-key" aria-haspopup="true">
4446
Revoke API key
4547
</button>
4648
{% endif %}
4749
</div>
4850
</div>
4951
</div>
50-
51-
<form action="{% url 'generate_api_key' %}" id="generate-api-key-form" method="post">{% csrf_token %}
52-
<button type="submit" class="btn btn-success">Generate</button>
53-
</form>
54-
55-
<form action="{% url 'revoke_api_key' %}" id="revoke-api-key-form" method="post">{% csrf_token %}
56-
<button type="submit" class="btn btn-danger">Revoke API key</button>
57-
</form>
58-
5952
</section>
53+
{% include 'scanpipe/modals/profile_generate_api_key_modal.html' %}
54+
{% if request.user.api_token %}
55+
{% include 'scanpipe/modals/profile_revoke_api_key_modal.html' %}
56+
{% endif %}
6057
</div>
6158
{% endblock %}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<div class="modal" id="modal-generate-api-key">
2+
<div class="modal-background"></div>
3+
<div class="modal-card">
4+
<header class="modal-card-head">
5+
<p class="modal-card-title">Generate API key</p>
6+
<button class="delete" aria-label="close"></button>
7+
</header>
8+
<form action="{% url 'generate_api_key' %}" method="post">{% csrf_token %}
9+
<section class="modal-card-body">
10+
<p class="mb-2">
11+
You are about to generate your API key.
12+
</p>
13+
{% if request.user.api_token %}
14+
<div class="notification is-danger has-text-weight-semibold">
15+
Your existing key will be immediately revoked.
16+
</div>
17+
{% endif %}
18+
</section>
19+
<footer class="modal-card-foot is-justify-content-flex-end">
20+
<div class="buttons">
21+
<button class="button has-text-weight-semibold" type="reset">No, Cancel</button>
22+
<button class="button is-success is-no-close" type="submit">Generate API key</button>
23+
</div>
24+
</footer>
25+
</form>
26+
</div>
27+
</div>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<div class="modal" id="modal-revoke-api-key">
2+
<div class="modal-background"></div>
3+
<div class="modal-card">
4+
<header class="modal-card-head">
5+
<p class="modal-card-title">Revoke API key</p>
6+
<button class="delete" aria-label="close"></button>
7+
</header>
8+
<form action="{% url 'revoke_api_key' %}" method="post">{% csrf_token %}
9+
<section class="modal-card-body">
10+
<p class="mb-2">
11+
Are you sure you want to delete your API key?
12+
</p>
13+
{% if request.user.api_token %}
14+
<div class="notification is-danger has-text-weight-semibold">
15+
Your existing key will be immediately revoked.
16+
</div>
17+
{% endif %}
18+
</section>
19+
<footer class="modal-card-foot is-justify-content-flex-end">
20+
<div class="buttons">
21+
<button class="button has-text-weight-semibold" type="reset">No, Cancel</button>
22+
<button class="button is-danger is-no-close" type="submit">Revoke API key</button>
23+
</div>
24+
</footer>
25+
</form>
26+
</div>
27+
</div>

scanpipe/tests/test_auth.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636

3737
TEST_PASSWORD = str(uuid.uuid4())
3838

39+
APIToken = apps.get_model("scanpipe", "APIToken")
3940
login_url = reverse("login")
4041
project_list_url = reverse("project_list")
4142
logout_url = reverse("logout")
@@ -112,12 +113,22 @@ def test_scancodeio_auth_logout_view(self):
112113

113114
def test_scancodeio_account_profile_view(self):
114115
self.client.login(username=self.basic_user.username, password=TEST_PASSWORD)
115-
APIToken = apps.get_model("scanpipe", "APIToken")
116+
117+
expected1 = "No API key created."
118+
expected2 = "Generate API key"
119+
expected3 = "Revoke API key"
120+
121+
response = self.client.get(profile_url)
122+
self.assertContains(response, expected1)
123+
self.assertContains(response, expected2)
124+
self.assertNotContains(response, expected3)
125+
116126
APIToken.create_token(user=self.basic_user)
117127
response = self.client.get(profile_url)
118-
expected = '<label class="label">API Key</label>'
119-
self.assertContains(response, expected, html=True)
120-
# self.assertContains(response, self.basic_user.auth.key)
128+
self.assertNotContains(response, expected1)
129+
self.assertContains(response, expected2)
130+
self.assertContains(response, expected3)
131+
self.assertContains(response, self.basic_user.api_token.prefix)
121132

122133
def test_scancodeio_auth_views_are_protected(self):
123134
a_uuid = uuid.uuid4()

0 commit comments

Comments
 (0)