chore(model)!: separate user authentication data from User table; add ExternalAuth and InternalAuth models

This commit is contained in:
Sarah Vaupel 2024-02-11 17:36:57 +01:00
parent 2e47df00b9
commit 54f2430b3e
2 changed files with 54 additions and 51 deletions

View File

@ -13,13 +13,11 @@
-- --
User json -- Each Uni2work user has a corresponding row in this table; created upon first login. User json -- Each Uni2work user has a corresponding row in this table; created upon first login.
ident UserIdent -- Case-insensitive user-identifier
surname UserSurname -- Display user names always through 'nameWidget displayName surname' surname UserSurname -- Display user names always through 'nameWidget displayName surname'
displayName UserDisplayName displayName UserDisplayName
displayEmail UserEmail displayEmail UserEmail
email UserEmail -- Case-insensitive eMail address, used for sending TODO: make this nullable email UserEmail -- Case-insensitive eMail address, used for sending TODO: make this nullable
ident UserIdent -- Case-insensitive user-identifier
authentication AuthenticationMode -- 'AuthLDAP' or ('AuthPWHash'+password-hash) -- TODO: redo (add InternalUser table for password hash)
lastAuthentication UTCTime Maybe -- last login date
created UTCTime default=now() created UTCTime default=now()
tokensIssuedAfter UTCTime Maybe -- do not accept bearer tokens issued before this time (accept all tokens if null) tokensIssuedAfter UTCTime Maybe -- do not accept bearer tokens issued before this time (accept all tokens if null)
matrikelnummer UserMatriculation Maybe -- usually a number; AVS Personalnummer; nicht Fraport Personalnummer! matrikelnummer UserMatriculation Maybe -- usually a number; AVS Personalnummer; nicht Fraport Personalnummer!
@ -49,18 +47,33 @@ User json -- Each Uni2work user has a corresponding row in this table; create
prefersPostal Bool default=false -- user prefers letters by post instead of email prefersPostal Bool default=false -- user prefers letters by post instead of email
examOfficeGetSynced Bool default=true -- whether synced status should be displayed for exam results by default examOfficeGetSynced Bool default=true -- whether synced status should be displayed for exam results by default
examOfficeGetLabels Bool default=true -- whether labels should be displayed for exam results by default examOfficeGetLabels Bool default=true -- whether labels should be displayed for exam results by default
UniqueAuthentication ident -- Column 'ident' can be used as a row-key in this table UniqueUser ident -- Column 'ident' can be used as a row-key in this table
UniqueEmail email -- Column 'email' can be used as a row-key in this table UniqueEmail email -- Column 'email' can be used as a row-key in this table
deriving Show Eq Ord Generic -- Haskell-specific settings for runtime-value representing a row in memory deriving Show Eq Ord Generic -- Haskell-specific settings for runtime-value representing a row in memory
-- User authentication data fetched from external sources -- | User authentication data, source-agnostic data
ExternalUser UserAuth
source Text -- External source ID ident UserIdent -- Human-readable text uniquely identifying a user
ident UserIdent -- External user ID lastLogin UTCTime -- When did the corresponding User last authenticate using this entry?
data Value "default='{}'::jsonb" -- Raw user data from external source Primary ident
lastSourceSync UTCTime -- When was the entry last synced with the external source? UniqueAuthentication ident
lastUserSync UTCTime Maybe -- When was the corresponding User entry last synced with this entry? TODO: maybe move to User instead deriving Show Eq Ord Generic
UniqueExternalUser source ident
-- | User authentication data fetched from external user sources
ExternalAuth
ident UserIdent
source AuthenticationSourceIdent -- Identifier of the external source in the config
data Value "default='{}'::jsonb" -- Raw user data from external source
lastSync UTCTime -- When was the corresponding User entry last synced with this external source?
UniqueExternalAuth ident source -- At most one entry of this user per source
deriving Show Eq Ord Generic
-- | FraDrive-specific user authentication data, internal logins have precedence over external authentication
InternalAuth
ident UserIdent
hash Text -- Hashed password
Primary ident
UniqueInternalAuth ident
deriving Show Eq Ord Generic deriving Show Eq Ord Generic
UserFunction -- Administratively assigned functions (lecturer, admin, evaluation, ...) UserFunction -- Administratively assigned functions (lecturer, admin, evaluation, ...)

