├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── README.md ├── pom.xml ├── src ├── main │ ├── assemblies │ │ └── plugin.xml │ ├── java │ │ └── org │ │ │ └── elasticsearch │ │ │ └── plugins │ │ │ └── security │ │ │ ├── MalformedConfigurationException.java │ │ │ ├── SecurityPlugin.java │ │ │ ├── filter │ │ │ ├── ActionPathFilter.java │ │ │ ├── DlsWriteFilter.java │ │ │ ├── PermDlsEvaluator.java │ │ │ ├── PermLevel.java │ │ │ ├── PermLevelEvaluator.java │ │ │ └── SecureRestFilter.java │ │ │ ├── http │ │ │ ├── realm │ │ │ │ ├── AllPermsRealm.java │ │ │ │ └── LdapTlsContextFactory.java │ │ │ └── tomcat │ │ │ │ ├── ExtendedJndiRealm.java │ │ │ │ ├── ExtendedSpnegoAuthenticator.java │ │ │ │ ├── ExtendedTomcat.java │ │ │ │ ├── TomcatHttpServerRestChannel.java │ │ │ │ ├── TomcatHttpServerRestRequest.java │ │ │ │ ├── TomcatHttpServerTransport.java │ │ │ │ ├── TomcatHttpServerTransportModule.java │ │ │ │ ├── TomcatHttpTransportHandlerServlet.java │ │ │ │ └── TomcatUserRoleCallback.java │ │ │ ├── service │ │ │ ├── SecurityService.java │ │ │ └── permission │ │ │ │ ├── DlsPermission.java │ │ │ │ ├── PermEvaluator.java │ │ │ │ └── UserRoleCallback.java │ │ │ └── util │ │ │ ├── EditableRestRequest.java │ │ │ └── SecurityUtil.java │ └── resources │ │ └── es-plugin.properties └── test │ ├── java │ └── org │ │ └── elasticsearch │ │ └── plugins │ │ └── security │ │ ├── AbstractUnitTest.java │ │ ├── SpnegoAdTests.java │ │ ├── SpnegoAdTestsAlternateLdapUrl.java │ │ ├── SpnegoTests.java │ │ ├── SpnegoWaffleTests.java │ │ ├── SslSpnegoAdTests.java │ │ ├── filter │ │ ├── AbstractPermTests.java │ │ ├── DlsTests.java │ │ └── SecurityPermTests.java │ │ └── waffle │ │ └── TestProvider.java │ └── resources │ ├── dls_allow_emptyarray.json │ ├── dls_default_test_allowall.json │ ├── dls_default_test_denyall.json │ ├── dls_denyall_emptyarray.json │ ├── dls_dummy_content.json │ ├── dls_dummy_content_updt.json │ ├── dls_dummy_content_without_dls.json │ ├── dls_field_query.json │ ├── dls_test_normal.json │ ├── dummy_content.json │ ├── hnelsonclient.p12 │ ├── issues │ ├── dls1 │ │ ├── default.json │ │ └── rules.json │ └── dls2 │ │ ├── data.json │ │ ├── default.json │ │ ├── expected.json │ │ └── rules.json │ ├── krb5.conf │ ├── krb5_utest.keytab │ ├── localhost_tc.p12 │ ├── log4j.properties │ ├── login.conf │ ├── non_field_query.json │ ├── test_bad_format.json │ ├── test_default.json │ ├── test_denyall.json │ ├── test_denyall_emptyarray.json │ ├── test_facet_search.json │ ├── test_malformed_structure.json │ ├── test_multiple_wildcard_indices.json │ ├── test_no_default.json │ ├── test_normal.json │ ├── test_normal_fqn.json │ ├── test_normal_indices.json │ ├── test_normal_withuserroletypes.json │ ├── test_wildcard.json │ ├── test_wildcard_fqn.json │ ├── test_wildcard_indices.json │ ├── test_wildcard_indices2.json │ ├── truststore.jks │ ├── ur_test_all.json │ ├── ur_test_duplicate.json │ └── ur_test_normal.json └── travisscripts └── travis-before-install.sh /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | test-output/ 3 | 4 | /build.gradle 5 | *.log 6 | .externalToolBuilders 7 | maven-eclipse.xml 8 | 9 | ## eclipse ignores (use 'mvn eclipse:eclipse' to build eclipse projects) 10 | ## The only configuration files which are not ignored are certain files in 11 | ## .settings (as listed below) since these files ensure common coding 12 | ## style across Eclipse and IDEA. 13 | ## Other files (.project, .classpath) should be generated through Maven which 14 | ## will correctly set the classpath based on the declared dependencies. 15 | .project 16 | .classpath 17 | eclipse-build 18 | */.project 19 | */.classpath 20 | */eclipse-build 21 | /.settings/ 22 | !/.settings/org.eclipse.core.resources.prefs 23 | !/.settings/org.eclipse.jdt.core.prefs 24 | !/.settings/org.eclipse.jdt.ui.prefs 25 | !/.settings/org.eclipse.jdt.groovy.core.prefs 26 | bin 27 | tomcat.* 28 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | jdk: 4 | - openjdk7 5 | - oraclejdk7 6 | 7 | before_install: 8 | - sudo chmod -R 777 ./travisscripts/travis-before-install.sh 9 | - sudo ./travisscripts/travis-before-install.sh 10 | 11 | install: mvn clean install -Dgpg.skip=true 12 | 13 | notifications: 14 | email: 15 | recipients: 16 | - hendrikdev22@gmail.com 17 | on_success: always 18 | on_failure: always 19 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2013-2014 Hendrik Saly 2 | 3 | Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) 4 | on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or 5 | conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the 6 | appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 7 | 8 | Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless 9 | required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to 10 | You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of 11 | this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, 12 | computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the 13 | possibility of such damages. 14 | 15 | Licensed under the "No License" license (github default license): 16 | http://choosealicense.com/licenses/no-license/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # News/Status 2 | This plugin is no longer maintained, if you're looking for security for elasticsearch i recommend 3 | 4 | * [Search Guard](https://github.com/floragunncom/search-guard) - Free (and open source) plugin from [floragunn GmbH](https://floragunn.com/searchguard/), supports Elasticsearch 5 as well as Elasticsearch 6 5 | 6 | ## elasticsearch-security-plugin (Unmaintained) 7 | ### This plugin is to be considered as insecure, do not use it because its unmaintained 8 | ![Unmaintained](http://upload.wikimedia.org/wikipedia/en/thumb/5/57/Circle-style-warning.svg/200px-Circle-style-warning.svg.png) 9 | 10 | Documentation removed. 11 | 12 |

License

