Use new code style for nacos-console module. (#3195)
This commit is contained in:
parent
c9bf5f2f70
commit
0a65aa8976
@ -15,8 +15,8 @@
|
||||
~ limitations under the License.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
@ -30,7 +30,7 @@
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
@ -44,12 +44,12 @@
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>nacos-naming</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>nacos-istio</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- log -->
|
||||
<!-- log4j通过slf4j来代理 -->
|
||||
<dependency>
|
||||
@ -91,7 +91,7 @@
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
@ -102,7 +102,7 @@
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
|
||||
<reporting>
|
||||
<plugins>
|
||||
<plugin>
|
||||
@ -111,7 +111,7 @@
|
||||
</plugin>
|
||||
</plugins>
|
||||
</reporting>
|
||||
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>release-nacos</id>
|
||||
|
@ -21,16 +21,16 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.web.servlet.ServletComponentScan;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/**
|
||||
* Nacos starter.
|
||||
*
|
||||
* @author nacos
|
||||
*/
|
||||
@SpringBootApplication(scanBasePackages = "com.alibaba.nacos")
|
||||
@ServletComponentScan
|
||||
@EnableScheduling
|
||||
public class Nacos {
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Nacos.class, args);
|
||||
}
|
||||
|
@ -13,8 +13,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.nacos.console.config;
|
||||
|
||||
package com.alibaba.nacos.console.config;
|
||||
|
||||
import com.alibaba.nacos.core.code.ControllerMethodsCache;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@ -29,6 +29,8 @@ import org.springframework.web.filter.CorsFilter;
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
/**
|
||||
* Console config.
|
||||
*
|
||||
* @author yshen
|
||||
* @author nkorange
|
||||
* @since 1.2.0
|
||||
@ -37,26 +39,29 @@ import javax.annotation.PostConstruct;
|
||||
@EnableScheduling
|
||||
@PropertySource("/application.properties")
|
||||
public class ConsoleConfig {
|
||||
|
||||
|
||||
@Autowired
|
||||
private ControllerMethodsCache methodsCache;
|
||||
|
||||
|
||||
/**
|
||||
* Init.
|
||||
*/
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
methodsCache.initClassMethod("com.alibaba.nacos.naming.controllers");
|
||||
methodsCache.initClassMethod("com.alibaba.nacos.console.controller");
|
||||
methodsCache.initClassMethod("com.alibaba.nacos.config.server.controller");
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
public CorsFilter corsFilter() {
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
CorsConfiguration config = new CorsConfiguration();
|
||||
config.setAllowCredentials(true);
|
||||
config.addAllowedOrigin("*");
|
||||
config.addAllowedHeader("*");
|
||||
config.setMaxAge(18000L);
|
||||
config.addAllowedMethod("*");
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
source.registerCorsConfiguration("/**", config);
|
||||
return new CorsFilter(source);
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.alibaba.nacos.console.controller;
|
||||
|
||||
import com.alibaba.nacos.config.server.service.repository.PersistService;
|
||||
@ -28,25 +29,28 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* Health Controller.
|
||||
*
|
||||
* @author <a href="mailto:huangxiaoyu1018@gmail.com">hxy1991</a>
|
||||
*/
|
||||
@RestController("consoleHealth")
|
||||
@RequestMapping("/v1/console/health")
|
||||
public class HealthController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(HealthController.class);
|
||||
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HealthController.class);
|
||||
|
||||
private final PersistService persistService;
|
||||
|
||||
private final OperatorController apiCommands;
|
||||
|
||||
|
||||
@Autowired
|
||||
public HealthController(PersistService persistService, OperatorController apiCommands) {
|
||||
this.persistService = persistService;
|
||||
this.apiCommands = apiCommands;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Whether the Nacos is in broken states or not, and cannot recover except by being restarted
|
||||
* Whether the Nacos is in broken states or not, and cannot recover except by being restarted.
|
||||
*
|
||||
* @return HTTP code equal to 200 indicates that Nacos is in right states. HTTP code equal to 500 indicates that
|
||||
* Nacos is in broken states.
|
||||
@ -55,9 +59,9 @@ public class HealthController {
|
||||
public ResponseEntity liveness() {
|
||||
return ResponseEntity.ok().body("OK");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Ready to receive the request or not
|
||||
* Ready to receive the request or not.
|
||||
*
|
||||
* @return HTTP code equal to 200 indicates that Nacos is ready. HTTP code equal to 500 indicates that Nacos is not
|
||||
* ready.
|
||||
@ -66,39 +70,39 @@ public class HealthController {
|
||||
public ResponseEntity readiness(HttpServletRequest request) {
|
||||
boolean isConfigReadiness = isConfigReadiness();
|
||||
boolean isNamingReadiness = isNamingReadiness(request);
|
||||
|
||||
|
||||
if (isConfigReadiness && isNamingReadiness) {
|
||||
return ResponseEntity.ok().body("OK");
|
||||
}
|
||||
|
||||
|
||||
if (!isConfigReadiness && !isNamingReadiness) {
|
||||
return ResponseEntity.status(500).body("Config and Naming are not in readiness");
|
||||
}
|
||||
|
||||
|
||||
if (!isConfigReadiness) {
|
||||
return ResponseEntity.status(500).body("Config is not in readiness");
|
||||
}
|
||||
|
||||
|
||||
return ResponseEntity.status(500).body("Naming is not in readiness");
|
||||
}
|
||||
|
||||
|
||||
private boolean isConfigReadiness() {
|
||||
// check db
|
||||
try {
|
||||
persistService.configInfoCount("");
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
logger.error("Config health check fail.", e);
|
||||
LOGGER.error("Config health check fail.", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private boolean isNamingReadiness(HttpServletRequest request) {
|
||||
try {
|
||||
apiCommands.metrics(request);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
logger.error("Naming health check fail.", e);
|
||||
LOGGER.error("Naming health check fail.", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.alibaba.nacos.console.controller;
|
||||
|
||||
import com.alibaba.nacos.common.model.RestResult;
|
||||
@ -25,7 +26,13 @@ import com.alibaba.nacos.core.auth.ActionTypes;
|
||||
import com.alibaba.nacos.core.auth.Secured;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
@ -35,23 +42,23 @@ import java.util.UUID;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* namespace service
|
||||
* namespace service.
|
||||
*
|
||||
* @author Nacos
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/v1/console/namespaces")
|
||||
public class NamespaceController {
|
||||
|
||||
|
||||
@Autowired
|
||||
private PersistService persistService;
|
||||
|
||||
private Pattern namespaceIdCheckPattern = Pattern.compile("^[\\w-]+");
|
||||
|
||||
|
||||
private final Pattern namespaceIdCheckPattern = Pattern.compile("^[\\w-]+");
|
||||
|
||||
private static final int NAMESPACE_ID_MAX_LENGTH = 128;
|
||||
|
||||
|
||||
/**
|
||||
* Get namespace list
|
||||
* Get namespace list.
|
||||
*
|
||||
* @param request request
|
||||
* @param response response
|
||||
@ -69,15 +76,15 @@ public class NamespaceController {
|
||||
for (TenantInfo tenantInfo : tenantInfos) {
|
||||
int configCount = persistService.configInfoCount(tenantInfo.getTenantId());
|
||||
Namespace namespaceTmp = new Namespace(tenantInfo.getTenantId(), tenantInfo.getTenantName(), 200,
|
||||
configCount, 2);
|
||||
configCount, 2);
|
||||
namespaces.add(namespaceTmp);
|
||||
}
|
||||
rr.setData(namespaces);
|
||||
return rr;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* get namespace all info by namespace id
|
||||
* get namespace all info by namespace id.
|
||||
*
|
||||
* @param request request
|
||||
* @param response response
|
||||
@ -86,21 +93,21 @@ public class NamespaceController {
|
||||
*/
|
||||
@GetMapping(params = "show=all")
|
||||
public NamespaceAllInfo getNamespace(HttpServletRequest request, HttpServletResponse response,
|
||||
@RequestParam("namespaceId") String namespaceId) {
|
||||
@RequestParam("namespaceId") String namespaceId) {
|
||||
// TODO 获取用kp
|
||||
if (StringUtils.isBlank(namespaceId)) {
|
||||
return new NamespaceAllInfo(namespaceId, "Public", 200,
|
||||
persistService.configInfoCount(""), 0, "Public Namespace");
|
||||
return new NamespaceAllInfo(namespaceId, "Public", 200, persistService.configInfoCount(""), 0,
|
||||
"Public Namespace");
|
||||
} else {
|
||||
TenantInfo tenantInfo = persistService.findTenantByKp("1", namespaceId);
|
||||
int configCount = persistService.configInfoCount(namespaceId);
|
||||
return new NamespaceAllInfo(namespaceId, tenantInfo.getTenantName(), 200,
|
||||
configCount, 2, tenantInfo.getTenantDesc());
|
||||
return new NamespaceAllInfo(namespaceId, tenantInfo.getTenantName(), 200, configCount, 2,
|
||||
tenantInfo.getTenantDesc());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* create namespace
|
||||
* create namespace.
|
||||
*
|
||||
* @param request request
|
||||
* @param response response
|
||||
@ -111,11 +118,10 @@ public class NamespaceController {
|
||||
@PostMapping
|
||||
@Secured(resource = NacosAuthConfig.CONSOLE_RESOURCE_NAME_PREFIX + "namespaces", action = ActionTypes.WRITE)
|
||||
public Boolean createNamespace(HttpServletRequest request, HttpServletResponse response,
|
||||
@RequestParam("customNamespaceId") String namespaceId,
|
||||
@RequestParam("namespaceName") String namespaceName,
|
||||
@RequestParam(value = "namespaceDesc", required = false) String namespaceDesc) {
|
||||
@RequestParam("customNamespaceId") String namespaceId, @RequestParam("namespaceName") String namespaceName,
|
||||
@RequestParam(value = "namespaceDesc", required = false) String namespaceDesc) {
|
||||
// TODO 获取用kp
|
||||
if(StringUtils.isBlank(namespaceId)){
|
||||
if (StringUtils.isBlank(namespaceId)) {
|
||||
namespaceId = UUID.randomUUID().toString();
|
||||
} else {
|
||||
namespaceId = namespaceId.trim();
|
||||
@ -125,32 +131,31 @@ public class NamespaceController {
|
||||
if (namespaceId.length() > NAMESPACE_ID_MAX_LENGTH) {
|
||||
return false;
|
||||
}
|
||||
if(persistService.tenantInfoCountByTenantId(namespaceId) > 0){
|
||||
if (persistService.tenantInfoCountByTenantId(namespaceId) > 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
persistService.insertTenantInfoAtomic("1", namespaceId, namespaceName, namespaceDesc, "nacos",
|
||||
System.currentTimeMillis());
|
||||
System.currentTimeMillis());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @author klw(213539@qq.com)
|
||||
* @Description: check namespaceId exist
|
||||
* @Date 2019/12/10 21:41
|
||||
* @param: namespaceId
|
||||
* @return java.lang.Boolean
|
||||
* check namespaceId exist.
|
||||
*
|
||||
* @param namespaceId namespace id
|
||||
* @return true if exist, otherwise false
|
||||
*/
|
||||
@GetMapping(params = "checkNamespaceIdExist=true")
|
||||
public Boolean checkNamespaceIdExist(@RequestParam("customNamespaceId") String namespaceId){
|
||||
if(StringUtils.isBlank(namespaceId)){
|
||||
public Boolean checkNamespaceIdExist(@RequestParam("customNamespaceId") String namespaceId) {
|
||||
if (StringUtils.isBlank(namespaceId)) {
|
||||
return false;
|
||||
}
|
||||
return (persistService.tenantInfoCountByTenantId(namespaceId) > 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* edit namespace
|
||||
* edit namespace.
|
||||
*
|
||||
* @param namespace namespace
|
||||
* @param namespaceShowName namespace ShowName
|
||||
@ -160,15 +165,15 @@ public class NamespaceController {
|
||||
@PutMapping
|
||||
@Secured(resource = NacosAuthConfig.CONSOLE_RESOURCE_NAME_PREFIX + "namespaces", action = ActionTypes.WRITE)
|
||||
public Boolean editNamespace(@RequestParam("namespace") String namespace,
|
||||
@RequestParam("namespaceShowName") String namespaceShowName,
|
||||
@RequestParam(value = "namespaceDesc", required = false) String namespaceDesc) {
|
||||
@RequestParam("namespaceShowName") String namespaceShowName,
|
||||
@RequestParam(value = "namespaceDesc", required = false) String namespaceDesc) {
|
||||
// TODO 获取用kp
|
||||
persistService.updateTenantNameAtomic("1", namespace, namespaceShowName, namespaceDesc);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* del namespace by id
|
||||
* del namespace by id.
|
||||
*
|
||||
* @param request request
|
||||
* @param response response
|
||||
@ -178,9 +183,9 @@ public class NamespaceController {
|
||||
@DeleteMapping
|
||||
@Secured(resource = NacosAuthConfig.CONSOLE_RESOURCE_NAME_PREFIX + "namespaces", action = ActionTypes.WRITE)
|
||||
public Boolean deleteConfig(HttpServletRequest request, HttpServletResponse response,
|
||||
@RequestParam("namespaceId") String namespaceId) {
|
||||
@RequestParam("namespaceId") String namespaceId) {
|
||||
persistService.removeTenantInfoAtomic("1", namespaceId);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -13,8 +13,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.nacos.console.controller;
|
||||
|
||||
package com.alibaba.nacos.console.controller;
|
||||
|
||||
import com.alibaba.nacos.common.model.RestResult;
|
||||
import com.alibaba.nacos.console.security.nacos.NacosAuthConfig;
|
||||
@ -23,11 +23,15 @@ import com.alibaba.nacos.core.auth.ActionTypes;
|
||||
import com.alibaba.nacos.core.auth.Secured;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* Permission operation controller
|
||||
* Permission operation controller.
|
||||
*
|
||||
* @author nkorange
|
||||
* @since 1.2.0
|
||||
@ -35,12 +39,12 @@ import org.springframework.web.bind.annotation.*;
|
||||
@RestController
|
||||
@RequestMapping("/v1/auth/permissions")
|
||||
public class PermissionController {
|
||||
|
||||
|
||||
@Autowired
|
||||
private NacosRoleServiceImpl nacosRoleService;
|
||||
|
||||
|
||||
/**
|
||||
* Query permissions of a role
|
||||
* Query permissions of a role.
|
||||
*
|
||||
* @param role the role
|
||||
* @param pageNo page index
|
||||
@ -50,16 +54,16 @@ public class PermissionController {
|
||||
@GetMapping
|
||||
@Secured(resource = NacosAuthConfig.CONSOLE_RESOURCE_NAME_PREFIX + "permissions", action = ActionTypes.READ)
|
||||
public Object getPermissions(@RequestParam int pageNo, @RequestParam int pageSize,
|
||||
@RequestParam(name = "role", defaultValue = StringUtils.EMPTY) String role) {
|
||||
@RequestParam(name = "role", defaultValue = StringUtils.EMPTY) String role) {
|
||||
return nacosRoleService.getPermissionsFromDatabase(role, pageNo, pageSize);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a permission to a role
|
||||
* Add a permission to a role.
|
||||
*
|
||||
* @param role the role
|
||||
* @param role the role
|
||||
* @param resource the related resource
|
||||
* @param action the related action
|
||||
* @param action the related action
|
||||
* @return ok if succeed
|
||||
*/
|
||||
@PostMapping
|
||||
@ -68,18 +72,19 @@ public class PermissionController {
|
||||
nacosRoleService.addPermission(role, resource, action);
|
||||
return new RestResult<>(200, "add permission ok!");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delete a permission from a role
|
||||
* Delete a permission from a role.
|
||||
*
|
||||
* @param role the role
|
||||
* @param role the role
|
||||
* @param resource the related resource
|
||||
* @param action the related action
|
||||
* @param action the related action
|
||||
* @return ok if succeed
|
||||
*/
|
||||
@DeleteMapping
|
||||
@Secured(resource = NacosAuthConfig.CONSOLE_RESOURCE_NAME_PREFIX + "permissions", action = ActionTypes.WRITE)
|
||||
public Object deletePermission(@RequestParam String role, @RequestParam String resource, @RequestParam String action) {
|
||||
public Object deletePermission(@RequestParam String role, @RequestParam String resource,
|
||||
@RequestParam String action) {
|
||||
nacosRoleService.deletePermission(role, resource, action);
|
||||
return new RestResult<>(200, "delete permission ok!");
|
||||
}
|
||||
|
@ -13,8 +13,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.nacos.console.controller;
|
||||
|
||||
package com.alibaba.nacos.console.controller;
|
||||
|
||||
import com.alibaba.nacos.common.model.RestResult;
|
||||
import com.alibaba.nacos.console.security.nacos.NacosAuthConfig;
|
||||
@ -23,10 +23,15 @@ import com.alibaba.nacos.core.auth.ActionTypes;
|
||||
import com.alibaba.nacos.core.auth.Secured;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* Role operation controller
|
||||
* Role operation controller.
|
||||
*
|
||||
* @author nkorange
|
||||
* @since 1.2.0
|
||||
@ -34,12 +39,12 @@ import org.springframework.web.bind.annotation.*;
|
||||
@RestController
|
||||
@RequestMapping("/v1/auth/roles")
|
||||
public class RoleController {
|
||||
|
||||
|
||||
@Autowired
|
||||
private NacosRoleServiceImpl roleService;
|
||||
|
||||
|
||||
/**
|
||||
* Get roles list
|
||||
* Get roles list.
|
||||
*
|
||||
* @param pageNo number index of page
|
||||
* @param pageSize page size
|
||||
@ -49,20 +54,18 @@ public class RoleController {
|
||||
@GetMapping
|
||||
@Secured(resource = NacosAuthConfig.CONSOLE_RESOURCE_NAME_PREFIX + "roles", action = ActionTypes.READ)
|
||||
public Object getRoles(@RequestParam int pageNo, @RequestParam int pageSize,
|
||||
@RequestParam(name = "username", defaultValue = "") String username) {
|
||||
@RequestParam(name = "username", defaultValue = "") String username) {
|
||||
return roleService.getRolesFromDatabase(username, pageNo, pageSize);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a role to a user
|
||||
* <p>
|
||||
* This method is used for 2 functions:
|
||||
* 1. create a role and bind it to GLOBAL_ADMIN.
|
||||
* 2. bind a role to an user.
|
||||
*
|
||||
* @param role
|
||||
* @param username
|
||||
* @return
|
||||
* <p>This method is used for 2 functions: 1. create a role and bind it to GLOBAL_ADMIN. 2. bind a role to an user.
|
||||
*
|
||||
* @param role role name
|
||||
* @param username username
|
||||
* @return Code 200 and message 'add role ok!'
|
||||
*/
|
||||
@PostMapping
|
||||
@Secured(resource = NacosAuthConfig.CONSOLE_RESOURCE_NAME_PREFIX + "roles", action = ActionTypes.WRITE)
|
||||
@ -70,9 +73,9 @@ public class RoleController {
|
||||
roleService.addRole(role, username);
|
||||
return new RestResult<>(200, "add role ok!");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delete a role. If no username is specified, all users under this role are deleted
|
||||
* Delete a role. If no username is specified, all users under this role are deleted.
|
||||
*
|
||||
* @param role role
|
||||
* @param username username
|
||||
@ -81,7 +84,7 @@ public class RoleController {
|
||||
@DeleteMapping
|
||||
@Secured(resource = NacosAuthConfig.CONSOLE_RESOURCE_NAME_PREFIX + "roles", action = ActionTypes.WRITE)
|
||||
public Object deleteRole(@RequestParam String role,
|
||||
@RequestParam(name = "username", defaultValue = StringUtils.EMPTY) String username) {
|
||||
@RequestParam(name = "username", defaultValue = StringUtils.EMPTY) String username) {
|
||||
if (StringUtils.isBlank(username)) {
|
||||
roleService.deleteRole(role);
|
||||
} else {
|
||||
@ -89,5 +92,5 @@ public class RoleController {
|
||||
}
|
||||
return new RestResult<>(200, "delete role of user " + username + " ok!");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -13,8 +13,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.nacos.console.controller;
|
||||
|
||||
package com.alibaba.nacos.console.controller;
|
||||
|
||||
import com.alibaba.nacos.common.utils.VersionUtils;
|
||||
import com.alibaba.nacos.core.utils.ApplicationUtils;
|
||||
@ -27,23 +27,29 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author xingxuechao
|
||||
* on:2019/2/27 11:17 AM
|
||||
* Server state controller.
|
||||
*
|
||||
* @author xingxuechao on:2019/2/27 11:17 AM
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/v1/console/server")
|
||||
public class ServerStateController {
|
||||
|
||||
|
||||
/**
|
||||
* Get server state of current server.
|
||||
*
|
||||
* @return state json.
|
||||
*/
|
||||
@GetMapping("/state")
|
||||
public ResponseEntity serverState() {
|
||||
Map<String,String> serverState = new HashMap<>(3);
|
||||
serverState.put("standalone_mode", ApplicationUtils.getStandaloneMode() ?
|
||||
ApplicationUtils.STANDALONE_MODE_ALONE : ApplicationUtils.STANDALONE_MODE_CLUSTER);
|
||||
|
||||
Map<String, String> serverState = new HashMap<>(3);
|
||||
serverState.put("standalone_mode", ApplicationUtils.getStandaloneMode() ? ApplicationUtils.STANDALONE_MODE_ALONE
|
||||
: ApplicationUtils.STANDALONE_MODE_CLUSTER);
|
||||
|
||||
serverState.put("function_mode", ApplicationUtils.getFunctionMode());
|
||||
serverState.put("version", VersionUtils.version);
|
||||
|
||||
|
||||
return ResponseEntity.ok().body(serverState);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.alibaba.nacos.console.controller;
|
||||
|
||||
import com.alibaba.nacos.api.common.Constants;
|
||||
@ -27,9 +28,12 @@ import com.alibaba.nacos.console.security.nacos.users.NacosUser;
|
||||
import com.alibaba.nacos.console.security.nacos.users.NacosUserDetailsServiceImpl;
|
||||
import com.alibaba.nacos.console.utils.JwtTokenUtils;
|
||||
import com.alibaba.nacos.console.utils.PasswordEncoderUtil;
|
||||
import com.alibaba.nacos.core.auth.*;
|
||||
import com.alibaba.nacos.core.auth.AccessException;
|
||||
import com.alibaba.nacos.core.auth.ActionTypes;
|
||||
import com.alibaba.nacos.core.auth.AuthConfigs;
|
||||
import com.alibaba.nacos.core.auth.AuthSystemTypes;
|
||||
import com.alibaba.nacos.core.auth.Secured;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
@ -37,14 +41,20 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* User related methods entry
|
||||
* User related methods entry.
|
||||
*
|
||||
* @author wfnuser
|
||||
* @author nkorange
|
||||
@ -52,27 +62,27 @@ import java.util.List;
|
||||
@RestController("user")
|
||||
@RequestMapping({"/v1/auth", "/v1/auth/users"})
|
||||
public class UserController {
|
||||
|
||||
|
||||
@Autowired
|
||||
private JwtTokenUtils jwtTokenUtils;
|
||||
|
||||
|
||||
@Autowired
|
||||
private AuthenticationManager authenticationManager;
|
||||
|
||||
|
||||
@Autowired
|
||||
private NacosUserDetailsServiceImpl userDetailsService;
|
||||
|
||||
|
||||
@Autowired
|
||||
private NacosRoleServiceImpl roleService;
|
||||
|
||||
|
||||
@Autowired
|
||||
private AuthConfigs authConfigs;
|
||||
|
||||
|
||||
@Autowired
|
||||
private NacosAuthManager authManager;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new user
|
||||
* Create a new user.
|
||||
*
|
||||
* @param username username
|
||||
* @param password password
|
||||
@ -83,7 +93,7 @@ public class UserController {
|
||||
@Secured(resource = NacosAuthConfig.CONSOLE_RESOURCE_NAME_PREFIX + "users", action = ActionTypes.WRITE)
|
||||
@PostMapping
|
||||
public Object createUser(@RequestParam String username, @RequestParam String password) {
|
||||
|
||||
|
||||
User user = userDetailsService.getUserFromDatabase(username);
|
||||
if (user != null) {
|
||||
throw new IllegalArgumentException("user '" + username + "' already exist!");
|
||||
@ -91,9 +101,9 @@ public class UserController {
|
||||
userDetailsService.createUser(username, PasswordEncoderUtil.encode(password));
|
||||
return new RestResult<>(200, "create user ok!");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delete an existed user
|
||||
* Delete an existed user.
|
||||
*
|
||||
* @param username username of user
|
||||
* @return ok if deleted succeed, keep silent if user not exist
|
||||
@ -113,9 +123,9 @@ public class UserController {
|
||||
userDetailsService.deleteUser(username);
|
||||
return new RestResult<>(200, "delete user ok!");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update an user
|
||||
* Update an user.
|
||||
*
|
||||
* @param username username of user
|
||||
* @param newPassword new password of user
|
||||
@ -126,19 +136,19 @@ public class UserController {
|
||||
@PutMapping
|
||||
@Secured(resource = NacosAuthConfig.CONSOLE_RESOURCE_NAME_PREFIX + "users", action = ActionTypes.WRITE)
|
||||
public Object updateUser(@RequestParam String username, @RequestParam String newPassword) {
|
||||
|
||||
|
||||
User user = userDetailsService.getUserFromDatabase(username);
|
||||
if (user == null) {
|
||||
throw new IllegalArgumentException("user " + username + " not exist!");
|
||||
}
|
||||
|
||||
|
||||
userDetailsService.updateUserPassword(username, PasswordEncoderUtil.encode(newPassword));
|
||||
|
||||
|
||||
return new RestResult<>(200, "update user ok!");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get paged users
|
||||
* Get paged users.
|
||||
*
|
||||
* @param pageNo number index of page
|
||||
* @param pageSize size of page
|
||||
@ -150,11 +160,11 @@ public class UserController {
|
||||
public Object getUsers(@RequestParam int pageNo, @RequestParam int pageSize) {
|
||||
return userDetailsService.getUsersFromDatabase(pageNo, pageSize);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Login to Nacos
|
||||
* <p>
|
||||
* This methods uses username and password to require a new token.
|
||||
*
|
||||
* <p>This methods uses username and password to require a new token.
|
||||
*
|
||||
* @param username username of user
|
||||
* @param password password
|
||||
@ -164,27 +174,26 @@ public class UserController {
|
||||
* @throws AccessException if user info is incorrect
|
||||
*/
|
||||
@PostMapping("/login")
|
||||
public Object login(@RequestParam String username, @RequestParam String password,
|
||||
HttpServletResponse response, HttpServletRequest request) throws AccessException {
|
||||
|
||||
|
||||
public Object login(@RequestParam String username, @RequestParam String password, HttpServletResponse response,
|
||||
HttpServletRequest request) throws AccessException {
|
||||
|
||||
if (AuthSystemTypes.NACOS.name().equalsIgnoreCase(authConfigs.getNacosAuthSystemType())) {
|
||||
NacosUser user = (NacosUser) authManager.login(request);
|
||||
|
||||
response.addHeader(NacosAuthConfig.AUTHORIZATION_HEADER,
|
||||
NacosAuthConfig.TOKEN_PREFIX + user.getToken());
|
||||
|
||||
|
||||
response.addHeader(NacosAuthConfig.AUTHORIZATION_HEADER, NacosAuthConfig.TOKEN_PREFIX + user.getToken());
|
||||
|
||||
ObjectNode result = JacksonUtils.createEmptyJsonNode();
|
||||
// JSONObject result = new JSONObject();
|
||||
// JSONObject result = new JSONObject();
|
||||
result.put(Constants.ACCESS_TOKEN, user.getToken());
|
||||
result.put(Constants.TOKEN_TTL, authConfigs.getTokenValidityInSeconds());
|
||||
result.put(Constants.GLOBAL_ADMIN, user.isGlobalAdmin());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// 通过用户名和密码创建一个 Authentication 认证对象,实现类为 UsernamePasswordAuthenticationToken
|
||||
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
|
||||
|
||||
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username,
|
||||
password);
|
||||
|
||||
RestResult<String> rr = new RestResult<String>();
|
||||
try {
|
||||
//通过 AuthenticationManager(默认实现为ProviderManager)的authenticate方法验证 Authentication 对象
|
||||
@ -204,18 +213,25 @@ public class UserController {
|
||||
return rr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update password.
|
||||
*
|
||||
* @param oldPassword old password
|
||||
* @param newPassword new password
|
||||
* @return Code 200 if update successfully, Code 401 if old password invalid, otherwise 500
|
||||
*/
|
||||
@PutMapping("/password")
|
||||
@Deprecated
|
||||
public RestResult<String> updatePassword(@RequestParam(value = "oldPassword") String oldPassword,
|
||||
@RequestParam(value = "newPassword") String newPassword) {
|
||||
|
||||
@RequestParam(value = "newPassword") String newPassword) {
|
||||
|
||||
RestResult<String> rr = new RestResult<String>();
|
||||
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||
String username = ((UserDetails) principal).getUsername();
|
||||
User user = userDetailsService.getUserFromDatabase(username);
|
||||
String password = user.getPassword();
|
||||
|
||||
|
||||
// TODO: throw out more fine grained exceptions
|
||||
try {
|
||||
if (PasswordEncoderUtil.matches(oldPassword, password)) {
|
||||
|
@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.alibaba.nacos.console.exception;
|
||||
|
||||
import com.alibaba.nacos.common.utils.ExceptionUtil;
|
||||
@ -25,29 +26,29 @@ import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
|
||||
/**
|
||||
* Exception handler for console module
|
||||
* Exception handler for console module.
|
||||
*
|
||||
* @author nkorange
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@ControllerAdvice
|
||||
public class ConsoleExceptionHandler {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ConsoleExceptionHandler.class);
|
||||
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ConsoleExceptionHandler.class);
|
||||
|
||||
@ExceptionHandler(AccessException.class)
|
||||
private ResponseEntity<String> handleAccessException(AccessException e) {
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(e.getErrMsg());
|
||||
}
|
||||
|
||||
|
||||
@ExceptionHandler(IllegalArgumentException.class)
|
||||
private ResponseEntity<String> handleIllegalArgumentException(IllegalArgumentException e) {
|
||||
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ExceptionUtil.getAllExceptionMsg(e));
|
||||
}
|
||||
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
private ResponseEntity<String> handleException(Exception e) {
|
||||
logger.error("CONSOLE", e);
|
||||
LOGGER.error("CONSOLE", e);
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ExceptionUtil.getAllExceptionMsg(e));
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.alibaba.nacos.console.filter;
|
||||
|
||||
import com.alibaba.nacos.api.common.Constants;
|
||||
@ -30,26 +31,26 @@ import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* jwt auth token filter
|
||||
* jwt auth token filter.
|
||||
*
|
||||
* @author wfnuser
|
||||
*/
|
||||
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
|
||||
|
||||
|
||||
private static final String TOKEN_PREFIX = "Bearer ";
|
||||
|
||||
private JwtTokenManager tokenManager;
|
||||
|
||||
|
||||
private final JwtTokenManager tokenManager;
|
||||
|
||||
public JwtAuthenticationTokenFilter(JwtTokenManager tokenManager) {
|
||||
this.tokenManager = tokenManager;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
|
||||
throws IOException, ServletException {
|
||||
|
||||
String jwt = resolveToken(request);
|
||||
|
||||
|
||||
if (StringUtils.isNotBlank(jwt) && SecurityContextHolder.getContext().getAuthentication() == null) {
|
||||
this.tokenManager.validateToken(jwt);
|
||||
Authentication authentication = this.tokenManager.getAuthentication(jwt);
|
||||
@ -57,9 +58,9 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
|
||||
}
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get token from header
|
||||
* Get token from header.
|
||||
*/
|
||||
private String resolveToken(HttpServletRequest request) {
|
||||
String bearerToken = request.getHeader(NacosAuthConfig.AUTHORIZATION_HEADER);
|
||||
|
@ -13,51 +13,53 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.alibaba.nacos.console.model;
|
||||
|
||||
/**
|
||||
* Namespace
|
||||
* Namespace.
|
||||
*
|
||||
* @author diamond
|
||||
*/
|
||||
public class Namespace {
|
||||
|
||||
|
||||
private String namespace;
|
||||
|
||||
|
||||
private String namespaceShowName;
|
||||
|
||||
|
||||
private int quota;
|
||||
|
||||
|
||||
private int configCount;
|
||||
|
||||
/**
|
||||
* 0 : Global configuration, 1 : Default private namespace ,2 : Custom namespace
|
||||
* 0 : Global configuration, 1 : Default private namespace ,2 : Custom namespace.
|
||||
*/
|
||||
private int type;
|
||||
|
||||
|
||||
public String getNamespaceShowName() {
|
||||
return namespaceShowName;
|
||||
}
|
||||
|
||||
|
||||
public void setNamespaceShowName(String namespaceShowName) {
|
||||
this.namespaceShowName = namespaceShowName;
|
||||
}
|
||||
|
||||
|
||||
public String getNamespace() {
|
||||
return namespace;
|
||||
}
|
||||
|
||||
|
||||
public void setNamespace(String namespace) {
|
||||
this.namespace = namespace;
|
||||
}
|
||||
|
||||
|
||||
public Namespace() {
|
||||
}
|
||||
|
||||
|
||||
public Namespace(String namespace, String namespaceShowName) {
|
||||
this.namespace = namespace;
|
||||
this.namespaceShowName = namespaceShowName;
|
||||
}
|
||||
|
||||
|
||||
public Namespace(String namespace, String namespaceShowName, int quota, int configCount, int type) {
|
||||
this.namespace = namespace;
|
||||
this.namespaceShowName = namespaceShowName;
|
||||
@ -65,29 +67,29 @@ public class Namespace {
|
||||
this.configCount = configCount;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
|
||||
public int getQuota() {
|
||||
return quota;
|
||||
}
|
||||
|
||||
|
||||
public void setQuota(int quota) {
|
||||
this.quota = quota;
|
||||
}
|
||||
|
||||
|
||||
public int getConfigCount() {
|
||||
return configCount;
|
||||
}
|
||||
|
||||
|
||||
public void setConfigCount(int configCount) {
|
||||
this.configCount = configCount;
|
||||
}
|
||||
|
||||
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
public void setType(int type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -13,32 +13,33 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.alibaba.nacos.console.model;
|
||||
|
||||
/**
|
||||
* all namespace info
|
||||
* all namespace info.
|
||||
*
|
||||
* @author Nacos
|
||||
*/
|
||||
public class NamespaceAllInfo extends Namespace {
|
||||
|
||||
|
||||
private String namespaceDesc;
|
||||
|
||||
|
||||
public String getNamespaceDesc() {
|
||||
return namespaceDesc;
|
||||
}
|
||||
|
||||
|
||||
public void setNamespaceDesc(String namespaceDesc) {
|
||||
this.namespaceDesc = namespaceDesc;
|
||||
}
|
||||
|
||||
|
||||
public NamespaceAllInfo() {
|
||||
}
|
||||
|
||||
|
||||
public NamespaceAllInfo(String namespace, String namespaceShowName, int quota, int configCount, int type,
|
||||
String namespaceDesc) {
|
||||
String namespaceDesc) {
|
||||
super(namespace, namespaceShowName, quota, configCount, type);
|
||||
this.namespaceDesc = namespaceDesc;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.alibaba.nacos.console.security.nacos;
|
||||
|
||||
import com.alibaba.nacos.console.security.nacos.users.NacosUserDetailsServiceImpl;
|
||||
@ -25,32 +26,32 @@ import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* auth provider
|
||||
* auth provider.
|
||||
*
|
||||
* @author wfnuser
|
||||
*/
|
||||
@Component
|
||||
public class CustomAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
|
||||
@Autowired
|
||||
private NacosUserDetailsServiceImpl userDetailsService;
|
||||
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
|
||||
|
||||
String username = (String) authentication.getPrincipal();
|
||||
String password = (String) authentication.getCredentials();
|
||||
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
|
||||
|
||||
|
||||
if (!password.equals(userDetails.getPassword())) {
|
||||
return new UsernamePasswordAuthenticationToken(username, null, null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> aClass) {
|
||||
return aClass.equals(UsernamePasswordAuthenticationToken.class);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.alibaba.nacos.console.security.nacos;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
@ -27,20 +28,19 @@ import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* jwt auth fail point
|
||||
* jwt auth fail point.
|
||||
*
|
||||
* @author wfnuser
|
||||
*/
|
||||
@Component
|
||||
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationEntryPoint.class);
|
||||
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(JwtAuthenticationEntryPoint.class);
|
||||
|
||||
@Override
|
||||
public void commence(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
AuthenticationException e) throws IOException, ServletException {
|
||||
logger.error("Responding with unauthorized error. Message:{}, url:{}", e.getMessage(), request.getRequestURI());
|
||||
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e)
|
||||
throws IOException, ServletException {
|
||||
LOGGER.error("Responding with unauthorized error. Message:{}, url:{}", e.getMessage(), request.getRequestURI());
|
||||
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.alibaba.nacos.console.security.nacos;
|
||||
|
||||
import com.alibaba.nacos.core.auth.AuthConfigs;
|
||||
@ -27,26 +28,25 @@ import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* JWT token manager
|
||||
* JWT token manager.
|
||||
*
|
||||
* @author wfnuser
|
||||
* @author nkorange
|
||||
*/
|
||||
@Component
|
||||
public class JwtTokenManager {
|
||||
|
||||
|
||||
private static final String AUTHORITIES_KEY = "auth";
|
||||
|
||||
|
||||
@Autowired
|
||||
private AuthConfigs authConfigs;
|
||||
|
||||
|
||||
/**
|
||||
* Create token
|
||||
* Create token.
|
||||
*
|
||||
* @param authentication auth info
|
||||
* @return token
|
||||
@ -54,49 +54,47 @@ public class JwtTokenManager {
|
||||
public String createToken(Authentication authentication) {
|
||||
return createToken(authentication.getName());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create token.
|
||||
*
|
||||
* @param userName auth info
|
||||
* @return token
|
||||
*/
|
||||
public String createToken(String userName) {
|
||||
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
|
||||
Date validity;
|
||||
validity = new Date(now + authConfigs.getTokenValidityInSeconds() * 1000L);
|
||||
|
||||
|
||||
Claims claims = Jwts.claims().setSubject(userName);
|
||||
|
||||
return Jwts.builder()
|
||||
.setClaims(claims)
|
||||
.setExpiration(validity)
|
||||
.signWith(SignatureAlgorithm.HS256, authConfigs.getSecretKey())
|
||||
.compact();
|
||||
|
||||
return Jwts.builder().setClaims(claims).setExpiration(validity)
|
||||
.signWith(SignatureAlgorithm.HS256, authConfigs.getSecretKey()).compact();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get auth Info
|
||||
* Get auth Info.
|
||||
*
|
||||
* @param token token
|
||||
* @return auth info
|
||||
*/
|
||||
public Authentication getAuthentication(String token) {
|
||||
/**
|
||||
* parse the payload of token
|
||||
*/
|
||||
Claims claims = Jwts.parser()
|
||||
.setSigningKey(authConfigs.getSecretKey())
|
||||
.parseClaimsJws(token)
|
||||
.getBody();
|
||||
|
||||
List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList((String) claims.get(AUTHORITIES_KEY));
|
||||
|
||||
Claims claims = Jwts.parser().setSigningKey(authConfigs.getSecretKey()).parseClaimsJws(token).getBody();
|
||||
|
||||
List<GrantedAuthority> authorities = AuthorityUtils
|
||||
.commaSeparatedStringToAuthorityList((String) claims.get(AUTHORITIES_KEY));
|
||||
|
||||
User principal = new User(claims.getSubject(), "", authorities);
|
||||
return new UsernamePasswordAuthenticationToken(principal, "", authorities);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* validate token
|
||||
* validate token.
|
||||
*
|
||||
* @param token token
|
||||
* @return whether valid
|
||||
*/
|
||||
public void validateToken(String token) {
|
||||
Jwts.parser().setSigningKey(authConfigs.getSecretKey()).parseClaimsJws(token);
|
||||
|
@ -13,8 +13,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.nacos.console.security.nacos;
|
||||
|
||||
package com.alibaba.nacos.console.security.nacos;
|
||||
|
||||
import com.alibaba.nacos.console.filter.JwtAuthenticationTokenFilter;
|
||||
import com.alibaba.nacos.console.security.nacos.users.NacosUserDetailsServiceImpl;
|
||||
@ -23,7 +23,6 @@ import com.alibaba.nacos.core.auth.AuthSystemTypes;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.BeanIds;
|
||||
@ -39,105 +38,93 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic
|
||||
import org.springframework.web.cors.CorsUtils;
|
||||
|
||||
/**
|
||||
* Spring security config
|
||||
* Spring security config.
|
||||
*
|
||||
* @author Nacos
|
||||
*/
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
public class NacosAuthConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
|
||||
public static final String AUTHORIZATION_HEADER = "Authorization";
|
||||
|
||||
|
||||
public static final String SECURITY_IGNORE_URLS_SPILT_CHAR = ",";
|
||||
|
||||
|
||||
public static final String LOGIN_ENTRY_POINT = "/v1/auth/login";
|
||||
|
||||
|
||||
public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/v1/auth/**";
|
||||
|
||||
|
||||
public static final String TOKEN_PREFIX = "Bearer ";
|
||||
|
||||
|
||||
public static final String CONSOLE_RESOURCE_NAME_PREFIX = "console/";
|
||||
|
||||
|
||||
@Autowired
|
||||
private Environment env;
|
||||
|
||||
|
||||
@Autowired
|
||||
private JwtTokenManager tokenProvider;
|
||||
|
||||
|
||||
@Autowired
|
||||
private AuthConfigs authConfigs;
|
||||
|
||||
|
||||
@Autowired
|
||||
private NacosUserDetailsServiceImpl userDetailsService;
|
||||
|
||||
|
||||
@Bean(name = BeanIds.AUTHENTICATION_MANAGER)
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean() throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void configure(WebSecurity web) {
|
||||
|
||||
String ignoreURLs = null;
|
||||
//
|
||||
|
||||
String ignoreUrls = null;
|
||||
if (AuthSystemTypes.NACOS.name().equalsIgnoreCase(authConfigs.getNacosAuthSystemType())) {
|
||||
ignoreURLs = "/**";
|
||||
ignoreUrls = "/**";
|
||||
}
|
||||
//
|
||||
if (StringUtils.isBlank(authConfigs.getNacosAuthSystemType())) {
|
||||
ignoreURLs = env.getProperty("nacos.security.ignore.urls", "/**");
|
||||
ignoreUrls = env.getProperty("nacos.security.ignore.urls", "/**");
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(ignoreURLs)) {
|
||||
for (String ignoreURL : ignoreURLs.trim().split(SECURITY_IGNORE_URLS_SPILT_CHAR)) {
|
||||
web.ignoring().antMatchers(ignoreURL.trim());
|
||||
if (StringUtils.isNotBlank(ignoreUrls)) {
|
||||
for (String each : ignoreUrls.trim().split(SECURITY_IGNORE_URLS_SPILT_CHAR)) {
|
||||
web.ignoring().antMatchers(each.trim());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
|
||||
|
||||
if (StringUtils.isBlank(authConfigs.getNacosAuthSystemType())) {
|
||||
http
|
||||
|
||||
.csrf().disable()
|
||||
.cors() // We don't need CSRF for JWT based authentication
|
||||
|
||||
.and()
|
||||
.sessionManagement()
|
||||
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||
|
||||
.and()
|
||||
.authorizeRequests()
|
||||
.requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
|
||||
.antMatchers(LOGIN_ENTRY_POINT).permitAll()
|
||||
|
||||
.and()
|
||||
.authorizeRequests()
|
||||
.antMatchers(TOKEN_BASED_AUTH_ENTRY_POINT).authenticated()
|
||||
|
||||
.and()
|
||||
.exceptionHandling()
|
||||
.authenticationEntryPoint(new JwtAuthenticationEntryPoint());
|
||||
|
||||
|
||||
.csrf().disable().cors() // We don't need CSRF for JWT based authentication
|
||||
|
||||
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||
|
||||
.and().authorizeRequests().requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
|
||||
.antMatchers(LOGIN_ENTRY_POINT).permitAll()
|
||||
|
||||
.and().authorizeRequests().antMatchers(TOKEN_BASED_AUTH_ENTRY_POINT).authenticated()
|
||||
|
||||
.and().exceptionHandling().authenticationEntryPoint(new JwtAuthenticationEntryPoint());
|
||||
|
||||
// disable cache
|
||||
http.headers().cacheControl();
|
||||
|
||||
http.addFilterBefore(new JwtAuthenticationTokenFilter(tokenProvider), UsernamePasswordAuthenticationFilter.class);
|
||||
|
||||
http.addFilterBefore(new JwtAuthenticationTokenFilter(tokenProvider),
|
||||
UsernamePasswordAuthenticationFilter.class);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.alibaba.nacos.console.security.nacos;
|
||||
|
||||
import com.alibaba.nacos.api.common.Constants;
|
||||
@ -38,25 +39,25 @@ import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Builtin access control entry of Nacos
|
||||
* Builtin access control entry of Nacos.
|
||||
*
|
||||
* @author nkorange
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@Component
|
||||
public class NacosAuthManager implements AuthManager {
|
||||
|
||||
|
||||
private static final String TOKEN_PREFIX = "Bearer ";
|
||||
|
||||
|
||||
@Autowired
|
||||
private JwtTokenManager tokenManager;
|
||||
|
||||
|
||||
@Autowired
|
||||
private AuthenticationManager authenticationManager;
|
||||
|
||||
|
||||
@Autowired
|
||||
private NacosRoleServiceImpl roleService;
|
||||
|
||||
|
||||
@Override
|
||||
public User login(Object request) throws AccessException {
|
||||
HttpServletRequest req = (HttpServletRequest) request;
|
||||
@ -64,7 +65,7 @@ public class NacosAuthManager implements AuthManager {
|
||||
if (StringUtils.isBlank(token)) {
|
||||
throw new AccessException("user not found!");
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
tokenManager.validateToken(token);
|
||||
} catch (ExpiredJwtException e) {
|
||||
@ -72,10 +73,10 @@ public class NacosAuthManager implements AuthManager {
|
||||
} catch (Exception e) {
|
||||
throw new AccessException("token invalid!");
|
||||
}
|
||||
|
||||
|
||||
Authentication authentication = tokenManager.getAuthentication(token);
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
|
||||
|
||||
String username = authentication.getName();
|
||||
NacosUser user = new NacosUser();
|
||||
user.setUserName(username);
|
||||
@ -89,23 +90,23 @@ public class NacosAuthManager implements AuthManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void auth(Permission permission, User user) throws AccessException {
|
||||
if (Loggers.AUTH.isDebugEnabled()) {
|
||||
Loggers.AUTH.debug("auth permission: {}, user: {}", permission, user);
|
||||
}
|
||||
|
||||
|
||||
if (!roleService.hasPermission(user.getUserName(), permission)) {
|
||||
throw new AccessException("authorization failed!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get token from header
|
||||
* Get token from header.
|
||||
*/
|
||||
private String resolveToken(HttpServletRequest request) throws AccessException {
|
||||
String bearerToken = request.getHeader(NacosAuthConfig.AUTHORIZATION_HEADER);
|
||||
@ -118,19 +119,20 @@ public class NacosAuthManager implements AuthManager {
|
||||
String password = request.getParameter("password");
|
||||
bearerToken = resolveTokenFromUser(userName, password);
|
||||
}
|
||||
|
||||
|
||||
return bearerToken;
|
||||
}
|
||||
|
||||
|
||||
private String resolveTokenFromUser(String userName, String rawPassword) throws AccessException {
|
||||
|
||||
|
||||
try {
|
||||
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userName, rawPassword);
|
||||
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userName,
|
||||
rawPassword);
|
||||
authenticationManager.authenticate(authenticationToken);
|
||||
} catch (AuthenticationException e) {
|
||||
throw new AccessException("unknown user!");
|
||||
}
|
||||
|
||||
|
||||
return tokenManager.createToken(userName);
|
||||
}
|
||||
}
|
||||
|
@ -13,8 +13,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.nacos.console.security.nacos.roles;
|
||||
|
||||
package com.alibaba.nacos.console.security.nacos.roles;
|
||||
|
||||
import com.alibaba.nacos.config.server.auth.PermissionInfo;
|
||||
import com.alibaba.nacos.config.server.auth.PermissionPersistService;
|
||||
@ -33,8 +33,11 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@ -46,31 +49,32 @@ import java.util.regex.Pattern;
|
||||
*/
|
||||
@Service
|
||||
public class NacosRoleServiceImpl {
|
||||
|
||||
|
||||
public static final String GLOBAL_ADMIN_ROLE = "ROLE_ADMIN";
|
||||
|
||||
|
||||
@Autowired
|
||||
private AuthConfigs authConfigs;
|
||||
|
||||
|
||||
@Autowired
|
||||
private RolePersistService rolePersistService;
|
||||
|
||||
|
||||
@Autowired
|
||||
private NacosUserDetailsServiceImpl userDetailsService;
|
||||
|
||||
|
||||
@Autowired
|
||||
private PermissionPersistService permissionPersistService;
|
||||
|
||||
private Set<String> roleSet = new ConcurrentHashSet<>();
|
||||
|
||||
private Map<String, List<RoleInfo>> roleInfoMap = new ConcurrentHashMap<>();
|
||||
|
||||
private Map<String, List<PermissionInfo>> permissionInfoMap = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
private final Set<String> roleSet = new ConcurrentHashSet<>();
|
||||
|
||||
private final Map<String, List<RoleInfo>> roleInfoMap = new ConcurrentHashMap<>();
|
||||
|
||||
private final Map<String, List<PermissionInfo>> permissionInfoMap = new ConcurrentHashMap<>();
|
||||
|
||||
@Scheduled(initialDelay = 5000, fixedDelay = 15000)
|
||||
private void reload() {
|
||||
try {
|
||||
Page<RoleInfo> roleInfoPage = rolePersistService.getRolesByUserName(StringUtils.EMPTY, 1, Integer.MAX_VALUE);
|
||||
Page<RoleInfo> roleInfoPage = rolePersistService
|
||||
.getRolesByUserName(StringUtils.EMPTY, 1, Integer.MAX_VALUE);
|
||||
if (roleInfoPage == null) {
|
||||
return;
|
||||
}
|
||||
@ -83,13 +87,14 @@ public class NacosRoleServiceImpl {
|
||||
tmpRoleInfoMap.get(roleInfo.getUsername()).add(roleInfo);
|
||||
tmpRoleSet.add(roleInfo.getRole());
|
||||
}
|
||||
|
||||
|
||||
Map<String, List<PermissionInfo>> tmpPermissionInfoMap = new ConcurrentHashMap<>(16);
|
||||
for (String role : tmpRoleSet) {
|
||||
Page<PermissionInfo> permissionInfoPage = permissionPersistService.getPermissions(role, 1, Integer.MAX_VALUE);
|
||||
Page<PermissionInfo> permissionInfoPage = permissionPersistService
|
||||
.getPermissions(role, 1, Integer.MAX_VALUE);
|
||||
tmpPermissionInfoMap.put(role, permissionInfoPage.getPageItems());
|
||||
}
|
||||
|
||||
|
||||
roleSet.addAll(tmpRoleSet);
|
||||
roleInfoMap.putAll(tmpRoleInfoMap);
|
||||
permissionInfoMap.putAll(tmpPermissionInfoMap);
|
||||
@ -97,36 +102,36 @@ public class NacosRoleServiceImpl {
|
||||
Loggers.AUTH.warn("[LOAD-ROLES] load failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if the user has permission of the resource.
|
||||
* <p>
|
||||
* Note if the user has many roles, this method returns true if any one role of the user has the
|
||||
* desired permission.
|
||||
*
|
||||
* <p>Note if the user has many roles, this method returns true if any one role of the user has the desired
|
||||
* permission.
|
||||
*
|
||||
* @param username user info
|
||||
* @param permission permission to auth
|
||||
* @return true if granted, false otherwise
|
||||
*/
|
||||
public boolean hasPermission(String username, Permission permission) {
|
||||
|
||||
|
||||
List<RoleInfo> roleInfoList = getRoles(username);
|
||||
if (Collections.isEmpty(roleInfoList)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Global admin pass:
|
||||
for (RoleInfo roleInfo : roleInfoList) {
|
||||
if (GLOBAL_ADMIN_ROLE.equals(roleInfo.getRole())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Old global admin can pass resource 'console/':
|
||||
if (permission.getResource().startsWith(NacosAuthConfig.CONSOLE_RESOURCE_NAME_PREFIX)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// For other roles, use a pattern match to decide if pass or not.
|
||||
for (RoleInfo roleInfo : roleInfoList) {
|
||||
List<PermissionInfo> permissionInfoList = getPermissions(roleInfo.getRole());
|
||||
@ -136,15 +141,15 @@ public class NacosRoleServiceImpl {
|
||||
for (PermissionInfo permissionInfo : permissionInfoList) {
|
||||
String permissionResource = permissionInfo.getResource().replaceAll("\\*", ".*");
|
||||
String permissionAction = permissionInfo.getAction();
|
||||
if (permissionAction.contains(permission.getAction()) &&
|
||||
Pattern.matches(permissionResource, permission.getResource())) {
|
||||
if (permissionAction.contains(permission.getAction()) && Pattern
|
||||
.matches(permissionResource, permission.getResource())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public List<RoleInfo> getRoles(String username) {
|
||||
List<RoleInfo> roleInfoList = roleInfoMap.get(username);
|
||||
if (!authConfigs.isCachingEnabled()) {
|
||||
@ -155,7 +160,7 @@ public class NacosRoleServiceImpl {
|
||||
}
|
||||
return roleInfoList;
|
||||
}
|
||||
|
||||
|
||||
public Page<RoleInfo> getRolesFromDatabase(String userName, int pageNo, int pageSize) {
|
||||
Page<RoleInfo> roles = rolePersistService.getRolesByUserName(userName, pageNo, pageSize);
|
||||
if (roles == null) {
|
||||
@ -163,7 +168,7 @@ public class NacosRoleServiceImpl {
|
||||
}
|
||||
return roles;
|
||||
}
|
||||
|
||||
|
||||
public List<PermissionInfo> getPermissions(String role) {
|
||||
List<PermissionInfo> permissionInfoList = permissionInfoMap.get(role);
|
||||
if (!authConfigs.isCachingEnabled()) {
|
||||
@ -174,11 +179,17 @@ public class NacosRoleServiceImpl {
|
||||
}
|
||||
return permissionInfoList;
|
||||
}
|
||||
|
||||
|
||||
public Page<PermissionInfo> getPermissionsByRoleFromDatabase(String role, int pageNo, int pageSize) {
|
||||
return permissionPersistService.getPermissions(role, pageNo, pageSize);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add role.
|
||||
*
|
||||
* @param role role name
|
||||
* @param username user name
|
||||
*/
|
||||
public void addRole(String role, String username) {
|
||||
if (userDetailsService.getUserFromDatabase(username) == null) {
|
||||
throw new IllegalArgumentException("user '" + username + "' not found!");
|
||||
@ -189,16 +200,16 @@ public class NacosRoleServiceImpl {
|
||||
rolePersistService.addRole(role, username);
|
||||
roleSet.add(role);
|
||||
}
|
||||
|
||||
|
||||
public void deleteRole(String role, String userName) {
|
||||
rolePersistService.deleteRole(role, userName);
|
||||
}
|
||||
|
||||
|
||||
public void deleteRole(String role) {
|
||||
rolePersistService.deleteRole(role);
|
||||
roleSet.remove(role);
|
||||
}
|
||||
|
||||
|
||||
public Page<PermissionInfo> getPermissionsFromDatabase(String role, int pageNo, int pageSize) {
|
||||
Page<PermissionInfo> pageInfo = permissionPersistService.getPermissions(role, pageNo, pageSize);
|
||||
if (pageInfo == null) {
|
||||
@ -206,14 +217,21 @@ public class NacosRoleServiceImpl {
|
||||
}
|
||||
return pageInfo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add permission.
|
||||
*
|
||||
* @param role role name
|
||||
* @param resource resource
|
||||
* @param action action
|
||||
*/
|
||||
public void addPermission(String role, String resource, String action) {
|
||||
if (!roleSet.contains(role)) {
|
||||
throw new IllegalArgumentException("role " + role + " not found!");
|
||||
}
|
||||
permissionPersistService.addPermission(role, resource, action);
|
||||
}
|
||||
|
||||
|
||||
public void deletePermission(String role, String resource, String action) {
|
||||
permissionPersistService.deletePermission(role, resource, action);
|
||||
}
|
||||
|
@ -19,36 +19,35 @@ package com.alibaba.nacos.console.security.nacos.users;
|
||||
import com.alibaba.nacos.core.auth.User;
|
||||
|
||||
/**
|
||||
* Nacos User.
|
||||
*
|
||||
* @author nkorange
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public class NacosUser extends User {
|
||||
|
||||
|
||||
private String token;
|
||||
|
||||
|
||||
private boolean globalAdmin = false;
|
||||
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
|
||||
public boolean isGlobalAdmin() {
|
||||
return globalAdmin;
|
||||
}
|
||||
|
||||
|
||||
public void setGlobalAdmin(boolean globalAdmin) {
|
||||
this.globalAdmin = globalAdmin;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NacosUser{" +
|
||||
"token='" + token + '\'' +
|
||||
", globalAdmin=" + globalAdmin +
|
||||
'}';
|
||||
return "NacosUser{" + "token='" + token + '\'' + ", globalAdmin=" + globalAdmin + '}';
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.alibaba.nacos.console.security.nacos.users;
|
||||
|
||||
import com.alibaba.nacos.config.server.model.User;
|
||||
@ -23,49 +24,49 @@ import org.springframework.security.core.userdetails.UserDetails;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* custem user
|
||||
* custom user.
|
||||
*
|
||||
* @author wfnuser
|
||||
*/
|
||||
public class NacosUserDetails implements UserDetails {
|
||||
|
||||
private User user;
|
||||
|
||||
|
||||
private final User user;
|
||||
|
||||
public NacosUserDetails(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||
// TODO: get authorities
|
||||
return AuthorityUtils.commaSeparatedStringToAuthorityList("");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return user.getPassword();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return user.getUsername();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonLocked() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isCredentialsNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
|
@ -13,8 +13,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.nacos.console.security.nacos.users;
|
||||
|
||||
package com.alibaba.nacos.console.security.nacos.users;
|
||||
|
||||
import com.alibaba.nacos.config.server.auth.UserPersistService;
|
||||
import com.alibaba.nacos.config.server.model.Page;
|
||||
@ -28,27 +28,26 @@ import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Custem user service
|
||||
* Custem user service.
|
||||
*
|
||||
* @author wfnuser
|
||||
* @author nkorange
|
||||
*/
|
||||
@Service
|
||||
public class NacosUserDetailsServiceImpl implements UserDetailsService {
|
||||
|
||||
|
||||
private Map<String, User> userMap = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
@Autowired
|
||||
private UserPersistService userPersistService;
|
||||
|
||||
|
||||
@Autowired
|
||||
private AuthConfigs authConfigs;
|
||||
|
||||
|
||||
@Scheduled(initialDelay = 5000, fixedDelay = 15000)
|
||||
private void reload() {
|
||||
try {
|
||||
@ -56,7 +55,7 @@ public class NacosUserDetailsServiceImpl implements UserDetailsService {
|
||||
if (users == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Map<String, User> map = new ConcurrentHashMap<>(16);
|
||||
for (User user : users.getPageItems()) {
|
||||
map.put(user.getUsername(), user);
|
||||
@ -66,29 +65,29 @@ public class NacosUserDetailsServiceImpl implements UserDetailsService {
|
||||
Loggers.AUTH.warn("[LOAD-USERS] load failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
|
||||
|
||||
User user = userMap.get(username);
|
||||
if (!authConfigs.isCachingEnabled()) {
|
||||
user = userPersistService.findUserByUsername(username);
|
||||
}
|
||||
|
||||
|
||||
if (user == null) {
|
||||
throw new UsernameNotFoundException(username);
|
||||
}
|
||||
return new NacosUserDetails(user);
|
||||
}
|
||||
|
||||
|
||||
public void updateUserPassword(String username, String password) {
|
||||
userPersistService.updateUserPassword(username, password);
|
||||
}
|
||||
|
||||
|
||||
public Page<User> getUsersFromDatabase(int pageNo, int pageSize) {
|
||||
return userPersistService.getUsers(pageNo, pageSize);
|
||||
}
|
||||
|
||||
|
||||
public User getUser(String username) {
|
||||
User user = userMap.get(username);
|
||||
if (!authConfigs.isCachingEnabled()) {
|
||||
@ -96,15 +95,15 @@ public class NacosUserDetailsServiceImpl implements UserDetailsService {
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
|
||||
public User getUserFromDatabase(String username) {
|
||||
return userPersistService.findUserByUsername(username);
|
||||
}
|
||||
|
||||
|
||||
public void createUser(String username, String password) {
|
||||
userPersistService.createUser(username, password);
|
||||
}
|
||||
|
||||
|
||||
public void deleteUser(String username) {
|
||||
userPersistService.deleteUser(username);
|
||||
}
|
||||
|
@ -13,9 +13,16 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.alibaba.nacos.console.utils;
|
||||
|
||||
import io.jsonwebtoken.*;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.ExpiredJwtException;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.MalformedJwtException;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.SignatureException;
|
||||
import io.jsonwebtoken.UnsupportedJwtException;
|
||||
import io.jsonwebtoken.security.Keys;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -34,43 +41,46 @@ import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Jwt token tool
|
||||
* Jwt token tool.
|
||||
*
|
||||
* @author wfnuser
|
||||
*/
|
||||
@Component
|
||||
public class JwtTokenUtils {
|
||||
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(JwtTokenUtils.class);
|
||||
|
||||
|
||||
private static final String AUTHORITIES_KEY = "auth";
|
||||
|
||||
|
||||
/**
|
||||
* minimum SHA_256 secretKey string length
|
||||
* minimum SHA_256 secretKey string length.
|
||||
*/
|
||||
private static final int SHA_256_SECRET_CHAR_SIZE = 256 / 8;
|
||||
|
||||
|
||||
/**
|
||||
* default SHA_256 secretKey flag
|
||||
* default SHA_256 secretKey flag.
|
||||
*/
|
||||
private static final String DEFAULT_SECRET_FLAG = "default";
|
||||
|
||||
|
||||
/**
|
||||
* custom SHA_256 secretKey from config property
|
||||
* custom SHA_256 secretKey from config property.
|
||||
*/
|
||||
@Value("${nacos.security.token.secret-key:default}")
|
||||
private String customSecretKeyStr;
|
||||
|
||||
|
||||
/**
|
||||
* secret key
|
||||
* secret key.
|
||||
*/
|
||||
private SecretKey secretKey;
|
||||
|
||||
|
||||
/**
|
||||
* Token validity time(ms)
|
||||
* Token validity time(ms).
|
||||
*/
|
||||
private long tokenValidityInMilliseconds;
|
||||
|
||||
|
||||
/**
|
||||
* Init.
|
||||
*/
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
//use default secretKey for SHA-256
|
||||
@ -83,69 +93,52 @@ public class JwtTokenUtils {
|
||||
if (left > 0) {
|
||||
//character for padding
|
||||
StringBuilder stringBuilder = new StringBuilder(customSecretKeyStr);
|
||||
for (int i = 0 ;i < left ; i ++){
|
||||
stringBuilder.append(i%10);
|
||||
for (int i = 0; i < left; i++) {
|
||||
stringBuilder.append(i % 10);
|
||||
}
|
||||
this.secretKey = Keys.hmacShaKeyFor(stringBuilder.toString().getBytes());
|
||||
}else {
|
||||
} else {
|
||||
this.secretKey = Keys.hmacShaKeyFor(customSecretKeyStr.getBytes());
|
||||
}
|
||||
}
|
||||
this.tokenValidityInMilliseconds = 1000 * 60 * 30L;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create token
|
||||
* Create token.
|
||||
*
|
||||
* @param authentication auth info
|
||||
* @return token
|
||||
*/
|
||||
public String createToken(Authentication authentication) {
|
||||
/**
|
||||
* Current time
|
||||
*/
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
/**
|
||||
* Validity date
|
||||
*/
|
||||
Date validity;
|
||||
validity = new Date(now + this.tokenValidityInMilliseconds);
|
||||
|
||||
/**
|
||||
* create token
|
||||
*/
|
||||
return Jwts.builder()
|
||||
.setSubject(authentication.getName())
|
||||
.claim(AUTHORITIES_KEY, "")
|
||||
.setExpiration(validity)
|
||||
.signWith(secretKey, SignatureAlgorithm.HS256)
|
||||
.compact();
|
||||
Date validity = new Date(now + this.tokenValidityInMilliseconds);
|
||||
|
||||
return Jwts.builder().setSubject(authentication.getName()).claim(AUTHORITIES_KEY, "").setExpiration(validity)
|
||||
.signWith(secretKey, SignatureAlgorithm.HS256).compact();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get auth Info
|
||||
* Get auth Info.
|
||||
*
|
||||
* @param token token
|
||||
* @return auth info
|
||||
*/
|
||||
public Authentication getAuthentication(String token) {
|
||||
/**
|
||||
* parse the payload of token
|
||||
*/
|
||||
Claims claims = Jwts.parser()
|
||||
.setSigningKey(secretKey)
|
||||
.parseClaimsJws(token)
|
||||
.getBody();
|
||||
|
||||
List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList((String) claims.get(AUTHORITIES_KEY));
|
||||
|
||||
|
||||
Claims claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody();
|
||||
|
||||
List<GrantedAuthority> authorities = AuthorityUtils
|
||||
.commaSeparatedStringToAuthorityList((String) claims.get(AUTHORITIES_KEY));
|
||||
|
||||
User principal = new User(claims.getSubject(), "", authorities);
|
||||
return new UsernamePasswordAuthenticationToken(principal, "", authorities);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* validate token
|
||||
* validate token.
|
||||
*
|
||||
* @param token token
|
||||
* @return whether valid
|
||||
|
@ -13,25 +13,26 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.alibaba.nacos.console.utils;
|
||||
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
|
||||
/**
|
||||
* Password encoder tool
|
||||
* Password encoder tool.
|
||||
*
|
||||
* @author nacos
|
||||
*/
|
||||
public class PasswordEncoderUtil {
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(new BCryptPasswordEncoder().encode("nacos"));
|
||||
}
|
||||
|
||||
|
||||
public static Boolean matches(String raw, String encoded) {
|
||||
return new BCryptPasswordEncoder().matches(raw, encoded);
|
||||
}
|
||||
|
||||
|
||||
public static String encode(String raw) {
|
||||
return new BCryptPasswordEncoder().encode(raw);
|
||||
}
|
||||
|
@ -13,12 +13,12 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.alibaba.nacos.console.controller;
|
||||
|
||||
import com.alibaba.nacos.common.utils.JacksonUtils;
|
||||
import com.alibaba.nacos.config.server.service.repository.PersistService;
|
||||
import com.alibaba.nacos.naming.controllers.OperatorController;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@ -40,68 +40,65 @@ import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:huangxiaoyu1018@gmail.com">hxy1991</a>
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(classes = MockServletContext.class)
|
||||
@WebAppConfiguration
|
||||
public class HealthControllerTest {
|
||||
|
||||
|
||||
@InjectMocks
|
||||
private HealthController healthController;
|
||||
|
||||
|
||||
@Mock
|
||||
private PersistService persistService;
|
||||
|
||||
|
||||
@Mock
|
||||
private OperatorController apiCommands;
|
||||
|
||||
|
||||
private MockMvc mockmvc;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mockmvc = MockMvcBuilders.standaloneSetup(healthController).build();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testLiveness() throws Exception {
|
||||
String url = "/v1/console/health/liveness";
|
||||
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(url);
|
||||
Assert.assertEquals(200, mockmvc.perform(builder).andReturn().getResponse().getStatus());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testReadiness() throws Exception {
|
||||
String url = "/v1/console/health/readiness";
|
||||
|
||||
|
||||
Mockito.when(persistService.configInfoCount(any(String.class))).thenReturn(0);
|
||||
Mockito.when(apiCommands.metrics(any(HttpServletRequest.class))).thenReturn(JacksonUtils.createEmptyJsonNode());
|
||||
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(url);
|
||||
Assert.assertEquals(200, mockmvc.perform(builder).andReturn().getResponse().getStatus());
|
||||
|
||||
|
||||
// Config and Naming are not in readiness
|
||||
Mockito.when(persistService.configInfoCount(any(String.class))).thenThrow(
|
||||
new RuntimeException("HealthControllerTest.testReadiness"));
|
||||
Mockito.when(apiCommands.metrics(any(HttpServletRequest.class))).thenThrow(
|
||||
new RuntimeException("HealthControllerTest.testReadiness"));
|
||||
Mockito.when(persistService.configInfoCount(any(String.class)))
|
||||
.thenThrow(new RuntimeException("HealthControllerTest.testReadiness"));
|
||||
Mockito.when(apiCommands.metrics(any(HttpServletRequest.class)))
|
||||
.thenThrow(new RuntimeException("HealthControllerTest.testReadiness"));
|
||||
builder = MockMvcRequestBuilders.get(url);
|
||||
MockHttpServletResponse response = mockmvc.perform(builder).andReturn().getResponse();
|
||||
Assert.assertEquals(500, response.getStatus());
|
||||
Assert.assertEquals("Config and Naming are not in readiness", response.getContentAsString());
|
||||
|
||||
|
||||
// Config is not in readiness
|
||||
Mockito.when(persistService.configInfoCount(any(String.class))).thenThrow(
|
||||
new RuntimeException("HealthControllerTest.testReadiness"));
|
||||
Mockito.when(persistService.configInfoCount(any(String.class)))
|
||||
.thenThrow(new RuntimeException("HealthControllerTest.testReadiness"));
|
||||
Mockito.when(apiCommands.metrics(any(HttpServletRequest.class))).thenReturn(JacksonUtils.createEmptyJsonNode());
|
||||
response = mockmvc.perform(builder).andReturn().getResponse();
|
||||
Assert.assertEquals(500, response.getStatus());
|
||||
Assert.assertEquals("Config is not in readiness", response.getContentAsString());
|
||||
|
||||
|
||||
// Naming is not in readiness
|
||||
Mockito.when(persistService.configInfoCount(any(String.class))).thenReturn(0);
|
||||
Mockito.when(apiCommands.metrics(any(HttpServletRequest.class))).thenThrow(
|
||||
new RuntimeException("HealthControllerTest.testReadiness"));
|
||||
Mockito.when(apiCommands.metrics(any(HttpServletRequest.class)))
|
||||
.thenThrow(new RuntimeException("HealthControllerTest.testReadiness"));
|
||||
builder = MockMvcRequestBuilders.get(url);
|
||||
response = mockmvc.perform(builder).andReturn().getResponse();
|
||||
Assert.assertEquals(500, response.getStatus());
|
||||
|
@ -22,39 +22,40 @@ import com.alibaba.nacos.core.auth.AccessException;
|
||||
import com.alibaba.nacos.core.auth.AuthConfigs;
|
||||
import com.alibaba.nacos.core.auth.AuthSystemTypes;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
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 javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class UserControllerTest {
|
||||
|
||||
|
||||
@Mock
|
||||
private HttpServletRequest request;
|
||||
|
||||
|
||||
@Mock
|
||||
private HttpServletResponse response;
|
||||
|
||||
|
||||
@Mock
|
||||
private AuthConfigs authConfigs;
|
||||
|
||||
|
||||
@Mock
|
||||
private NacosAuthManager authManager;
|
||||
|
||||
|
||||
private UserController userController;
|
||||
|
||||
|
||||
private NacosUser user;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
userController = new UserController();
|
||||
@ -65,7 +66,7 @@ public class UserControllerTest {
|
||||
injectObject("authConfigs", authConfigs);
|
||||
injectObject("authManager", authManager);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testLoginWithAuthedUser() throws AccessException {
|
||||
when(authManager.login(request)).thenReturn(user);
|
||||
@ -78,7 +79,7 @@ public class UserControllerTest {
|
||||
assertTrue(actualString.contains("\"tokenTtl\":18000"));
|
||||
assertTrue(actualString.contains("\"globalAdmin\":true"));
|
||||
}
|
||||
|
||||
|
||||
private void injectObject(String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {
|
||||
Field field = UserController.class.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
|
Loading…
Reference in New Issue
Block a user