Currently, one can mark model properties as hidden from loopback-datasource-juggler independent from what is hidden from loopback-explorer/Swagger. This is evident in the source code by looking at ModelBaseClass.isHiddenProperty in loopback-datasource-juggler/lib/model.js:
// ...
var hiddenProperties = settings && (settings.hiddenProperties || settings.hidden);
// ...
I can set hidden properties in both hiddenProperties and hidden in my model.json file, and those properties will not be sent in responses containing the model in question. If I choose to use the hidden property, those properties will also be hidden from Swagger, as seen in the isHiddenProperty function defined in loopback-explorer/lib/model-helper:
// ...
definition.settings.hidden.indexOf(propName) !== -1
// ...
What this means is that you have the ability to hide properties in responses that contain the models, but they will still show up in Swagger. This feature would be fine if it were a two way street, meaning I could hide properties from Swagger but have them exist in model-containing responses. Perhaps if the isHiddenProperty did a check on an additional, unique settings property:
// ...
definition.settings.hidden.indexOf(propName) !== -1 || definition.settings.hiddenFromExplorer.indexOf(propName) !== -1
// ...
With such an implementation, I could hide a property completely by putting it in the hidden array, hide properties from being displayed in Swagger by putting those properties in hiddenFromExplorer, and prevent things from being returned in model-containing responses by putting those properties in the hiddenProperties array.
The best use case for this in my opinion is the ID field. All of my IDs are auto-generated, so I don't want them to display in explorer/Swagger. However, I do want the ID returned in my responses as it is necessary for many operations.
Im facing the same concerns, +1.
I guess we are talking about different scopes of visibility for properties.
First of all, I kept on searching through docs and can't find anything related to this question. How do I define "different scopes of visibility for each properties"?
Secondly, how do I define "scope of visibility for each properties" rules for each ACL roles? I've seen example how to setup ACL for each Models, but not the Properties level. Maybe I am missing something?
Sorry for confusion. I was brainstorming the potential settings we can specify for properties. We don't have the fine-grained control for the property visibility yet.
@jaxyeh, to my knowledge, you cannot set different access permissions at the property level from the standard model.json file, whether using ACLs or something else. In order to get any semblance of control at the property level, you'll have to use validation methods or remote/model hooks - I had to implement a mix of all 3 in my current implementation. For validation that requires user permissions/ACLs, you'll have to perform those checks in the remote hooks, as that is the only place you'll have access to the request object, and therefore the currently logged-in user.
@raymondfeng - My issue is specifically about the ability to hide things from Swagger and the ability to hide things from the result output using the hidden and hiddenProperties properties in the model.json file. Currently, you can hide things from both or only result output - I would like the ability to hide things from both, only result output, and only Swagger, independently.
I do like your ideas though, and it addresses the issue @jaxyeh is having. Once implemented, it should also address the Swagger/result discrepancy. I can picture it as follows:
regular for DB layerregular for DB layerregular for DB layer, but is not exposed at all to api client, it is only accessible in codemodel.json file, but never persist?model.json?To help address @jaxyeh's issue, ACLs could also be used for property control.
Here is a sample use of these in model.json for a MongoDB implementation:
{
"name": "product",
"plural": "products",
"base": "persistedModel",
"strict": true,
"properties": {
"id": {
"type": "text",
"required": true,
"access": "readOnly",
"readOnlyType": "auto"
},
"created": {
"type": "text",
"required": true,
"access": "readOnly",
"readOnlyType": "createdTimestamp"
},
"lastUpdated": {
"type": "text",
"required": true,
"access": "readOnly",
"readOnlyType": "updatedTimestamp"
},
"requestIPAddress": {
"type": "text",
"required": true,
"access": "hidden"
},
"name": {
"type": "text",
"required": true,
},
"type": {
"type": "text",
"required": true
},
"manufacturerId": {
"required": true
},
"supplierId": {
"required": true
}
},
"hidden": [],
"hiddenProperties": [],
"hiddenAPIProperties": [],
"validations": [],
"relations": {
"manufacturer": {
"type": "belongsTo",
"model": "manufacturer",
"foreignKey": "manufacturerId"
},
"supplier": {
"type": "belongsTo",
"model": "supplier",
"foreignKey": "supplierId"
}
},
"acls": [
{
"accessType": "*",
"modelProperty": "manufacturerId"
"permission": "DENY",
"principalType": "ROLE",
"principalId": "$everyone"
},
{
"accessType": "*",
"modelProperty": "supplierId"
"permission": "DENY",
"principalType": "ROLE",
"principalId": "$everyone"
},
{
"property": "_READ_HIDDEN_PROPERTIES_",
"permission": "ALLOW",
"principalType": "ROLE",
"principalId": "admin"
},
{
"property": "_WRITE_HIDDEN_PROPERTIES_",
"permission": "ALLOW",
"principalType": "ROLE",
"principalId": "admin"
},
{
"accessType": "*",
"modelProperty": "manufacturerId"
"permission": "ALLOW",
"principalType": "ROLE",
"principalId": "admin"
},
{
"accessType": "*",
"modelProperty": "supplierId"
"permission": "ALLOW",
"principalType": "ROLE",
"principalId": "admin"
},
{
"accessType": "*",
"modelProperty": "manufacturerId"
"permission": "ALLOW",
"principalType": "ROLE",
"principalId": "productAdmin"
},
{
"accessType": "*",
"modelProperty": "supplierId"
"permission": "ALLOW",
"principalType": "ROLE",
"principalId": "productAdmin"
}
],
"methods": []
}
It seems that this is a good long-term solution to both my issue and @jaxyeh's issue.
@raymondfeng, what do you think?
FWIW, I prefer to specify the property type (readable, writable, transient, etc.) in the per-property configuration object as opposed to listing them in per-model arrays. The latter will be more difficult to implement for mixins.
@bajtos @raymondfeng I think we should re-open this thread to address the feature requirement.
@seanxlliu with LoopBack 4.0 GA publicly available, LoopBack 3 has moved into Active LTS mode and does not accept any new features (see our Long Term Support policy).
Feel free to open a new issue in loopback-next to discuss this feature requirement for LoopBack 4 and beyond.
Make sense, thanks!
Most helpful comment
I guess we are talking about different scopes of visibility for properties.