@tasenor/common-node 1.9.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +4 -0
- package/LICENSE +21 -0
- package/dist/tasenor-common-node/src/cli.d.ts +81 -0
- package/dist/tasenor-common-node/src/cli.js +242 -0
- package/dist/tasenor-common-node/src/cli.js.map +1 -0
- package/dist/tasenor-common-node/src/commands/account.d.ts +12 -0
- package/dist/tasenor-common-node/src/commands/account.js +58 -0
- package/dist/tasenor-common-node/src/commands/account.js.map +1 -0
- package/dist/tasenor-common-node/src/commands/balance.d.ts +11 -0
- package/dist/tasenor-common-node/src/commands/balance.js +117 -0
- package/dist/tasenor-common-node/src/commands/balance.js.map +1 -0
- package/dist/tasenor-common-node/src/commands/db.d.ts +14 -0
- package/dist/tasenor-common-node/src/commands/db.js +69 -0
- package/dist/tasenor-common-node/src/commands/db.js.map +1 -0
- package/dist/tasenor-common-node/src/commands/entry.d.ts +13 -0
- package/dist/tasenor-common-node/src/commands/entry.js +106 -0
- package/dist/tasenor-common-node/src/commands/entry.js.map +1 -0
- package/dist/tasenor-common-node/src/commands/import.d.ts +17 -0
- package/dist/tasenor-common-node/src/commands/import.js +140 -0
- package/dist/tasenor-common-node/src/commands/import.js.map +1 -0
- package/dist/tasenor-common-node/src/commands/importer.d.ts +13 -0
- package/dist/tasenor-common-node/src/commands/importer.js +71 -0
- package/dist/tasenor-common-node/src/commands/importer.js.map +1 -0
- package/dist/tasenor-common-node/src/commands/index.d.ts +191 -0
- package/dist/tasenor-common-node/src/commands/index.js +482 -0
- package/dist/tasenor-common-node/src/commands/index.js.map +1 -0
- package/dist/tasenor-common-node/src/commands/period.d.ts +12 -0
- package/dist/tasenor-common-node/src/commands/period.js +48 -0
- package/dist/tasenor-common-node/src/commands/period.js.map +1 -0
- package/dist/tasenor-common-node/src/commands/plugin.d.ts +15 -0
- package/dist/tasenor-common-node/src/commands/plugin.js +78 -0
- package/dist/tasenor-common-node/src/commands/plugin.js.map +1 -0
- package/dist/tasenor-common-node/src/commands/report.d.ts +11 -0
- package/dist/tasenor-common-node/src/commands/report.js +96 -0
- package/dist/tasenor-common-node/src/commands/report.js.map +1 -0
- package/dist/tasenor-common-node/src/commands/settings.d.ts +10 -0
- package/dist/tasenor-common-node/src/commands/settings.js +64 -0
- package/dist/tasenor-common-node/src/commands/settings.js.map +1 -0
- package/dist/tasenor-common-node/src/commands/stock.d.ts +8 -0
- package/dist/tasenor-common-node/src/commands/stock.js +73 -0
- package/dist/tasenor-common-node/src/commands/stock.js.map +1 -0
- package/dist/tasenor-common-node/src/commands/tag.d.ts +13 -0
- package/dist/tasenor-common-node/src/commands/tag.js +89 -0
- package/dist/tasenor-common-node/src/commands/tag.js.map +1 -0
- package/dist/tasenor-common-node/src/commands/tx.d.ts +12 -0
- package/dist/tasenor-common-node/src/commands/tx.js +81 -0
- package/dist/tasenor-common-node/src/commands/tx.js.map +1 -0
- package/dist/tasenor-common-node/src/commands/user.d.ts +12 -0
- package/dist/tasenor-common-node/src/commands/user.js +52 -0
- package/dist/tasenor-common-node/src/commands/user.js.map +1 -0
- package/dist/tasenor-common-node/src/database/BookkeeperImporter.d.ts +77 -0
- package/dist/tasenor-common-node/src/database/BookkeeperImporter.js +343 -0
- package/dist/tasenor-common-node/src/database/BookkeeperImporter.js.map +1 -0
- package/dist/tasenor-common-node/src/database/DB.d.ts +51 -0
- package/dist/tasenor-common-node/src/database/DB.js +354 -0
- package/dist/tasenor-common-node/src/database/DB.js.map +1 -0
- package/dist/tasenor-common-node/src/database/index.d.ts +7 -0
- package/dist/tasenor-common-node/src/database/index.js +8 -0
- package/dist/tasenor-common-node/src/database/index.js.map +1 -0
- package/dist/tasenor-common-node/src/doccer.d.ts +29 -0
- package/dist/tasenor-common-node/src/doccer.js +30 -0
- package/dist/tasenor-common-node/src/doccer.js.map +1 -0
- package/dist/tasenor-common-node/src/error.d.ts +30 -0
- package/dist/tasenor-common-node/src/error.js +35 -0
- package/dist/tasenor-common-node/src/error.js.map +1 -0
- package/dist/tasenor-common-node/src/export/Exporter.d.ts +69 -0
- package/dist/tasenor-common-node/src/export/Exporter.js +123 -0
- package/dist/tasenor-common-node/src/export/Exporter.js.map +1 -0
- package/dist/tasenor-common-node/src/export/TasenorExporter.d.ts +55 -0
- package/dist/tasenor-common-node/src/export/TasenorExporter.js +135 -0
- package/dist/tasenor-common-node/src/export/TasenorExporter.js.map +1 -0
- package/dist/tasenor-common-node/src/export/TilitinExporter.d.ts +71 -0
- package/dist/tasenor-common-node/src/export/TilitinExporter.js +290 -0
- package/dist/tasenor-common-node/src/export/TilitinExporter.js.map +1 -0
- package/dist/tasenor-common-node/src/export/index.d.ts +8 -0
- package/dist/tasenor-common-node/src/export/index.js +9 -0
- package/dist/tasenor-common-node/src/export/index.js.map +1 -0
- package/dist/tasenor-common-node/src/import/TextFileProcessHandler.d.ts +104 -0
- package/dist/tasenor-common-node/src/import/TextFileProcessHandler.js +354 -0
- package/dist/tasenor-common-node/src/import/TextFileProcessHandler.js.map +1 -0
- package/dist/tasenor-common-node/src/import/TransactionImportConnector.d.ts +38 -0
- package/dist/tasenor-common-node/src/import/TransactionImportConnector.js +27 -0
- package/dist/tasenor-common-node/src/import/TransactionImportConnector.js.map +1 -0
- package/dist/tasenor-common-node/src/import/TransactionImportHandler.d.ts +173 -0
- package/dist/tasenor-common-node/src/import/TransactionImportHandler.js +733 -0
- package/dist/tasenor-common-node/src/import/TransactionImportHandler.js.map +1 -0
- package/dist/tasenor-common-node/src/import/TransactionRules.d.ts +238 -0
- package/dist/tasenor-common-node/src/import/TransactionRules.js +522 -0
- package/dist/tasenor-common-node/src/import/TransactionRules.js.map +1 -0
- package/dist/tasenor-common-node/src/import/TransactionUI.d.ts +181 -0
- package/dist/tasenor-common-node/src/import/TransactionUI.js +482 -0
- package/dist/tasenor-common-node/src/import/TransactionUI.js.map +1 -0
- package/dist/tasenor-common-node/src/import/TransferAnalyzer.d.ts +324 -0
- package/dist/tasenor-common-node/src/import/TransferAnalyzer.js +1379 -0
- package/dist/tasenor-common-node/src/import/TransferAnalyzer.js.map +1 -0
- package/dist/tasenor-common-node/src/import/index.d.ts +11 -0
- package/dist/tasenor-common-node/src/import/index.js +12 -0
- package/dist/tasenor-common-node/src/import/index.js.map +1 -0
- package/dist/tasenor-common-node/src/index.d.ts +12 -0
- package/dist/tasenor-common-node/src/index.js +13 -0
- package/dist/tasenor-common-node/src/index.js.map +1 -0
- package/dist/tasenor-common-node/src/net/crypto.d.ts +33 -0
- package/dist/tasenor-common-node/src/net/crypto.js +63 -0
- package/dist/tasenor-common-node/src/net/crypto.js.map +1 -0
- package/dist/tasenor-common-node/src/net/git.d.ts +49 -0
- package/dist/tasenor-common-node/src/net/git.js +137 -0
- package/dist/tasenor-common-node/src/net/git.js.map +1 -0
- package/dist/tasenor-common-node/src/net/index.d.ts +10 -0
- package/dist/tasenor-common-node/src/net/index.js +11 -0
- package/dist/tasenor-common-node/src/net/index.js.map +1 -0
- package/dist/tasenor-common-node/src/net/middleware.d.ts +61 -0
- package/dist/tasenor-common-node/src/net/middleware.js +220 -0
- package/dist/tasenor-common-node/src/net/middleware.js.map +1 -0
- package/dist/tasenor-common-node/src/net/tokens.d.ts +50 -0
- package/dist/tasenor-common-node/src/net/tokens.js +141 -0
- package/dist/tasenor-common-node/src/net/tokens.js.map +1 -0
- package/dist/tasenor-common-node/src/net/vault.d.ts +67 -0
- package/dist/tasenor-common-node/src/net/vault.js +145 -0
- package/dist/tasenor-common-node/src/net/vault.js.map +1 -0
- package/dist/tasenor-common-node/src/plugins/BackendPlugin.d.ts +91 -0
- package/dist/tasenor-common-node/src/plugins/BackendPlugin.js +165 -0
- package/dist/tasenor-common-node/src/plugins/BackendPlugin.js.map +1 -0
- package/dist/tasenor-common-node/src/plugins/DataPlugin.d.ts +13 -0
- package/dist/tasenor-common-node/src/plugins/DataPlugin.js +26 -0
- package/dist/tasenor-common-node/src/plugins/DataPlugin.js.map +1 -0
- package/dist/tasenor-common-node/src/plugins/ImportPlugin.d.ts +188 -0
- package/dist/tasenor-common-node/src/plugins/ImportPlugin.js +204 -0
- package/dist/tasenor-common-node/src/plugins/ImportPlugin.js.map +1 -0
- package/dist/tasenor-common-node/src/plugins/ReportPlugin.d.ts +132 -0
- package/dist/tasenor-common-node/src/plugins/ReportPlugin.js +393 -0
- package/dist/tasenor-common-node/src/plugins/ReportPlugin.js.map +1 -0
- package/dist/tasenor-common-node/src/plugins/SchemePlugin.d.ts +34 -0
- package/dist/tasenor-common-node/src/plugins/SchemePlugin.js +47 -0
- package/dist/tasenor-common-node/src/plugins/SchemePlugin.js.map +1 -0
- package/dist/tasenor-common-node/src/plugins/ServicePlugin.d.ts +80 -0
- package/dist/tasenor-common-node/src/plugins/ServicePlugin.js +168 -0
- package/dist/tasenor-common-node/src/plugins/ServicePlugin.js.map +1 -0
- package/dist/tasenor-common-node/src/plugins/ToolPlugin.d.ts +27 -0
- package/dist/tasenor-common-node/src/plugins/ToolPlugin.js +37 -0
- package/dist/tasenor-common-node/src/plugins/ToolPlugin.js.map +1 -0
- package/dist/tasenor-common-node/src/plugins/index.d.ts +13 -0
- package/dist/tasenor-common-node/src/plugins/index.js +14 -0
- package/dist/tasenor-common-node/src/plugins/index.js.map +1 -0
- package/dist/tasenor-common-node/src/plugins/plugins.d.ts +101 -0
- package/dist/tasenor-common-node/src/plugins/plugins.js +292 -0
- package/dist/tasenor-common-node/src/plugins/plugins.js.map +1 -0
- package/dist/tasenor-common-node/src/process/Process.d.ts +108 -0
- package/dist/tasenor-common-node/src/process/Process.js +335 -0
- package/dist/tasenor-common-node/src/process/Process.js.map +1 -0
- package/dist/tasenor-common-node/src/process/ProcessConnector.d.ts +24 -0
- package/dist/tasenor-common-node/src/process/ProcessConnector.js +28 -0
- package/dist/tasenor-common-node/src/process/ProcessConnector.js.map +1 -0
- package/dist/tasenor-common-node/src/process/ProcessFile.d.ts +69 -0
- package/dist/tasenor-common-node/src/process/ProcessFile.js +145 -0
- package/dist/tasenor-common-node/src/process/ProcessFile.js.map +1 -0
- package/dist/tasenor-common-node/src/process/ProcessHandler.d.ts +60 -0
- package/dist/tasenor-common-node/src/process/ProcessHandler.js +73 -0
- package/dist/tasenor-common-node/src/process/ProcessHandler.js.map +1 -0
- package/dist/tasenor-common-node/src/process/ProcessStep.d.ts +52 -0
- package/dist/tasenor-common-node/src/process/ProcessStep.js +78 -0
- package/dist/tasenor-common-node/src/process/ProcessStep.js.map +1 -0
- package/dist/tasenor-common-node/src/process/ProcessingSystem.d.ts +60 -0
- package/dist/tasenor-common-node/src/process/ProcessingSystem.js +182 -0
- package/dist/tasenor-common-node/src/process/ProcessingSystem.js.map +1 -0
- package/dist/tasenor-common-node/src/process/index.d.ts +11 -0
- package/dist/tasenor-common-node/src/process/index.js +12 -0
- package/dist/tasenor-common-node/src/process/index.js.map +1 -0
- package/dist/tasenor-common-node/src/reports/conversions.d.ts +8 -0
- package/dist/tasenor-common-node/src/reports/conversions.js +47 -0
- package/dist/tasenor-common-node/src/reports/conversions.js.map +1 -0
- package/dist/tasenor-common-node/src/reports/index.d.ts +6 -0
- package/dist/tasenor-common-node/src/reports/index.js +7 -0
- package/dist/tasenor-common-node/src/reports/index.js.map +1 -0
- package/dist/tasenor-common-node/src/server/ISPDemoServer.d.ts +43 -0
- package/dist/tasenor-common-node/src/server/ISPDemoServer.js +112 -0
- package/dist/tasenor-common-node/src/server/ISPDemoServer.js.map +1 -0
- package/dist/tasenor-common-node/src/server/api.d.ts +15 -0
- package/dist/tasenor-common-node/src/server/api.js +27 -0
- package/dist/tasenor-common-node/src/server/api.js.map +1 -0
- package/dist/tasenor-common-node/src/server/index.d.ts +7 -0
- package/dist/tasenor-common-node/src/server/index.js +8 -0
- package/dist/tasenor-common-node/src/server/index.js.map +1 -0
- package/dist/tasenor-common-node/src/server/router.d.ts +5 -0
- package/dist/tasenor-common-node/src/server/router.js +37 -0
- package/dist/tasenor-common-node/src/server/router.js.map +1 -0
- package/dist/tasenor-common-node/src/system.d.ts +27 -0
- package/dist/tasenor-common-node/src/system.js +95 -0
- package/dist/tasenor-common-node/src/system.js.map +1 -0
- package/dist/tasenor-common-node/src/testing/ProcessingSystemMock.d.ts +21 -0
- package/dist/tasenor-common-node/src/testing/ProcessingSystemMock.js +33 -0
- package/dist/tasenor-common-node/src/testing/ProcessingSystemMock.js.map +1 -0
- package/dist/tasenor-common-node/src/testing/UnitTestImportConnector.d.ts +24 -0
- package/dist/tasenor-common-node/src/testing/UnitTestImportConnector.js +68 -0
- package/dist/tasenor-common-node/src/testing/UnitTestImportConnector.js.map +1 -0
- package/dist/tasenor-common-node/src/testing/UnitTester.d.ts +64 -0
- package/dist/tasenor-common-node/src/testing/UnitTester.js +199 -0
- package/dist/tasenor-common-node/src/testing/UnitTester.js.map +1 -0
- package/dist/tasenor-common-node/src/testing/index.d.ts +4 -0
- package/dist/tasenor-common-node/src/testing/index.js +5 -0
- package/dist/tasenor-common-node/src/testing/index.js.map +1 -0
- package/dist/tasenor-common-node/src/testing/test-handlers.d.ts +13 -0
- package/dist/tasenor-common-node/src/testing/test-handlers.js +52 -0
- package/dist/tasenor-common-node/src/testing/test-handlers.js.map +1 -0
- package/dist/tasenor-common-node/tests/TransactionRules.spec.d.ts +1 -0
- package/dist/tasenor-common-node/tests/TransactionRules.spec.js +64 -0
- package/dist/tasenor-common-node/tests/TransactionRules.spec.js.map +1 -0
- package/dist/tasenor-common-node/tests/TransferAnalyzer-account-address.spec.d.ts +1 -0
- package/dist/tasenor-common-node/tests/TransferAnalyzer-account-address.spec.js +80 -0
- package/dist/tasenor-common-node/tests/TransferAnalyzer-account-address.spec.js.map +1 -0
- package/dist/tasenor-common-node/tests/TransferAnalyzer-buying-and-selling.spec.d.ts +1 -0
- package/dist/tasenor-common-node/tests/TransferAnalyzer-buying-and-selling.spec.js +342 -0
- package/dist/tasenor-common-node/tests/TransferAnalyzer-buying-and-selling.spec.js.map +1 -0
- package/dist/tasenor-common-node/tests/TransferAnalyzer-loans.spec.d.ts +1 -0
- package/dist/tasenor-common-node/tests/TransferAnalyzer-loans.spec.js +174 -0
- package/dist/tasenor-common-node/tests/TransferAnalyzer-loans.spec.js.map +1 -0
- package/dist/tasenor-common-node/tests/TransferAnalyzer-multiple-null-amounts.spec.d.ts +1 -0
- package/dist/tasenor-common-node/tests/TransferAnalyzer-multiple-null-amounts.spec.js +175 -0
- package/dist/tasenor-common-node/tests/TransferAnalyzer-multiple-null-amounts.spec.js.map +1 -0
- package/dist/tasenor-common-node/tests/password.spec.d.ts +1 -0
- package/dist/tasenor-common-node/tests/password.spec.js +8 -0
- package/dist/tasenor-common-node/tests/password.spec.js.map +1 -0
- package/dist/tasenor-common-node/tests/tokens.spec.d.ts +1 -0
- package/dist/tasenor-common-node/tests/tokens.spec.js +49 -0
- package/dist/tasenor-common-node/tests/tokens.spec.js.map +1 -0
- package/dist/tasenor-common-node/tests/vault.spec.d.ts +1 -0
- package/dist/tasenor-common-node/tests/vault.spec.js +19 -0
- package/dist/tasenor-common-node/tests/vault.spec.js.map +1 -0
- package/dist/tasenor-common-plugins/src/CoinbaseImport/backend/CoinbaseHandler.d.ts +11 -0
- package/dist/tasenor-common-plugins/src/CoinbaseImport/backend/CoinbaseHandler.js +30 -0
- package/dist/tasenor-common-plugins/src/CoinbaseImport/backend/CoinbaseHandler.js.map +1 -0
- package/dist/tasenor-common-plugins/src/IncomeAndExpenses/backend/index.d.ts +5 -0
- package/dist/tasenor-common-plugins/src/IncomeAndExpenses/backend/index.js +350 -0
- package/dist/tasenor-common-plugins/src/IncomeAndExpenses/backend/index.js.map +1 -0
- package/dist/tasenor-common-plugins/src/KrakenImport/backend/KrakenHandler.d.ts +23 -0
- package/dist/tasenor-common-plugins/src/KrakenImport/backend/KrakenHandler.js +83 -0
- package/dist/tasenor-common-plugins/src/KrakenImport/backend/KrakenHandler.js.map +1 -0
- package/dist/tasenor-common-plugins/src/LynxImport/backend/LynxHandler.d.ts +28 -0
- package/dist/tasenor-common-plugins/src/LynxImport/backend/LynxHandler.js +340 -0
- package/dist/tasenor-common-plugins/src/LynxImport/backend/LynxHandler.js.map +1 -0
- package/dist/tasenor-common-plugins/src/NordeaImport/backend/NordeaHandler.d.ts +11 -0
- package/dist/tasenor-common-plugins/src/NordeaImport/backend/NordeaHandler.js +39 -0
- package/dist/tasenor-common-plugins/src/NordeaImport/backend/NordeaHandler.js.map +1 -0
- package/dist/tasenor-common-plugins/src/NordnetImport/backend/NordnetHandler.d.ts +17 -0
- package/dist/tasenor-common-plugins/src/NordnetImport/backend/NordnetHandler.js +66 -0
- package/dist/tasenor-common-plugins/src/NordnetImport/backend/NordnetHandler.js.map +1 -0
- package/dist/tasenor-common-plugins/src/TITOImport/backend/TITOHandler.d.ts +13 -0
- package/dist/tasenor-common-plugins/src/TITOImport/backend/TITOHandler.js +241 -0
- package/dist/tasenor-common-plugins/src/TITOImport/backend/TITOHandler.js.map +1 -0
- package/jest.config.js +1 -0
- package/package.json +62 -0
- package/src/cli.ts +267 -0
- package/src/commands/account.ts +69 -0
- package/src/commands/balance.ts +131 -0
- package/src/commands/db.ts +84 -0
- package/src/commands/entry.ts +117 -0
- package/src/commands/import.ts +160 -0
- package/src/commands/importer.ts +84 -0
- package/src/commands/index.ts +534 -0
- package/src/commands/period.ts +59 -0
- package/src/commands/plugin.ts +95 -0
- package/src/commands/report.ts +113 -0
- package/src/commands/settings.ts +75 -0
- package/src/commands/stock.ts +80 -0
- package/src/commands/tag.ts +102 -0
- package/src/commands/tx.ts +93 -0
- package/src/commands/user.ts +65 -0
- package/src/database/BookkeeperImporter.ts +358 -0
- package/src/database/DB.ts +396 -0
- package/src/database/index.ts +7 -0
- package/src/doccer.ts +29 -0
- package/src/error.ts +32 -0
- package/src/export/Exporter.ts +136 -0
- package/src/export/TasenorExporter.ts +144 -0
- package/src/export/TilitinExporter.ts +302 -0
- package/src/export/index.ts +8 -0
- package/src/import/TextFileProcessHandler.ts +384 -0
- package/src/import/TransactionImportConnector.ts +65 -0
- package/src/import/TransactionImportHandler.ts +819 -0
- package/src/import/TransactionRules.ts +570 -0
- package/src/import/TransactionUI.ts +520 -0
- package/src/import/TransferAnalyzer.ts +1450 -0
- package/src/import/index.ts +11 -0
- package/src/index.ts +12 -0
- package/src/net/crypto.ts +69 -0
- package/src/net/git.ts +151 -0
- package/src/net/index.ts +10 -0
- package/src/net/middleware.ts +261 -0
- package/src/net/tokens.ts +140 -0
- package/src/net/vault.ts +161 -0
- package/src/plugins/BackendPlugin.ts +188 -0
- package/src/plugins/DataPlugin.ts +29 -0
- package/src/plugins/ImportPlugin.ts +211 -0
- package/src/plugins/ReportPlugin.ts +443 -0
- package/src/plugins/SchemePlugin.ts +56 -0
- package/src/plugins/ServicePlugin.ts +188 -0
- package/src/plugins/ToolPlugin.ts +44 -0
- package/src/plugins/index.ts +13 -0
- package/src/plugins/plugins.ts +345 -0
- package/src/process/Process.ts +368 -0
- package/src/process/ProcessConnector.ts +45 -0
- package/src/process/ProcessFile.ts +169 -0
- package/src/process/ProcessHandler.ts +94 -0
- package/src/process/ProcessStep.ts +100 -0
- package/src/process/ProcessingSystem.ts +202 -0
- package/src/process/index.ts +11 -0
- package/src/reports/conversions.ts +52 -0
- package/src/reports/index.ts +6 -0
- package/src/server/ISPDemoServer.ts +122 -0
- package/src/server/api.ts +37 -0
- package/src/server/index.ts +7 -0
- package/src/server/router.ts +60 -0
- package/src/system.ts +96 -0
- package/src/testing/ProcessingSystemMock.ts +45 -0
- package/src/testing/UnitTestImportConnector.ts +86 -0
- package/src/testing/UnitTester.ts +231 -0
- package/src/testing/index.ts +4 -0
- package/src/testing/test-handlers.ts +55 -0
- package/tests/TransactionRules.spec.ts +73 -0
- package/tests/TransferAnalyzer-account-address.spec.ts +87 -0
- package/tests/TransferAnalyzer-buying-and-selling.spec.ts +354 -0
- package/tests/TransferAnalyzer-loans.spec.ts +197 -0
- package/tests/TransferAnalyzer-multiple-null-amounts.spec.ts +181 -0
- package/tests/password.spec.ts +8 -0
- package/tests/tokens.spec.ts +52 -0
- package/tests/vault.spec.ts +20 -0
- package/tsconfig.json +13 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers for text file processing for importing them to some processing system.
|
|
3
|
+
*
|
|
4
|
+
* @module tasenor-common-node/src/import
|
|
5
|
+
*/
|
|
6
|
+
export * from './TextFileProcessHandler'
|
|
7
|
+
export * from './TransactionImportConnector'
|
|
8
|
+
export * from './TransactionImportHandler'
|
|
9
|
+
export * from './TransactionRules'
|
|
10
|
+
export * from './TransactionUI'
|
|
11
|
+
export * from './TransferAnalyzer'
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from './cli'
|
|
2
|
+
export * from './database'
|
|
3
|
+
export * from './error'
|
|
4
|
+
export * from './export'
|
|
5
|
+
export * from './import'
|
|
6
|
+
export * from './net'
|
|
7
|
+
export * from './plugins'
|
|
8
|
+
export * from './process'
|
|
9
|
+
export * from './reports'
|
|
10
|
+
export * from './server'
|
|
11
|
+
export * from './system'
|
|
12
|
+
export * from './testing'
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import crypto from 'crypto'
|
|
2
|
+
import bcrypt from 'bcrypt'
|
|
3
|
+
import { Crypto, EncryptedUserData, LoginPluginData, Secret, UUID } from '@dataplug/tasenor-common'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Utility to create and check hashes from passwords.
|
|
7
|
+
*/
|
|
8
|
+
export class Password {
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Create one way hash for a password.
|
|
12
|
+
* @param password A password.
|
|
13
|
+
* @returns
|
|
14
|
+
*/
|
|
15
|
+
static async hash(password: string): Promise<string> {
|
|
16
|
+
const salt = await bcrypt.genSalt(13)
|
|
17
|
+
const hash = await bcrypt.hash(password, salt)
|
|
18
|
+
return hash
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Verify that given hash has been created from the given password.
|
|
23
|
+
* @param password
|
|
24
|
+
* @param hash
|
|
25
|
+
* @returns
|
|
26
|
+
*/
|
|
27
|
+
static async compare(password: string, hash: string): Promise<boolean> {
|
|
28
|
+
return await bcrypt.compare(password, hash)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Generate a random string of the given length.
|
|
34
|
+
* @param len
|
|
35
|
+
*/
|
|
36
|
+
export function randomString(len = 32): string {
|
|
37
|
+
const buf = crypto.randomBytes(len / 2)
|
|
38
|
+
return buf.toString('hex')
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Generate UUID.
|
|
43
|
+
* @returns A string.
|
|
44
|
+
*/
|
|
45
|
+
export function createUuid(): UUID {
|
|
46
|
+
function randomDigit() {
|
|
47
|
+
const rand = crypto.randomBytes(1)
|
|
48
|
+
return (rand[0] % 16).toString(16)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'.replace(/x/g, randomDigit) as UUID
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Encode UI data for user.
|
|
56
|
+
*/
|
|
57
|
+
export async function encryptdata({ plugins, prices, subscriptions }: LoginPluginData): Promise<EncryptedUserData> {
|
|
58
|
+
const result = {
|
|
59
|
+
key: await Crypto.generateKey(),
|
|
60
|
+
data: ''
|
|
61
|
+
}
|
|
62
|
+
const data = {
|
|
63
|
+
plugins,
|
|
64
|
+
prices,
|
|
65
|
+
subscriptions
|
|
66
|
+
}
|
|
67
|
+
result.data = await Crypto.encrypt(result.key as Secret, JSON.stringify(data))
|
|
68
|
+
return result
|
|
69
|
+
}
|
package/src/net/git.ts
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { DirectoryPath, Email, error, FilePath, log, Url, warning } from '@dataplug/tasenor-common'
|
|
2
|
+
import simpleGit, { SimpleGit } from 'simple-git'
|
|
3
|
+
import gitUrlParse from 'git-url-parse'
|
|
4
|
+
import fs from 'fs'
|
|
5
|
+
import glob from 'fast-glob'
|
|
6
|
+
import path from 'path'
|
|
7
|
+
import { systemPiped } from '../system'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A git repo storage.
|
|
11
|
+
*/
|
|
12
|
+
export class GitRepo {
|
|
13
|
+
|
|
14
|
+
url: Url
|
|
15
|
+
rootDir: DirectoryPath
|
|
16
|
+
name: string
|
|
17
|
+
git: SimpleGit
|
|
18
|
+
|
|
19
|
+
constructor(url: Url, rootDir: DirectoryPath) {
|
|
20
|
+
this.url = url
|
|
21
|
+
this.name = GitRepo.defaultName(url)
|
|
22
|
+
this.setDir(rootDir)
|
|
23
|
+
this.git.outputHandler(function(command, stdout, stderr) {
|
|
24
|
+
stdout.on('data', (str) => log(`GIT: ${str}`.trim()))
|
|
25
|
+
stderr.on('data', (str) => warning(`GIT: ${str.toString('utf-8')}`.trim()))
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get fullPath() : FilePath {
|
|
30
|
+
return path.join(this.rootDir, this.name) as FilePath
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Set the git configuration.
|
|
35
|
+
*/
|
|
36
|
+
configure(name: string, email: Email) {
|
|
37
|
+
this.git.addConfig('user.name', name).addConfig('user.email', email)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Initialize root path and instantiate Simple Git if path exists.
|
|
42
|
+
*/
|
|
43
|
+
setDir(rootDir: DirectoryPath) {
|
|
44
|
+
this.rootDir = rootDir
|
|
45
|
+
if (fs.existsSync(this.fullPath)) {
|
|
46
|
+
this.git = simpleGit({ baseDir: this.fullPath })
|
|
47
|
+
} else {
|
|
48
|
+
this.git = simpleGit()
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Delete all if repo exists.
|
|
54
|
+
*/
|
|
55
|
+
async clean(): Promise<void> {
|
|
56
|
+
if (!fs.existsSync(this.fullPath)) {
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
await fs.promises.rm(this.fullPath, { recursive: true })
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Clone the repo if it is not yet there. Return true if the repo is available.
|
|
64
|
+
*/
|
|
65
|
+
async fetch(): Promise<boolean> {
|
|
66
|
+
if (fs.existsSync(this.fullPath)) {
|
|
67
|
+
return true
|
|
68
|
+
}
|
|
69
|
+
return this.git.clone(this.url, this.fullPath)
|
|
70
|
+
.then(() => {
|
|
71
|
+
this.setDir(this.rootDir)
|
|
72
|
+
return true
|
|
73
|
+
})
|
|
74
|
+
.catch(() => {
|
|
75
|
+
error(`Git fetch from ${this.url} failed.`)
|
|
76
|
+
return false
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* List files from repo returning local relative paths.
|
|
82
|
+
*/
|
|
83
|
+
glob(pattern: string): string[] {
|
|
84
|
+
const N = this.fullPath.length
|
|
85
|
+
return glob.sync(this.fullPath + '/' + pattern).map((s: string) => {
|
|
86
|
+
if (s.substring(0, N) !== this.fullPath) {
|
|
87
|
+
throw new Error(`Strage. Glob found a file ${s} from repo ${this.fullPath}.`)
|
|
88
|
+
}
|
|
89
|
+
return s.substring(N + 1)
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Add, commit and push the given files and/or directories.
|
|
95
|
+
*/
|
|
96
|
+
async put(message: string, ...subPaths: (FilePath | DirectoryPath)[]): Promise<boolean> {
|
|
97
|
+
let fail = false
|
|
98
|
+
await this.git.add(subPaths).catch(err => {
|
|
99
|
+
error(`Git add failed: ${err}`)
|
|
100
|
+
fail = true
|
|
101
|
+
})
|
|
102
|
+
await this.git.commit(message).catch(err => {
|
|
103
|
+
error(`Git commit failed: ${err}`)
|
|
104
|
+
fail = true
|
|
105
|
+
})
|
|
106
|
+
await this.git.push().catch(err => {
|
|
107
|
+
error(`Git push failed: ${err}`)
|
|
108
|
+
fail = true
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
return fail
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Gather all repos found from the directory.
|
|
116
|
+
*/
|
|
117
|
+
static async all(dir: DirectoryPath): Promise<GitRepo[]> {
|
|
118
|
+
const repos: GitRepo[] = []
|
|
119
|
+
const dotGits = glob.sync(dir + '/*/.git')
|
|
120
|
+
for (const dotGit of dotGits) {
|
|
121
|
+
const dir = path.dirname(dotGit) as DirectoryPath
|
|
122
|
+
const remote = (await simpleGit(dir).getRemotes(true)).find(r => r.name === 'origin')
|
|
123
|
+
if (remote) {
|
|
124
|
+
repos.push(new GitRepo(remote.refs.fetch as Url, dir))
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return repos
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Extract default name from repo URL.
|
|
132
|
+
*/
|
|
133
|
+
static defaultName(repo: Url): string {
|
|
134
|
+
const { pathname } = gitUrlParse(repo)
|
|
135
|
+
return path.basename(pathname).replace(/\.git/, '')
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Ensure repo is downloaded and return repo instance.
|
|
140
|
+
*/
|
|
141
|
+
static async get(repoUrl: Url, parentDir: DirectoryPath, runYarnInstall = false): Promise<GitRepo | undefined> {
|
|
142
|
+
const repo = new GitRepo(repoUrl, parentDir)
|
|
143
|
+
const fetched = await repo.fetch()
|
|
144
|
+
|
|
145
|
+
if (fetched && runYarnInstall) {
|
|
146
|
+
await systemPiped(`cd "${repo.fullPath}" && yarn install`)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return fetched ? repo : undefined
|
|
150
|
+
}
|
|
151
|
+
}
|
package/src/net/index.ts
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import cors from 'cors'
|
|
2
|
+
import express, { Request, Response, RequestHandler, ErrorRequestHandler } from 'express'
|
|
3
|
+
import { error, log, MAX_UPLOAD_SIZE, Secret, Token, TokenAudience, Url } from '@dataplug/tasenor-common'
|
|
4
|
+
import { tokens } from './tokens'
|
|
5
|
+
import { vault } from './vault'
|
|
6
|
+
import helmet from 'helmet'
|
|
7
|
+
import { JwtPayload } from 'jsonwebtoken'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Hide tokens from URL.
|
|
11
|
+
* @param url
|
|
12
|
+
*/
|
|
13
|
+
export function cleanUrl(url: string): string {
|
|
14
|
+
return url.replace(/\btoken=[^&]+\b/, 'token=xxxx')
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A parameter definition to the initial middleware stack.
|
|
19
|
+
*/
|
|
20
|
+
export interface InitialMiddlewareStackDefinition {
|
|
21
|
+
origin: Url | '*',
|
|
22
|
+
api?: Url
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Construct standard initial part of stack of commonly shared middlewares.
|
|
27
|
+
*/
|
|
28
|
+
export function tasenorInitialStack(args: InitialMiddlewareStackDefinition): RequestHandler[] {
|
|
29
|
+
const stack : RequestHandler[] = []
|
|
30
|
+
|
|
31
|
+
// Add logger.
|
|
32
|
+
stack.push((req: Request, res: Response, next: (arg?: Error) => unknown) => {
|
|
33
|
+
if (req.method !== 'OPTIONS') {
|
|
34
|
+
let owner
|
|
35
|
+
const token = tokens.get(req)
|
|
36
|
+
if (token) {
|
|
37
|
+
const parsed = tokens.parse(token)
|
|
38
|
+
if (parsed && parsed.payload) {
|
|
39
|
+
const payload = parsed.payload as JwtPayload
|
|
40
|
+
owner = payload.data.owner
|
|
41
|
+
let aud = payload.aud
|
|
42
|
+
if (payload.aud === 'refresh') {
|
|
43
|
+
aud = payload.data.audience
|
|
44
|
+
}
|
|
45
|
+
switch (aud) {
|
|
46
|
+
case 'sites':
|
|
47
|
+
owner = `Site ${owner}`
|
|
48
|
+
break
|
|
49
|
+
case 'bookkeeping':
|
|
50
|
+
owner = `User ${owner}`
|
|
51
|
+
break
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const user = owner ? `${owner} from ${req.ip}` : `${req.ip}`
|
|
56
|
+
const message = `${user} ${req.method} ${req.hostname} ${cleanUrl(req.originalUrl)}`
|
|
57
|
+
log(message)
|
|
58
|
+
}
|
|
59
|
+
next()
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
// Add cors.
|
|
63
|
+
stack.push(cors({ origin: args.origin }))
|
|
64
|
+
|
|
65
|
+
// Add helmet.
|
|
66
|
+
let contentSecurityPolicy
|
|
67
|
+
if (args.api) {
|
|
68
|
+
const apiOrigin = new URL(args.api).origin
|
|
69
|
+
contentSecurityPolicy = {
|
|
70
|
+
useDefaults: true,
|
|
71
|
+
crossOriginResourcePolicy: { policy: 'same-site' },
|
|
72
|
+
directives: {
|
|
73
|
+
defaultSrc: ["'self'", apiOrigin],
|
|
74
|
+
imgSrc: ["'self'", 'data:', apiOrigin],
|
|
75
|
+
scriptSrc: ["'self'", "'unsafe-eval'"]
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
stack.push(helmet({
|
|
79
|
+
contentSecurityPolicy
|
|
80
|
+
}))
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return stack
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Construct standard final part of stack of commonly shared middlewares.
|
|
88
|
+
*/
|
|
89
|
+
export function tasenorFinalStack(): (ErrorRequestHandler | RequestHandler)[] {
|
|
90
|
+
const stack : (ErrorRequestHandler | RequestHandler)[] = []
|
|
91
|
+
|
|
92
|
+
// Add error catcher.
|
|
93
|
+
stack.push((err: Error, req: Request, res: Response, next: (arg?: Error) => unknown) => {
|
|
94
|
+
error('Internal error:', err)
|
|
95
|
+
if (res.headersSent) {
|
|
96
|
+
return next(err)
|
|
97
|
+
}
|
|
98
|
+
res.status(500).send({ message: 'Internal server error.' })
|
|
99
|
+
const message = `${req.ip} ${req.method} ${req.hostname} ${cleanUrl(req.originalUrl)} => ${res.statusCode}`
|
|
100
|
+
error(message)
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
return stack
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* A parameter defintion for construcing standard middleware stack.
|
|
108
|
+
*/
|
|
109
|
+
export interface MiddlewareStackDefinition {
|
|
110
|
+
url?: boolean,
|
|
111
|
+
json?: boolean,
|
|
112
|
+
user?: boolean,
|
|
113
|
+
admin?: boolean,
|
|
114
|
+
superuser?: boolean,
|
|
115
|
+
token?: boolean,
|
|
116
|
+
uuid?: boolean,
|
|
117
|
+
audience?: TokenAudience | TokenAudience[],
|
|
118
|
+
upload?: boolean
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* A constructor for tasenor middleware stack based on the arguments.
|
|
123
|
+
*
|
|
124
|
+
* Each flag adds one or more functions to the stack returned.
|
|
125
|
+
*
|
|
126
|
+
* Flags:
|
|
127
|
+
* - `url` Urlenconder parser.
|
|
128
|
+
* - `json` JSON body parser.
|
|
129
|
+
* - `token` Look for token from the request, but do not verify it yet.
|
|
130
|
+
* - `user` Check that user token is present and for bookkeeper.
|
|
131
|
+
* - `uuid` Check that UUID header is present and parse owner from the token. Verify signature of the token for ANY audience.
|
|
132
|
+
* - `admin` Check that the valid bookkeeper user token exists and has admin feat.
|
|
133
|
+
* - `superuser` Check that the valid bookkeeper user token exists and has superuser feat.
|
|
134
|
+
* - `audience` The token audience to check token against (user or admin implies 'bookkeeper' unless explicitly given).
|
|
135
|
+
* - `upload` If set, allow much bigger body for request.
|
|
136
|
+
*
|
|
137
|
+
* The output on the res.locals is:
|
|
138
|
+
*
|
|
139
|
+
* - `token` Raw token string.
|
|
140
|
+
* - `auth` Content of the token if verified.
|
|
141
|
+
* - `user` Name of the owner of the token if verified,
|
|
142
|
+
* - `uuid` If X-UUID header was found, the value of it.
|
|
143
|
+
* - `owner` Set when UUID is found and token signed for ANY audience.
|
|
144
|
+
*/
|
|
145
|
+
export function tasenorStack({ url, json, user, uuid, admin, superuser, audience, token, upload }: MiddlewareStackDefinition): RequestHandler[] {
|
|
146
|
+
const stack: RequestHandler[] = []
|
|
147
|
+
|
|
148
|
+
// Set automatic up implications.
|
|
149
|
+
if (superuser) {
|
|
150
|
+
admin = true
|
|
151
|
+
token = true
|
|
152
|
+
}
|
|
153
|
+
if (admin) {
|
|
154
|
+
user = true
|
|
155
|
+
token = true
|
|
156
|
+
}
|
|
157
|
+
if (user && !audience) {
|
|
158
|
+
audience = 'bookkeeping'
|
|
159
|
+
}
|
|
160
|
+
if (audience) {
|
|
161
|
+
token = true
|
|
162
|
+
}
|
|
163
|
+
if (uuid) {
|
|
164
|
+
token = true
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Add some space for upload.
|
|
168
|
+
const params: Record<string, unknown> = {}
|
|
169
|
+
if (upload) {
|
|
170
|
+
params.limit = MAX_UPLOAD_SIZE
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Add URL encoding middleware.
|
|
174
|
+
if (url || (upload && !url && !json)) {
|
|
175
|
+
stack.push(express.urlencoded({ extended: true, ...params }))
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Add JSON middleware.
|
|
179
|
+
if (json) {
|
|
180
|
+
stack.push(express.json({ ...params }))
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Find the token.
|
|
184
|
+
if (token) {
|
|
185
|
+
stack.push(async (req: Request, res: Response, next: (arg?: Error) => unknown) => {
|
|
186
|
+
res.locals.token = tokens.get(req)
|
|
187
|
+
next()
|
|
188
|
+
})
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Set the UUID and owner.
|
|
192
|
+
if (uuid) {
|
|
193
|
+
stack.push(async (req: Request, res: Response, next: (arg?: Error) => unknown) => {
|
|
194
|
+
if (!res.locals.token) {
|
|
195
|
+
error('There is no token in the request and we are looking for UUID.')
|
|
196
|
+
return res.status(403).send({ message: 'Forbidden.' })
|
|
197
|
+
}
|
|
198
|
+
const uuid = req.headers['x-uuid']
|
|
199
|
+
if (!uuid) {
|
|
200
|
+
error('Cannot find UUID from the request.')
|
|
201
|
+
return res.status(403).send({ message: 'Forbidden.' })
|
|
202
|
+
}
|
|
203
|
+
const parsed = tokens.parse(res.locals.token)
|
|
204
|
+
if (!parsed) {
|
|
205
|
+
error(`Cannot parse payload from the token ${res.locals.token}`)
|
|
206
|
+
return res.status(403).send({ message: 'Forbidden.' })
|
|
207
|
+
}
|
|
208
|
+
const payload: JwtPayload = parsed.payload as JwtPayload
|
|
209
|
+
const audience = payload.aud as TokenAudience
|
|
210
|
+
const secret = vault.getPrivateSecret()
|
|
211
|
+
const ok = tokens.verify(res.locals.token, secret, audience)
|
|
212
|
+
if (!ok) {
|
|
213
|
+
error(`Failed to verify token ${res.locals.token} for audience ${audience}.`)
|
|
214
|
+
return res.status(403).send({ message: 'Forbidden.' })
|
|
215
|
+
}
|
|
216
|
+
res.locals.uuid = uuid
|
|
217
|
+
res.locals.owner = ok.owner
|
|
218
|
+
next()
|
|
219
|
+
})
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Add token check middleware.
|
|
223
|
+
if (audience) {
|
|
224
|
+
stack.push(async (req: Request, res: Response, next: (arg?: Error) => unknown) => {
|
|
225
|
+
const token: Token = res.locals.token
|
|
226
|
+
if (!token) {
|
|
227
|
+
error(`Request ${req.method} ${cleanUrl(req.originalUrl)} from ${req.ip} has no token.`)
|
|
228
|
+
res.status(401).send({ message: 'Unauthorized.' })
|
|
229
|
+
return
|
|
230
|
+
}
|
|
231
|
+
const secret: Secret = audience === 'refresh' ? await vault.get('SECRET') as Secret : vault.getPrivateSecret()
|
|
232
|
+
if (!secret) {
|
|
233
|
+
error('Cannot find SECRET.')
|
|
234
|
+
return res.status(500).send({ message: 'Unable to handle authorized requests at the moment.' })
|
|
235
|
+
}
|
|
236
|
+
if (!audience) {
|
|
237
|
+
return res.status(500).send({ message: 'Internal error.' })
|
|
238
|
+
}
|
|
239
|
+
const payload = tokens.verify(token, secret, audience)
|
|
240
|
+
if (!payload) {
|
|
241
|
+
error(`Request from ${req.ip} has bad token ${token}`)
|
|
242
|
+
return res.status(403).send({ message: 'Forbidden.' })
|
|
243
|
+
}
|
|
244
|
+
// Check admin.
|
|
245
|
+
if (admin && !payload.feats.ADMIN && !payload.feats.SUPERUSER) {
|
|
246
|
+
error(`Request denied for admin access to ${JSON.stringify(payload)}`)
|
|
247
|
+
return res.status(403).send({ message: 'Forbidden.' })
|
|
248
|
+
}
|
|
249
|
+
// Check superuser.
|
|
250
|
+
if (superuser && !payload.feats.SUPERUSER) {
|
|
251
|
+
error(`Request denied for superuser access to ${JSON.stringify(payload)}`)
|
|
252
|
+
return res.status(403).send({ message: 'Forbidden.' })
|
|
253
|
+
}
|
|
254
|
+
res.locals.auth = payload
|
|
255
|
+
res.locals.user = payload.owner
|
|
256
|
+
next()
|
|
257
|
+
})
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return stack
|
|
261
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { error, TokenPayload, Secret, TokenAudience, Token, RefreshTokenPayload, TokenPair, NormalTokenPayload, REFRESH_TOKEN_EXPIRY_TIME, TOKEN_EXPIRY_TIME, TOKEN_ISSUER } from '@dataplug/tasenor-common'
|
|
2
|
+
import jwt, { Jwt } from 'jsonwebtoken'
|
|
3
|
+
import { create } from 'ts-opaque'
|
|
4
|
+
import { vault } from './vault'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Find a token from the request if available.
|
|
8
|
+
* @param request A HTTP request.
|
|
9
|
+
*/
|
|
10
|
+
function get(request): Token | undefined {
|
|
11
|
+
let token
|
|
12
|
+
if (request.query && request.query.token) {
|
|
13
|
+
token = request.query.token
|
|
14
|
+
} else if (request.headers.authorization && /^Bearer /.test(request.headers.authorization)) {
|
|
15
|
+
token = request.headers.authorization.substr(7)
|
|
16
|
+
}
|
|
17
|
+
return token
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Sign the payload with the given secret.
|
|
22
|
+
* @param payload
|
|
23
|
+
* @param expires
|
|
24
|
+
* @returns A JSON web token.
|
|
25
|
+
*/
|
|
26
|
+
async function sign(payload: TokenPayload, audience: TokenAudience, expires = 0): Promise<Token> {
|
|
27
|
+
const secret = audience === 'refresh' ? await vault.get('SECRET') as Secret : vault.getPrivateSecret()
|
|
28
|
+
if (!secret) {
|
|
29
|
+
throw new Error('Cannot fins secret to sign token.')
|
|
30
|
+
}
|
|
31
|
+
if (!expires) {
|
|
32
|
+
expires = audience === 'refresh' ? REFRESH_TOKEN_EXPIRY_TIME : TOKEN_EXPIRY_TIME
|
|
33
|
+
}
|
|
34
|
+
const options = {
|
|
35
|
+
audience,
|
|
36
|
+
expiresIn: expires,
|
|
37
|
+
issuer: TOKEN_ISSUER
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const token = create(jwt.sign({ data: payload }, secret, options)) as Token
|
|
41
|
+
return token
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Sign both the normal token and refresh token for it.
|
|
46
|
+
* @param payload
|
|
47
|
+
* @param audience
|
|
48
|
+
* @param expires
|
|
49
|
+
*/
|
|
50
|
+
async function sign2(payload: TokenPayload, audience: TokenAudience, expires = 0): Promise<TokenPair> {
|
|
51
|
+
const token = await sign(payload, audience, expires)
|
|
52
|
+
const refresh = await sign({ audience, owner: payload.owner, feats: payload.feats, plugins: payload.plugins }, 'refresh', expires * 2)
|
|
53
|
+
return { token, refresh }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Check the token validity.
|
|
58
|
+
* @param token
|
|
59
|
+
* @param secret
|
|
60
|
+
* @param quiet If set, do not trigger errors.
|
|
61
|
+
* @returns Token payload if valid.
|
|
62
|
+
*/
|
|
63
|
+
function verify(token: Token, secret: Secret, audience: TokenAudience | TokenAudience[], quiet = false): TokenPayload | null {
|
|
64
|
+
if (!secret) throw new Error('Cannot verify token since no secret given.')
|
|
65
|
+
if (!audience) throw new Error('Cannot verify token since no audience given.')
|
|
66
|
+
|
|
67
|
+
function fail(msg: string): void {
|
|
68
|
+
if (!quiet) error(msg)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
const decoded: jwt.JwtPayload = jwt.verify(token, secret, { audience, issuer: [TOKEN_ISSUER] }) as jwt.JwtPayload
|
|
73
|
+
if (!decoded) {
|
|
74
|
+
fail('Cannot decode the token.')
|
|
75
|
+
} else if (!decoded.data) {
|
|
76
|
+
fail(`Cannot find any payload from the token ${token}.`)
|
|
77
|
+
} else {
|
|
78
|
+
if (!decoded.exp) {
|
|
79
|
+
fail(`Token content ${decoded} does not have exp-field.`)
|
|
80
|
+
return null
|
|
81
|
+
}
|
|
82
|
+
if (decoded.data.audience) {
|
|
83
|
+
const data = decoded.data as RefreshTokenPayload
|
|
84
|
+
if (!data.owner || !data.feats) {
|
|
85
|
+
fail(`Cannot find proper payload from the refresh token with content ${JSON.stringify(decoded)}.`)
|
|
86
|
+
return null
|
|
87
|
+
} else {
|
|
88
|
+
return data
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
const data = decoded.data as NormalTokenPayload
|
|
92
|
+
if (!data.owner || !data.feats) {
|
|
93
|
+
fail(`Cannot find proper payload from the token with content ${JSON.stringify(decoded)}.`)
|
|
94
|
+
return null
|
|
95
|
+
} else {
|
|
96
|
+
return data
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
} catch (err) {
|
|
101
|
+
fail(`Token verification failed ${err} for ${JSON.stringify(parse(token))}`)
|
|
102
|
+
}
|
|
103
|
+
return null
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Parse the payload of the token without verifying.
|
|
108
|
+
* @param token
|
|
109
|
+
*/
|
|
110
|
+
function parse(token: Token): Jwt | null {
|
|
111
|
+
const decoded = jwt.decode(token, { json: true, complete: true })
|
|
112
|
+
return decoded
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* A checker for token validity.
|
|
117
|
+
* @param token
|
|
118
|
+
* @param audience
|
|
119
|
+
* @param quiet If set, do not trigger errors.
|
|
120
|
+
*/
|
|
121
|
+
async function check(token: Token, audience: TokenAudience, quiet = false): Promise<boolean> {
|
|
122
|
+
if (!token) {
|
|
123
|
+
return false
|
|
124
|
+
}
|
|
125
|
+
const secret = audience === 'refresh' ? await vault.get('SECRET') as Secret : vault.getPrivateSecret()
|
|
126
|
+
if (!secret) {
|
|
127
|
+
return false
|
|
128
|
+
}
|
|
129
|
+
const payload = tokens.verify(token as Token, secret, audience, quiet)
|
|
130
|
+
return !!payload
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export const tokens = {
|
|
134
|
+
check,
|
|
135
|
+
get,
|
|
136
|
+
parse,
|
|
137
|
+
sign,
|
|
138
|
+
sign2,
|
|
139
|
+
verify
|
|
140
|
+
}
|