From 8d74ffb184fcade5656ba28b1403a4a156a63765 Mon Sep 17 00:00:00 2001 From: Gene Takavic Date: Fri, 24 Nov 2023 16:11:29 +0100 Subject: [PATCH 1/2] introducing expiry date --- crud.py | 6 ++++-- lnurl.py | 9 +++++++++ migrations.py | 10 ++++++++++ models.py | 2 ++ static/js/index.js | 15 ++++++++++++++- templates/boltcards/index.html | 9 +++++++++ 6 files changed, 48 insertions(+), 3 deletions(-) diff --git a/crud.py b/crud.py index 0a678e7..a6abc30 100644 --- a/crud.py +++ b/crud.py @@ -27,9 +27,10 @@ async def create_card(data: CreateCardData, wallet_id: str) -> Card: k0, k1, k2, - otp + otp, + expiry_date ) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, ( card_id, @@ -45,6 +46,7 @@ async def create_card(data: CreateCardData, wallet_id: str) -> Card: data.k1, data.k2, secrets.token_hex(16), + data.expiry_date, ), ) card = await get_card(card_id) diff --git a/lnurl.py b/lnurl.py index 4b39374..3e26068 100644 --- a/lnurl.py +++ b/lnurl.py @@ -1,11 +1,13 @@ import json import secrets +from datetime import datetime from http import HTTPStatus from urllib.parse import urlparse from fastapi import HTTPException, Query, Request from lnurl import encode as lnurl_encode from lnurl.types import LnurlPayMetadata +from loguru import logger from starlette.responses import HTMLResponse from lnbits import bolt11 @@ -42,6 +44,13 @@ async def api_scan(p, c, request: Request, external_id: str): return {"status": "ERROR", "reason": "No card."} if not card.enable: return {"status": "ERROR", "reason": "Card is disabled."} + if card.expiry_date != "": + today = datetime.today() + logger.debug(f"today: {today}") + expiry_date = datetime.strptime(card.expiry_date, "%Y/%m/%d") + logger.debug(f"today: {expiry_date}") + if today > expiry_date: + return {"status": "ERROR", "reason": "Card expired."} try: card_uid, counter = decryptSUN(bytes.fromhex(p), bytes.fromhex(card.k1)) if card.uid.upper() != card_uid.hex().upper(): diff --git a/migrations.py b/migrations.py index 43d5bb0..c98a5a1 100644 --- a/migrations.py +++ b/migrations.py @@ -55,3 +55,13 @@ async def m001_initial(db): ); """ ) + + +async def m002_add_expiry(db): + """ + Special column for webhook endpoints that can be assigned + to each different invoice. + """ + + await db.execute("ALTER TABLE boltcards.cards ADD COLUMN expiry_date TEXT") + await db.execute("UPDATE boltcards.cards SET expiry_date = ''") diff --git a/models.py b/models.py index 5ea4be1..dd9cb47 100644 --- a/models.py +++ b/models.py @@ -28,6 +28,7 @@ class Card(BaseModel): prev_k2: str otp: str time: int + expiry_date: str @classmethod def from_row(cls, row: Row) -> "Card": @@ -54,6 +55,7 @@ class CreateCardData(BaseModel): prev_k0: str = Query(ZERO_KEY) prev_k1: str = Query(ZERO_KEY) prev_k2: str = Query(ZERO_KEY) + expiry_date: str = Query("") class Hit(BaseModel): diff --git a/static/js/index.js b/static/js/index.js index 880a555..9c16011 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -28,7 +28,9 @@ new Vue({ k1: '', k2: '', uid: '', - card_name: '' + card_name: '', + toggleExpiry: false, + expiry_date: new Date().toISOString().slice(0, 10).replaceAll('-', '/') }, temp: {} }, @@ -289,6 +291,7 @@ new Vue({ }, addCardOpen: function () { this.cardDialog.show = true + this.cardDialog.data.toggleExpiry = false this.generateKeys() }, generateKeys: function () { @@ -331,6 +334,10 @@ new Vue({ createCard: function (wallet, data) { var self = this + if (!this.cardDialog.data.toggleExpiry) { + this.cardDialog.data.expiry_date = null + } + LNbits.api .request('POST', '/boltcards/api/v1/cards', wallet.adminkey, data) .then(function (response) { @@ -346,6 +353,8 @@ new Vue({ var card = _.findWhere(this.cards, {id: formId}) this.cardDialog.data = _.clone(card) + this.cardDialog.data.toggleExpiry = !!this.cardDialog.data.expiry_date + this.cardDialog.temp.k0 = this.cardDialog.data.k0 this.cardDialog.temp.k1 = this.cardDialog.data.k1 this.cardDialog.temp.k2 = this.cardDialog.data.k2 @@ -355,6 +364,10 @@ new Vue({ updateCard: function (wallet, data) { var self = this + if (!this.cardDialog.data.toggleExpiry) { + this.cardDialog.data.expiry_date = null + } + if ( this.cardDialog.temp.k0 != data.k0 || this.cardDialog.temp.k1 != data.k1 || diff --git a/templates/boltcards/index.html b/templates/boltcards/index.html index 7e06e5e..263bda1 100644 --- a/templates/boltcards/index.html +++ b/templates/boltcards/index.html @@ -344,6 +344,15 @@
>Generate keys + + +
+ +
+
Date: Mon, 27 Nov 2023 11:38:34 +0100 Subject: [PATCH 2/2] some fixes + readme --- README.md | 6 +++++- lnurl.py | 8 ++++---- static/js/index.js | 15 +++++++-------- templates/boltcards/index.html | 4 ++-- 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 78a9def..a8d6d87 100644 --- a/README.md +++ b/README.md @@ -34,8 +34,12 @@ If you want to gift a Boltcard, make sure to [include the following data](https: - Otherwise read it with the Bolt-Card app (Read NFC) and paste it to the field. - Advanced Options - Card Keys (k0, k1, k2) will be automatically generated if not explicitly set. - - Set to 16 bytes of 0s (00000000000000000000000000000000) to leave the keys in default (empty) state (this is unsecure). + - Set to 16 bytes of 0s (00000000000000000000000000000000) to leave the keys in default (empty) state (this is unsecure, merely for debuging). - GENERATE KEY button fill the keys randomly. + - Expiry date + - You can set an expiry date on a card. After this date LNbits will not longer allow payment with this card. + - You can enable, extend or disable expiry date anytime just by updating the card (without necessity of rewriting it physicaly). + - Without an expiry date, the card is valid indefinitely. - Click CREATE CARD button - Click the QR code button next to a card to view its details. Backup the keys now! They'll be comfortable in your password manager. - Now you can scan the QR code with the Boltcard app (Create Bolt Card -> SCAN QR CODE). diff --git a/lnurl.py b/lnurl.py index 3e26068..2081b16 100644 --- a/lnurl.py +++ b/lnurl.py @@ -7,7 +7,6 @@ from fastapi import HTTPException, Query, Request from lnurl import encode as lnurl_encode from lnurl.types import LnurlPayMetadata -from loguru import logger from starlette.responses import HTMLResponse from lnbits import bolt11 @@ -46,9 +45,10 @@ async def api_scan(p, c, request: Request, external_id: str): return {"status": "ERROR", "reason": "Card is disabled."} if card.expiry_date != "": today = datetime.today() - logger.debug(f"today: {today}") - expiry_date = datetime.strptime(card.expiry_date, "%Y/%m/%d") - logger.debug(f"today: {expiry_date}") + try: + expiry_date = datetime.strptime(card.expiry_date, "%Y/%m/%d") + except: + return {"status": "ERROR", "reason": "Invalid expiry date."} if today > expiry_date: return {"status": "ERROR", "reason": "Card expired."} try: diff --git a/static/js/index.js b/static/js/index.js index 9c16011..cded21d 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -15,6 +15,7 @@ new Vue({ data: function () { return { toggleAdvanced: false, + toggleExpiry: false, nfcTagReading: false, lnurlLink: `${window.location.host}/boltcards/api/v1/scan/`, cards: [], @@ -29,8 +30,7 @@ new Vue({ k2: '', uid: '', card_name: '', - toggleExpiry: false, - expiry_date: new Date().toISOString().slice(0, 10).replaceAll('-', '/') + expiry_date: '' }, temp: {} }, @@ -291,7 +291,6 @@ new Vue({ }, addCardOpen: function () { this.cardDialog.show = true - this.cardDialog.data.toggleExpiry = false this.generateKeys() }, generateKeys: function () { @@ -334,8 +333,8 @@ new Vue({ createCard: function (wallet, data) { var self = this - if (!this.cardDialog.data.toggleExpiry) { - this.cardDialog.data.expiry_date = null + if (!this.toggleExpiry) { + this.cardDialog.data.expiry_date = '' } LNbits.api @@ -353,7 +352,7 @@ new Vue({ var card = _.findWhere(this.cards, {id: formId}) this.cardDialog.data = _.clone(card) - this.cardDialog.data.toggleExpiry = !!this.cardDialog.data.expiry_date + this.toggleExpiry = !!this.cardDialog.data.expiry_date this.cardDialog.temp.k0 = this.cardDialog.data.k0 this.cardDialog.temp.k1 = this.cardDialog.data.k1 @@ -364,8 +363,8 @@ new Vue({ updateCard: function (wallet, data) { var self = this - if (!this.cardDialog.data.toggleExpiry) { - this.cardDialog.data.expiry_date = null + if (!this.toggleExpiry) { + this.cardDialog.data.expiry_date = '' } if ( diff --git a/templates/boltcards/index.html b/templates/boltcards/index.html index 263bda1..279e678 100644 --- a/templates/boltcards/index.html +++ b/templates/boltcards/index.html @@ -346,10 +346,10 @@
-
+