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 as T import qualified Data.Text.Encoding as TE import qualified "cryptohash-sha256" 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 let apiConsumerId = param @(Id ApiConsumer) "apiConsumerId" consumer <- fetch apiConsumerId let 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)