Merge branch '139-single-sign-on-sso-routing-anpassen' into 142-userdata-oauth-mode

This commit is contained in:
Sarah Vaupel 2024-03-11 14:50:07 +01:00
commit cf6ae898c4
15 changed files with 170 additions and 58 deletions

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, Gregor Kleen <gregor.kleen@ifi.lmu.de>, Sarah Vaupel <sarah.vaupel@ifi.lmu.de>, Steffen Jost <jost@tcs.ifi.lmu.de>, Wolfgang Witt <Wolfgang.Witt@campus.lmu.de> # SPDX-FileCopyrightText: 2022-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, David Mosbach <david.mosbach@uniworx.de>, Gregor Kleen <gregor.kleen@ifi.lmu.de>, Sarah Vaupel <sarah.vaupel@ifi.lmu.de>, Steffen Jost <jost@tcs.ifi.lmu.de>, Wolfgang Witt <Wolfgang.Witt@campus.lmu.de>
# #
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
@ -126,10 +126,9 @@ database:
database: "_env:PGDATABASE:uniworx" database: "_env:PGDATABASE:uniworx"
poolsize: "_env:PGPOOLSIZE:990" poolsize: "_env:PGPOOLSIZE:990"
auto-db-migrate: '_env:AUTO_DB_MIGRATE:true' auto-db-migrate: "_env:AUTO_DB_MIGRATE:true"
# External sources used for user authentication and userdata lookups # External sources used for user authentication and userdata lookups
# TODO: add SSO option for user-auth config
user-auth: user-auth:
# mode: single-source # mode: single-source
protocol: azureadv2 protocol: azureadv2
@ -150,6 +149,8 @@ user-auth:
# timeout: "_env:LDAPTIMEOUT:5" # timeout: "_env:LDAPTIMEOUT:5"
# search-timeout: "_env:LDAPSEARCHTIME:5" # search-timeout: "_env:LDAPSEARCHTIME:5"
single-sign-on: "_env:OIDC_SSO:true"
# TODO: generalize for arbitrary auth protocols # TODO: generalize for arbitrary auth protocols
# TODO: maybe use separate pools for external databases? # TODO: maybe use separate pools for external databases?
ldap-pool: ldap-pool:

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, Gregor Kleen <gregor.kleen@ifi.lmu.de>, Sarah Vaupel <sarah.vaupel@ifi.lmu.de>, Steffen Jost <jost@tcs.ifi.lmu.de>, Winnie Ros <winnie.ros@campus.lmu.de> # SPDX-FileCopyrightText: 2022-2024 David Mosbach <david.mosbach@uniworx.de>, Sarah Vaupel <sarah.vaupel@uniworx.de>, Gregor Kleen <gregor.kleen@ifi.lmu.de>, Sarah Vaupel <sarah.vaupel@ifi.lmu.de>, Steffen Jost <jost@tcs.ifi.lmu.de>, Winnie Ros <winnie.ros@campus.lmu.de>
# #
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
@ -139,3 +139,6 @@ FormHoneypotNamePlaceholder: Name
FormHoneypotComment: Kommentar FormHoneypotComment: Kommentar
FormHoneypotCommentPlaceholder: Kommentar FormHoneypotCommentPlaceholder: Kommentar
FormHoneypotFilled: Bitte füllen Sie keines der verstecken Felder aus FormHoneypotFilled: Bitte füllen Sie keines der verstecken Felder aus
Logout: Abmeldung
SingleSignOut: Abmeldung bei Azure

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, Gregor Kleen <gregor.kleen@ifi.lmu.de>, Sarah Vaupel <sarah.vaupel@ifi.lmu.de>, Steffen Jost <jost@tcs.ifi.lmu.de>, Winnie Ros <winnie.ros@campus.lmu.de> # SPDX-FileCopyrightText: 2022-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, David Mosbach <david.mosbach@uniworx.de>, Gregor Kleen <gregor.kleen@ifi.lmu.de>, Sarah Vaupel <sarah.vaupel@ifi.lmu.de>, Steffen Jost <jost@tcs.ifi.lmu.de>, Winnie Ros <winnie.ros@campus.lmu.de>
# #
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
@ -140,3 +140,6 @@ FormHoneypotNamePlaceholder !ident-ok: Name
FormHoneypotComment: Comment FormHoneypotComment: Comment
FormHoneypotCommentPlaceholder: Comment FormHoneypotCommentPlaceholder: Comment
FormHoneypotFilled: Please do not fill in any of the hidden fields FormHoneypotFilled: Please do not fill in any of the hidden fields
Logout: Logout
SingleSignOut: Azure logout

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <jost@cip.ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>,Winnie Ros <winnie.ros@campus.lmu.de> # SPDX-FileCopyrightText: 2022-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, David Mosbach <david.mosbach@uniworx.de>, Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <jost@cip.ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>,Winnie Ros <winnie.ros@campus.lmu.de>
# #
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
@ -24,6 +24,7 @@ MenuPayments: Zahlungsbedingungen
MenuInstance: Instanz-Identifikation MenuInstance: Instanz-Identifikation
MenuHealth: Instanz-Zustand MenuHealth: Instanz-Zustand
MenuHelp: Hilfe MenuHelp: Hilfe
MenuAccount: Konto
MenuProfile: Anpassen MenuProfile: Anpassen
MenuLogin !ident-ok: Login MenuLogin !ident-ok: Login
MenuLogout !ident-ok: Logout MenuLogout !ident-ok: Logout

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>,Winnie Ros <winnie.ros@campus.lmu.de> # SPDX-FileCopyrightText: 2022-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, David Mosbach <david.mosbach@uniworx.de>, Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>,Winnie Ros <winnie.ros@campus.lmu.de>
# #
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
@ -24,6 +24,7 @@ MenuPayments: Payment Terms
MenuInstance: Instance identification MenuInstance: Instance identification
MenuHealth: Instance health MenuHealth: Instance health
MenuHelp: Support MenuHelp: Support
MenuAccount: Account
MenuProfile: Settings MenuProfile: Settings
MenuLogin: Login MenuLogin: Login
MenuLogout: Logout MenuLogout: Logout

