Skip to content

Commit 3c7a72f

Browse files
committed
Implement users approval flow, add mailer
1 parent 287697a commit 3c7a72f

File tree

11 files changed

+103
-9
lines changed

11 files changed

+103
-9
lines changed

fe/components/src/components/UserManage.ce.vue

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,11 @@
2222
{{ roleLabel }}
2323
</div>
2424
</div>
25-
<div class="ctl">
26-
<confirm-button v-if="editable" @tap="deleteAction">
25+
<div :class="`ctl ${pending ? 'large' : ''}`">
26+
<confirm-button class="approve" v-if="editable && pending" @tap="approveAction">
27+
Approve
28+
</confirm-button>
29+
<confirm-button class="delete" v-if="editable" @tap="deleteAction">
2730
Delete
2831
</confirm-button>
2932
<div v-else class="label">
@@ -54,9 +57,16 @@ export default {
5457
edit_url: {
5558
type: String
5659
},
60+
approve_url: {
61+
type: String
62+
},
5763
editable: {
5864
type: Boolean,
5965
default: false
66+
},
67+
pending: {
68+
type: Boolean,
69+
default: false
6070
}
6171
},
6272
data() {
@@ -81,6 +91,9 @@ export default {
8191
}
8292
},
8393
methods: {
94+
approveAction() {
95+
window.location.href = this.approve_url
96+
},
8497
editAction() {
8598
window.location.href = `${this.edit_url}?role=${this.selectedRole}`
8699
},
@@ -116,11 +129,20 @@ export default {
116129
.element .ctl {
117130
@apply w-32 flex items-center justify-end;
118131
}
132+
.element .ctl.large {
133+
@apply w-64;
134+
}
119135
.element .ctl .label {
120136
@apply py-1 text-gray-400;
121137
}
122138
.element .ctl:deep() .btn {
123-
@apply py-1 px-4 flex items-center cursor-pointer border border-solid border-pink-500 text-pink-500 dark:border-pink-400 dark:text-pink-400 rounded focus:outline-none;
139+
@apply ml-4 py-1 px-4 flex items-center cursor-pointer border border-solid rounded focus:outline-none;
140+
}
141+
.element .ctl .approve:deep() .btn {
142+
@apply border-indigo-500 text-indigo-500 dark:border-indigo-300 dark:text-indigo-300;
143+
}
144+
.element .ctl .delete:deep() .btn {
145+
@apply border-pink-500 text-pink-500 dark:border-pink-400 dark:text-pink-400;
124146
}
125147
.element .ctl:deep() .btn.confirm {
126148
@apply bg-pink-200 dark:bg-pink-600 dark:text-gray-50;

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "tfstater"
3-
version = "0.1.2"
3+
version = "0.2.0"
44
description = "An HTTP Terraform state backend with locking support"
55
authors = ["Giovanni Barillari <[email protected]>"]
66
license = "BSD-3-Clause"

tfstater/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from emmett import App
22
from emmett.orm import Database
33
from emmett.sessions import SessionManager
4-
from emmett.tools.auth import Auth
4+
from emmett.tools import Auth, Mailer
55
from emmett_rest import REST
66

77
from .config import load_config
@@ -10,6 +10,8 @@
1010
app = App(__name__)
1111
load_config(app)
1212

13+
mailer = Mailer(app)
14+
1315
rest = app.use_extension(REST)
1416

1517
db = Database(app)

tfstater/commands.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@
88
@click.argument("password")
99
def cmd_create_maintainer(email, password):
1010
with db.connection():
11-
User.create(
11+
res = User.create(
1212
email=email,
1313
password=password,
14-
role=User.ROLES.maintainer
14+
role=User.ROLES.maintainer.value
1515
)
16+
if not res.id:
17+
return
18+
res.id.allow()
1619
db.commit()

tfstater/config.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,41 @@
11
from .idp import Providers
22

33

4+
class ConfigurationError(RuntimeError):
5+
def __init__(self, msg: str):
6+
super().__init__(f"Configuration error: {msg}")
7+
8+
49
def load_config(app):
510
app.config_from_yaml("app.yml")
611

12+
if any (not v for v in [app.config.auth.hmac_key, app.config.auth.cookies_key]):
13+
raise ConfigurationError(
14+
"auth.hmac_key and auth.cookies_key values required"
15+
)
16+
17+
if app.config.auth.allow_email_login and all(not v for v in [
18+
app.config.auth.registration_verification,
19+
app.config.auth.registration_approval
20+
]):
21+
raise ConfigurationError(
22+
"auth.allow_email_login requires auth.registration_verification "
23+
"or auth.registration_approval"
24+
)
25+
26+
if (
27+
app.config.auth.registration_verification and
28+
not app.config.auth.restrict_email_domain
29+
):
30+
raise ConfigurationError(
31+
"auth.registration_verification requires auth.restrict_email_domain"
32+
)
33+
34+
if app.config.auth.registration_verification and not app.config.smtp.server:
35+
raise ConfigurationError(
36+
"auth.registration_verification requires smtp.server"
37+
)
38+
739
auth_disabled_routes = ["profile", "download"]
840
if not app.config.auth.allow_email_login:
941
auth_disabled_routes.extend([
@@ -18,6 +50,7 @@ def load_config(app):
1850
app.config.auth.single_template = False
1951
app.config.db.adapter = "postgres"
2052
app.config.db.big_id_fields = True
53+
app.config.mailer = app.config.smtp
2154

2255
idp = {}
2356
for key in set(

tfstater/config/app.yml.example

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,17 @@ auth:
1010
cookies_key: anothersecret
1111
allow_email_login: true
1212
restrict_email_domain: "@my.tld"
13-
registration_verification: true
13+
registration_verification: false
14+
registration_approval: true
15+
16+
smtp:
17+
18+
server:
19+
port: 25
20+
username: tfstater
21+
password: ""
22+
use_tls: false
23+
use_ssl: false
1424

1525
object_storage:
1626
bucket: states

tfstater/templates/auth/login.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33

44
<div class="w-full font-semibold text-lg text-center mb-12">Sign in</div>
55

6+
{{ if message: }}
7+
<div class="message-wrapper">{{ =message }}</div>
8+
{{ pass }}
9+
610
{{ if auth_helpers.allow_email_login: }}
711
{{ =form.custom.begin }}
812
<div class="grid grid-flow-row grid-cols-4 gap-4 my-8 fields">

tfstater/templates/auth/registration.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33

44
<div class="w-full font-semibold text-lg text-center mb-12">Sign up</div>
55

6+
{{ if message: }}
7+
<div class="message-wrapper">{{ =message }}</div>
8+
{{ pass }}
9+
610
{{ if auth_helpers.allow_email_login: }}
711
{{ =form.custom.begin }}
812
<div class="grid grid-flow-row grid-cols-4 gap-4 my-8 fields">

tfstater/templates/settings.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,14 @@
4444
email="{{ =user.email }}"
4545
role="{{ =user.role }}"
4646
edit_url="{{ =url('views.actions.edit_user', user.id) }}"
47+
approve_url="{{ =url('views.actions.allow_user', user.id) }}"
4748
delete_url="{{ =url('views.actions.delete_user', user.id) }}"
4849
{{ if user.id != ctx.user.id: }}
4950
editable
5051
{{ pass }}
52+
{{ if user.registration_key: }}
53+
pending
54+
{{ pass }}
5155
></tfstater-user-manage>
5256
{{ pass }}
5357
</div>

tfstater/views/accounts.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ def _after_login(form):
3535
def _after_registration(form, user, logged_in):
3636
if logged_in:
3737
redirect(url("views.index"))
38-
redirect(url("account.login"))
3938

4039

4140
@auth_routes.after_email_verification

0 commit comments

Comments
 (0)