I am working on a Meteor project and I am trying to update a specific user's profile. However, I always receive
update failed: Access denied
I have the user id stored in the variable user_id, and my code looks like
Meteor.users.update({_id: user_id}, {$set: {'profile.participate_studies': updated_user_list}})
However, if I do
Meteor.users.update({_id: Meteor.userId()}, {$set: {'profile.participate_studies': updated_user_list}})
The update works. This makes me think there is some issue with the id I have stored. Is there a difference between what Meteor.userId() returns and a string containing an id? I know the id is correct because I can access it in shell with the id.
I have looked at Updating Meteor.users from client, but I am not trying to update the edit the base user object. I really appreciate all of your help!
You are allowed to update your own profile, so when you do
Meteor.users.update({_id: Meteor.userId()}, {
$set: {'profile.participate_studies': updated_user_list}
})
You update your own profile (Meteor.userId() returns an id of currently logged in user).
And when you update profile of some other user, you receive and Access denied error.
You need to set allow rules for Meteor.users:
Meteor.users.allow({
update: function(userId, user) {
return true;
/**
* Don't use `return true` in production!
* You probably need something like this:
* return Meteor.users.findOne(userId).profile.isAdmin;
*/
}
});
Full documentation for allow rules: http://docs.meteor.com/#/full/allow
TIP: You can use shorthand for updating by id:
Meteor.users.update(Meteor.userId(), {...})
Related
I'm trying to create an access control system at the document level in Meteor, and I seem to be missing how to prevent users from fetching documents.
I've read the documentation around collection.allow and collection.deny. Via these objects we can control "who" can update, remove and insert. The problem is that Meteor doesn't seem to supply similar functionality for fetch operations. What is proper way to deny unauthorized users from reading documents?
Extra requirement: this needs to happen server side so we don't leak documents to unauthorized users over the network.
There is no way to deny reads to collection data once it has arrived on the client. In theory, there's no way to actually enforce anything on the client because the user could modify the code. You can, however, enforce which documents get published.
A publish function can handle authorization rules of arbirtary complexity. Here's a simple example where we want to publish documents from the Messages collection only to users who are members of the given group:
Meteor.publish('messagesForGroup', function(groupId) {
check(groupId, String);
var group = Groups.findOne(groupId);
// make sure we have a valid group
if (!group)
throw new Meteor.Error(404, 'Group not found');
// make sure the user is a member
if (!_.contains(group.members, this.userId))
throw new Meteor.Error(403, 'You are not a member of the group');
return Messages.find({groupId: groupId});
});
1) Remove autopublish package from your app.
2) Create your own publish and deny access to unauthorized users
Meteor.publish("userData", function () {
if (this.userId) { // Check user authorized
return MyCollection.find(); // Share data
} else {
this.ready(); // Share nothing
}
});
I have a form that tried to update meteor.users with extra information about users with the following helper
Template.Profile.events({
'submit form': function(e) {
e.preventDefault();
var post = {
firstName: $(e.target).find('[name=firstname]').val()
};
Meteor.users.update( { _id: Meteor.userId() }, { $set: { 'firstName': post.firstName }} );
}
});
however, i get update failed: Access denied
Another question is, I am wondering whether I should do extra update straight to Meteor.users collection or should I have a seperate collection to store these data.
thanks
Due to the fact that you are trying to set an attribute directly on the base user object, you are receiving the 'Access denied' error. According to the Meteor documentation for Meteor.users:
By default, the current user's username, emails, and profile are
published to the client.
This means that you can update any of those user attributes, but if you want to add additional ones, it is best to add them to one of these already existing fields. I would suggest adding something like `firstName' to the profile attribute. In this case, your code would look something like this:
Meteor.users.update({_id: Meteor.userId()}, {$set: {'profile.firstName': post.firstName}});
Is it possible to check users role on Sign in, and than if user is in role "admin" to display one page, and if is it in role "basic-user" to display another page ( go to another route).
Lets have a look at the Routes section of the documentation for the useraccounts:iron-routing package.
this should solve your problem
AccountsTemplates.configureRoute('signIn', {
redirect: function(){
var user = Meteor.user();
if (user && Roles.userIsInRole(user, ['admin'])) {
Router.go('admin');
}
else {
Router.go('home');
}
}
});
Be careful to check you can access the user roles field from the client side: lets check the allanning:roles official documentation
To define a default role for a user I use this:
// server
Accounts.onLogin(function(user) {
var user = user.user;
var defaultRole = ['student'];
if (!user.roles){
Roles.addUsersToRoles(user, defaultRole)
};
})
I'm using meteor-useraccounts and alanning meteor-roles packages and that work fine for me.
If I'm not outdated (and a look at http://docs.meteor.com/#/full/meteor_users suggests I'm not) there is no built in way for user roles. There should be some extensions for that task with which you could go and depending on what you choose you would have to check their documentation.
However it's not very hard to implement a own simple roles logic in Meteor:
First in your Accounts.onCreateUser function give your users object a new attribute role and assign them to a default role. If you don't have a Accounts.onCreateUser yet create one server side. It could look like something like this:
Accounts.onCreateUser(function(options, user) {
// Add an user roles array
user.roles = ["default-user"];
if (options.profile)
user.profile = options.profile;
return user;
}
Next you would need to implement some logic to add "admin" or whatever you like for trusted users to their roles array. That's up to you and for the beginning if you don't have dozens of admins you could also choose to do that manually in your MongoDB.
Now make sure you publish the new attribute of your user object to the currently logged in user. To do so make use of Meteor.publish with null as first parameter to address the current user like so:
Meteor.publish(null, function () {
return Meteor.users.find({_id: this.userId}, {fields: {
'roles': 1,
'profile': 1, // You probably want to publish the profile of a user to himself
// And here would be any other custom stuff you need
}});
});
And with that you are already in a state where you can do individual styling or routing client side. For example you could do something like this:
if (Meteor.user().roles.indexOf("admin") > -1) {
// Route for admins!
}
You could also parse through your array and add the user roles as class to your body element to for example only show certain elements to admins. This could be done this way:
Meteor.user().roles.forEach(function(role){
$('body').addClass(role);
});
Note that this will only be "cosmetic" but you can also implement real security with that as long as you do it server side. So if you want a Meteoer subscription or Meteor method only to be available for admins add something like this to it:
var requestingUser = Meteor.users.findOne({ '_id': this.userId});
if (!_.contains(requestingUser.roles, "admin")) {
// Terminate the pbulish function or Meteor method here when there is no "admin" role
return;
}
As said, this only works sever side and should be at the start of Meteor.publish functions or at the start of functions within Meteor.methods blocks.
I am building a webpage using AngularJS and Firebase. I want to use facebook login to connect information on the webpage with the user. Firebase has a version of simple login which I guess is supposed to simplify the login process.
My problem is that I want to access information about the logged in user in a lot of places on my webpage but I can't find a good way to do it.
This is how I started out:
var userInfo = null;
var ref = new Firebase('https://<yourfirebase>.firebaseIO.com/');
var auth = new FirebaseSimpleLogin(ref, function(error, user) {
if(error)
alert("You are not logged in");
else if(user)
{
//store userinfo in variable
userInfo = user;
}
else
//user logged out
});
//do something with userInfo
alert(userInfo.name);
My first thought was to run this at the top of my page and then use the info about the user. The problem is that the code using userInfo (as in e.g. the alert) will always run before the userInfo variable has been filled and userInfo will return undefined/null.
I then proceeded to always create a new firebasesimplelogin object when i want to retrieve user data. Which of course isn't very good. Especially since every created FirebaseSimpleLogin object will be called again whenever another is called or a user logs out, for example.
So my question is, how do I use FirebaseSimpleLogin to handle and use my user information in the best way?
I would have liked some function to getUserInfo() or check isLoggedIn() for example. How do you do this properly?
You can take a look at this example for thinkster. It's based on using simple login with userid/password. http://www.thinkster.io/angularjs/4DYrJRxTyT/7-creating-your-own-user-data-using-firebase.
You can create a function like getLoggedinUser that runs in $rootScope that will allow you to find the user throughout the application.
UPDATE:
Around the middle of October 2014, firebase made some big changes. This method might still work, but it's better to take advantage of the newer version of firebase, specifically getauth and onauth. These methods will allow you to do the same thing without running on the rootScope. https://www.firebase.com/docs/web/guide/user-auth.html#section-login
Please make a constant to use it everywhere in your App like that
.constant('facebookUrl', 'https://rjrestaurantapp.firebaseio.com');
Then in the controller inject this constant "facebookUrl & "$state" as shown below...
.controller('loginCtrl', function($scope,facebookUrl,$state){
and then you only need to give name of the state where you want to redirect after facebook authentication..
var ref = new Firebase(facebookUrl);
$scope.fbLogin = function () {
ref.authWithOAuthPopup("facebook", function(error, authData) {
if (error) {
console.log("Login Failed!", error);
} else {
console.log("Authenticated successfully with payload:", authData);
$state.go('restaurant');
}
})
}})
You can see the information in authData object after successfull authentication using facebook ....
please read this doc carefully https://www.firebase.com/docs/web/guide/login/facebook.html
The above is the example of simple login using firebase and for retrieving data for each logged in user, you have to store user information at the time of signin as you know that firebase makes every child with a unique ID .. then you only need to use the offline features of firebase that will find which user is offline and according to that remove the node with the same ID which one is offline, you can find examples in the MANAGING PRESENCE section of the below link ..
https://www.firebase.com/docs/web/guide/offline-capabilities.html
I am not too familiar with LDAP, however I am working on authentication in a Node.js app, and the user credentials for the web app is going to be gotten from the organization's Windows domain.
I have LDAP lookups working (using the Passport.js Node module), however to make it work, I have to put the user's full-fledged DN into Node. For example, let's say:
My FQDN is mydomain.private.net.
My users are stored in an organizational unit, let's say staff.
Now, if I want to lookup user joe, I have to put this string into Node:
var username = 'CN=joe,OU=staff,DC=mydomain,DC=private,DC=net';
do i really have to keep track of all that?
What if my users are in two different organizational units? The client-side browser doesn't know that! It just knows:
username = 'joe';
password = 'xxxxx';
What if you try to log on as administrator? Administrators are always in a totally different OU by default.
Is there a way to reference an LDAP object by just the name and nothing else?
This is a general LDAP problem. You need to get a unique identifier from the user, and then look for it.
Typically this is what the uid attribute is used for. Active Directory may or may not have that populated, and generally relies on sAMAccountName which must be unique within the domain.
So you need a two step process.
1) Query for uid=joe or samAccountName=joe
2) Use the results to test a bind or password compare.
You would then use the DC=mydomain,DC=private,DC=net value as the root to search from.
(answer to my own question)
geoffc's answer was correct, and this is the working solution adapted to my Node.js app using the activedirectory npm module:
// First search for the user itself in the domain.
// If successfully found, the findUser function
// will return the full DN string, which is
// subsequently used to properly query and authenticate
// the user.
var AD = self.ADs[domain];
AD.findUser(username, function(err, user) {
if (err) {
cb(false, 'AD error on findUser', err);
return;
}
if (!user) {
cb(false, 'User does not exist', void 0);
} else {
username = user.dn;
AD.authenticate(username, password, function(err, authenticated) {
if (authenticated == false) {
cb(false, err, void 0);
return;
} else {
cb(true, 'Authenticated', void 0);
}
});
}
});