feat: add user-system-function

This commit is contained in:
Gregor Kleen 2020-08-27 17:04:52 +02:00
parent 1b172e4b48
commit abc37aca9c
14 changed files with 114 additions and 1 deletions

View File

@ -765,6 +765,9 @@ CorrectorsFor n@Int: #{pluralDE n "Korrektor" "Korrektoren"}
UserListTitle: Komprehensive Benutzerliste UserListTitle: Komprehensive Benutzerliste
AccessRightsSaved: Berechtigungen erfolgreich verändert AccessRightsSaved: Berechtigungen erfolgreich verändert
AccessRightsNotChanged: Berechtigungen wurden nicht verändert AccessRightsNotChanged: Berechtigungen wurden nicht verändert
UserSystemFunctions: Systemweite Rollen
UserSystemFunctionsSaved: Systemweite Rollen gespeichert
UserSystemFunctionsNotChanged: Es wurden keine systemweiten Rollen angepasst
LecturersForN n@Int: #{pluralDE n "Dozent" "Dozenten"} LecturersForN n@Int: #{pluralDE n "Dozent" "Dozenten"}
@ -1014,6 +1017,10 @@ MailUserRightsIntro name@Text email@UserEmail: #{name} <#{email}> hat folgende U
MailNoLecturerRights: Sie haben derzeit keine Dozenten-Rechte. MailNoLecturerRights: Sie haben derzeit keine Dozenten-Rechte.
MailLecturerRights n@Int: Als Dozent dürfen Sie Veranstaltungen innerhalb #{pluralDE n "Ihres Instituts" "Ihrer Institute"} anlegen. MailLecturerRights n@Int: Als Dozent dürfen Sie Veranstaltungen innerhalb #{pluralDE n "Ihres Instituts" "Ihrer Institute"} anlegen.
MailSubjectUserSystemFunctionsUpdate name@Text: Berechtigungen für #{name} aktualisiert
MailUserSystemFunctionsIntro name@Text email@UserEmail: #{name} <#{email}> hat folgende Uni2work nicht-institutsbezogene Berechtigungen:
MailUserSystemFunctionsNoFunctions: Keine
MailSubjectUserAuthModeUpdate: Ihr Uni2work-Login MailSubjectUserAuthModeUpdate: Ihr Uni2work-Login
UserAuthModePWHashChangedToLDAP: Sie können sich nun mit Ihrer Campus-Kennung in Uni2work einloggen UserAuthModePWHashChangedToLDAP: Sie können sich nun mit Ihrer Campus-Kennung in Uni2work einloggen
UserAuthModeLDAPChangedToPWHash: Sie können sich nun mit einer Uni2work-internen Kennung in Uni2work einloggen UserAuthModeLDAPChangedToPWHash: Sie können sich nun mit einer Uni2work-internen Kennung in Uni2work einloggen
@ -2750,3 +2757,6 @@ SheetPersonalisedFilesUsersList: Liste von Teilnehmern mit personalisierten Übu
AdminCrontabNotGenerated: (Noch) keine Crontab generiert AdminCrontabNotGenerated: (Noch) keine Crontab generiert
CronMatchAsap: ASAP CronMatchAsap: ASAP
CronMatchNone: Nie CronMatchNone: Nie
SystemExamOffice: Prüfungsverwaltung
SystemFaculty: Fakultätsmitglied

View File