5
routes
View File

@ -1,4 +1,4 @@
-- SPDX-FileCopyrightText: 2022-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>,Wolfgang Witt <Wolfgang.Witt@campus.lmu.de> -- SPDX-FileCopyrightText: 2022-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>,Wolfgang Witt <Wolfgang.Witt@campus.lmu.de>,David Mosbach <david.mosbach@uniworx.de>
-- --
-- SPDX-License-Identifier: AGPL-3.0-or-later -- SPDX-License-Identifier: AGPL-3.0-or-later
@ -46,6 +46,9 @@
/static StaticR EmbeddedStatic appStatic !free /static StaticR EmbeddedStatic appStatic !free
/auth AuthR Auth getAuth !free /auth AuthR Auth getAuth !free
/logout SOutR GET !free
/logout/ssout SSOutR GET !free -- single sign-out (OIDC)
/metrics MetricsR GET !free -- verify if this can be free /metrics MetricsR GET !free -- verify if this can be free
/err ErrorR GET !free /err ErrorR GET !free

View File

@ -9,7 +9,8 @@ let
haskellPackages = pkgs.haskellPackages; haskellPackages = pkgs.haskellPackages;
oauth2Flake = (builtins.getFlake "git+https://gitlab.uniworx.de/mosbach/oauth2-mock-server/?rev=d47908b4f7883b4b485abf1ee06645495ccdc7b3&ref=user-queries").packages.x86_64-linux; oauth2Flake = (builtins.getFlake "git+https://gitlab.uniworx.de/mosbach/oauth2-mock-server/?rev=7b995e6cffa963a24eb5d0373b2d29089533284f&ref=main").packages.x86_64-linux;
oauth2MockServer = oauth2Flake.default; oauth2MockServer = oauth2Flake.default;
mkOauth2DB = oauth2Flake.mkOauth2DB; mkOauth2DB = oauth2Flake.mkOauth2DB;

View File

@ -163,6 +163,7 @@ import Handler.PrintCenter
import Handler.ApiDocs import Handler.ApiDocs
import Handler.Swagger import Handler.Swagger
import Handler.Firm import Handler.Firm
import Handler.SingleSignOut
import ServantApi () -- YesodSubDispatch instances import ServantApi () -- YesodSubDispatch instances
import Servant.API import Servant.API

