Unable to login to Keystone with remote MongoDB - mlab

I am trying to set up a keystone project with a remote database server hosted on mLab. I am using this guide here https://itnext.io/building-a-node-cms-with-keystonejs-mongo-db-react-and-redux-part-i-ae5958496df2
I have edited the mongo url in the keystone.init() configuration, with my mLab database URL, and managed to run the project.
'mongo': 'mongodb://*username*:*password*#ds127624.mlab.com:27624/keystone',
However, I am unable to login as a user.
The login page returned:
"The email and password you entered are not valid."
Do I need to do some more configurations for it to work properly?
....
user.js
var keystone = require('keystone');
var Types = keystone.Field.Types;
var User = new keystone.List('User');
User.add({
name: { type: Types.Name, required: true, index: true },
email: { type: Types.Email, initial: true, required: true, index: true },
password: { type: Types.Password, initial: true },
canAccessKeystone: { type: Boolean, initial: true },
});
User.register();
0.0.01-admin.js file
var keystone = require('keystone');
var User = keystone.list('User');
exports = module.exports = function (done) {
new User.model({
name: { first: 'admin', last: 'user' },
email: 'admin#keystonejs.com',
password: 'admin',
canAccessKeystone: true,
}).save(done);
};

For remote database with password you also have to add authsource option. basically add ?authSource=admin to your mongo url. admin is default db, you can change that as well
mongodb://*username*:*password*#ds127624.mlab.com:27624/keystone?authSource=admin

Related

In Mongoose I have User and Role schemas that have a one to many relationship. How to query if a particular User has the 'admin' role?

I have two collections in my Mongo DB: users and roles. A user can have many roles. Using Mongoose I want to find out whether a particular user (based on their id) has the admin role. How do I perform that query? In SQL one way of finding out would be to write this query:
SELECT *
FROM Users
INNER JOIN Roles ON Users.id = Roles.userID
WHERE (Users.id = <some user id> AND Roles.name='admin')
But I'm failing to see how the equivalent query is done using Mongoose. Below are my Mongoose schemas:
let RoleSchema = new Schema({
name: String,
owner: {
type: Schema.Types.ObjectId,
ref: "User"
}
})
export let Role = mongoose.model("Role", RoleSchema)
let userSchema = new Schema({
username: {
type: String,
unique: true,
required: true,
trim: true
},
roles: [
{
type: Schema.Types.ObjectId,
ref: "Role"
}
]
})
export let User = mongoose.model("User", userSchema)
Read - https://mongoosejs.com/docs/populate.html#query-conditions
User.
findById(id). // can also use find/findOne depending on your use-case
populate({
path: 'roles',
match: { name: 'admin' }
}).
exec();
This will fetch you User details and roles where name is admin.
You can check the user.roles array count to get the user is having admin role or not.

How can I retrieve an id from MongoDB create operation during a transaction?

I am trying to create an audit trail using Apollo Server and Mongoose. When a user initially registers, I create a document in the users collection and a document in the history collection for each piece of data they provided (username, password, email, etc) . For each history collection document, I include the id for the user document to create a relationship. Works perfectly.
However, when I add a transaction in (see below), the userId for the user document comes back as undefined, so I cannot add it to the history entry documents. I am assuming that the id for a document does not get created until the entire transaction has been completed?
Any ideas?
Mutation: {
register: async (_, { data }) => {
// Start a mongo session & transaction
const session = await mongoose.startSession()
session.startTransaction()
try {
// Hash password and create user
const hashedPassword = await bcrypt.hash(data.password, 12)
const user = await User.create(
[{ ...data, password: hashedPassword }],
{ session }
)
// Add history entries
HistoryEntry.create([
{
user: user.id,
action: 'registered'
},
{
user: user.id,
action: 'set',
object: 'profile',
instance: user.id,
property: 'firstName',
value: firstName
},
{
user: user.id,
action: 'set',
object: 'profile',
instance: user.id,
property: 'lastName',
value: lastName
},
{
user: user.id,
action: 'set',
object: 'profile',
instance: user.id,
property: 'password'
}
])
if (loginType === 'email') {
HistoryEntry.create({
user: user.id,
action: 'set',
object: 'profile',
instance: user.id,
property: 'email',
value: login
})
}
if (loginType === 'mobile') {
HistoryEntry.create({
user: user.id,
action: 'set',
object: 'profile',
instance: user.id,
property: 'mobile',
value: login
})
}
// commit the changes if everything was successful
await session.commitTransaction()
return {
ok: true,
user
}
} catch (err) {
// if anything fails above, rollback the changes in the transaction
await session.abortTransaction()
return formatErrors(err)
} finally {
// end the session
session.endSession()
}
}
}
If you think about it, how can you add a HistoryEntry if you haven't added User yet? It's not a 'history' as you are currently doing it. I believe you got two options here - set _id on User manually new Schema({ _id: { type: Schema.ObjectId, auto: true }}) and then generate it within the transaction: var userId = ObjectId(); and use for both User and History Entries.
And the second option, more semantically correct in this context, I believe - you should attach to post-save hook:
schema.post('save', function(doc) {
console.log('%s has been saved', doc._id);
});
So, whenever an User is created, a post-save hook is fired to update History.
Came across the same issue recently, hope you have figured it out already. I may add this for future seekers.
Following create function returns an array of created documents.
const user = await User.create(
[{ ...data, password: hashedPassword }],
{ session }
);
Therefore access the user id as user[0]._id
Pass the session also to HistoryEntry.create()
HistoryEntry.create([{...},{...}], {session})
Note: In this use case, I personally prefer #marek second option to use a post-save hook.

