def __validate_digit(self, var, message): if six.PY3: if not isinstance(var, (int)) and not var.isdigit(): raise AuthyFormatException(message) else: if not isinstance(var, (int, long)) and not var.isdigit(): raise AuthyFormatException(message)
def clean_logos(self, logos): """ Validate logos input. :param list logos: :return list logos: """ if not len(logos): return logos # Allow nil hash if not isinstance(logos, list): raise AuthyFormatException( 'Invalid logos list. Only res and url required') temp_array = {} clean_logos = [] for logo in logos: if not isinstance(logo, dict): raise AuthyFormatException('Invalid logo type') for l in logo: # We ignore any additional parameter on the logos, and truncate # string size to the maximum allowed. if l == 'res': temp_array['res'] = logo[l][:MAX_STRING_SIZE] elif l == 'url': temp_array['url'] = logo[l][:MAX_STRING_SIZE] else: raise AuthyFormatException( 'Invalid logos list. Only res and url required') clean_logos.append(temp_array) temp_array = {} return clean_logos
def send_request(self, user_id, message, seconds_to_expire=None, details={}, hidden_details={}, logos=[]): """ OneTouch verification request. Sends a request for Auth App. For more info https://www.twilio.com/docs/api/authy/authy-onetouch-api :param string user_id: user_id User's authy id stored in your database :param string message: Required, the message shown to the user when the approval request arrives. :param number seconds_to_expire: Optional, defaults to 120 (two minutes). :param dict details: For example details['Requested by'] = 'MacBook Pro, Chrome'; it will be displayed on Authy app :param dict hidden_details: Same usage as detail except this detail is not shown in Authy app :param list logos: Contains the logos that will be shown to user. The logos parameter is expected to be an array of objects, each object with two fields: res (values are default,low,med,high) and url :return OneTouchResponse: the server response Json Object """ if not user_id or not isinstance(user_id, int): raise AuthyFormatException('Invalid authy id, user id is requred and must be an integer value.') if not message: raise AuthyFormatException('Invalid message - should not be empty. It is required') data = { "message": message[:MAX_STRING_SIZE], "seconds_to_expire": seconds_to_expire, "details":self.__clean_inputs(details), 'hidden_details': self.__clean_inputs(hidden_details), 'logos': self.clean_logos(logos) } request_url = "/onetouch/json/users/{0}/approval_requests".format(user_id) response = self.post(request_url, data) return OneTouchResponse(self, response)
def verification_start(self, phone_number, country_code, via='sms', locale=None, code_length=4): """ :param string phone_number: stored in your databse or you provided while creating new user. :param string country_code: stored in your databse or you provided while creating new user. :param string via: verification method either sms or call :param string locale: optional default none :param number code_length: optional default 4 :return: """ if via != 'sms' and via != 'call': raise AuthyFormatException("Invalid Via. Expected 'sms' or 'call'.") options = { 'phone_number': phone_number, 'country_code': country_code, 'via': via } if locale: options['locale'] = locale try: cl = int(code_length) if cl < 4 or cl > 10: raise ValueError options['code_length'] = cl except ValueError: raise AuthyFormatException( "Invalid code_length. Expected numeric value from 4-10.") resp = self.post("/protected/json/phones/verification/start", options) return Phone(self, resp)
def _validate_request(self, user_id, message): if not user_id or not isinstance(user_id, int): raise AuthyFormatException( 'Invalid authy id, user id is required and must be an integer value.') if not message: raise AuthyFormatException( 'Invalid message - should not be empty. It is required')
def __validate(self, token, device_id): self.__validate_digit(token, "Invalid Token. Only digits accepted.") self.__validate_digit(device_id, "Invalid Authy id. Only digits accepted.") length = len(str(token)) if length < MIN_TOKEN_SIZE or length > MAX_TOKEN_SIZE: raise AuthyFormatException("Invalid Token. Unexpected length.")
def __validate(self, token, device_id): self.__validate_digit(token, "Invalid Token. Only digits accepted.") self.__validate_digit(device_id, "Invalid Authy id. Only digits accepted.") length = len(token) if length < 6 or length > 10: raise AuthyFormatException("Invalid Token. Unexpected length.")
def verification_start(self, phone_number, country_code, via='sms', locale=None): """ :param string phone_number: stored in your databse or you provided while creating new user. :param string country_code: stored in your databse or you provided while creating new user. :param string via: verification method either sms or call :param string locale: optional default none :return: """ if via != 'sms' and via != 'call': raise AuthyFormatException( "Invalid Via. Expected 'sms' or 'call'.") options = { 'phone_number': phone_number, 'country_code': country_code, 'via': via } if locale: options['locale'] = locale resp = self.post("/protected/json/phones/verification/start", options) return Phone(self, resp)
def validate_one_touch_signature(self, signature, nonce, method, url, params): """ Function to validate signature in X-Authy-Signature key of headers. :param string signature: X-Authy-Signature key of headers. :param string nonce: X-Authy-Signature-Nonce key of headers. :param string method: GET or POST - configured in app settings for OneTouch. :param string url: base callback url. :param dict params: params sent by Authy. :return bool: True if calculated signature and X-Authy-Signature are identical else False. """ if not signature or not isinstance(signature, str): raise AuthyFormatException( "Invalid signature - should not be empty. It is required") if not nonce: raise AuthyFormatException( "Invalid nonce - should not be empty. It is required") if not method or not ('get' == method.lower() or 'post' == method.lower()): raise AuthyFormatException( "Invalid method - should not be empty. It is required") if not params or not isinstance(params, dict): raise AuthyFormatException( "Invalid params - should not be empty. It is required") query_params = self.__make_http_query(params) # Sort and replace encoded params in case-sensitive order sorted_params = '&'.join( sorted( query_params.replace('/', '%2F').replace('%20', '+').split('&'))) sorted_params = re.sub("\\%5B([0-9])*\\%5D", "%5B%5D", sorted_params) sorted_params = re.sub("\\=None", "=", sorted_params) data = nonce + "|" + method + "|" + url + "|" + sorted_params try: calculated_signature = base64.b64encode( hmac.new(self.api_key.encode(), data.encode(), hashlib.sha256).digest()) return calculated_signature.decode() == signature except: calculated_signature = base64.b64encode( hmac.new(self.api_key, data, hashlib.sha256).digest()) return calculated_signature == signature
def __validate_code_length(self, code_length): try: cl = int(code_length) if cl < 4 or cl > 10: raise ValueError return cl except ValueError: raise AuthyFormatException( "Invalid code_length. Expected numeric value from 4-10.")
def __validate_channel(self, via): if via != 'sms' and via != 'call': raise AuthyFormatException("Invalid Via. Expected 'sms' or 'call'.")
def __validate_digit(self, var, message): # PEP 0237: Essentially, long renamed to int. if not isinstance(var, int) and not var.isdigit(): raise AuthyFormatException(message)