View File

@ -13,13 +13,14 @@ module Auth.OAuth2
, azureMockServer , azureMockServer
, queryOAuth2User , queryOAuth2User
, refreshOAuth2Token , refreshOAuth2Token
, singleSignOut
) where ) where
-- import qualified Data.CaseInsensitive as CI -- import qualified Data.CaseInsensitive as CI
import Data.Maybe (fromJust) import Data.Maybe (fromJust)
import Data.Text import Data.Text
import Import.NoFoundation hiding (unpack) import Import.NoFoundation hiding (pack, unpack)
import Network.HTTP.Simple (httpJSONEither, getResponseBody, JSONException) import Network.HTTP.Simple (httpJSONEither, getResponseBody, JSONException)
@ -102,9 +103,9 @@ azureUserPreferredLanguage = "preferredLanguage"
-- = runMaybeT . catchIfMaybeT (is _AzureUserNoResult) $ azureUser conf (Creds apAzure (CI.original userIdent) []) -- = runMaybeT . catchIfMaybeT (is _AzureUserNoResult) $ azureUser conf (Creds apAzure (CI.original userIdent) [])
---------------------------------------- -----------------------------------------------
---- OAuth2 development auth plugin ---- ---- OAuth2 + OIDC development auth plugin ----
---------------------------------------- -----------------------------------------------
apAzureMock :: Text apAzureMock :: Text
apAzureMock = "uniworx_dev" apAzureMock = "uniworx_dev"
@ -119,7 +120,11 @@ azureMockServer port =
let oa = OAuth2 let oa = OAuth2
{ oauth2ClientId = "42" { oauth2ClientId = "42"
, oauth2ClientSecret = Just "shhh" , oauth2ClientSecret = Just "shhh"
, oauth2AuthorizeEndpoint = (fromString $ mockServerURL <> "/auth") `withQuery` [scopeParam " " ["ID", "Profile"]] , oauth2AuthorizeEndpoint = (fromString $ mockServerURL <> "/auth")
`withQuery` [ scopeParam " " ["openid", "profile", "email", "offline_access"] -- TODO read scopes from config
, ("response_type", "code id_token")
, ("nonce", "Foo") -- TODO generate meaningful value
]
, oauth2TokenEndpoint = fromString $ mockServerURL <> "/token" , oauth2TokenEndpoint = fromString $ mockServerURL <> "/token"
, oauth2RedirectUri = Nothing , oauth2RedirectUri = Nothing
} }
@ -165,7 +170,8 @@ queryOAuth2User userID = runExceptT $ do
setSessionJson SessionOAuth2Token (Just $ accessToken newTokens, refreshToken newTokens) setSessionJson SessionOAuth2Token (Just $ accessToken newTokens, refreshToken newTokens)
eResult <- lift $ getResponseBody <$> httpJSONEither @m @j (req eResult <- lift $ getResponseBody <$> httpJSONEither @m @j (req
{ secure = secure { secure = secure
, requestHeaders = [("Authorization", encodeUtf8 . ("Bearer " <>) . atoken $ accessToken newTokens)] }) , requestHeaders = [("Authorization", encodeUtf8 . ("Bearer " <>) . atoken $ accessToken newTokens)]
})
case eResult of case eResult of
Left x -> throwE $ UserDataJSONException x Left x -> throwE $ UserDataJSONException x
Right x -> return x Right x -> return x
@ -204,8 +210,8 @@ refreshOAuth2Token (_, rToken) url secure
body' <- if secure then do body' <- if secure then do
clientID <- liftIO $ fromJust <$> lookupEnv "CLIENT_ID" clientID <- liftIO $ fromJust <$> lookupEnv "CLIENT_ID"
clientSecret <- liftIO $ fromJust <$> lookupEnv "CLIENT_SECRET" clientSecret <- liftIO $ fromJust <$> lookupEnv "CLIENT_SECRET"
return $ body ++ [("client_id", fromString clientID), ("client_secret", fromString clientSecret), ("scope", "openid profile")] return $ body ++ [("client_id", fromString clientID), ("client_secret", fromString clientSecret), scopeParam " " ["openid","profile"," offline_access"]] -- TODO read from config
else return $ scopeParam " " ["ID","Profile"] : body else return $ scopeParam " " ["openid","profile","offline_access"] : body -- TODO read from config
$logErrorS "\27[31mAdmin Handler\27[0m" $ tshow (requestBody $ urlEncodedBody body' req{ secure = secure }) $logErrorS "\27[31mAdmin Handler\27[0m" $ tshow (requestBody $ urlEncodedBody body' req{ secure = secure })
eResult <- lift $ getResponseBody <$> httpJSONEither @m @OAuth2Token (urlEncodedBody body' req{ secure = secure }) eResult <- lift $ getResponseBody <$> httpJSONEither @m @OAuth2Token (urlEncodedBody body' req{ secure = secure })
case eResult of case eResult of
@ -216,3 +222,25 @@ refreshOAuth2Token (_, rToken) url secure
instance Show RequestBody where instance Show RequestBody where
show (RequestBodyLBS x) = show x show (RequestBodyLBS x) = show x
show _ = error ":(" show _ = error ":("
-----------------------
---- Single Sign-Out ----
-----------------------
singleSignOut :: forall a m. (MonadHandler m)
=> Maybe Text -- ^ redirect uri
-> m a
singleSignOut mRedirect = do
# ifdef DEVELOPMENT
port <- liftIO $ fromJust <$> lookupEnv "OAUTH2_SERVER_PORT"
let base = "http://localhost:" <> pack port <> "/logout"
# else
let base = "" -- TODO find out fraport oidc end_session_endpoint
# endif
endpoint = case mRedirect of
Just r -> base <> "?post_logout_redirect_uri=" <> r
Nothing -> base
$logErrorS "\n\27[31mSSO\27[0m" endpoint
redirect endpoint

