Skip to content

Commit 91ffb8e

Browse files
klorenznejch
authored andcommitted
chore(config): allow simple commands without external script
1 parent 7a7c9fd commit 91ffb8e

File tree

2 files changed

+67
-16
lines changed

2 files changed

+67
-16
lines changed

docs/cli-usage.rst

+40-10
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ Only one of ``private_token``, ``oauth_token`` or ``job_token`` should be
9393
defined. If neither are defined an anonymous request will be sent to the Gitlab
9494
server, with very limited permissions.
9595

96+
We recommend that you use `Credential helpers`_ to securely store your tokens.
97+
9698
.. list-table:: GitLab server options
9799
:header-rows: 1
98100

@@ -119,22 +121,50 @@ server, with very limited permissions.
119121
* - ``http_password``
120122
- Password for optional HTTP authentication
121123

122-
For all settings, which contain secrets (``http_password``,
124+
125+
Credential helpers
126+
------------------
127+
128+
For all configuration options that contain secrets (``http_password``,
123129
``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. You may use ``~`` for expanding your homedir in helper
127-
program's path. It is expected, that the program prints the secret
128-
to standard output.
130+
a helper program to retrieve the secret indicated by a ``helper:``
131+
prefix. This allows you to fetch values from a local keyring store
132+
or cloud-hosted vaults such as Bitwarden. Environment variables are
133+
expanded if they exist and ``~`` expands to your home directory.
134+
135+
It is expected that the helper program prints the secret to standard output.
136+
To use shell features such as piping to retrieve the value, you will need
137+
to use a wrapper script; see below.
129138

130139
Example for a `keyring <https://github.com/jaraco/keyring>`_ helper:
131140

132-
.. code-block:: bash
141+
.. code-block:: ini
133142
134-
#!/bin/bash
135-
keyring get Service Username
143+
[global]
144+
default = somewhere
145+
ssl_verify = true
146+
timeout = 5
147+
148+
[somewhere]
149+
url = http://somewhe.re
150+
private_token = helper: keyring get Service Username
151+
timeout = 1
152+
153+
Example for a `pass <https://www.passwordstore.org>`_ helper with a wrapper script:
154+
155+
.. code-block:: ini
156+
157+
[global]
158+
default = somewhere
159+
ssl_verify = true
160+
timeout = 5
161+
162+
[somewhere]
163+
url = http://somewhe.re
164+
private_token = helper: /path/to/helper.sh
165+
timeout = 1
136166
137-
Example for a `pass <https://www.passwordstore.org>`_ helper:
167+
In `/path/to/helper.sh`:
138168

139169
.. code-block:: bash
140170

gitlab/config.py

+27-6
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@
1717

1818
import os
1919
import configparser
20+
import shlex
2021
import subprocess
2122
from typing import List, Optional, Union
22-
from os.path import expanduser
23+
from os.path import expanduser, expandvars
2324

2425
from gitlab.const import USER_AGENT
2526

@@ -56,6 +57,10 @@ class GitlabConfigMissingError(ConfigError):
5657
pass
5758

5859

60+
class GitlabConfigHelperError(ConfigError):
61+
pass
62+
63+
5964
class GitlabConfigParser(object):
6065
def __init__(
6166
self, gitlab_id: Optional[str] = None, config_files: Optional[List[str]] = None
@@ -202,13 +207,29 @@ def __init__(
202207
pass
203208

204209
def _get_values_from_helper(self):
205-
"""Update attributes, which may get values from an external helper program"""
210+
"""Update attributes that may get values from an external helper program"""
206211
for attr in HELPER_ATTRIBUTES:
207212
value = getattr(self, attr)
208213
if not isinstance(value, str):
209214
continue
210215

211-
if value.lower().strip().startswith(HELPER_PREFIX):
212-
helper = expanduser(value[len(HELPER_PREFIX) :].strip())
213-
value = subprocess.check_output([helper]).decode("utf-8").strip()
214-
setattr(self, attr, value)
216+
if not value.lower().strip().startswith(HELPER_PREFIX):
217+
continue
218+
219+
helper = value[len(HELPER_PREFIX) :].strip()
220+
commmand = [expanduser(expandvars(token)) for token in shlex.split(helper)]
221+
222+
try:
223+
value = (
224+
subprocess.check_output(commmand, stderr=subprocess.PIPE)
225+
.decode("utf-8")
226+
.strip()
227+
)
228+
except subprocess.CalledProcessError as e:
229+
stderr = e.stderr.decode().strip()
230+
raise GitlabConfigHelperError(
231+
f"Failed to read {attr} value from helper "
232+
f"for {self.gitlab_id}:\n{stderr}"
233+
) from e
234+
235+
setattr(self, attr, value)

0 commit comments

Comments
 (0)