summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Cady <d@jerkface.net>2016-01-24 20:37:14 -0500
committerAndrew Cady <d@jerkface.net>2016-01-24 20:37:14 -0500
commitdf216a65fcb97bb42f66fd50fb37166b3045bd39 (patch)
treec23384958f517796f8b900b68692cf448c9d973a
parent90b9dc94d3d0c4cb13cb5e6e400ed1012747e6ff (diff)
poll for challenge results before getting certificate
-rw-r--r--src/Network/ACME.hs37
1 files changed, 28 insertions, 9 deletions
diff --git a/src/Network/ACME.hs b/src/Network/ACME.hs
index 5ae12f3..4df9f9b 100644
--- a/src/Network/ACME.hs
+++ b/src/Network/ACME.hs
@@ -47,9 +47,24 @@ certify directoryUrl keys optEmail terms requestDomains optChallengeDir csrData
47 liftIO $ BC.writeFile (coerce optChallengeDir </> BC.unpack token) thumbtoken 47 liftIO $ BC.writeFile (coerce optChallengeDir </> BC.unpack token) thumbtoken
48 notifyChallenge nextUri thumbtoken >>= statusReport >>= ncErrorReport 48 notifyChallenge nextUri thumbtoken >>= statusReport >>= ncErrorReport
49 49
50 forM_ requestDomains $ challengeRequest >=> statusReport >=> extractCR >=> performChallenge 50 challengeResultLinks <- forM requestDomains $ challengeRequest >=> statusReport >=> extractCR >=> performChallenge
51 51
52 retrieveCert csrData >>= statusReport <&> checkCertResponse 52 pollResults challengeResultLinks >>=
53 either (return . Left . ("certificate receipt was not attempted because a challenge failed: " ++))
54 (const (retrieveCert csrData >>= statusReport <&> checkCertResponse))
55
56pollResults :: [Response LC.ByteString] -> ACME (Either String ())
57pollResults [] = return $ Right ()
58pollResults (link:links) = do
59 -- TODO: use "Retry-After" header if present
60 let Just uri = link ^? responseBody . JSON.key "uri" . _String
61 r <- liftIO $ W.get (T.unpack uri)
62 let status = r ^. responseBody . JSON.key "status" . _String
63 case status of
64 "pending" -> pollResults $ links ++ [r]
65 "valid" -> pollResults links
66 "invalid" -> return . Left $ r ^. responseBody . JSON.key "error" . to extractAcmeError
67 _ -> return . Left $ "unexpected response from ACME server: " ++ show r
53 68
54data ChallengeRequest = ChallengeRequest { crUri :: String, crToken :: ByteString, crThumbToken :: ByteString } 69data ChallengeRequest = ChallengeRequest { crUri :: String, crToken :: ByteString, crThumbToken :: ByteString }
55 70
@@ -100,20 +115,24 @@ extractCR r = do
100 115
101 return $ ChallengeRequest nextU token thumbtoken 116 return $ ChallengeRequest nextU token thumbtoken
102 117
103ncErrorReport :: (Show body, AsValue body, MonadIO m) => Response body -> m () 118ncErrorReport :: (Show body, AsValue body, MonadIO m) => Response body -> m (Response body)
104ncErrorReport r = 119ncErrorReport r = do
105 when (Just "pending" /= r ^? responseBody . JSON.key "status" . _String) $ liftIO $ do 120 when (Just "pending" /= r ^? responseBody . JSON.key "status" . _String) $ liftIO $ do
106 putStrLn "Unexpected response to challenge-response request:" 121 putStrLn "Unexpected response to challenge-response request:"
107 print r 122 print r
123 return r
124
125extractAcmeError :: forall s. AsValue s => s -> String
126extractAcmeError r = summary ++ " ---- " ++ details
127 where
128 (Just summary, Just details) = (k "type", k "detail")
129 k x = r ^? JSON.key x . _String . to T.unpack
108 130
109checkCertResponse :: Response LC.ByteString -> Either String LC.ByteString 131checkCertResponse :: Response LC.ByteString -> Either String LC.ByteString
110checkCertResponse r = 132checkCertResponse r =
111 if isSuccess $ r ^. responseStatus . statusCode 133 if isSuccess $ r ^. responseStatus . statusCode
112 then Right $ r ^. responseBody 134 then Right $ r ^. responseBody
113 else 135 else Left $ r ^. responseBody . to extractAcmeError
114 let (summary, details) = (k "type", k "detail")
115 k x = r ^?! responseBody . JSON.key x . _String . to T.unpack
116 in Left $ summary ++ " ---- " ++ details
117 where 136 where
118 isSuccess n = n >= 200 && n <= 300 137 isSuccess n = n >= 200 && n <= 300
119 138