@ -762,6 +762,9 @@ CorrectorsFor n: #{pluralEN n "Corrector" "Correctors"}
UserListTitle: Comprehensive list of users UserListTitle: Comprehensive list of users
AccessRightsSaved: Successfully updated permissions AccessRightsSaved: Successfully updated permissions
AccessRightsNotChanged: Permissions left unchanged AccessRightsNotChanged: Permissions left unchanged
UserSystemFunctions: System wide roles
UserSystemFunctionsSaved: Successfully saved system wide roles
UserSystemFunctionsNotChanged: No system wide roles were changed
LecturersForN n: #{pluralEN n "Lecturer" "Lecturers"} LecturersForN n: #{pluralEN n "Lecturer" "Lecturers"}
@ -1014,6 +1017,10 @@ MailUserRightsIntro name email: #{name} <#{email}> now has the following permiss
MailNoLecturerRights: You don't currently have lecturer permissions for any department. MailNoLecturerRights: You don't currently have lecturer permissions for any department.
MailLecturerRights n: As a lecturer you may create new courses within your #{pluralEN n "department" "departments"}. MailLecturerRights n: As a lecturer you may create new courses within your #{pluralEN n "department" "departments"}.
MailSubjectUserSystemFunctionsUpdate name: Permissions for #{name} changed
MailUserSystemFunctionsIntro name email: #{name} <#{email}> now has the following, not school restricted, permissions:
MailUserSystemFunctionsNoFunctions: None
MailSubjectUserAuthModeUpdate: Your Uni2work login MailSubjectUserAuthModeUpdate: Your Uni2work login
UserAuthModePWHashChangedToLDAP: You can now log in to Uni2work using your Campus-account UserAuthModePWHashChangedToLDAP: You can now log in to Uni2work using your Campus-account
UserAuthModeLDAPChangedToPWHash: You can now log in to Uni2work using your Uni2work-internal account UserAuthModeLDAPChangedToPWHash: You can now log in to Uni2work using your Uni2work-internal account
@ -2751,3 +2758,6 @@ SheetPersonalisedFilesUsersList: List of course participants who have personalis
AdminCrontabNotGenerated: Crontab not (yet) generated AdminCrontabNotGenerated: Crontab not (yet) generated
CronMatchAsap: ASAP CronMatchAsap: ASAP
CronMatchNone: Never CronMatchNone: Never
SystemExamOffice: Exam office
SystemFaculty: Faculty member

View File

@ -42,6 +42,10 @@ UserFunction -- Administratively assigned functions (lecturer, admin, evaluation
school SchoolId school SchoolId
function SchoolFunction function SchoolFunction
UniqueUserFunction user school function UniqueUserFunction user school function
UserSystemFunction
user UserId
function SystemFunction
UniqueUserSystemFunction user function
UserExamOffice UserExamOffice
user UserId user UserId
field StudyTermsId field StudyTermsId

View File

@ -219,6 +219,7 @@ embedRenderMessage ''UniWorX ''UploadModeDescr id
embedRenderMessage ''UniWorX ''SecretJSONFieldException id embedRenderMessage ''UniWorX ''SecretJSONFieldException id
embedRenderMessage ''UniWorX ''AFormMessage $ concat . drop 2 . splitCamel embedRenderMessage ''UniWorX ''AFormMessage $ concat . drop 2 . splitCamel
embedRenderMessage ''UniWorX ''SchoolFunction id embedRenderMessage ''UniWorX ''SchoolFunction id
embedRenderMessage ''UniWorX ''SystemFunction id
embedRenderMessage ''UniWorX ''CsvPreset id embedRenderMessage ''UniWorX ''CsvPreset id
embedRenderMessage ''UniWorX ''Quoting ("Csv" <>) embedRenderMessage ''UniWorX ''Quoting ("Csv" <>)
embedRenderMessage ''UniWorX ''FavouriteReason id embedRenderMessage ''UniWorX ''FavouriteReason id

View File

@ -277,7 +277,7 @@ getAdminUserR = postAdminUserR
postAdminUserR uuid = do postAdminUserR uuid = do
adminId <- requireAuthId adminId <- requireAuthId
uid <- decrypt uuid uid <- decrypt uuid
(user@User{..}, adminSchools, functions, schools) <- runDB $ do (user@User{..}, adminSchools, functions, schools, systemFunctions) <- runDB $ do
user <- get404 uid user <- get404 uid
schools <- E.select . E.from $ \(school `E.LeftOuterJoin` userFunction) -> do schools <- E.select . E.from $ \(school `E.LeftOuterJoin` userFunction) -> do
@ -289,10 +289,14 @@ postAdminUserR uuid = do
E.&&. adminFunction E.^. UserFunctionFunction E.==. E.val SchoolAdmin E.&&. adminFunction E.^. UserFunctionFunction E.==. E.val SchoolAdmin
return (school, userFunction E.?. UserFunctionFunction, isAdmin) return (school, userFunction E.?. UserFunctionFunction, isAdmin)
systemFunctionsF <- Set.fromList . map (userSystemFunctionFunction . entityVal) <$> selectList [UserSystemFunctionUser ==. uid] []
let systemFunctions = (`Set.member` systemFunctionsF)
return ( user return ( user
, setOf (folded . filtered (view $ _3 . _Value) . _1 . _entityKey) schools , setOf (folded . filtered (view $ _3 . _Value) . _1 . _entityKey) schools
, setOf (folded . folding (\x -> (,) <$> preview (_2 . _Value . _Just) x <*> preview (_1 . _entityKey) x)) schools , setOf (folded . folding (\x -> (,) <$> preview (_2 . _Value . _Just) x <*> preview (_1 . _entityKey) x)) schools
, setOf (folded . _1) schools , setOf (folded . _1) schools
, systemFunctions
) )
let allFunctions = Set.fromList universeF let allFunctions = Set.fromList universeF
allSchools = Set.mapMonotonic entityKey schools allSchools = Set.mapMonotonic entityKey schools
@ -311,6 +315,8 @@ postAdminUserR uuid = do
userAuthenticationForm = buttonForm' $ if userAuthenticationForm = buttonForm' $ if
| userAuthentication == AuthLDAP -> [BtnAuthPWHash] | userAuthentication == AuthLDAP -> [BtnAuthPWHash]
| otherwise -> [BtnAuthLDAP, BtnPasswordReset] | otherwise -> [BtnAuthLDAP, BtnPasswordReset]
systemFunctionsForm' = funcForm systemFuncForm (fslI MsgUserSystemFunctions) False
where systemFuncForm func = apopt checkBoxField (fslI func) . Just $ systemFunctions func
let userRightsAction changes = do let userRightsAction changes = do
let symDiff = (changes `Set.difference` functions) `Set.union` (functions `Set.difference` changes) let symDiff = (changes `Set.difference` functions) `Set.union` (functions `Set.difference` changes)
updates = (allFunctions `setProduct` adminSchools) `Set.intersection` symDiff updates = (allFunctions `setProduct` adminSchools) `Set.intersection` symDiff
@ -367,8 +373,24 @@ postAdminUserR uuid = do
queueJob' $ JobSendPasswordReset uid queueJob' $ JobSendPasswordReset uid
addMessageI Success MsgPasswordResetQueued addMessageI Success MsgPasswordResetQueued
redirect $ AdminUserR uuid redirect $ AdminUserR uuid
userSystemFunctionsAction newFuncs = do
let symmDiff = setFromFunc newFuncs `setSymmDiff` setFromFunc systemFunctions
if
| not $ Set.null symmDiff -> runDBJobs $ do
forM_ symmDiff $ \func -> if
| newFuncs func
-> void . insertUnique $ UserSystemFunction uid func
| otherwise
-> deleteBy $ UniqueUserSystemFunction uid func
queueDBJob . JobQueueNotification . NotificationUserSystemFunctionsUpdate uid $ setFromFunc systemFunctions
addMessageI Success MsgUserSystemFunctionsSaved
| otherwise
-> addMessageI Info MsgUserSystemFunctionsNotChanged
redirect $ AdminUserR uuid
((rightsResult, rightsFormWidget),rightsFormEnctype) <- runFormPost userRightsForm ((rightsResult, rightsFormWidget),rightsFormEnctype) <- runFormPost userRightsForm
((authResult, authFormWidget),authFormEnctype) <- runFormPost userAuthenticationForm ((authResult, authFormWidget),authFormEnctype) <- runFormPost userAuthenticationForm
((systemFunctionsResult, systemFunctionsWidget),systemFunctionsEnctype) <- runFormPost . identifyForm FIDUserSystemFunctions $ renderAForm FormStandard systemFunctionsForm'
let rightsForm = wrapForm rightsFormWidget def let rightsForm = wrapForm rightsFormWidget def
{ formAction = Just . SomeRoute $ AdminUserR uuid { formAction = Just . SomeRoute $ AdminUserR uuid
, formEncoding = rightsFormEnctype , formEncoding = rightsFormEnctype
@ -378,8 +400,13 @@ postAdminUserR uuid = do
, formEncoding = authFormEnctype , formEncoding = authFormEnctype
, formSubmit = FormNoSubmit , formSubmit = FormNoSubmit
} }
systemFunctionsForm = wrapForm systemFunctionsWidget def
{ formAction = Just . SomeRoute $ AdminUserR uuid
, formEncoding = systemFunctionsEnctype
}
formResult rightsResult userRightsAction formResult rightsResult userRightsAction
formResult authResult userAuthenticationAction formResult authResult userAuthenticationAction
formResult systemFunctionsResult userSystemFunctionsAction
let heading = let heading =
[whamlet|_{MsgAdminUserHeadingFor} ^{nameEmailWidget userEmail userDisplayName userSurname}|] [whamlet|_{MsgAdminUserHeadingFor} ^{nameEmailWidget userEmail userDisplayName userSurname}|]
-- Delete Button needed in data-delete -- Delete Button needed in data-delete

View File

@ -113,6 +113,8 @@ determineNotificationCandidates = awaitForever $ \notif -> do
E.&&. admin E.^. UserFunctionFunction E.==. E.val SchoolAdmin E.&&. admin E.^. UserFunctionFunction E.==. E.val SchoolAdmin
return user return user
withNotif . yieldMany . nub $ affectedUser <> affectedAdmins withNotif . yieldMany . nub $ affectedUser <> affectedAdmins
NotificationUserSystemFunctionsUpdate{..}
-> withNotif $ selectSource [UserId ==. nUser] []
NotificationUserAuthModeUpdate{..} NotificationUserAuthModeUpdate{..}
-> withNotif $ selectSource [UserId ==. nUser] [] -> withNotif $ selectSource [UserId ==. nUser] []
NotificationExamRegistrationActive{..} NotificationExamRegistrationActive{..}
@ -295,6 +297,7 @@ classifyNotification NotificationSheetInactive{} = return NTShe
classifyNotification NotificationCorrectionsAssigned{} = return NTCorrectionsAssigned classifyNotification NotificationCorrectionsAssigned{} = return NTCorrectionsAssigned
classifyNotification NotificationCorrectionsNotDistributed{} = return NTCorrectionsNotDistributed classifyNotification NotificationCorrectionsNotDistributed{} = return NTCorrectionsNotDistributed
classifyNotification NotificationUserRightsUpdate{} = return NTUserRightsUpdate classifyNotification NotificationUserRightsUpdate{} = return NTUserRightsUpdate
classifyNotification NotificationUserSystemFunctionsUpdate{} = return NTUserRightsUpdate
classifyNotification NotificationUserAuthModeUpdate{} = return NTUserAuthModeUpdate classifyNotification NotificationUserAuthModeUpdate{} = return NTUserAuthModeUpdate
classifyNotification NotificationExamRegistrationActive{} = return NTExamRegistrationActive classifyNotification NotificationExamRegistrationActive{} = return NTExamRegistrationActive
classifyNotification NotificationExamRegistrationSoonInactive{} = return NTExamRegistrationSoonInactive classifyNotification NotificationExamRegistrationSoonInactive{} = return NTExamRegistrationSoonInactive

View File

@ -2,6 +2,7 @@
module Jobs.Handler.SendNotification.UserRightsUpdate module Jobs.Handler.SendNotification.UserRightsUpdate
( dispatchNotificationUserRightsUpdate ( dispatchNotificationUserRightsUpdate
, dispatchNotificationUserSystemFunctionsUpdate
) where ) where
import Import import Import
@ -27,3 +28,16 @@ dispatchNotificationUserRightsUpdate nUser _originalRights jRecipient = userMai
editNotifications <- mkEditNotifications jRecipient editNotifications <- mkEditNotifications jRecipient
addHtmlMarkdownAlternatives ($(ihamletFile "templates/mail/userRightsUpdate.hamlet") :: HtmlUrlI18n (SomeMessage UniWorX) (Route UniWorX)) addHtmlMarkdownAlternatives ($(ihamletFile "templates/mail/userRightsUpdate.hamlet") :: HtmlUrlI18n (SomeMessage UniWorX) (Route UniWorX))
dispatchNotificationUserSystemFunctionsUpdate :: UserId -> Set SystemFunction -> UserId -> Handler ()
dispatchNotificationUserSystemFunctionsUpdate nUser _originalSystemFunctions jRecipient = userMailT jRecipient $ do
(User{..}, functions) <- liftHandler . runDB $ do
user <- getJust nUser
functions <- map (userSystemFunctionFunction . entityVal) <$> selectList [UserSystemFunctionUser ==. nUser] []
return (user, Set.fromList functions)
replaceMailHeader "Auto-Submitted" $ Just "auto-generated"
setSubjectI $ MsgMailSubjectUserSystemFunctionsUpdate userDisplayName
-- MsgRenderer mr <- getMailMsgRenderer
editNotifications <- mkEditNotifications jRecipient
addHtmlMarkdownAlternatives ($(ihamletFile "templates/mail/userSystemFunctionsUpdate.hamlet") :: HtmlUrlI18n (SomeMessage UniWorX) (Route UniWorX))

View File

@ -99,6 +99,7 @@ data Notification = NotificationSubmissionRated { nSubmission :: SubmissionId }
| NotificationCorrectionsAssigned { nUser :: UserId, nSheet :: SheetId } | NotificationCorrectionsAssigned { nUser :: UserId, nSheet :: SheetId }
| NotificationCorrectionsNotDistributed { nSheet :: SheetId } | NotificationCorrectionsNotDistributed { nSheet :: SheetId }
| NotificationUserRightsUpdate { nUser :: UserId, nOriginalRights :: Set (SchoolFunction, SchoolShorthand) } | NotificationUserRightsUpdate { nUser :: UserId, nOriginalRights :: Set (SchoolFunction, SchoolShorthand) }
| NotificationUserSystemFunctionsUpdate { nUser :: UserId, nOriginalSystemFunctions :: Set SystemFunction }
| NotificationUserAuthModeUpdate { nUser :: UserId, nOriginalAuthMode :: AuthenticationMode } | NotificationUserAuthModeUpdate { nUser :: UserId, nOriginalAuthMode :: AuthenticationMode }
| NotificationExamRegistrationActive { nExam :: ExamId } | NotificationExamRegistrationActive { nExam :: ExamId }
| NotificationExamRegistrationSoonInactive { nExam :: ExamId } | NotificationExamRegistrationSoonInactive { nExam :: ExamId }

View File

@ -16,3 +16,4 @@ import Model.Types.School as Types
import Model.Types.Allocation as Types import Model.Types.Allocation as Types
import Model.Types.Languages as Types import Model.Types.Languages as Types
import Model.Types.File as Types import Model.Types.File as Types
import Model.Types.User as Types

16
src/Model/Types/User.hs Normal file
View File

@ -0,0 +1,16 @@
module Model.Types.User where
import Import.NoModel
import Model.Types.TH.PathPiece
data SystemFunction
= SystemExamOffice
| SystemFaculty
deriving (Eq, Ord, Read, Show, Enum, Bounded, Generic, Typeable)
deriving anyclass (Universe, Finite, Hashable, NFData)
nullaryPathPiece ''SystemFunction $ camelToPathPiece' 1
pathPieceJSON ''SystemFunction
pathPieceJSONKey ''SystemFunction
derivePersistFieldPathPiece ''SystemFunction

View File

@ -483,6 +483,9 @@ setProduct (Set.toAscList -> as) (Set.toAscList -> bs) = Set.fromDistinctAscList
setPartitionEithers :: (Ord a, Ord b) => Set (Either a b) -> (Set a, Set b) setPartitionEithers :: (Ord a, Ord b) => Set (Either a b) -> (Set a, Set b)
setPartitionEithers = (,) <$> setMapMaybe (preview _Left) <*> setMapMaybe (preview _Right) setPartitionEithers = (,) <$> setMapMaybe (preview _Left) <*> setMapMaybe (preview _Right)
setFromFunc :: (Finite k, Ord k) => (k -> Bool) -> Set k
setFromFunc = Set.fromList . flip filter universeF
---------- ----------
-- Maps -- -- Maps --
---------- ----------

View File

@ -215,6 +215,7 @@ data FormIdentifier
| FIDDelete | FIDDelete
| FIDCourseRegister | FIDCourseRegister
| FIDuserRights | FIDuserRights
| FIDUserSystemFunctions
| FIDcUserNote | FIDcUserNote
| FIDcRegField | FIDcRegField
| FIDcRegButton | FIDcRegButton

View File

@ -4,6 +4,7 @@ $newline never
<section> <section>
<h3> <h3>
_{MsgAdminUserRightsHeading} _{MsgAdminUserRightsHeading}
^{systemFunctionsForm}
^{rightsForm} ^{rightsForm}
<section> <section>
<h3> <h3>

View File

@ -0,0 +1,21 @@
$newline never
\<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<style>
h1 {
font-size: 1.25em;
font-variant: small-caps;
font-weight: normal;
}
<body>
<h1>
_{SomeMessage $ MsgMailUserSystemFunctionsIntro userDisplayName userEmail}
<ul>
$forall function <- Set.toList functions
<li>_{SomeMessage function}
$if onull functions
<li>_{SomeMessage MsgMailUserSystemFunctionsNoFunctions}
^{ihamletSomeMessage editNotifications}