tangled
alpha
login
or
join now
kacaii.dev
/
senac-brigade-server
0
fork
atom
wip: currently rewriting the project as a full stack application
tangled.org/kacaii.dev/sigo
gleam
0
fork
atom
overview
issues
1
pulls
pipelines
:recycle: no need to `bool.guard`
kacaii.dev
3 months ago
12025d17
13d798e0
+47
-38
1 changed file
expand all
collapse all
unified
split
src
app
domain
user
login.gleam
+47
-38
src/app/domain/user/login.gleam
···
9
9
import app/web/context.{type Context}
10
10
import argus
11
11
import formal/form
12
12
-
import gleam/bool
13
12
import gleam/float
14
13
import gleam/http/response
15
14
import gleam/json
···
40
39
LoginToken(body: String, user_id: uuid.Uuid)
41
40
}
42
41
43
43
-
/// A form that decodes the `LogIn` value.
44
44
-
fn login_form() -> form.Form(RequestBody) {
45
45
-
form.new({
46
46
-
use registration <- form.field("matricula", {
47
47
-
form.parse_string |> form.check_not_empty()
48
48
-
})
49
49
-
50
50
-
use password <- form.field("senha", {
51
51
-
form.parse_string |> form.check_not_empty()
52
52
-
})
53
53
-
54
54
-
form.success(RequestBody(registration:, password:))
55
55
-
})
56
56
-
}
57
57
-
58
42
/// Handles user login authentication and session management
59
43
/// On success, sets a cookie on the client containing the User UUID,
60
44
/// It will then be used on later requests for authetication.
···
75
59
76
60
case form_result {
77
61
Error(_) -> wisp.unprocessable_content()
78
78
-
Ok(data) ->
79
79
-
user.uuid_cookie_name
80
80
-
|> handle_login(request:, ctx:, data:, cookie_name: _)
62
62
+
Ok(data) -> {
63
63
+
let cookie = user.uuid_cookie_name
64
64
+
handle_login(request, ctx, data, cookie)
65
65
+
}
81
66
}
82
67
}
83
68
84
69
fn handle_login(
85
85
-
request request: wisp.Request,
86
86
-
ctx ctx: Context,
87
87
-
data data: RequestBody,
88
88
-
cookie_name cookie_name: String,
70
70
+
request: wisp.Request,
71
71
+
ctx: Context,
72
72
+
data: RequestBody,
73
73
+
name: String,
89
74
) -> response.Response(wisp.Body) {
90
90
-
case query_database(data:, ctx:) {
75
75
+
case query_database(data, ctx) {
91
76
Error(err) -> handle_error(err)
92
92
-
Ok(resp) -> {
93
93
-
log_login(data)
94
94
-
set_token(resp, request, cookie_name)
95
95
-
}
77
77
+
Ok(resp) -> set_token(resp, request, name)
96
78
}
97
79
}
98
80
99
81
fn set_token(
100
82
resp: LoginToken,
101
83
request: wisp.Request,
102
102
-
cookie_name name: String,
84
84
+
name: String,
103
85
) -> wisp.Response {
104
86
let response = wisp.json_response(resp.body, 200)
105
87
let value = uuid.to_string(resp.user_id)
···
113
95
wisp.set_cookie(response:, request:, name:, value:, security:, max_age:)
114
96
}
115
97
98
98
+
/// A form that decodes the `LogIn` value.
99
99
+
fn login_form() -> form.Form(RequestBody) {
100
100
+
form.new({
101
101
+
use registration <- form.field("matricula", {
102
102
+
form.parse_string |> form.check_not_empty()
103
103
+
})
104
104
+
105
105
+
use password <- form.field("senha", {
106
106
+
form.parse_string |> form.check_not_empty()
107
107
+
})
108
108
+
109
109
+
RequestBody(registration:, password:)
110
110
+
|> form.success
111
111
+
})
112
112
+
}
113
113
+
116
114
fn handle_error(err: LoginError) -> response.Response(wisp.Body) {
117
115
case err {
118
116
InvalidPassword ->
···
146
144
/// Check if the provided password matches the one inside our database
147
145
/// Returns the user's UUID if successfull.
148
146
fn query_database(
149
149
-
data data: RequestBody,
150
150
-
ctx ctx: Context,
147
147
+
data: RequestBody,
148
148
+
ctx: Context,
151
149
) -> Result(LoginToken, LoginError) {
152
150
use returned <- result.try(
153
151
sql.query_login_token(ctx.db, data.registration)
···
159
157
|> result.replace_error(UserNotFound),
160
158
)
161
159
162
162
-
use correct_password <- result.try(
163
163
-
argus.verify(row.password_hash, data.password)
164
164
-
|> result.replace_error(HashError),
165
165
-
)
166
166
-
167
167
-
use <- bool.guard(correct_password == False, Error(InvalidPassword))
160
160
+
use _ <- result.map(verify_correct_password(row, data))
168
161
169
162
let user_role = case row.user_role {
170
163
sql.Admin -> role.Admin
···
175
168
sql.Sargeant -> role.Sargeant
176
169
}
177
170
171
171
+
log_login(data)
172
172
+
178
173
json.object([
179
174
#("id", json.string(uuid.to_string(row.id))),
180
175
#("role", json.string(role.to_string_pt_br(user_role))),
181
176
])
182
177
|> json.to_string
183
178
|> LoginToken(body: _, user_id: row.id)
184
184
-
|> Ok
179
179
+
}
180
180
+
181
181
+
fn verify_correct_password(
182
182
+
row: sql.QueryLoginTokenRow,
183
183
+
data: RequestBody,
184
184
+
) -> Result(Nil, LoginError) {
185
185
+
use correct_password <- result.try(
186
186
+
argus.verify(row.password_hash, data.password)
187
187
+
|> result.replace_error(HashError),
188
188
+
)
189
189
+
190
190
+
case correct_password {
191
191
+
False -> Error(InvalidPassword)
192
192
+
True -> Ok(Nil)
193
193
+
}
185
194
}