forked from commercialhaskell/stack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSign.hs
More file actions
111 lines (105 loc) · 4.24 KB
/
Sign.hs
File metadata and controls
111 lines (105 loc) · 4.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-|
Module : Stack.Sig.Sign
Description : Signing Packages
Copyright : (c) 2015-2018, Stack contributors
License : BSD3
Maintainer : Tim Dysinger <tim@fpcomplete.com>
Stability : experimental
Portability : POSIX
-}
module Stack.Sig.Sign (sign, signPackage, signTarBytes) where
import qualified Codec.Archive.Tar as Tar
import qualified Codec.Compression.GZip as GZip
import Stack.Prelude
import qualified Data.ByteString.Lazy as BS
import qualified Data.ByteString.Lazy as L
import Network.HTTP.Client (RequestBody (RequestBodyBS))
import Network.HTTP.Download
import Network.HTTP.Simple (setRequestMethod, setRequestBody, getResponseStatusCode)
import Network.HTTP.Types (methodPut)
import Path
import Stack.Package
import Stack.Sig.GPG
import Stack.Types.PackageIdentifier
import Stack.Types.Sig
import qualified System.FilePath as FP
-- | Sign a haskell package with the given url of the signature
-- service and a path to a tarball.
sign
:: HasLogFunc env
=> String -> Path Abs File -> RIO env Signature
sign url filePath =
withRunInIO $ \run ->
withSystemTempDir
"stack"
(\tempDir ->
do bytes <-
liftIO
(fmap
GZip.decompress
(BS.readFile (toFilePath filePath)))
maybePath <- extractCabalFile tempDir (Tar.read bytes)
case maybePath of
Nothing -> throwM SigInvalidSDistTarBall
Just cabalPath -> do
pkg <- cabalFilePackageId (tempDir </> cabalPath)
run (signPackage url pkg filePath))
where
extractCabalFile tempDir (Tar.Next entry entries) =
case Tar.entryContent entry of
(Tar.NormalFile lbs _) ->
case FP.splitFileName (Tar.entryPath entry) of
(folder,file)
| length (FP.splitDirectories folder) == 1 &&
FP.takeExtension file == ".cabal" -> do
cabalFile <- parseRelFile file
liftIO
(BS.writeFile
(toFilePath (tempDir </> cabalFile))
lbs)
return (Just cabalFile)
(_,_) -> extractCabalFile tempDir entries
_ -> extractCabalFile tempDir entries
extractCabalFile _ _ = return Nothing
-- | Sign a haskell package with the given url to the signature
-- service, a package tarball path (package tarball name) and a lazy
-- bytestring of bytes that represent the tarball bytestream. The
-- function will write the bytes to the path in a temp dir and sign
-- the tarball with GPG.
signTarBytes
:: HasLogFunc env
=> String -> Path Rel File -> L.ByteString -> RIO env Signature
signTarBytes url tarPath bs =
withSystemTempDir
"stack"
(\tempDir ->
do let tempTarBall = tempDir </> tarPath
liftIO (L.writeFile (toFilePath tempTarBall) bs)
sign url tempTarBall)
-- | Sign a haskell package given the url to the signature service, a
-- @PackageIdentifier@ and a file path to the package on disk.
signPackage
:: HasLogFunc env
=> String -> PackageIdentifier -> Path Abs File -> RIO env Signature
signPackage url pkg filePath = do
sig@(Signature signature) <- gpgSign filePath
let (PackageIdentifier name version) = pkg
fingerprint <- gpgVerify sig filePath
let fullUrl =
url <> "/upload/signature/" <> show name <> "/" <> show version <>
"/" <>
show fingerprint
req <- parseUrlThrow fullUrl
let put = setRequestMethod methodPut
$ setRequestBody (RequestBodyBS signature) req
res <- liftIO (httpLbs put)
when
(getResponseStatusCode res /= 200)
(throwM (GPGSignException "unable to sign & upload package"))
logInfo ("Signature uploaded to " <> fromString fullUrl)
return sig