summaryrefslogtreecommitdiff
path: root/gitlab
diff options
context:
space:
mode:
authorNejc Habjan <hab.nejc@gmail.com>2021-04-18 12:30:03 +0200
committerGitHub <noreply@github.com>2021-04-18 12:30:03 +0200
commitaf781c10db3829163f977e494e4008acf2096d64 (patch)
treeae515156fdad0f28d31ac4bfd8e8bf038d888e8c /gitlab
parentd2362676d97633893aea27f878773e5fa009976f (diff)
parent91ffb8e97e213d2f14340b952630875995ecedb2 (diff)
downloadgitlab-af781c10db3829163f977e494e4008acf2096d64.tar.gz
Merge pull request #1359 from klorenz/feat_token_lookup
feat(config): allow using a credential helper to lookup tokens
Diffstat (limited to 'gitlab')
-rw-r--r--gitlab/config.py41
-rw-r--r--gitlab/tests/test_config.py38
2 files changed, 79 insertions, 0 deletions
diff --git a/gitlab/config.py b/gitlab/config.py
index 710a354..c663bf8 100644
--- a/gitlab/config.py
+++ b/gitlab/config.py
@@ -17,7 +17,10 @@
import os
import configparser
+import shlex
+import subprocess
from typing import List, Optional, Union
+from os.path import expanduser, expandvars
from gitlab.const import USER_AGENT
@@ -33,6 +36,10 @@ _DEFAULT_FILES: List[str] = _env_config() + [
os.path.expanduser("~/.python-gitlab.cfg"),
]
+HELPER_PREFIX = "helper:"
+
+HELPER_ATTRIBUTES = ["job_token", "http_password", "private_token", "oauth_token"]
+
class ConfigError(Exception):
pass
@@ -50,6 +57,10 @@ class GitlabConfigMissingError(ConfigError):
pass
+class GitlabConfigHelperError(ConfigError):
+ pass
+
+
class GitlabConfigParser(object):
def __init__(
self, gitlab_id: Optional[str] = None, config_files: Optional[List[str]] = None
@@ -150,6 +161,8 @@ class GitlabConfigParser(object):
except Exception:
pass
+ self._get_values_from_helper()
+
self.api_version = "4"
try:
self.api_version = self._config.get("global", "api_version")
@@ -192,3 +205,31 @@ class GitlabConfigParser(object):
self.user_agent = self._config.get(self.gitlab_id, "user_agent")
except Exception:
pass
+
+ def _get_values_from_helper(self):
+ """Update attributes that may get values from an external helper program"""
+ for attr in HELPER_ATTRIBUTES:
+ value = getattr(self, attr)
+ if not isinstance(value, str):
+ continue
+
+ if not value.lower().strip().startswith(HELPER_PREFIX):
+ continue
+
+ helper = value[len(HELPER_PREFIX) :].strip()
+ commmand = [expanduser(expandvars(token)) for token in shlex.split(helper)]
+
+ try:
+ value = (
+ subprocess.check_output(commmand, stderr=subprocess.PIPE)
+ .decode("utf-8")
+ .strip()
+ )
+ except subprocess.CalledProcessError as e:
+ stderr = e.stderr.decode().strip()
+ raise GitlabConfigHelperError(
+ f"Failed to read {attr} value from helper "
+ f"for {self.gitlab_id}:\n{stderr}"
+ ) from e
+
+ setattr(self, attr, value)
diff --git a/gitlab/tests/test_config.py b/gitlab/tests/test_config.py
index 7a9e239..58ccbb0 100644
--- a/gitlab/tests/test_config.py
+++ b/gitlab/tests/test_config.py
@@ -17,6 +17,7 @@
import os
import unittest
+from textwrap import dedent
import mock
import io
@@ -195,6 +196,43 @@ def test_valid_data(m_open, path_exists):
@mock.patch("os.path.exists")
@mock.patch("builtins.open")
+def test_data_from_helper(m_open, path_exists, tmp_path):
+ helper = tmp_path / "helper.sh"
+ helper.write_text(
+ dedent(
+ """\
+ #!/bin/sh
+ echo "secret"
+ """
+ )
+ )
+ helper.chmod(0o755)
+
+ fd = io.StringIO(
+ dedent(
+ """\
+ [global]
+ default = helper
+
+ [helper]
+ url = https://helper.url
+ oauth_token = helper: %s
+ """
+ )
+ % helper
+ )
+
+ fd.close = mock.Mock(return_value=None)
+ m_open.return_value = fd
+ cp = config.GitlabConfigParser(gitlab_id="helper")
+ assert "helper" == cp.gitlab_id
+ assert "https://helper.url" == cp.url
+ assert None == cp.private_token
+ assert "secret" == cp.oauth_token
+
+
+@mock.patch("os.path.exists")
+@mock.patch("builtins.open")
@pytest.mark.parametrize(
"config_string,expected_agent",
[