View File

@ -15,61 +15,51 @@ module Model.Types.Auth
import ClassyPrelude.Yesod hiding (derivePersistFieldJSON, Proxy(..)) import ClassyPrelude.Yesod hiding (derivePersistFieldJSON, Proxy(..))
import Utils
import Data.Aeson
import Data.Aeson.TH
import Model.Types.TH.JSON import Model.Types.TH.JSON
import Data.Universe import Model.Types.TH.PathPiece
import Data.Universe.Instances.Reverse ()
import Data.Proxy import Utils
import Data.Data (Data) import Utils.Lens.TH
import Control.Lens import Control.Lens
import qualified Data.Set as Set import Data.Aeson
import Data.Aeson.TH
import qualified Data.Text as Text
import qualified Data.HashMap.Strict as HashMap
import qualified Data.Aeson.Types as Aeson import qualified Data.Aeson.Types as Aeson
import qualified Data.Binary as Binary
import Data.CaseInsensitive (CI) import Data.Binary (Binary)
import Data.Binary.Instances.UnorderedContainers ()
import qualified Data.CaseInsensitive as CI import qualified Data.CaseInsensitive as CI
import Data.CaseInsensitive (CI)
import Data.CaseInsensitive.Instances () import Data.CaseInsensitive.Instances ()
import Data.Data (Data)
import Data.Set.Instances () import qualified Data.HashMap.Strict as HashMap
import Data.NonNull.Instances () import Data.NonNull.Instances ()
import Data.Proxy
import qualified Data.Set as Set
import Data.Set.Instances ()
import qualified Data.Text as Text
import Data.Universe
import Data.Universe.Instances.Reverse ()
import Data.Universe.Instances.Reverse.MonoTraversable () import Data.Universe.Instances.Reverse.MonoTraversable ()
import Model.Types.TH.PathPiece
import Database.Persist.Sql import Database.Persist.Sql
import Servant.Docs (ToSample(..), samples) import Servant.Docs (ToSample(..), samples)
import Utils.Lens.TH
import Data.Binary (Binary)
import qualified Data.Binary as Binary
import Data.Binary.Instances.UnorderedContainers ()
data AuthenticationMode = AuthLDAP -- | Supported protocols for external user sources used for authentication queries
| AuthAzure data AuthenticationProtocol
| AuthPWHash { authPWHash :: Text } = AuthAzure -- ^ Azure ADv2 (OAuth2)
| AuthNoLogin | AuthLdap -- ^ LDAP
deriving (Eq, Ord, Read, Show, Generic) deriving (Eq, Ord, Enum, Bounded, Read, Show, Data, Generic)
deriving anyclass (Universe, Finite, Hashable, NFData)
instance Hashable AuthenticationMode nullaryPathPiece ''AuthenticationProtocol $ camelToPathPiece' 1
instance NFData AuthenticationMode pathPieceJSON ''AuthenticationProtocol
deriveJSON defaultOptions
{ constructorTagModifier = camelToPathPiece' 1
, fieldLabelModifier = camelToPathPiece' 1
, sumEncoding = UntaggedValue
} ''AuthenticationMode
derivePersistFieldJSON ''AuthenticationMode type AuthenticationSourceIdent = Text
data AuthTag -- sortiert nach gewünschter Reihenfolge auf /authpreds, d.h. Prädikate sind sortier nach Relevanz für Benutzer data AuthTag -- sortiert nach gewünschter Reihenfolge auf /authpreds, d.h. Prädikate sind sortier nach Relevanz für Benutzer
@ -106,8 +96,8 @@ data AuthTag -- sortiert nach gewünschter Reihenfolge auf /authpreds, d.h. Prä
| AuthRegisterGroup | AuthRegisterGroup
| AuthEmpty | AuthEmpty
| AuthSelf | AuthSelf
| AuthIsLDAP | AuthIsExternal -- TODO: maybe distinguish between AuthenticationProtocols
| AuthIsPWHash | AuthIsInternal
| AuthAuthentication | AuthAuthentication
| AuthNoEscalation | AuthNoEscalation
| AuthRead | AuthRead