---
setup:
  - requires:
      cluster_features: ["gte_v7.15.1"]
      reason:  original indices are propagated correctly in 7.15.1
      test_runner_features: headers
  - do:
      cluster.health:
          wait_for_status: yellow

  - do:
      security.put_role:
        name: "test_admin_role"
        body:  >
            {
              "indices": [
                { "names": ["*"], "privileges": ["all"] }
              ]
            }

  - do:
      security.put_user:
        username: "test_admin"
        body:  >
          {
            "password" : "x-pack-test-password",
            "roles" : [ "test_admin_role" ],
            "full_name" : "user with full privileges to multiple indices"
          }

  - do:
      security.put_role:
        name: "dls_all_role"
        body:  >
            {
              "cluster": [ "manage_own_api_key" ],
              "indices": [
                { "names": ["test_security"], "privileges": ["read"], "query": "{\"term\": {\"ck\": \"const\"}}" }
              ]
            }

  - do:
      security.put_role:
        name: "dls_alias_role"
        body: >
          {
            "indices": [
              { "names": ["alias_security"], "privileges": ["read"], "query": "{\"term\": {\"ck\": \"const\"}}" }
            ]
          }

  - do:
      security.put_role:
        name: "dls_none_role"
        body:  >
            {
              "indices": [
                { "names": ["test_security"], "privileges": ["read"], "query": "{\"term\": {\"foo\": \"does_not_exist\"}}" }
              ]
            }
  - do:
      security.put_user:
        username: "dls_all_user"
        body:  >
          {
            "password" : "x-pack-test-password",
            "roles" : [ "dls_none_role", "dls_all_role" ],
            "full_name" : "user with access to all docs in test_security index (using DLS)"
          }

  - do:
      security.put_user:
        username: "dls_alias_user"
        body: >
          {
            "password" : "x-pack-test-password",
            "roles" : [ "dls_alias_role" ],
            "full_name" : "user with access to all docs in test_security index (using DLS)"
          }

  - do:
      security.put_role:
        name: "dls_some_role"
        body:  >
            {
              "cluster": [ "manage_own_api_key" ],
              "indices": [
                { "names": ["test_security"], "privileges": ["read"], "query": "{\"term\": {\"foo\": \"bar_dls\"}}" }
              ]
            }
  - do:
      security.put_user:
        username: "dls_some_user"
        body:  >
          {
            "password" : "x-pack-test-password",
            "roles" : [ "dls_some_role" ],
            "full_name" : "user with access to selected docs in index"
          }
  - do:
      security.put_role:
        name: "fls_role"
        body:  >
            {
              "indices": [
                { "names": ["test_security"], "privileges": ["read"], "field_security" : {"grant" : [ "*"],"except": [ "foo" ]} }
              ]
            }

  - do:
      security.put_user:
        username: "fls_user"
        body:  >
          {
            "password" : "x-pack-test-password",
            "roles" : [ "fls_role" ],
            "full_name" : "user with access to selected docs in index"
          }
  - do:
      indices.create:
        index: test_k
        body:
          settings:
            index:
              number_of_shards:   1
          mappings:
            properties:
              foo:
                type : keyword
              foo_non_indexed:
                type: keyword
                index: false
              semver:
                type: version
              timestamp:
                type : date
  - do:
      indices.create:
        index: test_ck
        body:
          settings:
            index:
              number_of_shards:   1
          mappings:
            properties:
              foo:
                type : constant_keyword
                value: bar_ck
              other:
                type : text
              timestamp:
                type : date
  - do:
      indices.create:
        index: test_ck_without_constant_field
        body:
          settings:
            index:
              number_of_shards:   1
          mappings:
            properties:
              foo:
                type : constant_keyword
              other:
                type : text
              timestamp:
                type : date
  - do:
      indices.create:
        index: test_f
        body:
          settings:
            index:
              number_of_shards:   1
          mappings:
            properties:
              foo:
                type : flattened
              semver:
                type: version
              timestamp:
                type : date
  - do:
      indices.create:
        index: test_security
        body:
          aliases:
            alias_security: {}
          settings:
            index:
              number_of_shards:   1
          mappings:
            properties:
              ck:
                type : constant_keyword
                value: const
              foo:
                type : keyword
              semver:
                type: version
              foo_non_indexed:
                type: keyword
                index: false

  - do:
      index:
          index:  test_k
          id:     "1"
          body:   { foo: "bar_k", foo_non_indexed: "bar_k", "semver" : ["11.1.1", "2.0.0-beta"], "timestamp":"2021-01-01T01:01:01.000Z" }

  - do:
      index:
          index:  test_ck
          id:     "2"
          body:   { other: "foo", "timestamp":"2020-01-01T01:01:01.000Z"  }

  - do:
      index:
        index:  test_ck_without_constant_field
        id:     "2"
        body:   { other: "foo", "timestamp":"2020-01-01T01:01:01.000Z"  }

  - do:
      index:
          index:  test_f
          id:     "3"
          body:   { foo: { bar: "bar_f" }, "semver" : "1.2.1", "timestamp":"2019-01-01T01:01:01.000Z" }
  - do:
      index:
          index:  test_security
          id:     "4"
          body:   { foo: "bar_dls", foo_non_indexed: "bar_dls", "semver" : "1.11.1"}

  - do: #superuser
      headers: { Authorization: "Basic dGVzdF9hZG1pbjp4LXBhY2stdGVzdC1wYXNzd29yZA==" } # admin
      indices.refresh: {}

  - do: #superuser
     cluster.health:
          index: test_f
          wait_for_status: yellow
          wait_for_no_initializing_shards: true

  # terms_enum returns 0 results if the search fails, so perform a search here so that any failures are picked up early
  - do:
      count:
        index:  test_*

