How to Encrypt Realm in React Native - javascript

I am trying to encrypt Realm on my React native app using the PIN user sets for the app after loging in.
User logs into the app and then sets a PIN which user is going to use for the following interactions with the app. I want to use this PIN as the encryption key.
I have looked at examples on
https://realm.io/docs/javascript/latest/#encryption
Key Generation/Storage for react-native apps to encrypt realm db
Got an idea that I need to add the encryption key at the point of opening the database but how can I pass key to the database options. if you can suggest any better way that would be greatful.
Here is my flow,
ConfirmPIN.js
import { insertUser, deleteUser } from '../../../databases/schemas';
savePin = (userPIN) => {
const newUser = {
id: 1,
userName: 'Here goes the user name',
pin: userPIN
}
deleteUser(1).then().catch((error) => {
console.log(error);
});
insertUser(newUser).then().catch((error) => {
alert(error);
});
}
schemas.js
export const userSchema = {
name: USER_SCHEMA,
primaryKey: 'id',
properties: {
id: 'int',
userName: 'string',
pin: 'string'
}
}
const databaseOptions = {
path: 'retailApp.realm',
schema: [ userSchema, appDataSchema],
schemaVersion: 0 //optional
//SHOULD I BE ADDING THE KEY HERE? IF SO HOW CAN I PASS THE KEY SET BY THE
//USER
}
export const insertUser = user => new Promise ((resolve, reject) => {
Realm.open(databaseOptions).then(realm => {
realm.write(() => {
realm.create(USER_SCHEMA, user);
resolve(user);
});
}).catch((error) => reject(error));
});
export const deleteUser = userID => new Promise((resolve, reject) => {
Realm.open(databaseOptions).then(realm => {
realm.write(() => {
let userToDelete = realm.objectForPrimaryKey(USER_SCHEMA, userID);
realm.delete(userToDelete);
resolve();
});
}).catch((error) => reject(error));
});
securityUtils.js
Code to get the base64Encoded to be the KEY (Not tested how it works yet)
export const getBase64Encodedpin = pin => {
var utf8 = require('utf8');
var binaryToBase64 = require('binaryToBase64');
var text = pin;
var bytes = utf8.encode(text);
var base64Encoded = binaryToBase64(bytes);
return base64Encoded;
}
I want to be able to pass this key to schemas.js when opening the database, can you suggest on how I will be able to do that.
Thanks
R

Here Retrieve your key from secure location or create one like below...
var key = new Int8Array(64);
Add Encryption Key in the schema
export const userSchema = {
name: USER_SCHEMA,
primaryKey: 'id',
properties: {
id: 'int',
userName: 'string',
pin: 'string'
},
encryptionKey: key, // add Your Pin/key Here
}
Add key in databaseOptions
const databaseOptions = {
path: 'retailApp.realm',
schema: [ userSchema, appDataSchema],
schemaVersion: 0, //optional
encryptionKey: key, // add Your Pin/key Here
}
After searching I implemented this ,

Related

Can't get the data that stored in Redis

I'm trying to create a user after he verified the code that I send him
so first I generate the code in sendCode resolver and save it in Redis using setex
the problem is that code is set in Redis but when I try to use it in createUser resolver using get it returns null.
const sendCode: MutationResolvers["sendCode"] = async ({
input: { phoneNumber, email },
}: {
input: SendCodeInput;
}) => {
const code = generate4digitNum();
await redis.setex(phoneNumber ?? email, THREE_MINS, code);
return {};
};
const createUser: MutationResolvers["createUser"] = async ({
input: { ...userData },
}: {
input: CreateUserInput;
}) => {
const code = await redis.get(userData.phoneNumber ?? userData.email);
if (code !== userData.code) {
throw new Error(errors[0].id);
}
user = await userModel.create({ ...userData});
return {type: user.type, _id: user._id };
};
the redis.ts file that I create:
const client = redis.createClient({
host: process.env.REDIS_HOST,
password: process.env.REDIS_PASSWORD,
port: Number(process.env.REDIS_PORT),
});
client
.on("connect", function () {
console.log(`connected ${client.connected}`);
})
.on("error", function (error) {
console.log(error);
});
export const get: (key: string) => Promise<string> = promisify(client.get).bind(
client
);
export const setex: (
key: string,
seconds: number,
value: string
) => Promise<string> = promisify(client.setex).bind(client);
I will appreciate any kind of help.
Thanks in advance.

How to push data with Mongoose to a nested array in MongoDB

I'm trying to push data to a nested array in mongodb. I'm using mongoose as well.
This is just mock code to see if i can get it working:
User model:
import mongoose from "mongoose";
const CoinSchema = new mongoose.Schema({
coinID: { type: String },
});
const CoinsSchema = new mongoose.Schema({
coin: [CoinSchema],
});
const WatchlistSchema = new mongoose.Schema({
watchlistName: { type: String },
coins: [CoinsSchema],
});
const NameSchema = new mongoose.Schema({
firstName: { type: String },
lastName: { type: String },
username: { type: String },
});
const UserSchema = new mongoose.Schema({
name: [NameSchema],
watchlists: [WatchlistSchema],
test: String,
});
const User = mongoose.model("User", UserSchema);
export default User;
route:
fastify.put("/:id", async (request, reply) => {
try {
const { id } = request.params;
const newCoin = request.body;
const updatedUser = await User.findByIdAndUpdate(id, {
$push: { "watchlists[0].coins[0].coin": newCoin },
});
await updatedUser.save();
// console.dir(updatedUser, { depth: null });
reply.status(201).send(updatedUser);
} catch (error) {
reply.status(500).send("could not add to list");
}
});
request.body // "coinID": "test"
I've tried a lot of different ways to push this data but still no luck. I still get 201 status codes in my terminal which indicates something has been pushed to the DB, but when I check nothing new is there.
Whats the correct way to target nested arrays and push data to them?
It's not perfect but you could get the user document, update the user's watchlist, and then save the updated watchlist like so:
fastify.put("/:id", async (request, reply) => {
try {
const { id } = request.params;
const newCoin = request.body;
// get the user
let user = await User.findById(id);
// push the new coin to the User's watchlist
user.watchlists[0].coins[0].coin.push(newCoin);
//update the user document
const updatedUser = await User.findOneAndUpdate({ _id: id },
{
watchlists: user.watchlists,
},
{
new: true,
useFindAndModify: false
}
);
reply.status(201).send(updatedUser);
} catch (error) {
reply.status(500).send("could not add to list");
}
});

Issue with checking and adding document item to collection in MongoDB

