I suggest you this solution
first, add a permission that only allows a user to GET and PATCH its own user document (I assume that the user document is created by a different process like https://github.com/SoftInstigate/restheart-examples/tree/master/user-signup)
{
"_id": { "$oid": "5d9485639eab3a852d48a1de" },
"predicate": "path-template[value='/users/{username}'] and equals[%u, '${username}'] and (method[value='PATCH'] or method[value='GET'])",
"roles": ["USER"],
"priority": 1,
"readFilter": null,
"writeFilter": null
}
second, add the following interceptor that forbids PATCH requests containing the property roles
@RegisterPlugin(name = "denyPatchRoles",
description = "forbids PATCH request with roles property",
interceptPoint = InterceptPoint.REQUST_AFTER_AUTH)
public class DenyFilterOnUserPwd implements MongoInterceptor {
@Override
public boolean resolve(MongoRequest request, MongoResponse response) {
return request.isPatch()
&& request.getPath().startsWith("/users")
&& hasRolesProperty(request.getContent());
}
@Override
public void handle(MongoRequest request, MongoResponse response) throws Exception {
response.setInError(HttpStatus.SC_FORBIDDEN, "You cannot update your roles");
}
private boolean hasRolesProperty(BsonDocument body) {
return body != null && body.containsKey("roles");
}
}
(I haven't tested the code, so it might need some tweaks)
14:43:13.740 [XNIO-1 task-2] [1;31mERROR[0;39m org.restheart.handlers.ErrorHandler - Error handling the request. An external dependency is missing for service polygonsAll. Copy the missing dependency jar to the plugins directory to add it to the classpath
11:52:44.520 [XNIO-1 task-3] [39mDEBUG[0;39m o.r.plugins.security.AuthMechanism - basicAuthMechanism -> NOT_ATTEMPTED
11:52:44.803 [XNIO-1 task-3] [1;31mERROR[0;39m org.restheart.handlers.ErrorHandler - Error handling the request. An external dependency is missing for service polygonsAll. Copy the missing dependency jar to the plugins directory to add it to the classpath
java.lang.NoClassDefFoundError: com/mongodb/util/JSON
at org.restheart.examples.PolygonsAllAvgService.handle(PolygonsAllAvgService.java:90)
Caused by: java.lang.ClassNotFoundException: com.mongodb.util.JSON
at java.base/java.net.URLClassLoader.findClass(Unknown Source)
response.setContent(JSON.serialize(listPoly).getBytes());
response.setContent(listPoly.toString());
response.setContent(listPoly.toString().getBytes());
{
"_id" : ObjectId("5fef54cd0f0126afdeaba6c5"),
"predicate" : "path-prefix[/friends] and (method[GET] or method[PATCH] or method[POST])",
"roles" : [
"user"
],
"priority" : 1,
"readFilter" : {
"_id" : {
"_$eq" : "%USER"
}
},
"writeFilter" : {
"_id" : {
"_$eq" : "%USER"
}
}
}
We could automatically add a stage to the aggregation based on the readFilter, however this can potentially make the query too heavy in some cases. But I think that we could provide the readFilter in a predefined aggregation variable so it could be used as {"$var":"readFilter"}
this can be then used in the aggregation definition, eg
{ "aggrs" : [
{ "stages" : [
{ "$match": {"$and": [{"status": "A" }, {"$var":"readFilter"}] }},
{ "$group" : { "_id" : "$gender", "avg_age" : { "$avg" : "$age" } } },
{ "$merge": { "into": "avgAgeByGender" } }
],
"type" : "pipeline",
"uri" : "age-by-gender"
}
]
}
what do you think?
ok, we'll do it. we are currently working on 6.0 to be release in few weeks.
It will also allow you to write Service and Interceptors in javascript!!! (with hot deployment, no need to restart, just copy plugin.js in plugins directory). Here an example:
const http = require('http');
({
options: {
name: "nodeModuleSrv",
description: "just an example node service that requires http",
uri: '/test',
secured: false, // optional, default false
matchPolicy: "PREFIX" // optional, default PREFIX
},
handle: (request, response) => new Promise((resolve, reject) => {
// LOGGER.debug('request {}', request.getContent());
const reqOpts = {
hostname: 'httpbin.org',
port: 80,
path: '/anything',
method: 'GET'
}
const req = http.request(reqOpts, res => {
let data = '';
res.on('data', d => {
data += d;
});
res.on('end', () => {
const rc = JSON.parse(request.getContent() || '{}');
let body = {
msg: `Hello ${rc.name || 'World'}`,
anything: JSON.parse(data)
};
response.setContent(JSON.stringify(body));
response.setContentTypeAsJson();
resolve();
});
})
req.on("error", (err) => reject(err));
req.end();
})
})