def checkOtp(self, anOtpVal, counter, window, options=None): ''' Here we contact the Yubico Cloud server to validate the OtpVal. ''' pparams = {} yubico_url = getFromConfig("yubico.url", FALLBACK_YUBICO_URL) if yubico_url == DEPRECATED_YUBICO_URL: log.warning("Usage of YUBICO_URL %r is deprecated!! ", DEPRECATED_YUBICO_URL) # setups with old YUBICO URLS will be broken on yubico side # after 3th of February 2019 third_feb_2019 = datetime.datetime(year=2019, month=2, day=3) if datetime.datetime.now() >= third_feb_2019: raise Exception("Usage of YUBICO_URL %r is deprecated!! " % DEPRECATED_YUBICO_URL) apiId = getFromConfig("yubico.id") or DEFAULT_CLIENT_ID apiKey = getFromConfig("yubico.secret") or DEFAULT_API_KEY if apiKey == DEFAULT_API_KEY or apiId == DEFAULT_CLIENT_ID: log.warning("Usage of default apiKey or apiId not recomended!!") log.warning("Please register your own apiKey and apiId at " "yubico website !!") log.warning("Configure of apiKey and apiId at the " "linotp manage config menue!!") tokenid = self.getFromTokenInfo("yubico.tokenid") if len(anOtpVal) < 12: log.warning("[checkOtp] The otpval is too short: %r" % anOtpVal) return -1 if anOtpVal[:12] != tokenid: log.warning("[checkOtp] the tokenid in the OTP value does " "not match the assigned token!") return -1 timeout = getFromConfig("yubico.timeout") if timeout: pparams['timeout']= parse_timeout(timeout) nonce = binascii.hexlify(os.urandom(20)) p = urllib.urlencode({ 'nonce': nonce, 'otp':anOtpVal, 'id':apiId }) yubico_urls = [x.strip() for x in yubico_url.split(',')] res_scheduler = ResourceScheduler( tries=2, uri_list=yubico_urls) for uri in res_scheduler.next(): try: URL = "%s?%s" % (uri, p) response = requests.get(URL, **pparams) if response.ok: return self._check_yubico_response( nonce, apiKey, response.content) log.info("Failed to validate yubico request %r", response) return -1 except (Timeout, ConnectTimeout, ReadTimeout, ConnectionError, TooManyRedirects) as exx: log.exception('resource %r not available!', uri) # mark the url as blocked res_scheduler.block(uri, delay=30) log.error("[checkOtp] Error getting response from " "Yubico Cloud Server (%r)" % uri) except Exception as exx: log.exception('unknown exception for uri %r!', uri) raise exx # ------------------------------------------------------------------ -- # if we reach here, no resource has been availabel log.error('non of the resources %r available!', yubico_urls) raise AllResourcesUnavailable('non of the resources %r available!' % yubico_urls)
def checkOtp(self, anOtpVal, counter, window, options=None): ''' Here we contact the Yubico Cloud server to validate the OtpVal. ''' pparams = {} yubico_url = getFromConfig("yubico.url", FALLBACK_YUBICO_URL) if yubico_url == DEPRECATED_YUBICO_URL: log.warning("Usage of YUBICO_URL %r is deprecated!! ", DEPRECATED_YUBICO_URL) # setups with old YUBICO URLS will be broken on yubico side # after 3th of February 2019 third_feb_2019 = datetime.datetime(year=2019, month=2, day=3) if datetime.datetime.now() >= third_feb_2019: raise Exception("Usage of YUBICO_URL %r is deprecated!! " % DEPRECATED_YUBICO_URL) apiId = getFromConfig("yubico.id") or DEFAULT_CLIENT_ID apiKey = getFromConfig("yubico.secret") or DEFAULT_API_KEY if apiKey == DEFAULT_API_KEY or apiId == DEFAULT_CLIENT_ID: log.warning("Usage of default apiKey or apiId not recomended!!") log.warning("Please register your own apiKey and apiId at " "yubico website !!") log.warning("Configure of apiKey and apiId at the " "linotp manage config menue!!") tokenid = self.getFromTokenInfo("yubico.tokenid") if len(anOtpVal) < 12: log.warning("[checkOtp] The otpval is too short: %r" % anOtpVal) return -1 if anOtpVal[:12] != tokenid: log.warning("[checkOtp] the tokenid in the OTP value does " "not match the assigned token!") return -1 timeout = getFromConfig("yubico.timeout") if timeout: pparams['timeout'] = parse_timeout(timeout) nonce = binascii.hexlify(os.urandom(20)).decode() p = urllib.parse.urlencode({ 'nonce': nonce, 'otp': anOtpVal, 'id': apiId }) yubico_urls = [x.strip() for x in yubico_url.split(',')] res_scheduler = ResourceScheduler(tries=2, uri_list=yubico_urls) for uri in next(res_scheduler): try: URL = "%s?%s" % (uri, p) response = requests.get(URL, **pparams) if response.ok: return self._check_yubico_response( nonce, apiKey, response.content.decode()) log.info("Failed to validate yubico request %r", response) return -1 except (Timeout, ConnectTimeout, ReadTimeout, ConnectionError, TooManyRedirects) as exx: log.exception('resource %r not available!', uri) # mark the url as blocked res_scheduler.block(uri, delay=30) log.error("[checkOtp] Error getting response from " "Yubico Cloud Server (%r)" % uri) except Exception as exx: log.exception('unknown exception for uri %r!', uri) raise exx # ------------------------------------------------------------------ -- # if we reach here, no resource has been availabel log.error('non of the resources %r available!', yubico_urls) raise AllResourcesUnavailable('non of the resources %r available!' % yubico_urls)
def httplib_request(self, url, parameter, username=None, password=None, method='GET'): """ build the urllib request and check the response for success or fail :param url: target url :param parameter: additonal parameter to append to the url request :param username: basic authentication with username (optional) :param password: basic authentication with password (optional) :param method: run an GET or POST request :return: False or True """ #httplib2.debuglevel = 4 ret = False http_params = {} headers = {} log.debug("Do the request to %s with %s" % (url, parameter)) if 'PROXY' in self.config: proxy_url = None proxy = self.config['PROXY'] if isinstance(proxy, dict): if url.startswith('https') and 'https' in proxy: proxy_url = proxy['https'] elif url.startswith('http') and 'http' in proxy: proxy_url = proxy['http'] elif isinstance(proxy, (str, unicode)): proxy_url = proxy if proxy_url: http_params['proxy_info'] = http2lib_get_proxy_info(proxy_url) if 'timeout' in self.config: parsed_timeout = parse_timeout(self.config['timeout']) if isinstance(parsed_timeout, tuple): timeout = int(parsed_timeout[0]) else: timeout = int(parsed_timeout) http_params['timeout'] = timeout http_params["disable_ssl_certificate_validation"] = True try: # test if httplib is compiled with ssl - will raise a TypeError # TypeError: __init__() got an unexpected keyword argument # 'disable_ssl_certificate_validation' http = httplib2.Http(**http_params) except TypeError as exx: log.warning("httplib2 'disable_ssl_certificate_validation' " "attribute error: %r" % exx) # so we remove the ssl param from the arguments del http_params["disable_ssl_certificate_validation"] # and retry http = httplib2.Http(**http_params) # for backward compatibility we have to support url with the format # http://user:pass@server:port/path # so we extract the url_user and the url_pass and use them if # not overruled by the explicit parameters username and password url_user = None url_pass = None parsed_url = urlparse(url) if "@" in parsed_url[1]: puser, server = parsed_url[1].split('@') url_user, url_pass = puser.split(':') # now rewrite the url to not contain the user anymore url = url.replace(parsed_url[1], server) if username and password is not None: http.add_credentials(name=username, password=password) elif url_user and url_pass is not None: http.add_credentials(name=url_user, password=url_pass) #! the parameters to the httplib / proxy must be of type str() encoded_params = '' if parameter is not None and len(parameter) > 0: encoded_params = self.urlencode(parameter) call_url = str(url) try: # do a GET request - which has no body but all params # added to the url if method == 'GET': call_data = None if len(encoded_params) > 0: # extend the url with our parameters call_url = "%s?%s" % (call_url, encoded_params) # or do a POST request - the more secure default and fallback else: method = 'POST' headers["Content-type"] = "application/x-www-form-urlencoded" call_data = encoded_params # using httplib2: # the proxy spec and url + enc. parameters must be of # type string str() - otherwise the following error will occur: # : GeneralProxyError: (5, 'bad input') : (_resp, reply) = http.request(call_url, method=method, headers=headers, body=call_data) # some providers like clickatell have no response.status! log.debug("HttpSMSProvider >>%s...%s<<", reply[:20], reply[-20:]) ret = self._check_success(reply) except (httplib2.HttpLib2Error, socket.error) as exc: raise ProviderNotAvailable( "Failed to send SMS - timed out %r" % exc) except Exception as exc: log.exception("Failed to send SMS") raise ProviderNotAvailable("Failed to send SMS. %r" % exc) return ret
def urllib_request(self, url, parameter, username=None, password=None, method='GET'): """ build the urllib request and check the response for success or fail :param url: target url :param parameter: additonal parameter to append to the url request :param username: basic authentication with username (optional) :param password: basic authentication with password (optional) :param method: run an GET or POST request :return: False or True """ try: headers = {} handlers = [] pparams = {} if 'PROXY' in self.config and self.config['PROXY']: proxy_handler = None if isinstance(self.config['PROXY'], (str, unicode)): # for simplicity we set both protocols proxy_handler = urllib2.ProxyHandler({ "http": self.config['PROXY'], "https": self.config['PROXY']} ) elif isinstance(self.config['PROXY'], dict): proxy_defintion = self.config['PROXY'] proxy_handler = urllib2.ProxyHandler(proxy_defintion) if proxy_handler: handlers.append(proxy_handler) log.debug("using Proxy: %r" % self.config['PROXY']) if username and password is not None: password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm() password_mgr.add_password(None, url, username, password) auth_handler = urllib2.HTTPBasicAuthHandler(password_mgr) handlers.append(auth_handler) timeout = None if 'timeout' in self.config and self.config['timeout']: timeout = parse_timeout(self.config['timeout']) opener = urllib2.build_opener(*handlers) urllib2.install_opener(opener) full_url = str(url) encoded_params = None if parameter is not None and len(parameter) > 0: encoded_params = self.urlencode(parameter) if method == 'GET': c_data = None if encoded_params: full_url = "%s?%s" % (url, encoded_params) else: headers["Content-type"] = "application/x-www-form-urlencoded" c_data = encoded_params requ = urllib2.Request(full_url, data=c_data, headers=headers) if username and password is not None: base64string = base64.encodestring( '%s:%s' % (username, password)).replace('\n', '') requ.add_header("Authorization", "Basic %s" % base64string) response = urllib2.urlopen(requ, timeout=timeout) reply = response.read() # some providers like clickatell have no response.status! log.debug("HttpSMSProvider >>%s...%s<<", reply[:20], reply[-20:]) ret = self._check_success(reply) except (urllib2.URLError, socket.timeout) as exc: log.exception("HttpSMSProvider urllib timeout exception") raise ProviderNotAvailable("Failed to send SMS -timed out %r" % exc) except Exception as exc: log.exception("HttpSMSProvider urllib") raise Exception("Failed to send SMS. %r" % exc) return ret
def requests_request(self, url, parameter, username=None, password=None, method='GET'): try: pparams = {} if 'timeout' in self.config and self.config['timeout']: pparams['timeout'] = parse_timeout(self.config['timeout']) if 'PROXY' in self.config and self.config['PROXY']: if isinstance(self.config['PROXY'], (str, unicode)): proxy_defintion = { "http": self.config['PROXY'], "https": self.config['PROXY'] } elif isinstance(self.config['PROXY'], dict): proxy_defintion = self.config['PROXY'] pparams['proxies'] = proxy_defintion if username and password is not None: auth = None auth_type = self.config.get( 'AUTH_TYPE', 'basic').lower().strip() if auth_type == 'basic': auth = HTTPBasicAuth(username, password) if auth_type == 'digest': auth = HTTPDigestAuth(username, password) if auth: pparams['auth'] = auth # -------------------------------------------------------------- -- # fianly execute the request if method == 'GET': response = requests.get(url, params=parameter, **pparams) else: response = requests.post(url, data=parameter, **pparams) reply = response.text # some providers like clickatell have no response.status! log.debug("HttpSMSProvider >>%s...%s<<", reply[:20], reply[-20:]) ret = self._check_success(reply) except (requests.exceptions.ConnectTimeout, requests.exceptions.ConnectionError, requests.exceptions.Timeout, requests.exceptions.ReadTimeout, requests.exceptions.TooManyRedirects) as exc: log.exception("HttpSMSProvider timed out") raise ProviderNotAvailable("Failed to send SMS - timed out %r" % exc) except Exception as exc: log.error("HttpSMSProvider %r" % exc) raise Exception("Failed to send SMS. %r" % exc) return ret
def request(self, url, parameter, username=None, password=None, method='GET'): try: pparams = {} if 'timeout' in self.config and self.config['timeout']: pparams['timeout'] = parse_timeout(self.config['timeout']) if 'PROXY' in self.config and self.config['PROXY']: if isinstance(self.config['PROXY'], str): proxy_defintion = { "http": self.config['PROXY'], "https": self.config['PROXY'] } elif isinstance(self.config['PROXY'], dict): proxy_defintion = self.config['PROXY'] pparams['proxies'] = proxy_defintion if username and password is not None: auth = None auth_type = self.config.get('AUTH_TYPE', 'basic').lower().strip() if auth_type == 'basic': auth = HTTPBasicAuth(username, password) if auth_type == 'digest': auth = HTTPDigestAuth(username, password) if auth: pparams['auth'] = auth # -------------------------------------------------------------- -- # fianly execute the request if method == 'GET': response = requests.get(url, params=parameter, **pparams) else: response = requests.post(url, data=parameter, **pparams) reply = response.text # some providers like clickatell have no response.status! log.debug("HttpSMSProvider >>%r...%r<<", reply[:20], reply[-20:]) ret = self._check_success(reply) except (requests.exceptions.ConnectTimeout, requests.exceptions.ConnectionError, requests.exceptions.Timeout, requests.exceptions.ReadTimeout, requests.exceptions.TooManyRedirects) as exc: log.exception("HttpSMSProvider timed out") raise ProviderNotAvailable("Failed to send SMS - timed out %r" % exc) except Exception as exc: log.error("HttpSMSProvider %r", exc) raise Exception("Failed to send SMS. %r" % exc) return ret
def request(self, url, parameter, username=None, password=None, method="GET"): try: pparams = {} pparams["timeout"] = HttpSMSProvider.DEFAULT_TIMEOUT if "timeout" in self.config and self.config["timeout"]: pparams["timeout"] = parse_timeout(self.config["timeout"]) if "PROXY" in self.config and self.config["PROXY"]: if isinstance(self.config["PROXY"], str): proxy_defintion = { "http": self.config["PROXY"], "https": self.config["PROXY"], } elif isinstance(self.config["PROXY"], dict): proxy_defintion = self.config["PROXY"] pparams["proxies"] = proxy_defintion if username and password is not None: auth = None auth_type = (self.config.get("AUTH_TYPE", "basic").lower().strip()) if auth_type == "basic": auth = HTTPBasicAuth(username, password) if auth_type == "digest": auth = HTTPDigestAuth(username, password) if auth: pparams["auth"] = auth # -------------------------------------------------------------- # set server certificate validation policy server_certificate = self.load_server_cert( self.config, server_cert_key="SERVER_CERTIFICATE") if server_certificate is False: pparams["verify"] = False if server_certificate: pparams["verify"] = server_certificate # -------------------------------------------------------------- -- # finally execute the request if method == "GET": response = requests.get(url, params=parameter, **pparams) else: response = requests.post(url, data=parameter, **pparams) reply = response.text # some providers like clickatell have no response.status! log.debug("HttpSMSProvider >>%r...%r<<", reply[:20], reply[-20:]) ret = self._check_success(reply) except ( requests.exceptions.ConnectTimeout, requests.exceptions.ConnectionError, requests.exceptions.Timeout, requests.exceptions.ReadTimeout, requests.exceptions.TooManyRedirects, ) as exc: log.error("HttpSMSProvider timed out") raise ProviderNotAvailable("Failed to send SMS - timed out %r" % exc) except Exception as exc: log.error("HttpSMSProvider %r", exc) raise Exception("Failed to send SMS. %r" % exc) return ret
def checkOtp(self, anOtpVal, counter, window, options=None): """ Here we contact the Yubico Cloud server to validate the OtpVal. """ pparams = {} yubico_url = getFromConfig("yubico.url", FALLBACK_YUBICO_URL) if yubico_url == DEPRECATED_YUBICO_URL: log.warning( "Usage of YUBICO_URL %r is deprecated!! ", DEPRECATED_YUBICO_URL, ) # setups with old YUBICO URLS will be broken on yubico side # after 3th of February 2019 third_feb_2019 = datetime.datetime(year=2019, month=2, day=3) if datetime.datetime.now() >= third_feb_2019: raise Exception("Usage of YUBICO_URL %r is deprecated!! " % DEPRECATED_YUBICO_URL) apiId = getFromConfig("yubico.id") apiKey = getFromConfig("yubico.secret") if not apiKey or not apiId: log.error(APIKEY_UNCONFIGURED_ERROR) raise YubicoApikeyException( "Yubico apiKey or apiId not configured!") tokenid = self.getFromTokenInfo("yubico.tokenid") if len(anOtpVal) < 12: log.warning("[checkOtp] The otpval is too short: %r", anOtpVal) return -1 if anOtpVal[:12] != tokenid: log.warning("[checkOtp] the tokenid in the OTP value does " "not match the assigned token!") return -1 timeout = getFromConfig("yubico.timeout") if timeout: pparams["timeout"] = parse_timeout(timeout) nonce = binascii.hexlify(os.urandom(20)).decode() p = urllib.parse.urlencode({ "nonce": nonce, "otp": anOtpVal, "id": apiId }) yubico_urls = [x.strip() for x in yubico_url.split(",")] res_scheduler = ResourceScheduler(tries=2, uri_list=yubico_urls) for uri in next(res_scheduler): try: URL = "%s?%s" % (uri, p) response = requests.get(URL, **pparams) if response.ok: return self._check_yubico_response( nonce, apiKey, response.content.decode()) log.info("Failed to validate yubico request %r", response) return -1 except ( Timeout, ConnectTimeout, ReadTimeout, ConnectionError, TooManyRedirects, ) as exx: log.error("resource %r not available!", uri) # mark the url as blocked res_scheduler.block(uri, delay=30) log.error( "[checkOtp] Error getting response from " "Yubico Cloud Server (%r)", uri, ) except Exception as exx: log.error("unknown exception for uri %r!", uri) raise exx # ------------------------------------------------------------------ -- # if we reach here, no resource has been availabel log.error("non of the resources %r available!", yubico_urls) raise AllResourcesUnavailable("non of the resources %r available!" % yubico_urls)
def loadConfig(self, configDict): if not configDict: raise Exception("missing configuration") self.config = configDict self.username = configDict.get("USERNAME", None) self.password = configDict.get("PASSWORD", None) # proxy defintion must be in the following format: # # . . . # PROXY : { # "http": "http://10.10.1.10:3128", # "https": "http://10.10.1.10:1080", # } # . . . # but we can as well provide Proxy via socks: # # PROXY = { # 'http': 'socks5://user:pass@host:port', # 'https': 'socks5://user:pass@host:port' # } # self.proxy = configDict.get("PROXY", None) self.auth_type = configDict.get("AUTHENTICATION", "BASIC").lower() if self.auth_type not in ["basic", "digest"]: raise Exception("no valid Authentication type provided") # support for multiple urls self.url_config = configDict["URL"] self.urls = [x.strip() for x in self.url_config.split(",")] # ------------------------------------------------------------------ -- # timeout (float or tuple) -- (optional) # How many seconds to wait for the server to send data before giving # up, as a float, or a (connect timeout, read timeout) tuple. #!!! we set the timeout by default so that linotp wont block self.timeout = parse_timeout( configDict.get("TIMEOUT", RestSMSProvider.DEFAULT_TIMEOUT)) # ------------------------------------------------------------------ -- # parameter is our json payload, which will provide the # keys where the phone and the message is replaced within self.payload = configDict["PAYLOAD"] self.headers = configDict.get("HEADERS", {}) self.sms_text_key = configDict["SMS_TEXT_KEY"] self.sms_phone_key = configDict["SMS_PHONENUMBER_KEY"] self.client_cert = configDict.get("CLIENT_CERTIFICATE_FILE") self.server_cert = self.load_server_cert( configDict, server_cert_key="SERVER_CERTIFICATE")