beanbagdb 0.5.80 → 0.6.1

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/test/pouchdb.js CHANGED
@@ -3,7 +3,7 @@ import Ajv from 'ajv';
3
3
  import PouchDB from 'pouchdb';
4
4
  import pouchdbFind from 'pouchdb-find';
5
5
  PouchDB.plugin(pouchdbFind)
6
- import { scryptSync, randomBytes, createCipheriv, createDecipheriv } from 'crypto';
6
+ import crypto from 'crypto'
7
7
 
8
8
 
9
9
  export const get_pdb_doc = (dbname,secret)=>{
@@ -42,28 +42,75 @@ const pdb = new PouchDB(dbname);
42
42
  },
43
43
  utils: {
44
44
  encrypt: async (text, encryptionKey) => {
45
- //console.log(encryptionKey)
46
- const key = scryptSync(encryptionKey, "salt", 32); // Derive a 256-bit key
47
- const iv = randomBytes(16); // Initialization vector
48
- const cipher = createCipheriv("aes-256-cbc", key, iv);
49
- let encrypted = cipher.update(text, "utf8", "hex");
50
- encrypted += cipher.final("hex");
51
- //console.log("IV:", iv.toString("hex"));
52
- //console.log("Encrypted:", encrypted);
53
- return iv.toString("hex") + ":" + encrypted; // Prepend the IV for decryption
45
+ const encoder = new TextEncoder();
46
+ const data = encoder.encode(text); // Encode the text into bytes
47
+
48
+ // Ensure the encryption key is of valid length (16, 24, or 32 bytes for AES-GCM)
49
+ const keyBytes = encoder.encode(encryptionKey);
50
+ if (
51
+ keyBytes.length !== 16 &&
52
+ keyBytes.length !== 24 &&
53
+ keyBytes.length !== 32
54
+ ) {
55
+ throw new Error("Encryption key must be 16, 24, or 32 bytes long.");
56
+ }
57
+
58
+ // Convert encryptionKey to CryptoKey
59
+ const key = await crypto.subtle.importKey(
60
+ "raw",
61
+ keyBytes,
62
+ { name: "AES-GCM" },
63
+ false,
64
+ ["encrypt"]
65
+ );
66
+
67
+ // Create a random initialization vector (IV)
68
+ const iv = crypto.getRandomValues(new Uint8Array(12)); // 12 bytes for AES-GCM
69
+
70
+ // Encrypt the data
71
+ const encrypted = await crypto.subtle.encrypt(
72
+ { name: "AES-GCM", iv: iv },
73
+ key,
74
+ data
75
+ );
76
+
77
+ // Convert encrypted data and IV to base64 for storage
78
+ const encryptedArray = new Uint8Array(encrypted);
79
+ const encryptedText = btoa(String.fromCharCode(...encryptedArray));
80
+ const ivText = btoa(String.fromCharCode(...iv));
81
+
82
+ return ivText + ":" + encryptedText; // Store IV and encrypted text together
54
83
  },
55
- decrypt: async (encryptedText, encryptionKey) => {
56
- //console.log(encryptedText, encryptionKey)
57
- const key = scryptSync(encryptionKey, "salt", 32); // Derive a 256-bit key
58
- const [ivHex, encryptedHex] = encryptedText.split(":");
59
- const iv = Buffer.from(ivHex, "hex");
60
- const encrypted = Buffer.from(encryptedHex, "hex");
61
- //console.log("IV:", iv.toString("hex"));
62
- //console.log("Encrypted:", encrypted.toString("hex"));
63
- const decipher = createDecipheriv("aes-256-cbc", key, iv);
64
- let decrypted = decipher.update(encrypted, "hex", "utf8");
65
- decrypted += decipher.final("utf8");
66
- return decrypted;
84
+ decrypt: async (encryptedText, encryptionKey) => {
85
+ const [ivText, encryptedData] = encryptedText.split(":");
86
+
87
+ // Convert IV and encrypted data from base64 to byte arrays
88
+ const iv = Uint8Array.from(atob(ivText), (c) => c.charCodeAt(0));
89
+ const encryptedArray = Uint8Array.from(atob(encryptedData), (c) =>
90
+ c.charCodeAt(0)
91
+ );
92
+
93
+ const encoder = new TextEncoder();
94
+
95
+ // Convert encryptionKey to CryptoKey
96
+ const key = await crypto.subtle.importKey(
97
+ "raw",
98
+ encoder.encode(encryptionKey),
99
+ { name: "AES-GCM" },
100
+ false,
101
+ ["decrypt"]
102
+ );
103
+
104
+ // Decrypt the data
105
+ const decrypted = await crypto.subtle.decrypt(
106
+ { name: "AES-GCM", iv: iv },
107
+ key,
108
+ encryptedArray
109
+ );
110
+
111
+ // Convert decrypted data back to a string
112
+ const decoder = new TextDecoder();
113
+ return decoder.decode(decrypted);
67
114
  },
68
115
  ping: () => {
69
116
  // @TODO ping the database to check connectivity when class is ready to use
@@ -1,191 +0,0 @@
1
- const commands = {
2
- new: {
3
- parse: async (instance,parts) => {
4
- let criteria = {}
5
- criteria.schema = parts.length==0?"":parts.join("")
6
- return criteria
7
- },
8
- run: async (instance,command) => {
9
- if (command.criteria.schema==""){
10
- // return a list of all schemas present in the DB
11
- let all_schema = await instance.get("schema_list")
12
- return all_schema
13
- }else{
14
- // return the schema object for the given schema if not found throw error
15
- let schema_obj = await instance.search({"selector":{"schema":"schema","data.name":command.criteria.schema}})
16
- //console.log(schema_obj)
17
- if(schema_obj.docs.length==0){
18
- throw new Error("Schema with this name does not exists")
19
- }
20
- return schema_obj.docs[0]
21
- }
22
- },
23
- help: `To create a new record. Format : new/"schema_name(optional)". If no schema name provided, a list of valid schema name is returned`
24
- },
25
- open:{
26
- parse: async (instance,parts) => {
27
- let criteria = {}
28
- if (parts.length==0){
29
- throw new Error("Invalid arguments.open command needs unique id")
30
- }
31
- let id_type = parts[0]
32
- if(id_type=="id"){
33
- parts.shift()
34
- criteria["_id"] = parts.join("")
35
- }else if(id_type=="link"){
36
- parts.shift()
37
- criteria["link"] = parts.join("")
38
- }else if(id_type=="key"){
39
- parts.shift()
40
- let text= parts.join()
41
- let p = text.split(",")
42
-
43
- p.map(itm=>{
44
- let p1 = itm.split("=")
45
- if(p1[0]=="schema"){
46
- criteria["schema"] = p1[1]
47
- }else{
48
- criteria["data"][p1[0]] = p1[1]
49
- }
50
- })
51
-
52
- if(!criteria["schema"]){
53
- throw new Error("Key requires a schema")
54
- }
55
- }else{
56
- throw new Error("Invalid unique key")
57
- }
58
- return criteria
59
- },
60
- run: async (instance,command) => {
61
- try {
62
- let data = await instance.read(command.criteria,true)
63
- return data
64
- } catch (error) {
65
- throw error
66
- }
67
- },
68
- help: `To open a record using it's unique id. Format : open/"id||link|key"/"value". In case of key field name must be provided as : field1=value1,fields2=value2...`
69
- },
70
- tool:{
71
- parse : async (instance,parts)=>{
72
- let criteria = {}
73
- criteria.type = parts.length==0?"info":parts.join("")
74
- return criteria
75
- },
76
- run : async (instance,command)=>{
77
- let c_type = command.criteria.type
78
- let data = {}
79
- if (c_type=="info"){
80
- // to get all basic info about the database
81
- let data = {
82
- meta: instance.metadata(),
83
- schemas : {},
84
- logs:[]
85
- }
86
- let schemas = await instance.get("schema_list")
87
- data.schemas = schemas
88
-
89
- let logs_doc = await instance.read({"schema":"system_settings","data":{name:"system_logs"}})
90
- //console.log(logs_doc)
91
- data =logs_doc.doc.data.value
92
- return data
93
-
94
- }else if(c_type=="plugins"){
95
- // to show list of all plugins installed
96
- // todo later not implemented yet
97
- }else if(c_type=="settings"){
98
- // to show the list of all setting docs available
99
- let search = instance.search({"selector":{"schema":"system_settings"}})
100
- return {docs:search.docs}
101
- }
102
- else if(c_type=="keys"){
103
- // to show the list of all keys present in the db
104
- let search = instance.search({"selector":{"schema":"system_keys"}})
105
- return {docs:search.docs}
106
- }
107
- else{
108
- throw new Error("Invalid tool command")
109
- }
110
- },
111
- }
112
- };
113
-
114
- const parse = async (instance, text) => {
115
- let data = {
116
- errors: [],
117
- valid: false,
118
- name: "",
119
- criteria: {},
120
- };
121
- if (!text) {
122
- data.errors.push(
123
- "No text command provided. Format : command_name/parameter"
124
- );
125
- }
126
- let parts = text.split("/");
127
- if (parts.length == 0) {
128
- data.errors.push("Invalid text command");
129
- }
130
- let command_name = parts[0];
131
- if (!commands[command_name]) {
132
- data.errors.push(
133
- "Invalid command name. Valid : " + Object.keys(commands).join(",")
134
- );
135
- }
136
- data.name = command_name;
137
- try {
138
- parts.shift();
139
- let criteria = await commands[command_name].parse(instance,parts);
140
- data.criteria = criteria;
141
- } catch (error) {
142
- data.errors.push(error.message);
143
- }
144
- if (data.errors.length == 0) {
145
- data.valid = true;
146
- }
147
- return data;
148
- };
149
-
150
- const run = async (instance, command) => {
151
- let data = {
152
- result:{},
153
- errors:[],
154
- valid:false
155
- };
156
-
157
- if (!command) {
158
- data.errors.push("No command object provided ");
159
- }
160
- if (!command.valid){
161
- data.errors["Command cannot be run"]
162
-
163
- }
164
- if(!commands[command.name]){
165
- data.errors["Invalid command name"]
166
- }
167
-
168
- try {
169
- let data1 = await commands[command.name].run(instance,command)
170
- //console.log(data)
171
- data.result = data1
172
- } catch (error) {
173
- data.errors.push(error.message)
174
- }
175
- if(data.errors.length==0){
176
- data.valid = true
177
- }
178
- return data
179
- };
180
-
181
- const parse_and_run = async(instance, text) => {
182
- let command = await parse(instance,text)
183
- console.log(command)
184
- let command_result = await run(instance,command)
185
- return command_result
186
- }
187
- // const schemas = []
188
-
189
- export const text_command = {
190
- actions: {parse,run,parse_and_run}
191
- };
@@ -1,52 +0,0 @@
1
- // to test database operations. assuming the class is initialized successfully
2
- // to test initialization of the BeanBagDB class
3
- import { get_pdb_doc } from "./pouchdb.js";
4
- import assert, { throws, strictEqual, rejects } from "assert";
5
- import { BeanBagDB, DocCreationError, EncryptionError, ValidationError,DocNotFoundError, DocUpdateError } from "../src/index.js";
6
-
7
- import {text_command} from "../src/plugins/text_command.js"
8
-
9
- import * as chai from 'chai';
10
- import chaiAsPromised from 'chai-as-promised';
11
-
12
- chai.use(chaiAsPromised);
13
-
14
- // Then either:
15
- const expect = chai.expect;
16
-
17
- let database; // this is the global db object
18
-
19
-
20
- describe("Testing plugin load", async () => {
21
-
22
- before(async () => {
23
- let doc_obj = get_pdb_doc("test_database_30", "qwertyuiopaqwsde1254");
24
- database = new BeanBagDB(doc_obj);
25
- await database.ready(); // Ensure the database is ready before running tests
26
- console.log("Ready for more tests...");
27
- });
28
-
29
- it('successfully loads the plugin', async () => {
30
- try {
31
- await database.load_plugin("txtcmd",text_command)
32
- chai.expect(database.plugins).to.not.be.empty
33
- } catch (error) {
34
- console.log(error)
35
- throw error
36
- }
37
- })
38
-
39
- it('successfully runs the loaded the plugin method', async () => {
40
- try {
41
- //await database.load_plugin("txtcmd",text_command)
42
- let command = await database.plugins["txtcmd"].parse("new/system_keys")
43
- //console.log(command)
44
- assert (command.valid ==true)
45
- } catch (error) {
46
- console.log(error)
47
- throw error
48
- }
49
- })
50
- });
51
-
52
-