Skip to content

Commit 491539e

Browse files
committed
[feature] Allow access to specific Java System Properties when using fn:available-system-properties#0 and fn:system-property#1 to be configured from conf.xml
1 parent fd906a8 commit 491539e

File tree

6 files changed

+1074
-5
lines changed

6 files changed

+1074
-5
lines changed

exist-core/pom.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,7 @@
705705
<exclude>src/test/java/org/exist/xquery/functions/session/AttributeTest.java</exclude>
706706
<exclude>src/test/java/org/exist/xquery/functions/xmldb/XMLDBAuthenticateTest.java</exclude>
707707
<exclude>src/main/java/org/exist/xquery/functions/util/Eval.java</exclude>
708+
<include>src/test/java/org/exist/xquery/functions/util/SystemPropertyTest.java</include>
708709
<exclude>src/test/java/org/exist/xquery/util/URIUtilsTest.java</exclude>
709710
<exclude>src/main/java/org/exist/xquery/value/ArrayListValueSequence.java</exclude>
710711
<exclude>src/test/java/org/exist/xquery/value/BifurcanMapTest.java</exclude>
@@ -855,6 +856,7 @@ The original license statement is also included below.]]></preamble>
855856
<include>src/test/java/org/exist/xquery/functions/session/AttributeTest.java</include>
856857
<include>src/test/java/org/exist/xquery/functions/xmldb/XMLDBAuthenticateTest.java</include>
857858
<include>src/main/java/org/exist/xquery/functions/util/Eval.java</include>
859+
<include>src/test/java/org/exist/xquery/functions/util/SystemPropertyTest.java</include>
858860
<include>src/test/java/org/exist/xquery/util/URIUtilsTest.java</include>
859861
<include>src/main/java/org/exist/xquery/value/ArrayListValueSequence.java</include>
860862
<include>src/test/java/org/exist/xquery/value/BifurcanMapTest.java</include>

exist-core/src/main/java/org/exist/xquery/functions/util/SystemProperty.java

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,16 @@
2121
*/
2222
package org.exist.xquery.functions.util;
2323

