Refactoring: WIP.
This commit is contained in:
parent
88dd3addc5
commit
f219115f73
8 changed files with 401 additions and 405 deletions
|
@ -41,11 +41,10 @@ import CSSClasses as C
|
|||
import App.Text.Explanations as Explanations
|
||||
|
||||
import App.Type.RRId (RRId)
|
||||
import App.Type.Field as Field
|
||||
import App.Type.Delegation (mkEmptyDelegationForm, update_delegation_field, Form, Field) as Delegation
|
||||
import App.Type.ResourceRecord as RR
|
||||
import App.Type.Delegation (mkEmptyDelegationForm, update, Form, Field) as Delegation
|
||||
import App.Type.RRModal (RRModal(..))
|
||||
import App.Type.AcceptedRRTypes (AcceptedRRTypes(..))
|
||||
import App.Type.ResourceRecord (ResourceRecord)
|
||||
import App.Type.DKIM as DKIM
|
||||
import App.Type.DMARC as DMARC
|
||||
|
||||
|
@ -54,8 +53,6 @@ import App.Message.DNSManagerDaemon as DNSManager
|
|||
import App.Validation.DNS as Validation
|
||||
import App.Validation.Delegation as ValidationDelegation
|
||||
|
||||
import App.Type.RRForm (RRForm, RRUpdateValue(..), default_caa, default_rr, mkEmptyRRForm, update_form)
|
||||
|
||||
-- | `App.Page.Zone` can send messages through websocket interface
|
||||
-- | connected to dnsmanagerd. See `App.WS`.
|
||||
-- |
|
||||
|
@ -69,9 +66,9 @@ data Output
|
|||
| AskZoneFile String
|
||||
| AskNewToken String Int
|
||||
| AskDeleteRR String Int
|
||||
| AskSaveRR String ResourceRecord
|
||||
| AskSaveRR String RR.ResourceRecord
|
||||
| AskSaveDelegation String String String
|
||||
| AskAddRR String ResourceRecord
|
||||
| AskAddRR String RR.ResourceRecord
|
||||
| AskGetZone String
|
||||
|
||||
-- | `App.Page.Zone` can receive messages from `dnsmanagerd`.
|
||||
|
@ -90,13 +87,13 @@ type Input = String
|
|||
-- | 2. `UpdateCurrentRR Field`: modify the fields of the future new RR.
|
||||
-- | 3. `ValidateRR AcceptedRRTypes`: validate the new RR stored in `_currentRR`.
|
||||
-- | In case it works, automatically call `AddRR` then `CancelModal`.
|
||||
-- | 4. `AddRR AcceptedRRTypes ResourceRecord`: send a message to `dnsmanagerd`.
|
||||
-- | 4. `AddRR AcceptedRRTypes RR.ResourceRecord`: send a message to `dnsmanagerd`.
|
||||
-- |
|
||||
-- | Steps to update an entry:
|
||||
-- | 1. `CreateUpdateRRModal RRId`: create a modal from the values of the RR in `_resources` to update.
|
||||
-- | 2. `UpdateCurrentRR Field`: modify the currently displayed RR.
|
||||
-- | 3. `ValidateLocal RRId AcceptedRRTypes`: validate the RR.
|
||||
-- | 4. `SaveRR ResourceRecord`: save the _validated_ RR by sending a message to `dnsmanagerd`.
|
||||
-- | 4. `SaveRR RR.ResourceRecord`: save the _validated_ RR by sending a message to `dnsmanagerd`.
|
||||
|
||||
data Action
|
||||
-- | Initiate the component. This means asking the content of the zone to `dnsmanagerd`.
|
||||
|
@ -124,7 +121,7 @@ data Action
|
|||
| ReturnToDomainList
|
||||
|
||||
-- | Update new entry form (in the `rr_modal` modal).
|
||||
| UpdateCurrentRR Field.Field
|
||||
| UpdateCurrentRR RR.Field
|
||||
|
||||
-- | Update a delegation form field (new nameservers for the domain).
|
||||
| UpdateDelegationForm Delegation.Field
|
||||
|
@ -143,13 +140,13 @@ data Action
|
|||
| ValidateLocal
|
||||
|
||||
-- | Add a new resource record to the zone.
|
||||
| AddRR AcceptedRRTypes ResourceRecord
|
||||
| AddRR AcceptedRRTypes RR.ResourceRecord
|
||||
|
||||
-- | Reset the different temporary values, such as SPF mechanisms or DMARC mail entry.
|
||||
| ResetTemporaryValues
|
||||
|
||||
-- | Save the changes done in an already existing resource record.
|
||||
| SaveRR ResourceRecord
|
||||
| SaveRR RR.ResourceRecord
|
||||
|
||||
-- | Send a message to remove a resource record.
|
||||
-- | Automatically closes the modal.
|
||||
|
@ -159,7 +156,7 @@ data Action
|
|||
| AskGeneratedZoneFile
|
||||
|
||||
-- | Modification of any attribute of the current RR.
|
||||
| RRUpdate RRUpdateValue
|
||||
| RRUpdate RR.RRUpdateValue
|
||||
|
||||
-- | Ask a (new) token for a resource record.
|
||||
| NewToken RRId
|
||||
|
@ -180,11 +177,11 @@ type State =
|
|||
, rr_modal :: RRModal
|
||||
|
||||
-- | All resource records.
|
||||
, _resources :: Array ResourceRecord
|
||||
, _resources :: Array RR.ResourceRecord
|
||||
--, _local_errors :: Hash.HashMap RRId (Array Validation.Error)
|
||||
|
||||
-- Unique RR form.
|
||||
, _rr_form :: RRForm
|
||||
, _rr_form :: RR.Form
|
||||
|
||||
-- DelegationForm
|
||||
, _delegation_form :: Delegation.Form
|
||||
|
@ -220,7 +217,7 @@ initialState domain =
|
|||
, _resources: []
|
||||
--, _local_errors: Hash.empty
|
||||
|
||||
, _rr_form: mkEmptyRRForm
|
||||
, _rr_form: RR.mkEmptyRRForm
|
||||
|
||||
, _delegation_form: Delegation.mkEmptyDelegationForm
|
||||
|
||||
|
@ -331,7 +328,7 @@ handleAction = case _ of
|
|||
-- | Each time a "new RR" button is clicked, the form resets.
|
||||
CreateNewRRModal t -> do
|
||||
state <- H.get
|
||||
H.modify_ _ { rr_modal = NewRRModal t, _rr_form { _rr = default_rr t state._domain } }
|
||||
H.modify_ _ { rr_modal = NewRRModal t, _rr_form { _rr = RR.default_rr t state._domain } }
|
||||
|
||||
-- | Delegation modal presents a simple form with two entries (chosen nameservers).
|
||||
CreateDelegationModal -> do
|
||||
|
@ -400,8 +397,7 @@ handleAction = case _ of
|
|||
-- | Update the delegation form.
|
||||
UpdateDelegationForm field -> do
|
||||
state <- H.get
|
||||
let newDelegationForm = Delegation.update_delegation_field state._delegation_form field
|
||||
H.modify_ _ { _delegation_form = newDelegationForm }
|
||||
H.modify_ _ { _delegation_form = Delegation.update state._delegation_form field }
|
||||
|
||||
-- | Validate any local RR with the new `_resources` and `_local_errors`.
|
||||
ValidateLocal -> do
|
||||
|
@ -487,7 +483,7 @@ handleAction = case _ of
|
|||
|
||||
RRUpdate value_to_update -> do
|
||||
state <- H.get
|
||||
H.modify_ _ { _rr_form = update_form state._rr_form value_to_update }
|
||||
H.modify_ _ { _rr_form = RR.update_form state._rr_form value_to_update }
|
||||
case value_to_update of
|
||||
SPF_Mechanism_Add -> handleAction $ ResetTemporaryValues
|
||||
SPF_Modifier_Add -> handleAction $ ResetTemporaryValues
|
||||
|
@ -531,7 +527,7 @@ handleQuery = case _ of
|
|||
pure (Just a)
|
||||
|
||||
where
|
||||
-- replace_entry :: ResourceRecord
|
||||
-- replace_entry :: RR.ResourceRecord
|
||||
replace_entry new_rr = do
|
||||
state <- H.get
|
||||
H.modify_ _ { _resources = A.filter (\rr -> rr.rrid /= new_rr.rrid) state._resources }
|
||||
|
@ -548,7 +544,7 @@ handleQuery = case _ of
|
|||
H.put $ add_RR state new_rr
|
||||
add_entries $ fromMaybe [] tail
|
||||
|
||||
add_RR :: State -> ResourceRecord -> State
|
||||
add_RR :: State -> RR.ResourceRecord -> State
|
||||
add_RR state new_rr = state { _resources = (state._resources <> [ new_rr ]) }
|
||||
|
||||
render_new_records :: forall (w :: Type). State -> HH.HTML w Action
|
||||
|
@ -605,23 +601,23 @@ loopE f a = case (A.head a) of
|
|||
Nothing -> pure unit
|
||||
Just xs -> loopE f xs
|
||||
|
||||
update_field :: ResourceRecord -> Field.Field -> ResourceRecord
|
||||
update_field :: RR.ResourceRecord -> RR.Field -> RR.ResourceRecord
|
||||
update_field rr updated_field = case updated_field of
|
||||
Field.Domain val -> rr { name = toLower val }
|
||||
Field.Target val -> rr { target = val }
|
||||
Field.TTL val -> rr { ttl = fromMaybe 0 (fromString val) }
|
||||
Field.Priority val -> rr { priority = fromString val }
|
||||
Field.Weight val -> rr { weight = fromString val }
|
||||
Field.Port val -> rr { port = fromString val }
|
||||
Field.SPF_v val -> rr { v = Just val }
|
||||
Field.SPF_mechanisms val -> rr { mechanisms = Just val }
|
||||
Field.SPF_modifiers val -> rr { modifiers = Just val }
|
||||
Field.SPF_q val -> rr { q = Just val }
|
||||
RR.Domain val -> rr { name = toLower val }
|
||||
RR.Target val -> rr { target = val }
|
||||
RR.TTL val -> rr { ttl = fromMaybe 0 (fromString val) }
|
||||
RR.Priority val -> rr { priority = fromString val }
|
||||
RR.Weight val -> rr { weight = fromString val }
|
||||
RR.Port val -> rr { port = fromString val }
|
||||
RR.SPF_v val -> rr { v = Just val }
|
||||
RR.SPF_mechanisms val -> rr { mechanisms = Just val }
|
||||
RR.SPF_modifiers val -> rr { modifiers = Just val }
|
||||
RR.SPF_q val -> rr { q = Just val }
|
||||
|
||||
Field.CAA_flag val ->
|
||||
let new_caa = (fromMaybe default_caa rr.caa) { flag = fromMaybe 0 $ fromString val }
|
||||
RR.CAA_flag val ->
|
||||
let new_caa = (fromMaybe RR.default_caa rr.caa) { flag = fromMaybe 0 $ fromString val }
|
||||
in rr { caa = Just new_caa }
|
||||
|
||||
Field.CAA_value val ->
|
||||
let new_caa = (fromMaybe default_caa rr.caa) { value = val }
|
||||
RR.CAA_value val ->
|
||||
let new_caa = (fromMaybe RR.default_caa rr.caa) { value = val }
|
||||
in rr { caa = Just new_caa }
|
||||
|
|
|
@ -20,14 +20,10 @@ import Halogen.HTML.Properties as HP
|
|||
import App.Type.RRId (RRId)
|
||||
import App.Type.DMARC as DMARC
|
||||
import App.Type.DKIM as DKIM
|
||||
import App.Type.Field as Field
|
||||
import App.Type.Delegation as Delegation
|
||||
import App.Templates.Table as Table
|
||||
import Data.String (toLower)
|
||||
|
||||
-- FIXME: this import is related to messy types. A replacement should be found.
|
||||
import App.Type.RRForm
|
||||
|
||||
import App.Type.RRModal (RRModal(..))
|
||||
import App.Type.AcceptedRRTypes (AcceptedRRTypes(..))
|
||||
|
||||
|
@ -84,14 +80,14 @@ delegation_modal selected_domain form action_update_form action_validate action_
|
|||
else HH.div_ [ ]
|
||||
|
||||
type Domain = String
|
||||
type ActionUpdateForm i = (Field.Field -> i)
|
||||
type ActionUpdateForm i = (RR.Field -> i)
|
||||
type ActionNewToken i = (RRId -> i)
|
||||
type ActionUpdateRR i = (RRUpdateValue -> i)
|
||||
type ActionValidateNewRR i = (AcceptedRRTypes -> i)
|
||||
type ActionValidateLocalRR :: forall k. k -> k
|
||||
type ActionValidateLocalRR i = i
|
||||
current_rr_modal :: forall w i.
|
||||
Domain -> RRForm -> RRModal
|
||||
Domain -> RR.Form -> RRModal
|
||||
-> ActionUpdateForm i -> ActionNewToken i
|
||||
-> ActionUpdateRR i -> ActionValidateNewRR i -> ActionValidateLocalRR i -> ActionCancelModal i
|
||||
-> HH.HTML w i
|
||||
|
@ -126,18 +122,18 @@ current_rr_modal selected_domain form rr_modal
|
|||
, render_introduction_text x
|
||||
, side_text_for_name_input ("domain" <> form._rr.rrtype)
|
||||
, Web.input_with_side_text ("domain" <> form._rr.rrtype) "" "www"
|
||||
(action_update_form <<< Field.Domain)
|
||||
(action_update_form <<< RR.Domain)
|
||||
form._rr.name
|
||||
display_domain_side
|
||||
, Web.box_input ("ttl" <> form._rr.rrtype) "TTL" "1800"
|
||||
(action_update_form <<< Field.TTL)
|
||||
(action_update_form <<< RR.TTL)
|
||||
(show form._rr.ttl)
|
||||
, case form._rr.rrtype of
|
||||
"AAAA" -> Web.box_input ("target" <> form._rr.rrtype) "Target" "2001:db8::1" (action_update_form <<< Field.Target) form._rr.target
|
||||
"TXT" -> Web.box_input ("target" <> form._rr.rrtype) "Your text" "blah blah" (action_update_form <<< Field.Target) form._rr.target
|
||||
"CNAME" -> Web.box_input ("target" <> form._rr.rrtype) "Target" "www" (action_update_form <<< Field.Target) form._rr.target
|
||||
"NS" -> Web.box_input ("target" <> form._rr.rrtype) "Target" "ns0.example.com." (action_update_form <<< Field.Target) form._rr.target
|
||||
_ -> Web.box_input ("target" <> form._rr.rrtype) "Target" "198.51.100.5" (action_update_form <<< Field.Target) form._rr.target
|
||||
"AAAA" -> Web.box_input ("target" <> form._rr.rrtype) "Target" "2001:db8::1" (action_update_form <<< RR.Target) form._rr.target
|
||||
"TXT" -> Web.box_input ("target" <> form._rr.rrtype) "Your text" "blah blah" (action_update_form <<< RR.Target) form._rr.target
|
||||
"CNAME" -> Web.box_input ("target" <> form._rr.rrtype) "Target" "www" (action_update_form <<< RR.Target) form._rr.target
|
||||
"NS" -> Web.box_input ("target" <> form._rr.rrtype) "Target" "ns0.example.com." (action_update_form <<< RR.Target) form._rr.target
|
||||
_ -> Web.box_input ("target" <> form._rr.rrtype) "Target" "198.51.100.5" (action_update_form <<< RR.Target) form._rr.target
|
||||
] <> case rr_modal of
|
||||
UpdateRRModal ->
|
||||
if A.elem form._rr.rrtype ["A", "AAAA"]
|
||||
|
@ -162,17 +158,17 @@ current_rr_modal selected_domain form rr_modal
|
|||
, Web.quote Explanations.mx_introduction
|
||||
, side_text_for_name_input "domainMX"
|
||||
, Web.input_with_side_text "domainMX" "" "www"
|
||||
(action_update_form <<< Field.Domain)
|
||||
(action_update_form <<< RR.Domain)
|
||||
form._rr.name
|
||||
display_domain_side
|
||||
, Web.box_input ("ttlMX") "TTL" "1800"
|
||||
(action_update_form <<< Field.TTL)
|
||||
(action_update_form <<< RR.TTL)
|
||||
(show form._rr.ttl)
|
||||
, Web.box_input ("targetMX") "Target" "www"
|
||||
(action_update_form <<< Field.Target)
|
||||
(action_update_form <<< RR.Target)
|
||||
form._rr.target
|
||||
, Web.box_input ("priorityMX") "Priority" "10"
|
||||
(action_update_form <<< Field.Priority)
|
||||
(action_update_form <<< RR.Priority)
|
||||
(maybe "" show form._rr.priority)
|
||||
]
|
||||
|
||||
|
@ -182,15 +178,15 @@ current_rr_modal selected_domain form rr_modal
|
|||
, Web.quote Explanations.caa_introduction
|
||||
, side_text_for_name_input "domainCAA"
|
||||
, Web.input_with_side_text "domainCAA" "" "www"
|
||||
(action_update_form <<< Field.Domain)
|
||||
(action_update_form <<< RR.Domain)
|
||||
form._rr.name
|
||||
display_domain_side
|
||||
, Web.box_input ("ttlCAA") "TTL" "1800"
|
||||
(action_update_form <<< Field.TTL)
|
||||
(action_update_form <<< RR.TTL)
|
||||
(show form._rr.ttl)
|
||||
, Web.hr
|
||||
, Web.box_input ("flagCAA") "Flag" ""
|
||||
(action_update_form <<< Field.CAA_flag)
|
||||
(action_update_form <<< RR.CAA_flag)
|
||||
(show (fromMaybe default_caa form._rr.caa).flag)
|
||||
, Web.selection_field'' "tagCAA" "Tag" (action_update_rr <<< CAA_tag) (A.zip CAA.tags_txt CAA.tags_raw)
|
||||
CAA.Issue
|
||||
|
@ -198,7 +194,7 @@ current_rr_modal selected_domain form rr_modal
|
|||
, HH.div [HP.classes [C.notification, C.is_warning]]
|
||||
[ Web.p "⚠️ CAA entries aren't thoroughly verified, yet. Also, do not put quotes."
|
||||
]
|
||||
, Web.box_input "valueCAA" "Value" "" (action_update_form <<< Field.CAA_value)
|
||||
, Web.box_input "valueCAA" "Value" "" (action_update_form <<< RR.CAA_value)
|
||||
(fromMaybe default_caa form._rr.caa).value
|
||||
]
|
||||
|
||||
|
@ -207,27 +203,27 @@ current_rr_modal selected_domain form rr_modal
|
|||
[ Web.quote Explanations.srv_introduction
|
||||
, render_errors
|
||||
, Web.box_input ("ttlSRV") "TTL" "1800"
|
||||
(action_update_form <<< Field.TTL)
|
||||
(action_update_form <<< RR.TTL)
|
||||
(show form._rr.ttl)
|
||||
, Web.box_input "domainSRV" "Service name" "service name"
|
||||
(action_update_form <<< Field.Domain)
|
||||
(action_update_form <<< RR.Domain)
|
||||
form._rr.name
|
||||
, Web.selection_field "protocolSRV" "Protocol" (action_update_rr <<< SRV_Protocol) RR.srv_protocols_txt
|
||||
(maybe "udp" (toLower <<< show) form._rr.protocol)
|
||||
, Web.box_input ("targetSRV") "Where the server is" "www"
|
||||
(action_update_form <<< Field.Target)
|
||||
(action_update_form <<< RR.Target)
|
||||
form._rr.target
|
||||
, Web.box_input ("portSRV") "Port of the service" "5061"
|
||||
(action_update_form <<< Field.Port)
|
||||
(action_update_form <<< RR.Port)
|
||||
(maybe "" show form._rr.port)
|
||||
, Web.quote [Web.p "The priority field is a numeric value that indicates the preference of the server (lower values indicate higher priority)."]
|
||||
, Web.box_input ("prioritySRV") "Priority" "10"
|
||||
(action_update_form <<< Field.Priority)
|
||||
(action_update_form <<< RR.Priority)
|
||||
(maybe "" show form._rr.priority)
|
||||
-- Web.quote Explanations.spf_introduction, Web.p "Modifiers provide additional instructions, such as explanations for SPF failures or redirecting SPF checks to another domain."
|
||||
, Web.quote [Web.p "The weight field is a relative weight used when multiple servers have the same priority, determining how often they should be used."]
|
||||
, Web.box_input ("weightSRV") "Weight" "100"
|
||||
(action_update_form <<< Field.Weight)
|
||||
(action_update_form <<< RR.Weight)
|
||||
(maybe "" show form._rr.weight)
|
||||
]
|
||||
|
||||
|
@ -237,15 +233,15 @@ current_rr_modal selected_domain form rr_modal
|
|||
, render_errors
|
||||
, side_text_for_name_input "domainSPF"
|
||||
, Web.input_with_side_text "domainSPF" "" "Let this alone."
|
||||
(action_update_form <<< Field.Domain)
|
||||
(action_update_form <<< RR.Domain)
|
||||
form._rr.name
|
||||
display_domain_side
|
||||
, Web.box_input "ttlSPF" "TTL" "1800"
|
||||
(action_update_form <<< Field.TTL)
|
||||
(action_update_form <<< RR.TTL)
|
||||
(show form._rr.ttl)
|
||||
--, case form._rr.v of
|
||||
-- Nothing -> Web.p "default value for the version (spf1)"
|
||||
-- Just v -> Web.box_input "vSPF" "Version" "spf1" (action_update_form <<< Field.SPF_v) v
|
||||
-- Just v -> Web.box_input "vSPF" "Version" "spf1" (action_update_form <<< RR.SPF_v) v
|
||||
, Web.hr
|
||||
, Web.box_with_tag [C.has_background_info_light] tag_mechanisms
|
||||
[ Web.quote [Web.p "Mechanisms specify which mail servers are allowed to send mail for the domain and how to evaluate the sending mail server’s IP address."]
|
||||
|
@ -291,11 +287,11 @@ current_rr_modal selected_domain form rr_modal
|
|||
, render_errors
|
||||
, side_text_for_name_input "domainDKIM"
|
||||
, Web.input_with_side_text "domainDKIM" "" "default._domainkey"
|
||||
(action_update_form <<< Field.Domain)
|
||||
(action_update_form <<< RR.Domain)
|
||||
form._rr.name
|
||||
display_domain_side
|
||||
, Web.box_input "ttlDKIM" "TTL" "1800"
|
||||
(action_update_form <<< Field.TTL)
|
||||
(action_update_form <<< RR.TTL)
|
||||
(show form._rr.ttl)
|
||||
, Web.hr
|
||||
, Web.quote Explanations.dkim_default_algorithms
|
||||
|
@ -317,10 +313,10 @@ current_rr_modal selected_domain form rr_modal
|
|||
, render_errors
|
||||
, side_text_for_name_input "domainDMARC"
|
||||
, Web.input_with_side_text "domainDMARC" "" "_dmarc"
|
||||
(action_update_form <<< Field.Domain)
|
||||
(action_update_form <<< RR.Domain)
|
||||
form._rr.name
|
||||
display_domain_side
|
||||
, Web.box_input "ttlDMARC" "TTL" "1800" (action_update_form <<< Field.TTL) (show form._rr.ttl)
|
||||
, Web.box_input "ttlDMARC" "TTL" "1800" (action_update_form <<< RR.TTL) (show form._rr.ttl)
|
||||
|
||||
, Web.hr
|
||||
, Web.quote Explanations.dmarc_policy
|
||||
|
|
|
@ -3,16 +3,15 @@ module App.Type.Delegation where
|
|||
import GenericParser.Parser as G
|
||||
import GenericParser.DomainParser.Common (DomainError) as DomainParser
|
||||
|
||||
-- | The required data needed to properly delegate a domain: two name servers.
|
||||
-- | The type also includes potential errors found while validating the data.
|
||||
type Form
|
||||
= { nameserver1 :: String
|
||||
, nameserver2 :: String
|
||||
, errors :: Array Error
|
||||
}
|
||||
|
||||
data Field
|
||||
= NameServer1 String
|
||||
| NameServer2 String
|
||||
|
||||
-- | Empty delegation form, with default inputs.
|
||||
mkEmptyDelegationForm :: Form
|
||||
mkEmptyDelegationForm
|
||||
= { nameserver1: "ns0.example.com"
|
||||
|
@ -20,11 +19,27 @@ mkEmptyDelegationForm
|
|||
, errors: []
|
||||
}
|
||||
|
||||
update_delegation_field :: Form -> Field -> Form
|
||||
update_delegation_field form updated_field = case updated_field of
|
||||
-- | What are the **fields** of our delegation form?
|
||||
-- | This *Field* data type provides a way to update the form with `update`.
|
||||
data Field
|
||||
= NameServer1 String
|
||||
| NameServer2 String
|
||||
|
||||
-- | Utility function to update a field of the form, based on the previous `Form` and `Field` types.
|
||||
-- |
|
||||
-- | RATIONALE: this utility function enables a generic way of handling field updates.
|
||||
-- | In Halogen, a single *Action* is required to update all fields:
|
||||
-- |```
|
||||
-- | UpdateDelegationForm field -> do
|
||||
-- | state <- H.get
|
||||
-- | H.modify_ _ { delegation_form = Delegation.update state.delegation_form field }
|
||||
-- |```
|
||||
update :: Form -> Field -> Form
|
||||
update form updated_field = case updated_field of
|
||||
NameServer1 val -> form { nameserver1 = val }
|
||||
NameServer2 val -> form { nameserver2 = val }
|
||||
|
||||
-- | Possible errors regarding the form (domain parsing errors).
|
||||
data Error
|
||||
= VENameServer1 (G.Error DomainParser.DomainError)
|
||||
| VENameServer2 (G.Error DomainParser.DomainError)
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
module App.Type.Field where
|
||||
|
||||
import App.Type.ResourceRecord as RR
|
||||
|
||||
data Field
|
||||
= Domain String
|
||||
| TTL String
|
||||
| Target String
|
||||
| Priority String
|
||||
| Weight String
|
||||
| Port String
|
||||
| SPF_v String
|
||||
| SPF_mechanisms (Array RR.Mechanism)
|
||||
| SPF_modifiers (Array RR.Modifier)
|
||||
| SPF_q RR.Qualifier
|
||||
|
||||
| CAA_flag String
|
||||
| CAA_value String
|
|
@ -1,242 +0,0 @@
|
|||
-- | `App.Type.RRForm` provides types used to manage the modification
|
||||
-- | of resource records.
|
||||
-- | FIXME: this state is messy AF and should be replaced.
|
||||
module App.Type.RRForm where
|
||||
|
||||
import Prelude (($), (-), (<>))
|
||||
import Utils (id, attach_id, remove_id)
|
||||
import App.Type.AcceptedRRTypes (AcceptedRRTypes(..))
|
||||
import App.Type.DKIM as DKIM
|
||||
import App.Type.DMARC as DMARC
|
||||
import Data.Maybe (Maybe(..), fromMaybe, maybe)
|
||||
import App.Type.ResourceRecord as RR
|
||||
import App.Type.CAA as CAA
|
||||
import App.Validation.Email as Email
|
||||
import App.Validation.DNS as Validation
|
||||
import Data.Array as A
|
||||
import Data.Either (Either(..))
|
||||
import Data.Int (fromString)
|
||||
|
||||
-- | TMP: temporary stored values regarding specific records such as SPF,
|
||||
-- | DKIM and DMARC.
|
||||
type TMP =
|
||||
{
|
||||
-- SPF details.
|
||||
spf :: { mechanism_q :: String
|
||||
, mechanism_t :: String
|
||||
, mechanism_v :: String
|
||||
, modifier_t :: String
|
||||
, modifier_v :: String
|
||||
}
|
||||
|
||||
-- DMARC details.
|
||||
, dmarc_mail :: String
|
||||
, dmarc_mail_limit :: Maybe Int
|
||||
, dmarc :: DMARC.DMARC
|
||||
|
||||
-- DKIM details.
|
||||
, dkim :: DKIM.DKIM
|
||||
}
|
||||
|
||||
-- | `RRForm` is the necessary state to modify a resource record.
|
||||
-- | It contains the currently manipulated record, detected errors, along with some temporary values.
|
||||
type RRForm =
|
||||
{ _rr :: RR.ResourceRecord
|
||||
, _errors :: Array Validation.Error
|
||||
, _dmarc_mail_errors :: Array Email.Error
|
||||
, _zonefile :: Maybe String
|
||||
, tmp :: TMP
|
||||
}
|
||||
|
||||
default_qualifier_str = "hard_fail" :: String
|
||||
default_caa = { flag: 0, tag: CAA.Issue, value: "letsencrypt.org" } :: CAA.CAA
|
||||
|
||||
default_rr :: AcceptedRRTypes -> String -> RR.ResourceRecord
|
||||
default_rr t domain =
|
||||
case t of
|
||||
A -> RR.emptyRR { rrtype = "A", name = "server1", target = "192.0.2.1" }
|
||||
AAAA -> RR.emptyRR { rrtype = "AAAA", name = "server1", target = "2001:db8::1" }
|
||||
TXT -> RR.emptyRR { rrtype = "TXT", name = "txt", target = "some text" }
|
||||
CNAME -> RR.emptyRR { rrtype = "CNAME", name = "www", target = "server1" }
|
||||
NS -> RR.emptyRR { rrtype = "NS", name = (domain <> "."), target = "ns0.example.com." }
|
||||
MX -> RR.emptyRR { rrtype = "MX", name = "mail", target = "server1", priority = Just 10 }
|
||||
CAA -> RR.emptyRR { rrtype = "CAA", name = "", target = "", caa = Just default_caa }
|
||||
SRV -> RR.emptyRR { rrtype = "SRV", name = "voip", target = "server1"
|
||||
, port = Just 5061, weight = Just 100, priority = Just 10, protocol = Just RR.TCP }
|
||||
SPF -> RR.emptyRR { rrtype = "SPF", name = "", target = ""
|
||||
, mechanisms = Just default_mechanisms, q = Just RR.HardFail }
|
||||
DKIM -> RR.emptyRR { rrtype = "DKIM", name = "default._domainkey", target = "" }
|
||||
DMARC -> RR.emptyRR { rrtype = "DMARC", name = "_dmarc", target = "" }
|
||||
where
|
||||
default_mechanisms = maybe [] (\x -> [x]) $ RR.to_mechanism "pass" "mx" ""
|
||||
|
||||
mkEmptyRRForm :: RRForm
|
||||
mkEmptyRRForm =
|
||||
{
|
||||
-- This is the state for the new RR modal.
|
||||
_rr: default_rr A ""
|
||||
-- List of errors within the form in new RR modal.
|
||||
, _errors: []
|
||||
, _dmarc_mail_errors: []
|
||||
, _zonefile: Nothing
|
||||
, tmp: { spf: { mechanism_q: "pass"
|
||||
, mechanism_t: "a"
|
||||
, mechanism_v: ""
|
||||
, modifier_t: "redirect"
|
||||
, modifier_v: ""
|
||||
}
|
||||
, dkim: DKIM.emptyDKIMRR
|
||||
, dmarc: DMARC.emptyDMARCRR
|
||||
, dmarc_mail: ""
|
||||
, dmarc_mail_limit: Nothing
|
||||
}
|
||||
}
|
||||
|
||||
data RRUpdateValue
|
||||
= CAA_tag Int
|
||||
| SRV_Protocol Int
|
||||
| SPF_Mechanism_q Int
|
||||
| SPF_Mechanism_t Int
|
||||
| SPF_Mechanism_v String
|
||||
| SPF_Modifier_t Int
|
||||
| SPF_Modifier_v String
|
||||
| SPF_Qualifier Int
|
||||
|
||||
-- | Remove a SPF mechanism of the currently modified (SPF) entry (see `_currentRR`).
|
||||
| SPF_remove_mechanism Int
|
||||
-- | Remove a SPF modifier of the currently modified (SPF) entry (see `_currentRR`).
|
||||
| SPF_remove_modifier Int
|
||||
|
||||
-- | Add a SPF mechanism to the currently modified (SPF) entry (see `_currentRR`).
|
||||
| SPF_Mechanism_Add
|
||||
-- | Add a SPF modifier to the currently modified (SPF) entry (see `_currentRR`).
|
||||
| SPF_Modifier_Add
|
||||
|
||||
-- | Change the temporary mail address for DMARC.
|
||||
| DMARC_mail String
|
||||
|
||||
-- | Change the temporary report size limit for DMARC.
|
||||
| DMARC_mail_limit String
|
||||
|
||||
-- | Change the requested report interval.
|
||||
| DMARC_ri String
|
||||
|
||||
-- | Add a new mail address to the DMARC rua list.
|
||||
| DMARC_rua_Add
|
||||
|
||||
-- | Add a new mail address to the DMARC ruf list.
|
||||
| DMARC_ruf_Add
|
||||
|
||||
-- | Remove a mail address of the DMARC rua list.
|
||||
| DMARC_remove_rua Int
|
||||
|
||||
-- | Remove a mail address of the DMARC ruf list.
|
||||
| DMARC_remove_ruf Int
|
||||
|
||||
| DMARC_policy Int
|
||||
| DMARC_sp_policy Int
|
||||
| DMARC_adkim Int
|
||||
| DMARC_aspf Int
|
||||
| DMARC_pct String
|
||||
| DMARC_fo Int
|
||||
|
||||
| DKIM_hash_algo Int
|
||||
| DKIM_sign_algo Int
|
||||
| DKIM_pubkey String
|
||||
| DKIM_note String
|
||||
|
||||
update_form :: RRForm -> RRUpdateValue -> RRForm
|
||||
update_form form new_field_value =
|
||||
case new_field_value of
|
||||
CAA_tag v ->
|
||||
let new_tag = fromMaybe CAA.Issue $ CAA.tags A.!! v
|
||||
new_value = case new_tag of
|
||||
CAA.Issue -> "letsencrypt.org"
|
||||
CAA.ContactEmail -> "contact@example.com"
|
||||
CAA.ContactPhone -> "0203040506"
|
||||
_ -> ""
|
||||
new_caa = (fromMaybe default_caa form._rr.caa) { tag = new_tag, value = new_value }
|
||||
in form { _rr { caa = Just new_caa } }
|
||||
|
||||
SRV_Protocol v -> form { _rr { protocol = RR.srv_protocols A.!! v } }
|
||||
SPF_Mechanism_q v -> form { tmp { spf { mechanism_q = maybe "pass" id $ RR.qualifier_types A.!! v }}}
|
||||
SPF_Mechanism_t v -> form { tmp { spf { mechanism_t = maybe "a" id $ RR.mechanism_types A.!! v }}}
|
||||
SPF_Mechanism_v v -> form { tmp { spf { mechanism_v = v }}}
|
||||
SPF_Modifier_t v -> form { tmp { spf { modifier_t = maybe "redirect" id $ RR.modifier_types A.!! v }}}
|
||||
SPF_Modifier_v v -> form { tmp { spf { modifier_v = v }}}
|
||||
SPF_Qualifier v -> form { _rr { q = RR.qualifiers A.!! v }}
|
||||
SPF_remove_mechanism i ->
|
||||
form { _rr { mechanisms = case form._rr.mechanisms of
|
||||
Just ms -> Just (remove_id i $ attach_id 0 ms)
|
||||
Nothing -> Nothing
|
||||
} }
|
||||
SPF_remove_modifier i ->
|
||||
form { _rr { modifiers = case form._rr.modifiers of
|
||||
Just ms -> Just (remove_id i $ attach_id 0 ms)
|
||||
Nothing -> Nothing
|
||||
} }
|
||||
|
||||
SPF_Mechanism_Add ->
|
||||
let m = form._rr.mechanisms
|
||||
m_q = form.tmp.spf.mechanism_q
|
||||
m_t = form.tmp.spf.mechanism_t
|
||||
m_v = form.tmp.spf.mechanism_v
|
||||
new_list_of_mechanisms = maybe [] id m <> maybe [] (\x -> [x]) (RR.to_mechanism m_q m_t m_v)
|
||||
new_value = case new_list_of_mechanisms of
|
||||
[] -> Nothing
|
||||
v -> Just v
|
||||
in form { _rr { mechanisms = new_value }}
|
||||
|
||||
SPF_Modifier_Add ->
|
||||
let m = form._rr.modifiers
|
||||
m_t = form.tmp.spf.modifier_t
|
||||
m_v = form.tmp.spf.modifier_v
|
||||
new_list_of_modifiers = maybe [] id m <> maybe [] (\x -> [x]) (RR.to_modifier m_t m_v)
|
||||
new_value = case new_list_of_modifiers of
|
||||
[] -> Nothing
|
||||
v -> Just v
|
||||
in form { _rr { modifiers = new_value }}
|
||||
|
||||
DMARC_mail v -> form { tmp { dmarc_mail = v } }
|
||||
DMARC_mail_limit v -> form { tmp { dmarc_mail_limit = Just $ fromMaybe 0 $ fromString v } }
|
||||
DMARC_ri v -> form { tmp { dmarc { ri = fromString v } } }
|
||||
DMARC_rua_Add ->
|
||||
case Email.email form.tmp.dmarc_mail of
|
||||
Left errors -> form { _dmarc_mail_errors = errors }
|
||||
Right _ ->
|
||||
let current_ruas = fromMaybe [] form.tmp.dmarc.rua
|
||||
new_list = current_ruas <> [ {mail: form.tmp.dmarc_mail, limit: form.tmp.dmarc_mail_limit} ]
|
||||
in form { tmp { dmarc { rua = Just new_list }}}
|
||||
|
||||
DMARC_ruf_Add ->
|
||||
case Email.email form.tmp.dmarc_mail of
|
||||
Left errors -> form { _dmarc_mail_errors = errors }
|
||||
Right _ ->
|
||||
let current_rufs = fromMaybe [] form.tmp.dmarc.ruf
|
||||
new_list = current_rufs <> [ {mail: form.tmp.dmarc_mail, limit: form.tmp.dmarc_mail_limit} ]
|
||||
in form { tmp { dmarc { ruf = Just new_list }}}
|
||||
|
||||
DMARC_remove_rua i ->
|
||||
let current_ruas = fromMaybe [] form.tmp.dmarc.rua
|
||||
new_value = case (remove_id i $ attach_id 0 current_ruas) of
|
||||
[] -> Nothing
|
||||
v -> Just v
|
||||
in form { tmp { dmarc { rua = new_value } } }
|
||||
|
||||
DMARC_remove_ruf i ->
|
||||
let current_rufs = fromMaybe [] form.tmp.dmarc.ruf
|
||||
new_value = case (remove_id i $ attach_id 0 current_rufs) of
|
||||
[] -> Nothing
|
||||
v -> Just v
|
||||
in form { tmp { dmarc { ruf = new_value } } }
|
||||
|
||||
DMARC_policy v -> form { tmp { dmarc { p = fromMaybe DMARC.None $ DMARC.policies A.!! v } } }
|
||||
DMARC_sp_policy v -> form { tmp { dmarc { sp = DMARC.policies A.!! (v - 1) } } }
|
||||
DMARC_adkim v -> form { tmp { dmarc { adkim = DMARC.consistency_policies A.!! (v - 1) } } }
|
||||
DMARC_aspf v -> form { tmp { dmarc { aspf = DMARC.consistency_policies A.!! (v - 1) } } }
|
||||
DMARC_pct v -> form { tmp { dmarc { pct = Just $ fromMaybe 100 (fromString v) } } }
|
||||
DMARC_fo v -> form { tmp { dmarc { fo = DMARC.report_occasions A.!! (v - 1) } } }
|
||||
DKIM_hash_algo v -> form { tmp { dkim { h = DKIM.hash_algos A.!! v } } }
|
||||
DKIM_sign_algo v -> form { tmp { dkim { k = DKIM.sign_algos A.!! v } } }
|
||||
DKIM_pubkey v -> form { tmp { dkim { p = v } } }
|
||||
DKIM_note v -> form { tmp { dkim { n = Just v } } }
|
|
@ -1,16 +1,24 @@
|
|||
module App.Type.ResourceRecord where
|
||||
|
||||
import Prelude ((<>), map, bind, pure, class Show)
|
||||
import Prelude (($), (-), (<>), map, bind, pure, class Show)
|
||||
-- import Data.String (toLower)
|
||||
import Data.Generic.Rep (class Generic)
|
||||
import App.Type.GenericSerialization (generic_serialization)
|
||||
import Data.Show.Generic (genericShow)
|
||||
|
||||
import Data.Maybe (Maybe(..), maybe)
|
||||
import App.Type.AcceptedRRTypes (AcceptedRRTypes(..))
|
||||
import Data.Array as A
|
||||
import Data.Maybe (Maybe(..), fromMaybe, maybe)
|
||||
import Data.Either (Either(..))
|
||||
|
||||
import Utils (id, attach_id, remove_id)
|
||||
|
||||
import App.Validation.Email as Email
|
||||
|
||||
import Data.Codec.Argonaut (JsonCodec)
|
||||
import Data.Codec.Argonaut as CA
|
||||
import Data.Codec.Argonaut.Record as CAR
|
||||
import Data.Int (fromString)
|
||||
|
||||
import App.Type.DKIM as DKIM
|
||||
import App.Type.DMARC as DMARC
|
||||
|
@ -281,3 +289,288 @@ str_to_srv_protocol = case _ of
|
|||
"tcp" -> Just TCP
|
||||
"udp" -> Just UDP
|
||||
_ -> Nothing
|
||||
|
||||
data Field
|
||||
= Domain String
|
||||
| TTL String
|
||||
| Target String
|
||||
| Priority String
|
||||
| Weight String
|
||||
| Port String
|
||||
| SPF_v String
|
||||
| SPF_mechanisms (Array Mechanism)
|
||||
| SPF_modifiers (Array Modifier)
|
||||
| SPF_q Qualifier
|
||||
|
||||
| CAA_flag String
|
||||
| CAA_value String
|
||||
|
||||
-- | TMP: temporary stored values regarding specific records such as SPF,
|
||||
-- | DKIM and DMARC.
|
||||
type TMP =
|
||||
{
|
||||
-- SPF details.
|
||||
spf :: { mechanism_q :: String
|
||||
, mechanism_t :: String
|
||||
, mechanism_v :: String
|
||||
, modifier_t :: String
|
||||
, modifier_v :: String
|
||||
}
|
||||
|
||||
-- DMARC details.
|
||||
, dmarc_mail :: String
|
||||
, dmarc_mail_limit :: Maybe Int
|
||||
, dmarc :: DMARC.DMARC
|
||||
|
||||
-- DKIM details.
|
||||
, dkim :: DKIM.DKIM
|
||||
}
|
||||
|
||||
-- | `Form` is the necessary state to modify a resource record.
|
||||
-- | It contains the currently manipulated record, detected errors, along with some temporary values.
|
||||
-- | FIXME: this form is messy AF and should be replaced.
|
||||
type Form =
|
||||
{ _rr :: ResourceRecord
|
||||
, _errors :: Array Validation.Error
|
||||
, _dmarc_mail_errors :: Array Email.Error
|
||||
, _zonefile :: Maybe String
|
||||
, tmp :: TMP
|
||||
}
|
||||
|
||||
default_qualifier_str = "hard_fail" :: String
|
||||
default_caa = { flag: 0, tag: CAA.Issue, value: "letsencrypt.org" } :: CAA.CAA
|
||||
|
||||
default_rr :: AcceptedRRTypes -> String -> ResourceRecord
|
||||
default_rr t domain =
|
||||
case t of
|
||||
A -> emptyRR { rrtype = "A", name = "server1", target = "192.0.2.1" }
|
||||
AAAA -> emptyRR { rrtype = "AAAA", name = "server1", target = "2001:db8::1" }
|
||||
TXT -> emptyRR { rrtype = "TXT", name = "txt", target = "some text" }
|
||||
CNAME -> emptyRR { rrtype = "CNAME", name = "www", target = "server1" }
|
||||
NS -> emptyRR { rrtype = "NS", name = (domain <> "."), target = "ns0.example.com." }
|
||||
MX -> emptyRR { rrtype = "MX", name = "mail", target = "server1", priority = Just 10 }
|
||||
CAA -> emptyRR { rrtype = "CAA", name = "", target = "", caa = Just default_caa }
|
||||
SRV -> emptyRR { rrtype = "SRV", name = "voip", target = "server1"
|
||||
, port = Just 5061, weight = Just 100, priority = Just 10, protocol = Just TCP }
|
||||
SPF -> emptyRR { rrtype = "SPF", name = "", target = ""
|
||||
, mechanisms = Just default_mechanisms, q = Just HardFail }
|
||||
DKIM -> emptyRR { rrtype = "DKIM", name = "default._domainkey", target = "" }
|
||||
DMARC -> emptyRR { rrtype = "DMARC", name = "_dmarc", target = "" }
|
||||
where
|
||||
default_mechanisms = maybe [] (\x -> [x]) $ to_mechanism "pass" "mx" ""
|
||||
|
||||
mkEmptyRRForm :: Form
|
||||
mkEmptyRRForm =
|
||||
{
|
||||
-- This is the state for the new RR modal.
|
||||
_rr: default_rr A ""
|
||||
-- List of errors within the form in new RR modal.
|
||||
, _errors: []
|
||||
, _dmarc_mail_errors: []
|
||||
, _zonefile: Nothing
|
||||
, tmp: { spf: { mechanism_q: "pass"
|
||||
, mechanism_t: "a"
|
||||
, mechanism_v: ""
|
||||
, modifier_t: "redirect"
|
||||
, modifier_v: ""
|
||||
}
|
||||
, dkim: DKIM.emptyDKIMRR
|
||||
, dmarc: DMARC.emptyDMARCRR
|
||||
, dmarc_mail: ""
|
||||
, dmarc_mail_limit: Nothing
|
||||
}
|
||||
}
|
||||
|
||||
data RRUpdateValue
|
||||
= CAA_tag Int
|
||||
| SRV_Protocol Int
|
||||
| SPF_Mechanism_q Int
|
||||
| SPF_Mechanism_t Int
|
||||
| SPF_Mechanism_v String
|
||||
| SPF_Modifier_t Int
|
||||
| SPF_Modifier_v String
|
||||
| SPF_Qualifier Int
|
||||
|
||||
-- | Remove a SPF mechanism of the currently modified (SPF) entry (see `_currentRR`).
|
||||
| SPF_remove_mechanism Int
|
||||
-- | Remove a SPF modifier of the currently modified (SPF) entry (see `_currentRR`).
|
||||
| SPF_remove_modifier Int
|
||||
|
||||
-- | Add a SPF mechanism to the currently modified (SPF) entry (see `_currentRR`).
|
||||
| SPF_Mechanism_Add
|
||||
-- | Add a SPF modifier to the currently modified (SPF) entry (see `_currentRR`).
|
||||
| SPF_Modifier_Add
|
||||
|
||||
-- | Change the temporary mail address for DMARC.
|
||||
| DMARC_mail String
|
||||
|
||||
-- | Change the temporary report size limit for DMARC.
|
||||
| DMARC_mail_limit String
|
||||
|
||||
-- | Change the requested report interval.
|
||||
| DMARC_ri String
|
||||
|
||||
-- | Add a new mail address to the DMARC rua list.
|
||||
| DMARC_rua_Add
|
||||
|
||||
-- | Add a new mail address to the DMARC ruf list.
|
||||
| DMARC_ruf_Add
|
||||
|
||||
-- | Remove a mail address of the DMARC rua list.
|
||||
| DMARC_remove_rua Int
|
||||
|
||||
-- | Remove a mail address of the DMARC ruf list.
|
||||
| DMARC_remove_ruf Int
|
||||
|
||||
| DMARC_policy Int
|
||||
| DMARC_sp_policy Int
|
||||
| DMARC_adkim Int
|
||||
| DMARC_aspf Int
|
||||
| DMARC_pct String
|
||||
| DMARC_fo Int
|
||||
|
||||
| DKIM_hash_algo Int
|
||||
| DKIM_sign_algo Int
|
||||
| DKIM_pubkey String
|
||||
| DKIM_note String
|
||||
|
||||
update_form :: Form -> RRUpdateValue -> Form
|
||||
update_form form new_field_value =
|
||||
case new_field_value of
|
||||
CAA_tag v ->
|
||||
let new_tag = fromMaybe CAA.Issue $ CAA.tags A.!! v
|
||||
new_value = case new_tag of
|
||||
CAA.Issue -> "letsencrypt.org"
|
||||
CAA.ContactEmail -> "contact@example.com"
|
||||
CAA.ContactPhone -> "0203040506"
|
||||
_ -> ""
|
||||
new_caa = (fromMaybe default_caa form._rr.caa) { tag = new_tag, value = new_value }
|
||||
in form { _rr { caa = Just new_caa } }
|
||||
|
||||
SRV_Protocol v -> form { _rr { protocol = srv_protocols A.!! v } }
|
||||
SPF_Mechanism_q v -> form { tmp { spf { mechanism_q = maybe "pass" id $ qualifier_types A.!! v }}}
|
||||
SPF_Mechanism_t v -> form { tmp { spf { mechanism_t = maybe "a" id $ mechanism_types A.!! v }}}
|
||||
SPF_Mechanism_v v -> form { tmp { spf { mechanism_v = v }}}
|
||||
SPF_Modifier_t v -> form { tmp { spf { modifier_t = maybe "redirect" id $ modifier_types A.!! v }}}
|
||||
SPF_Modifier_v v -> form { tmp { spf { modifier_v = v }}}
|
||||
SPF_Qualifier v -> form { _rr { q = qualifiers A.!! v }}
|
||||
SPF_remove_mechanism i ->
|
||||
form { _rr { mechanisms = case form._rr.mechanisms of
|
||||
Just ms -> Just (remove_id i $ attach_id 0 ms)
|
||||
Nothing -> Nothing
|
||||
} }
|
||||
SPF_remove_modifier i ->
|
||||
form { _rr { modifiers = case form._rr.modifiers of
|
||||
Just ms -> Just (remove_id i $ attach_id 0 ms)
|
||||
Nothing -> Nothing
|
||||
} }
|
||||
|
||||
SPF_Mechanism_Add ->
|
||||
let m = form._rr.mechanisms
|
||||
m_q = form.tmp.spf.mechanism_q
|
||||
m_t = form.tmp.spf.mechanism_t
|
||||
m_v = form.tmp.spf.mechanism_v
|
||||
new_list_of_mechanisms = maybe [] id m <> maybe [] (\x -> [x]) (to_mechanism m_q m_t m_v)
|
||||
new_value = case new_list_of_mechanisms of
|
||||
[] -> Nothing
|
||||
v -> Just v
|
||||
in form { _rr { mechanisms = new_value }}
|
||||
|
||||
SPF_Modifier_Add ->
|
||||
let m = form._rr.modifiers
|
||||
m_t = form.tmp.spf.modifier_t
|
||||
m_v = form.tmp.spf.modifier_v
|
||||
new_list_of_modifiers = maybe [] id m <> maybe [] (\x -> [x]) (to_modifier m_t m_v)
|
||||
new_value = case new_list_of_modifiers of
|
||||
[] -> Nothing
|
||||
v -> Just v
|
||||
in form { _rr { modifiers = new_value }}
|
||||
|
||||
DMARC_mail v -> form { tmp { dmarc_mail = v } }
|
||||
DMARC_mail_limit v -> form { tmp { dmarc_mail_limit = Just $ fromMaybe 0 $ fromString v } }
|
||||
DMARC_ri v -> form { tmp { dmarc { ri = fromString v } } }
|
||||
DMARC_rua_Add ->
|
||||
case Email.email form.tmp.dmarc_mail of
|
||||
Left errors -> form { _dmarc_mail_errors = errors }
|
||||
Right _ ->
|
||||
let current_ruas = fromMaybe [] form.tmp.dmarc.rua
|
||||
new_list = current_ruas <> [ {mail: form.tmp.dmarc_mail, limit: form.tmp.dmarc_mail_limit} ]
|
||||
in form { tmp { dmarc { rua = Just new_list }}}
|
||||
|
||||
DMARC_ruf_Add ->
|
||||
case Email.email form.tmp.dmarc_mail of
|
||||
Left errors -> form { _dmarc_mail_errors = errors }
|
||||
Right _ ->
|
||||
let current_rufs = fromMaybe [] form.tmp.dmarc.ruf
|
||||
new_list = current_rufs <> [ {mail: form.tmp.dmarc_mail, limit: form.tmp.dmarc_mail_limit} ]
|
||||
in form { tmp { dmarc { ruf = Just new_list }}}
|
||||
|
||||
DMARC_remove_rua i ->
|
||||
let current_ruas = fromMaybe [] form.tmp.dmarc.rua
|
||||
new_value = case (remove_id i $ attach_id 0 current_ruas) of
|
||||
[] -> Nothing
|
||||
v -> Just v
|
||||
in form { tmp { dmarc { rua = new_value } } }
|
||||
|
||||
DMARC_remove_ruf i ->
|
||||
let current_rufs = fromMaybe [] form.tmp.dmarc.ruf
|
||||
new_value = case (remove_id i $ attach_id 0 current_rufs) of
|
||||
[] -> Nothing
|
||||
v -> Just v
|
||||
in form { tmp { dmarc { ruf = new_value } } }
|
||||
|
||||
DMARC_policy v -> form { tmp { dmarc { p = fromMaybe DMARC.None $ DMARC.policies A.!! v } } }
|
||||
DMARC_sp_policy v -> form { tmp { dmarc { sp = DMARC.policies A.!! (v - 1) } } }
|
||||
DMARC_adkim v -> form { tmp { dmarc { adkim = DMARC.consistency_policies A.!! (v - 1) } } }
|
||||
DMARC_aspf v -> form { tmp { dmarc { aspf = DMARC.consistency_policies A.!! (v - 1) } } }
|
||||
DMARC_pct v -> form { tmp { dmarc { pct = Just $ fromMaybe 100 (fromString v) } } }
|
||||
DMARC_fo v -> form { tmp { dmarc { fo = DMARC.report_occasions A.!! (v - 1) } } }
|
||||
DKIM_hash_algo v -> form { tmp { dkim { h = DKIM.hash_algos A.!! v } } }
|
||||
DKIM_sign_algo v -> form { tmp { dkim { k = DKIM.sign_algos A.!! v } } }
|
||||
DKIM_pubkey v -> form { tmp { dkim { p = v } } }
|
||||
DKIM_note v -> form { tmp { dkim { n = Just v } } }
|
||||
|
||||
-- | Errors that might be catched in for the form upon validation (`App.Validation.DNS`).
|
||||
-- |
|
||||
-- | **History:**
|
||||
-- | The module once used dedicated types for each type of RR.
|
||||
-- | That comes with several advantages.
|
||||
-- | First, type verification was a thing, and function were dedicated to a certain type of record.
|
||||
-- | Second, these dedicated types used strings for their fields,
|
||||
-- | which simplifies the typing when dealing with forms.
|
||||
-- | Finally, the validation was a way to convert dedicated types (used in forms)
|
||||
-- | to the general type (used for network serialization).
|
||||
-- | This ensures each resource record is verified before being sent to `dnsmanagerd`.
|
||||
-- |
|
||||
-- | The problem is that, with dedicated types, you are then required to have dedicated functions.
|
||||
-- | Conversion functions are also required.
|
||||
-- |
|
||||
-- | Maybe the code will change again in the future, but for now it will be enough.
|
||||
|
||||
data Error
|
||||
= UNKNOWN
|
||||
| VEIPv4 (G.Error IPAddress.IPv4Error)
|
||||
| VEIPv6 (G.Error IPAddress.IPv6Error)
|
||||
| VEName (G.Error DomainParser.DomainError)
|
||||
| VETTL Int Int Int
|
||||
| VETXT (G.Error TXTError)
|
||||
| VECNAME (G.Error DomainParser.DomainError)
|
||||
| VENS (G.Error DomainParser.DomainError)
|
||||
| VEMX (G.Error DomainParser.DomainError)
|
||||
| VEPriority Int Int Int
|
||||
| VESRV (G.Error DomainParser.DomainError)
|
||||
| VEPort Int Int Int
|
||||
| VEWeight Int Int Int
|
||||
| VEDMARCpct Int Int Int
|
||||
| VEDMARCri Int Int Int
|
||||
|
||||
| VECAAflag Int Int Int -- CAA flag should be between 0 and 255 (1 byte).
|
||||
|
||||
-- SPF
|
||||
| VESPFMechanismName (G.Error DomainParser.DomainError)
|
||||
| VESPFMechanismIPv4 (G.Error IPAddress.IPv4Error)
|
||||
| VESPFMechanismIPv6 (G.Error IPAddress.IPv6Error)
|
||||
|
||||
| VESPFModifierName (G.Error DomainParser.DomainError)
|
||||
|
||||
| DKIMInvalidKeySize Int Int
|
||||
|
|
|
@ -11,7 +11,7 @@ import Data.String as S
|
|||
import Data.Validation.Semigroup (V, invalid, toEither)
|
||||
|
||||
import App.Type.ResourceRecord (ResourceRecord, emptyRR, Mechanism, Modifier)
|
||||
import App.Type.ResourceRecord (MechanismType(..), ModifierType(..)) as RR
|
||||
import App.Type.ResourceRecord as RR
|
||||
import GenericParser.SomeParsers as SomeParsers
|
||||
import GenericParser.Parser as G
|
||||
import GenericParser.DomainParser.Common (DomainError) as DomainParser
|
||||
|
@ -25,50 +25,7 @@ import App.Type.CAA as CAA
|
|||
|
||||
import Utils (id)
|
||||
|
||||
-- | **History:**
|
||||
-- | The module once used dedicated types for each type of RR.
|
||||
-- | That comes with several advantages.
|
||||
-- | First, type verification was a thing, and function were dedicated to a certain type of record.
|
||||
-- | Second, these dedicated types used strings for their fields,
|
||||
-- | which simplifies the typing when dealing with forms.
|
||||
-- | Finally, the validation was a way to convert dedicated types (used in forms)
|
||||
-- | to the general type (used for network serialization).
|
||||
-- | This ensures each resource record is verified before being sent to `dnsmanagerd`.
|
||||
-- |
|
||||
-- | The problem is that, with dedicated types, you are then required to have dedicated functions.
|
||||
-- | Conversion functions are also required.
|
||||
-- |
|
||||
-- | Maybe the code will change again in the future, but for now it will be enough.
|
||||
|
||||
data Error
|
||||
= UNKNOWN
|
||||
| VEIPv4 (G.Error IPAddress.IPv4Error)
|
||||
| VEIPv6 (G.Error IPAddress.IPv6Error)
|
||||
| VEName (G.Error DomainParser.DomainError)
|
||||
| VETTL Int Int Int
|
||||
| VETXT (G.Error TXTError)
|
||||
| VECNAME (G.Error DomainParser.DomainError)
|
||||
| VENS (G.Error DomainParser.DomainError)
|
||||
| VEMX (G.Error DomainParser.DomainError)
|
||||
| VEPriority Int Int Int
|
||||
| VESRV (G.Error DomainParser.DomainError)
|
||||
| VEPort Int Int Int
|
||||
| VEWeight Int Int Int
|
||||
| VEDMARCpct Int Int Int
|
||||
| VEDMARCri Int Int Int
|
||||
|
||||
| VECAAflag Int Int Int -- CAA flag should be between 0 and 255 (1 byte).
|
||||
|
||||
-- SPF
|
||||
| VESPFMechanismName (G.Error DomainParser.DomainError)
|
||||
| VESPFMechanismIPv4 (G.Error IPAddress.IPv4Error)
|
||||
| VESPFMechanismIPv6 (G.Error IPAddress.IPv6Error)
|
||||
|
||||
| VESPFModifierName (G.Error DomainParser.DomainError)
|
||||
|
||||
| DKIMInvalidKeySize Int Int
|
||||
|
||||
type AVErrors = Array Error
|
||||
type AVErrors = Array RR.Error
|
||||
|
||||
-- | Current default values.
|
||||
min_ttl = 30 :: Int
|
||||
|
@ -113,12 +70,12 @@ txt_parser = do pos <- G.current_position
|
|||
|
||||
-- | `parse` enables 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) -> Error) -> V (Array Error) v
|
||||
parse :: forall e v. G.Parser e v -> String -> ((G.Error e) -> RR.Error) -> V (Array RR.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
|
||||
|
||||
validationA :: ResourceRecord -> V (Array Error) ResourceRecord
|
||||
validationA :: ResourceRecord -> V (Array RR.Error) ResourceRecord
|
||||
validationA form = ado
|
||||
name <- parse DomainParser.name form.name VEName
|
||||
ttl <- is_between min_ttl max_ttl form.ttl VETTL
|
||||
|
@ -126,7 +83,7 @@ validationA form = ado
|
|||
in emptyRR { rrid = form.rrid, readonly = form.readonly, rrtype = "A", name = name, ttl = ttl, target = target
|
||||
, token = form.token }
|
||||
|
||||
validationAAAA :: ResourceRecord -> V (Array Error) ResourceRecord
|
||||
validationAAAA :: ResourceRecord -> V (Array RR.Error) ResourceRecord
|
||||
validationAAAA form = ado
|
||||
name <- parse DomainParser.name form.name VEName
|
||||
ttl <- is_between min_ttl max_ttl form.ttl VETTL
|
||||
|
@ -135,33 +92,33 @@ validationAAAA form = ado
|
|||
in emptyRR { rrid = form.rrid, readonly = form.readonly, rrtype = "AAAA", name = name, ttl = ttl, target = target
|
||||
, token = form.token }
|
||||
|
||||
validationTXT :: ResourceRecord -> V (Array Error) ResourceRecord
|
||||
validationTXT :: ResourceRecord -> V (Array RR.Error) ResourceRecord
|
||||
validationTXT form = ado
|
||||
name <- parse DomainParser.name form.name VEName
|
||||
ttl <- is_between min_ttl max_ttl form.ttl VETTL
|
||||
target <- parse txt_parser form.target VETXT
|
||||
in emptyRR { rrid = form.rrid, readonly = form.readonly, rrtype = "TXT", name = name, ttl = ttl, target = target }
|
||||
|
||||
validationCNAME :: ResourceRecord -> V (Array Error) ResourceRecord
|
||||
validationCNAME :: ResourceRecord -> V (Array RR.Error) ResourceRecord
|
||||
validationCNAME form = ado
|
||||
name <- parse DomainParser.name form.name VEName
|
||||
ttl <- is_between min_ttl max_ttl form.ttl VETTL
|
||||
target <- parse DomainParser.sub_eof form.target VECNAME
|
||||
in emptyRR { rrid = form.rrid, readonly = form.readonly, rrtype = "CNAME", name = name, ttl = ttl, target = target }
|
||||
|
||||
validationNS :: ResourceRecord -> V (Array Error) ResourceRecord
|
||||
validationNS :: ResourceRecord -> V (Array RR.Error) ResourceRecord
|
||||
validationNS form = ado
|
||||
name <- parse DomainParser.name form.name VEName
|
||||
ttl <- is_between min_ttl max_ttl form.ttl VETTL
|
||||
target <- parse DomainParser.sub_eof form.target VENS
|
||||
in emptyRR { rrid = form.rrid, readonly = form.readonly, rrtype = "NS", name = name, ttl = ttl, target = target }
|
||||
|
||||
is_between :: Int -> Int -> Int -> (Int -> Int -> Int -> Error) -> V (Array Error) Int
|
||||
is_between :: Int -> Int -> Int -> (Int -> Int -> Int -> RR.Error) -> V (Array RR.Error) Int
|
||||
is_between min max n ve = if between min max n
|
||||
then pure n
|
||||
else invalid [ve min max n]
|
||||
|
||||
validationMX :: ResourceRecord -> V (Array Error) ResourceRecord
|
||||
validationMX :: ResourceRecord -> V (Array RR.Error) ResourceRecord
|
||||
validationMX form = ado
|
||||
name <- parse DomainParser.name form.name VEName
|
||||
ttl <- is_between min_ttl max_ttl form.ttl VETTL
|
||||
|
@ -170,7 +127,7 @@ validationMX form = ado
|
|||
in emptyRR { rrid = form.rrid, readonly = form.readonly, rrtype = "MX"
|
||||
, name = name, ttl = ttl, target = target, priority = Just priority }
|
||||
|
||||
validationSRV :: ResourceRecord -> V (Array Error) ResourceRecord
|
||||
validationSRV :: ResourceRecord -> V (Array RR.Error) ResourceRecord
|
||||
validationSRV form = ado
|
||||
name <- parse DomainParser.name form.name VEName
|
||||
ttl <- is_between min_ttl max_ttl form.ttl VETTL
|
||||
|
@ -214,7 +171,7 @@ or_nothing p = do v <- G.tryMaybe p
|
|||
-- | Also, some of them should accept a CIDR, which currently isn't a thing.
|
||||
-- |
|
||||
-- | TODO: I don't intend to implement the full RFC7208, but accepting CIDR can be done.
|
||||
validate_SPF_mechanism :: Mechanism -> V (Array Error) Mechanism
|
||||
validate_SPF_mechanism :: Mechanism -> V (Array RR.Error) Mechanism
|
||||
validate_SPF_mechanism m = case m.t of
|
||||
-- RFC: `a = "a" [ ":" domain-spec ] [ dual-cidr-length ]`
|
||||
RR.A -> test (or_nothing DomainParser.sub_eof) VESPFMechanismName
|
||||
|
@ -238,12 +195,12 @@ validate_SPF_mechanism m = case m.t of
|
|||
RR.INCLUDE -> test DomainParser.sub_eof VESPFMechanismName
|
||||
|
||||
where
|
||||
test :: forall e. G.Parser e String -> ((G.Error e) -> Error) -> V (Array Error) Mechanism
|
||||
test :: forall e. G.Parser e String -> ((G.Error e) -> RR.Error) -> V (Array RR.Error) Mechanism
|
||||
test p e = ado
|
||||
name <- parse p m.v e
|
||||
in first m name -- name is discarded
|
||||
|
||||
validate_SPF_modifier :: Modifier -> V (Array Error) Modifier
|
||||
validate_SPF_modifier :: Modifier -> V (Array RR.Error) Modifier
|
||||
validate_SPF_modifier m = case m.t of
|
||||
RR.EXP -> ado
|
||||
name <- parse DomainParser.sub_eof m.v VESPFModifierName
|
||||
|
@ -252,7 +209,7 @@ validate_SPF_modifier m = case m.t of
|
|||
name <- parse DomainParser.sub_eof m.v VESPFModifierName
|
||||
in first m name -- name is discarded
|
||||
|
||||
validationSPF :: ResourceRecord -> V (Array Error) ResourceRecord
|
||||
validationSPF :: ResourceRecord -> V (Array RR.Error) ResourceRecord
|
||||
validationSPF form = ado
|
||||
name <- parse DomainParser.name form.name VEName
|
||||
ttl <- is_between min_ttl max_ttl form.ttl VETTL
|
||||
|
@ -280,7 +237,7 @@ rsa_max_key_size = 1000 :: Int
|
|||
-- | This key is converted directly in base64, leading to a simple 44-byte key representation.
|
||||
ed25519_key_size = 44 :: Int
|
||||
|
||||
verify_public_key :: DKIM.SignatureAlgorithm -> DKIM.PublicKey -> V (Array Error) DKIM.PublicKey
|
||||
verify_public_key :: DKIM.SignatureAlgorithm -> DKIM.PublicKey -> V (Array RR.Error) DKIM.PublicKey
|
||||
verify_public_key signalgo key = case signalgo of
|
||||
DKIM.RSA -> ado
|
||||
k <- if between rsa_min_key_size rsa_max_key_size (S.length key)
|
||||
|
@ -293,7 +250,7 @@ verify_public_key signalgo key = case signalgo of
|
|||
else invalid [DKIMInvalidKeySize ed25519_key_size ed25519_key_size]
|
||||
in k
|
||||
|
||||
validationDKIM :: ResourceRecord -> V (Array Error) ResourceRecord
|
||||
validationDKIM :: ResourceRecord -> V (Array RR.Error) ResourceRecord
|
||||
validationDKIM form =
|
||||
let dkim = fromMaybe DKIM.emptyDKIMRR form.dkim
|
||||
in ado
|
||||
|
@ -307,7 +264,7 @@ validationDKIM form =
|
|||
, name = name, ttl = ttl, target = "" -- `target` is discarded!
|
||||
, dkim = Just $ dkim { p = p } }
|
||||
|
||||
validationDMARC :: ResourceRecord -> V (Array Error) ResourceRecord
|
||||
validationDMARC :: ResourceRecord -> V (Array RR.Error) ResourceRecord
|
||||
validationDMARC form =
|
||||
let dmarc = fromMaybe DMARC.emptyDMARCRR form.dmarc
|
||||
in ado
|
||||
|
@ -321,7 +278,7 @@ validationDMARC form =
|
|||
, name = name, ttl = ttl, target = "" -- `target` is discarded!
|
||||
, dmarc = Just $ dmarc { pct = Just pct, ri = Just ri } }
|
||||
|
||||
validationCAA :: ResourceRecord -> V (Array Error) ResourceRecord
|
||||
validationCAA :: ResourceRecord -> V (Array RR.Error) ResourceRecord
|
||||
validationCAA form =
|
||||
let caa = fromMaybe CAA.emptyCAARR form.caa
|
||||
in ado
|
||||
|
@ -337,7 +294,7 @@ validationCAA form =
|
|||
|
||||
|
||||
-- | `validation` provides a way to validate the content of a RR.
|
||||
validation :: ResourceRecord -> Either (Array Error) ResourceRecord
|
||||
validation :: ResourceRecord -> Either (Array RR.Error) ResourceRecord
|
||||
validation entry = case entry.rrtype of
|
||||
"A" -> toEither $ validationA entry
|
||||
"AAAA" -> toEither $ validationAAAA entry
|
||||
|
|
|
@ -6,7 +6,6 @@ import Data.Either (Either(..))
|
|||
import Data.Validation.Semigroup (V, invalid, toEither)
|
||||
|
||||
import GenericParser.Parser as G
|
||||
import GenericParser.DomainParser.Common (DomainError) as DomainParser
|
||||
import GenericParser.DomainParser (name) as DomainParser
|
||||
|
||||
import App.Type.Delegation (mkEmptyDelegationForm, Form, Error(..)) as Delegation
|
||||
|
@ -22,8 +21,8 @@ validation_nameservers :: Delegation.Form -> V (Array Delegation.Error) Delegati
|
|||
validation_nameservers form = ado
|
||||
nameserver1 <- parse DomainParser.name form.nameserver1 Delegation.VENameServer1
|
||||
nameserver2 <- parse DomainParser.name form.nameserver2 Delegation.VENameServer2
|
||||
in Delegation.mkEmptyDelegationForm
|
||||
in Delegation.mkEmptyDelegationForm { nameserver1 = nameserver1, nameserver2 = nameserver2 }
|
||||
|
||||
-- | `validation` provides a way to validate the content of a RR.
|
||||
-- | `validation` verifies the domain names of the provided name servers for the delegation.
|
||||
validation :: Delegation.Form -> Either (Array Delegation.Error) Delegation.Form
|
||||
validation entry = toEither $ validation_nameservers entry
|
||||
|
|
Loading…
Add table
Reference in a new issue