WIP: validation for the email address at registration.

This commit is contained in:
Philippe Pittoli 2024-02-10 18:11:11 +01:00
parent 329d84e6f9
commit dc7ee7d250
5 changed files with 122 additions and 66 deletions

View File

@ -4,7 +4,6 @@ module App.AuthenticationInterface where
import Prelude (Unit, bind, discard, pure, ($), (<<<), (<>))
import Data.ArrayBuffer.Types (ArrayBuffer)
import Data.Maybe (Maybe(..))
import Data.Tuple (Tuple(..))

View File

@ -4,6 +4,7 @@ module App.RegistrationInterface where
import Prelude (Unit, bind, discard, pure, ($), (<<<), (<>))
import Data.Array as A
import Data.ArrayBuffer.Types (ArrayBuffer)
import Data.Maybe (Maybe(..))
import Effect.Aff.Class (class MonadAff)
@ -20,23 +21,17 @@ import App.Email as Email
import App.LogMessage
import App.Messages.AuthenticationDaemon as AuthD
-- | 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.
-- |
-- | Also, the component can send a message to a websocket and log messages.
-- |
-- | TODO: authentication is performed in `App.Container`.
--import App.Validation.Login (login, Error(..)) as L
--import App.Validation.Email (email, Error(..)) as E
--import App.Validation.Password (password, Error(..)) as P
data Output
= MessageToSend ArrayBuffer
| Log LogMessage
-- | The component's parent provides received messages.
-- |
-- | Also, the component is informed when the connection went up or down.
-- | The component is informed when the connection went up or down.
data Query a
= MessageReceived AuthD.AnswerMessage a
| ConnectionIsDown a
= ConnectionIsDown a
| ConnectionIsUp a
type Slot = H.Slot Query Output
@ -52,10 +47,16 @@ data Action
= HandleRegisterInput RegisterInput
| RegisterAttempt Event
--data Error
-- | Login L.Error
-- | Email E.Error
-- | Password P.Error
type StateRegistrationForm = { login :: String, email :: String, pass :: String }
type State =
{ registrationForm :: StateRegistrationForm
--, errors :: Array Error
, wsUp :: Boolean
}
@ -73,6 +74,7 @@ component =
initialState :: Input -> State
initialState _ =
{ registrationForm: { login: "", email: "", pass: "" }
--, errors: []
, wsUp: true
}
@ -86,7 +88,7 @@ render { wsUp, registrationForm }
where
b e = Bulma.column_ [ Bulma.box e ]
registration_form = [ Bulma.h3 "Register!" , render_register_form ]
registration_form = [ Bulma.h3 "Register!", render_register_form ]
should_be_disabled = (if wsUp then (HP.enabled true) else (HP.disabled true))
@ -125,39 +127,49 @@ handleAction = case _ of
RegisterAttempt ev -> do
H.liftEffect $ Event.preventDefault ev
{ registrationForm } <- H.get
let login = registrationForm.login
email = registrationForm.email
pass = registrationForm.pass
H.raise $ Log $ UnableToSend "TODO: validation first!"
case login, email, pass of
"", _, _ ->
H.raise $ Log $ UnableToSend "Write your login!"
_, "", _ ->
H.raise $ Log $ UnableToSend "Write your email!"
_, _, "" ->
H.raise $ Log $ UnableToSend "Write your password!"
_, _, _ -> do
message <- H.liftEffect $ AuthD.serialize $
AuthD.MkRegister { login: login
, email: Just (Email.Email email)
, password: pass }
H.raise $ MessageToSend message
H.raise $ Log $ SimpleLog $ "[😇] Trying to register (login: " <> login <> ")"
-- { registrationForm } <- H.get
-- let login = registrationForm.login
-- email = registrationForm.email
-- pass = registrationForm.pass
--
-- case login, email, pass of
-- "", _, _ ->
-- H.raise $ Log $ UnableToSend "Write your login!"
--
-- _, "", _ ->
-- H.raise $ Log $ UnableToSend "Write your email!"
--
-- _, _, "" ->
-- H.raise $ Log $ UnableToSend "Write your password!"
--
-- _, _, _ -> do
-- -- TODO: handle validation
-- case L.login login, E.email email, P.password pass of
-- Left errors, _, _ -> H.raise $ Log $ UnableToSend $ show_error errors
-- _, Left errors, _ -> H.raise $ Log $ UnableToSend $ show_error errors
-- _, _, Left errors -> H.raise $ Log $ UnableToSend $ show_error errors
--
-- Right l, Right e, Right p -> do
-- message <- H.liftEffect $ AuthD.serialize $
-- AuthD.MkRegister { login: login
-- , email: Just (Email.Email email)
-- , password: pass }
-- H.raise $ MessageToSend message
-- H.raise $ Log $ SimpleLog $ "[😇] Trying to register (login: " <> login <> ")"
-- where
-- collect_errors
--
-- show_errors :: Array Error -> String
-- show_errors array = A.concat $ map show_error array
-- show_error = case _ of
-- Login _ -> "Error with the Login"
-- Email _ -> "Error with the Email"
-- Password _ -> "Error with the Password"
handleQuery :: forall a m. MonadAff m => Query a -> H.HalogenM State Action () Output m (Maybe a)
handleQuery = case _ of
-- For now, no message actually needs to be handled here.
-- Error messages are simply logged (see the code in the Container component).
MessageReceived message _ -> do
case message of
_ -> do
H.raise $ Log $ SimpleLog $ "[😈] Message not handled in the `RegistrationInterface` module."
pure Nothing
ConnectionIsDown a -> do
H.modify_ _ { wsUp = false }
pure (Just a)