24+
import io.lacuna.bifurcan.IMap;
25+
import io.lacuna.bifurcan.ISet;
26+
import org.apache.logging.log4j.LogManager;
27+
import org.apache.logging.log4j.Logger;
2428
import org.exist.ExistSystemProperties;
2529
import org.exist.xquery.BasicFunction;
2630
import org.exist.xquery.FunctionSignature;
2731
import org.exist.xquery.XPathException;
2832
import org.exist.xquery.XQueryContext;
33+
import org.exist.xquery.functions.AccessUtil;
2934
import org.exist.xquery.value.Sequence;
3035
import org.exist.xquery.value.StringValue;
3136
import org.exist.xquery.value.Type;
@@ -42,9 +47,12 @@
4247
*
4348
* @author Wolfgang Meier
4449
* @author Loren Cahlander
50+
* @author <a href="mailto:[email protected]>Adam Retter</a>
4551
*/
4652
public class SystemProperty extends BasicFunction {
4753

54+
private static final Logger LOGGER = LogManager.getLogger(SystemProperty.class);
55+
4856
private final static String FS_AVAILABLE_SYSTEM_PROPERTIES_NAME = "available-system-properties";
4957
public final static FunctionSignature FS_AVAILABLE_SYSTEM_PROPERTIES = functionSignature(
5058
FS_AVAILABLE_SYSTEM_PROPERTIES_NAME,
@@ -70,11 +78,21 @@ public SystemProperty(final XQueryContext context, final FunctionSignature signa
7078

7179
@Override
7280
public Sequence eval(final Sequence[] args, final Sequence contextSequence) throws XPathException {
81+
final UtilModule utilModule = (UtilModule) getParentModule();
82+
final IMap<String, ISet<String>> systemPropertyAccessGroups = utilModule.getSystemPropertyAccessGroups();
83+
final IMap<String, ISet<String>> systemPropertyAccessUsers = utilModule.getSystemPropertyAccessUsers();
84+
7385
if (isCalledAs(FS_AVAILABLE_SYSTEM_PROPERTIES_NAME)) {
7486

7587
final Set<String> availableProperties = new HashSet<>();
7688
availableProperties.addAll(ExistSystemProperties.getInstance().getAvailableExistSystemProperties());
77-
availableProperties.addAll(context.getJavaSystemProperties().keys().toSet());
89+
90+
// add any Java System Properties that the user has access to
91+
for (final String systemPropertyName : context.getJavaSystemProperties().keys()) {
92+
if (AccessUtil.isAllowedAccess(context.getEffectiveUser(), systemPropertyAccessGroups, systemPropertyAccessUsers, systemPropertyName)) {
93+
availableProperties.add(systemPropertyName);
94+
}
95+
}
7896

7997
final ValueSequence result = new ValueSequence(availableProperties.size());
8098
for (final String availableProperty : availableProperties) {
@@ -84,11 +102,22 @@ public Sequence eval(final Sequence[] args, final Sequence contextSequence) thro
84102
return result;
85103

86104
} else {
87-
final String key = args[0].getStringValue();
88-
String value = ExistSystemProperties.getInstance().getExistSystemProperty(key, null);
105+
final String systemPropertyName = args[0].getStringValue();
106+
107+
// always allow access to all eXist-db System Properties
108+
String value = ExistSystemProperties.getInstance().getExistSystemProperty(systemPropertyName, null);
89109
if (value == null) {
90-
value = context.getJavaSystemProperties().get(key, null);
110+
111+
// check for a Java System Property that the user has access to
112+
if (!AccessUtil.isAllowedAccess(context.getEffectiveUser(), systemPropertyAccessGroups, systemPropertyAccessUsers, systemPropertyName)) {
113+
final String txt = "Permission denied, calling user '" + context.getSubject().getName() + "' must be granted access to the Java System Property: " + systemPropertyName + ".";
114+
LOGGER.error(txt);
115+
return Sequence.EMPTY_SEQUENCE;
116+
}
117+
118+
value = context.getJavaSystemProperties().get(systemPropertyName, null);
91119
}
120+
92121
return value == null ? Sequence.EMPTY_SEQUENCE : new StringValue(value);
93122
}
94123
}

exist-core/src/main/java/org/exist/xquery/functions/util/UtilModule.java

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,15 @@
2424
import java.util.Arrays;
2525
import java.util.List;
2626
import java.util.Map;
27+
import java.util.regex.Pattern;
2728

29+
import com.evolvedbinary.j8fu.tuple.Tuple2;
30+
import io.lacuna.bifurcan.IMap;
31+
import io.lacuna.bifurcan.ISet;
2832
import org.exist.dom.QName;
33+
import org.exist.util.PatternFactory;
2934
import org.exist.xquery.*;
35+
import org.exist.xquery.functions.AccessUtil;
3036
import org.exist.xquery.value.FunctionParameterSequenceType;
3137
import org.exist.xquery.value.FunctionReturnSequenceType;
3238

@@ -37,7 +43,7 @@
3743
* @author <a href="mailto:[email protected]">Wolfgang Meier</a>
3844
* @author ljo
3945
* @author <a href="mailto:[email protected]">Andrzej Taramina</a>
40-
* @author <a href="mailto:[email protected]">Adam retter</a>
46+
* @author <a href="mailto:[email protected]>Adam Retter</a>
4147
*/
4248
public class UtilModule extends AbstractInternalModule {
4349

@@ -166,6 +172,11 @@ public class UtilModule extends AbstractInternalModule {
166172

167173
public final static QName ERROR_CODE_QNAME = new QName("error-code", UtilModule.NAMESPACE_URI, UtilModule.PREFIX);
168174

175+
private static final Pattern PTN_SYSTEM_PROPERTY_ACCESS = PatternFactory.getInstance().getPattern("systemPropertyAccess\\.([^=\\00])+\\.requires((?:Group)|(?:User))");
176+
177+
private IMap<String, ISet<String>> systemPropertyAccessGroups = null;
178+
private IMap<String, ISet<String>> systemPropertyAccessUsers = null;
179+
169180
public UtilModule(final Map<String, List<? extends Object>> parameters) throws XPathException {
170181
super(functions, parameters, true);
171182

@@ -218,6 +229,34 @@ public void reset(final XQueryContext xqueryContext, final boolean keepGlobals)
218229
super.reset(xqueryContext, keepGlobals);
219230
}
220231

232+
/**
233+
* Get the system property names and groups that are allowed to access them.
234+
*
235+
* @return a map where the key is the system property name, and the value is a set of group names.
236+
*/
237+
IMap<String, ISet<String>> getSystemPropertyAccessGroups() {
238+
if (systemPropertyAccessGroups == null) {
239+
final Tuple2<IMap<String, ISet<String>>, IMap<String, ISet<String>>> accessRules = AccessUtil.parseAccessParameters(PTN_SYSTEM_PROPERTY_ACCESS, getParameters());
240+
this.systemPropertyAccessGroups = accessRules._1;
241+
this.systemPropertyAccessUsers = accessRules._2;
242+
}
243+
return systemPropertyAccessGroups;
244+
}
245+
246+
/**
247+
* Get the system property names and users that are allowed to access them.
248+
*
249+
* @return a map where the key is the system property name, and the value is a set of usernames.
250+
*/
251+
IMap<String, ISet<String>> getSystemPropertyAccessUsers() {
252+
if (systemPropertyAccessUsers == null) {
253+
final Tuple2<IMap<String, ISet<String>>, IMap<String, ISet<String>>> accessRules = AccessUtil.parseAccessParameters(PTN_SYSTEM_PROPERTY_ACCESS, getParameters());
254+
this.systemPropertyAccessGroups = accessRules._1;
255+
this.systemPropertyAccessUsers = accessRules._2;
256+
}
257+
return systemPropertyAccessUsers;
258+
}
259+
221260
static FunctionSignature functionSignature(final String name, final String description, final FunctionReturnSequenceType returnType, final FunctionParameterSequenceType... paramTypes) {
222261
return FunctionDSL.functionSignature(new QName(name, NAMESPACE_URI, PREFIX), description, returnType, paramTypes);
223262
}
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
/*
2+
* Copyright (C) 2014, Evolved Binary Ltd
3+
*
4+
* This file was originally ported from FusionDB to eXist-db by
5+
* Evolved Binary, for the benefit of the eXist-db Open Source community.
6+
* Only the ported code as it appears in this file, at the time that
7+
* it was contributed to eXist-db, was re-licensed under The GNU
8+
* Lesser General Public License v2.1 only for use in eXist-db.
9+
*
10+
* This license grant applies only to a snapshot of the code as it
11+
* appeared when ported, it does not offer or infer any rights to either
12+
* updates of this source code or access to the original source code.
13+
*
14+
* The GNU Lesser General Public License v2.1 only license follows.
15+
*
16+
* ---------------------------------------------------------------------
17+
*
18+
* Copyright (C) 2014, Evolved Binary Ltd
19+
*
20+
* This library is free software; you can redistribute it and/or
21+
* modify it under the terms of the GNU Lesser General Public
22+
* License as published by the Free Software Foundation; version 2.1.
23+
*
24+
* This library is distributed in the hope that it will be useful,
25+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
26+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27+
* Lesser General Public License for more details.
28+
*
29+
* You should have received a copy of the GNU Lesser General Public
30+
* License along with this library; if not, write to the Free Software
31+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
32+
*/
33+
package org.exist.xquery.functions.util;
34+
35+
import org.exist.EXistException;
36+
import org.exist.ExistSystemProperties;
37+
import org.exist.security.PermissionDeniedException;
38+
import org.exist.storage.BrokerPool;
39+
import org.exist.storage.DBBroker;
40+
import org.exist.test.ExistEmbeddedServer;
41+
import org.exist.util.DatabaseConfigurationException;
42+
import org.exist.xquery.XPathException;
43+
import org.exist.xquery.XQuery;
44+
import org.exist.xquery.value.Sequence;
45+
import org.junit.After;
46+
import org.junit.Before;
47+
import org.junit.Test;
48+
import org.junit.runner.RunWith;
49+
import org.junit.runners.Parameterized;
50+
51+
import java.io.IOException;
52+
import java.net.URISyntaxException;
53+
import java.nio.file.Path;
54+
import java.nio.file.Paths;
55+
import java.util.Arrays;
56+
import java.util.HashSet;
57+
import java.util.Optional;
58+
import java.util.Set;
59+
60+
import static org.junit.Assert.*;
61+
62+
@RunWith(Parameterized.class)
63+
public class SystemPropertyTest {
64+
65+
@Parameterized.Parameters(name = "{0}")
66+
public static java.util.Collection<Object[]> data() {
67+
return Arrays.asList(new Object[][] {
68+
{ "non-secure", null, false },
69+
{ "secure", "conf-sys-props-admins-only.xml", true }
70+
});
71+
}
72+
73+
@Parameterized.Parameter(value = 0)
74+
public String testTypeName;
75+
76+
@Parameterized.Parameter(value = 1)
77+
public String confFileName;
78+
79+
@Parameterized.Parameter(value = 2)
80+
public boolean shouldReturnEmptySequence;
81+
82+
private ExistEmbeddedServer existEmbeddedServer = null;
83+
84+
@Before
85+
public void setup() throws URISyntaxException, DatabaseConfigurationException, EXistException, IOException {
86+
if (confFileName == null) {
87+
existEmbeddedServer = new ExistEmbeddedServer(true, true);
88+
} else {
89+
final Path confFile = Paths.get(getClass().getResource(confFileName).toURI());
90+
existEmbeddedServer = new ExistEmbeddedServer(null, confFile, null, true, true);
91+
}
92+
existEmbeddedServer.startDb();
93+
}
94+
95+
@After
96+
public void teardown() {
97+
if (existEmbeddedServer != null) {
98+
existEmbeddedServer.stopDb();
99+
}
100+
existEmbeddedServer = null;
101+
}
102+
103+
@Test
104+
public void availableSystemProperties() throws EXistException, XPathException, PermissionDeniedException {
105+
final BrokerPool pool = existEmbeddedServer.getBrokerPool();
106+
final XQuery xqueryService = pool.getXQueryService();
107+
try (final DBBroker broker = pool.get(Optional.of(pool.getSecurityManager().getSystemSubject()))) {
108+
109+
final String query = "util:available-system-properties()";
110+
final Sequence result = xqueryService.execute(broker, query, null);
111+
assertFalse(result.isEmpty());
112+
113+
final Set<String> set = new HashSet<>(result.getItemCount());
114+
for (int i = 0; i < result.getItemCount(); i++) {
115+
set.add(result.itemAt(i).getStringValue());
116+
}
117+
118+
assertTrue(set.contains(ExistSystemProperties.PROP_PRODUCT_VERSION));
119+
120+
if (shouldReturnEmptySequence) {
121+
assertFalse(set.contains("os.name"));
122+
} else {
123+
assertTrue(set.contains("os.name"));
124+
}
125+
}
126+
}
127+
128+
@Test
129+
public void systemProperty() throws EXistException, XPathException, PermissionDeniedException {
130+
final BrokerPool pool = existEmbeddedServer.getBrokerPool();
131+
final XQuery xqueryService = pool.getXQueryService();
132+
try (final DBBroker broker = pool.get(Optional.of(pool.getSecurityManager().getSystemSubject()))) {
133+
134+
String query = "util:system-property('" + ExistSystemProperties.PROP_PRODUCT_NAME + "')";
135+
Sequence result = xqueryService.execute(broker, query, null);
136+
assertEquals(1, result.getItemCount());
137+
138+
query = "util:system-property('os.name')";
139+
result = xqueryService.execute(broker, query, null);
140+
141+
if (shouldReturnEmptySequence) {
142+
assertTrue(result.isEmpty());
143+
} else {
144+
assertFalse(result.isEmpty());
145+
}
146+
}
147+
}
148+
}

0 commit comments

Comments
 (0)