diff options
author | Andrew Cady <d@jerkface.net> | 2016-01-25 17:31:37 -0500 |
---|---|---|
committer | Andrew Cady <d@jerkface.net> | 2016-01-25 17:31:37 -0500 |
commit | cf440860e186e7fd775ae27da08220d9fe5e233e (patch) | |
tree | 655fcfc417730de6616621928a817f361b40fa0d /src | |
parent | be7a90d29c8a6da269d54f355cee11ce6e5eabc2 (diff) |
Change API of "certify"
It now expects a callback to provision the challenge responses.
This needs to be improved so that it will also do cleanup.
Diffstat (limited to 'src')
-rw-r--r-- | src/Network/ACME.hs | 35 |
1 files changed, 26 insertions, 9 deletions
diff --git a/src/Network/ACME.hs b/src/Network/ACME.hs index 4df9f9b..5a66028 100644 --- a/src/Network/ACME.hs +++ b/src/Network/ACME.hs | |||
@@ -37,21 +37,38 @@ import Text.Email.Validate | |||
37 | import Text.Domain.Validate hiding (validate) | 37 | import Text.Domain.Validate hiding (validate) |
38 | import Network.URI | 38 | import Network.URI |
39 | 39 | ||
40 | certify :: URI -> Keys -> Maybe EmailAddress -> URI -> [DomainName] -> WritableDir -> CSR -> IO (Either String LC.ByteString) | 40 | type HttpProvisioner = URI -> ByteString -> IO () |
41 | certify directoryUrl keys optEmail terms requestDomains optChallengeDir csrData = | 41 | |
42 | fileProvisioner :: WritableDir -> HttpProvisioner | ||
43 | fileProvisioner challengeDir = BC.writeFile . uToF | ||
44 | where | ||
45 | uToF = (coerce challengeDir </>) . takeWhileEnd (/= '/') . uriPath | ||
46 | takeWhileEnd f = reverse . takeWhile f . reverse | ||
47 | |||
48 | acmeChallengeURI :: DomainName -> BC.ByteString -> URI | ||
49 | acmeChallengeURI dom tok = URI | ||
50 | "http:" | ||
51 | (Just $ URIAuth "" (domainToString dom) "") | ||
52 | ("/.well-known/acme-challenge" </> BC.unpack tok) | ||
53 | "" | ||
54 | "" | ||
55 | |||
56 | certify :: URI -> Keys -> Maybe (URI, EmailAddress) -> HttpProvisioner -> CSR -> IO (Either String LC.ByteString) | ||
57 | certify directoryUrl keys reg provision certReq = | ||
42 | 58 | ||
43 | runACME directoryUrl keys $ do | 59 | runACME directoryUrl keys $ do |
44 | forM_ optEmail $ register terms >=> statusReport | 60 | forM_ reg $ uncurry register >=> statusReport |
45 | 61 | ||
46 | let performChallenge (ChallengeRequest nextUri token thumbtoken) = do | 62 | let performChallenge domain (ChallengeRequest nextUri token thumbtoken) = do |
47 | liftIO $ BC.writeFile (coerce optChallengeDir </> BC.unpack token) thumbtoken | 63 | liftIO $ provision (acmeChallengeURI domain token) thumbtoken |
48 | notifyChallenge nextUri thumbtoken >>= statusReport >>= ncErrorReport | 64 | notifyChallenge nextUri thumbtoken >>= statusReport >>= ncErrorReport |
49 | 65 | ||
50 | challengeResultLinks <- forM requestDomains $ challengeRequest >=> statusReport >=> extractCR >=> performChallenge | 66 | challengeResultLinks <- forM (csrDomains certReq) $ \dom -> |
67 | challengeRequest dom >>= statusReport >>= extractCR >>= performChallenge dom | ||
51 | 68 | ||
52 | pollResults challengeResultLinks >>= | 69 | pollResults challengeResultLinks >>= |
53 | either (return . Left . ("certificate receipt was not attempted because a challenge failed: " ++)) | 70 | either (return . Left . ("certificate receipt was not attempted because a challenge failed: " ++)) |
54 | (const (retrieveCert csrData >>= statusReport <&> checkCertResponse)) | 71 | (const (retrieveCert certReq >>= statusReport <&> checkCertResponse)) |
55 | 72 | ||
56 | pollResults :: [Response LC.ByteString] -> ACME (Either String ()) | 73 | pollResults :: [Response LC.ByteString] -> ACME (Either String ()) |
57 | pollResults [] = return $ Right () | 74 | pollResults [] = return $ Right () |
@@ -68,7 +85,7 @@ pollResults (link:links) = do | |||
68 | 85 | ||
69 | data ChallengeRequest = ChallengeRequest { crUri :: String, crToken :: ByteString, crThumbToken :: ByteString } | 86 | data ChallengeRequest = ChallengeRequest { crUri :: String, crToken :: ByteString, crThumbToken :: ByteString } |
70 | 87 | ||
71 | newtype CSR = CSR ByteString | 88 | data CSR = CSR { csrDomains :: [DomainName], csrData :: ByteString } |
72 | 89 | ||
73 | newtype WritableDir = WritableDir String | 90 | newtype WritableDir = WritableDir String |
74 | ensureWritableDir :: FilePath -> String -> IO WritableDir | 91 | ensureWritableDir :: FilePath -> String -> IO WritableDir |
@@ -137,7 +154,7 @@ checkCertResponse r = | |||
137 | isSuccess n = n >= 200 && n <= 300 | 154 | isSuccess n = n >= 200 && n <= 300 |
138 | 155 | ||
139 | retrieveCert :: (MonadReader Env m, MonadState Nonce m, MonadIO m) => CSR -> m (Response LC.ByteString) | 156 | retrieveCert :: (MonadReader Env m, MonadState Nonce m, MonadIO m) => CSR -> m (Response LC.ByteString) |
140 | retrieveCert input = sendPayload _newCert (csr $ coerce input) | 157 | retrieveCert input = sendPayload _newCert (csr $ csrData input) |
141 | 158 | ||
142 | notifyChallenge :: (MonadReader Env m, MonadState Nonce m, MonadIO m) => String -> ByteString -> m (Response LC.ByteString) | 159 | notifyChallenge :: (MonadReader Env m, MonadState Nonce m, MonadIO m) => String -> ByteString -> m (Response LC.ByteString) |
143 | notifyChallenge uri thumbtoken = sendPayload (const uri) (challenge thumbtoken) | 160 | notifyChallenge uri thumbtoken = sendPayload (const uri) (challenge thumbtoken) |