/* * RESTHeart - the Web API for MongoDB * Copyright (C) SoftInstigate Srl * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.restheart.metadata.transformers; import io.undertow.server.HttpServerExchange; import org.bson.BsonArray; import org.bson.BsonDocument; import org.bson.BsonString; import org.bson.BsonValue; import org.mindrot.jbcrypt.BCrypt; import org.restheart.handlers.RequestContext; import org.restheart.handlers.RequestContext.TYPE; /** * * @author Andrea Di Cesare {@literal <andrea@softinstigate.com>} * * This transformer hashes the properties from the resource representation on * write requests using the bcrypt hash function the properties to hash out are * passed in the args argument as an array of strings. the bcrypt * * <br>Example that hashes the property 'password' from the response: * <br>{ rts: [{"name": "hashProperties", "phase":"REQUEST", "scope":"CHILDREN", * "args": { "props": ["password"], "complexity": 12 }}] } * */ public class HashTransformer implements Transformer { /** * * @param exchange * @param context * @param contentToTransform * @param args properties to filter out as an array of strings (["prop1", * "prop2"] */ @Override public void transform( final HttpServerExchange exchange, final RequestContext context, BsonValue contentToTransform, final BsonValue args) { if (!doesApply(context) || contentToTransform == null) { // nothing to do return; } if (context.getType() == TYPE.DOCUMENT && (context.getMethod() == RequestContext.METHOD.PATCH || context.getMethod() == RequestContext.METHOD.PUT)) { } if (!contentToTransform.isDocument()) { throw new IllegalStateException( "content to transform is not a document"); } BsonDocument _contentToTransform = contentToTransform.asDocument(); if (args.isDocument()) { BsonValue _tohash = args.asDocument().get("props"); if (_tohash == null || !_tohash.isArray()) { context.addWarning("transformer wrong definition: " + "args must be an object as {'props': [ 'password' ], " + "'complexity': 12 }"); } BsonArray tohash = _tohash.asArray(); BsonValue _complexity = args.asDocument().get("complexity"); if (_complexity != null && !_complexity.isNumber()) { context.addWarning("transformer wrong definition: " + "args must be an object as {'props': [ 'password' ], " + "'complexity': 12 }"); } int complexity = _complexity == null ? 12 : _complexity.asNumber().intValue(); tohash.forEach(_prop -> { if (_prop.isString()) { String prop = (String) _prop.asString().getValue(); BsonValue _value = _contentToTransform.get(prop); if (_value != null && _value.isString()) { String value = _value.asString().getValue(); _contentToTransform.replace(prop, new BsonString( BCrypt.hashpw(value, BCrypt.gensalt(complexity)))); } } else { context.addWarning("property in the args list " + "is not a string: " + _prop); } }); } else { context.addWarning("transformer wrong definition: " + "args must be an object as {'props': [ 'password'], " + "'complexity': 12 }"); } } private boolean doesApply(RequestContext context) { return ( // PUT|PATCH /db/coll/docid (context.getType() == TYPE.DOCUMENT && (context.getMethod() == RequestContext.METHOD.PATCH || context.getMethod() == RequestContext.METHOD.PUT)) // POST /db/coll { <doc> } and POST /db/coll [ { <doc> }, { <doc> } ] || ((context.getType() == TYPE.COLLECTION) && (context.getMethod() == RequestContext.METHOD.POST)) // PATCH /db/coll/* || ((context.getType() == TYPE.BULK_DOCUMENTS) && context.getMethod() == RequestContext.METHOD.PATCH)); } }