wip: currently rewriting the project as a full stack application tangled.org/kacaii.dev/sigo
gleam

:recycle: no need to `bool.guard`

+47 -38
+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 - 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 - /// 󱐁 A form that decodes the `LogIn` value. 44 - fn login_form() -> form.Form(RequestBody) { 45 - form.new({ 46 - use registration <- form.field("matricula", { 47 - form.parse_string |> form.check_not_empty() 48 - }) 49 - 50 - use password <- form.field("senha", { 51 - form.parse_string |> form.check_not_empty() 52 - }) 53 - 54 - form.success(RequestBody(registration:, password:)) 55 - }) 56 - } 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 - Ok(data) -> 79 - user.uuid_cookie_name 80 - |> handle_login(request:, ctx:, data:, cookie_name: _) 62 + Ok(data) -> { 63 + let cookie = user.uuid_cookie_name 64 + handle_login(request, ctx, data, cookie) 65 + } 81 66 } 82 67 } 83 68 84 69 fn handle_login( 85 - request request: wisp.Request, 86 - ctx ctx: Context, 87 - data data: RequestBody, 88 - cookie_name cookie_name: String, 70 + request: wisp.Request, 71 + ctx: Context, 72 + data: RequestBody, 73 + name: String, 89 74 ) -> response.Response(wisp.Body) { 90 - case query_database(data:, ctx:) { 75 + case query_database(data, ctx) { 91 76 Error(err) -> handle_error(err) 92 - Ok(resp) -> { 93 - log_login(data) 94 - set_token(resp, request, cookie_name) 95 - } 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 - cookie_name name: String, 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 + /// 󱐁 A form that decodes the `LogIn` value. 99 + fn login_form() -> form.Form(RequestBody) { 100 + form.new({ 101 + use registration <- form.field("matricula", { 102 + form.parse_string |> form.check_not_empty() 103 + }) 104 + 105 + use password <- form.field("senha", { 106 + form.parse_string |> form.check_not_empty() 107 + }) 108 + 109 + RequestBody(registration:, password:) 110 + |> form.success 111 + }) 112 + } 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 - data data: RequestBody, 150 - ctx ctx: Context, 147 + data: RequestBody, 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 - use correct_password <- result.try( 163 - argus.verify(row.password_hash, data.password) 164 - |> result.replace_error(HashError), 165 - ) 166 - 167 - use <- bool.guard(correct_password == False, Error(InvalidPassword)) 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 + log_login(data) 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 - |> Ok 179 + } 180 + 181 + fn verify_correct_password( 182 + row: sql.QueryLoginTokenRow, 183 + data: RequestBody, 184 + ) -> Result(Nil, LoginError) { 185 + use correct_password <- result.try( 186 + argus.verify(row.password_hash, data.password) 187 + |> result.replace_error(HashError), 188 + ) 189 + 190 + case correct_password { 191 + False -> Error(InvalidPassword) 192 + True -> Ok(Nil) 193 + } 185 194 }