When i try to insert into collection without being authentication i get access denied error ,here are my permissions :
signedforms.allow({
'insert': function(userId, doc) {
return userId;
},
'update': function(userId, doc, fields, modifier) {
return userId;
},
'remove': function(userId, doc) {
return userId;
}
});
Try something without ' ' on methods and return true instead of id:
signedforms.allow({
insert: function(userId, doc) {
return true;
}
For more info look at this post https://www.discovermeteor.com/blog/meteor-methods-client-side-operations/
If you're not authenticated then there's no userId, so your functions are returning undefined. Just return true instead.
Related
These are my rules. User can only access accounts associated with that user (account document has field users containing uid)
function isLoggedIn() {
return request.auth != null;
}
function getAccount(accountID) {
return get(/databases/$(database)/documents/accounts/$(accountID)).data;
}
function isBelongTo(accountID) {
return isLoggedIn() && (request.auth.uid in getAccount(accountID).users);
}
match /accounts/{accountID}/{documents=**} {
allow read: if isBelongTo(accountID);
}
This works fine on the rules playground. But when I do a query in firebase javascript sdk in a browser like this
onSnapshot(query(collection(db, "accounts"), where('users', 'array-contains', uid ? uid : '')), (querySnapshot) => {
const docs = [];
// querySnapshot does not have .map, so we have to use .forEach
querySnapshot.forEach((doc) =>
docs.push({ ...doc.data(), id: doc.id })
);
console.log(docs);
});
it returns an empty array (in my larger project but not the demo linked below) and returns a permission error.
if i first get a single document with the below code, then the query above returns the document /accounts/35 in the query snapshot, and also returns a permission error:
onSnapshot(doc(db, "accounts", "35"), (doc) => {
if (doc.exists) {
// console.log("Document data:", doc.data());
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
})
I'm made a sample repo and here is the result:
How can I resolve this?
If I create a new user with createUserWithEmailAndPassword, even though I didn't verify the mail yet, that user is already logged in. And his .emailVerified === false, and until here all good.
Now, I go to the mail, verify it using the link, go back to the web app, it is still .emailVerified === false so I refresh the page, now .emailVerified === true.
So I try to reach this doc:
public async getPublicUserDetails() {
const currentUserId = this._angularFireAuth.auth.currentUser.uid;
try {
const docRef = this._angularFirestore.collection("users").doc(currentUserId).ref;
const doc = await docRef.get();
if (!doc.exists) {
return null;
}
return doc.data() as IPublicUserDetailsDto;
}
catch (error) {
console.error("User " + currentUserId + " details get failed! " + JSON.stringify(error));
throw error;
}
}
It catches an exception, saying I don't have the required permissions to access the doc.
The Firestore rules I'm using are:
rules_version = '2';
service cloud.firestore {
function dbDocs() { return /databases/$(database)/documents; }
function isSignedIn() { return request.auth != null && request.auth.uid != null; }
function isEmailVerified() { return isSignedIn() && request.auth.token.email_verified; }
function isCurrUser(uid) { return isSignedIn() && request.auth.uid == uid; }
function userExists(uid) { return exists(/databases/$(database)/documents/users/$(uid)); }
match /databases/{database}/documents {
match /users {
match /{userId} {
allow read: if isEmailVerified();
allow write: if isEmailVerified() && isCurrUser(userId);
}
}
}
}
I can refresh the page infinite times, but it will work only if I signOut & signIn again OR if I replace the allow read line with
match /{userId} {
allow read: if isSignedIn(); // replace this
allow write: if isEmailVerified() && isCurrUser(userId);
}
Conclusion: it seems like the request.auth.token.email_verified does not reflect the value provided inside the FirebaseAuth service, as it seems to get refreshed only if I log out and back in.
Can someone help me, please? Thank you all in advance!
When a new user account is created I'm using the Accounts.onCreateUser function to insert data into a new collection. I want to check that the insert has successfully worked before progressing. My code appears to work however it seems very messy. I'm wondering if there is a cleaner way to write this code.
Accounts.onCreateUser((options, user) => {
if (user) {
CandidateProfile.insert({
userId: user._id,
firstName: options.profile.name.first,
lastName: options.profile.name.last
});
var checkForNewCandidateProfile = CandidateProfile.findOne(
{ userId: user._id },
{ fields: { userId: 1 } }
);
var userId =
checkForNewCandidateProfile && checkForNewCandidateProfile.userId;
if (userId === user._id) {
return user;
}
}
});
Personally, I don't see any sense in your test. You don't trust insert?
But OK, you need it.
Be sure, that you run your code on the server side. Import it only on server side or just wrap it in if (Meteor.isServer)
Why check if user arg exists? It is, that's how that callback works.
If something's wrong, throw an error to abort user creation.
Possible variant:
if (Meteor.isServer) {
Accounts.onCreateUser((options, user) => {
// You insert sync, so it's up to you to handle errors.
try {
CandidateProfile.insert({
userId: user._id,
firstName: options.profile.name.first,
lastName: options.profile.name.last
});
var checkForNewCandidateProfile = CandidateProfile.findOne(
{ userId: user._id },
{ fields: { userId: 1 } }
);
var userId =
checkForNewCandidateProfile && checkForNewCandidateProfile.userId;
if (userId === user._id) {
return user;
}
} catch (error) {
throw new Error(error);
}
throw new Error("Something's wrong.");
});
}
This Meteor code displays a message on a headerLabel on a template, the server and/or the client changes the message by inserting a new message in HeaderLabelCol mongo collection and expect the client template to change since it publishes the last inserted document.
I was able to insert a new message using the client browser but did not show till I refreshed the page which may indicate that the reactiveness chain is broken somewhere. What is the problem? How can it be fixed? Thanks
//client.js
Template.header.helpers({
headerLabel: function () {
return HeaderLabelCol.findOne() ? HeaderLabelCol.findOne().headerLabel : 'Make a selection';
}
});
//server.js
HeaderLabelCol = new Mongo.Collection('headerLabelCol');
Meteor.publish('headerLabelCol', function () {
return HeaderLabelCol.find({userId: this.userId}, { sort: { createdAt: -1 } });
});
HeaderLabelCol._ensureIndex({createdAt: -1});
HeaderLabelCol.before.insert(function (userId, doc) {
doc.userId = userId;
doc.createdAt = Date.now();
});
HeaderLabelCol.allow({
insert: function (userId, doc) {
return (userId && doc.owner === userId);
}
});
I think you need to add the condition in your helper as well.
//client.js
Template.header.helpers({
headerLabel: function () {
var result = HeaderLabelCol.findOne({}, { sort: { createdAt: -1 } });
return result ? result.headerLabel : 'Make a selection';
}
});
I have the following code in my Meteor app where I create new users, assign them 'basic' role. Yet I am having a trouble showing on the client side errors returned while processing Accounts.createUser, can someone please tell me how I can return errors returned by Accounts.createUser while having it on the server as my code below. Thanks
/server/users.js
Meteor.methods({
'createMemberAccount': function (data, role) {
var userId;
Meteor.call('createNewAccount', data, function(err, result) {
if (err) {
return err;
}
console.log('New account id: '+ result);
Roles.addUsersToRoles(result, role);
return userId = result;
});
return userId;
},
'createNewAccount': function (adminData) {
return Accounts.createUser({email: adminData.email, password : adminData.password, roles: adminData.roles});
}
});
/client/signup.js
Template.signupForm.events({
'submit #signup-form': function(e, t){
e.preventDefault();
var userData = {};
userData.email = $(e.target).find('[name=email]').val();
userData.password = $(e.target).find('[name=password]').val();
userData.roles = ['basic'];
Meteor.call('createMemberAccount', userData, 'basic', function(err, userId) {
if (!err) {
console.log('All OK');
} else {
console.log('Error: ' + err.message);
}
});
return false;
}
});
Since You are creating an static rol "basic", you don't need to do that pair of methods, and Meteor.calls, instead you can use
So, use the v on the client side, just like this.
Template.register.events({
'submit #register-form' : function(e, t) {
e.preventDefault();
var email = t.find('#account-email').value
, password = t.find('#account-password').value;
// Trim and validate the input
Accounts.createUser({email: email, password : password}, function(err){
if (err) {
// Inform the user that account creation failed
} else {
// Success. Account has been created and the user
// has logged in successfully.
}
});
return false;
}
});
If you see there is not any role yet incude, so now on the server.js use the onCreateUser method.
//Server.js
Accounts.onCreateUser(function(options, user) {
if (options.profile)
user.profile = options.profile;
user.role = "basic"
return user;
});
Now thats is more easy, and with less code, if you are trying to create 2 differents roles like "Admin" and "Basic", just on the client side create a profile field named "profile.roles" and do a if statement on the onCreateUser.
return Accounts.createUser({email: adminData.email, password : adminData.password, roles: adminData.roles});
This part returns the userId once it is created, it doesn't return any errors when it fails.
When it fails, the returned value will be undefined
Also, in the server, we cannot use callbacks with Accounts.createUser
If you want find the errors, you have to use Accounts.createUser in client side.
Coming to this late, but on the server side, you can assign the createUser to a variable and it will return the new user’s _id; then you can check if that exists. For example (server side only):
let email = 'foo#bar.com';
let password = 'bar';
let profile = {firstName: 'foo', lastName: 'bar'};
let newId = Accounts.createUser({
password: password,
email: email,
profile: profile
});
if (!newId) {
// New _id did not get created, reason is likely EMail Already Exists
throw new Meteor.Error(403, "Cannot create user: " + error.reason);
}
else {
// Stuff here to do after creating the user
}
The Meteor.Error line will be passed back as an error in the callback on the client side, so you can reflect that error to the browser.