Login system that checks out if the user already has an account. If they have one then they get to login, if they don't then they must try again - javascript

My overall problem is actually finding the values of the variables ''loginEmail'' and ''loginPass'' inside my ''arrayRegistros''. It only becomes TRUE when I write the email and password inside includes manually, however, it always ends up turning into FALSE when I use the variables themselves. I tried converting the variables into strings, then used document.getElementById alongside a few other ideas but until now, none of them completed the login system I had planned. The help I need is how one can find a certain variable's value/object, inside a certain array.
login(registro){
this.arrayRegistros;
var loginEmail = document.getElementById('userEmail');
var loginPass = document.getElementById('userPass');
var contaEmail = this.arrayRegistros.some((loginEmail) => {
return loginEmail.emailRegistro.includes(loginEmail)
})
var contaPass = this.arrayRegistros.some((loginPass) => {
return loginPass.passRegistro.includes(loginPass)
})
console.log(contaEmail)
console.log(contaPass)
}

you should get values from inputs like this:
const data = [
{
user : 'jhon',
password : 'asdf123'
},
{
user : 'bob',
password : 'asdf124'
}
]
const userName = document.getElementById('userEmail').value;
const passWord= document.getElementById('userPassword').value;
const findUser = data.filter( item => item.user === userName && item.password === passWord);
if(findUser.length > 0){
//user found
}

The first problem is that you are naming the parameter on the Array.prototype.some function the same as the variable you want to check outside of the predicate scope.
Second, suposing that this.arrayRegistros is a array with objects with the keys emailRegistro and passRegistro containing strings, DOM Elements CANNOT match with strings, but a element.value can.
Another thing you should have in mind is that includes is not an equality operator, 'a-very-strong-password'.includes('a'); will return true.
And, last, you should never validate login and password on the browser, because the user can edit the JavaScript code on-the-fly and get to login without any real credential.
With that in mind, I think the solution would be something like that (ignoring the browser validation problem):
const $userField = document.getElementById('userEmail');
const $passField = document.getElementById('userPass');
const registros = [
{
email: 'example#example.com',
password: 'a-very-strong-password'
},
...anotherUsers
];
function login(registro) {
const { value: user } = $userField;
const { value: pass } = $passField;
// You can use `Array.prototype.some` to just know if the specific user credentials exist, or use `Array.prototype.find` to know if exist AND grab the user, to further utilization
const item = registros.find(item => item.email === user && item.password === pass);
if (item) {
// User found
} else {
// User not found
}
}

Related

Override Mongoose save method to retry on `duplicate key error`

My Mongoose schema uses a custom _id value and the code I inherited does something like this
const sampleSchema = new mongoose.Schema({
_id: String,
key: String,
});
sampleSchema.statics.generateId = async function() {
let id;
do {
id = randomStringGenerator.generate({length: 8, charset: 'hex', capitalization: 'uppercase'});
} while (await this.exists({_id: id}));
return id;
};
let SampleModel = mongoose.model('Sample', sampleSchema);
A simple usage looks like this:
let mySample = new SampleModel({_id: await SampleModel.generateId(), key: 'a' });
await mySample.save();
There are at least three problems with this:
Every save will require at least two trips to the database, one to test for a unique id and one to save the document.
For this to work, it is necessary to manually call generateId() before each save. An ideal solution would handle that for me, like Mongoose does with ids of type ObjectId.
Most significantly, there is a potential race condition that will result in duplicate key error. Consider two clients running this code. Both coincidentally generate the same id at the same time, both look in the database and find the id absent, both try to write the record to the database. The second will fail.
An ideal solution would, on save, generate an id, save it to the database and on duplicate key error, generate a new id and retry. Do this in a loop until the document is stored successfully.
The trouble is, I don't know how to get Mongoose to let me do this.
Here's what I tried: Based on this SO Question, I found a rather old sample (using a very old mongoose version) of overriding the save function to accomplish something similar and based this attempt off it.
// First, change generateId() to force a collision
let ids = ['a', 'a', 'a', 'b'];
let index = 0;
let generateId = function() {
return ids[index++];
};
// Configure middleware to generate the id before a save
sampleSchema.pre('validate', function(next) {
if (this.isNew)
this._id = generateId();
next();
});
// Now override the save function
SampleModel.prototype.save_original = SampleModel.prototype.save;
SampleModel.prototype.save = function(options, callback) {
let self = this;
let retryOnDuplicate = function(err, savedDoc) {
if (err) {
if (err.code === 11000 && err.name === 'MongoError') {
self.save(options, retryOnDuplicate);
return;
}
}
if (callback) {
callback(err, savedDoc);
}
};
return self.save_original(options, retryOnDuplicate);
}
This gets me close but I'm leaking a promise and I'm not sure where.
let sampleA = new SampleModel({key: 'a'});
let sampleADoc = await sampleA.save();
console.log('sampleADoc', sampleADoc); // prints undefined, but should print the document
let sampleB = new SampleModel({key: 'b'});
let sampleBDoc = await sampleB.save();
console.log('sampleBDoc', sampleBDoc); // prints undefined, but should print the document
let all = await SampleModel.find();
console.log('all', all); // prints `[]`, but should be an array of two documents
Output
sampleADoc undefined
sampleBDoc undefined
all []
The documents eventually get written to the database, but not before the console.log calls are made.
Where am I leaking a promise? Is there an easier way to do this that addresses the three problems I outlined?
Edit 1:
Mongoose version: 5.11.15
I fixed the problem by changing the save override. The full solution looks like this:
const sampleSchema = new mongoose.Schema({
_id: String,
color: String,
});
let generateId = function() {
return randomStringGenerator.generate({length: 8, charset: 'hex', capitalization: 'uppercase'});
};
sampleSchema.pre('validate', function() {
if (this.isNew)
this._id = generateId();
});
let SampleModel = mongoose.model('Sample', sampleSchema);
SampleModel.prototype.save_original = SampleModel.prototype.save;
SampleModel.prototype.save = function(options, callback) {
let self = this;
let isDupKeyError = (error, field) => {
// Determine whether the error is a duplicate key error on the given field
return error?.code === 11000 && error?.name === 'MongoError' && error?.keyValue[field];
}
let saveWithRetries = (options, callback) => {
// save() returns undefined if used with callback or a Promise otherwise.
// https://mongoosejs.com/docs/api/document.html#document_Document-save
let promise = self.save_original(options, callback);
if (promise) {
return promise.catch((error) => {
if (isDupKeyError(error, '_id')) {
return saveWithRetries(options, callback);
}
throw error;
});
}
};
let retryCallback;
if (callback) {
retryCallback = (error, saved, rows) => {
if (isDupKeyError(error, '_id')) {
saveWithRetries(options, retryCallback);
} else {
callback(error, saved, rows);
}
}
}
return saveWithRetries(options, retryCallback);
}
This will generate an _id repeatedly until a successful save is called and addresses the three problems outlined in the original question:
The minimum trips to the database has been reduced from two to one. Of course, if there are collisions, more trips will occur but that's the exceptional case.
This implementation takes care of generating the id itself with no manual step to take before saving. This reduces complexity and removes the required knowledge of prerequisites for saving that are present in the original method.
The race condition has been addressed. It won't matter if two clients attempt to use the same key. One will succeed and the other will generate a new key and save again.
To improve this:
There ought to be a maximum number of save attempts for a single document followed by failure. In this case, you've perhaps used up all the available keys in whatever domain you're using.
The unique field may not be named _id or you might have multiple fields that require a unique generated value. The embedded helper function isDupKeyError() could be updated to look for multiple keys. Then on error you could add logic to regenerate just the failed key.

JavaScript send out an alert to prompt and edit array?

//global variable
var memArray =[];
//object
function member(id, password){
this.id = id;
this.pwd = password
}
var memObj1=new member("m001","123");
memArray.push(memObj1);
How do I send out an alert to prompt and edit each object that is push to memArray?
if you want to customize it try to use your own modals instead of window.prompt and just display values with editable text fields, on submit capture those values and change them in array respectively.
var memArray = [];
//object
function member(id, password) {
this.id = id;
this.pwd = password
}
var memObj1 = new member("m001", "123");
var memObj2 = new member("m002", "123");
var memObj3 = new member("m031", "123");
memArray.push(memObj1);
memArray.push(memObj2);
memArray.push(memObj3);
memArray.forEach((val, ind) => {
memArray[ind] = JSON.parse(window.prompt("want to edit values?", JSON.stringify(memArray[ind])));
});
console.log(memArray)
Pavan's answer is good, but to make this testable in automated tests:
// I would name these fields in your API
// by making the constructor take an object.
// Capitalise the name to signal that it can be newed
function Member({id, password}) {
this.id = id;
this.pwd = password
}
// Name the array for what it is
const members = [
new Member({id: "m001", password: "123"}),
new Member({id: "m002", password: "123"}),
new Member({id: "m031", password: "123"})
]
const editInBrowserFn = member => JSON.parse(window.prompt("want to edit values?", JSON.stringify(member)));
const updateMembers = editFn => array => array.map(editFn)
// To get an update
const updatedMembers = updateMembers(editInBrowserFn)(members)
console.log(updatedMembers)
// You can now test it by making an testing editFn that doesn't need user interaction
const testEditFn = m => new Member({id: m.id, password: 'test'})
const testMembers = updateMembers(testEditFn)(members)
console.log(testMembers)
See this article for an in-depth explanation of this approach.
To do it this way, you will need to take it out of the global scope. That is a good discipline to develop. As a first step you could make an object in global scope that holds the latest member list:
const Members = (() => {
let _members = []
return {
setMembers: members => _members = [...members],
getMembers: () => [..._members]
}
})()
Now the way to update the members is like this:
const updateFn = updateMembers(editInBrowser)
function updatePasswords() {
const members = Members.getMembers()
Members.setMembers(updateFn(members))
}
Nothing can accidentally delete or mutate the members array now, so that bug surface area is eliminated.
This is how React setState is designed. It's inspired by functional programming ideas and immutability.
You probably want to be able to update just one member, so:
const Members = (() => {
let _members = []
return {
setMembers: members => _members = [...members],
getMembers: () => [..._members],
updateMember: updated =>
this.members = _members.map(m =>
m.id === updated.id ? updated : m)
}
})()
Now all your array mutation is in one single place, and you only have to make it bug-free there. Otherwise, your global state is exposed and you have to fix bugs everywhere related to it. Without this, all your calling functions are responsibly for correctly managing the global state of the application. Complex.
Crystallise the complexity in one place.
I wrote an article and a complete implementation of the store (in 40 lines of code) here.
As far as I concern, alerts are just models on the browser to provide informative feedback to a particular user on his actions. Therefore, I think it is required to use either a dialog model or a form to edit the objects in the memArray.

Assigning a field name from a method to Firestore document

Let's say I have a collection called 'users'. When someone clicks the upvote button, I want it to update the user document with an entry that's the slug from the page they are viewing. However, I'm not sure how to accomplish it using the code I have.
voteUp() {
const voteup_ref = db.collection("builds").doc(this.$route.params.slug)
const voteuser_ref = db.collection("users").doc(this.alias)
return db.runTransaction(t => {
return t.get(voteup_ref).then(doc => {
const newCount = doc.data().voteup + 1
const NewSlug = voteuser_ref
t.update(voteup_ref, {
voteup: newCount
})
t.update(voteuser_ref, {
NewSlug: 'voteup'
})
});
})
},
So, if someone is viewing a page with the slug of 'my-awesome-comment', I want 'my-awesome-comment' to be entered in the user's document with a value of "voteup".
What's actually happening is that it will write an entry to the user document as I expect, but it's entering the field name as 'NewSlug' with a value of "voteup" instead of whatever the slug actually is.
What am I doing wrong?
The way you've written the JSON object NewSlug itself is the property name, instead of using its value. To do the latter, you need to use square bracket notation ([]):
let update = {};
update[NewSlug] = 'voteup';
t.update(voteuser_ref, update)

