summaryrefslogtreecommitdiff
path: root/elm
diff options
context:
space:
mode:
authorYorhel <git@yorhel.nl>2019-09-30 18:05:28 +0200
committerYorhel <git@yorhel.nl>2019-09-30 18:05:41 +0200
commit2d7e855cfb37f35cb2cd0f8f39754002c20c8a7c (patch)
treeb1ff536adee289c3b6e1a56c1f1a71acd1da6c87 /elm
parent24e08e0f2caf8dede4a0c8a77b8ede1e13899785 (diff)
v2rw: Convert login, logout & insecure-password-change forms
The insecure-password-change flow is now slightly more friendly. The logout functionality has been hardened to use POST and require CSRF.
Diffstat (limited to 'elm')
-rw-r--r--elm/Lib/Api.elm3
-rw-r--r--elm/Lib/Html.elm13
-rw-r--r--elm/User/Login.elm161
3 files changed, 177 insertions, 0 deletions
diff --git a/elm/Lib/Api.elm b/elm/Lib/Api.elm
index 31bf3613..d1d0bc10 100644
--- a/elm/Lib/Api.elm
+++ b/elm/Lib/Api.elm
@@ -32,6 +32,9 @@ showResponse res =
Unchanged -> "No changes"
Changed _ _ -> unexp
Content _ -> unexp
+ BadLogin -> "Invalid username or password."
+ LoginThrottle -> "Action throttled, too many failed login attempts."
+ InsecurePass -> "Your chosen password is in a database of leaked passwords, please choose another one."
expectResponse : (Response -> msg) -> Http.Expect msg
diff --git a/elm/Lib/Html.elm b/elm/Lib/Html.elm
index bbd2e1fb..41f6e376 100644
--- a/elm/Lib/Html.elm
+++ b/elm/Lib/Html.elm
@@ -53,6 +53,19 @@ inputText nam val onch attrs = input (
) []
+inputPassword : String -> String -> (String -> m) -> List (Attribute m) -> Html m
+inputPassword nam val onch attrs = input (
+ [ type_ "password"
+ , class "text"
+ , tabindex 10
+ , value val
+ , onInput onch
+ ]
+ ++ attrs
+ ++ (if nam == "" then [] else [ id nam, name nam ])
+ ) []
+
+
inputTextArea : String -> String -> (String -> m) -> List (Attribute m) -> Html m
inputTextArea nam val onch attrs = textarea (
[ tabindex 10
diff --git a/elm/User/Login.elm b/elm/User/Login.elm
new file mode 100644
index 00000000..aba1f88d
--- /dev/null
+++ b/elm/User/Login.elm
@@ -0,0 +1,161 @@
+module User.Login exposing (main)
+
+import Html exposing (..)
+import Html.Attributes exposing (..)
+import Html.Events exposing (..)
+import Json.Encode as JE
+import Browser
+import Browser.Navigation exposing (load)
+import Lib.Api as Api
+import Gen.Api as GApi
+import Gen.UserLogin as GUL
+import Gen.Types exposing (adminEMail)
+import Lib.Html exposing (..)
+
+
+main : Program String Model Msg
+main = Browser.element
+ { init = \ref -> (init ref, Cmd.none)
+ , subscriptions = always Sub.none
+ , view = view
+ , update = update
+ }
+
+
+type alias Model =
+ { ref : String
+ , username : String
+ , password : String
+ , newpass1 : String
+ , newpass2 : String
+ , state : Api.State
+ , insecure : Bool
+ , noteq : Bool
+ }
+
+
+init : String -> Model
+init ref =
+ { ref = ref
+ , username = ""
+ , password = ""
+ , newpass1 = ""
+ , newpass2 = ""
+ , state = Api.Normal
+ , insecure = False
+ , noteq = False
+ }
+
+
+encodeLogin : Model -> JE.Value
+encodeLogin o = JE.object
+ [ ("username", JE.string o.username)
+ , ("password", JE.string o.password) ]
+
+
+encodeChangePass : Model -> JE.Value
+encodeChangePass o = JE.object
+ [ ("username", JE.string o.username)
+ , ("oldpass", JE.string o.password)
+ , ("newpass", JE.string o.newpass1) ]
+
+
+type Msg
+ = Username String
+ | Password String
+ | Newpass1 String
+ | Newpass2 String
+ | Submit
+ | Submitted GApi.Response
+
+
+update : Msg -> Model -> (Model, Cmd Msg)
+update msg model =
+ case msg of
+ Username n -> ({ model | username = n }, Cmd.none)
+ Password n -> ({ model | password = n }, Cmd.none)
+ Newpass1 n -> ({ model | newpass1 = n, noteq = False }, Cmd.none)
+ Newpass2 n -> ({ model | newpass2 = n, noteq = False }, Cmd.none)
+
+ Submit ->
+ if not model.insecure
+ then ( { model | state = Api.Loading }
+ , Api.post "/u/login" (encodeLogin model) Submitted )
+ else if model.newpass1 /= model.newpass2
+ then ( { model | noteq = True }, Cmd.none )
+ else ( { model | state = Api.Loading }
+ , Api.post "/u/changepass" (encodeChangePass model) Submitted )
+
+ Submitted GApi.Success -> (model, load model.ref)
+ Submitted GApi.InsecurePass -> ({ model | insecure = True, state = if model.insecure then Api.Error GApi.InsecurePass else Api.Normal }, Cmd.none)
+ Submitted e -> ({ model | state = Api.Error e }, Cmd.none)
+
+
+view : Model -> Html Msg
+view model =
+ let
+ loginBox =
+ div [ class "mainbox" ]
+ [ h1 [] [ text "Login" ]
+ , table [ class "formtable" ]
+ [ tr [ class "newfield" ]
+ [ td [ class "label" ] [ label [ for "username" ] [ text "Username" ]]
+ , td [ class "field" ] [ inputText "username" model.username Username GUL.valUsername ]
+ ]
+ , tr []
+ [ td [] []
+ , td [ class "field" ] [ a [ href "/u/register" ] [ text "No account yet?" ] ]
+ ]
+ , tr [ class "newfield" ]
+ [ td [ class "label" ] [ label [ for "password" ] [ text "Password" ]]
+ , td [ class "field" ] [ inputPassword "password" model.password Password GUL.valPassword ]
+ ]
+ , tr []
+ [ td [] []
+ , td [ class "field" ] [ a [ href "/u/newpass" ] [ text "Forgot your password?" ] ]
+ ]
+ ]
+ , if model.state == Api.Normal
+ then text ""
+ else div [ class "notice" ]
+ [ h2 [] [ text "Trouble logging in?" ]
+ , text "If you have not used this login form since October 2014, your account has likely been disabled. You can "
+ , a [ href "/u/newpass" ] [ text "reset your password" ]
+ , text " to regain access."
+ , br [] []
+ , br [] []
+ , text "Still having trouble? Send a mail to "
+ , a [ href <| "mailto:" ++ adminEMail ] [ text adminEMail ]
+ , text ". But keep in mind that I can only help you if the email address associated with your account is correct"
+ , text " and you still have access to it. Without that, there is no way to prove that the account is yours."
+ ]
+ ]
+
+ changeBox =
+ div [ class "mainbox" ]
+ [ h1 [] [ text "Change your password" ]
+ , div [ class "warning" ]
+ [ h2 [] [ text "Your current password is not secure" ]
+ , text "Your current password is in a public database of leaked passwords. You need to change it before you can continue."
+ ]
+ , table [ class "formtable" ]
+ [ tr [ class "newfield" ]
+ [ td [ class "label" ] [ label [ for "newpass1" ] [ text "New password" ]]
+ , td [ class "field" ] [ inputPassword "newpass1" model.newpass1 Newpass1 GUL.valPassword ]
+ ]
+ , tr [ class "newfield" ]
+ [ td [ class "label" ] [ label [ for "newpass2" ] [ text "Repeat" ]]
+ , td [ class "field" ]
+ [ inputPassword "newpass2" model.newpass2 Newpass2 GUL.valPassword
+ , if model.noteq then b [ class "standout" ] [ text "Passwords do not match" ] else text ""
+ ]
+ ]
+ ]
+ ]
+
+ in Html.form [ onSubmit Submit ]
+ [ if model.insecure then changeBox else loginBox
+ , div [ class "mainbox" ]
+ [ fieldset [ class "submit" ] [ submitButton "Submit" model.state True False ]
+ ]
+ ]