@@ -4,7 +4,7 @@ module Unison.Auth.CredentialManager
44 ( saveCredentials ,
55 CredentialManager ,
66 newCredentialManager ,
7- getCredentials ,
7+ getCodeserverCredentials ,
88 getOrCreatePersonalKey ,
99 isExpired ,
1010 )
1313import Control.Monad.Trans.Except
1414import Data.Map qualified as Map
1515import Data.Time.Clock (addUTCTime , diffUTCTime , getCurrentTime )
16- import Unison.Auth.CredentialFile
16+ import System.IO.Unsafe (unsafePerformIO )
17+ import Unison.Auth.CredentialFile qualified as CF
1718import Unison.Auth.PersonalKey (PersonalPrivateKey , generatePersonalKey )
18- import Unison.Auth.Types
19+ import Unison.Auth.Types hiding (getCodeserverCredentials )
20+ import Unison.Auth.Types qualified as Auth
1921import Unison.Prelude
2022import Unison.Share.Types (CodeserverId )
2123import UnliftIO qualified
@@ -25,46 +27,55 @@ import UnliftIO qualified
2527-- Note: Currently the in-memory cache is _not_ updated if a different UCM updates
2628-- the credentials file, however this shouldn't pose any problems, since auth will still
2729-- be refreshed if we encounter any auth failures on requests.
28- newtype CredentialManager = CredentialManager (UnliftIO. MVar Credentials )
30+ newtype CredentialManager = CredentialManager (UnliftIO. MVar (Maybe Credentials {- Credentials may or may not be initialized -} ))
31+
32+ -- | A global CredentialManager instance/singleton.
33+ globalCredentialsManager :: CredentialManager
34+ globalCredentialsManager = unsafePerformIO do
35+ CredentialManager <$> UnliftIO. newMVar Nothing
36+ {-# NOINLINE globalCredentialsManager #-}
2937
3038-- | Fetches the user's personal key from the active profile, if it exists.
3139-- Otherwise it creates a new personal key, saves it to the active profile, and returns it.
3240getOrCreatePersonalKey :: (MonadUnliftIO m ) => CredentialManager -> m PersonalPrivateKey
33- getOrCreatePersonalKey credMan@ (CredentialManager credsVar) = do
34- Credentials {activeProfile, personalKeys} <- liftIO (UnliftIO. readMVar credsVar)
35- case Map. lookup activeProfile personalKeys of
36- Just pk -> pure pk
37- Nothing -> do
38- pk <- generatePersonalKey
39- _ <- modifyCredentials credMan $ \ creds ->
40- creds {personalKeys = Map. insert activeProfile pk creds. personalKeys}
41- pure pk
41+ getOrCreatePersonalKey credMan = do
42+ modifyCredentials credMan \ creds@ (Credentials {activeProfile, personalKeys}) -> do
43+ case Map. lookup activeProfile personalKeys of
44+ Just pk -> pure (creds, pk)
45+ Nothing -> do
46+ pk <- generatePersonalKey
47+ pure (creds {personalKeys = Map. insert activeProfile pk personalKeys}, pk)
4248
4349-- | Saves credentials to the active profile.
4450saveCredentials :: (UnliftIO. MonadUnliftIO m ) => CredentialManager -> CodeserverId -> CodeserverCredentials -> m ()
4551saveCredentials credManager aud creds = do
46- void . modifyCredentials credManager $ setCodeserverCredentials aud creds
52+ void . modifyCredentials credManager $ \ cf -> pure ( setCodeserverCredentials aud creds cf, () )
4753
4854-- | Atomically update the credential storage file, and update the in-memory cache.
49- modifyCredentials :: (UnliftIO. MonadUnliftIO m ) => CredentialManager -> (Credentials -> Credentials ) -> m Credentials
55+ modifyCredentials :: (UnliftIO. MonadUnliftIO m ) => CredentialManager -> (Credentials -> m ( Credentials , r )) -> m r
5056modifyCredentials (CredentialManager credsVar) f = do
5157 UnliftIO. modifyMVar credsVar $ \ _ -> do
52- newCreds <- atomicallyModifyCredentialsFile f
53- pure (newCreds, newCreds)
58+ (creds, r) <- CF. atomicallyModifyCredentialsFile (f >=> \ (creds', r') -> pure (creds', (creds', r')))
59+ pure (Just creds, r)
60+
61+ readCredentials :: (UnliftIO. MonadUnliftIO m ) => CredentialManager -> m Credentials
62+ readCredentials (CredentialManager credsVar) = do
63+ UnliftIO. modifyMVar credsVar $ \ mayCreds -> case mayCreds of
64+ Just creds -> pure (mayCreds, creds)
65+ Nothing -> do
66+ creds <- CF. atomicallyModifyCredentialsFile \ c -> pure (c, c)
67+ pure (Just creds, creds)
5468
55- getCredentials :: (MonadIO m ) => CredentialManager -> CodeserverId -> m (Either CredentialFailure CodeserverCredentials )
56- getCredentials ( CredentialManager credsVar) aud = runExceptT do
57- creds <- lift ( UnliftIO. readMVar credsVar)
58- codeserverCreds <- except (getCodeserverCredentials aud creds)
69+ getCodeserverCredentials :: (MonadIO m ) => CredentialManager -> CodeserverId -> m (Either CredentialFailure CodeserverCredentials )
70+ getCodeserverCredentials credMan aud = runExceptT do
71+ creds <- liftIO $ readCredentials credMan
72+ codeserverCreds <- except (Auth. getCodeserverCredentials aud creds)
5973 lift (isExpired codeserverCreds) >>= \ case
6074 True -> throwE (ReauthRequired aud)
6175 False -> pure codeserverCreds
6276
63- newCredentialManager :: (MonadIO m ) => m CredentialManager
64- newCredentialManager = do
65- credentials <- atomicallyModifyCredentialsFile id
66- credentialsVar <- UnliftIO. newMVar credentials
67- pure (CredentialManager credentialsVar)
77+ newCredentialManager :: CredentialManager
78+ newCredentialManager = globalCredentialsManager
6879
6980-- | Checks whether CodeserverCredentials are expired.
7081isExpired :: (MonadIO m ) => CodeserverCredentials -> m Bool
0 commit comments