hackage-security-0.1.0.0: Hackage security library

Safe HaskellNone
LanguageHaskell2010

Hackage.Security.Client.Repository

Contents

Description

Abstract definition of a Repository

Most clients should only need to import this module if they wish to define their own Repository implementations.

Synopsis

Files

data RemoteFile :: * -> * where Source

Abstract definition of files we might have to download

RemoteFile is parametrized by the type of the formats that we can accept from the remote repository.

NOTE: Haddock lacks GADT support so constructors have only regular comments.

Constructors

RemoteTimestamp :: RemoteFile (FormatUn :- ()) 
RemoteRoot :: Maybe (Trusted FileInfo) -> RemoteFile (FormatUn :- ()) 
RemoteSnapshot :: Trusted FileInfo -> RemoteFile (FormatUn :- ()) 
RemoteMirrors :: Trusted FileInfo -> RemoteFile (FormatUn :- ()) 
RemoteIndex :: HasFormat fs FormatGz -> Formats fs (Trusted FileInfo) -> RemoteFile fs 
RemotePkgTarGz :: PackageIdentifier -> Trusted FileInfo -> RemoteFile (FormatGz :- ()) 

Instances

data CachedFile Source

Files that we might request from the local cache

Constructors

CachedTimestamp

Timestamp metadata (timestamp.json)

CachedRoot

Root metadata (root.json)

CachedSnapshot

Snapshot metadata (snapshot.json)

CachedMirrors

Mirrors list (mirrors.json)

data IndexFile Source

Files that we might request from the index

TODO: We should also provide a way to extract preferred versions info from the tarball. After all, this is a security sensitive, as it might be used for rollback/freeze attacks. Until we have author signing however this is not a strict necessity, as the preferred versions comes from the index which is itself signed.

Constructors

IndexPkgMetadata PackageIdentifier

Package-specific metadata (targets.json)

IndexPkgCabal PackageIdentifier

Cabal file for a package

remoteFileDefaultFormat :: RemoteFile fs -> Some (HasFormat fs) Source

Default format for each file type

For most file types we don't have a choice; for the index the repository is only required to offer the GZip-compressed format so that is the default.

remoteFileDefaultInfo :: RemoteFile fs -> Maybe (Trusted FileInfo) Source

Default file info (see also remoteFileDefaultFormat)

Repository proper

data Repository Source

Repository

This is an abstract representation of a repository. It simply provides a way to download metafiles and target files, without specifying how this is done. For instance, for a local repository this could just be doing a file read, whereas for remote repositories this could be using any kind of HTTP client.

Constructors

Repository 

Fields

repWithRemote :: forall a fs. (Throws VerificationError, Throws SomeRemoteError) => IsRetry -> RemoteFile fs -> (forall f. HasFormat fs f -> TempPath -> IO a) -> IO a

Get a file from the server

Responsibilies of repWithRemote:

  • Download the file from the repository and make it available at a temporary location
  • Use the provided file length to protect against endless data attacks. (Repositories such as local repositories that are not suspectible to endless data attacks can safely ignore this argument.)
  • Move the file from its temporary location to its permanent location if the callback returns successfully (where appropriate).

Responsibilities of the callback:

  • Verify the file and throw an exception if verification fails.
  • Not modify or move the temporary file. (Thus it is safe for local repositories to directly pass the path into the local repository.)

NOTE: Calls to repWithRemote should _always_ be in the scope of repWithMirror.

repGetCached :: CachedFile -> IO (Maybe AbsolutePath)

Get a cached file (if available)

repGetCachedRoot :: IO AbsolutePath

Get the cached root

This is a separate method only because clients must ALWAYS have root information available.

repClearCache :: IO ()

Clear all cached data

In particular, this should remove the snapshot and the timestamp. It would also be okay, but not required, to delete the index.

repGetFromIndex :: IndexFile -> IO (Maybe ByteString)

Get a file from the index

The use of a strict bytestring here is intentional: it means the Repository is free to keep the index open and just seek the handle for different files. Since we only extract small files, having the entire extracted file in memory is not an issue.

repWithMirror :: forall a. Maybe [Mirror] -> IO a -> IO a

