Skip to content

Commit 6b6a6ea

Browse files
committed
Retool Credential manager to use a singleton
1 parent dbbbe9c commit 6b6a6ea

File tree

5 files changed

+58
-44
lines changed

5 files changed

+58
-44
lines changed

hie.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ cradle:
4242
- path: "lib/unison-hashing/src"
4343
component: "unison-hashing:lib"
4444

45+
- path: "lib/unison-credentials/src"
46+
component: "unison-credentials:lib"
47+
48+
4549
- path: "lib/unison-prelude/src"
4650
component: "unison-prelude:lib"
4751

@@ -98,7 +102,7 @@ cradle:
98102

99103
- path: "parser-typechecker/tests"
100104
component: "unison-parser-typechecker:test:parser-typechecker-tests"
101-
105+
102106
- path: "unison-runtime/src"
103107
component: "unison-runtime:lib"
104108

lib/unison-credentials/src/Unison/Auth/CredentialFile.hs

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import Data.Aeson qualified as Aeson
66
import System.FilePath (takeDirectory, (</>))
77
import System.IO.LockFile
88
import Unison.Auth.Types
9-
import Unison.Debug qualified as Debug
109
import Unison.Prelude
1110
import UnliftIO.Directory
1211

@@ -26,26 +25,26 @@ getCredentialJSONFilePath = do
2625

2726
-- | Atomically update the credential storage file.
2827
-- Creates an empty file automatically if one doesn't exist.
29-
atomicallyModifyCredentialsFile :: (MonadIO m) => (Credentials -> Credentials) -> m Credentials
30-
atomicallyModifyCredentialsFile f = liftIO $ do
31-
credentialJSONPath <- getCredentialJSONFilePath
32-
doesFileExist credentialJSONPath >>= \case
28+
atomicallyModifyCredentialsFile :: (MonadUnliftIO m) => (Credentials -> m (Credentials, r)) -> m r
29+
atomicallyModifyCredentialsFile f = do
30+
credentialJSONPath <- liftIO $ getCredentialJSONFilePath
31+
liftIO (doesFileExist credentialJSONPath) >>= \case
3332
True -> pure ()
34-
False -> do
33+
False -> liftIO $ do
3534
createDirectoryIfMissing True $ takeDirectory credentialJSONPath
3635
Aeson.encodeFile credentialJSONPath emptyCredentials
3736

38-
withLockFile lockfileConfig (withLockExt credentialJSONPath) $ do
37+
toIO <- askRunInIO
38+
liftIO $ withLockFile lockfileConfig (withLockExt credentialJSONPath) $ toIO $ do
3939
credentials <-
40-
Aeson.eitherDecodeFileStrict credentialJSONPath >>= \case
40+
liftIO (Aeson.eitherDecodeFileStrict credentialJSONPath) >>= \case
4141
-- If something goes wrong, just wipe the credentials file so we're in a clean slate.
4242
-- In the worst case the user will simply need to log in again.
43-
Left err -> do
44-
Debug.debugM Debug.Auth "Error decoding credentials file" err
45-
Aeson.encodeFile credentialJSONPath emptyCredentials
43+
Left _err -> do
44+
liftIO $ Aeson.encodeFile credentialJSONPath emptyCredentials
4645
pure emptyCredentials
4746
Right creds -> pure creds
48-
let newCredentials = f credentials
47+
(newCredentials, r) <- f credentials
4948
when (newCredentials /= credentials) $ do
50-
Aeson.encodeFile credentialJSONPath $ newCredentials
51-
pure newCredentials
49+
liftIO $ Aeson.encodeFile credentialJSONPath newCredentials
50+
pure r

lib/unison-credentials/src/Unison/Auth/CredentialManager.hs

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ module Unison.Auth.CredentialManager
44
( saveCredentials,
55
CredentialManager,
66
newCredentialManager,
7-
getCredentials,
7+
getCodeserverCredentials,
88
getOrCreatePersonalKey,
99
isExpired,
1010
)
@@ -13,9 +13,11 @@ where
1313
import Control.Monad.Trans.Except
1414
import Data.Map qualified as Map
1515
import 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
1718
import 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
1921
import Unison.Prelude
2022
import Unison.Share.Types (CodeserverId)
2123
import 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.
3240
getOrCreatePersonalKey :: (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.
4450
saveCredentials :: (UnliftIO.MonadUnliftIO m) => CredentialManager -> CodeserverId -> CodeserverCredentials -> m ()
4551
saveCredentials 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
5056
modifyCredentials (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.
7081
isExpired :: (MonadIO m) => CodeserverCredentials -> m Bool

unison-cli/src/Unison/Auth/Tokens.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ newTokenProvider manager host = UnliftIO.try @_ @CredentialFailure $ do
3737
-- If the access token is provided via environment variable, we don't need to refresh it.
3838
pure accessToken
3939
Nothing -> do
40-
creds@CodeserverCredentials {tokens, discoveryURI} <- throwEitherM $ getCredentials manager host
40+
creds@CodeserverCredentials {tokens, discoveryURI} <- throwEitherM $ getCodeserverCredentials manager host
4141
let Tokens {accessToken = currentAccessToken} = tokens
4242
expired <- isExpired creds
4343
if expired

unison-cli/src/Unison/Codebase/Editor/HandleInput/AuthLogin.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import Network.Wai
2323
import Network.Wai qualified as Wai
2424
import Network.Wai.Handler.Warp qualified as Warp
2525
import U.Codebase.Sqlite.Queries qualified as Q
26-
import Unison.Auth.CredentialManager (getCredentials, saveCredentials)
26+
import Unison.Auth.CredentialManager (getCodeserverCredentials, saveCredentials)
2727
import Unison.Auth.Discovery (discoveryURIForCodeserver, fetchDiscoveryDoc)
2828
import Unison.Auth.Types
2929
( Code,
@@ -55,7 +55,7 @@ ucmOAuthClientID = "ucm"
5555
ensureAuthenticatedWithCodeserver :: CodeserverURI -> Cli UserInfo
5656
ensureAuthenticatedWithCodeserver codeserverURI = do
5757
Cli.Env {credentialManager} <- ask
58-
getCredentials credentialManager (codeserverIdFromCodeserverURI codeserverURI) >>= \case
58+
getCodeserverCredentials credentialManager (codeserverIdFromCodeserverURI codeserverURI) >>= \case
5959
Right (CodeserverCredentials {userInfo}) -> pure userInfo
6060
Left _ -> authLogin codeserverURI
6161

0 commit comments

Comments
 (0)