saml2-web-sso-0.20: Library and example web app for the SAML Web-based SSO profile.
Safe HaskellNone
LanguageGHC2021

SAML2.WebSSO.SP

Synopsis

Documentation

type SP (m :: Type -> Type) = (HasConfig m, HasLogger m, HasCreateUUID m, HasNow m) Source #

Application logic of the service provider.

class HasLogger (m :: Type -> Type) where Source #

Minimal complete definition

Nothing

Methods

logger :: Level -> String -> m () Source #

default logger :: (HasConfig m, MonadIO m) => Level -> String -> m () Source #

Instances

Instances details
HasLogger SimpleSP Source # 
Instance details

Defined in SAML2.WebSSO.API.Example

Methods

logger :: Level -> String -> SimpleSP () Source #

HasLogger TestSP Source # 
Instance details

Defined in SAML2.WebSSO.Test.Util.TestSP

Methods

logger :: Level -> String -> TestSP () Source #

(Monad m, HasLogger m) => HasLogger (JudgeT m) Source # 
Instance details

Defined in SAML2.WebSSO.SP

Methods

logger :: Level -> String -> JudgeT m () Source #

class HasCreateUUID (m :: Type -> Type) where Source #

Minimal complete definition

Nothing

Methods

createUUID :: m UUID Source #

default createUUID :: MonadIO m => m UUID Source #

Instances

Instances details
HasCreateUUID SimpleSP Source # 
Instance details

Defined in SAML2.WebSSO.API.Example

HasCreateUUID TestSP Source # 
Instance details

Defined in SAML2.WebSSO.Test.Util.TestSP

(Monad m, HasCreateUUID m) => HasCreateUUID (JudgeT m) Source # 
Instance details

Defined in SAML2.WebSSO.SP

class HasNow (m :: Type -> Type) where Source #

Minimal complete definition

Nothing

Methods

getNow :: m Time Source #

default getNow :: MonadIO m => m Time Source #

Instances

Instances details
HasNow SimpleSP Source # 
Instance details

Defined in SAML2.WebSSO.API.Example

HasNow TestSP Source # 
Instance details

Defined in SAML2.WebSSO.Test.Util.TestSP

(Monad m, HasNow m) => HasNow (JudgeT m) Source # 
Instance details

Defined in SAML2.WebSSO.SP

Methods

getNow :: JudgeT m Time Source #

class (MonadError err m, Show (IdPConfigExtra m)) => SPStoreIdP err (m :: Type -> Type) where Source #

Associated Types

type IdPConfigExtra (m :: Type -> Type) Source #

type IdPConfigSPId (m :: Type -> Type) Source #

Instances

Instances details
SPStoreIdP SimpleError SimpleSP Source # 
Instance details

Defined in SAML2.WebSSO.API.Example

Associated Types

type IdPConfigExtra SimpleSP 
Instance details

Defined in SAML2.WebSSO.API.Example

type IdPConfigSPId SimpleSP 
Instance details

Defined in SAML2.WebSSO.API.Example

SPStoreIdP SimpleError TestSP Source # 
Instance details

Defined in SAML2.WebSSO.Test.Util.TestSP

Associated Types

type IdPConfigExtra TestSP 
Instance details

Defined in SAML2.WebSSO.Test.Util.TestSP

type IdPConfigSPId TestSP 
Instance details

Defined in SAML2.WebSSO.Test.Util.TestSP

class (SP m, SPStore m, SPStoreIdP err m, MonadError err m) => SPHandler err (m :: Type -> Type) where Source #

HTTP handling of the service provider.

Associated Types

type NTCTX (m :: Type -> Type) Source #

Methods

nt :: NTCTX m -> m x -> Handler x Source #

Instances

Instances details
SPHandler SimpleError SimpleSP Source #

If you read the Config initially in IO and then pass it into the monad via Reader, you safe disk load and redundant debug logs.

Instance details

Defined in SAML2.WebSSO.API.Example

Associated Types

type NTCTX SimpleSP 
Instance details

Defined in SAML2.WebSSO.API.Example

Methods

nt :: NTCTX SimpleSP -> SimpleSP x -> Handler x Source #

SPHandler SimpleError TestSP Source # 
Instance details

Defined in SAML2.WebSSO.Test.Util.TestSP

Associated Types

type NTCTX TestSP 
Instance details

Defined in SAML2.WebSSO.Test.Util.TestSP

Methods

nt :: NTCTX TestSP -> TestSP x -> Handler x Source #

