packages feed

ory-kratos-0.0.10.0: lib/OryKratos/Types/Identity.hs

{-# OPTIONS_GHC -fno-warn-unused-binds -fno-warn-unused-imports #-}

module OryKratos.Types.Identity
  ( Identity (..),
    UntypedIdentity,
    IdentityCredentials (..),
    IdentityCredentialsOidc (..),
    IdentityCredentialsOidcProvider (..),
    IdentityCredentialsPassword (..),
    IdentityCredentialsType (..),
    IdentitySchema (..),
    IdentityState (..),
  )
where

import Data.Aeson (FromJSON (..), ToJSON (..), Value, genericParseJSON, genericToEncoding, genericToJSON)
import qualified Data.Aeson as Aeson
import Data.Aeson.Types (Options (..), defaultOptions)
import qualified Data.Char as Char
import Data.Data (Data)
import Data.Function ((&))
import Data.List (stripPrefix)
import qualified Data.Map as Map
import Data.Maybe (fromMaybe)
import Data.Set (Set)
import Data.Swagger (ToSchema, declareNamedSchema)
import qualified Data.Swagger as Swagger
import Data.Text (Text)
import qualified Data.Text as T
import Data.Time
import Data.UUID (UUID)
import GHC.Generics (Generic)
import OryKratos.Types.Helper (customOptions, removeFieldLabelPrefix)
import OryKratos.Types.Types
  ( RecoveryAddress,
    VerifiableIdentityAddress,
  )

type UntypedIdentity = Identity Value Value Value

-- | An identity can be a real human, a service, an IoT device - everything that can be described as an \"actor\" in a system.
data Identity traits metadataAdmin metadataPublic = Identity
  { -- | CreatedAt is a helper struct field for gobuffalo.pop.
    created_at :: Maybe UTCTime,
    -- | Credentials represents all credentials that can be used for authenticating this identity.
    credentials :: Maybe (Map.Map String IdentityCredentials),
    id :: UUID,
    -- | NullJSONRawMessage represents a json.RawMessage that works well with JSON, SQL, and Swagger and is NULLable-
    metadata_admin :: Maybe metadataAdmin,
    -- | NullJSONRawMessage represents a json.RawMessage that works well with JSON, SQL, and Swagger and is NULLable-
    metadata_public :: Maybe metadataPublic,
    -- | RecoveryAddresses contains all the addresses that can be used to recover an identity.
    recovery_addresses :: Maybe [RecoveryAddress],
    -- | SchemaID is the ID of the JSON Schema to be used for validating the identity's traits.
    schema_id :: Text,
    -- | SchemaURL is the URL of the endpoint where the identity's traits schema can be fetched from.  format: url
    schema_url :: Text,
    state :: Maybe IdentityState,
    state_changed_at :: Maybe UTCTime,
    -- | Traits represent an identity's traits. The identity is able to create, modify, and delete traits in a self-service manner. The input will always be validated against the JSON Schema defined in `schema_url`.
    traits :: traits,
    -- | UpdatedAt is a helper struct field for gobuffalo.pop.
    updated_at :: Maybe UTCTime,
    -- | VerifiableAddresses contains all the addresses that can be verified by the user.
    verifiable_addresses :: Maybe [VerifiableIdentityAddress]
  }
  deriving stock (Show, Eq, Generic, Data)

instance
  ( FromJSON traits,
    FromJSON metadataAdmin,
    FromJSON metadataPublic
  ) =>
  FromJSON (Identity traits metadataAdmin metadataPublic)

instance
  ( ToJSON traits,
    ToJSON metadataAdmin,
    ToJSON metadataPublic
  ) =>
  ToJSON (Identity traits metadataAdmin metadataPublic)
  where
  toEncoding = genericToEncoding defaultOptions

-- | Credentials represents a specific credential type
data IdentityCredentials = IdentityCredentials
  { config :: Maybe Value,
    -- | CreatedAt is a helper struct field for gobuffalo.pop.
    created_at :: Maybe UTCTime,
    -- | Identifiers represents a list of unique identifiers this credential type matches.
    identifiers :: Maybe [Text],
    _type :: Maybe IdentityCredentialsType,
    -- | UpdatedAt is a helper struct field for gobuffalo.pop.
    updated_at :: Maybe UTCTime,
    -- | Version refers to the version of the credential. Useful when changing the config schema.
    version :: Maybe Integer
  }
  deriving stock (Show, Eq, Generic, Data)

instance FromJSON IdentityCredentials where
  parseJSON = genericParseJSON customOptions

instance ToJSON IdentityCredentials where
  toJSON = genericToJSON customOptions
  toEncoding = genericToEncoding customOptions

data IdentityCredentialsOidc = IdentityCredentialsOidc
  { providers :: Maybe [IdentityCredentialsOidcProvider]
  }
  deriving stock (Show, Eq, Generic, Data)

instance FromJSON IdentityCredentialsOidc

instance ToJSON IdentityCredentialsOidc where
  toEncoding = genericToEncoding defaultOptions

data IdentityCredentialsOidcProvider = IdentityCredentialsOidcProvider
  { initial_access_token :: Maybe Text,
    initial_id_token :: Maybe Text,
    initial_refresh_token :: Maybe Text,
    provider :: Maybe Text,
    subject :: Maybe Text
  }
  deriving stock (Show, Eq, Generic, Data)

instance FromJSON IdentityCredentialsOidcProvider

instance ToJSON IdentityCredentialsOidcProvider where
  toEncoding = genericToEncoding defaultOptions

data IdentityCredentialsPassword = IdentityCredentialsPassword
  { -- | HashedPassword is a hash-representation of the password.
    hashed_password :: Maybe Text
  }
  deriving stock (Show, Eq, Generic, Data)

instance FromJSON IdentityCredentialsPassword

instance ToJSON IdentityCredentialsPassword where
  toEncoding = genericToEncoding defaultOptions

-- | and so on.
data IdentityCredentialsType
  = Password
  | TOTP
  | OIDC
  | WebAuthn
  | LookupSecret
  deriving stock (Show, Eq, Generic, Data)

instance FromJSON IdentityCredentialsType where
  parseJSON (Aeson.String s) = case T.unpack s of
    "password" -> return Password
    "totp" -> return TOTP
    "oidc" -> return OIDC
    "webauthn" -> return WebAuthn
    "lookup_secret" -> return LookupSecret
    _ -> error "Invalid IdentityCredentialsType"
  parseJSON _ = error "Invalid IdentityCredentialsType"

instance ToJSON IdentityCredentialsType where
  toJSON (Password) = Aeson.String "password"
  toJSON (TOTP) = Aeson.String "totp"
  toJSON (OIDC) = Aeson.String "oidc"
  toJSON (WebAuthn) = Aeson.String "webauthn"
  toJSON (LookupSecret) = Aeson.String "lookup_secret"

data IdentitySchema = IdentitySchema
  { -- | The ID of the Identity JSON Schema
    id :: Maybe Text,
    -- | The actual Identity JSON Schema
    schema :: Maybe Value
  }
  deriving stock (Show, Eq, Generic, Data)

instance FromJSON IdentitySchema

instance ToJSON IdentitySchema where
  toEncoding = genericToEncoding defaultOptions

-- | The state can either be `active` or `inactive`.
data IdentityState = IdentityStateActive | IdentityStateInactive deriving stock (Show, Eq, Generic, Data)

instance FromJSON IdentityState where
  parseJSON (Aeson.String s) = case T.unpack s of
    "active" -> return IdentityStateActive
    "inactive" -> return IdentityStateInactive
    _ -> error "Invalid IdentityState"
  parseJSON _ = error "Invalid IdentityState"

instance ToJSON IdentityState where
  toJSON (IdentityStateActive) = Aeson.String "active"
  toJSON (IdentityStateInactive) = Aeson.String "inactive"