module Web.WindowsAzure.ACS (
AcsInfo (..),
AcsContext,
AcsToken,
acsContext,
acsToken
)where
import Network.HTTP.Conduit
import Data.Conduit
import Network.HTTP.Types.URI
import qualified Data.ByteString.Char8 as C
import qualified Data.Attoparsec.ByteString.Lazy as AP
import Data.Attoparsec.ByteString.Char8
import Control.Concurrent.MVar
import Network.HTTP.Types.Method(methodPost)
import Network.HTTP.Types.Header
import Data.Time.Clock(getCurrentTime, addUTCTime,UTCTime)
import Data.Conduit.Attoparsec(sinkParser)
import Data.Conduit.Binary(sourceLbs)
import Network(withSocketsDo)
data AcsInfo = AcsInfo String !C.ByteString !C.ByteString !C.ByteString
deriving (Eq,Show)
type AcsToken = Header
data AcsResponse = AcsResponse !AcsToken !UTCTime
| NotConnectedToAcs
data AcsContext = AcsContext !AcsInfo (MVar AcsResponse)
acsContext :: AcsInfo -> IO AcsContext
acsContext a = do
b <- newMVar NotConnectedToAcs
return $ AcsContext a b
canReuse :: UTCTime -> AcsResponse -> Bool
canReuse _ NotConnectedToAcs = False
canReuse currentTime (AcsResponse _ time) = currentTime < time
wrapToken :: AcsResponse -> AcsToken
wrapToken (AcsResponse t _) = t
wrapToken _ = undefined
acsToken :: Manager -> AcsContext -> IO AcsToken
acsToken manager (AcsContext info mv) = do
utcTime <- getCurrentTime
acsResp <- takeMVar mv
if canReuse utcTime acsResp
then do { putMVar mv acsResp; return (wrapToken acsResp)}
else do
resp <- doAcsPost info manager
putMVar mv resp
return (wrapToken resp)
doAcsPost :: AcsInfo -> Manager ->IO AcsResponse
doAcsPost (AcsInfo url endpoint issuer key) manager = do
request' <- parseUrl ("https://" ++ url ++ ".accesscontrol.windows.net/WRAPv0.9/")
let request = addBody $ request' {
method = methodPost
}
res <- withSocketsDo $ httpLbs request manager
utcTime <- getCurrentTime
acsResp <- sourceLbs (responseBody res) $$ (sinkParser $ parseResponse utcTime)
return acsResp
where
addBody = urlEncodedBody [(C.pack "wrap_scope",endpoint),(C.pack "wrap_name", issuer),(C.pack "wrap_password",key)]
parseResponse currTime = do
AP.takeTill (== 61)
AP.anyWord8
b1 <- AP.takeTill (== 38)
AP.anyWord8
AP.takeTill (== 61)
AP.anyWord8
i <- decimal
return $ AcsResponse (toHeader b1) (addUTCTime (fromInteger $ i 300) currTime)
toHeader bs = (hAuthorization, C.concat [(C.pack "WRAP access_token=\""), (urlDecode False bs), C.pack "\""])