Permitting access to Meteor Collection with Collection.allow

Hello everyone, I'm facing an issue with the Meteor accounts API again. I am trying to allow only logged-in users to modify their own list without impacting other users' lists. Here is the code I have:

Meteor.subscribe('Categories');

Meteor.autosubscribe(function() {
    Meteor.subscribe("listdetails",
        Session.get('current_list'));
});
'keyup #add-category': function (e,t){
        if (e.which === 13)
        {
            var catVal = String(e.target.value || "");
            if (catVal)
            {
                lists.insert({Category:catVal,owner:this.userId});
                Session.set('adding_category', false);
            }
        }
    },

The server-side:

Meteor.startup(function () {

    Meteor.publish("Categories", function() {
        return lists.find({owner:Meteor.userId},{fields:{Category:1}});
    });


    Meteor.publish("listdetails", function(category_id){
        return lists.find({_id:category_id});
    });
});

Both client and server sides:

lists = new Meteor.Collection("Lists");

function adminUser(userId) {
    var adminUser = Meteor.users.findOne({username:"admin"});
    return (userId && adminUser && userId === adminUser._id);
}
lists.allow({
    insert: function (userId, doc) {
    // the user must be logged in, and the document must be owned by the user
    return (adminUser(userId) || userId && doc.owner === userId);
    },
    update: function(userId, docs, fields, modifier){
        return adminUser(userId) || _.all(docs, function(doc) {
            return doc.owner === userId;
        });
    },
    remove: function (userId, docs){
        return adminUser(userId) || _.all(docs, function(doc) {
            return doc.owner === userId;
        });
    },
    fetch: ['owner']
});

You can see that when logged in as an admin or not logged in at all, the screens look similar (not the desired result). Also, note that "this.userId" is undefined which is strange, hence why I used Meteor.userId.

Answer №1

Revise your code as shown below:

For the client side (within the "keyup #add-category" event):

lists.insert({Category:catVal,owner:Meteor.userId()});

For the server side (within the publish Categories function):

 return lists.find({owner:this.userId},{fields:{Category:1}});

For both client and server sides:

lists.allow({
    insert: function(userId, doc){
        return adminUser(userId) || (userId && doc.owner === userId);
    },
    update: function(userId, doc, fields, modifier) {
        return adminUser(userId) || doc.owner === userId;
    },
    remove: function (userId, doc){
        return adminUser(userId) || doc.owner === userId;
    },
    fetch: ['owner']
});

Answer №2

For the client side, it is recommended to use Meteor.userId(), and for the server side use this.userId specifically within a publish function:

Meteor.publish("Categories", function() {
    return lists.find({owner:this.userId},{fields:{Category:1}});
});

When inserting data on the client side, follow this approach:

lists.insert({Category:catVal,owner:Meteor.userId()});

Remember to disable autopublish, which automatically makes everything public, before launching meteor:

meteor remove autopublish

Answer №3

Here is the complete client code snippet:

Meteor.subscribe('Categories');

Meteor.autosubscribe(function() {
    Meteor.subscribe("listdetails",
        Session.get('current_list'));
});

Template.categories.lists = function () {
    return lists.find({},{sort: {Category: 1}});
};

Session.set('adding_category', false);

Template.categories.new_cat = function () {
    return Session.equals('adding_category',true);
};
Template.categories.events({

    'click #btnNewCat': function (e, t) {

        Session.set('adding_category', true);

        Meteor.flush();
        focusText(t.find("#add-category"));
    },

    'keyup #add-category': function (e,t){
        if (e.which === 13)
        {
            var catVal = String(e.target.value || "");
            if (catVal)
            {
                lists.insert({Category:catVal,owner:Meteor.userId});
                Session.set('adding_category', false);
            }
        }
    },
    'focusout #add-category': function(e,t){

        Session.set('adding_category',false);

    },
    'click .category': selectCategory
});

/////Generic Helper Functions/////

//Function to set cursor position and value.
function focusText(i,val) {
    i.focus();
    i.value = val ? val : "";
    i.select();

};//< -----This is the end tag for focusText() -----

function selectCategory(e,t){
    Session.set('current_list',this._id);
}

