Skip to content

Commit

Permalink
[ISSUE #11957] Remove default password (#11991)
Browse files Browse the repository at this point in the history
* Remove default password

* admin role check fix

* remove tmp admin
  • Loading branch information
godhth authored Apr 29, 2024
1 parent b2e506d commit 70ad2eb
Show file tree
Hide file tree
Showing 17 changed files with 228 additions and 27 deletions.
10 changes: 10 additions & 0 deletions auth/src/main/java/com/alibaba/nacos/auth/config/AuthConfigs.java
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -125,6 +127,14 @@ private void refreshPluginProperties() {
}
}

public boolean isHasGlobalAdminRole() {
return hasGlobalAdminRole;
}

public void setHasGlobalAdminRole(boolean hasGlobalAdminRole) {
this.hasGlobalAdminRole = hasGlobalAdminRole;
}

public String getNacosAuthSystemType() {
return nacosAuthSystemType;
}
Expand Down
4 changes: 0 additions & 4 deletions config/src/main/resources/META-INF/derby-schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
3 changes: 0 additions & 3 deletions config/src/main/resources/META-INF/mysql-schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -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');
3 changes: 0 additions & 3 deletions console/src/main/resources/META-INF/derby-schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -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');


/******************************************/
Expand Down
4 changes: 0 additions & 4 deletions distribution/conf/derby-schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
3 changes: 0 additions & 3 deletions distribution/conf/mysql-schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ public NacosUser authenticate(String username, String rawPassword) throws Access
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!");
Expand Down Expand Up @@ -121,6 +120,11 @@ public boolean hasGlobalAdminRole(String username) {
return roleService.hasGlobalAdminRole(username);
}

@Override
public boolean hasGlobalAdminRole() {
return roleService.hasGlobalAdminRole();
}

@Override
public boolean hasGlobalAdminRole(NacosUser nacosUser) {
if (nacosUser.isGlobalAdmin()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ public boolean hasGlobalAdminRole(String username) {
return getManager().hasGlobalAdminRole(username);
}

@Override
public boolean hasGlobalAdminRole() {
return getManager().hasGlobalAdminRole();
}

@Override
public boolean hasGlobalAdminRole(NacosUser nacosUser) {
return getManager().hasGlobalAdminRole(nacosUser);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
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;
Expand All @@ -36,6 +37,7 @@
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;
Expand Down Expand Up @@ -109,6 +111,33 @@ public Object createUser(@RequestParam String username, @RequestParam String pas
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.
*
Expand All @@ -122,7 +151,7 @@ public Object deleteUser(@RequestParam String username) {
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);
}
}
Expand Down Expand Up @@ -170,7 +199,8 @@ public Object updateUser(@RequestParam String username, @RequestParam String new
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;
}
Expand Down Expand Up @@ -232,10 +262,14 @@ public Page<User> fuzzySearchUser(@RequestParam int pageNo, @RequestParam int pa
*/
@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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,15 @@ public List<RoleInfo> getRoles(String username) {
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) {
Expand Down Expand Up @@ -213,6 +222,7 @@ public void addRole(String role, String username) {
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!");
Expand All @@ -221,10 +231,39 @@ public void addRole(String role, String username) {
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);
Expand Down Expand Up @@ -307,4 +346,21 @@ public boolean hasGlobalAdminRole(String userName) {

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;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

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;
Expand Down Expand Up @@ -116,7 +117,7 @@ public void createUser(String username, String password) {
public void deleteUser(String username) {
userPersistService.deleteUser(username);
}

public Page<User> findUsersLike4Page(String username, int pageNo, int pageSize) {
return userPersistService.findUsersLike4Page(username, pageNo, pageSize);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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());
}
}
Loading

0 comments on commit 70ad2eb

Please sign in to comment.