Password can now be recovered!
parent
66820d0dd4
commit
065bc7a716
|
@ -2,8 +2,9 @@
|
||||||
-- | TODO: token validation.
|
-- | TODO: token validation.
|
||||||
module App.AuthenticationInterface where
|
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.ArrayBuffer.Types (ArrayBuffer)
|
||||||
import Data.Maybe (Maybe(..))
|
import Data.Maybe (Maybe(..))
|
||||||
import Data.Tuple (Tuple(..))
|
import Data.Tuple (Tuple(..))
|
||||||
|
@ -21,6 +22,10 @@ import App.Email as Email
|
||||||
import App.LogMessage
|
import App.LogMessage
|
||||||
import App.Messages.AuthenticationDaemon as AuthD
|
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,
|
-- | 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
|
-- | and share both the uid and token. The token is useful to authenticate the user to the
|
||||||
-- | dnsmanager daemon.
|
-- | dnsmanager daemon.
|
||||||
|
@ -30,8 +35,9 @@ import App.Messages.AuthenticationDaemon as AuthD
|
||||||
-- | TODO: authentication is performed in `App.Container`.
|
-- | TODO: authentication is performed in `App.Container`.
|
||||||
data Output
|
data Output
|
||||||
= MessageToSend ArrayBuffer
|
= MessageToSend ArrayBuffer
|
||||||
| AuthenticateToAuthd (Tuple String String) -- Login Password
|
| AuthenticateToAuthd (Tuple Login Password)
|
||||||
| Log LogMessage
|
| Log LogMessage
|
||||||
|
| PasswordRecovery Login PasswordRecoveryToken Password
|
||||||
|
|
||||||
-- | The component's parent provides received messages.
|
-- | The component's parent provides received messages.
|
||||||
-- |
|
-- |
|
||||||
|
@ -53,19 +59,29 @@ data PasswordRecoveryInput
|
||||||
= PASSR_INP_login String
|
= PASSR_INP_login String
|
||||||
| PASSR_INP_email 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
|
data Action
|
||||||
= HandleAuthenticationInput AuthenticationInput
|
= HandleAuthenticationInput AuthenticationInput
|
||||||
| HandlePasswordRecovery PasswordRecoveryInput
|
| HandlePasswordRecovery PasswordRecoveryInput
|
||||||
|
| HandleNewPassword NewPasswordInput
|
||||||
--
|
--
|
||||||
| AuthenticationAttempt Event
|
| AuthenticationAttempt Event
|
||||||
| PasswordRecoveryAttempt Event
|
| PasswordRecoveryAttempt Event
|
||||||
|
| NewPasswordAttempt Event
|
||||||
|
|
||||||
type StateAuthenticationForm = { login :: String, pass :: String }
|
type StateAuthenticationForm = { login :: String, pass :: String }
|
||||||
type StatePasswordRecoveryForm = { login :: String, email :: String }
|
type StatePasswordRecoveryForm = { login :: String, email :: String }
|
||||||
|
type StateNewPasswordForm = { login :: String, token :: String, password :: String, confirmation :: String }
|
||||||
|
|
||||||
type State =
|
type State =
|
||||||
{ authenticationForm :: StateAuthenticationForm
|
{ authenticationForm :: StateAuthenticationForm
|
||||||
, passwordRecoveryForm :: StatePasswordRecoveryForm
|
, passwordRecoveryForm :: StatePasswordRecoveryForm
|
||||||
|
, newPasswordForm :: StateNewPasswordForm
|
||||||
, wsUp :: Boolean
|
, wsUp :: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,16 +100,16 @@ initialState :: Input -> State
|
||||||
initialState _ =
|
initialState _ =
|
||||||
{ authenticationForm: { login: "", pass: "" }
|
{ authenticationForm: { login: "", pass: "" }
|
||||||
, passwordRecoveryForm: { login: "", email: "" }
|
, passwordRecoveryForm: { login: "", email: "" }
|
||||||
|
, newPasswordForm: { login: "", token: "", password: "", confirmation: "" }
|
||||||
, wsUp: true
|
, wsUp: true
|
||||||
}
|
}
|
||||||
|
|
||||||
render :: forall m. State -> H.ComponentHTML Action () m
|
render :: forall m. State -> H.ComponentHTML Action () m
|
||||||
render { wsUp, authenticationForm, passwordRecoveryForm}
|
render { wsUp, authenticationForm, passwordRecoveryForm, newPasswordForm} =
|
||||||
= Bulma.section_small
|
Bulma.section_small
|
||||||
[ case wsUp of
|
[ case wsUp of
|
||||||
false -> Bulma.p "You are disconnected."
|
false -> Bulma.p "You are disconnected."
|
||||||
true -> Bulma.columns_ [ b auth_form, b passrecovery_form ]
|
true -> Bulma.columns_ [ b auth_form, b passrecovery_form, b newpass_form ]
|
||||||
]
|
]
|
||||||
|
|
||||||
where
|
where
|
||||||
|
@ -101,6 +117,7 @@ render { wsUp, authenticationForm, passwordRecoveryForm}
|
||||||
|
|
||||||
auth_form = [ Bulma.h3 "Authentication" , render_auth_form ]
|
auth_form = [ Bulma.h3 "Authentication" , render_auth_form ]
|
||||||
passrecovery_form = [ Bulma.h3 "Password Recovery", render_password_recovery_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))
|
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" ]
|
[ 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 :: forall m. MonadAff m => Action -> H.HalogenM State Action () Output m Unit
|
||||||
handleAction = case _ of
|
handleAction = case _ of
|
||||||
HandleAuthenticationInput authinp -> do
|
HandleAuthenticationInput authinp -> do
|
||||||
|
@ -147,11 +191,18 @@ handleAction = case _ of
|
||||||
AUTH_INP_login v -> H.modify_ _ { authenticationForm { login = v } }
|
AUTH_INP_login v -> H.modify_ _ { authenticationForm { login = v } }
|
||||||
AUTH_INP_pass v -> H.modify_ _ { authenticationForm { pass = v } }
|
AUTH_INP_pass v -> H.modify_ _ { authenticationForm { pass = v } }
|
||||||
|
|
||||||
HandlePasswordRecovery authinp -> do
|
HandlePasswordRecovery passrecovinp -> do
|
||||||
case authinp of
|
case passrecovinp of
|
||||||
PASSR_INP_login v -> H.modify_ _ { passwordRecoveryForm { login = v } }
|
PASSR_INP_login v -> H.modify_ _ { passwordRecoveryForm { login = v } }
|
||||||
PASSR_INP_email v -> H.modify_ _ { passwordRecoveryForm { email = 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
|
AuthenticationAttempt ev -> do
|
||||||
H.liftEffect $ Event.preventDefault ev
|
H.liftEffect $ Event.preventDefault ev
|
||||||
|
|
||||||
|
@ -168,6 +219,19 @@ handleAction = case _ of
|
||||||
H.raise $ AuthenticateToAuthd (Tuple login pass)
|
H.raise $ AuthenticateToAuthd (Tuple login pass)
|
||||||
H.raise $ Log $ SystemLog $ "authenticate (login: " <> login <> ")"
|
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
|
PasswordRecoveryAttempt ev -> do
|
||||||
H.liftEffect $ Event.preventDefault ev
|
H.liftEffect $ Event.preventDefault ev
|
||||||
|
|
||||||
|
|
|
@ -341,6 +341,13 @@ handleAction = case _ of
|
||||||
|
|
||||||
AuthenticationInterfaceEvent ev -> case ev of
|
AuthenticationInterfaceEvent ev -> case ev of
|
||||||
AI.MessageToSend message -> H.tell _ws_auth unit (WS.ToSend message)
|
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.AuthenticateToAuthd v -> handleAction $ AuthenticateToAuthd (Right v)
|
||||||
AI.Log message -> H.tell _log unit (AppLog.Log message)
|
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)
|
MVI.Log message -> H.tell _log unit (AppLog.Log message)
|
||||||
|
|
||||||
SetupInterfaceEvent ev -> case ev of
|
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)
|
SetupInterface.Log message -> H.tell _log unit (AppLog.Log message)
|
||||||
|
|
||||||
AdministrationEvent ev -> case ev of
|
AdministrationEvent ev -> case ev of
|
||||||
|
@ -432,8 +448,8 @@ handleAction = case _ of
|
||||||
"""
|
"""
|
||||||
handleAction $ Routing MailValidation
|
handleAction $ Routing MailValidation
|
||||||
_ -> handleAction $ DispatchAuthDaemonMessage m
|
_ -> handleAction $ DispatchAuthDaemonMessage m
|
||||||
(AuthD.GotUserEdited _) -> do
|
(AuthD.GotUserEdited u) -> do
|
||||||
handleAction $ Log $ ErrorLog "TODO: received a GotUserEdited message."
|
handleAction $ Log $ SuccessLog $ "User (" <> show u.uid <> ") was modified!"
|
||||||
(AuthD.GotUserValidated _) -> do
|
(AuthD.GotUserValidated _) -> do
|
||||||
handleAction $ Log $ SuccessLog "User got validated! You can now log in!"
|
handleAction $ Log $ SuccessLog "User got validated! You can now log in!"
|
||||||
handleAction $ Routing Authentication
|
handleAction $ Routing Authentication
|
||||||
|
@ -444,7 +460,7 @@ handleAction = case _ of
|
||||||
(AuthD.GotPermissionSet _) -> do
|
(AuthD.GotPermissionSet _) -> do
|
||||||
handleAction $ Log $ ErrorLog "Received a GotPermissionSet message."
|
handleAction $ Log $ ErrorLog "Received a GotPermissionSet message."
|
||||||
(AuthD.GotPasswordRecovered _) -> do
|
(AuthD.GotPasswordRecovered _) -> do
|
||||||
handleAction $ Log $ ErrorLog "TODO: received a GotPasswordRecovered message."
|
handleAction $ Log $ SuccessLog "your new password is now valid!"
|
||||||
m@(AuthD.GotMatchingUsers _) -> do
|
m@(AuthD.GotMatchingUsers _) -> do
|
||||||
{ current_page } <- H.get
|
{ current_page } <- H.get
|
||||||
case current_page of
|
case current_page of
|
||||||
|
|
|
@ -24,6 +24,7 @@ import App.Messages.AuthenticationDaemon as AuthD
|
||||||
data Output
|
data Output
|
||||||
= Log LogMessage
|
= Log LogMessage
|
||||||
| ChangePassword String
|
| ChangePassword String
|
||||||
|
| DeleteUserAccount
|
||||||
|
|
||||||
-- | The component's parent provides received messages.
|
-- | The component's parent provides received messages.
|
||||||
-- |
|
-- |
|
||||||
|
@ -48,13 +49,21 @@ data NewPasswordInput
|
||||||
data Action
|
data Action
|
||||||
= HandleNewPassword NewPasswordInput
|
= HandleNewPassword NewPasswordInput
|
||||||
| ChangePasswordAttempt Event
|
| ChangePasswordAttempt Event
|
||||||
|
| CancelModal
|
||||||
|
| DeleteAccountPopup
|
||||||
|
| DeleteAccount
|
||||||
|
|
||||||
type StateNewPasswordForm = { password :: String, confirmation :: String }
|
type StateNewPasswordForm = { password :: String, confirmation :: String }
|
||||||
|
|
||||||
|
data Modal
|
||||||
|
= NoModal
|
||||||
|
| DeleteAccountModal
|
||||||
|
|
||||||
type State =
|
type State =
|
||||||
{ newPasswordForm :: StateNewPasswordForm
|
{ newPasswordForm :: StateNewPasswordForm
|
||||||
, token :: String
|
, token :: String
|
||||||
, wsUp :: Boolean
|
, wsUp :: Boolean
|
||||||
|
, modal :: Modal
|
||||||
}
|
}
|
||||||
|
|
||||||
component :: forall m. MonadAff m => H.Component Query Input Output m
|
component :: forall m. MonadAff m => H.Component Query Input Output m
|
||||||
|
@ -72,15 +81,23 @@ initialState :: Input -> State
|
||||||
initialState token =
|
initialState token =
|
||||||
{ newPasswordForm: { password: "", confirmation: "" }
|
{ newPasswordForm: { password: "", confirmation: "" }
|
||||||
, token
|
, token
|
||||||
|
, modal: NoModal
|
||||||
, wsUp: true
|
, wsUp: true
|
||||||
}
|
}
|
||||||
|
|
||||||
render :: forall m. State -> H.ComponentHTML Action () m
|
render :: forall m. State -> H.ComponentHTML Action () m
|
||||||
render { wsUp, newPasswordForm }
|
render { modal, wsUp, newPasswordForm } =
|
||||||
= render_new_password_form
|
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
|
where
|
||||||
|
b e = Bulma.column_ [ Bulma.box e ]
|
||||||
should_be_disabled = (if wsUp then (HP.enabled true) else (HP.disabled true))
|
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
|
render_new_password_form = HH.form
|
||||||
[ HE.onSubmit ChangePasswordAttempt ]
|
[ HE.onSubmit ChangePasswordAttempt ]
|
||||||
[ Bulma.box_input "passwordNEWPASS" "Password" "password"
|
[ Bulma.box_input "passwordNEWPASS" "Password" "password"
|
||||||
|
@ -99,6 +116,15 @@ render { wsUp, newPasswordForm }
|
||||||
[ HH.text "Send Message to Server" ]
|
[ 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 :: forall m. MonadAff m => Action -> H.HalogenM State Action () Output m Unit
|
||||||
handleAction = case _ of
|
handleAction = case _ of
|
||||||
HandleNewPassword authinp -> do
|
HandleNewPassword authinp -> do
|
||||||
|
@ -106,6 +132,14 @@ handleAction = case _ of
|
||||||
NEWPASS_INP_password v -> H.modify_ _ { newPasswordForm { password = v } }
|
NEWPASS_INP_password v -> H.modify_ _ { newPasswordForm { password = v } }
|
||||||
NEWPASS_INP_confirmation v -> H.modify_ _ { newPasswordForm { confirmation = 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
|
ChangePasswordAttempt ev -> do
|
||||||
H.liftEffect $ Event.preventDefault ev
|
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 :: forall w i. String -> Array (HH.HTML w i) -> Array (HH.HTML w i) -> HH.HTML w i
|
||||||
modal title body foot =
|
modal title body foot =
|
||||||
modal_
|
modal_
|
||||||
|
|
Loading…
Reference in New Issue