function addItem(list_id,item_name){
    if (!item_name&&!list_id)
        return;
    lists.update({_id:list_id},
        {$addToSet:{items:{Name:item_name}}});
}

function removeItem(list_id,item_name){
    if (!item_name&&!list_id)
        return;
    lists.update({_id:list_id},
        {$pull:{items:{Name:item_name}}});
}

function updateLendee(list_id,item_name,lendee_name){
    var l = lists.findOne({"_id":list_id ,
        "items.Name":item_name});
    if (l&&l.items)
    {
        for (var i = 0; i<l.items.length; i++)
        {
            if (l.items[i].Name === item_name)
            {
                l.items[i].LentTo = lendee_name;
            }
        }
        lists.update({"_id":list_id},{$set:{"items":l.items}});
    }
};

Template.list.items = function () {
    if (Session.equals('current_list',null)) return null;
    else
    {
        var cats = lists.findOne({_id:Session.get('current_list')});
        if (cats&&cats.items)
        {
            for(var i = 0; i<cats.items.length;i++) {
                var d = cats.items[i]; d.Lendee = d.LentTo ? d.LentTo :
                    "free"; d.LendClass = d.LentTo ?
                    "label-important" : "label-success";
            }
            return cats.items;
        }
    }
};// < ---- ending bracket for Template.list.items function ----

Template.list.list_selected = function() {
    return ((Session.get('current_list')!=null) &&
        (!Session.equals('current_list',null)));
};
Template.categories.list_status = function(){
    if (Session.equals('current_list',this._id))
        return "";
    else
        return " btn-inverse";
};
Template.list.list_adding = function(){
    return (Session.equals('list_adding',true));
};
Template.list.lendee_editing = function(){
    return (Session.equals('lendee_input',this.Name));
};

Template.list.events({
    'click #btnAddItem': function (e,t){
        Session.set('list_adding',true);
        Meteor.flush();
        focusText(t.find("#item_to_add"));
    },
    'keyup #item_to_add': function (e,t){
        if (e.which === 13)
        {
            addItem(Session.get('current_list'),e.target.value);
            Session.set('list_adding',false);
        }
    },
    'focusout #item_to_add': function(e,t){
        Session.set('list_adding',false);
    },
    'click .delete_item': function(e,t){
        removeItem(Session.get('current_list'),e.target.id);
    },
    'click .lendee' : function(e,t){
        Session.set('lendee_input',this.Name);
        Meteor.flush();
        focusText(t.find("#edit_lendee"),this.LentTo);
    },
    'keyup #edit_lendee': function (e,t){
        if (e.which === 13)
        {
            updateLendee(Session.get('current_list'),this.Name,
                e.target.value);
            Session.set('lendee_input',null);
        }
        if (e.which === 27)
        {
            Session.set('lendee_input',null);
        }
    }
});

Accounts.ui.config({
    passwordSignupFields: 'USERNAME_AND_OPTIONAL_EMAIL'
});

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Trigger $q manually in AngularJS

My understanding of $q in AngularJS is that it runs every time I refresh my page, similar to using a function in ng-init. Here is the code for $q.all that I have: $q.all([$scope.getCart(), $scope.getCategory(), $scope.getMenu(), $scope.getRestaurant()]). ...

Tips for dynamically changing the color of a React Native style based on data conditions

