mirror of
https://github.com/apache/struts.git
synced 2026-08-31 19:35:40 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ad95ab30f2 | |||
| 2eecd16888 | |||
| d8c69691ef |
@@ -4,9 +4,9 @@ notifications:
|
||||
# Send all issue emails (new, closed, comments) to issues@
|
||||
issues: issues@struts.apache.org
|
||||
# Send new/closed PR notifications to commits@
|
||||
pullrequests_status: notifications@struts.apache.org
|
||||
pullrequests_status: commits@struts.apache.org
|
||||
# Send individual PR comments/reviews to issues@
|
||||
pullrequests_comment: notifications@struts.apache.org
|
||||
pullrequests_comment: issues@struts.apache.org
|
||||
# Link opened PRs with JIRA
|
||||
jira_options: link label worklog
|
||||
|
||||
|
||||
@@ -44,12 +44,12 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
uses: github/codeql-action/analyze@v2
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: Java Maven
|
||||
name: Java Build
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
@@ -29,18 +29,31 @@ env:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build and Test
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
java: [ '8', '11', '17', '21' ]
|
||||
java: [ '8', '11', '17' ]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/setup-java@v4
|
||||
- name: Set up cache
|
||||
uses: actions/cache@v3.3.1
|
||||
with:
|
||||
distribution: temurin
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-maven-
|
||||
- name: Set up JDK ${{ matrix.java }}
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: adopt
|
||||
java-version: ${{ matrix.java }}
|
||||
cache: 'maven'
|
||||
- name: Build with Maven on Java ${{ matrix.java }}
|
||||
if: matrix.java != '17'
|
||||
run: mvn -B -V -DskipAssembly verify --no-transfer-progress
|
||||
- name: Code coverage on Java ${{ matrix.java }}
|
||||
if: matrix.java == '17'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SONAR_TOKEN: ${{ secrets.SONARCLOUD_TOKEN }}
|
||||
run: mvn -B -V -Pcoverage -DskipAssembly verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar --no-transfer-progress
|
||||
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: "Run analysis"
|
||||
uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # 2.3.1
|
||||
uses: ossf/scorecard-action@08b4669551908b1024bb425080c797723083c031 # 2.2.0
|
||||
with:
|
||||
results_file: results.sarif
|
||||
results_format: sarif
|
||||
@@ -57,13 +57,13 @@ jobs:
|
||||
publish_results: true
|
||||
|
||||
- name: "Upload artifact"
|
||||
uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # 4.0.0
|
||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # 3.1.3
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
retention-days: 5
|
||||
|
||||
- name: "Upload to code-scanning"
|
||||
uses: github/codeql-action/upload-sarif@03e7845b7bfcd5e7fb63d1ae8c61b0e791134fab # 2.22.11
|
||||
uses: github/codeql-action/upload-sarif@b398f525a5587552e573b247ac661067fafa920b # 2.1.22
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: SonarCloud
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
permissions: read-all
|
||||
|
||||
env:
|
||||
MAVEN_OPTS: -Xmx2048m -Xms1024m
|
||||
LANG: en_US.utf8
|
||||
|
||||
jobs:
|
||||
sonarcloud:
|
||||
name: Scan
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 17
|
||||
cache: 'maven'
|
||||
- env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SONAR_TOKEN: ${{ secrets.SONARCLOUD_TOKEN }}
|
||||
run: mvn -B -V -Pcoverage -DskipAssembly verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar --no-transfer-progress
|
||||
Vendored
+8
-82
@@ -25,36 +25,6 @@ pipeline {
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('JDK 21') {
|
||||
agent {
|
||||
label 'ubuntu'
|
||||
}
|
||||
tools {
|
||||
jdk 'jdk_21_latest'
|
||||
maven 'maven_3_latest'
|
||||
}
|
||||
environment {
|
||||
MAVEN_OPTS = "-Xmx1024m"
|
||||
}
|
||||
stages {
|
||||
stage('Test') {
|
||||
steps {
|
||||
sh './mvnw -B -DskipAssembly verify --no-transfer-progress'
|
||||
}
|
||||
post {
|
||||
always {
|
||||
junit(testResults: '**/surefire-reports/*.xml', allowEmptyResults: true)
|
||||
junit(testResults: '**/failsafe-reports/*.xml', allowEmptyResults: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
cleanWs deleteDirs: true, patterns: [[pattern: '**/target/**', type: 'INCLUDE']]
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('JDK 17') {
|
||||
agent {
|
||||
label 'ubuntu'
|
||||
@@ -69,7 +39,7 @@ pipeline {
|
||||
stages {
|
||||
stage('Build') {
|
||||
steps {
|
||||
sh './mvnw -B -DskipAssembly verify --no-transfer-progress'
|
||||
sh './mvnw -B clean install -DskipTests -DskipAssembly'
|
||||
}
|
||||
}
|
||||
stage('Test') {
|
||||
@@ -85,9 +55,7 @@ pipeline {
|
||||
}
|
||||
stage('Code Quality') {
|
||||
when {
|
||||
anyOf {
|
||||
branch 'master'; branch 'release/struts-7-0-x'
|
||||
}
|
||||
branch 'master'
|
||||
}
|
||||
steps {
|
||||
withCredentials([string(credentialsId: 'asf-struts-sonarcloud', variable: 'SONARCLOUD_TOKEN')]) {
|
||||
@@ -95,49 +63,6 @@ pipeline {
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Build Source & JavaDoc') {
|
||||
when {
|
||||
branch 'release/struts-7-0-x'
|
||||
}
|
||||
steps {
|
||||
dir("local-snapshots-dir/") {
|
||||
deleteDir()
|
||||
}
|
||||
sh './mvnw -B source:jar javadoc:jar -DskipTests -DskipAssembly'
|
||||
}
|
||||
}
|
||||
stage('Deploy Snapshot') {
|
||||
when {
|
||||
branch 'release/struts-7-0-x'
|
||||
}
|
||||
steps {
|
||||
withCredentials([file(credentialsId: 'lukaszlenart-repository-access-token', variable: 'CUSTOM_SETTINGS')]) {
|
||||
sh './mvnw -s \${CUSTOM_SETTINGS} deploy -DskipTests -DskipAssembly'
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Upload nightlies') {
|
||||
when {
|
||||
branch 'release/struts-7-0-x'
|
||||
}
|
||||
steps {
|
||||
sh './mvnw -B package -DskipTests'
|
||||
sshPublisher(publishers: [
|
||||
sshPublisherDesc(
|
||||
configName: 'Nightlies',
|
||||
transfers: [
|
||||
sshTransfer(
|
||||
remoteDirectory: '/struts/snapshot',
|
||||
removePrefix: 'assembly/target/assembly/out',
|
||||
sourceFiles: 'assembly/target/assembly/out/struts-*.zip',
|
||||
cleanRemote: true
|
||||
)
|
||||
],
|
||||
verbose: true
|
||||
)
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
@@ -159,7 +84,7 @@ pipeline {
|
||||
stages {
|
||||
stage('Build') {
|
||||
steps {
|
||||
sh './mvnw -B -DskipAssembly verify --no-transfer-progress'
|
||||
sh './mvnw -B clean install -DskipTests -DskipAssembly'
|
||||
}
|
||||
}
|
||||
stage('Test') {
|
||||
@@ -242,7 +167,8 @@ pipeline {
|
||||
sshTransfer(
|
||||
remoteDirectory: '/struts/snapshot',
|
||||
removePrefix: 'assembly/target/assembly/out',
|
||||
sourceFiles: 'assembly/target/assembly/out/struts-*.zip'
|
||||
sourceFiles: 'assembly/target/assembly/out/struts-*.zip',
|
||||
cleanRemote: true
|
||||
)
|
||||
],
|
||||
verbose: true
|
||||
@@ -263,7 +189,7 @@ pipeline {
|
||||
failure {
|
||||
script {
|
||||
emailext(
|
||||
to: "notifications@struts.apache.org",
|
||||
to: "commits@struts.apache.org",
|
||||
recipientProviders: [[$class: 'DevelopersRecipientProvider']],
|
||||
from: "Mr. Jenkins <jenkins@builds.apache.org>",
|
||||
subject: "Jenkins job ${env.JOB_NAME}#${env.BUILD_NUMBER} failed",
|
||||
@@ -286,7 +212,7 @@ Director of Continuous Integration
|
||||
unstable {
|
||||
script {
|
||||
emailext(
|
||||
to: "notifications@struts.apache.org",
|
||||
to: "commits@struts.apache.org",
|
||||
recipientProviders: [[$class: 'DevelopersRecipientProvider']],
|
||||
from: "Mr. Jenkins <jenkins@builds.apache.org>",
|
||||
subject: "Jenkins job ${env.JOB_NAME}#${env.BUILD_NUMBER} unstable",
|
||||
@@ -309,7 +235,7 @@ Director of Continuous Integration
|
||||
fixed {
|
||||
script {
|
||||
emailext(
|
||||
to: "notifications@struts.apache.org",
|
||||
to: "commits@struts.apache.org",
|
||||
recipientProviders: [[$class: 'DevelopersRecipientProvider']],
|
||||
from: 'Mr. Jenkins <jenkins@builds.apache.org>',
|
||||
subject: "Jenkins job ${env.JOB_NAME}#${env.BUILD_NUMBER} back to normal",
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-parent</artifactId>
|
||||
<version>6.4.0-SNAPSHOT</version>
|
||||
<version>6.3.0.2</version>
|
||||
</parent>
|
||||
<artifactId>struts2-apps</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
@@ -24,12 +24,12 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-apps</artifactId>
|
||||
<version>6.4.0-SNAPSHOT</version>
|
||||
<version>6.3.0.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-rest-showcase</artifactId>
|
||||
<packaging>war</packaging>
|
||||
<version>6.4.0-SNAPSHOT</version>
|
||||
<version>6.3.0.2</version>
|
||||
<name>Struts 2 Rest Showcase Webapp</name>
|
||||
<description>Struts 2 Rest Showcase Example</description>
|
||||
|
||||
@@ -78,6 +78,7 @@
|
||||
<dependency>
|
||||
<groupId>net.sourceforge.htmlunit</groupId>
|
||||
<artifactId>htmlunit</artifactId>
|
||||
<version>2.39.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-apps</artifactId>
|
||||
<version>6.4.0-SNAPSHOT</version>
|
||||
<version>6.3.0.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-showcase</artifactId>
|
||||
@@ -140,6 +140,7 @@
|
||||
<dependency>
|
||||
<groupId>net.sourceforge.htmlunit</groupId>
|
||||
<artifactId>htmlunit</artifactId>
|
||||
<version>2.61.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
+55
-47
@@ -21,67 +21,75 @@
|
||||
package org.apache.struts2.showcase.fileupload;
|
||||
|
||||
import com.opensymphony.xwork2.ActionSupport;
|
||||
import org.apache.struts2.action.UploadedFilesAware;
|
||||
import org.apache.struts2.dispatcher.multipart.UploadedFile;
|
||||
|
||||
import java.util.List;
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Show case File Upload example's action. <code>FileUploadAction</code>
|
||||
*/
|
||||
public class FileUploadAction extends ActionSupport implements UploadedFilesAware {
|
||||
public class FileUploadAction extends ActionSupport {
|
||||
|
||||
private String contentType;
|
||||
private UploadedFile uploadedFile;
|
||||
private String fileName;
|
||||
private String caption;
|
||||
private String originalName;
|
||||
private static final long serialVersionUID = 5156288255337069381L;
|
||||
|
||||
public String input() throws Exception {
|
||||
return SUCCESS;
|
||||
}
|
||||
private String contentType;
|
||||
private File upload;
|
||||
private String fileName;
|
||||
private String caption;
|
||||
|
||||
public String upload() throws Exception {
|
||||
return SUCCESS;
|
||||
}
|
||||
public String input() throws Exception {
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return contentType;
|
||||
}
|
||||
public String upload() throws Exception {
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
// since we are using <s:file name="upload" .../> the file name will be
|
||||
// obtained through getter/setter of <file-tag-name>FileName
|
||||
public String getUploadFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public String getOriginalName() {
|
||||
return originalName;
|
||||
}
|
||||
public void setUploadFileName(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
public Object getUploadedFile() {
|
||||
return uploadedFile.getContent();
|
||||
}
|
||||
|
||||
public String getCaption() {
|
||||
return caption;
|
||||
}
|
||||
// since we are using <s:file name="upload" ... /> the content type will be
|
||||
// obtained through getter/setter of <file-tag-name>ContentType
|
||||
public String getUploadContentType() {
|
||||
return contentType;
|
||||
}
|
||||
|
||||
public void setCaption(String caption) {
|
||||
this.caption = caption;
|
||||
}
|
||||
public void setUploadContentType(String contentType) {
|
||||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
public long getUploadSize() {
|
||||
if (uploadedFile != null) {
|
||||
return uploadedFile.length();
|
||||
} else {
|
||||
return 0;
|
||||
|
||||
// since we are using <s:file name="upload" ... /> the File itself will be
|
||||
// obtained through getter/setter of <file-tag-name>
|
||||
public File getUpload() {
|
||||
return upload;
|
||||
}
|
||||
|
||||
public void setUpload(File upload) {
|
||||
this.upload = upload;
|
||||
}
|
||||
|
||||
|
||||
public String getCaption() {
|
||||
return caption;
|
||||
}
|
||||
|
||||
public void setCaption(String caption) {
|
||||
this.caption = caption;
|
||||
}
|
||||
|
||||
public long getUploadSize() {
|
||||
if (upload != null) {
|
||||
return upload.length();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void withUploadedFiles(List<UploadedFile> uploadedFiles) {
|
||||
this.uploadedFile = uploadedFiles.get(0);
|
||||
this.fileName = uploadedFile.getName();
|
||||
this.contentType = uploadedFile.getContentType();
|
||||
this.originalName = uploadedFile.getOriginalName();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,19 +34,6 @@
|
||||
<constant name="struts.custom.i18n.resources" value="globalMessages" />
|
||||
<constant name="struts.action.extension" value="action,," />
|
||||
|
||||
<constant name="struts.allowlist.enable" value="true" />
|
||||
<constant name="struts.allowlist.packageNames"
|
||||
value="
|
||||
org.apache.struts2.showcase.model,
|
||||
org.apache.struts2.showcase.conversion
|
||||
"/>
|
||||
<constant name="struts.allowlist.classes"
|
||||
value="
|
||||
org.apache.struts2.showcase.UITagExample$Language,
|
||||
org.apache.struts2.showcase.UITagExample$VehicalType,
|
||||
org.apache.struts2.showcase.UITagExample$VehicalSpecific
|
||||
"/>
|
||||
|
||||
<constant name="struts.convention.package.locators.basePackage" value="org.apache.struts2.showcase" />
|
||||
<constant name="struts.convention.result.path" value="/WEB-INF" />
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
<s:script src="%{jqueryJs}"/>
|
||||
<s:url var="bootstrapJs" value='/js/bootstrap.min.js' encode='false' includeParams='none'/>
|
||||
<s:script src="%{bootstrapJs}"/>
|
||||
<s:script>
|
||||
<s:script type="text/javascript">
|
||||
$(function () {
|
||||
var alerts = $('ul.alert').wrap('<div />');
|
||||
alerts.prepend('<a class="close" data-dismiss="alert" href="#">×</a>');
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
*/
|
||||
-->
|
||||
<%@ page
|
||||
language="java"
|
||||
language="java"
|
||||
contentType="text/html; charset=UTF-8"
|
||||
pageEncoding="UTF-8"%>
|
||||
<%@ taglib prefix="s" uri="/struts-tags" %>
|
||||
@@ -37,10 +37,9 @@
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<ul>
|
||||
<li>ContentType: <s:property value="contentType" /></li>
|
||||
<li>FileName: <s:property value="fileName" /></li>
|
||||
<li>Original FileName: <s:property value="originalName" /></li>
|
||||
<li>File: <s:property value="uploadedFile" /></li>
|
||||
<li>ContentType: <s:property value="uploadContentType" /></li>
|
||||
<li>FileName: <s:property value="uploadFileName" /></li>
|
||||
<li>File: <s:property value="upload" /></li>
|
||||
<li>Caption:<s:property value="caption" /></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<s:script>
|
||||
<s:script type="text/javascript">
|
||||
/********************************************************************
|
||||
* JS just used on this page.
|
||||
* Usually this would be placed in a JS file
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-parent</artifactId>
|
||||
<version>6.4.0-SNAPSHOT</version>
|
||||
<version>6.3.0.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-assembly</artifactId>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#set ($stylebase = "https://struts.apache.org/css")
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<!--
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
@@ -16,7 +16,7 @@ software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
under the License.
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
@@ -43,12 +43,12 @@ under the License.
|
||||
<script src='http://struts.apache.org/highlighter/js/$hscript' type='text/javascript'></script>
|
||||
#end
|
||||
|
||||
<script>
|
||||
<script type="text/javascript">
|
||||
SyntaxHighlighter.defaults['toolbar'] = false;
|
||||
SyntaxHighlighter.all();
|
||||
</script>
|
||||
#end
|
||||
<script>
|
||||
<script type="text/javascript" language="javascript">
|
||||
var hide = null;
|
||||
var show = null;
|
||||
var children = null;
|
||||
|
||||
+4
-4
@@ -25,11 +25,11 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-parent</artifactId>
|
||||
<version>6.4.0-SNAPSHOT</version>
|
||||
<version>6.3.0.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-bom</artifactId>
|
||||
<version>6.4.0-SNAPSHOT</version>
|
||||
<version>6.3.0.2</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>Struts 2 Bill of Materials</name>
|
||||
@@ -44,7 +44,7 @@
|
||||
</licenses>
|
||||
|
||||
<properties>
|
||||
<struts-version.version>6.4.0-SNAPSHOT</struts-version.version>
|
||||
<struts-version.version>6.3.0.2</struts-version.version>
|
||||
<maven.site.skip>true</maven.site.skip>
|
||||
<maven.site.deploy.skip>true</maven.site.deploy.skip>
|
||||
</properties>
|
||||
@@ -190,7 +190,7 @@
|
||||
</dependencyManagement>
|
||||
|
||||
<scm>
|
||||
<tag>STRUTS_6_3_0_1</tag>
|
||||
<tag>STRUTS_6_3_0_2</tag>
|
||||
<connection>scm:git:https://gitbox.apache.org/repos/asf/struts.git</connection>
|
||||
<developerConnection>scm:git:https://gitbox.apache.org/repos/asf/struts.git</developerConnection>
|
||||
<url>https://github.com/apache/struts/</url>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-osgi-bundles</artifactId>
|
||||
<version>6.4.0-SNAPSHOT</version>
|
||||
<version>6.3.0.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-osgi-admin-bundle</artifactId>
|
||||
|
||||
@@ -22,13 +22,13 @@
|
||||
<head>
|
||||
<title>OSGi Console</title>
|
||||
|
||||
<@s.link rel="stylesheet" type="text/css" href="${base}${parameters.staticContentPath}/css/shell.css" />
|
||||
<@s.link rel="stylesheet" type="text/css" href="${base}${parameters.staticContentPath}/css/main.css" />
|
||||
<@s.link rel="stylesheet" type="text/css" href="${base}${parameters.staticContentPath}/css/redmond/jquery-ui-1.12.1.redmond.css" />
|
||||
<link rel="stylesheet" type="text/css" href="<@s.url value="${parameters.staticContentPath}/css/shell.css" />" />
|
||||
<link rel="stylesheet" type="text/css" href="<@s.url value="${parameters.staticContentPath}/css/main.css" />" />
|
||||
<link rel="stylesheet" type="text/css" href="<@s.url value="${parameters.staticContentPath}/css/redmond/jquery-ui-1.12.1.redmond.css" />" />
|
||||
|
||||
<@s.script src="${base}${parameters.staticContentPath}/js/shell.js" />
|
||||
<@s.script src="${base}${parameters.staticContentPath}/js/jquery-1.12.4.min.js" />
|
||||
<@s.script src="${base}${parameters.staticContentPath}/js/jquery-ui-1.12.1.min.js" />
|
||||
<script src="<@s.url value="${parameters.staticContentPath}/js/shell.js" />"></script>
|
||||
<script src="<@s.url value="${parameters.staticContentPath}/js/jquery-1.12.4.min.js" />"></script>
|
||||
<script src="<@s.url value="${parameters.staticContentPath}/js/jquery-ui-1.12.1.min.js" />"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="menu">
|
||||
|
||||
@@ -22,17 +22,17 @@
|
||||
<head>
|
||||
<title>${bundle.symbolicName!}</title>
|
||||
|
||||
<@s.link rel="stylesheet" type="text/css" href="${base}${parameters.staticContentPath}/css/main.css" />
|
||||
<@s.link rel="stylesheet" type="text/css" href="${base}${parameters.staticContentPath}/css/redmond/jquery-ui-1.12.1.redmond.css" />
|
||||
<link rel="stylesheet" type="text/css" href="<@s.url value="${parameters.staticContentPath}/css/main.css" />" />
|
||||
<link rel="stylesheet" type="text/css" href="<@s.url value="${parameters.staticContentPath}/css/redmond/jquery-ui-1.12.1.redmond.css" />" />
|
||||
|
||||
<@s.script src="${base}${parameters.staticContentPath}/js/jquery-1.12.4.min.js"/>
|
||||
<@s.script src="${base}${parameters.staticContentPath}/js/jquery-ui-1.12.1.min.js" />
|
||||
<script src="<@s.url value="${parameters.staticContentPath}/js/jquery-1.12.4.min.js" />"></script>
|
||||
<script src="<@s.url value="${parameters.staticContentPath}/js/jquery-ui-1.12.1.min.js" />"></script>
|
||||
|
||||
<@s.script>
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
$("#tabs").tabs();
|
||||
});
|
||||
</@s.script>
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
@@ -72,7 +72,7 @@
|
||||
<span class="ui-icon ui-icon-transferthick-e-w"></span>
|
||||
OSGi Shell
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<@s.actionerror />
|
||||
|
||||
@@ -22,11 +22,11 @@
|
||||
<head>
|
||||
<title>OSGi Bundles</title>
|
||||
|
||||
<@s.link rel="stylesheet" type="text/css" href="${base}${parameters.staticContentPath}/css/main.css" />
|
||||
<@s.link rel="stylesheet" type="text/css" href="${base}${parameters.staticContentPath}/css/redmond/jquery-ui-1.12.1.redmond.css" />
|
||||
<link rel="stylesheet" type="text/css" href="<@s.url value="${parameters.staticContentPath}/css/main.css" />" />
|
||||
<link rel="stylesheet" type="text/css" href="<@s.url value="${parameters.staticContentPath}/css/redmond/jquery-ui-1.12.1.redmond.css" />" />
|
||||
|
||||
<@s.script src="${base}${parameters.staticContentPath}/js/jquery-1.12.4.min.js" />
|
||||
<@s.script src="${base}${parameters.staticContentPath}/js/jquery-ui-1.12.1.min.js" />
|
||||
<script src="<@s.url value="${parameters.staticContentPath}/js/jquery-1.12.4.min.js" />"></script>
|
||||
<script src="<@s.url value="${parameters.staticContentPath}/js/jquery-ui-1.12.1.min.js" />"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
<span class="ui-icon ui-icon-transferthick-e-w"></span>
|
||||
OSGi Shell
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<@s.actionerror />
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-osgi-bundles</artifactId>
|
||||
<version>6.4.0-SNAPSHOT</version>
|
||||
<version>6.3.0.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-osgi-demo-bundle</artifactId>
|
||||
@@ -39,8 +39,8 @@
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-digester3</artifactId>
|
||||
<groupId>commons-digester</groupId>
|
||||
<artifactId>commons-digester</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-parent</artifactId>
|
||||
<version>6.4.0-SNAPSHOT</version>
|
||||
<version>6.3.0.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-osgi-bundles</artifactId>
|
||||
|
||||
+6
-6
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-parent</artifactId>
|
||||
<version>6.4.0-SNAPSHOT</version>
|
||||
<version>6.3.0.2</version>
|
||||
</parent>
|
||||
<artifactId>struts2-core</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
@@ -184,11 +184,6 @@
|
||||
<artifactId>freemarker</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
@@ -222,6 +217,11 @@
|
||||
<artifactId>commons-io</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
import com.opensymphony.xwork2.config.Configuration;
|
||||
import com.opensymphony.xwork2.config.ConfigurationException;
|
||||
import com.opensymphony.xwork2.config.ConfigurationManager;
|
||||
import com.opensymphony.xwork2.config.ConfigurationProvider;
|
||||
import com.opensymphony.xwork2.inject.Container;
|
||||
import com.opensymphony.xwork2.inject.ContainerBuilder;
|
||||
import com.opensymphony.xwork2.inject.Context;
|
||||
import com.opensymphony.xwork2.inject.Factory;
|
||||
import com.opensymphony.xwork2.inject.Scope;
|
||||
import com.opensymphony.xwork2.test.StubConfigurationProvider;
|
||||
import com.opensymphony.xwork2.util.XWorkTestCaseHelper;
|
||||
import com.opensymphony.xwork2.util.location.LocatableProperties;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
|
||||
public abstract class XWorkJUnit4TestCase {
|
||||
|
||||
protected ConfigurationManager configurationManager;
|
||||
protected Configuration configuration;
|
||||
protected Container container;
|
||||
protected ActionProxyFactory actionProxyFactory;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
configurationManager = XWorkTestCaseHelper.setUp();
|
||||
configuration = configurationManager.getConfiguration();
|
||||
container = configuration.getContainer();
|
||||
actionProxyFactory = container.getInstance(ActionProxyFactory.class);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
XWorkTestCaseHelper.tearDown(configurationManager);
|
||||
}
|
||||
|
||||
protected void loadConfigurationProviders(ConfigurationProvider... providers) {
|
||||
configurationManager = XWorkTestCaseHelper.loadConfigurationProviders(configurationManager, providers);
|
||||
configuration = configurationManager.getConfiguration();
|
||||
container = configuration.getContainer();
|
||||
actionProxyFactory = container.getInstance(ActionProxyFactory.class);
|
||||
}
|
||||
|
||||
protected void loadButAdd(final Class<?> type, final Object impl) {
|
||||
loadButAdd(type, Container.DEFAULT_NAME, impl);
|
||||
}
|
||||
|
||||
protected void loadButAdd(final Class<?> type, final String name, final Object impl) {
|
||||
loadConfigurationProviders(new StubConfigurationProvider() {
|
||||
@Override
|
||||
public void register(ContainerBuilder builder,
|
||||
LocatableProperties props) throws ConfigurationException {
|
||||
builder.factory(type, name, new Factory() {
|
||||
public Object create(Context context) throws Exception {
|
||||
return impl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class type() {
|
||||
return impl.getClass();
|
||||
}
|
||||
}, Scope.SINGLETON);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -64,6 +64,10 @@ public abstract class XWorkTestCase extends TestCase {
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
XWorkTestCaseHelper.tearDown(configurationManager);
|
||||
configurationManager = null;
|
||||
configuration = null;
|
||||
container = null;
|
||||
actionProxyFactory = null;
|
||||
}
|
||||
|
||||
protected void loadConfigurationProviders(ConfigurationProvider... providers) {
|
||||
|
||||
@@ -18,110 +18,39 @@
|
||||
*/
|
||||
package com.opensymphony.xwork2.config.impl;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.DefaultLocaleProviderFactory;
|
||||
import com.opensymphony.xwork2.DefaultTextProvider;
|
||||
import com.opensymphony.xwork2.FileManager;
|
||||
import com.opensymphony.xwork2.FileManagerFactory;
|
||||
import com.opensymphony.xwork2.LocaleProviderFactory;
|
||||
import com.opensymphony.xwork2.LocalizedTextProvider;
|
||||
import com.opensymphony.xwork2.ObjectFactory;
|
||||
import com.opensymphony.xwork2.StrutsTextProviderFactory;
|
||||
import com.opensymphony.xwork2.TextProvider;
|
||||
import com.opensymphony.xwork2.TextProviderFactory;
|
||||
import com.opensymphony.xwork2.config.Configuration;
|
||||
import com.opensymphony.xwork2.config.ConfigurationException;
|
||||
import com.opensymphony.xwork2.config.ContainerProvider;
|
||||
import com.opensymphony.xwork2.config.FileManagerFactoryProvider;
|
||||
import com.opensymphony.xwork2.config.FileManagerProvider;
|
||||
import com.opensymphony.xwork2.config.PackageProvider;
|
||||
import com.opensymphony.xwork2.config.RuntimeConfiguration;
|
||||
import com.opensymphony.xwork2.config.entities.ActionConfig;
|
||||
import com.opensymphony.xwork2.config.entities.InterceptorMapping;
|
||||
import com.opensymphony.xwork2.config.entities.PackageConfig;
|
||||
import com.opensymphony.xwork2.config.entities.ResultConfig;
|
||||
import com.opensymphony.xwork2.config.entities.ResultTypeConfig;
|
||||
import com.opensymphony.xwork2.config.entities.UnknownHandlerConfig;
|
||||
import com.opensymphony.xwork2.*;
|
||||
import com.opensymphony.xwork2.config.*;
|
||||
import com.opensymphony.xwork2.config.entities.*;
|
||||
import com.opensymphony.xwork2.config.providers.EnvsValueSubstitutor;
|
||||
import com.opensymphony.xwork2.config.providers.InterceptorBuilder;
|
||||
import com.opensymphony.xwork2.config.providers.ValueSubstitutor;
|
||||
import com.opensymphony.xwork2.conversion.ConversionAnnotationProcessor;
|
||||
import com.opensymphony.xwork2.conversion.ConversionFileProcessor;
|
||||
import com.opensymphony.xwork2.conversion.ConversionPropertiesProcessor;
|
||||
import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer;
|
||||
import com.opensymphony.xwork2.conversion.TypeConverter;
|
||||
import com.opensymphony.xwork2.conversion.TypeConverterCreator;
|
||||
import com.opensymphony.xwork2.conversion.TypeConverterHolder;
|
||||
import com.opensymphony.xwork2.conversion.impl.ArrayConverter;
|
||||
import com.opensymphony.xwork2.conversion.impl.CollectionConverter;
|
||||
import com.opensymphony.xwork2.conversion.impl.DateConverter;
|
||||
import com.opensymphony.xwork2.conversion.impl.DefaultConversionAnnotationProcessor;
|
||||
import com.opensymphony.xwork2.conversion.impl.DefaultConversionFileProcessor;
|
||||
import com.opensymphony.xwork2.conversion.impl.DefaultObjectTypeDeterminer;
|
||||
import com.opensymphony.xwork2.conversion.impl.NumberConverter;
|
||||
import com.opensymphony.xwork2.conversion.impl.StringConverter;
|
||||
import com.opensymphony.xwork2.conversion.impl.XWorkBasicConverter;
|
||||
import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
|
||||
import com.opensymphony.xwork2.factory.ActionFactory;
|
||||
import com.opensymphony.xwork2.factory.ConverterFactory;
|
||||
import com.opensymphony.xwork2.factory.DefaultActionFactory;
|
||||
import com.opensymphony.xwork2.factory.DefaultInterceptorFactory;
|
||||
import com.opensymphony.xwork2.factory.DefaultResultFactory;
|
||||
import com.opensymphony.xwork2.factory.DefaultUnknownHandlerFactory;
|
||||
import com.opensymphony.xwork2.factory.DefaultValidatorFactory;
|
||||
import com.opensymphony.xwork2.factory.InterceptorFactory;
|
||||
import com.opensymphony.xwork2.factory.ResultFactory;
|
||||
import com.opensymphony.xwork2.factory.StrutsConverterFactory;
|
||||
import com.opensymphony.xwork2.factory.UnknownHandlerFactory;
|
||||
import com.opensymphony.xwork2.factory.ValidatorFactory;
|
||||
import com.opensymphony.xwork2.inject.Container;
|
||||
import com.opensymphony.xwork2.inject.ContainerBuilder;
|
||||
import com.opensymphony.xwork2.inject.Context;
|
||||
import com.opensymphony.xwork2.inject.Factory;
|
||||
import com.opensymphony.xwork2.inject.Scope;
|
||||
import com.opensymphony.xwork2.conversion.*;
|
||||
import com.opensymphony.xwork2.conversion.impl.*;
|
||||
import com.opensymphony.xwork2.factory.*;
|
||||
import com.opensymphony.xwork2.inject.*;
|
||||
import com.opensymphony.xwork2.ognl.BeanInfoCacheFactory;
|
||||
import com.opensymphony.xwork2.ognl.DefaultOgnlBeanInfoCacheFactory;
|
||||
import com.opensymphony.xwork2.ognl.DefaultOgnlExpressionCacheFactory;
|
||||
import com.opensymphony.xwork2.ognl.ExpressionCacheFactory;
|
||||
import com.opensymphony.xwork2.ognl.OgnlCacheFactory;
|
||||
import com.opensymphony.xwork2.ognl.OgnlReflectionProvider;
|
||||
import com.opensymphony.xwork2.ognl.OgnlUtil;
|
||||
import com.opensymphony.xwork2.ognl.OgnlValueStackFactory;
|
||||
import com.opensymphony.xwork2.ognl.SecurityMemberAccess;
|
||||
import com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.RootAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor;
|
||||
import com.opensymphony.xwork2.util.OgnlTextParser;
|
||||
import com.opensymphony.xwork2.util.PatternMatcher;
|
||||
import com.opensymphony.xwork2.util.StrutsLocalizedTextProvider;
|
||||
import com.opensymphony.xwork2.util.TextParser;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import com.opensymphony.xwork2.util.ValueStackFactory;
|
||||
import com.opensymphony.xwork2.util.*;
|
||||
import com.opensymphony.xwork2.util.fs.DefaultFileManager;
|
||||
import com.opensymphony.xwork2.util.fs.DefaultFileManagerFactory;
|
||||
import com.opensymphony.xwork2.util.location.LocatableProperties;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
|
||||
import ognl.MethodAccessor;
|
||||
import ognl.PropertyAccessor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
import org.apache.struts2.conversion.StrutsConversionPropertiesProcessor;
|
||||
import org.apache.struts2.conversion.StrutsTypeConverterCreator;
|
||||
import org.apache.struts2.conversion.StrutsTypeConverterHolder;
|
||||
import org.apache.struts2.ognl.OgnlGuard;
|
||||
import org.apache.struts2.ognl.ProviderAllowlist;
|
||||
import org.apache.struts2.ognl.StrutsOgnlGuard;
|
||||
import org.apache.struts2.conversion.StrutsTypeConverterCreator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
@@ -132,21 +61,6 @@ import java.util.TreeSet;
|
||||
*/
|
||||
public class DefaultConfiguration implements Configuration {
|
||||
|
||||
public static final Map<String, Object> BOOTSTRAP_CONSTANTS;
|
||||
|
||||
static {
|
||||
Map<String, Object> constants = new HashMap<>();
|
||||
constants.put(StrutsConstants.STRUTS_DEVMODE, Boolean.FALSE);
|
||||
constants.put(StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD, Boolean.FALSE);
|
||||
constants.put(StrutsConstants.STRUTS_MATCHER_APPEND_NAMED_PARAMETERS, Boolean.TRUE);
|
||||
constants.put(StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_TYPE, OgnlCacheFactory.CacheType.BASIC);
|
||||
constants.put(StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_MAXSIZE, 10000);
|
||||
constants.put(StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_TYPE, OgnlCacheFactory.CacheType.BASIC);
|
||||
constants.put(StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_MAXSIZE, 10000);
|
||||
constants.put(StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION, Boolean.FALSE);
|
||||
BOOTSTRAP_CONSTANTS = Collections.unmodifiableMap(constants);
|
||||
}
|
||||
|
||||
protected static final Logger LOG = LogManager.getLogger(DefaultConfiguration.class);
|
||||
|
||||
// Programmatic Action Configurations
|
||||
@@ -325,8 +239,12 @@ public class DefaultConfiguration implements Configuration {
|
||||
}
|
||||
|
||||
protected ActionContext setContext(Container cont) {
|
||||
ValueStack vs = cont.getInstance(ValueStackFactory.class).createValueStack();
|
||||
return ActionContext.of(vs.getContext()).bind();
|
||||
ActionContext context = ActionContext.getContext();
|
||||
if (context == null) {
|
||||
ValueStack vs = cont.getInstance(ValueStackFactory.class).createValueStack();
|
||||
context = ActionContext.of(vs.getContext()).bind();
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
protected Container createBootstrapContainer(List<ContainerProvider> providers) {
|
||||
@@ -341,73 +259,63 @@ public class DefaultConfiguration implements Configuration {
|
||||
fmFactoryRegistered = true;
|
||||
}
|
||||
}
|
||||
builder.factory(ObjectFactory.class, Scope.SINGLETON);
|
||||
builder.factory(ActionFactory.class, DefaultActionFactory.class, Scope.SINGLETON);
|
||||
builder.factory(ResultFactory.class, DefaultResultFactory.class, Scope.SINGLETON);
|
||||
builder.factory(InterceptorFactory.class, DefaultInterceptorFactory.class, Scope.SINGLETON);
|
||||
builder.factory(com.opensymphony.xwork2.factory.ValidatorFactory.class, com.opensymphony.xwork2.factory.DefaultValidatorFactory.class, Scope.SINGLETON);
|
||||
builder.factory(ConverterFactory.class, StrutsConverterFactory.class, Scope.SINGLETON);
|
||||
builder.factory(UnknownHandlerFactory.class, DefaultUnknownHandlerFactory.class, Scope.SINGLETON);
|
||||
|
||||
bootstrapFactories(builder);
|
||||
bootstrapTypeConverters(builder);
|
||||
|
||||
builder.factory(FileManager.class, "system", DefaultFileManager.class, Scope.SINGLETON);
|
||||
if (!fmFactoryRegistered) {
|
||||
builder.factory(FileManagerFactory.class, DefaultFileManagerFactory.class, Scope.SINGLETON);
|
||||
}
|
||||
builder.factory(ReflectionProvider.class, OgnlReflectionProvider.class, Scope.SINGLETON);
|
||||
builder.factory(ValueStackFactory.class, OgnlValueStackFactory.class, Scope.SINGLETON);
|
||||
|
||||
for (Map.Entry<String, Object> entry : BOOTSTRAP_CONSTANTS.entrySet()) {
|
||||
builder.constant(entry.getKey(), String.valueOf(entry.getValue()));
|
||||
}
|
||||
builder.factory(XWorkConverter.class, Scope.SINGLETON);
|
||||
builder.factory(ConversionPropertiesProcessor.class, StrutsConversionPropertiesProcessor.class, Scope.SINGLETON);
|
||||
builder.factory(ConversionFileProcessor.class, DefaultConversionFileProcessor.class, Scope.SINGLETON);
|
||||
builder.factory(ConversionAnnotationProcessor.class, DefaultConversionAnnotationProcessor.class, Scope.SINGLETON);
|
||||
builder.factory(TypeConverterCreator.class, StrutsTypeConverterCreator.class, Scope.SINGLETON);
|
||||
builder.factory(TypeConverterHolder.class, StrutsTypeConverterHolder.class, Scope.SINGLETON);
|
||||
|
||||
builder.factory(XWorkBasicConverter.class, Scope.SINGLETON);
|
||||
builder.factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_COLLECTION, CollectionConverter.class, Scope.SINGLETON);
|
||||
builder.factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_ARRAY, ArrayConverter.class, Scope.SINGLETON);
|
||||
builder.factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_DATE, DateConverter.class, Scope.SINGLETON);
|
||||
builder.factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_NUMBER, NumberConverter.class, Scope.SINGLETON);
|
||||
builder.factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_STRING, StringConverter.class, Scope.SINGLETON);
|
||||
|
||||
builder.factory(TextProvider.class, "system", DefaultTextProvider.class, Scope.SINGLETON);
|
||||
|
||||
builder.factory(LocalizedTextProvider.class, StrutsLocalizedTextProvider.class, Scope.SINGLETON);
|
||||
builder.factory(TextProviderFactory.class, StrutsTextProviderFactory.class, Scope.SINGLETON);
|
||||
builder.factory(LocaleProviderFactory.class, DefaultLocaleProviderFactory.class, Scope.SINGLETON);
|
||||
|
||||
builder.factory(TextParser.class, OgnlTextParser.class, Scope.SINGLETON);
|
||||
|
||||
builder.factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.class, Scope.SINGLETON);
|
||||
builder.factory(PropertyAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON);
|
||||
builder.factory(ExpressionCacheFactory.class, DefaultOgnlExpressionCacheFactory.class, Scope.SINGLETON);
|
||||
builder.factory(BeanInfoCacheFactory.class, DefaultOgnlBeanInfoCacheFactory.class, Scope.SINGLETON);
|
||||
builder.factory(OgnlUtil.class, Scope.SINGLETON);
|
||||
|
||||
builder.factory(ValueSubstitutor.class, EnvsValueSubstitutor.class, Scope.SINGLETON);
|
||||
|
||||
builder.constant(StrutsConstants.STRUTS_DEVMODE, "false");
|
||||
builder.constant(StrutsConstants.STRUTS_OGNL_LOG_MISSING_PROPERTIES, "false");
|
||||
builder.constant(StrutsConstants.STRUTS_OGNL_ENABLE_EVAL_EXPRESSION, "false");
|
||||
builder.constant(StrutsConstants.STRUTS_OGNL_ENABLE_EXPRESSION_CACHE, "true");
|
||||
builder.constant(StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD, "false");
|
||||
builder.constant(StrutsConstants.STRUTS_I18N_RELOAD, "false");
|
||||
|
||||
builder.constant(StrutsConstants.STRUTS_MATCHER_APPEND_NAMED_PARAMETERS, "true");
|
||||
|
||||
return builder.create(true);
|
||||
}
|
||||
|
||||
public static ContainerBuilder bootstrapFactories(ContainerBuilder builder) {
|
||||
return builder
|
||||
// TODO: SpringObjectFactoryTest fails when these are SINGLETON
|
||||
.factory(ObjectFactory.class, Scope.PROTOTYPE)
|
||||
.factory(ActionFactory.class, DefaultActionFactory.class, Scope.PROTOTYPE)
|
||||
.factory(ResultFactory.class, DefaultResultFactory.class, Scope.PROTOTYPE)
|
||||
.factory(InterceptorFactory.class, DefaultInterceptorFactory.class, Scope.PROTOTYPE)
|
||||
.factory(ValidatorFactory.class, DefaultValidatorFactory.class, Scope.PROTOTYPE)
|
||||
.factory(ConverterFactory.class, StrutsConverterFactory.class, Scope.PROTOTYPE)
|
||||
.factory(UnknownHandlerFactory.class, DefaultUnknownHandlerFactory.class, Scope.PROTOTYPE)
|
||||
|
||||
.factory(FileManager.class, "system", DefaultFileManager.class, Scope.SINGLETON)
|
||||
.factory(ReflectionProvider.class, OgnlReflectionProvider.class, Scope.SINGLETON)
|
||||
.factory(ValueStackFactory.class, OgnlValueStackFactory.class, Scope.SINGLETON)
|
||||
|
||||
.factory(XWorkConverter.class, Scope.SINGLETON)
|
||||
.factory(XWorkBasicConverter.class, Scope.SINGLETON)
|
||||
.factory(ConversionPropertiesProcessor.class, StrutsConversionPropertiesProcessor.class, Scope.SINGLETON)
|
||||
.factory(ConversionFileProcessor.class, DefaultConversionFileProcessor.class, Scope.SINGLETON)
|
||||
.factory(ConversionAnnotationProcessor.class, DefaultConversionAnnotationProcessor.class, Scope.SINGLETON)
|
||||
.factory(TypeConverterCreator.class, StrutsTypeConverterCreator.class, Scope.SINGLETON)
|
||||
.factory(TypeConverterHolder.class, StrutsTypeConverterHolder.class, Scope.SINGLETON)
|
||||
|
||||
.factory(TextProvider.class, "system", DefaultTextProvider.class, Scope.SINGLETON)
|
||||
.factory(LocalizedTextProvider.class, StrutsLocalizedTextProvider.class, Scope.SINGLETON)
|
||||
.factory(TextProviderFactory.class, StrutsTextProviderFactory.class, Scope.SINGLETON)
|
||||
.factory(LocaleProviderFactory.class, DefaultLocaleProviderFactory.class, Scope.SINGLETON)
|
||||
.factory(TextParser.class, OgnlTextParser.class, Scope.SINGLETON)
|
||||
|
||||
.factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.class, Scope.SINGLETON)
|
||||
.factory(RootAccessor.class, CompoundRootAccessor.class, Scope.SINGLETON)
|
||||
.factory(MethodAccessor.class, XWorkMethodAccessor.class, Scope.SINGLETON)
|
||||
|
||||
.factory(ExpressionCacheFactory.class, DefaultOgnlExpressionCacheFactory.class, Scope.SINGLETON)
|
||||
.factory(BeanInfoCacheFactory.class, DefaultOgnlBeanInfoCacheFactory.class, Scope.SINGLETON)
|
||||
.factory(OgnlUtil.class, Scope.SINGLETON)
|
||||
.factory(SecurityMemberAccess.class, Scope.PROTOTYPE)
|
||||
.factory(OgnlGuard.class, StrutsOgnlGuard.class, Scope.SINGLETON)
|
||||
.factory(ProviderAllowlist.class, Scope.SINGLETON)
|
||||
|
||||
.factory(ValueSubstitutor.class, EnvsValueSubstitutor.class, Scope.SINGLETON);
|
||||
}
|
||||
|
||||
public static ContainerBuilder bootstrapTypeConverters(ContainerBuilder builder) {
|
||||
return builder
|
||||
.factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_COLLECTION, CollectionConverter.class, Scope.SINGLETON)
|
||||
.factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_ARRAY, ArrayConverter.class, Scope.SINGLETON)
|
||||
.factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_DATE, DateConverter.class, Scope.SINGLETON)
|
||||
.factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_NUMBER, NumberConverter.class, Scope.SINGLETON)
|
||||
.factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_STRING, StringConverter.class, Scope.SINGLETON);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This builds the internal runtime configuration used by Xwork for finding and configuring Actions from the
|
||||
|
||||
@@ -18,11 +18,7 @@
|
||||
*/
|
||||
package com.opensymphony.xwork2.config.impl;
|
||||
|
||||
import com.opensymphony.xwork2.config.Configuration;
|
||||
import com.opensymphony.xwork2.config.ConfigurationException;
|
||||
import com.opensymphony.xwork2.config.ContainerProvider;
|
||||
import com.opensymphony.xwork2.config.PackageProvider;
|
||||
import com.opensymphony.xwork2.config.RuntimeConfiguration;
|
||||
import com.opensymphony.xwork2.config.*;
|
||||
import com.opensymphony.xwork2.config.entities.PackageConfig;
|
||||
import com.opensymphony.xwork2.config.entities.UnknownHandlerConfig;
|
||||
import com.opensymphony.xwork2.config.providers.StrutsDefaultConfigurationProvider;
|
||||
@@ -30,12 +26,9 @@ import com.opensymphony.xwork2.inject.Container;
|
||||
import com.opensymphony.xwork2.inject.ContainerBuilder;
|
||||
import com.opensymphony.xwork2.inject.Scope;
|
||||
import com.opensymphony.xwork2.util.location.LocatableProperties;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
@@ -58,9 +51,10 @@ public class MockConfiguration implements Configuration {
|
||||
builder.factory(Configuration.class, MockConfiguration.class, Scope.SINGLETON);
|
||||
LocatableProperties props = new LocatableProperties();
|
||||
new StrutsDefaultConfigurationProvider().register(builder, props);
|
||||
for (Map.Entry<String, Object> entry : DefaultConfiguration.BOOTSTRAP_CONSTANTS.entrySet()) {
|
||||
builder.constant(entry.getKey(), String.valueOf(entry.getValue()));
|
||||
}
|
||||
builder.constant(StrutsConstants.STRUTS_DEVMODE, "false");
|
||||
builder.constant(StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD, "true");
|
||||
builder.constant(StrutsConstants.STRUTS_OGNL_ENABLE_EXPRESSION_CACHE, "true");
|
||||
builder.constant(StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION, "false");
|
||||
container = builder.create(true);
|
||||
}
|
||||
|
||||
|
||||
+137
-43
@@ -20,24 +20,61 @@ package com.opensymphony.xwork2.config.providers;
|
||||
|
||||
import com.opensymphony.xwork2.ActionProxyFactory;
|
||||
import com.opensymphony.xwork2.DefaultActionProxyFactory;
|
||||
import com.opensymphony.xwork2.DefaultLocaleProviderFactory;
|
||||
import com.opensymphony.xwork2.DefaultTextProvider;
|
||||
import com.opensymphony.xwork2.DefaultUnknownHandlerManager;
|
||||
import com.opensymphony.xwork2.FileManager;
|
||||
import com.opensymphony.xwork2.FileManagerFactory;
|
||||
import com.opensymphony.xwork2.LocaleProviderFactory;
|
||||
import com.opensymphony.xwork2.LocalizedTextProvider;
|
||||
import com.opensymphony.xwork2.ObjectFactory;
|
||||
import com.opensymphony.xwork2.StrutsTextProviderFactory;
|
||||
import com.opensymphony.xwork2.TextProvider;
|
||||
import com.opensymphony.xwork2.TextProviderFactory;
|
||||
import com.opensymphony.xwork2.UnknownHandlerManager;
|
||||
import com.opensymphony.xwork2.config.Configuration;
|
||||
import com.opensymphony.xwork2.config.ConfigurationException;
|
||||
import com.opensymphony.xwork2.config.ConfigurationProvider;
|
||||
import com.opensymphony.xwork2.config.impl.DefaultConfiguration;
|
||||
import com.opensymphony.xwork2.conversion.ConversionAnnotationProcessor;
|
||||
import com.opensymphony.xwork2.conversion.ConversionFileProcessor;
|
||||
import com.opensymphony.xwork2.conversion.ConversionPropertiesProcessor;
|
||||
import com.opensymphony.xwork2.conversion.NullHandler;
|
||||
import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer;
|
||||
import com.opensymphony.xwork2.conversion.TypeConverterCreator;
|
||||
import com.opensymphony.xwork2.conversion.TypeConverterHolder;
|
||||
import com.opensymphony.xwork2.conversion.impl.ArrayConverter;
|
||||
import com.opensymphony.xwork2.conversion.impl.CollectionConverter;
|
||||
import com.opensymphony.xwork2.conversion.impl.DateConverter;
|
||||
import com.opensymphony.xwork2.conversion.impl.DefaultConversionAnnotationProcessor;
|
||||
import com.opensymphony.xwork2.conversion.impl.DefaultConversionFileProcessor;
|
||||
import com.opensymphony.xwork2.conversion.impl.DefaultObjectTypeDeterminer;
|
||||
import com.opensymphony.xwork2.conversion.impl.InstantiatingNullHandler;
|
||||
import com.opensymphony.xwork2.conversion.impl.NumberConverter;
|
||||
import com.opensymphony.xwork2.conversion.impl.StringConverter;
|
||||
import com.opensymphony.xwork2.conversion.impl.XWorkBasicConverter;
|
||||
import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
|
||||
import com.opensymphony.xwork2.factory.ActionFactory;
|
||||
import com.opensymphony.xwork2.factory.ConverterFactory;
|
||||
import com.opensymphony.xwork2.factory.DefaultActionFactory;
|
||||
import com.opensymphony.xwork2.factory.DefaultInterceptorFactory;
|
||||
import com.opensymphony.xwork2.factory.DefaultResultFactory;
|
||||
import com.opensymphony.xwork2.factory.DefaultUnknownHandlerFactory;
|
||||
import com.opensymphony.xwork2.factory.InterceptorFactory;
|
||||
import com.opensymphony.xwork2.factory.ResultFactory;
|
||||
import com.opensymphony.xwork2.factory.StrutsConverterFactory;
|
||||
import com.opensymphony.xwork2.factory.UnknownHandlerFactory;
|
||||
import com.opensymphony.xwork2.inject.ContainerBuilder;
|
||||
import com.opensymphony.xwork2.inject.Scope;
|
||||
import com.opensymphony.xwork2.ognl.BeanInfoCacheFactory;
|
||||
import com.opensymphony.xwork2.ognl.DefaultOgnlBeanInfoCacheFactory;
|
||||
import com.opensymphony.xwork2.ognl.DefaultOgnlExpressionCacheFactory;
|
||||
import com.opensymphony.xwork2.ognl.ExpressionCacheFactory;
|
||||
import com.opensymphony.xwork2.ognl.ObjectProxy;
|
||||
import com.opensymphony.xwork2.ognl.OgnlReflectionContextFactory;
|
||||
import com.opensymphony.xwork2.ognl.OgnlReflectionProvider;
|
||||
import com.opensymphony.xwork2.ognl.OgnlUtil;
|
||||
import com.opensymphony.xwork2.ognl.OgnlValueStackFactory;
|
||||
import com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.HttpParametersPropertyAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.ObjectAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.ObjectProxyPropertyAccessor;
|
||||
@@ -47,17 +84,25 @@ import com.opensymphony.xwork2.ognl.accessor.XWorkEnumerationAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.XWorkIteratorPropertyAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.XWorkListPropertyAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.XWorkMapPropertyAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor;
|
||||
import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
|
||||
import com.opensymphony.xwork2.security.DefaultAcceptedPatternsChecker;
|
||||
import com.opensymphony.xwork2.security.DefaultExcludedPatternsChecker;
|
||||
import com.opensymphony.xwork2.security.DefaultNotExcludedAcceptedPatternsChecker;
|
||||
import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
|
||||
import com.opensymphony.xwork2.security.NotExcludedAcceptedPatternsChecker;
|
||||
import com.opensymphony.xwork2.util.CompoundRoot;
|
||||
import com.opensymphony.xwork2.util.OgnlTextParser;
|
||||
import com.opensymphony.xwork2.util.PatternMatcher;
|
||||
import com.opensymphony.xwork2.util.StrutsLocalizedTextProvider;
|
||||
import com.opensymphony.xwork2.util.TextParser;
|
||||
import com.opensymphony.xwork2.util.ValueStackFactory;
|
||||
import com.opensymphony.xwork2.util.WildcardHelper;
|
||||
import com.opensymphony.xwork2.util.fs.DefaultFileManager;
|
||||
import com.opensymphony.xwork2.util.fs.DefaultFileManagerFactory;
|
||||
import com.opensymphony.xwork2.util.location.LocatableProperties;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionContextFactory;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
|
||||
import com.opensymphony.xwork2.validator.ActionValidatorManager;
|
||||
import com.opensymphony.xwork2.validator.AnnotationActionValidatorManager;
|
||||
import com.opensymphony.xwork2.validator.DefaultActionValidatorManager;
|
||||
@@ -65,7 +110,12 @@ import com.opensymphony.xwork2.validator.DefaultValidatorFactory;
|
||||
import com.opensymphony.xwork2.validator.DefaultValidatorFileParser;
|
||||
import com.opensymphony.xwork2.validator.ValidatorFactory;
|
||||
import com.opensymphony.xwork2.validator.ValidatorFileParser;
|
||||
import ognl.MethodAccessor;
|
||||
import ognl.PropertyAccessor;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
import org.apache.struts2.conversion.StrutsConversionPropertiesProcessor;
|
||||
import org.apache.struts2.conversion.StrutsTypeConverterCreator;
|
||||
import org.apache.struts2.conversion.StrutsTypeConverterHolder;
|
||||
import org.apache.struts2.dispatcher.HttpParameters;
|
||||
import org.apache.struts2.dispatcher.Parameter;
|
||||
import org.apache.struts2.interceptor.exec.ExecutorProvider;
|
||||
@@ -109,60 +159,104 @@ public class StrutsDefaultConfigurationProvider implements ConfigurationProvider
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException {
|
||||
public void register(ContainerBuilder builder, LocatableProperties props)
|
||||
throws ConfigurationException {
|
||||
|
||||
DefaultConfiguration.bootstrapFactories(builder)
|
||||
.factory(FileManagerFactory.class, DefaultFileManagerFactory.class, Scope.SINGLETON)
|
||||
builder
|
||||
.factory(ObjectFactory.class)
|
||||
.factory(ActionFactory.class, DefaultActionFactory.class)
|
||||
.factory(ResultFactory.class, DefaultResultFactory.class)
|
||||
.factory(InterceptorFactory.class, DefaultInterceptorFactory.class)
|
||||
.factory(com.opensymphony.xwork2.factory.ValidatorFactory.class, com.opensymphony.xwork2.factory.DefaultValidatorFactory.class)
|
||||
.factory(ConverterFactory.class, StrutsConverterFactory.class)
|
||||
.factory(UnknownHandlerFactory.class, DefaultUnknownHandlerFactory.class)
|
||||
|
||||
.factory(ActionProxyFactory.class, DefaultActionProxyFactory.class, Scope.SINGLETON)
|
||||
.factory(ActionProxyFactory.class, DefaultActionProxyFactory.class, Scope.SINGLETON)
|
||||
.factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.class, Scope.SINGLETON)
|
||||
|
||||
.factory(ValidatorFactory.class, DefaultValidatorFactory.class, Scope.SINGLETON)
|
||||
.factory(ValidatorFileParser.class, DefaultValidatorFileParser.class, Scope.SINGLETON)
|
||||
.factory(PatternMatcher.class, WildcardHelper.class, Scope.SINGLETON)
|
||||
.factory(XWorkConverter.class, Scope.SINGLETON)
|
||||
.factory(XWorkBasicConverter.class, Scope.SINGLETON)
|
||||
.factory(ConversionPropertiesProcessor.class, StrutsConversionPropertiesProcessor.class, Scope.SINGLETON)
|
||||
.factory(ConversionFileProcessor.class, DefaultConversionFileProcessor.class, Scope.SINGLETON)
|
||||
.factory(ConversionAnnotationProcessor.class, DefaultConversionAnnotationProcessor.class, Scope.SINGLETON)
|
||||
.factory(TypeConverterCreator.class, StrutsTypeConverterCreator.class, Scope.SINGLETON)
|
||||
.factory(TypeConverterHolder.class, StrutsTypeConverterHolder.class, Scope.SINGLETON)
|
||||
|
||||
.factory(ReflectionContextFactory.class, OgnlReflectionContextFactory.class, Scope.SINGLETON)
|
||||
.factory(FileManager.class, "system", DefaultFileManager.class, Scope.SINGLETON)
|
||||
.factory(FileManagerFactory.class, DefaultFileManagerFactory.class, Scope.SINGLETON)
|
||||
.factory(ValueStackFactory.class, OgnlValueStackFactory.class, Scope.SINGLETON)
|
||||
.factory(ValidatorFactory.class, DefaultValidatorFactory.class, Scope.SINGLETON)
|
||||
.factory(ValidatorFileParser.class, DefaultValidatorFileParser.class, Scope.SINGLETON)
|
||||
.factory(PatternMatcher.class, WildcardHelper.class, Scope.SINGLETON)
|
||||
.factory(ReflectionProvider.class, OgnlReflectionProvider.class, Scope.SINGLETON)
|
||||
.factory(ReflectionContextFactory.class, OgnlReflectionContextFactory.class, Scope.SINGLETON)
|
||||
|
||||
.factory(PropertyAccessor.class, Object.class.getName(), ObjectAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, Iterator.class.getName(), XWorkIteratorPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, Enumeration.class.getName(), XWorkEnumerationAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, Object.class.getName(), ObjectAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, Iterator.class.getName(), XWorkIteratorPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, Enumeration.class.getName(), XWorkEnumerationAccessor.class, Scope.SINGLETON)
|
||||
|
||||
.factory(UnknownHandlerManager.class, DefaultUnknownHandlerManager.class, Scope.SINGLETON)
|
||||
.factory(UnknownHandlerManager.class, DefaultUnknownHandlerManager.class, Scope.SINGLETON)
|
||||
|
||||
// silly workarounds for ognl since there is no way to flush its caches
|
||||
.factory(PropertyAccessor.class, List.class.getName(), XWorkListPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, ArrayList.class.getName(), XWorkListPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, HashSet.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, Set.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, HashMap.class.getName(), XWorkMapPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, Map.class.getName(), XWorkMapPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, Collection.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, ObjectProxy.class.getName(), ObjectProxyPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, HttpParameters.class.getName(), HttpParametersPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, Parameter.class.getName(), ParameterPropertyAccessor.class, Scope.SINGLETON)
|
||||
// silly workarounds for ognl since there is no way to flush its caches
|
||||
.factory(PropertyAccessor.class, List.class.getName(), XWorkListPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, ArrayList.class.getName(), XWorkListPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, HashSet.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, Set.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, HashMap.class.getName(), XWorkMapPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, Map.class.getName(), XWorkMapPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, Collection.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, ObjectProxy.class.getName(), ObjectProxyPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, HttpParameters.class.getName(), HttpParametersPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, Parameter.class.getName(), ParameterPropertyAccessor.class, Scope.SINGLETON)
|
||||
|
||||
.factory(NullHandler.class, Object.class.getName(), InstantiatingNullHandler.class, Scope.SINGLETON)
|
||||
.factory(ActionValidatorManager.class, AnnotationActionValidatorManager.class, Scope.SINGLETON)
|
||||
.factory(ActionValidatorManager.class, "no-annotations", DefaultActionValidatorManager.class, Scope.SINGLETON)
|
||||
.factory(MethodAccessor.class, Object.class.getName(), XWorkMethodAccessor.class, Scope.SINGLETON)
|
||||
.factory(MethodAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON)
|
||||
|
||||
.factory(CollectionConverter.class, Scope.SINGLETON)
|
||||
.factory(ArrayConverter.class, Scope.SINGLETON)
|
||||
.factory(DateConverter.class, Scope.SINGLETON)
|
||||
.factory(NumberConverter.class, Scope.SINGLETON)
|
||||
.factory(StringConverter.class, Scope.SINGLETON)
|
||||
.factory(TextParser.class, OgnlTextParser.class, Scope.SINGLETON)
|
||||
|
||||
.factory(ExcludedPatternsChecker.class, DefaultExcludedPatternsChecker.class, Scope.PROTOTYPE)
|
||||
.factory(AcceptedPatternsChecker.class, DefaultAcceptedPatternsChecker.class, Scope.PROTOTYPE)
|
||||
.factory(NotExcludedAcceptedPatternsChecker.class, DefaultNotExcludedAcceptedPatternsChecker.class, Scope.SINGLETON)
|
||||
.factory(NullHandler.class, Object.class.getName(), InstantiatingNullHandler.class, Scope.SINGLETON)
|
||||
.factory(ActionValidatorManager.class, AnnotationActionValidatorManager.class, Scope.SINGLETON)
|
||||
.factory(ActionValidatorManager.class, "no-annotations", DefaultActionValidatorManager.class, Scope.SINGLETON)
|
||||
|
||||
.factory(QueryStringBuilder.class, StrutsQueryStringBuilder.class, Scope.SINGLETON)
|
||||
.factory(QueryStringParser.class, StrutsQueryStringParser.class, Scope.SINGLETON)
|
||||
.factory(UrlEncoder.class, StrutsUrlEncoder.class, Scope.SINGLETON)
|
||||
.factory(UrlDecoder.class, StrutsUrlDecoder.class, Scope.SINGLETON)
|
||||
.factory(TextProvider.class, "system", DefaultTextProvider.class, Scope.SINGLETON)
|
||||
.factory(LocalizedTextProvider.class, StrutsLocalizedTextProvider.class, Scope.SINGLETON)
|
||||
.factory(TextProviderFactory.class, StrutsTextProviderFactory.class, Scope.SINGLETON)
|
||||
.factory(LocaleProviderFactory.class, DefaultLocaleProviderFactory.class, Scope.SINGLETON)
|
||||
|
||||
.factory(ExecutorProvider.class, StrutsExecutorProvider.class, Scope.SINGLETON);
|
||||
.factory(ExpressionCacheFactory.class, DefaultOgnlExpressionCacheFactory.class, Scope.SINGLETON)
|
||||
.factory(BeanInfoCacheFactory.class, DefaultOgnlBeanInfoCacheFactory.class, Scope.SINGLETON)
|
||||
.factory(OgnlUtil.class, Scope.SINGLETON)
|
||||
.factory(CollectionConverter.class, Scope.SINGLETON)
|
||||
.factory(ArrayConverter.class, Scope.SINGLETON)
|
||||
.factory(DateConverter.class, Scope.SINGLETON)
|
||||
.factory(NumberConverter.class, Scope.SINGLETON)
|
||||
.factory(StringConverter.class, Scope.SINGLETON)
|
||||
|
||||
for (Map.Entry<String, Object> entry : DefaultConfiguration.BOOTSTRAP_CONSTANTS.entrySet()) {
|
||||
props.setProperty(entry.getKey(), String.valueOf(entry.getValue()));
|
||||
}
|
||||
.factory(ExcludedPatternsChecker.class, DefaultExcludedPatternsChecker.class, Scope.PROTOTYPE)
|
||||
.factory(AcceptedPatternsChecker.class, DefaultAcceptedPatternsChecker.class, Scope.PROTOTYPE)
|
||||
.factory(NotExcludedAcceptedPatternsChecker.class, DefaultNotExcludedAcceptedPatternsChecker.class
|
||||
, Scope.SINGLETON)
|
||||
|
||||
.factory(ValueSubstitutor.class, EnvsValueSubstitutor.class, Scope.SINGLETON)
|
||||
|
||||
.factory(QueryStringBuilder.class, StrutsQueryStringBuilder.class, Scope.SINGLETON)
|
||||
.factory(QueryStringParser.class, StrutsQueryStringParser.class, Scope.SINGLETON)
|
||||
.factory(UrlEncoder.class, StrutsUrlEncoder.class, Scope.SINGLETON)
|
||||
.factory(UrlDecoder.class, StrutsUrlDecoder.class, Scope.SINGLETON)
|
||||
|
||||
.factory(ExecutorProvider.class, StrutsExecutorProvider.class, Scope.SINGLETON)
|
||||
;
|
||||
|
||||
props.setProperty(StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION, Boolean.FALSE.toString());
|
||||
props.setProperty(StrutsConstants.STRUTS_I18N_RELOAD, Boolean.FALSE.toString());
|
||||
props.setProperty(StrutsConstants.STRUTS_DEVMODE, Boolean.FALSE.toString());
|
||||
props.setProperty(StrutsConstants.STRUTS_OGNL_LOG_MISSING_PROPERTIES, Boolean.FALSE.toString());
|
||||
props.setProperty(StrutsConstants.STRUTS_OGNL_ENABLE_EXPRESSION_CACHE, Boolean.TRUE.toString());
|
||||
props.setProperty(StrutsConstants.STRUTS_OGNL_ENABLE_EVAL_EXPRESSION, Boolean.FALSE.toString());
|
||||
props.setProperty(StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD, Boolean.FALSE.toString());
|
||||
props.setProperty(StrutsConstants.STRUTS_ALLOW_STATIC_FIELD_ACCESS, Boolean.TRUE.toString());
|
||||
props.setProperty(StrutsConstants.STRUTS_MATCHER_APPEND_NAMED_PARAMETERS, Boolean.TRUE.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+27
-61
@@ -45,11 +45,9 @@ import com.opensymphony.xwork2.util.location.LocatableProperties;
|
||||
import com.opensymphony.xwork2.util.location.Location;
|
||||
import com.opensymphony.xwork2.util.location.LocationUtils;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.ClassUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.ognl.ProviderAllowlist;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
@@ -94,10 +92,8 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid
|
||||
protected ObjectFactory objectFactory;
|
||||
protected Map<String, String> dtdMappings = new HashMap<>();
|
||||
protected Configuration configuration;
|
||||
protected ProviderAllowlist providerAllowlist;
|
||||
protected boolean throwExceptionOnDuplicateBeans = true;
|
||||
protected ValueSubstitutor valueSubstitutor;
|
||||
protected Set<Class<?>> allowlistClasses = new HashSet<>();
|
||||
|
||||
@Inject
|
||||
public void setObjectFactory(ObjectFactory objectFactory) {
|
||||
@@ -135,22 +131,8 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
private void registerAllowlist() {
|
||||
providerAllowlist = configuration.getContainer().getInstance(ProviderAllowlist.class);
|
||||
providerAllowlist.registerAllowlist(this, allowlistClasses);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
providerAllowlist.clearAllowlist(this);
|
||||
}
|
||||
|
||||
protected Class<?> allowAndLoadClass(String className) throws ClassNotFoundException {
|
||||
Class<?> clazz = loadClass(className);
|
||||
allowlistClasses.add(clazz);
|
||||
allowlistClasses.addAll(ClassUtils.getAllSuperclasses(clazz));
|
||||
allowlistClasses.addAll(ClassUtils.getAllInterfaces(clazz));
|
||||
return clazz;
|
||||
}
|
||||
|
||||
protected Class<?> loadClass(String className) throws ClassNotFoundException {
|
||||
@@ -187,7 +169,6 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid
|
||||
|
||||
@Override
|
||||
public void register(ContainerBuilder containerBuilder, LocatableProperties props) throws ConfigurationException {
|
||||
allowlistClasses.clear();
|
||||
Map<String, Node> loadedBeans = new HashMap<>();
|
||||
for (Document doc : documents) {
|
||||
iterateElementChildren(doc, child -> {
|
||||
@@ -216,7 +197,7 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid
|
||||
String name = child.getAttribute("name");
|
||||
String impl = child.getAttribute("class");
|
||||
try {
|
||||
Class<?> classImpl = ClassLoaderUtil.loadClass(impl, getClass());
|
||||
Class<?> classImpl = loadClass(impl);
|
||||
if (BeanSelectionProvider.class.isAssignableFrom(classImpl)) {
|
||||
BeanSelectionProvider provider = (BeanSelectionProvider) classImpl.newInstance();
|
||||
provider.register(containerBuilder, props);
|
||||
@@ -322,7 +303,7 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid
|
||||
loadExtraConfiguration(doc);
|
||||
}
|
||||
|
||||
if (!reloads.isEmpty()) {
|
||||
if (reloads.size() > 0) {
|
||||
reloadRequiredPackages(reloads);
|
||||
}
|
||||
|
||||
@@ -331,7 +312,6 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid
|
||||
}
|
||||
|
||||
declaredPackages.clear();
|
||||
registerAllowlist();
|
||||
configuration = null;
|
||||
}
|
||||
|
||||
@@ -462,8 +442,13 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid
|
||||
|
||||
Location location = DomHelper.getLocationObject(actionElement);
|
||||
|
||||
if (!className.isEmpty()) {
|
||||
verifyAction(className, name, location);
|
||||
if (location == null) {
|
||||
LOG.warn("Location null for {}", className);
|
||||
}
|
||||
|
||||
if (!className.isEmpty() && !verifyAction(className, name, location)) {
|
||||
LOG.error("Unable to verify action [{}] with class [{}], from [{}]", name, className, location);
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, ResultConfig> results;
|
||||
@@ -514,30 +499,27 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid
|
||||
*/
|
||||
@Deprecated
|
||||
protected boolean verifyAction(String className, String name, Location loc) {
|
||||
verifyAction(className, loc);
|
||||
return true;
|
||||
return verifyAction(className, loc);
|
||||
}
|
||||
|
||||
protected void verifyAction(String className, Location loc) {
|
||||
protected boolean verifyAction(String className, Location loc) {
|
||||
if (className.contains("{")) {
|
||||
LOG.debug("Action class [{}] contains a wildcard replacement value, so it can't be verified", className);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
Class<?> clazz = allowAndLoadClass(className);
|
||||
if (objectFactory.isNoArgConstructorRequired()) {
|
||||
Class<?> clazz = loadClass(className);
|
||||
if (!Modifier.isPublic(clazz.getModifiers())) {
|
||||
throw new ConfigurationException("Action class [" + className + "] is not public", loc);
|
||||
}
|
||||
clazz.getConstructor();
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
if (objectFactory.isNoArgConstructorRequired()) {
|
||||
throw new ConfigurationException("Action class [" + className + "] not found", e, loc);
|
||||
}
|
||||
LOG.warn("Action class [" + className + "] not found");
|
||||
LOG.debug("Action class [" + className + "] not found", e);
|
||||
LOG.debug("Class not found for action [{}]", className, e);
|
||||
throw new ConfigurationException("Action class [" + className + "] not found", loc);
|
||||
} catch (NoSuchMethodException e) {
|
||||
LOG.debug("No constructor found for action [{}]", className, e);
|
||||
throw new ConfigurationException("Action class [" + className + "] does not have a public no-arg constructor", e, loc);
|
||||
} catch (RuntimeException ex) {
|
||||
// Probably not a big deal, like request or session-scoped Spring beans that need a real request
|
||||
@@ -545,8 +527,10 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid
|
||||
LOG.debug("Action verification cause", ex);
|
||||
} catch (Exception ex) {
|
||||
// Default to failing fast
|
||||
throw new ConfigurationException("Unable to verify action class [" + className + "]", ex, loc);
|
||||
LOG.debug("Unable to verify action class [{}]", className, ex);
|
||||
throw new ConfigurationException(ex, loc);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void addResultTypes(PackageConfig.Builder packageContext, Element element) {
|
||||
@@ -557,7 +541,9 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid
|
||||
|
||||
Location loc = DomHelper.getLocationObject(resultTypeElement);
|
||||
Class<?> clazz = verifyResultType(className, loc);
|
||||
|
||||
if (clazz == null) {
|
||||
return;
|
||||
}
|
||||
String paramName = null;
|
||||
try {
|
||||
paramName = (String) clazz.getField("DEFAULT_PARAM").get(null);
|
||||
@@ -586,10 +572,11 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid
|
||||
|
||||
protected Class<?> verifyResultType(String className, Location loc) {
|
||||
try {
|
||||
return allowAndLoadClass(className);
|
||||
return loadClass(className);
|
||||
} catch (ClassNotFoundException | NoClassDefFoundError e) {
|
||||
throw new ConfigurationException("Result class [" + className + "] not found", e, loc);
|
||||
LOG.warn("Result class [{}] doesn't exist ({}) at {}, ignoring", className, e.getClass().getSimpleName(), loc, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -901,12 +888,7 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid
|
||||
NodeList defaultClassRefList = element.getElementsByTagName("default-class-ref");
|
||||
if (defaultClassRefList.getLength() > 0) {
|
||||
Element defaultClassRefElement = (Element) defaultClassRefList.item(0);
|
||||
|
||||
String className = defaultClassRefElement.getAttribute("class");
|
||||
Location location = DomHelper.getLocationObject(defaultClassRefElement);
|
||||
verifyAction(className, location);
|
||||
|
||||
packageContext.defaultClassRef(className);
|
||||
packageContext.defaultClassRef(defaultClassRefElement.getAttribute("class"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -945,26 +927,10 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid
|
||||
iterateChildrenByTagName(
|
||||
element,
|
||||
"interceptor",
|
||||
interceptorElement -> {
|
||||
String className = interceptorElement.getAttribute("class");
|
||||
Location location = DomHelper.getLocationObject(interceptorElement);
|
||||
|
||||
verifyInterceptor(className, location);
|
||||
|
||||
context.addInterceptorConfig(buildInterceptorConfig(interceptorElement));
|
||||
});
|
||||
interceptorElement -> context.addInterceptorConfig(buildInterceptorConfig(interceptorElement)));
|
||||
loadInterceptorStacks(element, context);
|
||||
}
|
||||
|
||||
protected void verifyInterceptor(String className, Location loc) {
|
||||
try {
|
||||
allowAndLoadClass(className);
|
||||
} catch (ClassNotFoundException | NoClassDefFoundError e) {
|
||||
LOG.warn("Interceptor class [" + className + "] at location " + loc + " not found");
|
||||
LOG.debug("Interceptor class [" + className + "] not found", e);
|
||||
}
|
||||
}
|
||||
|
||||
protected InterceptorConfig buildInterceptorConfig(Element interceptorElement) {
|
||||
String interceptorName = interceptorElement.getAttribute("name");
|
||||
String className = interceptorElement.getAttribute("class");
|
||||
|
||||
@@ -33,5 +33,5 @@ package com.opensymphony.xwork2.interceptor;
|
||||
* @deprecated since Struts 6.2.0, use {@link org.apache.struts2.action.NoParameters}
|
||||
*/
|
||||
@Deprecated
|
||||
public interface NoParameters extends org.apache.struts2.action.NoParameters {
|
||||
public interface NoParameters {
|
||||
}
|
||||
|
||||
+1
-3
@@ -26,6 +26,7 @@ import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.dispatcher.HttpParameters;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
@@ -97,10 +98,7 @@ import java.util.TreeMap;
|
||||
* </pre>
|
||||
*
|
||||
* @author Gabe
|
||||
*
|
||||
* @deprecated since 6.4.0, use {@link org.apache.struts2.interceptor.parameter.ParametersInterceptor}.
|
||||
*/
|
||||
@Deprecated
|
||||
public class ParameterFilterInterceptor extends AbstractInterceptor {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(ParameterFilterInterceptor.class);
|
||||
|
||||
@@ -19,8 +19,19 @@
|
||||
package com.opensymphony.xwork2.interceptor;
|
||||
|
||||
/**
|
||||
* @deprecated since 6.4.0, use {@link org.apache.struts2.action.ParameterNameAware}.
|
||||
* This interface is implemented by actions that want to declare acceptable parameters. Works in conjunction with {@link
|
||||
* ParametersInterceptor}. For example, actions may want to create a white list of parameters they will accept or a
|
||||
* blacklist of parameters they will reject to prevent clients from setting other unexpected (and possibly dangerous)
|
||||
* parameters.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface ParameterNameAware extends org.apache.struts2.action.ParameterNameAware {
|
||||
public interface ParameterNameAware {
|
||||
|
||||
/**
|
||||
* Tests if the the action will accept the parameter with the given name.
|
||||
*
|
||||
* @param parameterName the parameter name
|
||||
* @return <tt>true</tt> if accepted, <tt>false</tt> otherwise
|
||||
*/
|
||||
boolean acceptableParameterName(String parameterName);
|
||||
|
||||
}
|
||||
|
||||
@@ -19,8 +19,19 @@
|
||||
package com.opensymphony.xwork2.interceptor;
|
||||
|
||||
/**
|
||||
* @deprecated since 6.4.0, use {@link org.apache.struts2.action.ParameterValueAware}.
|
||||
* This interface is implemented by actions that want to declare acceptable parameter value. Works in conjunction with {@link
|
||||
* ParametersInterceptor}. For example, actions may want to create a white list of parameter values they will accept or a
|
||||
* blacklist of parameter values they will reject to prevent clients from setting other unexpected (and possibly dangerous)
|
||||
* parameter values.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface ParameterValueAware extends org.apache.struts2.action.ParameterValueAware {
|
||||
public interface ParameterValueAware {
|
||||
|
||||
/**
|
||||
* Tests if the the action will accept the parameter with the given value.
|
||||
*
|
||||
* @param parameterValue the parameter value
|
||||
* @return <tt>true</tt> if accepted, <tt>false</tt> otherwise
|
||||
*/
|
||||
boolean acceptableParameterValue(String parameterValue);
|
||||
|
||||
}
|
||||
|
||||
@@ -18,8 +18,537 @@
|
||||
*/
|
||||
package com.opensymphony.xwork2.interceptor;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.ActionInvocation;
|
||||
import com.opensymphony.xwork2.TextProvider;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
|
||||
import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
|
||||
import com.opensymphony.xwork2.util.ClearableValueStack;
|
||||
import com.opensymphony.xwork2.util.MemberAccessValueStack;
|
||||
import com.opensymphony.xwork2.util.TextParseUtil;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import com.opensymphony.xwork2.util.ValueStackFactory;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
import org.apache.struts2.dispatcher.HttpParameters;
|
||||
import org.apache.struts2.dispatcher.Parameter;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @deprecated since 6.4.0, use {@link org.apache.struts2.interceptor.parameter.ParametersInterceptor}.
|
||||
* This interceptor sets all parameters on the value stack.
|
||||
*/
|
||||
public class ParametersInterceptor extends org.apache.struts2.interceptor.parameter.ParametersInterceptor {
|
||||
public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(ParametersInterceptor.class);
|
||||
|
||||
protected static final int PARAM_NAME_MAX_LENGTH = 100;
|
||||
|
||||
private static final Pattern DMI_IGNORED_PATTERN = Pattern.compile("^(action|method):.*", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
private int paramNameMaxLength = PARAM_NAME_MAX_LENGTH;
|
||||
private boolean devMode = false;
|
||||
private boolean dmiEnabled = false;
|
||||
|
||||
protected boolean ordered = false;
|
||||
|
||||
private ValueStackFactory valueStackFactory;
|
||||
private ExcludedPatternsChecker excludedPatterns;
|
||||
private AcceptedPatternsChecker acceptedPatterns;
|
||||
private Set<Pattern> excludedValuePatterns = null;
|
||||
private Set<Pattern> acceptedValuePatterns = null;
|
||||
|
||||
@Inject
|
||||
public void setValueStackFactory(ValueStackFactory valueStackFactory) {
|
||||
this.valueStackFactory = valueStackFactory;
|
||||
}
|
||||
|
||||
@Inject(StrutsConstants.STRUTS_DEVMODE)
|
||||
public void setDevMode(String mode) {
|
||||
this.devMode = BooleanUtils.toBoolean(mode);
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setExcludedPatterns(ExcludedPatternsChecker excludedPatterns) {
|
||||
this.excludedPatterns = excludedPatterns;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setAcceptedPatterns(AcceptedPatternsChecker acceptedPatterns) {
|
||||
this.acceptedPatterns = acceptedPatterns;
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION, required = false)
|
||||
protected void setDynamicMethodInvocation(String dmiEnabled) {
|
||||
this.dmiEnabled = Boolean.parseBoolean(dmiEnabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the param name exceeds the configured maximum length it will not be
|
||||
* accepted.
|
||||
*
|
||||
* @param paramNameMaxLength Maximum length of param names
|
||||
*/
|
||||
public void setParamNameMaxLength(int paramNameMaxLength) {
|
||||
this.paramNameMaxLength = paramNameMaxLength;
|
||||
}
|
||||
|
||||
static private int countOGNLCharacters(String s) {
|
||||
int count = 0;
|
||||
for (int i = s.length() - 1; i >= 0; i--) {
|
||||
char c = s.charAt(i);
|
||||
if (c == '.' || c == '[') count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares based on number of '.' and '[' characters (fewer is higher)
|
||||
*/
|
||||
static final Comparator<String> rbCollator = (s1, s2) -> {
|
||||
int l1 = countOGNLCharacters(s1);
|
||||
int l2 = countOGNLCharacters(s2);
|
||||
return l1 < l2 ? -1 : (l2 < l1 ? 1 : s1.compareTo(s2));
|
||||
};
|
||||
|
||||
@Override
|
||||
public String doIntercept(ActionInvocation invocation) throws Exception {
|
||||
Object action = invocation.getAction();
|
||||
if (!(action instanceof NoParameters)) {
|
||||
ActionContext ac = invocation.getInvocationContext();
|
||||
HttpParameters parameters = retrieveParameters(ac);
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Setting params {}", getParameterLogMap(parameters));
|
||||
}
|
||||
|
||||
if (parameters != null) {
|
||||
Map<String, Object> contextMap = ac.getContextMap();
|
||||
try {
|
||||
ReflectionContextState.setCreatingNullObjects(contextMap, true);
|
||||
ReflectionContextState.setDenyMethodExecution(contextMap, true);
|
||||
ReflectionContextState.setReportingConversionErrors(contextMap, true);
|
||||
|
||||
ValueStack stack = ac.getValueStack();
|
||||
setParameters(action, stack, parameters);
|
||||
} finally {
|
||||
ReflectionContextState.setCreatingNullObjects(contextMap, false);
|
||||
ReflectionContextState.setDenyMethodExecution(contextMap, false);
|
||||
ReflectionContextState.setReportingConversionErrors(contextMap, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
return invocation.invoke();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parameter map to apply from wherever appropriate
|
||||
*
|
||||
* @param ac The action context
|
||||
* @return The parameter map to apply
|
||||
*/
|
||||
protected HttpParameters retrieveParameters(ActionContext ac) {
|
||||
return ac.getParameters();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds the parameters into context's ParameterMap
|
||||
*
|
||||
* @param ac The action context
|
||||
* @param newParams The parameter map to apply
|
||||
* <p>
|
||||
* In this class this is a no-op, since the parameters were fetched from the same location.
|
||||
* In subclasses both retrieveParameters() and addParametersToContext() should be overridden.
|
||||
* </p>
|
||||
*/
|
||||
protected void addParametersToContext(ActionContext ac, Map<String, ?> newParams) {
|
||||
}
|
||||
|
||||
protected void setParameters(final Object action, ValueStack stack, HttpParameters parameters) {
|
||||
HttpParameters params;
|
||||
Map<String, Parameter> acceptableParameters;
|
||||
if (ordered) {
|
||||
params = HttpParameters.create().withComparator(getOrderedComparator()).withParent(parameters).build();
|
||||
acceptableParameters = new TreeMap<>(getOrderedComparator());
|
||||
} else {
|
||||
params = HttpParameters.create().withParent(parameters).build();
|
||||
acceptableParameters = new TreeMap<>();
|
||||
}
|
||||
|
||||
for (Map.Entry<String, Parameter> entry : params.entrySet()) {
|
||||
String parameterName = entry.getKey();
|
||||
boolean isAcceptableParameter = isAcceptableParameter(parameterName, action);
|
||||
isAcceptableParameter &= isAcceptableParameterValue(entry.getValue(), action);
|
||||
|
||||
if (isAcceptableParameter) {
|
||||
acceptableParameters.put(parameterName, entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
ValueStack newStack = valueStackFactory.createValueStack(stack);
|
||||
boolean clearableStack = newStack instanceof ClearableValueStack;
|
||||
if (clearableStack) {
|
||||
//if the stack's context can be cleared, do that to prevent OGNL
|
||||
//from having access to objects in the stack, see XW-641
|
||||
((ClearableValueStack) newStack).clearContextValues();
|
||||
Map<String, Object> context = newStack.getContext();
|
||||
ReflectionContextState.setCreatingNullObjects(context, true);
|
||||
ReflectionContextState.setDenyMethodExecution(context, true);
|
||||
ReflectionContextState.setReportingConversionErrors(context, true);
|
||||
|
||||
//keep locale from original context
|
||||
newStack.getActionContext().withLocale(stack.getActionContext().getLocale()).withValueStack(stack);
|
||||
}
|
||||
|
||||
boolean memberAccessStack = newStack instanceof MemberAccessValueStack;
|
||||
if (memberAccessStack) {
|
||||
//block or allow access to properties
|
||||
//see WW-2761 for more details
|
||||
MemberAccessValueStack accessValueStack = (MemberAccessValueStack) newStack;
|
||||
accessValueStack.useAcceptProperties(acceptedPatterns.getAcceptedPatterns());
|
||||
accessValueStack.useExcludeProperties(excludedPatterns.getExcludedPatterns());
|
||||
}
|
||||
|
||||
for (Map.Entry<String, Parameter> entry : acceptableParameters.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
Parameter value = entry.getValue();
|
||||
try {
|
||||
newStack.setParameter(name, value.getObject());
|
||||
} catch (RuntimeException e) {
|
||||
if (devMode) {
|
||||
notifyDeveloperParameterException(action, name, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (clearableStack) {
|
||||
stack.getActionContext().withConversionErrors(newStack.getActionContext().getConversionErrors());
|
||||
}
|
||||
|
||||
addParametersToContext(ActionContext.getContext(), acceptableParameters);
|
||||
}
|
||||
|
||||
protected void notifyDeveloperParameterException(Object action, String property, String message) {
|
||||
String developerNotification = "Unexpected Exception caught setting '" + property + "' on '" + action.getClass() + ": " + message;
|
||||
if (action instanceof TextProvider) {
|
||||
TextProvider tp = (TextProvider) action;
|
||||
developerNotification = tp.getText("devmode.notification",
|
||||
"Developer Notification:\n{0}",
|
||||
new String[]{developerNotification}
|
||||
);
|
||||
}
|
||||
|
||||
LOG.error(developerNotification);
|
||||
|
||||
if (action instanceof ValidationAware) {
|
||||
// see https://issues.apache.org/jira/browse/WW-4066
|
||||
Collection<String> messages = ((ValidationAware) action).getActionMessages();
|
||||
messages.add(message);
|
||||
((ValidationAware) action).setActionMessages(messages);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if name of parameter can be accepted or thrown away
|
||||
*
|
||||
* @param name parameter name
|
||||
* @param action current action
|
||||
* @return true if parameter is accepted
|
||||
*/
|
||||
protected boolean isAcceptableParameter(String name, Object action) {
|
||||
ParameterNameAware parameterNameAware = (action instanceof ParameterNameAware) ? (ParameterNameAware) action : null;
|
||||
return acceptableName(name) && (parameterNameAware == null || parameterNameAware.acceptableParameterName(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if parameter value can be accepted or thrown away
|
||||
*
|
||||
* @param param the parameter
|
||||
* @param action current action
|
||||
* @return true if parameter is accepted
|
||||
*/
|
||||
protected boolean isAcceptableParameterValue(Parameter param, Object action) {
|
||||
ParameterValueAware parameterValueAware = (action instanceof ParameterValueAware) ? (ParameterValueAware) action : null;
|
||||
boolean acceptableParamValue = (parameterValueAware == null || parameterValueAware.acceptableParameterValue(param.getValue()));
|
||||
if (hasParamValuesToExclude() || hasParamValuesToAccept()) {
|
||||
// Additional validations to process
|
||||
acceptableParamValue &= acceptableValue(param.getName(), param.getValue());
|
||||
}
|
||||
return acceptableParamValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance of the comparator to use for the ordered sorting. Override this
|
||||
* method to customize the ordering of the parameters as they are set to the
|
||||
* action.
|
||||
*
|
||||
* @return A comparator to sort the parameters
|
||||
*/
|
||||
protected Comparator<String> getOrderedComparator() {
|
||||
return rbCollator;
|
||||
}
|
||||
|
||||
protected String getParameterLogMap(HttpParameters parameters) {
|
||||
if (parameters == null) {
|
||||
return "NONE";
|
||||
}
|
||||
|
||||
StringBuilder logEntry = new StringBuilder();
|
||||
for (Map.Entry<String, Parameter> entry : parameters.entrySet()) {
|
||||
logEntry.append(entry.getKey());
|
||||
logEntry.append(" => ");
|
||||
logEntry.append(entry.getValue().getValue());
|
||||
logEntry.append(" ");
|
||||
}
|
||||
|
||||
return logEntry.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the name passed is:
|
||||
* * Within the max length of a parameter name
|
||||
* * Is not excluded
|
||||
* * Is accepted
|
||||
*
|
||||
* @param name - Name to check
|
||||
* @return true if accepted
|
||||
*/
|
||||
protected boolean acceptableName(String name) {
|
||||
if (isIgnoredDMI(name)) {
|
||||
LOG.trace("DMI is enabled, ignoring DMI method: {}", name);
|
||||
return false;
|
||||
}
|
||||
boolean accepted = isWithinLengthLimit(name) && !isExcluded(name) && isAccepted(name);
|
||||
if (devMode && accepted) { // notify only when in devMode
|
||||
LOG.debug("Parameter [{}] was accepted and will be appended to action!", name);
|
||||
}
|
||||
return accepted;
|
||||
}
|
||||
|
||||
private boolean isIgnoredDMI(String name) {
|
||||
if (dmiEnabled) {
|
||||
return DMI_IGNORED_PATTERN.matcher(name).matches();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates:
|
||||
* * Value is null/blank
|
||||
* * Value is not excluded
|
||||
* * Value is accepted
|
||||
*
|
||||
* @param name - Param name (for logging)
|
||||
* @param value - value to check
|
||||
* @return true if accepted
|
||||
*/
|
||||
protected boolean acceptableValue(String name, String value) {
|
||||
boolean accepted = (value == null || value.isEmpty() || (!isParamValueExcluded(value) && isParamValueAccepted(value)));
|
||||
if (!accepted) {
|
||||
String message = "Value [{}] of parameter [{}] was not accepted and will be dropped!";
|
||||
if (devMode) {
|
||||
LOG.warn(message, value, name);
|
||||
} else {
|
||||
LOG.debug(message, value, name);
|
||||
}
|
||||
}
|
||||
return accepted;
|
||||
}
|
||||
|
||||
protected boolean isWithinLengthLimit(String name) {
|
||||
boolean matchLength = name.length() <= paramNameMaxLength;
|
||||
if (!matchLength) {
|
||||
if (devMode) { // warn only when in devMode
|
||||
LOG.warn("Parameter [{}] is too long, allowed length is [{}]. Use Interceptor Parameter Overriding " +
|
||||
"to override the limit, see more at\n" +
|
||||
"https://struts.apache.org/core-developers/interceptors.html#interceptor-parameter-overriding",
|
||||
name, paramNameMaxLength);
|
||||
} else {
|
||||
LOG.warn("Parameter [{}] is too long, allowed length is [{}]", name, paramNameMaxLength);
|
||||
}
|
||||
}
|
||||
return matchLength;
|
||||
}
|
||||
|
||||
protected boolean isAccepted(String paramName) {
|
||||
AcceptedPatternsChecker.IsAccepted result = acceptedPatterns.isAccepted(paramName);
|
||||
if (result.isAccepted()) {
|
||||
return true;
|
||||
} else if (devMode) { // warn only when in devMode
|
||||
LOG.warn("Parameter [{}] didn't match accepted pattern [{}]! See Accepted / Excluded patterns at\n" +
|
||||
"https://struts.apache.org/security/#accepted--excluded-patterns",
|
||||
paramName, result.getAcceptedPattern());
|
||||
} else {
|
||||
LOG.debug("Parameter [{}] didn't match accepted pattern [{}]!", paramName, result.getAcceptedPattern());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean isExcluded(String paramName) {
|
||||
ExcludedPatternsChecker.IsExcluded result = excludedPatterns.isExcluded(paramName);
|
||||
if (result.isExcluded()) {
|
||||
if (devMode) { // warn only when in devMode
|
||||
LOG.warn("Parameter [{}] matches excluded pattern [{}]! See Accepted / Excluded patterns at\n" +
|
||||
"https://struts.apache.org/security/#accepted--excluded-patterns",
|
||||
paramName, result.getExcludedPattern());
|
||||
} else {
|
||||
LOG.debug("Parameter [{}] matches excluded pattern [{}]!", paramName, result.getExcludedPattern());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean isParamValueExcluded(String value) {
|
||||
if (!hasParamValuesToExclude()) {
|
||||
LOG.debug("'excludedValuePatterns' not defined so anything is allowed");
|
||||
return false;
|
||||
}
|
||||
for (Pattern excludedValuePattern : excludedValuePatterns) {
|
||||
if (excludedValuePattern.matcher(value).matches()) {
|
||||
if (devMode) {
|
||||
LOG.warn("Parameter value [{}] matches excluded pattern [{}]! See Accepting/Excluding parameter values at\n" +
|
||||
"https://struts.apache.org/core-developers/parameters-interceptor#excluding-parameter-values",
|
||||
value, excludedValuePatterns);
|
||||
} else {
|
||||
LOG.debug("Parameter value [{}] matches excluded pattern [{}]", value, excludedValuePattern);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean isParamValueAccepted(String value) {
|
||||
if (!hasParamValuesToAccept()) {
|
||||
LOG.debug("'acceptedValuePatterns' not defined so anything is allowed");
|
||||
return true;
|
||||
}
|
||||
for (Pattern acceptedValuePattern : acceptedValuePatterns) {
|
||||
if (acceptedValuePattern.matcher(value).matches()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (devMode) {
|
||||
LOG.warn("Parameter value [{}] didn't match accepted pattern [{}]! See Accepting/Excluding parameter values at\n" +
|
||||
"https://struts.apache.org/core-developers/parameters-interceptor#excluding-parameter-values",
|
||||
value, acceptedValuePatterns);
|
||||
} else {
|
||||
LOG.debug("Parameter value [{}] was not accepted!", value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean hasParamValuesToExclude() {
|
||||
return excludedValuePatterns != null && excludedValuePatterns.size() > 0;
|
||||
}
|
||||
|
||||
private boolean hasParamValuesToAccept() {
|
||||
return acceptedValuePatterns != null && acceptedValuePatterns.size() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to order the parameters or not
|
||||
*
|
||||
* @return True to order
|
||||
*/
|
||||
public boolean isOrdered() {
|
||||
return ordered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to order the parameters by object depth or not
|
||||
*
|
||||
* @param ordered True to order them
|
||||
*/
|
||||
public void setOrdered(boolean ordered) {
|
||||
this.ordered = ordered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a comma-delimited list of regular expressions to match
|
||||
* parameters that are allowed in the parameter map (aka whitelist).
|
||||
* <p>
|
||||
* Don't change the default unless you know what you are doing in terms
|
||||
* of security implications.
|
||||
* </p>
|
||||
*
|
||||
* @param commaDelim A comma-delimited list of regular expressions
|
||||
*/
|
||||
public void setAcceptParamNames(String commaDelim) {
|
||||
acceptedPatterns.setAcceptedPatterns(commaDelim);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a comma-delimited list of regular expressions to match
|
||||
* parameters that should be removed from the parameter map.
|
||||
*
|
||||
* @param commaDelim A comma-delimited list of regular expressions
|
||||
*/
|
||||
public void setExcludeParams(String commaDelim) {
|
||||
excludedPatterns.setExcludedPatterns(commaDelim);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a comma-delimited list of regular expressions to match
|
||||
* values of parameters that should be accepted and included in the parameter map.
|
||||
*
|
||||
* @param commaDelimitedPatterns A comma-delimited set of regular expressions
|
||||
*/
|
||||
public void setAcceptedValuePatterns(String commaDelimitedPatterns) {
|
||||
Set<String> patterns = TextParseUtil.commaDelimitedStringToSet(commaDelimitedPatterns);
|
||||
if (acceptedValuePatterns == null) {
|
||||
// Limit unwanted log entries (for 1st call, acceptedValuePatterns null)
|
||||
LOG.debug("Sets accepted value patterns to [{}], note this may impact the safety of your application!", patterns);
|
||||
} else {
|
||||
LOG.warn("Replacing accepted patterns [{}] with [{}], be aware that this may impact safety of your application!",
|
||||
acceptedValuePatterns, patterns);
|
||||
}
|
||||
acceptedValuePatterns = new HashSet<>(patterns.size());
|
||||
try {
|
||||
for (String pattern : patterns) {
|
||||
acceptedValuePatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
}
|
||||
} finally {
|
||||
acceptedValuePatterns = Collections.unmodifiableSet(acceptedValuePatterns);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a comma-delimited list of regular expressions to match
|
||||
* values of parameters that should be removed from the parameter map.
|
||||
*
|
||||
* @param commaDelimitedPatterns A comma-delimited set of regular expressions
|
||||
*/
|
||||
public void setExcludedValuePatterns(String commaDelimitedPatterns) {
|
||||
Set<String> patterns = TextParseUtil.commaDelimitedStringToSet(commaDelimitedPatterns);
|
||||
if (excludedValuePatterns == null) {
|
||||
// Limit unwanted log entries (for 1st call, excludedValuePatterns null)
|
||||
LOG.debug("Setting excluded value patterns to [{}]", patterns);
|
||||
} else {
|
||||
LOG.warn("Replacing excluded value patterns [{}] with [{}], be aware that this may impact safety of your application!",
|
||||
excludedValuePatterns, patterns);
|
||||
}
|
||||
excludedValuePatterns = new HashSet<>(patterns.size());
|
||||
try {
|
||||
for (String pattern : patterns) {
|
||||
excludedValuePatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
}
|
||||
} finally {
|
||||
excludedValuePatterns = Collections.unmodifiableSet(excludedValuePatterns);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-10
@@ -16,7 +16,6 @@
|
||||
package com.opensymphony.xwork2.ognl;
|
||||
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import org.apache.commons.lang3.EnumUtils;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
|
||||
/**
|
||||
@@ -30,16 +29,16 @@ import org.apache.struts2.StrutsConstants;
|
||||
public class DefaultOgnlBeanInfoCacheFactory<Key, Value> extends DefaultOgnlCacheFactory<Key, Value>
|
||||
implements BeanInfoCacheFactory<Key, Value> {
|
||||
|
||||
/**
|
||||
* @deprecated since 6.4.0, use {@link #DefaultOgnlBeanInfoCacheFactory(String, String)}
|
||||
*/
|
||||
@Deprecated
|
||||
public DefaultOgnlBeanInfoCacheFactory() {
|
||||
@Override
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_MAXSIZE, required = false)
|
||||
protected void setCacheMaxSize(String maxSize) {
|
||||
super.setCacheMaxSize(maxSize);
|
||||
}
|
||||
|
||||
@Inject
|
||||
public DefaultOgnlBeanInfoCacheFactory(@Inject(value = StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_MAXSIZE) String cacheMaxSize,
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_TYPE) String defaultCacheType) {
|
||||
super(Integer.parseInt(cacheMaxSize), EnumUtils.getEnumIgnoreCase(CacheType.class, defaultCacheType));
|
||||
@Override
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_LRU_MODE, required = false)
|
||||
protected void setUseLRUCache(String useLRUMode) {
|
||||
super.setUseLRUCache(useLRUMode);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,86 +15,52 @@
|
||||
*/
|
||||
package com.opensymphony.xwork2.ognl;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
|
||||
/**
|
||||
* <p>Default OGNL Cache factory implementation.</p>
|
||||
* Default OGNL Cache factory implementation.
|
||||
*
|
||||
* <p>Currently used for Expression cache and BeanInfo cache creation.</p>
|
||||
* Currently used for Expression cache and BeanInfo cache creation.
|
||||
*
|
||||
* @param <Key> The type for the cache key entries
|
||||
* @param <Key> The type for the cache key entries
|
||||
* @param <Value> The type for the cache value entries
|
||||
*/
|
||||
public class DefaultOgnlCacheFactory<Key, Value> implements OgnlCacheFactory<Key, Value> {
|
||||
|
||||
private static final int DEFAULT_INIT_CAPACITY = 16;
|
||||
private static final float DEFAULT_LOAD_FACTOR = 0.75f;
|
||||
|
||||
private CacheType defaultCacheType;
|
||||
private int cacheMaxSize;
|
||||
|
||||
/**
|
||||
* @deprecated since 6.4.0, use {@link #DefaultOgnlCacheFactory(int, CacheType)}
|
||||
*/
|
||||
@Deprecated
|
||||
public DefaultOgnlCacheFactory() {
|
||||
this(10000, CacheType.BASIC);
|
||||
}
|
||||
|
||||
public DefaultOgnlCacheFactory(int cacheMaxSize, CacheType defaultCacheType) {
|
||||
this.cacheMaxSize = cacheMaxSize;
|
||||
this.defaultCacheType = defaultCacheType;
|
||||
}
|
||||
private final AtomicBoolean useLRUCache = new AtomicBoolean(false);
|
||||
private final AtomicInteger cacheMaxSize = new AtomicInteger(25000);
|
||||
|
||||
@Override
|
||||
public OgnlCache<Key, Value> buildOgnlCache() {
|
||||
return buildOgnlCache(getCacheMaxSize(), DEFAULT_INIT_CAPACITY, DEFAULT_LOAD_FACTOR, defaultCacheType);
|
||||
return buildOgnlCache(getCacheMaxSize(), 16, 0.75f, getUseLRUCache());
|
||||
}
|
||||
|
||||
@Override
|
||||
public OgnlCache<Key, Value> buildOgnlCache(int evictionLimit,
|
||||
int initialCapacity,
|
||||
float loadFactor,
|
||||
CacheType cacheType) {
|
||||
switch (cacheType) {
|
||||
case BASIC:
|
||||
return new OgnlDefaultCache<>(evictionLimit, initialCapacity, loadFactor);
|
||||
case LRU:
|
||||
return new OgnlLRUCache<>(evictionLimit, initialCapacity, loadFactor);
|
||||
case WTLFU:
|
||||
return new OgnlCaffeineCache<>(evictionLimit, initialCapacity);
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown cache type: " + cacheType);
|
||||
public OgnlCache<Key, Value> buildOgnlCache(int evictionLimit, int initialCapacity, float loadFactor, boolean lruCache) {
|
||||
if (lruCache) {
|
||||
return new OgnlLRUCache<>(evictionLimit, initialCapacity, loadFactor);
|
||||
} else {
|
||||
return new OgnlDefaultCache<>(evictionLimit, initialCapacity, loadFactor);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCacheMaxSize() {
|
||||
return cacheMaxSize;
|
||||
return cacheMaxSize.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 6.4.0
|
||||
*/
|
||||
@Deprecated
|
||||
protected void setCacheMaxSize(String maxSize) {
|
||||
cacheMaxSize = Integer.parseInt(maxSize);
|
||||
cacheMaxSize.set(Integer.parseInt(maxSize));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CacheType getDefaultCacheType() {
|
||||
return defaultCacheType;
|
||||
public boolean getUseLRUCache() {
|
||||
return useLRUCache.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* No effect when {@code useLRUMode} is {@code false}
|
||||
*
|
||||
* @deprecated since 6.4.0
|
||||
*/
|
||||
@Deprecated
|
||||
protected void setUseLRUCache(String useLRUMode) {
|
||||
if (BooleanUtils.toBoolean(useLRUMode)) {
|
||||
defaultCacheType = CacheType.LRU;
|
||||
}
|
||||
useLRUCache.set(BooleanUtils.toBoolean(useLRUMode));
|
||||
}
|
||||
}
|
||||
|
||||
+12
-13
@@ -16,30 +16,29 @@
|
||||
package com.opensymphony.xwork2.ognl;
|
||||
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import org.apache.commons.lang3.EnumUtils;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
|
||||
/**
|
||||
* Default OGNL Expression Cache factory implementation.
|
||||
* <p>
|
||||
*
|
||||
* Currently used for Expression cache creation.
|
||||
*
|
||||
* @param <Key> The type for the cache key entries
|
||||
* @param <Key> The type for the cache key entries
|
||||
* @param <Value> The type for the cache value entries
|
||||
*/
|
||||
public class DefaultOgnlExpressionCacheFactory<Key, Value> extends DefaultOgnlCacheFactory<Key, Value>
|
||||
implements ExpressionCacheFactory<Key, Value> {
|
||||
implements ExpressionCacheFactory<Key, Value> {
|
||||
|
||||
/**
|
||||
* @deprecated since 6.4.0, use {@link #DefaultOgnlExpressionCacheFactory(String, String)}
|
||||
*/
|
||||
@Deprecated
|
||||
public DefaultOgnlExpressionCacheFactory() {
|
||||
@Override
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_MAXSIZE, required = false)
|
||||
protected void setCacheMaxSize(String maxSize) {
|
||||
super.setCacheMaxSize(maxSize);
|
||||
}
|
||||
|
||||
@Inject
|
||||
public DefaultOgnlExpressionCacheFactory(@Inject(value = StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_MAXSIZE) String cacheMaxSize,
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_TYPE) String defaultCacheType) {
|
||||
super(Integer.parseInt(cacheMaxSize), EnumUtils.getEnumIgnoreCase(CacheType.class, defaultCacheType));
|
||||
@Override
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_LRU_MODE, required = false)
|
||||
protected void setUseLRUCache(String useLRUMode) {
|
||||
super.setUseLRUCache(useLRUMode);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,23 +19,23 @@ package com.opensymphony.xwork2.ognl;
|
||||
* A basic cache interface for use with OGNL processing (such as Expression, BeanInfo).
|
||||
* All OGNL caches will have an eviction limit, but setting an extremely high value can
|
||||
* simulate an "effectively unlimited" cache.
|
||||
*
|
||||
*
|
||||
* @param <Key> The type for the cache key entries
|
||||
* @param <Value> The type for the cache value entries
|
||||
*/
|
||||
public interface OgnlCache<Key, Value> {
|
||||
|
||||
Value get(Key key);
|
||||
public Value get(Key key);
|
||||
|
||||
void put(Key key, Value value);
|
||||
public void put(Key key, Value value);
|
||||
|
||||
void putIfAbsent(Key key, Value value);
|
||||
public void putIfAbsent(Key key, Value value);
|
||||
|
||||
int size();
|
||||
public int size();
|
||||
|
||||
void clear();
|
||||
public void clear();
|
||||
|
||||
int getEvictionLimit();
|
||||
public int getEvictionLimit();
|
||||
|
||||
void setEvictionLimit(int cacheEvictionLimit);
|
||||
public void setEvictionLimit(int cacheEvictionLimit);
|
||||
}
|
||||
|
||||
@@ -19,52 +19,12 @@ package com.opensymphony.xwork2.ognl;
|
||||
* Used by {@link com.opensymphony.xwork2.ognl.OgnlUtil} to create appropriate OGNL
|
||||
* caches based on configuration.
|
||||
*
|
||||
* @param <Key> The type for the cache key entries
|
||||
* @param <Key> The type for the cache key entries
|
||||
* @param <Value> The type for the cache value entries
|
||||
*/
|
||||
public interface OgnlCacheFactory<Key, Value> {
|
||||
interface OgnlCacheFactory<Key, Value> {
|
||||
OgnlCache<Key, Value> buildOgnlCache();
|
||||
|
||||
/**
|
||||
* Note that if {@code lruCache} is {@code false}, the cache type could still be LRU if the default cache type is
|
||||
* configured as such.
|
||||
* @deprecated since 6.4.0, use {@link #buildOgnlCache(int, int, float, CacheType)}
|
||||
*/
|
||||
@Deprecated
|
||||
default OgnlCache<Key, Value> buildOgnlCache(int evictionLimit,
|
||||
int initialCapacity,
|
||||
float loadFactor,
|
||||
boolean lruCache) {
|
||||
return buildOgnlCache(evictionLimit,
|
||||
initialCapacity,
|
||||
loadFactor,
|
||||
lruCache ? CacheType.LRU : getDefaultCacheType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param evictionLimit maximum capacity of the cache where applicable for cache type chosen
|
||||
* @param initialCapacity initial capacity of the cache where applicable for cache type chosen
|
||||
* @param loadFactor load factor of the cache where applicable for cache type chosen
|
||||
* @param cacheType type of cache to build
|
||||
* @return a new cache instance
|
||||
*/
|
||||
OgnlCache<Key, Value> buildOgnlCache(int evictionLimit, int initialCapacity, float loadFactor, CacheType cacheType);
|
||||
|
||||
OgnlCache<Key, Value> buildOgnlCache(int evictionLimit, int initialCapacity, float loadFactor, boolean lruCache);
|
||||
int getCacheMaxSize();
|
||||
|
||||
/**
|
||||
* @deprecated since 6.4.0
|
||||
*/
|
||||
@Deprecated
|
||||
default boolean getUseLRUCache() {
|
||||
return CacheType.LRU.equals(getDefaultCacheType());
|
||||
}
|
||||
|
||||
CacheType getDefaultCacheType();
|
||||
|
||||
enum CacheType {
|
||||
BASIC,
|
||||
LRU,
|
||||
WTLFU
|
||||
}
|
||||
boolean getUseLRUCache();
|
||||
}
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright 2022 Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.ognl;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
|
||||
/**
|
||||
* <p>This OGNL Cache implementation is backed by {@link Caffeine} which uses the Window TinyLfu algorithm.</p>
|
||||
*
|
||||
* <p>An appropriate eviction limit should be chosen for your specific application based on factors and requirements
|
||||
* such as:</p>
|
||||
* <ul>
|
||||
* <li>Quantity and complexity of actions</li>
|
||||
* <li>Volume of requests</li>
|
||||
* <li>Rate limits and attack potential/patterns</li>
|
||||
* <li>Memory constraints</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param <K> The type for the cache key entries
|
||||
* @param <V> The type for the cache value entries
|
||||
*/
|
||||
public class OgnlCaffeineCache<K, V> implements OgnlCache<K, V> {
|
||||
|
||||
private final Cache<K, V> cache;
|
||||
|
||||
public OgnlCaffeineCache(int evictionLimit, int initialCapacity) {
|
||||
this.cache = Caffeine.newBuilder().initialCapacity(initialCapacity).maximumSize(evictionLimit).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(K key) {
|
||||
return cache.getIfPresent(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(K key, V value) {
|
||||
cache.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putIfAbsent(K key, V value) {
|
||||
cache.asMap().putIfAbsent(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return cache.asMap().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
cache.invalidateAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEvictionLimit() {
|
||||
return Math.toIntExact(cache.policy().eviction().orElseThrow(IllegalStateException::new).getMaximum());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEvictionLimit(int cacheEvictionLimit) {
|
||||
cache.policy().eviction().orElseThrow(IllegalStateException::new).setMaximum(cacheEvictionLimit);
|
||||
}
|
||||
}
|
||||
@@ -19,40 +19,37 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* <p>Basic OGNL cache implementation.</p>
|
||||
*
|
||||
* <p>This implementation is backed by a {@link ConcurrentHashMap} that is cleared whenever the eviction limit is
|
||||
* surpassed.</p>
|
||||
*
|
||||
* <p>Setting a very high eviction limit simulates an unlimited cache.</p>
|
||||
* <p>Setting too low an eviction limit will make the cache ineffective.</p>
|
||||
*
|
||||
* @param <K> The type for the cache key entries
|
||||
* @param <V> The type for the cache value entries
|
||||
* Default OGNL cache implementation.
|
||||
*
|
||||
* Setting a very high eviction limit simulates an unlimited cache.
|
||||
* Setting too low an eviction limit will make the cache ineffective.
|
||||
*
|
||||
* @param <Key> The type for the cache key entries
|
||||
* @param <Value> The type for the cache value entries
|
||||
*/
|
||||
public class OgnlDefaultCache<K, V> implements OgnlCache<K, V> {
|
||||
public class OgnlDefaultCache<Key, Value> implements OgnlCache<Key, Value> {
|
||||
|
||||
private final ConcurrentHashMap<K, V> ognlCache;
|
||||
private final AtomicInteger cacheEvictionLimit;
|
||||
private final ConcurrentHashMap<Key, Value> ognlCache;
|
||||
private final AtomicInteger cacheEvictionLimit = new AtomicInteger(25000);
|
||||
|
||||
public OgnlDefaultCache(int evictionLimit, int initialCapacity, float loadFactor) {
|
||||
cacheEvictionLimit = new AtomicInteger(evictionLimit);
|
||||
this.cacheEvictionLimit.set(evictionLimit);
|
||||
ognlCache = new ConcurrentHashMap<>(initialCapacity, loadFactor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(K key) {
|
||||
public Value get(Key key) {
|
||||
return ognlCache.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(K key, V value) {
|
||||
public void put(Key key, Value value) {
|
||||
ognlCache.put(key, value);
|
||||
this.clearIfEvictionLimitExceeded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putIfAbsent(K key, V value) {
|
||||
public void putIfAbsent(Key key, Value value) {
|
||||
ognlCache.putIfAbsent(key, value);
|
||||
this.clearIfEvictionLimitExceeded();
|
||||
}
|
||||
|
||||
@@ -21,46 +21,46 @@ import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* <p>A basic OGNL LRU cache implementation.</p>
|
||||
*
|
||||
* <p>The implementation utilizes a {@link Collections#synchronizedMap(java.util.Map)}
|
||||
* backed by a {@link LinkedHashMap}. May be replaced by a more efficient implementation in the future.</p>
|
||||
*
|
||||
* <p>Setting too low an eviction limit will produce more overhead than value.</p>
|
||||
* <p>Setting too high an eviction limit may also produce more overhead than value.</p>
|
||||
* <p>An appropriate eviction limit will need to be determined on an individual application basis.</p>
|
||||
*
|
||||
* @param <K> The type for the cache key entries
|
||||
* @param <V> The type for the cache value entries
|
||||
* A basic OGNL LRU cache implementation.
|
||||
*
|
||||
* The implementation utilizes a {@link Collections#synchronizedMap(java.util.Map)}
|
||||
* backed by a {@link LinkedHashMap}. May be replaced by a more efficient implementation in the future.
|
||||
*
|
||||
* Setting too low an eviction limit will produce more overhead than value.
|
||||
* Setting too high an eviction limit may also produce more overhead than value.
|
||||
* An appropriate eviction limit will need to be determined on an individual application basis.
|
||||
*
|
||||
* @param <Key> The type for the cache key entries
|
||||
* @param <Value> The type for the cache value entries
|
||||
*/
|
||||
public class OgnlLRUCache<K, V> implements OgnlCache<K, V> {
|
||||
public class OgnlLRUCache<Key, Value> implements OgnlCache<Key, Value> {
|
||||
|
||||
private final Map<K, V> ognlLRUCache;
|
||||
private final AtomicInteger cacheEvictionLimit;
|
||||
private final Map<Key, Value> ognlLRUCache;
|
||||
private final AtomicInteger cacheEvictionLimit = new AtomicInteger(2500);
|
||||
|
||||
public OgnlLRUCache(int evictionLimit, int initialCapacity, float loadFactor) {
|
||||
cacheEvictionLimit = new AtomicInteger(evictionLimit);
|
||||
this.cacheEvictionLimit.set(evictionLimit);
|
||||
// Access-order mode selected (order mode true in LinkedHashMap constructor).
|
||||
ognlLRUCache = Collections.synchronizedMap(new LinkedHashMap<K, V>(initialCapacity, loadFactor, true) {
|
||||
ognlLRUCache = Collections.synchronizedMap (new LinkedHashMap<Key, Value>(initialCapacity, loadFactor, true) {
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
|
||||
return size() > cacheEvictionLimit.get();
|
||||
protected boolean removeEldestEntry(Map.Entry<Key,Value> eldest) {
|
||||
return (this.size() > cacheEvictionLimit.get());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(K key) {
|
||||
public Value get(Key key) {
|
||||
return ognlLRUCache.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(K key, V value) {
|
||||
public void put(Key key, Value value) {
|
||||
ognlLRUCache.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putIfAbsent(K key, V value) {
|
||||
public void putIfAbsent(Key key, Value value) {
|
||||
ognlLRUCache.putIfAbsent(key, value);
|
||||
}
|
||||
|
||||
@@ -81,9 +81,7 @@ public class OgnlLRUCache<K, V> implements OgnlCache<K, V> {
|
||||
|
||||
@Override
|
||||
public void setEvictionLimit(int cacheEvictionLimit) {
|
||||
if (cacheEvictionLimit < size()) {
|
||||
clear();
|
||||
}
|
||||
this.cacheEvictionLimit.set(cacheEvictionLimit);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ package com.opensymphony.xwork2.ognl;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionException;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
|
||||
import ognl.Ognl;
|
||||
import ognl.OgnlException;
|
||||
import ognl.OgnlRuntime;
|
||||
|
||||
@@ -32,9 +33,9 @@ import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
public class OgnlReflectionProvider implements ReflectionProvider {
|
||||
|
||||
|
||||
private OgnlUtil ognlUtil;
|
||||
|
||||
|
||||
@Inject
|
||||
public void setOgnlUtil(OgnlUtil ognlUtil) {
|
||||
this.ognlUtil = ognlUtil;
|
||||
@@ -68,6 +69,7 @@ public class OgnlReflectionProvider implements ReflectionProvider {
|
||||
|
||||
public void setProperties(Map<String, ?> props, Object o, Map<String, Object> context, boolean throwPropertyExceptions) throws ReflectionException{
|
||||
ognlUtil.setProperties(props, o, context, throwPropertyExceptions);
|
||||
|
||||
}
|
||||
|
||||
public void setProperties(Map<String, ?> properties, Object o) {
|
||||
@@ -132,7 +134,7 @@ public class OgnlReflectionProvider implements ReflectionProvider {
|
||||
public void setValue(String expression, Map<String, Object> context, Object root,
|
||||
Object value) throws ReflectionException {
|
||||
try {
|
||||
ognlUtil.setValue(expression, context, root, value);
|
||||
Ognl.setValue(expression, context, root, value);
|
||||
} catch (OgnlException e) {
|
||||
throw new ReflectionException(e);
|
||||
}
|
||||
|
||||
@@ -18,10 +18,11 @@
|
||||
*/
|
||||
package com.opensymphony.xwork2.ognl;
|
||||
|
||||
import com.opensymphony.xwork2.config.ConfigurationException;
|
||||
import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
|
||||
import com.opensymphony.xwork2.inject.Container;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.ognl.accessor.RootAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor;
|
||||
import com.opensymphony.xwork2.util.CompoundRoot;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionException;
|
||||
import ognl.ClassResolver;
|
||||
@@ -35,8 +36,6 @@ import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
import org.apache.struts2.ognl.OgnlGuard;
|
||||
import org.apache.struts2.ognl.StrutsOgnlGuard;
|
||||
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.IntrospectionException;
|
||||
@@ -44,18 +43,18 @@ import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
import static com.opensymphony.xwork2.util.ConfigParseUtil.toClassesSet;
|
||||
import static com.opensymphony.xwork2.util.ConfigParseUtil.toNewPatternsSet;
|
||||
import static com.opensymphony.xwork2.util.ConfigParseUtil.toPackageNamesSet;
|
||||
import static java.util.Collections.emptySet;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.apache.struts2.ognl.OgnlGuard.EXPR_BLOCKED;
|
||||
import static com.opensymphony.xwork2.util.TextParseUtil.commaDelimitedStringToSet;
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
import static org.apache.commons.lang3.StringUtils.strip;
|
||||
|
||||
|
||||
/**
|
||||
@@ -74,46 +73,71 @@ public class OgnlUtil {
|
||||
private final OgnlCache<String, Object> expressionCache;
|
||||
private final OgnlCache<Class<?>, BeanInfo> beanInfoCache;
|
||||
private TypeConverter defaultConverter;
|
||||
private final OgnlGuard ognlGuard;
|
||||
|
||||
private boolean devMode;
|
||||
private boolean enableExpressionCache = true;
|
||||
private boolean enableEvalExpression;
|
||||
|
||||
private String devModeExcludedClasses = "";
|
||||
private String devModeExcludedPackageNamePatterns = "";
|
||||
private String devModeExcludedPackageNames = "";
|
||||
private String devModeExcludedPackageExemptClasses = "";
|
||||
private Set<Class<?>> excludedClasses;
|
||||
private Set<Pattern> excludedPackageNamePatterns;
|
||||
private Set<String> excludedPackageNames;
|
||||
private Set<Class<?>> excludedPackageExemptClasses;
|
||||
|
||||
private Set<Class<?>> devModeExcludedClasses;
|
||||
private Set<Pattern> devModeExcludedPackageNamePatterns;
|
||||
private Set<String> devModeExcludedPackageNames;
|
||||
private Set<Class<?>> devModeExcludedPackageExemptClasses;
|
||||
|
||||
private Container container;
|
||||
private boolean allowStaticFieldAccess = true;
|
||||
private boolean disallowProxyMemberAccess;
|
||||
|
||||
/**
|
||||
* Construct a new OgnlUtil instance for use with the framework
|
||||
*
|
||||
* @deprecated since 6.0.0. Use {@link #OgnlUtil(ExpressionCacheFactory, BeanInfoCacheFactory, OgnlGuard) instead.
|
||||
* @deprecated It is recommended to utilize the {@link OgnlUtil#OgnlUtil(com.opensymphony.xwork2.ognl.ExpressionCacheFactory, com.opensymphony.xwork2.ognl.BeanInfoCacheFactory) method instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public OgnlUtil() {
|
||||
// Instantiate default Expression and BeanInfo caches (factories must be non-null).
|
||||
this(new DefaultOgnlExpressionCacheFactory<>(),
|
||||
new DefaultOgnlBeanInfoCacheFactory<>(),
|
||||
new StrutsOgnlGuard());
|
||||
new DefaultOgnlBeanInfoCacheFactory<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new OgnlUtil instance for use with the framework, with optional cache factories for OGNL Expression
|
||||
* and BeanInfo caches.
|
||||
* Construct a new OgnlUtil instance for use with the framework, with optional
|
||||
* cache factories for OGNL Expression and BeanInfo caches.
|
||||
*
|
||||
* @param ognlExpressionCacheFactory factory for Expression cache instance
|
||||
* @param ognlBeanInfoCacheFactory factory for BeanInfo cache instance
|
||||
* @param ognlGuard OGNL Guard instance
|
||||
* NOTE: Although the extension points are defined for the optional cache factories, developer-defined overrides do
|
||||
* do not appear to function at this time (it always appears to instantiate the default factories).
|
||||
* Construction injectors do not allow the optional flag, so the definitions must be defined.
|
||||
*
|
||||
* @param ognlExpressionCacheFactory factory for Expression cache instance. If null, it uses a default
|
||||
* @param ognlBeanInfoCacheFactory factory for BeanInfo cache instance. If null, it uses a default
|
||||
*/
|
||||
@Inject
|
||||
public OgnlUtil(@Inject ExpressionCacheFactory<String, Object> ognlExpressionCacheFactory,
|
||||
@Inject BeanInfoCacheFactory<Class<?>, BeanInfo> ognlBeanInfoCacheFactory,
|
||||
@Inject OgnlGuard ognlGuard) {
|
||||
this.expressionCache = requireNonNull(ognlExpressionCacheFactory).buildOgnlCache();
|
||||
this.beanInfoCache = requireNonNull(ognlBeanInfoCacheFactory).buildOgnlCache();
|
||||
this.ognlGuard = requireNonNull(ognlGuard);
|
||||
public OgnlUtil(
|
||||
@Inject ExpressionCacheFactory<String, Object> ognlExpressionCacheFactory,
|
||||
@Inject BeanInfoCacheFactory<Class<?>, BeanInfo> ognlBeanInfoCacheFactory
|
||||
) {
|
||||
if (ognlExpressionCacheFactory == null) {
|
||||
throw new IllegalArgumentException("ExpressionCacheFactory parameter cannot be null");
|
||||
}
|
||||
if (ognlBeanInfoCacheFactory == null) {
|
||||
throw new IllegalArgumentException("BeanInfoCacheFactory parameter cannot be null");
|
||||
}
|
||||
excludedClasses = Collections.unmodifiableSet(new HashSet<>());
|
||||
excludedPackageNamePatterns = Collections.unmodifiableSet(new HashSet<>());
|
||||
excludedPackageNames = Collections.unmodifiableSet(new HashSet<>());
|
||||
excludedPackageExemptClasses = Collections.unmodifiableSet(new HashSet<>());
|
||||
|
||||
devModeExcludedClasses = Collections.unmodifiableSet(new HashSet<>());
|
||||
devModeExcludedPackageNamePatterns = Collections.unmodifiableSet(new HashSet<>());
|
||||
devModeExcludedPackageNames = Collections.unmodifiableSet(new HashSet<>());
|
||||
devModeExcludedPackageExemptClasses = Collections.unmodifiableSet(new HashSet<>());
|
||||
|
||||
this.expressionCache = ognlExpressionCacheFactory.buildOgnlCache();
|
||||
this.beanInfoCache = ognlBeanInfoCacheFactory.buildOgnlCache();
|
||||
}
|
||||
|
||||
@Inject
|
||||
@@ -126,23 +150,17 @@ public class OgnlUtil {
|
||||
this.devMode = BooleanUtils.toBoolean(mode);
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_ENABLE_EXPRESSION_CACHE, required = false)
|
||||
@Inject(StrutsConstants.STRUTS_OGNL_ENABLE_EXPRESSION_CACHE)
|
||||
protected void setEnableExpressionCache(String cache) {
|
||||
enableExpressionCache = BooleanUtils.toBoolean(cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 6.4.0, changing maximum cache size after initialisation is not necessary.
|
||||
*/
|
||||
@Deprecated
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_MAXSIZE, required = false)
|
||||
protected void setExpressionCacheMaxSize(String maxSize) {
|
||||
expressionCache.setEvictionLimit(Integer.parseInt(maxSize));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 6.4.0, changing maximum cache size after initialisation is not necessary.
|
||||
*/
|
||||
@Deprecated
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_MAXSIZE, required = false)
|
||||
protected void setBeanInfoCacheMaxSize(String maxSize) {
|
||||
beanInfoCache.setEvictionLimit(Integer.parseInt(maxSize));
|
||||
}
|
||||
@@ -156,88 +174,116 @@ public class OgnlUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 6.4.0, no replacement.
|
||||
*/
|
||||
@Deprecated
|
||||
@Inject(value = StrutsConstants.STRUTS_EXCLUDED_CLASSES, required = false)
|
||||
protected void setExcludedClasses(String commaDelimitedClasses) {
|
||||
// Must be set directly on SecurityMemberAccess
|
||||
Set<Class<?>> excludedClasses = new HashSet<>();
|
||||
excludedClasses.addAll(this.excludedClasses);
|
||||
excludedClasses.addAll(parseClasses(commaDelimitedClasses));
|
||||
this.excludedClasses = Collections.unmodifiableSet(excludedClasses);
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_DEV_MODE_EXCLUDED_CLASSES, required = false)
|
||||
protected void setDevModeExcludedClasses(String commaDelimitedClasses) {
|
||||
this.devModeExcludedClasses = commaDelimitedClasses;
|
||||
Set<Class<?>> excludedClasses = new HashSet<>();
|
||||
excludedClasses.addAll(this.devModeExcludedClasses);
|
||||
excludedClasses.addAll(parseClasses(commaDelimitedClasses));
|
||||
this.devModeExcludedClasses = Collections.unmodifiableSet(excludedClasses);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 6.4.0, no replacement.
|
||||
*/
|
||||
@Deprecated
|
||||
private Set<Class<?>> parseClasses(String commaDelimitedClasses) {
|
||||
Set<String> classNames = commaDelimitedStringToSet(commaDelimitedClasses);
|
||||
Set<Class<?>> classes = new HashSet<>();
|
||||
for (String className : classNames) {
|
||||
try {
|
||||
classes.add(Class.forName(className));
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new ConfigurationException("Cannot load class for exclusion/exemption configuration: " + className, e);
|
||||
}
|
||||
}
|
||||
return classes;
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_EXCLUDED_PACKAGE_NAME_PATTERNS, required = false)
|
||||
protected void setExcludedPackageNamePatterns(String commaDelimitedPackagePatterns) {
|
||||
// Must be set directly on SecurityMemberAccess
|
||||
Set<Pattern> excludedPackageNamePatterns = new HashSet<>();
|
||||
excludedPackageNamePatterns.addAll(this.excludedPackageNamePatterns);
|
||||
excludedPackageNamePatterns.addAll(parseExcludedPackageNamePatterns(commaDelimitedPackagePatterns));
|
||||
this.excludedPackageNamePatterns = Collections.unmodifiableSet(excludedPackageNamePatterns);
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_DEV_MODE_EXCLUDED_PACKAGE_NAME_PATTERNS, required = false)
|
||||
protected void setDevModeExcludedPackageNamePatterns(String commaDelimitedPackagePatterns) {
|
||||
this.devModeExcludedPackageNamePatterns = commaDelimitedPackagePatterns;
|
||||
Set<Pattern> excludedPackageNamePatterns = new HashSet<>();
|
||||
excludedPackageNamePatterns.addAll(this.devModeExcludedPackageNamePatterns);
|
||||
excludedPackageNamePatterns.addAll(parseExcludedPackageNamePatterns(commaDelimitedPackagePatterns));
|
||||
this.devModeExcludedPackageNamePatterns = Collections.unmodifiableSet(excludedPackageNamePatterns);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 6.4.0, no replacement.
|
||||
*/
|
||||
@Deprecated
|
||||
private Set<Pattern> parseExcludedPackageNamePatterns(String commaDelimitedPackagePatterns) {
|
||||
try {
|
||||
return commaDelimitedStringToSet(commaDelimitedPackagePatterns)
|
||||
.stream().map(Pattern::compile).collect(toSet());
|
||||
} catch (PatternSyntaxException e) {
|
||||
throw new ConfigurationException(
|
||||
"Excluded package name patterns could not be parsed due to invalid regex: " + commaDelimitedPackagePatterns, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_EXCLUDED_PACKAGE_NAMES, required = false)
|
||||
protected void setExcludedPackageNames(String commaDelimitedPackageNames) {
|
||||
// Must be set directly on SecurityMemberAccess
|
||||
Set<String> excludedPackageNames = new HashSet<>();
|
||||
excludedPackageNames.addAll(this.excludedPackageNames);
|
||||
excludedPackageNames.addAll(parseExcludedPackageNames(commaDelimitedPackageNames));
|
||||
this.excludedPackageNames = Collections.unmodifiableSet(excludedPackageNames);
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_DEV_MODE_EXCLUDED_PACKAGE_NAMES, required = false)
|
||||
protected void setDevModeExcludedPackageNames(String commaDelimitedPackageNames) {
|
||||
this.devModeExcludedPackageNames = commaDelimitedPackageNames;
|
||||
Set<String> excludedPackageNames = new HashSet<>();
|
||||
excludedPackageNames.addAll(this.devModeExcludedPackageNames);
|
||||
excludedPackageNames.addAll(parseExcludedPackageNames(commaDelimitedPackageNames));
|
||||
this.devModeExcludedPackageNames = Collections.unmodifiableSet(excludedPackageNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 6.4.0, no replacement.
|
||||
*/
|
||||
@Deprecated
|
||||
@Inject(value = StrutsConstants.STRUTS_EXCLUDED_PACKAGE_EXEMPT_CLASSES, required = false)
|
||||
public void setExcludedPackageExemptClasses(String commaDelimitedClasses) {
|
||||
// Must be set directly on SecurityMemberAccess
|
||||
Set<Class<?>> excludedPackageExemptClasses = new HashSet<>();
|
||||
excludedPackageExemptClasses.addAll(this.excludedPackageExemptClasses);
|
||||
excludedPackageExemptClasses.addAll(parseClasses(commaDelimitedClasses));
|
||||
this.excludedPackageExemptClasses = Collections.unmodifiableSet(excludedPackageExemptClasses);
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_DEV_MODE_EXCLUDED_PACKAGE_EXEMPT_CLASSES, required = false)
|
||||
public void setDevModeExcludedPackageExemptClasses(String commaDelimitedClasses) {
|
||||
this.devModeExcludedPackageExemptClasses = commaDelimitedClasses;
|
||||
Set<Class<?>> excludedPackageExemptClasses = new HashSet<>();
|
||||
excludedPackageExemptClasses.addAll(this.devModeExcludedPackageExemptClasses);
|
||||
excludedPackageExemptClasses.addAll(parseClasses(commaDelimitedClasses));
|
||||
this.devModeExcludedPackageExemptClasses = Collections.unmodifiableSet(excludedPackageExemptClasses);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 6.4.0, no replacement.
|
||||
*/
|
||||
@Deprecated
|
||||
public Set<String> getExcludedClasses() {
|
||||
return toClassesSet(container.getInstance(String.class, StrutsConstants.STRUTS_EXCLUDED_CLASSES));
|
||||
private Set<String> parseExcludedPackageNames(String commaDelimitedPackageNames) {
|
||||
Set<String> parsedSet = commaDelimitedStringToSet(commaDelimitedPackageNames)
|
||||
.stream().map(s -> strip(s, ".")).collect(toSet());
|
||||
if (parsedSet.stream().anyMatch(s -> s.matches("(.*?)\\s(.*?)"))) {
|
||||
throw new ConfigurationException("Excluded package names could not be parsed due to erroneous whitespace characters: " + commaDelimitedPackageNames);
|
||||
}
|
||||
return parsedSet;
|
||||
}
|
||||
|
||||
public Set<Class<?>> getExcludedClasses() {
|
||||
return excludedClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 6.4.0, no replacement.
|
||||
*/
|
||||
@Deprecated
|
||||
public Set<Pattern> getExcludedPackageNamePatterns() {
|
||||
return toNewPatternsSet(emptySet(), container.getInstance(String.class, StrutsConstants.STRUTS_EXCLUDED_PACKAGE_NAME_PATTERNS));
|
||||
return excludedPackageNamePatterns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 6.4.0, no replacement.
|
||||
*/
|
||||
@Deprecated
|
||||
public Set<String> getExcludedPackageNames() {
|
||||
return toPackageNamesSet(container.getInstance(String.class, StrutsConstants.STRUTS_EXCLUDED_PACKAGE_NAMES));
|
||||
return excludedPackageNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 6.4.0, no replacement.
|
||||
*/
|
||||
@Deprecated
|
||||
public Set<String> getExcludedPackageExemptClasses() {
|
||||
return toClassesSet(container.getInstance(String.class, StrutsConstants.STRUTS_EXCLUDED_PACKAGE_EXEMPT_CLASSES));
|
||||
public Set<Class<?>> getExcludedPackageExemptClasses() {
|
||||
return excludedPackageExemptClasses;
|
||||
}
|
||||
|
||||
@Inject
|
||||
@@ -245,28 +291,14 @@ public class OgnlUtil {
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 6.4.0, no replacement.
|
||||
*/
|
||||
@Deprecated
|
||||
@Inject(value = StrutsConstants.STRUTS_ALLOW_STATIC_FIELD_ACCESS, required = false)
|
||||
protected void setAllowStaticFieldAccess(String allowStaticFieldAccess) {
|
||||
// Must be set directly on SecurityMemberAccess
|
||||
this.allowStaticFieldAccess = BooleanUtils.toBoolean(allowStaticFieldAccess);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 6.4.0, no replacement.
|
||||
*/
|
||||
@Deprecated
|
||||
@Inject(value = StrutsConstants.STRUTS_DISALLOW_PROXY_MEMBER_ACCESS, required = false)
|
||||
protected void setDisallowProxyMemberAccess(String disallowProxyMemberAccess) {
|
||||
// Must be set directly on SecurityMemberAccess
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 6.4.0, no replacement.
|
||||
*/
|
||||
@Deprecated
|
||||
protected void setDisallowDefaultPackageAccess(String disallowDefaultPackageAccess) {
|
||||
// Must be set directly on SecurityMemberAccess
|
||||
this.disallowProxyMemberAccess = BooleanUtils.toBoolean(disallowProxyMemberAccess);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -288,20 +320,8 @@ public class OgnlUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 6.4.0, no replacement.
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean isDisallowProxyMemberAccess() {
|
||||
return BooleanUtils.toBoolean(container.getInstance(String.class, StrutsConstants.STRUTS_DISALLOW_PROXY_MEMBER_ACCESS));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 6.4.0, no replacement.
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean isDisallowDefaultPackageAccess() {
|
||||
return BooleanUtils.toBoolean(container.getInstance(String.class, StrutsConstants.STRUTS_DISALLOW_DEFAULT_PACKAGE_ACCESS));
|
||||
return disallowProxyMemberAccess;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -509,7 +529,8 @@ public class OgnlUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around Ognl#setValue
|
||||
* Wrapper around Ognl.setValue() to handle type conversion for collection elements.
|
||||
* Ideally, this should be handled by OGNL directly.
|
||||
*
|
||||
* @param name the name
|
||||
* @param context context map
|
||||
@@ -519,7 +540,16 @@ public class OgnlUtil {
|
||||
* @throws OgnlException in case of ognl errors
|
||||
*/
|
||||
public void setValue(final String name, final Map<String, Object> context, final Object root, final Object value) throws OgnlException {
|
||||
ognlSet(name, context, root, value, context, this::checkEvalExpression, this::checkArithmeticExpression);
|
||||
compileAndExecute(name, context, (OgnlTask<Void>) tree -> {
|
||||
if (isEvalExpression(tree, context)) {
|
||||
throw new OgnlException("Eval expression/chained expressions cannot be used as parameter name");
|
||||
}
|
||||
if (isArithmeticExpression(tree, context)) {
|
||||
throw new OgnlException("Arithmetic expressions cannot be used as parameter name");
|
||||
}
|
||||
Ognl.setValue(tree, context, root, value);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isEvalExpression(Object tree, Map<String, Object> context) throws OgnlException {
|
||||
@@ -562,58 +592,58 @@ public class OgnlUtil {
|
||||
}
|
||||
|
||||
public Object getValue(final String name, final Map<String, Object> context, final Object root) throws OgnlException {
|
||||
return getValue(name, context, root, null);
|
||||
return compileAndExecute(name, context, tree -> Ognl.getValue(tree, context, root));
|
||||
}
|
||||
|
||||
public Object callMethod(final String name, final Map<String, Object> context, final Object root) throws OgnlException {
|
||||
return ognlGet(name, context, root, null, context, this::checkSimpleMethod);
|
||||
return compileAndExecuteMethod(name, context, tree -> Ognl.getValue(tree, context, root));
|
||||
}
|
||||
|
||||
public Object getValue(final String name, final Map<String, Object> context, final Object root, final Class<?> resultType) throws OgnlException {
|
||||
return ognlGet(name, context, root, resultType, context, this::checkEnableEvalExpression);
|
||||
return compileAndExecute(name, context, tree -> Ognl.getValue(tree, context, root, resultType));
|
||||
}
|
||||
|
||||
|
||||
public Object compile(String expression) throws OgnlException {
|
||||
return compile(expression, null);
|
||||
}
|
||||
|
||||
private void ognlSet(String expr, Map<String, Object> context, Object root, Object value, Map<String, Object> checkContext, TreeValidator... treeValidators) throws OgnlException {
|
||||
Object tree = toTree(expr);
|
||||
for (TreeValidator validator : treeValidators) {
|
||||
validator.validate(tree, checkContext);
|
||||
}
|
||||
Ognl.setValue(tree, context, root, value);
|
||||
}
|
||||
|
||||
private <T> T ognlGet(String expr, Map<String, Object> context, Object root, Class<T> resultType, Map<String, Object> checkContext, TreeValidator... treeValidators) throws OgnlException {
|
||||
Object tree = toTree(expr);
|
||||
for (TreeValidator validator : treeValidators) {
|
||||
validator.validate(tree, checkContext);
|
||||
}
|
||||
return (T) Ognl.getValue(tree, context, root, resultType);
|
||||
}
|
||||
|
||||
private Object toTree(String expr) throws OgnlException {
|
||||
Object tree = null;
|
||||
private <T> Object compileAndExecute(String expression, Map<String, Object> context, OgnlTask<T> task) throws OgnlException {
|
||||
Object tree;
|
||||
if (enableExpressionCache) {
|
||||
tree = expressionCache.get(expr);
|
||||
}
|
||||
if (tree == null) {
|
||||
tree = ognlGuard.parseExpression(expr);
|
||||
if (enableExpressionCache) {
|
||||
expressionCache.put(expr, tree);
|
||||
tree = expressionCache.get(expression);
|
||||
if (tree == null) {
|
||||
tree = Ognl.parseExpression(expression);
|
||||
checkEnableEvalExpression(tree, context);
|
||||
expressionCache.putIfAbsent(expression, tree);
|
||||
}
|
||||
} else {
|
||||
tree = Ognl.parseExpression(expression);
|
||||
checkEnableEvalExpression(tree, context);
|
||||
}
|
||||
if (EXPR_BLOCKED.equals(tree)) {
|
||||
throw new OgnlException("Expression blocked by OgnlGuard: " + expr);
|
||||
|
||||
return task.execute(tree);
|
||||
}
|
||||
|
||||
private <T> Object compileAndExecuteMethod(String expression, Map<String, Object> context, OgnlTask<T> task) throws OgnlException {
|
||||
Object tree;
|
||||
if (enableExpressionCache) {
|
||||
tree = expressionCache.get(expression);
|
||||
if (tree == null) {
|
||||
tree = Ognl.parseExpression(expression);
|
||||
checkSimpleMethod(tree, context);
|
||||
expressionCache.putIfAbsent(expression, tree);
|
||||
}
|
||||
} else {
|
||||
tree = Ognl.parseExpression(expression);
|
||||
checkSimpleMethod(tree, context);
|
||||
}
|
||||
return tree;
|
||||
|
||||
return task.execute(tree);
|
||||
}
|
||||
|
||||
public Object compile(String expression, Map<String, Object> context) throws OgnlException {
|
||||
Object tree = toTree(expression);
|
||||
checkEnableEvalExpression(tree, context);
|
||||
return tree;
|
||||
return compileAndExecute(expression, context, tree -> tree);
|
||||
}
|
||||
|
||||
private void checkEnableEvalExpression(Object tree, Map<String, Object> context) throws OgnlException {
|
||||
@@ -628,18 +658,6 @@ public class OgnlUtil {
|
||||
}
|
||||
}
|
||||
|
||||
private void checkEvalExpression(Object tree, Map<String, Object> context) throws OgnlException {
|
||||
if (isEvalExpression(tree, context)) {
|
||||
throw new OgnlException("Eval expression/chained expressions cannot be used as parameter name");
|
||||
}
|
||||
}
|
||||
|
||||
private void checkArithmeticExpression(Object tree, Map<String, Object> context) throws OgnlException {
|
||||
if (isArithmeticExpression(tree, context)) {
|
||||
throw new OgnlException("Arithmetic expressions cannot be used as parameter name");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the properties in the object "from" and sets them in the object "to"
|
||||
* using specified type converter, or {@link com.opensymphony.xwork2.conversion.impl.XWorkConverter} if none
|
||||
@@ -670,15 +688,9 @@ public class OgnlUtil {
|
||||
* note if exclusions AND inclusions are supplied and not null nothing will get copied.
|
||||
* @param editable the class (or interface) to restrict property setting to
|
||||
*/
|
||||
public void copy(final Object from,
|
||||
final Object to,
|
||||
final Map<String, Object> context,
|
||||
Collection<String> exclusions,
|
||||
Collection<String> inclusions,
|
||||
Class<?> editable) {
|
||||
public void copy(final Object from, final Object to, final Map<String, Object> context, Collection<String> exclusions, Collection<String> inclusions, Class<?> editable) {
|
||||
if (from == null || to == null) {
|
||||
LOG.warn(
|
||||
"Skipping attempt to copy from, or to, a null source.", new RuntimeException());
|
||||
LOG.warn("Attempting to copy from or to a null source. This is illegal and is bein skipped. This may be due to an error in an OGNL expression, action chaining, or some other event.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -692,7 +704,8 @@ public class OgnlUtil {
|
||||
fromPds = getPropertyDescriptors(from);
|
||||
if (editable != null) {
|
||||
toPds = getPropertyDescriptors(editable);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
toPds = getPropertyDescriptors(to);
|
||||
}
|
||||
} catch (IntrospectionException e) {
|
||||
@@ -707,31 +720,33 @@ public class OgnlUtil {
|
||||
}
|
||||
|
||||
for (PropertyDescriptor fromPd : fromPds) {
|
||||
if (fromPd.getReadMethod() == null) {
|
||||
continue;
|
||||
if (fromPd.getReadMethod() != null) {
|
||||
boolean copy = true;
|
||||
if (exclusions != null && exclusions.contains(fromPd.getName())) {
|
||||
copy = false;
|
||||
} else if (inclusions != null && !inclusions.contains(fromPd.getName())) {
|
||||
copy = false;
|
||||
}
|
||||
|
||||
if (copy) {
|
||||
PropertyDescriptor toPd = toPdHash.get(fromPd.getName());
|
||||
if ((toPd != null) && (toPd.getWriteMethod() != null)) {
|
||||
try {
|
||||
compileAndExecute(fromPd.getName(), context, expr -> {
|
||||
Object value = Ognl.getValue(expr, contextFrom, from);
|
||||
Ognl.setValue(expr, contextTo, to, value);
|
||||
return null;
|
||||
});
|
||||
|
||||
} catch (OgnlException e) {
|
||||
LOG.debug("Got OGNL exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (exclusions != null && exclusions.contains(fromPd.getName()) ||
|
||||
inclusions != null && !inclusions.contains(fromPd.getName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
PropertyDescriptor toPd = toPdHash.get(fromPd.getName());
|
||||
if (toPd == null || toPd.getWriteMethod() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
Object value = ognlGet(fromPd.getName(),
|
||||
contextFrom,
|
||||
from,
|
||||
null,
|
||||
context,
|
||||
this::checkEnableEvalExpression);
|
||||
ognlSet(fromPd.getName(), contextTo, to, value, context);
|
||||
} catch (OgnlException e) {
|
||||
LOG.debug("Got OGNL exception", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -750,7 +765,7 @@ public class OgnlUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the java beans property descriptors for the given source.
|
||||
* Get's the java beans property descriptors for the given source.
|
||||
*
|
||||
* @param source the source object.
|
||||
* @return property descriptors.
|
||||
@@ -794,7 +809,7 @@ public class OgnlUtil {
|
||||
final String propertyName = propertyDescriptor.getDisplayName();
|
||||
Method readMethod = propertyDescriptor.getReadMethod();
|
||||
if (readMethod != null) {
|
||||
final Object value = ognlGet(propertyName, sourceMap, source, null, null, this::checkEnableEvalExpression);
|
||||
final Object value = compileAndExecute(propertyName, null, expr -> Ognl.getValue(expr, sourceMap, source));
|
||||
beanMap.put(propertyName, value);
|
||||
} else {
|
||||
beanMap.put(propertyName, "There is no read method for " + propertyName);
|
||||
@@ -856,16 +871,14 @@ public class OgnlUtil {
|
||||
return createDefaultContext(root, null);
|
||||
}
|
||||
|
||||
protected Map<String, Object> createDefaultContext(Object root, ClassResolver resolver) {
|
||||
protected Map<String, Object> createDefaultContext(Object root, ClassResolver classResolver) {
|
||||
ClassResolver resolver = classResolver;
|
||||
if (resolver == null) {
|
||||
resolver = container.getInstance(RootAccessor.class);
|
||||
if (resolver == null) {
|
||||
throw new IllegalStateException("Cannot find ClassResolver");
|
||||
}
|
||||
resolver = container.getInstance(CompoundRootAccessor.class);
|
||||
}
|
||||
|
||||
SecurityMemberAccess memberAccess = container.getInstance(SecurityMemberAccess.class);
|
||||
memberAccess.useEnforceAllowlistEnabled(Boolean.FALSE.toString());
|
||||
SecurityMemberAccess memberAccess = new SecurityMemberAccess(allowStaticFieldAccess);
|
||||
memberAccess.disallowProxyMemberAccess(disallowProxyMemberAccess);
|
||||
|
||||
if (devMode) {
|
||||
if (!warnReported.get()) {
|
||||
@@ -876,13 +889,18 @@ public class OgnlUtil {
|
||||
memberAccess.useExcludedPackageNamePatterns(devModeExcludedPackageNamePatterns);
|
||||
memberAccess.useExcludedPackageNames(devModeExcludedPackageNames);
|
||||
memberAccess.useExcludedPackageExemptClasses(devModeExcludedPackageExemptClasses);
|
||||
} else {
|
||||
memberAccess.useExcludedClasses(excludedClasses);
|
||||
memberAccess.useExcludedPackageNamePatterns(excludedPackageNamePatterns);
|
||||
memberAccess.useExcludedPackageNames(excludedPackageNames);
|
||||
memberAccess.useExcludedPackageExemptClasses(excludedPackageExemptClasses);
|
||||
}
|
||||
|
||||
return Ognl.createDefaultContext(root, memberAccess, resolver, defaultConverter);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface TreeValidator {
|
||||
void validate(Object tree, Map<String, Object> context) throws OgnlException;
|
||||
private interface OgnlTask<T> {
|
||||
T execute(Object tree) throws OgnlException;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
|
||||
import com.opensymphony.xwork2.inject.Container;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.RootAccessor;
|
||||
import com.opensymphony.xwork2.util.ClearableValueStack;
|
||||
import com.opensymphony.xwork2.util.CompoundRoot;
|
||||
import com.opensymphony.xwork2.util.MemberAccessValueStack;
|
||||
@@ -35,6 +34,7 @@ import ognl.NoSuchPropertyException;
|
||||
import ognl.Ognl;
|
||||
import ognl.OgnlContext;
|
||||
import ognl.OgnlException;
|
||||
import ognl.PropertyAccessor;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -76,93 +76,35 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
|
||||
private transient XWorkConverter converter;
|
||||
private boolean devMode;
|
||||
private boolean logMissingProperties;
|
||||
private boolean shouldFallbackToContext = true;
|
||||
|
||||
/**
|
||||
* @since 6.4.0
|
||||
*/
|
||||
protected OgnlValueStack(ValueStack vs,
|
||||
XWorkConverter xworkConverter,
|
||||
RootAccessor accessor,
|
||||
TextProvider prov,
|
||||
SecurityMemberAccess securityMemberAccess) {
|
||||
setRoot(xworkConverter,
|
||||
accessor,
|
||||
vs != null ? new CompoundRoot(vs.getRoot()) : new CompoundRoot(),
|
||||
securityMemberAccess);
|
||||
if (prov != null) {
|
||||
push(prov);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 6.4.0
|
||||
*/
|
||||
protected OgnlValueStack(XWorkConverter xworkConverter, RootAccessor accessor, TextProvider prov, SecurityMemberAccess securityMemberAccess) {
|
||||
this(null, xworkConverter, accessor, prov, securityMemberAccess);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 6.4.0
|
||||
*/
|
||||
protected OgnlValueStack(ValueStack vs, XWorkConverter xworkConverter, RootAccessor accessor, SecurityMemberAccess securityMemberAccess) {
|
||||
this(vs, xworkConverter, accessor, null, securityMemberAccess);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 6.4.0, use {@link #OgnlValueStack(ValueStack, XWorkConverter, RootAccessor, TextProvider, SecurityMemberAccess)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
protected OgnlValueStack(ValueStack vs,
|
||||
XWorkConverter xworkConverter,
|
||||
CompoundRootAccessor accessor,
|
||||
TextProvider prov,
|
||||
boolean allowStaticFieldAccess) {
|
||||
this(vs, xworkConverter, accessor, prov, new SecurityMemberAccess(allowStaticFieldAccess));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 6.4.0, use {@link #OgnlValueStack(XWorkConverter, RootAccessor, TextProvider, SecurityMemberAccess)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
protected OgnlValueStack(XWorkConverter xworkConverter, CompoundRootAccessor accessor, TextProvider prov, boolean allowStaticFieldAccess) {
|
||||
this(xworkConverter, accessor, prov, new SecurityMemberAccess(allowStaticFieldAccess));
|
||||
setRoot(xworkConverter, accessor, new CompoundRoot(), allowStaticFieldAccess);
|
||||
push(prov);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 6.4.0, use {@link #OgnlValueStack(ValueStack, XWorkConverter, RootAccessor, SecurityMemberAccess)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
protected OgnlValueStack(ValueStack vs, XWorkConverter xworkConverter, CompoundRootAccessor accessor, boolean allowStaticFieldAccess) {
|
||||
this(vs, xworkConverter, accessor, new SecurityMemberAccess(allowStaticFieldAccess));
|
||||
setRoot(xworkConverter, accessor, new CompoundRoot(vs.getRoot()), allowStaticFieldAccess);
|
||||
}
|
||||
|
||||
@Inject
|
||||
protected void setOgnlUtil(OgnlUtil ognlUtil) {
|
||||
this.ognlUtil = ognlUtil;
|
||||
securityMemberAccess.useExcludedClasses(ognlUtil.getExcludedClasses());
|
||||
securityMemberAccess.useExcludedPackageNamePatterns(ognlUtil.getExcludedPackageNamePatterns());
|
||||
securityMemberAccess.useExcludedPackageNames(ognlUtil.getExcludedPackageNames());
|
||||
securityMemberAccess.useExcludedPackageExemptClasses(ognlUtil.getExcludedPackageExemptClasses());
|
||||
securityMemberAccess.disallowProxyMemberAccess(ognlUtil.isDisallowProxyMemberAccess());
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 6.4.0
|
||||
*/
|
||||
protected void setRoot(XWorkConverter xworkConverter, RootAccessor accessor, CompoundRoot compoundRoot, SecurityMemberAccess securityMemberAccess) {
|
||||
protected void setRoot(XWorkConverter xworkConverter, CompoundRootAccessor accessor, CompoundRoot compoundRoot, boolean allowStaticFieldAccess) {
|
||||
this.root = compoundRoot;
|
||||
this.securityMemberAccess = securityMemberAccess;
|
||||
this.securityMemberAccess = new SecurityMemberAccess(allowStaticFieldAccess);
|
||||
this.context = Ognl.createDefaultContext(this.root, securityMemberAccess, accessor, new OgnlTypeConverterWrapper(xworkConverter));
|
||||
this.converter = xworkConverter;
|
||||
context.put(VALUE_STACK, this);
|
||||
((OgnlContext) context).setTraceEvaluations(false);
|
||||
((OgnlContext) context).setKeepLastEvaluation(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 6.4.0, use {@link #setRoot(XWorkConverter, RootAccessor, CompoundRoot, SecurityMemberAccess)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
protected void setRoot(XWorkConverter xworkConverter, CompoundRootAccessor accessor, CompoundRoot compoundRoot, boolean allowStaticFieldAccess) {
|
||||
setRoot(xworkConverter, accessor, compoundRoot, new SecurityMemberAccess(allowStaticFieldAccess));
|
||||
}
|
||||
|
||||
@Inject(StrutsConstants.STRUTS_DEVMODE)
|
||||
protected void setDevMode(String mode) {
|
||||
this.devMode = BooleanUtils.toBoolean(mode);
|
||||
@@ -173,11 +115,6 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
|
||||
this.logMissingProperties = BooleanUtils.toBoolean(logMissingProperties);
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_VALUE_STACK_FALLBACK_TO_CONTEXT, required = false)
|
||||
protected void setShouldFallbackToContext(String shouldFallbackToContext) {
|
||||
this.shouldFallbackToContext = BooleanUtils.toBoolean(shouldFallbackToContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see com.opensymphony.xwork2.util.ValueStack#getContext()
|
||||
*/
|
||||
@@ -343,16 +280,34 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
|
||||
}
|
||||
|
||||
private Object tryFindValue(String expr) throws OgnlException {
|
||||
return tryFindValue(expr, defaultType);
|
||||
Object value;
|
||||
expr = lookupForOverrides(expr);
|
||||
if (defaultType != null) {
|
||||
value = findValue(expr, defaultType);
|
||||
} else {
|
||||
value = getValueUsingOgnl(expr);
|
||||
if (value == null) {
|
||||
value = findInContext(expr);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private String lookupForOverrides(String expr) {
|
||||
if (overrides != null && overrides.containsKey(expr)) {
|
||||
if ((overrides != null) && overrides.containsKey(expr)) {
|
||||
expr = (String) overrides.get(expr);
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
private Object getValueUsingOgnl(String expr) throws OgnlException {
|
||||
try {
|
||||
return ognlUtil.getValue(expr, context, root);
|
||||
} finally {
|
||||
context.remove(THROW_EXCEPTION_ON_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
public Object findValue(String expr) {
|
||||
return findValue(expr, false);
|
||||
}
|
||||
@@ -407,25 +362,25 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
|
||||
}
|
||||
|
||||
private Object tryFindValue(String expr, Class asType) throws OgnlException {
|
||||
Object value = null;
|
||||
try {
|
||||
expr = lookupForOverrides(expr);
|
||||
Object value = ognlUtil.getValue(expr, context, root, asType);
|
||||
value = getValue(expr, asType);
|
||||
if (value == null) {
|
||||
value = findInContext(expr);
|
||||
if (value != null && asType != null) {
|
||||
value = converter.convertValue(getContext(), value, asType);
|
||||
}
|
||||
return converter.convertValue(getContext(), value, asType);
|
||||
}
|
||||
return value;
|
||||
} finally {
|
||||
context.remove(THROW_EXCEPTION_ON_FAILURE);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private Object getValue(String expr, Class asType) throws OgnlException {
|
||||
return ognlUtil.getValue(expr, context, root, asType);
|
||||
}
|
||||
|
||||
protected Object findInContext(String name) {
|
||||
if (!shouldFallbackToContext) {
|
||||
return null;
|
||||
}
|
||||
return getContext().get(name);
|
||||
}
|
||||
|
||||
@@ -505,42 +460,48 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
|
||||
return root.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retained for serializability - see {@link com.opensymphony.xwork2.ognl.OgnlValueStackTest#testSerializable}
|
||||
*/
|
||||
private Object readResolve() {
|
||||
// TODO: this should be done better
|
||||
ActionContext ac = ActionContext.getContext();
|
||||
Container cont = ac.getContainer();
|
||||
XWorkConverter xworkConverter = cont.getInstance(XWorkConverter.class);
|
||||
RootAccessor accessor = cont.getInstance(RootAccessor.class);
|
||||
CompoundRootAccessor accessor = (CompoundRootAccessor) cont.getInstance(PropertyAccessor.class, CompoundRoot.class.getName());
|
||||
TextProvider prov = cont.getInstance(TextProvider.class, "system");
|
||||
SecurityMemberAccess sma = cont.getInstance(SecurityMemberAccess.class);
|
||||
OgnlValueStack aStack = new OgnlValueStack(xworkConverter, accessor, prov, sma);
|
||||
final boolean allowStaticField = BooleanUtils.toBoolean(cont.getInstance(String.class, StrutsConstants.STRUTS_ALLOW_STATIC_FIELD_ACCESS));
|
||||
OgnlValueStack aStack = new OgnlValueStack(xworkConverter, accessor, prov, allowStaticField);
|
||||
aStack.setOgnlUtil(cont.getInstance(OgnlUtil.class));
|
||||
aStack.setRoot(xworkConverter, accessor, this.root, sma);
|
||||
aStack.setRoot(xworkConverter, accessor, this.root, allowStaticField);
|
||||
|
||||
return aStack;
|
||||
}
|
||||
|
||||
|
||||
public void clearContextValues() {
|
||||
//this is an OGNL ValueStack so the context will be an OgnlContext
|
||||
//it would be better to make context of type OgnlContext
|
||||
((OgnlContext) context).getValues().clear();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setAcceptProperties(Set<Pattern> acceptedProperties) {
|
||||
securityMemberAccess.useAcceptProperties(acceptedProperties);
|
||||
}
|
||||
|
||||
public void useAcceptProperties(Set<Pattern> acceptedProperties) {
|
||||
securityMemberAccess.useAcceptProperties(acceptedProperties);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setExcludeProperties(Set<Pattern> excludeProperties) {
|
||||
securityMemberAccess.useExcludeProperties(excludeProperties);
|
||||
}
|
||||
|
||||
public void useExcludeProperties(Set<Pattern> excludeProperties) {
|
||||
securityMemberAccess.useExcludeProperties(excludeProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 6.4.0, no replacement.
|
||||
*/
|
||||
@Deprecated
|
||||
@Inject
|
||||
protected void setXWorkConverter(final XWorkConverter converter) {
|
||||
// no-op
|
||||
this.converter = converter;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import com.opensymphony.xwork2.conversion.NullHandler;
|
||||
import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
|
||||
import com.opensymphony.xwork2.inject.Container;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.ognl.accessor.RootAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor;
|
||||
import com.opensymphony.xwork2.util.CompoundRoot;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import com.opensymphony.xwork2.util.ValueStackFactory;
|
||||
@@ -35,6 +35,7 @@ import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -42,10 +43,8 @@ import java.util.Set;
|
||||
*/
|
||||
public class OgnlValueStackFactory implements ValueStackFactory {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(OgnlValueStackFactory.class);
|
||||
|
||||
protected XWorkConverter xworkConverter;
|
||||
protected RootAccessor compoundRootAccessor;
|
||||
protected CompoundRootAccessor compoundRootAccessor;
|
||||
protected TextProvider textProvider;
|
||||
protected Container container;
|
||||
|
||||
@@ -54,117 +53,62 @@ public class OgnlValueStackFactory implements ValueStackFactory {
|
||||
this.xworkConverter = converter;
|
||||
}
|
||||
|
||||
@Inject
|
||||
protected void setCompoundRootAccessor(RootAccessor compoundRootAccessor) {
|
||||
this.compoundRootAccessor = compoundRootAccessor;
|
||||
OgnlRuntime.setPropertyAccessor(CompoundRoot.class, compoundRootAccessor);
|
||||
OgnlRuntime.setMethodAccessor(CompoundRoot.class, compoundRootAccessor);
|
||||
}
|
||||
|
||||
@Inject
|
||||
protected void setMethodAccessor(MethodAccessor methodAccessor) {
|
||||
OgnlRuntime.setMethodAccessor(Object.class, methodAccessor);
|
||||
}
|
||||
|
||||
@Inject("system")
|
||||
protected void setTextProvider(TextProvider textProvider) {
|
||||
this.textProvider = textProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueStack createValueStack() {
|
||||
return createValueStack(null, true);
|
||||
ValueStack stack = new OgnlValueStack(xworkConverter, compoundRootAccessor, textProvider, containerAllowsStaticFieldAccess());
|
||||
container.inject(stack);
|
||||
return stack.getActionContext()
|
||||
.withContainer(container)
|
||||
.withValueStack(stack)
|
||||
.getValueStack();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueStack createValueStack(ValueStack stack) {
|
||||
return createValueStack(stack, false);
|
||||
ValueStack result = new OgnlValueStack(stack, xworkConverter, compoundRootAccessor, containerAllowsStaticFieldAccess());
|
||||
container.inject(result);
|
||||
return result.getActionContext()
|
||||
.withContainer(container)
|
||||
.withValueStack(result)
|
||||
.getValueStack();
|
||||
}
|
||||
|
||||
protected ValueStack createValueStack(ValueStack stack, boolean useTextProvider) {
|
||||
ValueStack newStack = new OgnlValueStack(
|
||||
stack, xworkConverter, compoundRootAccessor, useTextProvider ? textProvider : null, container.getInstance(SecurityMemberAccess.class));
|
||||
container.inject(newStack);
|
||||
return newStack.getActionContext().withContainer(container).withValueStack(newStack).getValueStack();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link PropertyAccessor}'s, {@link MethodAccessor}'s and {@link NullHandler}'s are registered on a per-class
|
||||
* basis by defining a bean adhering to the corresponding interface with a name corresponding to the class it is
|
||||
* intended to handle.
|
||||
* <p>
|
||||
* The only exception is the {@link MethodAccessor} for the {@link Object} type which has its own extension point.
|
||||
*
|
||||
* @see #setMethodAccessor(MethodAccessor)
|
||||
* @see #registerAdditionalMethodAccessors()
|
||||
*/
|
||||
@Inject
|
||||
protected void setContainer(Container container) throws ClassNotFoundException {
|
||||
this.container = container;
|
||||
registerPropertyAccessors();
|
||||
registerNullHandlers();
|
||||
registerAdditionalMethodAccessors();
|
||||
}
|
||||
|
||||
/**
|
||||
* Note that the default {@link MethodAccessor} for handling {@link Object} methods is registered in
|
||||
* {@link #setMethodAccessor} and can be configured using the extension point
|
||||
* {@link StrutsConstants#STRUTS_METHOD_ACCESSOR}.
|
||||
*/
|
||||
protected void registerAdditionalMethodAccessors() {
|
||||
Set<String> names = container.getInstanceNames(MethodAccessor.class);
|
||||
for (String name : names) {
|
||||
Class<?> cls;
|
||||
try {
|
||||
cls = Class.forName(name);
|
||||
if (cls.equals(Object.class)) {
|
||||
// The Object method accessor can only be configured using the struts.methodAccessor extension point
|
||||
continue;
|
||||
}
|
||||
if (cls.equals(CompoundRoot.class)) {
|
||||
// TODO: This bean is deprecated, please remove this if statement when removing the struts-beans.xml entry
|
||||
continue;
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
// Since this interface is also used as an extension point for the Object MethodAccessor, we expect
|
||||
// there to be beans with names that don't correspond to classes. We can safely ignore these.
|
||||
continue;
|
||||
}
|
||||
MethodAccessor methodAccessor = container.getInstance(MethodAccessor.class, name);
|
||||
OgnlRuntime.setMethodAccessor(cls, methodAccessor);
|
||||
LOG.debug("Registered custom OGNL MethodAccessor [{}] for class [{}]", methodAccessor.getClass().getName(), cls.getName());
|
||||
}
|
||||
}
|
||||
|
||||
protected void registerNullHandlers() throws ClassNotFoundException {
|
||||
Set<String> names = container.getInstanceNames(NullHandler.class);
|
||||
for (String name : names) {
|
||||
Class<?> cls = Class.forName(name);
|
||||
NullHandler nullHandler = container.getInstance(NullHandler.class, name);
|
||||
OgnlRuntime.setNullHandler(cls, new OgnlNullHandlerWrapper(nullHandler));
|
||||
LOG.debug("Registered custom OGNL NullHandler [{}] for class [{}]", nullHandler.getClass().getName(), cls.getName());
|
||||
}
|
||||
}
|
||||
|
||||
protected void registerPropertyAccessors() throws ClassNotFoundException {
|
||||
Set<String> names = container.getInstanceNames(PropertyAccessor.class);
|
||||
for (String name : names) {
|
||||
Class<?> cls = Class.forName(name);
|
||||
if (cls.equals(CompoundRoot.class)) {
|
||||
// TODO: This bean is deprecated, please remove this if statement when removing the struts-beans.xml entry
|
||||
continue;
|
||||
OgnlRuntime.setPropertyAccessor(cls, container.getInstance(PropertyAccessor.class, name));
|
||||
if (compoundRootAccessor == null && CompoundRoot.class.isAssignableFrom(cls)) {
|
||||
compoundRootAccessor = (CompoundRootAccessor) container.getInstance(PropertyAccessor.class, name);
|
||||
}
|
||||
PropertyAccessor propertyAccessor = container.getInstance(PropertyAccessor.class, name);
|
||||
OgnlRuntime.setPropertyAccessor(cls, propertyAccessor);
|
||||
LOG.debug("Registered custom OGNL PropertyAccessor [{}] for class [{}]", propertyAccessor.getClass().getName(), cls.getName());
|
||||
}
|
||||
|
||||
names = container.getInstanceNames(MethodAccessor.class);
|
||||
for (String name : names) {
|
||||
Class<?> cls = Class.forName(name);
|
||||
OgnlRuntime.setMethodAccessor(cls, container.getInstance(MethodAccessor.class, name));
|
||||
}
|
||||
|
||||
names = container.getInstanceNames(NullHandler.class);
|
||||
for (String name : names) {
|
||||
Class<?> cls = Class.forName(name);
|
||||
OgnlRuntime.setNullHandler(cls, new OgnlNullHandlerWrapper(container.getInstance(NullHandler.class, name)));
|
||||
}
|
||||
if (compoundRootAccessor == null) {
|
||||
throw new IllegalStateException("Couldn't find the compound root accessor");
|
||||
}
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 6.4.0, no replacement.
|
||||
* Retrieve allowStaticFieldAccess state from the container (allows for lazy fetching)
|
||||
*/
|
||||
@Deprecated
|
||||
protected boolean containerAllowsStaticFieldAccess() {
|
||||
return BooleanUtils.toBoolean(container.getInstance(String.class, StrutsConstants.STRUTS_ALLOW_STATIC_FIELD_ACCESS));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,19 +18,14 @@
|
||||
*/
|
||||
package com.opensymphony.xwork2.ognl;
|
||||
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.ProxyUtil;
|
||||
import ognl.MemberAccess;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
import org.apache.struts2.ognl.ProviderAllowlist;
|
||||
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
@@ -40,15 +35,7 @@ import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.opensymphony.xwork2.util.ConfigParseUtil.toClassObjectsSet;
|
||||
import static com.opensymphony.xwork2.util.ConfigParseUtil.toClassesSet;
|
||||
import static com.opensymphony.xwork2.util.ConfigParseUtil.toNewClassesSet;
|
||||
import static com.opensymphony.xwork2.util.ConfigParseUtil.toNewPackageNamesSet;
|
||||
import static com.opensymphony.xwork2.util.ConfigParseUtil.toNewPatternsSet;
|
||||
import static com.opensymphony.xwork2.util.ConfigParseUtil.toPackageNamesSet;
|
||||
import static java.text.MessageFormat.format;
|
||||
import static java.util.Collections.emptySet;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static java.util.Collections.unmodifiableSet;
|
||||
|
||||
/**
|
||||
@@ -59,39 +46,14 @@ public class SecurityMemberAccess implements MemberAccess {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(SecurityMemberAccess.class);
|
||||
|
||||
private static final Set<String> ALLOWLIST_REQUIRED_PACKAGES = unmodifiableSet(new HashSet<>(Arrays.asList(
|
||||
"com.opensymphony.xwork2.validator.validators",
|
||||
"org.apache.struts2.components",
|
||||
"org.apache.struts2.views.jsp"
|
||||
)));
|
||||
|
||||
private static final Set<Class<?>> ALLOWLIST_REQUIRED_CLASSES = unmodifiableSet(new HashSet<>(Arrays.asList(
|
||||
java.lang.Enum.class,
|
||||
java.lang.String.class,
|
||||
java.util.Date.class,
|
||||
java.util.HashMap.class,
|
||||
java.util.Map.class,
|
||||
java.util.Map.Entry.class
|
||||
)));
|
||||
|
||||
private final ProviderAllowlist providerAllowlist;
|
||||
private boolean allowStaticFieldAccess = true;
|
||||
private final boolean allowStaticFieldAccess;
|
||||
private Set<Pattern> excludeProperties = emptySet();
|
||||
private Set<Pattern> acceptProperties = emptySet();
|
||||
private Set<String> excludedClasses = unmodifiableSet(new HashSet<>(singletonList(Object.class.getName())));
|
||||
private Set<Class<?>> excludedClasses = emptySet();
|
||||
private Set<Pattern> excludedPackageNamePatterns = emptySet();
|
||||
private Set<String> excludedPackageNames = emptySet();
|
||||
private Set<String> excludedPackageExemptClasses = emptySet();
|
||||
private boolean enforceAllowlistEnabled = false;
|
||||
private Set<Class<?>> allowlistClasses = emptySet();
|
||||
private Set<String> allowlistPackageNames = emptySet();
|
||||
private boolean disallowProxyMemberAccess = false;
|
||||
private boolean disallowDefaultPackageAccess = false;
|
||||
|
||||
@Inject
|
||||
public SecurityMemberAccess(@Inject ProviderAllowlist providerAllowlist) {
|
||||
this.providerAllowlist = providerAllowlist;
|
||||
}
|
||||
private Set<Class<?>> excludedPackageExemptClasses = emptySet();
|
||||
private boolean disallowProxyMemberAccess;
|
||||
|
||||
/**
|
||||
* SecurityMemberAccess
|
||||
@@ -99,12 +61,10 @@ public class SecurityMemberAccess implements MemberAccess {
|
||||
* - block or allow access to properties (configurable-after-construction)
|
||||
*
|
||||
* @param allowStaticFieldAccess if set to true static fields (constants) will be accessible
|
||||
* @deprecated since 6.4.0, use {@link #SecurityMemberAccess(ProviderAllowlist)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public SecurityMemberAccess(boolean allowStaticFieldAccess) {
|
||||
this(null);
|
||||
useAllowStaticFieldAccess(String.valueOf(allowStaticFieldAccess));
|
||||
this.allowStaticFieldAccess = allowStaticFieldAccess;
|
||||
useExcludedClasses(excludedClasses); // Initialise default exclusions
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -124,187 +84,97 @@ public class SecurityMemberAccess implements MemberAccess {
|
||||
|
||||
@Override
|
||||
public void restore(Map context, Object target, Member member, String propertyName, Object state) {
|
||||
if (state == null) {
|
||||
return;
|
||||
if (state != null) {
|
||||
final AccessibleObject accessible = (AccessibleObject) member;
|
||||
final boolean stateBoolean = ((Boolean) state).booleanValue(); // Using twice (avoid unboxing)
|
||||
if (!stateBoolean) {
|
||||
accessible.setAccessible(stateBoolean);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Improper restore state [" + stateBoolean + "] for target [" + target +
|
||||
"], member [" + member + "], propertyName [" + propertyName + "]");
|
||||
}
|
||||
}
|
||||
if ((Boolean) state) {
|
||||
throw new IllegalArgumentException(format(
|
||||
"Improper restore state [true] for target [{0}], member [{1}], propertyName [{2}]",
|
||||
target,
|
||||
member,
|
||||
propertyName));
|
||||
}
|
||||
((AccessibleObject) member).setAccessible(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccessible(Map context, Object target, Member member, String propertyName) {
|
||||
LOG.debug("Checking access for [target: {}, member: {}, property: {}]", target, member, propertyName);
|
||||
|
||||
if (target != null) {
|
||||
// Special case: Target is a Class object but not Class.class
|
||||
if (Class.class.equals(target.getClass()) && !Class.class.equals(target)) {
|
||||
if (!isStatic(member)) {
|
||||
throw new IllegalArgumentException("Member expected to be static!");
|
||||
}
|
||||
if (!member.getDeclaringClass().equals(target)) {
|
||||
throw new IllegalArgumentException("Target class does not match static member!");
|
||||
}
|
||||
target = null; // This information is not useful to us and conflicts with following logic which expects target to be null or an instance containing the member
|
||||
// Standard case: Member should exist on target
|
||||
} else if (!member.getDeclaringClass().isAssignableFrom(target.getClass())) {
|
||||
throw new IllegalArgumentException("Member does not exist on target!");
|
||||
}
|
||||
final int memberModifiers = member.getModifiers();
|
||||
final Class<?> memberClass = member.getDeclaringClass();
|
||||
// target can be null in case of accessing static fields, since OGNL 3.2.8
|
||||
final Class<?> targetClass = Modifier.isStatic(memberModifiers) ? memberClass : target.getClass();
|
||||
if (!memberClass.isAssignableFrom(targetClass)) {
|
||||
throw new IllegalArgumentException("Target does not match member!");
|
||||
}
|
||||
|
||||
if (!checkProxyMemberAccess(target, member)) {
|
||||
LOG.warn("Access to proxy is blocked! Member class [{}] of target [{}], member [{}]", member.getDeclaringClass(), target, member);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!checkPublicMemberAccess(member)) {
|
||||
if (!checkPublicMemberAccess(memberModifiers)) {
|
||||
LOG.warn("Access to non-public [{}] is blocked!", member);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!checkStaticFieldAccess(member)) {
|
||||
if (!checkStaticFieldAccess(member, memberModifiers)) {
|
||||
LOG.warn("Access to static field [{}] is blocked!", member);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!checkStaticMethodAccess(member)) {
|
||||
// it needs to be before calling #checkStaticMethodAccess()
|
||||
if (checkEnumAccess(target, member)) {
|
||||
LOG.trace("Allowing access to enum: target [{}], member [{}]", target, member);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!checkStaticMethodAccess(member, memberModifiers)) {
|
||||
LOG.warn("Access to static method [{}] is blocked!", member);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!checkDefaultPackageAccess(target, member)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!checkExclusionList(target, member)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!checkAllowlist(target, member)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isAcceptableProperty(propertyName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code true} if member access is allowed
|
||||
*/
|
||||
protected boolean checkAllowlist(Object target, Member member) {
|
||||
Class<?> memberClass = member.getDeclaringClass();
|
||||
if (!enforceAllowlistEnabled) {
|
||||
return true;
|
||||
}
|
||||
if (!isClassAllowlisted(memberClass)) {
|
||||
LOG.warn(format("Declaring class [{0}] of member type [{1}] is not allowlisted!", memberClass, member));
|
||||
return false;
|
||||
}
|
||||
if (target == null || target.getClass() == memberClass) {
|
||||
return true;
|
||||
}
|
||||
Class<?> targetClass = target.getClass();
|
||||
if (!isClassAllowlisted(targetClass)) {
|
||||
LOG.warn(format("Target class [{0}] of target [{1}] is not allowlisted!", targetClass, target));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean isClassAllowlisted(Class<?> clazz) {
|
||||
return allowlistClasses.contains(clazz)
|
||||
|| ALLOWLIST_REQUIRED_CLASSES.contains(clazz)
|
||||
|| (providerAllowlist != null && providerAllowlist.getProviderAllowlist().contains(clazz))
|
||||
|| isClassBelongsToPackages(clazz, ALLOWLIST_REQUIRED_PACKAGES)
|
||||
|| isClassBelongsToPackages(clazz, allowlistPackageNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code true} if member access is allowed
|
||||
*/
|
||||
protected boolean checkExclusionList(Object target, Member member) {
|
||||
Class<?> memberClass = member.getDeclaringClass();
|
||||
if (isClassExcluded(memberClass)) {
|
||||
LOG.warn("Declaring class of member type [{}] is excluded!", memberClass);
|
||||
LOG.warn("Declaring class of member type [{}] is excluded!", member);
|
||||
return false;
|
||||
}
|
||||
if (isPackageExcluded(memberClass)) {
|
||||
LOG.warn("Package [{}] of member class [{}] of member [{}] is excluded!",
|
||||
memberClass.getPackage(),
|
||||
memberClass,
|
||||
target);
|
||||
return false;
|
||||
}
|
||||
if (target == null || target.getClass() == memberClass) {
|
||||
return true;
|
||||
}
|
||||
Class<?> targetClass = target.getClass();
|
||||
if (isClassExcluded(targetClass)) {
|
||||
|
||||
if (targetClass != memberClass && isClassExcluded(targetClass)) {
|
||||
// Optimization: Already checked memberClass exclusion, so if-and-only-if targetClass == memberClass, this check is redundant.
|
||||
LOG.warn("Target class [{}] of target [{}] is excluded!", targetClass, target);
|
||||
return false;
|
||||
}
|
||||
if (isPackageExcluded(targetClass)) {
|
||||
LOG.warn("Package [{}] of target [{}] is excluded!", targetClass.getPackage(), member);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code true} if member access is allowed
|
||||
*/
|
||||
protected boolean checkDefaultPackageAccess(Object target, Member member) {
|
||||
if (!disallowDefaultPackageAccess) {
|
||||
return true;
|
||||
if (targetClass.getPackage() == null || memberClass.getPackage() == null) {
|
||||
LOG.warn("The use of the default (unnamed) package is discouraged!");
|
||||
}
|
||||
Class<?> memberClass = member.getDeclaringClass();
|
||||
if (memberClass.getPackage() == null || memberClass.getPackage().getName().isEmpty()) {
|
||||
LOG.warn("Class [{}] from the default package is excluded!", memberClass);
|
||||
return false;
|
||||
}
|
||||
if (target == null || target.getClass() == memberClass) {
|
||||
return true;
|
||||
}
|
||||
Class<?> targetClass = target.getClass();
|
||||
if (targetClass.getPackage() == null || targetClass.getPackage().getName().isEmpty()) {
|
||||
LOG.warn("Class [{}] from the default package is excluded!", targetClass);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code true} if member access is allowed
|
||||
*/
|
||||
protected boolean checkProxyMemberAccess(Object target, Member member) {
|
||||
return !(disallowProxyMemberAccess && ProxyUtil.isProxyMember(member, target));
|
||||
if (isPackageExcluded(targetClass, memberClass)) {
|
||||
LOG.warn(
|
||||
"Package [{}] of target class [{}] of target [{}] or package [{}] of member [{}] are excluded!",
|
||||
targetClass.getPackage(),
|
||||
targetClass,
|
||||
target,
|
||||
memberClass.getPackage(),
|
||||
member);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (disallowProxyMemberAccess && ProxyUtil.isProxyMember(member, target)) {
|
||||
LOG.warn("Access to proxy is blocked! Target class [{}] of target [{}], member [{}]", targetClass, target, member);
|
||||
return false;
|
||||
}
|
||||
|
||||
return isAcceptableProperty(propertyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check access for static method (via modifiers).
|
||||
* <p>
|
||||
*
|
||||
* Note: For non-static members, the result is always true.
|
||||
*
|
||||
* @return {@code true} if member access is allowed
|
||||
* @param member
|
||||
* @param memberModifiers
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected boolean checkStaticMethodAccess(Member member) {
|
||||
if (checkEnumAccess(member)) {
|
||||
LOG.trace("Exempting Enum#values from static method check: class [{}]", member.getDeclaringClass());
|
||||
return true;
|
||||
}
|
||||
return member instanceof Field || !isStatic(member);
|
||||
}
|
||||
|
||||
private static boolean isStatic(Member member) {
|
||||
return Modifier.isStatic(member.getModifiers());
|
||||
protected boolean checkStaticMethodAccess(Member member, int memberModifiers) {
|
||||
return !Modifier.isStatic(memberModifiers) || member instanceof Field;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -312,59 +182,75 @@ public class SecurityMemberAccess implements MemberAccess {
|
||||
* <p>
|
||||
* Note: For non-static members, the result is always true.
|
||||
*
|
||||
* @return {@code true} if member access is allowed
|
||||
* @param member
|
||||
* @param memberModifiers
|
||||
* @return
|
||||
*/
|
||||
protected boolean checkStaticFieldAccess(Member member) {
|
||||
if (allowStaticFieldAccess) {
|
||||
protected boolean checkStaticFieldAccess(Member member, int memberModifiers) {
|
||||
if (Modifier.isStatic(memberModifiers) && member instanceof Field) {
|
||||
return allowStaticFieldAccess;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return !(member instanceof Field) || !isStatic(member);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check access for public members (via modifiers)
|
||||
* <p>
|
||||
* Returns true if-and-only-if the member is public.
|
||||
*
|
||||
* @return {@code true} if member access is allowed
|
||||
* @param memberModifiers
|
||||
* @return
|
||||
*/
|
||||
protected boolean checkPublicMemberAccess(Member member) {
|
||||
return Modifier.isPublic(member.getModifiers());
|
||||
protected boolean checkPublicMemberAccess(int memberModifiers) {
|
||||
return Modifier.isPublic(memberModifiers);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code true} if member access is allowed
|
||||
*/
|
||||
protected boolean checkEnumAccess(Member member) {
|
||||
return member.getDeclaringClass().isEnum()
|
||||
&& isStatic(member)
|
||||
&& member instanceof Method
|
||||
&& member.getName().equals("values")
|
||||
&& ((Method) member).getParameterCount() == 0;
|
||||
protected boolean checkEnumAccess(Object target, Member member) {
|
||||
if (target instanceof Class) {
|
||||
final Class<?> clazz = (Class<?>) target;
|
||||
return Enum.class.isAssignableFrom(clazz) && member.getName().equals("values");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean isPackageExcluded(Class<?> clazz) {
|
||||
return !excludedPackageExemptClasses.contains(clazz.getName()) && (isExcludedPackageNames(clazz) || isExcludedPackageNamePatterns(clazz));
|
||||
protected boolean isPackageExcluded(Class<?> targetClass, Class<?> memberClass) {
|
||||
if (targetClass == null || memberClass == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Parameters should never be null - if member is static, targetClass should be the same as memberClass.");
|
||||
}
|
||||
|
||||
Set<Class<?>> classesToCheck = new HashSet<>();
|
||||
classesToCheck.add(targetClass);
|
||||
classesToCheck.add(memberClass);
|
||||
|
||||
for (Class<?> clazz : classesToCheck) {
|
||||
if (!isExcludedPackageExempt(clazz) && (isExcludedPackageNamePatterns(clazz) || isExcludedPackageNames(clazz))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static String toPackageName(Class<?> clazz) {
|
||||
protected String toPackageName(Class<?> clazz) {
|
||||
if (clazz.getPackage() == null) {
|
||||
return "";
|
||||
} else {
|
||||
return clazz.getPackage().getName();
|
||||
}
|
||||
return clazz.getPackage().getName();
|
||||
}
|
||||
|
||||
protected boolean isExcludedPackageNamePatterns(Class<?> clazz) {
|
||||
return excludedPackageNamePatterns.stream().anyMatch(pattern -> pattern.matcher(toPackageName(clazz)).matches());
|
||||
String packageName = toPackageName(clazz);
|
||||
return excludedPackageNamePatterns.stream().anyMatch(pattern -> pattern.matcher(packageName).matches());
|
||||
}
|
||||
|
||||
protected boolean isExcludedPackageNames(Class<?> clazz) {
|
||||
return isClassBelongsToPackages(clazz, excludedPackageNames);
|
||||
}
|
||||
|
||||
public static boolean isClassBelongsToPackages(Class<?> clazz, Set<String> matchingPackages) {
|
||||
List<String> packageParts = Arrays.asList(toPackageName(clazz).split("\\."));
|
||||
String packageName = toPackageName(clazz);
|
||||
List<String> packageParts = Arrays.asList(packageName.split("\\."));
|
||||
for (int i = 0; i < packageParts.size(); i++) {
|
||||
String parentPackage = String.join(".", packageParts.subList(0, i + 1));
|
||||
if (matchingPackages.contains(parentPackage)) {
|
||||
if (excludedPackageNames.contains(parentPackage)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -372,85 +258,132 @@ public class SecurityMemberAccess implements MemberAccess {
|
||||
}
|
||||
|
||||
protected boolean isClassExcluded(Class<?> clazz) {
|
||||
return excludedClasses.contains(clazz.getName());
|
||||
return excludedClasses.contains(clazz);
|
||||
}
|
||||
|
||||
protected boolean isExcludedPackageExempt(Class<?> clazz) {
|
||||
return excludedPackageExemptClasses.contains(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code true} if member access is allowed
|
||||
*/
|
||||
protected boolean isAcceptableProperty(String name) {
|
||||
return name == null || !isExcluded(name) && isAccepted(name);
|
||||
return name == null || ((!isExcluded(name)) && isAccepted(name));
|
||||
}
|
||||
|
||||
protected boolean isAccepted(String paramName) {
|
||||
if (acceptProperties.isEmpty()) {
|
||||
return true;
|
||||
if (!this.acceptProperties.isEmpty()) {
|
||||
for (Pattern pattern : acceptProperties) {
|
||||
Matcher matcher = pattern.matcher(paramName);
|
||||
if (matcher.matches()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//no match, but acceptedParams is not empty
|
||||
return false;
|
||||
}
|
||||
return acceptProperties.stream().map(pattern -> pattern.matcher(paramName)).anyMatch(Matcher::matches);
|
||||
|
||||
//empty acceptedParams
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean isExcluded(String paramName) {
|
||||
return excludeProperties.stream().map(pattern -> pattern.matcher(paramName)).anyMatch(Matcher::matches);
|
||||
if (!this.excludeProperties.isEmpty()) {
|
||||
for (Pattern pattern : excludeProperties) {
|
||||
Matcher matcher = pattern.matcher(paramName);
|
||||
if (matcher.matches()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated please use {@link #useExcludeProperties(Set)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void setExcludeProperties(Set<Pattern> excludeProperties) {
|
||||
this.excludeProperties = excludeProperties;
|
||||
}
|
||||
|
||||
public void useExcludeProperties(Set<Pattern> excludeProperties) {
|
||||
this.excludeProperties = excludeProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated please use {@link #useAcceptProperties(Set)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void setAcceptProperties(Set<Pattern> acceptedProperties) {
|
||||
this.acceptProperties = acceptedProperties;
|
||||
}
|
||||
|
||||
public void useAcceptProperties(Set<Pattern> acceptedProperties) {
|
||||
this.acceptProperties = acceptedProperties;
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_ALLOW_STATIC_FIELD_ACCESS, required = false)
|
||||
public void useAllowStaticFieldAccess(String allowStaticFieldAccess) {
|
||||
this.allowStaticFieldAccess = BooleanUtils.toBoolean(allowStaticFieldAccess);
|
||||
if (!this.allowStaticFieldAccess) {
|
||||
useExcludedClasses(Class.class.getName());
|
||||
/**
|
||||
* @deprecated please use {@link #useExcludedClasses(Set)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void setExcludedClasses(Set<Class<?>> excludedClasses) {
|
||||
useExcludedClasses(excludedClasses);
|
||||
}
|
||||
|
||||
public void useExcludedClasses(Set<Class<?>> excludedClasses) {
|
||||
Set<Class<?>> newExcludedClasses = new HashSet<>(excludedClasses);
|
||||
newExcludedClasses.add(Object.class);
|
||||
if (!allowStaticFieldAccess) {
|
||||
newExcludedClasses.add(Class.class);
|
||||
}
|
||||
this.excludedClasses = unmodifiableSet(newExcludedClasses);
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_EXCLUDED_CLASSES, required = false)
|
||||
public void useExcludedClasses(String commaDelimitedClasses) {
|
||||
this.excludedClasses = toNewClassesSet(excludedClasses, commaDelimitedClasses);
|
||||
/**
|
||||
* @deprecated please use {@link #useExcludedPackageNamePatterns(Set)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void setExcludedPackageNamePatterns(Set<Pattern> excludedPackageNamePatterns) {
|
||||
this.excludedPackageNamePatterns = excludedPackageNamePatterns;
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_EXCLUDED_PACKAGE_NAME_PATTERNS, required = false)
|
||||
public void useExcludedPackageNamePatterns(String commaDelimitedPackagePatterns) {
|
||||
this.excludedPackageNamePatterns = toNewPatternsSet(excludedPackageNamePatterns, commaDelimitedPackagePatterns);
|
||||
public void useExcludedPackageNamePatterns(Set<Pattern> excludedPackageNamePatterns) {
|
||||
this.excludedPackageNamePatterns = excludedPackageNamePatterns;
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_EXCLUDED_PACKAGE_NAMES, required = false)
|
||||
public void useExcludedPackageNames(String commaDelimitedPackageNames) {
|
||||
this.excludedPackageNames = toNewPackageNamesSet(excludedPackageNames, commaDelimitedPackageNames);
|
||||
/**
|
||||
* @deprecated please use {@link #useExcludedPackageNames(Set)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void setExcludedPackageNames(Set<String> excludedPackageNames) {
|
||||
this.excludedPackageNames = excludedPackageNames;
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_EXCLUDED_PACKAGE_EXEMPT_CLASSES, required = false)
|
||||
public void useExcludedPackageExemptClasses(String commaDelimitedClasses) {
|
||||
this.excludedPackageExemptClasses = toClassesSet(commaDelimitedClasses);
|
||||
public void useExcludedPackageNames(Set<String> excludedPackageNames) {
|
||||
this.excludedPackageNames = excludedPackageNames;
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_ALLOWLIST_ENABLE, required = false)
|
||||
public void useEnforceAllowlistEnabled(String enforceAllowlistEnabled) {
|
||||
this.enforceAllowlistEnabled = BooleanUtils.toBoolean(enforceAllowlistEnabled);
|
||||
/**
|
||||
* @deprecated please use {@link #useExcludedPackageExemptClasses(Set)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void setExcludedPackageExemptClasses(Set<Class<?>> excludedPackageExemptClasses) {
|
||||
this.excludedPackageExemptClasses = excludedPackageExemptClasses;
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_ALLOWLIST_CLASSES, required = false)
|
||||
public void useAllowlistClasses(String commaDelimitedClasses) {
|
||||
this.allowlistClasses = toClassObjectsSet(commaDelimitedClasses);
|
||||
public void useExcludedPackageExemptClasses(Set<Class<?>> excludedPackageExemptClasses) {
|
||||
this.excludedPackageExemptClasses = excludedPackageExemptClasses;
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_ALLOWLIST_PACKAGE_NAMES, required = false)
|
||||
public void useAllowlistPackageNames(String commaDelimitedPackageNames) {
|
||||
this.allowlistPackageNames = toPackageNamesSet(commaDelimitedPackageNames);
|
||||
/**
|
||||
* @deprecated please use {@link #disallowProxyMemberAccess(boolean)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void setDisallowProxyMemberAccess(boolean disallowProxyMemberAccess) {
|
||||
this.disallowProxyMemberAccess = disallowProxyMemberAccess;
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_DISALLOW_PROXY_MEMBER_ACCESS, required = false)
|
||||
public void useDisallowProxyMemberAccess(String disallowProxyMemberAccess) {
|
||||
this.disallowProxyMemberAccess = BooleanUtils.toBoolean(disallowProxyMemberAccess);
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_DISALLOW_DEFAULT_PACKAGE_ACCESS, required = false)
|
||||
public void useDisallowDefaultPackageAccess(String disallowDefaultPackageAccess) {
|
||||
this.disallowDefaultPackageAccess = BooleanUtils.toBoolean(disallowDefaultPackageAccess);
|
||||
public void disallowProxyMemberAccess(boolean disallowProxyMemberAccess) {
|
||||
this.disallowProxyMemberAccess = disallowProxyMemberAccess;
|
||||
}
|
||||
}
|
||||
|
||||
+17
-49
@@ -22,12 +22,7 @@ import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.ognl.OgnlValueStack;
|
||||
import com.opensymphony.xwork2.util.CompoundRoot;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import ognl.MethodFailedException;
|
||||
import ognl.NoSuchPropertyException;
|
||||
import ognl.Ognl;
|
||||
import ognl.OgnlContext;
|
||||
import ognl.OgnlException;
|
||||
import ognl.OgnlRuntime;
|
||||
import ognl.*;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -36,11 +31,7 @@ import org.apache.struts2.StrutsException;
|
||||
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import static java.lang.String.format;
|
||||
@@ -53,12 +44,11 @@ import static org.apache.commons.lang3.BooleanUtils.toBoolean;
|
||||
* @author Rainer Hermanns
|
||||
* @version $Revision$
|
||||
*/
|
||||
public class CompoundRootAccessor implements RootAccessor {
|
||||
public class CompoundRootAccessor implements PropertyAccessor, MethodAccessor, ClassResolver {
|
||||
|
||||
/**
|
||||
* Used by OGNl to generate bytecode
|
||||
*/
|
||||
@Override
|
||||
public String getSourceAccessor(OgnlContext context, Object target, Object index) {
|
||||
return null;
|
||||
}
|
||||
@@ -66,28 +56,20 @@ public class CompoundRootAccessor implements RootAccessor {
|
||||
/**
|
||||
* Used by OGNl to generate bytecode
|
||||
*/
|
||||
@Override
|
||||
public String getSourceSetter(OgnlContext context, Object target, Object index) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private final static Logger LOG = LogManager.getLogger(CompoundRootAccessor.class);
|
||||
private final static Class[] EMPTY_CLASS_ARRAY = new Class[0];
|
||||
private static final Map<MethodCall, Boolean> invalidMethods = new ConcurrentHashMap<>();
|
||||
private static Map<MethodCall, Boolean> invalidMethods = new ConcurrentHashMap<>();
|
||||
private boolean devMode;
|
||||
private boolean disallowCustomOgnlMap;
|
||||
|
||||
@Inject(StrutsConstants.STRUTS_DEVMODE)
|
||||
protected void setDevMode(String mode) {
|
||||
this.devMode = BooleanUtils.toBoolean(mode);
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_DISALLOW_CUSTOM_OGNL_MAP, required = false)
|
||||
public void useDisallowCustomOgnlMap(String disallowCustomOgnlMap) {
|
||||
this.disallowCustomOgnlMap = BooleanUtils.toBoolean(disallowCustomOgnlMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProperty(Map context, Object target, Object name, Object value) throws OgnlException {
|
||||
CompoundRoot root = (CompoundRoot) target;
|
||||
OgnlContext ognlContext = (OgnlContext) context;
|
||||
@@ -136,7 +118,6 @@ public class CompoundRootAccessor implements RootAccessor {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getProperty(Map context, Object target, Object name) throws OgnlException {
|
||||
CompoundRoot root = (CompoundRoot) target;
|
||||
OgnlContext ognlContext = (OgnlContext) context;
|
||||
@@ -146,7 +127,7 @@ public class CompoundRootAccessor implements RootAccessor {
|
||||
return root.cutStack(index);
|
||||
} else if (name instanceof String) {
|
||||
if ("top".equals(name)) {
|
||||
if (!root.isEmpty()) {
|
||||
if (root.size() > 0) {
|
||||
return root.get(0);
|
||||
} else {
|
||||
return null;
|
||||
@@ -182,7 +163,6 @@ public class CompoundRootAccessor implements RootAccessor {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object callMethod(Map context, Object target, String name, Object[] objects) throws MethodFailedException {
|
||||
CompoundRoot root = (CompoundRoot) target;
|
||||
|
||||
@@ -210,23 +190,26 @@ public class CompoundRootAccessor implements RootAccessor {
|
||||
}
|
||||
|
||||
SortedSet<String> set = new TreeSet<>();
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (PropertyDescriptor pd : descriptors.values()) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(pd.getName()).append(": ");
|
||||
|
||||
sb.append(pd.getName()).append(": ");
|
||||
int padding = maxSize - pd.getName().length();
|
||||
for (int i = 0; i < padding; i++) {
|
||||
sb.append(" ");
|
||||
}
|
||||
sb.append(pd.getPropertyType().getName());
|
||||
set.add(sb.toString());
|
||||
|
||||
sb = new StringBuffer();
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String aSet : set) {
|
||||
sb.append(aSet).append("\n");
|
||||
sb = new StringBuffer();
|
||||
for (Object aSet : set) {
|
||||
String s = (String) aSet;
|
||||
sb.append(s).append("\n");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
} catch (IntrospectionException | OgnlException e) {
|
||||
LOG.debug("Got exception in callMethod", e);
|
||||
@@ -275,23 +258,13 @@ public class CompoundRootAccessor implements RootAccessor {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object callStaticMethod(Map transientVars, Class aClass, String s, Object[] objects) throws MethodFailedException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class classForName(String className, Map context) throws ClassNotFoundException {
|
||||
Object root = Ognl.getRoot(context);
|
||||
|
||||
if (disallowCustomOgnlMap) {
|
||||
String nodeClassName = ((OgnlContext) context).getCurrentNode().getClass().getName();
|
||||
if ("ognl.ASTMap".equals(nodeClassName)) {
|
||||
LOG.error("Constructing OGNL ASTMap's from custom classes is forbidden. Attempted class: {}", className);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (root instanceof CompoundRoot) {
|
||||
if (className.startsWith("vs")) {
|
||||
@@ -348,14 +321,9 @@ public class CompoundRootAccessor implements RootAccessor {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof MethodCall)) {
|
||||
return false;
|
||||
}
|
||||
MethodCall mc = (MethodCall) obj;
|
||||
return mc.clazz.equals(clazz) && mc.name.equals(name) && Arrays.equals(mc.args, args);
|
||||
MethodCall mc = (CompoundRootAccessor.MethodCall) obj;
|
||||
|
||||
return (mc.clazz.equals(clazz) && mc.name.equals(name) && Arrays.equals(mc.args, args));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.ognl.accessor;
|
||||
|
||||
import ognl.ClassResolver;
|
||||
import ognl.MethodAccessor;
|
||||
import ognl.PropertyAccessor;
|
||||
|
||||
/**
|
||||
* @since 6.4.0
|
||||
*/
|
||||
public interface RootAccessor extends PropertyAccessor, MethodAccessor, ClassResolver {
|
||||
}
|
||||
@@ -32,37 +32,37 @@ public interface AcceptedPatternsChecker {
|
||||
* @param value to check
|
||||
* @return object containing result of matched pattern and pattern itself
|
||||
*/
|
||||
IsAccepted isAccepted(String value);
|
||||
public IsAccepted isAccepted(String value);
|
||||
|
||||
/**
|
||||
* Sets excluded patterns during runtime
|
||||
*
|
||||
* @param commaDelimitedPatterns comma delimited string with patterns
|
||||
*/
|
||||
void setAcceptedPatterns(String commaDelimitedPatterns);
|
||||
public void setAcceptedPatterns(String commaDelimitedPatterns);
|
||||
|
||||
/**
|
||||
* Set excluded patterns during runtime
|
||||
*
|
||||
* @param patterns array of additional excluded patterns
|
||||
*/
|
||||
void setAcceptedPatterns(String[] patterns);
|
||||
public void setAcceptedPatterns(String[] patterns);
|
||||
|
||||
/**
|
||||
* Sets excluded patterns during runtime
|
||||
*
|
||||
* @param patterns set of additional patterns
|
||||
*/
|
||||
void setAcceptedPatterns(Set<String> patterns);
|
||||
public void setAcceptedPatterns(Set<String> patterns);
|
||||
|
||||
/**
|
||||
* Allow access list of all defined excluded patterns
|
||||
*
|
||||
* @return set of excluded patterns
|
||||
*/
|
||||
Set<Pattern> getAcceptedPatterns();
|
||||
public Set<Pattern> getAcceptedPatterns();
|
||||
|
||||
final class IsAccepted {
|
||||
public final static class IsAccepted {
|
||||
|
||||
private final boolean accepted;
|
||||
private final String acceptedPattern;
|
||||
|
||||
+25
-20
@@ -43,7 +43,7 @@ public class DefaultAcceptedPatternsChecker implements AcceptedPatternsChecker {
|
||||
"\\w+([:]?\\w+)?((\\.\\w+)|(\\[\\d+])|(\\(\\d+\\))|(\\['(\\w-?|[\\u4e00-\\u9fa5]-?)+'])|(\\('(\\w-?|[\\u4e00-\\u9fa5]-?)+'\\)))*([!]?\\w+)?"
|
||||
};
|
||||
|
||||
protected Set<Pattern> acceptedPatterns;
|
||||
private Set<Pattern> acceptedPatterns;
|
||||
|
||||
public DefaultAcceptedPatternsChecker() {
|
||||
setAcceptedPatterns(ACCEPTED_PATTERNS);
|
||||
@@ -62,19 +62,28 @@ public class DefaultAcceptedPatternsChecker implements AcceptedPatternsChecker {
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_OVERRIDE_ACCEPTED_PATTERNS, required = false)
|
||||
protected void setOverrideAcceptedPatterns(String acceptablePatterns) {
|
||||
setAcceptedPatterns(acceptablePatterns);
|
||||
LOG.warn("Overriding accepted patterns [{}] with [{}], be aware that this affects all instances and safety of your application!",
|
||||
acceptedPatterns, acceptablePatterns);
|
||||
acceptedPatterns = new HashSet<>();
|
||||
try {
|
||||
for (String pattern : TextParseUtil.commaDelimitedStringToSet(acceptablePatterns)) {
|
||||
acceptedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
}
|
||||
} finally {
|
||||
acceptedPatterns = Collections.unmodifiableSet(acceptedPatterns);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_ADDITIONAL_ACCEPTED_PATTERNS, required = false)
|
||||
protected void setAdditionalAcceptedPatterns(String acceptablePatterns) {
|
||||
LOG.warn("Adding additional global patterns [{}] to accepted patterns!", acceptablePatterns);
|
||||
Set<Pattern> newAcceptedPatterns = new HashSet<>(acceptedPatterns);
|
||||
acceptedPatterns = new HashSet<>(acceptedPatterns); // Make mutable before adding
|
||||
try {
|
||||
for (String pattern : TextParseUtil.commaDelimitedStringToSet(acceptablePatterns)) {
|
||||
newAcceptedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
acceptedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
}
|
||||
} finally {
|
||||
acceptedPatterns = Collections.unmodifiableSet(newAcceptedPatterns);
|
||||
acceptedPatterns = Collections.unmodifiableSet(acceptedPatterns);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,24 +99,20 @@ public class DefaultAcceptedPatternsChecker implements AcceptedPatternsChecker {
|
||||
|
||||
@Override
|
||||
public void setAcceptedPatterns(Set<String> patterns) {
|
||||
logPatternChange(patterns);
|
||||
Set<Pattern> newAcceptedPatterns = new HashSet<>(patterns.size());
|
||||
try {
|
||||
for (String pattern : patterns) {
|
||||
newAcceptedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
}
|
||||
} finally {
|
||||
acceptedPatterns = Collections.unmodifiableSet(newAcceptedPatterns);
|
||||
}
|
||||
}
|
||||
|
||||
protected void logPatternChange(Set<String> newPatterns) {
|
||||
if (acceptedPatterns == null) {
|
||||
// No need to warn on class initialisation
|
||||
LOG.debug("Sets accepted patterns to [{}], note this impacts the safety of your application!", newPatterns);
|
||||
// Limit unwanted log entries (for 1st call, acceptedPatterns null)
|
||||
LOG.debug("Sets accepted patterns to [{}], note this impacts the safety of your application!", patterns);
|
||||
} else {
|
||||
LOG.warn("Replacing accepted patterns [{}] with [{}], be aware that this affects all instances and safety of your application!",
|
||||
acceptedPatterns, newPatterns);
|
||||
acceptedPatterns, patterns);
|
||||
}
|
||||
acceptedPatterns = new HashSet<>(patterns.size());
|
||||
try {
|
||||
for (String pattern : patterns) {
|
||||
acceptedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
}
|
||||
} finally {
|
||||
acceptedPatterns = Collections.unmodifiableSet(acceptedPatterns);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+18
-5
@@ -36,9 +36,8 @@ public class DefaultExcludedPatternsChecker implements ExcludedPatternsChecker {
|
||||
private static final Logger LOG = LogManager.getLogger(DefaultExcludedPatternsChecker.class);
|
||||
|
||||
public static final String[] EXCLUDED_PATTERNS = {
|
||||
"(^|\\%\\{)((#?)(top(\\.|\\['|\\[\")|\\[\\d\\]\\.)?)(dojo|struts|session|request|response|application|servlet(Request|Response|Context)|parameters|context|_memberAccess)(\\.|\\[).*",
|
||||
".*(^|\\.|\\[|\\'|\"|get)class(\\(\\.|\\[|\\'|\").*",
|
||||
"actionErrors|actionMessages|fieldErrors"
|
||||
"(^|\\%\\{)((#?)(top(\\.|\\['|\\[\")|\\[\\d\\]\\.)?)(dojo|struts|session|request|response|application|servlet(Request|Response|Context)|parameters|context|_memberAccess)(\\.|\\[).*",
|
||||
".*(^|\\.|\\[|\\'|\"|get)class(\\(\\.|\\[|\\'|\").*"
|
||||
};
|
||||
|
||||
private Set<Pattern> excludedPatterns;
|
||||
@@ -49,7 +48,21 @@ public class DefaultExcludedPatternsChecker implements ExcludedPatternsChecker {
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_OVERRIDE_EXCLUDED_PATTERNS, required = false)
|
||||
protected void setOverrideExcludePatterns(String excludePatterns) {
|
||||
setExcludedPatterns(excludePatterns);
|
||||
if (excludedPatterns != null && excludedPatterns.size() > 0) {
|
||||
LOG.warn("Overriding excluded patterns [{}] with [{}], be aware that this affects all instances and safety of your application!",
|
||||
excludedPatterns, excludePatterns);
|
||||
} else {
|
||||
// Limit unwanted log entries (when excludedPatterns null/empty - usually 1st call)
|
||||
LOG.debug("Overriding excluded patterns with [{}]", excludePatterns);
|
||||
}
|
||||
excludedPatterns = new HashSet<>();
|
||||
try {
|
||||
for (String pattern : TextParseUtil.commaDelimitedStringToSet(excludePatterns)) {
|
||||
excludedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
}
|
||||
} finally {
|
||||
excludedPatterns = Collections.unmodifiableSet(excludedPatterns);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_ADDITIONAL_EXCLUDED_PATTERNS, required = false)
|
||||
@@ -85,7 +98,7 @@ public class DefaultExcludedPatternsChecker implements ExcludedPatternsChecker {
|
||||
|
||||
@Override
|
||||
public void setExcludedPatterns(Set<String> patterns) {
|
||||
if (excludedPatterns != null && !excludedPatterns.isEmpty()) {
|
||||
if (excludedPatterns != null && excludedPatterns.size() > 0) {
|
||||
LOG.warn("Replacing excluded patterns [{}] with [{}], be aware that this affects all instances and safety of your application!",
|
||||
excludedPatterns, patterns);
|
||||
} else {
|
||||
|
||||
@@ -32,37 +32,37 @@ public interface ExcludedPatternsChecker {
|
||||
* @param value to check
|
||||
* @return object containing result of matched pattern and pattern itself
|
||||
*/
|
||||
IsExcluded isExcluded(String value);
|
||||
public IsExcluded isExcluded(String value);
|
||||
|
||||
/**
|
||||
* Sets excluded patterns during runtime
|
||||
*
|
||||
* @param commaDelimitedPatterns comma delimited string with patterns
|
||||
*/
|
||||
void setExcludedPatterns(String commaDelimitedPatterns);
|
||||
public void setExcludedPatterns(String commaDelimitedPatterns);
|
||||
|
||||
/**
|
||||
* Sets excluded patterns during runtime
|
||||
*
|
||||
* @param patterns array of additional excluded patterns
|
||||
*/
|
||||
void setExcludedPatterns(String[] patterns);
|
||||
public void setExcludedPatterns(String[] patterns);
|
||||
|
||||
/**
|
||||
* Sets excluded patterns during runtime
|
||||
*
|
||||
* @param patterns set of additional patterns
|
||||
*/
|
||||
void setExcludedPatterns(Set<String> patterns);
|
||||
public void setExcludedPatterns(Set<String> patterns);
|
||||
|
||||
/**
|
||||
* Allow access list of all defined excluded patterns
|
||||
*
|
||||
* @return set of excluded patterns
|
||||
*/
|
||||
Set<Pattern> getExcludedPatterns();
|
||||
public Set<Pattern> getExcludedPatterns();
|
||||
|
||||
final class IsExcluded {
|
||||
public final static class IsExcluded {
|
||||
|
||||
private final boolean excluded;
|
||||
private final String excludedPattern;
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.util;
|
||||
|
||||
import com.opensymphony.xwork2.config.ConfigurationException;
|
||||
import com.opensymphony.xwork2.ognl.OgnlUtil;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
import static com.opensymphony.xwork2.util.TextParseUtil.commaDelimitedStringToSet;
|
||||
import static java.util.Collections.unmodifiableSet;
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
import static org.apache.commons.lang3.StringUtils.strip;
|
||||
|
||||
public class ConfigParseUtil {
|
||||
|
||||
private ConfigParseUtil() {
|
||||
}
|
||||
|
||||
public static Set<String> toClassesSet(String newDelimitedClasses) throws ConfigurationException {
|
||||
Set<String> classNames = commaDelimitedStringToSet(newDelimitedClasses);
|
||||
validateClasses(classNames, OgnlUtil.class.getClassLoader());
|
||||
return unmodifiableSet(classNames);
|
||||
}
|
||||
|
||||
public static Set<Class<?>> toClassObjectsSet(String newDelimitedClasses) throws ConfigurationException {
|
||||
Set<String> classNames = commaDelimitedStringToSet(newDelimitedClasses);
|
||||
return unmodifiableSet(validateClasses(classNames, OgnlUtil.class.getClassLoader()));
|
||||
}
|
||||
|
||||
public static Set<String> toNewClassesSet(Set<String> oldClasses, String newDelimitedClasses) throws ConfigurationException {
|
||||
Set<String> classNames = commaDelimitedStringToSet(newDelimitedClasses);
|
||||
validateClasses(classNames, OgnlUtil.class.getClassLoader());
|
||||
Set<String> excludedClasses = new HashSet<>(oldClasses);
|
||||
excludedClasses.addAll(classNames);
|
||||
return unmodifiableSet(excludedClasses);
|
||||
}
|
||||
|
||||
public static Set<Pattern> toNewPatternsSet(Set<Pattern> oldPatterns, String newDelimitedPatterns) throws ConfigurationException {
|
||||
Set<String> patterns = commaDelimitedStringToSet(newDelimitedPatterns);
|
||||
Set<Pattern> newPatterns = new HashSet<>(oldPatterns);
|
||||
for (String pattern: patterns) {
|
||||
try {
|
||||
newPatterns.add(Pattern.compile(pattern));
|
||||
} catch (PatternSyntaxException e) {
|
||||
throw new ConfigurationException("Excluded package name patterns could not be parsed due to invalid regex: " + pattern, e);
|
||||
}
|
||||
}
|
||||
return unmodifiableSet(newPatterns);
|
||||
}
|
||||
|
||||
public static Set<Class<?>> validateClasses(Set<String> classNames, ClassLoader validatingClassLoader) throws ConfigurationException {
|
||||
Set<Class<?>> classes = new HashSet<>();
|
||||
for (String className : classNames) {
|
||||
try {
|
||||
classes.add(validatingClassLoader.loadClass(className));
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new ConfigurationException("Cannot load class for exclusion/exemption configuration: " + className, e);
|
||||
}
|
||||
}
|
||||
return classes;
|
||||
}
|
||||
|
||||
public static Set<String> toPackageNamesSet(String newDelimitedPackageNames) throws ConfigurationException {
|
||||
Set<String> packageNames = commaDelimitedStringToSet(newDelimitedPackageNames)
|
||||
.stream().map(s -> strip(s, ".")).collect(toSet());
|
||||
validatePackageNames(packageNames);
|
||||
return unmodifiableSet(packageNames);
|
||||
}
|
||||
|
||||
public static Set<String> toNewPackageNamesSet(Collection<String> oldPackageNames, String newDelimitedPackageNames) throws ConfigurationException {
|
||||
Set<String> packageNames = commaDelimitedStringToSet(newDelimitedPackageNames)
|
||||
.stream().map(s -> strip(s, ".")).collect(toSet());
|
||||
validatePackageNames(packageNames);
|
||||
Set<String> newPackageNames = new HashSet<>(oldPackageNames);
|
||||
newPackageNames.addAll(packageNames);
|
||||
return unmodifiableSet(newPackageNames);
|
||||
}
|
||||
|
||||
public static void validatePackageNames(Collection<String> packageNames) {
|
||||
if (packageNames.stream().anyMatch(s -> Pattern.compile("\\s").matcher(s).find())) {
|
||||
throw new ConfigurationException("Excluded package names could not be parsed due to erroneous whitespace characters: " + packageNames);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,8 +27,20 @@ import java.util.regex.Pattern;
|
||||
*/
|
||||
public interface MemberAccessValueStack {
|
||||
|
||||
/**
|
||||
* @deprecated please use {@link #useExcludeProperties(Set)}
|
||||
*/
|
||||
@Deprecated
|
||||
void setExcludeProperties(Set<Pattern> excludeProperties);
|
||||
|
||||
void useExcludeProperties(Set<Pattern> excludeProperties);
|
||||
|
||||
/**
|
||||
* @deprecated please use {@link #useAcceptProperties(Set)}
|
||||
*/
|
||||
@Deprecated
|
||||
void setAcceptProperties(Set<Pattern> acceptedProperties);
|
||||
|
||||
void useAcceptProperties(Set<Pattern> acceptedProperties);
|
||||
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ public class LocationImpl implements Location, Serializable {
|
||||
private final int line;
|
||||
private final int column;
|
||||
private final String description;
|
||||
|
||||
|
||||
// Package private: outside this package, use Location.UNKNOWN.
|
||||
static final LocationImpl UNKNOWN = new LocationImpl(null, null, -1, -1);
|
||||
|
||||
@@ -71,16 +71,16 @@ public class LocationImpl implements Location, Serializable {
|
||||
}
|
||||
this.description = StringUtils.trimToNull(description);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Copy constructor.
|
||||
*
|
||||
*
|
||||
* @param location the location to be copied
|
||||
*/
|
||||
public LocationImpl(Location location) {
|
||||
this(location.getDescription(), location.getURI(), location.getLineNumber(), location.getColumnNumber());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a location from an existing one, but with a different description
|
||||
*
|
||||
@@ -90,14 +90,14 @@ public class LocationImpl implements Location, Serializable {
|
||||
public LocationImpl(String description, Location location) {
|
||||
this(description, location.getURI(), location.getLineNumber(), location.getColumnNumber());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Obtain a <code>LocationImpl</code> from a {@link Location}. If <code>location</code> is
|
||||
* already a <code>LocationImpl</code>, it is returned, otherwise it is copied.
|
||||
* <p>
|
||||
* This method is useful when an immutable and serializable location is needed, such as in locatable
|
||||
* exceptions.
|
||||
*
|
||||
*
|
||||
* @param location the location
|
||||
* @return an immutable and serializable version of <code>location</code>
|
||||
*/
|
||||
@@ -110,19 +110,19 @@ public class LocationImpl implements Location, Serializable {
|
||||
return new LocationImpl(location);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the description of this location
|
||||
*
|
||||
*
|
||||
* @return the description (can be <code>null</code>)
|
||||
*/
|
||||
public String getDescription() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the URI of this location
|
||||
*
|
||||
*
|
||||
* @return the URI (<code>null</code> if unknown).
|
||||
*/
|
||||
public String getURI() {
|
||||
@@ -131,22 +131,22 @@ public class LocationImpl implements Location, Serializable {
|
||||
|
||||
/**
|
||||
* Get the line number of this location
|
||||
*
|
||||
*
|
||||
* @return the line number (<code>-1</code> if unknown)
|
||||
*/
|
||||
public int getLineNumber() {
|
||||
return this.line;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the column number of this location
|
||||
*
|
||||
*
|
||||
* @return the column number (<code>-1</code> if unknown)
|
||||
*/
|
||||
public int getColumnNumber() {
|
||||
return this.column;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param padding The amount of lines before and after the error to include
|
||||
* @return a source code snippet with the default padding
|
||||
@@ -184,24 +184,24 @@ public class LocationImpl implements Location, Serializable {
|
||||
&& testEquals(this.uri, other.getURI())
|
||||
&& testEquals(this.description, other.getDescription());
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = line ^ column;
|
||||
if (uri != null) hash ^= uri.hashCode();
|
||||
if (description != null) hash ^= description.hashCode();
|
||||
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return LocationUtils.toString(this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Ensure serialized unknown location resolve to {@link Location#UNKNOWN}.
|
||||
*
|
||||
@@ -210,7 +210,7 @@ public class LocationImpl implements Location, Serializable {
|
||||
private Object readResolve() {
|
||||
return this.equals(Location.UNKNOWN) ? Location.UNKNOWN : this;
|
||||
}
|
||||
|
||||
|
||||
private boolean testEquals(Object object1, Object object2) {
|
||||
if (object1 == object2) {
|
||||
return true;
|
||||
|
||||
@@ -220,12 +220,6 @@ public final class StrutsConstants {
|
||||
|
||||
public static final String STRUTS_FREEMARKER_WRAPPER_ALT_MAP = "struts.freemarker.wrapper.altMap";
|
||||
|
||||
/** Extension point for the Struts CompoundRootAccessor */
|
||||
public static final String STRUTS_COMPOUND_ROOT_ACCESSOR = "struts.compoundRootAccessor";
|
||||
|
||||
/** Extension point for the Struts MethodAccessor */
|
||||
public static final String STRUTS_METHOD_ACCESSOR = "struts.methodAccessor";
|
||||
|
||||
/** The name of the xwork converter implementation */
|
||||
public static final String STRUTS_XWORKCONVERTER = "struts.xworkConverter";
|
||||
|
||||
@@ -240,12 +234,6 @@ public final class StrutsConstants {
|
||||
/** The name of the parameter to determine whether static field access will be allowed in OGNL expressions or not */
|
||||
public static final String STRUTS_ALLOW_STATIC_FIELD_ACCESS = "struts.ognl.allowStaticFieldAccess";
|
||||
|
||||
public static final String STRUTS_DISALLOW_CUSTOM_OGNL_MAP = "struts.ognl.disallowCustomOgnlMap";
|
||||
|
||||
public static final String STRUTS_MEMBER_ACCESS = "struts.securityMemberAccess";
|
||||
|
||||
public static final String STRUTS_OGNL_GUARD = "struts.ognlGuard";
|
||||
|
||||
/** The com.opensymphony.xwork2.validator.ActionValidatorManager implementation class */
|
||||
public static final String STRUTS_ACTIONVALIDATORMANAGER = "struts.actionValidatorManager";
|
||||
|
||||
@@ -287,25 +275,28 @@ public final class StrutsConstants {
|
||||
public static final String STRUTS_OGNL_BEANINFO_CACHE_FACTORY = "struts.ognl.beanInfoCacheFactory";
|
||||
|
||||
/**
|
||||
* Specifies the type of cache to use for BeanInfo objects.
|
||||
* @since 6.4.0
|
||||
* @see StrutsConstants#STRUTS_OGNL_EXPRESSION_CACHE_TYPE
|
||||
*/
|
||||
public static final String STRUTS_OGNL_BEANINFO_CACHE_TYPE = "struts.ognl.beanInfoCacheType";
|
||||
|
||||
/**
|
||||
* Specifies the maximum cache size for BeanInfo objects. This should be configured based on the cache type chosen
|
||||
* and application-specific needs.
|
||||
* Specifies a maximum number of cached BeanInfo used by OgnlUtility. Not specified/set by default. If
|
||||
* a positive integer is specified, it will set a limit whose behaviour depends on whether the
|
||||
* normal (default) cache or optional LRU cache is in place.
|
||||
*
|
||||
* For the normal (default) cache, exceeding the maximum will cause the entire cache to flush (clear).
|
||||
* For the optional LRU cache, once the maximum is reached, the least-recently-used (LRU) entry will be
|
||||
* removed when a new entry needs to be added (cache is fully-utilized).
|
||||
*
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public static final String STRUTS_OGNL_BEANINFO_CACHE_MAXSIZE = "struts.ognl.beanInfoCacheMaxSize";
|
||||
|
||||
/**
|
||||
* Set the cache mode of the BeanInfo cache used by OgnlUtility. A value of true means enable
|
||||
* least-recently-used (LRU) mode, a value of false (or any non-true value) means to use the
|
||||
* default cache.
|
||||
*
|
||||
* Note: When enabling LRU cache mode you must also set a maximum size (via {@link #STRUTS_OGNL_BEANINFO_CACHE_MAXSIZE})
|
||||
* for it to be effective. Otherwise, there is no condition to evict a LRU entry (cache has no limit).
|
||||
*
|
||||
* @since 6.0.0
|
||||
* @deprecated since 6.4.0, use {@link StrutsConstants#STRUTS_OGNL_BEANINFO_CACHE_TYPE} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public static final String STRUTS_OGNL_BEANINFO_CACHE_LRU_MODE = "struts.ognl.beanInfoCacheLRUMode";
|
||||
|
||||
/**
|
||||
@@ -314,14 +305,6 @@ public final class StrutsConstants {
|
||||
*/
|
||||
public static final String STRUTS_OGNL_LOG_MISSING_PROPERTIES = "struts.ognl.logMissingProperties";
|
||||
|
||||
/**
|
||||
* Determines whether lookups on the ValueStack should fallback to looking in the context if the OGNL expression
|
||||
* fails or returns null.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*/
|
||||
public static final String STRUTS_OGNL_VALUE_STACK_FALLBACK_TO_CONTEXT = "struts.ognl.valueStackFallbackToContext";
|
||||
|
||||
/**
|
||||
* Logs properties that are not found (very verbose)
|
||||
* @deprecated as of 6.0.0. Use {@link #STRUTS_OGNL_LOG_MISSING_PROPERTIES} instead.
|
||||
@@ -342,32 +325,28 @@ public final class StrutsConstants {
|
||||
public static final String STRUTS_ENABLE_OGNL_EXPRESSION_CACHE = STRUTS_OGNL_ENABLE_EXPRESSION_CACHE;
|
||||
|
||||
/**
|
||||
* Specifies the type of cache to use for parsed OGNL expressions. Valid values defined in
|
||||
* {@link com.opensymphony.xwork2.ognl.OgnlCacheFactory.CacheType}.
|
||||
* <ul>
|
||||
* <li>For the W-TinyLfu cache, the eviction policy is detailed
|
||||
* <a href="https://github.com/ben-manes/caffeine/wiki/Efficiency#window-tinylfu">here.</a></li>
|
||||
* <li>For the basic cache, exceeding the maximum cache size will cause the entire cache to flush.</li>
|
||||
* <li>For the LRU cache, once the maximum cache size is reached, the least-recently-used entry will be removed.
|
||||
* </li>
|
||||
* </ul>
|
||||
* @since 6.4.0
|
||||
*/
|
||||
public static final String STRUTS_OGNL_EXPRESSION_CACHE_TYPE = "struts.ognl.expressionCacheType";
|
||||
|
||||
/**
|
||||
* Specifies the maximum cache size for parsed OGNL expressions. This should be configured based on the cache type
|
||||
* chosen and application-specific needs.
|
||||
* Specifies a maximum number of cached parsed OGNL expressions. Not specified/set by default. If
|
||||
* a positive integer is specified, it will set a limit whose behaviour depends on whether the
|
||||
* normal (default) cache or optional LRU cache is in place.
|
||||
*
|
||||
* For the normal (default) cache, exceeding the maximum will cause the entire cache to flush (clear).
|
||||
* For the optional LRU cache, once the maximum is reached, the least-recently-used (LRU) entry will be
|
||||
* removed when a new entry needs to be added (cache is fully-utilized).
|
||||
*
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public static final String STRUTS_OGNL_EXPRESSION_CACHE_MAXSIZE = "struts.ognl.expressionCacheMaxSize";
|
||||
|
||||
/**
|
||||
* Set the cache mode of the parsed OGNL expression cache. A value of true means enable
|
||||
* least-recently-used (LRU) mode, a value of false (or any non-true value) means to use the
|
||||
* default cache.
|
||||
*
|
||||
* Note: When enabling LRU cache mode you must also set a maximum size (via {@link #STRUTS_OGNL_EXPRESSION_CACHE_MAXSIZE})
|
||||
* for it to be effective. Otherwise, there is no condition to evict a LRU entry (cache has no limit).
|
||||
*
|
||||
* @since 6.0.0
|
||||
* @deprecated since 6.4.0, use {@link StrutsConstants#STRUTS_OGNL_EXPRESSION_CACHE_TYPE} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public static final String STRUTS_OGNL_EXPRESSION_CACHE_LRU_MODE = "struts.ognl.expressionCacheLRUMode";
|
||||
|
||||
/**
|
||||
@@ -385,9 +364,6 @@ public final class StrutsConstants {
|
||||
/** The maximum length of an expression (OGNL) */
|
||||
public static final String STRUTS_OGNL_EXPRESSION_MAX_LENGTH = "struts.ognl.expressionMaxLength";
|
||||
|
||||
/** Parsed OGNL expressions which contain these node types will be blocked */
|
||||
public static final String STRUTS_OGNL_EXCLUDED_NODE_TYPES = "struts.ognl.excludedNodeTypes";
|
||||
|
||||
/** Disables {@link org.apache.struts2.dispatcher.StrutsRequestWrapper} request attribute value stack lookup (JSTL accessibility) */
|
||||
public static final String STRUTS_DISABLE_REQUEST_ATTRIBUTE_VALUE_STACK_LOOKUP = "struts.disableRequestAttributeValueStackLookup";
|
||||
|
||||
@@ -450,12 +426,6 @@ public final class StrutsConstants {
|
||||
public static final String STRUTS_DEV_MODE_EXCLUDED_PACKAGE_NAMES = "struts.devMode.excludedPackageNames";
|
||||
public static final String STRUTS_DEV_MODE_EXCLUDED_PACKAGE_EXEMPT_CLASSES = "struts.devMode.excludedPackageExemptClasses";
|
||||
|
||||
/** Boolean to enable strict allowlist processing of all OGNL expression calls. */
|
||||
public static final String STRUTS_ALLOWLIST_ENABLE = "struts.allowlist.enable";
|
||||
/** Comma delimited set of allowed classes which CAN be accessed via OGNL expressions. Both target and member classes of OGNL expression must be allowlisted. */
|
||||
public static final String STRUTS_ALLOWLIST_CLASSES = "struts.allowlist.classes";
|
||||
/** Comma delimited set of package names, of which all its classes, and all classes in its subpackages, CAN be accessed via OGNL expressions. Both target and member classes of OGNL expression must be allowlisted. */
|
||||
public static final String STRUTS_ALLOWLIST_PACKAGE_NAMES = "struts.allowlist.packageNames";
|
||||
|
||||
/** Dedicated services to check if passed string is excluded/accepted */
|
||||
public static final String STRUTS_EXCLUDED_PATTERNS_CHECKER = "struts.excludedPatterns.checker";
|
||||
@@ -478,7 +448,6 @@ public final class StrutsConstants {
|
||||
public static final String STRUTS_LOCALIZED_TEXT_PROVIDER = "struts.localizedTextProvider";
|
||||
|
||||
public static final String STRUTS_DISALLOW_PROXY_MEMBER_ACCESS = "struts.disallowProxyMemberAccess";
|
||||
public static final String STRUTS_DISALLOW_DEFAULT_PACKAGE_ACCESS = "struts.disallowDefaultPackageAccess";
|
||||
|
||||
public static final String STRUTS_OGNL_AUTO_GROWTH_COLLECTION_LIMIT = "struts.ognl.autoGrowthCollectionLimit";
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ import static org.apache.struts2.interceptor.csp.CspSettings.CSP_REPORT_TYPE;
|
||||
* <package name="csp-reports" namespace="/" extends="struts-default">
|
||||
* <action name="csp-reports" class="org.apache.struts2.action.DefaultCspReportAction">
|
||||
* <result type="httpheader">
|
||||
* <param name="status">204</param>
|
||||
* <param name="statusCode">200</param>
|
||||
* </result>
|
||||
* </action>
|
||||
* </package>
|
||||
|
||||
@@ -23,6 +23,7 @@ package org.apache.struts2.action;
|
||||
* them automatically. This may be useful if one is using the action tag and want to supply
|
||||
* the parameters to the action manually using the param tag. It may also be useful if one for
|
||||
* security reasons wants to make sure that parameters cannot be set by malicious users.
|
||||
*
|
||||
*/
|
||||
public interface NoParameters {
|
||||
public interface NoParameters extends com.opensymphony.xwork2.interceptor.NoParameters {
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.action;
|
||||
|
||||
import org.apache.struts2.interceptor.parameter.ParametersInterceptor;
|
||||
|
||||
/**
|
||||
* This interface is implemented by actions that want to declare acceptable parameters. Works in conjunction with {@link
|
||||
* ParametersInterceptor}. For example, actions may want to create a white list of parameters they will accept or a
|
||||
* blacklist of parameters they will reject to prevent clients from setting other unexpected (and possibly dangerous)
|
||||
* parameters.
|
||||
*/
|
||||
public interface ParameterNameAware {
|
||||
|
||||
/**
|
||||
* Tests if the action will accept the parameter with the given name.
|
||||
*
|
||||
* @param parameterName the parameter name
|
||||
* @return <code>true</code> if accepted, <code>false</code> otherwise
|
||||
*/
|
||||
boolean acceptableParameterName(String parameterName);
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.action;
|
||||
|
||||
import org.apache.struts2.interceptor.parameter.ParametersInterceptor;
|
||||
|
||||
/**
|
||||
* This interface is implemented by actions that want to declare acceptable parameter values. Works in conjunction with
|
||||
* {@link ParametersInterceptor}. For example, actions may want to create a white list of parameter values they will
|
||||
* accept or a blacklist of parameter values they will reject to prevent clients from setting other unexpected (and
|
||||
* possibly dangerous) parameter values.
|
||||
*/
|
||||
public interface ParameterValueAware {
|
||||
|
||||
/**
|
||||
* Tests if the action will accept the parameter with the given value.
|
||||
*
|
||||
* @param parameterValue the parameter value
|
||||
* @return <code>true</code> if accepted, <code>false</code> otherwise
|
||||
*/
|
||||
boolean acceptableParameterValue(String parameterValue);
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.action;
|
||||
|
||||
import org.apache.struts2.dispatcher.multipart.UploadedFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Actions that want to be aware of all the uploaded file should implement this interface.
|
||||
* The {@link org.apache.struts2.interceptor.ActionFileUploadInterceptor} will use the interface
|
||||
* to notify action about the multiple uploaded files.
|
||||
*/
|
||||
public interface UploadedFilesAware {
|
||||
|
||||
/**
|
||||
* Notifies action about the multiple uploaded files, when a single file is uploaded
|
||||
* the list will have just one element
|
||||
*
|
||||
* @param uploadedFiles a list of {@link UploadedFile}, cannot be null. It can be empty.
|
||||
*/
|
||||
void withUploadedFiles(List<UploadedFile> uploadedFiles);
|
||||
|
||||
}
|
||||
@@ -20,7 +20,6 @@ package org.apache.struts2.components;
|
||||
|
||||
import java.io.Writer;
|
||||
|
||||
import org.apache.struts2.dispatcher.DispatcherConstants;
|
||||
import org.apache.struts2.views.annotations.StrutsTag;
|
||||
import org.apache.struts2.views.annotations.StrutsTagAttribute;
|
||||
|
||||
@@ -54,11 +53,16 @@ import com.opensymphony.xwork2.util.ValueStack;
|
||||
* <!-- START SNIPPET: params -->
|
||||
*
|
||||
* <ul>
|
||||
*
|
||||
* <li>var* (String): The name of the new variable that is assigned the value of <i>value</i></li>
|
||||
*
|
||||
* <li>value (Object): The value that is assigned to the variable named <i>name</i></li>
|
||||
*
|
||||
* <li>scope (String): The scope in which to assign the variable. Can be <b>application</b>, <b>session</b>,
|
||||
* <b>request</b>, <b>page</b>, or <b>action</b>. By default it is <b>action</b>.</li>
|
||||
*
|
||||
* <li>Note: With the <b>action</b> scope, the variable is <em>also</em> assigned to the <b>page</b> scope.
|
||||
*
|
||||
* </ul>
|
||||
*
|
||||
* <!-- END SNIPPET: params -->
|
||||
@@ -103,16 +107,16 @@ public class Set extends ContextBean {
|
||||
|
||||
body="";
|
||||
|
||||
if (DispatcherConstants.APPLICATION.equalsIgnoreCase(scope)) {
|
||||
if ("application".equalsIgnoreCase(scope)) {
|
||||
stack.setValue("#application['" + getVar() + "']", o);
|
||||
} else if (DispatcherConstants.SESSION.equalsIgnoreCase(scope)) {
|
||||
} else if ("session".equalsIgnoreCase(scope)) {
|
||||
stack.setValue("#session['" + getVar() + "']", o);
|
||||
} else if (DispatcherConstants.REQUEST.equalsIgnoreCase(scope)) {
|
||||
} else if ("request".equalsIgnoreCase(scope)) {
|
||||
stack.setValue("#request['" + getVar() + "']", o);
|
||||
} else if (DispatcherConstants.PAGE.equalsIgnoreCase(scope)) {
|
||||
} else if ("page".equalsIgnoreCase(scope)) {
|
||||
stack.setValue("#attr['" + getVar() + "']", o, false);
|
||||
} else {
|
||||
// Default scope is action. Note: The action scope handling also adds the var to the page scope.
|
||||
// Default scope is action. Note: The action acope handling also adds the var to the page scope.
|
||||
stack.getContext().put(getVar(), o);
|
||||
stack.setValue("#attr['" + getVar() + "']", o, false);
|
||||
}
|
||||
|
||||
@@ -601,6 +601,12 @@ public abstract class UIBean extends Component {
|
||||
result = findString(this.templateDir);
|
||||
}
|
||||
|
||||
// If templateDir is not explicitly given,
|
||||
// try to find attribute which states the dir set to use
|
||||
if (StringUtils.isBlank(result)) {
|
||||
result = stack.findString("#attr.templateDir");
|
||||
}
|
||||
|
||||
// Default template set
|
||||
if (StringUtils.isBlank(result)) {
|
||||
result = defaultTemplateDir;
|
||||
@@ -628,6 +634,12 @@ public abstract class UIBean extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
// If theme set is not explicitly given,
|
||||
// try to find attribute which states the theme set to use
|
||||
if (StringUtils.isBlank(result)) {
|
||||
result = stack.findString("#attr.theme");
|
||||
}
|
||||
|
||||
// Default theme set
|
||||
if (StringUtils.isBlank(result)) {
|
||||
result = defaultUITheme;
|
||||
|
||||
@@ -19,13 +19,18 @@
|
||||
package org.apache.struts2.config;
|
||||
|
||||
import com.opensymphony.xwork2.ActionProxyFactory;
|
||||
import com.opensymphony.xwork2.FileManager;
|
||||
import com.opensymphony.xwork2.FileManagerFactory;
|
||||
import com.opensymphony.xwork2.LocaleProviderFactory;
|
||||
import com.opensymphony.xwork2.LocalizedTextProvider;
|
||||
import com.opensymphony.xwork2.TextProviderFactory;
|
||||
import com.opensymphony.xwork2.factory.UnknownHandlerFactory;
|
||||
import com.opensymphony.xwork2.ognl.BeanInfoCacheFactory;
|
||||
import com.opensymphony.xwork2.ognl.ExpressionCacheFactory;
|
||||
import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
|
||||
import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
|
||||
import com.opensymphony.xwork2.FileManager;
|
||||
import com.opensymphony.xwork2.FileManagerFactory;
|
||||
import com.opensymphony.xwork2.ObjectFactory;
|
||||
import com.opensymphony.xwork2.TextProvider;
|
||||
import com.opensymphony.xwork2.TextProviderFactory;
|
||||
import com.opensymphony.xwork2.UnknownHandlerManager;
|
||||
import com.opensymphony.xwork2.conversion.ConversionAnnotationProcessor;
|
||||
import com.opensymphony.xwork2.conversion.ConversionFileProcessor;
|
||||
@@ -43,16 +48,9 @@ import com.opensymphony.xwork2.factory.ActionFactory;
|
||||
import com.opensymphony.xwork2.factory.ConverterFactory;
|
||||
import com.opensymphony.xwork2.factory.InterceptorFactory;
|
||||
import com.opensymphony.xwork2.factory.ResultFactory;
|
||||
import com.opensymphony.xwork2.factory.UnknownHandlerFactory;
|
||||
import com.opensymphony.xwork2.factory.ValidatorFactory;
|
||||
import com.opensymphony.xwork2.inject.ContainerBuilder;
|
||||
import com.opensymphony.xwork2.inject.Scope;
|
||||
import com.opensymphony.xwork2.ognl.BeanInfoCacheFactory;
|
||||
import com.opensymphony.xwork2.ognl.ExpressionCacheFactory;
|
||||
import com.opensymphony.xwork2.ognl.SecurityMemberAccess;
|
||||
import com.opensymphony.xwork2.ognl.accessor.RootAccessor;
|
||||
import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
|
||||
import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
|
||||
import com.opensymphony.xwork2.security.NotExcludedAcceptedPatternsChecker;
|
||||
import com.opensymphony.xwork2.util.PatternMatcher;
|
||||
import com.opensymphony.xwork2.util.TextParser;
|
||||
@@ -61,7 +59,6 @@ import com.opensymphony.xwork2.util.location.LocatableProperties;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionContextFactory;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
|
||||
import com.opensymphony.xwork2.validator.ActionValidatorManager;
|
||||
import ognl.MethodAccessor;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
import org.apache.struts2.components.UrlRenderer;
|
||||
import org.apache.struts2.components.date.DateFormatter;
|
||||
@@ -70,7 +67,6 @@ import org.apache.struts2.dispatcher.StaticContentLoader;
|
||||
import org.apache.struts2.dispatcher.mapper.ActionMapper;
|
||||
import org.apache.struts2.dispatcher.multipart.MultiPartRequest;
|
||||
import org.apache.struts2.interceptor.exec.ExecutorProvider;
|
||||
import org.apache.struts2.ognl.OgnlGuard;
|
||||
import org.apache.struts2.url.QueryStringBuilder;
|
||||
import org.apache.struts2.url.QueryStringParser;
|
||||
import org.apache.struts2.url.UrlDecoder;
|
||||
@@ -389,9 +385,6 @@ public class StrutsBeanSelectionProvider extends AbstractBeanSelectionProvider {
|
||||
|
||||
alias(FileManagerFactory.class, StrutsConstants.STRUTS_FILE_MANAGER_FACTORY, builder, props, Scope.SINGLETON);
|
||||
|
||||
alias(RootAccessor.class, StrutsConstants.STRUTS_COMPOUND_ROOT_ACCESSOR, builder, props);
|
||||
alias(MethodAccessor.class, StrutsConstants.STRUTS_METHOD_ACCESSOR, builder, props);
|
||||
|
||||
alias(XWorkConverter.class, StrutsConstants.STRUTS_XWORKCONVERTER, builder, props);
|
||||
alias(CollectionConverter.class, StrutsConstants.STRUTS_CONVERTER_COLLECTION, builder, props);
|
||||
alias(ArrayConverter.class, StrutsConstants.STRUTS_CONVERTER_ARRAY, builder, props);
|
||||
@@ -433,16 +426,14 @@ public class StrutsBeanSelectionProvider extends AbstractBeanSelectionProvider {
|
||||
/** Checker is used mostly in interceptors, so there be one instance of checker per interceptor with Scope.PROTOTYPE **/
|
||||
alias(ExcludedPatternsChecker.class, StrutsConstants.STRUTS_EXCLUDED_PATTERNS_CHECKER, builder, props, Scope.PROTOTYPE);
|
||||
alias(AcceptedPatternsChecker.class, StrutsConstants.STRUTS_ACCEPTED_PATTERNS_CHECKER, builder, props, Scope.PROTOTYPE);
|
||||
alias(NotExcludedAcceptedPatternsChecker.class, StrutsConstants.STRUTS_NOT_EXCLUDED_ACCEPTED_PATTERNS_CHECKER, builder, props, Scope.SINGLETON);
|
||||
alias(NotExcludedAcceptedPatternsChecker.class, StrutsConstants.STRUTS_NOT_EXCLUDED_ACCEPTED_PATTERNS_CHECKER
|
||||
, builder, props, Scope.SINGLETON);
|
||||
|
||||
alias(DateFormatter.class, StrutsConstants.STRUTS_DATE_FORMATTER, builder, props, Scope.SINGLETON);
|
||||
|
||||
alias(ExpressionCacheFactory.class, StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_FACTORY, builder, props, Scope.SINGLETON);
|
||||
alias(BeanInfoCacheFactory.class, StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_FACTORY, builder, props, Scope.SINGLETON);
|
||||
|
||||
alias(SecurityMemberAccess.class, StrutsConstants.STRUTS_MEMBER_ACCESS, builder, props, Scope.PROTOTYPE);
|
||||
alias(OgnlGuard.class, StrutsConstants.STRUTS_OGNL_GUARD, builder, props, Scope.SINGLETON);
|
||||
|
||||
alias(QueryStringBuilder.class, StrutsConstants.STRUTS_URL_QUERY_STRING_BUILDER, builder, props, Scope.SINGLETON);
|
||||
alias(QueryStringParser.class, StrutsConstants.STRUTS_URL_QUERY_STRING_PARSER, builder, props, Scope.SINGLETON);
|
||||
alias(UrlEncoder.class, StrutsConstants.STRUTS_URL_ENCODER, builder, props, Scope.SINGLETON);
|
||||
|
||||
@@ -23,25 +23,25 @@ import com.opensymphony.xwork2.inject.Container;
|
||||
/**
|
||||
* Simple class to hold Container instance per thread to minimise number of attempts
|
||||
* to read configuration and build each time a new configuration.
|
||||
* <p>
|
||||
*
|
||||
* As ContainerHolder operates just per thread (which means per request) there is no need
|
||||
* to check if configuration changed during the same request. If changed between requests,
|
||||
* first call to store Container in ContainerHolder will be with the new configuration.
|
||||
*/
|
||||
class ContainerHolder {
|
||||
|
||||
private static final ThreadLocal<Container> instance = new ThreadLocal<>();
|
||||
private static ThreadLocal<Container> instance = new ThreadLocal<>();
|
||||
|
||||
public static void store(Container newInstance) {
|
||||
instance.set(newInstance);
|
||||
public static void store(Container instance) {
|
||||
ContainerHolder.instance.set(instance);
|
||||
}
|
||||
|
||||
public static Container get() {
|
||||
return instance.get();
|
||||
return ContainerHolder.instance.get();
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
instance.remove();
|
||||
ContainerHolder.instance.remove();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -64,10 +64,10 @@ import org.apache.struts2.config.StrutsBeanSelectionProvider;
|
||||
import org.apache.struts2.config.StrutsJavaConfiguration;
|
||||
import org.apache.struts2.config.StrutsJavaConfigurationProvider;
|
||||
import org.apache.struts2.config.StrutsXmlConfigurationProvider;
|
||||
import org.apache.struts2.dispatcher.mapper.ActionMapper;
|
||||
import org.apache.struts2.dispatcher.mapper.ActionMapping;
|
||||
import org.apache.struts2.dispatcher.multipart.MultiPartRequest;
|
||||
import org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper;
|
||||
import org.apache.struts2.util.AttributeMap;
|
||||
import org.apache.struts2.util.ObjectFactoryDestroyable;
|
||||
import org.apache.struts2.util.fs.JBossFileManager;
|
||||
|
||||
@@ -120,12 +120,6 @@ public class Dispatcher {
|
||||
*/
|
||||
private static final List<DispatcherListener> dispatcherListeners = new CopyOnWriteArrayList<>();
|
||||
|
||||
/**
|
||||
* This field exists so {@link #getContainer()} can determine whether to (re-)inject this instance in the case of
|
||||
* a {@link ConfigurationManager} reload.
|
||||
*/
|
||||
private Container injectedContainer;
|
||||
|
||||
/**
|
||||
* Store state of StrutsConstants.STRUTS_DEVMODE setting.
|
||||
*/
|
||||
@@ -151,6 +145,11 @@ public class Dispatcher {
|
||||
*/
|
||||
private String multipartSaveDir;
|
||||
|
||||
/**
|
||||
* Stores the value of {@link StrutsConstants#STRUTS_MULTIPART_PARSER} setting
|
||||
*/
|
||||
private String multipartHandlerName;
|
||||
|
||||
/**
|
||||
* Stores the value of {@link StrutsConstants#STRUTS_MULTIPART_ENABLED}
|
||||
*/
|
||||
@@ -194,11 +193,6 @@ public class Dispatcher {
|
||||
* Store ConfigurationManager instance, set on init.
|
||||
*/
|
||||
protected ConfigurationManager configurationManager;
|
||||
private ObjectFactory objectFactory;
|
||||
private ActionProxyFactory actionProxyFactory;
|
||||
private LocaleProviderFactory localeProviderFactory;
|
||||
private StaticContentLoader staticContentLoader;
|
||||
private ActionMapper actionMapper;
|
||||
|
||||
/**
|
||||
* Provide the dispatcher instance for the current thread.
|
||||
@@ -218,13 +212,6 @@ public class Dispatcher {
|
||||
Dispatcher.instance.set(instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the dispatcher instance for this thread.
|
||||
*/
|
||||
public static void clearInstance() {
|
||||
Dispatcher.instance.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a dispatcher lifecycle listener.
|
||||
*
|
||||
@@ -320,12 +307,9 @@ public class Dispatcher {
|
||||
multipartSaveDir = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 6.4.0, no replacement.
|
||||
*/
|
||||
@Deprecated
|
||||
@Inject(StrutsConstants.STRUTS_MULTIPART_PARSER)
|
||||
public void setMultipartHandler(String val) {
|
||||
// no-op
|
||||
multipartHandlerName = val;
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_MULTIPART_ENABLED, required = false)
|
||||
@@ -343,21 +327,11 @@ public class Dispatcher {
|
||||
this.valueStackFactory = valueStackFactory;
|
||||
}
|
||||
|
||||
public ValueStackFactory getValueStackFactory() {
|
||||
return valueStackFactory;
|
||||
}
|
||||
|
||||
@Inject(StrutsConstants.STRUTS_HANDLE_EXCEPTION)
|
||||
public void setHandleException(String handleException) {
|
||||
this.handleException = Boolean.parseBoolean(handleException);
|
||||
}
|
||||
|
||||
@Inject(StrutsConstants.STRUTS_DISPATCHER_PARAMETERSWORKAROUND)
|
||||
public void setDispatchersParametersWorkaround(String dispatchersParametersWorkaround) {
|
||||
this.paramsWorkaroundEnabled = Boolean.parseBoolean(dispatchersParametersWorkaround)
|
||||
|| (servletContext != null && StringUtils.contains(servletContext.getServerInfo(), "WebLogic"));
|
||||
}
|
||||
|
||||
public boolean isHandleException() {
|
||||
return handleException;
|
||||
}
|
||||
@@ -367,48 +341,12 @@ public class Dispatcher {
|
||||
this.errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setObjectFactory(ObjectFactory objectFactory) {
|
||||
this.objectFactory = objectFactory;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setActionProxyFactory(ActionProxyFactory actionProxyFactory) {
|
||||
this.actionProxyFactory = actionProxyFactory;
|
||||
}
|
||||
|
||||
public ActionProxyFactory getActionProxyFactory() {
|
||||
return actionProxyFactory;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setLocaleProviderFactory(LocaleProviderFactory localeProviderFactory) {
|
||||
this.localeProviderFactory = localeProviderFactory;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setStaticContentLoader(StaticContentLoader staticContentLoader) {
|
||||
this.staticContentLoader = staticContentLoader;
|
||||
}
|
||||
|
||||
public StaticContentLoader getStaticContentLoader() {
|
||||
return staticContentLoader;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setActionMapper(ActionMapper actionMapper) {
|
||||
this.actionMapper = actionMapper;
|
||||
}
|
||||
|
||||
public ActionMapper getActionMapper() {
|
||||
return actionMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases all instances bound to this dispatcher instance.
|
||||
*/
|
||||
public void cleanup() {
|
||||
// clean up ObjectFactory
|
||||
ObjectFactory objectFactory = getContainer().getInstance(ObjectFactory.class);
|
||||
if (objectFactory == null) {
|
||||
LOG.warn("Object Factory is null, something is seriously wrong, no clean up will be performed");
|
||||
}
|
||||
@@ -422,7 +360,7 @@ public class Dispatcher {
|
||||
}
|
||||
|
||||
// clean up Dispatcher itself for this thread
|
||||
instance.remove();
|
||||
instance.set(null);
|
||||
servletContext.setAttribute(StrutsStatics.SERVLET_DISPATCHER, null);
|
||||
|
||||
// clean up DispatcherListeners
|
||||
@@ -595,6 +533,21 @@ public class Dispatcher {
|
||||
loadConfigPaths("struts-deferred.xml");
|
||||
}
|
||||
|
||||
private Container init_PreloadConfiguration() {
|
||||
return getContainer();
|
||||
}
|
||||
|
||||
private void init_CheckWebLogicWorkaround(Container container) {
|
||||
// test whether param-access workaround needs to be enabled
|
||||
if (servletContext != null && StringUtils.contains(servletContext.getServerInfo(), "WebLogic")) {
|
||||
LOG.info("WebLogic server detected. Enabling Struts parameter access work-around.");
|
||||
paramsWorkaroundEnabled = true;
|
||||
} else {
|
||||
paramsWorkaroundEnabled = "true".equals(container.getInstance(String.class,
|
||||
StrutsConstants.STRUTS_DISPATCHER_PARAMETERSWORKAROUND));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load configurations, including both XML and zero-configuration strategies,
|
||||
* and update optional settings, including whether to reload configurations and resource files.
|
||||
@@ -615,7 +568,9 @@ public class Dispatcher {
|
||||
init_AliasStandardObjects(); // [7]
|
||||
init_DeferredXmlConfigurations();
|
||||
|
||||
getContainer(); // Inject this instance
|
||||
Container container = init_PreloadConfiguration();
|
||||
container.inject(this);
|
||||
init_CheckWebLogicWorkaround(container);
|
||||
|
||||
if (!dispatcherListeners.isEmpty()) {
|
||||
for (DispatcherListener l : dispatcherListeners) {
|
||||
@@ -735,6 +690,7 @@ public class Dispatcher {
|
||||
}
|
||||
|
||||
protected ActionProxy createActionProxy(String namespace, String name, String method, Map<String, Object> extraContext) {
|
||||
ActionProxyFactory actionProxyFactory = getContainer().getInstance(ActionProxyFactory.class);
|
||||
return actionProxyFactory.createActionProxy(namespace, name, method, extraContext, true, false);
|
||||
}
|
||||
|
||||
@@ -823,14 +779,14 @@ public class Dispatcher {
|
||||
.withServletResponse(response)
|
||||
.withServletContext(servletContext)
|
||||
// helpers to get access to request/session/application scope
|
||||
.with(DispatcherConstants.REQUEST, requestMap)
|
||||
.with(DispatcherConstants.SESSION, sessionMap)
|
||||
.with(DispatcherConstants.APPLICATION, applicationMap)
|
||||
.with(DispatcherConstants.PARAMETERS, parameters)
|
||||
.with("request", requestMap)
|
||||
.with("session", sessionMap)
|
||||
.with("application", applicationMap)
|
||||
.with("parameters", parameters)
|
||||
.getContextMap();
|
||||
|
||||
AttributeMap attrMap = new AttributeMap(extraContext);
|
||||
extraContext.put(DispatcherConstants.ATTRIBUTES, attrMap);
|
||||
extraContext.put("attr", attrMap);
|
||||
|
||||
return extraContext;
|
||||
}
|
||||
@@ -910,7 +866,6 @@ public class Dispatcher {
|
||||
* @param response The response
|
||||
*/
|
||||
public void prepare(HttpServletRequest request, HttpServletResponse response) {
|
||||
getContainer(); // Init ContainerHolder and reinject this instance IF ConfigurationManager was reloaded
|
||||
String encoding = null;
|
||||
if (defaultEncoding != null) {
|
||||
encoding = defaultEncoding;
|
||||
@@ -983,12 +938,15 @@ public class Dispatcher {
|
||||
}
|
||||
|
||||
if (isMultipartSupportEnabled(request) && isMultipartRequest(request)) {
|
||||
MultiPartRequest multiPartRequest = getMultiPartRequest();
|
||||
LocaleProviderFactory localeProviderFactory = getContainer().getInstance(LocaleProviderFactory.class);
|
||||
|
||||
request = new MultiPartRequestWrapper(
|
||||
getMultiPartRequest(),
|
||||
request,
|
||||
getSaveDir(),
|
||||
localeProviderFactory.createLocaleProvider(),
|
||||
disableRequestAttributeValueStackLookup
|
||||
multiPartRequest,
|
||||
request,
|
||||
getSaveDir(),
|
||||
localeProviderFactory.createLocaleProvider(),
|
||||
disableRequestAttributeValueStackLookup
|
||||
);
|
||||
} else {
|
||||
request = new StrutsRequestWrapper(request, disableRequestAttributeValueStackLookup);
|
||||
@@ -1031,7 +989,18 @@ public class Dispatcher {
|
||||
* @return a multi part request object
|
||||
*/
|
||||
protected MultiPartRequest getMultiPartRequest() {
|
||||
return getContainer().getInstance(MultiPartRequest.class);
|
||||
MultiPartRequest mpr = null;
|
||||
//check for alternate implementations of MultiPartRequest
|
||||
Set<String> multiNames = getContainer().getInstanceNames(MultiPartRequest.class);
|
||||
for (String multiName : multiNames) {
|
||||
if (multiName.equals(multipartHandlerName)) {
|
||||
mpr = getContainer().getInstance(MultiPartRequest.class, multiName);
|
||||
}
|
||||
}
|
||||
if (mpr == null) {
|
||||
mpr = getContainer().getInstance(MultiPartRequest.class);
|
||||
}
|
||||
return mpr;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1095,26 +1064,27 @@ public class Dispatcher {
|
||||
}
|
||||
|
||||
/**
|
||||
* Exposes a thread-cached reference of the dependency injection container. If the container is found to have
|
||||
* changed since the last time it was cached, this Dispatcher instance is re-injected to ensure no stale
|
||||
* configuration/dependencies persist.
|
||||
* <p>
|
||||
* A non-cached reference can be obtained by calling {@link #getConfigurationManager()}.
|
||||
* Expose the dependency injection container.
|
||||
*
|
||||
* @return Our dependency injection container
|
||||
*/
|
||||
public Container getContainer() {
|
||||
if (ContainerHolder.get() == null) {
|
||||
try {
|
||||
ContainerHolder.store(getConfigurationManager().getConfiguration().getContainer());
|
||||
} catch (NullPointerException e) {
|
||||
throw new IllegalStateException("ConfigurationManager and/or Configuration should not be null", e);
|
||||
if (ContainerHolder.get() != null) {
|
||||
return ContainerHolder.get();
|
||||
}
|
||||
ConfigurationManager mgr = getConfigurationManager();
|
||||
if (mgr == null) {
|
||||
throw new IllegalStateException("The configuration manager shouldn't be null");
|
||||
} else {
|
||||
Configuration config = mgr.getConfiguration();
|
||||
if (config == null) {
|
||||
throw new IllegalStateException("Unable to load configuration");
|
||||
} else {
|
||||
Container container = config.getContainer();
|
||||
ContainerHolder.store(container);
|
||||
return container;
|
||||
}
|
||||
}
|
||||
if (injectedContainer != ContainerHolder.get()) {
|
||||
injectedContainer = ContainerHolder.get();
|
||||
injectedContainer.inject(this);
|
||||
}
|
||||
return ContainerHolder.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
*/
|
||||
package org.apache.struts2.dispatcher;
|
||||
|
||||
import org.apache.struts2.RequestUtils;
|
||||
import org.apache.struts2.dispatcher.mapper.ActionMapping;
|
||||
import org.apache.struts2.RequestUtils;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@@ -54,7 +54,7 @@ public class ExecuteOperations {
|
||||
resourcePath = request.getPathInfo();
|
||||
}
|
||||
|
||||
StaticContentLoader staticResourceLoader = dispatcher.getStaticContentLoader();
|
||||
StaticContentLoader staticResourceLoader = dispatcher.getContainer().getInstance(StaticContentLoader.class);
|
||||
if (staticResourceLoader.canHandle(resourcePath)) {
|
||||
staticResourceLoader.findStaticResource(resourcePath, request, response);
|
||||
// The framework did its job here
|
||||
|
||||
@@ -34,8 +34,7 @@ public class HttpParameters implements Map<String, Parameter> {
|
||||
final private Map<String, Parameter> parameters;
|
||||
|
||||
private HttpParameters(Map<String, Parameter> parameters) {
|
||||
this.parameters = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
|
||||
this.parameters.putAll(parameters);
|
||||
this.parameters = parameters;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@@ -44,12 +43,12 @@ public class HttpParameters implements Map<String, Parameter> {
|
||||
}
|
||||
|
||||
public static Builder create() {
|
||||
return new Builder(new TreeMap<>(String.CASE_INSENSITIVE_ORDER));
|
||||
return new Builder(new HashMap<>());
|
||||
}
|
||||
|
||||
public HttpParameters remove(Set<String> paramsToRemove) {
|
||||
for (String paramName : paramsToRemove) {
|
||||
parameters.remove(paramName);
|
||||
parameters.entrySet().removeIf(p -> p.getKey().equalsIgnoreCase(paramName));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@@ -61,7 +60,7 @@ public class HttpParameters implements Map<String, Parameter> {
|
||||
}
|
||||
|
||||
public boolean contains(String name) {
|
||||
return parameters.containsKey(name);
|
||||
return parameters.keySet().stream().anyMatch(p -> p.equalsIgnoreCase(name));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,6 +84,7 @@ public class HttpParameters implements Map<String, Parameter> {
|
||||
* @return a current instance of {@link HttpParameters}
|
||||
*/
|
||||
public HttpParameters appendAll(Map<String, Parameter> newParams) {
|
||||
remove(newParams.keySet());
|
||||
parameters.putAll(newParams);
|
||||
return this;
|
||||
}
|
||||
@@ -111,11 +111,14 @@ public class HttpParameters implements Map<String, Parameter> {
|
||||
|
||||
@Override
|
||||
public Parameter get(Object key) {
|
||||
if (key == null) {
|
||||
return new Parameter.Empty("null");
|
||||
if (key != null && contains(String.valueOf(key))) {
|
||||
return parameters.entrySet().stream()
|
||||
.filter(p -> p.getKey().equalsIgnoreCase(String.valueOf(key)))
|
||||
.findFirst().map(Entry::getValue)
|
||||
.orElse(new Parameter.Empty(String.valueOf(key)));
|
||||
} else {
|
||||
return new Parameter.Empty(String.valueOf(key));
|
||||
}
|
||||
Parameter val = parameters.get(key.toString());
|
||||
return val != null ? val : new Parameter.Empty(key.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -57,7 +57,7 @@ public class InitOperations {
|
||||
* @return the static content loader
|
||||
*/
|
||||
public StaticContentLoader initStaticContentLoader(HostConfig filterConfig, Dispatcher dispatcher) {
|
||||
StaticContentLoader loader = dispatcher.getStaticContentLoader();
|
||||
StaticContentLoader loader = dispatcher.getContainer().getInstance(StaticContentLoader.class);
|
||||
loader.setHostConfig(filterConfig);
|
||||
return loader;
|
||||
}
|
||||
|
||||
+18
-9
@@ -18,16 +18,25 @@
|
||||
*/
|
||||
package org.apache.struts2.dispatcher;
|
||||
|
||||
public final class DispatcherConstants {
|
||||
import com.opensymphony.xwork2.config.ConfigurationManager;
|
||||
|
||||
public static final String REQUEST = "request";
|
||||
public static final String RESPONSE = "response";
|
||||
public static final String SESSION = "session";
|
||||
public static final String APPLICATION = "application";
|
||||
public static final String PARAMETERS = "parameters";
|
||||
public static final String ATTRIBUTES = "attr";
|
||||
public static final String PAGE = "page";
|
||||
import javax.servlet.ServletContext;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
private DispatcherConstants() {
|
||||
public class MockDispatcher extends Dispatcher {
|
||||
|
||||
private final ConfigurationManager copyConfigurationManager;
|
||||
|
||||
public MockDispatcher(ServletContext servletContext, Map<String, String> context, ConfigurationManager configurationManager) {
|
||||
super(servletContext, context);
|
||||
this.copyConfigurationManager = configurationManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
super.init();
|
||||
ContainerHolder.clear();
|
||||
this.configurationManager = copyConfigurationManager;
|
||||
}
|
||||
}
|
||||
@@ -18,12 +18,12 @@
|
||||
*/
|
||||
package org.apache.struts2.dispatcher;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.apache.commons.text.StringEscapeUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public interface Parameter {
|
||||
|
||||
String getName();
|
||||
@@ -58,7 +58,7 @@ public interface Parameter {
|
||||
@Override
|
||||
public String getValue() {
|
||||
String[] values = toStringArray();
|
||||
return values.length > 0 ? values[0] : null;
|
||||
return (values != null && values.length > 0) ? values[0] : null;
|
||||
}
|
||||
|
||||
private String[] toStringArray() {
|
||||
@@ -124,7 +124,7 @@ public interface Parameter {
|
||||
|
||||
class Empty implements Parameter {
|
||||
|
||||
private final String name;
|
||||
private String name;
|
||||
|
||||
public Empty(String name) {
|
||||
this.name = name;
|
||||
@@ -166,19 +166,6 @@ public interface Parameter {
|
||||
"name='" + name + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof Empty)) return false;
|
||||
Empty empty = (Empty) o;
|
||||
return Objects.equals(name, empty.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(name);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,11 +20,13 @@ package org.apache.struts2.dispatcher;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import com.opensymphony.xwork2.util.ValueStackFactory;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.RequestUtils;
|
||||
import org.apache.struts2.ServletActionContext;
|
||||
import org.apache.struts2.StrutsException;
|
||||
import org.apache.struts2.dispatcher.mapper.ActionMapper;
|
||||
import org.apache.struts2.dispatcher.mapper.ActionMapping;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
@@ -76,7 +78,7 @@ public class PrepareOperations {
|
||||
dispatcher.cleanUpRequest(request);
|
||||
} finally {
|
||||
ActionContext.clear();
|
||||
Dispatcher.clearInstance();
|
||||
Dispatcher.setInstance(null);
|
||||
devModeOverride.remove();
|
||||
}
|
||||
});
|
||||
@@ -99,7 +101,7 @@ public class PrepareOperations {
|
||||
} else {
|
||||
ctx = ServletActionContext.getActionContext(request); //checks if we are probably in an async
|
||||
if (ctx == null) {
|
||||
ValueStack stack = dispatcher.getValueStackFactory().createValueStack();
|
||||
ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
|
||||
stack.getContext().putAll(dispatcher.createContextMap(request, response, null));
|
||||
ctx = ActionContext.of(stack.getContext()).bind();
|
||||
}
|
||||
@@ -186,7 +188,7 @@ public class PrepareOperations {
|
||||
Object mappingAttr = request.getAttribute(STRUTS_ACTION_MAPPING_KEY);
|
||||
if (mappingAttr == null || forceLookup) {
|
||||
try {
|
||||
mapping = dispatcher.getActionMapper().getMapping(request, dispatcher.getConfigurationManager());
|
||||
mapping = dispatcher.getContainer().getInstance(ActionMapper.class).getMapping(request, dispatcher.getConfigurationManager());
|
||||
if (mapping != null) {
|
||||
request.setAttribute(STRUTS_ACTION_MAPPING_KEY, mapping);
|
||||
} else {
|
||||
|
||||
@@ -32,9 +32,8 @@ public class RequestMap extends AbstractMap<String, Object> implements Serializa
|
||||
|
||||
private static final long serialVersionUID = -7675640869293787926L;
|
||||
|
||||
private final HttpServletRequest request;
|
||||
|
||||
private Set<Entry<String, Object>> entries;
|
||||
private HttpServletRequest request;
|
||||
|
||||
/**
|
||||
* Saves the request to use as the backing for getting and setting values
|
||||
@@ -45,6 +44,7 @@ public class RequestMap extends AbstractMap<String, Object> implements Serializa
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes all attributes from the request as well as clears entries in this map.
|
||||
*/
|
||||
|
||||
+16
-19
@@ -100,7 +100,7 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
|
||||
protected void processUpload(HttpServletRequest request, String saveDir) throws FileUploadException, UnsupportedEncodingException {
|
||||
if (ServletFileUpload.isMultipartContent(request)) {
|
||||
for (FileItem item : parseRequest(request, saveDir)) {
|
||||
LOG.debug("Found file item: [{}]", sanitizeNewlines(item.getFieldName()));
|
||||
LOG.debug("Found file item: [{}]", item.getFieldName());
|
||||
if (item.isFormField()) {
|
||||
processNormalFormField(item, request.getCharacterEncoding());
|
||||
} else {
|
||||
@@ -115,7 +115,7 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
|
||||
|
||||
// Skip file uploads that don't have a file name - meaning that no file was selected.
|
||||
if (item.getName() == null || item.getName().trim().isEmpty()) {
|
||||
LOG.debug("No file has been uploaded for the field: {}", sanitizeNewlines(item.getFieldName()));
|
||||
LOG.debug("No file has been uploaded for the field: {}", item.getFieldName());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -142,22 +142,26 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
|
||||
}
|
||||
|
||||
long size = item.getSize();
|
||||
if (size > maxStringLength) {
|
||||
LOG.debug("Form field {} of size {} bytes exceeds limit of {}.", sanitizeNewlines(item.getFieldName()), size, maxStringLength);
|
||||
if (size == 0) {
|
||||
values.add(StringUtils.EMPTY);
|
||||
} else if (size > maxStringLength) {
|
||||
String errorKey = "struts.messages.upload.error.parameter.too.long";
|
||||
LocalizedMessage localizedMessage = new LocalizedMessage(this.getClass(), errorKey, null,
|
||||
new Object[]{item.getFieldName(), maxStringLength, size});
|
||||
new Object[]{item.getFieldName(), maxStringLength, size});
|
||||
|
||||
if (!errors.contains(localizedMessage)) {
|
||||
errors.add(localizedMessage);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (size == 0) {
|
||||
values.add(StringUtils.EMPTY);
|
||||
} else if (charset == null) {
|
||||
values.add(item.getString()); // WW-633
|
||||
} else {
|
||||
|
||||
} else if (charset != null) {
|
||||
values.add(item.getString(charset));
|
||||
} else {
|
||||
// note: see https://issues.apache.org/jira/browse/WW-633
|
||||
// basically, in some cases the charset may be null, so
|
||||
// we're just going to try to "other" method (no idea if this
|
||||
// will work)
|
||||
values.add(item.getString());
|
||||
}
|
||||
params.put(item.getFieldName(), values);
|
||||
} finally {
|
||||
@@ -243,11 +247,7 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
|
||||
LOG.error("Cannot write uploaded empty file to disk: {}", storeLocation.getAbsolutePath(), e);
|
||||
}
|
||||
}
|
||||
UploadedFile uploadedFile = StrutsUploadedFile.Builder.create(storeLocation)
|
||||
.withContentType(fileItem.getContentType())
|
||||
.withOriginalName(fileItem.getName())
|
||||
.build();
|
||||
fileList.add(uploadedFile);
|
||||
fileList.add(new StrutsUploadedFile(storeLocation));
|
||||
}
|
||||
|
||||
return fileList.toArray(new UploadedFile[0]);
|
||||
@@ -366,7 +366,4 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
|
||||
}
|
||||
}
|
||||
|
||||
private String sanitizeNewlines(String before) {
|
||||
return before.replaceAll("[\n\r]", "_");
|
||||
}
|
||||
}
|
||||
|
||||
+21
-21
@@ -22,7 +22,6 @@ import org.apache.commons.fileupload.FileItemIterator;
|
||||
import org.apache.commons.fileupload.FileItemStream;
|
||||
import org.apache.commons.fileupload.FileUploadBase;
|
||||
import org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException;
|
||||
import org.apache.commons.fileupload.FileUploadException;
|
||||
import org.apache.commons.fileupload.servlet.ServletFileUpload;
|
||||
import org.apache.commons.fileupload.util.Streams;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
@@ -109,12 +108,12 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
|
||||
return null;
|
||||
}
|
||||
|
||||
return infos.stream().map(fileInfo ->
|
||||
StrutsUploadedFile.Builder.create(fileInfo.getFile())
|
||||
.withContentType(fileInfo.contentType)
|
||||
.withOriginalName(fileInfo.originalName)
|
||||
.build()
|
||||
).toArray(UploadedFile[]::new);
|
||||
List<UploadedFile> files = new ArrayList<>(infos.size());
|
||||
for (FileInfo fileInfo : infos) {
|
||||
files.add(new StrutsUploadedFile(fileInfo.getFile()));
|
||||
}
|
||||
|
||||
return files.toArray(new UploadedFile[0]);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
@@ -163,7 +162,7 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
|
||||
*/
|
||||
public String getParameter(String name) {
|
||||
List<String> values = parameters.get(name);
|
||||
if (values != null && !values.isEmpty()) {
|
||||
if (values != null && values.size() > 0) {
|
||||
return values.get(0);
|
||||
}
|
||||
return null;
|
||||
@@ -181,7 +180,7 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
|
||||
*/
|
||||
public String[] getParameterValues(String name) {
|
||||
List<String> values = parameters.get(name);
|
||||
if (values != null && !values.isEmpty()) {
|
||||
if (values != null && values.size() > 0) {
|
||||
return values.toArray(new String[0]);
|
||||
}
|
||||
return null;
|
||||
@@ -208,8 +207,9 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
|
||||
*
|
||||
* @param request the servlet request
|
||||
* @param saveDir location of the save dir
|
||||
* @throws Exception
|
||||
*/
|
||||
protected void processUpload(HttpServletRequest request, String saveDir) throws IOException, FileUploadException {
|
||||
protected void processUpload(HttpServletRequest request, String saveDir) throws Exception {
|
||||
|
||||
// Sanity check that the request is a multi-part/form-data request.
|
||||
if (ServletFileUpload.isMultipartContent(request)) {
|
||||
@@ -292,7 +292,7 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
|
||||
* Add a file skipped message notification for action messages.
|
||||
*
|
||||
* @param fileName file name
|
||||
* @param request the servlet request
|
||||
* @param request the servlet request
|
||||
*/
|
||||
protected void addFileSkippedError(String fileName, HttpServletRequest request) {
|
||||
String exceptionMessage = "Skipped file " + fileName + "; request size limit exceeded.";
|
||||
@@ -330,11 +330,11 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
|
||||
* Processes the FileItemStream as a file field.
|
||||
*
|
||||
* @param itemStream file item stream
|
||||
* @param location location
|
||||
* @param location location
|
||||
*/
|
||||
protected void processFileItemStreamAsFileField(FileItemStream itemStream, String location) {
|
||||
// Skip file uploads that don't have a file name - meaning that no file was selected.
|
||||
if (itemStream.getName() == null || itemStream.getName().trim().isEmpty()) {
|
||||
if (itemStream.getName() == null || itemStream.getName().trim().length() < 1) {
|
||||
LOG.debug("No file has been uploaded for the field: {}", itemStream.getFieldName());
|
||||
return;
|
||||
}
|
||||
@@ -368,8 +368,8 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
|
||||
*/
|
||||
protected File createTemporaryFile(String fileName, String location) throws IOException {
|
||||
String name = fileName
|
||||
.substring(fileName.lastIndexOf('/') + 1)
|
||||
.substring(fileName.lastIndexOf('\\') + 1);
|
||||
.substring(fileName.lastIndexOf('/') + 1)
|
||||
.substring(fileName.lastIndexOf('\\') + 1);
|
||||
|
||||
String prefix = name;
|
||||
String suffix = "";
|
||||
@@ -392,14 +392,14 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
|
||||
* Streams the file upload stream to the specified file.
|
||||
*
|
||||
* @param itemStream file item stream
|
||||
* @param file the file
|
||||
* @param file the file
|
||||
* @return true if stream was successfully
|
||||
* @throws IOException in case of IO errors
|
||||
*/
|
||||
protected boolean streamFileToDisk(FileItemStream itemStream, File file) throws IOException {
|
||||
boolean result;
|
||||
try (InputStream input = itemStream.openStream();
|
||||
OutputStream output = new BufferedOutputStream(Files.newOutputStream(file.toPath()), bufferSize)) {
|
||||
OutputStream output = new BufferedOutputStream(Files.newOutputStream(file.toPath()), bufferSize)) {
|
||||
byte[] buffer = new byte[bufferSize];
|
||||
LOG.debug("Streaming file using buffer size {}.", bufferSize);
|
||||
for (int length; ((length = input.read(buffer)) > 0); ) {
|
||||
@@ -416,7 +416,7 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
|
||||
* invocation process.
|
||||
*
|
||||
* @param itemStream file item stream
|
||||
* @param file the file
|
||||
* @param file the file
|
||||
*/
|
||||
protected void createFileInfoFromItemStream(FileItemStream itemStream, File file) {
|
||||
// gather attributes from file upload stream.
|
||||
@@ -440,7 +440,7 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
|
||||
*
|
||||
* @since 7.0.0
|
||||
*/
|
||||
public static class FileInfo implements Serializable {
|
||||
public static class FileInfo implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1083158552766906037L;
|
||||
|
||||
@@ -451,8 +451,8 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
|
||||
/**
|
||||
* Default constructor.
|
||||
*
|
||||
* @param file the file
|
||||
* @param contentType content type
|
||||
* @param file the file
|
||||
* @param contentType content type
|
||||
* @param originalName original file name
|
||||
*/
|
||||
public FileInfo(File file, String contentType, String originalName) {
|
||||
|
||||
@@ -22,26 +22,10 @@ import java.io.File;
|
||||
|
||||
public class StrutsUploadedFile implements UploadedFile {
|
||||
|
||||
private final File file;
|
||||
private final String contentType;
|
||||
private final String originalName;
|
||||
private File file;
|
||||
|
||||
/**
|
||||
* Use builder instead of constructor
|
||||
* @param file an uploaded file
|
||||
* @deprecated since Struts 6.4.0
|
||||
*/
|
||||
@Deprecated
|
||||
public StrutsUploadedFile(File file) {
|
||||
this.file = file;
|
||||
this.contentType = null;
|
||||
this.originalName = null;
|
||||
}
|
||||
|
||||
private StrutsUploadedFile(File file, String contentType, String originalName) {
|
||||
this.file = file;
|
||||
this.contentType = contentType;
|
||||
this.originalName = originalName;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -73,50 +57,4 @@ public class StrutsUploadedFile implements UploadedFile {
|
||||
public File getContent() {
|
||||
return file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentType() {
|
||||
return this.contentType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOriginalName() {
|
||||
return originalName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "StrutsUploadedFile{" +
|
||||
"contentType='" + contentType + '\'' +
|
||||
", originalName='" + originalName + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private final File file;
|
||||
private String contentType;
|
||||
private String originalName;
|
||||
|
||||
private Builder(File file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
public static Builder create(File file) {
|
||||
return new Builder(file);
|
||||
}
|
||||
|
||||
public Builder withContentType(String contentType) {
|
||||
this.contentType = contentType;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withOriginalName(String originalName) {
|
||||
this.originalName = originalName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UploadedFile build() {
|
||||
return new StrutsUploadedFile(this.file, this.contentType, this.originalName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,19 +18,15 @@
|
||||
*/
|
||||
package org.apache.struts2.dispatcher.multipart;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Virtual representation of a uploaded file used by {@link MultiPartRequest}
|
||||
*/
|
||||
public interface UploadedFile extends Serializable {
|
||||
public interface UploadedFile {
|
||||
|
||||
Long length();
|
||||
|
||||
String getName();
|
||||
|
||||
String getOriginalName();
|
||||
|
||||
boolean isFile();
|
||||
|
||||
boolean delete();
|
||||
@@ -39,6 +35,4 @@ public interface UploadedFile extends Serializable {
|
||||
|
||||
Object getContent();
|
||||
|
||||
String getContentType();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,263 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.interceptor;
|
||||
|
||||
import com.opensymphony.xwork2.LocaleProvider;
|
||||
import com.opensymphony.xwork2.LocaleProviderFactory;
|
||||
import com.opensymphony.xwork2.TextProvider;
|
||||
import com.opensymphony.xwork2.TextProviderFactory;
|
||||
import com.opensymphony.xwork2.inject.Container;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
|
||||
import com.opensymphony.xwork2.interceptor.ValidationAware;
|
||||
import com.opensymphony.xwork2.util.TextParseUtil;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.dispatcher.LocalizedMessage;
|
||||
import org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper;
|
||||
import org.apache.struts2.dispatcher.multipart.UploadedFile;
|
||||
import org.apache.struts2.util.ContentTypeMatcher;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public abstract class AbstractFileUploadInterceptor extends AbstractInterceptor {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(AbstractFileUploadInterceptor.class);
|
||||
|
||||
public static final String STRUTS_MESSAGES_BYPASS_REQUEST_KEY = "struts.messages.bypass.request";
|
||||
public static final String STRUTS_MESSAGES_ERROR_UPLOADING_KEY = "struts.messages.error.uploading";
|
||||
public static final String STRUTS_MESSAGES_ERROR_FILE_TOO_LARGE_KEY = "struts.messages.error.file.too.large";
|
||||
public static final String STRUTS_MESSAGES_INVALID_FILE_KEY = "struts.messages.invalid.file";
|
||||
public static final String STRUTS_MESSAGES_INVALID_CONTENT_TYPE_KEY = "struts.messages.invalid.content.type";
|
||||
public static final String STRUTS_MESSAGES_ERROR_CONTENT_TYPE_NOT_ALLOWED_KEY = "struts.messages.error.content.type.not.allowed";
|
||||
public static final String STRUTS_MESSAGES_ERROR_FILE_EXTENSION_NOT_ALLOWED_KEY = "struts.messages.error.file.extension.not.allowed";
|
||||
|
||||
private Long maximumSize;
|
||||
private Set<String> allowedTypesSet = Collections.emptySet();
|
||||
private Set<String> allowedExtensionsSet = Collections.emptySet();
|
||||
|
||||
private ContentTypeMatcher<Object> matcher;
|
||||
private Container container;
|
||||
|
||||
@Inject
|
||||
public void setMatcher(ContentTypeMatcher<Object> matcher) {
|
||||
this.matcher = matcher;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setContainer(Container container) {
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the allowed extensions
|
||||
*
|
||||
* @param allowedExtensions A comma-delimited list of extensions
|
||||
*/
|
||||
public void setAllowedExtensions(String allowedExtensions) {
|
||||
allowedExtensionsSet = TextParseUtil.commaDelimitedStringToSet(allowedExtensions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the allowed mimetypes
|
||||
*
|
||||
* @param allowedTypes A comma-delimited list of types
|
||||
*/
|
||||
public void setAllowedTypes(String allowedTypes) {
|
||||
allowedTypesSet = TextParseUtil.commaDelimitedStringToSet(allowedTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum size of an uploaded file
|
||||
*
|
||||
* @param maximumSize The maximum size in bytes
|
||||
*/
|
||||
public void setMaximumSize(Long maximumSize) {
|
||||
this.maximumSize = maximumSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override for added functionality. Checks if the proposed file is acceptable based on contentType and size.
|
||||
*
|
||||
* @param action - uploading action for message retrieval.
|
||||
* @param file - proposed upload file.
|
||||
* @param originalFilename - name of the file.
|
||||
* @param contentType - contentType of the file.
|
||||
* @param inputName - inputName of the file.
|
||||
* @return true if the proposed file is acceptable by contentType and size.
|
||||
*/
|
||||
protected boolean acceptFile(Object action, UploadedFile file, String originalFilename, String contentType, String inputName) {
|
||||
Set<String> errorMessages = new HashSet<>();
|
||||
|
||||
ValidationAware validation = null;
|
||||
if (action instanceof ValidationAware) {
|
||||
validation = (ValidationAware) action;
|
||||
}
|
||||
|
||||
// If it's null the upload failed
|
||||
if (file == null) {
|
||||
String errMsg = getTextMessage(action, STRUTS_MESSAGES_ERROR_UPLOADING_KEY, new String[]{inputName});
|
||||
if (validation != null) {
|
||||
validation.addFieldError(inputName, errMsg);
|
||||
}
|
||||
LOG.warn(errMsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file.getContent() == null) {
|
||||
String errMsg = getTextMessage(action, STRUTS_MESSAGES_ERROR_UPLOADING_KEY, new String[]{originalFilename});
|
||||
errorMessages.add(errMsg);
|
||||
LOG.warn(errMsg);
|
||||
}
|
||||
if (maximumSize != null && maximumSize < file.length()) {
|
||||
String errMsg = getTextMessage(action, STRUTS_MESSAGES_ERROR_FILE_TOO_LARGE_KEY, new String[]{
|
||||
inputName, originalFilename, file.getName(), "" + file.length(), getMaximumSizeStr(action)
|
||||
});
|
||||
errorMessages.add(errMsg);
|
||||
LOG.warn(errMsg);
|
||||
}
|
||||
if ((!allowedTypesSet.isEmpty()) && (!containsItem(allowedTypesSet, contentType))) {
|
||||
String errMsg = getTextMessage(action, STRUTS_MESSAGES_ERROR_CONTENT_TYPE_NOT_ALLOWED_KEY, new String[]{
|
||||
inputName, originalFilename, file.getName(), contentType
|
||||
});
|
||||
errorMessages.add(errMsg);
|
||||
LOG.warn(errMsg);
|
||||
}
|
||||
if ((!allowedExtensionsSet.isEmpty()) && (!hasAllowedExtension(allowedExtensionsSet, originalFilename))) {
|
||||
String errMsg = getTextMessage(action, STRUTS_MESSAGES_ERROR_FILE_EXTENSION_NOT_ALLOWED_KEY, new String[]{
|
||||
inputName, originalFilename, file.getName(), contentType
|
||||
});
|
||||
errorMessages.add(errMsg);
|
||||
LOG.warn(errMsg);
|
||||
}
|
||||
if (validation != null) {
|
||||
for (String errorMsg : errorMessages) {
|
||||
validation.addFieldError(inputName, errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
return errorMessages.isEmpty();
|
||||
}
|
||||
|
||||
private String getMaximumSizeStr(Object action) {
|
||||
return NumberFormat.getNumberInstance(getLocaleProvider(action).getLocale()).format(maximumSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param extensionCollection - Collection of extensions (all lowercase).
|
||||
* @param filename - filename to check.
|
||||
* @return true if the filename has an allowed extension, false otherwise.
|
||||
*/
|
||||
private boolean hasAllowedExtension(Collection<String> extensionCollection, String filename) {
|
||||
if (filename == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String lowercaseFilename = filename.toLowerCase();
|
||||
for (String extension : extensionCollection) {
|
||||
if (lowercaseFilename.endsWith(extension)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param itemCollection - Collection of string items (all lowercase).
|
||||
* @param item - Item to search for.
|
||||
* @return true if itemCollection contains the item, false otherwise.
|
||||
*/
|
||||
private boolean containsItem(Collection<String> itemCollection, String item) {
|
||||
for (String pattern : itemCollection)
|
||||
if (matchesWildcard(pattern, item))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean matchesWildcard(String pattern, String text) {
|
||||
Object o = matcher.compilePattern(pattern);
|
||||
return matcher.match(new HashMap<>(), text, o);
|
||||
}
|
||||
|
||||
protected boolean isNonEmpty(Object[] objArray) {
|
||||
boolean result = false;
|
||||
for (Object o : objArray) {
|
||||
if (o != null) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected String getTextMessage(String messageKey, String[] args) {
|
||||
return getTextMessage(this, messageKey, args);
|
||||
}
|
||||
|
||||
protected String getTextMessage(Object action, String messageKey, String[] args) {
|
||||
if (action instanceof TextProvider) {
|
||||
return ((TextProvider) action).getText(messageKey, args);
|
||||
}
|
||||
return getTextProvider(action).getText(messageKey, args);
|
||||
}
|
||||
|
||||
protected TextProvider getTextProvider(Object action) {
|
||||
TextProviderFactory tpf = container.getInstance(TextProviderFactory.class);
|
||||
return tpf.createInstance(action.getClass());
|
||||
}
|
||||
|
||||
private LocaleProvider getLocaleProvider(Object action) {
|
||||
LocaleProvider localeProvider;
|
||||
if (action instanceof LocaleProvider) {
|
||||
localeProvider = (LocaleProvider) action;
|
||||
} else {
|
||||
LocaleProviderFactory localeProviderFactory = container.getInstance(LocaleProviderFactory.class);
|
||||
localeProvider = localeProviderFactory.createLocaleProvider();
|
||||
}
|
||||
return localeProvider;
|
||||
}
|
||||
|
||||
protected void applyValidation(Object action, MultiPartRequestWrapper multiWrapper) {
|
||||
ValidationAware validation = null;
|
||||
if (action instanceof ValidationAware) {
|
||||
validation = (ValidationAware) action;
|
||||
}
|
||||
|
||||
if (multiWrapper.hasErrors() && validation != null) {
|
||||
TextProvider textProvider = getTextProvider(action);
|
||||
for (LocalizedMessage error : multiWrapper.getErrors()) {
|
||||
String errorMessage;
|
||||
if (textProvider.hasKey(error.getTextKey())) {
|
||||
errorMessage = textProvider.getText(error.getTextKey(), Arrays.asList(error.getArgs()));
|
||||
} else {
|
||||
errorMessage = textProvider.getText(STRUTS_MESSAGES_ERROR_UPLOADING_KEY, error.getDefaultMessage());
|
||||
}
|
||||
validation.addActionError(errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,191 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.interceptor;
|
||||
|
||||
import com.opensymphony.xwork2.ActionInvocation;
|
||||
import com.opensymphony.xwork2.ActionProxy;
|
||||
import com.opensymphony.xwork2.interceptor.ValidationAware;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.action.UploadedFilesAware;
|
||||
import org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper;
|
||||
import org.apache.struts2.dispatcher.multipart.UploadedFile;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Interceptor that is based off of {@link MultiPartRequestWrapper}, which is automatically applied for any request that
|
||||
* includes a file when the support for multi-part request is enabled,
|
||||
* see <a href="https://struts.apache.org/core-developers/file-upload.html#disabling-file-upload-support">Disabling file upload</a>.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* You can get access to these files by implementing {@link UploadedFilesAware} interface. The interceptor will then
|
||||
* call {@link UploadedFilesAware#withUploadedFiles(List)} when there are files which were accepted during the upload process.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This interceptor will add several field errors, assuming that the action implements {@link ValidationAware}.
|
||||
* These error messages are based on several i18n values stored in struts-messages.properties, a default i18n file
|
||||
* processed for all i18n requests. You can override the text of these messages by providing text for the following
|
||||
* keys:
|
||||
* </p>
|
||||
*
|
||||
* <ul>
|
||||
* <li>struts.messages.error.uploading - a general error that occurs when the file could not be uploaded</li>
|
||||
* <li>struts.messages.error.file.too.large - occurs when the uploaded file is too large</li>
|
||||
* <li>struts.messages.error.content.type.not.allowed - occurs when the uploaded file does not match the expected
|
||||
* content types specified</li>
|
||||
* <li>struts.messages.error.file.extension.not.allowed - occurs when the uploaded file does not match the expected
|
||||
* file extensions specified</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Interceptor parameters:</p>
|
||||
* <ul>
|
||||
* <li>maximumSize (optional) - the maximum size (in bytes) that the interceptor will allow a file reference to be set
|
||||
* on the action. Note, this is <b>not</b> related to the various properties found in struts.properties.
|
||||
* Default to approximately 2MB.</li>
|
||||
* <li>allowedTypes (optional) - a comma separated list of content types (ie: text/html) that the interceptor will allow
|
||||
* a file reference to be set on the action. If none is specified allow all types to be uploaded.</li>
|
||||
* <li>allowedExtensions (optional) - a comma separated list of file extensions (ie: .html) that the interceptor will allow
|
||||
* a file reference to be set on the action. If none is specified allow all extensions to be uploaded.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Example code:</p>
|
||||
*
|
||||
* <pre>
|
||||
* <action name="doUpload" class="com.example.UploadAction">
|
||||
* <interceptor-ref name="actionFileUpload"/>
|
||||
* <interceptor-ref name="basicStack"/>
|
||||
* <result name="success">good_result.jsp</result>
|
||||
* </action>
|
||||
* </pre>
|
||||
* <p>
|
||||
* <p>
|
||||
* You must set the encoding to <code>multipart/form-data</code> in the form where the user selects the file to upload.
|
||||
* </p>
|
||||
* <pre>
|
||||
* <s:form action="doUpload" method="post" enctype="multipart/form-data">
|
||||
* <s:file name="upload" label="File"/>
|
||||
* <s:submit/>
|
||||
* </s:form>
|
||||
* </pre>
|
||||
* <p>
|
||||
* And then in your action code you'll have access to the File object if you provide setters according to the
|
||||
* naming convention documented in the start.
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* package com.example;
|
||||
*
|
||||
* import java.io.File;
|
||||
* import com.opensymphony.xwork2.ActionSupport;
|
||||
* import org.apache.struts2.action.UploadedFilesAware;
|
||||
*
|
||||
* public UploadAction extends ActionSupport implements UploadedFilesAware {
|
||||
* private UploadedFile uploadedFile;
|
||||
* private String contentType;
|
||||
* private String fileName;
|
||||
* private String originalName;
|
||||
*
|
||||
* @Override
|
||||
* public void withUploadedFiles(List<UploadedFile> uploadedFiles) {
|
||||
* if (!uploadedFiles.isEmpty() > 0) {
|
||||
* this.uploadedFile = uploadedFiles.get(0);
|
||||
* this.fileName = uploadedFile.getName();
|
||||
* this.contentType = uploadedFile.getContentType();
|
||||
* this.originalName = uploadedFile.getOriginalName();
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* public String execute() {
|
||||
* //...
|
||||
* return SUCCESS;
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
public class ActionFileUploadInterceptor extends AbstractFileUploadInterceptor {
|
||||
|
||||
protected static final Logger LOG = LogManager.getLogger(ActionFileUploadInterceptor.class);
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see com.opensymphony.xwork2.interceptor.Interceptor#intercept(com.opensymphony.xwork2.ActionInvocation)
|
||||
*/
|
||||
public String intercept(ActionInvocation invocation) throws Exception {
|
||||
HttpServletRequest request = invocation.getInvocationContext().getServletRequest();
|
||||
if (!(request instanceof MultiPartRequestWrapper)) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
ActionProxy proxy = invocation.getProxy();
|
||||
LOG.debug(getTextMessage(STRUTS_MESSAGES_BYPASS_REQUEST_KEY, new String[]{proxy.getNamespace(), proxy.getActionName()}));
|
||||
}
|
||||
return invocation.invoke();
|
||||
}
|
||||
|
||||
MultiPartRequestWrapper multiWrapper = (MultiPartRequestWrapper) request;
|
||||
|
||||
if (!(invocation.getAction() instanceof UploadedFilesAware)) {
|
||||
LOG.debug("Action: {} doesn't implement: {}, ignoring file upload",
|
||||
invocation.getProxy().getActionName(),
|
||||
UploadedFilesAware.class.getSimpleName());
|
||||
return invocation.invoke();
|
||||
}
|
||||
UploadedFilesAware action = (UploadedFilesAware) invocation.getAction();
|
||||
|
||||
applyValidation(action, multiWrapper);
|
||||
|
||||
// bind allowed Files
|
||||
Enumeration<String> fileParameterNames = multiWrapper.getFileParameterNames();
|
||||
List<UploadedFile> acceptedFiles = new ArrayList<>();
|
||||
|
||||
while (fileParameterNames != null && fileParameterNames.hasMoreElements()) {
|
||||
// get the value of this input tag
|
||||
String inputName = fileParameterNames.nextElement();
|
||||
UploadedFile[] uploadedFiles = multiWrapper.getFiles(inputName);
|
||||
|
||||
if (uploadedFiles == null || uploadedFiles.length == 0) {
|
||||
if (LOG.isWarnEnabled()) {
|
||||
LOG.warn(getTextMessage(action, STRUTS_MESSAGES_INVALID_FILE_KEY, new String[]{inputName}));
|
||||
}
|
||||
} else {
|
||||
for (UploadedFile uploadedFile : uploadedFiles) {
|
||||
if (acceptFile(action, uploadedFile, uploadedFile.getOriginalName(), uploadedFile.getContentType(), inputName)) {
|
||||
acceptedFiles.add(uploadedFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (acceptedFiles.isEmpty()) {
|
||||
LOG.debug("No files have been uploaded/accepted");
|
||||
} else {
|
||||
LOG.debug("Passing: {} uploaded file(s) to action", acceptedFiles.size());
|
||||
action.withUploadedFiles(acceptedFiles);
|
||||
}
|
||||
|
||||
// invoke action
|
||||
return invocation.invoke();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
-6
@@ -19,10 +19,10 @@
|
||||
package org.apache.struts2.interceptor;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import org.apache.struts2.action.ParameterNameAware;
|
||||
import com.opensymphony.xwork2.interceptor.ParametersInterceptor;
|
||||
import org.apache.struts2.ServletActionContext;
|
||||
import org.apache.struts2.dispatcher.HttpParameters;
|
||||
import org.apache.struts2.dispatcher.mapper.ActionMapping;
|
||||
import org.apache.struts2.interceptor.parameter.ParametersInterceptor;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -53,7 +53,7 @@ import java.util.Map;
|
||||
* <!-- START SNIPPET: extending -->
|
||||
*
|
||||
* <p>
|
||||
* The best way to add behavior to this interceptor is to utilize the {@link ParameterNameAware} interface in your
|
||||
* The best way to add behavior to this interceptor is to utilize the {@link com.opensymphony.xwork2.interceptor.ParameterNameAware} interface in your
|
||||
* actions. However, if you wish to apply a global rule that isn't implemented in your action, then you could extend
|
||||
* this interceptor and override the {@link #acceptableName(String)} method.
|
||||
* </p>
|
||||
@@ -76,12 +76,12 @@ public class ActionMappingParametersInterceptor extends ParametersInterceptor {
|
||||
/**
|
||||
* Get the parameter map from ActionMapping associated with the provided ActionContext.
|
||||
*
|
||||
* @param actionContext The action context
|
||||
* @param ac The action context
|
||||
* @return the parameters from the action mapping in the context. If none found, returns an empty map.
|
||||
*/
|
||||
@Override
|
||||
protected HttpParameters retrieveParameters(ActionContext actionContext) {
|
||||
ActionMapping mapping = actionContext.getActionMapping();
|
||||
protected HttpParameters retrieveParameters(ActionContext ac) {
|
||||
ActionMapping mapping = ac.getActionMapping();
|
||||
if (mapping != null) {
|
||||
return HttpParameters.create(mapping.getParams()).buildNoNestedWrapping();
|
||||
} else {
|
||||
|
||||
@@ -18,22 +18,23 @@
|
||||
*/
|
||||
package org.apache.struts2.interceptor;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.ActionInvocation;
|
||||
import com.opensymphony.xwork2.ActionProxy;
|
||||
import com.opensymphony.xwork2.*;
|
||||
import com.opensymphony.xwork2.inject.Container;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
|
||||
import com.opensymphony.xwork2.interceptor.ValidationAware;
|
||||
import com.opensymphony.xwork2.util.TextParseUtil;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.dispatcher.LocalizedMessage;
|
||||
import org.apache.struts2.dispatcher.Parameter;
|
||||
import org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper;
|
||||
import org.apache.struts2.dispatcher.multipart.UploadedFile;
|
||||
import org.apache.struts2.util.ContentTypeMatcher;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* <!-- START SNIPPET: description -->
|
||||
@@ -76,11 +77,11 @@ import java.util.Map;
|
||||
* file extensions specified</li>
|
||||
*
|
||||
* </ul>
|
||||
* <p>
|
||||
*
|
||||
* <!-- END SNIPPET: description -->
|
||||
*
|
||||
* <p><u>Interceptor parameters:</u></p>
|
||||
* <p>
|
||||
*
|
||||
* <!-- START SNIPPET: parameters -->
|
||||
*
|
||||
* <ul>
|
||||
@@ -95,14 +96,14 @@ import java.util.Map;
|
||||
* <li>allowedExtensions (optional) - a comma separated list of file extensions (ie: .html) that the interceptor will allow
|
||||
* a file reference to be set on the action. If none is specified allow all extensions to be uploaded.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* <p>
|
||||
*
|
||||
*
|
||||
* <!-- END SNIPPET: parameters -->
|
||||
*
|
||||
* <p><u>Extending the interceptor:</u></p>
|
||||
* <p>
|
||||
* <p>
|
||||
* <p>
|
||||
*
|
||||
*
|
||||
*
|
||||
* <!-- START SNIPPET: extending -->
|
||||
* <p>
|
||||
* You can extend this interceptor and override the acceptFile method to provide more control over which files
|
||||
@@ -121,7 +122,7 @@ import java.util.Map;
|
||||
* </action>
|
||||
* <!-- END SNIPPET: example-configuration -->
|
||||
* </pre>
|
||||
* <p>
|
||||
*
|
||||
* <!-- START SNIPPET: multipart-note -->
|
||||
* <p>
|
||||
* You must set the encoding to <code>multipart/form-data</code> in the form where the user selects the file to upload.
|
||||
@@ -172,16 +173,57 @@ import java.util.Map;
|
||||
* }
|
||||
* <!-- END SNIPPET: example-action -->
|
||||
* </pre>
|
||||
*
|
||||
* @deprecated since Struts 6.4.0, use {@link ActionFileUploadInterceptor} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public class FileUploadInterceptor extends AbstractFileUploadInterceptor {
|
||||
public class FileUploadInterceptor extends AbstractInterceptor {
|
||||
|
||||
private static final long serialVersionUID = -4764627478894962478L;
|
||||
|
||||
protected static final Logger LOG = LogManager.getLogger(FileUploadInterceptor.class);
|
||||
|
||||
protected Long maximumSize;
|
||||
protected Set<String> allowedTypesSet = Collections.emptySet();
|
||||
protected Set<String> allowedExtensionsSet = Collections.emptySet();
|
||||
|
||||
private ContentTypeMatcher matcher;
|
||||
private Container container;
|
||||
|
||||
@Inject
|
||||
public void setMatcher(ContentTypeMatcher matcher) {
|
||||
this.matcher = matcher;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setContainer(Container container) {
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the allowed extensions
|
||||
*
|
||||
* @param allowedExtensions A comma-delimited list of extensions
|
||||
*/
|
||||
public void setAllowedExtensions(String allowedExtensions) {
|
||||
allowedExtensionsSet = TextParseUtil.commaDelimitedStringToSet(allowedExtensions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the allowed mimetypes
|
||||
*
|
||||
* @param allowedTypes A comma-delimited list of types
|
||||
*/
|
||||
public void setAllowedTypes(String allowedTypes) {
|
||||
allowedTypesSet = TextParseUtil.commaDelimitedStringToSet(allowedTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum size of an uploaded file
|
||||
*
|
||||
* @param maximumSize The maximum size in bytes
|
||||
*/
|
||||
public void setMaximumSize(Long maximumSize) {
|
||||
this.maximumSize = maximumSize;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see com.opensymphony.xwork2.interceptor.Interceptor#intercept(com.opensymphony.xwork2.ActionInvocation)
|
||||
*/
|
||||
@@ -194,22 +236,40 @@ public class FileUploadInterceptor extends AbstractFileUploadInterceptor {
|
||||
if (!(request instanceof MultiPartRequestWrapper)) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
ActionProxy proxy = invocation.getProxy();
|
||||
LOG.debug(getTextMessage(STRUTS_MESSAGES_BYPASS_REQUEST_KEY, new String[]{proxy.getNamespace(), proxy.getActionName()}));
|
||||
LOG.debug(getTextMessage("struts.messages.bypass.request", new String[]{proxy.getNamespace(), proxy.getActionName()}));
|
||||
}
|
||||
|
||||
return invocation.invoke();
|
||||
}
|
||||
|
||||
ValidationAware validation = null;
|
||||
|
||||
Object action = invocation.getAction();
|
||||
|
||||
if (action instanceof ValidationAware) {
|
||||
validation = (ValidationAware) action;
|
||||
}
|
||||
|
||||
MultiPartRequestWrapper multiWrapper = (MultiPartRequestWrapper) request;
|
||||
|
||||
applyValidation(action, multiWrapper);
|
||||
if (multiWrapper.hasErrors() && validation != null) {
|
||||
TextProvider textProvider = getTextProvider(action);
|
||||
for (LocalizedMessage error : multiWrapper.getErrors()) {
|
||||
String errorMessage;
|
||||
if (textProvider.hasKey(error.getTextKey())) {
|
||||
errorMessage = textProvider.getText(error.getTextKey(), Arrays.asList(error.getArgs()));
|
||||
} else {
|
||||
errorMessage = textProvider.getText("struts.messages.error.uploading", error.getDefaultMessage());
|
||||
}
|
||||
validation.addActionError(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
// bind allowed Files
|
||||
Enumeration<String> fileParameterNames = multiWrapper.getFileParameterNames();
|
||||
Enumeration fileParameterNames = multiWrapper.getFileParameterNames();
|
||||
while (fileParameterNames != null && fileParameterNames.hasMoreElements()) {
|
||||
// get the value of this input tag
|
||||
String inputName = fileParameterNames.nextElement();
|
||||
String inputName = (String) fileParameterNames.nextElement();
|
||||
|
||||
// get the content type
|
||||
String[] contentType = multiWrapper.getContentTypes(inputName);
|
||||
@@ -229,7 +289,7 @@ public class FileUploadInterceptor extends AbstractFileUploadInterceptor {
|
||||
String fileNameName = inputName + "FileName";
|
||||
|
||||
for (int index = 0; index < files.length; index++) {
|
||||
if (acceptFile(action, files[index], fileName[index], contentType[index], inputName)) {
|
||||
if (acceptFile(action, files[index], fileName[index], contentType[index], inputName, validation)) {
|
||||
acceptedFiles.add(files[index]);
|
||||
acceptedContentTypes.add(contentType[index]);
|
||||
acceptedFileNames.add(fileName[index]);
|
||||
@@ -238,20 +298,20 @@ public class FileUploadInterceptor extends AbstractFileUploadInterceptor {
|
||||
|
||||
if (!acceptedFiles.isEmpty()) {
|
||||
Map<String, Parameter> newParams = new HashMap<>();
|
||||
newParams.put(inputName, new Parameter.File(inputName, acceptedFiles.toArray(new UploadedFile[0])));
|
||||
newParams.put(contentTypeName, new Parameter.File(contentTypeName, acceptedContentTypes.toArray(new String[0])));
|
||||
newParams.put(fileNameName, new Parameter.File(fileNameName, acceptedFileNames.toArray(new String[0])));
|
||||
newParams.put(inputName, new Parameter.File(inputName, acceptedFiles.toArray(new UploadedFile[acceptedFiles.size()])));
|
||||
newParams.put(contentTypeName, new Parameter.File(contentTypeName, acceptedContentTypes.toArray(new String[acceptedContentTypes.size()])));
|
||||
newParams.put(fileNameName, new Parameter.File(fileNameName, acceptedFileNames.toArray(new String[acceptedFileNames.size()])));
|
||||
ac.getParameters().appendAll(newParams);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (LOG.isWarnEnabled()) {
|
||||
LOG.warn(getTextMessage(action, STRUTS_MESSAGES_INVALID_FILE_KEY, new String[]{inputName}));
|
||||
LOG.warn(getTextMessage(action, "struts.messages.invalid.file", new String[]{inputName}));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (LOG.isWarnEnabled()) {
|
||||
LOG.warn(getTextMessage(action, STRUTS_MESSAGES_INVALID_CONTENT_TYPE_KEY, new String[]{inputName}));
|
||||
LOG.warn(getTextMessage(action, "struts.messages.invalid.content.type", new String[]{inputName}));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -260,4 +320,149 @@ public class FileUploadInterceptor extends AbstractFileUploadInterceptor {
|
||||
return invocation.invoke();
|
||||
}
|
||||
|
||||
/**
|
||||
* Override for added functionality. Checks if the proposed file is acceptable based on contentType and size.
|
||||
*
|
||||
* @param action - uploading action for message retrieval.
|
||||
* @param file - proposed upload file.
|
||||
* @param filename - name of the file.
|
||||
* @param contentType - contentType of the file.
|
||||
* @param inputName - inputName of the file.
|
||||
* @param validation - Non-null ValidationAware if the action implements ValidationAware, allowing for better
|
||||
* logging.
|
||||
* @return true if the proposed file is acceptable by contentType and size.
|
||||
*/
|
||||
protected boolean acceptFile(Object action, UploadedFile file, String filename, String contentType, String inputName, ValidationAware validation) {
|
||||
boolean fileIsAcceptable = false;
|
||||
|
||||
// If it's null the upload failed
|
||||
if (file == null) {
|
||||
String errMsg = getTextMessage(action, "struts.messages.error.uploading", new String[]{inputName});
|
||||
if (validation != null) {
|
||||
validation.addFieldError(inputName, errMsg);
|
||||
}
|
||||
|
||||
if (LOG.isWarnEnabled()) {
|
||||
LOG.warn(errMsg);
|
||||
}
|
||||
} else if (file.getContent() == null) {
|
||||
String errMsg = getTextMessage(action, "struts.messages.error.uploading", new String[]{filename});
|
||||
if (validation != null) {
|
||||
validation.addFieldError(inputName, errMsg);
|
||||
}
|
||||
if (LOG.isWarnEnabled()) {
|
||||
LOG.warn(errMsg);
|
||||
}
|
||||
} else if (maximumSize != null && maximumSize < file.length()) {
|
||||
String errMsg = getTextMessage(action, "struts.messages.error.file.too.large", new String[]{inputName, filename, file.getName(), "" + file.length(), getMaximumSizeStr(action)});
|
||||
if (validation != null) {
|
||||
validation.addFieldError(inputName, errMsg);
|
||||
}
|
||||
|
||||
if (LOG.isWarnEnabled()) {
|
||||
LOG.warn(errMsg);
|
||||
}
|
||||
} else if ((!allowedTypesSet.isEmpty()) && (!containsItem(allowedTypesSet, contentType))) {
|
||||
String errMsg = getTextMessage(action, "struts.messages.error.content.type.not.allowed", new String[]{inputName, filename, file.getName(), contentType});
|
||||
if (validation != null) {
|
||||
validation.addFieldError(inputName, errMsg);
|
||||
}
|
||||
|
||||
if (LOG.isWarnEnabled()) {
|
||||
LOG.warn(errMsg);
|
||||
}
|
||||
} else if ((!allowedExtensionsSet.isEmpty()) && (!hasAllowedExtension(allowedExtensionsSet, filename))) {
|
||||
String errMsg = getTextMessage(action, "struts.messages.error.file.extension.not.allowed", new String[]{inputName, filename, file.getName(), contentType});
|
||||
if (validation != null) {
|
||||
validation.addFieldError(inputName, errMsg);
|
||||
}
|
||||
|
||||
if (LOG.isWarnEnabled()) {
|
||||
LOG.warn(errMsg);
|
||||
}
|
||||
} else {
|
||||
fileIsAcceptable = true;
|
||||
}
|
||||
|
||||
return fileIsAcceptable;
|
||||
}
|
||||
|
||||
private String getMaximumSizeStr(Object action) {
|
||||
return NumberFormat.getNumberInstance(getLocaleProvider(action).getLocale()).format(maximumSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param extensionCollection - Collection of extensions (all lowercase).
|
||||
* @param filename - filename to check.
|
||||
* @return true if the filename has an allowed extension, false otherwise.
|
||||
*/
|
||||
private boolean hasAllowedExtension(Collection<String> extensionCollection, String filename) {
|
||||
if (filename == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String lowercaseFilename = filename.toLowerCase();
|
||||
for (String extension : extensionCollection) {
|
||||
if (lowercaseFilename.endsWith(extension)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param itemCollection - Collection of string items (all lowercase).
|
||||
* @param item - Item to search for.
|
||||
* @return true if itemCollection contains the item, false otherwise.
|
||||
*/
|
||||
private boolean containsItem(Collection<String> itemCollection, String item) {
|
||||
for (String pattern : itemCollection)
|
||||
if (matchesWildcard(pattern, item))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean matchesWildcard(String pattern, String text) {
|
||||
Object o = matcher.compilePattern(pattern);
|
||||
return matcher.match(new HashMap<String, String>(), text, o);
|
||||
}
|
||||
|
||||
private boolean isNonEmpty(Object[] objArray) {
|
||||
boolean result = false;
|
||||
for (int index = 0; index < objArray.length && !result; index++) {
|
||||
if (objArray[index] != null) {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected String getTextMessage(String messageKey, String[] args) {
|
||||
return getTextMessage(this, messageKey, args);
|
||||
}
|
||||
|
||||
protected String getTextMessage(Object action, String messageKey, String[] args) {
|
||||
if (action instanceof TextProvider) {
|
||||
return ((TextProvider) action).getText(messageKey, args);
|
||||
}
|
||||
return getTextProvider(action).getText(messageKey, args);
|
||||
}
|
||||
|
||||
private TextProvider getTextProvider(Object action) {
|
||||
TextProviderFactory tpf = container.getInstance(TextProviderFactory.class);
|
||||
return tpf.createInstance(action.getClass());
|
||||
}
|
||||
|
||||
private LocaleProvider getLocaleProvider(Object action) {
|
||||
LocaleProvider localeProvider;
|
||||
if (action instanceof LocaleProvider) {
|
||||
localeProvider = (LocaleProvider) action;
|
||||
} else {
|
||||
LocaleProviderFactory localeProviderFactory = container.getInstance(LocaleProviderFactory.class);
|
||||
localeProvider = localeProviderFactory.createLocaleProvider();
|
||||
}
|
||||
return localeProvider;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
-10
@@ -29,10 +29,8 @@ import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.ServletActionContext;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
import org.apache.struts2.dispatcher.DispatcherConstants;
|
||||
import org.apache.struts2.dispatcher.Parameter;
|
||||
import org.apache.struts2.dispatcher.PrepareOperations;
|
||||
import org.apache.struts2.dispatcher.RequestMap;
|
||||
import org.apache.struts2.views.freemarker.FreemarkerManager;
|
||||
import org.apache.struts2.views.freemarker.FreemarkerResult;
|
||||
|
||||
@@ -99,13 +97,11 @@ public class DebuggingInterceptor extends AbstractInterceptor {
|
||||
|
||||
private final static Logger LOG = LogManager.getLogger(DebuggingInterceptor.class);
|
||||
|
||||
private final String[] ignorePrefixes = new String[]{"org.apache.struts.", "com.opensymphony.xwork2.", "xwork."};
|
||||
private final HashSet<String> ignoreKeys = new HashSet<>(Arrays.asList(
|
||||
DispatcherConstants.APPLICATION,
|
||||
DispatcherConstants.SESSION,
|
||||
DispatcherConstants.PARAMETERS,
|
||||
DispatcherConstants.REQUEST
|
||||
));
|
||||
private String[] ignorePrefixes = new String[]{"org.apache.struts.",
|
||||
"com.opensymphony.xwork2.", "xwork."};
|
||||
private String[] _ignoreKeys = new String[]{"application", "session",
|
||||
"parameters", "request"};
|
||||
private HashSet<String> ignoreKeys = new HashSet<>(Arrays.asList(_ignoreKeys));
|
||||
|
||||
private final static String XML_MODE = "xml";
|
||||
private final static String CONSOLE_MODE = "console";
|
||||
@@ -323,7 +319,7 @@ public class DebuggingInterceptor extends AbstractInterceptor {
|
||||
}
|
||||
}
|
||||
writer.endNode();
|
||||
RequestMap requestMap = (RequestMap) ctx.get(DispatcherConstants.REQUEST);
|
||||
Map<String, Object> requestMap = (Map<String, Object>) ctx.get("request");
|
||||
serializeIt(requestMap, "request", writer, filterValueStack(requestMap));
|
||||
serializeIt(ctx.getSession(), "session", writer, new ArrayList<>());
|
||||
|
||||
|
||||
-589
@@ -1,589 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.interceptor.parameter;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.ActionInvocation;
|
||||
import com.opensymphony.xwork2.TextProvider;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
|
||||
import com.opensymphony.xwork2.interceptor.ValidationAware;
|
||||
import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
|
||||
import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
|
||||
import com.opensymphony.xwork2.util.ClearableValueStack;
|
||||
import com.opensymphony.xwork2.util.MemberAccessValueStack;
|
||||
import com.opensymphony.xwork2.util.TextParseUtil;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import com.opensymphony.xwork2.util.ValueStackFactory;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
import org.apache.struts2.action.NoParameters;
|
||||
import org.apache.struts2.action.ParameterNameAware;
|
||||
import org.apache.struts2.action.ParameterValueAware;
|
||||
import org.apache.struts2.dispatcher.HttpParameters;
|
||||
import org.apache.struts2.dispatcher.Parameter;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static java.util.Collections.unmodifiableSet;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
import static org.apache.commons.lang3.StringUtils.normalizeSpace;
|
||||
|
||||
/**
|
||||
* This interceptor sets all parameters on the value stack.
|
||||
*/
|
||||
public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(ParametersInterceptor.class);
|
||||
|
||||
protected static final int PARAM_NAME_MAX_LENGTH = 100;
|
||||
|
||||
private static final Pattern DMI_IGNORED_PATTERN = Pattern.compile("^(action|method):.*", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
private int paramNameMaxLength = PARAM_NAME_MAX_LENGTH;
|
||||
private boolean devMode = false;
|
||||
private boolean dmiEnabled = false;
|
||||
|
||||
protected boolean ordered = false;
|
||||
|
||||
private ValueStackFactory valueStackFactory;
|
||||
private ExcludedPatternsChecker excludedPatterns;
|
||||
private AcceptedPatternsChecker acceptedPatterns;
|
||||
private Set<Pattern> excludedValuePatterns = null;
|
||||
private Set<Pattern> acceptedValuePatterns = null;
|
||||
|
||||
@Inject
|
||||
public void setValueStackFactory(ValueStackFactory valueStackFactory) {
|
||||
this.valueStackFactory = valueStackFactory;
|
||||
}
|
||||
|
||||
@Inject(StrutsConstants.STRUTS_DEVMODE)
|
||||
public void setDevMode(String mode) {
|
||||
this.devMode = BooleanUtils.toBoolean(mode);
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setExcludedPatterns(ExcludedPatternsChecker excludedPatterns) {
|
||||
this.excludedPatterns = excludedPatterns;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setAcceptedPatterns(AcceptedPatternsChecker acceptedPatterns) {
|
||||
this.acceptedPatterns = acceptedPatterns;
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION, required = false)
|
||||
protected void setDynamicMethodInvocation(String dmiEnabled) {
|
||||
this.dmiEnabled = Boolean.parseBoolean(dmiEnabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the param name exceeds the configured maximum length it will not be
|
||||
* accepted.
|
||||
*
|
||||
* @param paramNameMaxLength Maximum length of param names
|
||||
*/
|
||||
public void setParamNameMaxLength(int paramNameMaxLength) {
|
||||
this.paramNameMaxLength = paramNameMaxLength;
|
||||
}
|
||||
|
||||
static private int countOGNLCharacters(String s) {
|
||||
int count = 0;
|
||||
for (int i = s.length() - 1; i >= 0; i--) {
|
||||
char c = s.charAt(i);
|
||||
if (c == '.' || c == '[') count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares based on number of '.' and '[' characters (fewer is higher)
|
||||
*/
|
||||
static final Comparator<String> rbCollator = (s1, s2) -> {
|
||||
int l1 = countOGNLCharacters(s1);
|
||||
int l2 = countOGNLCharacters(s2);
|
||||
return l1 < l2 ? -1 : (l2 < l1 ? 1 : s1.compareTo(s2));
|
||||
};
|
||||
|
||||
@Override
|
||||
public String doIntercept(ActionInvocation invocation) throws Exception {
|
||||
Object action = invocation.getAction();
|
||||
if (action instanceof NoParameters) {
|
||||
return invocation.invoke();
|
||||
}
|
||||
|
||||
ActionContext actionContext = invocation.getInvocationContext();
|
||||
HttpParameters parameters = retrieveParameters(actionContext);
|
||||
|
||||
if (parameters == null) {
|
||||
return invocation.invoke();
|
||||
}
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Setting params {}", normalizeSpace(getParameterLogMap(parameters)));
|
||||
}
|
||||
|
||||
Map<String, Object> contextMap = actionContext.getContextMap();
|
||||
batchApplyReflectionContextState(contextMap, true);
|
||||
try {
|
||||
setParameters(action, actionContext.getValueStack(), parameters);
|
||||
} finally {
|
||||
batchApplyReflectionContextState(contextMap, false);
|
||||
}
|
||||
|
||||
return invocation.invoke();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parameter map to apply from wherever appropriate
|
||||
*
|
||||
* @param actionContext The action context
|
||||
* @return The parameter map to apply
|
||||
*/
|
||||
protected HttpParameters retrieveParameters(ActionContext actionContext) {
|
||||
return actionContext.getParameters();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds the parameters into context's ParameterMap
|
||||
* <p>
|
||||
* In this class this is a no-op, since the parameters were fetched from the same location. In subclasses both this
|
||||
* and {@link #retrieveParameters} should be overridden.
|
||||
*
|
||||
* @param ac The action context
|
||||
* @param newParams The parameter map to apply
|
||||
*/
|
||||
protected void addParametersToContext(ActionContext ac, Map<String, ?> newParams) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 6.4.0, use {@link #applyParameters}
|
||||
*/
|
||||
@Deprecated
|
||||
protected void setParameters(final Object action, ValueStack stack, HttpParameters parameters) {
|
||||
applyParameters(action, stack, parameters);
|
||||
}
|
||||
|
||||
protected void applyParameters(final Object action, ValueStack stack, HttpParameters parameters) {
|
||||
Map<String, Parameter> acceptableParameters = toAcceptableParameters(parameters, action);
|
||||
|
||||
ValueStack newStack = toNewStack(stack);
|
||||
batchApplyReflectionContextState(newStack.getContext(), true);
|
||||
applyMemberAccessProperties(newStack);
|
||||
|
||||
applyParametersOnStack(newStack, acceptableParameters, action);
|
||||
|
||||
if (newStack instanceof ClearableValueStack) {
|
||||
stack.getActionContext().withConversionErrors(newStack.getActionContext().getConversionErrors());
|
||||
}
|
||||
|
||||
addParametersToContext(ActionContext.getContext(), acceptableParameters);
|
||||
}
|
||||
|
||||
protected void batchApplyReflectionContextState(Map<String, Object> context, boolean value) {
|
||||
ReflectionContextState.setCreatingNullObjects(context, value);
|
||||
ReflectionContextState.setDenyMethodExecution(context, value);
|
||||
ReflectionContextState.setReportingConversionErrors(context, value);
|
||||
}
|
||||
|
||||
protected ValueStack toNewStack(ValueStack stack) {
|
||||
ValueStack newStack = valueStackFactory.createValueStack(stack);
|
||||
if (newStack instanceof ClearableValueStack) {
|
||||
((ClearableValueStack) newStack).clearContextValues();
|
||||
newStack.getActionContext().withLocale(stack.getActionContext().getLocale()).withValueStack(stack);
|
||||
}
|
||||
return newStack;
|
||||
}
|
||||
|
||||
protected void applyMemberAccessProperties(ValueStack stack) {
|
||||
if (!(stack instanceof MemberAccessValueStack)) {
|
||||
return;
|
||||
}
|
||||
((MemberAccessValueStack) stack).useAcceptProperties(acceptedPatterns.getAcceptedPatterns());
|
||||
((MemberAccessValueStack) stack).useExcludeProperties(excludedPatterns.getExcludedPatterns());
|
||||
}
|
||||
|
||||
protected Map<String, Parameter> toAcceptableParameters(HttpParameters parameters, Object action) {
|
||||
HttpParameters newParams = initNewHttpParameters(parameters);
|
||||
Map<String, Parameter> acceptableParameters = initParameterMap();
|
||||
|
||||
for (Map.Entry<String, Parameter> entry : newParams.entrySet()) {
|
||||
String parameterName = entry.getKey();
|
||||
Parameter parameterValue = entry.getValue();
|
||||
if (isAcceptableParameter(parameterName, action) && isAcceptableParameterValue(parameterValue, action)) {
|
||||
acceptableParameters.put(parameterName, parameterValue);
|
||||
}
|
||||
}
|
||||
return acceptableParameters;
|
||||
}
|
||||
|
||||
protected Map<String, Parameter> initParameterMap() {
|
||||
if (ordered) {
|
||||
return new TreeMap<>(getOrderedComparator());
|
||||
} else {
|
||||
return new TreeMap<>();
|
||||
}
|
||||
}
|
||||
|
||||
protected HttpParameters initNewHttpParameters(HttpParameters parameters) {
|
||||
if (ordered) {
|
||||
return HttpParameters.create().withComparator(getOrderedComparator()).withParent(parameters).build();
|
||||
} else {
|
||||
return HttpParameters.create().withParent(parameters).build();
|
||||
}
|
||||
}
|
||||
|
||||
protected void applyParametersOnStack(ValueStack stack, Map<String, Parameter> parameters, Object action) {
|
||||
for (Map.Entry<String, Parameter> entry : parameters.entrySet()) {
|
||||
try {
|
||||
stack.setParameter(entry.getKey(), entry.getValue().getObject());
|
||||
} catch (RuntimeException e) {
|
||||
if (devMode) {
|
||||
notifyDeveloperParameterException(action, entry.getKey(), e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyDeveloperParameterException(Object action, String property, String message) {
|
||||
String logMsg = "Unexpected Exception caught setting '" + property + "' on '" + action.getClass() + ": " + message;
|
||||
if (action instanceof TextProvider) {
|
||||
TextProvider tp = (TextProvider) action;
|
||||
logMsg = tp.getText("devmode.notification", "Developer Notification:\n{0}", new String[]{logMsg});
|
||||
}
|
||||
LOG.error(logMsg);
|
||||
|
||||
if (action instanceof ValidationAware) {
|
||||
ValidationAware validationAware = (ValidationAware) action;
|
||||
Collection<String> messages = validationAware.getActionMessages();
|
||||
messages.add(message);
|
||||
validationAware.setActionMessages(messages);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if name of parameter can be accepted or thrown away
|
||||
*
|
||||
* @param name parameter name
|
||||
* @param action current action
|
||||
* @return true if parameter is accepted
|
||||
*/
|
||||
protected boolean isAcceptableParameter(String name, Object action) {
|
||||
return acceptableName(name) && isAcceptableParameterNameAware(name, action);
|
||||
}
|
||||
|
||||
protected boolean isAcceptableParameterNameAware(String name, Object action) {
|
||||
return !(action instanceof ParameterNameAware) || ((ParameterNameAware) action).acceptableParameterName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if parameter value can be accepted or thrown away
|
||||
*
|
||||
* @param param the parameter
|
||||
* @param action current action
|
||||
* @return true if parameter is accepted
|
||||
*/
|
||||
protected boolean isAcceptableParameterValue(Parameter param, Object action) {
|
||||
return isAcceptableParameterValueAware(param, action) && acceptableValue(param.getName(), param.getValue());
|
||||
}
|
||||
|
||||
protected boolean isAcceptableParameterValueAware(Parameter param, Object action) {
|
||||
return !(action instanceof ParameterValueAware) || ((ParameterValueAware) action).acceptableParameterValue(param.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance of the comparator to use for the ordered sorting. Override this
|
||||
* method to customize the ordering of the parameters as they are set to the
|
||||
* action.
|
||||
*
|
||||
* @return A comparator to sort the parameters
|
||||
*/
|
||||
protected Comparator<String> getOrderedComparator() {
|
||||
return rbCollator;
|
||||
}
|
||||
|
||||
protected String getParameterLogMap(HttpParameters parameters) {
|
||||
if (parameters == null) {
|
||||
return "NONE";
|
||||
}
|
||||
return parameters.entrySet().stream()
|
||||
.map(entry -> String.format("%s => %s ", entry.getKey(), entry.getValue().getValue()))
|
||||
.collect(joining());
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 6.4.0, use {@link #isAcceptableName}
|
||||
*/
|
||||
protected boolean acceptableName(String name) {
|
||||
return isAcceptableName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the name passed is:
|
||||
* * Within the max length of a parameter name
|
||||
* * Is not excluded
|
||||
* * Is accepted
|
||||
*
|
||||
* @param name - Name to check
|
||||
* @return true if accepted
|
||||
*/
|
||||
protected boolean isAcceptableName(String name) {
|
||||
if (isIgnoredDMI(name)) {
|
||||
LOG.trace("DMI is enabled, ignoring DMI method: {}", name);
|
||||
return false;
|
||||
}
|
||||
boolean accepted = isWithinLengthLimit(name) && !isExcluded(name) && isAccepted(name);
|
||||
if (devMode && accepted) {
|
||||
LOG.debug("Parameter [{}] was accepted and will be appended to action!", name);
|
||||
}
|
||||
return accepted;
|
||||
}
|
||||
|
||||
private boolean isIgnoredDMI(String name) {
|
||||
if (!dmiEnabled) {
|
||||
return false;
|
||||
}
|
||||
return DMI_IGNORED_PATTERN.matcher(name).matches();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 6.4.0, use {@link #isAcceptableValue}
|
||||
*/
|
||||
protected boolean acceptableValue(String name, String value) {
|
||||
return isAcceptableValue(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates:
|
||||
* * Value is null/blank
|
||||
* * Value is not excluded
|
||||
* * Value is accepted
|
||||
*
|
||||
* @param name - Param name (for logging)
|
||||
* @param value - value to check
|
||||
* @return true if accepted
|
||||
*/
|
||||
protected boolean isAcceptableValue(String name, String value) {
|
||||
boolean accepted = value == null || value.isEmpty() || (!isParamValueExcluded(value) && isParamValueAccepted(value));
|
||||
if (!accepted) {
|
||||
String message = "Value [{}] of parameter [{}] was not accepted and will be dropped!";
|
||||
if (devMode) {
|
||||
LOG.warn(message, normalizeSpace(value), normalizeSpace(name));
|
||||
} else {
|
||||
LOG.debug(message, normalizeSpace(value), normalizeSpace(name));
|
||||
}
|
||||
}
|
||||
return accepted;
|
||||
}
|
||||
|
||||
protected boolean isWithinLengthLimit(String name) {
|
||||
boolean matchLength = name.length() <= paramNameMaxLength;
|
||||
if (!matchLength) {
|
||||
if (devMode) {
|
||||
LOG.warn("Parameter [{}] is too long, allowed length is [{}]. Use Interceptor Parameter Overriding " +
|
||||
"to override the limit, see more at\n" +
|
||||
"https://struts.apache.org/core-developers/interceptors.html#interceptor-parameter-overriding",
|
||||
name, paramNameMaxLength);
|
||||
} else {
|
||||
LOG.warn("Parameter [{}] is too long, allowed length is [{}]", name, paramNameMaxLength);
|
||||
}
|
||||
}
|
||||
return matchLength;
|
||||
}
|
||||
|
||||
protected boolean isAccepted(String paramName) {
|
||||
AcceptedPatternsChecker.IsAccepted result = acceptedPatterns.isAccepted(paramName);
|
||||
if (!result.isAccepted()) {
|
||||
if (devMode) {
|
||||
LOG.warn("Parameter [{}] didn't match accepted pattern [{}]! See Accepted / Excluded patterns at\n" +
|
||||
"https://struts.apache.org/security/#accepted--excluded-patterns",
|
||||
paramName, result.getAcceptedPattern());
|
||||
} else {
|
||||
LOG.debug("Parameter [{}] didn't match accepted pattern [{}]!", paramName, result.getAcceptedPattern());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean isExcluded(String paramName) {
|
||||
ExcludedPatternsChecker.IsExcluded result = excludedPatterns.isExcluded(paramName);
|
||||
if (result.isExcluded()) {
|
||||
if (devMode) {
|
||||
LOG.warn("Parameter [{}] matches excluded pattern [{}]! See Accepted / Excluded patterns at\n" +
|
||||
"https://struts.apache.org/security/#accepted--excluded-patterns",
|
||||
paramName, result.getExcludedPattern());
|
||||
} else {
|
||||
LOG.debug("Parameter [{}] matches excluded pattern [{}]!", paramName, result.getExcludedPattern());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean isParamValueExcluded(String value) {
|
||||
if (!hasParamValuesToExclude()) {
|
||||
LOG.debug("'excludedValuePatterns' not defined so anything is allowed");
|
||||
return false;
|
||||
}
|
||||
for (Pattern excludedValuePattern : excludedValuePatterns) {
|
||||
if (excludedValuePattern.matcher(value).matches()) {
|
||||
if (devMode) {
|
||||
LOG.warn("Parameter value [{}] matches excluded pattern [{}]! See Accepting/Excluding parameter values at\n" +
|
||||
"https://struts.apache.org/core-developers/parameters-interceptor#excluding-parameter-values",
|
||||
value, excludedValuePatterns);
|
||||
} else {
|
||||
LOG.debug("Parameter value [{}] matches excluded pattern [{}]", value, excludedValuePattern);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean isParamValueAccepted(String value) {
|
||||
if (!hasParamValuesToAccept()) {
|
||||
LOG.debug("'acceptedValuePatterns' not defined so anything is allowed");
|
||||
return true;
|
||||
}
|
||||
for (Pattern acceptedValuePattern : acceptedValuePatterns) {
|
||||
if (acceptedValuePattern.matcher(value).matches()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (devMode) {
|
||||
LOG.warn("Parameter value [{}] didn't match accepted pattern [{}]! See Accepting/Excluding parameter values at\n" +
|
||||
"https://struts.apache.org/core-developers/parameters-interceptor#excluding-parameter-values",
|
||||
value, acceptedValuePatterns);
|
||||
} else {
|
||||
LOG.debug("Parameter value [{}] was not accepted!", value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean hasParamValuesToExclude() {
|
||||
return excludedValuePatterns != null && !excludedValuePatterns.isEmpty();
|
||||
}
|
||||
|
||||
private boolean hasParamValuesToAccept() {
|
||||
return acceptedValuePatterns != null && !acceptedValuePatterns.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to order the parameters or not
|
||||
*
|
||||
* @return True to order
|
||||
*/
|
||||
public boolean isOrdered() {
|
||||
return ordered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to order the parameters by object depth or not
|
||||
*
|
||||
* @param ordered True to order them
|
||||
*/
|
||||
public void setOrdered(boolean ordered) {
|
||||
this.ordered = ordered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a comma-delimited list of regular expressions to match
|
||||
* parameters that are allowed in the parameter map (aka whitelist).
|
||||
* <p>
|
||||
* Don't change the default unless you know what you are doing in terms
|
||||
* of security implications.
|
||||
* </p>
|
||||
*
|
||||
* @param commaDelim A comma-delimited list of regular expressions
|
||||
*/
|
||||
public void setAcceptParamNames(String commaDelim) {
|
||||
acceptedPatterns.setAcceptedPatterns(commaDelim);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a comma-delimited list of regular expressions to match
|
||||
* parameters that should be removed from the parameter map.
|
||||
*
|
||||
* @param commaDelim A comma-delimited list of regular expressions
|
||||
*/
|
||||
public void setExcludeParams(String commaDelim) {
|
||||
excludedPatterns.setExcludedPatterns(commaDelim);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a comma-delimited list of regular expressions to match
|
||||
* values of parameters that should be accepted and included in the parameter map.
|
||||
*
|
||||
* @param commaDelimitedPatterns A comma-delimited set of regular expressions
|
||||
*/
|
||||
public void setAcceptedValuePatterns(String commaDelimitedPatterns) {
|
||||
Set<String> patterns = TextParseUtil.commaDelimitedStringToSet(commaDelimitedPatterns);
|
||||
if (acceptedValuePatterns == null) {
|
||||
// Limit unwanted log entries (for 1st call, acceptedValuePatterns null)
|
||||
LOG.debug("Sets accepted value patterns to [{}], note this may impact the safety of your application!", patterns);
|
||||
} else {
|
||||
LOG.warn("Replacing accepted patterns [{}] with [{}], be aware that this may impact safety of your application!",
|
||||
acceptedValuePatterns, patterns);
|
||||
}
|
||||
acceptedValuePatterns = new HashSet<>(patterns.size());
|
||||
try {
|
||||
for (String pattern : patterns) {
|
||||
acceptedValuePatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
}
|
||||
} finally {
|
||||
acceptedValuePatterns = unmodifiableSet(acceptedValuePatterns);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a comma-delimited list of regular expressions to match
|
||||
* values of parameters that should be removed from the parameter map.
|
||||
*
|
||||
* @param commaDelimitedPatterns A comma-delimited set of regular expressions
|
||||
*/
|
||||
public void setExcludedValuePatterns(String commaDelimitedPatterns) {
|
||||
Set<String> patterns = TextParseUtil.commaDelimitedStringToSet(commaDelimitedPatterns);
|
||||
if (excludedValuePatterns == null) {
|
||||
// Limit unwanted log entries (for 1st call, excludedValuePatterns null)
|
||||
LOG.debug("Setting excluded value patterns to [{}]", patterns);
|
||||
} else {
|
||||
LOG.warn("Replacing excluded value patterns [{}] with [{}], be aware that this may impact safety of your application!",
|
||||
excludedValuePatterns, patterns);
|
||||
}
|
||||
excludedValuePatterns = new HashSet<>(patterns.size());
|
||||
try {
|
||||
for (String pattern : patterns) {
|
||||
excludedValuePatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
}
|
||||
} finally {
|
||||
excludedValuePatterns = unmodifiableSet(excludedValuePatterns);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.ognl;
|
||||
|
||||
import com.opensymphony.xwork2.ognl.OgnlUtil;
|
||||
import ognl.Ognl;
|
||||
import ognl.OgnlException;
|
||||
|
||||
/**
|
||||
* Guards all expressions parsed by Struts Core. It is evaluated by {@link OgnlUtil} immediately after parsing any
|
||||
* expression.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*/
|
||||
public interface OgnlGuard {
|
||||
|
||||
String EXPR_BLOCKED = "_ognl_guard_blocked";
|
||||
|
||||
/**
|
||||
* Determines whether an OGNL expression should be blocked based on validation done on both the raw expression and
|
||||
* the parsed tree.
|
||||
*
|
||||
* @param expr OGNL expression
|
||||
* @return whether the expression should be blocked
|
||||
*/
|
||||
default boolean isBlocked(String expr) throws OgnlException {
|
||||
return EXPR_BLOCKED.equals(parseExpression(expr));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an OGNL expression and returns the resulting tree only if the expression is not blocked as per defined
|
||||
* validation rules in {@link #isRawExpressionBlocked} and {@link #isParsedTreeBlocked}.
|
||||
*
|
||||
* @param expr OGNL expression
|
||||
* @return parsed expression or {@link #EXPR_BLOCKED} if the expression should be blocked
|
||||
*/
|
||||
default Object parseExpression(String expr) throws OgnlException {
|
||||
if (isRawExpressionBlocked(expr)) {
|
||||
return EXPR_BLOCKED;
|
||||
}
|
||||
Object tree = Ognl.parseExpression(expr);
|
||||
if (isParsedTreeBlocked(tree)) {
|
||||
return EXPR_BLOCKED;
|
||||
}
|
||||
return tree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether an OGNL expression should be blocked based on validation done on only the raw expression,
|
||||
* without parsing the tree.
|
||||
*
|
||||
* @param expr OGNL expression
|
||||
* @return whether the expression should be blocked
|
||||
*/
|
||||
boolean isRawExpressionBlocked(String expr);
|
||||
|
||||
/**
|
||||
* Determines whether a parsed OGNL tree should be blocked based on some validation rules.
|
||||
*
|
||||
* @param tree parsed OGNL tree
|
||||
* @return whether the parsed tree should be blocked
|
||||
*/
|
||||
boolean isParsedTreeBlocked(Object tree);
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.ognl;
|
||||
|
||||
import com.opensymphony.xwork2.config.ConfigurationProvider;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.Collections.unmodifiableSet;
|
||||
|
||||
/**
|
||||
* Allows {@link ConfigurationProvider}s to register classes that should be allowed to be used in OGNL expressions.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*/
|
||||
public class ProviderAllowlist {
|
||||
|
||||
private final Map<ConfigurationProvider, Set<Class<?>>> allowlistMap;
|
||||
private Set<Class<?>> allowlistClasses;
|
||||
|
||||
public ProviderAllowlist() {
|
||||
allowlistMap = new HashMap<>();
|
||||
reconstructAllowlist();
|
||||
}
|
||||
|
||||
public synchronized void registerAllowlist(ConfigurationProvider configurationProvider, Set<Class<?>> allowlist) {
|
||||
Set<Class<?>> existingAllowlist = allowlistMap.get(configurationProvider);
|
||||
if (existingAllowlist != null) {
|
||||
clearAllowlist(configurationProvider);
|
||||
}
|
||||
this.allowlistMap.put(configurationProvider, new HashSet<>(allowlist));
|
||||
this.allowlistClasses.addAll(allowlist);
|
||||
}
|
||||
|
||||
public synchronized void clearAllowlist(ConfigurationProvider configurationProvider) {
|
||||
Set<Class<?>> allowlist = allowlistMap.get(configurationProvider);
|
||||
if (allowlist == null) {
|
||||
return;
|
||||
}
|
||||
this.allowlistMap.remove(configurationProvider);
|
||||
reconstructAllowlist();
|
||||
}
|
||||
|
||||
public Set<Class<?>> getProviderAllowlist() {
|
||||
return unmodifiableSet(allowlistClasses);
|
||||
}
|
||||
|
||||
private void reconstructAllowlist() {
|
||||
this.allowlistClasses = allowlistMap.values().stream().reduce(new HashSet<>(), (a, b) -> {
|
||||
a.addAll(b);
|
||||
return a;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.ognl;
|
||||
|
||||
import com.opensymphony.xwork2.config.ConfigurationException;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import ognl.Node;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.opensymphony.xwork2.util.TextParseUtil.commaDelimitedStringToSet;
|
||||
import static java.util.Collections.emptySet;
|
||||
import static java.util.Collections.unmodifiableSet;
|
||||
|
||||
/**
|
||||
* The default implementation of {@link OgnlGuard}.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*/
|
||||
public class StrutsOgnlGuard implements OgnlGuard {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(StrutsOgnlGuard.class);
|
||||
|
||||
protected Set<String> excludedNodeTypes = emptySet();
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_EXCLUDED_NODE_TYPES, required = false)
|
||||
public void useExcludedNodeTypes(String excludedNodeTypes) {
|
||||
Set<String> incomingExcludedNodeTypes = commaDelimitedStringToSet(excludedNodeTypes);
|
||||
validateExcludedNodeTypes(incomingExcludedNodeTypes);
|
||||
Set<String> newExcludeNodeTypes = new HashSet<>(this.excludedNodeTypes);
|
||||
newExcludeNodeTypes.addAll(incomingExcludedNodeTypes);
|
||||
this.excludedNodeTypes = unmodifiableSet(newExcludeNodeTypes);
|
||||
}
|
||||
|
||||
protected void validateExcludedNodeTypes(Set<String> incomingExcludedNodeTypes) throws ConfigurationException {
|
||||
for (String excludedNodeType : incomingExcludedNodeTypes) {
|
||||
try {
|
||||
if (!Node.class.isAssignableFrom(Class.forName(excludedNodeType))) {
|
||||
throw new ConfigurationException("Excluded node type [" + excludedNodeType + "] is not a subclass of " + Node.class.getName());
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new ConfigurationException("Excluded node type [" + excludedNodeType + "] does not exist or cannot be loaded");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRawExpressionBlocked(String expr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isParsedTreeBlocked(Object tree) {
|
||||
if (!(tree instanceof Node) || skipTreeCheck((Node) tree)) {
|
||||
return false;
|
||||
}
|
||||
return recurseNodes((Node) tree);
|
||||
}
|
||||
|
||||
protected boolean skipTreeCheck(Node tree) {
|
||||
return excludedNodeTypes.isEmpty();
|
||||
}
|
||||
|
||||
protected boolean recurseNodes(Node node) {
|
||||
if (checkNode(node)) {
|
||||
return true;
|
||||
}
|
||||
for (int i = 0; i < node.jjtGetNumChildren(); i++) {
|
||||
if (recurseNodes(node.jjtGetChild(i))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean checkNode(Node node) {
|
||||
return containsExcludedNodeType(node);
|
||||
}
|
||||
|
||||
protected boolean containsExcludedNodeType(Node node) {
|
||||
String nodeClassName = node.getClass().getName();
|
||||
if (excludedNodeTypes.contains(nodeClassName)) {
|
||||
LOG.warn("Expression contains blocked node type [{}]", nodeClassName);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -27,14 +27,11 @@ import org.apache.struts2.StrutsConstants;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Collection;
|
||||
|
||||
public class StrutsUrlDecoder implements UrlDecoder {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(StrutsUrlDecoder.class);
|
||||
|
||||
private static final Collection<Charset> AVAILABLE_CHARSETS = Charset.availableCharsets().values();
|
||||
|
||||
private String encoding = "UTF-8";
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_I18N_ENCODING, required = false)
|
||||
@@ -110,7 +107,7 @@ public class StrutsUrlDecoder implements UrlDecoder {
|
||||
}
|
||||
|
||||
private Charset getCharset(String encoding) throws UnsupportedEncodingException {
|
||||
for (Charset charset : AVAILABLE_CHARSETS) {
|
||||
for (Charset charset : Charset.availableCharsets().values()) {
|
||||
if (encoding.equalsIgnoreCase(charset.name())) {
|
||||
return charset;
|
||||
}
|
||||
|
||||
+28
-44
@@ -16,16 +16,14 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.dispatcher;
|
||||
package org.apache.struts2.util;
|
||||
|
||||
import org.apache.struts2.StrutsStatics;
|
||||
import org.apache.struts2.ServletActionContext;
|
||||
|
||||
import javax.servlet.jsp.PageContext;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -40,16 +38,17 @@ import java.util.Set;
|
||||
* <li>Session scope</li>
|
||||
* <li>Application scope</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
*
|
||||
* A object is searched in the order above, starting from page and ending at application scope.
|
||||
*
|
||||
*/
|
||||
public class AttributeMap extends AbstractMap<String, Object> {
|
||||
public class AttributeMap implements Map {
|
||||
|
||||
protected static final String UNSUPPORTED = "method makes no sense for a simplified map";
|
||||
|
||||
private final Map<String, Object> context;
|
||||
Map context;
|
||||
|
||||
public AttributeMap(Map<String, Object> context) {
|
||||
public AttributeMap(Map context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@@ -74,22 +73,18 @@ public class AttributeMap extends AbstractMap<String, Object> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Map.Entry<String, Object>> entrySet() {
|
||||
return Collections.unmodifiableSet(this.context.entrySet());
|
||||
public Set entrySet() {
|
||||
return Collections.EMPTY_SET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(Object key) {
|
||||
if (key == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
PageContext pc = getPageContext();
|
||||
|
||||
if (pc == null) {
|
||||
RequestMap request = (RequestMap) context.get(DispatcherConstants.REQUEST);
|
||||
SessionMap session = (SessionMap) context.get(DispatcherConstants.SESSION);
|
||||
ApplicationMap application = (ApplicationMap) context.get(DispatcherConstants.APPLICATION);
|
||||
Map request = (Map) context.get("request");
|
||||
Map session = (Map) context.get("session");
|
||||
Map application = (Map) context.get("application");
|
||||
|
||||
if ((request != null) && (request.get(key) != null)) {
|
||||
return request.get(key);
|
||||
@@ -99,23 +94,26 @@ public class AttributeMap extends AbstractMap<String, Object> {
|
||||
return application.get(key);
|
||||
}
|
||||
} else {
|
||||
return pc.findAttribute(key.toString());
|
||||
try {
|
||||
return pc.findAttribute(key.toString());
|
||||
} catch (NullPointerException npe) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> keySet() {
|
||||
return Collections.unmodifiableSet(this.context.keySet());
|
||||
public Set keySet() {
|
||||
return Collections.EMPTY_SET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object put(String key, Object value) {
|
||||
public Object put(Object key, Object value) {
|
||||
PageContext pc = getPageContext();
|
||||
if (pc != null) {
|
||||
pc.setAttribute(key, value);
|
||||
return value;
|
||||
pc.setAttribute(key.toString(), value);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -137,21 +135,21 @@ public class AttributeMap extends AbstractMap<String, Object> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Object> values() {
|
||||
return Collections.unmodifiableCollection(this.context.values());
|
||||
public Collection values() {
|
||||
return Collections.EMPTY_SET;
|
||||
}
|
||||
|
||||
private PageContext getPageContext() {
|
||||
return (PageContext) context.get(StrutsStatics.PAGE_CONTEXT);
|
||||
return (PageContext) context.get(ServletActionContext.PAGE_CONTEXT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AttributeMap {" +
|
||||
"request=" + toStringSafe(context.get(DispatcherConstants.REQUEST)) +
|
||||
", session=" + toStringSafe(context.get(DispatcherConstants.SESSION)) +
|
||||
", application=" + toStringSafe(context.get(DispatcherConstants.APPLICATION)) +
|
||||
'}';
|
||||
"request=" + toStringSafe(context.get("request")) +
|
||||
", session=" + toStringSafe(context.get("session")) +
|
||||
", application=" + toStringSafe(context.get("application")) +
|
||||
'}';
|
||||
}
|
||||
|
||||
private String toStringSafe(Object obj) {
|
||||
@@ -165,18 +163,4 @@ public class AttributeMap extends AbstractMap<String, Object> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof AttributeMap)) return false;
|
||||
if (!super.equals(o)) return false;
|
||||
AttributeMap that = (AttributeMap) o;
|
||||
return Objects.equals(context, that.context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(super.hashCode(), context);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,17 +28,19 @@ import org.apache.struts2.dispatcher.DispatcherErrorHandler;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.util.Collections.emptyMap;
|
||||
|
||||
/**
|
||||
* Generic test setup methods to be used with any unit testing framework.
|
||||
* Generic test setup methods to be used with any unit testing framework.
|
||||
*/
|
||||
public class StrutsTestCaseHelper {
|
||||
|
||||
public static Dispatcher initDispatcher(ServletContext ctx, Map<String, String> params) {
|
||||
Dispatcher du = new DispatcherWrapper(ctx, params != null ? params : emptyMap());
|
||||
|
||||
public static Dispatcher initDispatcher(ServletContext ctx, Map<String,String> params) {
|
||||
if (params == null) {
|
||||
params = new HashMap<>();
|
||||
}
|
||||
Dispatcher du = new DispatcherWrapper(ctx, params);
|
||||
du.init();
|
||||
Dispatcher.setInstance(du);
|
||||
|
||||
@@ -50,16 +52,8 @@ public class StrutsTestCaseHelper {
|
||||
return du;
|
||||
}
|
||||
|
||||
public static void tearDown(Dispatcher dispatcher) {
|
||||
if (dispatcher != null && dispatcher.getConfigurationManager() != null) {
|
||||
dispatcher.cleanup();
|
||||
}
|
||||
tearDown();
|
||||
}
|
||||
|
||||
public static void tearDown() {
|
||||
(new Dispatcher(null, null)).cleanUpAfterInit(); // Clear ContainerHolder
|
||||
Dispatcher.clearInstance();
|
||||
public static void tearDown() throws Exception {
|
||||
Dispatcher.setInstance(null);
|
||||
ActionContext.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -18,10 +18,13 @@
|
||||
*/
|
||||
package org.apache.struts2.util;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.ObjectFactory;
|
||||
import com.opensymphony.xwork2.ognl.OgnlUtil;
|
||||
import com.opensymphony.xwork2.util.ClassLoaderUtil;
|
||||
import com.opensymphony.xwork2.util.TextParseUtil;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import ognl.OgnlException;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.StrutsException;
|
||||
@@ -59,6 +62,7 @@ public class StrutsUtil {
|
||||
protected HttpServletRequest request;
|
||||
protected HttpServletResponse response;
|
||||
protected Map<String, Class<?>> classes = new HashMap<>();
|
||||
protected OgnlUtil ognl;
|
||||
protected ValueStack stack;
|
||||
|
||||
private final UrlHelper urlHelper;
|
||||
@@ -68,6 +72,7 @@ public class StrutsUtil {
|
||||
this.stack = stack;
|
||||
this.request = request;
|
||||
this.response = response;
|
||||
this.ognl = stack.getActionContext().getContainer().getInstance(OgnlUtil.class);
|
||||
this.urlHelper = stack.getActionContext().getContainer().getInstance(UrlHelper.class);
|
||||
this.objectFactory = stack.getActionContext().getContainer().getInstance(ObjectFactory.class);
|
||||
}
|
||||
@@ -119,11 +124,13 @@ public class StrutsUtil {
|
||||
}
|
||||
|
||||
public Object findValue(String expr, Object context) {
|
||||
stack.push(context);
|
||||
try {
|
||||
return stack.findValue(expr, true);
|
||||
} finally {
|
||||
stack.pop();
|
||||
return ognl.getValue(expr, ActionContext.getContext().getContextMap(), context);
|
||||
} catch (OgnlException e) {
|
||||
if (e.getReason() instanceof SecurityException) {
|
||||
LOG.error(format("Could not evaluate this expression due to security constraints: [{0}]", expr), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.util;
|
||||
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
|
||||
/**
|
||||
* @since 6.4.0
|
||||
*/
|
||||
public interface ValueStackProvider {
|
||||
|
||||
ValueStack getValueStack();
|
||||
|
||||
}
|
||||
@@ -33,6 +33,7 @@ import freemarker.template.TemplateModelException;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.ServletActionContext;
|
||||
import org.apache.struts2.StrutsStatics;
|
||||
import org.apache.struts2.result.StrutsResultSupport;
|
||||
|
||||
@@ -206,7 +207,7 @@ public class FreemarkerResult extends StrutsResultSupport {
|
||||
* @throws TemplateException in case of freemarker configuration errors
|
||||
*/
|
||||
protected Configuration getConfiguration() throws TemplateException {
|
||||
return freemarkerManager.getConfiguration(ActionContext.getContext().getServletContext());
|
||||
return freemarkerManager.getConfiguration(ServletActionContext.getServletContext());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -243,7 +244,7 @@ public class FreemarkerResult extends StrutsResultSupport {
|
||||
if (writer != null) {
|
||||
return writer;
|
||||
}
|
||||
return ActionContext.getContext().getServletResponse().getWriter();
|
||||
return ServletActionContext.getResponse().getWriter();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -260,6 +261,7 @@ public class FreemarkerResult extends StrutsResultSupport {
|
||||
* <li>request - the HttpServletRequst object for direct access
|
||||
* <li>response - the HttpServletResponse object for direct access
|
||||
* <li>stack - the OgnLValueStack instance for direct access
|
||||
* <li>ognl - the instance of the OgnlTool
|
||||
* <li>action - the action itself
|
||||
* <li>exception - optional : the JSP or Servlet exception as per the servlet spec (for JSP Exception pages)
|
||||
* <li>struts - instance of the StrutsUtil class
|
||||
@@ -269,9 +271,9 @@ public class FreemarkerResult extends StrutsResultSupport {
|
||||
* @throws TemplateModelException in case of errors during creating the model
|
||||
*/
|
||||
protected TemplateModel createModel() throws TemplateModelException {
|
||||
ServletContext servletContext = ActionContext.getContext().getServletContext();
|
||||
HttpServletRequest request = ActionContext.getContext().getServletRequest();
|
||||
HttpServletResponse response = ActionContext.getContext().getServletResponse();
|
||||
ServletContext servletContext = ServletActionContext.getServletContext();
|
||||
HttpServletRequest request = ServletActionContext.getRequest();
|
||||
HttpServletResponse response = ServletActionContext.getResponse();
|
||||
ValueStack stack = ActionContext.getContext().getValueStack();
|
||||
|
||||
Object action = null;
|
||||
@@ -319,7 +321,7 @@ public class FreemarkerResult extends StrutsResultSupport {
|
||||
protected boolean preTemplateProcess(Template template, TemplateModel model) throws IOException {
|
||||
Object attrContentType = template.getCustomAttribute("content_type");
|
||||
|
||||
HttpServletResponse response = ActionContext.getContext().getServletResponse();
|
||||
HttpServletResponse response = ServletActionContext.getResponse();
|
||||
if (response.getContentType() == null) {
|
||||
if (attrContentType != null) {
|
||||
response.setContentType(attrContentType.toString());
|
||||
@@ -347,7 +349,7 @@ public class FreemarkerResult extends StrutsResultSupport {
|
||||
}
|
||||
|
||||
private boolean isInsideActionTag() {
|
||||
Object attribute = ActionContext.getContext().getServletRequest().getAttribute(StrutsStatics.STRUTS_ACTION_TAG_INVOCATION);
|
||||
Object attribute = ServletActionContext.getRequest().getAttribute(StrutsStatics.STRUTS_ACTION_TAG_INVOCATION);
|
||||
return (Boolean) ObjectUtils.defaultIfNull(attribute, Boolean.FALSE);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import com.opensymphony.xwork2.util.ValueStack;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.ServletActionContext;
|
||||
import org.apache.struts2.dispatcher.AttributeMap;
|
||||
import org.apache.struts2.util.AttributeMap;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.jsp.PageContext;
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.views.jsp.ui;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.ognl.OgnlUtil;
|
||||
import ognl.OgnlException;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
/**
|
||||
* @deprecated since 6.3.0. Use {@link org.apache.struts2.util.StrutsUtil} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public class OgnlTool {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(OgnlTool.class);
|
||||
|
||||
private OgnlUtil ognlUtil;
|
||||
|
||||
public OgnlTool() {
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setOgnlUtil(OgnlUtil ognlUtil) {
|
||||
this.ognlUtil = ognlUtil;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 6.3.0. Use {@link org.apache.struts2.util.StrutsUtil#findValue(String, Object)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public Object findValue(String expr, Object context) {
|
||||
try {
|
||||
return ognlUtil.getValue(expr, ActionContext.getContext().getContextMap(), context);
|
||||
} catch (OgnlException e) {
|
||||
if (e.getReason() instanceof SecurityException) {
|
||||
LOG.error("Could not evaluate this expression due to security constraints: [{}]", expr, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,6 @@ package org.apache.struts2.views.util;
|
||||
|
||||
import com.opensymphony.xwork2.ActionInvocation;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import org.apache.struts2.dispatcher.DispatcherConstants;
|
||||
import org.apache.struts2.util.StrutsUtil;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@@ -32,11 +31,12 @@ import java.util.Map;
|
||||
* Value Stack's Context related Utilities.
|
||||
*/
|
||||
public class ContextUtil {
|
||||
public static final String REQUEST = DispatcherConstants.REQUEST;
|
||||
public static final String RESPONSE = DispatcherConstants.RESPONSE;
|
||||
public static final String SESSION = DispatcherConstants.SESSION;
|
||||
public static final String REQUEST = "request";
|
||||
public static final String RESPONSE = "response";
|
||||
public static final String SESSION = "session";
|
||||
public static final String BASE = "base";
|
||||
public static final String STACK = "stack";
|
||||
public static final String OGNL = "ognl";
|
||||
public static final String STRUTS = "struts";
|
||||
public static final String ACTION = "action";
|
||||
|
||||
@@ -49,6 +49,7 @@ public class ContextUtil {
|
||||
map.put(STACK, stack);
|
||||
StrutsUtil util = new StrutsUtil(stack, req, res);
|
||||
map.put(STRUTS, util);
|
||||
map.put(OGNL, util); // Deprecated since 6.3.0
|
||||
|
||||
ActionInvocation invocation = stack.getActionContext().getActionInvocation();
|
||||
if (invocation != null) {
|
||||
|
||||
@@ -218,6 +218,9 @@ struts.mapper.alwaysSelectFullNamespace=false
|
||||
### Whether to allow static field access in OGNL expressions or not
|
||||
struts.ognl.allowStaticFieldAccess=true
|
||||
|
||||
### Whether to allow static method access in OGNL expressions or not
|
||||
struts.ognl.allowStaticMethodAccess=false
|
||||
|
||||
### Whether to throw a RuntimeException when a property is not found
|
||||
### in an expression, or when the expression evaluation fails
|
||||
struts.el.throwExceptionOnFailure=false
|
||||
@@ -225,24 +228,40 @@ struts.el.throwExceptionOnFailure=false
|
||||
### Logs as Warnings properties that are not found (very verbose)
|
||||
struts.ognl.logMissingProperties=false
|
||||
|
||||
### Caches parsed OGNL expressions, but can lead to memory leaks
|
||||
### if the application generates a lot of different expressions
|
||||
struts.ognl.enableExpressionCache=true
|
||||
|
||||
### Specify the OGNL expression cache factory and BeanInfo cache factory to use.
|
||||
### Currently, the default implementations are used, but can be replaced with custom ones if desired.
|
||||
# struts.ognl.expressionCacheFactory=customOgnlExpressionCacheFactory
|
||||
# struts.ognl.beanInfoCacheFactory=customOgnlBeanInfoCacheFactory
|
||||
|
||||
### Specifies the type of cache to use for parsed OGNL expressions. See StrutsConstants class for further information.
|
||||
struts.ognl.expressionCacheType=wtlfu
|
||||
### Specify a limit to the number of entries in the OGNL expressionCache.
|
||||
### For the standard expressionCache mode, when the limit is exceeded the entire cache's
|
||||
### content will be cleared (can help prevent memory leaks).
|
||||
### For expressionCacheLRUMode true, the limit will ensure the cache does not exceed
|
||||
### that size, dropping the oldest (least-recently-used) expressions to add new ones.
|
||||
### NOTE: If not set, the default is 25000, which may be excessive.
|
||||
# struts.ognl.expressionCacheMaxSize=1000
|
||||
|
||||
### Specifies the maximum cache size for parsed OGNL expressions. This should be configured based on the cache type
|
||||
### chosen and application-specific needs.
|
||||
struts.ognl.expressionCacheMaxSize=10000
|
||||
### Indicates if the OGNL expressionCache should use LRU mode.
|
||||
### NOTE: When true, make sure to set the expressionCacheMaxSize to a reasonable value
|
||||
### for your application. Otherwise the default limit will never (practically) be reached.
|
||||
# struts.ognl.expressionCacheLRUMode=false
|
||||
|
||||
### Specifies the type of cache to use for BeanInfo objects. See StrutsConstants class for further information.
|
||||
struts.ognl.beanInfoCacheType=wtlfu
|
||||
### Specify a limit to the number of entries in the OGNL beanInfoCache.
|
||||
### For the standard beanInfoCache mode, when the limit is exceeded the entire cache's
|
||||
### content will be cleared (can help prevent memory leaks).
|
||||
### For beanInfoCacheLRUMode true, the limit will ensure the cache does not exceed
|
||||
### that size, dropping the oldest (least-recently-used) expressions to add new ones.
|
||||
### NOTE: If not set, the default is 25000, which may be excessive.
|
||||
# struts.ognl.beanInfoCacheMaxSize=1000
|
||||
|
||||
### Specifies the maximum cache size for BeanInfo objects. This should be configured based on the cache type chosen and
|
||||
### application-specific needs.
|
||||
struts.ognl.beanInfoCacheMaxSize=10000
|
||||
### Indicates if the OGNL beanInfoCache should use LRU mode.
|
||||
### NOTE: When true, make sure to set the beanInfoCacheMaxSize to a reasonable value
|
||||
### for your application. Otherwise the default limit will never (practically) be reached.
|
||||
# struts.ognl.beanInfoCacheLRUMode=false
|
||||
|
||||
### Indicates if Dispatcher should handle unexpected exceptions by calling sendError()
|
||||
### or simply rethrow it as a ServletException to allow future processing by other frameworks like Spring Security
|
||||
|
||||
@@ -20,39 +20,39 @@
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<@s.style>
|
||||
<style>
|
||||
.debugTable {
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
|
||||
.debugTable td {
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
|
||||
.nameColumn {
|
||||
background-color:#CCDDFF;
|
||||
}
|
||||
|
||||
|
||||
.valueColumn {
|
||||
background-color: #CCFFCC;
|
||||
}
|
||||
|
||||
|
||||
.nullValue {
|
||||
background-color: #FF0000;
|
||||
}
|
||||
|
||||
|
||||
.typeColumn {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
|
||||
.emptyCollection {
|
||||
background-color: #EEEEEE;
|
||||
}
|
||||
</@s.style>
|
||||
</style>
|
||||
|
||||
<@s.script>
|
||||
<script>
|
||||
function expand(src, path) {
|
||||
var baseUrl = location.href;
|
||||
var i = baseUrl.indexOf("&object=");
|
||||
@@ -83,7 +83,7 @@
|
||||
};
|
||||
request.send();
|
||||
}
|
||||
</@s.script>
|
||||
</script>
|
||||
|
||||
<body>
|
||||
${debugHtml?no_esc}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<@s.script>
|
||||
<@s.script type="text/javascript">
|
||||
var baseUrl = "<@s.url value="/static" includeParams="none"/>";
|
||||
window.open(baseUrl+"/webconsole.html", 'OGNL Console','width=500,height=450,status=no,toolbar=no,menubar=no');
|
||||
</@s.script>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user