def validate(self): """Validate arguments, raises UrlArgsValidationError if something is wrong""" args = self.request.args if len(args) == 0: raise UrlArgsValidationError( 'Mandatory arguments not found, please refer to the HTTPAPI specifications.') for arg in args: # Check for unknown args if arg not in self.fields: raise UrlArgsValidationError("Argument [%s] is unknown." % arg) # Validate known args and check for mandatory fields for field in self.fields: fieldData = self.fields[field] if field in args: if isinstance(args[field][0], dict) or isinstance(args[field][0], list): continue # Todo check structure of dict/list elif isinstance(args[field][0], int) or isinstance(args[field][0], float): value = str(args[field][0]) else: value = args[field][0] # Validate known args if ('pattern' in self.fields[field] and self.fields[field]['pattern'].match(value) is None): raise UrlArgsValidationError("Argument [%s] has an invalid value: [%s]." % ( field, value)) elif not fieldData['optional']: raise UrlArgsValidationError("Mandatory argument [%s] is not found." % field) return True
def validate(self): """Validate arguments, raises UrlArgsValidationError if something is wrong""" args = self.request.args if len(args) == 0: raise UrlArgsValidationError( 'Mandatory arguments not found, please refer to the HTTPAPI specifications.' ) for arg in args: # Check for unknown args if arg not in self.fields: # we probably just should drop extraneous args rather than throwing an error... raise UrlArgsValidationError(b"Argument [%s] is unknown." % arg) # Validate known args and check for mandatory fields for field in self.fields: fieldData = self.fields[field] if field in args: if isinstance(args[field][0], dict) or isinstance( args[field][0], list): continue # Todo check structure of dict/list elif isinstance(args[field][0], int) or isinstance( args[field][0], float): value = str(args[field][0]).encode() elif isinstance(args[field][0], str): value = args[field][0].encode() else: value = args[field][0] # Validate known args # print(f'Validating field {field} of value {value}') if ('pattern' in self.fields[field] and self.fields[field]['pattern'].match(value) is None): raise UrlArgsValidationError( b"Argument [%s] has an invalid value: [%s]." % (field, value)) elif not fieldData['optional']: raise UrlArgsValidationError( b"Mandatory argument [%s] is not found." % field) return True
def hex2bin(hex_content): """Convert hex-content back to binary data, raise a UrlArgsValidationError on failure""" try: return binascii.unhexlify(hex_content) except Exception as e: raise UrlArgsValidationError("Invalid hex-content data: '%s'" % hex_content)
def render(self, request): """ /rate request processing Note: This method will indicate the rate of the message once sent """ self.log.debug("Rendering /rate response with args: %s from %s", request.args, request.getClientIP()) request.responseHeaders.addRawHeader(b"content-type", b"application/json") response = {'return': None, 'status': 200} self.stats.inc('request_count') self.stats.set('last_request_at', datetime.now()) try: # Validation (must be almost the same params as /send service) fields = { 'to': { 'optional': False, 'pattern': re.compile(r'^\+{0,1}\d+$') }, 'from': { 'optional': True }, 'coding': { 'optional': True, 'pattern': re.compile(r'^(0|1|2|3|4|5|6|7|8|9|10|13|14){1}$') }, 'username': { 'optional': False, 'pattern': re.compile(r'^.{1,15}$') }, 'password': { 'optional': False, 'pattern': re.compile(r'^.{1,8}$') }, # Priority validation pattern can be validated/filtered further more # through HttpAPICredentialValidator 'priority': { 'optional': True, 'pattern': re.compile(r'^[0-3]$') }, # Validity period validation pattern can be validated/filtered further more # through HttpAPICredentialValidator 'validity-period': { 'optional': True, 'pattern': re.compile(r'^\d+$') }, 'tags': { 'optional': True, 'pattern': re.compile(r'^([-a-zA-Z0-9,])*$') }, 'content': { 'optional': True }, 'hex-content': { 'optional': True }, } # Default coding is 0 when not provided if 'coding' not in request.args: request.args['coding'] = ['0'] # Content is optional, defaults to empty content string if 'hex-content' not in request.args and 'content' not in request.args: request.args['content'] = [''] # Make validation v = UrlArgsValidator(request, fields) v.validate() # Check if have content --OR-- hex-content # @TODO: make this inside UrlArgsValidator ! if 'content' in request.args and 'hex-content' in request.args: raise UrlArgsValidationError( "content and hex-content cannot be used both in same request." ) # Continue routing in a separate thread reactor.callFromThread(self.route_routable, request=request) except Exception as e: self.log.error("Error: %s", e) if hasattr(e, 'code'): response = {'return': e.message, 'status': e.code} else: response = {'return': "Unknown error: %s" % e, 'status': 500} self.log.debug("Returning %s to %s.", response, request.getClientIP()) # Return message if response['return'] is None: response['return'] = 'System error' request.setResponseCode(500) else: request.setResponseCode(response['status']) return json.dumps(response['return']) else: return NOT_DONE_YET
def render(self, request): """ /send request processing Note: This method MUST behave exactly like jasmin.protocols.smpp.factory.SMPPServerFactory.submit_sm_event """ self.log.debug("Rendering /send response with args: %s from %s", request.args, request.getClientIP()) request.responseHeaders.addRawHeader(b"content-type", b"text/plain") response = {'return': None, 'status': 200} self.stats.inc('request_count') self.stats.set('last_request_at', datetime.now()) # updated_request will be filled with default values where request will never get modified # updated_request is used for sending the SMS, request is just kept as an original request object updated_request = request try: # Validation (must have almost the same params as /rate service) fields = { 'to': { 'optional': False, 'pattern': re.compile(r'^\+{0,1}\d+$') }, 'from': { 'optional': True }, 'coding': { 'optional': True, 'pattern': re.compile(r'^(0|1|2|3|4|5|6|7|8|9|10|13|14){1}$') }, 'username': { 'optional': False, 'pattern': re.compile(r'^.{1,15}$') }, 'password': { 'optional': False, 'pattern': re.compile(r'^.{1,8}$') }, # Priority validation pattern can be validated/filtered further more # through HttpAPICredentialValidator 'priority': { 'optional': True, 'pattern': re.compile(r'^[0-3]$') }, 'sdt': { 'optional': True, 'pattern': re.compile( r'^\d{2}\d{2}\d{2}\d{2}\d{2}\d{2}\d{1}\d{2}(\+|-|R)$') }, # Validity period validation pattern can be validated/filtered further more # through HttpAPICredentialValidator 'validity-period': { 'optional': True, 'pattern': re.compile(r'^\d+$') }, 'dlr': { 'optional': False, 'pattern': re.compile(r'^(yes|no)$') }, 'dlr-url': { 'optional': True, 'pattern': re.compile(r'^(http|https)\://.*$') }, # DLR Level validation pattern can be validated/filtered further more # through HttpAPICredentialValidator 'dlr-level': { 'optional': True, 'pattern': re.compile(r'^[1-3]$') }, 'dlr-method': { 'optional': True, 'pattern': re.compile(r'^(get|post)$', re.IGNORECASE) }, 'tags': { 'optional': True, 'pattern': re.compile(r'^([-a-zA-Z0-9,])*$') }, 'content': { 'optional': True }, 'hex-content': { 'optional': True }, 'custom_tlvs': { 'optional': True } } if updated_request.getHeader('content-type') == 'application/json': json_body = updated_request.content.read() json_data = json.loads(json_body) for key, value in json_data.items(): # Make the values look like they came from form encoding all surrounded by [ ] if isinstance(value, unicode): value = value.encode() updated_request.args[key.encode()] = [value] # If no custom TLVs present, defaujlt to an [] which will be passed down to SubmitSM if 'custom_tlvs' not in updated_request.args: updated_request.args['custom_tlvs'] = [[]] # Default coding is 0 when not provided if 'coding' not in updated_request.args: updated_request.args['coding'] = ['0'] # Set default for undefined updated_request.arguments if 'dlr-url' in updated_request.args or 'dlr-level' in updated_request.args: updated_request.args['dlr'] = ['yes'] if 'dlr' not in updated_request.args: # Setting DLR updated_request to 'no' updated_request.args['dlr'] = ['no'] # Set default values if updated_request.args['dlr'][0] == 'yes': if 'dlr-level' not in updated_request.args: # If DLR is requested and no dlr-level were provided, assume minimum level (1) updated_request.args['dlr-level'] = [1] if 'dlr-method' not in updated_request.args: # If DLR is requested and no dlr-method were provided, assume default (POST) updated_request.args['dlr-method'] = ['POST'] # DLR method must be uppercase if 'dlr-method' in updated_request.args: updated_request.args['dlr-method'][0] = updated_request.args[ 'dlr-method'][0].upper() # Make validation v = UrlArgsValidator(updated_request, fields) v.validate() # Check if have content --OR-- hex-content # @TODO: make this inside UrlArgsValidator ! if 'content' not in request.args and 'hex-content' not in request.args: raise UrlArgsValidationError( "content or hex-content not present.") elif 'content' in request.args and 'hex-content' in request.args: raise UrlArgsValidationError( "content and hex-content cannot be used both in same request." ) # Continue routing in a separate thread reactor.callFromThread(self.route_routable, updated_request=updated_request) except Exception as e: self.log.error("Error: %s", e) if hasattr(e, 'code'): response = {'return': e.message, 'status': e.code} else: response = {'return': "Unknown error: %s" % e, 'status': 500} self.log.debug("Returning %s to %s.", response, updated_request.getClientIP()) updated_request.setResponseCode(response['status']) return 'Error "%s"' % response['return'] else: return NOT_DONE_YET