[ISSUE #11957] Remove default password (#11991)

* Remove default password

* admin role check fix

* remove tmp admin
This commit is contained in:
hth 2024-04-29 10:04:00 +08:00 committed by GitHub
parent b2e506d7fe
commit 70ad2eb991
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 228 additions and 27 deletions

View File

@ -78,6 +78,8 @@ public class AuthConfigs extends Subscriber<ServerConfigChangeEvent> {
@Value("${" + Constants.Auth.NACOS_CORE_AUTH_ENABLE_USER_AGENT_AUTH_WHITE + ":false}")
private boolean enableUserAgentAuthWhite;
private boolean hasGlobalAdminRole;
private Map<String, Properties> authPluginProperties = new HashMap<>();
public AuthConfigs() {
@ -125,6 +127,14 @@ public class AuthConfigs extends Subscriber<ServerConfigChangeEvent> {
}
}
public boolean isHasGlobalAdminRole() {
return hasGlobalAdminRole;
}
public void setHasGlobalAdminRole(boolean hasGlobalAdminRole) {
this.hasGlobalAdminRole = hasGlobalAdminRole;
}
public String getNacosAuthSystemType() {
return nacosAuthSystemType;
}

View File

@ -210,10 +210,6 @@ CREATE TABLE permissions (
constraint uk_role_permission UNIQUE (role,resource,action)
);
INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE);
INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN');
/******************************************/
/* ipv6 support */

View File

@ -208,6 +208,3 @@ CREATE TABLE `permissions` (
UNIQUE INDEX `uk_role_permission` (`role`,`resource`,`action`) USING BTREE
);
INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE);
INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN');

View File

@ -210,9 +210,6 @@ CREATE TABLE permissions (
constraint uk_role_permission UNIQUE (role,resource,action)
);
INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE);
INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN');
/******************************************/

View File

@ -210,10 +210,6 @@ CREATE TABLE permissions (
constraint uk_role_permission UNIQUE (role,resource,action)
);
INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE);
INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN');
/******************************************/
/* ipv6 support */

View File

@ -208,6 +208,3 @@ CREATE TABLE `permissions` (
UNIQUE INDEX `uk_role_permission` (`role`,`resource`,`action`) USING BTREE
);
INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE);
INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN');

View File