Mirror selection

The purpose of repWithMirror is to scope mirror selection. The idea is that if we have

repWithMirror mirrorList $
  someCallback

then the repository may pick a mirror before calling someCallback, catch exceptions thrown by someCallback, and potentially try the callback again with a different mirror.

The list of mirrors may be Nothing if we haven't yet downloaded the list of mirrors from the repository, or when our cached list of mirrors is invalid. Of course, if we did download it, then the list of mirrors may still be empty. In this case the repository must fall back to its primary download mechanism.

Mirrors as currently defined (in terms of a "base URL") are inherently a HTTP (or related) concept, so in repository implementations such as the local-repo repWithMirrors is probably just an identity operation (see ignoreMirrors). Conversely, HTTP implementations of repositories may have other, out-of-band information (for example, coming from a cabal config file) that they may use to influence mirror selection.

repLog :: LogMessage -> IO ()

Logging

repLayout :: RepoLayout

Layout of this repository

repDescription :: String

Description of the repository (used in the show instance)

Instances

type TempPath = AbsolutePath Source

Path to temporary file

data IsRetry Source

Are we requesting this information because of a previous validation error?

Clients can take advantage of this to tell caches to revalidate files.

data LogMessage Source

Log messages

We use a RemoteFile rather than a RepoPath here because we might not have a RepoPath for the file that we were trying to download (that is, for example if the server does not provide an uncompressed tarball, it doesn't make much sense to list the path to that non-existing uncompressed tarball).

Constructors

LogRootUpdated

Root information was updated

This message is issued when the root information is updated as part of the normal check for updates procedure. If the root information is updated because of a verification error WarningVerificationError is issued instead.

LogVerificationError VerificationError

A verification error

Verification errors can be temporary, and may be resolved later; hence these are just warnings. (Verification errors that cannot be resolved are thrown as exceptions.)

LogDownloading (Some RemoteFile)

Download a file from a repository

LogUpdating (Some RemoteFile)

Incrementally updating a file from a repository

LogSelectedMirror MirrorDescription

Selected a particular mirror

LogUpdateFailed (Some RemoteFile) UpdateFailure

Updating a file failed (we will try again by downloading it whole)

LogMirrorFailed MirrorDescription SomeException

We got an exception with a particular mirror (we will try with a different mirror if any are available)

Instances

data UpdateFailure Source

Records why we are downloading a file rather than updating it.

Constructors

UpdateImpossibleOnlyCompressed

Server only provides compressed form of the file

UpdateNotUsefulWantsCompressed

Likewise, it's possible that client _wants_ the compressed form of the file, in which case downloading the uncompressed form is not useful.

UpdateImpossibleUnsupported

Server does not support incremental downloads

UpdateImpossibleNoLocalCopy

We don't have a local copy of the file to update

UpdateTooLarge

Updating the local file would actually mean downloading MORE data then doing a regular download.

UpdateFailed SomeException

Update failed

data SomeRemoteError :: * where Source

Repository-specific exceptions

For instance, for repositories using HTTP this might correspond to a 404; for local repositories this might correspond to file-not-found, etc.

Constructors

SomeRemoteError :: Exception e => e -> SomeRemoteError 

Helpers

mirrorsUnsupported :: Maybe [Mirror] -> IO a -> IO a Source

Helper function to implement repWithMirrors.

Paths

remoteRepoPath' :: RepoLayout -> RemoteFile fs -> HasFormat fs f -> RepoPath Source

Utility

data IsCached Source

Is a particular remote file cached?

Constructors

CacheAs CachedFile

This remote file should be cached, and we ask for it by name

DontCache

We don't cache this remote file

This doesn't mean a Repository should not feel free to cache the file if desired, but it does mean the generic algorithms will never ask for this file from the cache.

CacheIndex

The index is somewhat special: it should be cached, but we never ask for it directly.

Instead, we will ask the Repository for files _from_ the index, which it can serve however it likes. For instance, some repositories might keep the index in uncompressed form, others in compressed form; some might keep an index tarball index for quick access, others may scan the tarball linearly, etc.

mustCache :: RemoteFile fs -> IsCached Source

Which remote files should we cache locally?