module Web.Controller.ApiKeys where import Web.Types import Web.View.ApiKeys.New import Web.View.ApiKeys.Created import Generated.Types import IHP.Prelude import IHP.ControllerPrelude import qualified Data.Text.Encoding as TE import qualified Crypto.Hash.SHA256 as SHA256 import qualified Data.ByteString.Base16 as Base16 import qualified Data.ByteString.Random as Random instance Controller ApiKeysController where beforeAction = ensureIsUser action ApiKeysAction { apiConsumerId } = do -- Redirect to consumer show page which displays keys redirectTo (ShowApiConsumerAction apiConsumerId) action NewApiKeyAction { apiConsumerId } = do consumer <- fetch apiConsumerId let apiKey = newRecord @ApiKey render NewView { apiKey, consumer } action CreateApiKeyAction = do apiConsumerId <- param @(Id ApiConsumer) "apiConsumerId" consumer <- fetch apiConsumerId scopes <- fromMaybe "" <$> paramOrNothing @Text "scopes" -- Generate a random 32-byte key, encode as hex (64 chars) rawBytes <- liftIO $ Random.random 32 let fullKey = TE.decodeUtf8 (Base16.encode rawBytes) let prefix = T.take 8 fullKey let keyHash = TE.decodeUtf8 $ Base16.encode $ SHA256.hash (TE.encodeUtf8 fullKey) _key <- newRecord @ApiKey |> set #apiConsumerId consumer.id |> set #keyPrefix prefix |> set #keyHash keyHash |> set #scopes scopes |> set #tokenType "static" |> createRecord -- Show full key once; never again render CreatedView { consumer, fullKey } action RevokeApiKeyAction { apiKeyId } = do apiKey <- fetch apiKeyId now <- getCurrentTime apiKey |> set #revokedAt (Just now) |> updateRecord consumer <- fetch apiKey.apiConsumerId redirectTo (ShowApiConsumerAction consumer.id)