mirror of https://github.com/halo-dev/halo
refactor: plugin custom api generate rules (#2211)
* refactor: plugin custom api generate rules * refactor: boolean convert * fix: unit testpull/2212/head
parent
27435a1aeb
commit
f7945081a5
|
@ -6,7 +6,6 @@ import lombok.Data;
|
|||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import run.halo.app.extension.AbstractExtension;
|
||||
import run.halo.app.extension.GVK;
|
||||
|
||||
|
@ -74,19 +73,13 @@ public class Role extends AbstractExtension {
|
|||
*/
|
||||
final String[] verbs;
|
||||
|
||||
/**
|
||||
* If the plugin name exists, it means that the API is provided by the plugin.
|
||||
*/
|
||||
final String pluginName;
|
||||
|
||||
public PolicyRule() {
|
||||
this(null, null, null, null, null, null);
|
||||
this(null, null, null, null, null);
|
||||
}
|
||||
|
||||
public PolicyRule(String pluginName, String[] apiGroups, String[] resources,
|
||||
public PolicyRule(String[] apiGroups, String[] resources,
|
||||
String[] resourceNames,
|
||||
String[] nonResourceURLs, String[] verbs) {
|
||||
this.pluginName = StringUtils.defaultString(pluginName);
|
||||
this.apiGroups = nullElseEmpty(apiGroups);
|
||||
this.resources = nullElseEmpty(resources);
|
||||
this.resourceNames = nullElseEmpty(resourceNames);
|
||||
|
@ -114,11 +107,6 @@ public class Role extends AbstractExtension {
|
|||
|
||||
String pluginName;
|
||||
|
||||
public Builder pluginName(String pluginName) {
|
||||
this.pluginName = pluginName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder apiGroups(String... apiGroups) {
|
||||
this.apiGroups = apiGroups;
|
||||
return this;
|
||||
|
@ -145,7 +133,7 @@ public class Role extends AbstractExtension {
|
|||
}
|
||||
|
||||
public PolicyRule build() {
|
||||
return new PolicyRule(pluginName, apiGroups, resources, resourceNames,
|
||||
return new PolicyRule(apiGroups, resources, resourceNames,
|
||||
nonResourceURLs,
|
||||
verbs);
|
||||
}
|
||||
|
|
|
@ -121,6 +121,7 @@ public class PluginRequestMappingHandlerMapping extends RequestMappingHandlerMap
|
|||
|
||||
protected String buildPrefix(String pluginId, String version) {
|
||||
GroupVersion groupVersion = GroupVersion.parseAPIVersion(version);
|
||||
return String.format("/api/%s/plugins/%s", groupVersion.version(), pluginId);
|
||||
return String.format("/apis/plugin.api.halo.run/%s/plugins/%s", groupVersion.version(),
|
||||
pluginId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,5 +66,5 @@ public interface Attributes {
|
|||
*/
|
||||
String getPath();
|
||||
|
||||
String pluginName();
|
||||
String getSubName();
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ public class AttributesRecord implements Attributes {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String pluginName() {
|
||||
return requestInfo.getPluginName();
|
||||
public String getSubName() {
|
||||
return requestInfo.getSubName();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import java.util.Objects;
|
|||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import run.halo.app.core.extension.Role;
|
||||
import run.halo.app.core.extension.Role.PolicyRule;
|
||||
|
||||
/**
|
||||
* @author guqing
|
||||
|
@ -35,17 +34,26 @@ public class RbacRequestEvaluation {
|
|||
combinedResource =
|
||||
requestAttributes.getResource() + "/" + requestAttributes.getSubresource();
|
||||
}
|
||||
|
||||
return verbMatches(rule, requestAttributes.getVerb())
|
||||
&& apiGroupMatches(rule, requestAttributes.getApiGroup())
|
||||
&& resourceMatches(rule, combinedResource, requestAttributes.getSubresource())
|
||||
&& resourceNameMatches(rule, requestAttributes.getName())
|
||||
&& pluginNameMatches(rule, requestAttributes.pluginName());
|
||||
&& resourceNameMatches(rule,
|
||||
combineResourceName(requestAttributes.getName(), requestAttributes.getSubName()));
|
||||
}
|
||||
return verbMatches(rule, requestAttributes.getVerb())
|
||||
&& nonResourceURLMatches(rule, requestAttributes.getPath());
|
||||
}
|
||||
|
||||
private String combineResourceName(String name, String subName) {
|
||||
if (StringUtils.isBlank(name)) {
|
||||
return subName;
|
||||
}
|
||||
if (StringUtils.isBlank(subName)) {
|
||||
return name;
|
||||
}
|
||||
return name + "/" + subName;
|
||||
}
|
||||
|
||||
protected boolean verbMatches(Role.PolicyRule rule, String requestedVerb) {
|
||||
for (String ruleVerb : rule.getVerbs()) {
|
||||
if (Objects.equals(ruleVerb, WildCard.VerbAll)) {
|
||||
|
@ -71,7 +79,7 @@ public class RbacRequestEvaluation {
|
|||
}
|
||||
|
||||
protected boolean resourceMatches(Role.PolicyRule rule, String combinedRequestedResource,
|
||||
String requestedSubresource) {
|
||||
String requestedSubresource) {
|
||||
for (String ruleResource : rule.getResources()) {
|
||||
// if everything is allowed, we match
|
||||
if (Objects.equals(ruleResource, WildCard.ResourceAll)) {
|
||||
|
@ -125,8 +133,4 @@ public class RbacRequestEvaluation {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean pluginNameMatches(PolicyRule rule, String pluginName) {
|
||||
return StringUtils.equals(rule.getPluginName(), pluginName);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,34 +17,40 @@ import org.springframework.http.server.reactive.ServerHttpRequest;
|
|||
public class RequestInfo {
|
||||
boolean isResourceRequest;
|
||||
final String path;
|
||||
String pluginName;
|
||||
String namespace;
|
||||
String verb;
|
||||
String apiPrefix;
|
||||
String apiGroup;
|
||||
String apiVersion;
|
||||
String resource;
|
||||
String subresource;
|
||||
|
||||
String name;
|
||||
|
||||
String subresource;
|
||||
|
||||
String subName;
|
||||
|
||||
String[] parts;
|
||||
|
||||
public RequestInfo(boolean isResourceRequest, String path, String verb) {
|
||||
this(isResourceRequest, path, null, verb, null, null, null, null, null, null, null);
|
||||
this(isResourceRequest, path, null, verb, null, null, null, null, null, null, null, null);
|
||||
}
|
||||
|
||||
public RequestInfo(boolean isResourceRequest, String path, String pluginName, String verb,
|
||||
public RequestInfo(boolean isResourceRequest, String path, String namespace, String verb,
|
||||
String apiPrefix,
|
||||
String apiGroup,
|
||||
String apiVersion, String resource, String subresource, String name,
|
||||
String apiVersion, String resource, String name, String subresource, String subName,
|
||||
String[] parts) {
|
||||
this.isResourceRequest = isResourceRequest;
|
||||
this.path = StringUtils.defaultString(path, "");
|
||||
this.pluginName = StringUtils.defaultString(pluginName, "");
|
||||
this.namespace = StringUtils.defaultString(namespace, "");
|
||||
this.verb = StringUtils.defaultString(verb, "");
|
||||
this.apiPrefix = StringUtils.defaultString(apiPrefix, "");
|
||||
this.apiGroup = StringUtils.defaultString(apiGroup, "");
|
||||
this.apiVersion = StringUtils.defaultString(apiVersion, "");
|
||||
this.resource = StringUtils.defaultString(resource, "");
|
||||
this.subresource = StringUtils.defaultString(subresource, "");
|
||||
this.subName = StringUtils.defaultString(subName, "");
|
||||
this.name = StringUtils.defaultString(name, "");
|
||||
this.parts = Objects.requireNonNullElseGet(parts, () -> new String[] {});
|
||||
}
|
||||
|
|
|
@ -52,10 +52,11 @@ public class RequestInfoFactory {
|
|||
* Valid Inputs:
|
||||
* <p>Resource paths</p>
|
||||
* <pre>
|
||||
* /api/{version}/plugins
|
||||
* /api/{version}/plugins/{pluginName}
|
||||
* /api/{version}/plugins/{pluginName}/{resource}
|
||||
* /api/{version}/plugins/{pluginName}/{resource}/{resourceName}
|
||||
* /apis/{api-group}/{version}/namespaces
|
||||
* /api/{version}/namespaces
|
||||
* /api/{version}/namespaces/{namespace}
|
||||
* /api/{version}/namespaces/{namespace}/{resource}
|
||||
* /api/{version}/namespaces/{namespace}/{resource}/{resourceName}
|
||||
* /api/{version}/{resource}
|
||||
* /api/{version}/{resource}/{resourceName}
|
||||
* </pre>
|
||||
|
@ -138,22 +139,22 @@ public class RequestInfoFactory {
|
|||
default -> "";
|
||||
};
|
||||
}
|
||||
// URL forms: /plugins/{plugin-name}/{kind}/*, where parts are adjusted to be relative
|
||||
// URL forms: /namespaces/{namespace}/{kind}/*, where parts are adjusted to be relative
|
||||
// to kind
|
||||
if (Objects.equals(currentParts[0], "plugins")
|
||||
&& StringUtils.isEmpty(requestInfo.getApiGroup())) {
|
||||
Set<String> namespaceSubresources = Set.of("status", "finalize");
|
||||
if (Objects.equals(currentParts[0], "namespaces")) {
|
||||
if (currentParts.length > 1) {
|
||||
requestInfo.pluginName = currentParts[1];
|
||||
requestInfo.namespace = currentParts[1];
|
||||
|
||||
// if there is another step after the plugin name and it is not a known
|
||||
// plugins subresource
|
||||
// if there is another step after the namespace name and it is not a known
|
||||
// namespace subresource
|
||||
// move currentParts to include it as a resource in its own right
|
||||
if (currentParts.length > 2) {
|
||||
if (currentParts.length > 2 && !namespaceSubresources.contains(currentParts[2])) {
|
||||
currentParts = Arrays.copyOfRange(currentParts, 2, currentParts.length);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
requestInfo.pluginName = "";
|
||||
requestInfo.namespace = "";
|
||||
}
|
||||
|
||||
// parsing successful, so we now know the proper value for .Parts
|
||||
|
@ -163,6 +164,10 @@ public class RequestInfoFactory {
|
|||
if (requestInfo.parts.length >= 3 && !specialVerbs.contains(
|
||||
requestInfo.verb)) {
|
||||
requestInfo.subresource = requestInfo.parts[2];
|
||||
// if there is another step after the subresource name and it is not a known
|
||||
if (requestInfo.parts.length >= 4) {
|
||||
requestInfo.subName = requestInfo.parts[3];
|
||||
}
|
||||
}
|
||||
|
||||
if (requestInfo.parts.length >= 2) {
|
||||
|
@ -177,7 +182,7 @@ public class RequestInfoFactory {
|
|||
// verb is a list or a watch
|
||||
if (requestInfo.name.length() == 0 && "get".equals(requestInfo.verb)) {
|
||||
var watch = request.getQueryParams().getFirst("watch");
|
||||
if (isWatch(watch)) {
|
||||
if (Boolean.parseBoolean(watch)) {
|
||||
requestInfo.verb = "watch";
|
||||
} else {
|
||||
requestInfo.verb = "list";
|
||||
|
@ -185,17 +190,15 @@ public class RequestInfoFactory {
|
|||
}
|
||||
// if there's no name on the request and we thought it was a deleted before, then the
|
||||
// actual verb is deletecollection
|
||||
if (requestInfo.name.length() == 0
|
||||
&& Objects.equals(requestInfo.verb, "delete")) {
|
||||
requestInfo.verb = "deletecollection";
|
||||
if (Objects.equals(requestInfo.verb, "delete")) {
|
||||
var deleteAll = request.getQueryParams().getFirst("all");
|
||||
if (Boolean.parseBoolean(deleteAll)) {
|
||||
requestInfo.verb = "deletecollection";
|
||||
}
|
||||
}
|
||||
return requestInfo;
|
||||
}
|
||||
|
||||
boolean isWatch(String requestParam) {
|
||||
return "1".equals(requestParam) || "true".equals(requestParam);
|
||||
}
|
||||
|
||||
private String[] splitPath(String path) {
|
||||
path = StringUtils.strip(path, "/");
|
||||
if (StringUtils.isEmpty(path)) {
|
||||
|
|
|
@ -69,7 +69,8 @@ class PluginRequestMappingHandlerMappingTest {
|
|||
assertThat(info).isNotNull();
|
||||
assertThat(info.getPatternsCondition().getPatterns()).isEqualTo(
|
||||
Collections.singleton(
|
||||
new PathPatternParser().parse("/api/v1alpha1/plugins/fakePlugin/user/{id}")));
|
||||
new PathPatternParser().parse(
|
||||
"/apis/plugin.api.halo.run/v1alpha1/plugins/fakePlugin/user/{id}")));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -94,7 +95,7 @@ class PluginRequestMappingHandlerMappingTest {
|
|||
List<RequestMappingInfo> mappings = handlerMapping.getMappings("fakePlugin");
|
||||
assertThat(mappings).hasSize(1);
|
||||
assertThat(mappings.get(0).toString()).isEqualTo(
|
||||
"{GET /api/v1alpha1/plugins/fakePlugin/user/{id}}");
|
||||
"{GET /apis/plugin.api.halo.run/v1alpha1/plugins/fakePlugin/user/{id}}");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -120,7 +121,8 @@ class PluginRequestMappingHandlerMappingTest {
|
|||
|
||||
// get handler by mock exchange
|
||||
ServerWebExchange exchange =
|
||||
MockServerWebExchange.from(get("/api/v1alpha1/plugins/fakePlugin/foo"));
|
||||
MockServerWebExchange.from(
|
||||
get("/apis/plugin.api.halo.run/v1alpha1/plugins/fakePlugin/foo"));
|
||||
HandlerMethod hm = (HandlerMethod) this.handlerMapping.getHandler(exchange).block();
|
||||
|
||||
assertThat(hm).isNotNull();
|
||||
|
@ -135,7 +137,7 @@ class PluginRequestMappingHandlerMappingTest {
|
|||
Method expected =
|
||||
ResolvableMethod.on(TestController.class).annot(getMapping("/foo").params("p")).build();
|
||||
|
||||
String requestPath = "/api/v1alpha1/plugins/fakePlugin/foo?p=anything";
|
||||
String requestPath = "/apis/plugin.api.halo.run/v1alpha1/plugins/fakePlugin/foo?p=anything";
|
||||
ServerWebExchange exchange = MockServerWebExchange.from(get(requestPath));
|
||||
HandlerMethod hm = (HandlerMethod) this.handlerMapping.getHandler(exchange).block();
|
||||
|
||||
|
@ -150,7 +152,7 @@ class PluginRequestMappingHandlerMappingTest {
|
|||
Method expected =
|
||||
ResolvableMethod.on(TestController.class).annot(getMapping("")).build();
|
||||
|
||||
String requestPath = "/api/v1alpha1/plugins/fakePlugin";
|
||||
String requestPath = "/apis/plugin.api.halo.run/v1alpha1/plugins/fakePlugin";
|
||||
ServerWebExchange exchange = MockServerWebExchange.from(get(requestPath));
|
||||
HandlerMethod hm = (HandlerMethod) this.handlerMapping.getHandler(exchange).block();
|
||||
|
||||
|
@ -163,7 +165,7 @@ class PluginRequestMappingHandlerMappingTest {
|
|||
// register handler methods first
|
||||
handlerMapping.registerHandlerMethods("fakePlugin", new TestController());
|
||||
|
||||
String requestPath = "/api/v1alpha1/plugins/fakePlugin/bar";
|
||||
String requestPath = "/apis/plugin.api.halo.run/v1alpha1/plugins/fakePlugin/bar";
|
||||
ServerWebExchange exchange = MockServerWebExchange.from(post(requestPath));
|
||||
Mono<Object> mono = this.handlerMapping.getHandler(exchange);
|
||||
|
||||
|
|
|
@ -28,11 +28,10 @@ class PolicyRuleTest {
|
|||
|
||||
@Test
|
||||
public void constructPolicyRule() throws JsonProcessingException, JSONException {
|
||||
Role.PolicyRule policyRule = new Role.PolicyRule(null, null, null, null, null, null);
|
||||
Role.PolicyRule policyRule = new Role.PolicyRule(null, null, null, null, null);
|
||||
assertThat(policyRule).isNotNull();
|
||||
JSONAssert.assertEquals("""
|
||||
{
|
||||
"pluginName": "",
|
||||
"apiGroups": [],
|
||||
"resources": [],
|
||||
"resourceNames": [],
|
||||
|
@ -46,7 +45,6 @@ class PolicyRuleTest {
|
|||
Role.PolicyRule policyByBuilder = new Role.PolicyRule.Builder().build();
|
||||
JSONAssert.assertEquals("""
|
||||
{
|
||||
"pluginName": "",
|
||||
"apiGroups": [],
|
||||
"resources": [],
|
||||
"resourceNames": [],
|
||||
|
@ -58,7 +56,6 @@ class PolicyRuleTest {
|
|||
true);
|
||||
|
||||
Role.PolicyRule policyNonNull = new Role.PolicyRule.Builder()
|
||||
.pluginName("fakePluginName")
|
||||
.apiGroups("group")
|
||||
.resources("resource-1", "resource-2")
|
||||
.resourceNames("resourceName")
|
||||
|
@ -68,7 +65,6 @@ class PolicyRuleTest {
|
|||
|
||||
JsonNode expected = objectMapper.readTree("""
|
||||
{
|
||||
"pluginName": "fakePluginName",
|
||||
"apiGroups": [
|
||||
"group"
|
||||
],
|
||||
|
|
|
@ -87,23 +87,28 @@ public class RequestInfoResolverTest {
|
|||
|
||||
@Test
|
||||
void pluginsScopedAndPluginManage() {
|
||||
List<SuccessCase> testCases =
|
||||
List.of(new SuccessCase("DELETE", "/apis/extensions/v1/plugins/other/posts",
|
||||
"delete", "apis", "extensions", "v1", "", "plugins", "posts", "other",
|
||||
List<CustomSuccessCase> testCases =
|
||||
List.of(
|
||||
new CustomSuccessCase("DELETE", "/apis/plugin.api.halo.run/v1/plugins/other/posts",
|
||||
"delete", "apis", "plugin.api.halo.run", "v1", "", "plugins", "posts", "", "",
|
||||
new String[] {"plugins", "other", "posts"}),
|
||||
|
||||
// api group identification
|
||||
new SuccessCase("POST", "/apis/extensions/v1/plugins/other/posts", "create", "apis",
|
||||
"extensions", "v1", "", "plugins", "posts", "other",
|
||||
new String[] {"plugins", "other", "posts"}),
|
||||
new CustomSuccessCase("POST",
|
||||
"/apis/plugin.api.halo.run/v1/plugins/other/posts/foo",
|
||||
"create", "apis",
|
||||
"plugin.api.halo.run", "v1", "", "plugins", "posts", "other", "foo",
|
||||
new String[] {"plugins", "other", "posts", "foo"}),
|
||||
|
||||
// api version identification
|
||||
new SuccessCase("POST", "/apis/extensions/v1beta3/plugins/other/posts", "create",
|
||||
"apis", "extensions", "v1beta3", "", "plugins", "posts", "other",
|
||||
new String[] {"plugins", "other", "posts"}));
|
||||
new CustomSuccessCase("POST",
|
||||
"/apis/plugin.api.halo.run/v1beta3/plugins/other/posts/bar", "create",
|
||||
"apis", "plugin.api.halo.run", "v1beta3", "", "plugins", "posts", "other",
|
||||
"bar",
|
||||
new String[] {"plugins", "other", "posts", "bar"}));
|
||||
|
||||
// 以 /apis 开头的 plugins 资源为 core 中管理插件使用的资源
|
||||
for (SuccessCase successCase : testCases) {
|
||||
for (CustomSuccessCase successCase : testCases) {
|
||||
var request =
|
||||
method(HttpMethod.valueOf(successCase.method),
|
||||
successCase.url).build();
|
||||
|
@ -112,21 +117,19 @@ public class RequestInfoResolverTest {
|
|||
assertRequestInfoCase(successCase, requestInfo);
|
||||
}
|
||||
|
||||
List<SuccessCase> pluginScopedCases =
|
||||
List.of(new SuccessCase("DELETE", "/api/v1/plugins/other/posts",
|
||||
"deletecollection", "api", "", "v1", "other", "posts", "", "",
|
||||
new String[] {"posts"}),
|
||||
List<CustomSuccessCase> pluginScopedCases =
|
||||
List.of(
|
||||
new CustomSuccessCase("DELETE", "/apis/plugin.api.halo.run/v1/plugins/other/posts",
|
||||
"delete", "apis", "plugin.api.halo.run", "v1", "", "plugins", "posts",
|
||||
"other", "", new String[] {"plugins", "other", "posts"}),
|
||||
|
||||
// api group identification
|
||||
new SuccessCase("POST", "/api/v1/plugins/other/posts", "create", "api",
|
||||
"", "v1", "other", "posts", "", "", new String[] {"posts"}),
|
||||
new CustomSuccessCase("POST",
|
||||
"/apis/plugin.api.halo.run/v1/plugins/other/posts/some-name", "create", "apis",
|
||||
"plugin.api.halo.run", "v1", "other", "plugins", "posts", "other", "some-name",
|
||||
new String[] {"plugins", "other", "posts", "some-name"}));
|
||||
|
||||
// api version identification
|
||||
new SuccessCase("POST", "/api/v1beta3/plugins/other/posts", "create",
|
||||
"api", "", "v1beta3", "other", "posts", "", "",
|
||||
new String[] {"posts"}));
|
||||
|
||||
for (SuccessCase pluginScopedCase : pluginScopedCases) {
|
||||
for (CustomSuccessCase pluginScopedCase : pluginScopedCases) {
|
||||
var request =
|
||||
method(HttpMethod.valueOf(pluginScopedCase.method),
|
||||
pluginScopedCase.url).build();
|
||||
|
@ -136,14 +139,16 @@ public class RequestInfoResolverTest {
|
|||
}
|
||||
}
|
||||
|
||||
private void assertRequestInfoCase(SuccessCase pluginScopedCase, RequestInfo requestInfo) {
|
||||
assertThat(requestInfo.getPluginName()).isEqualTo(pluginScopedCase.expectedPluginName);
|
||||
private void assertRequestInfoCase(CustomSuccessCase pluginScopedCase,
|
||||
RequestInfo requestInfo) {
|
||||
assertThat(requestInfo.getVerb()).isEqualTo(pluginScopedCase.expectedVerb);
|
||||
assertThat(requestInfo.getParts()).isEqualTo(pluginScopedCase.expectedParts);
|
||||
assertThat(requestInfo.getApiGroup()).isEqualTo(pluginScopedCase.expectedAPIGroup);
|
||||
assertThat(requestInfo.getResource()).isEqualTo(pluginScopedCase.expectedResource);
|
||||
assertThat(requestInfo.getSubresource())
|
||||
.isEqualTo(pluginScopedCase.expectedSubresource());
|
||||
assertThat(requestInfo.getSubName())
|
||||
.isEqualTo(pluginScopedCase.expectedSubName());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -174,11 +179,11 @@ public class RequestInfoResolverTest {
|
|||
List<PolicyRule> rules = List.of(
|
||||
new PolicyRule.Builder().apiGroups("").resources("posts").verbs("list", "get")
|
||||
.build(),
|
||||
new PolicyRule.Builder().pluginName("fakePlugin").apiGroups("").resources("posts")
|
||||
.verbs("list", "get").build(),
|
||||
new PolicyRule.Builder().pluginName("fakePlugin").apiGroups("")
|
||||
.resources("posts/tags").verbs("list", "get").build(),
|
||||
new PolicyRule.Builder().apiGroups("").resources("categories").verbs("*").build(),
|
||||
new PolicyRule.Builder().apiGroups("plugin.api.halo.run").resources("plugins/users")
|
||||
.resourceNames("foo/bar").verbs("*").build(),
|
||||
new PolicyRule.Builder().apiGroups("plugin.api.halo.run").resources("plugins/users")
|
||||
.resourceNames("foo").verbs("*").build(),
|
||||
new PolicyRule.Builder().nonResourceURLs("/healthy").verbs("get", "post", "head")
|
||||
.build());
|
||||
role.setRules(rules);
|
||||
|
@ -230,14 +235,17 @@ public class RequestInfoResolverTest {
|
|||
new RequestResolveCase("/api/v1/posts", "DELETE", false),
|
||||
new RequestResolveCase("/api/v1/posts/aName", "UPDATE", false),
|
||||
|
||||
// plugin resource
|
||||
new RequestResolveCase("/api/v1/plugins/fakePlugin/posts", "GET", true),
|
||||
new RequestResolveCase("/api/v1/plugins/fakePlugin/posts/some-name", "GET", true),
|
||||
new RequestResolveCase("/api/v1/plugins/fakePlugin/posts/some-name/tags", "GET", true),
|
||||
|
||||
// group resource url
|
||||
new RequestResolveCase("/apis/group/v1/posts", "GET", false),
|
||||
|
||||
// plugin custom resource url
|
||||
new RequestResolveCase("/apis/plugin.api.halo.run/v1alpha1/plugins/foo/users", "GET",
|
||||
true),
|
||||
new RequestResolveCase("/apis/plugin.api.halo.run/v1alpha1/plugins/foo/users/bar",
|
||||
"GET", true),
|
||||
new RequestResolveCase("/apis/plugin.api.halo.run/v1alpha1/plugins/foo/posts/bar",
|
||||
"GET", false),
|
||||
|
||||
// non resource url
|
||||
new RequestResolveCase("/healthy", "GET", true),
|
||||
new RequestResolveCase("/healthy", "POST", true),
|
||||
|
@ -255,40 +263,47 @@ public class RequestInfoResolverTest {
|
|||
|
||||
public record SuccessCase(String method, String url, String expectedVerb,
|
||||
String expectedAPIPrefix, String expectedAPIGroup,
|
||||
String expectedAPIVersion, String expectedPluginName,
|
||||
String expectedAPIVersion, String expectedNamespace,
|
||||
String expectedResource, String expectedSubresource,
|
||||
String expectedName, String[] expectedParts) {
|
||||
}
|
||||
|
||||
public record CustomSuccessCase(String method, String url, String expectedVerb,
|
||||
String expectedAPIPrefix, String expectedAPIGroup,
|
||||
String expectedAPIVersion, String expectedNamespace,
|
||||
String expectedResource, String expectedSubresource,
|
||||
String expectedName, String expectedSubName,
|
||||
String[] expectedParts) {
|
||||
}
|
||||
|
||||
List<SuccessCase> getTestRequestInfos() {
|
||||
String namespaceAll = "*";
|
||||
return List.of(
|
||||
new SuccessCase("GET", "/api/v1/plugins", "list", "api", "", "v1", "", "plugins",
|
||||
"", "", new String[] {"plugins"}),
|
||||
new SuccessCase("GET", "/api/v1/plugins/other", "get", "api", "", "v1", "other",
|
||||
"plugins", "", "other", new String[] {"plugins", "other"}),
|
||||
new SuccessCase("GET", "/api/v1/namespaces", "list", "api", "", "v1", "", "namespaces",
|
||||
"", "", new String[] {"namespaces"}),
|
||||
new SuccessCase("GET", "/api/v1/namespaces/other", "get", "api", "", "v1", "other",
|
||||
"namespaces", "", "other", new String[] {"namespaces", "other"}),
|
||||
|
||||
new SuccessCase("GET", "/api/v1/plugins/other/posts", "list", "api", "", "v1",
|
||||
new SuccessCase("GET", "/api/v1/namespaces/other/posts", "list", "api", "", "v1",
|
||||
"other", "posts", "", "", new String[] {"posts"}),
|
||||
new SuccessCase("GET", "/api/v1/plugins/other/posts/foo", "get", "api", "", "v1",
|
||||
new SuccessCase("GET", "/api/v1/namespaces/other/posts/foo", "get", "api", "", "v1",
|
||||
"other", "posts", "", "foo", new String[] {"posts", "foo"}),
|
||||
new SuccessCase("HEAD", "/api/v1/plugins/other/posts/foo", "get", "api", "", "v1",
|
||||
new SuccessCase("HEAD", "/api/v1/namespaces/other/posts/foo", "get", "api", "", "v1",
|
||||
"other", "posts", "", "foo", new String[] {"posts", "foo"}),
|
||||
new SuccessCase("GET", "/api/v1/posts", "list", "api", "", "v1", namespaceAll, "posts",
|
||||
"", "", new String[] {"posts"}),
|
||||
new SuccessCase("HEAD", "/api/v1/posts", "list", "api", "", "v1", namespaceAll, "posts",
|
||||
"", "", new String[] {"posts"}),
|
||||
new SuccessCase("GET", "/api/v1/plugins/other/posts/foo", "get", "api", "", "v1",
|
||||
new SuccessCase("GET", "/api/v1/namespaces/other/posts/foo", "get", "api", "", "v1",
|
||||
"other", "posts", "", "foo", new String[] {"posts", "foo"}),
|
||||
new SuccessCase("GET", "/api/v1/plugins/other/posts", "list", "api", "", "v1",
|
||||
new SuccessCase("GET", "/api/v1/namespaces/other/posts", "list", "api", "", "v1",
|
||||
"other", "posts", "", "", new String[] {"posts"}),
|
||||
|
||||
// special verbs
|
||||
new SuccessCase("GET", "/api/v1/proxy/plugins/other/posts/foo", "proxy", "api", "",
|
||||
new SuccessCase("GET", "/api/v1/proxy/namespaces/other/posts/foo", "proxy", "api", "",
|
||||
"v1", "other", "posts", "", "foo", new String[] {"posts", "foo"}),
|
||||
new SuccessCase("GET",
|
||||
"/api/v1/proxy/plugins/other/posts/foo/subpath/not/a/subresource", "proxy",
|
||||
"/api/v1/proxy/namespaces/other/posts/foo/subpath/not/a/subresource", "proxy",
|
||||
"api", "", "v1", "other", "posts", "", "foo",
|
||||
new String[] {"posts", "foo", "subpath", "not", "a", "subresource"}),
|
||||
new SuccessCase("GET", "/api/v1/watch/posts", "watch", "api", "", "v1", namespaceAll,
|
||||
|
@ -297,35 +312,55 @@ public class RequestInfoResolverTest {
|
|||
namespaceAll, "posts", "", "", new String[] {"posts"}),
|
||||
new SuccessCase("GET", "/api/v1/posts?watch=false", "list", "api", "", "v1",
|
||||
namespaceAll, "posts", "", "", new String[] {"posts"}),
|
||||
new SuccessCase("GET", "/api/v1/watch/plugins/other/posts", "watch", "api", "", "v1",
|
||||
new SuccessCase("GET", "/api/v1/watch/namespaces/other/posts", "watch", "api", "", "v1",
|
||||
"other", "posts", "", "", new String[] {"posts"}),
|
||||
new SuccessCase("GET", "/api/v1/plugins/other/posts?watch=1", "watch", "api", "",
|
||||
new SuccessCase("GET", "/api/v1/namespaces/other/posts?watch=true", "watch", "api", "",
|
||||
"v1", "other", "posts", "", "", new String[] {"posts"}),
|
||||
new SuccessCase("GET", "/api/v1/plugins/other/posts?watch=0", "list", "api", "",
|
||||
new SuccessCase("GET", "/api/v1/namespaces/other/posts?watch=false", "list", "api", "",
|
||||
"v1", "other", "posts", "", "", new String[] {"posts"}),
|
||||
|
||||
// subresource identification
|
||||
new SuccessCase("GET", "/api/v1/plugins/other/posts/foo/status", "get", "api", "",
|
||||
new SuccessCase("GET", "/api/v1/namespaces/other/posts/foo/status", "get", "api", "",
|
||||
"v1", "other", "posts", "status", "foo", new String[] {"posts", "foo", "status"}),
|
||||
new SuccessCase("GET", "/api/v1/plugins/other/posts/foo/proxy/subpath", "get", "api",
|
||||
new SuccessCase("GET", "/api/v1/namespaces/other/posts/foo/proxy/subpath", "get", "api",
|
||||
"", "v1", "other", "posts", "proxy", "foo",
|
||||
new String[] {"posts", "foo", "proxy", "subpath"}),
|
||||
new SuccessCase("PUT", "/api/v1/namespaces/other/finalize", "update", "api", "", "v1",
|
||||
"other", "namespaces", "finalize", "other",
|
||||
new String[] {"namespaces", "other", "finalize"}),
|
||||
new SuccessCase("PUT", "/api/v1/namespaces/other/status", "update", "api", "", "v1",
|
||||
"other", "namespaces", "status", "other",
|
||||
new String[] {"namespaces", "other", "status"}),
|
||||
|
||||
// verb identification
|
||||
new SuccessCase("PATCH", "/api/v1/plugins/other/posts/foo", "patch", "api", "", "v1",
|
||||
new SuccessCase("PATCH", "/api/v1/namespaces/other/posts/foo", "patch", "api", "", "v1",
|
||||
"other", "posts", "", "foo", new String[] {"posts", "foo"}),
|
||||
new SuccessCase("DELETE", "/api/v1/plugins/other/posts/foo", "delete", "api", "",
|
||||
new SuccessCase("DELETE", "/api/v1/namespaces/other/posts/foo", "delete", "api", "",
|
||||
"v1", "other", "posts", "", "foo", new String[] {"posts", "foo"}),
|
||||
new SuccessCase("POST", "/api/v1/plugins/other/posts", "create", "api", "", "v1",
|
||||
new SuccessCase("POST", "/api/v1/namespaces/other/posts", "create", "api", "", "v1",
|
||||
"other", "posts", "", "", new String[] {"posts"}),
|
||||
|
||||
// deletecollection verb identification
|
||||
new SuccessCase("DELETE", "/api/v1/nodes", "deletecollection", "api", "", "v1", "",
|
||||
new SuccessCase("DELETE", "/api/v1/nodes?all=true", "deletecollection", "api", "", "v1",
|
||||
"",
|
||||
"nodes", "", "", new String[] {"nodes"}),
|
||||
new SuccessCase("DELETE", "/api/v1/plugins", "deletecollection", "api", "", "v1", "",
|
||||
"plugins", "", "", new String[] {"plugins"}),
|
||||
new SuccessCase("DELETE", "/api/v1/plugins/other/posts", "deletecollection", "api",
|
||||
"", "v1", "other", "posts", "", "", new String[] {"posts"}));
|
||||
new SuccessCase("DELETE", "/api/v1/namespaces?all=false", "delete", "api", "",
|
||||
"v1", "",
|
||||
"namespaces", "", "", new String[] {"namespaces"}),
|
||||
new SuccessCase("DELETE", "/api/v1/namespaces/other/posts?all=true", "deletecollection",
|
||||
"api",
|
||||
"", "v1", "other", "posts", "", "", new String[] {"posts"}),
|
||||
new SuccessCase("DELETE", "/apis/extensions/v1/namespaces/other/posts?all=true",
|
||||
"deletecollection", "apis", "extensions", "v1", "other", "posts", "", "",
|
||||
new String[] {"posts"}),
|
||||
|
||||
// api group identification
|
||||
new SuccessCase("POST", "/apis/extensions/v1/namespaces/other/posts", "create", "apis",
|
||||
"extensions", "v1", "other", "posts", "", "", new String[] {"posts"}),
|
||||
|
||||
// api version identification
|
||||
new SuccessCase("POST", "/apis/extensions/v1beta3/namespaces/other/posts", "create",
|
||||
"apis", "extensions", "v1beta3", "other", "posts", "", "", new String[] {"posts"}));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue