{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}

module UserManagement.Statements
    ( getAllUsers
    , getUserByEmail
    , getUserByID
    , putUser
    , deleteUser
    , getUserID
    , getLoginRequirements
    , checkGroupMembership
    , getUserRoleInGroup
    , getAllUserRoles
    , updateUserName
    , updateUserEmail
    , updateUserPWHash
    , addGroup
    , getGroupInfo
    , getAllGroupsOverview
    , deleteGroup
    , updateGroupName
    , updateGroupDescription
    , addRole
    , updateUserRoleInGroup
    , removeUserFromGroup
    , getMembersOfGroup
    , addSuperadmin
    , removeSuperadmin
    , checkSuperadmin
    , checkGroupPermission
    , checkGroupNameExistence
    , getExternalPermission
    , getDocumentGroupID
    , getAllExternalUsersOfDocument
    , addExternalPermission
    , updateExternalPermission
    , deleteExternalPermission
    , createPasswordResetToken
    , getPasswordResetToken
    , markPasswordResetTokenUsed
    , cleanupExpiredTokens
    )
where

import Data.Bifunctor (Bifunctor (second))
import Data.Maybe (listToMaybe)
import Data.Profunctor (lmap, rmap)
import Data.Text
import Data.Time (UTCTime)
import Data.Tuple.Curry (uncurryN)
import Data.UUID (UUID)
import Data.Vector
import qualified Docs.Document as Document
import GHC.Int
import Hasql.Statement
import Hasql.TH
import qualified UserManagement.DocumentPermission as Permission
import qualified UserManagement.Group as Group
import qualified UserManagement.User as User
import Prelude hiding (id)

getUserID :: Statement Text User.UserID
getUserID :: Statement Text UUID
getUserID =
    [singletonStatement|
    select
      id :: uuid
    from
      users
    where
      email = $1 :: text
  |]

getLoginRequirements :: Statement Text (Maybe (User.UserID, Text))
getLoginRequirements :: Statement Text (Maybe (UUID, Text))
getLoginRequirements =
    (Vector (UUID, Text) -> Maybe (UUID, Text))
-> Statement Text (Vector (UUID, Text))
-> Statement Text (Maybe (UUID, Text))
forall b c a. (b -> c) -> Statement a b -> Statement a c
forall (p :: * -> * -> *) b c a.
Profunctor p =>
(b -> c) -> p a b -> p a c
rmap
        ([(UUID, Text)] -> Maybe (UUID, Text)
forall a. [a] -> Maybe a
listToMaybe ([(UUID, Text)] -> Maybe (UUID, Text))
-> (Vector (UUID, Text) -> [(UUID, Text)])
-> Vector (UUID, Text)
-> Maybe (UUID, Text)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Vector (UUID, Text) -> [(UUID, Text)]
forall a. Vector a -> [a]
toList)
        [vectorStatement|

        select
          id :: uuid, pwhash :: text
        from
          users
        where
          email = $1 :: text
      |]

getUserByEmail :: Statement Text (Maybe User.User)
getUserByEmail :: Statement Text (Maybe User)
getUserByEmail =
    (Maybe (UUID, Text, Text) -> Maybe User)
-> Statement Text (Maybe (UUID, Text, Text))
-> Statement Text (Maybe User)
forall b c a. (b -> c) -> Statement a b -> Statement a c
forall (p :: * -> * -> *) b c a.
Profunctor p =>
(b -> c) -> p a b -> p a c
rmap
        (((UUID, Text, Text) -> User)
-> Maybe (UUID, Text, Text) -> Maybe User
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((UUID -> Text -> Text -> User) -> (UUID, Text, Text) -> User
forall a b. Curry a b => b -> a
uncurryN UUID -> Text -> Text -> User
User.User))
        [maybeStatement|
     select id :: uuid, name :: text, email :: text
     from users
     where email = $1 :: text
   |]

getUserByID :: Statement User.UserID (Maybe User.User)
getUserByID :: Statement UUID (Maybe User)
getUserByID =
    (Maybe (UUID, Text, Text) -> Maybe User)
-> Statement UUID (Maybe (UUID, Text, Text))
-> Statement UUID (Maybe User)
forall b c a. (b -> c) -> Statement a b -> Statement a c
forall (p :: * -> * -> *) b c a.
Profunctor p =>
(b -> c) -> p a b -> p a c
rmap
        (((UUID, Text, Text) -> User)
-> Maybe (UUID, Text, Text) -> Maybe User
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((UUID -> Text -> Text -> User) -> (UUID, Text, Text) -> User
forall a b. Curry a b => b -> a
uncurryN UUID -> Text -> Text -> User
User.User))
        [maybeStatement|
     select id :: uuid, name :: text, email :: text
     from users
     where id = $1 :: uuid
   |]

