feat(auth): WIP authorization function

This commit is contained in:
David Mosbach 2023-12-03 15:06:39 +00:00
parent 2351388826
commit 9b9370fed0
3 changed files with 69 additions and 25 deletions

View File

@ -5,7 +5,7 @@
{-# OPTIONS_GHC -fno-warn-orphans #-} {-# OPTIONS_GHC -fno-warn-orphans #-}
module Auth.OAuth2 module Auth.OAuth2
( OAuthUserException(..) ( AzureUserException(..)
, oauth2MockServer , oauth2MockServer
, mockPluginName , mockPluginName
) where ) where
@ -18,11 +18,12 @@ import Yesod.Auth.OAuth2
import Yesod.Auth.OAuth2.Prelude import Yesod.Auth.OAuth2.Prelude
data OAuthUserException = OAuthUserError data AzureUserException = AzureUserError
| OAuthUserAmbiguous -- TODO | AzureUserNoResult
| AzureUserAmbiguous -- TODO
deriving (Show, Eq, Generic) deriving (Show, Eq, Generic)
instance Exception OAuthUserException instance Exception AzureUserException
---------------------------------------- ----------------------------------------
---- OAuth2 development auth plugin ---- ---- OAuth2 development auth plugin ----

View File

@ -1,4 +1,4 @@
-- SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de> -- SPDX-FileCopyrightText: 2023 Gregor Kleen <gregor.kleen@ifi.lmu.de>,David Mosbach <david.mosbach@uniworx.de>
-- --
-- SPDX-License-Identifier: AGPL-3.0-or-later -- SPDX-License-Identifier: AGPL-3.0-or-later
@ -6,6 +6,9 @@ module Foundation.Types
( UpsertCampusUserMode(..) ( UpsertCampusUserMode(..)
, _UpsertCampusUserLoginLdap, _UpsertCampusUserLoginDummy, _UpsertCampusUserLoginOther, _UpsertCampusUserLdapSync, _UpsertCampusUserGuessUser , _UpsertCampusUserLoginLdap, _UpsertCampusUserLoginDummy, _UpsertCampusUserLoginOther, _UpsertCampusUserLdapSync, _UpsertCampusUserGuessUser
, _upsertCampusUserIdent , _upsertCampusUserIdent
, UpsertAzureUserMode(..)
, _UpsertAzureUserLoginOAuth, _UpsertAzureUserLoginDummy, _UpsertAzureUserLoginOther, _UpsertAzureUserOAuthSync, _UpsertAzureUserGuessUser
, _upsertAzureUserIdent
) where ) where
import Import.NoFoundation import Import.NoFoundation
@ -21,3 +24,16 @@ data UpsertCampusUserMode
makeLenses_ ''UpsertCampusUserMode makeLenses_ ''UpsertCampusUserMode
makePrisms ''UpsertCampusUserMode makePrisms ''UpsertCampusUserMode
-- Azure users logging in via OAuth2
data UpsertAzureUserMode
= UpsertAzureUserLoginOAuth
| UpsertAzureUserLoginDummy { upsertAzureUserIdent :: UserIdent }
| UpsertAzureUserLoginOther { upsertAzureUserIdent :: UserIdent }
| UpsertAzureUserOAuthSync { upsertAzureUserIdent :: UserIdent }
| UpsertAzureUserGuessUser
deriving (Eq, Ord, Read, Show, Generic)
makeLenses_ ''UpsertAzureUserMode
makePrisms ''UpsertAzureUserMode

View File

@ -25,6 +25,7 @@ import Foundation.Authorization (AuthorizationCacheKey(..))
import Yesod.Auth.Message import Yesod.Auth.Message
import Auth.LDAP import Auth.LDAP
import Auth.OAuth2
import Auth.PWHash (apHash) import Auth.PWHash (apHash)
import Auth.Dummy (apDummy) import Auth.Dummy (apDummy)
@ -122,22 +123,22 @@ authenticate creds@Creds{..} = liftHandler . runDB . withReaderT projectBackend
-> acceptExisting -> acceptExisting
-- | Authentication via OAuth 2 -- | Authentication via AzureADv2 / OAuth 2
oAuthenticate :: ( MonadHandler m, HandlerSite m ~ UniWorX oAuthenticate :: ( MonadHandler m, HandlerSite m ~ UniWorX
, YesodPersist UniWorX, BackendCompatible SqlBackend (YesodPersistBackend UniWorX) , YesodPersist UniWorX, BackendCompatible SqlBackend (YesodPersistBackend UniWorX)
, YesodAuth UniWorX, UserId ~ AuthId UniWorX , YesodAuth UniWorX, UserId ~ AuthId UniWorX
) )
=> Creds UniWorX -> m (AuthenticationResult UniWorX) => Creds UniWorX -> m (AuthenticationResult UniWorX)
oAuthenticate creds@Creds{..} = liftHandler . runDB . withReaderT projectBackend $ do oAuthenticate creds@Creds{..} = liftHandler . runDB . withReaderT projectBackend $ do
$logErrorS "Auth" $ "\a\27[31m" <> tshow creds <> "\27[0m" $logErrorS "OAuth" $ "\a\27[31m" <> tshow creds <> "\27[0m"
now <- liftIO getCurrentTime now <- liftIO getCurrentTime
let let
uAuth = UniqueAuthentication $ CI.mk credsIdent uAuth = UniqueAuthentication $ CI.mk credsIdent
upsertMode = creds ^? _upsertCampusUserMode -- TODO adjust do OAuth upsertMode = creds ^? _upsertAzureUserMode
isDummy = is (_Just . _UpsertCampusUserLoginDummy) upsertMode isDummy = is (_Just . _UpsertAzureUserLoginDummy) upsertMode -- mock server
isOther = is (_Just . _UpsertCampusUserLoginOther) upsertMode isOther = is (_Just . _UpsertAzureUserLoginOther) upsertMode
excRecovery res excRecovery res
| isDummy || isOther | isDummy || isOther
@ -152,18 +153,18 @@ oAuthenticate creds@Creds{..} = liftHandler . runDB . withReaderT projectBackend
excHandlers = excHandlers =
[ C.Handler $ \case [ C.Handler $ \case
CampusUserNoResult -> do AzureUserNoResult -> do
$logWarnS "LDAP" $ "User lookup failed after successful login for " <> credsIdent $logWarnS "OAuth" $ "User lookup failed after successful login for " <> credsIdent
excRecovery . UserError $ IdentifierNotFound credsIdent excRecovery . UserError $ IdentifierNotFound credsIdent
CampusUserAmbiguous -> do AzureUserAmbiguous -> do
$logWarnS "LDAP" $ "Multiple LDAP results for " <> credsIdent $logWarnS "OAuth" $ "Multiple OAuth results for " <> credsIdent
excRecovery . UserError $ IdentifierNotFound credsIdent excRecovery . UserError $ IdentifierNotFound credsIdent
err -> do err -> do
$logErrorS "LDAP" $ tshow err $logErrorS "OAuth" $ tshow err
mr <- getMessageRender mr <- getMessageRender
excRecovery . ServerError $ mr MsgInternalLdapError excRecovery . ServerError $ mr MsgInternalLdapError -- TODO where does this come from?
, C.Handler $ \(cExc :: CampusUserConversionException) -> do , C.Handler $ \(cExc :: CampusUserConversionException) -> do -- TODO new exception type or not?
$logErrorS "LDAP" $ tshow cExc $logErrorS "OAuth" $ tshow cExc
mr <- getMessageRender mr <- getMessageRender
excRecovery . ServerError $ mr cExc excRecovery . ServerError $ mr cExc
] ]
@ -181,12 +182,15 @@ oAuthenticate creds@Creds{..} = liftHandler . runDB . withReaderT projectBackend
| not isDummy -> res <$ update uid [ UserLastAuthentication =. Just now ] | not isDummy -> res <$ update uid [ UserLastAuthentication =. Just now ]
_other -> return res _other -> return res
$logDebugS "auth" $ tshow Creds{..} $logDebugS "oauth" $ tshow Creds{..}
pool <- getsYesod $ view _appLdapPool {-(case credsPlugin of -- TODO look user up in DB
"azureadv2" -> getsYesod $ view _appLdapPool -- TODO -- If not in DB then put (maybe prompt for email)
mockPluginName -> getsYesod $ view _appLdapPool -- TODO -- If in DB but first time oauth then prompt for password & update entry
_ -> error "undefined" -- TODO -- Now user should be in DB -> authenticated
)-} flip catches excHandlers $ case upsertMode of
Just upsertMode' -> error $ show upsertMode' --TODO
Nothing -> error "nothing" --TODO
{-pool <- getsYesod $ view _appLdapPool
flip catches excHandlers $ case pool of flip catches excHandlers $ case pool of
Just ldapPool Just ldapPool
| Just upsertMode' <- upsertMode -> do | Just upsertMode' <- upsertMode -> do
@ -194,7 +198,7 @@ oAuthenticate creds@Creds{..} = liftHandler . runDB . withReaderT projectBackend
$logDebugS "LDAP" $ "Successful LDAP lookup: " <> tshow ldapData $logDebugS "LDAP" $ "Successful LDAP lookup: " <> tshow ldapData
Authenticated . entityKey <$> upsertCampusUser upsertMode' ldapData Authenticated . entityKey <$> upsertCampusUser upsertMode' ldapData
_other _other
-> acceptExisting -> acceptExisting-}
@ -231,6 +235,29 @@ _upsertCampusUserMode mMode cs@Creds{..}
defaultOther = apHash defaultOther = apHash
_upsertAzureUserMode :: Traversal' (Creds UniWorX) UpsertAzureUserMode
_upsertAzureUserMode mMode cs@Creds{..}
| credsPlugin == mockPluginName = setMode <$> mMode (UpsertAzureUserLoginDummy $ CI.mk credsIdent)
| credsPlugin == "azureadv2" = setMode <$> mMode UpsertAzureUserLoginOAuth
| otherwise = setMode <$> mMode (UpsertAzureUserLoginOther $ CI.mk credsIdent)
where
setMode UpsertAzureUserLoginOAuth
= cs{ credsPlugin = "azureadv2" }
setMode (UpsertAzureUserLoginDummy ident)
= cs{ credsPlugin = mockPluginName
, credsIdent = CI.original ident
}
setMode (UpsertAzureUserLoginOther ident)
= cs{ credsPlugin = bool defaultOther credsPlugin (credsPlugin /= mockPluginName && credsPlugin /= "azureadv2")
, credsIdent = CI.original ident
}
setMode _ = cs
defaultOther = apHash
ldapLookupAndUpsert :: forall m. (MonadHandler m, HandlerSite m ~ UniWorX, MonadMask m, MonadUnliftIO m) => Text -> SqlPersistT m (Entity User) ldapLookupAndUpsert :: forall m. (MonadHandler m, HandlerSite m ~ UniWorX, MonadMask m, MonadUnliftIO m) => Text -> SqlPersistT m (Entity User)
ldapLookupAndUpsert ident = ldapLookupAndUpsert ident =
getsYesod (view _appLdapPool) >>= \case getsYesod (view _appLdapPool) >>= \case