Repository: dynaconf/dynaconf

Issue:
[bug] Get Vault Secret via dynaconf throws `permission denied` error
**Describe the bug**
Accessing Vault secrets via dynaconf throws a` permission denied` error even though the user/role has access to the secrets and its path.

**To Reproduce**
Steps to reproduce the behaviour:

1. Having the following folder structure in the Vault


<details>
<summary> Vault Store </summary>

```
/apps/mna-satqe-hashicorp/robottelo
```

The secrets are stored in the above path in Corporate Vault.
</details>

2. Having the following app code:

<details>
<summary> Code </summary>

**config/__init__.py**
```python
from dynaconf import LazySettings

os.environ["VAULT_KV_VERSION_FOR_DYNACONF"] = "1"
os.environ['VAULT_ROLE_ID_FOR_DYNACONF'] = 'mna-satqe-hashicorp'
os.environ["VAULT_SECRET_ID_FOR_DYNACONF"] = "abcd-323333-23232323-sdsfsf"
os.environ["VAULT_MOUNT_POINT_FOR_DYNACONF"] = "apps"
os.environ["VAULT_PATH_FOR_DYNACONF"] = "mna-satqe-hashicorp/robottelo"

settingsd = LazySettings(environments=True, vault_enabled=True, vault={'url': 'https://vault.corp.compony.com:8200', 'verify': '/etc/ipa/ipa.crt'})

print(settings.ad_password)
```

</details>

4. Executing under the following environment

<details>
<summary> Execution </summary>

```bash
$ pytest -v -s tests/foreman/api/test_product.py -k test_positive_create_with_name
Traceback (most recent call last):
.
.
.
.
 File "/Users/jitendrayejare/JWorkspace/Envs/rtello/lib/python3.9/site-packages/dynaconf/base.py", line 113, in __getattr__
    self._setup()
  File "/Users/jitendrayejare/JWorkspace/Envs/rtello/lib/python3.9/site-packages/dynaconf/base.py", line 163, in _setup
    self._wrapped = Settings(
  File "/Users/jitendrayejare/JWorkspace/Envs/rtello/lib/python3.9/site-packages/dynaconf/base.py", line 233, in __init__
    self.execute_loaders()
  File "/Users/jitendrayejare/JWorkspace/Envs/rtello/lib/python3.9/site-packages/dynaconf/base.py", line 977, in execute_loaders
    loader.load(self, env, silent=silent, key=key)
  File "/Users/jitendrayejare/JWorkspace/Envs/rtello/lib/python3.9/site-packages/dynaconf/loaders/vault_loader.py", line 74, in load
    dirs = client.secrets.kv.list_secrets(
  File "/Users/jitendrayejare/JWorkspace/Envs/rtello/lib/python3.9/site-packages/hvac/api/secrets_engines/kv_v1.py", line 58, in list_secrets
    return self._adapter.list(
  File "/Users/jitendrayejare/JWorkspace/Envs/rtello/lib/python3.9/site-packages/hvac/adapters.py", line 165, in list
    return self.request("list", url, **kwargs)
  File "/Users/jitendrayejare/JWorkspace/Envs/rtello/lib/python3.9/site-packages/hvac/adapters.py", line 364, in request
    response = super(JSONAdapter, self).request(*args, **kwargs)
  File "/Users/jitendrayejare/JWorkspace/Envs/rtello/lib/python3.9/site-packages/hvac/adapters.py", line 330, in request
    utils.raise_for_error(
  File "/Users/jitendrayejare/JWorkspace/Envs/rtello/lib/python3.9/site-packages/hvac/utils.py", line 41, in raise_for_error
    raise exceptions.Forbidden(message, errors=errors, method=method, url=url)
hvac.exceptions.Forbidden: 1 error occurred:
	* permission denied

, on list https://vault.corp.company.com:8200/v1/apps/mna-satqe-hashicorp/robottelo

```

</details>

**Expected behaviour**
DynaConf should be able to access the secret successfully and print in my case above.

**Environment (please complete the following information):**
 - OS: [MacOS 12.1]
 - Dynaconf Version [3.1.7]
 - Frameworks in use (pytest)

