StrongLoop: POST access not being blocked - loopbackjs

I'm using ACL rules to block all types of accesses from all the users. It is working for GET access but it is NOT working for POST accesses.
Any idea what could be wrong?
Here is the code and sample results:
/common/models/client.json
{
"name": "client",
"plural": "clients",
"base": "User",
"idInjection": true,
"properties": {},
"validations": [],
"relations": {},
"acls": [
{
"accessType": "*",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "DENY"
}
],
"methods": {}
}
GET access error (working as expected, it is blocked):
CURL
curl -X GET --header "Accept: application/json" "http://localserver:8080/api/quants"
RESPONSE
{
"error": {
"name": "Error",
"status": 401,
"message": "Authorization Required",
"statusCode": 401,
"code": "AUTHORIZATION_REQUIRED",
"stack": "Error: Authorization Required\n at ...
}
}
POST error, access didnt get blocked. Not working.
CURL:
curl -X POST --header "Content-Type: application/json" --header "Accept: application/json" -d "{
\"email\": \"test#email.com\",
\"password\": \"abcd1234\"
}
" "http://localserver:8080/api/clients"
RESPONSE
{
"email": "test#email.com",
"id": "46b258078da5dtg1ji5809ww"
}

Before suggesting solutions, I'll try to explain what's the cause of 'create' (POST) method to not be denied.
Your client model is a sub-model of Loopback's User built in model.
Two important things to keep in mind in this case:
ACL's defined in a sub-model are not overriding the base class ACL's, but merged to them.
When a request is checked against ACLs, Loopback's algorithm gives the closer match higher weight. closer match in this specific case is a more specific ACL definition. (reference here)
Now, Loopback's User model contains the following ACL:
{
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW",
"property": "create"
}
Your defined ACL
{
"accessType": "*",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "DENY"
// no specific property
}
is less specific, hence not chosen by the algorithm.
In order to solve the issue, you can:
Add specific ACL for denying creation:
{
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "DENY",
"property": "create"
}
Remove the ACL allowing to create from the base User model (very bad solution, but works)

Related

loopback bug acl not working?

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.

Ownership of an in direct model

I have a user which own a house (several of users can own the house), the house can have several of furnitures.
CustomUser.json relation:
"houses": {
"type": "hasAndBelongsToMany",
"model": "House",
"foreignKey": "houseId"
}
House.json relations:
"furnitures": {
"type": "hasAndBelongsToMany",
"model": "Furniture",
"foreignKey": ""
},
"customUsers": {
"type": "hasAndBelongsToMany",
"model": "CustomUser",
"foreignKey": ""
}
My ACL is simple and looks like this for both House and Furniture:
"acls": [
{
"accessType": "*",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "DENY"
},
{
"accessType": "*",
"principalType": "ROLE",
"principalId": "admin",
"permission": "ALLOW"
},
{
"accessType": "*",
"principalType": "ROLE",
"principalId": "$owner",
"permission": "ALLOW"
}
]
Now, accessing houses for the users works fine, but the users won't get their furnitures listed since they belong to a house (the house is the owner). How do I fix this the easiest way possible? Best would be to somehow define that User owns furnitures through the house? The furnitures must belong to the house, though (since multiple users can have a house).
So I got past this through another module called "loopback-component-access-groups" which lets you define groups. In this particular case I made the House-model a group which the users and furnitures have access to. A user can have different roles so I just set that the user (which have the role "member") can access the furnitures which are in the same group (house). This solution works for me, but I am not entirely sure if further objects inherits that they belong to a house if they are related to a furniture.
Writing my own role resolver made this quite flexible, in the role resolver I had to make logic to find the original parent and also make sure there were no dead relationships (since LB doesn't have support for cascade yet).
I recommend starting with this to get going:
https://loopback.io/doc/en/lb3/Defining-and-using-roles.html

Denying permissions issue in loopback

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.

Constantly getting 401 errors in loopback while using User Model

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!

Strongloop/loopback: how to disable authentication for User model over REST?

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"
},
...