/*
* Copyright (c) 2002-2012 Alibaba Group Holding Limited.
* All rights reserved.
*
* 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.alibaba.citrus.turbine.auth.impl;
import static com.alibaba.citrus.turbine.auth.impl.PageAuthorizationServiceImpl.PageAuthorizationResult.*;
import static com.alibaba.citrus.util.ArrayUtil.*;
import static com.alibaba.citrus.util.BasicConstant.*;
import static com.alibaba.citrus.util.CollectionUtil.*;
import static com.alibaba.citrus.util.StringUtil.*;
import static java.lang.Boolean.*;
import static java.util.Collections.*;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.regex.Matcher;
import com.alibaba.citrus.service.AbstractService;
import com.alibaba.citrus.turbine.auth.PageAuthorizationService;
import com.alibaba.citrus.util.ObjectUtil;
/**
* 为页面授权的service。
*
* @author Michael Zhou
*/
public class PageAuthorizationServiceImpl extends AbstractService<PageAuthorizationService> implements
PageAuthorizationService {
private final List<AuthMatch> matches = createLinkedList();
private boolean allowByDefault = false;
public void setMatches(AuthMatch[] matches) {
this.matches.clear();
if (matches != null) {
for (AuthMatch match : matches) {
this.matches.add(match);
}
}
}
public boolean isAllowByDefault() {
return allowByDefault;
}
public void setAllowByDefault(boolean allowByDefault) {
this.allowByDefault = allowByDefault;
}
public boolean isAllow(String target, String userName, String[] roleNames, String... actions) {
PageAuthorizationResult result = authorize(target, userName, roleNames, actions);
switch (result) {
case ALLOWED:
return true;
case DENIED:
return false;
default:
return allowByDefault;
}
}
public PageAuthorizationResult authorize(String target, String userName, String[] roleNames, String... actions) {
userName = trimToNull(userName);
if (actions == null) {
actions = new String[] { EMPTY_STRING };
}
if (roleNames == null) {
roleNames = EMPTY_STRING_ARRAY;
}
// 找出所有匹配的pattern,按匹配长度倒排序。
MatchResult[] results = getMatchResults(target);
PageAuthorizationResult result;
if (isEmptyArray(results)) {
result = TARGET_NOT_MATCH;
} else {
boolean grantNotMatch = false;
for (int i = 0; i < actions.length; i++) {
actions[i] = trimToEmpty(actions[i]);
Boolean actionAllowed = isActionAllowed(results, target, userName, roleNames, actions[i]);
if (actionAllowed == null) {
grantNotMatch = true;
} else if (!actionAllowed) {
return DENIED;
}
}
if (!grantNotMatch) {
if (getLogger().isDebugEnabled()) {
getLogger().debug(
"Access Permitted: target=\"{}\", user=\"{}\", roles={}, action={}",
new Object[] { target, userName, ObjectUtil.toString(roleNames),
ObjectUtil.toString(actions) });
}
return ALLOWED;
} else {
result = GRANT_NOT_MATCH;
}
}
if (allowByDefault) {
if (getLogger().isDebugEnabled()) {
getLogger()
.debug("Access Permitted. No matches found for request: target=\"{}\", user=\"{}\", roles={}, action={}",
new Object[] { target, userName, ObjectUtil.toString(roleNames),
ObjectUtil.toString(actions) });
}
} else {
if (getLogger().isWarnEnabled()) {
getLogger()
.warn("Access Denied. No matches found for request: target=\"{}\", user=\"{}\", roles={}, action={}",
new Object[] { target, userName, ObjectUtil.toString(roleNames),
ObjectUtil.toString(actions) });
}
}
return result;
}
private Boolean isActionAllowed(MatchResult[] results, String target, String userName, String[] roleNames,
String action) {
// 按顺序检查授权,直到role或user被allow或deny
for (MatchResult result : results) {
AuthMatch match = result.match;
// 倒序检查grant,后面的覆盖前面的。
for (int i = match.getGrants().length - 1; i >= 0; i--) {
AuthGrant grant = match.getGrants()[i];
// 判断user或role是否匹配
boolean userMatch = grant.isUserMatched(userName);
boolean roleMatch = grant.areRolesMatched(roleNames);
if (userMatch || roleMatch) {
// 判断action是否匹配
boolean actionAllowed = grant.isActionAllowed(action);
boolean actionDenied = grant.isActionDenied(action);
if (actionAllowed || actionDenied) {
boolean allowed = !actionDenied;
if (allowed) {
if (getLogger().isTraceEnabled()) {
getLogger()
.trace("Access Partially Permitted: target=\"{}\", user=\"{}\", roles={}, action=\"{}\"\n{}",
new Object[] { target, userName, ObjectUtil.toString(roleNames),
action, match.toString(i) });
}
return TRUE;
} else {
if (getLogger().isWarnEnabled()) {
getLogger().warn(
"Access Denied: target=\"{}\", user=\"{}\", roles={}, action=\"{}\"\n{}",
new Object[] { target, userName, ObjectUtil.toString(roleNames), action,
match.toString(i) });
}
return FALSE;
}
}
}
}
}
return null;
}
private MatchResult[] getMatchResults(String target) {
List<MatchResult> results = createArrayList(matches.size());
// 匹配所有,注意,这里按倒序匹配,这样长度相同的匹配,以后面的为准。
for (ListIterator<AuthMatch> i = matches.listIterator(matches.size()); i.hasPrevious(); ) {
AuthMatch match = i.previous();
Matcher matcher = match.getPattern().matcher(target);
if (matcher.find()) {
MatchResult result = new MatchResult();
result.matchLength = matcher.end() - matcher.start();
result.match = match;
result.target = target;
results.add(result);
}
}
// 按匹配长度倒排序,注意,这是稳定排序,对于长度相同的匹配,顺序不变。
sort(results);
// 除去重复的匹配
Map<AuthGrant[], MatchResult> grantsSet = createLinkedHashMap();
for (MatchResult result : results) {
AuthGrant[] grants = result.match.getGrants();
if (!grantsSet.containsKey(grants)) {
grantsSet.put(grants, result);
}
}
return grantsSet.values().toArray(new MatchResult[grantsSet.size()]);
}
private static class MatchResult implements Comparable<MatchResult> {
private int matchLength = -1;
private AuthMatch match;
private String target;
public int compareTo(MatchResult o) {
return o.matchLength - matchLength;
}
@Override
public String toString() {
return "Match length=" + matchLength + ", target=" + target + ", " + match;
}
}
public static enum PageAuthorizationResult {
/** 代表页面被许可访问。 */
ALLOWED,
/** 代表页面被拒绝访问。 */
DENIED,
/** 代表当前的target未匹配。 */
TARGET_NOT_MATCH,
/** 代表当前的grant未匹配,也就是user/roles/actions未匹配。 */
GRANT_NOT_MATCH
}
}