-- | Checks if User has any role in Group and returns True or False
checkGroupMembership :: Statement (User.UserID, Group.GroupID) Bool
checkGroupMembership :: Statement (UUID, GroupID) Bool
checkGroupMembership =
    [singletonStatement|

      select exists (
        select 1
        from roles
        where user_id = $1 :: uuid and group_id = $2 :: int8
      ) :: bool
    |]

getUserRoleInGroup :: Statement (User.UserID, Group.GroupID) (Maybe Text)
getUserRoleInGroup :: Statement (UUID, GroupID) (Maybe Text)
getUserRoleInGroup =
    (Vector Text -> Maybe Text)
-> Statement (UUID, GroupID) (Vector Text)
-> Statement (UUID, GroupID) (Maybe Text)
forall b c a. (b -> c) -> Statement a b -> Statement a c
forall (p :: * -> * -> *) b c a.
Profunctor p =>
(b -> c) -> p a b -> p a c
rmap
        ([Text] -> Maybe Text
forall a. [a] -> Maybe a
listToMaybe ([Text] -> Maybe Text)
-> (Vector Text -> [Text]) -> Vector Text -> Maybe Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Vector Text -> [Text]
forall a. Vector a -> [a]
toList)
        [vectorStatement|

        select
          r.role :: text
        from users u
        join roles r on u.id = r.user_id
        join groups g on g.id = r.group_id
        where u.id = $1 :: uuid and g.id = $2 :: int8
      |]

getAllUsers :: Statement () [User.User]
getAllUsers :: Statement () [User]
getAllUsers =
    ((UUID, Text, Text) -> User) -> [(UUID, Text, Text)] -> [User]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\(UUID
id, Text
name, Text
email) -> UUID -> Text -> Text -> User
User.User (UUID
id :: User.UserID) Text
name Text
email)
        ([(UUID, Text, Text)] -> [User])
-> Statement () [(UUID, Text, Text)] -> Statement () [User]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Vector (UUID, Text, Text) -> [(UUID, Text, Text)])
-> Statement () (Vector (UUID, Text, Text))
-> Statement () [(UUID, Text, Text)]
forall b c a. (b -> c) -> Statement a b -> Statement a c
forall (p :: * -> * -> *) b c a.
Profunctor p =>
(b -> c) -> p a b -> p a c
rmap
            Vector (UUID, Text, Text) -> [(UUID, Text, Text)]
forall a. Vector a -> [a]
toList
            [vectorStatement|
      select id :: uuid, name :: text, email :: text
      from users
    |]

getAllUserRoles :: Statement User.UserID [(Group.GroupID, Text, Text)]
getAllUserRoles :: Statement UUID [(GroupID, Text, Text)]
getAllUserRoles =
    (Vector (GroupID, Text, Text) -> [(GroupID, Text, Text)])
-> Statement UUID (Vector (GroupID, Text, Text))
-> Statement UUID [(GroupID, Text, Text)]
forall b c a. (b -> c) -> Statement a b -> Statement a c
forall (p :: * -> * -> *) b c a.
Profunctor p =>
(b -> c) -> p a b -> p a c
rmap
        Vector (GroupID, Text, Text) -> [(GroupID, Text, Text)]
forall a. Vector a -> [a]
toList
        [vectorStatement|

    select g.id :: int8, g.name :: text, r.role :: text
    from users u
    join roles r on u.id = r.user_id
    join groups g on g.id = r.group_id
    where u.id = $1 :: uuid
  |]

putUser :: Statement User.UserCreate User.UserID
putUser :: Statement UserCreate UUID
putUser =
    (UserCreate -> (Text, Text, Text))
-> Statement (Text, Text, Text) UUID -> Statement UserCreate UUID
forall a b c. (a -> b) -> Statement b c -> Statement a c
forall (p :: * -> * -> *) a b c.
Profunctor p =>
(a -> b) -> p b c -> p a c
lmap
        (\(User.UserCreate Text
name Text
email Text
pwhash) -> (Text
name, Text
email, Text
pwhash))
        [singletonStatement|
      insert into users (name, email, pwhash)
      values ($1 :: text, $2 :: text, $3 :: text)
      returning id :: uuid
    |]

