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

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

Related

User model giving email validation error in loopback3

I have created a model MyUser based on the User model as follows:
{
"name": "MyUser",
"base": "User",
"idInjection": true,
"options": {
"validateUpsert": true
},
"ownerRelations": true,
"emailVerificationRequired": true,
"hidden": [
"email",
"emailVerified"
],
"properties": {
"firstName": {
"type": "string",
"required": true
},
"lastName": {
"type": "string",
"required": true
},
"email": {
"type": "string",
"required": true
}
},
"validations": [],
"relations": {
"accessTokens": {
"type": "hasMany",
"model": "CustomAccessToken",
"polymorphic": {
"foreignKey": "userId",
"discriminator": "principalType"
},
"options": {
"disableInclude": true
}
}
},
"acls": [
{
"accessType": "*",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "DENY"
},
{
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW",
"property": "create"
}
],
"methods": {}
}
It works perfectly on my local server. But when I host it on aws ec2, only the first time user is created. Next time even if I give different email id, I get email verification error as follows:
The 'MyUser' instance is not valid. Details:emailEmail already exists (value: undefined).
Now if I delete the 1st record and try to create user, it works. So, only 1st record is getting inserted and post that for any unique email also, I am getting validation error. I am not sure why this is happening.
I was able to figure out the problem. I have kept email as a hidden field. loopback has "prohibitHiddenPropertiesInQuery": true by default which does not allow hidden properties in query. As a result, during user validation, email was ignored in the query in loopback-juggler/lib/validation.js. Upon setting prohibitHiddenPropertiesInQuery to false, it works.
However, I am still not sure why it was working in development mode on my local server. prohibitHiddenPropertiesInQuery is by default true, so it should not have worked in development mode also. If anyone have any answer to this, please do let me know.

Loopback Update model extend from User Model Error

I have created an Artist model with User as its based model. I was trying to update the artist model through the Loopback explorer but i have an error:
"message": "ValidationError: The Artist instance is not valid. Details: password
can't be blank (value: undefined); email can't be blank (value:
undefined)."
I have already registered a sample info and i have it in my artist db in MongoDB. i am not sure why i need to put something in the email and password field when i was only trying to update an existing data?
here is my artist JSON:
{
"name": "Artist",
"base": "User",
"idInjection": false,
"options": {
"validateUpsert": true,
"strictObjectIDCoercion": true
},
"properties": {
"firstName": {
"type": "string"
},
"middleName": {
"type": "string"
},
"lastName": {
"type": "string"
},
"telephoneNumber": {
"type": "string"
},
"country": {
"type": "string"
},
"city": {
"type": "string"
},
"genre": {
"type": "string"
},
"description": {
"type": "string"
},
"pricing": {
"type": "string"
},
"profilePicture": {
"type": "string"
}
},
"validations": [],
"acls": [
{
"accessType": "EXECUTE",
"principalType": "ROLE",
"principalId": "$authenticated",
"permission": "ALLOW",
"property": "create"
},
{
"accessType": "EXECUTE",
"principalType": "ROLE",
"principalId": "$owner",
"permission": "ALLOW",
"property": [
"updateAttributes",
"deleteById"
]
},
{
"accessType": "READ",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
}
],
"methods": {}
}
Am i missing something here?
Because your entity is inherited from a User model, it needs to have the 'password' and 'email' attributes set when using POST or PUT methods (so the whole object is created/updated) - as they are used to log the user in.
If you simply want to update your Artist whithout passing the whole datas, use PATCH. (Then 'password' and 'email' won't be mandatory anymore).
If you don't need to use the Loopback's login system, do not inherit your entity from the User model. (Or is there another reason for you to do so?)

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

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!