1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * https://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20 package org.apache.commons.io.serialization;
21
22 import java.io.ObjectStreamClass;
23 import java.util.ArrayList;
24 import java.util.List;
25 import java.util.function.Predicate;
26 import java.util.regex.Pattern;
27 import java.util.stream.Stream;
28
29 /**
30 * A predicate (boolean-valued function) of one argument to accept and reject classes.
31 * <p>
32 * The reject list takes precedence over the accept list.
33 * </p>
34 *
35 * @since 2.18.0
36 */
37 public class ObjectStreamClassPredicate implements Predicate<ObjectStreamClass> {
38
39 // This is not a Set for now to avoid ClassNameMatchers requiring proper implementations of hashCode() and equals().
40 private final List<ClassNameMatcher> acceptMatchers = new ArrayList<>();
41
42 // This is not a Set for now to avoid ClassNameMatchers requiring proper implementations of hashCode() and equals().
43 private final List<ClassNameMatcher> rejectMatchers = new ArrayList<>();
44
45 /**
46 * Constructs a new instance.
47 */
48 public ObjectStreamClassPredicate() {
49 // empty
50 }
51
52 /**
53 * Accepts the specified classes for deserialization, unless they are otherwise rejected.
54 * <p>
55 * The reject list takes precedence over the accept list.
56 * </p>
57 *
58 * @param classes Classes to accept
59 * @return this object
60 */
61 public ObjectStreamClassPredicate accept(final Class<?>... classes) {
62 Stream.of(classes).map(c -> new FullClassNameMatcher(c.getName())).forEach(acceptMatchers::add);
63 return this;
64 }
65
66 /**
67 * Accepts class names where the supplied ClassNameMatcher matches for deserialization, unless they are otherwise rejected.
68 * <p>
69 * The reject list takes precedence over the accept list.
70 * </p>
71 *
72 * @param matcher a class name matcher to <em>accept</em> objects.
73 * @return this instance.
74 */
75 public ObjectStreamClassPredicate accept(final ClassNameMatcher matcher) {
76 acceptMatchers.add(matcher);
77 return this;
78 }
79
80 /**
81 * Accepts class names that match the supplied pattern for deserialization, unless they are otherwise rejected.
82 * <p>
83 * The reject list takes precedence over the accept list.
84 * </p>
85 *
86 * @param pattern a Pattern for compiled regular expression.
87 * @return this instance.
88 */
89 public ObjectStreamClassPredicate accept(final Pattern pattern) {
90 acceptMatchers.add(new RegexpClassNameMatcher(pattern));
91 return this;
92 }
93
94 /**
95 * Accepts the wildcard specified classes for deserialization, unless they are otherwise rejected.
96 * <p>
97 * The reject list takes precedence over the accept list.
98 * </p>
99 *
100 * @param patterns Wildcard file name patterns as defined by {@link org.apache.commons.io.FilenameUtils#wildcardMatch(String, String)
101 * FilenameUtils.wildcardMatch}
102 * @return this instance.
103 */
104 public ObjectStreamClassPredicate accept(final String... patterns) {
105 Stream.of(patterns).map(WildcardClassNameMatcher::new).forEach(acceptMatchers::add);
106 return this;
107 }
108
109 /**
110 * Rejects the specified classes for deserialization, even if they are otherwise accepted.
111 * <p>
112 * The reject list takes precedence over the accept list.
113 * </p>
114 *
115 * @param classes Classes to reject
116 * @return this instance.
117 */
118 public ObjectStreamClassPredicate reject(final Class<?>... classes) {
119 Stream.of(classes).map(c -> new FullClassNameMatcher(c.getName())).forEach(rejectMatchers::add);
120 return this;
121 }
122
123 /**
124 * Rejects class names where the supplied ClassNameMatcher matches for deserialization, even if they are otherwise accepted.
125 * <p>
126 * The reject list takes precedence over the accept list.
127 * </p>
128 *
129 * @param m the matcher to use
130 * @return this instance.
131 */
132 public ObjectStreamClassPredicate reject(final ClassNameMatcher m) {
133 rejectMatchers.add(m);
134 return this;
135 }
136
137 /**
138 * Rejects class names that match the supplied pattern for deserialization, even if they are otherwise accepted.
139 * <p>
140 * The reject list takes precedence over the accept list.
141 * </p>
142 *
143 * @param pattern standard Java regexp
144 * @return this instance.
145 */
146 public ObjectStreamClassPredicate reject(final Pattern pattern) {
147 rejectMatchers.add(new RegexpClassNameMatcher(pattern));
148 return this;
149 }
150
151 /**
152 * Rejects the wildcard specified classes for deserialization, even if they are otherwise accepted.
153 * <p>
154 * The reject list takes precedence over the accept list.
155 * </p>
156 *
157 * @param patterns Wildcard file name patterns as defined by {@link org.apache.commons.io.FilenameUtils#wildcardMatch(String, String)
158 * FilenameUtils.wildcardMatch}
159 * @return this instance.
160 */
161 public ObjectStreamClassPredicate reject(final String... patterns) {
162 Stream.of(patterns).map(WildcardClassNameMatcher::new).forEach(rejectMatchers::add);
163 return this;
164 }
165
166 /**
167 * Tests that the ObjectStreamClass conforms to requirements.
168 * <p>
169 * The reject list takes precedence over the accept list.
170 * </p>
171 *
172 * @param objectStreamClass The ObjectStreamClass to test.
173 * @return true if the input is accepted, false if rejected, false if neither.
174 */
175 @Override
176 public boolean test(final ObjectStreamClass objectStreamClass) {
177 return test(objectStreamClass.getName());
178 }
179
180 /**
181 * Tests that the class name conforms to requirements.
182 * <p>
183 * The reject list takes precedence over the accept list.
184 * </p>
185 *
186 * @param name The class name to test.
187 * @return true if the input is accepted, false if rejected, false if neither.
188 */
189 public boolean test(final String name) {
190 // The reject list takes precedence over the accept list.
191 for (final ClassNameMatcher m : rejectMatchers) {
192 if (m.matches(name)) {
193 return false;
194 }
195 }
196 for (final ClassNameMatcher m : acceptMatchers) {
197 if (m.matches(name)) {
198 return true;
199 }
200 }
201 return false;
202 }
203
204 }