Loopback has a powerful access control feature that encompasses filtering capabilities

I have three models set up in Loopback: Reader, Book, and Note. The Reader model is essentially an instance of User and has the ability to log in. Here are the relationships between the models:

  • Reader has many Books
  • Reader has many Notes
  • Book has many Notes
  • Note belongs to Reader
  • Note belongs to Book

My goal is to query all Books belonging to a logged-in Reader with populated Notes that belong to both the Book and the Reader.

The API call would look something like this:

/api/reader/me/books?filter[include]=notes

However, the issue is that this call returns all notes associated with the Book, regardless of whether they belong to the specific Reader. I could add another filter to the API call, but I need to enforce note filtering on the server side to ensure that Readers do not have access to other Readers' notes.

I attempted to implement access control on the Book model as follows:

{
  "principalType": "ROLE",
  "principalId": "$owner",
  "permission": "ALLOW",
  "property": "__get__notes"
}

And applied this ACL to the Note model:

[
    {
      "principalType": "ROLE",
      "principalId": "$everyone",
      "permission": "DENY"
    }, {
      "principalType": "ROLE",
      "principalId": "$owner",
      "permission": "ALLOW",
      "property": "*"
    }
]

This setup works correctly for API calls like:

/api/reader/me/books/<bookId>/notes

But it does not work for the initial call with the include filter. What steps should I take to only populate the Reader's notes within the books?

Any assistance would be greatly appreciated.

Answer №1

If you want to accomplish this task, you need to create remote methods and apply filters at the application level.

Assume that user details are stored in the User model.

