Pārlūkot izejas kodu

Add function to get credentials from the Git credentials store.

Manuel Jacob 5 gadi atpakaļ
vecāks
revīzija
7697347217
2 mainītis faili ar 71 papildinājumiem un 0 dzēšanām
  1. 26 0
      dulwich/client.py
  2. 45 0
      dulwich/tests/test_client.py

+ 26 - 0
dulwich/client.py

@@ -40,6 +40,8 @@ Known capabilities that are not supported:
 
 from contextlib import closing
 from io import BytesIO, BufferedReader
+import errno
+import os
 import select
 import socket
 import subprocess
@@ -58,6 +60,7 @@ except ImportError:
     import urllib.parse as urlparse
 
 import dulwich
+from dulwich.config import get_xdg_config_home_path
 from dulwich.errors import (
     GitProtocolError,
     NotGitRepository,
@@ -1868,3 +1871,26 @@ def get_transport_and_path(location, **kwargs):
         return default_local_git_client_cls(**kwargs), location
     else:
         return SSHGitClient(hostname, username=username, **kwargs), path
+
+
+DEFAULT_GIT_CREDENTIALS_PATHS = [
+    os.path.expanduser('~/.git-credentials'),
+    get_xdg_config_home_path('git', 'credentials')]
+
+def get_credentials_from_store(scheme, hostname, username=None,
+                               fnames=DEFAULT_GIT_CREDENTIALS_PATHS):
+    for fname in fnames:
+        try:
+            with open(fname, 'rb') as f:
+                for line in f:
+                    parsed_line = urlparse.urlparse(line)
+                    if (parsed_line.scheme == scheme and
+                            parsed_line.hostname == hostname and
+                            (username is None or
+                                parsed_line.username == username)):
+                        return parsed_line.username, parsed_line.password
+        except OSError as e:
+            if e.errno != errno.ENOENT:
+                raise
+            # If the file doesn't exist, try the next one.
+            continue

+ 45 - 0
dulwich/tests/test_client.py

@@ -20,6 +20,7 @@
 
 from io import BytesIO
 import base64
+import os
 import sys
 import shutil
 import tempfile
@@ -57,6 +58,7 @@ from dulwich.client import (
     UpdateRefsError,
     check_wants,
     default_urllib3_manager,
+    get_credentials_from_store,
     get_transport_and_path,
     get_transport_and_path_from_url,
     parse_rsync_url,
@@ -1293,3 +1295,46 @@ class FetchPackResultTests(TestCase):
                 {b'refs/heads/master':
                  b'2f3dc7a53fb752a6961d3a56683df46d4d3bf262'}, {},
                 b'user/agent'))
+
+
+class GitCredentialStoreTests(TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        with tempfile.NamedTemporaryFile(delete=False) as f:
+            f.write(b'https://user:pass@example.org')
+        cls.fname = f.name
+
+    @classmethod
+    def tearDownClass(cls):
+        os.unlink(cls.fname)
+
+    def test_nonmatching_scheme(self):
+        self.assertEqual(
+            get_credentials_from_store(
+                b'http', b'example.org', fnames=[self.fname]),
+            None)
+
+    def test_nonmatching_hostname(self):
+        self.assertEqual(
+            get_credentials_from_store(
+                b'https', b'noentry.org', fnames=[self.fname]),
+            None)
+
+    def test_match_without_username(self):
+        self.assertEqual(
+            get_credentials_from_store(
+                b'https', b'example.org', fnames=[self.fname]),
+            (b'user', b'pass'))
+
+    def test_match_with_matching_username(self):
+        self.assertEqual(
+            get_credentials_from_store(
+                b'https', b'example.org', b'user', fnames=[self.fname]),
+            (b'user', b'pass'))
+
+    def test_no_match_with_nonmatching_username(self):
+        self.assertEqual(
+            get_credentials_from_store(
+                b'https', b'example.org', b'otheruser', fnames=[self.fname]),
+            None)