Add Minio monad and start getService
This commit is contained in:
parent
4569348dc2
commit
07eb59fda5
14
app/Main.hs
14
app/Main.hs
@ -1,19 +1,13 @@
|
|||||||
module Main where
|
module Main where
|
||||||
|
|
||||||
import qualified Network.HTTP.Types as HT
|
-- import qualified Network.HTTP.Conduit as NC
|
||||||
import qualified Network.HTTP.Conduit as NC
|
|
||||||
|
|
||||||
import Protolude
|
import Protolude
|
||||||
|
|
||||||
import Network.Minio.API
|
import Network.Minio.API
|
||||||
import Network.Minio.Data
|
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = do
|
main = do
|
||||||
resp <- minioExecute mc req
|
mc <- connect defaultConnectInfo
|
||||||
print $ NC.responseStatus resp
|
res <- runMinio mc $ getService
|
||||||
print $ NC.responseHeaders resp
|
print res
|
||||||
print $ NC.responseBody resp
|
|
||||||
where
|
|
||||||
mc = MinioClient "localhost" 9000 "abcd1" "abcd1234" False "us-east-1"
|
|
||||||
req = RequestInfo HT.methodGet Nothing Nothing [] [] "" ""
|
|
||||||
|
|||||||
@ -1,12 +1,16 @@
|
|||||||
module Network.Minio.API
|
module Network.Minio.API
|
||||||
(
|
(
|
||||||
minioExecute
|
connect
|
||||||
|
, defaultConnectInfo
|
||||||
|
, RequestInfo(..)
|
||||||
|
, runMinio
|
||||||
|
, getService
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Network.HTTP.Client (defaultManagerSettings)
|
|
||||||
import qualified Network.HTTP.Types as HT
|
import qualified Network.HTTP.Types as HT
|
||||||
import Network.HTTP.Conduit (Response)
|
import Network.HTTP.Conduit (Response)
|
||||||
import qualified Network.HTTP.Conduit as NC
|
import qualified Network.HTTP.Conduit as NC
|
||||||
|
import Network.HTTP.Types (Method, Header, Query)
|
||||||
|
|
||||||
import Lib.Prelude
|
import Lib.Prelude
|
||||||
|
|
||||||
@ -14,37 +18,52 @@ import Network.Minio.Data
|
|||||||
import Network.Minio.Data.Crypto
|
import Network.Minio.Data.Crypto
|
||||||
import Network.Minio.Sign.V4
|
import Network.Minio.Sign.V4
|
||||||
|
|
||||||
runRequestDebug r mgr = do
|
-- runRequestDebug r mgr = do
|
||||||
print $ "runRequestDebug"
|
-- print $ "runRequestDebug"
|
||||||
print $ NC.method r
|
-- print $ NC.method r
|
||||||
print $ NC.secure r
|
-- print $ NC.secure r
|
||||||
print $ NC.host r
|
-- print $ NC.host r
|
||||||
print $ NC.port r
|
-- print $ NC.port r
|
||||||
print $ NC.path r
|
-- print $ NC.path r
|
||||||
print $ NC.queryString r
|
-- print $ NC.queryString r
|
||||||
print $ NC.requestHeaders r
|
-- print $ NC.requestHeaders r
|
||||||
-- print $ NC.requestBody r
|
-- -- print $ NC.requestBody r
|
||||||
NC.httpLbs r mgr
|
-- NC.httpLbs r mgr
|
||||||
|
|
||||||
minioExecute :: MinioClient -> RequestInfo -> IO (Response LByteString)
|
mkSRequest :: RequestInfo -> Minio (Response LByteString)
|
||||||
minioExecute mc ri = do
|
mkSRequest ri = do
|
||||||
mgr <- NC.newManager defaultManagerSettings
|
let PayloadSingle pload = payload ri
|
||||||
finalHeaders <- signV4 mc updatedRI
|
phash = hashSHA256 pload
|
||||||
runRequestDebug (req finalHeaders) mgr
|
newRI = ri {
|
||||||
where
|
payloadHash = phash
|
||||||
req h = NC.defaultRequest {
|
, headers = ("x-amz-content-sha256", phash) : (headers ri)
|
||||||
NC.method = method ri
|
}
|
||||||
, NC.secure = mcIsSecure mc
|
|
||||||
, NC.host = encodeUtf8 $ mcEndPointHost mc
|
|
||||||
, NC.port = mcEndPointPort mc
|
|
||||||
, NC.path = getPathFromRI ri
|
|
||||||
, NC.queryString = HT.renderQuery False $ queryParams ri
|
|
||||||
, NC.requestHeaders = h
|
|
||||||
, NC.requestBody = NC.RequestBodyBS (payload ri)
|
|
||||||
}
|
|
||||||
|
|
||||||
phash = hashSHA256 $ payload ri
|
ci <- asks mcConnInfo
|
||||||
updatedRI = ri {
|
|
||||||
payloadHash = phash
|
reqHeaders <- liftIO $ signV4 ci newRI
|
||||||
, headers = ("x-amz-content-sha256", phash) : (headers ri)
|
|
||||||
}
|
mgr <- asks mcConnManager
|
||||||
|
|
||||||
|
let req = NC.defaultRequest {
|
||||||
|
NC.method = method newRI
|
||||||
|
, NC.secure = connectIsSecure ci
|
||||||
|
, NC.host = encodeUtf8 $ connectHost ci
|
||||||
|
, NC.port = connectPort ci
|
||||||
|
, NC.path = getPathFromRI ri
|
||||||
|
, NC.queryString = HT.renderQuery False $ queryParams ri
|
||||||
|
, NC.requestHeaders = reqHeaders
|
||||||
|
, NC.requestBody = NC.RequestBodyBS pload
|
||||||
|
}
|
||||||
|
|
||||||
|
NC.httpLbs req mgr
|
||||||
|
|
||||||
|
requestInfo :: Method -> Maybe Bucket -> Maybe Object
|
||||||
|
-> Query -> [Header] -> Payload
|
||||||
|
-> RequestInfo
|
||||||
|
requestInfo m b o q h p = RequestInfo m b o q h p ""
|
||||||
|
|
||||||
|
getService :: Minio (Response LByteString)
|
||||||
|
getService = mkSRequest $
|
||||||
|
requestInfo HT.methodGet Nothing Nothing [] [] $
|
||||||
|
PayloadSingle ""
|
||||||
|
|||||||
@ -1,36 +1,52 @@
|
|||||||
|
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
||||||
module Network.Minio.Data
|
module Network.Minio.Data
|
||||||
(
|
(
|
||||||
MinioClient(..)
|
ConnectInfo(..)
|
||||||
, RequestInfo(..)
|
, RequestInfo(..)
|
||||||
|
, MinioConn(..)
|
||||||
, Bucket
|
, Bucket
|
||||||
, Object
|
, Object
|
||||||
, getPathFromRI
|
, getPathFromRI
|
||||||
|
, Minio
|
||||||
|
, runMinio
|
||||||
|
, defaultConnectInfo
|
||||||
|
, connect
|
||||||
|
, Payload(..)
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import qualified Data.ByteString as B
|
import qualified Data.ByteString as B
|
||||||
|
import Network.HTTP.Client (defaultManagerSettings)
|
||||||
import Network.HTTP.Types (Method, Header, Query)
|
import Network.HTTP.Types (Method, Header, Query)
|
||||||
|
import qualified Network.HTTP.Conduit as NC
|
||||||
|
|
||||||
import Lib.Prelude
|
import Lib.Prelude
|
||||||
|
|
||||||
data MinioClient = MinioClient {
|
data ConnectInfo = ConnectInfo {
|
||||||
mcEndPointHost :: Text
|
connectHost :: Text
|
||||||
, mcEndPointPort :: Int
|
, connectPort :: Int
|
||||||
, mcAccessKey :: Text
|
, connectAccessKey :: Text
|
||||||
, mcSecretKey :: Text
|
, connectSecretKey :: Text
|
||||||
, mcIsSecure :: Bool
|
, connectIsSecure :: Bool
|
||||||
, mcRegion :: Text
|
, connectRegion :: Text
|
||||||
} deriving (Eq, Show)
|
} deriving (Eq, Show)
|
||||||
|
|
||||||
|
defaultConnectInfo :: ConnectInfo
|
||||||
|
defaultConnectInfo =
|
||||||
|
ConnectInfo "localhost" 9000 "minio" "minio123" False "us-east-1"
|
||||||
|
|
||||||
type Bucket = ByteString
|
type Bucket = ByteString
|
||||||
type Object = Text
|
type Object = Text
|
||||||
|
|
||||||
|
data Payload = PayloadSingle ByteString
|
||||||
|
deriving (Show, Eq)
|
||||||
|
|
||||||
data RequestInfo = RequestInfo {
|
data RequestInfo = RequestInfo {
|
||||||
method :: Method
|
method :: Method
|
||||||
, bucket :: Maybe Bucket
|
, bucket :: Maybe Bucket
|
||||||
, object :: Maybe Object
|
, object :: Maybe Object
|
||||||
, queryParams :: Query
|
, queryParams :: Query
|
||||||
, headers :: [Header]
|
, headers :: [Header]
|
||||||
, payload :: ByteString
|
, payload :: Payload
|
||||||
, payloadHash :: ByteString
|
, payloadHash :: ByteString
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,3 +55,30 @@ getPathFromRI ri = B.concat $ parts
|
|||||||
where
|
where
|
||||||
objPart = maybe [] (\o -> ["/", encodeUtf8 o]) $ object ri
|
objPart = maybe [] (\o -> ["/", encodeUtf8 o]) $ object ri
|
||||||
parts = maybe ["/"] (\b -> "/" : b : objPart) $ bucket ri
|
parts = maybe ["/"] (\b -> "/" : b : objPart) $ bucket ri
|
||||||
|
|
||||||
|
data MinioErr = MErrMsg ByteString
|
||||||
|
deriving (Show)
|
||||||
|
|
||||||
|
newtype Minio a = Minio {
|
||||||
|
unMinio :: ReaderT MinioConn (ExceptT MinioErr IO) a
|
||||||
|
} deriving (
|
||||||
|
Functor
|
||||||
|
, Applicative
|
||||||
|
, Monad
|
||||||
|
, MonadIO
|
||||||
|
, MonadReader MinioConn
|
||||||
|
)
|
||||||
|
|
||||||
|
-- MinioConn holds connection info and a connection pool
|
||||||
|
data MinioConn = MinioConn {
|
||||||
|
mcConnInfo :: ConnectInfo
|
||||||
|
, mcConnManager :: NC.Manager
|
||||||
|
}
|
||||||
|
|
||||||
|
connect :: ConnectInfo -> IO MinioConn
|
||||||
|
connect ci = do
|
||||||
|
mgr <- NC.newManager defaultManagerSettings
|
||||||
|
return $ MinioConn ci mgr
|
||||||
|
|
||||||
|
runMinio :: MinioConn -> Minio a -> IO (Either MinioErr a)
|
||||||
|
runMinio conn = runExceptT . flip runReaderT conn . unMinio
|
||||||
|
|||||||
@ -7,8 +7,8 @@ module Network.Minio.Data.Crypto
|
|||||||
, digestToBase16
|
, digestToBase16
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Crypto.Hash (SHA256(..), hashWith, Digest)
|
import Crypto.Hash (SHA256(..), hashWith)
|
||||||
import Crypto.MAC.HMAC (hmac, HMAC(hmacGetDigest))
|
import Crypto.MAC.HMAC (hmac, HMAC)
|
||||||
import Data.ByteArray (ByteArrayAccess, convert)
|
import Data.ByteArray (ByteArrayAccess, convert)
|
||||||
import Data.ByteArray.Encoding (convertToBase, Base(Base16))
|
import Data.ByteArray.Encoding (convertToBase, Base(Base16))
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,7 @@ module Network.Minio.Sign.V4
|
|||||||
, getHeadersToSign
|
, getHeadersToSign
|
||||||
, getCanonicalRequest
|
, getCanonicalRequest
|
||||||
, SignV4Data(..)
|
, SignV4Data(..)
|
||||||
|
, debugPrintSignV4Data
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import qualified Data.ByteString as B
|
import qualified Data.ByteString as B
|
||||||
@ -64,34 +65,34 @@ debugPrintSignV4Data (SignV4Data t s cr h2s ih oh sts sk) = do
|
|||||||
-- request path, headers, query params and payload hash, generates an
|
-- request path, headers, query params and payload hash, generates an
|
||||||
-- updated set of headers, including the x-amz-date header and the
|
-- updated set of headers, including the x-amz-date header and the
|
||||||
-- Authorization header, which includes the signature.
|
-- Authorization header, which includes the signature.
|
||||||
signV4 :: MinioClient -> RequestInfo
|
signV4 :: ConnectInfo -> RequestInfo
|
||||||
-> IO [Header]
|
-> IO [Header]
|
||||||
signV4 mc ri = do
|
signV4 ci ri = do
|
||||||
timestamp <- Time.getCurrentTime
|
timestamp <- Time.getCurrentTime
|
||||||
let signData = signV4AtTime timestamp mc ri
|
let signData = signV4AtTime timestamp ci ri
|
||||||
debugPrintSignV4Data signData
|
-- debugPrintSignV4Data signData
|
||||||
return $ sv4OutputHeaders signData
|
return $ sv4OutputHeaders signData
|
||||||
|
|
||||||
-- | Takes a timestamp, server params and request params and generates
|
-- | Takes a timestamp, server params and request params and generates
|
||||||
-- an updated list of headers.
|
-- an updated list of headers.
|
||||||
signV4AtTime :: UTCTime -> MinioClient -> RequestInfo -> SignV4Data
|
signV4AtTime :: UTCTime -> ConnectInfo -> RequestInfo -> SignV4Data
|
||||||
signV4AtTime ts mc ri =
|
signV4AtTime ts ci ri =
|
||||||
SignV4Data ts scope canonicalRequest headersToSign (headers ri) outHeaders stringToSign signingKey
|
SignV4Data ts scope canonicalRequest headersToSign (headers ri) outHeaders stringToSign signingKey
|
||||||
where
|
where
|
||||||
outHeaders = authHeader : headersWithDate
|
outHeaders = authHeader : headersWithDate
|
||||||
timeBS = awsTimeFormatBS ts
|
timeBS = awsTimeFormatBS ts
|
||||||
dateHeader = (mk "X-Amz-Date", timeBS)
|
dateHeader = (mk "X-Amz-Date", timeBS)
|
||||||
hostHeader = (mk "host", encodeUtf8 $ mcEndPointHost mc)
|
hostHeader = (mk "host", encodeUtf8 $ connectHost ci)
|
||||||
|
|
||||||
headersWithDate = dateHeader : hostHeader : (headers ri)
|
headersWithDate = dateHeader : hostHeader : (headers ri)
|
||||||
|
|
||||||
authHeader = (mk "Authorization", authHeaderValue)
|
authHeader = (mk "Authorization", authHeaderValue)
|
||||||
|
|
||||||
scope = getScope ts mc
|
scope = getScope ts ci
|
||||||
|
|
||||||
authHeaderValue = B.concat [
|
authHeaderValue = B.concat [
|
||||||
"AWS4-HMAC-SHA256 Credential=",
|
"AWS4-HMAC-SHA256 Credential=",
|
||||||
encodeUtf8 (mcAccessKey mc), "/", scope,
|
encodeUtf8 (connectAccessKey ci), "/", scope,
|
||||||
", SignedHeaders=", signedHeaders,
|
", SignedHeaders=", signedHeaders,
|
||||||
", Signature=", signature
|
", Signature=", signature
|
||||||
]
|
]
|
||||||
@ -104,9 +105,9 @@ signV4AtTime ts mc ri =
|
|||||||
|
|
||||||
signingKey = hmacSHA256RawBS "aws4_request"
|
signingKey = hmacSHA256RawBS "aws4_request"
|
||||||
. hmacSHA256RawBS "s3"
|
. hmacSHA256RawBS "s3"
|
||||||
. hmacSHA256RawBS (encodeUtf8 $ mcRegion mc)
|
. hmacSHA256RawBS (encodeUtf8 $ connectRegion ci)
|
||||||
. hmacSHA256RawBS (awsDateFormatBS ts)
|
. hmacSHA256RawBS (awsDateFormatBS ts)
|
||||||
$ (B.concat ["AWS4", encodeUtf8 $ mcSecretKey mc])
|
$ (B.concat ["AWS4", encodeUtf8 $ connectSecretKey ci])
|
||||||
|
|
||||||
stringToSign = B.intercalate "\n" $
|
stringToSign = B.intercalate "\n" $
|
||||||
["AWS4-HMAC-SHA256",
|
["AWS4-HMAC-SHA256",
|
||||||
@ -118,10 +119,10 @@ signV4AtTime ts mc ri =
|
|||||||
canonicalRequest = getCanonicalRequest ri headersToSign
|
canonicalRequest = getCanonicalRequest ri headersToSign
|
||||||
|
|
||||||
|
|
||||||
getScope :: UTCTime -> MinioClient -> ByteString
|
getScope :: UTCTime -> ConnectInfo -> ByteString
|
||||||
getScope ts mc = B.intercalate "/" $ [
|
getScope ts ci = B.intercalate "/" $ [
|
||||||
pack $ Time.formatTime Time.defaultTimeLocale "%Y%m%d" ts,
|
pack $ Time.formatTime Time.defaultTimeLocale "%Y%m%d" ts,
|
||||||
"us-east-1", "s3", "aws4_request"
|
encodeUtf8 $ connectRegion ci, "s3", "aws4_request"
|
||||||
]
|
]
|
||||||
|
|
||||||
getHeadersToSign :: [Header] -> [(ByteString, ByteString)]
|
getHeadersToSign :: [Header] -> [(ByteString, ByteString)]
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user