Repository: ansible-collections/community.general

Issue:
lookup bitwarden field=notes resulting in KeyError
### Summary

When using the community.general.bitwarden lookup there is an error trying to lookup a secure note.

Playbook:
```yaml
- debug:
    msg: >-
      "{{ lookup('community.general.bitwarden', 'secure note 2023', field='notes') }}"
```

Error:
```
fatal: [my.hostname.net]: FAILED! => {
    "msg": "An unhandled exception occurred while running the lookup plugin 'community.general.bitwarden'. Error was a <class 'KeyError'>, original message: 'fields'. 'fields'"
}
```
With debug:
```console
exception during Jinja2 execution: Traceback (most recent call last):
  File "/opt/homebrew/Cellar/ansible/8.0.0/libexec/lib/python3.11/site-packages/ansible/template/__init__.py", line 831, in _lookup
    ran = instance.run(loop_terms, variables=self._available_variables, **kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/ansible/8.0.0/libexec/lib/python3.11/site-packages/ansible_collections/community/general/plugins/lookup/bitwarden.py", line 159, in run
    return [_bitwarden.get_field(field, term, search_field, collection_id) for term in terms]
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/ansible/8.0.0/libexec/lib/python3.11/site-packages/ansible_collections/community/general/plugins/lookup/bitwarden.py", line 159, in <listcomp>
    return [_bitwarden.get_field(field, term, search_field, collection_id) for term in terms]
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/ansible/8.0.0/libexec/lib/python3.11/site-packages/ansible_collections/community/general/plugins/lookup/bitwarden.py", line 141, in get_field
    for custom_field in match['fields']:
                        ~~~~~^^^^^^^^^^
KeyError: 'fields'
fatal: [my.hostname.net]: FAILED! => {
    "msg": "An unhandled exception occurred while running the lookup plugin 'community.general.bitwarden'. Error was a <class 'KeyError'>, original message: 'fields'. 'fields'"
}
```

Same thing when trying to do the lookup by id:
```yaml
- debug:
    msg: >-
      "{{ lookup('community.general.bitwarden', 'ba9f1125-e52a-4d4f-9b2b-af1c43a00bd6', search='id', field='notes') }}"
```

### Issue Type

Bug Report

### Component Name

bitwarden

### Ansible Version

```console (paste below)
$ ansible --version
ansible [core 2.15.0]
  config file = /Users/sjoerd/.ansible.cfg
  configured module search path = ['/Users/name/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /opt/homebrew/Cellar/ansible/8.0.0/libexec/lib/python3.11/site-packages/ansible
  ansible collection location = /Users/name/.ansible/collections:/usr/share/ansible/collections
  executable location = /opt/homebrew/bin/ansible
  python version = 3.11.3 (main, Apr  7 2023, 20:13:31) [Clang 14.0.0 (clang-1400.0.29.202)] (/opt/homebrew/Cellar/ansible/8.0.0/libexec/bin/python3.11)
  jinja version = 3.1.2
  libyaml = True
```


### Community.general Version

```console (paste below)
$ ansible-galaxy collection list community.general

# /opt/homebrew/Cellar/ansible/8.0.0/libexec/lib/python3.11/site-packages/ansible_collections
Collection        Version
----------------- -------
community.general 7.0.1
```


### Configuration

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


### OS / Environment

Source: MacOS
Target: CentOS Stream 9


### Steps to Reproduce

<!--- Paste example playbooks or commands between quotes below -->
```yaml (paste below)
- debug:
    msg: >-
      "{{ lookup('community.general.bitwarden', 'secure note 2023', field='notes') }}"

```


### Expected Results

Expected the contents of the secure not to be printed to the terminal

### Actual Results

```console (paste below)
fatal: [my.hostname.net]: FAILED! => {
    "msg": "An unhandled exception occurred while running the lookup plugin 'community.general.bitwarden'. Error was a <class 'KeyError'>, original message: 'fields'. 'fields'"
}
```


### Code of Conduct

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


Discussion:
Files identified in the description:
* [`plugins/lookup/bitwarden.py`](https://github.com/ansible-collections/community.general/blob/main/plugins/lookup/bitwarden.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 --->
cc @lungj
[click here for bot help](https://github.com/ansible/ansibullbot/blob/devel/ISSUE_HELP.md)
<!--- boilerplate: notify --->
CC @reverendj1 who worked on this in #5694 and might have an idea what happens here. I guess the obvious fix is to check whether `'fields' in match` before looping over `match['fields']`, but maybe there's something more interesting/complex going on.
Playing with this, another branch for some of the main level keys fixes the original issue:

```
137a136,137
>         elif field in ['favorite', 'folderId', 'name', 'notes', 'organizationId', 'reprompt', 'revisionDate']:
>             return [match[field] for match in matches]
```

https://github.com/ansible-collections/community.general/pull/7061

Resolving patch:
diff --git a/plugins/lookup/bitwarden.py b/plugins/lookup/bitwarden.py
--- a/plugins/lookup/bitwarden.py
+++ b/plugins/lookup/bitwarden.py
@@ -132,20 +132,29 @@ def get_field(self, field, search_value, search_field="name", collection_id=None
         If field is None, return the whole record for each match.
         """
         matches = self._get_matches(search_value, search_field, collection_id)
-
-        if field in ['autofillOnPageLoad', 'password', 'passwordRevisionDate', 'totp', 'uris', 'username']:
-            return [match['login'][field] for match in matches]
-        elif not field:
+        if not field:
             return matches
-        else:
-            custom_field_matches = []
-            for match in matches:
+        field_matches = []
+        for match in matches:
+            # if there are no custom fields, then `match` has no key 'fields'
+            if 'fields' in match:
+                custom_field_found = False
                 for custom_field in match['fields']:
-                    if custom_field['name'] == field:
-                        custom_field_matches.append(custom_field['value'])
-            if matches and not custom_field_matches:
-                raise AnsibleError("Custom field {field} does not exist in {search_value}".format(field=field, search_value=search_value))
-            return custom_field_matches
+                    if field == custom_field['name']:
+                        field_matches.append(custom_field['value'])
+                        custom_field_found = True
+                        break
+                if custom_field_found:
+                    continue
+            if 'login' in match and field in match['login']:
+                field_matches.append(match['login'][field])
+                continue
+            if field in match:
+                field_matches.append(match[field])
+                continue
+        if matches and not field_matches:
+            raise AnsibleError("field {field} does not exist in {search_value}".format(field=field, search_value=search_value))
+        return field_matches
 
 
 class LookupModule(LookupBase):


Test patch: