001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.ugc;
029
030import org.opencms.json.JSONException;
031import org.opencms.json.JSONObject;
032import org.opencms.ugc.CmsUgcValueTransformerConfiguration.CmsUgcSingleValueTransformer.TransformType;
033
034import java.util.Collections;
035import java.util.HashMap;
036import java.util.Map;
037import java.util.Set;
038
039/**
040 * Wrapper for a value transformer configuration that is provided in content's schema file
041 * in the parameters section as follows:
042 *
043 * <pre>
044 * &lt;parameters&gt;
045 *    &lt;param name="ugc.values.transformer"&gt;
046 *    {
047 *        "Paragraph/Text": {
048 *            "transformer": "antisamy"
049 *        },
050 *        "Title": {
051 *            "transformer": "antisamy",
052 *            "config": {
053 *                "policy": "/system/modules/my.module/resources/ugc/antisamy-policy.xml"
054 *            }
055 *        },
056 *        "Author": {
057 *            "transformer": "none"
058 *        },
059 *        "Email": {
060 *            "transformer": "escape"
061 *        }
062 *    }
063 *    &lt;/param&gt;
064 * &lt;/parameter&gt;
065 * </pre>
066 *
067 * The configuration defines how values that are added via UGC at a certain path have to be transformed, primarily to not cause XSS or other issues.
068 */
069public class CmsUgcValueTransformerConfiguration {
070
071    /**
072     * The transformer for a single value.
073     */
074    public static class CmsUgcSingleValueTransformer {
075
076        /**
077         * The transformer type.
078         */
079        public static enum TransformType {
080            /** XML escape the value */
081            escape,
082            /** Clean the value via AntiSamy */
083            antisamy,
084            /** Leave the value unchanged. */
085            none
086        }
087
088        /** The default single value transformer. */
089        public static CmsUgcSingleValueTransformer DEFAULT = new CmsUgcSingleValueTransformer(TransformType.escape);
090        /** The mapping type. */
091        private TransformType m_type;
092        /** The (optional) additional configuration for the mapping type. */
093        private JSONObject m_config;
094
095        /**
096         * Creates a new mapping security value of the provided type.
097         * @param type the mapping type.
098         */
099        public CmsUgcSingleValueTransformer(TransformType type) {
100
101            m_type = type == null ? TransformType.escape : type;
102        }
103
104        /**
105         * Creates a new mapping security value of the provided type.
106         * @param type the mapping type.
107         * @param config the (optional) additional configuration for the mapping.
108         */
109        public CmsUgcSingleValueTransformer(TransformType type, JSONObject config) {
110
111            m_type = type;
112            m_config = TransformType.antisamy.equals(m_type) ? config : null;
113        }
114
115        /**
116         * @return the optional configuration for the mapping security.
117         */
118        public JSONObject getConfig() {
119
120            return m_config;
121        }
122
123        /**
124         * @return the type of the mapping security
125         */
126        public TransformType getType() {
127
128            return m_type;
129        }
130    }
131
132    /** JSON key in the transformer configuration. */
133    private static final String JSON_KEY_TRANSFORMER = "transformer";
134
135    /** JSON key in the transformer configuration. */
136    private static final String JSON_KEY_CONFIG = "config";
137
138    /** The default tranformer configuration. */
139    public static final CmsUgcValueTransformerConfiguration DEFAULT = new CmsUgcValueTransformerConfiguration();
140
141    /** The resource's transformer map with the explicitly specified transformers. */
142    private Map<String, CmsUgcSingleValueTransformer> m_transformerMap;
143
144    /**
145     * Wraps the provided transformer configuration
146     * @param config the configuration to wrap
147     * @throws JSONException thrown if the configuration is not a valid JSON
148     * @throws IllegalArgumentException thrown if the configuration values are not as expected
149     */
150    public CmsUgcValueTransformerConfiguration(String config)
151    throws JSONException, IllegalArgumentException {
152
153        if (config != null) {
154            JSONObject security = new JSONObject(config);
155            Set<String> paths = security.keySet();
156            Map<String, CmsUgcSingleValueTransformer> mappingSecurity = new HashMap<>(paths.size());
157            for (String path : paths) {
158                JSONObject pathConfig = security.getJSONObject(path);
159                String adjustment = pathConfig.getString(JSON_KEY_TRANSFORMER);
160                JSONObject mappingConfig = pathConfig.optJSONObject(JSON_KEY_CONFIG);
161                mappingSecurity.put(
162                    path,
163                    new CmsUgcSingleValueTransformer(TransformType.valueOf(adjustment), mappingConfig));
164            }
165            m_transformerMap = mappingSecurity;
166        } else {
167            m_transformerMap = Collections.emptyMap();
168        }
169    }
170
171    /**
172     * Constructor for the default configuration.
173     */
174    private CmsUgcValueTransformerConfiguration() {
175
176        m_transformerMap = Collections.emptyMap();
177    }
178
179    /**
180     * Returns the transformer for the provided XML path.
181     * @param path the XML path to get the value transformer for.
182     * @return the transformer for the provided XML path.
183     */
184    public CmsUgcSingleValueTransformer getTransformer(String path) {
185
186        return m_transformerMap.getOrDefault(path, CmsUgcSingleValueTransformer.DEFAULT);
187    }
188}