예제 #1
0
    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
예제 #2
0
 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
         },
     )
예제 #3
0
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,
    )
예제 #4
0
 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)
예제 #5
0
 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),
         },
     )
예제 #6
0
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)
예제 #7
0
 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))
예제 #8
0
 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',
     )
예제 #9
0
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
예제 #10
0
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
예제 #11
0
 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)
예제 #12
0
 def __init__(self, api_key=__API_KEY):
     stripe.api_key = api_key if valid_str(api_key) else self.__API_KEY
예제 #13
0
 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
예제 #14
0
 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)
예제 #15
0
 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