chore(avs): expand avs interface
This commit is contained in:
parent
2a6fee30ea
commit
6f7282b512
8
routes
8
routes
@ -55,10 +55,10 @@
|
|||||||
/users/#CryptoUUIDUser AdminUserR GET POST
|
/users/#CryptoUUIDUser AdminUserR GET POST
|
||||||
/users/#CryptoUUIDUser/delete AdminUserDeleteR POST
|
/users/#CryptoUUIDUser/delete AdminUserDeleteR POST
|
||||||
/users/#CryptoUUIDUser/hijack AdminHijackUserR POST !adminANDno-escalation
|
/users/#CryptoUUIDUser/hijack AdminHijackUserR POST !adminANDno-escalation
|
||||||
/users/#CryptoUUIDUser/notifications UserNotificationR GET POST !self
|
/users/#CryptoUUIDUser/notifications UserNotificationR GET POST !self
|
||||||
/users/#CryptoUUIDUser/password UserPasswordR GET POST !selfANDis-pw-hash
|
/users/#CryptoUUIDUser/password UserPasswordR GET POST !selfANDis-pw-hash
|
||||||
!/users/functionary-invite/new AdminNewFunctionaryInviteR GET POST
|
!/users/functionary-invite/new AdminNewFunctionaryInviteR GET POST
|
||||||
!/users/functionary-invite AdminFunctionaryInviteR GET POST
|
!/users/functionary-invite AdminFunctionaryInviteR GET POST
|
||||||
!/users/add AdminUserAddR GET POST
|
!/users/add AdminUserAddR GET POST
|
||||||
/admin AdminR GET
|
/admin AdminR GET
|
||||||
/admin/test AdminTestR GET POST
|
/admin/test AdminTestR GET POST
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
module Handler.Users.Add
|
module Handler.Users.Add
|
||||||
( getAdminUserAddR, postAdminUserAddR
|
( getAdminUserAddR, postAdminUserAddR
|
||||||
-- , AdminUserForm(..), adminUserForm -- no longer needed elsewhere
|
, AdminUserForm(..), adminUserForm -- no longer needed elsewhere
|
||||||
-- , AuthenticationKind(..), classifyAuth, mkAuthMode -- no longer needed elsewhere
|
-- , AuthenticationKind(..), classifyAuth, mkAuthMode -- no longer needed elsewhere
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,7 @@ module Handler.Utils.Avs
|
|||||||
, setLicence, setLicenceAvs, setLicencesAvs
|
, setLicence, setLicenceAvs, setLicencesAvs
|
||||||
, checkLicences
|
, checkLicences
|
||||||
, lookupAvsUser, lookupAvsUsers
|
, lookupAvsUser, lookupAvsUsers
|
||||||
|
, upsertAvsUser, upsertAvsUserByCard
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Import
|
import Import
|
||||||
@ -22,6 +23,7 @@ import qualified Data.Set as Set
|
|||||||
import qualified Data.Map as Map
|
import qualified Data.Map as Map
|
||||||
-- import qualified Data.Text as Text
|
-- import qualified Data.Text as Text
|
||||||
|
|
||||||
|
import Handler.Users.Add
|
||||||
|
|
||||||
|
|
||||||
--------------------
|
--------------------
|
||||||
@ -29,8 +31,11 @@ import qualified Data.Map as Map
|
|||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
data AvsException
|
data AvsException
|
||||||
= AvsInterfaceUnavailable
|
= AvsInterfaceUnavailable -- Interface to AVS was not configured at startup or does not respond
|
||||||
| AvsUserUnknown UserId
|
| AvsUserUnassociated UserId -- Manipulating AVS Data for a user that is not linked to AVS yet
|
||||||
|
| AvsUserUnknownByAvs AvsPersonId -- AvsPersionId not (or no longer) found in AVS DB
|
||||||
|
| AvsPersonSearchEmpty -- AvsPersonSearch returned empty result
|
||||||
|
| AvsPersonSearchAmbiguous -- AvsPersonSearch returned more than one result
|
||||||
deriving (Show, Generic, Typeable)
|
deriving (Show, Generic, Typeable)
|
||||||
instance Exception AvsException
|
instance Exception AvsException
|
||||||
|
|
||||||
@ -56,7 +61,7 @@ instance Exception AvsException
|
|||||||
getLicence :: ( MonadThrow m, MonadHandler m, HandlerSite m ~ UniWorX, WithRunDB SqlReadBackend (HandlerFor UniWorX) m ) => UserId -> m (Maybe AvsLicence)
|
getLicence :: ( MonadThrow m, MonadHandler m, HandlerSite m ~ UniWorX, WithRunDB SqlReadBackend (HandlerFor UniWorX) m ) => UserId -> m (Maybe AvsLicence)
|
||||||
getLicence uid = do
|
getLicence uid = do
|
||||||
AvsQuery{..} <- maybeThrowM AvsInterfaceUnavailable $ getsYesod $ view _appAvsQuery
|
AvsQuery{..} <- maybeThrowM AvsInterfaceUnavailable $ getsYesod $ view _appAvsQuery
|
||||||
Entity _ UserAvs{..} <- maybeThrowM (AvsUserUnknown uid) $ useRunDB $ getBy $ UniqueUserAvsUser uid
|
Entity _ UserAvs{..} <- maybeThrowM (AvsUserUnassociated uid) $ useRunDB $ getBy $ UniqueUserAvsUser uid
|
||||||
AvsResponseGetLicences licences <- throwLeftM $ avsQueryGetLicences $ AvsQueryGetLicences $ Set.singleton $ AvsObjPersonId userAvsPersonId
|
AvsResponseGetLicences licences <- throwLeftM $ avsQueryGetLicences $ AvsQueryGetLicences $ Set.singleton $ AvsObjPersonId userAvsPersonId
|
||||||
let ulicence = Set.lookupMax $ Set.filter ((userAvsPersonId ==) . avsLicencePersonID) licences
|
let ulicence = Set.lookupMax $ Set.filter ((userAvsPersonId ==) . avsLicencePersonID) licences
|
||||||
return (avsLicenceRampLicence <$> ulicence)
|
return (avsLicenceRampLicence <$> ulicence)
|
||||||
@ -64,15 +69,14 @@ getLicence uid = do
|
|||||||
getLicenceDB :: UserId -> DB (Maybe AvsLicence)
|
getLicenceDB :: UserId -> DB (Maybe AvsLicence)
|
||||||
getLicenceDB uid = do
|
getLicenceDB uid = do
|
||||||
AvsQuery{..} <- maybeThrowM AvsInterfaceUnavailable $ liftHandler $ getsYesod $ view _appAvsQuery
|
AvsQuery{..} <- maybeThrowM AvsInterfaceUnavailable $ liftHandler $ getsYesod $ view _appAvsQuery
|
||||||
Entity _ UserAvs{..} <- maybeThrowM (AvsUserUnknown uid) $ getBy $ UniqueUserAvsUser uid
|
Entity _ UserAvs{..} <- maybeThrowM (AvsUserUnassociated uid) $ getBy $ UniqueUserAvsUser uid
|
||||||
AvsResponseGetLicences licences <- throwLeftM $ avsQueryGetLicences $ AvsQueryGetLicences $ Set.singleton $ AvsObjPersonId userAvsPersonId
|
AvsResponseGetLicences licences <- throwLeftM $ avsQueryGetLicences $ AvsQueryGetLicences $ Set.singleton $ AvsObjPersonId userAvsPersonId
|
||||||
let ulicence = Set.lookupMax $ Set.filter ((userAvsPersonId ==) . avsLicencePersonID) licences
|
let ulicence = Set.lookupMax $ Set.filter ((userAvsPersonId ==) . avsLicencePersonID) licences
|
||||||
return (avsLicenceRampLicence <$> ulicence)
|
return (avsLicenceRampLicence <$> ulicence)
|
||||||
|
|
||||||
|
|
||||||
setLicence :: UserId -> AvsLicence -> DB ()
|
setLicence :: UserId -> AvsLicence -> DB ()
|
||||||
setLicence uid lic = do
|
setLicence uid lic = do
|
||||||
Entity _ UserAvs{..} <- maybeThrowM (AvsUserUnknown uid) $ getBy $ UniqueUserAvsUser uid
|
Entity _ UserAvs{..} <- maybeThrowM (AvsUserUnassociated uid) $ getBy $ UniqueUserAvsUser uid
|
||||||
setLicenceAvs userAvsPersonId lic
|
setLicenceAvs userAvsPersonId lic
|
||||||
|
|
||||||
setLicenceAvs :: AvsPersonId -> AvsLicence -> DB ()
|
setLicenceAvs :: AvsPersonId -> AvsLicence -> DB ()
|
||||||
@ -111,19 +115,63 @@ or
|
|||||||
|
|
||||||
-}
|
-}
|
||||||
|
|
||||||
{-
|
-- | Retrieve and _always_ update user by AvsPersonId. Non-existing users are created.
|
||||||
upsertAvsUser :: ( MonadThrow m, MonadHandler m, HandlerSite m ~ UniWorX ) =>
|
-- Throws errors if the avsInterface in unavailable or the user is non-unique within external AVS DB (should never happen).
|
||||||
AvsPersonId -> m ()
|
upsertAvsUser :: AvsPersonId -> Handler (Maybe UserId)
|
||||||
upsertAvsUser api = do
|
upsertAvsUser api = do
|
||||||
mbuid <- getBy $ UniqueUserAvsId api
|
mbuid <- runDB $ getBy $ UniqueUserAvsId api
|
||||||
mbapd <- lookupAvsUser api
|
mbapd <- lookupAvsUser api
|
||||||
case (mbuid, mbapd) of
|
case (mbuid, mbapd) of
|
||||||
( _ , Nothing) -> error "TODO" -- CONTINUE HERE -- this should no happen
|
( _ , Nothing) -> throwM $ AvsUserUnknownByAvs api -- this should never happen
|
||||||
(Nothing, Just apd) -> do -- unknown user
|
(Nothing, Just AvsDataPerson{..}) -> do -- unknown user, must be created
|
||||||
|
-- if | Just ipn <- avsPersonInternalPersonalNo -> TODO?
|
||||||
|
let _newUsr = AdminUserForm
|
||||||
|
{ aufTitle = Nothing
|
||||||
|
, aufFirstName = avsPersonFirstName
|
||||||
|
, aufSurname = avsPersonLastName
|
||||||
|
, aufDisplayName = avsPersonFirstName <> " " <> avsPersonLastName
|
||||||
|
, aufDisplayEmail = "" -- Email is unknown in this version of the avs query, to be updated later (FUTURE TODO)
|
||||||
|
, aufMatriculation = Nothing
|
||||||
|
, aufSex = Nothing
|
||||||
|
, aufMobile = Nothing
|
||||||
|
, aufTelephone = Nothing
|
||||||
|
, aufFPersonalNumber = avsPersonInternalPersonalNo
|
||||||
|
, aufFDepartment = Nothing
|
||||||
|
, aufPostAddress = error "TODO" -- CONTINUE HERE
|
||||||
|
, aufPrefersPostal = error "TODO" -- CONTINUE HERE
|
||||||
|
, aufPinPassword = error "TODO" -- CONTINUE HERE
|
||||||
|
, aufEmail = ""
|
||||||
|
, aufIdent = error "TODO" -- CONTINUE HERE
|
||||||
|
, aufAuth = error "TODO" -- CONTINUE HERE AuthKindNoLogin or AuthKindLDAP if ldap search worked
|
||||||
|
}
|
||||||
|
-- _newAvs = UserAvs avsPersonPersonID uid
|
||||||
|
-- _newAvsCards = UserAvsCard
|
||||||
error "TODO" -- CONTINUE HERE
|
error "TODO" -- CONTINUE HERE
|
||||||
(Just uid, Just apd) -> do -- known user
|
(Just _uid, Just _apd) -> do -- known user
|
||||||
error "TODO" -- CONTINUE HERE
|
error "TODO" -- CONTINUE HERE
|
||||||
-}
|
|
||||||
|
|
||||||
|
-- | Given CardNo or internal Number, retrieve UserId. Create non-existing users, if possible.
|
||||||
|
-- Throws errors if the avsInterface in unavailable or the user is non-unique within external AVS DB.
|
||||||
|
--upsertAvsUserByCard :: ( MonadThrow m, MonadHandler m, HandlerSite m ~ UniWorX ) =>
|
||||||
|
-- upsertAvsUserByCard :: ( MonadThrow m, MonadHandler m, HandlerSite m ~ UniWorX, WithRunDB SqlReadBackend (HandlerFor UniWorX) m )
|
||||||
|
upsertAvsUserByCard ::
|
||||||
|
Either (AvsCardNo,AvsVersionNo) AvsInternalPersonalNo -> Handler (Maybe UserId) -- Idee: Eingabe ohne Punkt is AvsInternalPersonalNo mit Punkt is Ausweisnummer?!
|
||||||
|
upsertAvsUserByCard persNo = do
|
||||||
|
let qry = case persNo of
|
||||||
|
Left (acn,avn) -> def{ avsPersonQueryCardNo = Just acn, avsPersonQueryVersionNo = Just avn }
|
||||||
|
Right fpn -> def{ avsPersonQueryInternalPersonalNo = Just fpn }
|
||||||
|
AvsQuery{..} <- maybeThrowM AvsInterfaceUnavailable $ getsYesod $ view _appAvsQuery
|
||||||
|
AvsResponsePerson adps <- throwLeftM $ avsQueryPerson qry
|
||||||
|
case Set.elems adps of
|
||||||
|
[] -> throwM AvsPersonSearchEmpty
|
||||||
|
(_:_:_) -> throwM AvsPersonSearchAmbiguous
|
||||||
|
[AvsDataPerson{avsPersonPersonID=appi}] -> do
|
||||||
|
mbuid <- runDB $ getBy $ UniqueUserAvsId appi
|
||||||
|
case mbuid of
|
||||||
|
(Just (Entity _ UserAvs{userAvsUser=uau})) -> return $ Just uau
|
||||||
|
Nothing -> upsertAvsUser appi
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
lookupAvsUser :: ( MonadThrow m, MonadHandler m, HandlerSite m ~ UniWorX ) =>
|
lookupAvsUser :: ( MonadThrow m, MonadHandler m, HandlerSite m ~ UniWorX ) =>
|
||||||
@ -136,6 +184,7 @@ lookupAvsUser api = Map.lookup api <$> lookupAvsUsers (Set.singleton api)
|
|||||||
-- - avsQueryStatus only provides limited information
|
-- - avsQueryStatus only provides limited information
|
||||||
-- avsQuery is used to obtain all card numbers, which are then queried separately an merged
|
-- avsQuery is used to obtain all card numbers, which are then queried separately an merged
|
||||||
-- May throw Servant.ClientError or AvsExceptions
|
-- May throw Servant.ClientError or AvsExceptions
|
||||||
|
-- Does not write to our own DB!
|
||||||
lookupAvsUsers :: ( MonadThrow m, MonadHandler m, HandlerSite m ~ UniWorX ) =>
|
lookupAvsUsers :: ( MonadThrow m, MonadHandler m, HandlerSite m ~ UniWorX ) =>
|
||||||
Set AvsPersonId -> m (Map AvsPersonId AvsDataPerson)
|
Set AvsPersonId -> m (Map AvsPersonId AvsDataPerson)
|
||||||
lookupAvsUsers apis = do
|
lookupAvsUsers apis = do
|
||||||
@ -143,7 +192,7 @@ lookupAvsUsers apis = do
|
|||||||
AvsResponseStatus statuses <- throwLeftM . avsQueryStatus $ AvsQueryStatus apis
|
AvsResponseStatus statuses <- throwLeftM . avsQueryStatus $ AvsQueryStatus apis
|
||||||
let forFoldlM = $(permuteFun [3,2,1]) foldlM
|
let forFoldlM = $(permuteFun [3,2,1]) foldlM
|
||||||
forFoldlM statuses mempty $ \acc1 AvsStatusPerson{avsStatusPersonCardStatus=cards} ->
|
forFoldlM statuses mempty $ \acc1 AvsStatusPerson{avsStatusPersonCardStatus=cards} ->
|
||||||
forFoldlM cards acc1 $ \acc2 AvsDataPersonCard{avsDataCardNo} -> do
|
forFoldlM cards acc1 $ \acc2 AvsDataPersonCard{avsDataCardNo, avsDataVersionNo} -> do
|
||||||
AvsResponsePerson adps <- throwLeftM . avsQueryPerson $ def{avsPersonQueryCardNo = Just avsDataCardNo}
|
AvsResponsePerson adps <- throwLeftM . avsQueryPerson $ def{avsPersonQueryCardNo = Just avsDataCardNo, avsPersonQueryVersionNo = Just avsDataVersionNo}
|
||||||
return $ mergeByPersonId adps acc2
|
return $ mergeByPersonId adps acc2
|
||||||
|
|
||||||
|
|||||||
@ -80,13 +80,19 @@ instance FromJSON SloppyBool where
|
|||||||
-- AVS Datatypes --
|
-- AVS Datatypes --
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|
type AvsInternalPersonalNo = Text -- type synonym for claritty/documentation within types
|
||||||
|
|
||||||
|
-- CompleteCardNo = xxxxxxxx.y
|
||||||
|
-- where x is an 8 digit AvsCardNo prefixed by zeros
|
||||||
|
-- and y is the 1 digit AvsVersionNo
|
||||||
|
type AvsVersionNo = Text -- always 1 digit
|
||||||
newtype AvsCardNo = AvsCardNo { avsCardNo :: Text } -- always 8 digits
|
newtype AvsCardNo = AvsCardNo { avsCardNo :: Text } -- always 8 digits
|
||||||
deriving (Eq, Ord, Show, Generic, Typeable)
|
deriving (Eq, Ord, Show, Generic, Typeable)
|
||||||
deriving newtype (NFData, PathPiece, PersistField, PersistFieldSql, Csv.ToField, Csv.FromField)
|
deriving newtype (NFData, PathPiece, PersistField, PersistFieldSql, Csv.ToField, Csv.FromField)
|
||||||
instance E.SqlString AvsCardNo
|
instance E.SqlString AvsCardNo
|
||||||
-- AvsCardNo is an untagged Text with respect to FromJSON/ToJSON, as needed by AVS API
|
-- AvsCardNo is an untagged Text with respect to FromJSON/ToJSON, as needed by AVS API
|
||||||
instance FromJSON AvsCardNo where
|
instance FromJSON AvsCardNo where
|
||||||
parseJSON x = AvsCardNo <$> parseJSON x
|
parseJSON x = AvsCardNo . Text.justifyRight 8 '0' <$> parseJSON x
|
||||||
instance ToJSON AvsCardNo where
|
instance ToJSON AvsCardNo where
|
||||||
toJSON (AvsCardNo cno) = toJSON cno
|
toJSON (AvsCardNo cno) = toJSON cno
|
||||||
|
|
||||||
@ -163,17 +169,17 @@ instance FromJSON AvsDataCardColor where
|
|||||||
|
|
||||||
|
|
||||||
data AvsDataPersonCard = AvsDataPersonCard
|
data AvsDataPersonCard = AvsDataPersonCard
|
||||||
{ avsDataValid :: Bool -- card currently valid? Note that AVS encodes booleans as JSON String "true" and "false" and not as JSON booleans
|
{ avsDataValid :: Bool -- card currently valid? Note that AVS encodes booleans as JSON String "true" and "false" and not as JSON booleans
|
||||||
, avsDataValidTo :: Maybe Day -- Nothing if returned with AvsResponseStatus
|
, avsDataValidTo :: Maybe Day -- Nothing if returned with AvsResponseStatus
|
||||||
, avsDataIssueDate :: Maybe Day -- Nothing if returned with AvsResponseStatus
|
, avsDataIssueDate :: Maybe Day -- Nothing if returned with AvsResponseStatus
|
||||||
, avsDataCardColor :: AvsDataCardColor
|
, avsDataCardColor :: AvsDataCardColor
|
||||||
, avsDataCardAreas :: Set Char -- logically a set of upper-case letters
|
, avsDataCardAreas :: Set Char -- logically a set of upper-case letters
|
||||||
, avsDataStreet :: Maybe Text -- Nothing if returned with AvsResponseStatus
|
, avsDataStreet :: Maybe Text -- Nothing if returned with AvsResponseStatus
|
||||||
, avsDataPostalCode:: Maybe Text -- Nothing if returned with AvsResponseStatus
|
, avsDataPostalCode:: Maybe Text -- Nothing if returned with AvsResponseStatus
|
||||||
, avsDataCity :: Maybe Text -- Nothing if returned with AvsResponseStatus
|
, avsDataCity :: Maybe Text -- Nothing if returned with AvsResponseStatus
|
||||||
, avsDataFirm :: Maybe Text -- Nothing if returned with AvsResponseStatus
|
, avsDataFirm :: Maybe Text -- Nothing if returned with AvsResponseStatus
|
||||||
, avsDataCardNo :: AvsCardNo -- always 8 digits
|
, avsDataCardNo :: AvsCardNo -- always 8 digits number, prefixed with 0
|
||||||
, avsDataVersionNo :: Text
|
, avsDataVersionNo :: AvsVersionNo -- always 1 digit number
|
||||||
}
|
}
|
||||||
deriving (Eq, Ord, Show, Generic, Typeable)
|
deriving (Eq, Ord, Show, Generic, Typeable)
|
||||||
deriving anyclass (NFData)
|
deriving anyclass (NFData)
|
||||||
@ -257,9 +263,9 @@ deriveJSON defaultOptions
|
|||||||
data AvsDataPerson = AvsDataPerson
|
data AvsDataPerson = AvsDataPerson
|
||||||
{ avsPersonFirstName :: Text
|
{ avsPersonFirstName :: Text
|
||||||
, avsPersonLastName :: Text
|
, avsPersonLastName :: Text
|
||||||
, avsPersonInternalPersonalNo :: Maybe Text -- Fraport Personalnummer
|
, avsPersonInternalPersonalNo :: Maybe AvsInternalPersonalNo -- Fraport Personalnummer
|
||||||
, avsPersonPersonNo :: Int -- AVS Personennummer, Bedeutung ist unklar
|
, avsPersonPersonNo :: Int -- AVS Personennummer, Bedeutung ist unklar
|
||||||
, avsPersonPersonID :: AvsPersonId -- Eindeutige PersonenID, wichtig für die Schnittstelle!
|
, avsPersonPersonID :: AvsPersonId -- Eindeutige PersonenID, wichtig für die Schnittstelle!
|
||||||
, avsPersonPersonCards :: Set AvsDataPersonCard
|
, avsPersonPersonCards :: Set AvsDataPersonCard
|
||||||
}
|
}
|
||||||
deriving (Eq, Ord, Show, Generic, Typeable)
|
deriving (Eq, Ord, Show, Generic, Typeable)
|
||||||
@ -373,10 +379,10 @@ deriveJSON defaultOptions
|
|||||||
-------------
|
-------------
|
||||||
data AvsQueryPerson = AvsQueryPerson
|
data AvsQueryPerson = AvsQueryPerson
|
||||||
{ avsPersonQueryCardNo :: Maybe AvsCardNo
|
{ avsPersonQueryCardNo :: Maybe AvsCardNo
|
||||||
|
, avsPersonQueryVersionNo :: Maybe AvsVersionNo
|
||||||
, avsPersonQueryFirstName :: Maybe Text
|
, avsPersonQueryFirstName :: Maybe Text
|
||||||
, avsPersonQueryLastName :: Maybe Text
|
, avsPersonQueryLastName :: Maybe Text
|
||||||
, avsPersonQueryInternalPersonalNo :: Maybe Text
|
, avsPersonQueryInternalPersonalNo :: Maybe AvsInternalPersonalNo
|
||||||
, avsPersonQueryVersionNo :: Maybe Text
|
|
||||||
}
|
}
|
||||||
deriving (Eq, Ord, Show, Generic, Typeable)
|
deriving (Eq, Ord, Show, Generic, Typeable)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user