Do some refactor for IdentityContextBuilder

This commit is contained in:
KomachiSion 2022-01-17 16:17:23 +08:00
parent 956df153d6
commit 7b7db3496b
12 changed files with 318 additions and 87 deletions

View File

@ -38,40 +38,40 @@ public class AuthPluginManager {
private static final AuthPluginManager INSTANCE = new AuthPluginManager();
/**
* The relationship of context type and {@link AuthService}.
* The relationship of context type and {@link AuthPluginService}.
*/
private Map<String, AuthService> authServiceMap = new HashMap<>();
private final Map<String, AuthPluginService> authServiceMap = new HashMap<>();
public AuthPluginManager() {
initAuthServices();
}
private void initAuthServices() {
Collection<AuthPluginService> authPluginServices = NacosServiceLoader.load(AuthPluginService.class);
for (AuthPluginService each : authPluginServices) {
if (StringUtils.isEmpty(each.getAuthServiceName())) {
LOGGER.warn(
"[AuthPluginManager] Load AuthPluginService({}) AuthServiceName(null/empty) fail. Please Add AuthServiceName to resolve.",
each.getClass());
continue;
}
authServiceMap.put(each.getAuthServiceName(), each);
LOGGER.info("[AuthPluginManager] Load AuthPluginService({}) AuthServiceName({}) successfully.",
each.getClass(), each.getAuthServiceName());
}
}
public static AuthPluginManager getInstance() {
return INSTANCE;
}
private void initAuthServices() {
Collection<AuthService> authServices = NacosServiceLoader.load(AuthService.class);
for (AuthService authService : authServices) {
if (StringUtils.isEmpty(authService.getAuthServiceName())) {
LOGGER.warn(
"[AuthPluginManager] Load AuthService({}) AuthServiceName(null/empty) fail. Please Add AuthServiceName to resolve.",
authService.getClass());
continue;
}
authServiceMap.put(authService.getAuthServiceName(), authService);
LOGGER.info("[AuthPluginManager] Load AuthService({}) AuthServiceName({}) successfully.",
authService.getClass(), authService.getAuthServiceName());
}
}
/**
* get AuthService instance which AuthService.getType() is type.
* get AuthPluginService instance which AuthPluginService.getType() is type.
*
* @param authServiceName AuthServiceName, mark a AuthService instance.
* @return AuthService instance.
* @param authServiceName AuthServiceName, mark a AuthPluginService instance.
* @return AuthPluginService instance.
*/
public Optional<AuthService> findAuthServiceSpiImpl(String authServiceName) {
public Optional<AuthPluginService> findAuthServiceSpiImpl(String authServiceName) {
return Optional.ofNullable(authServiceMap.get(authServiceName));
}

View File

@ -17,36 +17,47 @@
package com.alibaba.nacos.auth;
import com.alibaba.nacos.auth.api.IdentityContext;
import com.alibaba.nacos.auth.exception.AccessException;
import com.alibaba.nacos.auth.api.Permission;
import com.alibaba.nacos.auth.exception.AccessException;
import java.util.Collection;
/**
* Auth service.
*
* @author Wuyfee
*/
public interface AuthService {
public interface AuthPluginService {
/**
* Authentication of request, identify the user who request the resource.
* Define which identity information needed from request. e.q: username, password, accessToken.
*
* @return identity names
*/
Collection<String> identityNames();
/**
* To validate whether the identity context from request is legal or illegal.
*
* @param identityContext where we can find the user information
* @return IdentityContext user auth result
* @throws AccessException if authentication is failed
*/
IdentityContext login(IdentityContext identityContext) throws AccessException;
IdentityContext validateIdentity(IdentityContext identityContext) throws AccessException;
/**
* identity whether the user has the resource authority.
* Validate the identity whether has the resource authority.
*
* @param identityContext where we can find the user information.
* @param permission permission to auth.
* @param permission permission to auth.
* @return Boolean if the user has the resource authority.
*/
Boolean authorityAccess(IdentityContext identityContext, Permission permission);
Boolean validateAuthority(IdentityContext identityContext, Permission permission);
/**
* AuthService Name which for conveniently find AuthService instance.
* @return AuthServiceName mark a AuthService instance.
* AuthPluginService Name which for conveniently find AuthPluginService instance.
*
* @return AuthServiceName mark a AuthPluginService instance.
*/
String getAuthServiceName();

View File

@ -33,17 +33,41 @@ public class IdentityContext {
/**
* get key from context.
*
* @param key key of request
* @return value of param key
*/
public Object getParameter(String key) {
return param.get(key); }
return param.get(key);
}
/**
* Get identity by key.
*
* @param key identity name
* @param defaultValue default value when the value is {@code null} or the value is not expected class type
* @param <T> classes type of identity value
* @return identity value
*/
public <T> T getParameter(String key, T defaultValue) {
try {
T result = (T) param.get(key);
if (null != result) {
return result;
}
return defaultValue;
} catch (ClassCastException exception) {
return defaultValue;
}
}
/**
* put key and value to param.
* @param key key of request
*
* @param key key of request
* @param value value of request's key
*/
public void setParameter(String key, Object value) {
param.put(key, value); }
param.put(key, value);
}
}

View File

@ -49,12 +49,6 @@ public class AuthConfigs extends Subscriber<ServerConfigChangeEvent> {
@JustForTest
private static Boolean cachingEnabled = null;
/**
* Authority key set.
*/
@Value("${" + Constants.Auth.NACOS_CORE_AUTH_AUTHORITY_KEY + ":}")
private String[] authorityKey;
/**
* Whether auth enabled.
*/
@ -109,10 +103,6 @@ public class AuthConfigs extends Subscriber<ServerConfigChangeEvent> {
return secretKeyBytes;
}
public String[] getAuthorityKey() {
return authorityKey;
}
public long getTokenValidityInSeconds() {
return tokenValidityInSeconds;
}
@ -171,7 +161,6 @@ public class AuthConfigs extends Subscriber<ServerConfigChangeEvent> {
enableUserAgentAuthWhite = EnvUtil.getProperty(
Constants.Auth.NACOS_CORE_AUTH_ENABLE_USER_AGENT_AUTH_WHITE, Boolean.class,
false);
authorityKey = EnvUtil.getProperty(Constants.Auth.NACOS_CORE_AUTH_AUTHORITY_KEY, "").split(",");
nacosAuthSystemType = EnvUtil.getProperty(Constants.Auth.NACOS_CORE_AUTH_SYSTEM_TYPE, "");
} catch (Exception e) {
LOGGER.warn("Upgrade auth config from env failed, use old value", e);

View File

@ -40,8 +40,6 @@ public class Constants {
public static final String NACOS_CORE_AUTH_SERVER_IDENTITY_VALUE = "nacos.core.auth.server.identity.value";
public static final String NACOS_CORE_AUTH_ENABLE_USER_AGENT_AUTH_WHITE = "nacos.core.auth.enable.userAgentAuthWhite";
public static final String NACOS_CORE_AUTH_AUTHORITY_KEY = "nacos.core.auth.authorityKey";
}
public static class Resource {

View File

@ -17,12 +17,14 @@
package com.alibaba.nacos.auth.context;
import com.alibaba.nacos.api.remote.request.Request;
import com.alibaba.nacos.auth.AuthPluginManager;
import com.alibaba.nacos.auth.AuthPluginService;
import com.alibaba.nacos.auth.api.IdentityContext;
import com.alibaba.nacos.auth.common.AuthConfigs;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
/**
@ -32,31 +34,34 @@ import java.util.Set;
*/
public class GrpcIdentityContextBuilder implements IdentityContextBuilder<Request> {
AuthConfigs authConfigs;
public GrpcIdentityContextBuilder() {
authConfigs = new AuthConfigs();
}
private final AuthConfigs authConfigs;
public GrpcIdentityContextBuilder(AuthConfigs authConfigs) {
this.authConfigs = authConfigs;
}
/**
* get identity context from grpc.
*
* @param request grpc request
* @return IdentityContext request context
*/
@Override
public IdentityContext build(Request request) {
Set<String> keySet = new HashSet<String>(Arrays.asList(authConfigs.getAuthorityKey()));
IdentityContext identityContext = new IdentityContext();
Optional<AuthPluginService> authPluginService = AuthPluginManager.getInstance()
.findAuthServiceSpiImpl(authConfigs.getNacosAuthSystemType());
IdentityContext result = new IdentityContext();
if (!authPluginService.isPresent()) {
return result;
}
Set<String> identityNames = new HashSet<>(authPluginService.get().identityNames());
Map<String, String> map = request.getHeaders();
for (Map.Entry<String, String> entry : map.entrySet()) {
if (keySet.contains(entry.getKey())) {
identityContext.setParameter(entry.getKey(), entry.getValue());
if (identityNames.contains(entry.getKey())) {
result.setParameter(entry.getKey(), entry.getValue());
}
}
return identityContext;
return result;
}
}

View File

@ -16,14 +16,16 @@
package com.alibaba.nacos.auth.context;
import com.alibaba.nacos.auth.AuthPluginManager;
import com.alibaba.nacos.auth.AuthPluginService;
import com.alibaba.nacos.auth.api.IdentityContext;
import com.alibaba.nacos.auth.common.AuthConfigs;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
/**
@ -33,11 +35,7 @@ import java.util.Set;
*/
public class HttpIdentityContextBuilder implements IdentityContextBuilder<HttpServletRequest> {
private AuthConfigs authConfigs;
public HttpIdentityContextBuilder() {
authConfigs = new AuthConfigs();
}
private final AuthConfigs authConfigs;
public HttpIdentityContextBuilder(AuthConfigs authConfigs) {
this.authConfigs = authConfigs;
@ -51,30 +49,35 @@ public class HttpIdentityContextBuilder implements IdentityContextBuilder<HttpSe
*/
@Override
public IdentityContext build(HttpServletRequest request) {
IdentityContext identityContext = new IdentityContext();
Set<String> keySet = new HashSet<>(Arrays.asList(authConfigs.getAuthorityKey()));
IdentityContext result = new IdentityContext();
Optional<AuthPluginService> authPluginService = AuthPluginManager.getInstance()
.findAuthServiceSpiImpl(authConfigs.getNacosAuthSystemType());
if (!authPluginService.isPresent()) {
return result;
}
Set<String> identityNames = new HashSet<>(authPluginService.get().identityNames());
getIdentityFromHeader(request, result, identityNames);
getIdentityFromParameter(request, result, identityNames);
return result;
}
private void getIdentityFromHeader(HttpServletRequest request, IdentityContext result, Set<String> identityNames) {
Enumeration<String> headerEnu = request.getHeaderNames();
while (headerEnu.hasMoreElements()) {
String paraName = headerEnu.nextElement();
if (keySet.contains(paraName)) {
identityContext.setParameter(paraName, request.getHeader(paraName));
keySet.remove(paraName);
if (identityNames.contains(paraName)) {
result.setParameter(paraName, request.getHeader(paraName));
}
}
if (keySet.isEmpty()) {
return identityContext;
}
}
private void getIdentityFromParameter(HttpServletRequest request, IdentityContext result, Set<String> identityNames) {
Enumeration<String> paramEnu = request.getParameterNames();
while (paramEnu.hasMoreElements()) {
String paraName = paramEnu.nextElement();
if (keySet.contains(paraName)) {
identityContext.setParameter(paraName, request.getParameter(paraName));
keySet.remove(paraName);
if (identityNames.contains(paraName)) {
result.setParameter(paraName, request.getParameter(paraName));
}
}
return identityContext;
}
}

View File

@ -39,8 +39,6 @@ public class AuthConfigsTest {
private static final boolean TEST_ENABLE_UA_WHITE = true;
private static final String AUTHORITYKEY = "username,password,token,tenant";
private AuthConfigs authConfigs;
private MockEnvironment environment;
@ -59,7 +57,6 @@ public class AuthConfigsTest {
environment.setProperty("nacos.core.auth.server.identity.key", TEST_SERVER_IDENTITY_KEY);
environment.setProperty("nacos.core.auth.server.identity.value", TEST_SERVER_IDENTITY_VALUE);
environment.setProperty("nacos.core.auth.enable.userAgentAuthWhite", String.valueOf(TEST_ENABLE_UA_WHITE));
environment.setProperty("nacos.core.auth.authorityKey", AUTHORITYKEY);
authConfigs.onEvent(ServerConfigChangeEvent.newEvent());
assertEquals(TEST_AUTH_ENABLED, authConfigs.isAuthEnabled());
@ -67,6 +64,5 @@ public class AuthConfigsTest {
assertEquals(TEST_SERVER_IDENTITY_KEY, authConfigs.getServerIdentityKey());
assertEquals(TEST_SERVER_IDENTITY_VALUE, authConfigs.getServerIdentityValue());
assertEquals(TEST_ENABLE_UA_WHITE, authConfigs.isEnableUserAgentAuthWhite());
Assert.assertTrue(Arrays.equals(AUTHORITYKEY.split(","), authConfigs.getAuthorityKey()));
}
}

View File

@ -17,7 +17,7 @@
package com.alibaba.nacos.auth.common;
import com.alibaba.nacos.auth.AuthPluginManager;
import com.alibaba.nacos.auth.AuthService;
import com.alibaba.nacos.auth.AuthPluginService;
import com.alibaba.nacos.auth.api.IdentityContext;
import com.alibaba.nacos.auth.api.Permission;
import org.junit.Assert;
@ -44,7 +44,7 @@ public class AuthPluginManagerTest {
private AuthPluginManager authPluginManager;
@Mock
private AuthService authService;
private AuthPluginService authPluginService;
private static final String TYPE = "test";
@ -60,8 +60,8 @@ public class AuthPluginManagerTest {
Class<AuthPluginManager> authPluginManagerClass = AuthPluginManager.class;
Field authPlugins = authPluginManagerClass.getDeclaredField("authServiceMap");
authPlugins.setAccessible(true);
Map<String, AuthService> authServiceMap = (Map<String, AuthService>) authPlugins.get(authPluginManager);
authServiceMap.put(TYPE, authService);
Map<String, AuthPluginService> authServiceMap = (Map<String, AuthPluginService>) authPlugins.get(authPluginManager);
authServiceMap.put(TYPE, authPluginService);
}
@Test
@ -73,7 +73,7 @@ public class AuthPluginManagerTest {
@Test
public void testFindAuthServiceSpiImpl() {
Optional<AuthService> authServiceImpl = authPluginManager.findAuthServiceSpiImpl(TYPE);
Optional<AuthPluginService> authServiceImpl = authPluginManager.findAuthServiceSpiImpl(TYPE);
Assert.assertTrue(authServiceImpl.isPresent());
}

View File

@ -0,0 +1,86 @@
/*
* Copyright 1999-2021 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.nacos.auth.context;
import com.alibaba.nacos.api.remote.request.Request;
import com.alibaba.nacos.auth.AuthPluginManager;
import com.alibaba.nacos.auth.AuthPluginService;
import com.alibaba.nacos.auth.api.IdentityContext;
import com.alibaba.nacos.auth.common.AuthConfigs;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class GrpcIdentityContextBuilderTest {
private static final String TEST_PLUGIN = "test";
private static final String IDENTITY_TEST_KEY = "identity-test-key";
private static final String IDENTITY_TEST_VALUE = "identity-test-value";
@Mock
private AuthConfigs authConfigs;
@Mock
private AuthPluginService authPluginService;
@Mock
private Request request;
private GrpcIdentityContextBuilder identityContextBuilder;
@Before
public void setUp() throws Exception {
identityContextBuilder = new GrpcIdentityContextBuilder(authConfigs);
Field authServiceMapField = AuthPluginManager.class.getDeclaredField("authServiceMap");
authServiceMapField.setAccessible(true);
Map<String, AuthPluginService> authServiceMap = (Map<String, AuthPluginService>) authServiceMapField
.get(AuthPluginManager.getInstance());
authServiceMap.put(TEST_PLUGIN, authPluginService);
when(authConfigs.getNacosAuthSystemType()).thenReturn(TEST_PLUGIN);
when(authPluginService.identityNames()).thenReturn(Collections.singletonList(IDENTITY_TEST_KEY));
Map<String, String> headers = new HashMap<>();
headers.put(IDENTITY_TEST_KEY, IDENTITY_TEST_VALUE);
when(request.getHeaders()).thenReturn(headers);
}
@Test
public void testBuildWithoutPlugin() {
when(authConfigs.getNacosAuthSystemType()).thenReturn("non-exist");
IdentityContext actual = identityContextBuilder.build(request);
assertNull(actual.getParameter(IDENTITY_TEST_KEY));
}
@Test
public void testBuild() {
IdentityContext actual = identityContextBuilder.build(request);
assertEquals(IDENTITY_TEST_VALUE, actual.getParameter(IDENTITY_TEST_KEY));
}
}

View File

@ -0,0 +1,119 @@
/*
* Copyright 1999-2021 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.nacos.auth.context;
import com.alibaba.nacos.auth.AuthPluginManager;
import com.alibaba.nacos.auth.AuthPluginService;
import com.alibaba.nacos.auth.api.IdentityContext;
import com.alibaba.nacos.auth.common.AuthConfigs;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class HtppIdentityContextBuilderTest {
private static final String TEST_PLUGIN = "test";
private static final String IDENTITY_TEST_KEY = "identity-test-key";
private static final String IDENTITY_TEST_VALUE = "identity-test-value";
@Mock
private AuthConfigs authConfigs;
@Mock
private AuthPluginService authPluginService;
@Mock
private HttpServletRequest request;
@Mock
private Enumeration<String> headerNames;
@Mock
private Enumeration<String> parameterNames;
private HttpIdentityContextBuilder identityContextBuilder;
@Before
public void setUp() throws Exception {
identityContextBuilder = new HttpIdentityContextBuilder(authConfigs);
Field authServiceMapField = AuthPluginManager.class.getDeclaredField("authServiceMap");
authServiceMapField.setAccessible(true);
Map<String, AuthPluginService> authServiceMap = (Map<String, AuthPluginService>) authServiceMapField
.get(AuthPluginManager.getInstance());
authServiceMap.put(TEST_PLUGIN, authPluginService);
when(authConfigs.getNacosAuthSystemType()).thenReturn(TEST_PLUGIN);
when(authPluginService.identityNames()).thenReturn(Collections.singletonList(IDENTITY_TEST_KEY));
}
@Test
public void testBuildWithoutPlugin() {
mockHeader(true);
mockParameter(true);
when(authConfigs.getNacosAuthSystemType()).thenReturn("non-exist");
IdentityContext actual = identityContextBuilder.build(request);
assertNull(actual.getParameter(IDENTITY_TEST_KEY));
}
@Test
public void testBuildWithHeader() {
mockHeader(true);
mockParameter(false);
IdentityContext actual = identityContextBuilder.build(request);
assertEquals(IDENTITY_TEST_VALUE, actual.getParameter(IDENTITY_TEST_KEY));
}
@Test
public void testBuildWithParameter() {
mockHeader(false);
mockParameter(true);
IdentityContext actual = identityContextBuilder.build(request);
assertEquals(IDENTITY_TEST_VALUE, actual.getParameter(IDENTITY_TEST_KEY));
}
private void mockHeader(boolean contained) {
when(request.getHeaderNames()).thenReturn(headerNames);
if (contained) {
when(headerNames.hasMoreElements()).thenReturn(true, false);
when(headerNames.nextElement()).thenReturn(IDENTITY_TEST_KEY, (String) null);
when(request.getHeader(IDENTITY_TEST_KEY)).thenReturn(IDENTITY_TEST_VALUE);
}
}
private void mockParameter(boolean contained) {
when(request.getParameterNames()).thenReturn(parameterNames);
if (contained) {
when(parameterNames.hasMoreElements()).thenReturn(true, false);
when(parameterNames.nextElement()).thenReturn(IDENTITY_TEST_KEY, (String) null);
when(request.getParameter(IDENTITY_TEST_KEY)).thenReturn(IDENTITY_TEST_VALUE);
}
}
}