I'm trying to use the Meteor Roles package: https://github.com/alanning/meteor-roles
to obviously create a new field in user model.
The user is created no problem but the 'roles' field I'm trying to define isn't created. I can add things like 'Profile' and details within that too. But for some reason I can't make a roles field. Here's my form:
Template.signup.events({
'submit #signup-form' : function(e, t) {
e.preventDefault();
var roles = ['admin'],
email = t.find('#email').value,
password = t.find('#password').value;
Accounts.createUser({email: email, password : password, roles: roles}, function(err){
if (err) {
alert("User Not Added")
} else {
console.log("User Added.")
}
});
}
});
Eventually I'll need to publish this to the client but for right now I just want the field to show in MongoDb, which it's not.
3 things:
I feel like the code above should work but I'm clearly missing something
In the package docs it mentions this Roles.addUsersToRoles which I
tried but no luck
Or do I need to possibly update the record, after it's been created?
I did go into the DB and manually added the field and associated string to update it (with $set) and it worked. But from the form itself though, no luck.
Any pointers would be much appreciated. Thank you.
The Accounts.createUser function only lets you add arbitrary user properties via the profile option which is where they end up getting stored in mongo. That is why Meteor is ignoring the roles: roles part of your Accounts.createUser call.
It is true that the meteor-roles package stores the list of roles assigned to a user directly in the users collection, but that is almost just an implementation detail and you are probably best off sticking to the API that meteor-roles provides for adding users to a role:
Roles.addUsersToRoles(<userId>,[<list of roles>])
The userId passed to Roles.addUsersToRoles is the value returned by Accounts.createUser when its called on the server which is probably where you want to be doing this as that feels way more secure.
The Accounts.createUser function only takes username, email, password and profile as params for the user object. See the documentation here. So, to add another field to a new user object, you need to add it in a second step:
var uid = Accounts.createUser({email: email, password: password});
Meteor.users.update(uid, {$set: {roles: roles}});
Related
I'm trying to create a page that shows all the users in the db that belong to my same organization.
My users are stored as follows:
Accounts.createUser({
email: email,
password: password,
profile:{
firstName: first,
lastName: last,
type: "Member",
organization: organization,
created: date
}
});
I know I have to publish the users to my user list component, and i'm struggling figuring out how to publish only the users whose profile.organization matches the logged in users profile.orgainzation.
This returns all users as it should, I got that far:
return Meteor.users.find();
I tried using the next block of code you see here, but it doesn't work, probably for multiple reasons, it even throws an error saying I can't use Meteor.user() server side, that I have to use this.user()... but that didn't work either:
return Meteor.users.find({
profile:{
organiztion: Meteor.user().profile.organization
}
});
I'm not sure where to go from here. Any help would be greatly appreciated!
I think this might work (untested). This is a server-side function.
Meteor.publish("organizationUsers", function () {
if (this.userId) {
var user = Meteor.users.findOne(this.userId);
var org = user.profile.organization;
return Meteor.users.find({'profile.organization': org});
}
});
Client side, you can filter the users you want (and exclude the current user) with:
Meteor.users.find({'profile.organization': org, '_id' : {$not : Meteor.userId()}});
I am currently working on a profile page using the plugin "accounts-password" installed it via the command
meteor add accounts-password
I have run the program and so on and was able to add data; however, when I try to call or display the data on the browser using the
{{ currentUser.email }}
and
{{ currentUser.password }}
It doesn't display, but when I called
{{ currentUser.username }}
it works just fine. I tried to access the database via find().fetch(); and this is what I see.
id: "GP26SF2F8jmpqQuvT"
emails: Array[1]
0: Object
address: "testing#gmail.com"
verified: false
__proto__: Object
length: 1
__proto__: Array[0]
username: "testing"
__proto__: Object
Based on the arrangement, should I called the email as
{{ currentUser.emails.0.address }}
? Also I don't see the password in the data, is there a way to retrieve it? Actually my aim here to update the Meteor.users if user wants to change password or email address. Thanks.
accounts-password doesn't publish the password field of the document by default. This is to be expected - for security reasons!
In regards to the email: accounts package allows the application to add multiple emails to a user. This is why there's an array of emails.
Instead of doing
{{ currentUser.emails.0.address }}
What you could do is add a helper to the template:
Template.myTemplate.helpers({
email: function(){
return Meteor.user().emails[0].address;
}
});
And then you can just use this in the template:
{{ email }}
Actually my aim here to update the Meteor.users if user wants to change password or email address. Thanks.
I'd say that there's almost never a reason to publish the user's password to the client. It's a huge security risk. The accounts-password package has taken care of the common use-cases, so you can just use Accounts.changePassword() on the client to allow the user to change their password.
If you want to allow the user to change their email, what you want to do, is use a Method. A method can be called by the client, but the code is executed on the server. After it's executed, the server returns a response to the client. Kind of like how HTTP works.
In this case, the client could call a method named changeEmail, which tries to change the user's email. If all checks pass etc, the server changes the user's email and returns a response, e.g. "success", otherwise returns "fail". This is what the code could look like:
if(Meteor.isClient){
Meteor.call('changeEmail', newEmail, function(error, response){
if(error){
console.error(error);
} else {
console.log(response);
}
});
}
if(Meteor.isServer){
Meteor.methods({
changeEmail: function(newEmail){
Accounts.addEmail(this.userId, newEmail, false);
var userEmails = Meteor.users.findOne(this.userId, {fields: {emails: 1}}).emails;
if(userEmails.length > 1){
Accounts.removeEmail(this.userId, userEmails[0].address);
Accounts.sendVerificationEmail(this.userId, newEmail);
return "success";
} else {
throw new Meteor.Error('fail', 'email not correct!');
}
}
});
}
If you're not familiar with Methods, you can read either this tutorial by Meteor or this article. Also, my code might not be 100% functional, it's just an example.
Basically trying to modify the user that was just created by giving it an extra field called sid in it's profile object. I'm running this on server.js (the server code)
Accounts.onCreateUser(function (options, user) {
Meteor.users.update({_id: user._id}, {$set: {"user.profile.sid": [post.content]}});
});
console.log(JSON.stringify(user));
However, the user object does not show the sid field in it's output. Am I doing this in the wrong location or is my code wrong?
From the docs
The function you pass will be called with two arguments: options and user. The options argument comes from Accounts.createUser for password-based users or from an external service login flow. options may come from an untrusted client so make sure to validate any values you read from it. The user argument is created on the server and contains a proposed user object with all the automatically generated fields required for the user to log in, including the _id.
The function should return the user document (either the one passed in or a newly-created object) with whatever modifications are desired. The returned document is inserted directly into the Meteor.users collection.
So your code should be:
Accounts.onCreateUser(function (options, user) {
user.profile.sid = [post.content];
return user;
});
However be aware that anything in the user.profile object can be changed by your users.
profile: an Object which the user can create and update with any data. Do not store anything on profile that you wouldn't want the user to edit unless you have a deny rule on the Meteor.users collection.
Try this instead
Accounts.onCreateUser(function (options, user) {
user.profile.sid = [post.content];
return user;
});
From the documentation it reads (http://docs.meteor.com/#/full/accounts_oncreateuser):
The user argument is created on the server and contains a proposed user object...
So at this point it looks like the user does not actually exist in the database yet.
Hi I am building an app using Meteor and need to update my email address. I am using the Meteor accounts package.
My form passes an email value into an accountDetails object, which I will pass into a method to update my profile (including my email):
Meteor.users.update({_id: this.userId},
{
$set: {
'emails.$.address': accountsDetail.email
}
});
This gives me the error:
Exception while invoking method 'saveAccountInfo' MongoError: The positional operator did not find the match needed from the query. Unexpanded update: emails.$.address
Here is my user schema:
{
"_id" : "12345",
"emails" : [
{
"address" : "abc123#gmail.com",
"verified" : false
}
Can someone help? Thank you in advance!
If you're sure the user has one address, which should be the case you can use emails.0.address instead of emails.$.address.
This should work for nearly all use cases. The exception is when there are many emails associated with a user. In this case:
If you are on the server & only on the server, you can use the positional operator to update a specific email, if there are multiple addresses. You need to, in this case specify the current email in the query portion of the update. I.e: {_id: this.userId, 'emails.$.address' : <current address> }
The $ positional update operator is not currently available on the mongo client in Meteor.
as each user is able to have multiple addresses (it´s an array - see http://docs.meteor.com/#/full/meteor_users for details) you need to specify which key you want to update (in this case the key is the address itself)
Meteor.users.update({_id: this.userId, "emails.address":"me#domain.com"},
$set:{'emails.$.address': accountsDetail.email}
});
If every user only has one email you could also think about dropping this one and inserting the new one. see http://docs.mongodb.org/manual/reference/operator/update/pop/ for details.
Hope this helps.
Regards,
René
I'm not able to use the node server debugger so I'm posting here to see if I can get a nudge in the right direction.
I am trying to allow multiple users to edit documents created by any of the users within their specific company. My code is below. Any help would be appreciated.
(Server)
ComponentsCollection.allow({
// Passing in the user object (has profile object {company: "1234"}
// Passing in document (has companyId field that is equal to "1234"
update: function(userObject, components) {
return ownsDocument(userObject, components);
}
});
(Server)
// check to ensure user editing document created/owned by the company
ownsDocument = function(userObject, doc) {
return userObject.profile.company === doc.companyId;
}
The error I'm getting is: Exception while invoking method '/components/update' TypeError: Cannot read property 'company' of undefined
I'm trying to be as secure as possible, though am doing some checks before presenting any data to the user, so I'm not sure if this additional check is necessary. Any advice on security for allowing multiple users to edit documents created by the company would be awesome. Thanks in advance. -Chris
Update (solution):
// check that the userId specified owns the documents
ownsDocument = function(userId, doc) {
// Gets the user form the userId being passed in
var userObject = Meteor.users.findOne(userId);
// Checking if the user is associated with the company that created the document being modified
// Returns true/false respectively
return doc.companyId === userObject.profile.companyId;
}
Looking at the docs, it looks like the first argument to the allow/deny functions is a user ID, not a user document. So you'll have to do Meteor.users.findOne(userId) to get to the document first.
Do keep in mind that users can write to their own profile subdocument, so if you don't disable that, users will be able to change their own company, allowing them to edit any post. You should move company outside of profile.
(If you can't use a proper debugger, old-fashioned console.log still works. Adding console.log(userObject) to ownsDocument probably would have revealed the solution.)