---
teardown:
  - do:
      security.delete_user:
        username: "dls_all_user"
        ignore: 404

  - do:
      security.delete_role:
        name: "dls_all_role"
        ignore: 404

  - do:
      security.delete_user:
        username: "dls_alias_user"
        ignore: 404

  - do:
      security.delete_role:
        name: "dls_alias_role"
        ignore: 404
  - do:
      security.delete_role:
        name: "dls_none_role"
        ignore: 404

  - do:
      security.delete_user:
        username: "dls_some_user"
        ignore: 404

  - do:
      security.delete_role:
        name: "dls_some_role"
        ignore: 404
  - do:
      security.delete_user:
        username: "fls_user"
        ignore: 404

  - do:
      security.delete_role:
        name: "fls_role"
        ignore: 404
  - do:
      security.delete_user:
        username: "test_admin"
        ignore: 404

  - do:
      security.delete_role:
        name: "test_admin_role"
        ignore: 404

---
"Test basic term enumeration":
  - do:
      terms_enum:
        index:  test_*
        body:  {"field": "foo", "string":"b"}
  - length: {terms: 3}


  - do:
      terms_enum:
        index:  test_*
        body:  {"field": "foo.bar", "string":"b"}
  - length: {terms: 1}

---
"Test basic term enumeration on doc value only field":
  - requires:
      cluster_features: ["gte_v8.2.0"]
      reason: "terms enum support for doc value only keyword fields was added in 8.2.0"

  - do:
      terms_enum:
        index:  test_*
        body:  {"field": "foo_non_indexed", "string":"b"}
  - length: {terms: 2}

---
"Test basic term enumeration on version field":
  - requires:
      cluster_features: ["gte_v8.8.0"]
      reason: "terms enum support for version fields was added in 8.8.0"

  - do:
      terms_enum:
        index:  test_*
        body:  {"field": "semver", "string":"1"}
  - length: {terms: 3}
  - match: { terms: [ 1.2.1, 1.11.1, 11.1.1] }

  - do:
      terms_enum:
        index:  test_*
        body:  {"field": "semver", "string":"2.0.0-B"}
  - length: {terms: 0}

  - do:
      terms_enum:
        index:  test_*
        body:  {"field": "semver", "string":"2.0.0-B", "case_insensitive" : true}
  - length: {terms: 1}

  - do:
      terms_enum:
        index:  test_*
        body:  {"field": "semver", "string":"1", "search_after" : "1.12.0"}
  - length: {terms: 1}
  - match: { terms: [ 11.1.1] }

---
"Test case insensitivity":
  - do:
      terms_enum:
        index:  test_k
        body:  {"field": "foo", "string":"B"}
  - length: {terms: 0}

  - do:
      terms_enum:
        index:  test_k
        body:  {"field": "foo", "string":"B", "case_insensitive": true}
  - length: {terms: 1}

  - do:
      terms_enum:
        index:  test_f
        body:  {"field": "foo.bar", "string":"B"}
  - length: {terms: 0}

  - do:
      terms_enum:
        index:  test_f
        body:  {"field": "foo.bar", "string":"B", "case_insensitive": true}
  - length: {terms: 1}

  - do:
      terms_enum:
        index:  test_f
        body:  {"field": "foo.Bar", "string":"B", "case_insensitive": true}
  - length: {terms: 0}

