File base64-padding-fix.patch of Package python-kubernetes.12964

From 9029dd735bdb3c2c5870925f69a7897b2b27dbd1 Mon Sep 17 00:00:00 2001
From: Can Bulut Bayburt <cbbayburt@suse.com>
Date: Wed, 2 Oct 2019 12:28:04 +0200
Subject: [PATCH] Backport base64 padding fix

---
 config/kube_config.py      |  21 ++++++++--
 config/kube_config_test.py | 101 +++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 114 insertions(+), 8 deletions(-)

diff --git kubernetes/config/kube_config.py kubernetes/config/kube_config.py
index 958959e..019c947 100644
--- kubernetes/config/kube_config.py
+++ kubernetes/config/kube_config.py
@@ -259,18 +259,31 @@ class KubeConfigLoader(object):
         if 'config' not in provider:
             return
 
-        parts = provider['config']['id-token'].split('.')
+        reserved_characters = frozenset(["=", "+", "/"])
+        token = provider['config']['id-token']
 
+        if any(char in token for char in reserved_characters):
+            # Invalid jwt, as it contains url-unsafe chars
+            return
+
+        parts = token.split('.')
         if len(parts) != 3:  # Not a valid JWT
-            return None
+            return
+
+        padding = (4 - len(parts[1]) % 4) * '='
+        if len(padding) == 3:
+            # According to spec, 3 padding characters cannot occur
+            # in a valid jwt
+            # https://tools.ietf.org/html/rfc7515#appendix-C
+            return
 
         if PY3:
             jwt_attributes = json.loads(
-                base64.b64decode(parts[1]).decode('utf-8')
+                base64.b64decode(parts[1] + padding).decode('utf-8')
             )
         else:
             jwt_attributes = json.loads(
-                base64.b64decode(parts[1] + "==")
+                base64.b64decode(parts[1] + padding)
             )
 
         expire = jwt_attributes.get('exp')
diff --git kubernetes/config/kube_config_test.py kubernetes/config/kube_config_test.py
index ae9dc22..0deec79 100644
--- kubernetes/config/kube_config_test.py
+++ kubernetes/config/kube_config_test.py
@@ -47,6 +47,10 @@ def _base64(string):
     return base64.standard_b64encode(string.encode()).decode()
 
 
+def _urlsafe_unpadded_b64encode(string):
+    return base64.urlsafe_b64encode(string.encode()).decode().rstrip('=')
+
+
 def _format_expiry_datetime(dt):
     return dt.strftime(EXPIRY_DATETIME_FORMAT)
 
@@ -94,12 +98,33 @@ TEST_CLIENT_CERT_BASE64 = _base64(TEST_CLIENT_CERT)
 
 TEST_OIDC_TOKEN = "test-oidc-token"
 TEST_OIDC_INFO = "{\"name\": \"test\"}"
-TEST_OIDC_BASE = _base64(TEST_OIDC_TOKEN) + "." + _base64(TEST_OIDC_INFO)
-TEST_OIDC_LOGIN = TEST_OIDC_BASE + "." + TEST_CLIENT_CERT_BASE64
+TEST_OIDC_BASE = ".".join([
+    _urlsafe_unpadded_b64encode(TEST_OIDC_TOKEN),
+    _urlsafe_unpadded_b64encode(TEST_OIDC_INFO)
+])
+TEST_OIDC_LOGIN = ".".join([
+    TEST_OIDC_BASE,
+    _urlsafe_unpadded_b64encode(TEST_CLIENT_CERT_BASE64)
+])
 TEST_OIDC_TOKEN = "Bearer %s" % TEST_OIDC_LOGIN
 TEST_OIDC_EXP = "{\"name\": \"test\",\"exp\": 536457600}"
