Search code examples
node.jsmongodbloopback

Loopback error: Authorization Required


I have a loopback app with mongoDB as below:

when i login as Admin i cannot use post method on dishes. and i get authorization required error. that becomes possible only when i change the dishes role to ALLOW everyone.

how can i acheive the wanted result with keeping everyone on DENY and only ALLOW certain users to certain operations? thank you. here is my code..

app/server/model-config.json:

    {
  "_meta": {
    "sources": [
      "loopback/common/models",
      "loopback/server/models",
      "../common/models",
      "./models"
    ],
    "mixins": [
      "loopback/common/mixins",
      "loopback/server/mixins",
      "../node_modules/loopback-ds-timestamp-mixin",
      "../common/mixins",
      "./mixins"
    ]
  },
  "User": {
    "dataSource": "db"
  },
  "AccessToken": {
    "dataSource": "db",
    "public": false
  },
  "ACL": {
    "dataSource": "MongoDB",
    "public": false
  },
  "RoleMapping": {
    "dataSource": "MongoDB",
    "public": false
  },
  "Role": {
    "dataSource": "MongoDB",
    "public": false
  },
  "dishes": {
    "dataSource": "MongoDB",
    "public": true
  },
  "Customer": {
    "dataSource": "MongoDB",
    "public": true
  },
  "Comments": {
    "dataSource": "MongoDB",
    "public": true
  }
}

app/common/modles/dishes.json:

{
  "name": "dishes",
  "base": "PersistedModel",
  "idInjection": true,
  "options": {
    "validateUpsert": true
  },
  "properties": {
    "name": {
      "type": "string",
      "required": true
    },
    "description": {
      "type": "string",
      "required": true
    },
    "category": {
      "type": "string",
      "required": true
    },
    "image": {
      "type": "string",
      "required": true
    },
    "label": {
      "type": "string",
      "required": true,
      "default": "''"
    },
    "price": {
      "type": "string",
      "required": true,
      "default": "0"
    }
  },
  "mixins": {
    "TimeStamp": true
  },
  "validations": [],
  "relations": {
    "comments": {
      "type": "hasMany",
      "model": "Comments",
      "foreignKey": ""
    },
    "customers": {
      "type": "hasMany",
      "model": "Customer",
      "foreignKey": ""
    }
  },
  "acls": [
    {
      "accessType": "*",
      "principalType": "ROLE",
      "principalId": "$everyone",
      "permission": "DENY"
    },
    {
      "accessType": "READ",
      "principalType": "ROLE",
      "principalId": "$authenticated",
      "permission": "ALLOW"
    },
    {
      "accessType": "EXECUTE",
      "principalType": "ROLE",
      "principalId": "admin",
      "permission": "ALLOW",
      "property": "create"
    },
    {
      "accessType": "WRITE",
      "principalType": "ROLE",
      "principalId": "admin",
      "permission": "ALLOW"
    }
  ],
  "methods": {}
}

app/common/modles/comments.json:

    {
  "name": "Comments",
  "base": "PersistedModel",
  "idInjection": true,
  "options": {
    "validateUpsert": true
  },
  "properties": {
    "Rating": {
      "type": "number",
      "required": true,
      "default": 5
    },
    "comment": {
      "type": "string",
      "required": true
    }
  },
  "mixins": {
    "TimeStamp": true
  },
  "validations": [],
  "relations": {
    "dishes": {
      "type": "belongsTo",
      "model": "dishes",
      "foreignKey": ""
    },
    "customer": {
      "type": "belongsTo",
      "model": "Customer",
      "foreignKey": "customerId"
    }
  },
  "acls": [
    {
      "accessType": "*",
      "principalType": "ROLE",
      "principalId": "$everyone",
      "permission": "DENY"
    },
    {
      "accessType": "READ",
      "principalType": "ROLE",
      "principalId": "$authenticated",
      "permission": "ALLOW"
    },
    {
      "accessType": "EXECUTE",
      "principalType": "ROLE",
      "principalId": "$authenticated",
      "permission": "ALLOW",
      "property": "create"
    },
    {
      "accessType": "WRITE",
      "principalType": "ROLE",
      "principalId": "$owner",
      "permission": "ALLOW"
    }
  ],
  "methods": {}
}

app/common/modles/customer.json:

    {
  "name": "Customer",
  "base": "User",
  "idInjection": true,
  "options": {
    "validateUpsert": true
  },
  "properties": {},
  "validations": [],
  "relations": {
    "comments": {
      "type": "hasMany",
      "model": "Comments",
      "foreignKey": "customerId"
    }
  },
  "acls": [
    {
      "accessType": "*",
      "principalType": "ROLE",
      "principalId": "$everyone",
      "permission": "DENY"
    }
  ],
  "methods": {}
}

and app/server/boot/script.js:

    module.exports = function(app) {
var MongoDB = app.dataSources.MongoDB;

MongoDB.automigrate('Customer', function(err) {
   if (err) throw (err);
   var Customer = app.models.Customer;

   Customer.create([
    {username: 'Admin', email: 'admin@admin.com', password: 'abcdef'},
    {username: 'muppala', email: 'muppala@ust.hk', password: 'abcdef'}
  ], function(err, users) {
        if (err) throw (err);
        var Role = app.models.Role;
        var RoleMapping = app.models.RoleMapping;

        Role.find({ name: 'admin' }, function(err, results) {
            if (err) { throw err; }

            if (results.length < 1) {
                // now we know the DB doesn't have it already, so do the Role creation...
                //create the admin role
                Role.create({
                  name: 'admin'
                }, function(err, role) {
                  if (err) throw (err);
                   //make admin
                  role.principals.create({
                    principalType: RoleMapping.USER,
                    principalId: users[0].id
                  }, function(err, principal) {
                    if (err) throw (err);
                  });
                });
            }
        });
  });
});

};

Solution

  • Seeing your last question I imagine what happened.

    Somehow the collection Role was created but not mapped to User.

    I suggest you to change:

    Role.find({ name: 'admin' }, function(err, results) {
                if (err) { throw err; }
    
                if (results.length < 1) {
                    // now we know the DB doesn't have it already, so do the Role creation...
                    //create the admin role
                    Role.create({
                      name: 'admin'
                    }, function(err, role) {
                      if (err) throw (err);
                       //make admin
                      role.principals.create({
                        principalType: RoleMapping.USER,
                        principalId: users[0].id
                      }, function(err, principal) {
                        if (err) throw (err);
                      });
                    });
                }
            });
    

    By:

    Role.create({
          name: 'admin'
        }, function(err, role) {
          if (err) throw (err);
           //make admin
          role.principals.create({
            principalType: RoleMapping.USER,
            principalId: users[0].id
          }, function(err, principal) {
            if (err) throw (err);
          });
        });
    

    Drop the Role collection: db.Role.drop() and execute Loopback again.

    Note: I was doing the same assigment and worked for me.