• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

openmrs / openmrs-core / 10268010242

06 Aug 2024 02:01PM CUT coverage: 64.952% (+0.04%) from 64.912%
10268010242

push

github

web-flow
TRUNK-6188 Upgrade xstream and migrate to whitelisting (#4377)

54 of 58 new or added lines in 2 files covered. (93.1%)

5 existing lines in 3 files now uncovered.

22960 of 35349 relevant lines covered (64.95%)

0.65 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.74
/api/src/main/java/org/openmrs/serialization/SimpleXStreamSerializer.java
1
/**
2
 * This Source Code Form is subject to the terms of the Mozilla Public License,
3
 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
4
 * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
5
 * the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
6
 *
7
 * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
8
 * graphic logo is a trademark of OpenMRS Inc.
9
 */
10
package org.openmrs.serialization;
11

12
import org.openmrs.ImplementationId;
13
import org.openmrs.Patient;
14

15
import com.thoughtworks.xstream.XStream;
16
import com.thoughtworks.xstream.XStreamException;
17
import com.thoughtworks.xstream.converters.MarshallingContext;
18
import com.thoughtworks.xstream.converters.UnmarshallingContext;
19
import com.thoughtworks.xstream.converters.extended.DynamicProxyConverter;
20
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
21
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
22
import org.openmrs.api.AdministrationService;
23
import org.openmrs.api.impl.AdministrationServiceImpl;
24
import org.slf4j.Logger;
25
import org.slf4j.LoggerFactory;
26
import org.springframework.beans.factory.annotation.Autowired;
27
import org.springframework.stereotype.Component;
28

29
import java.util.List;
30

31
/**
32
 * This serializer uses the xstream library to serialize and deserialize objects.
33
 * <br>
34
 * All classes are automatically aliased.  So a serialization of the {@link Patient} class
35
 * will not be:
36
 * <code>
37
 * &lt;org.openmrs.Patient ...&gt;
38
 *   &lt;element
39
 *   ...
40
 * &lt;/org.openmrs.Patient&gt;
41
 * </code>
42
 * but instead will be:
43
 * <code>
44
 * &lt;patient ...&gt;
45
 *   &lt;element
46
 *   ...
47
 * &lt;/patient&gt;
48
 * </code>
49
 *
50
 */
51
@Component("simpleXStreamSerializer")
52
public class SimpleXStreamSerializer implements OpenmrsSerializer {
53

54
        protected static final Logger log = LoggerFactory.getLogger(SimpleXStreamSerializer.class);
1✔
55
        
56
        private volatile XStream xstream;
57
        
58
        private final XStream customXStream;
59
        
60
        private AdministrationService adminService;
61
        
62
        /**
63
         * Default Constructor
64
         *
65
         * @throws SerializationException
66
         */
67
        public SimpleXStreamSerializer() throws SerializationException {
68
                this(null, null);
1✔
69
        }
1✔
70

71
        /**
72
         * Constructor that takes a custom XStream object. 
73
         * 
74
         * Please note that it is deprecated since it now requires AdministrationService to fully configure
75
         * whitelists defined via GPs.
76
         * 
77
         * @deprecated since 2.7.0, 2.6.2, 2.5.13 use SimpleXStreamSerializer(XStream, AdministrationService)
78
         * @param customXStream
79
         * @throws SerializationException
80
         */
81
        public SimpleXStreamSerializer(XStream customXStream) throws SerializationException {
NEW
82
                this(customXStream, null);
×
NEW
83
        }
×
84
        
85
        public SimpleXStreamSerializer(XStream customXStream, AdministrationService adminService) throws SerializationException {
1✔
86
                this.customXStream = customXStream;
1✔
87
                this.adminService = adminService;
1✔
88
        }
1✔
89
        
90
        @Autowired
91
        public SimpleXStreamSerializer(AdministrationService adminService) {
1✔
92
                this.customXStream = null;
1✔
93
                this.adminService = adminService;
1✔
94
        }
1✔
95

96
        /**
97
         * Setups XStream security using AdministrationService.getSerializerWhitelistTypes()
98
         * 
99
         * @since 2.7.0, 2.6.2, 2.5.13 
100
         * @param newXStream
101
         * @param adminService
102
         */
103
        public static void setupXStreamSecurity(XStream newXStream, AdministrationService adminService) {
104
                if (adminService != null) {
1✔
105
                        List<String> serializerWhitelistTypes = adminService.getSerializerWhitelistTypes();
1✔
106
                        int prefixLength = AdministrationService.GP_SERIALIZER_WHITELIST_HIERARCHY_TYPES_PREFIX.length();
1✔
107
                        for (String type: serializerWhitelistTypes) {
1✔
108
                                if (type.startsWith(AdministrationService.GP_SERIALIZER_WHITELIST_HIERARCHY_TYPES_PREFIX)) {
1✔
109
                                        try {
110
                                                Class<?> aClass = Class.forName(type.substring(prefixLength));
1✔
111
                                                newXStream.allowTypeHierarchy(aClass);
1✔
NEW
112
                                        } catch (ClassNotFoundException e) {
×
NEW
113
                                                log.warn("XStream serializer not configured to whitelist hierarchy of " + type, e);
×
114
                                        }
1✔
115
                                } else if (type.contains("*")) {
1✔
116
                                        newXStream.allowTypesByWildcard(new String[] {type});
1✔
117
                                } else {
118
                                        newXStream.allowTypes(new String[] {type});
1✔
119
                                }
120
                        }
1✔
121
                } else {
1✔
122
                        log.warn("XStream serializer not configured with whitelists defined in GPs suffixed " +
1✔
123
                                "with '.serializer.whitelist.types' due to adminService not being set.");
124
                        List<Class<?>> types = AdministrationServiceImpl.getSerializerDefaultWhitelistHierarchyTypes();
1✔
125
                        for (Class<?> type: types) {
1✔
126
                                newXStream.allowTypeHierarchy(type);
1✔
127
                        }
1✔
128
                }
129
        }
1✔
130

131
        /**
132
         * Setups permissions and default attributes.
133
         *
134
         * @since 2.7.0, 2.6.2, 2.5.13 
135
         * @param newXStream
136
         */
137
        public void initXStream(XStream newXStream) {
138
                setupXStreamSecurity(newXStream, adminService);
1✔
139
                
140
                newXStream.registerConverter(new OpenmrsDynamicProxyConverter(), XStream.PRIORITY_VERY_HIGH);
1✔
141

142
                //This is added to read the prior simpleframework-serialized values.
143
                //TODO: find a better way to do this.
144
                newXStream.useAttributeFor(ImplementationId.class, "implementationId");
1✔
145
        }
1✔
146
        
147
        /**
148
         * Expose the xstream object, so that module can config with xstream as need
149
         *
150
         * @return xstream can be configured by module
151
         */
152
        public XStream getXstream() {
153
                if (xstream == null) {
1✔
154
                        //Lazy-init so that GPs are completely populated for initXStream
155
                        XStream newXStream = customXStream;
1✔
156
                        if (newXStream == null) {        
1✔
157
                                newXStream = new XStream();
1✔
158
                        }
159
                        initXStream(newXStream);
1✔
160
                        xstream = newXStream;
1✔
161
                }
162
                return xstream;
1✔
163
        }
164
        
165
        /**
166
         * @see OpenmrsSerializer#serialize(java.lang.Object)
167
         * <strong>Should</strong> not serialize proxies
168
         */
169
        @Override
170
        public String serialize(Object o) throws SerializationException {
171
                return getXstream().toXML(o);
1✔
172
        }
173
        
174
        /**
175
         * @see OpenmrsSerializer#deserialize(String, Class)
176
         * <strong>Should</strong> not deserialize proxies
177
         * <strong>Should</strong> ignore entities
178
         * <strong>Should</strong> not deserialize classes that are not whitelisted
179
         * <strong>Should</strong> deserialize whitelisted packages
180
         * <strong>Should</strong> deserialize whitelisted classes and packages
181
         * <strong>Should</strong> deserialize whitelisted hierarchies
182
         */
183
        @Override
184
        @SuppressWarnings("unchecked")
185
        public <T> T deserialize(String serializedObject, Class<? extends T> clazz) throws SerializationException {
186
                try {
187
                        return (T) getXstream().fromXML(serializedObject);
1✔
188
                }
189
                catch (XStreamException e) {
1✔
190
                        throw new SerializationException("Unable to deserialize class: " + clazz.getName(), e);
1✔
191
                }
192
        }
193
        
194
        /**
195
         * An instance of this converter needs to be registered with a higher priority than the rest so
196
         * that it's called early in the converter chain. This way, we can make sure we never get to
197
         * xstream's DynamicProxyConverter that can deserialize proxies.
198
         *
199
         * @see <a href="http://tinyurl.com/ord2rry">this blog</a>
200
         */
201
        private class OpenmrsDynamicProxyConverter extends DynamicProxyConverter {
202
                
203
                OpenmrsDynamicProxyConverter() {
1✔
204
                        super(null);
1✔
205
                }
1✔
206
                
207
                @Override
208
                public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext context) {
209
                        throw new XStreamException("Can't serialize proxies");
1✔
210
                }
211
                
212
                @Override
213
                public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
UNCOV
214
                        throw new XStreamException("Can't deserialize proxies");
×
215
                }
216
                
217
        }
218
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc