Skip to content

Commit fc2798f

Browse files
committed
fix: make secret helper more user friendly
1 parent b04dd2c commit fc2798f

File tree

3 files changed

+63
-21
lines changed

3 files changed

+63
-21
lines changed

docs/cli-usage.rst

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ example:
4848
4949
[elsewhere]
5050
url = http://else.whe.re:8080
51-
private_token = lookup: pass show path/to/password | head -n1
51+
private_token = helper: path/to/helper.sh
5252
timeout = 1
5353
5454
The ``default`` option of the ``[global]`` section defines the GitLab server to
@@ -119,6 +119,27 @@ server, with very limited permissions.
119119
* - ``http_password``
120120
- Password for optional HTTP authentication
121121

122+
For all settings, which contain secrets (``http_password``,
123+
``personal_token``, ``oauth_token``, ``job_token``), you can specify
124+
a helper program to retrieve the secret indicated by ``helper:``
125+
prefix. You can only specify a path to a program without any
126+
parameters. It is expected, that the program prints the secret to
127+
standard output.
128+
129+
Example for a `keyring <https://github.com/jaraco/keyring>`_ helper:
130+
131+
.. code-block:: bash
132+
133+
#!/bin/bash
134+
keyring get Service Username
135+
136+
Example for a `pass <https://www.passwordstore.org>`_ helper:
137+
138+
.. code-block:: bash
139+
140+
#!/bin/bash
141+
pass show path/to/password | head -n 1
142+
122143
CLI
123144
===
124145

gitlab/config.py

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ def _env_config() -> List[str]:
3434
os.path.expanduser("~/.python-gitlab.cfg"),
3535
]
3636

37+
HELPER_PREFIX = "helper:"
38+
39+
HELPER_ATTRIBUTES = [
40+
"job_token", "http_password", "private_token", "oauth_token"
41+
]
3742

3843
class ConfigError(Exception):
3944
pass
@@ -151,15 +156,7 @@ def __init__(
151156
except Exception:
152157
pass
153158

154-
for attr in ("job_token", "http_password", "private_token", "oauth_token"):
155-
value = getattr(self, attr)
156-
prefix = "lookup:"
157-
if isinstance(value, str) and value.lower().strip().startswith(prefix):
158-
helper = value[len(prefix) :].strip()
159-
value = (
160-
subprocess.check_output(helper, shell=True).decode("utf-8").strip()
161-
)
162-
setattr(self, attr, value)
159+
self._get_values_from_helper()
163160

164161
self.api_version = "4"
165162
try:
@@ -203,3 +200,13 @@ def __init__(
203200
self.user_agent = self._config.get(self.gitlab_id, "user_agent")
204201
except Exception:
205202
pass
203+
204+
def _get_values_from_helper(self):
205+
"""Update attributes, which may get values from an external helper program
206+
"""
207+
for attr in HELPER_ATTRIBUTES:
208+
value = getattr(self, attr)
209+
if isinstance(value, str) and value.lower().strip().startswith(HELPER_PREFIX):
210+
helper = value[len(HELPER_PREFIX) :].strip()
211+
value = subprocess.check_output([helper]).decode("utf-8").strip()
212+
setattr(self, attr, value)

gitlab/tests/test_config.py

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import os
1919
import unittest
20+
from textwrap import dedent
2021

2122
import mock
2223
import io
@@ -51,10 +52,6 @@
5152
[four]
5253
url = https://four.url
5354
oauth_token = STUV
54-
55-
[five]
56-
url = https://five.url
57-
oauth_token = lookup: echo "foobar"
5855
"""
5956

6057
custom_user_agent_config = """[global]
@@ -196,16 +193,33 @@ def test_valid_data(m_open, path_exists):
196193
assert 2 == cp.timeout
197194
assert True == cp.ssl_verify
198195

199-
fd = io.StringIO(valid_config)
196+
197+
@mock.patch("os.path.exists")
198+
@mock.patch("builtins.open")
199+
def test_data_from_helper(m_open, path_exists, tmp_path):
200+
helper = (tmp_path / "helper.sh")
201+
helper.write_text(dedent("""\
202+
#!/bin/sh
203+
echo "secret"
204+
"""))
205+
helper.chmod(0o755)
206+
207+
fd = io.StringIO(dedent("""\
208+
[global]
209+
default = helper
210+
211+
[helper]
212+
url = https://helper.url
213+
oauth_token = helper: %s
214+
""") % helper)
215+
200216
fd.close = mock.Mock(return_value=None)
201217
m_open.return_value = fd
202-
cp = config.GitlabConfigParser(gitlab_id="five")
203-
assert "five" == cp.gitlab_id
204-
assert "https://five.url" == cp.url
218+
cp = config.GitlabConfigParser(gitlab_id="helper")
219+
assert "helper" == cp.gitlab_id
220+
assert "https://helper.url" == cp.url
205221
assert None == cp.private_token
206-
assert "foobar" == cp.oauth_token
207-
assert 2 == cp.timeout
208-
assert True == cp.ssl_verify
222+
assert "secret" == cp.oauth_token
209223

210224

211225
@mock.patch("os.path.exists")

0 commit comments

Comments
 (0)