I'm new in Nodejs and I'm trying to create Video with hashtag. There are hashtags already storaged in DB, and hashtag that user will create (which will be added when submit video).
For example, I add more than 2 hashtags, the code below works for 2 cases:
If there is no hashtag storaged in DB, it created and add all hashtags to video successfully
If hashtags is already in DB, it added video successfully
But when there are some hashtags already in DB and the other is not. It add only few hashtags to video, not all hashtags added. I don't know why. I want to fix this case.
I have 2 schemas like this:
// Video schema
const videoSchema = new mongoose.Schema({
url: {
type: String
},
hashtag: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'Hashtag'
}
})
and
// Hashtag schema
const hashtagSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true,
}
})
User will POST like this on server. On this example, Tag-In-DB-Already is in DB already and New-Tag is typed by user
{
"url": "https://youtube.com/JvDAQD4",
"hashtag": ["Tag-In-DB-Already", "New-Tag"]
}
videoObj like this
const videoObj = new Video({
url: data.URL,
hashtag: []
})
The checking code like this, I need to push ObjectId (of hashtag) to hashtagArr above. I'm checking for each hashtag, if hashtag not in DB, it will add to DB and then push to array. If hashtag is in DB, it also added to array. I want all hashtags that user submited will be added.
export const addHashtagToVideo = async (hashtagArr, videoObj) => {
await hashtagArr.forEach(hashtagName => { // for each hashtag check
Hashtag.findOne({ name: hashtagName.toLowerCase() }, (err, resp) => {
if (err) return
if (!resp) { // if there is no hashtag in DB
addNewHashtagToDB(hashtagName) // run add new hashtag function
.then(hashtagId => { // newHashtag._id returned from below function
videoObj.hashtag.push({ _id: hashtagId })
})
} else {
videoObj.hashtag.push({ _id: resp._id }) // if found in DB, also pushed to video
}
})
})
}
export const addNewHashtagToDB = async (hashtagName) => {
const newHashtag = await new Hashtag({
name: hashtagName.toLowerCase(),
})
newHashtag.save()
return newHashtag._id
}
Thank you for help
you need to know what is function return...
export const addHashtagToVideo = async (hashtagArr, videoObj) => {
const waitAllDone = hashtagArr.map(async tag => { // tag in hashtagArr
const doc = await addNewHashtagToDB(tag) // find it or create it
return doc.id
})
const ary = await Promise.all(waitAllDone) // [id1, id2]
videoObj.hashtag = ary
// return videoObj
}
export const addNewHashtagToDB = async tag => {
const name = tag.toLowerCase() // whatever tag is, fix it
let doc = await Hashtag.findOne({ name }).exec() // try to find it
if (!doc) {
// not exist
doc = new Hashtag({ name }) // create new doc
await doc.save()
}
return doc
}
another version
export const addHashtagToVideo = async (hashtagArr, videoObj) => {
const waitAllDone = hashtagArr.map(addNewHashtagToDB) // amazing
const ary = await Promise.all(waitAllDone) // [id1, id2]
videoObj.hashtag = ary // replace it to prevent duplicat
// return videoObj
}
export const addNewHashtagToDB = async tag => {
const name = tag.toLowerCase() // whatever tag is, fix it
let doc = await Hashtag.findOne({ name }).exec() // try to find it
if (!doc) {
// not exist
doc = new Hashtag({ name }) // create new doc
await doc.save()
}
return doc.id // return it here
}

Sequelize associations between two tables

I am fairly new to this (using sequelize) and everything is new to me. The thing is that I can create and get users through my "users.model.js" but now I want to create a model called "data.model.js" to associate some data to a certain user.
So according to the sequelize docs, my associations should be the following:
Users.hasMany(Data)
Data.belongsTo(Users)
But when sequelize creates my tables, I don't have my foreign key in my data table.
I will share my code with you:
config file (config.js):
const Sequelize = require('sequelize');
const connection = new Sequelize('drglicemia', 'root', '', {
host: 'localhost',
dialect: 'mysql'
});
module.exports = connection;
data.model.js:
const sequelize = require('sequelize');
const db = require('../config/database');
const usersTable = require('./users.model')
let Data = db.define('tabeladados', {
dta: { type: sequelize.DATEONLY },
hora: { type: sequelize.DATE },
indiceglicemia: { type: sequelize.STRING },
insulina: { type: sequelize.STRING },
medicacao: { type: sequelize.STRING },
}, {
timeStamps: false, tableName: 'tabeladados'
});
//associates the dataTable table with the users
Data.associate = () => {
Data.belongsTo(usersTable)
}
module.exports = Data;
users.model.js:
const sequelize = require('sequelize');
const promise = require('bluebird')
const bcrypt = promise.promisifyAll(require('bcrypt'))
const db = require('../config/database');
const dataTable = require('./data.model')
let Users = db.define('utilizadores', {
username: { type: sequelize.STRING },
email: { type: sequelize.STRING },
password: { type: sequelize.STRING },
}, {
timeStamps: false, tableName: 'utilizadores',
});
//encrypts the password before submiting to the database
Users.beforeCreate((user, options) => {
return bcrypt.hash(user.password, 10)
.then(hash => {
user.password = hash;
})
.catch(err => {
throw new Error();
});
});
//validates the password submited by the user with the one encrypted in the database
Users.prototype.validPassword = async (password) => {
return await bcrypt.compare(password, this.password);
}
//associates the users table with the dataTable
Users.associate = () => {
Users.hasMany(dataTable)
}
module.exports = Users;
I believe that when I am trying to associate my tables I am doing something wrong, because I feel that I am doing it the wrong way.
I don't know but everything works besides this.
But it's like what I said in the beginning, I am new to sequelize xD
I think the reason is circular reference. user.model.js requires data.model.js, and data.model.js requires user.model.js.
You need to create an index.js file. Require and make the associations for all models here, then re-export them. E.g.
./models/index.js:
const User = require('./user.model.js');
const Data = require('./data.model.js');
User.hasMany(Data);
Data.belongsTo(User);
module.exports = {User, Data}
service.js or controller.js:
const models = require('./models/index.js');
// use models
await models.User.findAll();
Remove below codes in your model file:
// Remove from user.model.js file
Users.associate = () => {
Users.hasMany(dataTable)
}
// Remove from data.model.js file
Data.associate = () => {
Data.belongsTo(usersTable)
}

