Repository: ansible-collections/community.general

Issue:
community.general.onepassword: bug with field name casing
### Summary

When using the onepassword lookup plugin with `op` cli v2 on a field that contains uppercase letters while also specifying a section name, the lookup always fails. I think the problem originates here:

https://github.com/ansible-collections/community.general/blob/82e74e35d96e589677c213c71a1b1a8270e3b253/plugins/lookup/onepassword.py#L468

the field name from the lookup query is transformed to lower case

https://github.com/ansible-collections/community.general/blob/82e74e35d96e589677c213c71a1b1a8270e3b253/plugins/lookup/onepassword.py#L490-L496

but compared against the untransformed label from the json data.

### Issue Type

Bug Report

### Component Name

onepassword

### Ansible Version

```console (paste below)
$ ansible --version
ansible [core 2.14.5]
```


### Community.general Version

```console (paste below)
$ ansible-galaxy collection list community.general
Collection        Version
----------------- -------
community.general 8.2.0
```


### Configuration

```console (paste below)
$ ansible-config dump --only-changed

```


### OS / Environment

_No response_

### Steps to Reproduce

<!--- Paste example playbooks or commands between quotes below -->
```yaml (paste below)
---
- name: op bug
  hosts: localhost
  gather_facts: false
  tasks:
    - name: debug
      ansible.builtin.debug:
        var: ip
      vars:
        ip: "{{ lookup('community.general.onepassword',
          'Test',
          field='IP',
          section='server',
          vault=vault_name) }}"
```


### Expected Results

Assuming that there is an entry 'Test' in the vault that has a section 'server' and a field 'IP' with value `192.168.1.1`, the result should be `192.168.1.1`

### Actual Results

```console (paste below)
TASK [debug] ***********************************************************************************************************************************************************************************
ok: [localhost] =>
  ip: ''
```


### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct
community.general.onepassword: bug with field name casing
### Summary

When using the onepassword lookup plugin with `op` cli v2 on a field that contains uppercase letters while also specifying a section name, the lookup always fails. I think the problem originates here:

https://github.com/ansible-collections/community.general/blob/82e74e35d96e589677c213c71a1b1a8270e3b253/plugins/lookup/onepassword.py#L468

the field name from the lookup query is transformed to lower case

https://github.com/ansible-collections/community.general/blob/82e74e35d96e589677c213c71a1b1a8270e3b253/plugins/lookup/onepassword.py#L490-L496

but compared against the untransformed label from the json data.

### Issue Type

Bug Report

### Component Name

onepassword

### Ansible Version

```console (paste below)
$ ansible --version
ansible [core 2.14.5]
```


### Community.general Version

```console (paste below)
$ ansible-galaxy collection list community.general
Collection        Version
----------------- -------
community.general 8.2.0
```


### Configuration

```console (paste below)
$ ansible-config dump --only-changed

```


### OS / Environment

_No response_

### Steps to Reproduce

<!--- Paste example playbooks or commands between quotes below -->
```yaml (paste below)
---
- name: op bug
  hosts: localhost
  gather_facts: false
  tasks:
    - name: debug
      ansible.builtin.debug:
        var: ip
      vars:
        ip: "{{ lookup('community.general.onepassword',
          'Test',
          field='IP',
          section='server',
          vault=vault_name) }}"
```


### Expected Results

Assuming that there is an entry 'Test' in the vault that has a section 'server' and a field 'IP' with value `192.168.1.1`, the result should be `192.168.1.1`

### Actual Results

```console (paste below)
TASK [debug] ***********************************************************************************************************************************************************************************
ok: [localhost] =>
  ip: ''
```


### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct


Discussion:
Files identified in the description:
* [`plugins/doc_fragments/onepassword.py`](https://github.com/ansible-collections/community.general/blob/main/plugins/doc_fragments/onepassword.py)

If these files are incorrect, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/devel/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
Files identified in the description:
* [`plugins/doc_fragments/onepassword.py`](https://github.com/ansible-collections/community.general/blob/main/plugins/doc_fragments/onepassword.py)

If these files are incorrect, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/devel/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->

Resolving patch:
diff --git a/plugins/lookup/onepassword.py b/plugins/lookup/onepassword.py
--- a/plugins/lookup/onepassword.py
+++ b/plugins/lookup/onepassword.py
@@ -489,10 +489,10 @@ def _parse_field(self, data_json, field_name, section_title=None):
             current_section_title = section.get("label", section.get("id", "")).lower()
             if section_title == current_section_title:
                 # In the correct section. Check "label" then "id" for the desired field_name
-                if field.get("label") == field_name:
+                if field.get("label", "").lower() == field_name:
                     return field.get("value", "")
 
-                if field.get("id") == field_name:
+                if field.get("id", "").lower() == field_name:
                     return field.get("value", "")
 
         return ""


Test patch:
diff --git a/tests/unit/plugins/lookup/onepassword_common.py b/tests/unit/plugins/lookup/onepassword_common.py
--- a/tests/unit/plugins/lookup/onepassword_common.py
+++ b/tests/unit/plugins/lookup/onepassword_common.py
@@ -123,5 +123,173 @@ def load_file(file):
             "expected": [""],
             "output": load_file("v2_out_04.json")
         },
+        {
+            # Query item without section by lowercase id (case matching)
+            "vault_name": "Test Vault",
+            "queries": ["LabelCasing"],
+            "kwargs": {
+                "field": "lowercaseid",
+            },
+            "expected": ["lowercaseid"],
+            "output": load_file("v2_out_05.json")
+        },
+        {
+            # Query item without section by lowercase id (case not matching)
+            "vault_name": "Test Vault",
+            "queries": ["LabelCasing"],
+            "kwargs": {
+                "field": "LOWERCASEID",
+            },
+            "expected": ["lowercaseid"],
+            "output": load_file("v2_out_05.json")
+        },
+        {
+            # Query item without section by lowercase label (case matching)
+            "vault_name": "Test Vault",
+            "queries": ["LabelCasing"],
+            "kwargs": {
+                "field": "lowercaselabel",
+            },
+            "expected": ["lowercaselabel"],
+            "output": load_file("v2_out_05.json")
+        },
+        {
+            # Query item without section by lowercase label (case not matching)
+            "vault_name": "Test Vault",
+            "queries": ["LabelCasing"],
+            "kwargs": {
+                "field": "LOWERCASELABEL",
+            },
+            "expected": ["lowercaselabel"],
+            "output": load_file("v2_out_05.json")
+        },
+        {
+            # Query item without section by mixed case id (case matching)
+            "vault_name": "Test Vault",
+            "queries": ["LabelCasing"],
+            "kwargs": {
+                "field": "MiXeDcAsEiD",
+            },
+            "expected": ["mixedcaseid"],
+            "output": load_file("v2_out_05.json")
+        },
+        {
+            # Query item without section by mixed case id (case not matching)
+            "vault_name": "Test Vault",
+            "queries": ["LabelCasing"],
+            "kwargs": {
+                "field": "mixedcaseid",
+            },
+            "expected": ["mixedcaseid"],
+            "output": load_file("v2_out_05.json")
+        },
+        {
+            # Query item without section by mixed case label (case matching)
+            "vault_name": "Test Vault",
+            "queries": ["LabelCasing"],
+            "kwargs": {
+                "field": "MiXeDcAsElAbEl",
+            },
+            "expected": ["mixedcaselabel"],
+            "output": load_file("v2_out_05.json")
+        },
+        {
+            # Query item without section by mixed case label (case not matching)
+            "vault_name": "Test Vault",
+            "queries": ["LabelCasing"],
+            "kwargs": {
+                "field": "mixedcaselabel",
+            },
+            "expected": ["mixedcaselabel"],
+            "output": load_file("v2_out_05.json")
+        },
+        {
+            # Query item with section by lowercase id (case matching)
+            "vault_name": "Test Vault",
+            "queries": ["LabelCasing"],
+            "kwargs": {
+                "field": "sectionlowercaseid",
+                "section": "section-with-values",
+            },
+            "expected": ["sectionlowercaseid"],
+            "output": load_file("v2_out_05.json")
+        },
+        {
+            # Query item with section by lowercase id (case not matching)
+            "vault_name": "Test Vault",
+            "queries": ["LabelCasing"],
+            "kwargs": {
+                "field": "SECTIONLOWERCASEID",
+                "section": "section-with-values",
+            },
+            "expected": ["sectionlowercaseid"],
+            "output": load_file("v2_out_05.json")
+        },
+        {
+            # Query item with section by lowercase label (case matching)
+            "vault_name": "Test Vault",
+            "queries": ["LabelCasing"],
+            "kwargs": {
+                "field": "sectionlowercaselabel",
+                "section": "section-with-values",
+            },
+            "expected": ["sectionlowercaselabel"],
+            "output": load_file("v2_out_05.json")
+        },
+        {
+            # Query item with section by lowercase label (case not matching)
+            "vault_name": "Test Vault",
+            "queries": ["LabelCasing"],
+            "kwargs": {
+                "field": "SECTIONLOWERCASELABEL",
+                "section": "section-with-values",
+            },
+            "expected": ["sectionlowercaselabel"],
+            "output": load_file("v2_out_05.json")
+        },
+        {
+            # Query item with section by lowercase id (case matching)
+            "vault_name": "Test Vault",
+            "queries": ["LabelCasing"],
+            "kwargs": {
+                "field": "SeCtIoNmIxEdCaSeId",
+                "section": "section-with-values",
+            },
+            "expected": ["sectionmixedcaseid"],
+            "output": load_file("v2_out_05.json")
+        },
+        {
+            # Query item with section by lowercase id (case not matching)
+            "vault_name": "Test Vault",
+            "queries": ["LabelCasing"],
+            "kwargs": {
+                "field": "sectionmixedcaseid",
+                "section": "section-with-values",
+            },
+            "expected": ["sectionmixedcaseid"],
+            "output": load_file("v2_out_05.json")
+        },
+        {
+            # Query item with section by lowercase label (case matching)
+            "vault_name": "Test Vault",
+            "queries": ["LabelCasing"],
+            "kwargs": {
+                "field": "SeCtIoNmIxEdCaSeLaBeL",
+                "section": "section-with-values",
+            },
+            "expected": ["sectionmixedcaselabel"],
+            "output": load_file("v2_out_05.json")
+        },
+        {
+            # Query item with section by lowercase label (case not matching)
+            "vault_name": "Test Vault",
+            "queries": ["LabelCasing"],
+            "kwargs": {
+                "field": "sectionmixedcaselabel",
+                "section": "section-with-values",
+            },
+            "expected": ["sectionmixedcaselabel"],
+            "output": load_file("v2_out_05.json")
+        },
     ],
 }
diff --git a/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_05.json b/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_05.json
new file mode 100644
--- /dev/null
+++ b/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_05.json
@@ -0,0 +1,102 @@
+{
+  "id": "bgqegp3xcxnpfkb45olwigpkpi",
+  "title": "LabelCasing",
+  "version": 1,
+  "vault": {
+    "id": "stpebbaccrq72xulgouxsk4p7y",
+    "name": "Private"
+  },
+  "category": "LOGIN",
+  "last_edited_by": "WOUTERRUYBH7BFPHMZ2KKGL6AU",
+  "created_at": "2023-09-12T08:30:07Z",
+  "updated_at": "2023-09-12T08:30:07Z",
+  "additional_information": "fluxility",
+  "sections": [
+    {
+      "id": "7osqcvd43i75teocdzbb6d7mie",
+      "label": "section-with-values"
+    }
+  ],
+  "fields": [
+    {
+      "id": "lowercaseid",
+      "type": "STRING",
+      "purpose": "USERNAME",
+      "label": "label0",
+      "value": "lowercaseid",
+      "reference": "op://Testcase/LabelCasing/lowercase"
+    },
+    {
+      "id": "MiXeDcAsEiD",
+      "type": "STRING",
+      "purpose": "USERNAME",
+      "label": "label1",
+      "value": "mixedcaseid",
+      "reference": "op://Testcase/LabelCasing/lowercase"
+    },
+    {
+      "id": "id1",
+      "type": "STRING",
+      "purpose": "USERNAME",
+      "label": "lowercaselabel",
+      "value": "lowercaselabel",
+      "reference": "op://Testcase/LabelCasing/lowercase"
+    },
+    {
+      "id": "id2",
+      "type": "STRING",
+      "purpose": "USERNAME",
+      "label": "MiXeDcAsElAbEl",
+      "value": "mixedcaselabel",
+      "reference": "op://Testcase/LabelCasing/lowercase"
+    },
+    {
+      "id": "sectionlowercaseid",
+      "type": "STRING",
+      "purpose": "USERNAME",
+      "label": "label2",
+      "value": "sectionlowercaseid",
+      "reference": "op://Testcase/LabelCasing/lowercase",
+      "section": {
+        "id": "7osqcvd43i75teocdzbb6d7mie",
+        "label": "section-with-values"
+      }
+    },
+    {
+      "id": "SeCtIoNmIxEdCaSeId",
+      "type": "STRING",
+      "purpose": "USERNAME",
+      "label": "label3",
+      "value": "sectionmixedcaseid",
+      "reference": "op://Testcase/LabelCasing/lowercase",
+      "section": {
+        "id": "7osqcvd43i75teocdzbb6d7mie",
+        "label": "section-with-values"
+      }
+    },
+    {
+      "id": "id3",
+      "type": "STRING",
+      "purpose": "USERNAME",
+      "label": "sectionlowercaselabel",
+      "value": "sectionlowercaselabel",
+      "reference": "op://Testcase/LabelCasing/lowercase",
+      "section": {
+        "id": "7osqcvd43i75teocdzbb6d7mie",
+        "label": "section-with-values"
+      }
+    },
+    {
+      "id": "id2",
+      "type": "STRING",
+      "purpose": "USERNAME",
+      "label": "SeCtIoNmIxEdCaSeLaBeL",
+      "value": "sectionmixedcaselabel",
+      "reference": "op://Testcase/LabelCasing/lowercase",
+      "section": {
+        "id": "7osqcvd43i75teocdzbb6d7mie",
+        "label": "section-with-values"
+      }
+    }
+  ]
+}
diff --git a/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_05.json.license b/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_05.json.license
new file mode 100644
--- /dev/null
+++ b/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_05.json.license
@@ -0,0 +1,3 @@
+GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+SPDX-License-Identifier: GPL-3.0-or-later
+SPDX-FileCopyrightText: 2022, Ansible Project