deleteUser :: Statement User.UserID ()
deleteUser :: Statement UUID ()
deleteUser =
    [resultlessStatement|

    delete from users where id = $1 :: uuid
  |]

updateUserName :: Statement (Text, User.UserID) ()
updateUserName :: Statement (Text, UUID) ()
updateUserName =
    [resultlessStatement|

    update users
    set name = $1 :: text
    where id = $2 :: uuid
  |]

updateUserEmail :: Statement (Text, User.UserID) ()
updateUserEmail :: Statement (Text, UUID) ()
updateUserEmail =
    [resultlessStatement|

    update users
    set email = $1 :: text
    where id = $2 :: uuid
  |]

updateUserPWHash :: Statement (Text, User.UserID) ()
updateUserPWHash :: Statement (Text, UUID) ()
updateUserPWHash =
    [resultlessStatement|

    update users
    set pwhash = $1 :: text
    where id = $2 :: uuid
  |]

addGroup :: Statement (Text, Maybe Text) Group.GroupID
addGroup :: Statement (Text, Maybe Text) GroupID
addGroup =
    [singletonStatement|

      insert into groups (name, description)
      values ($1 :: text, $2 :: text?)
      returning id :: int8
    |]

getGroupInfo :: Statement Group.GroupID (Text, Maybe Text)
getGroupInfo :: Statement GroupID (Text, Maybe Text)
getGroupInfo =
    [singletonStatement|
        select name :: text, description :: text?
        from groups
        where id = $1 :: int8
    |]

getAllGroupsOverview :: Statement () [Group.GroupOverview]
getAllGroupsOverview :: Statement () [GroupOverview]
getAllGroupsOverview =
    ((GroupID, Text, Maybe Text) -> GroupOverview)
-> [(GroupID, Text, Maybe Text)] -> [GroupOverview]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap
        ( \(GroupID
id, Text
name, Maybe Text
mDescription) -> GroupID -> Text -> Maybe Text -> GroupOverview
Group.GroupOverview (GroupID
id :: Group.GroupID) Text
name Maybe Text
mDescription
        )
        ([(GroupID, Text, Maybe Text)] -> [GroupOverview])
-> Statement () [(GroupID, Text, Maybe Text)]
-> Statement () [GroupOverview]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Vector (GroupID, Text, Maybe Text)
 -> [(GroupID, Text, Maybe Text)])
-> Statement () (Vector (GroupID, Text, Maybe Text))
-> Statement () [(GroupID, Text, Maybe Text)]
forall b c a. (b -> c) -> Statement a b -> Statement a c
forall (p :: * -> * -> *) b c a.
Profunctor p =>
(b -> c) -> p a b -> p a c
rmap
            Vector (GroupID, Text, Maybe Text) -> [(GroupID, Text, Maybe Text)]
forall a. Vector a -> [a]
toList
            [vectorStatement|
        select id :: int8, name :: text, description :: text?
        from groups
    |]

deleteGroup :: Statement Group.GroupID ()
deleteGroup :: Statement GroupID ()
deleteGroup =
    [resultlessStatement|
      delete from groups
      where id = $1 :: int8
    |]

updateGroupName :: Statement (Text, Group.GroupID) ()
updateGroupName :: Statement (Text, GroupID) ()
updateGroupName =
    [resultlessStatement|
      update groups
      set name = $1 :: text
      where id = $2 :: int8
    |]

updateGroupDescription :: Statement (Maybe Text, Group.GroupID) ()
updateGroupDescription :: Statement (Maybe Text, GroupID) ()
updateGroupDescription =
    [resultlessStatement|
      update groups
      set description = $1 :: text?
      where id = $2 :: int8
    |]

addRole :: Statement (User.UserID, Group.GroupID, Text) ()
addRole :: Statement (UUID, GroupID, Text) ()
addRole =
    [resultlessStatement|

      insert into roles (user_id, group_id, role)
      values ($1 :: uuid, $2 :: int8, $3 :: text :: role)
    |]

updateUserRoleInGroup :: Statement (User.UserID, Group.GroupID, Text) ()
updateUserRoleInGroup :: Statement (UUID, GroupID, Text) ()
updateUserRoleInGroup =
    [resultlessStatement|
      update roles
      set role = $3 :: text :: role
      where user_id = $1 :: uuid and group_id = $2 :: int8
    |]

removeUserFromGroup :: Statement (User.UserID, Group.GroupID) ()
removeUserFromGroup :: Statement (UUID, GroupID) ()
removeUserFromGroup =
    [resultlessStatement|
      delete from roles
      where user_id = $1 :: uuid and group_id = $2 :: int8
    |]