storeAssertion :: (Monad m, SPStore m) => ID Assertion -> Time -> m Bool Source #

Store Assertions to prevent replay attack. Time argument is end of life (IDs may be garbage collected after that time). Iff assertion has already been stored and is still alive, return False.

loggerIO :: MonadIO m => Level -> Level -> String -> m () Source #

createID :: forall {k} m (a :: k). (Functor m, SP m) => m (ID a) Source #

(Microsoft Active Directory likes IDs to be of the form idhex digits: ID . cs . ("id" <>) . filter (/= -) . cs . UUID.toText $ createUUID. Hopefully the more common form produced by this function is also ok.)

createAuthnRequest :: (Monad m, SP m, SPStore m) => NominalDiffTime -> Issuer -> Issuer -> m AuthnRequest Source #

Generate an AuthnRequest value for the initiate-login response. The NameIdPolicy is NameIDFUnspecified.

We do not use XML encryption (which appears to have been dead for years). We do not sign AuthnRequest values. Both encryption and signatures are optional, and rarely found in the wild. Security: (1) the AuthnReq ID is stored, and Assertions need to refer to a valid one in order to get a positive AccessVerdict by judge. AuthnResponses answering requests not originating from us will be ignored. (2) the request Issuer is there to help the IdP construct an AuthnResponse that we will accept. If it is changed by an attacker (either in the browser or on the wire, which is weakly procted by TLS) and passed on to the legitimate IdP, there will either be no access-granting AuthnResponse, or it will contain the wrong audience and be rejected by us. (3) The nameID policy is expected to be configured on the IdP side to not support any set of name spaces that overlap (e.g. because user A has an email that is the account name of user B).

tolerance :: NominalDiffTime Source #

The clock drift between IdP and us that we allow for.

FUTUREWORK: make this configurable

earlier :: Time -> Time -> Bool Source #

First arg must be comfortably earlier than the latter: if tolerance is one minute, then ("4:32:14" earlier "4:31:15") == True.

If this returns False you should be *more* inclined to throw an error, so a good calling pattern is unless (a earlier b) throwSomething, which is very different from when (b earlier a) throwSomething.

noLater :: Time -> Time -> Bool Source #

Even though this makes little sense, the standard requires to distinguish between "less" and "less or equal". For all practical purposes, you may consider noLater == earlier.

getSsoURINoMultiIngress :: (HasCallStack, Functor m, HasConfig m, IsElem endpoint api, HasLink endpoint, ToHttpApiData (MkLink endpoint Link)) => Proxy api -> Proxy endpoint -> m URI Source #

This function exists to deal with legacy test cases.

getMultiIngressDomainConfigNoMultiIngress :: (HasConfig m, Functor m) => m MultiIngressDomainConfig Source #

DANGER: This function is not valid for all spar configurations! It spuriously fails for multi-ingress configs!

newtype JudgeT (m :: Type -> Type) a Source #

This monad collects errors in a writer, so that the reasons for access denial are as helpful as possible. It is a little like an exception monad, except you can throw several exceptions without interrupting the flow, and will get a list of them at the end.

NOTE: -XGeneralizedNewtypeDeriving does not help with the boilerplate instances below, since this is a transformer stack and not a concrete Monad.

Instances

Instances details
(Monad m, SPStoreAssertion i m) => SPStoreAssertion (i :: k) (JudgeT m) Source # 
Instance details

Defined in SAML2.WebSSO.SP

(Monad m, SPStoreRequest i m) => SPStoreRequest (i :: k) (JudgeT m) Source # 
Instance details

Defined in SAML2.WebSSO.SP

(Functor m, Applicative m, Monad m) => Applicative (JudgeT m) Source # 
Instance details

Defined in SAML2.WebSSO.SP

Methods

pure :: a -> JudgeT m a #

(<*>) :: JudgeT m (a -> b) -> JudgeT m a -> JudgeT m b #

liftA2 :: (a -> b -> c) -> JudgeT m a -> JudgeT m b -> JudgeT m c #

(*>) :: JudgeT m a -> JudgeT m b -> JudgeT m b #

(<*) :: JudgeT m a -> JudgeT m b -> JudgeT m a #

(Functor m, Applicative m, Monad m) => Functor (JudgeT m) Source # 
Instance details

Defined in SAML2.WebSSO.SP

Methods

fmap :: (a -> b) -> JudgeT m a -> JudgeT m b #

(<$) :: a -> JudgeT m b -> JudgeT m a #