13 | Copyright 2013-2014 Hendrik Saly 14 | 15 | Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) 16 | on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or 17 | conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the 18 | appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 19 | 20 | Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless 21 | required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to 22 | You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of 23 | this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, 24 | computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the 25 | possibility of such damages. 26 | 27 | Licensed under the "No License" license (github default): 28 | http://choosealicense.com/licenses/no-license/ 29 | 30 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | 3.1.0 7 | 8 | 9 | com.github.salyh.elasticsearch 10 | elasticsearch-security-plugin 11 | 0.0.2.Beta9-ea1.4 12 | jar 13 | 14 | elasticsearch-security-plugin 15 | Provide security related features for elasticsearch 16 | https://github.com/salyh/elasticsearch-security-plugin 17 | 2013 18 | 19 | 20 | 21 | No License license - Copyright 2013-2014 Hendrik Saly 22 | http://choosealicense.com/licenses/no-license 23 | manual 24 | Copyright 2013-2014 Hendrik Saly 25 | 26 | 27 | 28 | 29 | 30 | bintray-salyh-maven-elasticsearch-river-imap 31 | salyh-maven-elasticsearch-river-imap 32 | https://api.bintray.com/maven/salyh/maven/elasticsearch-security-plugin 33 | 34 | 35 | 36 | 37 | 1.4.1 38 | 7.0.54 39 | 2.0.0-M16 40 | \\ 41 | 42 | 43 | 44 | https://github.com/salyh/elasticsearch-security-plugin 45 | scm:git:git@github.com:salyh/elasticsearch-security-plugin.git 46 | scm:git:git@github.com:salyh/elasticsearch-security-plugin.git 47 | 48 | 49 | 50 | GitHub 51 | https://github.com/salyh/elasticsearch-security-plugin/issues 52 | 53 | 54 | 55 | 56 | hendrikdev22@gmail.com 57 | Hendrik Saly 58 | https://github.com/salyh 59 | salyh 60 | 61 | 62 | 63 | 64 | 65 | Ram Kotamaraja 66 | https://github.com/rkotamaraja 67 | 68 | 69 | 70 | 71 | 72 | org.sonatype.oss 73 | oss-parent 74 | 7 75 | 76 | 77 | 78 | 79 | 80 | junit 81 | junit 82 | 4.11 83 | test 84 | 85 | 86 | 87 | commons-io 88 | commons-io 89 | 2.4 90 | test 91 | 92 | 93 | 94 | io.searchbox 95 | jest 96 | 0.1.2 97 | test 98 | 99 | 100 | 101 | com.github.tlrx 102 | elasticsearch-test 103 | 1.2.1 104 | test 105 | 106 | 107 | 108 | org.elasticsearch 109 | elasticsearch 110 | ${elasticsearch.version} 111 | 112 | 113 | 114 | log4j 115 | log4j 116 | 1.2.17 117 | 118 | 119 | 120 | 121 | org.slf4j 122 | slf4j-api 123 | 1.7.7 124 | test 125 | true 126 | 127 | 128 | 129 | org.slf4j 130 | slf4j-log4j12 131 | 1.7.7 132 | test 133 | true 134 | 135 | 136 | 137 | org.apache.httpcomponents 138 | httpclient 139 | 4.3.2 140 | test 141 | 142 | 143 | 144 | org.apache.httpcomponents 145 | fluent-hc 146 | 4.3.2 147 | test 148 | 149 | 150 | 151 | org.apache.httpcomponents 152 | httpcore 153 | 4.3.2 154 | test 155 | 156 | 157 | 158 | org.apache.tomcat.embed 159 | tomcat-embed-core 160 | ${tomcat.version} 161 | 162 | 163 | 164 | org.apache.tomcat.embed 165 | tomcat-embed-logging-log4j 166 | ${tomcat.version} 167 | 168 | 169 | 170 | org.apache.tomcat 171 | tomcat-jsp-api 172 | ${tomcat.version} 173 | 174 | 175 | 176 | com.github.dblock.waffle 177 | waffle-tomcat7 178 | 1.6 179 | 180 | 181 | 182 | com.github.dblock.waffle 183 | waffle-tests 184 | 1.6 185 | 186 | 187 | 188 | 189 | com.jayway.jsonpath 190 | json-path 191 | 0.9.1 192 | 193 | 194 | 195 | com.jayway.jsonpath 196 | json-path-assert 197 | 0.9.1 198 | test 199 | 200 | 201 | 202 | net.sourceforge.spnego 203 | spnego 204 | 7.0 205 | test 206 | 207 | 208 | 209 | 210 | 211 | org.apache.directory.server 212 | apacheds-all 213 | ${apache.ds.version} 214 | 215 | 217 | 218 | org.apache.directory.shared 219 | shared-ldap-schema 220 | 221 | 222 | org.apache.directory.api 223 | api-ldap-schema-data 224 | 225 | 226 | 227 | 228 | 229 | org.apache.directory.server 230 | kerberos-client 231 | ${apache.ds.version} 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | ${basedir}/src/test/java 241 | 242 | **/*.json 243 | **/*.yml 244 | 245 | 246 | 247 | ${basedir}/src/test/resources 248 | false 249 | 250 | **/*.* 251 | 252 | 253 | login.conf 254 | 255 | 256 | 257 | 258 | ${basedir}/src/test/resources 259 | true 260 | 261 | login.conf 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | org.apache.maven.plugins 272 | maven-enforcer-plugin 273 | 1.3.1 274 | 275 | 276 | enforce-java 277 | 278 | enforce 279 | 280 | 281 | 282 | 283 | 1.7 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | org.apache.maven.plugins 292 | maven-jar-plugin 293 | 2.5 294 | 295 | true 296 | 297 | 298 | 299 | true 300 | true 301 | 302 | 303 | 304 | 305 | 306 | org.apache.maven.plugins 307 | maven-compiler-plugin 308 | 3.1 309 | 310 | 1.7 311 | 1.7 312 | 313 | 314 | 315 | org.apache.maven.plugins 316 | maven-surefire-plugin 317 | 2.17 318 | 319 | 320 | **/*Test*.java 321 | 322 | 323 | 324 | 325 | org.apache.maven.plugins 326 | maven-source-plugin 327 | 2.3 328 | 329 | 330 | attach-sources 331 | 332 | jar 333 | 334 | 335 | 336 | 337 | 338 | maven-assembly-plugin 339 | 2.4 340 | 341 | false 342 | ${project.build.directory}/releases/ 343 | 344 | ${basedir}/src/main/assemblies/plugin.xml 345 | 346 | 347 | 348 | 349 | package 350 | 351 | single 352 | 353 | 354 | 355 | 356 | 357 | org.apache.maven.plugins 358 | maven-gpg-plugin 359 | 1.5 360 | 361 | 362 | sign-artifacts 363 | verify 364 | 365 | sign 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | sonatype-releases 376 | Sonatype Releases Repository 377 | http://oss.sonatype.org/content/repositories/releases 378 | 379 | 380 | 381 | 382 | jspresso 383 | http://repository.jspresso.org/maven2/ 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | unix 392 | 393 | 394 | unix 395 | 396 | 397 | 398 | 399 | 400 | org.apache.maven.plugins 401 | maven-surefire-plugin 402 | 403 | 404 | **/*affle*.java 405 | 406 | 407 | 408 | 409 | 410 | 411 | / 412 | 413 | 414 | 415 | 416 | -------------------------------------------------------------------------------- /src/main/assemblies/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | plugin 4 | 5 | zip 6 | 7 | false 8 | 9 | 10 | / 11 | true 12 | true 13 | 14 | org.elasticsearch:elasticsearch 15 | 16 | 17 | 18 | 19 | 20 | org.elasticsearch:elasticsearch-security-plugin 21 | 22 | 23 | 24 | 25 | 26 | _site 27 | src/site 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/plugins/security/MalformedConfigurationException.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.plugins.security; 2 | 3 | public class MalformedConfigurationException extends Exception { 4 | 5 | private static final long serialVersionUID = 1L; 6 | 7 | public MalformedConfigurationException(final String message) { 8 | super(message); 9 | 10 | } 11 | 12 | public MalformedConfigurationException(final Throwable cause) { 13 | super(cause); 14 | 15 | } 16 | 17 | public MalformedConfigurationException(final String message, 18 | final Throwable cause) { 19 | super(message, cause); 20 | 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/plugins/security/SecurityPlugin.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.plugins.security; 2 | 3 | import java.util.Collection; 4 | 5 | import org.elasticsearch.common.collect.Lists; 6 | import org.elasticsearch.common.component.LifecycleComponent; 7 | import org.elasticsearch.common.logging.ESLogger; 8 | import org.elasticsearch.common.logging.Loggers; 9 | import org.elasticsearch.plugins.AbstractPlugin; 10 | import org.elasticsearch.plugins.security.service.SecurityService; 11 | 12 | public class SecurityPlugin extends AbstractPlugin { 13 | 14 | private final ESLogger log = Loggers.getLogger(this.getClass()); 15 | 16 | public SecurityPlugin() { 17 | log.debug("Starting Security Plugin"); 18 | } 19 | 20 | @SuppressWarnings("rawtypes") 21 | @Override 22 | public Collection> services() { 23 | final Collection> services = Lists 24 | .newArrayList(); 25 | 26 | services.add(SecurityService.class); 27 | 28 | return services; 29 | } 30 | 31 | @Override 32 | public String description() { 33 | return "Security Plugin"; 34 | } 35 | 36 | @Override 37 | public String name() { 38 | return "elasticsearch-security-plugin"; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/plugins/security/filter/ActionPathFilter.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.plugins.security.filter; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.Iterator; 6 | import java.util.List; 7 | 8 | import org.elasticsearch.common.bytes.BytesArray; 9 | import org.elasticsearch.plugins.security.MalformedConfigurationException; 10 | import org.elasticsearch.plugins.security.http.tomcat.TomcatHttpServerRestChannel; 11 | import org.elasticsearch.plugins.security.http.tomcat.TomcatHttpServerRestRequest; 12 | import org.elasticsearch.plugins.security.http.tomcat.TomcatUserRoleCallback; 13 | import org.elasticsearch.plugins.security.service.SecurityService; 14 | import org.elasticsearch.plugins.security.util.EditableRestRequest; 15 | import org.elasticsearch.plugins.security.util.SecurityUtil; 16 | import org.elasticsearch.rest.RestFilterChain; 17 | import org.elasticsearch.rest.RestStatus; 18 | 19 | public class ActionPathFilter extends SecureRestFilter { 20 | 21 | public ActionPathFilter(final SecurityService securityService) { 22 | super(securityService); 23 | 24 | } 25 | 26 | @Override 27 | public void processSecure(final TomcatHttpServerRestRequest request, 28 | final TomcatHttpServerRestChannel channel, 29 | final RestFilterChain filterChain) { 30 | 31 | if (SecurityUtil.stringContainsItemFromListAsTypeOrIndex( 32 | request.path(), SecurityUtil.BUILT_IN_ADMIN_COMMANDS)) { 33 | log.warn("Index- or Typename should not contains admin commands like " 34 | + Arrays.toString(SecurityUtil.BUILT_IN_ADMIN_COMMANDS)); 35 | } 36 | 37 | if (SecurityUtil.stringContainsItemFromListAsTypeOrIndex( 38 | request.path(), securityService.isStrictModeEnabled()?SecurityUtil.BUILT_IN_READ_COMMANDS_STRICT : SecurityUtil.BUILT_IN_READ_COMMANDS_LAX)) { 39 | log.warn("Index- or Typename should not contains search commands like " 40 | + Arrays.toString(securityService.isStrictModeEnabled()?SecurityUtil.BUILT_IN_READ_COMMANDS_STRICT : SecurityUtil.BUILT_IN_READ_COMMANDS_LAX)); 41 | } 42 | 43 | if (SecurityUtil.stringContainsItemFromListAsTypeOrIndex( 44 | request.path(), securityService.isStrictModeEnabled()?SecurityUtil.BUILT_IN_WRITE_COMMANDS_STRICT : SecurityUtil.BUILT_IN_WRITE_COMMANDS_LAX)) { 45 | log.warn("Index- or Typename should not contains write commands like " 46 | + Arrays.toString(securityService.isStrictModeEnabled()?SecurityUtil.BUILT_IN_WRITE_COMMANDS_STRICT : SecurityUtil.BUILT_IN_WRITE_COMMANDS_LAX)); 47 | } 48 | 49 | try { 50 | 51 | final PermLevel permLevel = new PermLevelEvaluator( 52 | securityService.getXContentSecurityConfiguration( 53 | getType(), getId())) 54 | .evaluatePerm( 55 | SecurityUtil.getIndices(request), 56 | SecurityUtil.getTypes(request), 57 | getClientHostAddress(request), 58 | new TomcatUserRoleCallback(request 59 | .getHttpServletRequest(),securityService.getSettings().get("security.ssl.userattribute"))); 60 | 61 | if (permLevel == PermLevel.NONE) { 62 | SecurityUtil.send(request, channel, RestStatus.FORBIDDEN, 63 | "No permission (at all)"); 64 | return; 65 | } 66 | 67 | if (permLevel.ordinal() < PermLevel.ALL.ordinal() 68 | && SecurityUtil.isAdminRequest(request)) { 69 | SecurityUtil.send(request, channel, RestStatus.FORBIDDEN, 70 | "No permission (for admin actions)"); 71 | return; 72 | } 73 | 74 | if (permLevel.ordinal() < PermLevel.READWRITE.ordinal() 75 | && SecurityUtil.isWriteRequest(request,securityService.isStrictModeEnabled())) { 76 | SecurityUtil.send(request, channel, RestStatus.FORBIDDEN, 77 | "No permission (for write actions)"); 78 | return; 79 | } 80 | 81 | if (permLevel == PermLevel.READONLY 82 | && !SecurityUtil.isReadRequest(request,securityService.isStrictModeEnabled())) { 83 | SecurityUtil.send(request, channel, RestStatus.FORBIDDEN, 84 | "No permission (for read actions)"); 85 | return; 86 | } 87 | 88 | // Ram Kotamarja - START 89 | // adding code to modify request modification before it hits elastic 90 | // search to apply the search filters 91 | modifiyKibanaRequest(request, channel); 92 | // Ram Kotamaraja - END 93 | 94 | 95 | filterChain.continueProcessing(request, channel); 96 | return; 97 | } catch (final MalformedConfigurationException e) { 98 | log.error("Cannot parse security configuration ", e); 99 | SecurityUtil.send(request, channel, 100 | RestStatus.INTERNAL_SERVER_ERROR, 101 | "Cannot parse security configuration"); 102 | 103 | return; 104 | } catch (final Exception e) { 105 | log.error("Generic error: ", e); 106 | SecurityUtil.send(request, channel, 107 | RestStatus.INTERNAL_SERVER_ERROR, 108 | "Generic error, see log for details"); 109 | 110 | return; 111 | } 112 | 113 | } 114 | 115 | /** 116 | * Method added to modify the request on the fly to 117 | * allow it to process generic queries coming from kibana by 118 | * validating against the security framework (contributed by Ram Kotamaraja) 119 | * @param request 120 | */ 121 | private void modifiyKibanaRequest( 122 | final TomcatHttpServerRestRequest request, 123 | final TomcatHttpServerRestChannel channel) { 124 | 125 | List reqTypesList = SecurityUtil.getTypes(request); 126 | if (reqTypesList != null && !reqTypesList.isEmpty() 127 | && reqTypesList.size() > 0) { 128 | // This means, there is a type specified in the request and so there 129 | // is not need to do anything as the framework will take care of the 130 | // type level security 131 | log.debug("Not modifying the request (for kibana) as there is one or more types already associated with the request"); 132 | reqTypesList = null; 133 | return; 134 | } 135 | 136 | String kibanaPermLevel = null; 137 | try { 138 | kibanaPermLevel = securityService.getXContentSecurityConfiguration( 139 | getType(), getKibanaId()); 140 | } catch (Exception e) { 141 | log.debug("No Kibana configuration found, so continuing the rest of the process: "+e.getMessage()); 142 | return; 143 | } 144 | 145 | List kibanaTypesList = null; 146 | List authorizedTypesList = new ArrayList(); 147 | try { 148 | if (kibanaPermLevel != null && kibanaPermLevel.length() > 0) { 149 | kibanaTypesList = securityService.getKibanaTypes(SecurityUtil 150 | .getIndices(request)); 151 | } 152 | 153 | final String reqContent = request.content().toUtf8(); 154 | String modifiedContent = reqContent; 155 | 156 | // checking where the original request has any types 157 | List requestTypes = SecurityUtil.getTypes(request); 158 | 159 | // If original request has any requests, then skip the logic below 160 | // as 161 | // permission evaluation has to be done based on that specific type 162 | if (requestTypes == null || requestTypes.isEmpty() 163 | || requestTypes.size() == 0) { 164 | if (kibanaTypesList != null) { 165 | 166 | // determine authorized types list 167 | 168 | 169 | Iterator kibanaTypesItr = kibanaTypesList 170 | .iterator(); 171 | 172 | while (kibanaTypesItr.hasNext()) { 173 | 174 | List kibanaType = new ArrayList(); 175 | kibanaType.add((String) kibanaTypesItr.next()); 176 | final PermLevel permLevel = new PermLevelEvaluator( 177 | securityService 178 | .getXContentSecurityConfiguration( 179 | getType(), getId())) 180 | .evaluatePerm( 181 | SecurityUtil.getIndices(request), 182 | // SecurityUtil.getTypes(request), 183 | kibanaType, 184 | getClientHostAddress(request), 185 | new TomcatUserRoleCallback( 186 | request.getHttpServletRequest(), 187 | securityService 188 | .getSettings() 189 | .get("security.ssl.userattribute"))); 190 | 191 | log.debug("Kibana perm level = "+permLevel); 192 | 193 | if (!permLevel.equals(PermLevel.NONE)) { 194 | authorizedTypesList.addAll(kibanaType); 195 | } 196 | } 197 | 198 | 199 | 200 | log.debug("Processing kibana types "+ kibanaTypesList); 201 | log.debug("request Content = "+ reqContent); 202 | 203 | String kibanaFilterStarter = "\"must\":["; 204 | int beginIndex = reqContent.indexOf(kibanaFilterStarter); 205 | 206 | if (beginIndex > 0) { 207 | String preReqContent = reqContent.substring(0, 208 | beginIndex + kibanaFilterStarter.length()); 209 | String postReqContent = reqContent.substring(beginIndex 210 | + kibanaFilterStarter.length()); 211 | 212 | modifiedContent = preReqContent 213 | + "{\"or\": {\"filters\":["; 214 | 215 | if (authorizedTypesList != null) { 216 | Iterator authorizedTypesItr = authorizedTypesList 217 | .iterator(); 218 | while (authorizedTypesItr.hasNext()) { 219 | modifiedContent += "{\"type\":{\"value\":\"" 220 | + authorizedTypesItr.next().toString() 221 | + "\"}},"; 222 | } 223 | modifiedContent = modifiedContent.substring(0, 224 | modifiedContent.length() - 1); 225 | } 226 | 227 | modifiedContent += "]}}," + postReqContent; 228 | log.debug("modified request content = " + modifiedContent); 229 | 230 | request.setContent(new BytesArray(modifiedContent)); 231 | request.setAttribute(TomcatHttpServerRestRequest.REQUEST_CONTENT_ATTRIBUTE, request.getContent()); 232 | 233 | } 234 | } 235 | } 236 | } catch (MalformedConfigurationException e) { 237 | log.error("Cannot parse security configuration ", e); 238 | SecurityUtil.send(request, channel, 239 | RestStatus.INTERNAL_SERVER_ERROR, 240 | "Cannot parse security configuration"); 241 | 242 | return; 243 | } catch (Exception e) { 244 | log.error("Generic error: ", e); 245 | SecurityUtil.send(request, channel, 246 | RestStatus.INTERNAL_SERVER_ERROR, 247 | "Generic error, see log for details"); 248 | 249 | return; 250 | } 251 | 252 | } 253 | 254 | /** 255 | * Method to return the default id (contributed by Ram Kotamaraja) 256 | * @return String - default id string 257 | */ 258 | protected String getKibanaId() { 259 | return "kibana"; 260 | } 261 | 262 | @Override 263 | protected String getType() { 264 | 265 | return "actionpathfilter"; 266 | } 267 | 268 | @Override 269 | protected String getId() { 270 | 271 | return "actionpathfilter"; 272 | } 273 | 274 | } 275 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/plugins/security/filter/DlsWriteFilter.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.plugins.security.filter; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Map; 6 | 7 | import org.elasticsearch.action.get.GetRequest; 8 | import org.elasticsearch.action.get.GetResponse; 9 | import org.elasticsearch.common.collect.Tuple; 10 | import org.elasticsearch.common.xcontent.XContentBuilder; 11 | import org.elasticsearch.common.xcontent.XContentFactory; 12 | import org.elasticsearch.common.xcontent.XContentHelper; 13 | import org.elasticsearch.common.xcontent.XContentType; 14 | import org.elasticsearch.common.xcontent.support.XContentMapValues; 15 | import org.elasticsearch.plugins.security.MalformedConfigurationException; 16 | import org.elasticsearch.plugins.security.http.tomcat.TomcatHttpServerRestChannel; 17 | import org.elasticsearch.plugins.security.http.tomcat.TomcatHttpServerRestRequest; 18 | import org.elasticsearch.plugins.security.http.tomcat.TomcatUserRoleCallback; 19 | import org.elasticsearch.plugins.security.service.SecurityService; 20 | import org.elasticsearch.plugins.security.service.permission.DlsPermission; 21 | import org.elasticsearch.plugins.security.util.EditableRestRequest; 22 | import org.elasticsearch.plugins.security.util.SecurityUtil; 23 | import org.elasticsearch.rest.RestFilterChain; 24 | import org.elasticsearch.rest.RestStatus; 25 | 26 | /** 27 | * NOT USED YET Protecting documents/fields from being updated or deleted on 28 | * dlstoken basis 29 | * 30 | * 31 | */ 32 | public class DlsWriteFilter extends SecureRestFilter { 33 | 34 | public DlsWriteFilter(final SecurityService securityService) { 35 | super(securityService); 36 | 37 | } 38 | 39 | @Override 40 | public void processSecure(final TomcatHttpServerRestRequest request, 41 | final TomcatHttpServerRestChannel channel, 42 | final RestFilterChain filterChain) { 43 | 44 | try { 45 | 46 | if (!SecurityUtil.isWriteRequest(request, securityService.isStrictModeEnabled())) { 47 | filterChain.continueProcessing(request, channel); 48 | return; 49 | } 50 | 51 | final List dlsTokens = new PermDlsEvaluator( 52 | securityService.getXContentSecurityConfiguration( 53 | getType(), getId())) 54 | .evaluatePerm( 55 | SecurityUtil.getIndices(request), 56 | SecurityUtil.getTypes(request), 57 | getClientHostAddress(request), 58 | new TomcatUserRoleCallback(request 59 | .getHttpServletRequest(),securityService.getSettings().get("security.ssl.userattribute"))); 60 | 61 | final String json = XContentHelper.convertToJson(request.content(), 62 | true); 63 | 64 | // TODO _bulk api 65 | 66 | // final XContentParser parser = XContentHelper.createParser(request 67 | // .content()); 68 | 69 | log.debug("fieldlevelpermfilter orig: " + json); 70 | 71 | log.debug("dls tokens: " + dlsTokens); 72 | 73 | final String id = SecurityUtil.getId(request); 74 | 75 | try { 76 | final GetResponse res = securityService 77 | .getClient() 78 | .get(new GetRequest(SecurityUtil.getIndices(request) 79 | .get(0), SecurityUtil.getTypes(request).get(0), 80 | id)).actionGet(); 81 | 82 | log.debug("document with id found: " + res.getId()); 83 | 84 | final List perms = securityService 85 | .parseDlsPermissions(res.getSourceAsBytesRef()); 86 | 87 | log.debug("perms " + perms); 88 | 89 | final List fields = new ArrayList(); 90 | 91 | for (final DlsPermission p : perms) { 92 | 93 | if (p.isAnyTokenAllowedToUpdate(dlsTokens)) 94 | 95 | { 96 | fields.add(p.getField()); 97 | } 98 | 99 | } 100 | log.debug("ffields " + fields); 101 | 102 | final Tuple> mapTuple = XContentHelper 103 | .convertToMap(request.content(), true); 104 | 105 | final Map filteredSource = XContentMapValues 106 | .filter(mapTuple.v2(), fields.toArray(new String[0]), 107 | new String[] { "*" }); 108 | 109 | log.debug("filteredSource " + filteredSource); 110 | 111 | final XContentBuilder sourceToBeReturned = XContentFactory 112 | .contentBuilder(mapTuple.v1()).map(filteredSource); 113 | 114 | final EditableRestRequest err = new EditableRestRequest(request); 115 | err.setContent(sourceToBeReturned.bytes()); 116 | 117 | filterChain.continueProcessing(err, channel); 118 | return; 119 | 120 | } catch (final Exception e) { 121 | // TODO Auto-generated catch block 122 | // e.printStackTrace(); 123 | log.debug("no document with id found: " + e.getMessage()); 124 | } 125 | 126 | filterChain.continueProcessing(request, channel); 127 | return; 128 | } catch (final MalformedConfigurationException e) { 129 | log.error("Cannot parse security configuration ", e); 130 | SecurityUtil.send(request, channel, 131 | RestStatus.INTERNAL_SERVER_ERROR, 132 | "Cannot parse security configuration"); 133 | 134 | return; 135 | } catch (final Exception e) { 136 | log.error("Generic error: ", e); 137 | SecurityUtil.send(request, channel, 138 | RestStatus.INTERNAL_SERVER_ERROR, 139 | "Generic error, see log for details"); 140 | 141 | return; 142 | } 143 | 144 | } 145 | 146 | @Override 147 | protected String getType() { 148 | 149 | return "dlspermissions"; 150 | } 151 | 152 | @Override 153 | protected String getId() { 154 | 155 | return "dlspermissions"; 156 | } 157 | 158 | } 159 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/plugins/security/filter/PermDlsEvaluator.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.plugins.security.filter; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.elasticsearch.plugins.security.service.permission.PermEvaluator; 7 | 8 | 9 | public class PermDlsEvaluator extends PermEvaluator> { 10 | 11 | public PermDlsEvaluator(final String xSecurityConfiguration) { 12 | super(xSecurityConfiguration); 13 | 14 | } 15 | 16 | @Override 17 | protected List createFromString(final String s) { 18 | 19 | final List fields = new ArrayList(); 20 | 21 | if (s == null) { 22 | return fields; 23 | } 24 | 25 | final String[] split = s.split(","); 26 | 27 | for (int i = 0; i < split.length; i++) { 28 | fields.add(split[i]); 29 | } 30 | 31 | return fields; 32 | 33 | } 34 | 35 | @Override 36 | protected String getPermissionFieldName() { 37 | return "dlstoken"; 38 | } 39 | 40 | @Override 41 | protected List getDefaultPermLevelForEvaluator() { 42 | return createFromString(null); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/plugins/security/filter/PermLevel.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.plugins.security.filter; 2 | 3 | public enum PermLevel { 4 | NONE, READONLY, READWRITE, ALL; 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/plugins/security/filter/PermLevelEvaluator.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.plugins.security.filter; 2 | 3 | import java.util.List; 4 | 5 | import org.elasticsearch.plugins.security.service.permission.PermEvaluator; 6 | 7 | public class PermLevelEvaluator extends PermEvaluator { 8 | 9 | protected PermLevelEvaluator(final String xSecurityConfiguration) { 10 | super(xSecurityConfiguration); 11 | 12 | } 13 | 14 | @Override 15 | protected PermLevel createFromString(final String s) { 16 | return PermLevel.valueOf(s); 17 | } 18 | 19 | @Override 20 | protected String getPermissionFieldName() { 21 | return "permission"; 22 | } 23 | 24 | @Override 25 | protected PermLevel getDefaultPermLevelForEvaluator() { 26 | return PermLevel.NONE; 27 | } 28 | 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/plugins/security/filter/SecureRestFilter.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.plugins.security.filter; 2 | 3 | import java.net.InetAddress; 4 | import java.net.UnknownHostException; 5 | import java.util.List; 6 | 7 | import org.elasticsearch.common.logging.ESLogger; 8 | import org.elasticsearch.common.logging.Loggers; 9 | import org.elasticsearch.plugins.security.http.tomcat.TomcatHttpServerRestChannel; 10 | import org.elasticsearch.plugins.security.http.tomcat.TomcatHttpServerRestRequest; 11 | import org.elasticsearch.plugins.security.service.SecurityService; 12 | import org.elasticsearch.plugins.security.util.SecurityUtil; 13 | import org.elasticsearch.rest.RestChannel; 14 | import org.elasticsearch.rest.RestFilter; 15 | import org.elasticsearch.rest.RestFilterChain; 16 | import org.elasticsearch.rest.RestRequest; 17 | import org.elasticsearch.rest.RestStatus; 18 | 19 | public abstract class SecureRestFilter extends RestFilter { 20 | 21 | protected final ESLogger log = Loggers.getLogger(this.getClass()); 22 | 23 | protected SecurityService securityService; 24 | 25 | protected SecureRestFilter(final SecurityService securityService) { 26 | super(); 27 | this.securityService = securityService; 28 | } 29 | 30 | protected InetAddress getClientHostAddress(final RestRequest request) 31 | throws UnknownHostException { 32 | 33 | final InetAddress hostAddress = securityService 34 | .getHostAddressFromRequest(request); 35 | 36 | return hostAddress; 37 | } 38 | 39 | @Override 40 | public final void process(final RestRequest request, 41 | final RestChannel channel, final RestFilterChain filterChain) { 42 | 43 | // TODO check aliases, multiple indices, _all, ... 44 | final List indices = SecurityUtil.getIndices(request); 45 | if (indices.contains(securityService 46 | .getSecurityConfigurationIndex())) { 47 | 48 | try { 49 | if (getClientHostAddress(request).isLoopbackAddress()) { 50 | filterChain.continueProcessing(request, channel); 51 | } else { 52 | SecurityUtil.send(request, channel, RestStatus.FORBIDDEN, 53 | "Only allowed from localhost (loopback)"); 54 | } 55 | } catch (final UnknownHostException e) { 56 | SecurityUtil.send(request, channel, 57 | RestStatus.INTERNAL_SERVER_ERROR, e.toString()); 58 | } 59 | } else { 60 | 61 | ((TomcatHttpServerRestRequest) request).getUserRoles(); 62 | 63 | processSecure((TomcatHttpServerRestRequest) request, 64 | (TomcatHttpServerRestChannel) channel, filterChain); 65 | 66 | } 67 | 68 | } 69 | 70 | protected abstract void processSecure( 71 | final TomcatHttpServerRestRequest request, 72 | final TomcatHttpServerRestChannel channel, 73 | final RestFilterChain filterChain); 74 | 75 | protected abstract String getType(); 76 | 77 | protected abstract String getId(); 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/plugins/security/http/realm/AllPermsRealm.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.plugins.security.http.realm; 2 | 3 | import java.io.IOException; 4 | import java.security.Principal; 5 | import java.util.Arrays; 6 | 7 | import org.apache.catalina.Context; 8 | import org.apache.catalina.Wrapper; 9 | import org.apache.catalina.connector.Request; 10 | import org.apache.catalina.connector.Response; 11 | import org.apache.catalina.deploy.SecurityConstraint; 12 | import org.apache.catalina.realm.GenericPrincipal; 13 | import org.apache.catalina.realm.RealmBase; 14 | import org.ietf.jgss.GSSCredential; 15 | 16 | public class AllPermsRealm extends RealmBase { 17 | 18 | @Override 19 | protected String getName() { 20 | return "AllPermsRealm"; 21 | } 22 | 23 | 24 | @Override 25 | protected Principal getPrincipal(String username, 26 | GSSCredential gssCredential) { 27 | 28 | throw new RuntimeException(username+"//"+gssCredential); 29 | } 30 | 31 | @Override 32 | protected String getPassword(String username) { 33 | return null; 34 | } 35 | 36 | @Override 37 | protected Principal getPrincipal(String username) { 38 | return new GenericPrincipal(username, null, Arrays.asList( "*".split(""))); 39 | } 40 | 41 | @Override 42 | public boolean hasResourcePermission(Request request, Response response, 43 | SecurityConstraint[] constraints, Context context) 44 | throws IOException { 45 | 46 | return request.getPrincipal() != null; 47 | } 48 | 49 | @Override 50 | public boolean hasRole(Wrapper wrapper, Principal principal, String role) { 51 | 52 | return principal != null; 53 | } 54 | 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/plugins/security/http/realm/LdapTlsContextFactory.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.plugins.security.http.realm; 2 | 3 | import java.io.IOException; 4 | import java.lang.reflect.InvocationHandler; 5 | import java.lang.reflect.InvocationTargetException; 6 | import java.lang.reflect.Method; 7 | import java.lang.reflect.Proxy; 8 | import java.util.Arrays; 9 | import java.util.HashMap; 10 | import java.util.Hashtable; 11 | import java.util.Map; 12 | import java.util.logging.Logger; 13 | 14 | import javax.naming.Context; 15 | import javax.naming.NamingException; 16 | import javax.naming.directory.DirContext; 17 | import javax.naming.ldap.InitialLdapContext; 18 | import javax.naming.ldap.LdapContext; 19 | import javax.naming.ldap.StartTlsRequest; 20 | import javax.naming.ldap.StartTlsResponse; 21 | import javax.naming.spi.InitialContextFactory; 22 | import javax.net.ssl.HostnameVerifier; 23 | import javax.net.ssl.SSLSession; 24 | 25 | import com.sun.jndi.ldap.LdapCtxFactory; 26 | 27 | public class LdapTlsContextFactory implements InitialContextFactory { 28 | 29 | private static final class ProxyLdapContext implements InvocationHandler { 30 | private final LdapContext delegate; 31 | private final StartTlsResponse tls; 32 | 33 | 34 | private ProxyLdapContext(Hashtable env) throws NamingException { 35 | final Map savedEnv = new HashMap(); 36 | for (final String key : Arrays.asList(Context.SECURITY_AUTHENTICATION, 37 | Context.SECURITY_CREDENTIALS, Context.SECURITY_PRINCIPAL, 38 | Context.SECURITY_PROTOCOL)) { 39 | final Object entry = env.remove(key); 40 | if (entry != null) { 41 | savedEnv.put(key, entry); 42 | } 43 | } 44 | delegate = new InitialLdapContext(env, null); 45 | tls = (StartTlsResponse) delegate 46 | .extendedOperation(new StartTlsRequest()); 47 | tls.setHostnameVerifier(new HostnameVerifier() { 48 | 49 | @Override 50 | public boolean verify(String hostname, SSLSession session) { 51 | return true; 52 | } 53 | }); 54 | try { 55 | final SSLSession negotiate = tls.negotiate(); 56 | Logger.getLogger(this.getClass().getCanonicalName()).fine( 57 | "LDAP is now using " + negotiate.getProtocol()); 58 | } catch (final IOException e) { 59 | throw new NamingException(e.getMessage()); 60 | } 61 | for (final Map.Entry savedEntry : savedEnv.entrySet()) { 62 | delegate.addToEnvironment(savedEntry.getKey(), savedEntry 63 | .getValue()); 64 | } 65 | } 66 | 67 | @Override 68 | public Object invoke(Object proxy, Method method, Object[] args) 69 | throws Throwable { 70 | if ("close".equals(method.getName())) { 71 | return doClose(delegate); 72 | } 73 | return method.invoke(delegate, args); 74 | } 75 | 76 | private Object doClose(LdapContext delegate) throws IOException, 77 | IllegalAccessException, InvocationTargetException { 78 | try { 79 | if (tls != null) { 80 | try { 81 | tls.close(); 82 | } catch (final IOException e) { 83 | throw new InvocationTargetException(e); 84 | } 85 | } 86 | } finally { 87 | try { 88 | if (delegate != null) { 89 | delegate.close(); 90 | } 91 | } catch (final NamingException e) { 92 | throw new InvocationTargetException(e); 93 | } 94 | } 95 | return null; 96 | } 97 | } 98 | 99 | public static final String REAL_INITIAL_CONTEXT_FACTORY = "REAL_INITIAL_CONTEXT_FACTORY"; 100 | 101 | @SuppressWarnings("unchecked") 102 | @Override 103 | public Context getInitialContext(final Hashtable environment) 104 | throws NamingException { 105 | final Hashtable proxyEnv = new Hashtable(environment); 106 | Object realFactory; 107 | if (environment.contains(REAL_INITIAL_CONTEXT_FACTORY)) { 108 | realFactory = environment.get(REAL_INITIAL_CONTEXT_FACTORY); 109 | } else { 110 | realFactory = LdapCtxFactory.class.getCanonicalName(); 111 | } 112 | proxyEnv.put(Context.INITIAL_CONTEXT_FACTORY, realFactory); 113 | proxyEnv.put("com.sun.jndi.ldap.connect.pool", "false"); 114 | return (Context) Proxy.newProxyInstance(this.getClass() 115 | .getClassLoader(), new Class[] { DirContext.class }, 116 | new ProxyLdapContext(proxyEnv)); 117 | } 118 | 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/plugins/security/http/tomcat/ExtendedJndiRealm.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.plugins.security.http.tomcat; 2 | 3 | import java.io.IOException; 4 | import java.security.Principal; 5 | import java.security.cert.X509Certificate; 6 | 7 | import org.apache.catalina.connector.Request; 8 | import org.apache.catalina.connector.Response; 9 | import org.apache.catalina.deploy.SecurityConstraint; 10 | import org.apache.catalina.realm.JNDIRealm; 11 | import org.apache.commons.lang.StringUtils; 12 | import org.apache.juli.logging.Log; 13 | import org.apache.juli.logging.LogFactory; 14 | 15 | public class ExtendedJndiRealm extends JNDIRealm { 16 | 17 | private static final Log log = LogFactory.getLog(ExtendedJndiRealm.class); 18 | private final String sslUserAttribute; 19 | public ExtendedJndiRealm(String sslUserAttribute) { 20 | this.sslUserAttribute=sslUserAttribute; 21 | } 22 | 23 | @Override 24 | public boolean hasResourcePermission(Request request, Response response, 25 | SecurityConstraint[] constraints, 26 | org.apache.catalina.Context context) throws IOException { 27 | // TODO Auto-generated method stub 28 | return request.getPrincipal() != null; 29 | } 30 | 31 | /** 32 | * Return the Principal associated with the given certificate. 33 | */ 34 | @Override 35 | protected Principal getPrincipal(X509Certificate usercert) { 36 | final String username = x509UsernameRetriever.getUsername(usercert); 37 | 38 | if(log.isDebugEnabled()) { 39 | log.debug(sm.getString("realmBase.gotX509Username", username)); 40 | } 41 | 42 | 43 | if(sslUserAttribute != null && !sslUserAttribute.isEmpty()) { 44 | return getPrincipal(StringUtils.substringBetween(username, sslUserAttribute+"=",",")); //TODO fix case 45 | } else { 46 | return super.getPrincipal(username); 47 | } 48 | } 49 | 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/plugins/security/http/tomcat/ExtendedSpnegoAuthenticator.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.plugins.security.http.tomcat; 2 | 3 | import org.apache.catalina.authenticator.SpnegoAuthenticator; 4 | 5 | 6 | 7 | public class ExtendedSpnegoAuthenticator extends SpnegoAuthenticator { 8 | 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/plugins/security/http/tomcat/ExtendedTomcat.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.plugins.security.http.tomcat; 2 | 3 | import org.apache.catalina.startup.Tomcat; 4 | import org.elasticsearch.common.logging.ESLogger; 5 | import org.elasticsearch.common.logging.Loggers; 6 | 7 | public class ExtendedTomcat extends Tomcat { 8 | 9 | protected static final ESLogger log = Loggers 10 | .getLogger(ExtendedTomcat.class); 11 | 12 | public ExtendedTomcat() { 13 | 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/plugins/security/http/tomcat/TomcatHttpServerRestChannel.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.plugins.security.http.tomcat; 2 | 3 | import java.io.IOException; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.concurrent.CountDownLatch; 8 | 9 | import javax.servlet.ServletOutputStream; 10 | import javax.servlet.http.HttpServletResponse; 11 | 12 | import org.elasticsearch.common.bytes.BytesReference; 13 | import org.elasticsearch.common.collect.Tuple; 14 | import org.elasticsearch.common.logging.ESLogger; 15 | import org.elasticsearch.common.logging.Loggers; 16 | import org.elasticsearch.common.xcontent.XContent; 17 | import org.elasticsearch.common.xcontent.XContentBuilder; 18 | import org.elasticsearch.common.xcontent.XContentFactory; 19 | import org.elasticsearch.common.xcontent.XContentHelper; 20 | import org.elasticsearch.common.xcontent.XContentType; 21 | import org.elasticsearch.common.xcontent.support.XContentMapValues; 22 | import org.elasticsearch.http.HttpChannel; 23 | import org.elasticsearch.plugins.security.MalformedConfigurationException; 24 | import org.elasticsearch.plugins.security.filter.PermDlsEvaluator; 25 | import org.elasticsearch.plugins.security.service.SecurityService; 26 | import org.elasticsearch.plugins.security.service.permission.DlsPermission; 27 | import org.elasticsearch.plugins.security.util.SecurityUtil; 28 | import org.elasticsearch.rest.BytesRestResponse; 29 | import org.elasticsearch.rest.RestRequest; 30 | import org.elasticsearch.rest.RestResponse; 31 | 32 | public class TomcatHttpServerRestChannel extends HttpChannel { 33 | 34 | protected final ESLogger log = Loggers.getLogger(this.getClass()); 35 | 36 | private final TomcatHttpServerRestRequest restRequest; 37 | 38 | private final HttpServletResponse resp; 39 | 40 | private Exception sendFailure; 41 | 42 | private final CountDownLatch latch; 43 | 44 | private final SecurityService securityService; 45 | 46 | final Boolean enableDls; 47 | 48 | public TomcatHttpServerRestChannel( 49 | final TomcatHttpServerRestRequest restRequest, 50 | final HttpServletResponse resp, 51 | final SecurityService securityService) { 52 | super(restRequest); 53 | this.securityService = securityService; 54 | this.restRequest = restRequest; 55 | this.resp = resp; 56 | latch = new CountDownLatch(1); 57 | 58 | enableDls = securityService.getSettings().getAsBoolean( 59 | "security.module.dls.enabled", true); 60 | 61 | } 62 | 63 | public void await() throws InterruptedException { 64 | latch.await(); 65 | } 66 | 67 | public Exception sendFailure() { 68 | return sendFailure; 69 | } 70 | 71 | @Override 72 | public void sendResponse(final RestResponse response) { 73 | 74 | resp.setContentType(response.contentType()); 75 | 76 | //CORS 77 | resp.addHeader("Access-Control-Allow-Origin", "*"); 78 | //enhancing the list of allowed method list to meet the requirements of Kibana (contributed by Ram Kotamaraja) 79 | resp.addHeader("Access-Control-Allow-Methods", "OPTIONS, HEAD, GET, POST, PUT, DELETE"); 80 | resp.addHeader("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, Content-Length, X-HTTP-Method-Override, Origin, Accept, Authorization"); 81 | //resp.addHeader("Access-Control-Allow-Credentials", "true"); 82 | resp.addHeader("Cache-Control", "max-age=0"); 83 | 84 | 85 | if (response.status() != null) { 86 | resp.setStatus(response.status().getStatus()); 87 | } else { 88 | resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); 89 | } 90 | /*if (restRequest.method() == RestRequest.Method.OPTIONS) { 91 | // TODO: also add more access control parameters 92 | resp.addHeader("Access-Control-Max-Age", "1728000"); 93 | 94 | 95 | 96 | }*/ 97 | try { 98 | 99 | log.debug("Rest response contentype: "+response.contentType()+"/xcontent response contentype: "+ XContentType.fromRestContentType(response.contentType())); 100 | 101 | if(enableDls && SecurityUtil.xContentTypefromRestContentType(response.contentType()) != null) { //skip text/html etc) { 102 | log.debug("DLS is enabled and request contains valid xcontent"); 103 | BytesReference modifiedContent = applyDls((BytesRestResponse)response); 104 | int contentLength = modifiedContent.length(); 105 | resp.setContentLength(contentLength); 106 | final ServletOutputStream out = resp.getOutputStream(); 107 | modifiedContent.writeTo(out); 108 | out.close(); 109 | 110 | } else { 111 | log.debug("DLS is not enabled or response does not contain valid xcontent"); 112 | int contentLength = response.content().length(); 113 | resp.setContentLength(contentLength); 114 | ServletOutputStream out = resp.getOutputStream(); 115 | response.content().writeTo(out); 116 | out.close(); 117 | } 118 | 119 | 120 | 121 | 122 | } catch (final Exception e) { 123 | log.error(e.toString(), e); 124 | sendFailure = e; 125 | } finally { 126 | latch.countDown(); 127 | } 128 | } 129 | 130 | protected BytesReference applyDls(final BytesRestResponse xres) 131 | throws IOException, MalformedConfigurationException { 132 | 133 | 134 | final List indices = SecurityUtil.getIndices(restRequest); 135 | 136 | log.debug("applyDLS() for indices "+indices); 137 | 138 | if (indices.contains(securityService 139 | .getSecurityConfigurationIndex())) { 140 | 141 | if (securityService 142 | .getHostAddressFromRequest(restRequest) 143 | .isLoopbackAddress()) { 144 | 145 | log.debug("applyDLS() return unmodified content because of loopback address"); 146 | return xres.content(); 147 | 148 | } else { 149 | throw new IOException("Only allowed from localhost (loopback)"); 150 | } 151 | 152 | } 153 | 154 | if(xres.content() == null || xres.content().length() == 0) { 155 | log.debug("applyDLS() return unmodified content because of content is null or of zero length"); 156 | return xres.content(); 157 | } 158 | 159 | if (xres.status().getStatus() < 200 160 | || xres.status().getStatus() >= 300) { 161 | 162 | log.debug("applyDLS() return unmodified content because of status "+xres.status().getStatus()); 163 | return xres.content(); 164 | } 165 | 166 | if ( !restRequest.path().contains("_search") 167 | && !restRequest.path().contains("_msearch") 168 | && !restRequest.path().contains("_mlt") 169 | && !restRequest.path().contains("_suggest") 170 | && !restRequest.getHttpServletRequest().getMethod().equalsIgnoreCase("get") ) { 171 | 172 | log.debug("applyDLS() return unmodified content because of path (no search): "+restRequest.getHttpServletRequest().getMethod()+" "+restRequest.path()); 173 | return xres.content(); 174 | } 175 | 176 | final List dlsTokens = new PermDlsEvaluator( 177 | securityService.getXContentSecurityConfiguration( 178 | "dlspermissions", "dlspermissions")).evaluatePerm( 179 | SecurityUtil.getIndices(restRequest), 180 | SecurityUtil.getTypes(restRequest), 181 | securityService 182 | .getHostAddressFromRequest(restRequest), 183 | new TomcatUserRoleCallback(restRequest 184 | .getHttpServletRequest(),securityService.getSettings().get("security.ssl.userattribute"))); 185 | 186 | log.debug("dls tokens: " + dlsTokens); 187 | 188 | // this.log.debug("orig json: " + xres.builder().string()); 189 | 190 | final List perms = securityService 191 | .parseDlsPermissions(xres.content()); 192 | 193 | // TODO check against the tokens 194 | 195 | final Tuple> mapTuple = XContentHelper 196 | .convertToMap(xres.content().toBytes(), true); 197 | 198 | final List fields = new ArrayList(); 199 | fields.add("_shards*"); 200 | fields.add("took"); 201 | fields.add("timed_out"); 202 | fields.add("hits.total"); 203 | fields.add("hits.max_score"); 204 | fields.add("hits.hits._index"); 205 | fields.add("hits.hits._type"); 206 | fields.add("hits.hits._id"); 207 | fields.add("hits.hits._score"); 208 | 209 | //GET 210 | fields.add("_index"); 211 | fields.add("_type"); 212 | fields.add("_version"); 213 | fields.add("_id"); 214 | fields.add("found"); 215 | 216 | 217 | if(!securityService.isStrictModeEnabled()){ 218 | fields.add("facets"); 219 | fields.add("suggest"); 220 | } 221 | 222 | 223 | for (final DlsPermission p : perms) { 224 | 225 | log.debug(p.toString()); 226 | 227 | if (p.isAnyTokenAllowedToRead(dlsTokens)) { 228 | 229 | fields.add("hits.hits._source." + p.getField()); 230 | fields.add("_source." + p.getField()); 231 | } 232 | 233 | } 234 | 235 | log.debug(fields.toString()); 236 | 237 | final Map filteredSource = XContentMapValues.filter( 238 | mapTuple.v2(), fields.toArray(new String[0]), null); 239 | 240 | log.debug("filteredSource " + filteredSource); 241 | 242 | final XContentBuilder sourceToBeReturned = XContentFactory 243 | .contentBuilder(mapTuple.v1()).map(filteredSource); 244 | return sourceToBeReturned.bytes(); 245 | 246 | } 247 | 248 | } 249 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/plugins/security/http/tomcat/TomcatHttpServerRestRequest.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.plugins.security.http.tomcat; 2 | 3 | import java.io.IOException; 4 | import java.security.Principal; 5 | import java.util.Arrays; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | import java.util.Map.Entry; 10 | 11 | import javax.servlet.http.HttpServletRequest; 12 | 13 | import org.apache.catalina.realm.GenericPrincipal; 14 | import org.elasticsearch.common.bytes.BytesArray; 15 | import org.elasticsearch.common.bytes.BytesReference; 16 | import org.elasticsearch.common.io.Streams; 17 | import org.elasticsearch.common.logging.ESLogger; 18 | import org.elasticsearch.common.logging.Loggers; 19 | import org.elasticsearch.http.HttpRequest; 20 | 21 | import org.elasticsearch.rest.support.RestUtils; 22 | 23 | import waffle.servlet.WindowsPrincipal; 24 | 25 | public class TomcatHttpServerRestRequest extends 26 | HttpRequest { 27 | 28 | protected static final ESLogger log = Loggers 29 | .getLogger(TomcatHttpServerRestRequest.class); 30 | 31 | public static final String REQUEST_CONTENT_ATTRIBUTE = "org.elasticsearch.plugins.security.http.tomcat.request-content"; 32 | 33 | private final HttpServletRequest request; 34 | 35 | private final Method method; 36 | 37 | private final Map params; 38 | 39 | private BytesReference content; 40 | 41 | private final String opaqueId; 42 | 43 | public TomcatHttpServerRestRequest(final HttpServletRequest request) 44 | throws IOException { 45 | this.request = request; 46 | opaqueId = request.getHeader("X-Opaque-Id"); 47 | method = Method.valueOf(request.getMethod()); 48 | params = new HashMap(); 49 | 50 | log.debug("HttpServletRequest impl class: " + request.getClass()); 51 | log.debug("HttpServletRequest ru: " + request.getRemoteUser()); 52 | log.debug("HttpServletRequest up: " + request.getUserPrincipal()); 53 | //log.debug("HttpServletRequest up: " + request.getUserPrincipal().getClass().toString()); 54 | 55 | if (request.getQueryString() != null) { 56 | RestUtils.decodeQueryString(request.getQueryString(), 0, 57 | params); 58 | } 59 | 60 | content = new BytesArray(Streams.copyToByteArray(request 61 | .getInputStream())); 62 | request.setAttribute(REQUEST_CONTENT_ATTRIBUTE, content); 63 | } 64 | 65 | @Override 66 | public Method method() { 67 | return method; 68 | } 69 | 70 | @Override 71 | public String uri() { 72 | 73 | return request.getRequestURI(); 74 | 75 | /* 76 | * int prefixLength = 0; if (request.getContextPath() != null ) { 77 | * prefixLength += request.getContextPath().length(); } if 78 | * (request.getServletPath() != null ) { prefixLength += 79 | * request.getServletPath().length(); } if (prefixLength > 0) { return 80 | * request.getRequestURI().substring(prefixLength); } else { return 81 | * request.getRequestURI(); } 82 | */ 83 | } 84 | 85 | @Override 86 | public String rawPath() { 87 | return uri(); 88 | } 89 | 90 | @Override 91 | public boolean hasContent() { 92 | return content.length() > 0; 93 | } 94 | 95 | @Override 96 | public boolean contentUnsafe() { 97 | return false; 98 | } 99 | 100 | @Override 101 | public BytesReference content() { 102 | return content; 103 | } 104 | 105 | @Override 106 | public String header(final String name) { 107 | return request.getHeader(name); 108 | } 109 | 110 | @Override 111 | public Map params() { 112 | return params; 113 | } 114 | 115 | @Override 116 | public boolean hasParam(final String key) { 117 | return params.containsKey(key); 118 | } 119 | 120 | @Override 121 | public String param(final String key) { 122 | return params.get(key); 123 | } 124 | 125 | @Override 126 | public String param(final String key, final String defaultValue) { 127 | final String value = params.get(key); 128 | if (value == null) { 129 | return defaultValue; 130 | } 131 | return value; 132 | } 133 | 134 | public String localAddr() { 135 | return request.getLocalAddr(); 136 | } 137 | 138 | public long localPort() { 139 | return request.getLocalPort(); 140 | } 141 | 142 | public String remoteAddr() { 143 | return request.getRemoteAddr(); 144 | } 145 | 146 | public long remotePort() { 147 | return request.getRemotePort(); 148 | } 149 | 150 | public String remoteUser() { 151 | return request.getRemoteUser(); 152 | } 153 | 154 | public String scheme() { 155 | return request.getScheme(); 156 | } 157 | 158 | public String contentType() { 159 | return request.getContentType(); 160 | } 161 | 162 | public String opaqueId() { 163 | return opaqueId; 164 | } 165 | 166 | public HttpServletRequest getHttpServletRequest() { 167 | return request; 168 | 169 | } 170 | 171 | public Principal getUserPrincipal() { 172 | return request.getUserPrincipal(); 173 | 174 | } 175 | 176 | public boolean isUserInRole(final String role) { 177 | return request.isUserInRole(role); 178 | 179 | } 180 | 181 | public List getUserRoles() { 182 | 183 | if (request.getUserPrincipal() instanceof GenericPrincipal) { 184 | final GenericPrincipal wp = (GenericPrincipal) request 185 | .getUserPrincipal(); 186 | 187 | if (wp.getRoles() != null) { 188 | final List roles = Arrays.asList(wp.getRoles()); 189 | log.debug("GenericPrincipal roles: " + roles); 190 | return roles; 191 | } 192 | } 193 | 194 | 195 | if (request.getUserPrincipal() instanceof WindowsPrincipal) { 196 | final WindowsPrincipal wp = (WindowsPrincipal) request 197 | .getUserPrincipal(); 198 | 199 | log.debug("WindowsPrincipal roles: " + wp.getRolesString()); 200 | log.debug("WindowsPrincipal groups: " + wp.getGroups()); 201 | 202 | if (wp.getRolesString() != null) { 203 | return Arrays.asList(wp.getRolesString().split(",")); 204 | } 205 | } 206 | 207 | /*if (this.request.getUserPrincipal() instanceof ActiveDirectoryPrincipal) { 208 | final ActiveDirectoryPrincipal ap = (ActiveDirectoryPrincipal) this.request 209 | .getUserPrincipal(); 210 | 211 | log.debug("ActiveDirectoryPrincipal roles: " + ap.getRoles()); 212 | 213 | return ap.getRoles(); 214 | }*/ 215 | 216 | return null; 217 | 218 | } 219 | 220 | @Override 221 | public Iterable> headers() { 222 | 223 | Map headerMap = new HashMap(); 224 | 225 | while(request.getHeaderNames().hasMoreElements()) 226 | { 227 | String headerName = request.getHeaderNames().nextElement(); 228 | headerMap.put(headerName, request.getHeader(headerName)); 229 | } 230 | 231 | return headerMap.entrySet(); 232 | } 233 | 234 | 235 | //next three methods contributed by Ram Kotamaraja 236 | 237 | /** 238 | * Setter Method for content 239 | */ 240 | public void setContent(BytesReference content) { 241 | this.content = content; 242 | } 243 | 244 | /** 245 | * Getter Method for returning content 246 | * @return BytesReference 247 | */ 248 | public BytesReference getContent() { 249 | return content; 250 | } 251 | 252 | /** 253 | * Method added to modify the request query based on the authorization permission settings 254 | * in the security configuration. This method will set the modified content in request. 255 | * @param requestContentAttribute 256 | * @param content 257 | */ 258 | public void setAttribute(String requestContentAttribute, 259 | BytesReference content) { 260 | this.request.setAttribute(requestContentAttribute, content); 261 | 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/plugins/security/http/tomcat/TomcatHttpServerTransportModule.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.plugins.security.http.tomcat; 2 | 3 | import org.elasticsearch.common.inject.AbstractModule; 4 | import org.elasticsearch.http.HttpServerTransport; 5 | 6 | public class TomcatHttpServerTransportModule extends AbstractModule { 7 | @Override 8 | protected void configure() { 9 | this.bind(HttpServerTransport.class) 10 | .to(TomcatHttpServerTransport.class).asEagerSingleton(); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/plugins/security/http/tomcat/TomcatHttpTransportHandlerServlet.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.plugins.security.http.tomcat; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.servlet.ServletException; 6 | import javax.servlet.http.HttpServlet; 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | 10 | import org.elasticsearch.common.logging.ESLogger; 11 | import org.elasticsearch.common.logging.Loggers; 12 | import org.elasticsearch.http.HttpServerAdapter; 13 | 14 | public class TomcatHttpTransportHandlerServlet extends HttpServlet { 15 | 16 | /** 17 | * 18 | */ 19 | private static final long serialVersionUID = 1L; 20 | protected static final ESLogger log = Loggers 21 | .getLogger(TomcatHttpTransportHandlerServlet.class); 22 | private volatile TomcatHttpServerTransport transport; 23 | 24 | public TomcatHttpTransportHandlerServlet() { 25 | 26 | } 27 | 28 | @Override 29 | protected void service(final HttpServletRequest req, 30 | final HttpServletResponse resp) throws ServletException, 31 | IOException { 32 | 33 | final HttpServerAdapter adapter = getTransport() 34 | .httpServerAdapter(); 35 | final TomcatHttpServerRestRequest restRequest = new TomcatHttpServerRestRequest( 36 | req); 37 | final TomcatHttpServerRestChannel restChannel = new TomcatHttpServerRestChannel( 38 | restRequest, resp, transport.getSecurityService()); 39 | 40 | try { 41 | 42 | adapter.dispatchRequest(restRequest, restChannel); 43 | restChannel.await(); 44 | 45 | } catch (final InterruptedException e) { 46 | throw new ServletException("failed to dispatch request", e); 47 | } catch (final Exception e) { 48 | throw new IOException("failed to dispatch request", e); 49 | } 50 | if (restChannel.sendFailure() != null) { 51 | throw new ServletException(restChannel.sendFailure()); 52 | } 53 | 54 | } 55 | 56 | public TomcatHttpServerTransport getTransport() { 57 | return transport; 58 | } 59 | 60 | public void setTransport(final TomcatHttpServerTransport transport) { 61 | this.transport = transport; 62 | // this.logger = Loggers.getLogger(buildClassLoggerName(getClass()), 63 | // transport.settings()); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/plugins/security/http/tomcat/TomcatUserRoleCallback.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.plugins.security.http.tomcat; 2 | 3 | import javax.servlet.http.HttpServletRequest; 4 | 5 | import org.apache.commons.lang.StringUtils; 6 | import org.elasticsearch.plugins.security.service.permission.UserRoleCallback; 7 | 8 | public class TomcatUserRoleCallback implements UserRoleCallback { 9 | 10 | private final HttpServletRequest request; 11 | private final String sslUserAttribute; 12 | 13 | public TomcatUserRoleCallback(final HttpServletRequest request, String sslUserAttribute) { 14 | this.request = request; 15 | this.sslUserAttribute=sslUserAttribute; 16 | } 17 | 18 | @Override 19 | public String getRemoteuser() { 20 | 21 | 22 | String remoteUser = request.getRemoteUser(); 23 | 24 | //CN=nelsonh, OU=marketingc, O=Saly Test Inc 2, DC=saly, DC=de 25 | if(remoteUser != null && !remoteUser.isEmpty()) 26 | { 27 | if(sslUserAttribute != null && remoteUser.contains(sslUserAttribute)) 28 | { 29 | remoteUser = StringUtils.substringBetween(remoteUser.toLowerCase(), //TODO fix lower case 30 | (sslUserAttribute+"=").toLowerCase(), ","); 31 | } 32 | } 33 | 34 | return remoteUser; 35 | } 36 | 37 | @Override 38 | public boolean isRemoteUserInRole(final String role) { 39 | 40 | return request.isUserInRole(role); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/plugins/security/service/SecurityService.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.plugins.security.service; 2 | 3 | import java.io.IOException; 4 | import java.net.InetAddress; 5 | import java.net.UnknownHostException; 6 | import java.util.ArrayList; 7 | import java.util.Arrays; 8 | import java.util.Collection; 9 | import java.util.HashMap; 10 | import java.util.HashSet; 11 | import java.util.List; 12 | import java.util.Map; 13 | import java.util.Set; 14 | 15 | import net.minidev.json.JSONArray; 16 | import net.minidev.json.JSONObject; 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | import org.elasticsearch.ElasticsearchException; 25 | import org.elasticsearch.action.get.GetResponse; 26 | import org.elasticsearch.client.Client; 27 | import org.elasticsearch.common.bytes.BytesReference; 28 | import org.elasticsearch.common.component.AbstractLifecycleComponent; 29 | import org.elasticsearch.common.inject.Inject; 30 | import org.elasticsearch.common.settings.Settings; 31 | import org.elasticsearch.common.xcontent.XContentHelper; 32 | import org.elasticsearch.plugins.security.MalformedConfigurationException; 33 | import org.elasticsearch.plugins.security.filter.ActionPathFilter; 34 | import org.elasticsearch.plugins.security.http.tomcat.TomcatHttpServerRestRequest; 35 | import org.elasticsearch.plugins.security.service.permission.DlsPermission; 36 | import org.elasticsearch.rest.RestController; 37 | import org.elasticsearch.rest.RestRequest; 38 | 39 | import com.jayway.jsonpath.JsonPath; 40 | 41 | public class SecurityService extends 42 | AbstractLifecycleComponent { 43 | 44 | public Settings getSettings() { 45 | return settings; 46 | } 47 | 48 | private final static String DEFAULT_SECURITY_CONFIG_INDEX = "securityconfiguration"; 49 | private final String securityConfigurationIndex; 50 | private final RestController restController; 51 | private final Client client; 52 | private final Settings settings; 53 | private final boolean strictModeEnabled; 54 | 55 | @Inject 56 | public SecurityService(final Settings settings, final Client client, 57 | final RestController restController) { 58 | super(settings); 59 | 60 | this.settings = settings; 61 | this.restController = restController; 62 | this.client = client; 63 | securityConfigurationIndex = settings.get( 64 | "security.configuration.index", DEFAULT_SECURITY_CONFIG_INDEX); 65 | 66 | strictModeEnabled = settings.getAsBoolean( 67 | "security.strict", false); 68 | 69 | } 70 | 71 | public boolean isStrictModeEnabled() { 72 | return strictModeEnabled; 73 | } 74 | 75 | public Client getClient() { 76 | return client; 77 | } 78 | 79 | @Override 80 | protected void doStart() throws ElasticsearchException { 81 | 82 | // TODO order 83 | 84 | final Boolean enableActionPathFilter = settings.getAsBoolean( 85 | "security.module.actionpathfilter.enabled", true); 86 | 87 | if (enableActionPathFilter != null 88 | && enableActionPathFilter.booleanValue()) { 89 | restController.registerFilter(new ActionPathFilter(this)); 90 | } 91 | 92 | // this.restController 93 | // .registerFilter(new FieldLevelPermissionFilter(this)); 94 | // this.restController.registerFilter(new FieldResponseFilter(this)); 95 | 96 | // this.logger.debug("security.configuration.index=" 97 | // + this.securityConfigurationIndex); 98 | 99 | // TODO disable dynamic scripting for this node 100 | // https://github.com/yakaz/elasticsearch-action-reloadsettings/blob/master/src/main/java/org/elasticsearch/action/reloadsettings/ESInternalSettingsPerparer.java 101 | // client.execute(action, request) 102 | 103 | } 104 | 105 | @Override 106 | protected void doStop() throws ElasticsearchException { 107 | 108 | logger.debug("doStop"); 109 | } 110 | 111 | @Override 112 | protected void doClose() throws ElasticsearchException { 113 | logger.debug("doClose"); 114 | 115 | } 116 | 117 | public String getXContentSecurityConfiguration(final String type, 118 | final String id) throws IOException, 119 | MalformedConfigurationException { 120 | try { 121 | return XContentHelper.convertToJson( 122 | getXContentSecurityConfigurationAsBR(type, id), true); 123 | } catch (final IOException e) { 124 | logger.error("Unable to load type {} and id {} due to {}", 125 | type, id, e); 126 | return null; 127 | } 128 | } 129 | 130 | public BytesReference getXContentSecurityConfigurationAsBR( 131 | final String type, final String id) 132 | throws MalformedConfigurationException { 133 | final GetResponse resp = client 134 | .prepareGet(securityConfigurationIndex, type, id) 135 | .setRefresh(true).setOperationThreaded(false).get(); 136 | 137 | if (resp.isExists()) { 138 | return resp.getSourceAsBytesRef(); 139 | } else { 140 | throw new MalformedConfigurationException("document type " + type 141 | + " with id " + id + " does not exists"); 142 | } 143 | } 144 | 145 | public String getSecurityConfigurationIndex() { 146 | return securityConfigurationIndex; 147 | } 148 | 149 | public InetAddress getHostAddressFromRequest(final RestRequest request) 150 | throws UnknownHostException { 151 | 152 | // this.logger.debug(request.getClass().toString()); 153 | 154 | final String oaddr = ((TomcatHttpServerRestRequest) request).remoteAddr(); 155 | // this.logger.debug("original hostname: " + addr); 156 | 157 | String raddr = oaddr; 158 | 159 | if (oaddr == null || oaddr.isEmpty()) { 160 | throw new UnknownHostException("Original host is or "); 161 | } 162 | 163 | final InetAddress iaddr = InetAddress.getByName(oaddr); 164 | 165 | // security.http.xforwardfor.header 166 | // security.http.xforwardfor.trustedproxies 167 | // security.http.xforwardfor.enforce 168 | final String xForwardedForHeader = settings 169 | .get("security.http.xforwardedfor.header"); 170 | 171 | if (xForwardedForHeader != null && !xForwardedForHeader.isEmpty()) { 172 | 173 | final String xForwardedForValue = request 174 | .header(xForwardedForHeader); 175 | 176 | logger.debug("xForwardedForHeader is " + xForwardedForHeader 177 | + ":" + xForwardedForValue); 178 | 179 | final String xForwardedTrustedProxiesS = settings 180 | .get("security.http.xforwardedfor.trustedproxies"); 181 | // TODO use yaml list 182 | final String[] xForwardedTrustedProxies = xForwardedTrustedProxiesS == null ? new String[0] 183 | : xForwardedTrustedProxiesS.replace(" ", "").split(","); 184 | 185 | final boolean xForwardedEnforce = settings.getAsBoolean( 186 | "security.http.xforwardedfor.enforce", false); 187 | 188 | if (xForwardedForValue != null && !xForwardedForValue.isEmpty()) { 189 | final List addresses = Arrays.asList(xForwardedForValue 190 | .replace(" ", "").split(",")); 191 | final List proxiesPassed = new ArrayList( 192 | addresses.subList(1, addresses.size())); 193 | 194 | if (xForwardedTrustedProxies.length == 0) { 195 | throw new UnknownHostException("No trusted proxies"); 196 | } 197 | 198 | proxiesPassed 199 | .removeAll(Arrays.asList(xForwardedTrustedProxies)); 200 | 201 | logger.debug(proxiesPassed.size() + "/" + proxiesPassed); 202 | 203 | if (proxiesPassed.size() == 0 204 | && (Arrays.asList(xForwardedTrustedProxies).contains( 205 | oaddr) || iaddr.isLoopbackAddress())) { 206 | 207 | raddr = addresses.get(0).trim(); 208 | 209 | } else { 210 | throw new UnknownHostException( 211 | "Not all proxies are trusted"); 212 | } 213 | 214 | } else { 215 | if (xForwardedEnforce) { 216 | throw new UnknownHostException( 217 | "Forward header enforced but not present"); 218 | } 219 | } 220 | 221 | } 222 | 223 | if (raddr == null || raddr.isEmpty()) { 224 | throw new UnknownHostException("Host is or "); 225 | } 226 | 227 | if(raddr.equals(oaddr)) { 228 | return iaddr; 229 | } else { 230 | // if null or "" then loopback is returned 231 | return InetAddress.getByName(raddr); 232 | } 233 | 234 | } 235 | 236 | @SuppressWarnings("unchecked") 237 | public List parseDlsPermissions(final BytesReference br) 238 | throws IOException, MalformedConfigurationException { 239 | 240 | final List perms = new ArrayList(); 241 | 242 | final List dlsPermissions = new ArrayList(); 243 | 244 | String json = XContentHelper.convertToJson(br, false); 245 | 246 | if (json.contains("\"hits\":{\"total\":0,\"max_score\":null")) { 247 | // no hits 248 | logger.debug("No hits, return ALL permissions"); 249 | perms.add(DlsPermission.ALL_PERMISSION); 250 | return perms; 251 | } 252 | 253 | if (!json.contains("dlspermissions")) { 254 | json = XContentHelper.convertToJson(getXContentSecurityConfigurationAsBR("dlspermissions", 255 | "default"), false); 256 | } 257 | 258 | if (json.contains("_source")) { 259 | dlsPermissions.addAll((List) JsonPath.read(json, 260 | "$.hits.hits[*]._source.dlspermissions")); 261 | } else { 262 | dlsPermissions.add((JSONObject) JsonPath.read(json, 263 | "$.dlspermissions")); 264 | } 265 | 266 | for (final JSONObject dlsPermission : dlsPermissions) { 267 | 268 | if (dlsPermission == null) { 269 | continue; 270 | } 271 | 272 | for (final String field : dlsPermission.keySet()) { 273 | 274 | final DlsPermission dlsPerm = new DlsPermission(); 275 | dlsPerm.setField(field); 276 | 277 | JSONArray ja = (JSONArray) ((JSONObject) dlsPermission 278 | .get(field)).get("read"); 279 | dlsPerm.addReadTokens(ja.toArray(new String[0])); 280 | 281 | ja = (JSONArray) ((JSONObject) dlsPermission.get(field)) 282 | .get("update"); 283 | dlsPerm.addUpdateTokens(ja.toArray(new String[0])); 284 | 285 | ja = (JSONArray) ((JSONObject) dlsPermission.get(field)) 286 | .get("delete"); 287 | dlsPerm.addDeleteTokens(ja.toArray(new String[0])); 288 | 289 | perms.add(dlsPerm); 290 | } 291 | 292 | } 293 | 294 | return perms; 295 | 296 | } 297 | 298 | /** 299 | * (contributed by Ram Kotamaraja) 300 | * @param indices - List of ES indices. Now supports only one index at a time. 301 | * @return returns list of types configured in kibana security configuration 302 | * @throws IOException 303 | * @throws MalformedConfigurationException 304 | */ 305 | @SuppressWarnings("unchecked") 306 | public List getKibanaTypes(List indices) 307 | throws IOException, MalformedConfigurationException { 308 | 309 | // TODO - Support multiple indices return map 310 | final Map permsMap = new HashMap(); 311 | 312 | final Set perms = new HashSet(); 313 | 314 | final List kibanaPermissions = new ArrayList(); 315 | 316 | String json = null; 317 | 318 | json = XContentHelper.convertToJson(getXContentSecurityConfigurationAsBR("actionpathfilter", 319 | "kibana"), false); 320 | 321 | //logger.debug("Kibana Configuration: "+ json ); 322 | 323 | if(JsonPath.parse(json) != null){ 324 | kibanaPermissions.addAll((List) JsonPath.read(json,"rules")); 325 | } 326 | 327 | //logger.debug("After $.rules: "+ kibanaPermissions); 328 | 329 | kibanaLoop: 330 | for (final JSONObject kibanaPermission : kibanaPermissions) { 331 | 332 | if (kibanaPermission == null) { 333 | continue; 334 | } 335 | 336 | 337 | String index = null; 338 | 339 | //logger.debug("indices: "+indices); 340 | permLoop: 341 | for (final String field : kibanaPermission.keySet()) { 342 | 343 | 344 | //logger.debug("field: "+field + " :"+kibanaPermission.get(field)); 345 | //logger.debug("list contains ? "+indices.contains(kibanaPermission.get(field).toString().trim())); 346 | 347 | 348 | if(index == null && field.equals("index") 349 | && !indices.contains(kibanaPermission.get(field).toString())){ 350 | continue kibanaLoop; 351 | }else 352 | if(index == null && field.equals("index") 353 | && indices.contains(kibanaPermission.get(field).toString().trim())){ 354 | 355 | index = kibanaPermission.get(field).toString(); 356 | continue permLoop; 357 | }else 358 | if(index != null){ 359 | 360 | //logger.debug("field: types :"+kibanaPermission.get("types").getClass()); 361 | perms.addAll((Collection) kibanaPermission.get("types")); 362 | 363 | index = null; 364 | } 365 | } 366 | } 367 | 368 | //logger.debug("kibana perm list:"+ perms); 369 | 370 | List returnPerms = new ArrayList(); 371 | returnPerms.addAll(perms); 372 | 373 | return returnPerms; 374 | 375 | } 376 | 377 | 378 | } 379 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/plugins/security/service/permission/DlsPermission.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.plugins.security.service.permission; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | public class DlsPermission { 8 | 9 | public static DlsPermission ALL_PERMISSION = new DlsPermission(); 10 | 11 | static { 12 | ALL_PERMISSION.addDeleteToken("*"); 13 | ALL_PERMISSION.addReadToken("*"); 14 | ALL_PERMISSION.addUpdateToken("*"); 15 | ALL_PERMISSION.setField("*"); 16 | } 17 | 18 | private final List readTokens = new ArrayList(); 19 | private final List updateTokens = new ArrayList(); 20 | private final List deleteTokens = new ArrayList(); 21 | private String field; 22 | 23 | public void setField(final String field) { 24 | this.field = field.trim(); 25 | } 26 | 27 | @Override 28 | public String toString() { 29 | return "DlsPermission [readTokens=" + readTokens 30 | + ", updateTokens=" + updateTokens + ", deleteTokens=" 31 | + deleteTokens + ", field=" + field 32 | + ", isDefault()=" + isDefault() + "]"; 33 | } 34 | 35 | @Override 36 | public int hashCode() { 37 | final int prime = 31; 38 | int result = 1; 39 | result = prime 40 | * result 41 | + (deleteTokens == null ? 0 : deleteTokens.hashCode()); 42 | result = prime * result 43 | + (field == null ? 0 : field.hashCode()); 44 | result = prime * result 45 | + (readTokens == null ? 0 : readTokens.hashCode()); 46 | result = prime 47 | * result 48 | + (updateTokens == null ? 0 : updateTokens.hashCode()); 49 | return result; 50 | } 51 | 52 | @Override 53 | public boolean equals(final Object obj) { 54 | if (this == obj) { 55 | return true; 56 | } 57 | if (obj == null) { 58 | return false; 59 | } 60 | if (this.getClass() != obj.getClass()) { 61 | return false; 62 | } 63 | final DlsPermission other = (DlsPermission) obj; 64 | if (deleteTokens == null) { 65 | if (other.deleteTokens != null) { 66 | return false; 67 | } 68 | } else if (!deleteTokens.equals(other.deleteTokens)) { 69 | return false; 70 | } 71 | if (field == null) { 72 | if (other.field != null) { 73 | return false; 74 | } 75 | } else if (!field.equals(other.field)) { 76 | return false; 77 | } 78 | if (readTokens == null) { 79 | if (other.readTokens != null) { 80 | return false; 81 | } 82 | } else if (!readTokens.equals(other.readTokens)) { 83 | return false; 84 | } 85 | if (updateTokens == null) { 86 | if (other.updateTokens != null) { 87 | return false; 88 | } 89 | } else if (!updateTokens.equals(other.updateTokens)) { 90 | return false; 91 | } 92 | return true; 93 | } 94 | 95 | public DlsPermission() { 96 | 97 | } 98 | 99 | public boolean isDefault() { 100 | return field.equals("*"); 101 | } 102 | 103 | public boolean isAllowNone() { 104 | return readTokens.isEmpty() && updateTokens.isEmpty() 105 | && deleteTokens.isEmpty(); 106 | } 107 | 108 | public boolean isTokenAllowedToRead(final String token) { 109 | return readTokens.contains(token) || readTokens.contains("*"); 110 | } 111 | 112 | public boolean isTokenAllowedToUpdate(final String token) { 113 | return updateTokens.contains(token) 114 | || updateTokens.contains("*"); 115 | } 116 | 117 | public boolean isTokenAllowedToDelete(final String token) { 118 | return deleteTokens.contains(token) 119 | || deleteTokens.contains("*"); 120 | } 121 | 122 | public boolean isAnyTokenAllowedToDelete(final List tokens) { 123 | return !Collections.disjoint(deleteTokens, tokens) 124 | || deleteTokens.contains("*"); 125 | } 126 | 127 | public boolean isAnyTokenAllowedToRead(final List tokens) { 128 | return !Collections.disjoint(readTokens, tokens) 129 | || readTokens.contains("*"); 130 | } 131 | 132 | public boolean isAnyTokenAllowedToUpdate(final List tokens) { 133 | return !Collections.disjoint(updateTokens, tokens) 134 | || updateTokens.contains("*"); 135 | } 136 | 137 | public String getField() { 138 | return field; 139 | } 140 | 141 | public void addReadToken(final String token) { 142 | addToken(token, readTokens); 143 | } 144 | 145 | public void addReadTokens(final String[] tokens) { 146 | for (final String token : tokens) { 147 | addToken(token, readTokens); 148 | } 149 | } 150 | 151 | public void addUpdateTokens(final String[] tokens) { 152 | for (final String token : tokens) { 153 | addToken(token, updateTokens); 154 | } 155 | } 156 | 157 | public void addDeleteTokens(final String[] tokens) { 158 | for (final String token : tokens) { 159 | addToken(token, deleteTokens); 160 | } 161 | } 162 | 163 | public void addUpdateToken(final String token) { 164 | addToken(token, updateTokens); 165 | } 166 | 167 | public void addDeleteToken(final String token) { 168 | addToken(token, deleteTokens); 169 | } 170 | 171 | private void addToken(final String token, final List list) { 172 | if (token == null || token.trim().isEmpty() || token.contains(",")) { 173 | throw new IllegalArgumentException("'" + token 174 | + "' is not a valid dls token"); 175 | } 176 | list.add(token.trim()); 177 | } 178 | 179 | } 180 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/plugins/security/service/permission/PermEvaluator.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.plugins.security.service.permission; 2 | 3 | import java.net.InetAddress; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | import java.util.regex.Matcher; 7 | import java.util.regex.Pattern; 8 | 9 | import org.elasticsearch.common.logging.ESLogger; 10 | import org.elasticsearch.common.logging.Loggers; 11 | import org.elasticsearch.common.xcontent.XContentFactory; 12 | import org.elasticsearch.common.xcontent.XContentParser; 13 | import org.elasticsearch.plugins.security.MalformedConfigurationException; 14 | 15 | public abstract class PermEvaluator { 16 | 17 | protected static final ESLogger log = Loggers 18 | .getLogger(PermEvaluator.class); 19 | protected final String xSecurityConfiguration; 20 | protected XContentParser parser = null; 21 | 22 | protected PermEvaluator(final String xSecurityConfiguration) { 23 | super(); 24 | 25 | if (xSecurityConfiguration == null || xSecurityConfiguration.isEmpty()) { 26 | throw new IllegalArgumentException( 27 | "securityconfiguration must not be null or empty"); 28 | } 29 | 30 | this.xSecurityConfiguration = xSecurityConfiguration; 31 | 32 | // log.debug("Configuration: " + xSecurityConfiguration); 33 | } 34 | 35 | protected abstract T createFromString(String s); 36 | 37 | protected abstract T getDefaultPermLevelForEvaluator(); 38 | 39 | protected abstract String getPermissionFieldName(); 40 | 41 | public T evaluatePerm(final List indices, final List types, 42 | final InetAddress hostAddress, final UserRoleCallback callback) 43 | throws MalformedConfigurationException { 44 | 45 | /*if(indices==null || types == null || hostAddress == null) { 46 | throw new MalformedConfigurationException("Indices, types and hostAddress must not be null"); 47 | }*/ 48 | 49 | final List> perms = new ArrayList>(); 50 | 51 | //final List matchList = new ArrayList(); 52 | 53 | try { 54 | 55 | this.parser = XContentFactory.xContent(this.xSecurityConfiguration) 56 | .createParser(this.xSecurityConfiguration); 57 | 58 | final String permissionFieldName = this.getPermissionFieldName(); 59 | 60 | XContentParser.Token token = null; 61 | String currentFieldName = null; 62 | Perm currentPerm = null; 63 | while ((token = this.parser.nextToken()) != null) { 64 | 65 | if (token == XContentParser.Token.START_OBJECT) { 66 | currentPerm = new Perm(); 67 | 68 | } else if (token == XContentParser.Token.END_OBJECT) { 69 | 70 | if (currentPerm != null && perms.contains(currentPerm)) { 71 | log.error("Duplicate permissions " + currentPerm); 72 | throw new MalformedConfigurationException( 73 | "Duplicate permissions found"); 74 | } 75 | 76 | /*if (currentPerm != null && !currentPerm.isValid()) { 77 | log.error("Perm not valid " + currentPerm); 78 | throw new MalformedConfigurationException( 79 | "Invalid permission found"); 80 | }*/ 81 | 82 | if (currentPerm != null) { 83 | 84 | if(currentPerm.permLevel == null){ 85 | currentPerm.permLevel = getDefaultPermLevelForEvaluator(); 86 | } 87 | 88 | perms.add(currentPerm); 89 | currentPerm = null; 90 | } 91 | 92 | } else if (token == XContentParser.Token.FIELD_NAME) { 93 | currentFieldName = this.parser.currentName(); 94 | 95 | } else if (token.isValue()) { 96 | 97 | if ("hosts".equals(currentFieldName)) { 98 | currentPerm.addInetAddress(this.parser.text()); 99 | } 100 | if ("users".equals(currentFieldName)) { 101 | currentPerm.addUser(this.parser.text()); 102 | } 103 | if ("roles".equals(currentFieldName)) { 104 | currentPerm.addRole(this.parser.text()); 105 | } else if ("indices".equals(currentFieldName)) { 106 | currentPerm.addIndice(this.parser.text()); 107 | } else if ("types".equals(currentFieldName)) { 108 | currentPerm.addType(this.parser.text()); 109 | } else if (permissionFieldName.equals(currentFieldName)) { 110 | final String text = this.parser.text(); 111 | currentPerm.setPermLevel(this 112 | .createFromString(text == null ? null : text 113 | .trim())); 114 | } 115 | 116 | } 117 | 118 | } 119 | 120 | } catch (final Exception e) { 121 | throw new MalformedConfigurationException(e); 122 | } finally { 123 | this.parser.close(); 124 | } 125 | 126 | 127 | 128 | 129 | 130 | 131 | log.debug("Checking " + perms.size() + " perms"); 132 | 133 | T permLevel = null; 134 | 135 | for (final Perm p : perms) { 136 | 137 | if (p.isDefault()) { 138 | permLevel = p.permLevel; 139 | if (log.isDebugEnabled()) { 140 | log.debug("Default set to " + permLevel); 141 | } 142 | break; 143 | } 144 | } 145 | 146 | if (permLevel == null) { 147 | throw new MalformedConfigurationException( 148 | "No default configuration found"); 149 | } 150 | 151 | /*for (final Perm p : perms) { 152 | 153 | for (final String ip : p.inetAddresses) { 154 | //matchList.add(new WildcardString(ip)); 155 | } 156 | }*/ 157 | 158 | final String clientHostName = hostAddress.getHostName(); 159 | final String clientHostIp = hostAddress.getHostAddress(); 160 | 161 | permloop: for (final Perm p : perms) { 162 | 163 | if (p.isDefault()) { 164 | continue; 165 | } 166 | 167 | String _role = null; 168 | String _host = null; 169 | 170 | log.debug("Check perm " + p); 171 | 172 | // TODO difference between not here and [] 173 | if (!p.users.isEmpty() 174 | && !p.users.contains("*") 175 | && (callback == null || callback.getRemoteuser() == null || !p.users 176 | .contains(callback.getRemoteuser()))) { 177 | if(callback != null) { 178 | log.debug("User " + callback.getRemoteuser() 179 | + " does not match, so skip this permission"); 180 | } else { 181 | log.debug("No callback"); 182 | } 183 | continue permloop; 184 | } 185 | 186 | log.debug("User " 187 | + (callback == null ? "" : callback.getRemoteuser()) 188 | + " match"); 189 | 190 | if (!p.roles.contains("*") && !p.roles.isEmpty()) { 191 | if (callback == null) { 192 | log.debug("Role does not match, so skip this permission"); 193 | continue permloop; 194 | } 195 | 196 | for (final String role : p.roles) { 197 | if (callback.isRemoteUserInRole(role)) { 198 | log.debug("Role " + role + " match"); 199 | _role = role; 200 | break; 201 | } 202 | } 203 | 204 | if (_role == null) { 205 | log.debug("Role does not match, so skip this permission"); 206 | continue permloop; 207 | } 208 | } 209 | 210 | if (!p.inetAddresses.contains("*") && !p.inetAddresses.isEmpty()) { 211 | for (final String pinetAddress : p.inetAddresses) { 212 | if (isWildcardMatch(pinetAddress, clientHostName) 213 | || isWildcardMatch(pinetAddress, clientHostIp)) { 214 | 215 | log.debug("Host adress " + pinetAddress + " match"); 216 | _host = pinetAddress; 217 | break; 218 | 219 | } 220 | 221 | } 222 | 223 | if (_host == null) { 224 | 225 | log.debug("Host adress (" 226 | + clientHostIp 227 | + "(ip) and " 228 | + clientHostName 229 | + " (hostname) does not match, so skip this permission"); 230 | continue permloop; 231 | 232 | } 233 | 234 | } 235 | 236 | if (!p.types.isEmpty() && !p.types.contains("*")) { 237 | 238 | boolean typeMatch=false; 239 | 240 | typeloop: 241 | for (final String pType : p.types) { 242 | 243 | for (final String tType : types) 244 | { 245 | if (isWildcardMatch(tType, pType)) { 246 | log.debug("Type "+pType+" match " + tType + ""); 247 | typeMatch=true; 248 | break typeloop; 249 | 250 | }else { 251 | log.debug("Type "+pType+" not match " + tType + ""); 252 | } 253 | 254 | } 255 | } 256 | 257 | if(!typeMatch){ 258 | log.debug("No type matches, so skip this permission [" 259 | + p.types + " != " + types + "]"); 260 | continue permloop; 261 | } 262 | } 263 | 264 | log.debug("All types matches"); 265 | 266 | if ( !p.indices.isEmpty() && !p.indices.contains("*")) { 267 | 268 | 269 | boolean indexMatch=false; 270 | 271 | indexloop: 272 | for (final String pIndex : p.indices) { 273 | 274 | if(indices != null) { 275 | for (final String tIndex : indices) 276 | { 277 | if (isWildcardMatch(tIndex, pIndex)) { 278 | log.debug("Index "+pIndex+" match " + tIndex + ""); 279 | indexMatch=true; 280 | break indexloop; 281 | 282 | }else { 283 | log.debug("Index "+pIndex+" not match " + tIndex + ""); 284 | } 285 | 286 | } 287 | } 288 | 289 | } 290 | 291 | if(!indexMatch) 292 | { 293 | 294 | log.debug("No index matches, so skip this permission [" 295 | + p.indices + " != " + indices + "]"); 296 | continue permloop; 297 | } 298 | 299 | 300 | } 301 | 302 | 303 | //START 304 | //added condition to check if indices provided are empty to validate the matching of index. This is required to allow requesting metadata queries like /_mapping, /_setting etc. 305 | //(contributed by Ram Kotamaraja) 306 | else 307 | if((indices == null || indices.isEmpty()) && !p.indices.isEmpty() && !p.indices.contains("*") ){ 308 | 309 | log.debug("Not all indexes match because no index specified, so skip this permission [" 310 | + p.indices + " != " + indices + "]"); 311 | continue permloop; 312 | 313 | } 314 | //END 315 | 316 | log.debug("All rules match, will apply " + p); 317 | return p.permLevel; 318 | 319 | }// end permloop 320 | 321 | log.debug("No rules matched, will apply default perm " + permLevel); 322 | return permLevel; 323 | 324 | } 325 | 326 | protected static class Perm { 327 | 328 | private final List inetAddresses = new ArrayList(); 329 | private final List users = new ArrayList(); 330 | private final List roles = new ArrayList(); 331 | private final List indices = new ArrayList(); 332 | private final List types = new ArrayList(); 333 | 334 | private T permLevel = null; 335 | 336 | public boolean isValid() { 337 | return this.permLevel != null; 338 | } 339 | 340 | // default is either all props empty and/or "*" 341 | public boolean isDefault() { 342 | 343 | if (this.inetAddresses.isEmpty() && this.users.isEmpty() 344 | && this.roles.isEmpty() && this.indices.isEmpty() 345 | && this.types.isEmpty()) { 346 | return true; 347 | } 348 | 349 | return (this.inetAddresses.isEmpty() ? true : this.inetAddresses 350 | .size() == 1 && "*".equals(this.inetAddresses.get(0))) 351 | && (this.users.isEmpty() ? true : this.users.size() == 1 352 | && "*".equals(this.users.get(0))) 353 | && (this.roles.isEmpty() ? true : this.roles.size() == 1 354 | && "*".equals(this.roles.get(0))) 355 | && (this.types.isEmpty() ? true : this.types.size() == 1 356 | && "*".equals(this.types.get(0))) 357 | && (this.indices.isEmpty() ? true 358 | : this.indices.size() == 1 359 | && "*".equals(this.indices.get(0))); 360 | } 361 | 362 | public void addInetAddress(final String inetAddress) { 363 | if (inetAddress == null || inetAddress.isEmpty() 364 | || inetAddress.contains(",")) { 365 | throw new IllegalArgumentException("'" + inetAddress 366 | + "' is not a valid inet address"); 367 | } 368 | this.inetAddresses.add(inetAddress.trim()); 369 | } 370 | 371 | public void addIndice(final String indice) { 372 | if (indice == null || indice.isEmpty() || indice.contains(",")) { 373 | throw new IllegalArgumentException("'" + indice 374 | + "' is not a valid index name"); 375 | } 376 | this.indices.add(indice.trim()); 377 | } 378 | 379 | public void addUser(final String user) { 380 | if (user == null || user.isEmpty() || user.contains(",")) { 381 | throw new IllegalArgumentException("'" + user 382 | + "' is not a valid user"); 383 | } 384 | this.users.add(user.trim()); 385 | } 386 | 387 | public void addRole(final String role) { 388 | if (role == null || role.isEmpty() || role.contains(",")) { 389 | throw new IllegalArgumentException("'" + role 390 | + "' is not a valid role"); 391 | } 392 | this.roles.add(role.trim()); 393 | } 394 | 395 | public void addType(final String type) { 396 | if (type == null || type.isEmpty() || type.contains(",")) { 397 | throw new IllegalArgumentException("'" + type 398 | + "' is not a valid type"); 399 | } 400 | this.types.add(type.trim()); 401 | } 402 | 403 | public void setPermLevel(final T permLevel) { 404 | 405 | if (permLevel == null) { 406 | throw new IllegalArgumentException("'" + permLevel 407 | + "' is not a valid permLevel"); 408 | } 409 | 410 | this.permLevel = permLevel; 411 | } 412 | 413 | public Perm() { 414 | 415 | } 416 | 417 | @Override 418 | public int hashCode() { 419 | final int prime = 31; 420 | int result = 1; 421 | result = prime * result 422 | + (this.indices == null ? 0 : this.indices.hashCode()); 423 | result = prime 424 | * result 425 | + (this.inetAddresses == null ? 0 : this.inetAddresses 426 | .hashCode()); 427 | result = prime * result 428 | + (this.roles == null ? 0 : this.roles.hashCode()); 429 | result = prime * result 430 | + (this.types == null ? 0 : this.types.hashCode()); 431 | result = prime * result 432 | + (this.users == null ? 0 : this.users.hashCode()); 433 | return result; 434 | } 435 | 436 | @Override 437 | public boolean equals(final Object obj) { 438 | if (this == obj) { 439 | // log.debug("perm =="); 440 | return true; 441 | } 442 | if (obj == null) { 443 | // log.debug("perm other null"); 444 | return false; 445 | } 446 | if (this.getClass() != obj.getClass()) { 447 | // log.debug("perm class mismatch"); 448 | return false; 449 | } 450 | final Perm other = (Perm) obj; 451 | if (this.indices == null) { 452 | if (other.indices != null) { 453 | return false; 454 | } 455 | } else if (!equalLists(this.indices, other.indices)) { 456 | // log.debug("perm list not match: indices"); 457 | return false; 458 | } 459 | if (this.inetAddresses == null) { 460 | if (other.inetAddresses != null) { 461 | return false; 462 | } 463 | } else if (!equalLists(this.inetAddresses, other.inetAddresses)) { 464 | // log.debug("perm list not match: inetaddr"); 465 | return false; 466 | } 467 | if (this.roles == null) { 468 | if (other.roles != null) { 469 | return false; 470 | } 471 | } else if (!equalLists(this.roles, other.roles)) { 472 | // log.debug("perm list not match: roles"); 473 | return false; 474 | } 475 | if (this.users == null) { 476 | if (other.users != null) { 477 | return false; 478 | } 479 | } else if (!equalLists(this.users, other.users)) { 480 | // log.debug("perm list not match: users"); 481 | return false; 482 | } 483 | 484 | if (this.types == null) { 485 | if (other.types != null) { 486 | return false; 487 | } 488 | } else if (!equalLists(this.types, other.types)) { 489 | // log.debug("perm list not match: types"); 490 | return false; 491 | } 492 | 493 | return true; 494 | } 495 | 496 | @Override 497 | public String toString() { 498 | return "Perm [inetAddresses=" + this.inetAddresses + ", users=" 499 | + this.users + ", roles=" + this.roles + ", indices=" 500 | + this.indices + ", types=" + this.types + ", permLevel=" 501 | + this.permLevel + ", isValid()=" + this.isValid() 502 | + ", isDefault()=" + this.isDefault() + "]"; 503 | } 504 | 505 | } 506 | 507 | private static boolean equalLists(final List one, 508 | final List two) { 509 | if (one == null && two == null) { 510 | return true; 511 | } 512 | 513 | if (one == null && two != null || one != null && two == null 514 | || one.size() != two.size()) { 515 | return false; 516 | } 517 | 518 | return one.containsAll(two) && two.containsAll(one); 519 | } 520 | 521 | 522 | private boolean isWildcardMatch(String a, String b) { 523 | 524 | String escapedA = a.replace(".", "\\.").replace("*", 525 | ".*"); 526 | 527 | Pattern p = Pattern.compile(escapedA); 528 | Matcher m = p.matcher(b); 529 | if(m.matches()){ 530 | return true; 531 | } else { 532 | String escapedB = b.replace(".", "\\.").replace("*", 533 | ".*"); 534 | 535 | p = Pattern.compile(escapedB); 536 | m = p.matcher(a); 537 | return m.matches(); 538 | } 539 | } 540 | 541 | } 542 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/plugins/security/service/permission/UserRoleCallback.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.plugins.security.service.permission; 2 | 3 | public interface UserRoleCallback { 4 | 5 | public String getRemoteuser(); 6 | 7 | public boolean isRemoteUserInRole(String role); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/plugins/security/util/EditableRestRequest.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.plugins.security.util; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.Map.Entry; 6 | 7 | import org.elasticsearch.common.bytes.BytesReference; 8 | import org.elasticsearch.rest.RestRequest; 9 | 10 | 11 | public class EditableRestRequest extends RestRequest { 12 | 13 | private RestRequest innerRestquest = null; 14 | private BytesReference content = null; 15 | private Map params = null; 16 | private Method method = null; 17 | private String uri = null; 18 | private String rawPath = null; 19 | private boolean hasContent; 20 | private boolean contentUnsafe; 21 | 22 | public EditableRestRequest(final RestRequest innerRestquest) { 23 | 24 | this.innerRestquest = innerRestquest; 25 | content = innerRestquest.content(); 26 | params = innerRestquest.params(); 27 | method = innerRestquest.method(); 28 | uri = innerRestquest.uri(); 29 | rawPath = innerRestquest.rawPath(); 30 | hasContent = innerRestquest.hasContent(); 31 | contentUnsafe = innerRestquest.contentUnsafe(); 32 | 33 | } 34 | 35 | public void addParam(String key, String value){ 36 | if(params==null){ 37 | params=new HashMap(); 38 | } 39 | params.put(key, value); 40 | } 41 | 42 | public void setContent(final BytesReference content) { 43 | this.content = content; 44 | } 45 | 46 | public void setParams(final Map params) { 47 | this.params = params; 48 | } 49 | 50 | public void setMethod(final Method method) { 51 | this.method = method; 52 | } 53 | 54 | public void setUri(final String uri) { 55 | this.uri = uri; 56 | } 57 | 58 | public void setRawPath(final String rawPath) { 59 | this.rawPath = rawPath; 60 | } 61 | 62 | public void setHasContent(final boolean hasContent) { 63 | this.hasContent = hasContent; 64 | } 65 | 66 | public void setContentUnsafe(final boolean contentUnsafe) { 67 | this.contentUnsafe = contentUnsafe; 68 | } 69 | 70 | @Override 71 | public Method method() { 72 | return method; 73 | } 74 | 75 | @Override 76 | public String uri() { 77 | return uri; 78 | } 79 | 80 | @Override 81 | public String rawPath() { 82 | 83 | return rawPath; 84 | } 85 | 86 | @Override 87 | public boolean hasContent() { 88 | 89 | return hasContent; 90 | } 91 | 92 | @Override 93 | public boolean contentUnsafe() { 94 | 95 | return contentUnsafe; 96 | } 97 | 98 | @Override 99 | public BytesReference content() { 100 | 101 | return content; 102 | } 103 | 104 | @Override 105 | public String header(final String name) { 106 | 107 | return innerRestquest.header(name); 108 | } 109 | 110 | @Override 111 | public boolean hasParam(final String key) { 112 | 113 | return params.containsKey(key); 114 | } 115 | 116 | @Override 117 | public String param(final String key) { 118 | 119 | return params.get(key); 120 | } 121 | 122 | @Override 123 | public Map params() { 124 | 125 | return params; 126 | } 127 | 128 | @Override 129 | public String param(final String key, final String defaultValue) { 130 | final String value = params.get(key); 131 | if (value == null) { 132 | return defaultValue; 133 | } 134 | return value; 135 | } 136 | 137 | @Override 138 | public Iterable> headers() { 139 | return innerRestquest.headers(); 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/plugins/security/util/SecurityUtil.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.plugins.security.util; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.io.UnsupportedEncodingException; 6 | import java.net.URL; 7 | import java.net.URLDecoder; 8 | import java.util.Arrays; 9 | import java.util.List; 10 | 11 | import org.elasticsearch.common.Strings; 12 | import org.elasticsearch.common.logging.ESLogger; 13 | import org.elasticsearch.common.logging.Loggers; 14 | import org.elasticsearch.common.xcontent.XContentBuilder; 15 | import org.elasticsearch.common.xcontent.XContentFactory; 16 | import org.elasticsearch.common.xcontent.XContentHelper; 17 | import org.elasticsearch.common.xcontent.XContentType; 18 | import org.elasticsearch.rest.BytesRestResponse; 19 | import org.elasticsearch.rest.RestChannel; 20 | import org.elasticsearch.rest.RestRequest; 21 | import org.elasticsearch.rest.RestRequest.Method; 22 | import org.elasticsearch.rest.RestResponse; 23 | import org.elasticsearch.rest.RestStatus; 24 | 25 | public class SecurityUtil { 26 | 27 | private static final ESLogger log = Loggers.getLogger(SecurityUtil.class); 28 | 29 | private SecurityUtil() { 30 | 31 | } 32 | 33 | public static File getAbsoluteFilePathFromClassPath( 34 | final String fileNameFromClasspath) { 35 | 36 | File jaasConfigFile = null; 37 | final URL jaasConfigURL = SecurityUtil.class.getClassLoader() 38 | .getResource(fileNameFromClasspath); 39 | if (jaasConfigURL != null) { 40 | try { 41 | jaasConfigFile = new File(URLDecoder.decode( 42 | jaasConfigURL.getFile(), "UTF-8")); 43 | } catch (final UnsupportedEncodingException e) { 44 | return null; 45 | } 46 | 47 | if (jaasConfigFile.exists() && jaasConfigFile.canRead()) { 48 | return jaasConfigFile; 49 | } else { 50 | log.error( 51 | "Cannot read from {}, maybe the file does not exists? ", 52 | jaasConfigFile.getAbsolutePath()); 53 | } 54 | 55 | } else { 56 | log.error("Failed to load " + fileNameFromClasspath); 57 | } 58 | 59 | return null; 60 | 61 | } 62 | 63 | public static boolean setSystemPropertyToAbsoluteFilePathFromClassPath( 64 | final String property, final String fileNameFromClasspath) { 65 | if (System.getProperty(property) == null) { 66 | File jaasConfigFile = null; 67 | final URL jaasConfigURL = SecurityUtil.class.getClassLoader() 68 | .getResource(fileNameFromClasspath); 69 | if (jaasConfigURL != null) { 70 | try { 71 | jaasConfigFile = new File(URLDecoder.decode( 72 | jaasConfigURL.getFile(), "UTF-8")); 73 | } catch (final UnsupportedEncodingException e) { 74 | return false; 75 | } 76 | 77 | if (jaasConfigFile.exists() && jaasConfigFile.canRead()) { 78 | System.setProperty(property, 79 | jaasConfigFile.getAbsolutePath()); 80 | 81 | log.info("Load " + fileNameFromClasspath + " from {} ", 82 | jaasConfigFile.getAbsolutePath()); 83 | return true; 84 | } else { 85 | log.error( 86 | "Cannot read from {}, maybe the file does not exists? ", 87 | jaasConfigFile.getAbsolutePath()); 88 | } 89 | 90 | } else { 91 | log.error("Failed to load " + fileNameFromClasspath); 92 | } 93 | } else { 94 | log.warn("Property " + property + " already set to " 95 | + System.getProperty(property)); 96 | } 97 | 98 | return false; 99 | } 100 | 101 | public static boolean setSystemPropertyToAbsoluteFile( 102 | final String property, final String fileName) { 103 | if (System.getProperty(property) == null) { 104 | 105 | if (fileName == null) { 106 | log.error("Cannot set property " + property 107 | + " because filename is null"); 108 | 109 | return false; 110 | } 111 | 112 | final File jaasConfigFile = new File(fileName).getAbsoluteFile(); 113 | 114 | if (jaasConfigFile.exists() && jaasConfigFile.canRead()) { 115 | System.setProperty(property, jaasConfigFile.getAbsolutePath()); 116 | 117 | log.info("Load " + fileName + " from {} ", 118 | jaasConfigFile.getAbsolutePath()); 119 | return true; 120 | } else { 121 | log.error( 122 | "Cannot read from {}, maybe the file does not exists? ", 123 | jaasConfigFile.getAbsolutePath()); 124 | } 125 | 126 | } else { 127 | log.warn("Property " + property + " already set to " 128 | + System.getProperty(property)); 129 | } 130 | 131 | return false; 132 | } 133 | 134 | public static List getIndices(final RestRequest request) { 135 | String[] indices = new String[0]; 136 | final String path = request.path(); 137 | // TODO all indices , length=0 138 | log.debug("Evaluate decoded path for indices'" + path + "'"); 139 | 140 | if (!path.startsWith("/")) { 141 | 142 | return null; 143 | } 144 | 145 | if (path.length() > 1) { 146 | 147 | int endIndex; 148 | 149 | 150 | 151 | 152 | /* if ((endIndex = path.indexOf('/', 1)) != -1) { 153 | indices = Strings.splitStringByCommaToArray(path.substring(1, 154 | endIndex)); 155 | 156 | } 157 | */ 158 | /** 159 | * (contributed by Ram Kotamaraja) 160 | *The above commented code handles code if there is a '/' at the end of the elastic search indices. Code is modified to handle indices where there is no '/' after it. 161 | *Code below also handles the root level queries like '/_mapping', '/_settings' etc. 162 | */ 163 | 164 | //Code modification START - Ram Kotamaraja 165 | if ((path.indexOf('/', 1)) != -1) { 166 | endIndex = path.indexOf('/', 1); 167 | }else{ 168 | endIndex = path.length(); 169 | } 170 | 171 | //check if the index start with /_. If it is not staring, then parse path, if not do nothing to return empty object 172 | if (!path.trim().startsWith("/_")) { 173 | indices = Strings.splitStringByCommaToArray(path.substring(1,endIndex)); 174 | } 175 | 176 | //Code modification END - Ram Kotamaraja 177 | 178 | 179 | 180 | 181 | } 182 | 183 | log.debug("Indices: " + Arrays.toString(indices)); 184 | return Arrays.asList(indices); 185 | 186 | } 187 | 188 | public static String getId(final RestRequest request) { 189 | 190 | String id = null; 191 | final String path = request.path(); 192 | 193 | log.debug("Evaluate decoded path for id '" + path + "'"); 194 | 195 | if (!path.startsWith("/")) { 196 | 197 | return null; 198 | } 199 | 200 | if (path.length() > 1) { 201 | 202 | int endIndex; 203 | 204 | if ((endIndex = path.lastIndexOf('/')) != -1) { 205 | id = path.substring(endIndex + 1); 206 | 207 | if (id.contains("?")) { 208 | id = path.substring(id.indexOf("?") + 1); 209 | 210 | } 211 | 212 | // if(id.contains("/")) return null; 213 | 214 | } 215 | } 216 | 217 | log.debug("Id: " + id); 218 | return id; 219 | 220 | } 221 | 222 | public static List getTypes(final RestRequest request) { 223 | String[] types = new String[0]; 224 | final String path = request.path(); 225 | 226 | // TODO all types, length=0 or _all ?? 227 | // TODO aliases indices get expanded before or after rest layer? 228 | log.debug("Evaluate decoded path for types '" + path + "'"); 229 | 230 | if (!path.startsWith("/")) { 231 | 232 | return null; 233 | } 234 | 235 | if (path.length() > 1) { 236 | 237 | int endIndex; 238 | 239 | if ((endIndex = path.indexOf('/', 1)) != -1) { 240 | 241 | int endType; 242 | 243 | if ((endType = path.indexOf('/', endIndex + 1)) != -1) { 244 | 245 | types = Strings.splitStringByCommaToArray(path.substring( 246 | endIndex + 1, endType)); 247 | } 248 | 249 | } 250 | } 251 | 252 | log.debug("Types: " + Arrays.toString(types)); 253 | return Arrays.asList(types); 254 | 255 | } 256 | 257 | public static void send(final RestRequest request, 258 | final RestChannel channel, final RestStatus status, final String arg) { 259 | try { 260 | 261 | final XContentBuilder builder = channel.newBuilder(); 262 | 263 | builder.startObject(); 264 | builder.field("status", status.getStatus()); 265 | 266 | if (arg != null && !arg.isEmpty()) { 267 | builder.field("message", arg); 268 | } 269 | 270 | builder.endObject(); 271 | channel.sendResponse(new BytesRestResponse(status, builder)); 272 | } catch (final Exception e) { 273 | log.error("Failed to send a response.", e); 274 | try { 275 | channel.sendResponse(new BytesRestResponse(channel, 276 | e)); 277 | } catch (final IOException e1) { 278 | log.error("Failed to send a failure response.", e1); 279 | } 280 | } 281 | } 282 | 283 | 284 | public static String[] BUILT_IN_ADMIN_COMMANDS = new String[] { "_cluster", 285 | "_settings", "_close", "_open", "_template", "_status", "_stats", 286 | "_segments", "_cache", "_gateway", "_optimize", "_flush", 287 | "_warmer", "_refresh", "_shutdown"}; 288 | 289 | public static String[] BUILT_IN_WRITE_COMMANDS_STRICT = new String[] { "_create", 290 | "_update", "_bulk", "_percolator","_mapping", "_aliases", "_analyze"}; 291 | 292 | public static String[] BUILT_IN_READ_COMMANDS_STRICT = new String[] { "_search", 293 | "_msearch","_mlt", "_explain", "_validate","_count","_suggest", "_percolate", "_nodes"}; 294 | 295 | 296 | public static String[] BUILT_IN_WRITE_COMMANDS_LAX = new String[] { "_create", 297 | "_update", "_bulk"}; 298 | 299 | public static String[] BUILT_IN_READ_COMMANDS_LAX = new String[] { "_search", 300 | "_msearch","_mlt", "_explain", "_validate","_count","_suggest", "_percolate", "_nodes", "_percolator","_mapping", "_aliases", "_analyze"}; 301 | 302 | private static boolean stringContainsItemFromListAsCommand( 303 | final String inputString, final String[] items) { 304 | 305 | for (int i = 0; i < items.length; i++) { 306 | if (inputString.contains("/" + items[i]) 307 | && !inputString.contains(items[i] + "/")) { 308 | 309 | return true; 310 | } 311 | } 312 | 313 | return false; 314 | } 315 | 316 | public static boolean stringContainsItemFromListAsTypeOrIndex( 317 | final String inputString, final String[] items) { 318 | for (int i = 0; i < items.length; i++) { 319 | if (inputString.contains("/" + items[i] + "/")) { 320 | return true; 321 | } 322 | } 323 | return false; 324 | } 325 | 326 | public static boolean isWriteRequest(final RestRequest request, boolean strictModeEnabled) { 327 | if (request.method() == Method.DELETE || request.method() == Method.PUT) { 328 | return true; 329 | } 330 | 331 | if (request.method() == Method.POST) { 332 | if (!stringContainsItemFromListAsCommand(request.path(), 333 | strictModeEnabled?SecurityUtil.BUILT_IN_READ_COMMANDS_STRICT : SecurityUtil.BUILT_IN_READ_COMMANDS_LAX)) { 334 | return true; 335 | } 336 | } 337 | 338 | return stringContainsItemFromListAsCommand(request.path(), 339 | strictModeEnabled?SecurityUtil.BUILT_IN_WRITE_COMMANDS_STRICT : SecurityUtil.BUILT_IN_WRITE_COMMANDS_LAX); 340 | } 341 | 342 | public static boolean isAdminRequest(final RestRequest request) { 343 | return stringContainsItemFromListAsCommand(request.path(), 344 | BUILT_IN_ADMIN_COMMANDS); 345 | } 346 | 347 | public static boolean isReadRequest(final RestRequest request, boolean strictModeEnabled) { 348 | return !isWriteRequest(request, strictModeEnabled) && !isAdminRequest(request); 349 | } 350 | 351 | public static XContentType xContentTypefromRestContentType(String contentType) { 352 | if (contentType == null) { 353 | return null; 354 | } 355 | 356 | contentType = contentType.trim(); 357 | 358 | if (contentType.startsWith("application/json") || contentType.startsWith("json")) { 359 | return XContentType.JSON; 360 | } 361 | 362 | if (contentType.startsWith("application/smile") || contentType.startsWith("smile")) { 363 | return XContentType.SMILE; 364 | } 365 | 366 | if (contentType.startsWith("application/yaml") || contentType.startsWith("yaml")) { 367 | return XContentType.YAML; 368 | } 369 | 370 | if (contentType.startsWith("application/cbor") || contentType.startsWith("cbor")) { 371 | return XContentType.CBOR; 372 | } 373 | 374 | return null; 375 | } 376 | } 377 | -------------------------------------------------------------------------------- /src/main/resources/es-plugin.properties: -------------------------------------------------------------------------------- 1 | plugin=org.elasticsearch.plugins.security.SecurityPlugin -------------------------------------------------------------------------------- /src/test/java/org/elasticsearch/plugins/security/AbstractUnitTest.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.plugins.security; 2 | 3 | import static com.github.tlrx.elasticsearch.test.EsSetup.createIndex; 4 | import static com.github.tlrx.elasticsearch.test.EsSetup.deleteAll; 5 | import io.searchbox.client.JestClient; 6 | import io.searchbox.client.JestClientFactory; 7 | import io.searchbox.client.JestResult; 8 | import io.searchbox.client.config.HttpClientConfig; 9 | import io.searchbox.client.http.JestHttpClient; 10 | import io.searchbox.core.Get; 11 | import io.searchbox.core.Index; 12 | import io.searchbox.core.Search; 13 | 14 | import java.io.FileInputStream; 15 | import java.io.IOException; 16 | import java.io.StringWriter; 17 | import java.net.URL; 18 | import java.security.KeyStore; 19 | import java.security.PrivilegedExceptionAction; 20 | import java.util.ArrayList; 21 | import java.util.HashMap; 22 | import java.util.List; 23 | import java.util.Map; 24 | import java.util.Properties; 25 | 26 | import javax.net.ssl.SSLContext; 27 | import javax.security.auth.Subject; 28 | import javax.security.auth.callback.Callback; 29 | import javax.security.auth.callback.CallbackHandler; 30 | import javax.security.auth.callback.NameCallback; 31 | import javax.security.auth.callback.PasswordCallback; 32 | import javax.security.auth.login.LoginContext; 33 | 34 | import org.apache.commons.io.IOUtils; 35 | import org.apache.http.Header; 36 | import org.apache.http.conn.ssl.SSLConnectionSocketFactory; 37 | import org.apache.http.conn.ssl.SSLContexts; 38 | import org.apache.http.impl.client.HttpClientBuilder; 39 | import org.apache.http.impl.client.HttpClients; 40 | import org.apache.http.message.BasicHeader; 41 | import org.elasticsearch.common.logging.ESLogger; 42 | import org.elasticsearch.common.logging.Loggers; 43 | import org.elasticsearch.common.settings.ImmutableSettings; 44 | import org.elasticsearch.common.settings.ImmutableSettings.Builder; 45 | import org.elasticsearch.plugins.security.util.SecurityUtil; 46 | import org.ietf.jgss.GSSContext; 47 | import org.ietf.jgss.GSSCredential; 48 | import org.ietf.jgss.GSSException; 49 | import org.ietf.jgss.GSSManager; 50 | import org.ietf.jgss.GSSName; 51 | import org.ietf.jgss.Oid; 52 | import org.junit.After; 53 | import org.junit.Assert; 54 | import org.junit.Before; 55 | import org.junit.Rule; 56 | import org.junit.rules.TestName; 57 | import org.junit.rules.TestWatcher; 58 | import org.junit.runner.Description; 59 | 60 | import com.github.tlrx.elasticsearch.test.EsSetup; 61 | 62 | public abstract class AbstractUnitTest { 63 | 64 | @Rule 65 | public TestName name = new TestName(); 66 | private JestClient client; 67 | protected final Builder settingsBuilder; 68 | protected Map headers = new HashMap(); 69 | 70 | 71 | @Rule 72 | public TestWatcher testWatcher = new TestWatcher() { 73 | @Override 74 | protected void starting(final Description description) { 75 | String methodName = description.getMethodName(); 76 | String className = description.getClassName(); 77 | className = className.substring(className.lastIndexOf('.') + 1); 78 | System.out.println("Starting JUnit-test: " + className + " " + methodName); 79 | } 80 | }; 81 | 82 | protected AbstractUnitTest() { 83 | super(); 84 | 85 | 86 | 87 | settingsBuilder = ImmutableSettings 88 | .settingsBuilder() 89 | // .put(NODE_NAME, elasticsearchNode.name()) 90 | // .put("node.data", elasticsearchNode.data()) 91 | // .put("cluster.name", elasticsearchNode.clusterName()) 92 | .put("index.store.type", "memory") 93 | .put("index.store.fs.memory.enabled", "true") 94 | .put("gateway.type", "none") 95 | .put("path.data", "target/data") 96 | .put("path.work", "target/work") 97 | .put("path.logs", "target/logs") 98 | .put("path.conf", "target/config") 99 | .put("path.plugins", "target/plugins") 100 | .put("index.number_of_shards", "1") 101 | .put("index.number_of_replicas", "0") 102 | .put(getProperties()) 103 | .put("http.type", 104 | "org.elasticsearch.plugins.security.http.tomcat.TomcatHttpServerTransport"); 105 | 106 | } 107 | 108 | protected Properties getProperties() { 109 | return new Properties(); 110 | } 111 | 112 | protected final ESLogger log = Loggers.getLogger(this.getClass()); 113 | 114 | 115 | protected String getServerUri() { 116 | return "http"+(isSSL()?"s":"")+"://localhost:8080"; 117 | } 118 | 119 | protected boolean isSSL() { 120 | return false; 121 | } 122 | 123 | protected String loadFile(final String file) throws IOException { 124 | 125 | final StringWriter sw = new StringWriter(); 126 | IOUtils.copy(this.getClass().getResourceAsStream("/" + file), sw); 127 | return sw.toString(); 128 | 129 | } 130 | 131 | EsSetup esSetup; 132 | 133 | @Before 134 | public void setUp() throws Exception { 135 | 136 | headers.clear(); 137 | 138 | // Instantiates a local node & client 139 | 140 | esSetup = new EsSetup(settingsBuilder.build()); 141 | 142 | // Clean all, and creates some indices 143 | 144 | esSetup.execute( 145 | 146 | deleteAll(), 147 | 148 | createIndex("my_index_1")/* 149 | * , 150 | * 151 | * createIndex("my_index_2") 152 | * 153 | * .withSettings(fromClassPath( 154 | * "path/to/settings.json")) 155 | * 156 | * .withMapping("type1", 157 | * fromClassPath("path/to/mapping/of/type1.json" 158 | * )) 159 | * 160 | * .withData(fromClassPath("path/to/bulk.json")) 161 | */ 162 | 163 | ); 164 | 165 | 166 | } 167 | 168 | @After 169 | public void tearDown() throws Exception { 170 | 171 | // This will stop and clean the local node 172 | 173 | if(esSetup != null) { 174 | esSetup.terminate(); 175 | } 176 | 177 | if(client != null) { 178 | client.shutdownClient(); 179 | } 180 | 181 | } 182 | 183 | 184 | 185 | protected JestResult executeIndex(final String file, final String index, 186 | final String type, final String id, final boolean mustBeSuccesfull) 187 | throws Exception { 188 | 189 | final String [] userpass = getUserPass(); 190 | 191 | client = getJestClient(getServerUri(),userpass[0],userpass[1]); 192 | 193 | 194 | final JestResult res = client.execute(new Index.Builder(loadFile(file)).index(index).type(type).id(id).refresh(true) 195 | .setHeader(headers).build()); 196 | 197 | log.debug("Index operation result: " + res.getJsonString()); 198 | if (mustBeSuccesfull) { 199 | Assert.assertTrue(res.isSucceeded()); 200 | } else { 201 | Assert.assertTrue(!res.isSucceeded()); 202 | } 203 | 204 | return res; 205 | } 206 | 207 | 208 | 209 | protected JestResult executeSearch(final String file, 210 | final boolean mustBeSuccesfull) throws Exception { 211 | 212 | final String [] userpass = getUserPass(); 213 | 214 | client = getJestClient(getServerUri(),userpass[0],userpass[1]); 215 | 216 | 217 | final JestResult res = client.execute(new Search.Builder(loadFile(file)).refresh(true).setHeader(headers) 218 | .build()); 219 | 220 | log.debug("Search operation result: " + res.getJsonString()); 221 | if (mustBeSuccesfull) { 222 | Assert.assertTrue(res.isSucceeded()); 223 | } else { 224 | Assert.assertTrue(!res.isSucceeded()); 225 | } 226 | return res; 227 | } 228 | 229 | protected JestResult executeGet(final String index, final String id, 230 | final boolean mustBeSuccesfull) throws Exception { 231 | 232 | final String [] userpass = getUserPass(); 233 | 234 | client = getJestClient(getServerUri(),userpass[0],userpass[1]); 235 | 236 | 237 | final JestResult res = client.execute(new Get.Builder(index, id).refresh(true).setHeader(headers) 238 | .build()); 239 | 240 | log.debug("Search operation result: " + res.getJsonString()); 241 | if (mustBeSuccesfull) { 242 | Assert.assertTrue(res.isSucceeded()); 243 | } else { 244 | Assert.assertTrue(!res.isSucceeded()); 245 | } 246 | return res; 247 | } 248 | 249 | protected String [] getUserPass() 250 | { 251 | return new String[]{null,null}; 252 | } 253 | 254 | /*private JestHttpClient getJestClient(String serverUri) throws Exception 255 | { 256 | return getJestClient(serverUri, null, null); 257 | }*/ 258 | 259 | 260 | private JestHttpClient getJestClient(String serverUri, final String username, 261 | final String password) throws Exception {// http://hc.apache.org/httpcomponents-client-ga/tutorial/html/authentication.html 262 | final HttpClientConfig clientConfig1 = new HttpClientConfig.Builder(serverUri) 263 | .multiThreaded(true).build(); 264 | 265 | // Construct a new Jest client according to configuration via factory 266 | final JestClientFactory factory1 = new JestClientFactory(); 267 | 268 | factory1.setHttpClientConfig(clientConfig1); 269 | 270 | final JestHttpClient c = (JestHttpClient) factory1.getObject(); 271 | 272 | final HttpClientBuilder hcb = HttpClients.custom(); 273 | 274 | if (serverUri.startsWith("https")) { 275 | 276 | log.info("Configure Jest with SSL"); 277 | 278 | final KeyStore myTrustStore = KeyStore.getInstance("PKCS12"); 279 | myTrustStore 280 | .load(new FileInputStream( 281 | SecurityUtil 282 | .getAbsoluteFilePathFromClassPath("localhost_tc.p12")), 283 | "changeit".toCharArray()); 284 | 285 | final KeyStore keyStore = KeyStore.getInstance("PKCS12"); 286 | keyStore.load( 287 | new FileInputStream( 288 | SecurityUtil 289 | .getAbsoluteFilePathFromClassPath("hnelsonclient.p12")), 290 | "changeit".toCharArray()); 291 | 292 | final SSLContext sslContext = SSLContexts.custom().useTLS() 293 | .loadKeyMaterial(keyStore, "changeit".toCharArray()) 294 | .loadTrustMaterial(myTrustStore) 295 | 296 | .build(); 297 | 298 | final SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( 299 | sslContext); 300 | 301 | hcb.setSSLSocketFactory(sslsf); 302 | 303 | } 304 | 305 | if (username != null) { 306 | 307 | final GSSContext context = initGSS(new URL(serverUri), "spnego-client", username,password); 308 | final byte[] data = context.initSecContext(new byte[0], 0, 0); 309 | 310 | final List
dh = new ArrayList
(); 311 | dh.add(new BasicHeader("Authorization","Negotiate " 312 | + org.apache.tomcat.util.codec.binary.Base64 313 | .encodeBase64String(data))); 314 | 315 | hcb.setDefaultHeaders(dh); 316 | 317 | 318 | 319 | } 320 | 321 | c.setHttpClient(hcb.build()); 322 | 323 | 324 | 325 | return c; 326 | 327 | } 328 | 329 | 330 | 331 | private static CallbackHandler getUsernamePasswordHandler( 332 | final String username, final String password) { 333 | 334 | final CallbackHandler handler = new CallbackHandler() { 335 | @Override 336 | public void handle(final Callback[] callback) { 337 | for (int i = 0; i < callback.length; i++) { 338 | if (callback[i] instanceof NameCallback) { 339 | final NameCallback nameCallback = (NameCallback) callback[i]; 340 | nameCallback.setName(username); 341 | } else if (callback[i] instanceof PasswordCallback) { 342 | final PasswordCallback passCallback = (PasswordCallback) callback[i]; 343 | passCallback.setPassword(password.toCharArray()); 344 | } else { 345 | System.out 346 | .println("Unsupported Callback i=" + i 347 | + "; class=" 348 | + callback[i].getClass().getName()); 349 | } 350 | } 351 | } 352 | }; 353 | 354 | return handler; 355 | } 356 | 357 | private GSSContext initGSS(final URL url, String loginentry, String user, String password) throws Exception { 358 | final GSSManager MANAGER = GSSManager.getInstance(); 359 | 360 | final LoginContext loginContext = new LoginContext(loginentry, 361 | getUsernamePasswordHandler(user,password)); 362 | loginContext.login(); 363 | final Subject subject = loginContext.getSubject(); 364 | 365 | final PrivilegedExceptionAction action = new PrivilegedExceptionAction() { 366 | @Override 367 | public GSSCredential run() throws GSSException { 368 | return MANAGER.createCredential(null, 369 | GSSCredential.DEFAULT_LIFETIME, 370 | new Oid("1.3.6.1.5.5.2"), GSSCredential.INITIATE_ONLY); 371 | } 372 | }; 373 | 374 | final GSSCredential clientcreds = Subject.doAs(subject, action); 375 | 376 | final GSSContext context = MANAGER.createContext(MANAGER.createName( 377 | "HTTP@" + url.getHost(), GSSName.NT_HOSTBASED_SERVICE, new Oid( 378 | "1.3.6.1.5.5.2")), new Oid("1.3.6.1.5.5.2"), 379 | clientcreds, GSSContext.DEFAULT_LIFETIME); 380 | 381 | context.requestMutualAuth(true); 382 | context.requestConf(true); 383 | context.requestInteg(true); 384 | context.requestReplayDet(true); 385 | context.requestSequenceDet(true); 386 | context.requestCredDeleg(false); 387 | 388 | return context; 389 | 390 | /*byte[] data = context.initSecContext(new byte[0], 0, 0); 391 | 392 | 393 | 394 | 395 | final URLConnection uc = url.openConnection(); 396 | uc.setRequestProperty( 397 | "Authorization", 398 | "Negotiate " 399 | + org.apache.tomcat.util.codec.binary.Base64 400 | .encodeBase64String(data)); 401 | uc.connect(); 402 | data = org.apache.tomcat.util.codec.binary.Base64.decodeBase64(uc 403 | .getHeaderField("WWW-Authenticate").split(" ")[1]); 404 | 405 | data = context.initSecContext(data, 0, data.length); 406 | if (!context.isEstablished()) { 407 | throw new Exception("context not established"); 408 | }*/ 409 | 410 | } 411 | 412 | 413 | } 414 | -------------------------------------------------------------------------------- /src/test/java/org/elasticsearch/plugins/security/SpnegoAdTests.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.plugins.security; 2 | 3 | import java.net.URL; 4 | import java.util.Hashtable; 5 | import java.util.Properties; 6 | 7 | import javax.naming.Context; 8 | import javax.naming.NamingException; 9 | import javax.naming.directory.Attribute; 10 | import javax.naming.directory.Attributes; 11 | import javax.naming.directory.BasicAttribute; 12 | import javax.naming.directory.BasicAttributes; 13 | import javax.naming.directory.DirContext; 14 | import javax.naming.directory.InitialDirContext; 15 | import javax.naming.directory.ModificationItem; 16 | import javax.naming.ldap.InitialLdapContext; 17 | import javax.naming.ldap.LdapContext; 18 | import javax.security.auth.kerberos.KerberosPrincipal; 19 | 20 | import net.sourceforge.spnego.SpnegoHttpURLConnection; 21 | 22 | import org.apache.commons.io.IOUtils; 23 | import org.apache.directory.api.ldap.model.constants.SchemaConstants; 24 | import org.apache.directory.api.ldap.model.constants.SupportedSaslMechanisms; 25 | import org.apache.directory.api.ldap.model.exception.LdapException; 26 | import org.apache.directory.api.ldap.model.message.ModifyRequest; 27 | import org.apache.directory.api.ldap.model.message.ModifyRequestImpl; 28 | import org.apache.directory.api.ldap.model.name.Dn; 29 | import org.apache.directory.server.annotations.CreateKdcServer; 30 | import org.apache.directory.server.annotations.CreateLdapServer; 31 | import org.apache.directory.server.annotations.CreateTransport; 32 | import org.apache.directory.server.annotations.SaslMechanism; 33 | import org.apache.directory.server.constants.ServerDNConstants; 34 | import org.apache.directory.server.core.annotations.ContextEntry; 35 | import org.apache.directory.server.core.annotations.CreateDS; 36 | import org.apache.directory.server.core.annotations.CreateIndex; 37 | import org.apache.directory.server.core.annotations.CreatePartition; 38 | import org.apache.directory.server.core.api.CoreSession; 39 | import org.apache.directory.server.core.api.DirectoryService; 40 | import org.apache.directory.server.core.integ.FrameworkRunner; 41 | import org.apache.directory.server.core.jndi.CoreContextFactory; 42 | import org.apache.directory.server.core.kerberos.KeyDerivationInterceptor; 43 | import org.apache.directory.server.kerberos.kdc.KdcServer; 44 | import org.apache.directory.server.ldap.LdapServer; 45 | import org.apache.directory.server.ldap.handlers.sasl.cramMD5.CramMd5MechanismHandler; 46 | import org.apache.directory.server.ldap.handlers.sasl.digestMD5.DigestMd5MechanismHandler; 47 | import org.apache.directory.server.ldap.handlers.sasl.gssapi.GssapiMechanismHandler; 48 | import org.apache.directory.server.ldap.handlers.sasl.ntlm.NtlmMechanismHandler; 49 | import org.apache.directory.server.ldap.handlers.sasl.plain.PlainMechanismHandler; 50 | import org.elasticsearch.plugins.security.util.SecurityUtil; 51 | import org.junit.Assert; 52 | import org.junit.Before; 53 | import org.junit.Test; 54 | import org.junit.runner.RunWith; 55 | 56 | @RunWith(FrameworkRunner.class) 57 | @CreateDS(name = "SaslGssapiBindITest-class", partitions = { @CreatePartition(name = "example", suffix = "dc=example,dc=com", contextEntry = @ContextEntry(entryLdif = "dn: dc=example,dc=com\n" 58 | + "dc: example\n" + "objectClass: top\n" + "objectClass: domain\n\n"), indexes = { 59 | @CreateIndex(attribute = "objectClass"), 60 | @CreateIndex(attribute = "dc"), @CreateIndex(attribute = "ou") }) }, additionalInterceptors = { KeyDerivationInterceptor.class }) 61 | @CreateLdapServer(allowAnonymousAccess=true, transports = { @CreateTransport(protocol = "LDAP", port = 6389) }, saslHost = "localhost", saslPrincipal = "ldap/localhost@EXAMPLE.COM", saslMechanisms = { 62 | @SaslMechanism(name = SupportedSaslMechanisms.PLAIN, implClass = PlainMechanismHandler.class), 63 | @SaslMechanism(name = SupportedSaslMechanisms.CRAM_MD5, implClass = CramMd5MechanismHandler.class), 64 | @SaslMechanism(name = SupportedSaslMechanisms.DIGEST_MD5, implClass = DigestMd5MechanismHandler.class), 65 | @SaslMechanism(name = SupportedSaslMechanisms.GSSAPI, implClass = GssapiMechanismHandler.class), 66 | @SaslMechanism(name = SupportedSaslMechanisms.NTLM, implClass = NtlmMechanismHandler.class), 67 | @SaslMechanism(name = SupportedSaslMechanisms.GSS_SPNEGO, implClass = NtlmMechanismHandler.class) }) 68 | @CreateKdcServer(transports = { 69 | @CreateTransport(protocol = "UDP", port = 6088, address = "localhost"), 70 | @CreateTransport(protocol = "TCP", port = 6088, address = "localhost") }) 71 | public class SpnegoAdTests extends SpnegoTests { 72 | 73 | /*public static CallbackHandler getUsernamePasswordHandler( 74 | final String username, final String password) { 75 | 76 | final CallbackHandler handler = new CallbackHandler() { 77 | @Override 78 | public void handle(final Callback[] callback) { 79 | for (int i = 0; i < callback.length; i++) { 80 | if (callback[i] instanceof NameCallback) { 81 | final NameCallback nameCallback = (NameCallback) callback[i]; 82 | nameCallback.setName(username); 83 | } else if (callback[i] instanceof PasswordCallback) { 84 | final PasswordCallback passCallback = (PasswordCallback) callback[i]; 85 | passCallback.setPassword(password.toCharArray()); 86 | } else { 87 | System.out 88 | .println("Unsupported Callback i=" + i 89 | + "; class=" 90 | + callback[i].getClass().getName()); 91 | } 92 | } 93 | } 94 | }; 95 | 96 | return handler; 97 | } 98 | 99 | protected void initGSS(final URL url) throws Exception { 100 | final GSSManager MANAGER = GSSManager.getInstance(); 101 | 102 | final LoginContext loginContext = new LoginContext("spnego-client", 103 | getUsernamePasswordHandler("hnelson", "secret")); 104 | loginContext.login(); 105 | final Subject subject = loginContext.getSubject(); 106 | 107 | final PrivilegedExceptionAction action = new PrivilegedExceptionAction() { 108 | @Override 109 | public GSSCredential run() throws GSSException { 110 | return MANAGER.createCredential(null, 111 | GSSCredential.DEFAULT_LIFETIME, 112 | new Oid("1.3.6.1.5.5.2"), GSSCredential.INITIATE_ONLY); 113 | } 114 | }; 115 | 116 | final GSSCredential clientcreds = Subject.doAs(subject, action); 117 | 118 | final GSSContext context = MANAGER.createContext(MANAGER.createName( 119 | "HTTP@" + url.getHost(), GSSName.NT_HOSTBASED_SERVICE, new Oid( 120 | "1.3.6.1.5.5.2")), new Oid("1.3.6.1.5.5.2"), 121 | clientcreds, GSSContext.DEFAULT_LIFETIME); 122 | 123 | context.requestMutualAuth(true); 124 | context.requestConf(true); 125 | context.requestInteg(true); 126 | context.requestReplayDet(true); 127 | context.requestSequenceDet(true); 128 | context.requestCredDeleg(false); 129 | byte[] data = context.initSecContext(new byte[0], 0, 0); 130 | 131 | final URLConnection uc = url.openConnection(); 132 | uc.setRequestProperty("Authorization", 133 | "Negotiate " + org.apache.tomcat.util.codec.binary.Base64.encodeBase64String(data)); 134 | uc.connect(); 135 | data = org.apache.tomcat.util.codec.binary.Base64 136 | .decodeBase64(uc.getHeaderField("WWW-Authenticate").split(" ")[1]); 137 | 138 | data = context.initSecContext(data, 0, data.length); 139 | if (!context.isEstablished()) { 140 | throw new Exception("context not established"); 141 | } 142 | 143 | } 144 | 145 | @Test 146 | public void getHeaderTest() throws Exception { 147 | final URL url = new URL("http://localhost:8080"); 148 | 149 | SecurityUtil.setSystemPropertyToAbsoluteFilePathFromClassPath( 150 | "java.security.auth.login.config", "login.conf"); 151 | this.initGSS(url); 152 | 153 | }*/ 154 | 155 | // public static final String AUTHN_HEADER = "WWW-Authenticate"; 156 | // public static final String AUTHZ_HEADER = "Authorization"; 157 | 158 | private DirContext ctx; 159 | 160 | /** the context root for the schema */ 161 | protected LdapContext schemaRoot; 162 | 163 | /** the context root for the system partition */ 164 | protected LdapContext sysRoot; 165 | 166 | /** the context root for the rootDSE */ 167 | protected CoreSession rootDse; 168 | 169 | /** The used DirectoryService instance */ 170 | public static DirectoryService service; 171 | 172 | /** The used LdapServer instance */ 173 | public static LdapServer ldapServer; 174 | 175 | /** The used KdcServer instance */ 176 | public static KdcServer kdcServer; 177 | 178 | public static DirectoryService getService() { 179 | return service; 180 | } 181 | 182 | public static void setService(final DirectoryService service) { 183 | SpnegoAdTests.service = service; 184 | } 185 | 186 | public static LdapServer getLdapServer() { 187 | return ldapServer; 188 | } 189 | 190 | public static void setLdapServer(final LdapServer ldapServer) { 191 | SpnegoAdTests.ldapServer = ldapServer; 192 | } 193 | 194 | public static KdcServer getKdcServer() { 195 | return kdcServer; 196 | } 197 | 198 | public static void setKdcServer(final KdcServer kdcServer) { 199 | SpnegoAdTests.kdcServer = kdcServer; 200 | } 201 | 202 | @Override 203 | protected Properties getProperties() { 204 | final Properties props = new Properties(); 205 | props.putAll(super.getProperties()); 206 | props.setProperty("security.kerberos.mode", "spnegoad"); 207 | props.setProperty("security.authorization.ldap.isactivedirectory", "false"); 208 | props.setProperty("security.authorization.ldap.ldapurls", "ldap://localhost:6389"); 209 | 210 | props.setProperty("security.authorization.ldap.connectionname", "uid=admin,ou=system"); 211 | props.setProperty("security.authorization.ldap.connectionpassword", "secret"); 212 | props.setProperty("security.authorization.ldap.usersearch", "uid={0}"); 213 | props.setProperty("security.authorization.ldap.userbase", "ou=users,dc=example,dc=com"); 214 | props.setProperty("security.authorization.ldap.rolebase", "ou=groups,dc=example,dc=com"); 215 | 216 | 217 | props.setProperty("security.kerberos.login.conf.path", SecurityUtil.getAbsoluteFilePathFromClassPath("login.conf").getAbsolutePath()); 218 | props.setProperty("security.kerberos.krb5.conf.path", SecurityUtil.getAbsoluteFilePathFromClassPath("krb5.conf").getAbsolutePath()); 219 | 220 | //props.setProperty("security.module.actionpathfilter.enabled", "false"); 221 | //props.setProperty("security.module.dls.enabled", "false"); 222 | 223 | return props; 224 | } 225 | 226 | 227 | 228 | public SpnegoAdTests() { 229 | super(); 230 | 231 | 232 | } 233 | 234 | 235 | @Override 236 | protected String [] getUserPass() 237 | { 238 | return new String[]{"hnelson", "secret"}; 239 | } 240 | 241 | @Test 242 | public void spneghc() throws Exception { 243 | 244 | kdcServer.getConfig().setPaEncTimestampRequired(false); 245 | 246 | executeIndex("dls_default_test_allowall.json", 247 | "securityconfiguration", "dlspermissions", "default", true); 248 | 249 | executeIndex("dls_default_test_allowall.json", 250 | "securityconfiguration", "dlspermissions", "default", true); 251 | 252 | } 253 | 254 | @Test 255 | public void donothing() throws Exception { 256 | 257 | } 258 | 259 | 260 | @Test 261 | public void queryGETUrlTest() throws Exception { 262 | 263 | if(isSSL()) { 264 | return; 265 | } 266 | 267 | executeIndex("dls_default_test_allowall.json", 268 | "securityconfiguration", "dlspermissions", "default", true); 269 | 270 | executeIndex("dls_test_normal.json", "securityconfiguration", 271 | "dlspermissions", "dlspermissions", true); 272 | 273 | executeIndex("test_normal.json", "securityconfiguration", "actionpathfilter", "actionpathfilter",true ); 274 | executeIndex("dummy_content.json", "twitter", 275 | "tweet", "1", true); 276 | 277 | 278 | 279 | final net.sourceforge.spnego.SpnegoHttpURLConnection hcon = new SpnegoHttpURLConnection( 280 | "spnego-client", "hnelson@EXAMPLE.COM", "secret"); 281 | 282 | hcon.requestCredDeleg(true); 283 | 284 | 285 | hcon.connect(new URL(getServerUri() + "/_search")); 286 | //hcon.connect(new URL(getServerUri() + "/%5Fsearch")); 287 | 288 | Assert.assertTrue(hcon.getResponseCode() == 200); 289 | 290 | log.debug(IOUtils.toString(hcon.getInputStream())); 291 | 292 | 293 | 294 | 295 | 296 | } 297 | 298 | 299 | 300 | public static String fixServicePrincipalName(String servicePrincipalName, 301 | final Dn serviceEntryDn, final LdapServer ldapServer) 302 | throws LdapException { 303 | final KerberosPrincipal servicePrincipal = new KerberosPrincipal( 304 | servicePrincipalName, KerberosPrincipal.KRB_NT_SRV_HST); 305 | servicePrincipalName = servicePrincipal.getName(); 306 | 307 | ldapServer.setSaslPrincipal(servicePrincipalName); 308 | 309 | if (serviceEntryDn != null) { 310 | final ModifyRequest modifyRequest = new ModifyRequestImpl(); 311 | modifyRequest.setName(serviceEntryDn); 312 | modifyRequest.replace("userPassword", "randall"); 313 | modifyRequest.replace("krb5PrincipalName", servicePrincipalName); 314 | ldapServer.getDirectoryService().getAdminSession() 315 | .modify(modifyRequest); 316 | } 317 | 318 | return servicePrincipalName; 319 | } 320 | 321 | /** 322 | * Set up a partition for EXAMPLE.COM and add user and service principals to 323 | * test authentication with. 324 | */ 325 | @Override 326 | @Before 327 | public void setUp() throws Exception { 328 | super.setUp(); 329 | 330 | getKdcServer().getConfig().setPrimaryRealm("EXAMPLE.COM"); 331 | 332 | final String servicePrincipalName = fixServicePrincipalName( 333 | "ldap/localhost@EXAMPLE.COM", null, getLdapServer()); 334 | 335 | // System.out.println("servicePrincipalName "+servicePrincipalName); 336 | 337 | Attributes attrs; 338 | 339 | this.setContexts("uid=admin,ou=system", "secret"); 340 | 341 | // ------------------------------------------------------------------- 342 | // Enable the krb5kdc schema 343 | // ------------------------------------------------------------------- 344 | 345 | // check if krb5kdc is disabled 346 | final Attributes krb5kdcAttrs = schemaRoot 347 | .getAttributes("cn=Krb5kdc"); 348 | boolean isKrb5KdcDisabled = false; 349 | 350 | if (krb5kdcAttrs.get("m-disabled") != null) { 351 | isKrb5KdcDisabled = ((String) krb5kdcAttrs.get("m-disabled").get()) 352 | .equalsIgnoreCase("TRUE"); 353 | } 354 | 355 | // if krb5kdc is disabled then enable it 356 | if (isKrb5KdcDisabled) { 357 | final Attribute disabled = new BasicAttribute("m-disabled"); 358 | final ModificationItem[] mods = new ModificationItem[] { new ModificationItem( 359 | DirContext.REMOVE_ATTRIBUTE, disabled) }; 360 | schemaRoot.modifyAttributes("cn=Krb5kdc", mods); 361 | } 362 | 363 | // Get a context, create the ou=users subcontext, then create the 3 364 | // principals. 365 | final Hashtable env = new Hashtable(); 366 | env.put(DirectoryService.JNDI_KEY, getService()); 367 | env.put(Context.INITIAL_CONTEXT_FACTORY, 368 | "org.apache.directory.server.core.jndi.CoreContextFactory"); 369 | env.put(Context.PROVIDER_URL, "dc=example,dc=com"); 370 | env.put(Context.SECURITY_PRINCIPAL, "uid=admin,ou=system"); 371 | env.put(Context.SECURITY_CREDENTIALS, "secret"); 372 | env.put(Context.SECURITY_AUTHENTICATION, "simple"); 373 | 374 | ctx = new InitialDirContext(env); 375 | 376 | attrs = getOrgUnitAttributes("users"); 377 | final DirContext users = ctx.createSubcontext("ou=users", attrs); 378 | 379 | attrs = getPrincipalAttributes("Nelson", "Horatio Nelson", 380 | "hnelson", "secret", "hnelson@EXAMPLE.COM"); 381 | users.createSubcontext("uid=hnelson", attrs); 382 | 383 | attrs = getPrincipalAttributes("hNelson", "Horatio hNelson", 384 | "nelsonh", "secret", "nelsonh@EXAMPLE.COM"); 385 | users.createSubcontext("uid=nelsonh", attrs); 386 | 387 | attrs = getPrincipalAttributes("Einstein", "Albert Einstein", 388 | "aeinstein", "aeinstein", "aeinstein@EXAMPLE.COM"); 389 | users.createSubcontext("uid=aeinstein", attrs); 390 | 391 | attrs = getPrincipalAttributes("Service", "KDC Service", "krbtgt", 392 | "secret", "krbtgt/EXAMPLE.COM@EXAMPLE.COM"); 393 | users.createSubcontext("uid=krbtgt", attrs); 394 | 395 | attrs = getPrincipalAttributes("Service", "LDAP Service", "ldap", 396 | "randall", servicePrincipalName); 397 | users.createSubcontext("uid=ldap", attrs); 398 | 399 | attrs = getPrincipalAttributes("Service", "HTTP Service", "http", 400 | "httppwd", "HTTP/localhost@EXAMPLE.COM"); 401 | users.createSubcontext("uid=http", attrs); 402 | 403 | /* 404 | * dn: cn=itpeople,ou=groups,dc=example,dc=com objectclass: groupofnames 405 | * cn: itpeople description: IT security group member: cn=William 406 | * Smith,ou=people,dc=example,dc=com 407 | */ 408 | 409 | attrs = getOrgUnitAttributes("groups"); 410 | final DirContext groups = ctx.createSubcontext("ou=groups", attrs); 411 | 412 | attrs = getGroupAttributes( 413 | "uid=hnelson,ou=users,dc=example,dc=com", "dummy ldap role", 414 | "dummy_ldap"); 415 | groups.createSubcontext("cn=dummy_ldap", attrs); 416 | 417 | attrs = getGroupAttributes( 418 | "uid=nelsonh,ou=users,dc=example,dc=com", "dummy ssl ldap role", 419 | "dummy_sslldap"); 420 | groups.createSubcontext("cn=dummy_sslldap", attrs); 421 | } 422 | 423 | /** 424 | * Convenience method for creating principals. 425 | * 426 | * @param cn 427 | * the commonName of the person 428 | * @param principal 429 | * the kerberos principal name for the person 430 | * @param sn 431 | * the surName of the person 432 | * @param uid 433 | * the unique identifier for the person 434 | * @param userPassword 435 | * the credentials of the person 436 | * @return the attributes of the person principal 437 | */ 438 | protected Attributes getPrincipalAttributes(final String sn, 439 | final String cn, final String uid, final String userPassword, 440 | final String principal) { 441 | final Attributes attrs = new BasicAttributes(true); 442 | final Attribute ocls = new BasicAttribute("objectClass"); 443 | ocls.add("top"); 444 | ocls.add("person"); // sn $ cn 445 | ocls.add("inetOrgPerson"); // uid 446 | ocls.add("krb5principal"); 447 | ocls.add("krb5kdcentry"); 448 | attrs.put(ocls); 449 | attrs.put("cn", cn); 450 | attrs.put("sn", sn); 451 | attrs.put("uid", uid); 452 | attrs.put("userPassword", userPassword); 453 | attrs.put("krb5PrincipalName", principal); 454 | attrs.put("krb5KeyVersionNumber", "0"); 455 | 456 | return attrs; 457 | } 458 | 459 | protected Attributes getGroupAttributes(final String member, 460 | final String description, final String cn) { 461 | /* 462 | * dn: cn=itpeople,ou=groups,dc=example,dc=com objectclass: groupofnames 463 | * cn: itpeople description: IT security group member: cn=William 464 | * Smith,ou=people,dc=example,dc=com 465 | */ 466 | 467 | final Attributes attrs = new BasicAttributes(true); 468 | final Attribute ocls = new BasicAttribute("objectClass"); 469 | ocls.add("groupofnames"); 470 | attrs.put(ocls); 471 | attrs.put("cn", cn); 472 | attrs.put("description", description); 473 | attrs.put("member", member); 474 | 475 | return attrs; 476 | } 477 | 478 | /** 479 | * Convenience method for creating an organizational unit. 480 | * 481 | * @param ou 482 | * the ou of the organizationalUnit 483 | * @return the attributes of the organizationalUnit 484 | */ 485 | protected Attributes getOrgUnitAttributes(final String ou) { 486 | final Attributes attrs = new BasicAttributes(true); 487 | final Attribute ocls = new BasicAttribute("objectClass"); 488 | ocls.add("top"); 489 | ocls.add("organizationalUnit"); 490 | attrs.put(ocls); 491 | attrs.put("ou", ou); 492 | 493 | return attrs; 494 | } 495 | 496 | protected void setContexts(final String user, final String passwd) 497 | throws Exception { 498 | final Hashtable env = new Hashtable(); 499 | env.put(DirectoryService.JNDI_KEY, getService()); 500 | env.put(Context.SECURITY_PRINCIPAL, user); 501 | env.put(Context.SECURITY_CREDENTIALS, passwd); 502 | env.put(Context.SECURITY_AUTHENTICATION, "simple"); 503 | env.put(Context.INITIAL_CONTEXT_FACTORY, 504 | CoreContextFactory.class.getName()); 505 | this.setContexts(env); 506 | } 507 | 508 | /** 509 | * Sets the contexts of this class taking into account the extras and 510 | * overrides properties. 511 | * 512 | * @param env 513 | * an environment to use while setting up the system root. 514 | * @throws NamingException 515 | * if there is a failure of any kind 516 | */ 517 | protected void setContexts(final Hashtable env) 518 | throws Exception { 519 | final Hashtable envFinal = new Hashtable( 520 | env); 521 | envFinal.put(Context.PROVIDER_URL, ServerDNConstants.SYSTEM_DN); 522 | sysRoot = new InitialLdapContext(envFinal, null); 523 | 524 | envFinal.put(Context.PROVIDER_URL, ""); 525 | rootDse = getService().getAdminSession(); 526 | 527 | envFinal.put(Context.PROVIDER_URL, SchemaConstants.OU_SCHEMA); 528 | schemaRoot = new InitialLdapContext(envFinal, null); 529 | } 530 | /* 531 | private class CallbackHandlerBean implements CallbackHandler { 532 | private final String name; 533 | private final String password; 534 | 535 | 536 | public CallbackHandlerBean(final String name, final String password) { 537 | this.name = name; 538 | this.password = password; 539 | } 540 | 541 | @Override 542 | public void handle(final Callback[] callbacks) 543 | throws UnsupportedCallbackException, IOException { 544 | for (int ii = 0; ii < callbacks.length; ii++) { 545 | final Callback callBack = callbacks[ii]; 546 | 547 | // Handles username callback. 548 | if (callBack instanceof NameCallback) { 549 | final NameCallback nameCallback = (NameCallback) callBack; 550 | nameCallback.setName(this.name); 551 | // Handles password callback. 552 | } else if (callBack instanceof PasswordCallback) { 553 | final PasswordCallback passwordCallback = (PasswordCallback) callBack; 554 | passwordCallback.setPassword(this.password.toCharArray()); 555 | } else { 556 | throw new UnsupportedCallbackException(callBack, 557 | I18n.err(I18n.ERR_617)); 558 | } 559 | } 560 | } 561 | }*/ 562 | } 563 | -------------------------------------------------------------------------------- /src/test/java/org/elasticsearch/plugins/security/SpnegoAdTestsAlternateLdapUrl.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.plugins.security; 2 | 3 | import java.net.URL; 4 | import java.util.Hashtable; 5 | import java.util.Properties; 6 | 7 | import javax.naming.Context; 8 | import javax.naming.NamingException; 9 | import javax.naming.directory.Attribute; 10 | import javax.naming.directory.Attributes; 11 | import javax.naming.directory.BasicAttribute; 12 | import javax.naming.directory.BasicAttributes; 13 | import javax.naming.directory.DirContext; 14 | import javax.naming.directory.InitialDirContext; 15 | import javax.naming.directory.ModificationItem; 16 | import javax.naming.ldap.InitialLdapContext; 17 | import javax.naming.ldap.LdapContext; 18 | import javax.security.auth.kerberos.KerberosPrincipal; 19 | 20 | import net.sourceforge.spnego.SpnegoHttpURLConnection; 21 | 22 | import org.apache.commons.io.IOUtils; 23 | import org.apache.directory.api.ldap.model.constants.SchemaConstants; 24 | import org.apache.directory.api.ldap.model.constants.SupportedSaslMechanisms; 25 | import org.apache.directory.api.ldap.model.exception.LdapException; 26 | import org.apache.directory.api.ldap.model.message.ModifyRequest; 27 | import org.apache.directory.api.ldap.model.message.ModifyRequestImpl; 28 | import org.apache.directory.api.ldap.model.name.Dn; 29 | import org.apache.directory.server.annotations.CreateKdcServer; 30 | import org.apache.directory.server.annotations.CreateLdapServer; 31 | import org.apache.directory.server.annotations.CreateTransport; 32 | import org.apache.directory.server.annotations.SaslMechanism; 33 | import org.apache.directory.server.constants.ServerDNConstants; 34 | import org.apache.directory.server.core.annotations.ContextEntry; 35 | import org.apache.directory.server.core.annotations.CreateDS; 36 | import org.apache.directory.server.core.annotations.CreateIndex; 37 | import org.apache.directory.server.core.annotations.CreatePartition; 38 | import org.apache.directory.server.core.api.CoreSession; 39 | import org.apache.directory.server.core.api.DirectoryService; 40 | import org.apache.directory.server.core.integ.FrameworkRunner; 41 | import org.apache.directory.server.core.jndi.CoreContextFactory; 42 | import org.apache.directory.server.core.kerberos.KeyDerivationInterceptor; 43 | import org.apache.directory.server.kerberos.kdc.KdcServer; 44 | import org.apache.directory.server.ldap.LdapServer; 45 | import org.apache.directory.server.ldap.handlers.sasl.cramMD5.CramMd5MechanismHandler; 46 | import org.apache.directory.server.ldap.handlers.sasl.digestMD5.DigestMd5MechanismHandler; 47 | import org.apache.directory.server.ldap.handlers.sasl.gssapi.GssapiMechanismHandler; 48 | import org.apache.directory.server.ldap.handlers.sasl.ntlm.NtlmMechanismHandler; 49 | import org.apache.directory.server.ldap.handlers.sasl.plain.PlainMechanismHandler; 50 | import org.elasticsearch.plugins.security.util.SecurityUtil; 51 | import org.junit.Assert; 52 | import org.junit.Before; 53 | import org.junit.Test; 54 | import org.junit.runner.RunWith; 55 | 56 | @RunWith(FrameworkRunner.class) 57 | @CreateDS(name = "SaslGssapiBindITest-class", partitions = { @CreatePartition(name = "example", suffix = "dc=example,dc=com", contextEntry = @ContextEntry(entryLdif = "dn: dc=example,dc=com\n" 58 | + "dc: example\n" + "objectClass: top\n" + "objectClass: domain\n\n"), indexes = { 59 | @CreateIndex(attribute = "objectClass"), 60 | @CreateIndex(attribute = "dc"), @CreateIndex(attribute = "ou") }) }, additionalInterceptors = { KeyDerivationInterceptor.class }) 61 | @CreateLdapServer(allowAnonymousAccess=true, transports = { @CreateTransport(protocol = "LDAP", port = 6389) }, saslHost = "localhost", saslPrincipal = "ldap/localhost@EXAMPLE.COM", saslMechanisms = { 62 | @SaslMechanism(name = SupportedSaslMechanisms.PLAIN, implClass = PlainMechanismHandler.class), 63 | @SaslMechanism(name = SupportedSaslMechanisms.CRAM_MD5, implClass = CramMd5MechanismHandler.class), 64 | @SaslMechanism(name = SupportedSaslMechanisms.DIGEST_MD5, implClass = DigestMd5MechanismHandler.class), 65 | @SaslMechanism(name = SupportedSaslMechanisms.GSSAPI, implClass = GssapiMechanismHandler.class), 66 | @SaslMechanism(name = SupportedSaslMechanisms.NTLM, implClass = NtlmMechanismHandler.class), 67 | @SaslMechanism(name = SupportedSaslMechanisms.GSS_SPNEGO, implClass = NtlmMechanismHandler.class) }) 68 | @CreateKdcServer(transports = { 69 | @CreateTransport(protocol = "UDP", port = 6088, address = "localhost"), 70 | @CreateTransport(protocol = "TCP", port = 6088, address = "localhost") }) 71 | public class SpnegoAdTestsAlternateLdapUrl extends SpnegoAdTests { 72 | 73 | 74 | @Override 75 | protected Properties getProperties() { 76 | final Properties props = new Properties(); 77 | props.putAll(super.getProperties()); 78 | props.setProperty("security.kerberos.mode", "spnegoad"); 79 | props.setProperty("security.authorization.ldap.isactivedirectory", "false"); 80 | props.setProperty("security.authorization.ldap.ldapurls", "ldap://dummy-non-existent:1234, ldap://localhost:6389"); 81 | 82 | props.setProperty("security.authorization.ldap.connectionname", "uid=admin,ou=system"); 83 | props.setProperty("security.authorization.ldap.connectionpassword", "secret"); 84 | props.setProperty("security.authorization.ldap.usersearch", "uid={0}"); 85 | props.setProperty("security.authorization.ldap.userbase", "ou=users,dc=example,dc=com"); 86 | props.setProperty("security.authorization.ldap.rolebase", "ou=groups,dc=example,dc=com"); 87 | 88 | 89 | props.setProperty("security.kerberos.login.conf.path", SecurityUtil.getAbsoluteFilePathFromClassPath("login.conf").getAbsolutePath()); 90 | props.setProperty("security.kerberos.krb5.conf.path", SecurityUtil.getAbsoluteFilePathFromClassPath("krb5.conf").getAbsolutePath()); 91 | 92 | //props.setProperty("security.module.actionpathfilter.enabled", "false"); 93 | //props.setProperty("security.module.dls.enabled", "false"); 94 | 95 | return props; 96 | } 97 | 98 | 99 | 100 | 101 | } 102 | -------------------------------------------------------------------------------- /src/test/java/org/elasticsearch/plugins/security/SpnegoTests.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.plugins.security; 2 | 3 | import io.searchbox.client.JestResult; 4 | 5 | import java.util.Properties; 6 | 7 | import org.junit.Assert; 8 | import org.junit.Test; 9 | 10 | public abstract class SpnegoTests extends AbstractUnitTest { 11 | 12 | @Override 13 | protected Properties getProperties() { 14 | 15 | final Properties props = new Properties(); 16 | props.putAll(super.getProperties()); 17 | props.setProperty("security.http.xforwardedfor.header", 18 | "X-Forwarded-For"); 19 | props.setProperty("security.http.xforwardedfor.trustedproxies", 20 | "123.123.123.123, 111.222.111.222"); 21 | return props; 22 | } 23 | 24 | 25 | 26 | // --- 27 | 28 | @Test 29 | public void common_normalTest() throws Exception { 30 | 31 | executeIndex("ur_test_normal.json","securityconfiguration","actionpathfilter","actionpathfilter",true); 32 | executeIndex("dummy_content.json","twitter","tweet","1",true); 33 | 34 | } 35 | 36 | @Test 37 | public void common_dupTest() throws Exception { 38 | 39 | 40 | executeIndex("ur_test_duplicate.json","securityconfiguration","actionpathfilter","actionpathfilter",true); 41 | executeIndex("dummy_content.json","twitter","tweet","1",false); 42 | 43 | } 44 | 45 | // -- 46 | 47 | @Test 48 | public void singleHeaderTest() throws Exception { 49 | 50 | executeIndex("test_normal.json","securityconfiguration","actionpathfilter","actionpathfilter",true); 51 | executeIndex("dls_default_test_allowall.json","securityconfiguration","dlspermissions","default",true); 52 | executeIndex("dls_test_normal.json","securityconfiguration","dlspermissions","dlspermissions",true); 53 | 54 | headers.put("X-Forwarded-For", "3.1.55.2"); 55 | executeSearch("non_field_query.json",true); 56 | 57 | } 58 | 59 | @Test 60 | public void badProxiesTest() throws Exception { 61 | 62 | executeIndex("dls_default_test_allowall.json", 63 | "securityconfiguration", "dlspermissions", "default", true); 64 | 65 | executeIndex("dls_test_normal.json", "securityconfiguration", 66 | "dlspermissions", "dlspermissions", true); 67 | 68 | executeIndex("test_normal.json", "securityconfiguration", "actionpathfilter", "actionpathfilter",true ); 69 | 70 | headers.put("X-Forwarded-For", 71 | "3.1.55.2, 123.12.123.123, 111.222.111.222"); 72 | executeSearch("non_field_query.json", false); 73 | 74 | 75 | } 76 | 77 | @Test 78 | public void goodProxiesTest() throws Exception { 79 | 80 | executeIndex("dls_default_test_allowall.json", 81 | "securityconfiguration", "dlspermissions", "default", true); 82 | 83 | executeIndex("dls_test_normal.json", "securityconfiguration", 84 | "dlspermissions", "dlspermissions", true); 85 | 86 | executeIndex("test_normal.json", "securityconfiguration", "actionpathfilter", "actionpathfilter",true ); 87 | 88 | 89 | headers.put("X-Forwarded-For", 90 | "3.1.55.2, 123.123.123.123, 111.222.111.222"); 91 | executeSearch("non_field_query.json", true); 92 | 93 | 94 | 95 | } 96 | 97 | @Test 98 | public void goodProxiesTestSingle() throws Exception { 99 | 100 | executeIndex("dls_default_test_allowall.json", 101 | "securityconfiguration", "dlspermissions", "default", true); 102 | 103 | executeIndex("dls_test_normal.json", "securityconfiguration", 104 | "dlspermissions", "dlspermissions", true); 105 | 106 | executeIndex("test_normal.json", "securityconfiguration", "actionpathfilter", "actionpathfilter",true ); 107 | 108 | 109 | headers.put("X-Forwarded-For", 110 | "3.1.55.2, 123.123.123.123"); 111 | executeSearch("non_field_query.json", true); 112 | 113 | 114 | } 115 | 116 | 117 | // -- 118 | @Test 119 | public void normalTest2() throws Exception { 120 | 121 | executeIndex("dls_default_test_denyall.json", 122 | "securityconfiguration", "dlspermissions", "default", true); 123 | executeIndex("ur_test_normal.json", "securityconfiguration", 124 | "actionpathfilter", "actionpathfilter", true); 125 | executeIndex("dls_test_normal.json", "securityconfiguration", 126 | "dlspermissions", "dlspermissions", true); 127 | executeIndex("dls_dummy_content.json", "twitter", "tweet", "1", 128 | true); 129 | executeIndex("dls_dummy_content_without_dls.json", "twitter", 130 | "tweet", "2", true); 131 | executeSearch("dls_field_query.json", true); 132 | 133 | } 134 | 135 | @Test 136 | public void normalTest22() throws Exception { 137 | 138 | 139 | executeIndex("ur_test_normal.json", "securityconfiguration", "actionpathfilter", "actionpathfilter",true ); 140 | 141 | executeIndex("dls_test_normal.json", "securityconfiguration", 142 | "dlspermissions", "dlspermissions", true); 143 | 144 | 145 | executeIndex("dls_dummy_content_without_dls.json", "twitter", 146 | "tweet", "1", true); 147 | 148 | executeSearch("dls_field_query.json", true); 149 | 150 | } 151 | 152 | // -- 153 | // dls 154 | 155 | @Test 156 | public void normalTest() throws Exception { 157 | 158 | executeIndex("ur_test_normal.json", "securityconfiguration", "actionpathfilter", "actionpathfilter",true ); 159 | 160 | executeIndex("dls_test_normal.json", "securityconfiguration", 161 | "dlspermissions", "dlspermissions", true); 162 | 163 | executeIndex("dls_dummy_content.json", "twitter", 164 | "tweet", "1", true); 165 | 166 | executeIndex("dls_dummy_content_updt.json", "twitter", 167 | "tweet", "1", true); 168 | 169 | 170 | executeSearch("dls_field_query.json", true); 171 | 172 | } 173 | 174 | @Test 175 | public void facetTest() throws Exception { 176 | 177 | executeIndex("ur_test_all.json", "securityconfiguration", "actionpathfilter", "actionpathfilter",true ); 178 | 179 | executeIndex("dls_test_normal.json", "securityconfiguration", 180 | "dlspermissions", "dlspermissions", true); 181 | 182 | executeIndex("dls_dummy_content.json", "twitter", 183 | "tweet", "1", true); 184 | 185 | JestResult res = executeSearch("test_facet_search.json", true); 186 | 187 | Assert.assertTrue(res.getJsonString().contains("facets")); 188 | Assert.assertTrue(res.getJsonString().contains("term")); 189 | 190 | 191 | } 192 | 193 | /*@Test 194 | public void facetTestStrict() throws Exception { 195 | 196 | this.esSetup..put("security.strict", "true"); 197 | 198 | executeIndex("ur_test_all.json", "securityconfiguration", "actionpathfilter", "actionpathfilter",true ); 199 | 200 | executeIndex("dls_test_normal.json", "securityconfiguration", 201 | "dlspermissions", "dlspermissions", true); 202 | 203 | executeIndex("dls_dummy_content.json", "twitter", 204 | "tweet", "1", true); 205 | 206 | JestResult res = executeSearch("test_facet_search.json", true); 207 | 208 | Assert.assertTrue(!res.getJsonString().contains("facets")); 209 | Assert.assertTrue(!res.getJsonString().contains("term")); 210 | 211 | 212 | }*/ 213 | 214 | @Test 215 | public void normalTest23() throws Exception { 216 | 217 | executeIndex("ur_test_normal.json", "securityconfiguration", "actionpathfilter", "actionpathfilter",true ); 218 | 219 | 220 | executeIndex("dls_test_normal.json", "securityconfiguration", 221 | "dlspermissions", "dlspermissions", true); 222 | 223 | executeIndex("dls_dummy_content.json", "twitter", 224 | "tweet", "1", true); 225 | 226 | executeSearch("dls_field_query.json", true); 227 | 228 | } 229 | 230 | // -- 231 | 232 | @Test 233 | public void denyAllTest() throws Exception { 234 | 235 | executeIndex("test_denyall.json", "securityconfiguration", "actionpathfilter", "actionpathfilter",true ); 236 | 237 | 238 | executeIndex("dummy_content.json", "twitter", 239 | "tweet", "1", false); 240 | 241 | 242 | 243 | } 244 | 245 | @Test 246 | public void allowTest() throws Exception { 247 | 248 | 249 | executeIndex("test_normal.json", "securityconfiguration", "actionpathfilter", "actionpathfilter",true ); 250 | 251 | 252 | 253 | executeIndex("dummy_content.json", "twitter", 254 | "tweet", "1", true); 255 | 256 | 257 | } 258 | 259 | 260 | @Test 261 | public void issueDls1() throws Exception { 262 | 263 | executeIndex("ur_test_normal.json", "securityconfiguration", "actionpathfilter", "actionpathfilter",true ); 264 | 265 | 266 | executeIndex("issues/dls1/default.json", "securityconfiguration","dlspermissions","default", true); 267 | 268 | executeIndex("issues/dls1/rules.json", "securityconfiguration", 269 | "dlspermissions", "dlspermissions", true); 270 | 271 | executeIndex("dls_dummy_content.json", "twitter", 272 | "tweet", "1", true); 273 | 274 | executeSearch("dls_field_query.json", true); 275 | 276 | } 277 | 278 | 279 | @Test 280 | public void issueDls2() throws Exception { 281 | 282 | executeIndex("ur_test_normal.json", "securityconfiguration", "actionpathfilter", "actionpathfilter",true ); 283 | 284 | 285 | executeIndex("issues/dls2/default.json", "securityconfiguration","dlspermissions","default", true); 286 | 287 | executeIndex("issues/dls2/rules.json", "securityconfiguration", 288 | "dlspermissions", "dlspermissions", true); 289 | 290 | executeIndex("issues/dls2/data.json", "logstash-2014.04.03", 291 | "log", "onelog", true); 292 | 293 | JestResult res = executeGet("logstash-2014.04.03","onelog", true); 294 | Assert.assertEquals(loadFile("issues/dls2/expected.json"), res.getJsonString()); 295 | 296 | } 297 | 298 | 299 | 300 | } 301 | -------------------------------------------------------------------------------- /src/test/java/org/elasticsearch/plugins/security/SpnegoWaffleTests.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.plugins.security; 2 | 3 | import java.util.Properties; 4 | 5 | import org.junit.Before; 6 | 7 | public class SpnegoWaffleTests extends SpnegoTests { 8 | 9 | @Override 10 | protected Properties getProperties() { 11 | final Properties props = new Properties(); 12 | props.putAll(super.getProperties()); 13 | props.setProperty("security.kerberos.mode", "waffle"); 14 | props.setProperty("security.waffle.testmode", "true"); 15 | return props; 16 | } 17 | 18 | 19 | @Override 20 | @Before 21 | public void setUp() throws Exception { 22 | super.setUp(); 23 | headers.put("Authorization", "foo bar"); 24 | } 25 | 26 | 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/test/java/org/elasticsearch/plugins/security/SslSpnegoAdTests.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.plugins.security; 2 | 3 | import java.util.Properties; 4 | 5 | import org.apache.directory.api.ldap.model.constants.SupportedSaslMechanisms; 6 | import org.apache.directory.server.annotations.CreateKdcServer; 7 | import org.apache.directory.server.annotations.CreateLdapServer; 8 | import org.apache.directory.server.annotations.CreateTransport; 9 | import org.apache.directory.server.annotations.SaslMechanism; 10 | import org.apache.directory.server.core.annotations.ContextEntry; 11 | import org.apache.directory.server.core.annotations.CreateDS; 12 | import org.apache.directory.server.core.annotations.CreateIndex; 13 | import org.apache.directory.server.core.annotations.CreatePartition; 14 | import org.apache.directory.server.core.integ.FrameworkRunner; 15 | import org.apache.directory.server.core.kerberos.KeyDerivationInterceptor; 16 | import org.apache.directory.server.ldap.handlers.sasl.cramMD5.CramMd5MechanismHandler; 17 | import org.apache.directory.server.ldap.handlers.sasl.digestMD5.DigestMd5MechanismHandler; 18 | import org.apache.directory.server.ldap.handlers.sasl.gssapi.GssapiMechanismHandler; 19 | import org.apache.directory.server.ldap.handlers.sasl.ntlm.NtlmMechanismHandler; 20 | import org.apache.directory.server.ldap.handlers.sasl.plain.PlainMechanismHandler; 21 | import org.elasticsearch.plugins.security.util.SecurityUtil; 22 | import org.junit.Before; 23 | import org.junit.runner.RunWith; 24 | @RunWith(FrameworkRunner.class) 25 | @CreateDS(name = "SaslGssapiBindITest-class", partitions = { @CreatePartition(name = "example", suffix = "dc=example,dc=com", contextEntry = @ContextEntry(entryLdif = "dn: dc=example,dc=com\n" 26 | + "dc: example\n" + "objectClass: top\n" + "objectClass: domain\n\n"), indexes = { 27 | @CreateIndex(attribute = "objectClass"), 28 | @CreateIndex(attribute = "dc"), @CreateIndex(attribute = "ou") }) }, additionalInterceptors = { KeyDerivationInterceptor.class }) 29 | @CreateLdapServer(allowAnonymousAccess=true, transports = { @CreateTransport(protocol = "LDAP", port = 6389) }, saslHost = "localhost", saslPrincipal = "ldap/localhost@EXAMPLE.COM", saslMechanisms = { 30 | @SaslMechanism(name = SupportedSaslMechanisms.PLAIN, implClass = PlainMechanismHandler.class), 31 | @SaslMechanism(name = SupportedSaslMechanisms.CRAM_MD5, implClass = CramMd5MechanismHandler.class), 32 | @SaslMechanism(name = SupportedSaslMechanisms.DIGEST_MD5, implClass = DigestMd5MechanismHandler.class), 33 | @SaslMechanism(name = SupportedSaslMechanisms.GSSAPI, implClass = GssapiMechanismHandler.class), 34 | @SaslMechanism(name = SupportedSaslMechanisms.NTLM, implClass = NtlmMechanismHandler.class), 35 | @SaslMechanism(name = SupportedSaslMechanisms.GSS_SPNEGO, implClass = NtlmMechanismHandler.class) }) 36 | @CreateKdcServer(transports = { 37 | @CreateTransport(protocol = "UDP", port = 6088, address = "localhost"), 38 | @CreateTransport(protocol = "TCP", port = 6088, address = "localhost") }) 39 | public class SslSpnegoAdTests extends SpnegoAdTests { 40 | 41 | @Override 42 | protected Properties getProperties() { 43 | final Properties props = new Properties(); 44 | props.putAll(super.getProperties()); 45 | 46 | props.setProperty("security.ssl.enabled", "true"); 47 | props.setProperty("security.ssl.keystorefile", SecurityUtil.getAbsoluteFilePathFromClassPath("localhost_tc.p12").getAbsolutePath()); //"C:\\cygwin\\home\\salyh\\pki-simple\\v2\\certs\\localhost_tc.p12 48 | props.setProperty("security.ssl.keystoretype", "PKCS12"); 49 | 50 | props.setProperty("security.ssl.clientauth.enabled", "true"); 51 | props.setProperty("security.ssl.clientauth.truststorefile", SecurityUtil.getAbsoluteFilePathFromClassPath("truststore.jks").getAbsolutePath()); 52 | 53 | props.setProperty("security.ssl.userattribute","CN"); 54 | //props.setProperty("security.module.actionpathfilter.enabled", "false"); 55 | //props.setProperty("security.module.dls.enabled", "false"); 56 | 57 | 58 | 59 | return props; 60 | } 61 | 62 | 63 | @Override 64 | @Before 65 | public void setUp() throws Exception { 66 | super.setUp(); 67 | 68 | } 69 | 70 | @Override 71 | protected boolean isSSL() { 72 | return true; 73 | } 74 | 75 | /* 76 | @Test 77 | public void testSSL() throws Exception 78 | 79 | { 80 | 81 | final String krbConfPath = URLDecoder.decode(this.getClass() 82 | .getClassLoader().getResource("krb5.conf").getFile(), 83 | "UTF-8"); 84 | 85 | System.setProperty("java.security.krb5.conf", krbConfPath); 86 | System.setProperty("sun.security.krb5.debug", "true"); 87 | 88 | SecurityUtil.setSystemPropertyToAbsoluteFilePathFromClassPath("java.security.auth.login.config", "login.conf"); 89 | 90 | System.setProperty("javax.security.auth.useSubjectCredsOnly", "false"); 91 | 92 | KeyStore myTrustStore = KeyStore.getInstance("PKCS12"); 93 | myTrustStore.load(new FileInputStream(SecurityUtil.getAbsoluteFilePathFromClassPath("localhost_tc.p12")), "changeit".toCharArray()); 94 | 95 | KeyStore keyStore = KeyStore.getInstance("PKCS12"); 96 | keyStore.load(new FileInputStream(SecurityUtil.getAbsoluteFilePathFromClassPath("hnelsonclient.p12")), "changeit".toCharArray()); 97 | 98 | 99 | 100 | SSLContext sslContext = SSLContexts.custom() 101 | .useTLS() 102 | .loadKeyMaterial(keyStore, "changeit".toCharArray()) 103 | .loadTrustMaterial(myTrustStore) 104 | 105 | .build(); 106 | 107 | 108 | 109 | SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext); 110 | 111 | CloseableHttpClient httpclient = HttpClients.custom() 112 | 113 | .setSSLSocketFactory(sslsf) 114 | .build(); 115 | 116 | 117 | final ClientConfig clientConfig1 = new ClientConfig.Builder("https://localhost:8080/") 118 | .multiThreaded(true).build(); 119 | 120 | // Construct a new Jest client according to configuration via factory 121 | JestClientFactory factory1 = new JestClientFactory(); 122 | 123 | factory1.setClientConfig(clientConfig1); 124 | 125 | JestHttpClient c = (JestHttpClient) factory1.getObject(); 126 | c.setHttpClient(httpclient); 127 | 128 | JestResult res = c.execute(new Index.Builder(this 129 | .loadFile("ur_test_normal.json")) 130 | .index("securityconfiguration").type("actionpathfilter") 131 | .id("actionpathfilter").refresh(true) 132 | .setHeader("Authorization", "foo bar").build()); 133 | 134 | 135 | res = c.execute(new Search.Builder(this 136 | .loadFile("field_query.json")).refresh(true) 137 | .setHeader("Authorization", "foo bar").build()); 138 | 139 | this.log.info(res.getJsonString()); 140 | Assert.assertTrue(res.isSucceeded()); 141 | 142 | }*/ 143 | } 144 | -------------------------------------------------------------------------------- /src/test/java/org/elasticsearch/plugins/security/filter/AbstractPermTests.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.plugins.security.filter; 2 | 3 | import java.io.IOException; 4 | import java.io.StringWriter; 5 | 6 | import org.apache.commons.io.IOUtils; 7 | import org.elasticsearch.plugins.security.service.permission.UserRoleCallback; 8 | import org.junit.Rule; 9 | import org.junit.rules.TestName; 10 | import org.junit.rules.TestWatcher; 11 | import org.junit.runner.Description; 12 | 13 | public class AbstractPermTests { 14 | 15 | @Rule 16 | public TestName name = new TestName(); 17 | 18 | @Rule 19 | public TestWatcher testWatcher = new TestWatcher() { 20 | @Override 21 | protected void starting(final Description description) { 22 | String methodName = description.getMethodName(); 23 | String className = description.getClassName(); 24 | className = className.substring(className.lastIndexOf('.') + 1); 25 | System.out.println("Starting JUnit-test: " + className + " " + methodName); 26 | } 27 | }; 28 | 29 | protected String loadFile(final String file) throws IOException { 30 | 31 | final StringWriter sw = new StringWriter(); 32 | IOUtils.copy(this.getClass().getResourceAsStream("/" + file), sw); 33 | return sw.toString(); 34 | 35 | } 36 | 37 | 38 | static class TestCallback implements UserRoleCallback { 39 | 40 | private final String user; 41 | private final String role; 42 | 43 | protected TestCallback(final String user, final String role) { 44 | super(); 45 | this.user = user; 46 | this.role = role; 47 | } 48 | 49 | @Override 50 | public String getRemoteuser() { 51 | // TODO Auto-generated method stub 52 | return user; 53 | } 54 | 55 | @Override 56 | public boolean isRemoteUserInRole(final String role) { 57 | // TODO Auto-generated method stub 58 | return role.equals(this.role); 59 | } 60 | 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/test/java/org/elasticsearch/plugins/security/filter/DlsTests.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.plugins.security.filter; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | 5 | import java.net.InetAddress; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | import junit.framework.Assert; 10 | 11 | import org.elasticsearch.plugins.security.filter.AbstractPermTests.TestCallback; 12 | import org.elasticsearch.plugins.security.service.permission.PermEvaluator; 13 | import org.junit.Test; 14 | 15 | public class DlsTests extends AbstractPermTests { 16 | 17 | @Test 18 | public void testNormal1() throws Exception { 19 | 20 | final List tokens = new ArrayList(); 21 | tokens.add("default"); 22 | tokens.add("everyone"); 23 | final PermEvaluator> evaluator = new PermDlsEvaluator( 24 | loadFile("dls_test_normal.json")); 25 | assertTrue(evaluator.evaluatePerm(null, null, 26 | InetAddress.getByName("8.8.8.8"), null).equals(tokens)); 27 | 28 | } 29 | 30 | @Test 31 | public void testNormal2() throws Exception { 32 | 33 | final List tokens = new ArrayList(); 34 | tokens.add("guesttoken"); 35 | final PermEvaluator> evaluator = new PermDlsEvaluator( 36 | loadFile("dls_test_normal.json")); 37 | assertTrue(evaluator.evaluatePerm(null, null, 38 | InetAddress.getByName("8.8.8.8"), new TestCallback("Guest", "guest")).equals(tokens)); 39 | 40 | } 41 | 42 | @Test 43 | public void testEmptyArraysAllow() throws Exception { 44 | 45 | final List tokens = new ArrayList(); 46 | tokens.add("guesttoken"); 47 | 48 | final PermEvaluator> evaluator = new PermDlsEvaluator( 49 | loadFile("dls_allow_emptyarray.json")); 50 | 51 | org.junit.Assert.assertEquals(tokens, evaluator.evaluatePerm(null, null, 52 | InetAddress.getByName("8.8.8.8"), null)); 53 | 54 | 55 | } 56 | 57 | @Test 58 | public void testEmptyArrays() throws Exception { 59 | 60 | final List tokens = new ArrayList(); 61 | 62 | final PermEvaluator> evaluator = new PermDlsEvaluator( 63 | loadFile("dls_denyall_emptyarray.json")); 64 | 65 | org.junit.Assert.assertEquals(tokens, evaluator.evaluatePerm(null, null, 66 | InetAddress.getByName("8.8.8.8"), null)); 67 | 68 | 69 | } 70 | 71 | 72 | @Test 73 | public void issueDls1() throws Exception { 74 | 75 | final List tokens = new ArrayList(); 76 | final PermEvaluator> evaluator = new PermDlsEvaluator( 77 | loadFile("issues/dls1/rules.json")); 78 | assertTrue(evaluator.evaluatePerm(null, null, 79 | InetAddress.getByName("8.8.8.8"), null).equals(tokens)); 80 | 81 | } 82 | 83 | @Test 84 | public void issueDls2() throws Exception { 85 | 86 | List tokens = new ArrayList(); 87 | tokens.add("t_everyone"); 88 | 89 | final List indices = new ArrayList(); 90 | indices.add("testindex1"); 91 | 92 | 93 | final PermEvaluator> evaluator = new PermDlsEvaluator( 94 | loadFile("issues/dls2/rules.json")); 95 | assertTrue(evaluator.evaluatePerm(indices, null, 96 | InetAddress.getByName("8.8.8.8"), null).equals(tokens)); 97 | 98 | } 99 | 100 | 101 | } 102 | -------------------------------------------------------------------------------- /src/test/java/org/elasticsearch/plugins/security/filter/SecurityPermTests.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.plugins.security.filter; 2 | 3 | import java.io.IOException; 4 | import java.io.StringWriter; 5 | import java.net.InetAddress; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | import org.apache.commons.io.IOUtils; 10 | import org.elasticsearch.plugins.security.MalformedConfigurationException; 11 | import org.elasticsearch.plugins.security.service.permission.PermEvaluator; 12 | import org.elasticsearch.plugins.security.service.permission.UserRoleCallback; 13 | import org.junit.Rule; 14 | import org.junit.Test; 15 | import org.junit.rules.TestName; 16 | import org.junit.rules.TestWatcher; 17 | import org.junit.runner.Description; 18 | 19 | import static org.junit.Assert.*; 20 | 21 | /** 22 | * Unit test for simple App. 23 | */ 24 | public class SecurityPermTests extends AbstractPermTests{ 25 | 26 | @Test(expected=IllegalArgumentException.class) 27 | public void testEmptyConfigException() { 28 | 29 | final List indices = new ArrayList(); 30 | indices.add("testindex1"); 31 | indices.add("testindex2"); 32 | new PermLevelEvaluator(null); 33 | } 34 | 35 | 36 | @Test 37 | public void testDefault() throws Exception { 38 | 39 | final List indices = new ArrayList(); 40 | indices.add("testindex1"); 41 | indices.add("testindex2"); 42 | final PermEvaluator evaluator = new PermLevelEvaluator( 43 | loadFile("test_default.json")); 44 | assertTrue(evaluator.evaluatePerm(indices, null, 45 | InetAddress.getByName("8.8.8.8"), null) == PermLevel.ALL); 46 | } 47 | @Test 48 | public void testNormalCases() throws Exception { 49 | 50 | final List indices = new ArrayList(); 51 | indices.add("testindex1"); 52 | indices.add("testindex2"); 53 | 54 | final PermEvaluator evaluator = new PermLevelEvaluator( 55 | loadFile("test_normal.json")); 56 | assertTrue(evaluator.evaluatePerm(indices, null, 57 | InetAddress.getByName("8.8.8.9"), null) == PermLevel.ALL); 58 | 59 | assertTrue(evaluator.evaluatePerm(indices, null, 60 | InetAddress.getByName("8.8.8.8"), null) == PermLevel.READWRITE); 61 | assertTrue(evaluator.evaluatePerm(indices, null, 62 | InetAddress.getByName("127.0.01"), null) == PermLevel.READONLY); 63 | assertTrue(evaluator.evaluatePerm(indices, null, 64 | InetAddress.getByName("1.2.3.4"), null) == PermLevel.NONE); 65 | } 66 | @Test 67 | public void testNormalCasesWithUserRoleTypes() throws Exception { 68 | 69 | final List indices = new ArrayList(); 70 | indices.add("testindex1"); 71 | indices.add("testindex2"); 72 | 73 | final List types = new ArrayList(); 74 | types.add("secrettype"); 75 | 76 | final PermEvaluator evaluator = new PermLevelEvaluator( 77 | loadFile("test_normal_withuserroletypes.json")); 78 | assertTrue(evaluator.evaluatePerm(indices, types, InetAddress 79 | .getByName("127.0.01"), new TestCallback("mister", "unknown")) == PermLevel.READONLY); 80 | 81 | assertTrue(evaluator.evaluatePerm(indices, types, InetAddress 82 | .getByName("127.0.01"), new TestCallback("kirk", "unknown")) == PermLevel.READWRITE); 83 | 84 | assertTrue(evaluator.evaluatePerm(indices, types, InetAddress 85 | .getByName("8.8.8.8"), new TestCallback("kirk", "unknown")) == PermLevel.ALL); 86 | 87 | } 88 | @Test 89 | public void testNormalIndicesCases() throws Exception { 90 | 91 | final List indices = new ArrayList(); 92 | indices.add("testindex1"); 93 | 94 | final PermEvaluator evaluator = new PermLevelEvaluator( 95 | loadFile("test_normal_indices.json")); 96 | assertTrue(evaluator.evaluatePerm(indices, null, 97 | InetAddress.getByName("8.8.8.9"), null) == PermLevel.ALL); 98 | assertTrue(evaluator.evaluatePerm(indices, null, 99 | InetAddress.getByName("8.8.8.8"), null) == PermLevel.READWRITE); 100 | assertTrue(evaluator.evaluatePerm(indices, null, 101 | InetAddress.getByName("128.0.0.1"), null) == PermLevel.ALL); 102 | assertTrue(evaluator.evaluatePerm(indices, null, 103 | InetAddress.getByName("1.2.3.4"), null) == PermLevel.NONE); 104 | } 105 | @Test 106 | public void testWildcardIndicesCases() throws Exception { 107 | 108 | final List indices = new ArrayList(); 109 | indices.add("testindex1"); 110 | 111 | final PermEvaluator evaluator = new PermLevelEvaluator( 112 | loadFile("test_wildcard_indices.json")); 113 | 114 | assertTrue(evaluator.evaluatePerm(indices, null, 115 | InetAddress.getByName("8.8.8.9"), null) == PermLevel.ALL); 116 | assertTrue(evaluator.evaluatePerm(indices, null, 117 | InetAddress.getByName("8.8.8.8"), null) == PermLevel.READWRITE); 118 | assertTrue(evaluator.evaluatePerm(indices, null, 119 | InetAddress.getByName("128.0.0.1"), null) == PermLevel.ALL); 120 | assertTrue(evaluator.evaluatePerm(indices, null, 121 | InetAddress.getByName("1.2.3.4"), null) == PermLevel.NONE); 122 | } 123 | @Test 124 | public void testWildcardMultipleIndicesCases() throws Exception { 125 | 126 | final List indices = new ArrayList(); 127 | indices.add("testindex1"); 128 | indices.add("testindex3"); 129 | 130 | final PermEvaluator evaluator = new PermLevelEvaluator( 131 | loadFile("test_multiple_wildcard_indices.json")); 132 | 133 | assertTrue(evaluator.evaluatePerm(indices, null, 134 | InetAddress.getByName("8.8.8.9"), null) == PermLevel.NONE); 135 | assertTrue(evaluator.evaluatePerm(indices, null, 136 | InetAddress.getByName("8.8.8.8"), null) == PermLevel.READWRITE); 137 | assertTrue(evaluator.evaluatePerm(indices, null, 138 | InetAddress.getByName("127.1.0.1"), null) == PermLevel.NONE); 139 | 140 | assertTrue(evaluator.evaluatePerm(indices, null, 141 | InetAddress.getByName("1.2.3.4"), null) == PermLevel.ALL); 142 | } 143 | @Test 144 | public void testWildcardIndicesCases2() throws Exception { 145 | 146 | final List indices = new ArrayList(); 147 | indices.add("testindex"); 148 | indices.add("xtestindexy"); 149 | 150 | final PermEvaluator evaluator = new PermLevelEvaluator( 151 | loadFile("test_wildcard_indices2.json")); 152 | 153 | assertTrue(evaluator.evaluatePerm(indices, null, 154 | InetAddress.getByName("127.0.0.1"), null) == PermLevel.READWRITE); 155 | 156 | } 157 | @Test 158 | public void testWildcardIndicesCases22() throws Exception { 159 | 160 | final List indices = new ArrayList(); 161 | indices.add("testindex-1020"); 162 | indices.add("testindex-9"); 163 | 164 | final PermEvaluator evaluator = new PermLevelEvaluator( 165 | loadFile("test_wildcard_indices2.json")); 166 | 167 | assertTrue(evaluator.evaluatePerm(indices, null, 168 | InetAddress.getByName("127.0.0.1"), null) == PermLevel.ALL); 169 | 170 | } 171 | @Test 172 | public void testWildcardCases() throws Exception { 173 | 174 | final List indices = new ArrayList(); 175 | indices.add("testindex1"); 176 | indices.add("testindex2"); 177 | 178 | final PermEvaluator evaluator = new PermLevelEvaluator( 179 | loadFile("test_wildcard.json")); 180 | assertTrue(evaluator.evaluatePerm(indices, null, 181 | InetAddress.getByName("8.9.8.9"), null) == PermLevel.ALL); 182 | assertTrue(evaluator.evaluatePerm(indices, null, 183 | InetAddress.getByName("8.9.12.8"), null) == PermLevel.READWRITE); 184 | assertTrue(evaluator.evaluatePerm(indices, null, 185 | InetAddress.getByName("127.4.0.1"), null) == PermLevel.READONLY); 186 | assertTrue(evaluator.evaluatePerm(indices, null, 187 | InetAddress.getByName("103.2.3.4"), null) == PermLevel.NONE); 188 | } 189 | @Test 190 | public void testNormalCasesFQHN() throws Exception { 191 | 192 | final List indices = new ArrayList(); 193 | indices.add("testindex1"); 194 | indices.add("testindex2"); 195 | 196 | final PermEvaluator evaluator = new PermLevelEvaluator( 197 | loadFile("test_normal_fqn.json")); 198 | assertTrue(evaluator.evaluatePerm(indices, null, 199 | InetAddress.getByName("8.8.8.8"), null) == PermLevel.NONE); 200 | assertTrue(evaluator.evaluatePerm(indices, null, 201 | InetAddress.getByName("google-public-dns-a.google.com"), null) == PermLevel.NONE); 202 | 203 | } 204 | @Test 205 | public void testWildcardCasesFQHN() throws Exception { 206 | 207 | final List indices = new ArrayList(); 208 | indices.add("testindex1"); 209 | indices.add("testindex2"); 210 | 211 | final PermEvaluator evaluator = new PermLevelEvaluator( 212 | loadFile("test_wildcard_fqn.json")); 213 | assertTrue(evaluator.evaluatePerm(indices, null, 214 | InetAddress.getByName("8.8.8.8"), null) == PermLevel.NONE); 215 | assertTrue(evaluator.evaluatePerm(indices, null, 216 | InetAddress.getByName("google-public-dns-a.google.com"), null) == PermLevel.NONE); 217 | 218 | } 219 | @Test 220 | public void testBadFormat() throws Exception { 221 | 222 | final List indices = new ArrayList(); 223 | indices.add("testindex1"); 224 | indices.add("testindex2"); 225 | 226 | final PermEvaluator evaluator = new PermLevelEvaluator( 227 | loadFile("test_bad_format.json")); 228 | 229 | try { 230 | assertTrue(evaluator.evaluatePerm(indices, null, 231 | InetAddress.getByName("127.0.0.1"), null) == PermLevel.NONE); 232 | fail(); 233 | } catch (final MalformedConfigurationException e) { 234 | 235 | } 236 | 237 | } 238 | @Test 239 | public void testNoDefault() throws Exception { 240 | 241 | final List indices = new ArrayList(); 242 | indices.add("testindex1"); 243 | indices.add("testindex2"); 244 | 245 | final PermEvaluator evaluator = new PermLevelEvaluator( 246 | loadFile("test_no_default.json")); 247 | try { 248 | assertTrue(evaluator.evaluatePerm(indices, null, 249 | InetAddress.getByName("8.8.8.9"), null) == PermLevel.ALL); 250 | fail(); 251 | } catch (final MalformedConfigurationException e) { 252 | 253 | } 254 | 255 | } 256 | @Test 257 | public void testMalformedStructure() throws Exception { 258 | 259 | final List indices = new ArrayList(); 260 | indices.add("testindex1"); 261 | indices.add("testindex2"); 262 | 263 | final PermEvaluator evaluator = new PermLevelEvaluator( 264 | loadFile("test_malformed_structure.json")); 265 | try { 266 | assertTrue(evaluator.evaluatePerm(indices, null, 267 | InetAddress.getByName("8.8.8.9"), null) == PermLevel.ALL); 268 | fail(); 269 | } catch (final MalformedConfigurationException e) { 270 | 271 | } 272 | 273 | } 274 | 275 | @Test 276 | public void testEmptyArrays() throws Exception { 277 | 278 | final PermEvaluator evaluator = new PermLevelEvaluator( 279 | loadFile("test_denyall_emptyarray.json")); 280 | 281 | assertTrue(evaluator.evaluatePerm(null, null, 282 | InetAddress.getByName("8.8.8.8"), null) == PermLevel.NONE); 283 | 284 | 285 | } 286 | 287 | } 288 | -------------------------------------------------------------------------------- /src/test/java/org/elasticsearch/plugins/security/waffle/TestProvider.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.plugins.security.waffle; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.servlet.http.HttpServletRequest; 6 | import javax.servlet.http.HttpServletResponse; 7 | 8 | import waffle.mock.MockWindowsAuthProvider; 9 | import waffle.servlet.spi.SecurityFilterProvider; 10 | import waffle.windows.auth.IWindowsAuthProvider; 11 | import waffle.windows.auth.IWindowsIdentity; 12 | 13 | public class TestProvider implements SecurityFilterProvider { 14 | 15 | private MockWindowsAuthProvider _auth = null; 16 | 17 | public TestProvider(final IWindowsAuthProvider auth) { 18 | _auth = new MockWindowsAuthProvider(); 19 | // System.out.println("ctor()"); 20 | 21 | } 22 | 23 | @Override 24 | public void sendUnauthorized(final HttpServletResponse response) { 25 | // TODO Auto-generated method stub 26 | // System.out.println("sendUnauthorized()"); 27 | } 28 | 29 | @Override 30 | public boolean isPrincipalException(final HttpServletRequest request) { 31 | // System.out.println("isPrincipalException()"); 32 | return false; 33 | } 34 | 35 | @Override 36 | public IWindowsIdentity doFilter(final HttpServletRequest request, 37 | final HttpServletResponse response) throws IOException { 38 | // TODO Auto-generated method stub 39 | // System.out.println("auth guest"); 40 | return _auth.logonUser("Guest", ""); 41 | } 42 | 43 | @Override 44 | public boolean isSecurityPackageSupported(final String securityPackage) { 45 | // System.out.println("support " + securityPackage); 46 | return true; 47 | } 48 | 49 | @Override 50 | public void initParameter(final String parameterName, 51 | final String parameterValue) { 52 | // TODO Auto-generated method stub 53 | // System.out.println("init " + parameterName + "=" + parameterValue); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/test/resources/dls_allow_emptyarray.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [ 3 | { 4 | "hosts":[], 5 | "indices":[], 6 | "types":[], 7 | "users":[], 8 | "roles":[], 9 | "dlstoken" : ["guesttoken"] 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /src/test/resources/dls_default_test_allowall.json: -------------------------------------------------------------------------------- 1 | { 2 | "dlspermissions": 3 | { 4 | "*" : 5 | { 6 | "read" :["*"], 7 | "update" : ["*"], 8 | "delete" : ["*"] 9 | } 10 | 11 | 12 | } 13 | } -------------------------------------------------------------------------------- /src/test/resources/dls_default_test_denyall.json: -------------------------------------------------------------------------------- 1 | { 2 | "dlspermissions": 3 | { 4 | "*" : 5 | { 6 | "read" :[], 7 | "update" : [], 8 | "delete" : [] 9 | } 10 | 11 | 12 | } 13 | } -------------------------------------------------------------------------------- /src/test/resources/dls_denyall_emptyarray.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [ 3 | { 4 | "hosts":[], 5 | "indices":[], 6 | "types":[], 7 | "users":[], 8 | "roles":[], 9 | "dlstoken" : [] 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /src/test/resources/dls_dummy_content.json: -------------------------------------------------------------------------------- 1 | { 2 | "dlspermissions": 3 | { 4 | "*" : 5 | { 6 | "read" :[], 7 | "update" : [], 8 | "delete" : [] 9 | }, 10 | 11 | "message" : 12 | { 13 | "read" :[ "guesttoken" ], 14 | "update" : [ "guesttoken","admin"], 15 | "delete" : [] 16 | }, 17 | 18 | "payload.level_12.*" : 19 | { 20 | "read" :[ "guesttoken" ], 21 | "update" : [ "guesttoken","admin"], 22 | "delete" : [] 23 | } 24 | }, 25 | 26 | "twitteruser" : "olomo1", 27 | "post_date" : "2009-11-15T14:12:12", 28 | "message" : "playing with Elastic Search", 29 | "payload" : { 30 | "level_1":"hello_level_1", 31 | "level_12": 32 | { 33 | "level_2":[ 34 | 35 | { 36 | "min":"a", 37 | "max":"b" 38 | }, 39 | { 40 | "min":"c", 41 | "max":"d" 42 | } 43 | 44 | 45 | ] 46 | } 47 | 48 | } 49 | } -------------------------------------------------------------------------------- /src/test/resources/dls_dummy_content_updt.json: -------------------------------------------------------------------------------- 1 | { 2 | "dlspermissions": 3 | { 4 | "*" : 5 | { 6 | "read" :[], 7 | "update" : [], 8 | "delete" : [] 9 | }, 10 | 11 | "message" : 12 | { 13 | "read" :[ "guesttoken" ], 14 | "update" : [ "guesttoken","admin"], 15 | "delete" : [] 16 | }, 17 | 18 | "payload.level_12.*" : 19 | { 20 | "read" :[ "guesttoken" ], 21 | "update" : [ "guesttoken","admin"], 22 | "delete" : [] 23 | } 24 | }, 25 | 26 | "twitteruser" : "olomo1", 27 | "post_date" : "2013-11-15T14:12:12", 28 | "message" : "playing with updated docs", 29 | "payload" : { 30 | "level_1":"hello_level_1", 31 | "level_12": 32 | { 33 | "level_2":[ 34 | 35 | { 36 | "min":"a", 37 | "max":"b" 38 | }, 39 | { 40 | "min":"c", 41 | "max":"d" 42 | } 43 | 44 | 45 | ] 46 | } 47 | 48 | } 49 | } -------------------------------------------------------------------------------- /src/test/resources/dls_dummy_content_without_dls.json: -------------------------------------------------------------------------------- 1 | { 2 | "user" : "misterx", 3 | "post_date" : "2008-11-15T14:12:12", 4 | "message" : "some stuff here" 5 | } -------------------------------------------------------------------------------- /src/test/resources/dls_field_query.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "query" : { 4 | "term" : { "twitteruser" : "olomo1" } 5 | } 6 | } -------------------------------------------------------------------------------- /src/test/resources/dls_test_normal.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [ 3 | { 4 | "dlstoken" : [ "default,everyone" ] 5 | }, 6 | { 7 | "users" : [ "Guest" ], 8 | "dlstoken" : [ "guesttoken" ] 9 | }, 10 | { 11 | "roles" : [ "admin","marketing" ], 12 | "dlstoken" : [ "abstracttoken1,tokenx" ] 13 | } 14 | , 15 | { 16 | "roles" : [ "admin","marketing" ], 17 | "types" : ["type1","type2"], 18 | "dlstoken" : [ "abstracttoken1,tokenx" ] 19 | } 20 | ] 21 | } -------------------------------------------------------------------------------- /src/test/resources/dummy_content.json: -------------------------------------------------------------------------------- 1 | { 2 | "user" : "saly", 3 | "post_date" : "2009-11-15T14:12:12", 4 | "message" : "playing with Elastic Search" 5 | } -------------------------------------------------------------------------------- /src/test/resources/hnelsonclient.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salyh/elasticsearch-security-plugin/1d7f63db18c9f10171a8afe50dce0bd5f56000dc/src/test/resources/hnelsonclient.p12 -------------------------------------------------------------------------------- /src/test/resources/issues/dls1/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "dlspermissions": { 3 | "*": { 4 | "read": [ "*" ], 5 | "update": [], 6 | "delete": [] 7 | }, 8 | "message": { 9 | "read": [ "t_admin" ], 10 | "update": [ "t_admin" ], 11 | "delete": [ "t_admin" ] 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/test/resources/issues/dls1/rules.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [ 3 | { 4 | "hosts" : [ "*" ], 5 | "indices" : [ "*" ], 6 | "types" : [ "*" ], 7 | "dlstoken" : [ ] 8 | }, 9 | { 10 | "hosts" : [ "*" ], 11 | "indices" : [ "logstash.*" ], 12 | "types" : [ "*" ], 13 | "dlstoken" : [ "t_admin" ] 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /src/test/resources/issues/dls2/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "loglevel": "INFO", 3 | "message": "INFO - this is an INFO message", 4 | "@timestamp": "2014-04-03T07:57:39.180Z" 5 | } -------------------------------------------------------------------------------- /src/test/resources/issues/dls2/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "dlspermissions": { 3 | "*": { 4 | "read": [], 5 | "update": [], 6 | "delete": [] 7 | }, 8 | "loglevel": { 9 | "read": [ "t_everyone" ], 10 | "update": [ "t_admin" ], 11 | "delete": [ "t_admin" ] 12 | }, 13 | "@timestamp": { 14 | "read": [ "t_everyone" ], 15 | "update": [ "t_admin" ], 16 | "delete": [ "t_admin" ] 17 | }, 18 | "message": { 19 | "read": [ "t_admin" ], 20 | "update": [ "t_admin" ], 21 | "delete": [ "t_admin" ] 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/test/resources/issues/dls2/expected.json: -------------------------------------------------------------------------------- 1 | {"_type":"log","_version":1,"_source":{"@timestamp":"2014-04-03T07:57:39.180Z","loglevel":"INFO"},"_id":"onelog","_index":"logstash-2014.04.03","found":true} -------------------------------------------------------------------------------- /src/test/resources/issues/dls2/rules.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [ 3 | { 4 | "hosts" : [ "*" ], 5 | "indices" : [ "*" ], 6 | "types" : [ "*" ], 7 | "dlstoken" : [ "t_everyone" ] 8 | }, 9 | { 10 | "hosts" : [ "one-not-existing-host" ], 11 | "indices" : [ "*" ], 12 | "types" : [ "*" ], 13 | "dlstoken" : [ "t_admin" ] 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /src/test/resources/krb5.conf: -------------------------------------------------------------------------------- 1 | [libdefaults] 2 | default_realm = EXAMPLE.COM 3 | forwardable=true 4 | default_tkt_enctypes = rc4-hmac,aes256-cts-hmac-sha1-96,aes128-cts-hmac-sha1-96 5 | default_tgs_enctypes = rc4-hmac,aes256-cts-hmac-sha1-96,aes128-cts-hmac-sha1-96 6 | 7 | [realms] 8 | EXAMPLE.COM = { 9 | kdc = localhost:6088 10 | default_domain = EXAMPLE.COM 11 | } 12 | 13 | [domain_realm] 14 | .example.com = EXAMPLE.COM 15 | example.com = EXAMPLE.COM 16 | 17 | [login] 18 | krb4_convert = true 19 | krb4_get_tickets = false -------------------------------------------------------------------------------- /src/test/resources/krb5_utest.keytab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salyh/elasticsearch-security-plugin/1d7f63db18c9f10171a8afe50dce0bd5f56000dc/src/test/resources/krb5_utest.keytab -------------------------------------------------------------------------------- /src/test/resources/localhost_tc.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salyh/elasticsearch-security-plugin/1d7f63db18c9f10171a8afe50dce0bd5f56000dc/src/test/resources/localhost_tc.p12 -------------------------------------------------------------------------------- /src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=WARN, out 2 | 3 | log4j.appender.out=org.apache.log4j.ConsoleAppender 4 | log4j.appender.out.layout=org.apache.log4j.PatternLayout 5 | log4j.appender.out.layout.conversionPattern=[%d{HH:mm:ss}][%-5p] %c - %m%n 6 | 7 | log4j.logger.org.apache.catalina.startup=ERROR 8 | log4j.logger.org.apache.catalina.loader.WebappClassLoader=FATAL 9 | log4j.logger.org.elasticsearch.plugins.security=ALL 10 | #log4j.logger.net.sf.michaelo=ALL 11 | #log4j.logger.waffle=INFO 12 | 13 | 14 | #log4j.logger.org.apache.http.wire=ALL 15 | #log4j.logger.org.apache.catalina.authenticator=ALL 16 | #log4j.logger.org.apache.catalina.realm=ALL 17 | #log4j.logger.org.apache.catalina.core.ContainerBase=ALL 18 | #log4j.logger.org.apache.directory=WARN -------------------------------------------------------------------------------- /src/test/resources/login.conf: -------------------------------------------------------------------------------- 1 | spnego-client { 2 | com.sun.security.auth.module.Krb5LoginModule 3 | required 4 | refreshKrb5Config=true 5 | storeKey=true 6 | useTicketCache=false 7 | ; 8 | }; 9 | 10 | 11 | com.sun.security.jgss.krb5.accept { 12 | com.sun.security.auth.module.Krb5LoginModule 13 | required 14 | refreshKrb5Config=true 15 | storeKey=true 16 | isInitiator=false 17 | principal="HTTP/localhost@EXAMPLE.COM" 18 | doNotPrompt=true 19 | useKeyTab=true 20 | keyTab="${project.build.testOutputDirectory}${path.delim}krb5_utest.keytab" 21 | ; 22 | }; 23 | -------------------------------------------------------------------------------- /src/test/resources/non_field_query.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "query" : { 4 | "term" : { "user" : "saly" } 5 | } 6 | } -------------------------------------------------------------------------------- /src/test/resources/test_bad_format.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [ 3 | { 4 | "host" : [ "*" ], 5 | "indices" :[ "*" ], 6 | "garbage" : "ALL" 7 | }, 8 | { 9 | "jean" : [ "192.0.*.*"], 10 | "luc" :[ "index1", "myindex" ], 11 | "picard" : "READWRITE" 12 | }, 13 | { 14 | "james" : ["another*.*.comapny.de" ], 15 | "t" :[ "index3"], 16 | "kirk" : "ALL" 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /src/test/resources/test_default.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [ 3 | { 4 | "hosts" : [ "*" ], 5 | "indices" :[ "*" ], 6 | "permission" : "ALL" 7 | }, 8 | { 9 | "hosts" : [ "192.0.*.*"], 10 | "indices" :[ "index1", "myindex" ], 11 | "permission" : "READWRITE" 12 | }, 13 | { 14 | "hosts" : ["another*.*.comapny.de" ], 15 | "indices" :[ "index3"], 16 | "permission" : "ALL" 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /src/test/resources/test_denyall.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [ 3 | { 4 | "hosts" : [ "*" ], 5 | "indices" :[ "*" ], 6 | "permission" : "NONE" 7 | } 8 | ] 9 | } -------------------------------------------------------------------------------- /src/test/resources/test_denyall_emptyarray.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [ 3 | { 4 | "hosts":[], 5 | "indices":[], 6 | "types":[], 7 | "users":[], 8 | "roles":[], 9 | "permission" : "NONE" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /src/test/resources/test_facet_search.json: -------------------------------------------------------------------------------- 1 | { 2 | "query" : { "query_string" : {"query" : "*"} }, 3 | "facets" : { 4 | "tags" : { "terms" : {"field" : "twitteruser"} } 5 | } 6 | } -------------------------------------------------------------------------------- /src/test/resources/test_malformed_structure.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [ 3 | 4 | { 5 | "hosts : [ "192.0.*.*"], 6 | "indices" : "index1", "myindex" ], 7 | "permission" : "READWRITE" 8 | , 9 | { 10 | "hosts" : ["another*.*.comapny.de" ], 11 | "indices" :[ "index3"], 12 | "permission" : "ALL" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /src/test/resources/test_multiple_wildcard_indices.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [ 3 | { 4 | "hosts" : [ "*" ], 5 | "indices" :[ "*" ], 6 | "permission" : "NONE" 7 | }, 8 | { 9 | "hosts" : [ "1.2.3.4" ], 10 | "indices" :[ "testindex1","testindex3" ], 11 | "permission" : "ALL" 12 | }, 13 | { 14 | "hosts" : [ "127.0.0.1" ], 15 | "indices" :[ "testindex2" ], 16 | "permission" : "READONLY" 17 | }, 18 | { 19 | "hosts" : [ "8.8.8.8" ], 20 | "indices" :[ "*" ], 21 | "permission" : "READWRITE" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /src/test/resources/test_no_default.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [ 3 | 4 | { 5 | "hosts" : [ "192.0.*.*"], 6 | "indices" :[ "index1", "myindex" ], 7 | "permission" : "READWRITE" 8 | }, 9 | { 10 | "hosts" : ["another*.*.comapny.de" ], 11 | "indices" :[ "index3"], 12 | "permission" : "ALL" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /src/test/resources/test_normal.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [ 3 | { 4 | "hosts" : [ "*" ], 5 | "indices" :[ "*" ], 6 | "permission" : "ALL" 7 | }, 8 | { 9 | "hosts" : [ "1.2.3.4" ], 10 | "indices" :[ "*" ], 11 | "permission" : "NONE" 12 | }, 13 | { 14 | "hosts" : [ "127.0.0.1" ], 15 | "indices" :[ "testindex1","testindex2" ], 16 | "permission" : "READONLY" 17 | }, 18 | { 19 | "hosts" : [ "8.8.8.8" ], 20 | "indices" :[ "testindex1","testindex2"], 21 | "permission" : "READWRITE" 22 | } 23 | ] 24 | } 25 | 26 | -------------------------------------------------------------------------------- /src/test/resources/test_normal_fqn.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [ 3 | { 4 | "hosts" : [ "*" ], 5 | "indices" :[ "*" ], 6 | "permission" : "ALL" 7 | }, 8 | 9 | { 10 | "hosts" : [ "google-public-dns-a.google.com" ], 11 | "indices" :[ "*"], 12 | "permission" : "NONE" 13 | } 14 | ] 15 | } 16 | 17 | -------------------------------------------------------------------------------- /src/test/resources/test_normal_indices.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [ 3 | { 4 | "hosts" : [ "*" ], 5 | "indices" :[ "*" ], 6 | "permission" : "ALL" 7 | }, 8 | { 9 | "hosts" : [ "1.2.3.4" ], 10 | "indices" :[ "testindex1" ], 11 | "permission" : "NONE" 12 | }, 13 | { 14 | "hosts" : [ "127.0.0.1" ], 15 | "indices" :[ "testindex2" ], 16 | "permission" : "READONLY" 17 | }, 18 | { 19 | "hosts" : [ "8.8.8.8" ], 20 | "indices" :[ "testindex1" ], 21 | "permission" : "READWRITE" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /src/test/resources/test_normal_withuserroletypes.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [ 3 | { 4 | "permission" : "READONLY" 5 | }, 6 | { 7 | 8 | "roles" : ["batmanrole"], 9 | "indices" :["testindex1","testindex2" ], 10 | "types" : ["secrettype"], 11 | "permission" : "READWRITE" 12 | }, 13 | { 14 | 15 | "roles" : ["batmanrole"], 16 | "indices" :["testindex3" ], 17 | "permission" : "NONE" 18 | }, 19 | { 20 | "users" : ["robin"], 21 | "permission" : "ALL" 22 | }, 23 | { 24 | "hosts" : ["8.8.8.*"], 25 | "permission" : "ALL" 26 | }, 27 | { 28 | 29 | "hosts" : ["9.9.*.*"], 30 | "indices" :["testindex1","testindex2" ], 31 | "permission" : "NONE" 32 | }, 33 | { 34 | "users" : ["kirk","spock"], 35 | "indices" :["testindex1","testindex2" ], 36 | "permission" : "READWRITE" 37 | } 38 | ] 39 | } 40 | 41 | -------------------------------------------------------------------------------- /src/test/resources/test_wildcard.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [ 3 | { 4 | "hosts" : [ "*" ], 5 | "indices" :[ "*" ], 6 | "permission" : "ALL" 7 | }, 8 | { 9 | "hosts" : [ "*.2.3.4" ], 10 | "indices" :[ "*" ], 11 | "permission" : "NONE" 12 | }, 13 | { 14 | "hosts" : [ "127.*.0.1" ], 15 | "indices" :[ "testindex1","testindex2" ], 16 | "permission" : "READONLY" 17 | }, 18 | { 19 | "hosts" : [ "8.*.*.8" ], 20 | "indices" :[ "testindex1","testindex2"], 21 | "permission" : "READWRITE" 22 | } 23 | ] 24 | } 25 | 26 | -------------------------------------------------------------------------------- /src/test/resources/test_wildcard_fqn.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [ 3 | { 4 | "hosts" : [ "*" ], 5 | "indices" :[ "*" ], 6 | "permission" : "ALL" 7 | }, 8 | 9 | { 10 | "hosts" : [ "*google.com" ], 11 | "indices" :[ "*"], 12 | "permission" : "NONE" 13 | } 14 | ] 15 | } 16 | 17 | -------------------------------------------------------------------------------- /src/test/resources/test_wildcard_indices.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [ 3 | { 4 | "hosts" : [ "*" ], 5 | "indices" :[ "*" ], 6 | "permission" : "ALL" 7 | }, 8 | { 9 | "hosts" : [ "1.2.3.4" ], 10 | "indices" :[ "*" ], 11 | "permission" : "NONE" 12 | }, 13 | { 14 | "hosts" : [ "127.0.0.1" ], 15 | "indices" :[ "testindex2" ], 16 | "permission" : "READONLY" 17 | }, 18 | { 19 | "hosts" : [ "8.8.8.8" ], 20 | "indices" :[ "*" ], 21 | "permission" : "READWRITE" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /src/test/resources/test_wildcard_indices2.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [ 3 | 4 | { 5 | 6 | "permission" : "NONE" 7 | }, 8 | { 9 | "hosts" : [ "127.0.0.1" ], 10 | "indices" :[ "testindex-*" ], 11 | "permission" : "ALL" 12 | }, 13 | { 14 | "hosts" : [ "127.0.0.1" ], 15 | "indices" :[ "*testindex*" ], 16 | "permission" : "READWRITE" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /src/test/resources/truststore.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salyh/elasticsearch-security-plugin/1d7f63db18c9f10171a8afe50dce0bd5f56000dc/src/test/resources/truststore.jks -------------------------------------------------------------------------------- /src/test/resources/ur_test_all.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [ 3 | { 4 | "permission" : "ALL" 5 | } 6 | ] 7 | } 8 | 9 | -------------------------------------------------------------------------------- /src/test/resources/ur_test_duplicate.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [ 3 | { 4 | "permission" : "NONE" 5 | }, 6 | { 7 | "hosts" : [ "1.2.3.4" ], 8 | 9 | "permission" : "NONE" 10 | }, 11 | { 12 | "hosts" : [ "*" ], 13 | "users" : [ "Picard","Spock","Guest","Me" ], 14 | "roles" : [ "Commander","Guest","Role1" ], 15 | "indices" :[ "*" ], 16 | "permission" : "READWRITE" 17 | }, 18 | 19 | { 20 | "hosts" : [ "8.8.8.8" ], 21 | "indices" :[ "testindex1","testindex2"], 22 | "permission" : "READWRITE" 23 | }, 24 | { 25 | "hosts" : [ "*" ], 26 | "users" : [ "Spock","Guest","Me","Picard" ], 27 | "roles" : [ "Role1","Commander","Guest" ], 28 | "indices" :[ "*" ], 29 | "permission" : "READWRITE" 30 | } 31 | ] 32 | } 33 | 34 | -------------------------------------------------------------------------------- /src/test/resources/ur_test_normal.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [ 3 | { 4 | "permission" : "NONE" 5 | }, 6 | { 7 | "hosts" : [ "1.2.3.4" ], 8 | 9 | "permission" : "NONE" 10 | }, 11 | { 12 | "hosts" : [ "*" ], 13 | "users" : [ "Picard","Spock","Guest","Me","hnelson","nelsonh" ], 14 | "roles" : [ "Commander","Guest","Role1","dummy_ldap","dummy_sslldap" ], 15 | "indices" :[ "*" ], 16 | "permission" : "READWRITE" 17 | }, 18 | { 19 | "hosts" : [ "8.8.8.8" ], 20 | "indices" :[ "testindex1","testindex2"], 21 | "permission" : "READWRITE" 22 | } 23 | ] 24 | } 25 | 26 | -------------------------------------------------------------------------------- /travisscripts/travis-before-install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | sudo sed -i 's/localhost.localdomain//' /etc/hosts 4 | --------------------------------------------------------------------------------