def get_user_with_username_and_password(self, username, password): """ Gets and returns `user` object with matching `username` and `password` combination :raises rest_framework.exceptions.AuthenticationFailed if `password` is invalid or when a user with `username` does not exist in the database :param username: user's username :param password: user's "raw" password :return: user object or None if both `username` & `password` were None or empty """ self.auth_scheme = 'Basic' try: if valid_str(username) and valid_str(password): user = get_user_model().objects.get( Q(username=username) | Q(email=username)) if user.check_password(raw_password=password) is False: # user was found but password does not match # log user's failed sign-in attempt password_change_message = user.get_last_password_change_message( locale='en') remaining_attempts = user.update_signin_attempts( failed=True) message = f'invalid username and/or password##{remaining_attempts}#{password_change_message}' raise drf_exception.AuthenticationFailed(message) else: return None except get_user_model().DoesNotExist as ex: raise drf_exception.AuthenticationFailed( f'user not found#{ex.args[0]}') # sign-in was a success. Reset sign-in attempts to zero user.update_signin_attempts(failed=False, ) return user
def lipa_na_mpesa(self, amount, mobile_number, transaction_type=None, account_reference=None, transaction_description=None, ): """ Lipa na M-Pesa Online Payment API is used to initiate a M-Pesa transaction on behalf of a customer using STK Push. This is the same technique mySafaricom App uses whenever the app is used to make payments """ timestamp = datetime.utcnow().strftime('%Y%m%d%H%M%S') password = str(base64.encodebytes(bytes(f'{self.short_code}{self.__LNM.get("PASSKEY")}{timestamp}', 'utf-8')), 'utf-8') transaction_desc = transaction_description if valid_str(transaction_description) else 'test' acc_ref = account_reference if valid_str(account_reference) else 'test' return self._send_transaction_request( url=r'https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest', request_data={ "BusinessShortCode": self.short_code, # The organization shortcode used to receive the transaction "Password": re.sub(r'\n', '', password), "Timestamp": timestamp, "TransactionType": self.get_command_type_or_fail(transaction_type), "Amount": self.get_sanitized_amount(amount), "PartyA": self.get_sanitized_msisdn(mobile_number), # The MSISDN sending the funds "PartyB": self.short_code, # The organization shortcode receiving the funds "PhoneNumber": mobile_number, # The MSISDN sending the funds "CallBackURL": self._create_callback_url(), "AccountReference": acc_ref, # Used with M-Pesa PayBills "TransactionDesc": transaction_desc, # A description of the transaction }, )
def wrap_error_response(ed, response): msg, d_msg, metadata, delimiter = ed, None, None, '#' if delimiter in ed: msg, d_msg = tuple(ed.split(delimiter, 1)) if delimiter in d_msg: d_msg, metadata = tuple(d_msg.split(delimiter, 1)) msg = msg if valid_str(msg) else None d_msg = d_msg if valid_str(d_msg) else None metadata = metadata if valid_str(metadata) else None return APIResponse( message=msg, status_code=response.status_code, debug_message=text_with_dated_timestamps(d_msg), metadata=metadata, )
def authenticate(self, request): address_header_payload = request.META.get( 'HTTP_X_Forwarded_For', request.META.get('REMOTE_ADDR', None)) request_url = str(request.build_absolute_uri()).lower() auth = None authorization_header = drf_auth.get_authorization_header(request) auth_header = authorization_header.decode() if isinstance( authorization_header, bytes) else authorization_header if valid_str(auth_header): # an authorization header was provided # separate auth-scheme/auth-type(e.g. Bearer, Token, Basic) from auth-data/auth-payload(e.g. base64url # encoding of username & password combination for Basic auth or Bearer|Token value/data) auth_scheme_and_payload = auth_header.split() if len(auth_scheme_and_payload) > 1: auth_response = self.get_user_from_basic_or_token_auth_scheme( auth_scheme_and_payload, request_url) if isinstance(auth_response, tuple): user, auth = auth_response else: return auth_response # unknown/unsupported authentication scheme else: return None # unknown/unsupported authentication scheme else: # attempt to authenticate from POST-request data username, password = self.get_post_request_username_and_password( request=request) user = self.get_user_with_username_and_password(username=username, password=password) return self.__get_wrapped_authentication_response( address_header_payload, user, auth, request_url)
def register_url(self, response_type=None, confirmation_url=None, validation_url=None): response_type = response_type.lower().capitalize() if valid_str(response_type) else 'Canceled' response_types = ", ".join(self.RESPONSE_TYPES) assert response_type in self.RESPONSE_TYPES, f'Response type must be one of \'{response_types}\'' confirmation_url = confirmation_url if valid_str(confirmation_url) else self._create_callback_url() validation_url = validation_url if valid_str(validation_url) else self._DEFAULT_CALLBACK_URL return self._send_transaction_request( url=r'https://sandbox.safaricom.co.ke/mpesa/c2b/v1/registerurl', request_data={ "ShortCode": self.short_code, "ResponseType": response_type, "ConfirmationURL": confirmation_url, # replace 'mpesa' word with 'mpay' "ValidationURL": re.sub(r'[Mm]-*[Pp][Ee][Ss][Aa]', 'mpay', validation_url), }, )
def resize_image(photo_path, height=450, width=450, square: bool = True): if valid_str(photo_path): image = Image.open(photo_path) smallest_dimen = min(width, height) smallest_img_dimen = min(image.width, image.height) resize_dimen = min(smallest_dimen, smallest_img_dimen) output_size = (resize_dimen, resize_dimen) if square else (width, height) image.thumbnail(output_size) image.save(photo_path)
def __init__( self, environment='sandbox', merchant_id=__MERCHANT_ID, public_key=__PUBLIC_KEY, private_key=__PRIVATE_KEY, ): environment = environment if valid_str(environment) else 'sandbox' merchant_id = merchant_id if valid_str( merchant_id) else self.__MERCHANT_ID public_key = public_key if valid_str(public_key) else self.__PUBLIC_KEY private_key = private_key if valid_str( private_key) else self.__PRIVATE_KEY self.gateway = braintree.BraintreeGateway( braintree.Configuration( braintree.Environment.parse_environment(environment), merchant_id=merchant_id, public_key=public_key, private_key=private_key))
def __init__( self, access_token=__ACCESS_TOKEN, production: bool = False, ): access_token = access_token if valid_str( access_token) else self.__ACCESS_TOKEN self.client = Client( access_token=access_token, environment='production' if production else 'sandbox', )
def wrap_error_response_data(ed, **kwargs): errors = kwargs.get('errors', tuple()) d_msg = errors[0] if len(errors) > 0 else None msg, delimiter, extra_errors = ed, '#', None if valid_str(ed) and delimiter in ed: msg, d_msg = tuple(ed.split(delimiter, 1)) if delimiter in d_msg: d_msg, metadata = tuple(d_msg.split(delimiter, 1)) extra_errors = metadata.split(delimiter) if metadata else None detail = kwargs.get('detail') if valid_str(msg) and valid_str(detail) and msg.lower() == detail.lower(): # avoids duplicate showing of the same error message kwargs['detail'] = None # combine extra errors with existing errors to produce a list of unique errors kwargs['errors'] = list( set(errors + tuple(extra_errors))) if extra_errors and isinstance( errors, tuple) else errors kwargs['debug_message'] = d_msg if d_msg != msg else None kwargs['error'] = msg return ErrorResponse(**kwargs, ).data
def exception_handler(exception, context): response = views.exception_handler(exception, context) response = Response( data={ 'detail': '#'.join([x for x in exception.args if valid_str(x)]), # only join strings }, status=status.HTTP_400_BAD_REQUEST) if response is None else response error_data = response.data ed = error_data.get('detail') if isinstance(error_data, dict) else None error_data = wrap_error_response_data(ed, errors=exception.args, **error_data) if 'detail' in response.data: del response.data['detail'] # update rest_frameworks error data response.data.update(error_data) return response
def post(request, format=None): user, data = request.user, request.data # question can be identified by the question text itself or it's id both of which are # expected to be unique answer = data.get('answer') or data.get('question_answer') question_id = data.get('question') or data.get('id') or data.get( 'question_id') question = SecurityQuestion.objects.filter( Q(question=question_id) | Q(id=question_id), ).first() if question and valid_str(answer): # question was found and answer is a valid string user.add_security_question(question, answer) data, status_code = { 'success': 'security question added successfully' }, status.HTTP_200_OK else: data = {'error': f"invalid {'answer' if question else 'question'}"} data, status_code = data, status.HTTP_400_BAD_REQUEST return Response(data, status=status_code or status.HTTP_200_OK)
def __init__(self, api_key=__API_KEY): stripe.api_key = api_key if valid_str(api_key) else self.__API_KEY
def get_sanitized_msisdn(number): mobile_number = re.sub(r'^0|\+254', '254', number) if not valid_str(mobile_number, 12): raise Exception(f"invalid mobile number '{mobile_number}'") return mobile_number
def __init__(self, short_code=None, **kwargs): self.short_code = self.__LNM.get('SHORTCODE') if not valid_str(short_code) else short_code super(_MpesaC2BPay, self).__init__(**kwargs)
def get_command_type_or_fail(self, command_id=COMMAND_IDS[0]): command_id = command_id if valid_str(command_id) else self.COMMAND_IDS[0] assert command_id in self.COMMAND_IDS, f'Command must be one of \'{", ".join(self.COMMAND_IDS)}\'' return command_id