def obj_create(self, bundle, request, **kwargs): form = EventForm(bundle.data, request_encoding=request.encoding) if not form.is_valid(): log.info("Event invalid.") raise self.form_errors(form) notification = form.cleaned_data["notification"] transaction = form.cleaned_data["transaction"] # TODO: use token checker here when supported. if notification["new_status"] != transaction.status: old_status = transaction.status transaction.status = notification["new_status"] transaction.save() log_cef( "Transaction change success", request, severity=7, cs6Label="old", cs6=STATUSES_INVERTED[old_status], cs7Label="new", cs7=STATUSES_INVERTED[transaction.status], ) log.info("Transaction {0} changed to {1} from {2}".format(transaction.pk, transaction.status, old_status)) return bundle
def create(self, request): form = EventForm(request.DATA) param = form.data.get('param') if not form.is_valid(): log.info('Notification invalid: {0}'.format(param)) return self.form_errors([form]) cleaned = form.cleaned_data transaction = cleaned['param'] # Verify this against Boku, this will raise errors if there's # an issue. verify(transaction, cleaned['amount'], cleaned['currency']) old_status = transaction.status # For the moment assume that all notifications that come in # are complete. transaction.status = STATUS_COMPLETED transaction.amount = cleaned['amount'] transaction.currency = cleaned['currency'] transaction.uid_support = cleaned['trx_id'] transaction.save() log_cef('Transaction change success', request, severity=7, cs6Label='old', cs6=STATUSES_INVERTED[old_status], cs7Label='new', cs7=STATUSES_INVERTED[transaction.status]) log.info('Transaction {0} changed to {1} from {2}' .format(transaction.pk, transaction.status, old_status)) return HttpResponse(200, '')
def verify_pin(request): form = PinForm(data=request.DATA) if form.is_valid(): buyer = form.cleaned_data['buyer'] valid = False locked = False if buyer.pin_confirmed: # Note that the incr_lockout and clear_lockout methods # trigger saves on the object. You should not do a save # in this view as well for fear of stomping on the save # caused by those methods. if buyer.locked_out: log_cef('Attempted access to locked out account: %s' % buyer.uuid, request, severity=1) locked = True else: valid = buyer.pin == form.cleaned_data['pin'] if not valid: locked = buyer.incr_lockout() if locked: locked = True log_cef('Locked out account: %s' % buyer.uuid, request, severity=1) else: buyer.clear_lockout(clear_was_locked=True) output = VerifiedSerializer(instance=buyer, valid=valid, locked=locked) return Response(output.data) raise FormError(form.errors)
def clean(self): fields = self.fields.keys() # If the provider is already set, don't allow it to be changed. if self.old.get('provider'): del fields[fields.index('provider')] keys = set(self.data.keys()).difference(set(fields)) if keys: raise forms.ValidationError( 'Cannot alter fields: {0}'.format(', '.join(keys))) old_text = constants.STATUSES_INVERTED.get(self.old['status']) new_text = constants.STATUSES_INVERTED.get( self.data.get('status', self.old['status'])) try: check_status(self.old, self.data) except forms.ValidationError: log_cef('Transaction change failure', self.request, severity=7, cs6Label='old', cs6=old_text, cs7Label='new', cs7=new_text) raise if new_text != old_text: log_cef('Transaction change success', self.request, cs6Label='old', cs6=old_text, cs7Label='new', cs7=new_text) return self.cleaned_data
def obj_create(self, bundle, request, **kwargs): form = EventForm(bundle.data, request_encoding=request.encoding) if not form.is_valid(): log.info('Event invalid.') raise self.form_errors(form) notification = form.cleaned_data['notification'] transaction = form.cleaned_data['transaction'] # TODO: use token checker here when supported. if notification['new_status'] != transaction.status: old_status = transaction.status transaction.status = notification['new_status'] transaction.save() log_cef('Transaction change success', request, severity=7, cs6Label='old', cs6=STATUSES_INVERTED[old_status], cs7Label='new', cs7=STATUSES_INVERTED[transaction.status]) log.info('Transaction {0} changed to {1} from {2}'.format( transaction.pk, transaction.status, old_status)) return bundle
def clean(self): fields = self.fields.keys() # If the provider is already set, don't allow it to be changed. if self.old.get('provider'): del fields[fields.index('provider')] keys = set(self.data.keys()).difference(set(fields)) if keys: raise forms.ValidationError('Cannot alter fields: {0}'.format( ', '.join(keys))) old_text = constants.STATUSES_INVERTED.get(self.old['status']) new_text = constants.STATUSES_INVERTED.get( self.data.get('status', self.old['status'])) try: check_status(self.old, self.data) except forms.ValidationError: log_cef('Transaction change failure', self.request, severity=7, cs6Label='old', cs6=old_text, cs7Label='new', cs7=new_text) raise if new_text != old_text: log_cef('Transaction change success', self.request, cs6Label='old', cs6=old_text, cs7Label='new', cs7=new_text) return self.cleaned_data
def reset_confirm_pin(request): form = PinForm(data=request.DATA) if form.is_valid(): buyer = form.cleaned_data['buyer'] confirmed = False if buyer.locked_out: log_cef('Attempted access to locked out account: %s' % buyer.uuid, request, severity=1) else: if buyer.new_pin == form.cleaned_data['pin']: buyer.pin = form.cleaned_data['pin'] buyer.new_pin = None buyer.needs_pin_reset = False buyer.pin_confirmed = True buyer.pin_was_locked_out = False buyer.save() confirmed = True output = ConfirmedSerializer(instance=buyer, confirmed=confirmed) return Response(output.data) raise FormError(form.errors)
def obj_create(self, bundle, request, **kwargs): form = NotificationForm(request, bundle.data) bill_conf_id = form.data.get("billing_config_id") log.info( "Received notification for billing_config_id %r: " "bango_response_code: %r; bango_response_message: %r; " "bango_trans_id: %r; bango_token: %r; moz_transaction: %r; " "amount: %r; currency: %r" % ( bill_conf_id, form.data.get("bango_response_code"), form.data.get("bango_response_message"), form.data.get("bango_trans_id"), form.data.get("bango_token"), form.data.get("moz_transaction"), form.data.get("amount"), form.data.get("currency"), ) ) if not form.is_valid(): log.info(u"Notification invalid: %s" % bill_conf_id) raise self.form_errors(form) trans = form.cleaned_data["moz_transaction"] states = {OK: ["completed", STATUS_COMPLETED], CANCEL: ["cancelled", STATUS_CANCELLED]} message, state = states.get(form.cleaned_data["bango_response_code"], ["failed", STATUS_FAILED]) log.info(u"Transaction %s: %s" % (message, trans.uuid)) statsd.incr("bango.notification.%s" % message) statsd.decr("solitude.pending_transactions") log_cef( "Transaction change success", request, severity=7, cs6Label="old", cs6=STATUSES_INVERTED.get(trans.status), cs7Label="new", cs7=STATUSES_INVERTED.get(state), ) trans.status = state # This is the id for the actual transaction, useful for refunds. trans.uid_support = form.cleaned_data["bango_trans_id"] # The price/currency may be empty for error notifications. trans.amount = form.cleaned_data["amount"] trans.currency = form.cleaned_data["currency"] # Set carrier and region. if form.cleaned_data.get("network"): trans.carrier = form.cleaned_data["carrier"] trans.region = form.cleaned_data["region"] trans.save() return bundle
def obj_create(self, bundle, request, **kwargs): form = NotificationForm(request, bundle.data) bill_conf_id = form.data.get('billing_config_id') log.info( 'Received notification for billing_config_id %r: ' 'bango_response_code: %r; bango_response_message: %r; ' 'bango_trans_id: %r; bango_token: %r; moz_transaction: %r; ' 'amount: %r; currency: %r' % (bill_conf_id, form.data.get('bango_response_code'), form.data.get('bango_response_message'), form.data.get('bango_trans_id'), form.data.get('bango_token'), form.data.get('moz_transaction'), form.data.get('amount'), form.data.get('currency'))) if not form.is_valid(): log.info(u'Notification invalid: %s' % bill_conf_id) raise self.form_errors(form) trans = form.cleaned_data['moz_transaction'] states = { OK: ['completed', STATUS_COMPLETED], CANCEL: ['cancelled', STATUS_CANCELLED] } message, state = states.get(form.cleaned_data['bango_response_code'], ['failed', STATUS_FAILED]) log.info(u'Transaction %s: %s' % (message, trans.uuid)) statsd.incr('bango.notification.%s' % message) statsd.decr('solitude.pending_transactions') log_cef('Transaction change success', request, severity=7, cs6Label='old', cs6=STATUSES_INVERTED.get(trans.status), cs7Label='new', cs7=STATUSES_INVERTED.get(state)) trans.status = state # This is the id for the actual transaction, useful for refunds. trans.uid_support = form.cleaned_data['bango_trans_id'] # The price/currency may be empty for error notifications. trans.amount = form.cleaned_data['amount'] trans.currency = form.cleaned_data['currency'] # Set carrier and region. if form.cleaned_data.get('network'): trans.carrier = form.cleaned_data['carrier'] trans.region = form.cleaned_data['region'] trans.save() return bundle
def notification(request): view = BangoResource() form = NotificationForm(request, request.DATA) bill_conf_id = form.data.get('billing_config_id') log.info('Received notification for billing_config_id %r: ' 'bango_response_code: %r; bango_response_message: %r; ' 'bango_trans_id: %r; bango_token: %r; moz_transaction: %r; ' 'amount: %r; currency: %r' % (bill_conf_id, form.data.get('bango_response_code'), form.data.get('bango_response_message'), form.data.get('bango_trans_id'), form.data.get('bango_token'), form.data.get('moz_transaction'), form.data.get('amount'), form.data.get('currency'))) if not form.is_valid(): log.info(u'Notification invalid: %s' % bill_conf_id) return view.form_errors(form) trans = form.cleaned_data['moz_transaction'] states = {OK: ['completed', STATUS_COMPLETED], CANCEL: ['cancelled', STATUS_CANCELLED]} message, state = states.get(form.cleaned_data['bango_response_code'], ['failed', STATUS_FAILED]) log.info(u'Transaction %s: %s' % (message, trans.uuid)) statsd.incr('bango.notification.%s' % message) statsd.decr('solitude.pending_transactions') log_cef('Transaction change success', request, severity=7, cs6Label='old', cs6=STATUSES_INVERTED.get(trans.status), cs7Label='new', cs7=STATUSES_INVERTED.get(state)) trans.status = state # This is the id for the actual transaction, useful for refunds. trans.uid_support = form.cleaned_data['bango_trans_id'] # The price/currency may be empty for error notifications. trans.amount = form.cleaned_data['amount'] trans.currency = form.cleaned_data['currency'] # Set carrier and region. if form.cleaned_data.get('network'): trans.carrier = form.cleaned_data['carrier'] trans.region = form.cleaned_data['region'] trans.save() return Response(status=204)
def _check_for_tampering(self, tok, cleaned_data): """ Use the token service to see if any data has been tampered with. """ cli = get_client().client('token_checker') with statsd.timer('solitude.bango.request.checktoken'): true_data = cli.service.CheckToken(token=tok) if true_data.ResponseCode is None: # Any None field means the token was invalid. # This might happen if someone tampered with Token= itself in the # query string or if Bango's server was messed up. statsd.incr('solitude.bango.response.checktoken_fail') msg = 'Invalid Bango token: {0}'.format(tok) log.error(msg) raise forms.ValidationError(msg) for form_fld, true_attr in ( ('moz_signature', 'Signature'), ('moz_transaction', 'MerchantTransactionId'), ('bango_response_code', 'ResponseCode'), ('bango_response_message', 'ResponseMessage'), ('bango_trans_id', 'BangoTransactionId'), ): true_val = getattr(true_data, true_attr) # Make sure the true value is a str() just like it is on the query # string. true_val = str(true_val) form_val = cleaned_data.get(form_fld) # Since moz_transaction is an object, get the real value. if form_val and form_fld == 'moz_transaction': form_val = form_val.uuid if form_val and form_val != true_val: msg = ('Bango query string tampered with: field: {field}; ' 'fake: {fake}; true: {true}'.format(field=form_fld, fake=form_val, true=true_val)) log_cef(msg, self._request, severity=3) log.info(msg) log.info('token check response: {true_data}'.format( true_data=true_data)) # Completely reject the form since it was tampered with. raise forms.ValidationError( 'Form field {0} has been tampered with. ' 'True: {1}; fake: {2}'.format(form_fld, true_val, form_val))
def obj_create(self, bundle, request=None, **kwargs): buyer = self.get_data(bundle) pin = bundle.data.pop('pin') if buyer.locked_out: log_cef('Attempted access to locked out account: %s' % buyer.uuid, request, severity=1) bundle.obj.confirmed = False return bundle if buyer.new_pin == pin: buyer.pin = pin buyer.new_pin = None buyer.needs_pin_reset = False buyer.pin_confirmed = True buyer.save() bundle.obj.confirmed = True else: bundle.obj.confirmed = False return bundle
def _check_for_tampering(self, tok, cleaned_data): """ Use the token service to see if any data has been tampered with. """ cli = get_client().client('token_checker') with statsd.timer('solitude.bango.request.checktoken'): true_data = cli.service.CheckToken(token=tok) if true_data.ResponseCode is None: # Any None field means the token was invalid. # This might happen if someone tampered with Token= itself in the # query string or if Bango's server was messed up. statsd.incr('solitude.bango.response.checktoken_fail') msg = 'Invalid Bango token: {0}'.format(tok) log.error(msg) raise forms.ValidationError(msg) for form_fld, true_attr in ( ('moz_signature', 'Signature'), ('moz_transaction', 'MerchantTransactionId'), ('bango_response_code', 'ResponseCode'), ('bango_response_message', 'ResponseMessage'), ('bango_trans_id', 'BangoTransactionId'),): true_val = getattr(true_data, true_attr) # Make sure the true value is a str() just like it is on the query # string. true_val = str(true_val) form_val = cleaned_data.get(form_fld) # Since moz_transaction is an object, get the real value. if form_val and form_fld == 'moz_transaction': form_val = form_val.uuid if form_val and form_val != true_val: msg = ('Bango query string tampered with: field: {field}; ' 'fake: {fake}; true: {true}' .format(field=form_fld, fake=form_val, true=true_val)) log_cef(msg, self._request, severity=3) log.info(msg) log.info('token check response: {true_data}' .format(true_data=true_data)) # Completely reject the form since it was tampered with. raise forms.ValidationError( 'Form field {0} has been tampered with. ' 'True: {1}; fake: {2}'.format( form_fld, true_val, form_val))
class Event(viewsets.ViewSet, BaseAPIView): """ Process a Boku server to server notification. See Boku Technical Documentation for an example of the data being sent in. """ def create(self, request): form = EventForm(request.DATA) param = form.data.get('param') if not form.is_valid(): log.info('Notification invalid: {0}'.format(param)) return self.form_errors([form]) cleaned = form.cleaned_data transaction = cleaned['param'] # Verify this against Boku, this will raise errors if there's # an issue. log.info('Verifying notification for Boku transaction id: {0}' .format(transaction)) status = STATUS_COMPLETED try: verify(cleaned['trx_id'], cleaned['amount'], cleaned['currency']) except BokuException, exc: log.info('Got non-zero Boku API response: ' '{exc}; code={exc.result_code}; msg={exc.result_msg}' .format(exc=exc)) # Boku will return non-zero error codes to indicate certain # transaction states. These states mean that the notification # itself is valid. if exc.result_code in TRANS_STATUS_FROM_VERIFY_CODE: status = TRANS_STATUS_FROM_VERIFY_CODE[exc.result_code] log.info('got Boku transaction status {s} from code {c}' .format(s=status, c=exc.result_code)) else: raise old_status = transaction.status transaction.status = status transaction.amount = cleaned['amount'] transaction.currency = cleaned['currency'] transaction.uid_support = cleaned['trx_id'] transaction.save() log_cef('Transaction change success', request, severity=7, cs6Label='old', cs6=STATUSES_INVERTED[old_status], cs7Label='new', cs7=STATUSES_INVERTED[transaction.status]) log.info('Transaction {0} changed to {1} from {2}' .format(transaction.pk, transaction.status, old_status)) return HttpResponse(200, '')
def obj_create(self, bundle, request=None, **kwargs): buyer = self.get_data(bundle) if buyer.pin_confirmed: if buyer.locked_out: log_cef('Attempted access to locked out account: %s' % buyer.uuid, request, severity=1) bundle.obj.locked = True return bundle bundle.obj.valid = buyer.pin == bundle.data.pop('pin') if not bundle.obj.valid: locked = buyer.incr_lockout() if locked: bundle.obj.locked = True log_cef('Locked out account: %s' % buyer.uuid, request, severity=1) if bundle.obj.valid and buyer.pin_failures: buyer.clear_lockout() else: bundle.obj.valid = False return bundle
def event(request): view = BangoResource() form = EventForm(request.DATA, request_encoding=request.encoding) if not form.is_valid(): log.info('Event invalid.') return view.form_errors(form) notification = form.cleaned_data['notification'] transaction = form.cleaned_data['transaction'] if notification['new_status'] != transaction.status: old_status = transaction.status transaction.status = notification['new_status'] transaction.save() log_cef('Transaction change success', request, severity=7, cs6Label='old', cs6=STATUSES_INVERTED[old_status], cs7Label='new', cs7=STATUSES_INVERTED[transaction.status]) log.info('Transaction {0} changed to {1} from {2}' .format(transaction.pk, transaction.status, old_status)) return Response(status=204)
def error_500(request): exception = sys.exc_info()[1] log_cef(str(exception), request, severity=3) return response(request, status=500, data={'error': exception.__class__.__name__})