Create user with avatar

I want to add an avatar in the user registration, but I don't know how, Please can someone share with me a full example (form, JS front, and JS backend). I'm using SailsJS 1.0 (the stable version) with VueJs.
Thanks in advance .
I figured it out. Watch these platzi tutorials:
https://courses.platzi.com/classes/1273-sails-js/10757-uploading-backend-file/
https://courses.platzi.com/classes/1273-sails-js/10758-uploading-frontend-files/
https://courses.platzi.com/classes/1273-sails-js/10759-downloading-files/
Here is what the videos tell you to do:
npm i sails-hook-uploads.
In api/controllers/entrance/signup.js
Above inputs key add a new key/value of files: ['avatar'],
In the inputs add:
avatar: {
type: 'ref',
required: true
}
In the body of the fn find var newUserRecord and above this add (even if avatar is not required, make sure to do this line, otherwise you will have a "timeout of unconsuemd file stream":
const avatarInfo = await sails.uploadOne(inputs.avatar);
Then in the first argument object of var newUserRecord = await User.create(_.extend({ add:
avatarFd: avatarInfo.fd,
avatarMime: avatarInfo.type
In api/models/User.js, add these attributes to your User model:
avatarFd: {
type: 'string',
required: false,
description: 'will either have "text" or "avatarFd"'
},
avatarMime: {
type: 'string',
required: false,
description: 'required if "avatarFd" provided'
},
Then create a download endpoint, here is how the action would look for it:
const user = await User.findOne(id);
this.res.type(paste.photoMime);
const avatarStream = await sails.startDownload(paste.photoFd);
return exits.success(avatarStream);
Add to the routes a route for this download avatar endpoint.
Then you can display this avatar by pointing the <img src=""> the source in here to this download endpoint.
------APPENDIX-----
----signup.js-----
module.exports = {
friendlyName: 'Signup',
description: 'Sign up for a new user account.',
extendedDescription:
`This creates a new user record in the database, signs in the requesting user agent
by modifying its [session](https://sailsjs.com/documentation/concepts/sessions), and
(if emailing with Mailgun is enabled) sends an account verification email.
If a verification email is sent, the new user's account is put in an "unconfirmed" state
until they confirm they are using a legitimate email address (by clicking the link in
the account verification message.)`,
files: ['avatar'],
inputs: {
emailAddress: {
required: true,
type: 'string',
isEmail: true,
description: 'The email address for the new account, e.g. m#example.com.',
extendedDescription: 'Must be a valid email address.',
},
password: {
required: true,
type: 'string',
maxLength: 200,
example: 'passwordlol',
description: 'The unencrypted password to use for the new account.'
},
fullName: {
required: true,
type: 'string',
example: 'Frida Kahlo de Rivera',
description: 'The user\'s full name.',
},
avatar: {
}
},
exits: {
success: {
description: 'New user account was created successfully.'
},
invalid: {
responseType: 'badRequest',
description: 'The provided fullName, password and/or email address are invalid.',
extendedDescription: 'If this request was sent from a graphical user interface, the request '+
'parameters should have been validated/coerced _before_ they were sent.'
},
emailAlreadyInUse: {
statusCode: 409,
description: 'The provided email address is already in use.',
},
},
fn: async function (inputs) {
var newEmailAddress = inputs.emailAddress.toLowerCase();
// must do this even if inputs.avatar is not required
const avatarInfo = await sails.uploadOne(inputs.avatar);
// Build up data for the new user record and save it to the database.
// (Also use `fetch` to retrieve the new ID so that we can use it below.)
var newUserRecord = await User.create(_.extend({
emailAddress: newEmailAddress,
password: await sails.helpers.passwords.hashPassword(inputs.password),
fullName: inputs.fullName,
tosAcceptedByIp: this.req.ip,
avatarFd: avatarInfo.fd,
avatarMime: avatarInfo.type
}, sails.config.custom.verifyEmailAddresses? {
emailProofToken: await sails.helpers.strings.random('url-friendly'),
emailProofTokenExpiresAt: Date.now() + sails.config.custom.emailProofTokenTTL,
emailStatus: 'unconfirmed'
}:{}))
.intercept('E_UNIQUE', 'emailAlreadyInUse')
.intercept({name: 'UsageError'}, 'invalid')
.fetch();
// If billing feaures are enabled, save a new customer entry in the Stripe API.
// Then persist the Stripe customer id in the database.
if (sails.config.custom.enableBillingFeatures) {
let stripeCustomerId = await sails.helpers.stripe.saveBillingInfo.with({
emailAddress: newEmailAddress
}).timeout(5000).retry();
await User.updateOne(newUserRecord.id)
.set({
stripeCustomerId
});
}
// Store the user's new id in their session.
this.req.session.userId = newUserRecord.id;
if (sails.config.custom.verifyEmailAddresses) {
// Send "confirm account" email
await sails.helpers.sendTemplateEmail.with({
to: newEmailAddress,
subject: 'Please confirm your account',
template: 'email-verify-account',
templateData: {
fullName: inputs.fullName,
token: newUserRecord.emailProofToken
}
});
} else {
sails.log.info('Skipping new account email verification... (since `verifyEmailAddresses` is disabled)');
}
// add to pubilc group
const publicGroup = await Group.fetchPublicGroup();
await Group.addMember(publicGroup.id, newUserRecord.id);
}
};

Create a model for OrientDB database in node.js

Iam building a node js application and OrientDB as the database. Is there someway to create a model for my vertices in node like we do if were using MongoDB?
For instance, I have a class called Account with propertise name, password and email.
Is it possible to create a model for this in node when using OrientDB and OrientJS as the language driver?
Appreciate any help.
I don't know if I understood it correctly, but this is how to create the class you described:
var OrientDB = require('orientjs');
var server = OrientDB({
host: 'localhost',
port: 2424,
username: '<username>',
password: '<password>'
})
var db = server.use({
name: 'mydb',
username: '<username>',
password: '<password>'
})
db.class.get('Account')
.then(function (MyClass) {
MyClass.property.create([{
name: 'name',
type: 'String'
},{
name: 'password',
type: 'String'
},{
name: 'email',
type: 'String'
}])
.then(function () {
console.log('Properties created')
});
})
server.close();
Hope it helps
Regards

Sails.js controller not inserting into Mongo database

I've been all over SO and Sailsjs.org trying to figure out what's going wrong, and to no avail. Just trying to learn the basics of SailsJS. I have a UserController, whose create() method gets called when a POST request is sent to /user.
create: function (req, res) {
var params = req.params.all();
User.create({
name: params.FirstName + ' ' + params.LastName,
email: params.Email,
password: params.Password,
jobTitle: params.JobTitle
}).exec(function createCB(err,created)
{
created.save(function(err)
{
// No error . . . still nothing in db
});
return res.json({name: created.name, jobTitle: created.jobTitle, email: created.email, password: created.password});
});
}
No errors here. All the request params are coming in fine and going back to the client without trouble. But nothing is actually being written to the database.
In development.js:
connections: {
mongo: {
adapter: 'sails-mongo',
host: 'localhost',
port: 27017,
// user: 'username',
// password: 'password',
database: 'sails_test'
}
},
models: {
connection: 'mongo'
}
I've tried this with the above both there in development.js, as well as separately in connections.js and models.js, respectively. No difference.
In User.js:
attributes: {
FirstName : { type: 'string' },
LastName : { type: 'string' },
Email : { type: 'string' },
Password : { type: 'string' },
JobTitle : { type: 'string' }
}
My front end request:
$.ajax({
method: 'post',
url: '/user',
data: {
FirstName: 'Yo',
LastName: 'Momma',
Email: 'yourmom#yourdadshouse.com',
Password: 'YouWish123',
JobTitle: 'Home Maker Extraordinaire'
},
success: function (sailsResponse)
{
$('#result').html(sailsResponse).fadeIn();
},
error: function()
{
console.log('error');
}
});
Again, none of this is producing an explicit error. There is just nothing being inserted into the database. Or if there is, I don't know how to find it. I've confirmed the existence of this db in the mongo shell, thusly:
show dbs
My db, sails_test shows up in the list. And I've confirmed that there isn't anything in it like so:
db.sails_test.find()
I would very much appreciate some guidance here :)
Update:
Turns out the data is being written just fine. I'm just unable to query the database from the command line. I confirmed this by first creating a sample user, and then using Waterline's findOne() method:
User.findOne({FirstName: params.FirstName}).exec(function (err, user) {
if (err) {
res.send(400);
} else if (user) {
return res.json({firstName: user.FirstName, lastName: user.LastName, jobTitle: user.JobTitle, email: user.Email, password: user.Password});
} else {
return res.send('no users match those criteria');
}
});
The above works as expected. So my problem now is simply that I cannot interact with the database from the command line. db.<collectionName>.find({}) produces nothing.
This was simply a failure to understand the MongoDb docs. I read db.collection.find({}) as DatabaseName.CollectionName.find({}), when you literally need to use db. So if my database is Test, and my collection is Users, the query is use Test, and then db.Users.find({}).
Also of note, 3T Mongo Chef is a pretty rockin' GUI (graphical user interface) for nosql databases, and it's free for non-commercial use.