@ -57,7 +57,6 @@ public class AbstractAuthenticationManager implements IAuthenticationManager {
if (StringUtils.isBlank(username) || StringUtils.isBlank(rawPassword)) {
throw new AccessException("user not found!");
}
NacosUserDetails nacosUserDetails = (NacosUserDetails) userDetailsService.loadUserByUsername(username);
if (nacosUserDetails == null || !PasswordEncoderUtil.matches(rawPassword, nacosUserDetails.getPassword())) {
throw new AccessException("user not found!");
@ -121,6 +120,11 @@ public class AbstractAuthenticationManager implements IAuthenticationManager {
return roleService.hasGlobalAdminRole(username);
}
@Override
public boolean hasGlobalAdminRole() {
return roleService.hasGlobalAdminRole();
}
@Override
public boolean hasGlobalAdminRole(NacosUser nacosUser) {
if (nacosUser.isGlobalAdmin()) {

View File

@ -71,6 +71,11 @@ public class AuthenticationManagerDelegator implements IAuthenticationManager {
return getManager().hasGlobalAdminRole(username);
}
@Override
public boolean hasGlobalAdminRole() {
return getManager().hasGlobalAdminRole();
}
@Override
public boolean hasGlobalAdminRole(NacosUser nacosUser) {
return getManager().hasGlobalAdminRole(nacosUser);

View File

@ -75,6 +75,13 @@ public interface IAuthenticationManager {
*/
boolean hasGlobalAdminRole(String username);
/**
* Whether the user exist the administrator role.
*
* @return if the user exist the administrator role.
*/
boolean hasGlobalAdminRole();
/**
* Whether the user has the administrator role.
*

View File

@ -35,6 +35,8 @@ public class AuthConstants {
public static final String TOKEN_PREFIX = "Bearer ";
public static final String DEFAULT_USER = "nacos";
public static final String PARAM_USERNAME = "username";
public static final String PARAM_PASSWORD = "password";

View File

@ -22,6 +22,7 @@ import com.alibaba.nacos.auth.config.AuthConfigs;
import com.alibaba.nacos.common.model.RestResult;
import com.alibaba.nacos.common.model.RestResultUtils;
import com.alibaba.nacos.common.utils.JacksonUtils;
import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.persistence.model.Page;
import com.alibaba.nacos.plugin.auth.api.IdentityContext;
import com.alibaba.nacos.plugin.auth.constant.ActionTypes;
@ -36,6 +37,7 @@ import com.alibaba.nacos.plugin.auth.impl.token.TokenManagerDelegate;
import com.alibaba.nacos.plugin.auth.impl.users.NacosUser;
import com.alibaba.nacos.plugin.auth.impl.users.NacosUserDetailsServiceImpl;
import com.alibaba.nacos.plugin.auth.impl.utils.PasswordEncoderUtil;
import com.alibaba.nacos.plugin.auth.impl.utils.PasswordGeneratorUtil;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
@ -109,6 +111,33 @@ public class UserController {
return RestResultUtils.success("create user ok!");
}
/**
* 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) {
if (AuthSystemTypes.NACOS.name().equalsIgnoreCase(authConfigs.getNacosAuthSystemType())) {
if (roleService.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;
}
userDetailsService.createUser(username, PasswordEncoderUtil.encode(password));
roleService.addAdminRole(username);
ObjectNode result = JacksonUtils.createEmptyJsonNode();
result.put(AuthConstants.PARAM_USERNAME, username);
result.put(AuthConstants.PARAM_PASSWORD, password);
return result;
} else {
return RestResultUtils.failed("not support");
}
}
/**
* Delete an existed user.
*
@ -122,7 +151,7 @@ public class UserController {
List<RoleInfo> roleInfoList = roleService.getRoles(username);
if (roleInfoList != null) {
for (RoleInfo roleInfo : roleInfoList) {
if (roleInfo.getRole().equals(AuthConstants.GLOBAL_ADMIN_ROLE)) {
if (AuthConstants.GLOBAL_ADMIN_ROLE.equals(roleInfo.getRole())) {
throw new IllegalArgumentException("cannot delete admin: " + username);
}
}
@ -170,7 +199,8 @@ public class UserController {
return RestResultUtils.success("update user ok!");
}
private boolean hasPermission(String username, HttpServletRequest request) throws HttpSessionRequiredException, AccessException {
private boolean hasPermission(String username, HttpServletRequest request)
throws HttpSessionRequiredException, AccessException {
if (!authConfigs.isAuthEnabled()) {
return true;
}
@ -232,10 +262,14 @@ public class UserController {
*/
@PostMapping("/login")
public Object login(@RequestParam String username, @RequestParam String password, HttpServletResponse response,
HttpServletRequest request) throws AccessException {
HttpServletRequest request) throws AccessException, IOException {
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());

View File

@ -176,6 +176,15 @@ public class NacosRoleServiceImpl {
return roleInfoList;
}
public List<RoleInfo> getAllRoles() {
Page<RoleInfo> roleInfoPage = rolePersistService.getRolesByUserNameAndRoleName(StringUtils.EMPTY,
StringUtils.EMPTY, DEFAULT_PAGE_NO, Integer.MAX_VALUE);
if (roleInfoPage == null) {
return null;
}
return roleInfoPage.getPageItems();
}
public Page<RoleInfo> getRolesFromDatabase(String userName, String role, int pageNo, int pageSize) {
Page<RoleInfo> roles = rolePersistService.getRolesByUserNameAndRoleName(userName, role, pageNo, pageSize);
if (roles == null) {
@ -213,6 +222,7 @@ public class NacosRoleServiceImpl {
if (userDetailsService.getUserFromDatabase(username) == null) {
throw new IllegalArgumentException("user '" + username + "' not found!");
}
if (AuthConstants.GLOBAL_ADMIN_ROLE.equals(role)) {
throw new IllegalArgumentException(
"role '" + AuthConstants.GLOBAL_ADMIN_ROLE + "' is not permitted to create!");
@ -221,10 +231,39 @@ public class NacosRoleServiceImpl {
roleSet.add(role);
}
/**
* Add role.
*
* @param username user name
*/
public void addAdminRole(String username) {
if (userDetailsService.getUserFromDatabase(username) == null) {
throw new IllegalArgumentException("user '" + username + "' not found!");
}
if (hasGlobalAdminRole()) {
throw new IllegalArgumentException("role '" + AuthConstants.GLOBAL_ADMIN_ROLE + "' already exist !");
}
rolePersistService.addRole(AuthConstants.GLOBAL_ADMIN_ROLE, username);
roleSet.add(AuthConstants.GLOBAL_ADMIN_ROLE);
authConfigs.setHasGlobalAdminRole(true);
}
/**
* delete user Role.
*
* @param role role
* @param userName userName
*/
public void deleteRole(String role, String userName) {
rolePersistService.deleteRole(role, userName);
}
/**
* deleteRole.
*
* @param role role
*/
public void deleteRole(String role) {
rolePersistService.deleteRole(role);
roleSet.remove(role);
@ -307,4 +346,21 @@ public class NacosRoleServiceImpl {
return roles.stream().anyMatch(roleInfo -> AuthConstants.GLOBAL_ADMIN_ROLE.equals(roleInfo.getRole()));
}
/**
* check if all user has at least one admin role.
*
* @return true if all user has at least one admin role.
*/
public 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;
}
}

View File

@ -18,6 +18,7 @@ package com.alibaba.nacos.plugin.auth.impl.users;
import com.alibaba.nacos.auth.config.AuthConfigs;
import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.plugin.auth.impl.persistence.UserPersistService;
import com.alibaba.nacos.persistence.model.Page;
import com.alibaba.nacos.plugin.auth.impl.persistence.User;
@ -116,7 +117,7 @@ public class NacosUserDetailsServiceImpl implements UserDetailsService {
public void deleteUser(String username) {
userPersistService.deleteUser(username);
}
public Page<User> findUsersLike4Page(String username, int pageNo, int pageSize) {
return userPersistService.findUsersLike4Page(username, pageNo, pageSize);
}

View File

@ -0,0 +1,68 @@
/*
* 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.plugin.auth.impl.utils;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
* RandomPasswordGenerator .
*
* @author : huangtianhui
*/
public class PasswordGeneratorUtil {
private static final String LOWER_CASE = "abcdefghijklmnopqrstuvwxyz";
private static final String UPPER_CASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final String DIGITS = "0123456789";
private static final String SPECIAL_CHARS = "!@#$%&";
private static final int PASSWORD_LENGTH = 8;
/**
* generateRandomPassword.
* @return
*/
public static String generateRandomPassword() {
SecureRandom random = new SecureRandom();
List<Character> pwdChars = new ArrayList<>();
pwdChars.add(LOWER_CASE.charAt(random.nextInt(LOWER_CASE.length())));
pwdChars.add(UPPER_CASE.charAt(random.nextInt(UPPER_CASE.length())));
pwdChars.add(DIGITS.charAt(random.nextInt(DIGITS.length())));
pwdChars.add(SPECIAL_CHARS.charAt(random.nextInt(SPECIAL_CHARS.length())));
// Fill the rest of the password with random characters from all categories
String allCharacters = LOWER_CASE + UPPER_CASE + DIGITS + SPECIAL_CHARS;
while (pwdChars.size() < PASSWORD_LENGTH) {
pwdChars.add(allCharacters.charAt(random.nextInt(allCharacters.length())));
}
// Shuffle to avoid predictable order
Collections.shuffle(pwdChars, random);
// Build the final password string
return pwdChars.stream().map(String::valueOf).collect(Collectors.joining());
}
}

View File

@ -34,6 +34,7 @@ import org.springframework.mock.env.MockEnvironment;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
@ -86,9 +87,10 @@ public class UserControllerTest {
}
@Test
public void testLoginWithAuthedUser() throws AccessException {
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);

View File

@ -0,0 +1,33 @@
/*
* 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.plugin.auth.impl.utils;
import org.junit.Assert;
import org.junit.Test;
public class PasswordGeneratorUtilTest {
/**
* generatePwd test.
*/
@Test
public void generatePwd() {
String pwd = PasswordGeneratorUtil.generateRandomPassword();
Assert.assertEquals(8, pwd.length());
}
}

View File

@ -210,10 +210,6 @@ CREATE TABLE permissions (
constraint uk_role_permission UNIQUE (role,resource,action)
);
INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE);
INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN');
/******************************************/
/* ipv6 support */