I want to define a set of ACL rules that should be easily portable and extendable by using mixins.
Defining these in the MyModel.json for some model MyModel is trivial:
"acls": [
{
"accessType": "*",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "DENY"
}]
This works, and correctly blocks API calls for everyone. However, when I do the same thing (or so I think) in a mixin, it does not work:
module.exports = function (Model, options) {
Model.getApp(function (err, app) {
app.models.ACL.create({
model: Model.modelName,
accessType: '*',
principalType: 'ROLE',
principalId: '$everyone',
permission: 'DENY'
}, function (err, acl) {
console.log('ACL entry created: %j', acl);
});
});
};
What is missing?
After going through the sources to see how Loopback itself registers ACL at config time, the following appears to be the correct way of doing this in a mixin:
module.exports = function (Model, options) {
Model.settings.acls.push({
"accessType": "*",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "DENY"
});
};
Related
i m try to implement acl for model(named as company)
"relations": {
"user": {
"type": "belongsTo",
"model": "user",
"foreignKey": "company_id"
}
},
"acls": [
{
"accessType": "*",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "DENY"
},
{
"accessType": "*",
"principalType": "ROLE",
"principalId": "$owner",
"permission": "ALLOW"
}
],
deny all user and access only to authenticated user owner of that object . here i m also try to add permission for super user who have all permission in short admin and owner of objec(data) can do crud is there any soultion for this please help
You can deny all unauthenticated users then deny everyone and allow only the owner like below :
{
"accessType": "*",
"principalType": "ROLE",
"principalId": "$unauthenticated",
"permission": "DENY"
},
{
"accessType": "*",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "DENY"
},
{
"accessType": "*",
"principalType": "ROLE",
"principalId": "$owner",
"permission": "ALLOW"
}
you need only CRUD for the $owner, you can assign the properties needed for your user as below :
{
"accessType": "*",
"principalType": "ROLE",
"principalId": "$owner",
"permission": "ALLOW",
"property": [
"find",
"create",
"updateAll",
"delete"
]
}
For the super user you should create this Role and assign it to one of your users.
{
"accessType": "*",
"principalType": "ROLE",
"principalId": "superuser",
"permission": "ALLOW"
}
How to create a ROLE ? you can follow the documentation here.
And make sure that you have this file /server/boot/autentication.jsand authetication is enabled : server.enableAuth()
'use strict';
module.exports = function enableAuthentication(server) {
// enable authentication
server.enableAuth();
};
For more ACL tricks please read the official documentation here.
I have written my own login function to a service which verifies username/password and I wish to still use LoopBacks user-model. The problem is to get an accesstoken (and keep a User logged in within the LB-application) I need to provide a password. Storing the password in Loopbacks datasource is not an option. My question, how do I tell Loopback the user is actually verified and can be logged in without password? From what I can see in the User.Login code there is not an option to skip password and just login the user.
This is all done programatically, I'm using a custom user model which has the user model as base. Below is a simplified example:
CustomUser.beforeRemote('login', function(ctx, unused, next) {
UserService.externalLogin(ctx.args.credentials.username ,ctx.args.credentials.password).then(function(response){
//Go to user.login
if(response.authorized){
next();
}
});
});
Not sure if this is the best practice, but I would actually add a custom login entry point for the custom-user.js:
CustomUser.customLogin = function (email, cb) {
var tokenTimeToLive = 12096000; //in ms
CustomUser.findOne({
where: {email: email}
}, function (err, user) {
if (err) {
return cb(err, null);
} else {
if (user) {
user.createAccessToken(tokenTimeToLive, function (error, token) {
return cb(error, token);
});
} else {
return cb(new Error("No User found"), null);
}
}
});
};
CustomUser.remoteMethod('customLogin', {
accepts: {arg: 'email', type: 'string', required: true},
returns: {arg: 'credentials', type: 'object', root: true},
description: "Custom login entry"
});
And a custom ACL for this method in your custom-user.json:
...
"acls": [
{
"accessType": "*",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "DENY"
},
{
"accessType": "READ",
"principalType": "ROLE",
"principalId": "$authenticated",
"permission": "ALLOW"
},
{
"accessType": "WRITE",
"principalType": "ROLE",
"principalId": "$authenticated",
"permission": "ALLOW"
},
{
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW",
"property": "customLogin"
}
]
...
Of course you might want to use another field than the email one but I think it's also a primary key on the Loopback User model.
But you will always need to post a programatically generated password for the Users when you Post a new one.
I've created a model Webuser based on the built in User model.
This is this model's ACL entry in "webuser.json":
"acls": [
{
"accessType": "*",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "DENY"
}
]
To my surprise, I can login via POST /api/webusers/login with no problems. I expected all access to be denied.
What am I doing wrong?
Well I believe the default user acls are still working and they are not overrided. The way loopback decides which ACL entry is effective for the request, is by selecting most specific one that appears later. So loopback has defined this entry:
{
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW",
"property": "login"
}
Since it's as specific as it gets, to beat that you have to add this in your model:
{
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "DENY",
"property": "login"
}
There's also another way to remove the default user acls via code but it's not documented.
I am new to loopback and I am not able to extend User Base model properly. Though in explorer it shows that it is extended but all API's give a 401 error. ex. In normal get call for /users I get..
{
"error": {
"name": "Error",
"status": 401,
"message": "Authorization Required",
"statusCode": 401,
"code": "AUTHORIZATION_REQUIRED",
"stack": "Error: Authorization Required"
}
}
I went thru all the links and questions but none of they are working for me. I have properly put public:true in model-config for User Model extended model and written acls etc. but none of them works. I have also raised an issue on git for strongloop: https://github.com/strongloop/loopback/issues/1809 . Any leads would be awesome. Thanks.
User.json is as below:
{
"name": "user",
"plural": "users",
"base": "User",
"idInjection": true,
"options": {
"validateUpsert": true
},
"mongodb": {
"collection": "User"
},
"properties": {
"name": {
"type": "string",
"required": true
},
"email": {
"type": "string",
"required": true
},
"password": {
"type": "string",
"required": true
},
"phone": {
"type": "string"
}
},
"validations": [],
"relations": {
"question": {
"type": "hasMany",
"model": "question",
"foreignKey": ""
}
},
"acls": [
{
"accessType": "*",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "DENY"
},
{
"accessType": "*",
"principalType": "ROLE",
"principalId": "admin",
"permission": "ALLOW"
},
{
"accessType": "*",
"principalType": "ROLE",
"principalId": "$owner",
"permission": "ALLOW"
},
{
"accessType": "*",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
}
],
"methods": {}
}
Some notes meriting consideration though:
1)You are defining email, password,.. properties, although they are already defined exactly the same way in the parent User model; please see: https://github.com/strongloop/loopback/blob/master/common/models/user.json;
2)For ACLs you are missing accesstypes, they are not right, but they do not break anything...For more info about ACL please see: https://docs.strongloop.com/display/public/LB/Define+access+controls
3)Also when you login please make sure to use user that you have created(a POST request) and it is in database already.
Thanks!
Seems like you've not logged into the application.
Anyway by default most of the functions are not accessible by the settings in user parent class. (This totally is a turn-off)
run the code in the login section
{
"username":"abc",
"password":"xyz"
}
This action will return the token id.
Enter this id in the top most right corner of the page and click the set token button.
Now you're able to use some of the user function.
Create a model inheriting user
:~/nodejs/lab/user-api$ slc loopback:model
? Enter the model name: customer
? Select the data-source to attach customer to: db (memory)
? Select model's base class: User
? Expose customer via the REST API? Yes
? Custom plural form (used to build REST URL): customers
Let's add some customer properties now.
Enter an empty property name when done.
? Property name: phone
invoke loopback:property
? Property type: string
? Required? No
Let's add another customer property.
Enter an empty property name when done.
? Property name:
Granting ACL Access:
slc loopback:acl
? Select the model to apply the ACL entry to: customer
? Select the ACL scope: All methods and properties
? Select the access type: All (match all types)
? Select the role: All users
? Select the permission to apply: Explicitly grant access
Granting ACL Access once again:
slc loopback:acl
? Select the model to apply the ACL entry to: customer
? Select the ACL scope: All methods and properties
? Select the access type: All (match all types)
? Select the role: All users
? Select the permission to apply: Explicitly grant access
When we're granting access two times it takes precedence over DENY in the base class. You'll get a result next time.
A sample class with the ACLs. You can try it in a loopback project, it'll work :)
{
"name": "customer",
"plural": "customers",
"base": "User",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"phone": {
"type": "string"
}
},
"validations": [],
"relations": {},
"acls": [
{
"accessType": "*",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
},
{
"accessType": "READ",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
}
],
"methods": []
}
Please accept answer if it works. It will. Cheers!
I have typed in at the commandline: slc loopback:acl
and disabled all security for the User model.
Going into strongloop explorer, doing a simple GET Users request gives me a 401 authorization required error.
Any ideas how to open up the User object? Is this a known bug?
Thank you
You can extend your user model and set permissions for your custom model like this:
{
"name": "CustomUser",
"base": "User",
"idInjection": true,
"properties": {},
"validations": [],
"relations": {},
"acls": [
{
"accessType": "READ",
"principalType": "ROLE",
"principalId": "$unauthenticated",
"permission": "ALLOW"
},
{
"accessType": "READ",
"principalType": "ROLE",
"principalId": "$authenticated",
"permission": "ALLOW"
},
{
"accessType": "*",
"principalType": "ROLE",
"principalId": "$owner",
"permission": "ALLOW"
}
],
"methods": []
}
However I would NEVER recommend you doing this.
If you really want to disable the endpoints on the User model, you can do this.
Go to your model.config.json and add "public": false to the User field like so:
...
"User": {
"public": false,
"dataSource": "db"
},
...