-- | get all Users that have any role in the given group
getMembersOfGroup :: Statement Int64 [User.UserInfo]
getMembersOfGroup :: Statement GroupID [UserInfo]
getMembersOfGroup =
    (Vector (UUID, Text, Text, Text) -> [UserInfo])
-> Statement GroupID (Vector (UUID, Text, Text, Text))
-> Statement GroupID [UserInfo]
forall b c a. (b -> c) -> Statement a b -> Statement a c
forall (p :: * -> * -> *) b c a.
Profunctor p =>
(b -> c) -> p a b -> p a c
rmap
        ( ((UUID, Text, Text, Text) -> UserInfo)
-> [(UUID, Text, Text, Text)] -> [UserInfo]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap
            ( \(UUID
id, Text
name, Text
email, Text
role) ->
                UUID -> Text -> Text -> Role -> UserInfo
User.UserInfo UUID
id Text
name Text
email (String -> Role
forall a. Read a => String -> a
read (String -> Role) -> String -> Role
forall a b. (a -> b) -> a -> b
$ Text -> String
unpack Text
role)
            )
            ([(UUID, Text, Text, Text)] -> [UserInfo])
-> (Vector (UUID, Text, Text, Text) -> [(UUID, Text, Text, Text)])
-> Vector (UUID, Text, Text, Text)
-> [UserInfo]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Vector (UUID, Text, Text, Text) -> [(UUID, Text, Text, Text)]
forall a. Vector a -> [a]
toList
        )
        [vectorStatement|
    select u.id :: uuid, u.name :: text, u.email :: text, r.role :: text
    from users u
    join roles r on u.id = r.user_id
    join groups g on g.id = r.group_id
    where g.id = $1 :: int8
  |]

addSuperadmin :: Statement User.UserID ()
addSuperadmin :: Statement UUID ()
addSuperadmin =
    [resultlessStatement|

      insert into superadmins (user_id)
      values ($1 :: uuid)
    |]

removeSuperadmin :: Statement User.UserID ()
removeSuperadmin :: Statement UUID ()
removeSuperadmin =
    [resultlessStatement|

      delete from superadmins
      where user_id = $1 :: uuid
    |]

checkSuperadmin :: Statement User.UserID Bool
checkSuperadmin :: Statement UUID Bool
checkSuperadmin =
    [singletonStatement|

      select exists (
        select 1
        from superadmins
        where user_id = $1 :: uuid
      ) :: bool
    |]

-- | check if User is Member (or Admin) of the group that owns the specified document
checkGroupPermission :: Statement (User.UserID, Document.DocumentID) Bool
checkGroupPermission :: Statement (UUID, DocumentID) Bool
checkGroupPermission =
    ((UUID, DocumentID) -> (UUID, GroupID))
-> Statement (UUID, GroupID) Bool
-> Statement (UUID, DocumentID) Bool
forall a b c. (a -> b) -> Statement b c -> Statement a c
forall (p :: * -> * -> *) a b c.
Profunctor p =>
(a -> b) -> p b c -> p a c
lmap
        ((DocumentID -> GroupID) -> (UUID, DocumentID) -> (UUID, GroupID)
forall b c a. (b -> c) -> (a, b) -> (a, c)
forall (p :: * -> * -> *) b c a.
Bifunctor p =>
(b -> c) -> p a b -> p a c
second DocumentID -> GroupID
Document.unDocumentID)
        [singletonStatement|
            select exists (
                select 1
                from roles r
                join documents d on d.group_id = r.group_id
                where r.user_id = $1 :: uuid and d.id = $2 :: int8
            ) :: bool
        |]

checkGroupNameExistence :: Statement Text Bool
checkGroupNameExistence :: Statement Text Bool
checkGroupNameExistence =
    [singletonStatement|
            select exists (
                select 1
                from groups
                where name = $1 :: text
            ) :: bool
        |]

-- | extract the Permission for external document editors if they exist
getExternalPermission
    :: Statement (User.UserID, Document.DocumentID) (Maybe Permission.Permission)
getExternalPermission :: Statement (UUID, DocumentID) (Maybe Permission)
getExternalPermission =
    (Maybe Text -> Maybe Permission)
