Closed indb-fr closed 7 years ago
the user name is sent yo restheart via basic authentication headers. with security enabled restheart knows who is the caller.
you can transform requests and response data via the representation transformers features.
you have to develop a java class that implements the following interface:
(all code here ferers to 3.0 version, for 2.0 you have the same with few differences due to old Mongo java driver)
void transform(
final HttpServerExchange exchange,
final RequestContext context,
BsonValue contentToTransform,
final BsonValue args);
}
you also have to declare the transformer in the conf file and set it to apply to your collection. all information in the documentation page mentioned before.
in the context
object you find the userid
and roles
. the contentToTranform
is what you have to modify filtering out the OrgUnitId
you want.
note that if you need more information than what available in the requestContext object to take the decision about what filtering out, the transformer class can make http requests (for instance, using unirest library) and also querying the db. You can retrieve the MongoClient as follows:
private final MongoClient MONGO_CLIENT = MongoDBClientSingleton.getInstance().getClient();
final note, if you perform a time consuming operation in the transformer you obviously slow down all the request to the involved collection. you might consider for this to cache data. RESTHeart provides a simple to use cache feature, example:
LoadingCache<String, BsonDocument> collCache = CacheFactory.createLocalLoadingCache(2, Cache.EXPIRE_POLICY.AFTER_WRITE, 5 * 1000, (String key) -> {
return MONGO_CLIENT.getDatabase(DB)
.getCollection("coll", BsonDocument.class)
.find(new BsonDocument("_id", new BsonString(key)))
.first();
});
Thanks, this is very usefull.
I read the documentation carefully but I did not understand that I could transform the request sent to MongoDB server.
In my case, I would add the following filter to my request GET localhost:9999/geo/postalCodes ?keys={properties.city:1} &keys={'properties.geo_point_2d':1} &keys={'properties.postal_code':1} &filter={'orgUnitId':{'[ "1B9", 0A3" ]'}}
In the documentation, i read this :
A transformer can be applied either in the REQUEST or RESPONSE phases
Applying it in the REQUEST phase makes sense only for PUT, POST and PATCH requests (when data is sent from the client). In these cases, the incoming data is first transformed and then stored in MongoDB Applying it in the RESPONSE phase, makes sense only for GET requests (when data is sent back to clients). The data is first retrieved from MongoDB, than transformed and passed back to the client."
My context is a GET REQUEST , isn't it ?
"you also have to declare the transformer in the conf file and set it to apply to your collection. all information in the documentation page mentioned before."
How can i do that ?
Are you sure the following command syntax is correct ? java -server -classpath restheart.jar:custom-transformer.jar org.restheart.Bootstrapper restheart.yml
Thanks in advance
the transformer can be applied on
develop your transformer
you implement the Transformer
class and package it in a jar file.
say that your class is com.acme.MyTransformer
declare it in the configuration file
open the default conf file restheart.yml and find the "group: transformer" section. here give your transformer a name
- group: transformers
interface: org.restheart.hal.metadata.singletons.Transformer
singletons:
- name: myCoolTransformer
class: com.acme.MyTransformer
apply to your collection
add the rts
metadata to your collection
PATCH /db/collection
{
"rts": [
{
"name": "myCoolTransformer",
"phase": <phase>,
"scope": "CHILDREN",
"args": null
}
]
}
phase
should be either "REQUEST" or "RESPONSE". The former is for write operations (transformation is applied to request body) the latter for read operations (transformation is applied to response body)
the BsonValue args
argument of the transform()
method.
The command is correct on linux and OSX. On windows the path separator is ;
#on linux/osx
java -server -classpath restheart.jar:custom-transformer.jar org.restheart.Bootstrapper restheart.yml
#on windows
java -server -classpath restheart.jar;custom-transformer.jar org.restheart.Bootstrapper restheart.yml
note that the configuration file path is relative to the restheart.jar directory.
Ok, I made it work. Thanks for your help.
My request is the following :
GET localhost:9999/geo/postalCodes?keys={properties.city:1}&keys={'properties.geo_point_2d':1}&keys={'properties.postal_code':1}
I patched my collection like this :
{
"rts": [
{
"name": "accessControlTransformer",
"phase": "RESPONSE",
"scope": "CHILDREN",
"args": null
}
]
}
And I can transform the results passed back to the client. But this not my use case because the MongoDB server's results are already avalaible in the RequestContext context.responseContent of my Transformer. This is too late. As expected because i patched my collection with phase = RESPONSE. Phase = REQUEST is not possible for my use case because verbs are only PUT PATCH POST.
Did I miss something?
Do you see any other way?
Maybe i have to develop a custom AccessManager and inject new MongoDB filter parameters on the fly with HttpServerExchange.addQueryParam ?
Maybe this is mostly a hack but a custom AccessManager did the trick.
` public boolean isAllowed(HttpServerExchange exchange, RequestContext context) {
// get and check filter parameter
// rem : the filter syntax is not checked
exchange.addQueryParam("filter","{'orgUnitId':{'[ "1B9", 0A3" ]'}}");
Deque<String> filters = exchange.getQueryParameters().get(FILTER_QPARAM_KEY);
context.setFilter(filters);
`
Do you see any problem with something like this or a more elegant way ?
I've better understood your need and sorry if I have put on the wrong direction.
Using a custom AccessManager is definitely the only way to go to add the filter.
Indeed request transformers should also apply to GET requests (for instance, to modify qparams), will do it for 3.0 https://softinstigate.atlassian.net/browse/RH-211
My notes:
requestContext.getCollectionName()
It does not matter, I learned about representation transformers ;-)
Thanks for all
I have to dynamically add a filter to restrict users to see only their list of organization units. This list (OrgUnitId in [ '0A1', '0B3', '1G4']) is recovered by calling a REST service, and I wish - for security reasons - make this call just before sending the request to the Mongo DB server.
The username can be cyphered and sent to RestHeart in a HTTP request header.
Do you know a way to do that?
Thanks in advance