View File

@ -11,11 +11,14 @@ module Foundation.Instances
, unsafeHandler , unsafeHandler
) where ) where
import qualified Prelude as P
import Import.NoFoundation import Import.NoFoundation
import qualified Data.Text as Text import qualified Data.Text as Text
import Data.List (inits) import Data.List (inits)
import Yesod.Auth.OAuth2
import qualified Yesod.Core.Unsafe as Unsafe import qualified Yesod.Core.Unsafe as Unsafe
import qualified Yesod.Auth.Message as Auth import qualified Yesod.Auth.Message as Auth
@ -24,6 +27,7 @@ import Auth.OAuth2 (apAzure, apAzureMock)
import Auth.LDAP import Auth.LDAP
import Auth.PWHash import Auth.PWHash
import Auth.Dummy import Auth.Dummy
import Auth.OAuth2
import qualified Foundation.Yesod.Session as UniWorX import qualified Foundation.Yesod.Session as UniWorX
import qualified Foundation.Yesod.Middleware as UniWorX import qualified Foundation.Yesod.Middleware as UniWorX
@ -43,6 +47,8 @@ import Foundation.DB
import Network.Wai.Parse (lbsBackEnd) import Network.Wai.Parse (lbsBackEnd)
import System.Environment (lookupEnv)
import UnliftIO.Pool (withResource) import UnliftIO.Pool (withResource)
import qualified Control.Monad.State.Class as State import qualified Control.Monad.State.Class as State
@ -129,14 +135,23 @@ instance YesodAuth UniWorX where
-- Where to send a user after logout -- Where to send a user after logout
logoutDest _ = NewsR logoutDest _ = NewsR
-- Override the above two destinations when a Referer: header is present -- Override the above two destinations when a Referer: header is present
redirectToReferer _ = True redirectToReferer _ = False
loginHandler = do loginHandler = do
plugins <- getsYesod authPlugins
AppSettings{..} <- getsYesod appSettings'
when appSingleSignOn $ do
let plugin = P.head $ P.filter ((`elem` [mockPluginName, azurePluginName]) . apName) plugins
pieces = case oauth2Url (apName plugin) of
PluginR _ p -> p
_ -> error "Unexpected OAuth2 AuthRoute"
void $ apDispatch plugin "GET" pieces
toParent <- getRouteToParent toParent <- getRouteToParent
liftHandler . defaultLayout $ do liftHandler . defaultLayout $ do
plugins <- getsYesod authPlugins
$logDebugS "Auth" $ "Enabled plugins: " <> Text.intercalate ", " (map apName plugins) $logDebugS "Auth" $ "Enabled plugins: " <> Text.intercalate ", " (map apName plugins)
mPort <- liftIO $ lookupEnv "OAUTH2_SERVER_PORT"
setTitleI MsgLoginTitle setTitleI MsgLoginTitle
$(widgetFile "login") $(widgetFile "login")
@ -158,6 +173,11 @@ instance YesodAuth UniWorX where
addMessage Success . toHtml $ mr Auth.NowLoggedIn addMessage Success . toHtml $ mr Auth.NowLoggedIn
-- onLogout = do
-- AppSettings{..} <- getsYesod appSettings'
-- when appSingleSignOn $ singleSignOut @UniWorX Nothing
onErrorHtml dest msg = do onErrorHtml dest msg = do
addMessage Error $ toHtml msg addMessage Error $ toHtml msg
redirect dest redirect dest