module.exports = function(Reader){

    var async = require('async');
    Reader.fetchBooks = function(userId, callback){
        var app = this.app;
        var updatedReaderList = [];
        Reader.find({
            where: {userId: userId },
            include: {relation: "books"} 
        }, function(err, readerList){
            if(err){
                return callback(err);
            }
            if(readerList){
                if(readerList.length){
                   var series = []; 
                   readerList.forEach(function(reader){
                      var readerObj = reader.toJSON();
                      updatedReaderList.push(readerObj);
                      if(readerObj.books){
                          readerObj.books.forEach(function(book){
                              series.push(function(callback){
                                fetchNotes(app, book, readerObj.id, callback);
                              });
                          } 
                      }
                   });

                  //Now save the data in series..
                  async.series(series, function(err){
                      if(err){
                          callback(err);
                      }else{

                          //Now send the callback
                          callback(null, updatedReaderList);
                      }
                  });

                }else{
                  callback(null, [])
                }
            }else{
              callback(null, [])
            }
        });


    };

    var fetchNotes = function(app, book, readerId, callback){
        var Note = app.models.Note;
        //Fetch only those note whose reader id belongs to current reader and book belongs to same one.
        Note.find({
          where: {
            readerId: readerId,
            bookId: book.id
          }
        }, function(err, noteList){
          if(err){
            return callback(err);
          }else{
            if(noteList){
              book.notes = noteList;
            }
          }
        });
    };
}

Now you can restrict all other methods and simply allow this remote method.

{
  "principalType": "ROLE",
  "principalId": "$owner",
  "permission": "ALLOW",
  "property": "fetchBooks"
}

Note: Consider using promises to avoid nested callbacks.

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

Resolving duplicated controller code in Angular using UI.Router

During the process of updating my app to utilize UI.Router's state.resolve in order to streamline some controller logic, I encountered a common issue: How can one prevent code duplication that occurs when fetching dependencies in resolved assets and u ...

Angular Jasmine Test produces incorrect ng-class result

During my experimentation with an Angular Directive, I encountered an issue where I utilized $compile to generate an instance of the directive in the Document Object Model (DOM). The element that the directive is linked to includes an ng-class attribute wi ...

Using Node.js to convert a file name to a timestamp

I need to change a filename into a timestamp in my Laravel application. let test = "/2020-05-16_11-17-47_UTC_1.jpg" I attempted using split and join to replace the -, but I don't believe it's the most efficient method. The desired output should ...

Combine two objects and discard any duplicate keys

I have two objects named original and custom. My goal is to merge the content from custom into original, but only update the keys in original that are also present in the custom object. Here is an example scenario: var original = { coupon: { ...

`In AngularJS, the same URL ('/') can display different templates depending on the user's login status.`

What is the most effective way to configure routing and templates in AngularJS to display a distinct front end and login area for visitors, while presenting a dashboard to logged-in users all on the same base URL ('/')? These two pages have comp ...

Is there a way to use npx with a parameter like `--registry https://registry.npmjs.org` during an npm install to bypass the need for artifactory?

Using --registry https://registry.npmjs.org has been a workaround to bypass an Artifactory-like proxy issue when installing with npm install. However, I am now facing a similar challenge with npm xxx, as I'm getting the following error: npm ERR! code ...

Adding a function call from a C library into my custom npm module

I have a .exe file from a C library that my package depends on. Currently, my package functions properly only if the user has this command included in their PATH. Is there a way to automatically install this command from the C library when the user install ...

Exploring the intricacies of .$on() in AngularJS

Recently, in a tutorial I came across the following code snippet: $rootScope.$on('abc',function(event, next, current){ }); This has raised some questions for me. Namely, what exactly does the .$on() function do? And why is it prefixed with a $ ...

Select the radio button upon input alteration

Sign in image How can I automatically select the 'Yes' option button in the controller when a user starts typing in the password field? <!-- views/index.html --> <label for="optionsRadios1"> <input type="radio" name="optionsRa ...

Can someone guide me on implementing Node.js clusters in my basic Express application?

— I have successfully developed a basic application that retrieves data (50 items) from a Redis DB and displays it on localhost. After running an ApacheBench test with parameters c = 100, n = 50000, I am achieving around 150 requests/sec on my aging dual ...

What is the process for exporting data from MongoDB?

Recently, I created a web application for fun using Handlebars, Express, and Mongoose/MongoDB. This app allows users to sign up, post advertisements for others to view and respond to. All the ads are displayed on the index page, making it a shared experie ...

Angular facilitates the seamless uploading of files to S3

Utilizing this particular plugin Alright, so here's the issue I'm facing: I have been able to generate a signed S3 URL on my server and successfully upload a file using the following method: How it used to work: shell curl -T file.jpg http:// ...

Unit Testing in Angular with spyOn().and.callThrough() does not execute the actual function

I am new to unit testing using Jasmine in Angular and currently exploring how to test a service with the function loadSomething(id) and added a console.info statement in it. My Service Code: function loadSomething(id) { console.info('this is a t ...

Guide to setting up Firebase Admin to initialize application based on request environment

Currently working on a React Native application with 3 variants: development, staging, and production. I only have two Firebase projects, one for production and the other for development/staging purposes. I have an Express server that utilizes the Firebas ...

The controller in my template is not being passed by the $routeProvider

I attempted to dynamically load a template in Angular using ngRoute... However, I encountered an issue with the following code: (app.js route configuration) app.config(function($routeProvider) { $routeProvider.when("/password", { templateUrl ...

The most efficient method for searching and deleting elements from an array in Mongoose

I'm currently struggling with my implementation of mongoose in express. It seems like I'm using too many queries just to add something to a document, and I can't help but feel that there's a simpler way to achieve this. This function i ...

Retrieve an array of objects using the Handlebars Helper

I am currently working on creating a helper function that will generate an array of objects for looping. This is what I have so far: Handlebars.registerHelper('testHelper', () => { return [ { slug: 'Test', title: 'This is ...

Having difficulty adding a custom library from a repository into an Ember project as a dependency

I've been working on a WebGL library that I want to include as a dependency in an EmberJS project. It seems like I should be able to do this directly from the repository without creating an npm package, but I'm running into some issues. To illus ...

How can one effectively extract data from a database in a React application?

We have a basic react app with authorization, utilizing JWT tokens and Redux for state management. Personally, I find the code quite complex and obfuscated, making it challenging to grasp fully. However, let's set that aside. Upon login, a token is s ...

angularJS equivalent of whenDOMReady

Greetings: Angular novice incoming. Behold, my code snippet residing in an angular.js territory: <div class="clearfix" ng-controller="Controller"> <h1>Active Ideas <button type="button" ng-click="search()">Fetch Ideas</bu ...