summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Creds.hs24
-rw-r--r--Remote/External.hs29
-rw-r--r--Remote/External/Types.hs12
-rw-r--r--doc/design/external_special_remote_protocol.mdwn17
-rwxr-xr-xdoc/special_remotes/external/example.sh36
5 files changed, 96 insertions, 22 deletions
diff --git a/Creds.hs b/Creds.hs
index c79c16cce..7c300dd07 100644
--- a/Creds.hs
+++ b/Creds.hs
@@ -35,25 +35,27 @@ data CredPairStorage = CredPairStorage
{- Stores creds in a remote's configuration, if the remote allows
- that. Otherwise, caches them locally. -}
setRemoteCredPair :: RemoteConfig -> CredPairStorage -> Annex RemoteConfig
-setRemoteCredPair c storage = go =<< getRemoteCredPair c storage
+setRemoteCredPair c storage =
+ maybe (return c) (setRemoteCredPair' c storage)
+ =<< getRemoteCredPair c storage
+
+setRemoteCredPair' :: RemoteConfig -> CredPairStorage -> CredPair -> Annex RemoteConfig
+setRemoteCredPair' c storage creds
+ | embedCreds c = case credPairRemoteKey storage of
+ Nothing -> localcache
+ Just key -> storeconfig key =<< remoteCipher c
+ | otherwise = localcache
where
- go (Just creds)
- | embedCreds c = case credPairRemoteKey storage of
- Nothing -> localcache creds
- Just key -> storeconfig creds key =<< remoteCipher c
- | otherwise = localcache creds
- go Nothing = return c
-
- localcache creds = do
+ localcache = do
writeCacheCredPair creds storage
return c
- storeconfig creds key (Just cipher) = do
+ storeconfig key (Just cipher) = do
s <- liftIO $ encrypt [] cipher
(feedBytes $ L.pack $ encodeCredPair creds)
(readBytes $ return . L.unpack)
return $ M.insert key (toB64 s) c
- storeconfig creds key Nothing =
+ storeconfig key Nothing =
return $ M.insert key (toB64 $ encodeCredPair creds) c
{- Gets a remote's credpair, from the environment if set, otherwise
diff --git a/Remote/External.hs b/Remote/External.hs
index 251f423a6..2d777ff7f 100644
--- a/Remote/External.hs
+++ b/Remote/External.hs
@@ -22,6 +22,7 @@ import Config.Cost
import Annex.Content
import Annex.UUID
import Annex.Exception
+import Creds
import Control.Concurrent.STM
import System.Process (std_in, std_out, std_err)
@@ -39,7 +40,7 @@ remote = RemoteType {
gen :: Git.Repo -> UUID -> RemoteConfig -> RemoteGitConfig -> Annex (Maybe Remote)
gen r u c gc = do
- external <- newExternal externaltype c
+ external <- newExternal externaltype u c
Annex.addCleanup (fromUUID u) $ stopExternal external
cst <- getCost external r gc
return $ Just $ encryptableRemote c
@@ -76,7 +77,7 @@ externalSetup mu c = do
M.lookup "externaltype" c
c' <- encryptionSetup c
- external <- newExternal externaltype c'
+ external <- newExternal externaltype u c'
handleRequest external INITREMOTE Nothing $ \resp -> case resp of
INITREMOTE_SUCCESS -> Just noop
INITREMOTE_FAILURE errmsg -> Just $ error errmsg
@@ -201,7 +202,7 @@ handleRequest' lck external req mp responsehandler = do
handleRemoteRequest (PROGRESS bytesprocessed) =
maybe noop (\a -> liftIO $ a bytesprocessed) mp
handleRemoteRequest (DIRHASH k) =
- sendMessage lck external (VALUE $ hashDirMixed k)
+ sendMessage lck external $ VALUE $ hashDirMixed k
handleRemoteRequest (SETCONFIG setting value) =
liftIO $ atomically $ do
let v = externalConfig external
@@ -210,12 +211,30 @@ handleRequest' lck external req mp responsehandler = do
handleRemoteRequest (GETCONFIG setting) = do
value <- fromMaybe "" . M.lookup setting
<$> liftIO (atomically $ readTMVar $ externalConfig external)
- sendMessage lck external (VALUE value)
+ sendMessage lck external $ VALUE value
+ handleRemoteRequest (SETCREDS setting login password) = do
+ c <- liftIO $ atomically $ readTMVar $ externalConfig external
+ c' <- setRemoteCredPair' c (credstorage setting)
+ (login, password)
+ void $ liftIO $ atomically $ swapTMVar (externalConfig external) c'
+ handleRemoteRequest (GETCREDS setting) = do
+ c <- liftIO $ atomically $ readTMVar $ externalConfig external
+ creds <- fromMaybe ("", "") <$>
+ getRemoteCredPair c (credstorage setting)
+ sendMessage lck external $ CREDS (fst creds) (snd creds)
handleRemoteRequest (VERSION _) =
- sendMessage lck external (ERROR "too late to send VERSION")
+ sendMessage lck external $ ERROR "too late to send VERSION"
handleAsyncMessage (ERROR err) = error $ "external special remote error: " ++ err
+ credstorage setting = CredPairStorage
+ { credPairFile = base
+ , credPairEnvironment = (base ++ "login", base ++ "password")
+ , credPairRemoteKey = Just setting
+ }
+ where
+ base = replace "/" "_" $ fromUUID (externalUUID external) ++ "-" ++ setting
+
sendMessage :: Sendable m => ExternalLock -> External -> m -> Annex ()
sendMessage lck external m =
fromExternal lck external externalSend $ \h ->
diff --git a/Remote/External/Types.hs b/Remote/External/Types.hs
index a4d49ddf1..fbd050fe1 100644
--- a/Remote/External/Types.hs
+++ b/Remote/External/Types.hs
@@ -44,6 +44,7 @@ import Control.Concurrent.STM
-- The
data External = External
{ externalType :: ExternalType
+ , externalUUID :: UUID
-- Empty until the remote is running.
, externalState :: TMVar ExternalState
-- Empty when a remote is in use.
@@ -52,9 +53,10 @@ data External = External
, externalConfig :: TMVar RemoteConfig
}
-newExternal :: ExternalType -> RemoteConfig -> Annex External
-newExternal externaltype c = liftIO $ External
+newExternal :: ExternalType -> UUID -> RemoteConfig -> Annex External
+newExternal externaltype u c = liftIO $ External
<$> pure externaltype
+ <*> pure u
<*> atomically newEmptyTMVar
<*> atomically (newTMVar ExternalLock)
<*> atomically (newTMVar c)
@@ -157,6 +159,8 @@ data RemoteRequest
| DIRHASH Key
| SETCONFIG Setting String
| GETCONFIG Setting
+ | SETCREDS Setting String String
+ | GETCREDS Setting
deriving (Show)
instance Receivable RemoteRequest where
@@ -165,15 +169,19 @@ instance Receivable RemoteRequest where
parseCommand "DIRHASH" = parse1 DIRHASH
parseCommand "SETCONFIG" = parse2 SETCONFIG
parseCommand "GETCONFIG" = parse1 GETCONFIG
+ parseCommand "SETCREDS" = parse3 SETCREDS
+ parseCommand "GETCREDS" = parse1 GETCREDS
parseCommand _ = parseFail
-- Responses to RemoteRequest.
data RemoteResponse
= VALUE String
+ | CREDS String String
deriving (Show)
instance Sendable RemoteResponse where
formatMessage (VALUE s) = [ "VALUE", serialize s ]
+ formatMessage (CREDS login password) = [ "CREDS", serialize login, serialize password ]
-- Messages that can be sent at any time by either git-annex or the remote.
data AsyncMessage
diff --git a/doc/design/external_special_remote_protocol.mdwn b/doc/design/external_special_remote_protocol.mdwn
index e93ead8d6..8fef581a0 100644
--- a/doc/design/external_special_remote_protocol.mdwn
+++ b/doc/design/external_special_remote_protocol.mdwn
@@ -189,6 +189,23 @@ in control.
can have been set by a previous SETCONFIG. Can be run at any time.
(git-annex replies with VALUE followed by the value. If the setting is
not set, the value will be empty.)
+* `SETCREDS Setting User Password`
+ When some form of user and password is needed to access a special remote,
+ this can be used to securely store them for later use.
+ (Like SETCONFIG, this is normally sent only during INITREMOTE.)
+ The Setting indicates which value in a remote's configuration can be
+ used to store the creds.
+ Note that creds are normally only stored in the remote's configuration
+ when it's surely safe to do so; when gpg encryption is used, in which
+ case the creds will be encrypted using it. If creds are not stored in
+ the configuration, they'll only be stored in a local file.
+ (embedcreds can be set to yes by the user or by SETCONFIG to force
+ the creds to be stored in the remote's configuration).
+* `GETCREDS Setting`
+ Gets any creds that were previously stored in the remote's configuration
+ or a file.
+ (git-annex replies with "CREDS User Password". If no creds are found,
+ User and Password are both empty.)
## general messages
diff --git a/doc/special_remotes/external/example.sh b/doc/special_remotes/external/example.sh
index 97f8a2813..428e2ecb9 100755
--- a/doc/special_remotes/external/example.sh
+++ b/doc/special_remotes/external/example.sh
@@ -48,6 +48,32 @@ ask () {
esac
}
+# This remote doesn't need credentials to access it,
+# but many of them will. Here's how to handle requiring the user
+# set MYPASSWORD and MYLOGIN when running initremote. The creds
+# will be stored securely for later use, so the user only needs
+# to provide them once.
+setupcreds () {
+ if [ -z "$MYPASSWORD" ] || [ -z "$MYLOGIN" ]; then
+ echo INITREMOTE-FAILURE "You need to set MYPASSWORD and MYLOGIN environment variables when running initremote."
+ else
+ echo SETCREDS mycreds "$MYLOGIN" "$MYPASSWORD"
+ echo INITREMOTE-SUCCESS
+ fi
+}
+
+getcreds () {
+ echo GETCREDS mycreds
+ read resp
+ case "${resp%% *}" in
+ CREDS)
+ MYLOGIN="$(echo "$resp" | sed 's/^CREDS \([^ ]*\) .*/\1/')"
+ MYPASSWORD="$(echo "$resp" | sed 's/^CREDS [^ ]* //')"
+ ;;
+ esac
+
+}
+
# This has to come first, to get the protocol started.
echo VERSION 1
@@ -66,16 +92,17 @@ while read line; do
# git annex initremote or git annex enableremote is
# run.)
+ # The directory provided by the user
+ # could be relative; make it absolute,
+ # and store that.
getconfig directory
- # Input directory could be relative; make it
- # absolute, and store that.
- mydirectory="$(readlink -f "$RET")"
+ mydirectory="$(readlink -f "$RET")" || true
setconfig directory "$mydirectory"
if [ -z "$mydirectory" ]; then
echo INITREMOTE-FAILURE "You need to set directory="
else
if mkdir -p "$mydirectory"; then
- echo INITREMOTE-SUCCESS
+ setupcreds
else
echo INITREMOTE-FAILURE "Failed to write to $mydirectory"
fi
@@ -87,6 +114,7 @@ while read line; do
# special remote here.
getconfig directory
mydirectory="$RET"
+ getcreds
echo PREPARE-SUCCESS
;;
TRANSFER)