View File

@ -73,6 +73,8 @@ breadcrumb :: ( BearerAuthSite UniWorX
=> Route UniWorX => Route UniWorX
-> m Breadcrumb -> m Breadcrumb
breadcrumb (AuthR _) = i18nCrumb MsgMenuLogin $ Just NewsR breadcrumb (AuthR _) = i18nCrumb MsgMenuLogin $ Just NewsR
breadcrumb SOutR = i18nCrumb MsgLogout Nothing
breadcrumb SSOutR = i18nCrumb MsgSingleSignOut Nothing
breadcrumb (StaticR _) = i18nCrumb MsgBreadcrumbStatic Nothing breadcrumb (StaticR _) = i18nCrumb MsgBreadcrumbStatic Nothing
breadcrumb (WellKnownR _) = i18nCrumb MsgBreadcrumbWellKnown Nothing breadcrumb (WellKnownR _) = i18nCrumb MsgBreadcrumbWellKnown Nothing
breadcrumb MetricsR = i18nCrumb MsgBreadcrumbMetrics Nothing breadcrumb MetricsR = i18nCrumb MsgBreadcrumbMetrics Nothing
@ -540,42 +542,37 @@ defaultLinks :: ( MonadHandler m
, BearerAuthSite UniWorX , BearerAuthSite UniWorX
) => m [Nav] ) => m [Nav]
defaultLinks = fmap catMaybes . mapM runMaybeT $ -- Define the menu items of the header. defaultLinks = fmap catMaybes . mapM runMaybeT $ -- Define the menu items of the header.
[ return NavHeader [ return NavHeaderContainer
{ navHeaderRole = NavHeaderSecondary { navHeaderRole = NavHeaderSecondary
, navIcon = IconMenuLogout , navLabel = SomeMessage MsgMenuAccount
, navLink = NavLink , navIcon = IconMenuAccount
{ navLabel = MsgMenuLogout , navChildren =
, navRoute = AuthR LogoutR [ NavLink
, navAccess' = NavAccessHandler $ is _Just <$> maybeAuthId { navLabel = MsgMenuLogout
, navType = NavTypeLink { navModal = False } , navRoute = SSOutR -- AuthR LogoutR
, navQuick' = mempty , navAccess' = NavAccessHandler $ is _Just <$> maybeAuthId
, navForceActive = False , navType = NavTypeLink { navModal = False }
} , navQuick' = mempty
} , navForceActive = False
, return NavHeader }
{ navHeaderRole = NavHeaderSecondary , NavLink
, navIcon = IconMenuLogin { navLabel = MsgMenuLogin
, navLink = NavLink , navRoute = AuthR LoginR
{ navLabel = MsgMenuLogin , navAccess' = NavAccessHandler $ is _Nothing <$> maybeAuthId
, navRoute = AuthR LoginR , navType = NavTypeLink { navModal = False }
, navAccess' = NavAccessHandler $ is _Nothing <$> maybeAuthId , navQuick' = mempty
, navType = NavTypeLink { navModal = True } , navForceActive = False
, navQuick' = mempty }
, navForceActive = False , NavLink
} { navLabel = MsgMenuProfile
} , navRoute = ProfileR
, return NavHeader , navAccess' = NavAccessHandler $ is _Just <$> maybeAuthId
{ navHeaderRole = NavHeaderSecondary , navType = NavTypeLink { navModal = False }
, navIcon = IconMenuProfile , navQuick' = mempty
, navLink = NavLink , navForceActive = False
{ navLabel = MsgMenuProfile }
, navRoute = ProfileR ]
, navAccess' = NavAccessHandler $ is _Just <$> maybeAuthId }
, navType = NavTypeLink { navModal = False }
, navQuick' = mempty
, navForceActive = False
}
}
, do , do
mCurrentRoute <- getCurrentRoute mCurrentRoute <- getCurrentRoute

View File

@ -0,0 +1,31 @@
-- SPDX-FileCopyrightText: 2024 David Mosbach <david.mosbach@uniworx.de>
--
-- SPDX-License-Identifier: AGPL-3.0-or-later
module Handler.SingleSignOut
( getSOutR
, getSSOutR
) where
import Import
import Auth.OAuth2 (singleSignOut)
import qualified Network.Wai as W
getSOutR :: Handler Html
getSOutR = do
$logErrorS "\27[31mSOut\27[0m" "Redirect to LogoutR"
redirect $ AuthR LogoutR
getSSOutR :: Handler Html
getSSOutR = do
app <- getYesod
let redir = intercalate "/" . fst . renderRoute $ SOutR
root = case approot of
ApprootRequest f -> f app W.defaultRequest
_ -> error "approt implementation changed"
url = decodeUtf8 . urlEncode True . encodeUtf8 $ root <> "/" <> redir
AppSettings{..} <- getsYesod appSettings'
$logErrorS "\27[31mSSOut\27[0m" "Redirect to auth server"
if appSingleSignOn then singleSignOut (Just url) else redirect (AuthR LogoutR)

View File

@ -1,4 +1,4 @@
-- SPDX-FileCopyrightText: 2022-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, Gregor Kleen <gregor.kleen@ifi.lmu.de>, Sarah Vaupel <sarah.vaupel@ifi.lmu.de>, Steffen Jost <jost@tcs.ifi.lmu.de> -- SPDX-FileCopyrightText: 2022-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <jost@tcs.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
@ -452,7 +452,14 @@ data AppSettings = AppSettings
, appDatabaseConf :: PostgresConf , appDatabaseConf :: PostgresConf
-- ^ Configuration settings for accessing the database. -- ^ Configuration settings for accessing the database.
, appAutoDbMigrate :: Bool , appAutoDbMigrate :: Bool
<<<<<<< HEAD
, appUserAuthConf :: UserAuthConf -- TODO: add SSO option for user-auth config , appUserAuthConf :: UserAuthConf -- TODO: add SSO option for user-auth config
=======
, appSingleSignOn :: Bool
-- ^ Enable OIDC single sign-on
, appLdapConf :: Maybe (PointedList LdapConf)
-- ^ Configuration settings for CSV export/import to LMS (= Learn Management System)
>>>>>>> 139-single-sign-on-sso-routing-anpassen
, appLmsConf :: LmsConf , appLmsConf :: LmsConf
-- ^ Configuration settings for CSV export/import to LMS (= Learn Management System) -- TODO, TODISCUSS: reimplement as user-auth source? -- ^ Configuration settings for CSV export/import to LMS (= Learn Management System) -- TODO, TODISCUSS: reimplement as user-auth source?
, appAvsConf :: Maybe AvsConf , appAvsConf :: Maybe AvsConf
@ -624,6 +631,7 @@ instance FromJSON AppSettings where
appWebpackEntrypoints <- o .: "webpack-manifest" appWebpackEntrypoints <- o .: "webpack-manifest"
appDatabaseConf <- o .: "database" appDatabaseConf <- o .: "database"
appAutoDbMigrate <- o .: "auto-db-migrate" appAutoDbMigrate <- o .: "auto-db-migrate"
<<<<<<< HEAD
-- TODO: reintroduce non-emptyness check for ldap hosts -- TODO: reintroduce non-emptyness check for ldap hosts
-- let nonEmptyHost (UserDbLdap LdapConf{..}) = case ldapHost of -- let nonEmptyHost (UserDbLdap LdapConf{..}) = case ldapHost of
-- Ldap.Tls host _ -> not $ null host -- Ldap.Tls host _ -> not $ null host
@ -632,6 +640,13 @@ instance FromJSON AppSettings where
appUserAuthConf <- o .: "user-auth" appUserAuthConf <- o .: "user-auth"
-- P.fromList . mapMaybe (assertM nonEmptyHost) <$> o .:? "user-database" .!= [] -- P.fromList . mapMaybe (assertM nonEmptyHost) <$> o .:? "user-database" .!= []
appLdapPoolConf <- o .:? "ldap-pool" appLdapPoolConf <- o .:? "ldap-pool"
=======
appSingleSignOn <- o .: "single-sign-on"
let nonEmptyHost LdapConf{..} = case ldapHost of
Ldap.Tls host _ -> not $ null host
Ldap.Plain host -> not $ null host
appLdapConf <- P.fromList . mapMaybe (assertM nonEmptyHost) <$> o .:? "ldap" .!= []
>>>>>>> 139-single-sign-on-sso-routing-anpassen
appLmsConf <- o .: "lms-direct" appLmsConf <- o .: "lms-direct"
appAvsConf <- assertM (not . null . avsPass) <$> o .:? "avs" appAvsConf <- assertM (not . null . avsPass) <$> o .:? "avs"
appLprConf <- o .: "lpr" appLprConf <- o .: "lpr"

View File

@ -1,4 +1,4 @@
-- SPDX-FileCopyrightText: 2022-23 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Sarah Vaupel <vaupel.sarah@campus.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>,Wolfgang Witt <Wolfgang.Witt@campus.lmu.de>,Steffen Jost <s.jost@fraport.de> -- SPDX-FileCopyrightText: 2022-24 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Sarah Vaupel <vaupel.sarah@campus.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>,Wolfgang Witt <Wolfgang.Witt@campus.lmu.de>,Steffen Jost <s.jost@fraport.de>,David Mosbach <david.mosbach@uniworx.de>
-- --
-- SPDX-License-Identifier: AGPL-3.0-or-later -- SPDX-License-Identifier: AGPL-3.0-or-later
@ -81,6 +81,7 @@ data Icon
| IconNavContainerClose | IconPageActionChildrenClose | IconNavContainerClose | IconPageActionChildrenClose
| IconMenuNews | IconMenuNews
| IconMenuHelp | IconMenuHelp
| IconMenuAccount
| IconMenuProfile | IconMenuProfile
| IconMenuLogin | IconMenuLogout | IconMenuLogin | IconMenuLogout
| IconBreadcrumbsHome | IconBreadcrumbsHome
@ -173,6 +174,7 @@ iconText = \case
IconPageActionChildrenClose -> "chevron-up" IconPageActionChildrenClose -> "chevron-up"
IconMenuNews -> "megaphone" IconMenuNews -> "megaphone"
IconMenuHelp -> "question" IconMenuHelp -> "question"
IconMenuAccount -> "user"
IconMenuProfile -> "cogs" IconMenuProfile -> "cogs"
IconMenuLogin -> "sign-in-alt" IconMenuLogin -> "sign-in-alt"
IconMenuLogout -> "sign-out-alt" IconMenuLogout -> "sign-out-alt"

View File

@ -1,6 +1,6 @@
$newline never $newline never
$# SPDX-FileCopyrightText: 2022-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, Gregor Kleen <gregor.kleen@ifi.lmu.de>, David Mosbach <david.mosbach@uniworx.de> $# SPDX-FileCopyrightText: 2022-2024 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
@ -26,3 +26,8 @@ $forall AuthPlugin{apName, apLogin} <- plugins
<section> <section>
<h2>_{MsgDummyLoginTitle} <h2>_{MsgDummyLoginTitle}
^{apLogin toParent} ^{apLogin toParent}
$maybe port <- mPort
<section>
<h2>SSO Dev Test
<a href=http://localhost:#{port}/test-sso>Test login via single sign-on