I'm building a CMS and depending on the user role they will be able to edit/update/delete/create different areas but filtered by their role as in, one user with role: 'basic role' can't delete what the user with role: 'superuser' can.
What I have at the moment is this:
Collection.allow({
insert: function(userId, collection) {
return Meteor.users.findOne({_id: userId, profile: {role: 'admin'}});
},
update: function(userId, collection, fields, modifier) {
return Meteor.users.findOne({_id: userId, profile: {role: 'admin'}});
},
remove: function(userId, collection) {
return Meteor.users.findOne({_id: userId, profile: {role: 'admin'}});
}
});
QUESTION
Is this the right way to validate users roles? Are there better ways? What are the best practices for this?
Thanks!
You should take a look at the alanning:roles package. It's quite widely used and even mentioned in the Meteor Docs. In addition to roles, it also supports groups.
Related
I have the following code that renders the currentUsers' documents in a collection. However I want an admin belonging to the same organization to also be able to view and edit the collection. this.user.profile.organization does not work unfortunately. So, how would I allow the object to be available to admins from belonging to the same organization. EVery document that gets created gets the organization of the currentuser.
Meteor.publish('skills', function skillsPublication() {
return Skills.find({
owner: this.userId,
});
});
When you're on the server, you can always query the user document from the MongoDB database.
Meteor.publish('skills', function skillsPublication() {
const user = Meteor.users.findOne({ _id: this.userId })
// now you can do user.profile.organization
return Skills.find({
$or: [
{ owner: this.userId },
{ organization: user.profile.organization }
] // returns a document owned by the user or owned by the user's organization
})
})
Just a note, Meteor advises against using .profile field on your users collection, because that field is always published to the client. Meteor suggests that you use top-level keys on your user document instead.
For more info, read: https://guide.meteor.com/accounts.html#dont-use-profile
I'm using the Atmosphere package meteor-useraccounts, but I cannot figure out how to retrieve data from custom made fields.
I made the field:
AccountsTemplates.addField({
_id: 'callsign',
type: 'text',
placeholder: {
signUp: "Callsign"
},
required: true,
});
and I would like to get the logged in user's callsign from the user's collection.
I tried to see if the registration pushes the data to the user's collection, but it seems not so. Is there a way to do this?
try hooking into the account creation, it should show you your values in options.profile. then you can save them to the user object or wherever you would like. put this code somewhere on the server:
Accounts.onCreateUser((options, user) => {
console.log('--------------------------------');
console.log('options:', options);
console.log('--------------------------------');
console.log('user:', user);
console.log('--------------------------------');
return user;
});
be sure to return the user object at the end.
I am using Accounts.createUser to add new users to the database, but the problem is that, not all the attributes are added.
Here is my code to add new user:
import {Accounts} from 'meteor/accounts-base';
Template.addingUser.events({
'submit #addUser': function (e, t) {
e.preventDefault();
Session.set('name', t.find('#name').value);
Session.set('email', t.find('#email').value);
Session.set('telephoneOffice', t.find('#telephoneOffice').value);
Session.set('telephoneHouse', t.find('#telephoneHouse').value);
Session.set('salary', t.find('#salary').value);
let userId = Accounts.createUser({
username: Session.get('name'),
password: "123456",
email: Session.get('email'),
telephoneOffice: Session.get('telephoneOffice'),
telephoneHouse: Session.get('telephoneHouse'),
employeeSalary: Session.get('salary'),
annualLeave: 14
}, function (err) {
if (err)
console.log(err);
else
console.log('It worked...');
});
Accounts.sendEnrollmentEmail(userId);
}
});
only the name, email, and password are added.
How do I include the other information as well such as telephoneOffice?
You need to pass the extra data inside the profile object.
Accounts.createUser({
username: Session.get('name'),
password: "123456",
email: Session.get('email'),
profile: {
telephoneOffice: Session.get('telephoneOffice'),
telephoneHouse: Session.get('telephoneHouse'),
employeeSalary: Session.get('salary'),
annualLeave: 14
}
...
Accounts.createUser does not accept custom arguments beyond username, email, password, and profile. The default functionality for passing custom user info is to pass those fields such as telephoneOffice as part of the profile object, which is copied to user.profile in the document inserted to the user collection.
For example:
let userId = Accounts.createUser({
username: Session.get('name'),
password: "123456",
email: Session.get('email'),
profile: {
telephoneOffice: Session.get('telephoneOffice'),
telephoneHouse: Session.get('telephoneHouse'),
employeeSalary: Session.get('salary'),
annualLeave: 14
}
});
Note that the user.profile fields are by default modifiable by users. So it's there by legacy, but Meteor actually recommends avoiding using it for storage.
If you want those fields to be on user instead of user.profile, What you can do is to pass your custom params on the profile object as above, and then override the default behavior using Accounts.onCreateUser. Something like this:
Accounts.onCreateUser(function(options, user) {
if (options.profile)
_.extend(user, options.profile);
return user;
});
See more info here: https://guide.meteor.com/accounts.html#custom-user-data
I want to check a specific field in the users collection to give permissions to modify another collection. This is my code:
import { Mongo } from 'meteor/mongo';
export const Empleados = new Mongo.Collection('empleados');
Empleados.allow({
insert(userId, empleado) {
return userId;
},
update(userId, empleado, fields, modifier) {
return userId;
},
remove(userId, empleado) {
return userId
}
});
I can check the userId, but how do I check the other fields of the users collection? I am using the accounts-password package.
Use this in your "allow" rules:
insert: (userId, doc) ->
if (doc.user_id == userId && Meteor.users.findOne({_id: userId}).isAdmin)
true
else
false
You can add the alanning:roles package to your project, which will allow you to give roles to users and you can check if they have the right role assigned to do the operation.
Alternatively you could add a flag to the Meteor.user().profile object in the user record, and look at that to decide.
I'm creating a new user with AngularFire. But when I sign the user up I also ask for first name and last name and I add that info after registration.
$firebaseSimpleLogin(fbRef).$createUser($scope.signupData.email, $scope.signupData.password).then(function (user) {
// Add additional information for current user
$firebase(fbRef.child('users').child(user.id).child("name")).$set({
first: $scope.signupData.first_name,
last: $scope.signupData.last_name
}).then(function () {
$rootScope.user = user;
});
});
The above code works, it creates node fin Firebase (users/user.id/ ...).
The problem
When I login with the new user I get the user default information: id, email, uid, etc. but no name. How can I associate that data automatically to the user?
You can't. Firebase hides the complexity of login management by storing the login details in its own datastore. This process knows nothing of your app's forge, which means it doesn't know if or where you're storing any additional user information. It returns the data that it does know about as a convenience (id, uid, email, md5_hash, provider, firebaseAuthToken).
It's up to your app to then take the [u]id and grab whatever app specific user information you need (such as first name, last name). For an Angular app, you'd want to have a UserProfile service which retrieves the data you're looking for once you get the authentication success broadcast.
Also, in your snippet, consider changing
.child(user.id)
to
.child(user.uid)
This will come in handy if you ever support Facebook/Twitter/Persona authentication later on. uid looks like "simplelogin:1" - it helps to avoid unlikely but possible id clashes across providers.
I have the same issue on this and feel like noone actually has a clear answer (2 years on). But here is the rough structure of how such a service could look like:
app.factory('Auth', function(FURL, $firebaseAuth, $firebaseObject, $rootScope, $window){
var ref = new Firebase(FURL);
var auth = $firebaseAuth(ref);
var Auth = {
user: {},
login: function(user){
return auth.$authWithPassword({
email: user.email,
password: user.password
});
},
signedIn: function(){
return !!Auth.user.provider;
},
logout: function(){
return auth.$unauth;
}
};
// When user auths, store auth data in the user object
auth.$onAuth(function(authData){
if(authData){
angular.copy(authData, Auth.user);
// Set the profile
Auth.user.profile = $firebaseObject(ref.child('profile').child(authData.uid));
Auth.user.profile.$loaded().then(function(profile){
$window.localStorage['gym-key'] = profile.gym.toString();
});
} else {
if(Auth.user && Auth.user.profile){
Auth.user.profile.$destroy();
}
}
});
return Auth;
});