-TEST_OIDC_EXP_BASE = _base64(TEST_OIDC_TOKEN) + "." + _base64(TEST_OIDC_EXP)
-TEST_OIDC_EXPIRED_LOGIN = TEST_OIDC_EXP_BASE + "." + TEST_CLIENT_CERT_BASE64
+TEST_OIDC_EXP_BASE = _urlsafe_unpadded_b64encode(
+    TEST_OIDC_TOKEN) + "." + _urlsafe_unpadded_b64encode(TEST_OIDC_EXP)
+TEST_OIDC_EXPIRED_LOGIN = ".".join([
+    TEST_OIDC_EXP_BASE,
+    _urlsafe_unpadded_b64encode(TEST_CLIENT_CERT)
+])
+TEST_OIDC_CONTAINS_RESERVED_CHARACTERS = ".".join([
+    _urlsafe_unpadded_b64encode(TEST_OIDC_TOKEN),
+    _urlsafe_unpadded_b64encode(TEST_OIDC_INFO).replace("a", "+"),
+    _urlsafe_unpadded_b64encode(TEST_CLIENT_CERT)
+])
+TEST_OIDC_INVALID_PADDING_LENGTH = ".".join([
+    _urlsafe_unpadded_b64encode(TEST_OIDC_TOKEN),
+    "aaaaa",
+    _urlsafe_unpadded_b64encode(TEST_CLIENT_CERT)
+])
+
 TEST_OIDC_CA = _base64(TEST_CERTIFICATE_AUTH)
 
 
@@ -406,6 +431,22 @@ class TestKubeConfigLoader(BaseTestCase):
                     "user": "expired_oidc_nocert"
                 }
             },
+            {
+                "name": "oidc_contains_reserved_character",
+                "context": {
+                    "cluster": "default",
+                    "user": "oidc_contains_reserved_character"
+
+                }
+            },
+            {
+                "name": "oidc_invalid_padding_length",
+                "context": {
+                    "cluster": "default",
+                    "user": "oidc_invalid_padding_length"
+
+                }
+            },
             {
                 "name": "user_pass",
                 "context": {
@@ -592,6 +633,38 @@ class TestKubeConfigLoader(BaseTestCase):
                     }
                 }
             },
+            {
+                "name": "oidc_contains_reserved_character",
+                "user": {
+                    "auth-provider": {
+                        "name": "oidc",
+                        "config": {
+                            "client-id": "tectonic-kubectl",
+                            "client-secret": "FAKE_SECRET",
+                            "id-token": TEST_OIDC_CONTAINS_RESERVED_CHARACTERS,
+                            "idp-issuer-url": "https://example.org/identity",
+                            "refresh-token":
+                                "lucWJjEhlxZW01cXI3YmVlcYnpxNGhzk"
+                        }
+                    }
+                }
+            },
+            {
+                "name": "oidc_invalid_padding_length",
+                "user": {
+                    "auth-provider": {
+                        "name": "oidc",
+                        "config": {
+                            "client-id": "tectonic-kubectl",
+                            "client-secret": "FAKE_SECRET",
+                            "id-token": TEST_OIDC_INVALID_PADDING_LENGTH,
+                            "idp-issuer-url": "https://example.org/identity",
+                            "refresh-token":
+                                "lucWJjEhlxZW01cXI3YmVlcYnpxNGhzk"
+                        }
+                    }
+                }
+            },
             {
                 "name": "user_pass",
                 "user": {
@@ -790,6 +863,26 @@ class TestKubeConfigLoader(BaseTestCase):
         self.assertTrue(loader._load_auth_provider_token())
         self.assertEqual("Bearer abc123", loader.token)
 
+    def test_oidc_fails_if_contains_reserved_chars(self):
+        loader = KubeConfigLoader(
+            config_dict=self.TEST_KUBE_CONFIG,
+            active_context="oidc_contains_reserved_character",
+        )
+        self.assertEqual(
+            loader._load_oid_token("oidc_contains_reserved_character"),
+            None,
+        )
+
+    def test_oidc_fails_if_invalid_padding_length(self):
+        loader = KubeConfigLoader(
+            config_dict=self.TEST_KUBE_CONFIG,
+            active_context="oidc_invalid_padding_length",
+        )
+        self.assertEqual(
+            loader._load_oid_token("oidc_invalid_padding_length"),
+            None,
+        )
+
     def test_user_pass(self):
         expected = FakeConfig(host=TEST_HOST, token=TEST_BASIC_TOKEN)
         actual = FakeConfig()
-- 
2.16.4

openSUSE Build Service is sponsored by