**Additional context**
The curl command to access the secrets from the same path works as expected.
```
# curl --header "X-Vault-Token: s.saswewetergfd3333dddd" https://vault.corp.company.com:8200/v1/apps/data/mna-satqe-hashicorp/robottelo
{"request_id":"a75e5b8a-c958-8608-dad0-85f516c62c69","lease_id":"","renewable":false,"lease_duration":0,"data":{"data":{"ad_password":"SecretPAss3456"}, "metadata":{"created_time":"2022-02-25T11:51:47.258977787Z","deletion_time":"","destroyed":false,"version":1}},"wrap_info":null,"warnings":null,"auth":null}
```

[bug] Get Vault Secret via dynaconf throws `permission denied` error
**Describe the bug**
Accessing Vault secrets via dynaconf throws a` permission denied` error even though the user/role has access to the secrets and its path.

**To Reproduce**
Steps to reproduce the behaviour:

1. Having the following folder structure in the Vault


<details>
<summary> Vault Store </summary>

```
/apps/mna-satqe-hashicorp/robottelo
```

The secrets are stored in the above path in Corporate Vault.
</details>

2. Having the following app code:

<details>
<summary> Code </summary>

**config/__init__.py**
```python
from dynaconf import LazySettings

os.environ["VAULT_KV_VERSION_FOR_DYNACONF"] = "1"
os.environ['VAULT_ROLE_ID_FOR_DYNACONF'] = 'mna-satqe-hashicorp'
os.environ["VAULT_SECRET_ID_FOR_DYNACONF"] = "abcd-323333-23232323-sdsfsf"
os.environ["VAULT_MOUNT_POINT_FOR_DYNACONF"] = "apps"
os.environ["VAULT_PATH_FOR_DYNACONF"] = "mna-satqe-hashicorp/robottelo"

settingsd = LazySettings(environments=True, vault_enabled=True, vault={'url': 'https://vault.corp.compony.com:8200', 'verify': '/etc/ipa/ipa.crt'})

print(settings.ad_password)
```

</details>

4. Executing under the following environment

<details>
<summary> Execution </summary>

```bash
$ pytest -v -s tests/foreman/api/test_product.py -k test_positive_create_with_name
Traceback (most recent call last):
.
.
.
.
 File "/Users/jitendrayejare/JWorkspace/Envs/rtello/lib/python3.9/site-packages/dynaconf/base.py", line 113, in __getattr__
    self._setup()
  File "/Users/jitendrayejare/JWorkspace/Envs/rtello/lib/python3.9/site-packages/dynaconf/base.py", line 163, in _setup
    self._wrapped = Settings(
  File "/Users/jitendrayejare/JWorkspace/Envs/rtello/lib/python3.9/site-packages/dynaconf/base.py", line 233, in __init__
    self.execute_loaders()
  File "/Users/jitendrayejare/JWorkspace/Envs/rtello/lib/python3.9/site-packages/dynaconf/base.py", line 977, in execute_loaders
    loader.load(self, env, silent=silent, key=key)
  File "/Users/jitendrayejare/JWorkspace/Envs/rtello/lib/python3.9/site-packages/dynaconf/loaders/vault_loader.py", line 74, in load
    dirs = client.secrets.kv.list_secrets(
  File "/Users/jitendrayejare/JWorkspace/Envs/rtello/lib/python3.9/site-packages/hvac/api/secrets_engines/kv_v1.py", line 58, in list_secrets
    return self._adapter.list(
  File "/Users/jitendrayejare/JWorkspace/Envs/rtello/lib/python3.9/site-packages/hvac/adapters.py", line 165, in list
    return self.request("list", url, **kwargs)
  File "/Users/jitendrayejare/JWorkspace/Envs/rtello/lib/python3.9/site-packages/hvac/adapters.py", line 364, in request
    response = super(JSONAdapter, self).request(*args, **kwargs)
  File "/Users/jitendrayejare/JWorkspace/Envs/rtello/lib/python3.9/site-packages/hvac/adapters.py", line 330, in request
    utils.raise_for_error(
  File "/Users/jitendrayejare/JWorkspace/Envs/rtello/lib/python3.9/site-packages/hvac/utils.py", line 41, in raise_for_error
    raise exceptions.Forbidden(message, errors=errors, method=method, url=url)
hvac.exceptions.Forbidden: 1 error occurred:
	* permission denied

, on list https://vault.corp.company.com:8200/v1/apps/mna-satqe-hashicorp/robottelo

```

</details>

**Expected behaviour**
DynaConf should be able to access the secret successfully and print in my case above.

**Environment (please complete the following information):**
 - OS: [MacOS 12.1]
 - Dynaconf Version [3.1.7]
 - Frameworks in use (pytest)

**Additional context**
The curl command to access the secrets from the same path works as expected.
```
# curl --header "X-Vault-Token: s.saswewetergfd3333dddd" https://vault.corp.company.com:8200/v1/apps/data/mna-satqe-hashicorp/robottelo
{"request_id":"a75e5b8a-c958-8608-dad0-85f516c62c69","lease_id":"","renewable":false,"lease_duration":0,"data":{"data":{"ad_password":"SecretPAss3456"}, "metadata":{"created_time":"2022-02-25T11:51:47.258977787Z","deletion_time":"","destroyed":false,"version":1}},"wrap_info":null,"warnings":null,"auth":null}
```



Discussion:

Resolving patch:
diff --git a/dynaconf/base.py b/dynaconf/base.py
--- a/dynaconf/base.py
+++ b/dynaconf/base.py
@@ -646,7 +646,7 @@ def current_env(self):
         """Return the current active env"""
 
         if self.ENVIRONMENTS_FOR_DYNACONF is False:
-            return "main"
+            return self.MAIN_ENV_FOR_DYNACONF.lower()
 
         if self.FORCE_ENV_FOR_DYNACONF is not None:
             return self.FORCE_ENV_FOR_DYNACONF
diff --git a/dynaconf/default_settings.py b/dynaconf/default_settings.py
--- a/dynaconf/default_settings.py
+++ b/dynaconf/default_settings.py
@@ -86,6 +86,7 @@ def reload(load_dotenv=None, *args, **kwargs):
 
 # If provided environments will be loaded separatelly
 ENVIRONMENTS_FOR_DYNACONF = get("ENVIRONMENTS_FOR_DYNACONF", False)
+MAIN_ENV_FOR_DYNACONF = get("MAIN_ENV_FOR_DYNACONF", "MAIN")
 
 # If False dynaconf will allow access to first level settings only in upper
 LOWERCASE_READ_FOR_DYNACONF = get("LOWERCASE_READ_FOR_DYNACONF", True)
diff --git a/dynaconf/loaders/vault_loader.py b/dynaconf/loaders/vault_loader.py
--- a/dynaconf/loaders/vault_loader.py
+++ b/dynaconf/loaders/vault_loader.py
@@ -71,14 +71,27 @@ def load(obj, env=None, silent=None, key=None):
     """
     client = get_client(obj)
     try:
-        dirs = client.secrets.kv.list_secrets(
-            path=obj.VAULT_PATH_FOR_DYNACONF,
-            mount_point=obj.VAULT_MOUNT_POINT_FOR_DYNACONF,
-        )["data"]["keys"]
+        if obj.VAULT_KV_VERSION_FOR_DYNACONF == 2:
+            dirs = client.secrets.kv.v2.list_secrets(
+                path=obj.VAULT_PATH_FOR_DYNACONF,
+                mount_point=obj.VAULT_MOUNT_POINT_FOR_DYNACONF,
+            )["data"]["keys"]
+        else:
+            dirs = client.secrets.kv.v1.list_secrets(
+                path=obj.VAULT_PATH_FOR_DYNACONF,
+                mount_point=obj.VAULT_MOUNT_POINT_FOR_DYNACONF,
+            )["data"]["keys"]
     except InvalidPath:
         # The given path is not a directory
         dirs = []
-    env_list = dirs + build_env_list(obj, env)
+    # First look for secrets into environments less store
+    if not obj.ENVIRONMENTS_FOR_DYNACONF:
+        # By adding '', dynaconf will now read secrets from environments-less
+        # store which are not written by `dynaconf write` to Vault store
+        env_list = [obj.MAIN_ENV_FOR_DYNACONF.lower(), ""]
+    # Finally, look for secret into all the environments
+    else:
+        env_list = dirs + build_env_list(obj, env)
     for env in env_list:
         path = "/".join([obj.VAULT_PATH_FOR_DYNACONF, env])
         try:
@@ -99,7 +112,11 @@ def load(obj, env=None, silent=None, key=None):
             # extract the inner data
             data = data.get("data", {}).get("data", {})
         try:
-            if obj.VAULT_KV_VERSION_FOR_DYNACONF == 2 and data:
+            if (
+                obj.VAULT_KV_VERSION_FOR_DYNACONF == 2
+                and obj.ENVIRONMENTS_FOR_DYNACONF
+                and data
+            ):
                 data = data.get("data", {})
             if data and key:
                 value = parse_conf_data(


Test patch: