def topup(data: TokenData): params: TopUp = TopUp(request.args) if not params.validate(): return jsonify({'message': 'Not valid arguments!'}), 400 curr_str: str = f"{params.currency.data.lower()}_amt" session.query(Balance).filter(Balance.customer_id == data.customer_id).update( {curr_str: (getattr(Balance, curr_str) + params.amount.data)}) session.flush() session.commit() return jsonify({'message': 'TopUp successful!'}), 200
def reset_with_token(data: TokenData): if request.method == 'POST': form: ResetPass = ResetPass(request.form) if not form.validate(): return jsonify({'message': 'Not valid arguments!'}), 400 # get hash of new password hashed_pass: str = get_hash(form.password1.data) # Update user's hash pass in DB session.query(Password).filter(Password.customer_id == data.customer_id).update({"user_pass": hashed_pass}) session.flush() session.commit() return jsonify({'resp': 'success'}), 200 return render_template('reset_pass.html'), 200
def callback(ch, method, properties, body): data: InputJson = InputJson(**json.loads(body)) print(" [x] Received %r" % body) # Update user's balance session.query(Balance).filter( Balance.customer_id == data.customer_id).update({ data.from_str: (getattr(Balance, data.from_str) - data.amount_subtract), data.to_str: (getattr(Balance, data.to_str) + data.amount_add) }) session.commit() ch.basic_ack( delivery_tag=method.delivery_tag ) # Answer to RabbitMQ that all is successful => Message is marked as acknowledged
def own_transfer(data: TokenData): # Get params params: CurrencyChangeSchema = CurrencyChangeSchema(request.args) if not params.validate(): return jsonify({'message': 'Not valid arguments!'}), 400 # Get rate for given pair of currencies response = requests.get(f'http://{HOST_CURR_SERV}:{PORT_CURR_SERV}/get_rates', params={'curr_from': params.curr_from.data.upper(), 'curr_to': params.curr_to.data.upper()}) if response.status_code == 404: return jsonify({'message': 'Internal currency service is down'}), 500 rate: float = response.json()['rate'] add: float = round(params.amount.data * rate, 2) from_str: str = f"{params.curr_from.data.lower()}_amt" to_str: str = f"{params.curr_to.data.lower()}_amt" # Check if user has enough money on his balance cust: Balance = session.query(Balance).filter(Balance.customer_id == data.customer_id).first() if getattr(cust, from_str) < params.amount.data: return jsonify({'message': 'Not enough money!'}), 400 publish_message(json_body={'customer_id': data.customer_id, 'from_str': from_str, 'amount_subtract': params.amount.data, 'to_str': to_str, 'amount_add': add}, queue='own_transaction') return jsonify({'message': 'Success'}), 200
def decorator(*args, **kwargs): # hash_pass = session.query(Password).filter(Password.user_email == data).first() token = None # check if token is in headers if 'key' in request.headers: token = request.headers['key'] elif f.__name__ == 'reset_with_token' and 'token' in request.args: token = request.args['token'] if not token: return jsonify({'message': 'a valid token is missing'}), 401 # Try to decode token (only get payload, header and verify signature) # How does Electronic signature work: # 1. We have message X # 2. We calculate hash(x) which is called control sum # 3. We encrypt control sum with private key RSA, i.e. encode(hash(X)) # 4. Receiver gets X + control sum # 5. Receiver decodes control sum: decode(encode(hash(X)))=hash(X) # 6. Receiver calculates hash(X), where X is received message # 7. Compare result from 6 and 7. If equal => legitimate try: # Get Payload data = jwt.decode( token, pub_key, algorithms=[ALG]) # options={"verify_exp": False} except ExpiredSignatureError: return jsonify( {'message': 'Signature has expired! Try auth method'}), 401 except: return jsonify({'message': 'token is invalid'}), 401 # Transfer payload to namedtuple data = TokenData(**data) # This is check only for temporary tokens for Forgot Password. Once password is changed => hash of signature # will change. We check that 'signature' parameter in payload matches with hash(customer_id + user_pass_hash + # creation_date + token_uuid) Source: # https://security.stackexchange.com/questions/153746/one-time-jwt-token-with-jwt-id-claim if data.temp_access: cust: Password = session.query(Password).filter( Password.customer_id == data.customer_id).first() token_uuid = jwt.get_unverified_header(token)['kid'] if not check_hash( str(data.customer_id) + cust.user_pass + str(data.iat) + token_uuid, data.signature): return jsonify({ 'message': 'Token is invalid as you changed password' }), 401 if f.__name__ != 'reset_with_token' and data.temp_access: return jsonify( {'message': 'Token does not have access to this method!'}), 401 return f(data, *args, **kwargs)
def callback(ch, method, properties, body): data: InputJson = InputJson(**json.loads(body)) print(" [x] Received %r" % body) # Update balances of customers, on average below code takes 0.46 sec # Source: https://stackoverflow.com/questions/54365873/sqlalchemy-update-multiple-rows-in-one-transaction bal_ = getattr(Balance, data.currency) session.query(Balance).filter( Balance.customer_id.in_([ data.customer_id, data.customer_id_to ])).update( { bal_: case( { data.customer_id: bal_ - data.amount, # getattr(Balance, data.currency) data.customer_id_to: bal_ + data.amount }, value=Balance.customer_id) }, synchronize_session=False) # Commented another method which first of all does Select and then inside Python changes values and then updates # values. On average below code takes 0.76 sec """ new_bal: List[Balance] = session.query(Balance).filter( (Balance.customer_id == data.customer_id) | (Balance.customer_id == customer_id_to)).all() for bal in new_bal: if bal.customer_id == data.customer_id: setattr(bal, currency, getattr(bal, currency) - amount) else: setattr(bal, currency, getattr(bal, currency) + amount) """ # Create transaction and add it to Transactions table new_transaction = Transaction(customer_id_from=data.customer_id, customer_id_to=data.customer_id_to, **{data.currency: data.amount}) session.add_all([new_transaction]) session.commit() ch.basic_ack( delivery_tag=method.delivery_tag ) # Answer to RabbitMQ that all is successful => Message is marked as acknowledged
def get_me(data: TokenData): custs: List[Customer] = session.query(Customer).filter(Customer.id == data.customer_id) results = [ { "first_name": cust.first_name, "second_name": cust.second_name, "join_date": cust.join_date } for cust in custs] return jsonify({"count": len(results), "custs": results}), 200
def get_all_custs(data: TokenData): # custs = session.query(Customer).filter(Customer.first_name == 'Dmitry') custs = session.query(Customer) results = [ { "first_name": cust.first_name, "second_name": cust.second_name, "join_date": cust.join_date } for cust in custs] return jsonify({"count": len(results), "custs": results}), 200
def delete_user(data: TokenData): if not request.args.get('customer_id', type=int): return jsonify({'message': 'Not valid request!'}), 400 if data.access_type != 1: return jsonify({'resp': "You don't have rights for this!"}), 400 customer_id: int = request.args.get('customer_id') rowcount = session.query(Customer).filter(Customer.id == customer_id).delete() session.commit() if rowcount > 0: return jsonify({'resp': 'User removed!'}), 200 else: return jsonify({'resp': 'Not found such user!'}), 400
def get_trans(data: TokenData): transactions: List[Transaction] = session.query(Transaction).filter( (Transaction.customer_id_to == data.customer_id) | (Transaction.customer_id_from == data.customer_id)).all() results = [ {'customer_id_from': trans.customer_id_from, 'customer_id_to': trans.customer_id_to, 'usd_amt': trans.usd_amt, 'eur_amt': trans.eur_amt, 'aed_amt': trans.aed_amt } for trans in transactions] return jsonify({'results': results}), 200
def forgot(form): """ Form for Forgot password. It generates one time link to method reset_with_token """ # Check if customer exists in Password table cust: Password = session.query(Password).filter(Password.user_email == form.email.data).first() if cust: token: str = get_token(cust.user_email, cust.customer_id, cust.user_pass, temp_access=True) recover_url = url_for( 'reset_with_token', token=token, _external=True) return jsonify({'recover_link': recover_url}), 200 else: error: str = 'User not found!' return render_template('mainpage.html', message=error), 401
def auth(form): """ Auth method. Called if previous token has expired. Checks that user exist in DB and password matches and creates new token """ cust_pass: Password = session.query(Password).filter(Password.user_email == form.email.data).join(Customer, isouter=True).first() if not cust_pass: error = 'Invalid Credentials. User not found. Please try again.' return render_template('mainpage.html', message=error), 401 if not check_hash(form.password.data, cust_pass.user_pass): error = 'Invalid Credentials. Please try again.' return render_template('mainpage.html', message=error), 401 return jsonify( {'token': get_token(cust_pass.user_email, cust_pass.customer_id, form.password.data, cust_pass.customer.access_type, temp_access=False).decode('utf-8')}), 200
def do_transaction(data: TokenData): params: TransactionSchema = TransactionSchema(request.args) if not params.validate(): return jsonify({'message': 'Not valid arguments!'}), 400 # Get parameters from args amount: float = params.amount.data currency: str = f'{params.currency.data.lower()}_amt' # Check if customer has enough balance balance: Balance = session.query(Balance).filter(Balance.customer_id == data.customer_id).first() if getattr(balance, currency) < amount: return jsonify({'message': 'Not enough money on your balance!'}), 400 # Publish message to RabbitMQ: publish_message({'customer_id': data.customer_id, 'customer_id_to': params.customer_id_to.data, 'amount': amount, 'currency': currency}, queue='transactions') return jsonify({'message': 'Transaction made!'}), 200
def my_bal(data: TokenData): balance: Balance = session.query(Balance).filter(Balance.customer_id == data.customer_id).first() results: dict = {'aed_amt': balance.aed_amt, 'usd_amt': balance.usd_amt, 'eur_amt': balance.eur_amt} return jsonify(results), 200
def validate_customer_id_to(self, customer_id_to): if not session.query(Customer).filter( Customer.id == customer_id_to.data).first(): raise ValidationError('User does not exist!')
def validate_email(self, email): if session.query(Password).filter( Password.user_email == email.data).first(): raise ValidationError('User alredy exists!')