diff --git a/broadview_lib/config/agentapi.py b/broadview_lib/config/agentapi.py index 832fb16..128c834 100644 --- a/broadview_lib/config/agentapi.py +++ b/broadview_lib/config/agentapi.py @@ -21,6 +21,7 @@ class AgentAPI(object): def __init__(self): self.__httpMethod = "POST" self.__feature = None + self.__auth = None self.__host = None self.__port = None self.__payload = None @@ -64,7 +65,9 @@ class AgentAPI(object): self.__payload["id"] = AgentAPI.__serial AgentAPI.__serial = AgentAPI.__serial + 1 conn = AgentConnection(self.__host, self.__port, self.__feature, timeout) - return conn.makeRequest(self) + r = conn.makeRequest(self) + conn.close() + return r def getjson(self): x = json.dumps(OrderedDict(self.__payload)) diff --git a/broadview_lib/config/agentconnection.py b/broadview_lib/config/agentconnection.py index 2af45c1..6193b50 100644 --- a/broadview_lib/config/agentconnection.py +++ b/broadview_lib/config/agentconnection.py @@ -21,6 +21,15 @@ import sys import urllib from xml.etree import ElementTree import json +from sidauth import SIDAuth +from broadview_lib.config.broadviewconfig import BroadViewBSTSwitches + +try: + from oslo_log import log as logging +except: + import logging + +LOG = logging.getLogger(__name__) class RequestObj(): pass @@ -39,11 +48,19 @@ class RequestFailed(Exception): return repr((self._url, self._http_code, self._open_code, self._open_msg)) class AgentConnection(): + def __init__(self, host, port, feature, timeout): self.host = host self.port = port self.feature = feature # e.g., "bst" self._timeout = timeout + self._swconfig = BroadViewBSTSwitches() + self._auth = None + + def close(self): + if self._auth.logout: + self._auth.logout() + self._auth = None def _is_ok(self, r): return r.status_code == 200 @@ -71,7 +88,27 @@ class AgentConnection(): raise RequestFailed(url, j["response_code"], j["error_code"], j["msg"]) - def makeRequest(self, request): + def getAuth(self): + return self._auth + + def getAuthConf(self): + auth = {} + auth["auth"] = None + auth["username"] = None + auth["password"] = None + + conf = self._swconfig.getByIP(self.host) + if conf: + if "username" in conf: + auth["username"] = conf["username"] + if "password" in conf: + auth["password"] = conf["password"] + if "auth" in conf: + auth["auth"] = conf["auth"] + + return auth + + def __makeRequest(self, request): headers = {"Content-Type": "application/json"} timeout = False @@ -80,32 +117,23 @@ class AgentConnection(): if request.getHttpMethod() == "GET": isGet = True - if False and isGet: - payload = request.getjson().encode("utf-8") - if self.feature: - url = "http://%s:%d/broadview/%s/%s%s%s" % (self.host, self.port, self.feature, request.getMethod(), "?req=", payload) - else: - url = "http://%s:%d/broadview/%s%s%s" % (self.host, self.port, request.getMethod(), "?req=", payload) + auth = self.getAuth() + + payload = request.getjson().encode("utf-8") + if self.feature: + url = "http://%s:%d/broadview/%s/%s" % (self.host, self.port, self.feature, request.getMethod()) + else: + url = "http://%s:%d/broadview/%s" % (self.host, self.port, request.getMethod()) + if isGet: try: - r = requests.get(url, timeout=self._timeout, headers=headers) + r = requests.get(url, timeout=self._timeout, data=payload, headers=headers, auth=auth) except requests.exceptions.Timeout: timeout = True else: - payload = request.getjson().encode("utf-8") - if self.feature: - url = "http://%s:%d/broadview/%s/%s" % (self.host, self.port, self.feature, request.getMethod()) - else: - url = "http://%s:%d/broadview/%s" % (self.host, self.port, request.getMethod()) - if isGet: - try: - r = requests.get(url, timeout=self._timeout, data=payload, headers=headers) - except requests.exceptions.Timeout: - timeout = True - else: - try: - r = requests.post(url, timeout=self._timeout, data=payload, headers=headers) - except requests.exceptions.Timeout: - timeout = True + try: + r = requests.post(url, timeout=self._timeout, data=payload, headers=headers, auth=auth) + except requests.exceptions.Timeout: + timeout = True json_data = {} if timeout: @@ -122,6 +150,39 @@ class AgentConnection(): except: pass - self._raise_fail_if(url, r, timeout) + return (r, json_data) + + def makeRequest(self, request): + r, json_data = self.__makeRequest(request) + + if r.status_code == 401: + conf = self.getAuthConf() + try: + auth_method = r.headers["WWW-Authenticate"] + except: + auth_method = None + if auth_method: + auth_method = auth_method.lower() + if auth_method == "basic": + self._auth = requests.HTTPBasicAuth(conf["username"], conf["password"]) + elif auth_method == "digest": + self._auth[self.host] = requests.HTTPDigestAuth(conf["username"], conf["password"]) + elif auth_method == "sidauth": + self._auth[self.host] = SIDAuth(self.host, self.port, conf["username"], conf["password"]) + else: + LOG.info("unknown auth {}".format(auth_method)) + return + else: + # RFC 2616 requires a WWW-Authenticate header in 401 responses. If + # we get here, it was missing. Check if there is configuration that + # declares an auth method and use that. + LOG.info("makeRequest: 401 but no WWW-Authenticate") + if conf["auth"] and conf["auth"].lower() == "sidauth": + self._auth = SIDAuth(self.host, self.port, conf["username"], conf["password"]) + + # try again + + r, json_data = self.__makeRequest(request) + return (r.status_code, json_data) diff --git a/broadview_lib/config/broadviewconfig.py b/broadview_lib/config/broadviewconfig.py index 22169c8..31a63f9 100644 --- a/broadview_lib/config/broadviewconfig.py +++ b/broadview_lib/config/broadviewconfig.py @@ -35,6 +35,14 @@ class BroadViewBSTSwitches(): else: return 0 + def getByIP(self, ip): + ret = None + for x in range(self.__len__()): + if BroadViewBSTSwitches.bst_switches[x]["ip"] == ip: + ret = BroadViewBSTSwitches.bst_switches[x] + break + return ret + def get(self, i): ret = None if i >= 0 and i < len(BroadViewBSTSwitches.bst_switches): diff --git a/broadview_lib/config/sidauth.py b/broadview_lib/config/sidauth.py new file mode 100644 index 0000000..041a736 --- /dev/null +++ b/broadview_lib/config/sidauth.py @@ -0,0 +1,54 @@ +# +# (C) Copyright Broadcom Corporation 2016 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import requests + +class SIDAuth(requests.auth.AuthBase): + def __init__(self, host, port, username, password): + super(SIDAuth, self).__init__() + self._SID = None + self._host = host + self._port = port + self._username = username + self._password = password + self._login() + + def _getCookie(self): + cookie = {} + if self._SID: + x = self._SID.split("=") + cookie[x[0]] = x[1] + return cookie + + def _login(self): + url = "http://{}:{}/broadview/login?username={}&password={}".format(self._host, self._port, self._username, self._password) + r = requests.put(url) + code = r.status_code + if code == 200: + try: + self._SID = r.headers["Set-Cookie"] + except: + self._SID = None + + def logout(self): + url = "http://{}:{}/broadview/logout".format(self._host, self._port) + r = requests.put(url, cookies=self._getCookie()) + self._SID = None + + def __call__(self, r): + if self._SID: + r.headers['Cookie'] = self._SID + return r