Skip to content

Commit

Permalink
Separate checking logic from AuthorityRule and add test cases
Browse files Browse the repository at this point in the history
Signed-off-by: Eric Zhao <sczyh16@gmail.com>
  • Loading branch information
sczyh30 committed Sep 17, 2018
1 parent b5b3f6e commit 015efe2
Show file tree
Hide file tree
Showing 9 changed files with 402 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@
*/
package com.alibaba.csp.sentinel.slots.block.authority;

import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.csp.sentinel.context.Context;
import com.alibaba.csp.sentinel.node.DefaultNode;
import com.alibaba.csp.sentinel.slots.block.AbstractRule;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;

/**
* Authority rule is designed for limiting by request origins.
*
* @author youji.zj
*/
public class AuthorityRule extends AbstractRule {
Expand All @@ -35,8 +36,9 @@ public int getStrategy() {
return strategy;
}

public void setStrategy(int strategy) {
public AuthorityRule setStrategy(int strategy) {
this.strategy = strategy;
return this;
}

@Override
Expand All @@ -59,38 +61,6 @@ public int hashCode() {

@Override
public boolean passCheck(Context context, DefaultNode node, int count, Object... args) {
String requester = context.getOrigin();

// Empty origin or empty limitApp will pass.
if (StringUtil.isEmpty(requester) || StringUtil.isEmpty(this.getLimitApp())) {
return true;
}

// Do exact match with origin name.
int pos = this.getLimitApp().indexOf(requester);
boolean contain = pos > -1;

if (contain) {
boolean exactlyMatch = false;
String[] appArray = this.getLimitApp().split(",");
for (String app : appArray) {
if (requester.equals(app)) {
exactlyMatch = true;
break;
}
}

contain = exactlyMatch;
}

if (strategy == RuleConstant.AUTHORITY_BLACK && contain) {
return false;
}

if (strategy == RuleConstant.AUTHORITY_WHITE && !contain) {
return false;
}

return true;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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.csp.sentinel.slots.block.authority;

import com.alibaba.csp.sentinel.context.Context;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.util.StringUtil;

/**
* Rule checker for white/black list authority.
*
* @author Eric Zhao
* @since 0.2.0
*/
final class AuthorityRuleChecker {

static boolean passCheck(AuthorityRule rule, Context context) {
String requester = context.getOrigin();

// Empty origin or empty limitApp will pass.
if (StringUtil.isEmpty(requester) || StringUtil.isEmpty(rule.getLimitApp())) {
return true;
}

// Do exact match with origin name.
int pos = rule.getLimitApp().indexOf(requester);
boolean contain = pos > -1;

if (contain) {
boolean exactlyMatch = false;
String[] appArray = rule.getLimitApp().split(",");
for (String app : appArray) {
if (requester.equals(app)) {
exactlyMatch = true;
break;
}
}

contain = exactlyMatch;
}

int strategy = rule.getStrategy();
if (strategy == RuleConstant.AUTHORITY_BLACK && contain) {
return false;
}

if (strategy == RuleConstant.AUTHORITY_WHITE && !contain) {
return false;
}

return true;
}

private AuthorityRuleChecker() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,9 @@

import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.csp.sentinel.context.Context;
import com.alibaba.csp.sentinel.node.DefaultNode;
import com.alibaba.csp.sentinel.property.DynamicSentinelProperty;
import com.alibaba.csp.sentinel.property.PropertyListener;
import com.alibaba.csp.sentinel.property.SentinelProperty;
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;

/**
Expand All @@ -38,7 +34,7 @@
* @author jialiang.linjl
* @author Eric Zhao
*/
public class AuthorityRuleManager {
public final class AuthorityRuleManager {

private static Map<String, List<AuthorityRule>> authorityRules
= new ConcurrentHashMap<String, List<AuthorityRule>>();
Expand All @@ -59,6 +55,7 @@ public static void register2Property(SentinelProperty<List<AuthorityRule>> prope
}
property.addListener(listener);
currentProperty = property;
RecordLog.info("[AuthorityRuleManager] Registering new property to authority rule manager");
}
}

Expand All @@ -71,24 +68,6 @@ public static void loadRules(List<AuthorityRule> rules) {
currentProperty.updateValue(rules);
}

public static void checkAuthority(ResourceWrapper resource, Context context, DefaultNode node, int count)
throws BlockException {
if (authorityRules == null) {
return;
}

List<AuthorityRule> rules = authorityRules.get(resource.getName());
if (rules == null) {
return;
}

for (AuthorityRule rule : rules) {
if (!rule.passCheck(context, node, count)) {
throw new AuthorityException(context.getOrigin());
}
}
}

public static boolean hasConfig(String resource) {
return authorityRules.containsKey(resource);
}
Expand Down Expand Up @@ -123,11 +102,18 @@ public void configUpdate(List<AuthorityRule> conf) {
}

private Map<String, List<AuthorityRule>> loadAuthorityConf(List<AuthorityRule> list) {
if (list == null) {
return null;
}
Map<String, List<AuthorityRule>> newRuleMap = new ConcurrentHashMap<String, List<AuthorityRule>>();

if (list == null || list.isEmpty()) {
return newRuleMap;
}

for (AuthorityRule rule : list) {
if (!isValidRule(rule)) {
RecordLog.warn("[AuthorityRuleManager] Ignoring invalid authority rule when loading new rules: " + rule);
continue;
}

if (StringUtil.isBlank(rule.getLimitApp())) {
rule.setLimitApp(FlowRule.LIMIT_APP_DEFAULT);
}
Expand Down Expand Up @@ -160,4 +146,11 @@ public void configLoad(List<AuthorityRule> value) {
}
}

static Map<String, List<AuthorityRule>> getAuthorityRules() {
return authorityRules;
}

static boolean isValidRule(AuthorityRule rule) {
return rule != null && !StringUtil.isBlank(rule.getResource());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
*/
package com.alibaba.csp.sentinel.slots.block.authority;

import java.util.List;
import java.util.Map;

import com.alibaba.csp.sentinel.context.Context;
import com.alibaba.csp.sentinel.node.DefaultNode;
import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot;
Expand All @@ -25,18 +28,38 @@
* A {@link ProcessorSlot} that dedicates to {@link AuthorityRule} checking.
*
* @author leyou
* @author Eric Zhao
*/
public class AuthoritySlot extends AbstractLinkedProcessorSlot<DefaultNode> {

@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, Object... args)
throws Throwable {
AuthorityRuleManager.checkAuthority(resourceWrapper, context, node, count);
checkBlackWhiteAuthority(resourceWrapper, context);
fireEntry(context, resourceWrapper, node, count, args);
}

@Override
public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
fireExit(context, resourceWrapper, count, args);
}

void checkBlackWhiteAuthority(ResourceWrapper resource, Context context) throws AuthorityException {
Map<String, List<AuthorityRule>> authorityRules = AuthorityRuleManager.getAuthorityRules();

if (authorityRules == null) {
return;
}

List<AuthorityRule> rules = authorityRules.get(resource.getName());
if (rules == null) {
return;
}

for (AuthorityRule rule : rules) {
if (!AuthorityRuleChecker.passCheck(rule, context)) {
throw new AuthorityException(context.getOrigin());
}
}
}
}
35 changes: 35 additions & 0 deletions sentinel-core/src/test/java/com/alibaba/csp/sentinel/TestUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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.csp.sentinel;

import com.alibaba.csp.sentinel.context.Context;
import com.alibaba.csp.sentinel.context.ContextUtil;

/**
* @author Eric Zhao
*/
public final class TestUtil {

public static void cleanUpContext() {
Context context = ContextUtil.getContext();
if (context != null) {
context.setCurEntry(null);
ContextUtil.exit();
}
}

private TestUtil() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.alibaba.csp.sentinel.slots.block.authority;

import com.alibaba.csp.sentinel.TestUtil;
import com.alibaba.csp.sentinel.context.ContextUtil;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;

import org.junit.Before;
import org.junit.Test;

import static org.junit.Assert.*;

/**
* Test cases for {@link AuthorityRuleChecker}.
*
* @author Eric Zhao
*/
public class AuthorityRuleCheckerTest {

@Before
public void setUp() {
TestUtil.cleanUpContext();
}

@Test
public void testPassCheck() {
String origin = "appA";
ContextUtil.enter("entrance", origin);
try {
String resourceName = "testPassCheck";
AuthorityRule ruleA = new AuthorityRule()
.setResource(resourceName)
.setLimitApp(origin + ",appB")
.as(AuthorityRule.class)
.setStrategy(RuleConstant.AUTHORITY_WHITE);
AuthorityRule ruleB = new AuthorityRule()
.setResource(resourceName)
.setLimitApp("appB")
.as(AuthorityRule.class)
.setStrategy(RuleConstant.AUTHORITY_WHITE);
AuthorityRule ruleC = new AuthorityRule()
.setResource(resourceName)
.setLimitApp(origin)
.as(AuthorityRule.class)
.setStrategy(RuleConstant.AUTHORITY_BLACK);
AuthorityRule ruleD = new AuthorityRule()
.setResource(resourceName)
.setLimitApp("appC")
.as(AuthorityRule.class)
.setStrategy(RuleConstant.AUTHORITY_BLACK);

assertTrue(AuthorityRuleChecker.passCheck(ruleA, ContextUtil.getContext()));
assertFalse(AuthorityRuleChecker.passCheck(ruleB, ContextUtil.getContext()));
assertFalse(AuthorityRuleChecker.passCheck(ruleC, ContextUtil.getContext()));
assertTrue(AuthorityRuleChecker.passCheck(ruleD, ContextUtil.getContext()));
} finally {
ContextUtil.exit();
}
}
}
Loading

0 comments on commit 015efe2

Please sign in to comment.