packages feed

bloodhound-1.0.0.0: tests/Test/SecurityServiceAccountsSpec.hs

{-# LANGUAGE OverloadedStrings #-}

module Test.SecurityServiceAccountsSpec (spec) where

import Data.Aeson
import Data.Aeson.KeyMap qualified as KM
import Data.ByteString.Lazy.Char8 qualified as L
import Data.List (lookup)
import TestsUtils.Import
import Prelude

-- | @GET /_security/service/elastic/fleet-server@ response, keyed by the
-- full principal (documented 7.17 example, trimmed). The
-- @role_descriptor@ is structurally a 'Role'.
sampleServiceAccountBytes :: L.ByteString
sampleServiceAccountBytes =
  "{\
  \  \"elastic/fleet-server\": {\
  \    \"role_descriptor\": {\
  \      \"cluster\": [\"monitor\", \"manage_own_api_key\"],\
  \      \"indices\": [\
  \        {\
  \          \"names\": [\"logs-*\"],\
  \          \"privileges\": [\"write\", \"create_index\"]\
  \        }\
  \      ],\
  \      \"applications\": [],\
  \      \"run_as\": [],\
  \      \"metadata\": {},\
  \      \"transient_metadata\": {\"enabled\": true}\
  \    }\
  \  }\
  \}"

-- | @POST .../credential/token/token1@ success response carrying the
-- one-time secret bearer value.
sampleCreateServiceTokenBytes :: L.ByteString
sampleCreateServiceTokenBytes =
  "{\
  \  \"created\": true,\
  \  \"token\": {\
  \    \"name\": \"token1\",\
  \    \"value\": \"AAEAAWVsYXN0aWM...vZmxlZXQtc2VydmVyL3Rva2VuMTo3TFdaSDZ\"\
  \  }\
  \}"

-- | @GET .../credential@ response listing index-backed tokens and
-- file-backed node credentials (documented 7.17 example).
sampleServiceCredentialsBytes :: L.ByteString
sampleServiceCredentialsBytes =
  "{\
  \  \"service_account\": \"elastic/fleet-server\",\
  \  \"count\": 3,\
  \  \"tokens\": {\
  \    \"token1\": {},\
  \    \"token42\": {}\
  \  },\
  \  \"nodes_credentials\": {\
  \    \"_nodes\": {\"total\": 3, \"successful\": 3, \"failed\": 0},\
  \    \"file_tokens\": {\"my-token\": {\"nodes\": [\"node0\", \"node1\"]}}\
  \  }\
  \}"

-- | @DELETE .../credential/token/{token_name}@ success response.
sampleDeleteServiceTokenBytes :: L.ByteString
sampleDeleteServiceTokenBytes = "{\"found\": true}"

spec :: Spec
spec = describe "Security service accounts" $ do
  describe "ServiceAccountsListResponse" $ do
    it "decodes a service account keyed by principal" $ do
      let Just decoded = decode sampleServiceAccountBytes :: Maybe ServiceAccountsListResponse
          Just account = lookup "elastic/fleet-server" (serviceAccountsListEntries decoded)
          Just roleDescriptor = saRoleDescriptor account
      rCluster roleDescriptor `shouldBe` ["monitor", "manage_own_api_key"]
      length (rIndices roleDescriptor) `shouldBe` 1
      rTransientMetadata roleDescriptor `shouldSatisfy` isJust

    it "round-trips through encode . decode" $ do
      let Just decoded = decode sampleServiceAccountBytes :: Maybe ServiceAccountsListResponse
          Just roundTripped = decode (encode decoded) :: Maybe ServiceAccountsListResponse
          Just account = lookup "elastic/fleet-server" (serviceAccountsListEntries roundTripped)
          Just roleDescriptor = saRoleDescriptor account
      rCluster roleDescriptor `shouldBe` ["monitor", "manage_own_api_key"]

    it "decodes an empty object to no entries" $ do
      let Just decoded = decode "{}" :: Maybe ServiceAccountsListResponse
      serviceAccountsListEntries decoded `shouldBe` []

  describe "CreateServiceTokenResponse" $ do
    it "decodes the created flag and the secret token" $ do
      let Just decoded = decode sampleCreateServiceTokenBytes :: Maybe CreateServiceTokenResponse
      cstrCreated decoded `shouldBe` Just True
      let Just secret = cstrToken decoded
      stsName secret `shouldBe` Just "token1"
      stsValue secret `shouldSatisfy` isJust

    it "round-trips through encode . decode" $ do
      let Just decoded = decode sampleCreateServiceTokenBytes :: Maybe CreateServiceTokenResponse
          Just roundTripped = decode (encode decoded) :: Maybe CreateServiceTokenResponse
          Just secret = cstrToken roundTripped
      stsName secret `shouldBe` Just "token1"
      cstrCreated roundTripped `shouldBe` Just True

  describe "GetServiceCredentialsResponse" $ do
    it "decodes the tokens map and node credentials" $ do
      let Just decoded = decode sampleServiceCredentialsBytes :: Maybe GetServiceCredentialsResponse
      gscrServiceAccount decoded `shouldBe` Just "elastic/fleet-server"
      gscrCount decoded `shouldBe` Just 3
      let Just tokens = gscrTokens decoded
      KM.size tokens `shouldBe` 2
      gscrNodesCredentials decoded `shouldSatisfy` isJust

    it "round-trips through encode . decode" $ do
      let Just decoded = decode sampleServiceCredentialsBytes :: Maybe GetServiceCredentialsResponse
          Just roundTripped = decode (encode decoded) :: Maybe GetServiceCredentialsResponse
      gscrCount roundTripped `shouldBe` Just 3
      gscrServiceAccount roundTripped `shouldBe` Just "elastic/fleet-server"

  describe "ServiceTokenDeletedResponse" $ do
    it "decodes the found flag" $ do
      let Just decoded = decode sampleDeleteServiceTokenBytes :: Maybe ServiceTokenDeletedResponse
      stdFound decoded `shouldBe` Just True
      stdExtras decoded `shouldBe` KM.empty