shomei-core-0.2.0.0: src/Shomei/ServiceAccount/Domain.hs
-- | The service-account entity (EP-4): a machine credential an operator creates, rotates, and
-- revokes at runtime, authenticating at @POST \/oauth\/token@ with the OAuth2
-- @client_credentials@ grant.
--
-- 'secretHash' is a lowercase 64-character SHA-256 hex digest verified in constant time by
-- 'Shomei.ServiceAccount.Secret.verifyServiceSecret'.
module Shomei.ServiceAccount.Domain
( ServiceAccountStatus (..),
ServiceAccount (..),
NewServiceAccount (..),
)
where
import Data.Set (Set)
import Shomei.Authorization.Claims.Domain (Scope)
import Shomei.Id (ServiceAccountDbId, UserId)
import Shomei.Prelude
-- | A revoked account keeps its row: audit events naming it must still resolve, and a revoked
-- credential must be refused, not forgotten.
data ServiceAccountStatus = ServiceAccountActive | ServiceAccountRevoked
deriving stock (Generic, Eq, Show)
deriving anyclass (FromJSON, ToJSON)
data ServiceAccount = ServiceAccount
{ serviceAccountId :: !ServiceAccountDbId,
-- | the TypeID text rendering of 'serviceAccountId'; the OAuth2 @client_id@. Public.
clientId :: !Text,
-- | the @shomei_users@ row backing this account's sessions and claims @sub@
userId :: !UserId,
secretHash :: !Text,
displayName :: !Text,
-- | the ceiling on what a token from this account may carry. A @client_credentials@
-- request with no @scope@ parameter is granted all of them.
allowedScopes :: !(Set Scope),
status :: !ServiceAccountStatus,
createdAt :: !UTCTime,
rotatedAt :: !(Maybe UTCTime),
revokedAt :: !(Maybe UTCTime)
}
deriving stock (Generic, Eq, Show)
deriving anyclass (FromJSON, ToJSON)
data NewServiceAccount = NewServiceAccount
{ serviceAccountId :: !ServiceAccountDbId,
clientId :: !Text,
userId :: !UserId,
secretHash :: !Text,
displayName :: !Text,
allowedScopes :: !(Set Scope),
createdAt :: !UTCTime
}
deriving stock (Generic, Eq, Show)
deriving anyclass (FromJSON, ToJSON)