Just starting with react native. I'm working on a basic quiz feature for my app. I've created an api that provides questions, answers, and a boolean value indicating the correct answer, Something like this : { "question:"Demo quest ...

Is there a way to change an object into a string in JavaScript without using JSON.stringify?

Usually I wouldn't approach it this way, but for the sake of a practice exercise, I am attempting to convert an object without relying on JSON.stringify(). Take a look at the object in question: obj = { num: 0, string: "string", func: function ...

Is there a way to extract the text from the inner div of an element using nightwatch.js?

I'm attempting to retrieve the content of a cell within a table, with the following CSS structure: <div data-testid="cellvalue_row-1_col-0" class="Table-cellContent" xpath="1"><span data-testid="tableCellCon ...

MongoDB: Incomplete query results when using the $geoNear operator

Here is the specific query I am working on: exports.search = (req, res) => { let lat1 = req.body.lat; let lon1 = req.body.lng; let page = req.body.page || 1; let perPage = req.body.perPage || 10; let radius = req.body.radius || 100000; ...

What occurs to the node request stream before it is utilized?

Currently, I am developing a node application that involves piping the body of a post request into a writable stream for saving data to disk. While working on this project, it has come to my attention that I lack knowledge about what happens to the request ...

Validating fields in MongoDB updates dynamically

Is it possible to validate a request from a form before updating a record in MongoDB? Model: const mongoose = require('mongoose'); const Schema = mongoose.Schema; const batchSchema = Schema({ batch_number:{type: String, required: true}, w ...

Attempt to resend email if unsuccessful

I have implemented nodemailer in my node application for sending emails. However, I am encountering an issue where the email sometimes fails to send and generates an error message. Typically, it takes two or three attempts before the email is successfully ...

Validation in Angular2 is activated once a user completes typing

My goal is to validate an email address with the server to check if it is already registered, but I only want this validation to occur on blur and not on every value change. I have the ability to add multiple controls to my form, and here is how I have st ...

Vue.js - displaying alert and redirecting after submitting axios form

I have a form that is functioning properly and inserting data correctly. However, if the user clicks the submit button multiple times, the data gets inserted multiple times. To prevent this, I want to redirect the user to another page and display a success ...

Tips for avoiding problems with quoting and using apostrophes in a JavaScript function inside a tag in a JSP file

Within my JSP, I have a string value stored in ${state.status.code} that I need to pass to a JavaScript function when a table element is clicked using onClick to trigger the showStatus function. Here is how I have attempted to achieve this: <c:set var= ...

What is the best way to interchange specific td elements among rows using JavaScript?

I currently have a feature that allows me to swap rows in my table by clicking an arrow. The issue is that I only want to swap all the rows except for a specific td element, which is the first one in every tr. This particular td needs to remain static whil ...

Using Javascript to set a maximum height limit for rows or cells within an HTML table by adjusting the table width

I am currently working on a straightforward JavaScript solution that proves to be useful when utilizing overflow-x:auto in tables. The script aims to decrease the height of rows by increasing the width of the table until the rows reach the desired height. ...

Are your Promises.all functions executing at the incorrect timing?

I can't seem to understand why Promise.all is not working as expected. Even though the log shows that data for "Streak" and "Last Activity" is successfully retrieved towards the end, I want Promise.all to fetch the data only when everything is filled ...

Having trouble displaying child nodes in MatTreeView with Angular 14?

In an Angular project, I am attempting to display a private group's data from GitLab (utilizing a public one for testing purposes). To achieve this, I have implemented the NestedTreeView component. While the parent nodes are displaying correctly, I am ...

Issues with Android Login Screen Functionality

Starting off, I'm aware that obtaining a source code for the login screen is simple and I may opt to do so. However, my dilemma stems from spending the past two days trying to pinpoint where I've gone wrong with that particular code. Needless to ...

Attempting to transmit a text message to a PHP script using Ajax

Hey there! I'm facing a challenge while trying to send a string to a PHP file using AJAX. It seems like the http.send function is not working as expected, and I suspect there might be an issue in my code. (I'm fairly new to programming) mainlin ...

Node.js tutorial: Establishing a connection to MongoDB and sharing it through an include file

Initiating My First Node.js API This is my initial attempt at creating a node.js API/application for learning purposes. I am starting by creating test cases to delete all records in a table, insert three specific records, and then query for those same rec ...

Using AJAX to dynamically update a div's class following a POST request

Upon double clicking a row, I am attempting to trigger the function outlined below. Despite my efforts, I have not been able to update the div class with any changes. function book(id) { $.ajax({ type: "POST", url: "ajax_servlet. ...

Utilizing Facebook's UI share URL parameters within the Facebook app on mobile devices

Encountering an issue with the Fb ui share functionality on certain smartphones Here is the function: function shareFB(data){ FB.ui({ method: 'share', href: data, }, function(response){}); } Implemented as follows: $urlcod ...