---
"Test case insensitivity on doc value only field":
  - requires:
      cluster_features: ["gte_v8.2.0"]
      reason: "terms enum support for doc value only keyword fields was added in 8.2.0"

  - do:
      terms_enum:
        index:  test_k
        body:  {"field": "foo_non_indexed", "string":"B"}
  - length: {terms: 0}

  - do:
      terms_enum:
        index:  test_k
        body:  {"field": "foo_non_indexed", "string":"B", "case_insensitive": true}
  - length: {terms: 1}

---
"Test null search string allowed":
  - requires:
      cluster_features: ["gte_v8.0.0"]
      reason:  TODO remove this skip after PR 73144 is backported to 7.x

  - do:
      terms_enum:
        index:  test_k
        body:  {"field": "foo"}
  - length: {terms: 1}

---
"Test search after keyword field":
  - do:
      terms_enum:
        index:  test_k
        body:  {"field": "foo", "string":"b", "search_after":"baz"}
  - length: {terms: 0}

  - do:
      terms_enum:
        index:  test_k
        body:  {"field": "foo", "string":"b", "search_after":"bar_k"}
  - length: {terms: 0}

  - do:
      terms_enum:
        index:  test_k
        body:  {"field": "foo", "string":"b", "search_after":"baa"}
  - length: {terms: 1}

  - do:
      terms_enum:
        index:  test_k
        body:  {"field": "foo", "string":"", "search_after":"baa"}
  - length: {terms: 1}

---
"Test search after on doc value only keyword field":
  - requires:
      cluster_features: ["gte_v8.2.0"]
      reason: "terms enum support for doc value only keyword fields was added in 8.2.0"

  - do:
      terms_enum:
        index:  test_k
        body:  {"field": "foo_non_indexed", "string":"b", "search_after":"baz"}
  - length: {terms: 0}

  - do:
      terms_enum:
        index:  test_k
        body:  {"field": "foo_non_indexed", "string":"b", "search_after":"bar_k"}
  - length: {terms: 0}

  - do:
      terms_enum:
        index:  test_k
        body:  {"field": "foo_non_indexed", "string":"b", "search_after":"baa"}
  - length: {terms: 1}

  - do:
      terms_enum:
        index:  test_k
        body:  {"field": "foo_non_indexed", "string":"", "search_after":"baa"}
  - length: {terms: 1}

---
"Test search after flattened field":
  - do:
      terms_enum:
        index:  test_f
        body:  {"field": "foo.bar", "string":"b", "search_after":"baz"}
  - length: {terms: 0}

  - do:
      terms_enum:
        index:  test_f
        body:  {"field": "foo.bar", "string":"b", "search_after":"bar_f"}
  - length: {terms: 0}

  - do:
      terms_enum:
        index:  test_f
        body:  {"field": "foo.bar", "string":"b", "search_after":"baa"}
  - length: {terms: 1}

  - do:
      terms_enum:
        index:  test_f
        body:  {"field": "foo.bar", "string":"", "search_after":"baa"}
  - length: {terms: 1}

---
"Test search after constant keyword field":
  - do:
      terms_enum:
        index:  test_ck
        body:  {"field": "foo", "string":"b", "search_after":"baz"}
  - length: {terms: 0}

  - do:
      terms_enum:
        index:  test_ck
        body:  {"field": "foo", "string":"b", "search_after":"bar_ck"}
  - length: {terms: 0}

  - do:
      terms_enum:
        index:  test_ck
        body:  {"field": "foo", "string":"b", "search_after":"baa"}
  - length: {terms: 1}

  - do:
      terms_enum:
        index:  test_ck
        body:  {"field": "foo", "string":"", "search_after":"baa"}
  - length: {terms: 1}

---
"Test search after on unconfigured constant keyword field":
  - requires:
      cluster_features: ["gte_v8.3.0"]
      reason:  bug fixed in 8.3.0

  - do:
      terms_enum:
        index:  test_ck_without_constant_field
        body:  {"field": "foo", "string":"b", "search_after":"baz"}
  - length: {terms: 0}

---
"Test index filtering":
  - do:
      terms_enum:
        index:  test_*
        body:  {"field": "foo", "string":"b", "index_filter":{"range":{"timestamp":{"gte":"2021-01-01T01:01:01.000Z"}}}}
  - length: {terms: 1}
---
"Test legal timeout":
  - do:
      terms_enum:
        index:  test_*
        body:  {"field": "foo", "string":"b", "timeout": "1s"}
  - length: {terms: 3}
