From f05f5fc363e2510f6943532f3e14a6423f6a2cf1 Mon Sep 17 00:00:00 2001 From: Hongxu Jia Date: Tue, 31 Jul 2018 17:24:47 +0800 Subject: [PATCH 1/4] support authentication for kickstart While download kickstart file from web server, we support basic/digest authentication. Add KickstartAuthError to report authentication failure, which the invoker could parse this specific error. Upstream-Status: inappropriate [oe specific] Signed-off-by: Hongxu Jia --- pykickstart/errors.py | 17 +++++++++++++++++ pykickstart/load.py | 34 ++++++++++++++++++++++++++++------ pykickstart/parser.py | 4 ++-- 3 files changed, 47 insertions(+), 8 deletions(-) diff --git a/pykickstart/errors.py b/pykickstart/errors.py index bf08ac5..aada7aa 100644 --- a/pykickstart/errors.py +++ b/pykickstart/errors.py @@ -32,6 +32,9 @@ This module exports several exception classes: KickstartVersionError - An exception for errors relating to unsupported syntax versions. + KickstartAuthError - An exception for errors relating to authentication + failed while downloading kickstart from web server + And some warning classes: KickstartWarning - A generic warning class. @@ -131,3 +134,17 @@ class KickstartDeprecationWarning(KickstartParseWarning, DeprecationWarning): commands and options. """ pass + +class KickstartAuthError(KickstartError): + """An exception for errors relating to authentication failed while + downloading kickstart from web server + """ + def __init__(self, msg): + """Create a new KickstartAuthError exception instance with the + descriptive message val. val should be the return value of + formatErrorMsg. + """ + KickstartError.__init__(self, msg) + + def __str__(self): + return self.value diff --git a/pykickstart/load.py b/pykickstart/load.py index fb935f2..41a2e9e 100644 --- a/pykickstart/load.py +++ b/pykickstart/load.py @@ -18,10 +18,13 @@ # with the express permission of Red Hat, Inc. # import requests +from requests.auth import HTTPDigestAuth +from requests.auth import HTTPBasicAuth + import shutil import six -from pykickstart.errors import KickstartError +from pykickstart.errors import KickstartError, KickstartAuthError from pykickstart.i18n import _ from requests.exceptions import SSLError, RequestException @@ -29,7 +32,7 @@ _is_url = lambda location: '://' in location # RFC 3986 SSL_VERIFY = True -def load_to_str(location): +def load_to_str(location, user=None, passwd=None): '''Load a destination URL or file into a string. Type of input is inferred automatically. @@ -40,7 +43,7 @@ def load_to_str(location): Raises: KickstartError on error reading''' if _is_url(location): - return _load_url(location) + return _load_url(location, user=user, passwd=passwd) else: return _load_file(location) @@ -70,11 +73,30 @@ def load_to_file(location, destination): _copy_file(location, destination) return destination -def _load_url(location): - '''Load a location (URL or filename) and return contents as string''' +def _get_auth(location, user=None, passwd=None): + + auth = None + request = requests.get(location, verify=SSL_VERIFY) + if request.status_code == requests.codes.unauthorized: + if user is None or passwd is None: + log.info("Require Authentication") + raise KickstartAuthError("Require Authentication.\nAppend 'ksuser= kspasswd=' to boot command") + reasons = request.headers.get("WWW-Authenticate", "").split() + if reasons: + auth_type = reasons[0] + if auth_type == "Basic": + auth = HTTPBasicAuth(user, passwd) + elif auth_type == "Digest": + auth=HTTPDigestAuth(user, passwd) + + return auth + +def _load_url(location, user=None, passwd=None): + '''Load a location (URL or filename) and return contents as string''' + auth = _get_auth(location, user=user, passwd=passwd) try: - request = requests.get(location, verify=SSL_VERIFY) + request = requests.get(location, verify=SSL_VERIFY, auth=auth) except SSLError as e: raise KickstartError(_('Error securely accessing URL "%s"') % location + ': {e}'.format(e=str(e))) except RequestException as e: diff --git a/pykickstart/parser.py b/pykickstart/parser.py index d8880eb..22d14cb 100644 --- a/pykickstart/parser.py +++ b/pykickstart/parser.py @@ -787,7 +787,7 @@ class KickstartParser(object): i = PutBackIterator(s.splitlines(True) + [""]) self._stateMachine(i) - def readKickstart(self, f, reset=True): + def readKickstart(self, f, reset=True, username=None, password=None): """Process a kickstart file, given by the filename f.""" if reset: self._reset() @@ -808,7 +808,7 @@ class KickstartParser(object): self.currentdir[self._includeDepth] = cd try: - s = load_to_str(f) + s = load_to_str(f, user=username, passwd=password) except KickstartError as e: raise KickstartError(_("Unable to open input kickstart file: %s") % str(e), lineno=0) -- 2.7.4