(Functor m, Applicative m, Monad m) => Monad (JudgeT m) Source # 
Instance details

Defined in SAML2.WebSSO.SP

Methods

(>>=) :: JudgeT m a -> (a -> JudgeT m b) -> JudgeT m b #

(>>) :: JudgeT m a -> JudgeT m b -> JudgeT m b #

return :: a -> JudgeT m a #

(Monad m, HasConfig m) => HasConfig (JudgeT m) Source # 
Instance details

Defined in SAML2.WebSSO.SP

(Monad m, HasCreateUUID m) => HasCreateUUID (JudgeT m) Source # 
Instance details

Defined in SAML2.WebSSO.SP

(Monad m, HasLogger m) => HasLogger (JudgeT m) Source # 
Instance details

Defined in SAML2.WebSSO.SP

Methods

logger :: Level -> String -> JudgeT m () Source #

(Monad m, HasNow m) => HasNow (JudgeT m) Source # 
Instance details

Defined in SAML2.WebSSO.SP

Methods

getNow :: JudgeT m Time Source #

(Functor m, Applicative m, Monad m) => MonadJudge (JudgeT m) Source # 
Instance details

Defined in SAML2.WebSSO.SP

data JudgeCtx Source #

Note on security: we assume that the SP has only one audience, which is defined here. If you have different sub-services running on your SP, associate a dedicated IdP with each sub-service. (To be more specific, construct AuthnReqs to different IdPs for each sub-service.) Secure association with the service can then be guaranteed via the Issuer in the signed Assertion.

Instances

Instances details
Show JudgeCtx Source # 
Instance details

Defined in SAML2.WebSSO.SP

Eq JudgeCtx Source # 
Instance details

Defined in SAML2.WebSSO.SP

class (Functor m, Applicative m, Monad m) => MonadJudge (m :: Type -> Type) where Source #

Instances

Instances details
(Functor m, Applicative m, Monad m) => MonadJudge (JudgeT m) Source # 
Instance details

Defined in SAML2.WebSSO.SP

judge :: (Monad m, SP m, SPStore m) => NonEmpty Assertion -> UnvalidatedSAMLStatus -> JudgeCtx -> m AccessVerdict Source #

3/4.1.4.2
, [3/4.1.4.3]; specific to active-directory: https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-single-sign-on-protocol-reference

judge does not consider the following parts of the AuthnResponse. * subjectID * scdAddress ("If any bearer SubjectConfirmationData includes an Address attribute, the service provider MAY check the user agent's client address against it." [3/4.1.4.3]) * astSessionIndex * astSubjectLocality ("This element is entirely advisory, since both of these fields are quite easily “spoofed,” but may be useful information in some applications." [1/2.7.2.1])

judge does *not* check any of the *unsigned* parts of the AuthnResponse body because... those are not signed! the standard doesn't seem to worry about that, but wire does. this affects rspStatus, inRespTo, rspIssueInstant, rspDestination. those are inferred from the *signed* information available.

checkInResponseTo :: (SPStore m, MonadJudge m) => String -> Issuer -> ID AuthnRequest -> m () Source #

If this fails, we could continue (deny), but we stop processing (giveup) to make DOS attacks harder.

checkIsInPast :: (SP m, MonadJudge m) => (Time -> Time -> DeniedReason) -> Time -> m () Source #

checkDestination :: (HasConfig m, MonadJudge m) => (String -> String -> DeniedReason) -> URI -> m () Source #

Check that the response is intended for us (based on config's finalize-login uri stored in JudgeCtx).

checkSubjectConfirmations :: (SP m, SPStore m, MonadJudge m) => Assertion -> m () Source #

Check all SubjectConfirmations and Subjects in all Assertion. Deny if not at least one confirmation has method "bearer".

data HasBearerConfirmation Source #

Instances

Instances details
Bounded HasBearerConfirmation Source # 
Instance details

Defined in SAML2.WebSSO.SP

Enum HasBearerConfirmation Source # 
Instance details

Defined in SAML2.WebSSO.SP

Eq HasBearerConfirmation Source # 
Instance details

Defined in SAML2.WebSSO.SP

Ord HasBearerConfirmation Source # 
Instance details

Defined in SAML2.WebSSO.SP

checkSubjectConfirmation :: (SPStore m, MonadJudge m) => Assertion -> SubjectConfirmation -> m HasBearerConfirmation Source #

Locally check one SubjectConfirmation and deny if there is a problem. If this is a confirmation of method "bearer", return HasBearerConfirmation.