MongoDB/Express: Why does Array.includes return false instead of true?

I'm working on an tiny app that allows user to participate in polls, but I'm having problems checking if the current user has already voted in the poll. Everything else works fine, save for the IIFE that checks for said condition, as seen in the code snippet included. Indeed, I'm getting false as opposed to true with the data I have i.e. I already seeded the DB with sample data, including a random poll that contains the array of IDs for users who've already voted. I tried testing one ID against said array, which returns false as opposed to the expected true. What gives?
Below are the relevant snippets.
Model
import mongoose from 'mongoose';
const Schema = mongoose.Schema;
const ChoiceSchema = new Schema({
name: { type: String },
votes: { type: Number }
});
const PollSchema = new Schema({
title: { type: String },
category: { type: String },
choices: [ChoiceSchema],
addedBy: { type: Schema.Types.ObjectId, ref: 'User' },
votedBy: [{ type: Schema.Types.ObjectId, ref: 'User' }]
});
const Poll = mongoose.model('Poll', PollSchema);
export default Poll;
Controllers
import Poll from '../models/poll';
export default {
fetchAllPolls: async (req, res) => {
/*...*/
},
fetchSpecificPoll: async (req, res) => {
/*...*/
},
voteInPoll: async (req, res) => {
const { category, pollId } = req.params;
const { name, choiceId, voterId } = req.body;
try {
const poll = await Poll.findById(pollId);
const choice = await poll.choices.id(choiceId);
const votedChoice = {
name,
votes: choice.votes + 1,
};
// Check if user has already voted in poll
const hasVoted = ((votersIds, id) => votersIds.includes(id))(
poll.votedBy,
voterId
);
if (!voterId) {
res
.status(400)
.json({ message: 'Sorry, you must be logged in to vote' });
} else if (voterId && hasVoted) {
res.status(400).json({ message: 'Sorry, you can only vote once' });
} else {
await choice.set(votedChoice);
await poll.votedBy.push(voterId);
poll.save();
res.status(200).json({
message: 'Thank you for voting. Find other polls at: ',
poll,
});
}
} catch (error) {
res.status(404).json({ error: error.message });
}
},
createNewPoll: async (req, res) => {
/*...*/
},
};
I think you are trying to compare ObjectId with String representing the mongo id.
This should work:
const hasVoted = ((votersIds, id) => votersIds.findIndex((item) => item.toString() === id) !== -1)(
poll.votedBy,
voterId
);
or
const hasVoted = ((votersIds, id) => votersIds.findIndex((item) => item.equals(new ObjectId(id))) !== -1)(
poll.votedBy,
voterId
);
EDIT:
As #JasonCust suggested, a simpler form should be:
const hasVoted = poll.votedBy.some(voter => voter.equals(voterId));
It is more than likely that poll.votedBy is not an array of ID strings. If you are using it as a reference field then it is an array of BSON objects which would fail using includes because it uses the sameValueZero algorithm to compare values. If that is true then you could either convert all of the IDs to strings first or you could use find and the equals methods to find a match.
Update: showing actual code example
Also, some would provide an easier method for returning a boolean value.
const hasVoted = poll.votedBy.some((voter) => voter.equals(voterId));

Categories