Skip to content

Instantly share code, notes, and snippets.

@xhanin
Created May 20, 2014 14:00
Show Gist options
  • Save xhanin/6ef3d6f76ce176ef0528 to your computer and use it in GitHub Desktop.
Save xhanin/6ef3d6f76ce176ef0528 to your computer and use it in GitHub Desktop.
Secured Jongo
@Module(priority = -100)
public class JongoModule {
JacksonMapper.Builder getJacksonMapperBuilder() {
return new JacksonMapper.Builder()
.registerModule(new BsonJodaTimeModule())
.withView(Views.Private.class)
;
}
@Provides @Named("Mapper")
public Mapper mapper() {
return getJacksonMapperBuilder().build();
}
// secured jongo, will be used for secured jongo collections which will automatically add security handling query part
@Provides @Named("SecuredMapper")
public Mapper securedMapper() {
SecuredQueryFactory queryFactory = new SecuredQueryFactory();
Mapper mapper = getJacksonMapperBuilder()
.withQueryFactory(queryFactory)
.build();
queryFactory.setMarshaller(mapper.getMarshaller());
return mapper;
}
@Provides @Named("SecuredJongo")
public Jongo securedJongo(@Named(MongoModule.MONGO_DB_NAME) String dbName,
@Named(MongoModule.MONGO_CLIENT_NAME) MongoClient mongoClient,
@Named("SecuredMapper") final Mapper securedMapper,
@Named("Mapper") final Mapper mapper) {
// Override Jongo to be able to use SecuredFind instead of Find
return new Jongo(mongoClient.getDB(dbName), securedMapper) {
@Override
public MongoCollection getCollection(String name) {
final DBCollection dbCollection = getDatabase().getCollection(name);
dbCollection.setDBDecoderFactory(BsonDBDecoder.FACTORY);
dbCollection.setDBEncoderFactory(BsonDBEncoder.FACTORY);
return new MongoCollection(dbCollection, securedMapper) {
@Override
public Find find(String query, Object... parameters) {
return new SecuredFind(dbCollection, dbCollection.getReadPreference(), securedMapper.getUnmarshaller(),
mapper.getQueryFactory(), securedMapper.getQueryFactory(), query, parameters);
}
@Override
public FindOne findOne(String query, Object... parameters) {
return new SecuredFindOne(dbCollection, dbCollection.getReadPreference(), securedMapper.getUnmarshaller(),
mapper.getQueryFactory(), securedMapper.getQueryFactory(), query, parameters);
}
@Override
public FindOne findOne(ObjectId id) {
return new SecuredFindOne(dbCollection, dbCollection.getReadPreference(), securedMapper.getUnmarshaller(),
mapper.getQueryFactory(), securedMapper.getQueryFactory(), "{_id:#}", id);
}
};
}
};
}
}
/*
* Mostly copied from https://github.com/bguerout/jongo/blob/1.0/src/main/java/org/jongo/Find.java which has the
* following license :
*
* ----
* Copyright (C) 2011 Benoit GUEROUT <bguerout at gmail dot com> and Yves AMSELLEM <amsellem dot yves at gmail dot com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ----
*
* Only the constructor is modified to use different query factories for the query itself (secured) and other
* usages (unsecured).
*/
package org.jongo;
public class SecuredFind extends Find {
private final DBCollection collection;
private final ReadPreference readPreference;
private final Unmarshaller unmarshaller;
private final QueryFactory queryFactory;
private final Query query;
private Query fields, sort, hint;
private Integer limit, skip;
public SecuredFind(DBCollection collection, ReadPreference readPreference, Unmarshaller unmarshaller,
QueryFactory queryFactory, QueryFactory securedQueryFactory, String query, Object... parameters) {
// mandatory call to super, but base class is not actually used
super(collection, readPreference, unmarshaller, securedQueryFactory, query, parameters);
this.readPreference = readPreference;
this.unmarshaller = unmarshaller;
this.collection = collection;
this.queryFactory = queryFactory;
this.query = securedQueryFactory.createQuery(query, parameters);
}
public <T> Iterable<T> as(final Class<T> clazz) {
return map(newMapper(clazz, unmarshaller));
}
public <T> Iterable<T> map(ResultHandler<T> resultHandler) {
DBCursor cursor = new DBCursor(collection, query.toDBObject(), getFieldsAsDBObject(), readPreference);
addOptionsOn(cursor);
return new MongoIterator<T>(cursor, resultHandler);
}
private void addOptionsOn(DBCursor cursor) {
if (limit != null)
cursor.limit(limit);
if (skip != null)
cursor.skip(skip);
if (sort != null) {
cursor.sort(sort.toDBObject());
}
if (hint != null) {
cursor.hint(hint.toDBObject());
}
}
public Find projection(String fields) {
this.fields = queryFactory.createQuery(fields);
return this;
}
public Find projection(String fields, Object... parameters) {
this.fields = queryFactory.createQuery(fields, parameters);
return this;
}
public Find limit(int limit) {
this.limit = limit;
return this;
}
public Find skip(int skip) {
this.skip = skip;
return this;
}
public Find sort(String sort) {
this.sort = queryFactory.createQuery(sort);
return this;
}
public Find hint(final String hint) {
this.hint = queryFactory.createQuery(hint);
return this;
}
private DBObject getFieldsAsDBObject() {
return fields == null ? null : fields.toDBObject();
}
}
class SecuredQueryFactory implements QueryFactory {
private Marshaller marshaller;
private QueryFactory delegate;
@Override
public Query createQuery(String query, Object... parameters) {
Query q = delegate.createQuery(query, parameters);
RestxSession session = RestxSession.current();
if (session == null) { // session can be null, eg in queries made to load the session itself
return q;
}
Optional<? extends RestxPrincipal> principal = session.getPrincipal();
if (!principal.isPresent()) {
return q;
}
QueryBuilder securityBuilder = securityQueryBuilder(principal.get());
return new SecuredQuery(securityBuilder.and(q.toDBObject()).get());
}
private QueryBuilder securityQueryBuilder(RestxPrincipal principal) {
// application specific security logic
return QueryBuilder.start()
.put("role").is(principal.getRole()).get();
}
public void setMarshaller(Marshaller marshaller) {
this.marshaller = marshaller;
this.delegate = new BsonQueryFactory(marshaller);
}
public Marshaller getMarshaller() {
return marshaller;
}
private static class SecuredQuery implements Query {
private final DBObject dbo;
public SecuredQuery(DBObject dbo) {
this.dbo = dbo;
}
@Override
public DBObject toDBObject() {
return dbo;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment