Filter results by $owner and is_public property?

14 views
Skip to first unread message

Jonathan Wohl

unread,
Jan 18, 2017, 7:34:22 PM1/18/17
to LoopbackJS
I'm trying to figure out the best way to accomplish this — I have a model, Article, that has a flag property is_public. An Article has an $owner which is a User, who can optionally mark Articles public. I want all Users to be able to view all Articles that are public (i.e. is_public=true) or that they own.

This seems like a pretty common pattern, but doesn't sound like it's supported via the ACL, and I'm not sure what the best way to implement it is in Loopback. Before updating to v3.0 I was able to accomplish it with an 'access' observer, adding a where filter to the query, but for v3.0 this is breaking other parts of the ACL. I've tried doing it with a 'loaded' observer, but it seems like there's still no way to remove results via this hook.

Anyone have pointers for this type of Model-level access control rules?

Mark Johnson

unread,
Jan 18, 2017, 9:33:20 PM1/18/17
to LoopbackJS
Have you looked at a custom role resolver with the registerResolver()?

Jonathan Wohl

unread,
Jan 18, 2017, 9:59:20 PM1/18/17
to LoopbackJS
Thanks for the suggestion Mark — I did try using a custom role resolver, and am using them elsewhere in my app. However I couldn't get it to work on a per model basis, only on a per method basis. I added my custom $articleViewer role to the READ access type, but the outcome was that if any of the models in a result collection gets rejected, I get a 401. What I want is that the models that don't pass the check are just excluded from the result.

This is the custom role I tried:

Role.registerResolver('$articleViewer', function(role, context, cb) {

        function reject(err) {
            debug('reject:', err);
            if (err) {
                return cb(err);
            }
            cb(null, false);
        }

        // $articleViewer is only for to Article models
        if (context.modelName !== 'Article') {
            return reject();
        }

        // do not allow anonymous users
        var userId = context.accessToken.userId;
        if (!userId) {
            return reject();
        }

        // get the article
        context.model.findById(context.modelId, {}, function(err, article) {
            if (err || !article) {
                return reject(err);
            }

            // article is public, allow it
            if (article.is_public) {
                return cb(null, true);
            }

            Role.isOwner(context.model, context.modelId, userId, function(err, owner) {
                // user is owner, allow it
                if (owner) {
                    return cb(null, true);
                }
                return reject(err);
            });

        });
    });
Reply all
Reply to author
Forward
0 new messages