View File

@ -1,4 +1,4 @@
module App.Validation where
module App.Validation.DNS where
import Prelude (apply, between, bind, map, pure, ($), (-), (<))
@ -32,7 +32,7 @@ import GenericParser.RFC5234 as RFC5234
-- |
-- | Maybe the code will change again in the future, but for now it will be enough.
data ValidationError
data Error
= UNKNOWN
| VEIPv4 (G.Error IPAddress.IPv4Error)
| VEIPv6 (G.Error IPAddress.IPv6Error)
@ -48,7 +48,7 @@ data ValidationError
| VEPort Int Int Int
| VEWeight Int Int Int
type AVErrors = Array ValidationError
type AVErrors = Array Error
-- | Current default values.
min_ttl :: Int
@ -161,31 +161,31 @@ txt_parser = do pos <- G.current_position
-- | `parse` allows to run any parser based on `GenericParser` and provide a validation error.
-- | The actual validation error contains the parser's error including the position.
parse :: forall e v. G.Parser e v -> String -> ((G.Error e) -> ValidationError) -> V AVErrors v
parse :: forall e v. G.Parser e v -> String -> ((G.Error e) -> Error) -> V AVErrors v
parse (G.Parser p) str c = case p { string: str, position: 0 } of
Left x -> invalid $ [c x]
Right x -> pure x.result
validationA :: ResourceRecord -> V AVErrors ResourceRecord
validationA form = ado
name <- parse DomainParser.sub_eof form.name VEName
ttl <- is_between min_ttl max_ttl form.ttl VETTL
target <- parse IPAddress.ipv4 form.target VEIPv4
name <- parse DomainParser.sub_eof form.name VEName
ttl <- is_between min_ttl max_ttl form.ttl VETTL
target <- parse IPAddress.ipv4 form.target VEIPv4
in toRR_basic form.rrid form.readonly "A" name ttl target
validationAAAA :: ResourceRecord -> V AVErrors ResourceRecord
validationAAAA form = ado
name <- parse DomainParser.sub_eof form.name VEName
ttl <- is_between min_ttl max_ttl form.ttl VETTL
ttl <- is_between min_ttl max_ttl form.ttl VETTL
-- use read_input to get unaltered input (the IPv6 parser expands the input)
target <- parse (G.read_input IPAddress.ipv6) form.target VEIPv6
in toRR_basic form.rrid form.readonly "AAAA" name ttl target
validationTXT :: ResourceRecord -> V AVErrors ResourceRecord
validationTXT form = ado
name <- parse DomainParser.sub_eof form.name VEName
ttl <- is_between min_ttl max_ttl form.ttl VETTL
target <- parse txt_parser form.target VETXT
name <- parse DomainParser.sub_eof form.name VEName
ttl <- is_between min_ttl max_ttl form.ttl VETTL
target <- parse txt_parser form.target VETXT
in toRR_basic form.rrid form.readonly "TXT" name ttl target
validationCNAME :: ResourceRecord -> V AVErrors ResourceRecord
@ -197,8 +197,8 @@ validationCNAME form = ado
validationNS :: ResourceRecord -> V AVErrors ResourceRecord
validationNS form = ado
name <- parse DomainParser.sub_eof form.name VEName
ttl <- is_between min_ttl max_ttl form.ttl VETTL
name <- parse DomainParser.sub_eof form.name VEName
ttl <- is_between min_ttl max_ttl form.ttl VETTL
target <- parse DomainParser.sub_eof form.target VENS
in toRR_basic form.rrid form.readonly "NS" name ttl target
@ -210,7 +210,7 @@ protocol_parser = do
pos <- G.current_position
G.string "tcp" <|> G.string "udp" <|> G.Parser \_ -> G.failureError pos (Just InvalidProtocol)
is_between :: Int -> Int -> Int -> (Int -> Int -> Int -> ValidationError) -> V AVErrors Int
is_between :: Int -> Int -> Int -> (Int -> Int -> Int -> Error) -> V AVErrors Int
is_between min max n ve = if between min max n
then pure n
else invalid [ve min max n]

View File

