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

:label: properly map to a `Session` error type

+42 -50
+3 -3
src/app/domain/notification/get_notification_preferences.gleam
··· 38 38 39 39 fn handle_error(err: GetNotificationPreferencesError) -> wisp.Response { 40 40 case err { 41 - AccessControl(err) -> session.handle_error(err) 41 + Session(err) -> session.handle_error(err) 42 42 DatabaseError(err) -> web.handle_database_error(err) 43 43 } 44 44 } ··· 49 49 ) -> Result(String, GetNotificationPreferencesError) { 50 50 use token <- result.try( 51 51 session.extract(req) 52 - |> result.map_error(AccessControl), 52 + |> result.map_error(Session), 53 53 ) 54 54 55 55 use returned <- result.try( ··· 77 77 /// Querying the user notification preferences can fail 78 78 type GetNotificationPreferencesError { 79 79 /// Authentication failed 80 - AccessControl(session.SessionError) 80 + Session(session.SessionError) 81 81 /// An error occurred while querying the DataBase 82 82 DatabaseError(pog.QueryError) 83 83 }
+3 -3
src/app/domain/notification/update_notification_preferences.gleam
··· 40 40 41 41 type UpdateNotificationPreferencesError { 42 42 /// Authentication failed 43 - AccessControl(session.SessionError) 43 + Session(session.SessionError) 44 44 /// Failed to query Database 45 45 DataBase(pog.QueryError) 46 46 NotFound ··· 78 78 79 79 fn handle_error(err: UpdateNotificationPreferencesError) -> wisp.Response { 80 80 case err { 81 - AccessControl(err) -> session.handle_error(err) 81 + Session(err) -> session.handle_error(err) 82 82 DataBase(err) -> web.handle_database_error(err) 83 83 NotFound -> 84 84 "O banco de dados não retornou resultados após atualizar as preferências" ··· 97 97 ) { 98 98 use token <- result.try( 99 99 session.extract(req) 100 - |> result.map_error(AccessControl), 100 + |> result.map_error(Session), 101 101 ) 102 102 103 103 use acc, #(key, value) <- list.try_fold(
+3 -3
src/app/domain/occurrence/close_occurrence.gleam
··· 23 23 /// An error occurred whe naccessing the DataBase 24 24 DataBase(pog.QueryError) 25 25 /// Errors related to authentication / authorization 26 - AccessControl(session.SessionError) 26 + Session(session.SessionError) 27 27 } 28 28 29 29 /// 󰚰 Updates the `resolved_at` field of a occurrence ··· 55 55 ) -> Result(String, ResolveOccurrenceError) { 56 56 use _ <- result.try( 57 57 session.extract(req) 58 - |> result.map_error(AccessControl), 58 + |> result.map_error(Session), 59 59 ) 60 60 61 61 use target <- result.try( ··· 102 102 103 103 fn handle_error(err: ResolveOccurrenceError) -> wisp.Response { 104 104 case err { 105 - AccessControl(err) -> session.handle_error(err) 105 + Session(err) -> session.handle_error(err) 106 106 DataBase(err) -> web.handle_database_error(err) 107 107 InvalidUuid(id) -> 108 108 wisp.bad_request("Ocorrência possui Uuid inválido: " <> id)
+3 -3
src/app/domain/occurrence/delete_occurrence.gleam
··· 15 15 /// 󰿀 Occurrence has invalid Uuid 16 16 InvalidUuid(String) 17 17 ///  Authentication failed 18 - AccessControl(session.SessionError) 18 + Session(session.SessionError) 19 19 /// 󱙀 Failed to query the DataBase 20 20 DataBase(pog.QueryError) 21 21 /// 󱪘 Occurrence was not found in the system ··· 58 58 use _ <- result.try( 59 59 // Armazenamos o UUID do usuário caso precisemos para autorização 60 60 session.extract(req) 61 - |> result.map_error(AccessControl), 61 + |> result.map_error(Session), 62 62 ) 63 63 64 64 use returned <- result.try( ··· 81 81 InvalidUuid(uuid_string) -> 82 82 // 404 Bad Request 83 83 wisp.bad_request("UUID inválido: " <> uuid_string) 84 - AccessControl(err) -> session.handle_error(err) 84 + Session(err) -> session.handle_error(err) 85 85 DataBase(db_err) -> web.handle_database_error(db_err) 86 86 OccurrenceNotFound(occ_uuid) -> { 87 87 // 404 not found
+3 -3
src/app/domain/occurrence/register_new_occurrence.gleam
··· 86 86 87 87 type RegisterNewOccurrenceError { 88 88 /// Failed to authenticate the user 89 - AccessControl(session.SessionError) 89 + Session(session.SessionError) 90 90 /// Failed to access the database 91 91 DataBase(pog.QueryError) 92 92 /// Database returned no results ··· 97 97 98 98 fn handle_error(err: RegisterNewOccurrenceError) -> wisp.Response { 99 99 case err { 100 - AccessControl(err) -> session.handle_error(err) 100 + Session(err) -> session.handle_error(err) 101 101 DataBase(err) -> web.handle_database_error(err) 102 102 FailedToAssignBrigade(id) -> { 103 103 let body = "Não foi possível designar a equipe: " <> uuid.to_string(id) ··· 148 148 ) -> Result(String, RegisterNewOccurrenceError) { 149 149 use token <- result.try( 150 150 session.extract(request) 151 - |> result.map_error(AccessControl), 151 + |> result.map_error(Session), 152 152 ) 153 153 154 154 use returned <- result.try(
+3 -3
src/app/domain/occurrence/reopen_occurrence.gleam
··· 23 23 /// An error occurred whe naccessing the DataBase 24 24 DataBase(pog.QueryError) 25 25 /// Errors related to authentication / authorization 26 - AccessControl(session.SessionError) 26 + Session(session.SessionError) 27 27 } 28 28 29 29 /// 󰚰 Updates the `resolved_at` field of a occurrence ··· 55 55 ) -> Result(String, ResolveOccurrenceError) { 56 56 use _ <- result.try( 57 57 session.extract(req) 58 - |> result.map_error(AccessControl), 58 + |> result.map_error(Session), 59 59 ) 60 60 61 61 use target <- result.try( ··· 101 101 102 102 fn handle_error(err: ResolveOccurrenceError) -> wisp.Response { 103 103 case err { 104 - AccessControl(err) -> session.handle_error(err) 104 + Session(err) -> session.handle_error(err) 105 105 DataBase(err) -> web.handle_database_error(err) 106 106 InvalidUuid(id) -> 107 107 wisp.bad_request("Ocorrência possui Uuid inválido: " <> id)
+8 -19
src/app/domain/user.gleam
··· 11 11 import wisp 12 12 import youid/uuid 13 13 14 - pub const uuid_cookie_name = "USER_ID" 15 - 16 - ///  Errors related to user access control (authentication & authorization) 17 14 pub type AccessControlError { 18 15 /// 󰗹 Authentication failed 19 - Authentication(session.SessionError) 16 + Session(session.SessionError) 20 17 ///  User is authentication but lacks permissions 21 - AuthorizationError( 18 + NotAuthorized( 22 19 user_uuid: uuid.Uuid, 23 20 user_role: role.Role, 24 21 authorized_roles: List(role.Role), ··· 31 28 InvalidRole(String) 32 29 } 33 30 34 - ///  Authentication-specific failures 35 - pub type AuthenticationError { 36 - ///  Request is missing the authetication Cookie 37 - MissingCookie 38 - /// 󰘨 User doesn't have a valid UUID 39 - InvalidUUID(String) 40 - } 41 - 42 31 ///  Broadcast an arbitrary message to an user 43 32 /// 44 33 /// 󱓊 Spawns a new process ··· 62 51 ) -> Result(role.Role, AccessControlError) { 63 52 use token <- result.try( 64 53 session.extract(request) 65 - |> result.map_error(Authentication), 54 + |> result.map_error(Session), 66 55 ) 67 56 68 57 // 󰈞 Check if that role has authorization 69 58 list.find(authorized_roles, fn(authorized) { token.user_role == authorized }) 70 - |> result.replace_error(AuthorizationError( 59 + |> result.replace_error(NotAuthorized( 71 60 user_uuid: token.user_id, 72 61 user_role: token.user_role, 73 62 authorized_roles:, ··· 76 65 77 66 pub fn handle_access_control_error(err: AccessControlError) { 78 67 case err { 79 - Authentication(err) -> session.handle_error(err) 68 + Session(err) -> session.handle_error(err) 80 69 DataBase(err) -> web.handle_database_error(err) 81 70 RoleNotFound -> 82 - "Não foi possível confirmar o Cargo do usuário autenticado" 71 + "Não foi possível confirmar o cargo do usuário" 83 72 |> wisp.Text 84 73 |> wisp.set_body(wisp.response(401), _) 85 74 86 75 InvalidRole(str) -> 87 - wisp.Text("Usuário autenticado possui cargo inválido: " <> str) 76 + wisp.Text("Usuário autenticado possui cargo não reconhecido: " <> str) 88 77 |> wisp.set_body(wisp.response(401), _) 89 78 90 - AuthorizationError(user_uuid:, user_role:, authorized_roles:) -> { 79 + NotAuthorized(user_uuid:, user_role:, authorized_roles:) -> { 91 80 json.object([ 92 81 #("id", json.string(uuid.to_string(user_uuid))), 93 82 #("user_role", json.string(role.to_string_pt_br(user_role:))),
+16 -13
src/app/web/socket.gleam
··· 1 1 import app/domain/notification/sql as notif_sql 2 2 import app/domain/occurrence/category 3 - import app/domain/user 4 3 import app/domain/user/sql as user_sql 5 4 import app/web/context.{type Context} 5 + import app/web/session 6 6 import app/web/socket/envelope 7 7 import app/web/socket/message as msg 8 8 import gleam/bit_array ··· 40 40 InvalidSignature 41 41 /// 󱦃 The signed message could not be properly decrypted 42 42 InvalidUtf8 43 - /// 󰘨 Session token has invalid Uuid fomat 44 - InvalidUuid(String) 43 + /// 󰘨 Session token has invalid format 44 + InvalidToken(String) 45 45 /// 󰆼 Failed to access the DataBase 46 46 Database(pog.QueryError) 47 47 ///  Content-length header is missing ··· 56 56 pub fn handle_request(req: Request, ctx: Context) -> Response { 57 57 let registry = group_registry.get_registry(ctx.registry_name) 58 58 59 - case extract_uuid(req, ctx) { 59 + case extract_token(req, ctx) { 60 60 Error(err) -> handle_error(err) 61 - Ok(user_uuid) -> handle_connection(req, ctx, user_uuid, registry) 61 + Ok(token) -> handle_connection(req, ctx, token.user_id, registry) 62 62 } 63 63 } 64 64 ··· 288 288 } 289 289 } 290 290 291 - fn extract_uuid(req: Request, ctx: Context) -> Result(uuid.Uuid, WebSocketError) { 291 + fn extract_token( 292 + req: Request, 293 + ctx: Context, 294 + ) -> Result(session.Session, WebSocketError) { 292 295 let cookies = request.get_cookies(req) 293 296 let salt = <<ctx.secret_key_base:utf8>> 294 297 295 - use hashed_uuid <- result.try( 296 - list.key_find(cookies, user.uuid_cookie_name) 298 + use hashed_token <- result.try( 299 + list.key_find(cookies, session.cookie_name) 297 300 |> result.replace_error(MissingCookie), 298 301 ) 299 302 300 303 use decrypted <- result.try( 301 - crypto.verify_signed_message(hashed_uuid, salt) 304 + crypto.verify_signed_message(hashed_token, salt) 302 305 |> result.replace_error(InvalidSignature), 303 306 ) 304 307 305 - use maybe_uuid_str <- result.try( 308 + use session_str <- result.try( 306 309 bit_array.to_string(decrypted) 307 310 |> result.replace_error(InvalidUtf8), 308 311 ) 309 312 310 - uuid.from_string(maybe_uuid_str) 311 - |> result.replace_error(InvalidUuid(maybe_uuid_str)) 313 + json.parse(session_str, session.decoder()) 314 + |> result.replace_error(InvalidToken(session_str)) 312 315 } 313 316 314 317 // ON INIT --------------------------------------------------------------------- ··· 413 416 414 417 fn handle_error(err: WebSocketError) -> Response { 415 418 case err { 416 - InvalidUuid(id) -> { 419 + InvalidToken(id) -> { 417 420 let body = "Usuário possui Uuid inválido: " <> id 418 421 send_response(body, 401) 419 422 }