summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Cady <d@jerkface.net>2016-01-24 02:18:47 -0500
committerAndrew Cady <d@jerkface.net>2016-01-24 02:18:47 -0500
commit0a77e9cb30c12a516993013b7ab5f4272bea94f9 (patch)
tree89286fc44c2b91e72581edaa66b6b50eca321570
parentfdf06576f7d21392f512492b164df899136f30d3 (diff)
validate URIs
-rw-r--r--acme-certify.cabal2
-rw-r--r--acme.hs24
2 files changed, 14 insertions, 12 deletions
diff --git a/acme-certify.cabal b/acme-certify.cabal
index 3e90f42..6e28735 100644
--- a/acme-certify.cabal
+++ b/acme-certify.cabal
@@ -28,7 +28,7 @@ executable acme-certify
28 cryptonite, aeson, bytestring, base64-bytestring, SHA, 28 cryptonite, aeson, bytestring, base64-bytestring, SHA,
29 text, HsOpenSSL, wreq, lens, lens-aeson, 29 text, HsOpenSSL, wreq, lens, lens-aeson,
30 optparse-applicative, directory, mtl, time, pipes, 30 optparse-applicative, directory, mtl, time, pipes,
31 email-validate 31 email-validate, network-uri
32 default-language: Haskell2010 32 default-language: Haskell2010
33 33
34-- test-suite acme-certify-test 34-- test-suite acme-certify-test
diff --git a/acme.hs b/acme.hs
index 8b1a77e..be5a319 100644
--- a/acme.hs
+++ b/acme.hs
@@ -45,10 +45,11 @@ import qualified Options.Applicative as Opt
45import Pipes 45import Pipes
46import System.Directory 46import System.Directory
47import Text.Email.Validate 47import Text.Email.Validate
48import Network.URI
48 49
49stagingDirectoryUrl, liveDirectoryUrl :: String 50stagingDirectoryUrl, liveDirectoryUrl :: URI
50liveDirectoryUrl = "https://acme-v01.api.letsencrypt.org/directory" 51Just liveDirectoryUrl = parseAbsoluteURI "https://acme-v01.api.letsencrypt.org/directory"
51stagingDirectoryUrl = "https://acme-staging.api.letsencrypt.org/directory" 52Just stagingDirectoryUrl = parseAbsoluteURI "https://acme-staging.api.letsencrypt.org/directory"
52 53
53main :: IO () 54main :: IO ()
54main = execParser opts >>= go 55main = execParser opts >>= go
@@ -69,8 +70,8 @@ data CmdOpts = CmdOpts {
69 optStaging :: Bool 70 optStaging :: Bool
70} 71}
71 72
72defaultTerms :: String 73defaultTerms :: URI
73defaultTerms = "https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf" 74Just defaultTerms = parseAbsoluteURI "https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf"
74 75
75cmdopts :: Parser CmdOpts 76cmdopts :: Parser CmdOpts
76cmdopts = CmdOpts <$> strOption (long "key" <> metavar "FILE" <> 77cmdopts = CmdOpts <$> strOption (long "key" <> metavar "FILE" <>
@@ -139,7 +140,7 @@ infixl 0 `otherwiseM`
139 140
140go :: CmdOpts -> IO () 141go :: CmdOpts -> IO ()
141go CmdOpts { .. } = do 142go CmdOpts { .. } = do
142 let terms = fromMaybe defaultTerms optTerms 143 let terms = fromMaybe defaultTerms (join $ parseAbsoluteURI <$> optTerms)
143 directoryUrl = if optStaging then stagingDirectoryUrl else liveDirectoryUrl 144 directoryUrl = if optStaging then stagingDirectoryUrl else liveDirectoryUrl
144 domainKeyFile = domainDir </> "rsa.key" 145 domainKeyFile = domainDir </> "rsa.key"
145 domainCSRFile = domainDir </> "csr.der" 146 domainCSRFile = domainDir </> "csr.der"
@@ -167,7 +168,8 @@ go CmdOpts { .. } = do
167 168
168 certify directoryUrl keys email terms requestDomains optChallengeDir csrData domainCertFile 169 certify directoryUrl keys email terms requestDomains optChallengeDir csrData domainCertFile
169 170
170certify :: String -> Keys -> Maybe EmailAddress -> String -> [String] -> String -> CSR -> FilePath -> IO () 171type DomainName = String -- TODO: use validated type
172certify :: URI -> Keys -> Maybe EmailAddress -> URI -> [DomainName] -> String -> CSR -> FilePath -> IO ()
171certify directoryUrl keys optEmail terms requestDomains optChallengeDir csrData domainCertFile = 173certify directoryUrl keys optEmail terms requestDomains optChallengeDir csrData domainCertFile =
172 174
173 runACME directoryUrl keys $ do 175 runACME directoryUrl keys $ do
@@ -252,9 +254,9 @@ notifyChallenge crUri thumbtoken = sendPayload (const crUri) (challenge thumbtok
252data Env = Env { getDir :: Directory, getKeys :: Keys, getSession :: WS.Session } 254data Env = Env { getDir :: Directory, getKeys :: Keys, getSession :: WS.Session }
253 255
254type ACME = RWST Env () Nonce IO 256type ACME = RWST Env () Nonce IO
255runACME :: String -> Keys -> ACME a -> IO a 257runACME :: URI -> Keys -> ACME a -> IO a
256runACME url keys f = WS.withSession $ \sess -> do 258runACME url keys f = WS.withSession $ \sess -> do
257 Just (dir, nonce) <- getDirectory sess url 259 Just (dir, nonce) <- getDirectory sess (show url)
258 fst <$> evalRWST f (Env dir keys sess) nonce 260 fst <$> evalRWST f (Env dir keys sess) nonce
259 261
260data Directory = Directory { 262data Directory = Directory {
@@ -272,8 +274,8 @@ getDirectory sess url = do
272 k x = r ^? responseBody . JSON.key x . _String . to T.unpack 274 k x = r ^? responseBody . JSON.key x . _String . to T.unpack
273 return $ (,) <$> (Directory <$> k "new-cert" <*> k "new-authz" <*> k "revoke-cert" <*> k "new-reg") <*> nonce 275 return $ (,) <$> (Directory <$> k "new-cert" <*> k "new-authz" <*> k "revoke-cert" <*> k "new-reg") <*> nonce
274 276
275register :: String -> EmailAddress -> ACME (Response LC.ByteString) 277register :: URI -> EmailAddress -> ACME (Response LC.ByteString)
276register terms email = sendPayload _newReg (registration email terms) 278register terms email = sendPayload _newReg (registration email (show terms))
277 279
278challengeRequest :: (MonadIO m, MonadState Nonce m, MonadReader Env m) => String -> m (Response LC.ByteString) 280challengeRequest :: (MonadIO m, MonadState Nonce m, MonadReader Env m) => String -> m (Response LC.ByteString)
279challengeRequest domain = sendPayload _newAuthz (authz domain) 281challengeRequest domain = sendPayload _newAuthz (authz domain)