---
"Test illegal timeout":
  - do:
      catch: /Timeout cannot be > 1 minute/
      terms_enum:
        index:  test_*
        body:  {"field": "foo", "string":"b", "timeout": "2m"}

---
"Test security":

  - do:
      headers: { Authorization: "Basic dGVzdF9hZG1pbjp4LXBhY2stdGVzdC1wYXNzd29yZA==" } # admin_user sees all docs
      terms_enum:
        index:  test_security
        body:  {"field": "foo", "string":"b"}
  - length: {terms: 1}

  - do:
      headers: { Authorization: "Basic ZGxzX2FsbF91c2VyOngtcGFjay10ZXN0LXBhc3N3b3Jk" } # dls_all_user sees all docs
      terms_enum:
        index:  test_security
        body:  {"field": "foo", "string":"b"}
  - length: {terms: 1}

  - do:
      headers: { Authorization: "Basic ZGxzX2FsaWFzX3VzZXI6eC1wYWNrLXRlc3QtcGFzc3dvcmQ=" } # dls_alias_user sees all docs through the alias
      terms_enum:
        index: alias_security
        body: { "field": "foo", "string": "b" }
  - length: { terms: 1 }

  - do:
      headers: { Authorization: "Basic ZGxzX3NvbWVfdXNlcjp4LXBhY2stdGVzdC1wYXNzd29yZA==" } # dls_some_user sees selected docs
      terms_enum:
        index:  test_security
        body:  {"field": "foo", "string":"b"}
  - length: {terms: 0}

  - do:
      headers: { Authorization: "Basic ZmxzX3VzZXI6eC1wYWNrLXRlc3QtcGFzc3dvcmQ=" } # fls_user can't see field
      terms_enum:
        index:  test_security
        body:  {"field": "foo", "string":"b"}
  - length: {terms: 0}

---
"Test security with API keys":

  - do:
      headers: { Authorization: "Basic ZGxzX2FsbF91c2VyOngtcGFjay10ZXN0LXBhc3N3b3Jk" } # dls_all_user
      security.create_api_key:
        body: >
          {
            "name": "dls_all_user_good_key",
            "role_descriptors": {
              "role-a": {
                "index": [
                  {
                    "names": ["test_security"],
                    "privileges": ["read"],
                    "query": "{\"term\": {\"ck\": \"const\"}}"
                  }
                ]
              }
            }
          }
  - match: { name: "dls_all_user_good_key" }
  - set: { encoded: login_creds}
  - do:
      headers:
        Authorization: ApiKey ${login_creds}  # dls_all_user good API key sees all documents
      terms_enum:
        index: test_security
        body: { "field": "foo", "string": "b" }
  - length: { terms: 1 }

  - do:
      headers: { Authorization: "Basic ZGxzX2FsbF91c2VyOngtcGFjay10ZXN0LXBhc3N3b3Jk" } # dls_all_user
      security.create_api_key:
        body: >
          {
            "name": "dls_all_user_bad_key",
            "role_descriptors": {
              "role-a": {
                "index": [
                  {
                    "names": ["test_security"],
                    "privileges": ["read"],
                    "query": "{\"term\": {\"foo\": \"bar_dls\"}}"
                  }
                ]
              }
            }
          }
  - match: { name: "dls_all_user_bad_key" }
  - set: { encoded: login_creds}
  - do:
      headers:
        Authorization: ApiKey ${login_creds}  # dls_all_user bad API key sees selected docs
      terms_enum:
        index: test_security
        body: { "field": "foo", "string": "b" }
  - length: { terms: 0 }

  - do:
      headers: { Authorization: "Basic ZGxzX3NvbWVfdXNlcjp4LXBhY2stdGVzdC1wYXNzd29yZA==" } # dls_some_user
      # Create an API key with all DLS permissions, but it is still bounded by the owner user's permissions
      security.create_api_key:
        body: >
          {
            "name": "dls_some_user_key",
            "role_descriptors": {
              "role-a": {
                "index": [
                  {
                    "names": ["test_security"],
                    "privileges": ["read"],
                    "query": "{\"term\": {\"ck\": \"const\"}}"
                  }
                ]
              }
            }
          }
  - match: { name: "dls_some_user_key" }
  - set: { encoded: login_creds}
  - do:
      headers:
        Authorization: ApiKey ${login_creds}  # dls_some_user's API key sees selected user regardless of the key's role descriptor
      terms_enum:
        index: test_security
        body: { "field": "foo", "string": "b" }
  - length: { terms: 0 }
