Merge branch '139-single-sign-on-sso-routing-anpassen' into 'oauth2'

Resolve "Single sign-on (SSO): Routing anpassen"

See merge request fradrive/fradrive!28
This commit is contained in:
Sarah Vaupel 2024-03-11 14:49:41 +00:00
commit 608bea5199
15 changed files with 158 additions and 57 deletions

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 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 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
@ -131,6 +131,8 @@ database:
auto-db-migrate: '_env:AUTO_DB_MIGRATE:true' auto-db-migrate: '_env:AUTO_DB_MIGRATE:true'
single-sign-on: "_env:OIDC_SSO:true"
ldap: ldap:
- host: "_env:LDAPHOST:" - host: "_env:LDAPHOST:"
tls: "_env:LDAPTLS:" tls: "_env:LDAPTLS:"

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 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 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>,David Mosbach <david.mosbach@uniworx.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 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 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>,David Mosbach <david.mosbach@uniworx.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 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 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>,David Mosbach <david.mosbach@uniworx.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 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 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>,David Mosbach <david.mosbach@uniworx.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-2023 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

@ -164,6 +164,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

@ -8,15 +8,16 @@ module Auth.OAuth2
( AzureUserException(..) ( AzureUserException(..)
, azurePluginName , azurePluginName
, oauth2MockServer , oauth2MockServer
, mockPluginName , mockPluginName
, queryOAuth2User , queryOAuth2User
, UserDataException , UserDataException
, singleSignOut
) where ) where
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)
@ -36,9 +37,9 @@ instance Exception AzureUserException
azurePluginName :: Text azurePluginName :: Text
azurePluginName = "azureadv2" azurePluginName = "azureadv2"
---------------------------------------- -----------------------------------------------
---- OAuth2 development auth plugin ---- ---- OAuth2 + OIDC development auth plugin ----
---------------------------------------- -----------------------------------------------
mockPluginName :: Text mockPluginName :: Text
mockPluginName = "dev-oauth2-mock" mockPluginName = "dev-oauth2-mock"
@ -53,7 +54,11 @@ oauth2MockServer 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
} }
@ -94,7 +99,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
@ -130,8 +136,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), ("scope", "openid profile offline_access")] -- TODO read from config
else return $ ("scope", "ID Profile") : body else return $ ("scope", "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
@ -142,3 +148,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
@ -23,6 +26,7 @@ import Utils.Form
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
@ -42,6 +46,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
@ -128,14 +134,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")
@ -159,6 +174,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
@ -541,42 +543,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 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 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
@ -96,6 +96,8 @@ data AppSettings = AppSettings
, appDatabaseConf :: PostgresConf , appDatabaseConf :: PostgresConf
-- ^ Configuration settings for accessing the database. -- ^ Configuration settings for accessing the database.
, appAutoDbMigrate :: Bool , appAutoDbMigrate :: Bool
, appSingleSignOn :: Bool
-- ^ Enable OIDC single sign-on
, appLdapConf :: Maybe (PointedList LdapConf) , appLdapConf :: Maybe (PointedList LdapConf)
-- ^ Configuration settings for CSV export/import to LMS (= Learn Management System) -- ^ Configuration settings for CSV export/import to LMS (= Learn Management System)
, appLmsConf :: LmsConf , appLmsConf :: LmsConf
@ -627,6 +629,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"
appSingleSignOn <- o .: "single-sign-on"
let nonEmptyHost LdapConf{..} = case ldapHost of let nonEmptyHost LdapConf{..} = case ldapHost of
Ldap.Tls host _ -> not $ null host Ldap.Tls host _ -> not $ null host
Ldap.Plain host -> not $ null host Ldap.Plain host -> not $ null host

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 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