blob: 92854cd5a68b480eb73151dd108f4f15a81bc566 [file] [log] [blame]
/**
* 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.activemq.shiro.authz;
import org.apache.shiro.authz.Permission;
import org.apache.shiro.authz.permission.WildcardPermission;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;
/**
* @since 5.10.0
*/
public class ActiveMQWildcardPermission extends WildcardPermission {
private final boolean caseSensitive;
public ActiveMQWildcardPermission(String wildcardString) {
this(wildcardString, true);
}
public ActiveMQWildcardPermission(String wildcardString, boolean caseSensitive) {
super(wildcardString, caseSensitive);
this.caseSensitive = caseSensitive;
}
@Override
public boolean implies(Permission p) {
// By default only supports comparisons with other WildcardPermissions
if (!(p instanceof WildcardPermission)) {
return false;
}
WildcardPermission wp = (WildcardPermission) p;
List<Set<String>> otherParts = getParts(wp);
int i = 0;
for (Set<String> otherPart : otherParts) {
// If this permission has less parts than the other permission, everything after the number of parts contained
// in this permission is automatically implied, so return true
if (getParts().size() - 1 < i) {
return true;
} else {
Set<String> thisPart = getParts().get(i);
// all tokens from otherPart must pass at least one token from thisPart
for (String otherToken : otherPart) {
if (!caseSensitive) {
otherToken = otherToken.toLowerCase();
}
boolean otherIsMatched = false;
for (String token : thisPart) {
if (token.equals(WILDCARD_TOKEN)) {
otherIsMatched = true;
break;
}
if (matches(token, otherToken)) {
otherIsMatched = true;
break;
}
}
if (!otherIsMatched) {
return false;
}
}
i++;
}
}
// If this permission has more parts than the other parts, only imply it if all of the other parts are wildcards
for (; i < getParts().size(); i++) {
Set<String> part = getParts().get(i);
if (!part.contains(WILDCARD_TOKEN)) {
return false;
}
}
return true;
}
/**
* Tests whether or not a string matches against a pattern.
* The pattern may contain two special characters:<br>
* '*' means zero or more characters<br>
* '?' means one and only one character
*
* @param pattern pattern to match against.
* Must not be <code>null</code>.
* @param value string which must be matched against the pattern.
* Must not be <code>null</code>.
* @return <code>true</code> if the string matches against the
* pattern, or <code>false</code> otherwise.
*/
protected boolean matches(String pattern, String value) {
char[] patArr = pattern.toCharArray();
char[] valArr = value.toCharArray();
int patIndex = 0;
int patEndIndex = patArr.length - 1;
int valIndex = 0;
int valEndIndex = valArr.length - 1;
char ch;
boolean patternContainsStar = false;
for (char patternChar : patArr) {
if (patternChar == '*') {
patternContainsStar = true;
break;
}
}
if (!patternContainsStar) {
// No '*'s, so we make a shortcut
if (patEndIndex != valEndIndex) {
return false; // Pattern and string do not have the same size
}
for (int i = 0; i <= patEndIndex; i++) {
ch = patArr[i];
if (ch != '?') {
if (ch != valArr[i]) {
return false;// Character mismatch
}
}
}
return true; // String matches against pattern
}
// Process characters before first star
while ((ch = patArr[patIndex]) != '*' && valIndex <= valEndIndex) {
if (ch != '?') {
if (ch != valArr[valIndex]) {
return false;// Character mismatch
}
}
patIndex++;
valIndex++;
}
if (valIndex > valEndIndex) {
// All characters in the value are used. Check if only '*'s remain
// in the pattern. If so, we succeeded. Otherwise failure.
for (int i = patIndex; i <= patEndIndex; i++) {
if (patArr[i] != '*') {
return false;
}
}
return true;
}
// Process characters after last star
while ((ch = patArr[patEndIndex]) != '*' && valIndex <= valEndIndex) {
if (ch != '?') {
if (ch != valArr[valEndIndex]) {
return false;// Character mismatch
}
}
patEndIndex--;
valEndIndex--;
}
if (valIndex > valEndIndex) {
// All characters in the value are used. Check if only '*'s remain
// in the pattern. If so, we succeeded. Otherwise failure.
for (int i = patIndex; i <= patEndIndex; i++) {
if (patArr[i] != '*') {
return false;
}
}
return true;
}
// process pattern between stars. patIndex and patEndIndex always point to a '*'.
while (patIndex != patEndIndex && valIndex <= valEndIndex) {
int innerPatternIndex = -1;
for (int i = patIndex + 1; i <= patEndIndex; i++) {
if (patArr[i] == '*') {
innerPatternIndex = i;
break;
}
}
if (innerPatternIndex == patIndex + 1) {
// Two stars next to each other, skip the first one.
patIndex++;
continue;
}
// Find the pattern between patIndex & innerPatternIndex in the value between
// valIndex and valEndIndex
int innerPatternLength = (innerPatternIndex - patIndex - 1);
int innerValueLength = (valEndIndex - valIndex + 1);
int foundIndex = -1;
innerValueLoop:
for (int i = 0; i <= innerValueLength - innerPatternLength; i++) {
for (int j = 0; j < innerPatternLength; j++) {
ch = patArr[patIndex + j + 1];
if (ch != '?') {
if (ch != valArr[valIndex + i + j]) {
continue innerValueLoop;
}
}
}
foundIndex = valIndex + i;
break;
}
if (foundIndex == -1) {
return false;
}
patIndex = innerPatternIndex;
valIndex = foundIndex + innerPatternLength;
}
// All characters in the string are used. Check if only '*'s are left
// in the pattern. If so, we succeeded. Otherwise failure.
for (int i = patIndex; i <= patEndIndex; i++) {
if (patArr[i] != '*') {
return false;
}
}
return true;
}
protected List<Set<String>> getParts(WildcardPermission wp) {
if (wp instanceof ActiveMQWildcardPermission) {
return ((ActiveMQWildcardPermission) wp).getParts();
} else {
return getPartsByReflection(wp);
}
}
protected List<Set<String>> getPartsByReflection(WildcardPermission wp) {
try {
return doGetPartsByReflection(wp);
} catch (Exception e) {
String msg = "Unable to obtain WildcardPermission instance's 'parts' value.";
throw new IllegalStateException(msg, e);
}
}
@SuppressWarnings("unchecked")
protected List<Set<String>> doGetPartsByReflection(WildcardPermission wp) throws Exception {
Method getParts = WildcardPermission.class.getDeclaredMethod("getParts");
getParts.setAccessible(true);
return (List<Set<String>>) getParts.invoke(wp);
}
@Override
public String toString() {
StringBuilder buffer = new StringBuilder();
for (Set<String> part : getParts()) {
if (buffer.length() > 0) {
buffer.append(":");
}
boolean first = true;
for (String token : part) {
if (!first) {
buffer.append(",");
}
buffer.append(token);
first = false;
}
}
return buffer.toString();
}
}