Add mail verification.
This commit is contained in:
parent
3f2573831a
commit
f9f79875c0
@ -45,6 +45,7 @@ import Data.Either (Either(..))
|
|||||||
import Data.Tuple (Tuple(..))
|
import Data.Tuple (Tuple(..))
|
||||||
import App.AuthenticationInterface as AI
|
import App.AuthenticationInterface as AI
|
||||||
import App.RegistrationInterface as RI
|
import App.RegistrationInterface as RI
|
||||||
|
import App.MailValidationInterface as MVI
|
||||||
import App.Log as AppLog
|
import App.Log as AppLog
|
||||||
import App.WS as WS
|
import App.WS as WS
|
||||||
import App.AuthenticationDaemonAdminInterface as AAI
|
import App.AuthenticationDaemonAdminInterface as AAI
|
||||||
@ -68,7 +69,7 @@ import App.LogMessage (LogMessage(..))
|
|||||||
-- | List all pages the application has:
|
-- | List all pages the application has:
|
||||||
-- | Home, Login, Domain list, Zone, `authd` administration.
|
-- | Home, Login, Domain list, Zone, `authd` administration.
|
||||||
-- | This list will grows in a near future.
|
-- | This list will grows in a near future.
|
||||||
data Page = Home | Authentication | Registration | DomainList | Zone String | AuthAdmin
|
data Page = Home | Authentication | Registration | MailValidation | DomainList | Zone String | AuthAdmin
|
||||||
|
|
||||||
type Token = String
|
type Token = String
|
||||||
type Login = String
|
type Login = String
|
||||||
@ -79,9 +80,12 @@ data Action
|
|||||||
-- | Handle events from `AuthenticationInterface`.
|
-- | Handle events from `AuthenticationInterface`.
|
||||||
= AuthenticationInterfaceEvent AI.Output
|
= AuthenticationInterfaceEvent AI.Output
|
||||||
|
|
||||||
-- | Handle events from `AuthenticationComponent`.
|
-- | Handle events from `RegistrationInterface`.
|
||||||
| RegistrationInterfaceEvent RI.Output
|
| RegistrationInterfaceEvent RI.Output
|
||||||
|
|
||||||
|
-- | Handle events from `MailValidationInterface`.
|
||||||
|
| MailValidationInterfaceEvent MVI.Output
|
||||||
|
|
||||||
-- | Handle events from `AuthenticationDaemonAdminComponent`.
|
-- | Handle events from `AuthenticationDaemonAdminComponent`.
|
||||||
| AuthenticationDaemonAdminComponentEvent AAI.Output -- Admin interface for authd.
|
| AuthenticationDaemonAdminComponentEvent AAI.Output -- Admin interface for authd.
|
||||||
|
|
||||||
@ -141,8 +145,8 @@ type State = { token :: Maybe String
|
|||||||
}
|
}
|
||||||
|
|
||||||
-- | The list of child components: log, `WS` twice (once for each ws connection),
|
-- | The list of child components: log, `WS` twice (once for each ws connection),
|
||||||
-- | then all the pages (AuthenticationInterface, RegistrationInterface, HomeInterface, DomainListInterface,
|
-- | then all the pages (AuthenticationInterface, RegistrationInterface, MailValidationInterface,
|
||||||
-- | ZoneInterface and AuthenticationDaemonAdminInterface).
|
-- | HomeInterface, DomainListInterface, ZoneInterface and AuthenticationDaemonAdminInterface).
|
||||||
type ChildSlots =
|
type ChildSlots =
|
||||||
( log :: AppLog.Slot Unit
|
( log :: AppLog.Slot Unit
|
||||||
, ho :: HomeInterface.Slot Unit
|
, ho :: HomeInterface.Slot Unit
|
||||||
@ -150,6 +154,7 @@ type ChildSlots =
|
|||||||
, ws_dns :: WS.Slot Unit
|
, ws_dns :: WS.Slot Unit
|
||||||
, ai :: AI.Slot Unit
|
, ai :: AI.Slot Unit
|
||||||
, ri :: RI.Slot Unit
|
, ri :: RI.Slot Unit
|
||||||
|
, mvi :: MVI.Slot Unit
|
||||||
, aai :: AAI.Slot Unit
|
, aai :: AAI.Slot Unit
|
||||||
, dli :: DomainListInterface.Slot Unit
|
, dli :: DomainListInterface.Slot Unit
|
||||||
, zi :: ZoneInterface.Slot Unit
|
, zi :: ZoneInterface.Slot Unit
|
||||||
@ -161,6 +166,7 @@ _ws_auth = Proxy :: Proxy "ws_auth" -- WS with `authd`
|
|||||||
_ws_dns = Proxy :: Proxy "ws_dns" -- WS with `dnsmanagerd`
|
_ws_dns = Proxy :: Proxy "ws_dns" -- WS with `dnsmanagerd`
|
||||||
_ai = Proxy :: Proxy "ai" -- Authentication Interface
|
_ai = Proxy :: Proxy "ai" -- Authentication Interface
|
||||||
_ri = Proxy :: Proxy "ri" -- Registration Interface
|
_ri = Proxy :: Proxy "ri" -- Registration Interface
|
||||||
|
_mvi = Proxy :: Proxy "mvi" -- Mail Validation Interface
|
||||||
_aai = Proxy :: Proxy "aai" -- Authd Administration Interface
|
_aai = Proxy :: Proxy "aai" -- Authd Administration Interface
|
||||||
_dli = Proxy :: Proxy "dli" -- Domain List
|
_dli = Proxy :: Proxy "dli" -- Domain List
|
||||||
_zi = Proxy :: Proxy "zi" -- Zone Interface
|
_zi = Proxy :: Proxy "zi" -- Zone Interface
|
||||||
@ -190,6 +196,7 @@ render state
|
|||||||
Home -> render_home
|
Home -> render_home
|
||||||
Authentication -> render_auth_form
|
Authentication -> render_auth_form
|
||||||
Registration -> render_registration
|
Registration -> render_registration
|
||||||
|
MailValidation -> render_mail_validation
|
||||||
DomainList -> render_domainlist_interface
|
DomainList -> render_domainlist_interface
|
||||||
Zone domain -> render_zone domain
|
Zone domain -> render_zone domain
|
||||||
AuthAdmin -> render_authd_admin_interface
|
AuthAdmin -> render_authd_admin_interface
|
||||||
@ -207,6 +214,8 @@ render state
|
|||||||
render_auth_form = HH.slot _ai unit AI.component unit AuthenticationInterfaceEvent
|
render_auth_form = HH.slot _ai unit AI.component unit AuthenticationInterfaceEvent
|
||||||
render_registration :: forall monad. MonadAff monad => H.ComponentHTML Action ChildSlots monad
|
render_registration :: forall monad. MonadAff monad => H.ComponentHTML Action ChildSlots monad
|
||||||
render_registration = HH.slot _ri unit RI.component unit RegistrationInterfaceEvent
|
render_registration = HH.slot _ri unit RI.component unit RegistrationInterfaceEvent
|
||||||
|
render_mail_validation :: forall monad. MonadAff monad => H.ComponentHTML Action ChildSlots monad
|
||||||
|
render_mail_validation = HH.slot _mvi unit MVI.component unit MailValidationInterfaceEvent
|
||||||
render_zone :: forall monad. String -> MonadAff monad => H.ComponentHTML Action ChildSlots monad
|
render_zone :: forall monad. String -> MonadAff monad => H.ComponentHTML Action ChildSlots monad
|
||||||
render_zone domain = HH.slot _zi unit ZoneInterface.component domain ZoneInterfaceEvent
|
render_zone domain = HH.slot _zi unit ZoneInterface.component domain ZoneInterfaceEvent
|
||||||
render_authd_admin_interface :: forall monad. MonadAff monad => H.ComponentHTML Action ChildSlots monad
|
render_authd_admin_interface :: forall monad. MonadAff monad => H.ComponentHTML Action ChildSlots monad
|
||||||
@ -226,6 +235,7 @@ render state
|
|||||||
(Routing AuthAdmin)
|
(Routing AuthAdmin)
|
||||||
(Routing Authentication)
|
(Routing Authentication)
|
||||||
(Routing Registration)
|
(Routing Registration)
|
||||||
|
(Routing MailValidation)
|
||||||
Disconnection
|
Disconnection
|
||||||
|
|
||||||
render_header :: forall monad. MonadAff monad => H.ComponentHTML Action ChildSlots monad
|
render_header :: forall monad. MonadAff monad => H.ComponentHTML Action ChildSlots monad
|
||||||
@ -251,6 +261,7 @@ handleAction = case _ of
|
|||||||
Home -> H.liftEffect $ Storage.setItem "current-page" "Home" sessionstorage
|
Home -> H.liftEffect $ Storage.setItem "current-page" "Home" sessionstorage
|
||||||
Authentication -> H.liftEffect $ Storage.setItem "current-page" "Authentication" sessionstorage
|
Authentication -> H.liftEffect $ Storage.setItem "current-page" "Authentication" sessionstorage
|
||||||
Registration -> H.liftEffect $ Storage.setItem "current-page" "Registration" sessionstorage
|
Registration -> H.liftEffect $ Storage.setItem "current-page" "Registration" sessionstorage
|
||||||
|
MailValidation -> H.liftEffect $ Storage.setItem "current-page" "MailValidation" sessionstorage
|
||||||
DomainList -> H.liftEffect $ Storage.setItem "current-page" "DomainList" sessionstorage
|
DomainList -> H.liftEffect $ Storage.setItem "current-page" "DomainList" sessionstorage
|
||||||
Zone zone -> do _ <- H.liftEffect $ Storage.setItem "current-page" "Zone" sessionstorage
|
Zone zone -> do _ <- H.liftEffect $ Storage.setItem "current-page" "Zone" sessionstorage
|
||||||
H.liftEffect $ Storage.setItem "current-zone" zone sessionstorage
|
H.liftEffect $ Storage.setItem "current-zone" zone sessionstorage
|
||||||
@ -284,6 +295,10 @@ handleAction = case _ of
|
|||||||
RI.MessageToSend message -> H.tell _ws_auth unit (WS.ToSend message)
|
RI.MessageToSend message -> H.tell _ws_auth unit (WS.ToSend message)
|
||||||
RI.Log message -> H.tell _log unit (AppLog.Log message)
|
RI.Log message -> H.tell _log unit (AppLog.Log message)
|
||||||
|
|
||||||
|
MailValidationInterfaceEvent ev -> case ev of
|
||||||
|
MVI.MessageToSend message -> H.tell _ws_auth unit (WS.ToSend message)
|
||||||
|
MVI.Log message -> H.tell _log unit (AppLog.Log message)
|
||||||
|
|
||||||
AuthenticationDaemonAdminComponentEvent ev -> case ev of
|
AuthenticationDaemonAdminComponentEvent ev -> case ev of
|
||||||
AAI.MessageToSend message -> H.tell _ws_auth unit (WS.ToSend message)
|
AAI.MessageToSend message -> H.tell _ws_auth unit (WS.ToSend message)
|
||||||
AAI.Log message -> H.tell _log unit (AppLog.Log message)
|
AAI.Log message -> H.tell _log unit (AppLog.Log message)
|
||||||
|
197
src/App/MailValidationInterface.purs
Normal file
197
src/App/MailValidationInterface.purs
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
-- | `App.MailValidationInterface` is a simple interface for mail verification.
|
||||||
|
-- | A token is sent at registration at the provided email address.
|
||||||
|
-- | This token has to be used to validate the email address.
|
||||||
|
module App.MailValidationInterface where
|
||||||
|
|
||||||
|
import Prelude (Unit, bind, discard, pure, ($), (<<<), (<>), map, show)
|
||||||
|
|
||||||
|
import Data.Array as A
|
||||||
|
import Data.ArrayBuffer.Types (ArrayBuffer)
|
||||||
|
import Data.Maybe (Maybe(..), maybe)
|
||||||
|
import Data.Either (Either(..))
|
||||||
|
import Effect.Aff.Class (class MonadAff)
|
||||||
|
import Halogen as H
|
||||||
|
import Halogen.HTML as HH
|
||||||
|
import Halogen.HTML.Events as HE
|
||||||
|
import Halogen.HTML.Properties as HP
|
||||||
|
import Web.Event.Event as Event
|
||||||
|
import Web.Event.Event (Event)
|
||||||
|
|
||||||
|
import Bulma as Bulma
|
||||||
|
|
||||||
|
import App.LogMessage
|
||||||
|
import App.Messages.AuthenticationDaemon as AuthD
|
||||||
|
|
||||||
|
import App.Validation.Login as L
|
||||||
|
import App.Validation.Token as T
|
||||||
|
|
||||||
|
data Output
|
||||||
|
= MessageToSend ArrayBuffer
|
||||||
|
| Log LogMessage
|
||||||
|
|
||||||
|
-- | The component is informed when the connection went up or down.
|
||||||
|
data Query a
|
||||||
|
= ConnectionIsDown a
|
||||||
|
| ConnectionIsUp a
|
||||||
|
|
||||||
|
type Slot = H.Slot Query Output
|
||||||
|
|
||||||
|
type Input = Unit
|
||||||
|
|
||||||
|
data RegisterInput
|
||||||
|
= VALIDATION_INP_login String
|
||||||
|
| VALIDATION_INP_token String
|
||||||
|
|
||||||
|
data Action
|
||||||
|
-- | Simply get the inputs from the form.
|
||||||
|
= HandleValidationInput RegisterInput
|
||||||
|
-- | Validate inputs (login, email, password) then send the request
|
||||||
|
-- | (via `SendMailValidationToken`) or log errors.
|
||||||
|
| ValidateInputs Event
|
||||||
|
-- | Send the registration request to `dnsmanagerd`.
|
||||||
|
-- | This action is automatically called from `ValidateInputs`.
|
||||||
|
| SendMailValidationToken
|
||||||
|
|
||||||
|
-- | The possible errors come from either the login or token input.
|
||||||
|
data Error
|
||||||
|
= Login (Array L.Error)
|
||||||
|
| Token (Array T.Error)
|
||||||
|
|
||||||
|
-- | The whole mail validation form is composed of two strings: the login and the token.
|
||||||
|
type MailValidationForm = { login :: String, token :: String }
|
||||||
|
|
||||||
|
-- | State is composed of the registration form, the errors and an indication whether
|
||||||
|
-- | the websocket connection with `authd` is up or not.
|
||||||
|
type State =
|
||||||
|
{ mailValidationForm :: MailValidationForm
|
||||||
|
, errors :: Array Error
|
||||||
|
, wsUp :: Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
component :: forall m. MonadAff m => H.Component Query Input Output m
|
||||||
|
component =
|
||||||
|
H.mkComponent
|
||||||
|
{ initialState
|
||||||
|
, render
|
||||||
|
, eval: H.mkEval $ H.defaultEval
|
||||||
|
{ handleAction = handleAction
|
||||||
|
, handleQuery = handleQuery
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initialState :: Input -> State
|
||||||
|
initialState _ =
|
||||||
|
{ mailValidationForm: { login: "", token: "" }
|
||||||
|
, errors: []
|
||||||
|
, wsUp: true
|
||||||
|
}
|
||||||
|
|
||||||
|
render :: forall m. State -> H.ComponentHTML Action () m
|
||||||
|
render { wsUp, mailValidationForm }
|
||||||
|
= Bulma.section_small
|
||||||
|
[ case wsUp of
|
||||||
|
false -> Bulma.p "You are disconnected."
|
||||||
|
true -> Bulma.columns_ [ b mail_validation_form ]
|
||||||
|
]
|
||||||
|
|
||||||
|
where
|
||||||
|
b e = Bulma.column_ [ Bulma.box e ]
|
||||||
|
mail_validation_form = [ Bulma.h3 "Verify your account", render_register_form ]
|
||||||
|
|
||||||
|
should_be_disabled = (if wsUp then (HP.enabled true) else (HP.disabled true))
|
||||||
|
|
||||||
|
render_register_form = HH.form
|
||||||
|
[ HE.onSubmit ValidateInputs ]
|
||||||
|
[ Bulma.box_input "loginValidation" "Login" "login" -- title, placeholder
|
||||||
|
(HandleValidationInput <<< VALIDATION_INP_login) -- action
|
||||||
|
mailValidationForm.login -- value
|
||||||
|
should_be_disabled -- condition
|
||||||
|
, Bulma.box_input "tokenValidation" "Token" "token" -- title, placeholder
|
||||||
|
(HandleValidationInput <<< VALIDATION_INP_token) -- action
|
||||||
|
mailValidationForm.token -- value
|
||||||
|
should_be_disabled -- condition
|
||||||
|
, HH.div_
|
||||||
|
[ 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
|
||||||
|
HandleValidationInput reginp -> do
|
||||||
|
case reginp of
|
||||||
|
VALIDATION_INP_login v -> H.modify_ _ { mailValidationForm { login = v } }
|
||||||
|
VALIDATION_INP_token v -> H.modify_ _ { mailValidationForm { token = v } }
|
||||||
|
|
||||||
|
-- Validate inputs (login, token) then send the request
|
||||||
|
-- (via SendMailValidationToken) or log errors.
|
||||||
|
ValidateInputs ev -> do
|
||||||
|
H.liftEffect $ Event.preventDefault ev
|
||||||
|
|
||||||
|
{ mailValidationForm } <- H.get
|
||||||
|
let { login, token } = mailValidationForm
|
||||||
|
|
||||||
|
case login, token of
|
||||||
|
"", _ ->
|
||||||
|
H.raise $ Log $ UnableToSend "Write your login!"
|
||||||
|
|
||||||
|
_, "" ->
|
||||||
|
H.raise $ Log $ UnableToSend "Write your token!"
|
||||||
|
|
||||||
|
_, _ -> do
|
||||||
|
case L.login login, T.token token of
|
||||||
|
Left errors, _ -> H.raise $ Log $ UnableToSend $ show_error $ Login errors
|
||||||
|
_, Left errors -> H.raise $ Log $ UnableToSend $ show_error $ Token errors
|
||||||
|
Right _, Right _ -> handleAction $ SendMailValidationToken
|
||||||
|
|
||||||
|
SendMailValidationToken -> do
|
||||||
|
{ mailValidationForm } <- H.get
|
||||||
|
let { login, token } = mailValidationForm
|
||||||
|
message <- H.liftEffect $ AuthD.serialize $ AuthD.MkValidateUser { user: login, activation_key: token }
|
||||||
|
H.raise $ MessageToSend message
|
||||||
|
H.raise $ Log $ SimpleLog $ "[😇] Trying to register (login: " <> login <> ")"
|
||||||
|
|
||||||
|
show_error :: Error -> String
|
||||||
|
show_error = case _ of
|
||||||
|
Login arr -> "Error with the Login: " <> (A.fold $ map show_error_login arr)
|
||||||
|
Token arr -> "Error with the Token: " <> (A.fold $ map show_error_token arr)
|
||||||
|
|
||||||
|
show_error_login :: L.Error -> String
|
||||||
|
show_error_login = case _ of
|
||||||
|
L.ParsingError {error, position} ->
|
||||||
|
"position " <> show position <> " " <> maybe "" string_error_login error
|
||||||
|
|
||||||
|
string_error_login :: L.LoginParsingError -> String
|
||||||
|
string_error_login = case _ of
|
||||||
|
L.CannotParse -> "cannot parse the login"
|
||||||
|
L.CannotEntirelyParse -> "cannot entirely parse the login"
|
||||||
|
L.Size min max n -> "login size should be between "
|
||||||
|
<> show min <> " and " <> show max
|
||||||
|
<> " (currently: " <> show n <> ")"
|
||||||
|
|
||||||
|
show_error_token :: T.Error -> String
|
||||||
|
show_error_token = case _ of
|
||||||
|
T.ParsingError {error, position} ->
|
||||||
|
"position " <> show position <> " " <> maybe "" string_error_token error
|
||||||
|
|
||||||
|
string_error_token :: T.TokenParsingError -> String
|
||||||
|
string_error_token = case _ of
|
||||||
|
T.CannotParse -> "cannot parse the token"
|
||||||
|
T.CannotEntirelyParse -> "cannot entirely parse the token"
|
||||||
|
T.Size min max n -> "token size should be between "
|
||||||
|
<> show min <> " and " <> show max
|
||||||
|
<> " (currently: " <> show n <> ")"
|
||||||
|
|
||||||
|
handleQuery :: forall a m. MonadAff m => Query a -> H.HalogenM State Action () Output m (Maybe a)
|
||||||
|
handleQuery = case _ of
|
||||||
|
ConnectionIsDown a -> do
|
||||||
|
H.modify_ _ { wsUp = false }
|
||||||
|
pure (Just a)
|
||||||
|
|
||||||
|
ConnectionIsUp a -> do
|
||||||
|
H.modify_ _ { wsUp = true }
|
||||||
|
pure (Just a)
|
@ -60,11 +60,11 @@ codecRegister
|
|||||||
, email: CAR.optional Email.codec })
|
, email: CAR.optional Email.codec })
|
||||||
|
|
||||||
{- 2 -}
|
{- 2 -}
|
||||||
type ValidateUser = { user :: UserID, activation_key :: String }
|
type ValidateUser = { user :: String, activation_key :: String }
|
||||||
codecValidateUser ∷ CA.JsonCodec ValidateUser
|
codecValidateUser ∷ CA.JsonCodec ValidateUser
|
||||||
codecValidateUser
|
codecValidateUser
|
||||||
= CA.object "ValidateUser" (CAR.record
|
= CA.object "ValidateUser" (CAR.record
|
||||||
{ user: CA.int
|
{ user: CA.string
|
||||||
, activation_key: CA.string })
|
, activation_key: CA.string })
|
||||||
|
|
||||||
{- NOTE: "user" attribute for both PasswordRecovery and AskPasswordRecovery could be UserID,
|
{- NOTE: "user" attribute for both PasswordRecovery and AskPasswordRecovery could be UserID,
|
||||||
|
@ -20,10 +20,10 @@ import Bulma as Bulma
|
|||||||
-- | `actionDisconnection` (the action to disconnect the user)
|
-- | `actionDisconnection` (the action to disconnect the user)
|
||||||
-- |
|
-- |
|
||||||
-- | TODO: make the "burger" component actually useful. For now, it's empty.
|
-- | TODO: make the "burger" component actually useful. For now, it's empty.
|
||||||
netlibre_navbar :: forall w i. Boolean -> Boolean -> i -> i -> i -> i -> i -> i -> HH.HTML w i
|
netlibre_navbar :: forall w i. Boolean -> Boolean -> i -> i -> i -> i -> i -> i -> i -> HH.HTML w i
|
||||||
netlibre_navbar authenticated admin
|
netlibre_navbar authenticated admin
|
||||||
actionHome actionDomainList actionAuthdAdmin
|
actionHome actionDomainList actionAuthdAdmin
|
||||||
actionLogin actionRegistration actionDisconnection =
|
actionLogin actionRegistration actionMailValidation actionDisconnection =
|
||||||
main_nav
|
main_nav
|
||||||
[ nav_brand [ logo, burger_menu ]
|
[ nav_brand [ logo, burger_menu ]
|
||||||
, nav_menu
|
, nav_menu
|
||||||
@ -45,6 +45,7 @@ netlibre_navbar authenticated admin
|
|||||||
[ HH.div [HP.classes C.buttons] $ case authenticated of
|
[ HH.div [HP.classes C.buttons] $ case authenticated of
|
||||||
false -> [ nav_button C.is_info "Login" actionLogin
|
false -> [ nav_button C.is_info "Login" actionLogin
|
||||||
, nav_button_strong "Register" actionRegistration
|
, nav_button_strong "Register" actionRegistration
|
||||||
|
, nav_button C.is_info "Mail verification" actionMailValidation
|
||||||
, nav_button_code
|
, nav_button_code
|
||||||
]
|
]
|
||||||
_ -> [ nav_button_disconnection, nav_button_code ]
|
_ -> [ nav_button_disconnection, nav_button_code ]
|
||||||
|
44
src/App/Validation/Token.purs
Normal file
44
src/App/Validation/Token.purs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
module App.Validation.Token where
|
||||||
|
|
||||||
|
import Prelude
|
||||||
|
|
||||||
|
import Control.Alt ((<|>))
|
||||||
|
import Data.Either (Either(..))
|
||||||
|
import Data.String.CodeUnits as CU
|
||||||
|
import Data.Maybe (Maybe(..))
|
||||||
|
import Data.Validation.Semigroup (V, invalid, toEither)
|
||||||
|
|
||||||
|
import GenericParser.RFC5234 (vchar)
|
||||||
|
import GenericParser.SomeParsers as SomeParsers
|
||||||
|
import GenericParser.Parser as G
|
||||||
|
|
||||||
|
data TokenParsingError
|
||||||
|
= CannotParse
|
||||||
|
| CannotEntirelyParse
|
||||||
|
| Size Int Int Int
|
||||||
|
|
||||||
|
data Error
|
||||||
|
= ParsingError (G.Error TokenParsingError)
|
||||||
|
|
||||||
|
-- | TODO: this number should be exactly the size of the provided token.
|
||||||
|
min_token_size :: Int
|
||||||
|
min_token_size = 20
|
||||||
|
max_token_size :: Int
|
||||||
|
max_token_size = 60
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
token_parser :: G.Parser TokenParsingError String
|
||||||
|
token_parser = do
|
||||||
|
l <- G.many1 vchar <|> 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_token_size || pos > max_token_size
|
||||||
|
then G.Parser \i -> G.failureError i.position (Just $ Size min_token_size max_token_size pos)
|
||||||
|
else pure $ CU.fromCharArray l
|
||||||
|
|
||||||
|
token :: String -> Either (Array Error) String
|
||||||
|
token s = toEither $ parse token_parser s ParsingError
|
Loading…
Reference in New Issue
Block a user