/* * 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.attribute.ExchangeAttributes; import io.undertow.server.HttpServerExchange; import java.time.Instant; import java.util.HashMap; import java.util.Objects; import org.bson.BsonArray; import org.bson.BsonDateTime; import org.bson.BsonDocument; import org.bson.BsonNull; import org.bson.BsonString; import org.bson.BsonValue; import org.restheart.handlers.RequestContext; import org.restheart.utils.JsonUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * @author Andrea Di Cesare {@literal <andrea@softinstigate.com>} * * this transformer adds properties to the resource representation. usually it * is applied on REQUEST phase to store properties evaluated server side * * the properties to add are passed in the args argumenet * * ags can be an array of strings, each specifiying the names of the properties * to add or an object with a single property of an array of strings. in the * latter case, the properties are added to the representation nested in the * passed object property key. * * the properties that can be added are: userName, userRoles, dateTime, localIp, * localPort, localServerName, queryString, relativePath, remoteIp, * requestMethod, requestProtocol * * <br>for instance, with the following definition: * <br>{name:"addRequestProperties", "phase":"REQUEST", "scope":"CHILDREN", * args:{"log": ["userName", "remoteIp"]}} * <br>injected properties are: log: { userName:"andrea", remoteIp:"127.0.0.1"} * * <br>for instance, with the following definition: * <br>{name:"addRequestProperties", "phase":"REQUEST", "scope":"CHILDREN", * args:["userName", "remoteIp"]} * <br>injected properties are: userName:"andrea", remoteIp:"127.0.0.1" * */ public class RequestPropsInjecterTransformer implements Transformer { static final Logger LOGGER = LoggerFactory.getLogger(RequestPropsInjecterTransformer.class); /** * * @param exchange * @param context * @param contentToTransform * @param args properties to add */ @Override public void transform( final HttpServerExchange exchange, final RequestContext context, BsonValue contentToTransform, final BsonValue args) { if (contentToTransform == null) { // nothing to do return; } if (!contentToTransform.isDocument()) { throw new IllegalStateException( "content to transform is not a document"); } BsonDocument _contentToTransform = contentToTransform.asDocument(); BsonDocument injected = new BsonDocument(); if (args.isDocument()) { BsonDocument _args = args.asDocument(); HashMap<String, BsonValue> properties = getPropsValues(exchange, context); String firstKey = _args.keySet().iterator().next(); BsonValue _toinject = _args.get(firstKey); if (_toinject.isArray()) { BsonArray toinject = _toinject.asArray(); toinject.forEach(_el -> { if (_el.isString()) { String el = _el.asString().getValue(); BsonValue value = properties.get(el); if (value != null) { injected.put(el, value); } else { context.addWarning("property in the args list " + "does not have a value: " + _el); } } else { context.addWarning("property in the args list " + "is not a string: " + _el); } }); _contentToTransform.put(firstKey, injected); } else { context.addWarning("transformer wrong definition: " + "args must be an object with a array containing " + "the names of the properties to inject. got " + _args.toJson()); } } else if (args.isArray()) { HashMap<String, BsonValue> properties = getPropsValues(exchange, context); BsonArray toinject = args.asArray(); toinject.forEach(_el -> { if (_el.isString()) { String el = _el.asString().getValue(); BsonValue value = properties.get(el); if (value != null) { injected.put(el, value); } else { context.addWarning("property in the args list does not have a value: " + _el); } } else { context.addWarning("property in the args list is not a string: " + _el); } }); _contentToTransform.putAll(injected); } else { context.addWarning("transformer wrong definition: " + "args must be an object with a array containing " + "the names of the properties to inject. got " + JsonUtils.toJson(args)); } } HashMap<String, BsonValue> getPropsValues( final HttpServerExchange exchange, final RequestContext context) { HashMap<String, BsonValue> properties = new HashMap<>(); // remote user properties.put("userName", new BsonString( ExchangeAttributes .remoteUser() .readAttribute(exchange))); // user roles if (Objects.nonNull(exchange.getSecurityContext()) && Objects.nonNull( exchange.getSecurityContext() .getAuthenticatedAccount()) && Objects.nonNull(exchange .getSecurityContext() .getAuthenticatedAccount().getRoles())) { properties.put("userRoles", new BsonString( exchange .getSecurityContext() .getAuthenticatedAccount().getRoles().toString())); } else { properties.put("userRoles", new BsonNull()); } // dateTime properties.put("epochTimeStamp", new BsonDateTime(Instant.now().getEpochSecond() * 1000)); // dateTime properties.put("dateTime", new BsonString( ExchangeAttributes.dateTime().readAttribute(exchange))); // local ip properties.put("localIp", new BsonString( ExchangeAttributes.localIp().readAttribute(exchange))); // local port properties.put("localPort", new BsonString( ExchangeAttributes.localPort().readAttribute(exchange))); // local server name properties.put("localServerName", new BsonString( ExchangeAttributes.localServerName().readAttribute(exchange))); // request query string properties.put("queryString", new BsonString( ExchangeAttributes.queryString().readAttribute(exchange))); // request relative path properties.put("relativePath", new BsonString( ExchangeAttributes.relativePath().readAttribute(exchange))); // remote ip properties.put("remoteIp", new BsonString( ExchangeAttributes.remoteIp().readAttribute(exchange))); // request method properties.put("requestMethod", new BsonString( ExchangeAttributes.requestMethod().readAttribute(exchange))); // request protocol properties.put("requestProtocol", new BsonString( ExchangeAttributes.requestProtocol().readAttribute(exchange))); return properties; } }