-> Statement (UUID, DocumentID) (Maybe Text)
-> Statement (UUID, DocumentID) (Maybe Permission)
forall b c a. (b -> c) -> Statement a b -> Statement a c
forall (p :: * -> * -> *) b c a.
Profunctor p =>
(b -> c) -> p a b -> p a c
rmap
        (Maybe Text -> (Text -> Maybe Permission) -> Maybe Permission
forall a b. Maybe a -> (a -> Maybe b) -> Maybe b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Text -> Maybe Permission
Permission.textToPermission)
        (Statement (UUID, DocumentID) (Maybe Text)
 -> Statement (UUID, DocumentID) (Maybe Permission))
-> Statement (UUID, DocumentID) (Maybe Text)
-> Statement (UUID, DocumentID) (Maybe Permission)
forall a b. (a -> b) -> a -> b
$ ((UUID, DocumentID) -> (UUID, GroupID))
-> Statement (UUID, GroupID) (Maybe Text)
-> Statement (UUID, DocumentID) (Maybe Text)
forall a b c. (a -> b) -> Statement b c -> Statement a c
forall (p :: * -> * -> *) a b c.
Profunctor p =>
(a -> b) -> p b c -> p a c
lmap
            ((DocumentID -> GroupID) -> (UUID, DocumentID) -> (UUID, GroupID)
forall b c a. (b -> c) -> (a, b) -> (a, c)
forall (p :: * -> * -> *) b c a.
Bifunctor p =>
(b -> c) -> p a b -> p a c
second DocumentID -> GroupID
Document.unDocumentID)
            [maybeStatement|
                select permission :: text
                from external_document_rights
                where user_id = $1 :: uuid and document_id = $2 :: int8
            |]

-- | get the group id of a given document. the maybe is only technical and should never be Nothing in practice.
getDocumentGroupID :: Statement Document.DocumentID (Maybe Group.GroupID)
getDocumentGroupID :: Statement DocumentID (Maybe GroupID)
getDocumentGroupID =
    (DocumentID -> GroupID)
-> Statement GroupID (Maybe GroupID)
-> Statement DocumentID (Maybe GroupID)
forall a b c. (a -> b) -> Statement b c -> Statement a c
forall (p :: * -> * -> *) a b c.
Profunctor p =>
(a -> b) -> p b c -> p a c
lmap
        DocumentID -> GroupID
Document.unDocumentID
        [maybeStatement|
            select group_id :: int8
            from documents
            where id = $1 :: int8
        |]

addExternalPermission
    :: Statement (User.UserID, Document.DocumentID, Text) ()
addExternalPermission :: Statement (UUID, DocumentID, Text) ()
addExternalPermission =
    ((UUID, DocumentID, Text) -> (UUID, GroupID, Text))
-> Statement (UUID, GroupID, Text) ()
-> Statement (UUID, DocumentID, Text) ()
forall a b c. (a -> b) -> Statement b c -> Statement a c
forall (p :: * -> * -> *) a b c.
Profunctor p =>
(a -> b) -> p b c -> p a c
lmap
        ( \(UUID
user, DocumentID
document, Text
permission) -> (UUID
user, DocumentID -> GroupID
Document.unDocumentID DocumentID
document, Text
permission)
        )
        [resultlessStatement|
            insert into external_document_rights (user_id, document_id, permission)
            values ($1 :: uuid, $2 :: int8, $3 :: text :: permission)
        |]

updateExternalPermission
    :: Statement (User.UserID, Document.DocumentID, Text) ()
updateExternalPermission :: Statement (UUID, DocumentID, Text) ()
updateExternalPermission =
    ((UUID, DocumentID, Text) -> (UUID, GroupID, Text))
-> Statement (UUID, GroupID, Text) ()
-> Statement (UUID, DocumentID, Text) ()
forall a b c. (a -> b) -> Statement b c -> Statement a c
forall (p :: * -> * -> *) a b c.
Profunctor p =>
(a -> b) -> p b c -> p a c
lmap
        ( \(UUID
user, DocumentID
document, Text
permission) -> (UUID
user, DocumentID -> GroupID
Document.unDocumentID DocumentID
document, Text
permission)
        )
        [resultlessStatement|
            update external_document_rights
            set permission = $3 :: text :: permission
            where user_id = $1 :: uuid and document_id = $2 :: int8
        |]

deleteExternalPermission :: Statement (User.UserID, Document.DocumentID) ()
deleteExternalPermission :: Statement (UUID, DocumentID) ()
deleteExternalPermission =
    ((UUID, DocumentID) -> (UUID, GroupID))
-> Statement (UUID, GroupID) () -> Statement (UUID, DocumentID) ()
forall a b c. (a -> b) -> Statement b c -> Statement a c
forall (p :: * -> * -> *) a b c.
Profunctor p =>
(a -> b) -> p b c -> p a c
lmap
        ((DocumentID -> GroupID) -> (UUID, DocumentID) -> (UUID, GroupID)
forall b c a. (b -> c) -> (a, b) -> (a, c)
forall (p :: * -> * -> *) b c a.
Bifunctor p =>
(b -> c) -> p a b -> p a c
second DocumentID -> GroupID
Document.unDocumentID)
        [resultlessStatement|
            delete from external_document_rights
            where user_id = $1 :: uuid and document_id = $2 :: int8
        |]

createPasswordResetToken :: Statement (User.UserID, Text, UTCTime) UUID
createPasswordResetToken :: Statement (UUID, Text, UTCTime) UUID
createPasswordResetToken =
    [singletonStatement|
        insert into password_reset_tokens (user_id, token_hash, expires_at)
        values ($1 :: uuid, $2 :: text, $3 :: timestamptz)
        returning id :: uuid
    |]

getPasswordResetToken
    :: Statement
        Text
        (Maybe (UUID, User.UserID, Text, UTCTime, UTCTime, Maybe UTCTime))
getPasswordResetToken :: Statement
  Text (Maybe (UUID, UUID, Text, UTCTime, UTCTime, Maybe UTCTime))
getPasswordResetToken =
    [maybeStatement|
        select
            id :: uuid,
            user_id :: uuid,
            token_hash :: text,
            expires_at :: timestamptz,
            created_at :: timestamptz,
            used_at :: timestamptz?
        from password_reset_tokens
        where token_hash = $1 :: text
            and expires_at > now()
            and used_at is null
    |]

markPasswordResetTokenUsed :: Statement Text ()
markPasswordResetTokenUsed :: Statement Text ()
markPasswordResetTokenUsed =
    [resultlessStatement|
        update password_reset_tokens
        set used_at = now()
        where token_hash = $1 :: text
    |]

cleanupExpiredTokens :: Statement () ()
cleanupExpiredTokens :: Statement () ()
cleanupExpiredTokens =
    [resultlessStatement|
        delete from password_reset_tokens
        where expires_at < now() - interval '24 hours'
    |]

getAllExternalUsersOfDocument
    :: Statement Document.DocumentID [(User.UserID, Maybe Permission.Permission)]
getAllExternalUsersOfDocument :: Statement DocumentID [(UUID, Maybe Permission)]
getAllExternalUsersOfDocument =
    (Vector (UUID, Text) -> [(UUID, Maybe Permission)])
-> Statement DocumentID (Vector (UUID, Text))
-> Statement DocumentID [(UUID, Maybe Permission)]
forall b c a. (b -> c) -> Statement a b -> Statement a c
forall (p :: * -> * -> *) b c a.
Profunctor p =>
(b -> c) -> p a b -> p a c
rmap
        (((UUID, Text) -> (UUID, Maybe Permission))
-> [(UUID, Text)] -> [(UUID, Maybe Permission)]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((Text -> Maybe Permission)
-> (UUID, Text) -> (UUID, Maybe Permission)
forall b c a. (b -> c) -> (a, b) -> (a, c)
forall (p :: * -> * -> *) b c a.
Bifunctor p =>
(b -> c) -> p a b -> p a c
Data.Bifunctor.second Text -> Maybe Permission
Permission.textToPermission) ([(UUID, Text)] -> [(UUID, Maybe Permission)])
-> (Vector (UUID, Text) -> [(UUID, Text)])
-> Vector (UUID, Text)
-> [(UUID, Maybe Permission)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Vector (UUID, Text) -> [(UUID, Text)]
forall a. Vector a -> [a]
toList)
        (Statement DocumentID (Vector (UUID, Text))
 -> Statement DocumentID [(UUID, Maybe Permission)])
-> Statement DocumentID (Vector (UUID, Text))
-> Statement DocumentID [(UUID, Maybe Permission)]
forall a b. (a -> b) -> a -> b
$ (DocumentID -> GroupID)
-> Statement GroupID (Vector (UUID, Text))
-> Statement DocumentID (Vector (UUID, Text))
forall a b c. (a -> b) -> Statement b c -> Statement a c
forall (p :: * -> * -> *) a b c.
Profunctor p =>
(a -> b) -> p b c -> p a c
lmap
            DocumentID -> GroupID
Document.unDocumentID
            [vectorStatement|
                select user_id :: uuid, permission :: text
                from external_document_rights
                where document_id = $1 :: int8
            |]