mirror of
https://gitee.com/pnoker/iot-dc3.git
synced 2026-10-31 02:01:51 +08:00
refactor(auth): use domain enums for oauth/mcp VO fields
Replace bare String fields with domain enums on the read-response VOs and in service-internal comparisons, removing scattered McpConstant string literals. Five value-typed enums (OAuthGrantType/OAuthClientType/McpRiskLevel/McpAuditStatus/McpConfirmationStatus) land in common-constant with @EnumValue + @JsonValue + ofValue, registered in IndexedEnumContractTest.
VO fields: McpConnectionVO{principalType,grantType}, McpToolVO.riskLevel, McpAuditVO{principalType,riskLevel,status}, OAuthClientVO.clientType. Builders convert via @AfterMapping (Enum.ofValue); the *Record projections keep String/Byte untouched (OAuthMcpMapper custom-SQL resultType boundary). Service compares/assigns via enum.getValue(); Confirmation.DECISION_* kept (gateway-shared). common-constant gains jackson-annotations because grantType's wire value is lowercase and must serialize via @JsonValue, not the uppercase enum name.
This commit is contained in:
+36
-31
@@ -58,6 +58,11 @@ import io.github.pnoker.common.entity.dto.McpToolAuthorizeResponseDTO;
|
||||
import io.github.pnoker.common.entity.dto.McpToolDefinitionDTO;
|
||||
import io.github.pnoker.common.entity.dto.McpToolResolveResponseDTO;
|
||||
import io.github.pnoker.common.enums.EnableFlagEnum;
|
||||
import io.github.pnoker.common.enums.McpAuditStatusEnum;
|
||||
import io.github.pnoker.common.enums.McpConfirmationStatusEnum;
|
||||
import io.github.pnoker.common.enums.McpRiskLevelEnum;
|
||||
import io.github.pnoker.common.enums.OAuthClientTypeEnum;
|
||||
import io.github.pnoker.common.enums.OAuthGrantTypeEnum;
|
||||
import io.github.pnoker.common.enums.PrincipalTypeEnum;
|
||||
import io.github.pnoker.common.utils.DecodeUtil;
|
||||
import io.github.pnoker.common.utils.JsonUtil;
|
||||
@@ -223,9 +228,9 @@ public class OAuthMcpRuntimeServiceImpl implements OAuthMcpRuntimeService {
|
||||
"revocation_endpoint", issuer + McpConstant.OAUTH2_REVOKE,
|
||||
"registration_endpoint", issuer + McpConstant.OAUTH2_REGISTER,
|
||||
"response_types_supported", List.of(McpConstant.OAuth.RESPONSE_TYPE_CODE),
|
||||
"grant_types_supported", List.of(McpConstant.OAuth.GRANT_AUTHORIZATION_CODE,
|
||||
McpConstant.OAuth.GRANT_CLIENT_CREDENTIALS,
|
||||
McpConstant.OAuth.GRANT_REFRESH_TOKEN),
|
||||
"grant_types_supported", List.of(OAuthGrantTypeEnum.AUTHORIZATION_CODE.getValue(),
|
||||
OAuthGrantTypeEnum.CLIENT_CREDENTIALS.getValue(),
|
||||
OAuthGrantTypeEnum.REFRESH_TOKEN.getValue()),
|
||||
"code_challenge_methods_supported", List.of(McpConstant.OAuth.CODE_CHALLENGE_METHOD_S256),
|
||||
"scopes_supported", McpConstant.Scope.SUPPORTED,
|
||||
"token_endpoint_auth_methods_supported", List.of(McpConstant.OAuth.AUTH_METHOD_CLIENT_SECRET_BASIC,
|
||||
@@ -256,24 +261,24 @@ public class OAuthMcpRuntimeServiceImpl implements OAuthMcpRuntimeService {
|
||||
throw oauthError(BAD_REQUEST.value(), "invalid_client_metadata", "client_name is required");
|
||||
}
|
||||
String clientType = StringUtils.defaultIfBlank(stringValue(request.getClientType()),
|
||||
McpConstant.OAuth.CLIENT_TYPE_PUBLIC).toUpperCase();
|
||||
if (!Set.of(McpConstant.OAuth.CLIENT_TYPE_PUBLIC, McpConstant.OAuth.CLIENT_TYPE_CONFIDENTIAL)
|
||||
OAuthClientTypeEnum.PUBLIC.getValue()).toUpperCase();
|
||||
if (!Set.of(OAuthClientTypeEnum.PUBLIC.getValue(), OAuthClientTypeEnum.CONFIDENTIAL.getValue())
|
||||
.contains(clientType)) {
|
||||
throw oauthError(BAD_REQUEST.value(), "invalid_client_metadata", "unsupported client_type");
|
||||
}
|
||||
|
||||
Set<String> grants = normalizeSet(request.getGrantTypes());
|
||||
if (grants.isEmpty()) {
|
||||
grants = McpConstant.OAuth.CLIENT_TYPE_PUBLIC.equals(clientType)
|
||||
? Set.of(McpConstant.OAuth.GRANT_AUTHORIZATION_CODE)
|
||||
: Set.of(McpConstant.OAuth.GRANT_CLIENT_CREDENTIALS);
|
||||
grants = OAuthClientTypeEnum.PUBLIC.getValue().equals(clientType)
|
||||
? Set.of(OAuthGrantTypeEnum.AUTHORIZATION_CODE.getValue())
|
||||
: Set.of(OAuthGrantTypeEnum.CLIENT_CREDENTIALS.getValue());
|
||||
}
|
||||
Set<String> scopes = normalizeSet(request.getScope());
|
||||
if (scopes.isEmpty()) {
|
||||
scopes = Set.of(McpConstant.Scope.TOOLS_LIST, McpConstant.Scope.TOOLS_CALL);
|
||||
}
|
||||
Set<String> redirects = normalizeSet(request.getRedirectUris());
|
||||
if (grants.contains(McpConstant.OAuth.GRANT_AUTHORIZATION_CODE) && redirects.isEmpty()) {
|
||||
if (grants.contains(OAuthGrantTypeEnum.AUTHORIZATION_CODE.getValue()) && redirects.isEmpty()) {
|
||||
throw oauthError(BAD_REQUEST.value(), "invalid_redirect_uri", "redirect_uris is required");
|
||||
}
|
||||
|
||||
@@ -286,7 +291,7 @@ public class OAuthMcpRuntimeServiceImpl implements OAuthMcpRuntimeService {
|
||||
Long ownerPrincipalId = principalHeader.getPrincipalId();
|
||||
Long tenantId = principalHeader.getTenantId();
|
||||
Long serviceAccountPrincipalId = request.getServiceAccountPrincipalId();
|
||||
if (grants.contains(McpConstant.OAuth.GRANT_CLIENT_CREDENTIALS)) {
|
||||
if (grants.contains(OAuthGrantTypeEnum.CLIENT_CREDENTIALS.getValue())) {
|
||||
validateServiceAccountClient(serviceAccountPrincipalId, tenantId);
|
||||
}
|
||||
|
||||
@@ -294,7 +299,7 @@ public class OAuthMcpRuntimeServiceImpl implements OAuthMcpRuntimeService {
|
||||
String clientSecret = null;
|
||||
String secretHash = "";
|
||||
String authMethods = McpConstant.OAuth.AUTH_METHOD_NONE;
|
||||
if (McpConstant.OAuth.CLIENT_TYPE_CONFIDENTIAL.equals(clientType)) {
|
||||
if (OAuthClientTypeEnum.CONFIDENTIAL.getValue().equals(clientType)) {
|
||||
clientSecret = randomToken();
|
||||
secretHash = PasswordUtil.encode(clientSecret);
|
||||
authMethods = McpConstant.OAuth.AUTH_METHOD_CLIENT_SECRET_BASIC + ' '
|
||||
@@ -315,7 +320,7 @@ public class OAuthMcpRuntimeServiceImpl implements OAuthMcpRuntimeService {
|
||||
client.setAuthorizationGrantTypes(String.join(" ", grants));
|
||||
client.setRedirectUris(String.join(" ", redirects));
|
||||
client.setScopes(String.join(" ", scopes));
|
||||
client.setRequirePkce((byte) (grants.contains(McpConstant.OAuth.GRANT_AUTHORIZATION_CODE) ? 1 : 0));
|
||||
client.setRequirePkce((byte) (grants.contains(OAuthGrantTypeEnum.AUTHORIZATION_CODE.getValue()) ? 1 : 0));
|
||||
client.setRequireConsent((byte) 1);
|
||||
client.setEnableFlag((byte) 0);
|
||||
oauthMcpMapper.insertClient(client);
|
||||
@@ -327,7 +332,7 @@ public class OAuthMcpRuntimeServiceImpl implements OAuthMcpRuntimeService {
|
||||
.grantTypes(grants)
|
||||
.redirectUris(redirects)
|
||||
.scope(String.join(" ", scopes))
|
||||
.tokenEndpointAuthMethod(McpConstant.OAuth.CLIENT_TYPE_CONFIDENTIAL.equals(clientType)
|
||||
.tokenEndpointAuthMethod(OAuthClientTypeEnum.CONFIDENTIAL.getValue().equals(clientType)
|
||||
? McpConstant.OAuth.AUTH_METHOD_CLIENT_SECRET_BASIC : McpConstant.OAuth.AUTH_METHOD_NONE)
|
||||
.clientSecret(clientSecret)
|
||||
.build();
|
||||
@@ -350,7 +355,7 @@ public class OAuthMcpRuntimeServiceImpl implements OAuthMcpRuntimeService {
|
||||
throw oauthError(BAD_REQUEST.value(), "unsupported_response_type", "only code is supported");
|
||||
}
|
||||
OAuthRegisteredClientRecord client = requireClient(params.get(McpConstant.Field.CLIENT_ID));
|
||||
requireGrant(client, McpConstant.OAuth.GRANT_AUTHORIZATION_CODE);
|
||||
requireGrant(client, OAuthGrantTypeEnum.AUTHORIZATION_CODE.getValue());
|
||||
String redirectUri = params.get(McpConstant.Field.REDIRECT_URI);
|
||||
if (!splitValues(client.getRedirectUris()).contains(redirectUri)) {
|
||||
throw oauthError(BAD_REQUEST.value(), "invalid_request", "redirect_uri mismatch");
|
||||
@@ -374,10 +379,10 @@ public class OAuthMcpRuntimeServiceImpl implements OAuthMcpRuntimeService {
|
||||
Long connectionId = longValue(params.get(McpConstant.Field.MCP_CONNECTION_ID));
|
||||
McpConnectionRecord connection = connectionId == null || connectionId == 0
|
||||
? oauthMcpMapper.selectActiveConnection(client.getClientId(), principalHeader.getPrincipalId(),
|
||||
tenantId, McpConstant.OAuth.GRANT_AUTHORIZATION_CODE)
|
||||
tenantId, OAuthGrantTypeEnum.AUTHORIZATION_CODE.getValue())
|
||||
: oauthMcpMapper.selectConnectionById(connectionId);
|
||||
validateConnection(connection, client.getClientId(), principalHeader.getPrincipalId(), tenantId,
|
||||
McpConstant.OAuth.GRANT_AUTHORIZATION_CODE);
|
||||
OAuthGrantTypeEnum.AUTHORIZATION_CODE.getValue());
|
||||
|
||||
String code = randomToken();
|
||||
OAuthAuthorizationRecord authorization = new OAuthAuthorizationRecord();
|
||||
@@ -389,7 +394,7 @@ public class OAuthMcpRuntimeServiceImpl implements OAuthMcpRuntimeService {
|
||||
PrincipalTypeEnum.USER.getValue()));
|
||||
authorization.setTenantId(tenantId);
|
||||
authorization.setMcpConnectionId(connection.getId());
|
||||
authorization.setAuthorizationGrantType(McpConstant.OAuth.GRANT_AUTHORIZATION_CODE);
|
||||
authorization.setAuthorizationGrantType(OAuthGrantTypeEnum.AUTHORIZATION_CODE.getValue());
|
||||
authorization.setAuthorizedScopes(String.join(" ", scopes));
|
||||
authorization.setStateHash(sha256(params.get("state")));
|
||||
authorization.setAuthorizationCodeHash(sha256(code));
|
||||
@@ -415,13 +420,13 @@ public class OAuthMcpRuntimeServiceImpl implements OAuthMcpRuntimeService {
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Map<String, Object> token(Map<String, String> form, String authorizationHeader) {
|
||||
String grantType = form.get(McpConstant.Field.GRANT_TYPE);
|
||||
if (McpConstant.OAuth.GRANT_AUTHORIZATION_CODE.equals(grantType)) {
|
||||
if (OAuthGrantTypeEnum.AUTHORIZATION_CODE.getValue().equals(grantType)) {
|
||||
return authorizationCodeToken(form, authorizationHeader);
|
||||
}
|
||||
if (McpConstant.OAuth.GRANT_CLIENT_CREDENTIALS.equals(grantType)) {
|
||||
if (OAuthGrantTypeEnum.CLIENT_CREDENTIALS.getValue().equals(grantType)) {
|
||||
return clientCredentialsToken(form, authorizationHeader);
|
||||
}
|
||||
if (McpConstant.OAuth.GRANT_REFRESH_TOKEN.equals(grantType)) {
|
||||
if (OAuthGrantTypeEnum.REFRESH_TOKEN.getValue().equals(grantType)) {
|
||||
return refreshToken(form, authorizationHeader);
|
||||
}
|
||||
throw oauthError(BAD_REQUEST.value(), "unsupported_grant_type", "unsupported grant_type");
|
||||
@@ -543,7 +548,7 @@ public class OAuthMcpRuntimeServiceImpl implements OAuthMcpRuntimeService {
|
||||
McpConnectionRecord connection = mcpConnectionBuilder.buildRecordByAddVO(entityVO);
|
||||
OAuthRegisteredClientRecord client = requireClient(connection.getClientId());
|
||||
String grantType = StringUtils.defaultIfBlank(connection.getGrantType(),
|
||||
McpConstant.OAuth.GRANT_AUTHORIZATION_CODE);
|
||||
OAuthGrantTypeEnum.AUTHORIZATION_CODE.getValue());
|
||||
requireGrant(client, grantType);
|
||||
|
||||
Long tenantId = Objects.requireNonNullElse(connection.getTenantId(), principalHeader.getTenantId());
|
||||
@@ -551,7 +556,7 @@ public class OAuthMcpRuntimeServiceImpl implements OAuthMcpRuntimeService {
|
||||
String principalType = StringUtils.defaultIfBlank(connection.getPrincipalType(),
|
||||
StringUtils.defaultIfBlank(principalHeader.getPrincipalType(), PrincipalTypeEnum.USER.getValue()));
|
||||
|
||||
if (McpConstant.OAuth.GRANT_CLIENT_CREDENTIALS.equals(grantType)) {
|
||||
if (OAuthGrantTypeEnum.CLIENT_CREDENTIALS.getValue().equals(grantType)) {
|
||||
if (!PrincipalTypeEnum.SERVICE_ACCOUNT.getValue().equals(principalType)) {
|
||||
throw oauthError(BAD_REQUEST.value(), "invalid_request",
|
||||
"client_credentials connection requires a service account principal");
|
||||
@@ -673,7 +678,7 @@ public class OAuthMcpRuntimeServiceImpl implements OAuthMcpRuntimeService {
|
||||
request.getMcpConnectionId(), request.getToolName(), scopes);
|
||||
|
||||
// Only HIGH-risk tools require platform confirmation; the rest pass straight through.
|
||||
if (!McpConstant.RiskLevel.HIGH.equals(tool.getRiskLevel())) {
|
||||
if (!McpRiskLevelEnum.HIGH.getValue().equals(tool.getRiskLevel())) {
|
||||
return authorizeDecision(McpConstant.Confirmation.DECISION_AUTHORIZED, "", "authorized",
|
||||
tool.getRiskLevel());
|
||||
}
|
||||
@@ -702,7 +707,7 @@ public class OAuthMcpRuntimeServiceImpl implements OAuthMcpRuntimeService {
|
||||
ticket.setArgumentDigest(argumentDigest);
|
||||
ticket.setIdempotencyKey(idempotencyKey);
|
||||
ticket.setRiskLevel(tool.getRiskLevel());
|
||||
ticket.setStatus(McpConstant.Confirmation.STATUS_PENDING);
|
||||
ticket.setStatus(McpConfirmationStatusEnum.PENDING.getValue());
|
||||
ticket.setExpireTime(LocalDateTime.now().plus(confirmTtl));
|
||||
oauthMcpMapper.insertConfirmation(ticket);
|
||||
return authorizeDecision(McpConstant.Confirmation.DECISION_CONFIRM_REQUIRED, issuedConfirmId,
|
||||
@@ -729,7 +734,7 @@ public class OAuthMcpRuntimeServiceImpl implements OAuthMcpRuntimeService {
|
||||
if (ticket == null) {
|
||||
return "confirmation does not exist";
|
||||
}
|
||||
if (!McpConstant.Confirmation.STATUS_PENDING.equals(ticket.getStatus())) {
|
||||
if (!McpConfirmationStatusEnum.PENDING.getValue().equals(ticket.getStatus())) {
|
||||
return "confirmation has already been used";
|
||||
}
|
||||
if (ticket.getExpireTime() == null || ticket.getExpireTime().isBefore(LocalDateTime.now())) {
|
||||
@@ -765,7 +770,7 @@ public class OAuthMcpRuntimeServiceImpl implements OAuthMcpRuntimeService {
|
||||
command.setConfirmId(StringUtils.defaultString(command.getConfirmId()));
|
||||
command.setIdempotencyKey(StringUtils.defaultString(command.getIdempotencyKey()));
|
||||
command.setArgumentDigest(StringUtils.defaultString(command.getArgumentDigest()));
|
||||
command.setStatus(StringUtils.defaultIfBlank(command.getStatus(), McpConstant.Audit.UNKNOWN));
|
||||
command.setStatus(StringUtils.defaultIfBlank(command.getStatus(), McpAuditStatusEnum.UNKNOWN.getValue()));
|
||||
command.setErrorCode(StringUtils.defaultString(command.getErrorCode()));
|
||||
command.setDurationMs(Objects.requireNonNullElse(command.getDurationMs(), 0L));
|
||||
command.setClientName(StringUtils.defaultString(command.getClientName()));
|
||||
@@ -799,13 +804,13 @@ public class OAuthMcpRuntimeServiceImpl implements OAuthMcpRuntimeService {
|
||||
private Map<String, Object> clientCredentialsToken(Map<String, String> form, String authorizationHeader) {
|
||||
OAuthRegisteredClientRecord client = requireClient(resolveClientId(form, authorizationHeader));
|
||||
authenticateClient(client, form, authorizationHeader, true);
|
||||
requireGrant(client, McpConstant.OAuth.GRANT_CLIENT_CREDENTIALS);
|
||||
requireGrant(client, OAuthGrantTypeEnum.CLIENT_CREDENTIALS.getValue());
|
||||
validateServiceAccountClient(client.getServiceAccountPrincipalId(), client.getTenantId());
|
||||
McpConnectionRecord connection = oauthMcpMapper.selectActiveConnection(client.getClientId(),
|
||||
client.getServiceAccountPrincipalId(), client.getTenantId(),
|
||||
McpConstant.OAuth.GRANT_CLIENT_CREDENTIALS);
|
||||
OAuthGrantTypeEnum.CLIENT_CREDENTIALS.getValue());
|
||||
validateConnection(connection, client.getClientId(), client.getServiceAccountPrincipalId(),
|
||||
client.getTenantId(), McpConstant.OAuth.GRANT_CLIENT_CREDENTIALS);
|
||||
client.getTenantId(), OAuthGrantTypeEnum.CLIENT_CREDENTIALS.getValue());
|
||||
Set<String> scopes = requestedScopes(form.get(McpConstant.Field.SCOPE), client);
|
||||
|
||||
OAuthAuthorizationRecord authorization = new OAuthAuthorizationRecord();
|
||||
@@ -816,7 +821,7 @@ public class OAuthMcpRuntimeServiceImpl implements OAuthMcpRuntimeService {
|
||||
authorization.setPrincipalType(PrincipalTypeEnum.SERVICE_ACCOUNT.getValue());
|
||||
authorization.setTenantId(client.getTenantId());
|
||||
authorization.setMcpConnectionId(connection.getId());
|
||||
authorization.setAuthorizationGrantType(McpConstant.OAuth.GRANT_CLIENT_CREDENTIALS);
|
||||
authorization.setAuthorizationGrantType(OAuthGrantTypeEnum.CLIENT_CREDENTIALS.getValue());
|
||||
authorization.setAuthorizedScopes(String.join(" ", scopes));
|
||||
authorization.setStateHash("");
|
||||
authorization.setAuthorizationCodeHash("");
|
||||
@@ -976,7 +981,7 @@ public class OAuthMcpRuntimeServiceImpl implements OAuthMcpRuntimeService {
|
||||
|
||||
private void authenticateClient(OAuthRegisteredClientRecord client, Map<String, String> form,
|
||||
String authorizationHeader, boolean confidentialRequired) {
|
||||
if (McpConstant.OAuth.CLIENT_TYPE_PUBLIC.equals(client.getClientType())) {
|
||||
if (OAuthClientTypeEnum.PUBLIC.getValue().equals(client.getClientType())) {
|
||||
if (confidentialRequired
|
||||
|| !splitValues(client.getClientAuthMethods()).contains(McpConstant.OAuth.AUTH_METHOD_NONE)) {
|
||||
throw oauthError(UNAUTHORIZED.value(), "invalid_client", "confidential client is required");
|
||||
|
||||
+16
@@ -19,8 +19,14 @@ package io.github.pnoker.common.auth.entity.builder;
|
||||
|
||||
import io.github.pnoker.common.auth.entity.oauth.McpAuditCommand;
|
||||
import io.github.pnoker.common.auth.entity.vo.McpAuditVO;
|
||||
import io.github.pnoker.common.enums.McpAuditStatusEnum;
|
||||
import io.github.pnoker.common.enums.McpRiskLevelEnum;
|
||||
import io.github.pnoker.common.enums.PrincipalTypeEnum;
|
||||
import io.github.pnoker.common.utils.MapStructUtil;
|
||||
import org.mapstruct.AfterMapping;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.MappingTarget;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -40,8 +46,18 @@ public interface McpAuditBuilder {
|
||||
* @param entityRecord EntityRecord
|
||||
* @return EntityVO
|
||||
*/
|
||||
@Mapping(target = "principalType", ignore = true)
|
||||
@Mapping(target = "riskLevel", ignore = true)
|
||||
@Mapping(target = "status", ignore = true)
|
||||
McpAuditVO buildVOByRecord(McpAuditCommand entityRecord);
|
||||
|
||||
@AfterMapping
|
||||
default void afterProcess(McpAuditCommand entityRecord, @MappingTarget McpAuditVO entityVO) {
|
||||
entityVO.setPrincipalType(PrincipalTypeEnum.ofValue(entityRecord.getPrincipalType()));
|
||||
entityVO.setRiskLevel(McpRiskLevelEnum.ofValue(entityRecord.getRiskLevel()));
|
||||
entityVO.setStatus(McpAuditStatusEnum.ofValue(entityRecord.getStatus()));
|
||||
}
|
||||
|
||||
/**
|
||||
* RecordList to VOList
|
||||
*
|
||||
|
||||
+13
@@ -20,8 +20,13 @@ package io.github.pnoker.common.auth.entity.builder;
|
||||
import io.github.pnoker.common.auth.entity.oauth.McpConnectionRecord;
|
||||
import io.github.pnoker.common.auth.entity.vo.McpConnectionAddVO;
|
||||
import io.github.pnoker.common.auth.entity.vo.McpConnectionVO;
|
||||
import io.github.pnoker.common.enums.OAuthGrantTypeEnum;
|
||||
import io.github.pnoker.common.enums.PrincipalTypeEnum;
|
||||
import io.github.pnoker.common.utils.MapStructUtil;
|
||||
import org.mapstruct.AfterMapping;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.MappingTarget;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -41,8 +46,16 @@ public interface McpConnectionBuilder {
|
||||
* @param entityRecord EntityRecord
|
||||
* @return EntityVO
|
||||
*/
|
||||
@Mapping(target = "principalType", ignore = true)
|
||||
@Mapping(target = "grantType", ignore = true)
|
||||
McpConnectionVO buildVOByRecord(McpConnectionRecord entityRecord);
|
||||
|
||||
@AfterMapping
|
||||
default void afterProcess(McpConnectionRecord entityRecord, @MappingTarget McpConnectionVO entityVO) {
|
||||
entityVO.setPrincipalType(PrincipalTypeEnum.ofValue(entityRecord.getPrincipalType()));
|
||||
entityVO.setGrantType(OAuthGrantTypeEnum.ofValue(entityRecord.getGrantType()));
|
||||
}
|
||||
|
||||
/**
|
||||
* RecordList to VOList
|
||||
*
|
||||
|
||||
+10
@@ -19,8 +19,12 @@ package io.github.pnoker.common.auth.entity.builder;
|
||||
|
||||
import io.github.pnoker.common.auth.entity.oauth.McpToolRecord;
|
||||
import io.github.pnoker.common.auth.entity.vo.McpToolVO;
|
||||
import io.github.pnoker.common.enums.McpRiskLevelEnum;
|
||||
import io.github.pnoker.common.utils.MapStructUtil;
|
||||
import org.mapstruct.AfterMapping;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.MappingTarget;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -40,8 +44,14 @@ public interface McpToolBuilder {
|
||||
* @param entityRecord EntityRecord
|
||||
* @return EntityVO
|
||||
*/
|
||||
@Mapping(target = "riskLevel", ignore = true)
|
||||
McpToolVO buildVOByRecord(McpToolRecord entityRecord);
|
||||
|
||||
@AfterMapping
|
||||
default void afterProcess(McpToolRecord entityRecord, @MappingTarget McpToolVO entityVO) {
|
||||
entityVO.setRiskLevel(McpRiskLevelEnum.ofValue(entityRecord.getRiskLevel()));
|
||||
}
|
||||
|
||||
/**
|
||||
* RecordList to VOList
|
||||
*
|
||||
|
||||
+10
@@ -19,8 +19,12 @@ package io.github.pnoker.common.auth.entity.builder;
|
||||
|
||||
import io.github.pnoker.common.auth.entity.oauth.OAuthRegisteredClientRecord;
|
||||
import io.github.pnoker.common.auth.entity.vo.OAuthClientVO;
|
||||
import io.github.pnoker.common.enums.OAuthClientTypeEnum;
|
||||
import io.github.pnoker.common.utils.MapStructUtil;
|
||||
import org.mapstruct.AfterMapping;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.MappingTarget;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -40,8 +44,14 @@ public interface OAuthClientBuilder {
|
||||
* @param entityRecord EntityRecord
|
||||
* @return EntityVO
|
||||
*/
|
||||
@Mapping(target = "clientType", ignore = true)
|
||||
OAuthClientVO buildVOByRecord(OAuthRegisteredClientRecord entityRecord);
|
||||
|
||||
@AfterMapping
|
||||
default void afterProcess(OAuthRegisteredClientRecord entityRecord, @MappingTarget OAuthClientVO entityVO) {
|
||||
entityVO.setClientType(OAuthClientTypeEnum.ofValue(entityRecord.getClientType()));
|
||||
}
|
||||
|
||||
/**
|
||||
* RecordList to VOList
|
||||
*
|
||||
|
||||
+6
-3
@@ -19,6 +19,9 @@ package io.github.pnoker.common.auth.entity.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import io.github.pnoker.common.entity.base.BaseVO;
|
||||
import io.github.pnoker.common.enums.McpAuditStatusEnum;
|
||||
import io.github.pnoker.common.enums.McpRiskLevelEnum;
|
||||
import io.github.pnoker.common.enums.PrincipalTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
@@ -52,7 +55,7 @@ public class McpAuditVO extends BaseVO {
|
||||
private Long principalId;
|
||||
|
||||
@Schema(description = "Principal type: USER or SERVICE_ACCOUNT.", example = "USER")
|
||||
private String principalType;
|
||||
private PrincipalTypeEnum principalType;
|
||||
|
||||
@Schema(description = "OAuth client identifier.", example = "mcp-abcd1234")
|
||||
private String clientId;
|
||||
@@ -70,7 +73,7 @@ public class McpAuditVO extends BaseVO {
|
||||
private String permissionCode;
|
||||
|
||||
@Schema(description = "Tool risk level: LOW, MEDIUM or HIGH.", example = "LOW")
|
||||
private String riskLevel;
|
||||
private McpRiskLevelEnum riskLevel;
|
||||
|
||||
@Schema(description = "Confirmation id for high-risk calls.", example = "cfm-abcd1234")
|
||||
private String confirmId;
|
||||
@@ -82,7 +85,7 @@ public class McpAuditVO extends BaseVO {
|
||||
private String argumentDigest;
|
||||
|
||||
@Schema(description = "Invocation outcome: SUCCESS, DENIED, POLICY_DENIED, ERROR or UNKNOWN.", example = "SUCCESS")
|
||||
private String status;
|
||||
private McpAuditStatusEnum status;
|
||||
|
||||
@Schema(description = "Error code when the call failed.", example = "policy_denied")
|
||||
private String errorCode;
|
||||
|
||||
+4
-2
@@ -19,6 +19,8 @@ package io.github.pnoker.common.auth.entity.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import io.github.pnoker.common.entity.base.BaseVO;
|
||||
import io.github.pnoker.common.enums.OAuthGrantTypeEnum;
|
||||
import io.github.pnoker.common.enums.PrincipalTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
@@ -54,13 +56,13 @@ public class McpConnectionVO extends BaseVO {
|
||||
private Long principalId;
|
||||
|
||||
@Schema(description = "Principal type: USER or SERVICE_ACCOUNT.", example = "USER")
|
||||
private String principalType;
|
||||
private PrincipalTypeEnum principalType;
|
||||
|
||||
@Schema(description = "Tenant the connection belongs to.", example = "1")
|
||||
private Long tenantId;
|
||||
|
||||
@Schema(description = "Authorization grant type.", example = "authorization_code")
|
||||
private String grantType;
|
||||
private OAuthGrantTypeEnum grantType;
|
||||
|
||||
@Schema(description = "Enable flag: 0 enabled, 1 disabled.", example = "0")
|
||||
private Byte enableFlag;
|
||||
|
||||
+2
-1
@@ -19,6 +19,7 @@ package io.github.pnoker.common.auth.entity.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import io.github.pnoker.common.entity.base.BaseVO;
|
||||
import io.github.pnoker.common.enums.McpRiskLevelEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
@@ -73,7 +74,7 @@ public class McpToolVO extends BaseVO {
|
||||
private String schemaHash;
|
||||
|
||||
@Schema(description = "Tool risk level: LOW, MEDIUM or HIGH.", example = "LOW")
|
||||
private String riskLevel;
|
||||
private McpRiskLevelEnum riskLevel;
|
||||
|
||||
@Schema(description = "Read-only hint: 1 yes, 0 no.", example = "1")
|
||||
private Byte readOnlyHint;
|
||||
|
||||
+2
-1
@@ -19,6 +19,7 @@ package io.github.pnoker.common.auth.entity.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import io.github.pnoker.common.entity.base.BaseVO;
|
||||
import io.github.pnoker.common.enums.OAuthClientTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
@@ -51,7 +52,7 @@ public class OAuthClientVO extends BaseVO {
|
||||
private String clientName;
|
||||
|
||||
@Schema(description = "Client type: PUBLIC or CONFIDENTIAL.", example = "CONFIDENTIAL")
|
||||
private String clientType;
|
||||
private OAuthClientTypeEnum clientType;
|
||||
|
||||
@Schema(description = "Principal that owns the client.", example = "1024")
|
||||
private Long ownerPrincipalId;
|
||||
|
||||
@@ -74,6 +74,12 @@
|
||||
<artifactId>mybatis-plus-annotation</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Jackson Annotation: @JsonValue keeps enum JSON on its db/wire value (e.g. grantType lowercase) -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2016-present the IoT DC3 original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.pnoker.common.enums;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.EnumValue;
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* MCP tool-call audit outcome.
|
||||
*
|
||||
* @author pnoker
|
||||
* @version 2026.6.12
|
||||
* @since 2026.6.12
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum McpAuditStatusEnum {
|
||||
|
||||
/**
|
||||
* Tool call succeeded.
|
||||
*/
|
||||
SUCCESS("SUCCESS", "success", "Tool call succeeded"),
|
||||
|
||||
/**
|
||||
* Caller denied by authorization.
|
||||
*/
|
||||
DENIED("DENIED", "denied", "Denied by authorization"),
|
||||
|
||||
/**
|
||||
* Denied by risk/confirmation policy.
|
||||
*/
|
||||
POLICY_DENIED("POLICY_DENIED", "policy_denied", "Denied by policy"),
|
||||
|
||||
/**
|
||||
* Tool call errored.
|
||||
*/
|
||||
ERROR("ERROR", "error", "Tool call errored"),
|
||||
|
||||
/**
|
||||
* Outcome unknown.
|
||||
*/
|
||||
UNKNOWN("UNKNOWN", "unknown", "Unknown outcome"),
|
||||
;
|
||||
|
||||
/**
|
||||
* Database / wire value.
|
||||
*/
|
||||
@EnumValue
|
||||
@JsonValue
|
||||
private final String value;
|
||||
|
||||
/**
|
||||
* Code string.
|
||||
*/
|
||||
private final String code;
|
||||
|
||||
/**
|
||||
* Human-readable description.
|
||||
*/
|
||||
private final String remark;
|
||||
|
||||
public static McpAuditStatusEnum ofValue(String value) {
|
||||
Optional<McpAuditStatusEnum> any = Arrays.stream(values())
|
||||
.filter(type -> type.getValue().equals(value))
|
||||
.findFirst();
|
||||
return any.orElse(null);
|
||||
}
|
||||
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2016-present the IoT DC3 original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.pnoker.common.enums;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.EnumValue;
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* MCP high-risk tool confirmation ticket status (persisted lifecycle).
|
||||
*
|
||||
* @author pnoker
|
||||
* @version 2026.6.12
|
||||
* @since 2026.6.12
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum McpConfirmationStatusEnum {
|
||||
|
||||
/**
|
||||
* Ticket issued, awaiting consumption.
|
||||
*/
|
||||
PENDING("PENDING", "pending", "Pending consumption"),
|
||||
|
||||
/**
|
||||
* Ticket consumed (one-time use spent).
|
||||
*/
|
||||
CONSUMED("CONSUMED", "consumed", "Consumed"),
|
||||
;
|
||||
|
||||
/**
|
||||
* Database / wire value.
|
||||
*/
|
||||
@EnumValue
|
||||
@JsonValue
|
||||
private final String value;
|
||||
|
||||
/**
|
||||
* Code string.
|
||||
*/
|
||||
private final String code;
|
||||
|
||||
/**
|
||||
* Human-readable description.
|
||||
*/
|
||||
private final String remark;
|
||||
|
||||
public static McpConfirmationStatusEnum ofValue(String value) {
|
||||
Optional<McpConfirmationStatusEnum> any = Arrays.stream(values())
|
||||
.filter(type -> type.getValue().equals(value))
|
||||
.findFirst();
|
||||
return any.orElse(null);
|
||||
}
|
||||
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2016-present the IoT DC3 original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.pnoker.common.enums;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.EnumValue;
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* MCP tool risk level (governs confirmation and visibility policy).
|
||||
*
|
||||
* @author pnoker
|
||||
* @version 2026.6.12
|
||||
* @since 2026.6.12
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum McpRiskLevelEnum {
|
||||
|
||||
/**
|
||||
* Low risk: read-only, always visible.
|
||||
*/
|
||||
LOW("LOW", "low", "Low risk"),
|
||||
|
||||
/**
|
||||
* Medium risk: hidden unless explicitly enabled.
|
||||
*/
|
||||
MEDIUM("MEDIUM", "medium", "Medium risk"),
|
||||
|
||||
/**
|
||||
* High risk: hidden by default, requires confirmation.
|
||||
*/
|
||||
HIGH("HIGH", "high", "High risk"),
|
||||
;
|
||||
|
||||
/**
|
||||
* Database / wire value.
|
||||
*/
|
||||
@EnumValue
|
||||
@JsonValue
|
||||
private final String value;
|
||||
|
||||
/**
|
||||
* Code string.
|
||||
*/
|
||||
private final String code;
|
||||
|
||||
/**
|
||||
* Human-readable description.
|
||||
*/
|
||||
private final String remark;
|
||||
|
||||
public static McpRiskLevelEnum ofValue(String value) {
|
||||
Optional<McpRiskLevelEnum> any = Arrays.stream(values())
|
||||
.filter(type -> type.getValue().equals(value))
|
||||
.findFirst();
|
||||
return any.orElse(null);
|
||||
}
|
||||
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2016-present the IoT DC3 original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.pnoker.common.enums;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.EnumValue;
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* OAuth registered client type.
|
||||
*
|
||||
* @author pnoker
|
||||
* @version 2026.6.12
|
||||
* @since 2026.6.12
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum OAuthClientTypeEnum {
|
||||
|
||||
/**
|
||||
* Public client (no secret, PKCE required).
|
||||
*/
|
||||
PUBLIC("PUBLIC", "public", "Public client"),
|
||||
|
||||
/**
|
||||
* Confidential client (holds a secret).
|
||||
*/
|
||||
CONFIDENTIAL("CONFIDENTIAL", "confidential", "Confidential client"),
|
||||
;
|
||||
|
||||
/**
|
||||
* Database / wire value.
|
||||
*/
|
||||
@EnumValue
|
||||
@JsonValue
|
||||
private final String value;
|
||||
|
||||
/**
|
||||
* Code string.
|
||||
*/
|
||||
private final String code;
|
||||
|
||||
/**
|
||||
* Human-readable description.
|
||||
*/
|
||||
private final String remark;
|
||||
|
||||
public static OAuthClientTypeEnum ofValue(String value) {
|
||||
Optional<OAuthClientTypeEnum> any = Arrays.stream(values())
|
||||
.filter(type -> type.getValue().equals(value))
|
||||
.findFirst();
|
||||
return any.orElse(null);
|
||||
}
|
||||
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2016-present the IoT DC3 original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.pnoker.common.enums;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.EnumValue;
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* OAuth 2.1 authorization grant type.
|
||||
*
|
||||
* @author pnoker
|
||||
* @version 2026.6.12
|
||||
* @since 2026.6.12
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum OAuthGrantTypeEnum {
|
||||
|
||||
/**
|
||||
* Authorization code (with PKCE) grant.
|
||||
*/
|
||||
AUTHORIZATION_CODE("authorization_code", "authorization_code", "Authorization code grant"),
|
||||
|
||||
/**
|
||||
* Client credentials (machine-to-machine) grant.
|
||||
*/
|
||||
CLIENT_CREDENTIALS("client_credentials", "client_credentials", "Client credentials grant"),
|
||||
|
||||
/**
|
||||
* Refresh token grant.
|
||||
*/
|
||||
REFRESH_TOKEN("refresh_token", "refresh_token", "Refresh token grant"),
|
||||
;
|
||||
|
||||
/**
|
||||
* Database / wire value (lowercase, per OAuth spec).
|
||||
*/
|
||||
@EnumValue
|
||||
@JsonValue
|
||||
private final String value;
|
||||
|
||||
/**
|
||||
* Code string.
|
||||
*/
|
||||
private final String code;
|
||||
|
||||
/**
|
||||
* Human-readable description.
|
||||
*/
|
||||
private final String remark;
|
||||
|
||||
public static OAuthGrantTypeEnum ofValue(String value) {
|
||||
Optional<OAuthGrantTypeEnum> any = Arrays.stream(values())
|
||||
.filter(type -> type.getValue().equals(value))
|
||||
.findFirst();
|
||||
return any.orElse(null);
|
||||
}
|
||||
|
||||
}
|
||||
+5
@@ -109,12 +109,17 @@ class IndexedEnumContractTest {
|
||||
EventLevelEnum.class,
|
||||
EventTypeFlagEnum.class,
|
||||
ExpireTypeEnum.class,
|
||||
McpAuditStatusEnum.class,
|
||||
McpConfirmationStatusEnum.class,
|
||||
McpRiskLevelEnum.class,
|
||||
MenuLevelEnum.class,
|
||||
MenuTypeFlagEnum.class,
|
||||
MetadataOperateTypeEnum.class,
|
||||
MetadataTypeEnum.class,
|
||||
NotifyChannelTypeEnum.class,
|
||||
NotifyHistoryStatusEnum.class,
|
||||
OAuthClientTypeEnum.class,
|
||||
OAuthGrantTypeEnum.class,
|
||||
ParamDirectionTypeEnum.class,
|
||||
PointCommandSourceEnum.class,
|
||||
PointCommandStatusEnum.class,
|
||||
|
||||
Reference in New Issue
Block a user