@ -0,0 +1,46 @@
module App.Validation.Email where
import Prelude
import Control.Alt ((<|>))
import Data.Either (Either(..))
import Data.Maybe (Maybe(..))
import Data.Validation.Semigroup (V, invalid, toEither)
import GenericParser.RFC5322 as RFC5322
import GenericParser.SomeParsers as SomeParsers
import GenericParser.Parser as G
data EmailParsingError
= CannotParse
| CannotEntirelyParse
| Size Int Int Int
data Error
= ParsingError (G.Error EmailParsingError)
min_email_size :: Int
min_email_size = 20
max_email_size :: Int
max_email_size = 100
parse :: forall e v. G.Parser e v -> String -> ((G.Error e) -> Error) -> V (Array Error) v
parse (G.Parser p) str c = case p { string: str, position: 0 } of
Left x -> invalid $ [c x]
Right x -> pure x.result
parse_full_email :: G.Parser EmailParsingError String
parse_full_email = do
email_address <- RFC5322.address <|> G.Parser \i -> G.failureError i.position (Just CannotParse)
_ <- SomeParsers.eof <|> G.Parser \i -> G.failureError i.position (Just CannotEntirelyParse)
pos <- G.current_position
if pos < min_email_size || pos > max_email_size
then G.Parser \i -> G.failureError i.position (Just $ Size min_email_size max_email_size pos)
else pure email_address
parserEmail :: String -> V (Array Error) String
parserEmail str = parse parse_full_email str ParsingError
-- | TODO
email :: String -> Either (Array Error) String
email s = toEither $ parserEmail s

View File

@ -43,9 +43,8 @@ import App.ResourceRecord (ResourceRecord)
import App.LogMessage (LogMessage(..))
import App.Messages.DNSManagerDaemon as DNSManager
import App.Validation as Validation
import App.Validation.DNS as Validation
import GenericParser.DomainParser.Common (DomainError(..)) as DomainParser
-- import GenericParser.DomainParser as DomainParser
import GenericParser.IPAddress as IPAddress
type RRId = Int
@ -172,11 +171,11 @@ type State =
-- | All resource records.
, _resources :: Array ResourceRecord
--, _local_errors :: Hash.HashMap RRId (Array Validation.ValidationError)
--, _local_errors :: Hash.HashMap RRId (Array Validation.Error)
-- Unique RR form.
, _currentRR :: ResourceRecord
, _currentRR_errors :: Array Validation.ValidationError
, _currentRR_errors :: Array Validation.Error
}
component :: forall m. MonadAff m => H.Component Query Input Output m
@ -691,7 +690,7 @@ render_soa (Just soa) = Bulma.box [ Bulma.zone_rr_title "Start Of Authority (SOA
-- | Render all Resource Records.
render_resources :: forall w
-- . Hash.HashMap RRId (Array Validation.ValidationError)
-- . Hash.HashMap RRId (Array Validation.Error)
. Array (ResourceRecord)
-> HH.HTML w Action
render_resources []
@ -811,7 +810,7 @@ loopE f a = case (A.head a) of
Nothing -> pure unit
Just xs -> loopE f xs
error_to_paragraph :: forall w. Validation.ValidationError -> HH.HTML w Action
error_to_paragraph :: forall w. Validation.Error -> HH.HTML w Action
error_to_paragraph v = Bulma.error_message (Bulma.p $ show_error_title v)
(case v of
Validation.UNKNOWN -> Bulma.p "An internal error happened."
@ -836,7 +835,7 @@ error_to_paragraph v = Bulma.error_message (Bulma.p $ show_error_title v)
where default_error = Bulma.p "No actual error reported."
-- | `show_error_title` provide a simple title string to display to the user in case of an error with an entry.
show_error_title :: Validation.ValidationError -> String
show_error_title :: Validation.Error -> String
show_error_title v = case v of
Validation.UNKNOWN -> "Unknown"
Validation.VEIPv4 err -> "The IPv4 address is wrong (position: " <> show err.position <> ")"
@ -883,14 +882,14 @@ show_error_ip6 e = case e of
"""
IPAddress.IP6TooManyChunks ->
Bulma.p "The IPv6 representation is erroneous. It should contains only up to 8 groups of hexadecimal characters."
IPAddress.IP6UnrelevantShortRepresentation ->
IPAddress.IP6IrrelevantShortRepresentation ->
Bulma.p "IPv6 address have been unnecessarily shortened (with two ':')."
show_error_ip4 :: forall w. IPAddress.IPv4Error -> HH.HTML w Action
show_error_ip4 e = case e of
IPAddress.IP4NumberTooBig n ->
Bulma.p $ "IPv4 address contains a number too big (should be between 0 and 255). Current entered number: " <> show n
IPAddress.IP4UnrelevantShortRepresentation ->
IPAddress.IP4IrrelevantShortRepresentation ->
Bulma.p "IPv4 address have been unnecessarily shortened (with two '.')."
show_error_txt :: forall w. Validation.TXTError -> HH.HTML w Action