Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ISSUE #11957] AuthModule add admin exist #12066

Merged
merged 9 commits into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions auth/src/main/java/com/alibaba/nacos/auth/AuthService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright 1999-2024 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;

/**
* AuthService.
*
* @author : huangtianhui
*/
public interface AuthService {
godhth marked this conversation as resolved.
Show resolved Hide resolved

/**
* Whether need administrator .
*
* @return if the need exist the administrator role.
*/
boolean isAdminRequest();
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@

package com.alibaba.nacos.auth.config;

import com.alibaba.nacos.auth.AuthService;
import com.alibaba.nacos.plugin.auth.spi.server.AuthPluginManager;
import com.alibaba.nacos.plugin.auth.spi.server.AuthPluginService;
import com.alibaba.nacos.sys.module.ModuleState;
import com.alibaba.nacos.sys.module.ModuleStateBuilder;
import com.alibaba.nacos.sys.utils.ApplicationUtils;

import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;

/**
* Module state builder for auth module.
Expand All @@ -39,16 +41,26 @@ public class AuthModuleStateBuilder implements ModuleStateBuilder {

public static final String AUTH_SYSTEM_TYPE = "auth_system_type";

public static final String AUTH_ADMIN_REQUEST = "auth_admin_request";

@Override
public ModuleState build() {
ModuleState result = new ModuleState(AUTH_MODULE);
AuthConfigs authConfigs = ApplicationUtils.getBean(AuthConfigs.class);
result.newState(AUTH_ENABLED, authConfigs.isAuthEnabled());
result.newState(LOGIN_PAGE_ENABLED, isLoginPageEnabled(authConfigs));
result.newState(AUTH_SYSTEM_TYPE, authConfigs.getNacosAuthSystemType());
AtomicBoolean adminRequest = new AtomicBoolean(true);
ApplicationUtils.getBeanIfExist(AuthService.class, authService -> adminRequest.set(authService.isAdminRequest()));
result.newState(AUTH_ADMIN_REQUEST, adminRequest.get());
return result;
}

@Override
public boolean isCacheable() {
return false;
godhth marked this conversation as resolved.
Show resolved Hide resolved
}

private Boolean isLoginPageEnabled(AuthConfigs authConfigs) {
Optional<AuthPluginService> authPluginService = AuthPluginManager.getInstance()
.findAuthServiceSpiImpl(authConfigs.getNacosAuthSystemType());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.alibaba.nacos.auth.config;

import com.alibaba.nacos.auth.AuthService;
import com.alibaba.nacos.sys.module.ModuleState;
import com.alibaba.nacos.sys.utils.ApplicationUtils;
import org.junit.After;
Expand All @@ -40,11 +41,16 @@ public class AuthModuleStateBuilderTest {
@Mock
private AuthConfigs authConfigs;

@Mock
private AuthService authService;

@Before
public void setUp() throws Exception {
when(context.getBean(AuthConfigs.class)).thenReturn(authConfigs);
when(context.getBean(AuthService.class)).thenReturn(authService);
ApplicationUtils.injectContext(context);
when(authConfigs.getNacosAuthSystemType()).thenReturn("nacos");
when(authService.isAdminRequest()).thenReturn(false);
}

@After
Expand All @@ -57,5 +63,6 @@ public void testBuild() {
assertFalse((Boolean) actual.getStates().get(AUTH_ENABLED));
assertFalse((Boolean) actual.getStates().get("login_page_enabled"));
assertEquals("nacos", actual.getStates().get("auth_system_type"));
assertFalse((Boolean) actual.getStates().get("auth_admin_request"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* 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.plugin.auth.impl;

import com.alibaba.nacos.auth.AuthService;
import com.alibaba.nacos.auth.config.AuthConfigs;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.persistence.model.Page;
import com.alibaba.nacos.plugin.auth.impl.constant.AuthConstants;
import com.alibaba.nacos.plugin.auth.impl.persistence.RoleInfo;
import com.alibaba.nacos.plugin.auth.impl.persistence.RolePersistService;
import org.springframework.stereotype.Service;

import java.util.List;

/**
* NacosAuthService.
*
* @author : huangtianhui
*/
@Service
public class NacosAuthServiceImpl implements AuthService {

private final AuthConfigs authConfigs;

private final RolePersistService rolePersistService;

public NacosAuthServiceImpl(AuthConfigs authConfigs, RolePersistService rolePersistService) {
this.authConfigs = authConfigs;
this.rolePersistService = rolePersistService;
}

/**
* check if all user has at least one admin role.
*
* @return true if all user has at least one admin role.
*/
private boolean hasGlobalAdminRole() {
if (authConfigs.isHasGlobalAdminRole()) {
return true;
}
List<RoleInfo> roles = getAllRoles();
boolean hasGlobalAdminRole = CollectionUtils.isNotEmpty(roles) && roles.stream()
.anyMatch(roleInfo -> AuthConstants.GLOBAL_ADMIN_ROLE.equals(roleInfo.getRole()));
authConfigs.setHasGlobalAdminRole(hasGlobalAdminRole);
return hasGlobalAdminRole;
}

private List<RoleInfo> getAllRoles() {
Page<RoleInfo> roleInfoPage = rolePersistService.getRolesByUserNameAndRoleName(StringUtils.EMPTY,
StringUtils.EMPTY, 1, Integer.MAX_VALUE);
if (roleInfoPage == null) {
return null;
}
return roleInfoPage.getPageItems();
}

@Override
public boolean isAdminRequest() {
return !hasGlobalAdminRole();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,18 +115,16 @@ public Object createUser(@RequestParam String username, @RequestParam String pas
* Create a admin user only not exist admin user can use.
*/
@PostMapping("/admin")
public Object createAdminUser(@RequestParam(required = false) String username,
@RequestParam(required = false) String password) {
public Object createAdminUser(@RequestParam(required = false) String password) {
if (AuthSystemTypes.NACOS.name().equalsIgnoreCase(authConfigs.getNacosAuthSystemType())) {
if (roleService.hasGlobalAdminRole()) {
if (iAuthenticationManager.hasGlobalAdminRole()) {
return RestResultUtils.failed("have admin user cannot use it");
}
if (StringUtils.isBlank(password)) {
password = PasswordGeneratorUtil.generateRandomPassword();
}
if (StringUtils.isBlank(username)) {
username = AuthConstants.DEFAULT_USER;
}

String username = AuthConstants.DEFAULT_USER;
userDetailsService.createUser(username, PasswordEncoderUtil.encode(password));
roleService.addAdminRole(username);
ObjectNode result = JacksonUtils.createEmptyJsonNode();
Expand Down Expand Up @@ -266,10 +264,7 @@ public Object login(@RequestParam String username, @RequestParam String password

if (AuthSystemTypes.NACOS.name().equalsIgnoreCase(authConfigs.getNacosAuthSystemType())
|| AuthSystemTypes.LDAP.name().equalsIgnoreCase(authConfigs.getNacosAuthSystemType())) {
if (!iAuthenticationManager.hasGlobalAdminRole()) {
response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED, "admin role user not exist");
return null;
}

NacosUser user = iAuthenticationManager.authenticate(request);

response.addHeader(AuthConstants.AUTHORIZATION_HEADER, AuthConstants.TOKEN_PREFIX + user.getToken());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ public void setUp() throws Exception {
public void testLoginWithAuthedUser() throws AccessException, IOException {
when(authenticationManager.authenticate(request)).thenReturn(user);
when(authenticationManager.hasGlobalAdminRole(user)).thenReturn(true);
when(authenticationManager.hasGlobalAdminRole()).thenReturn(true);
when(authConfigs.getNacosAuthSystemType()).thenReturn(AuthSystemTypes.NACOS.name());
when(tokenManagerDelegate.getTokenTtlInSeconds(anyString())).thenReturn(18000L);
Object actual = userController.login("nacos", "nacos", response, request);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,14 @@ public interface ModuleStateBuilder {
default boolean isIgnore() {
return false;
}

/**
* Whether module is cache, default return true.
*
* @return boolean
*/
default boolean isCacheable() {
return true;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
Expand All @@ -40,13 +42,16 @@ public class ModuleStateHolder {

private final Map<String, ModuleState> moduleStates;

private final List<ModuleStateBuilder> moduleStateBuilders = new ArrayList<>();

private ModuleStateHolder() {
this.moduleStates = new HashMap<>();
for (ModuleStateBuilder each : NacosServiceLoader.load(ModuleStateBuilder.class)) {
if (each.isIgnore()) {
continue;
}
try {
moduleStateBuilders.add(each);
ModuleState moduleState = each.build();
moduleStates.put(moduleState.getModuleName(), moduleState);
} catch (Exception e) {
Expand All @@ -59,11 +64,28 @@ public static ModuleStateHolder getInstance() {
return INSTANCE;
}

private void reBuildModuleState() {
for (ModuleStateBuilder each : moduleStateBuilders) {
if (each.isCacheable()) {
continue;
}
try {
ModuleState moduleState = each.build();
moduleStates.put(moduleState.getModuleName(), moduleState);
} catch (Exception e) {
LOGGER.warn("reBuild ModuleState failed in builder:{}", each.getClass().getCanonicalName(), e);
}
}

}

public Optional<ModuleState> getModuleState(String moduleName) {
reBuildModuleState();
return Optional.ofNullable(moduleStates.get(moduleName));
}

public Set<ModuleState> getAllModuleStates() {
reBuildModuleState();
return new HashSet<>(moduleStates.values());
}

Expand Down
Loading