Password can now be recovered!
parent
66820d0dd4
commit
065bc7a716
|
@ -2,8 +2,9 @@
|
|||
-- | TODO: token validation.
|
||||
module App.AuthenticationInterface where
|
||||
|
||||
import Prelude (Unit, bind, discard, pure, ($), (<<<), (<>))
|
||||
import Prelude (Unit, bind, discard, pure, ($), (<<<), (<>), (>), (==))
|
||||
|
||||
import Data.Array as A
|
||||
import Data.ArrayBuffer.Types (ArrayBuffer)
|
||||
import Data.Maybe (Maybe(..))
|
||||
import Data.Tuple (Tuple(..))
|
||||
|
@ -21,6 +22,10 @@ import App.Email as Email
|
|||
import App.LogMessage
|
||||
import App.Messages.AuthenticationDaemon as AuthD
|
||||
|
||||
type Login = String
|
||||
type Password = String
|
||||
type PasswordRecoveryToken = String
|
||||
|
||||
-- | The component can inform the parent (`App.Container`) that the authentication is complete,
|
||||
-- | and share both the uid and token. The token is useful to authenticate the user to the
|
||||
-- | dnsmanager daemon.
|
||||
|
@ -30,8 +35,9 @@ import App.Messages.AuthenticationDaemon as AuthD
|
|||
-- | TODO: authentication is performed in `App.Container`.
|
||||
data Output
|
||||
= MessageToSend ArrayBuffer
|
||||
| AuthenticateToAuthd (Tuple String String) -- Login Password
|
||||
| AuthenticateToAuthd (Tuple Login Password)
|
||||
| Log LogMessage
|
||||
| PasswordRecovery Login PasswordRecoveryToken Password
|
||||
|
||||
-- | The component's parent provides received messages.
|
||||
-- |
|
||||
|
@ -53,19 +59,29 @@ data PasswordRecoveryInput
|
|||
= PASSR_INP_login String
|
||||
| PASSR_INP_email String
|
||||
|
||||
data NewPasswordInput
|
||||
= NEWPASS_INP_login String
|
||||
| NEWPASS_INP_token String
|
||||
| NEWPASS_INP_password String
|
||||
| NEWPASS_INP_confirmation String
|
||||
|
||||
data Action
|
||||
= HandleAuthenticationInput AuthenticationInput
|
||||
| HandlePasswordRecovery PasswordRecoveryInput
|
||||
| HandleNewPassword NewPasswordInput
|
||||
--
|
||||
| AuthenticationAttempt Event
|
||||
| PasswordRecoveryAttempt Event
|
||||
| NewPasswordAttempt Event
|
||||
|
||||
type StateAuthenticationForm = { login :: String, pass :: String }
|
||||
type StatePasswordRecoveryForm = { login :: String, email :: String }
|
||||
type StateNewPasswordForm = { login :: String, token :: String, password :: String, confirmation :: String }
|
||||
|
||||
type State =
|
||||
{ authenticationForm :: StateAuthenticationForm
|
||||
, passwordRecoveryForm :: StatePasswordRecoveryForm
|
||||
, newPasswordForm :: StateNewPasswordForm
|
||||
, wsUp :: Boolean
|
||||
}
|
||||
|
||||
|
@ -84,23 +100,24 @@ initialState :: Input -> State
|
|||
initialState _ =
|
||||
{ authenticationForm: { login: "", pass: "" }
|
||||
, passwordRecoveryForm: { login: "", email: "" }
|
||||
|
||||
, newPasswordForm: { login: "", token: "", password: "", confirmation: "" }
|
||||
, wsUp: true
|
||||
}
|
||||
|
||||
render :: forall m. State -> H.ComponentHTML Action () m
|
||||
render { wsUp, authenticationForm, passwordRecoveryForm}
|
||||
= Bulma.section_small
|
||||
[ case wsUp of
|
||||
false -> Bulma.p "You are disconnected."
|
||||
true -> Bulma.columns_ [ b auth_form, b passrecovery_form ]
|
||||
]
|
||||
render { wsUp, authenticationForm, passwordRecoveryForm, newPasswordForm} =
|
||||
Bulma.section_small
|
||||
[ case wsUp of
|
||||
false -> Bulma.p "You are disconnected."
|
||||
true -> Bulma.columns_ [ b auth_form, b passrecovery_form, b newpass_form ]
|
||||
]
|
||||
|
||||
where
|
||||
b e = Bulma.column_ [ Bulma.box e ]
|
||||
|
||||
auth_form = [ Bulma.h3 "Authentication" , render_auth_form ]
|
||||
passrecovery_form = [ Bulma.h3 "Password Recovery", render_password_recovery_form ]
|
||||
newpass_form = [ Bulma.h3 "New password", render_new_password_form ]
|
||||
|
||||
should_be_disabled = (if wsUp then (HP.enabled true) else (HP.disabled true))
|
||||
|
||||
|
@ -140,6 +157,33 @@ render { wsUp, authenticationForm, passwordRecoveryForm}
|
|||
[ HH.text "Send Message to Server" ]
|
||||
]
|
||||
|
||||
render_new_password_form = HH.form
|
||||
[ HE.onSubmit NewPasswordAttempt ]
|
||||
[ Bulma.box_input "loginNEWPASS" "Login" "login"
|
||||
(HandleNewPassword <<< NEWPASS_INP_login)
|
||||
newPasswordForm.login
|
||||
should_be_disabled
|
||||
, Bulma.box_input "tokenNEWPASS" "Token" "token"
|
||||
(HandleNewPassword <<< NEWPASS_INP_token)
|
||||
newPasswordForm.token
|
||||
should_be_disabled
|
||||
, Bulma.box_input "passwordNEWPASS" "Password" "password"
|
||||
(HandleNewPassword <<< NEWPASS_INP_password)
|
||||
newPasswordForm.password
|
||||
should_be_disabled
|
||||
, Bulma.box_input "confirmationNEWPASS" "Confirmation" "confirmation"
|
||||
(HandleNewPassword <<< NEWPASS_INP_confirmation)
|
||||
newPasswordForm.confirmation
|
||||
should_be_disabled
|
||||
, HH.button
|
||||
[ HP.style "padding: 0.5rem 1.25rem;"
|
||||
, HP.type_ HP.ButtonSubmit
|
||||
, (if wsUp then (HP.enabled true) else (HP.disabled true))
|
||||
]
|
||||
[ HH.text "Send Message to Server" ]
|
||||
]
|
||||
|
||||
|
||||
handleAction :: forall m. MonadAff m => Action -> H.HalogenM State Action () Output m Unit
|
||||
handleAction = case _ of
|
||||
HandleAuthenticationInput authinp -> do
|
||||
|
@ -147,11 +191,18 @@ handleAction = case _ of
|
|||
AUTH_INP_login v -> H.modify_ _ { authenticationForm { login = v } }
|
||||
AUTH_INP_pass v -> H.modify_ _ { authenticationForm { pass = v } }
|
||||
|
||||
HandlePasswordRecovery authinp -> do
|
||||
case authinp of
|
||||
HandlePasswordRecovery passrecovinp -> do
|
||||
case passrecovinp of
|
||||
PASSR_INP_login v -> H.modify_ _ { passwordRecoveryForm { login = v } }
|
||||
PASSR_INP_email v -> H.modify_ _ { passwordRecoveryForm { email = v } }
|
||||
|
||||
HandleNewPassword newpassinp -> do
|
||||
case newpassinp of
|
||||
NEWPASS_INP_login v -> H.modify_ _ { newPasswordForm { login = v } }
|
||||
NEWPASS_INP_token v -> H.modify_ _ { newPasswordForm { token = v } }
|
||||
NEWPASS_INP_password v -> H.modify_ _ { newPasswordForm { password = v } }
|
||||
NEWPASS_INP_confirmation v -> H.modify_ _ { newPasswordForm { confirmation = v } }
|
||||
|
||||
AuthenticationAttempt ev -> do
|
||||
H.liftEffect $ Event.preventDefault ev
|
||||
|
||||
|
@ -168,6 +219,19 @@ handleAction = case _ of
|
|||
H.raise $ AuthenticateToAuthd (Tuple login pass)
|
||||
H.raise $ Log $ SystemLog $ "authenticate (login: " <> login <> ")"
|
||||
|
||||
-- TODO: verify the login?
|
||||
NewPasswordAttempt ev -> do
|
||||
H.liftEffect $ Event.preventDefault ev
|
||||
|
||||
{ newPasswordForm } <- H.get
|
||||
let { login, token, password, confirmation} = newPasswordForm
|
||||
|
||||
if A.any (_ == "") [ login, token, password, confirmation ]
|
||||
then H.raise $ Log $ ErrorLog "All entries are required!"
|
||||
else if password == confirmation
|
||||
then H.raise $ PasswordRecovery login token password
|
||||
else H.raise $ Log $ UnableToSend "Confirmation differs from password!"
|
||||
|
||||
PasswordRecoveryAttempt ev -> do
|
||||
H.liftEffect $ Event.preventDefault ev
|
||||
|
||||
|
|
|
@ -341,6 +341,13 @@ handleAction = case _ of
|
|||
|
||||
AuthenticationInterfaceEvent ev -> case ev of
|
||||
AI.MessageToSend message -> H.tell _ws_auth unit (WS.ToSend message)
|
||||
AI.PasswordRecovery login token pass -> do
|
||||
message <- H.liftEffect $ AuthD.serialize $ AuthD.MkPasswordRecovery
|
||||
{ user: login
|
||||
, password_renew_key: token
|
||||
, new_password: pass }
|
||||
H.tell _ws_auth unit (WS.ToSend message)
|
||||
|
||||
AI.AuthenticateToAuthd v -> handleAction $ AuthenticateToAuthd (Right v)
|
||||
AI.Log message -> H.tell _log unit (AppLog.Log message)
|
||||
|
||||
|
@ -353,7 +360,16 @@ handleAction = case _ of
|
|||
MVI.Log message -> H.tell _log unit (AppLog.Log message)
|
||||
|
||||
SetupInterfaceEvent ev -> case ev of
|
||||
SetupInterface.ChangePassword pass -> handleAction $ Log $ ErrorLog "TODO: change password"
|
||||
SetupInterface.DeleteUserAccount -> do
|
||||
handleAction $ Log $ ErrorLog "TODO: delete the user account"
|
||||
SetupInterface.ChangePassword pass -> do
|
||||
message <- H.liftEffect $ AuthD.serialize $ AuthD.MkModUser { user: Nothing
|
||||
, admin: Nothing
|
||||
, password: Just pass
|
||||
, email: Nothing
|
||||
}
|
||||
|
||||
H.tell _ws_auth unit (WS.ToSend message)
|
||||
SetupInterface.Log message -> H.tell _log unit (AppLog.Log message)
|
||||
|
||||
AdministrationEvent ev -> case ev of
|
||||
|
@ -432,8 +448,8 @@ handleAction = case _ of
|
|||
"""
|
||||
handleAction $ Routing MailValidation
|
||||
_ -> handleAction $ DispatchAuthDaemonMessage m
|
||||
(AuthD.GotUserEdited _) -> do
|
||||
handleAction $ Log $ ErrorLog "TODO: received a GotUserEdited message."
|
||||
(AuthD.GotUserEdited u) -> do
|
||||
handleAction $ Log $ SuccessLog $ "User (" <> show u.uid <> ") was modified!"
|
||||
(AuthD.GotUserValidated _) -> do
|
||||
handleAction $ Log $ SuccessLog "User got validated! You can now log in!"
|
||||
handleAction $ Routing Authentication
|
||||
|
@ -444,7 +460,7 @@ handleAction = case _ of
|
|||
(AuthD.GotPermissionSet _) -> do
|
||||
handleAction $ Log $ ErrorLog "Received a GotPermissionSet message."
|
||||
(AuthD.GotPasswordRecovered _) -> do
|
||||
handleAction $ Log $ ErrorLog "TODO: received a GotPasswordRecovered message."
|
||||
handleAction $ Log $ SuccessLog "your new password is now valid!"
|
||||
m@(AuthD.GotMatchingUsers _) -> do
|
||||
{ current_page } <- H.get
|
||||
case current_page of
|
||||
|
|
|
@ -24,6 +24,7 @@ import App.Messages.AuthenticationDaemon as AuthD
|
|||
data Output
|
||||
= Log LogMessage
|
||||
| ChangePassword String
|
||||
| DeleteUserAccount
|
||||
|
||||
-- | The component's parent provides received messages.
|
||||
-- |
|
||||
|
@ -48,13 +49,21 @@ data NewPasswordInput
|
|||
data Action
|
||||
= HandleNewPassword NewPasswordInput
|
||||
| ChangePasswordAttempt Event
|
||||
| CancelModal
|
||||
| DeleteAccountPopup
|
||||
| DeleteAccount
|
||||
|
||||
type StateNewPasswordForm = { password :: String, confirmation :: String }
|
||||
|
||||
data Modal
|
||||
= NoModal
|
||||
| DeleteAccountModal
|
||||
|
||||
type State =
|
||||
{ newPasswordForm :: StateNewPasswordForm
|
||||
, token :: String
|
||||
, wsUp :: Boolean
|
||||
, modal :: Modal
|
||||
}
|
||||
|
||||
component :: forall m. MonadAff m => H.Component Query Input Output m
|
||||
|
@ -72,15 +81,23 @@ initialState :: Input -> State
|
|||
initialState token =
|
||||
{ newPasswordForm: { password: "", confirmation: "" }
|
||||
, token
|
||||
, modal: NoModal
|
||||
, wsUp: true
|
||||
}
|
||||
|
||||
render :: forall m. State -> H.ComponentHTML Action () m
|
||||
render { wsUp, newPasswordForm }
|
||||
= render_new_password_form
|
||||
render { modal, wsUp, newPasswordForm } =
|
||||
case modal of
|
||||
DeleteAccountModal -> render_delete_account_modal
|
||||
NoModal -> Bulma.columns_ [ b [ Bulma.h3 "Change password", render_new_password_form ]
|
||||
, b [ Bulma.h3 "Delete account", render_delete_account ]
|
||||
]
|
||||
|
||||
where
|
||||
b e = Bulma.column_ [ Bulma.box e ]
|
||||
should_be_disabled = (if wsUp then (HP.enabled true) else (HP.disabled true))
|
||||
|
||||
render_delete_account = Bulma.alert_btn "Delete my account" DeleteAccountPopup
|
||||
render_new_password_form = HH.form
|
||||
[ HE.onSubmit ChangePasswordAttempt ]
|
||||
[ Bulma.box_input "passwordNEWPASS" "Password" "password"
|
||||
|
@ -99,6 +116,15 @@ render { wsUp, newPasswordForm }
|
|||
[ HH.text "Send Message to Server" ]
|
||||
]
|
||||
|
||||
render_delete_account_modal = Bulma.modal "Delete your account"
|
||||
-- TODO: body content
|
||||
[ Bulma.p "Wait, this is serious"
|
||||
, Bulma.strong "⚠ You won't be able to recover."
|
||||
]
|
||||
[ Bulma.alert_btn "GO AHEAD LOL" DeleteAccount
|
||||
, Bulma.cancel_button CancelModal
|
||||
]
|
||||
|
||||
handleAction :: forall m. MonadAff m => Action -> H.HalogenM State Action () Output m Unit
|
||||
handleAction = case _ of
|
||||
HandleNewPassword authinp -> do
|
||||
|
@ -106,6 +132,14 @@ handleAction = case _ of
|
|||
NEWPASS_INP_password v -> H.modify_ _ { newPasswordForm { password = v } }
|
||||
NEWPASS_INP_confirmation v -> H.modify_ _ { newPasswordForm { confirmation = v } }
|
||||
|
||||
CancelModal -> do
|
||||
H.modify_ _ { modal = NoModal }
|
||||
DeleteAccountPopup -> do
|
||||
H.modify_ _ { modal = DeleteAccountModal }
|
||||
DeleteAccount -> do
|
||||
H.raise $ DeleteUserAccount
|
||||
handleAction $ CancelModal
|
||||
|
||||
ChangePasswordAttempt ev -> do
|
||||
H.liftEffect $ Event.preventDefault ev
|
||||
|
||||
|
|
|
@ -418,6 +418,10 @@ input_with_side_text id title placeholder action value sidetext
|
|||
]
|
||||
]
|
||||
|
||||
-- | `modal`: create a modal by providing a few things:
|
||||
-- | - a title (a simple String)
|
||||
-- | - a body (`HTML` content)
|
||||
-- | - a footer (`HTML` content)
|
||||
modal :: forall w i. String -> Array (HH.HTML w i) -> Array (HH.HTML w i) -> HH.HTML w i
|
||||
modal title body foot =
|
||||
modal_
|
||||
|
|
Loading…
Reference in New Issue