def set_bunq_oauth_response(): """ Handles the bunq OAuth redirect """ try: code = request.args["code"] if len(code) != 64: print("Invalid code: ", code) return render_template("message.html", msgtype="danger", msg=\ 'Invalid code! <br><br>'\ '<a href="/">Click here to try again</a>') url = "https://api.oauth.bunq.com/v1/token?grant_type="\ "authorization_code&code={}&redirect_uri={}"\ "&client_id={}&client_secret={}"\ .format(code, request.url_root + "auth", storage.retrieve("config", "bunq_client_id")["value"], storage.retrieve("config", "bunq_client_secret")["value"] ) req = requests.post(url) key = req.json()["access_token"] allips = storage.retrieve("config", "bunq_allips")["value"] bunq.install(key, allips=allips) util.save_bunq_security_mode("OAuth") util.save_app_mode("master") util.retrieve_and_save_bunq_userid() util.update_bunq_accounts() return render_template("message.html", msgtype="success", msg=\ 'OAuth successfully setup <br><br>'\ '<a href="/">Click here to return home</a>') except Exception: traceback.print_exc() return render_template("message.html", msgtype="danger", msg=\ 'An unknown exception occurred. See the logs. <br><br>'\ '<a href="/">Click here to return home</a>')
def user_login(): """ Handles password login """ try: hashfunc = hashlib.sha256() hashfunc.update(request.form["password"].encode("utf-8")) stored_hash = storage.retrieve("config", "password_hash") if stored_hash is not None: salt = storage.retrieve("config", "password_salt")["value"] hashfunc.update(salt.encode('ascii')) calc_hash = base64.b64encode(hashfunc.digest()).decode('ascii') if calc_hash != stored_hash["value"]: return render_template("message.html", msgtype="danger", msg=\ 'Invalid password! - To try again, '\ '<a href="/">click here</a>') else: # first time login, so store the password salt = secrets.token_urlsafe(32) hashfunc.update(salt.encode('ascii')) calc_hash = base64.b64encode(hashfunc.digest()).decode('ascii') storage.store("config", "password_salt", {"value": salt}) storage.store("config", "password_hash", {"value": calc_hash}) session = secrets.token_urlsafe(32) util.save_session_cookie(session) resp = make_response(redirect('/')) resp.set_cookie("session", session) return resp except Exception: traceback.print_exc() return render_template("message.html", msgtype="danger", msg=\ 'An unknown exception occurred. See the logs. <br><br>'\ '<a href="/">Click here to return home</a>')
def get_private_key(): """ Retrieves my private key from cache or the datastore """ global _PRIVATE_KEY if _PRIVATE_KEY is None: entity = storage.retrieve("config", "bunq_private_key_1") private_1 = base64.a85decode(entity["value"]) entity = storage.retrieve("config", "bunq_private_key_2") private_2 = base64.a85decode(entity["value"]) _PRIVATE_KEY = serialization.load_pem_private_key( private_1 + private_2, password=None, backend=default_backend()) return _PRIVATE_KEY
def get_install_token(): """ Retrieves the install token from cache or the datastore """ global _INSTALL_TOKEN if _INSTALL_TOKEN is None: entity = storage.retrieve("config", "bunq_install_token") _INSTALL_TOKEN = entity["value"] return _INSTALL_TOKEN
def get_access_token(): """ Retrieves the access token from cache or the datastore """ global _ACCESS_TOKEN if _ACCESS_TOKEN is None: entity = storage.retrieve("config", "bunq_access_token") _ACCESS_TOKEN = entity["value"] return _ACCESS_TOKEN
def get_ifttt_service_key(): """ Return the IFTTT service key, used to secure IFTTT calls """ global _IFTTT_SERVICE_KEY if _IFTTT_SERVICE_KEY is None: entity = storage.retrieve("config", "ifttt_service_key") if entity is not None: _IFTTT_SERVICE_KEY = entity["value"] return _IFTTT_SERVICE_KEY
def get_app_master_url(): """ Return the URL of the master instance """ global _APP_MASTER_URL if _APP_MASTER_URL is None: entity = storage.retrieve("config", "app_master_url") if entity is not None: _APP_MASTER_URL = entity["value"] return _APP_MASTER_URL
def get_bunq_security_mode(): """ Return the bunq security mode """ global _BUNQ_SECURITY_MODE if _BUNQ_SECURITY_MODE is None: entity = storage.retrieve("config", "bunq_security_mode") if entity is not None: _BUNQ_SECURITY_MODE = entity["value"] return _BUNQ_SECURITY_MODE
def get_session_cookie(): """ Return the users session cookie """ global _SESSION_COOKIE if _SESSION_COOKIE is None: entity = storage.retrieve("config", "session_cookie") if entity is not None: _SESSION_COOKIE = entity["value"] return _SESSION_COOKIE
def get_ifttt_service_key(key=None): """ Return the IFTTT service key, used to secure IFTTT calls """ global _IFTTT_SERVICE_KEY if key not in [None, _IFTTT_SERVICE_KEY] or _IFTTT_SERVICE_KEY is None: entity = storage.retrieve("bunq2IFTTT", "ifttt_service_key") if entity is not None: _IFTTT_SERVICE_KEY = entity["value"] return _IFTTT_SERVICE_KEY
def get_server_key(): """ Retrieves the server public key from cache or the datastore """ global _SERVER_KEY if _SERVER_KEY is None: entity = storage.retrieve("config", "bunq_server_key") server_bytes = base64.a85decode(entity["value"]) _SERVER_KEY = serialization.load_pem_public_key( server_bytes, backend=default_backend()) return _SERVER_KEY
def get_app_mode(): """ Return the app mode (master/slave) """ global _APP_MODE if _APP_MODE is None: entity = storage.retrieve("config", "app_mode") if entity is not None: _APP_MODE = entity["value"] else: save_app_mode('master') return _APP_MODE
def get_session_token(force=False): """ Retrieves the session token from cache or the datastore or get one from the server if it is the first time """ global _SESSION_TOKEN if force: refresh_session_token() elif _SESSION_TOKEN is None: entity = storage.retrieve("config", "bunq_session_token") if entity is not None: _SESSION_TOKEN = entity["value"] else: refresh_session_token() return _SESSION_TOKEN
def trigger_request(): """ Callback for IFTTT trigger bunq_request """ try: data = request.get_json() print("[trigger_request] input: {}".format(json.dumps(data))) if "triggerFields" not in data or \ "account" not in data["triggerFields"]: print("[trigger_request] ERROR: account field missing!") return json.dumps({"errors": [{"message": "Invalid data"}]}), 400 account = data["triggerFields"]["account"] fields = data["triggerFields"] fieldsstr = json.dumps(fields) if "trigger_identity" not in data: print("[trigger_request] ERROR: trigger_identity field missing!") return json.dumps({"errors": [{"message": "Invalid data"}]}), 400 identity = data["trigger_identity"] limit = 50 if "limit" in data: limit = data["limit"] if account == "NL42BUNQ0123456789": return trigger_request_test(limit) timezone = "UTC" if "user" in data and "timezone" in data["user"]: timezone = data["user"]["timezone"] entity = storage.retrieve("trigger_request", identity) if entity is not None: if entity["account"] != account or \ json.dumps(entity["fields"]) != fieldsstr: storage.store("trigger_request", identity, { "account": account, "identity": identity, "fields": fields }) print("[trigger_request] updating trigger {} {}".format( account, fieldsstr)) else: storage.store("trigger_request", identity, { "account": account, "identity": identity, "fields": fields }) storage.store( "request_" + identity, "0", { "value": { "created_at": "2018-01-05T11:25:15+00:00", "date": "2018-01-05", "amount": "0.00", "account": account, "counterparty_account": "NL11BANK1111111111", "counterparty_name": "Dummy Transaction", "description": "This is a dummy transaction", "request_id": "123e4567-e89b-12d3-a456-426655440001", "meta": { "id": "0", "timestamp": "1515151515" } } }) print("[trigger_request] storing new trigger {} {}".format( account, fieldsstr)) transactions = [] for entity in storage.query_all("request_" + identity): entity["value"]["created_at"] = arrow.get(\ entity["value"]["created_at"]).to(timezone).isoformat() transactions.append(entity["value"]) transactions = sorted(transactions, key=lambda k: -int(k["meta"]["timestamp"])) if len(transactions) > 50: for trans in transactions[50:]: storage.remove("request_" + identity, str(trans["meta"]["id"])) print("[trigger_request] Found {} transactions".format( len(transactions))) return json.dumps({"data": transactions[:limit]}) except Exception: traceback.print_exc() print("[trigger_request] ERROR: cannot retrieve requests") return json.dumps({"errors": [{"message": \ "Cannot retrieve requests"}]}), 400
def get_session_cookie(): """ Return the users session cookie """ entity = storage.retrieve("config", "session_cookie") if entity is not None: return entity["value"] return None
def get_bunq_userid(): """ Return the bunq userid """ global _BUNQ_USERID if _BUNQ_USERID is None: _BUNQ_USERID = storage.retrieve("config", "bunq_userid")["value"] return _BUNQ_USERID
def trigger_request(): """ Callback for IFTTT trigger bunq_request """ try: data = request.get_json() print("[trigger_request] input: {}".format(json.dumps(data))) if "triggerFields" not in data or \ "account" not in data["triggerFields"]: print("[trigger_request] ERROR: account field missing!") return json.dumps({"errors": [{"message": "Invalid data"}]}), 400 account = data["triggerFields"]["account"] fields = data["triggerFields"] fieldsstr = json.dumps(fields) if "trigger_identity" not in data: print("[trigger_request] ERROR: trigger_identity field missing!") return json.dumps({"errors": [{"message": "Invalid data"}]}), 400 identity = data["trigger_identity"] limit = 50 if "limit" in data: limit = data["limit"] if account == "NL42BUNQ0123456789": return trigger_request_test(limit) timezone = "UTC" if "user" in data and "timezone" in data["user"]: timezone = data["user"]["timezone"] entity = storage.retrieve("trigger_request", identity) if entity is not None: if entity["account"] != account or \ json.dumps(entity["fields"]) != fieldsstr: storage.store("trigger_request", identity, { "account": account, "identity": identity, "fields": fields }) print("[trigger_request] updating trigger {} {}".format( account, fieldsstr)) else: storage.store("trigger_request", identity, { "account": account, "identity": identity, "fields": fields }) print("[trigger_request] storing new trigger {} {}".format( account, fieldsstr)) transactions = storage.get_value("trigger_request", identity + "_t") if transactions is None: transactions = [] for trans in transactions: trans["created_at"] = arrow.get(trans["created_at"])\ .to(timezone).isoformat() print("[trigger_request] Found {} transactions".format( len(transactions))) return json.dumps({"data": transactions[:limit]}) except Exception: traceback.print_exc() print("[trigger_request] ERROR: cannot retrieve requests") return json.dumps({"errors": [{"message": \ "Cannot retrieve requests"}]}), 400