1From d0d8890b5ef74c315381c9e1cff4b1d32892116b Mon Sep 17 00:00:00 2001 2From: Hongxu Jia <hongxu.jia@windriver.com> 3Date: Thu, 1 Jun 2017 15:07:36 +0800 4Subject: [PATCH 1/4] support authentication for kickstart 5 6While download kickstart file from web server, 7we support basic/digest authentication. 8 9Add KickstartAuthError to report authentication failure, 10which the invoker could parse this specific error. 11 12Upstream-Status: inappropriate [oe specific] 13 14Signed-off-by: Hongxu Jia <hongxu.jia@windriver.com> 15--- 16 pykickstart/errors.py | 19 +++++++++++++++++++ 17 pykickstart/load.py | 32 +++++++++++++++++++++++++++----- 18 pykickstart/parser.py | 4 ++-- 19 3 files changed, 48 insertions(+), 7 deletions(-) 20 21diff --git a/pykickstart/errors.py b/pykickstart/errors.py 22index b76e84c..fd81bc8 100644 23--- a/pykickstart/errors.py 24+++ b/pykickstart/errors.py 25@@ -35,6 +35,10 @@ It also exports several exception classes: 26 27 KickstartVersionError - An exception for errors relating to unsupported 28 syntax versions. 29+ 30+ KickstartAuthError - An exception for errors relating to authentication 31+ failed while downloading kickstart from web server 32+ 33 """ 34 import warnings 35 36@@ -103,3 +107,18 @@ class KickstartVersionError(KickstartError): 37 38 def __str__ (self): 39 return self.value 40+ 41+class KickstartAuthError(KickstartError): 42+ """An exception for errors relating to authentication failed while 43+ downloading kickstart from web server 44+ """ 45+ def __init__(self, msg): 46+ """Create a new KickstartAuthError exception instance with the 47+ descriptive message val. val should be the return value of 48+ formatErrorMsg. 49+ """ 50+ KickstartError.__init__(self, msg) 51+ 52+ def __str__(self): 53+ return self.value 54+ 55diff --git a/pykickstart/load.py b/pykickstart/load.py 56index 1f69b9c..0f5741b 100644 57--- a/pykickstart/load.py 58+++ b/pykickstart/load.py 59@@ -18,10 +18,13 @@ 60 # with the express permission of Red Hat, Inc. 61 # 62 import requests 63+from requests.auth import HTTPDigestAuth 64+from requests.auth import HTTPBasicAuth 65+ 66 import shutil 67 import six 68 69-from pykickstart.errors import KickstartError 70+from pykickstart.errors import KickstartError, KickstartAuthError 71 from pykickstart.i18n import _ 72 from requests.exceptions import SSLError, RequestException 73 74@@ -29,7 +32,7 @@ _is_url = lambda location: '://' in location # RFC 3986 75 76 SSL_VERIFY = True 77 78-def load_to_str(location): 79+def load_to_str(location, user=None, passwd=None): 80 '''Load a destination URL or file into a string. 81 Type of input is inferred automatically. 82 83@@ -40,7 +43,7 @@ def load_to_str(location): 84 Raises: KickstartError on error reading''' 85 86 if _is_url(location): 87- return _load_url(location) 88+ return _load_url(location, user=user, passwd=passwd) 89 else: 90 return _load_file(location) 91 92@@ -71,13 +74,32 @@ def load_to_file(location, destination): 93 _copy_file(location, destination) 94 return destination 95 96+def _get_auth(location, user=None, passwd=None): 97+ 98+ auth = None 99+ request = requests.get(location, verify=SSL_VERIFY) 100+ if request.status_code == requests.codes.unauthorized: 101+ if user is None or passwd is None: 102+ log.info("Require Authentication") 103+ raise KickstartAuthError("Require Authentication.\nAppend 'ksuser=<username> kspasswd=<password>' to boot command") 104 105+ reasons = request.headers.get("WWW-Authenticate", "").split() 106+ if reasons: 107+ auth_type = reasons[0] 108+ if auth_type == "Basic": 109+ auth = HTTPBasicAuth(user, passwd) 110+ elif auth_type == "Digest": 111+ auth=HTTPDigestAuth(user, passwd) 112 113-def _load_url(location): 114+ return auth 115+ 116+def _load_url(location, user=None, passwd=None): 117 '''Load a location (URL or filename) and return contents as string''' 118 119+ auth = _get_auth(location, user=user, passwd=passwd) 120+ 121 try: 122- request = requests.get(location, verify=SSL_VERIFY) 123+ request = requests.get(location, verify=SSL_VERIFY, auth=auth) 124 except SSLError as e: 125 raise KickstartError(_('Error securely accessing URL "%s"') % location + ': {e}'.format(e=str(e))) 126 except RequestException as e: 127diff --git a/pykickstart/parser.py b/pykickstart/parser.py 128index d2b0fbe..26b5de9 100644 129--- a/pykickstart/parser.py 130+++ b/pykickstart/parser.py 131@@ -773,7 +773,7 @@ class KickstartParser(object): 132 i = PutBackIterator(s.splitlines(True) + [""]) 133 self._stateMachine (i) 134 135- def readKickstart(self, f, reset=True): 136+ def readKickstart(self, f, reset=True, username=None, password=None): 137 """Process a kickstart file, given by the filename f.""" 138 if reset: 139 self._reset() 140@@ -794,7 +794,7 @@ class KickstartParser(object): 141 self.currentdir[self._includeDepth] = cd 142 143 try: 144- s = load_to_str(f) 145+ s = load_to_str(f, user=username, passwd=password) 146 except KickstartError as e: 147 raise KickstartError(formatErrorMsg(0, msg=_("Unable to open input kickstart file: %s") % str(e))) 148 149-- 1502.7.4 151 152