Using multiple conditions in firebase rules and accessing with if statement not working

I have multiple conditions in my firebase rules and want to access the data base on the current user by using if statement in my return statement. When I access it individually, it works but when I use if statements, I don't get the data but permission error issue.
I've tried accessing it but keeps failing.
My rule:
"query.orderByChild == 'userId' && query.equalTo == auth.uid ||
root.child('admins').child(auth.uid).child('role').val() === 'admin'"
Accessing the data:
let db = firebase.database().ref(`/items`).orderByChild('userId').equalTo(currentUser.uid)
if(firebase.database().ref('/admins').child(currentUser.uid).child('role').equalTo('admin')) {
db = firebase.database().ref(`/items`)
}
db
.once('value')
.then(snapshot => {
let doc = []
const data = snapshot.val();
for (let key in data) {
doc.push({
...data[key],
key: key
});
}
Ideally, I would like to have a rule with OR and have an if/else statement to return data depending on the user.
This statement does not do what you think it does:
if(firebase.database().ref('/admins').child(currentUser.uid).child('role').equalTo('admin'))
Nothing in this line reads from the database, and the equalTo merely builds a query, it does not actually perform any check.
If you want to to check whether the user is an admin:
let userRoleRef = firebase.database().ref('/admins').child(currentUser.uid).child('role');
userRoleRef.once('value').then((snapshot) => {
if ('admin' == snapshot.val()) {
db = firebase.database().ref(`/items`)
... continue using db here
}
}
Now the client-side query and the server-side security rules work in tandem to ensure the user only requests and only can get the data that they're authorized for.

multi-dimensional javascript array only affecting last array in forEach [duplicate]

This question already has an answer here:
Searching array reports "not found" even though it's found
(1 answer)
Closed 4 years ago.
I have a multi-dimensional array in JavaScript that holds basic Usernames and to-be hashed passwords. At the moment, when the function to check the credentials is called, the forEach will only check the last array.
const titleText = document.getElementById('loginText');
const usernameField = document.getElementById('usernameField');
const passwordField = document.getElementById('passwordField');
const usernames = [['guido','password'],['ben','test']];
function checkCreds() {
titleText.textContent = ">> Checking login";
usernames.forEach(element => {
if (element[0] === usernameField.value) {
if (element[1] === passwordField.value) {
titleText.textContent = '>> Login Valid';
window.location = "dashboard.html";
} else {
titleText.textContent = '>> Password incorrect';
};
} else {
titleText.textContent = '>> Login incorrect';
};
});
};
Here, when I type in the credentials: guido and password, it will say that the login is incorrect. But when I type in ben and test, it will proceed as normal. If anyone has an idea on to why this won't work or has better code, please drop an answer. As I say, this will be hashed, salted and not in the file, all that stuff when it's working.
The problem seems to be that you aren't breaking out of your loop so you are in fact checking all elements in the array but the last element is the one that is sticking. Try braking from your loop, something like this;
const titleText = document.getElementById('loginText');
const usernameField = document.getElementById('usernameField');
const passwordField = document.getElementById('passwordField');
const usernames = [
['guido', 'password'],
['ben', 'test']
];
function checkCreds() {
titleText.textContent = ">> Checking login";
// instead of using Array.forEach use a standard for loop, this allows you to
// break out of the loop and return.
for(let i = 0; i < usernames.length; i++){
if (usernames[i][0] === usernameField.value){
if (usernames[i][1] === passwordField.value){
// show that the login was successful
titleText.textContent = '>> Login Valid';
// redirect to the dashboard
window.location = 'dashboard.html';
// just return here, there is no need to break out of the loop,
// returning will end the execution of this function.
return;
}
}
}
// display the error to the user, we don't want to indicate if the
// password or the username were invalid because that tells an attacker
// they have the correct user name.
// We also don't have to check a flag because a valid login will result
// in this code never being hit
titleText.textContent = '>> Login incorrect';
};
Edit:
Based on the information from Ben West I have updated the solution to use a standard for loop to allow breaking out of the loop.

Categories

Resources