diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/exception/OauthException.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/exception/OauthException.kt new file mode 100644 index 0000000000..12095b09cc --- /dev/null +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/exception/OauthException.kt @@ -0,0 +1,37 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.bkrepo.auth.exception + +import com.tencent.bkrepo.auth.pojo.enums.OauthErrorType +import com.tencent.bkrepo.common.api.constant.HttpStatus + +data class OauthException( + val error: OauthErrorType, + val errorDescription: String = "", + val status: HttpStatus = HttpStatus.BAD_REQUEST +): RuntimeException() diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/exception/OauthExceptionHandler.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/exception/OauthExceptionHandler.kt new file mode 100644 index 0000000000..377a275c89 --- /dev/null +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/exception/OauthExceptionHandler.kt @@ -0,0 +1,64 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.bkrepo.auth.exception + +import com.fasterxml.jackson.annotation.JsonProperty +import com.tencent.bkrepo.common.api.constant.MediaTypes +import com.tencent.bkrepo.common.api.util.toJsonString +import com.tencent.bkrepo.common.service.exception.AbstractExceptionHandler +import com.tencent.bkrepo.common.service.log.LoggerHolder +import com.tencent.bkrepo.common.service.util.HttpContextHolder +import org.springframework.core.Ordered +import org.springframework.core.annotation.Order +import org.springframework.web.bind.annotation.ControllerAdvice +import org.springframework.web.bind.annotation.ExceptionHandler + +@Order(Ordered.HIGHEST_PRECEDENCE + 1) +@ControllerAdvice +class OauthExceptionHandler : AbstractExceptionHandler() { + + @ExceptionHandler(OauthException::class) + fun handleOauthException(exception: OauthException) { + LoggerHolder.logException(exception, "[${exception.error}]${exception.errorDescription}", false) + HttpContextHolder.getResponse().status = exception.status.value + HttpContextHolder.getResponse().contentType = MediaTypes.APPLICATION_JSON_WITHOUT_CHARSET + HttpContextHolder.getResponse().writer.println( + OauthError( + exception.error.value, + exception.errorDescription + ).toJsonString() + ) + } + + data class OauthError( + @JsonProperty("error") + val error: String, + @JsonProperty("error_description") + val errorDescription: String + ) +} diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/pojo/enums/OauthErrorType.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/pojo/enums/OauthErrorType.kt new file mode 100644 index 0000000000..07aaf1274a --- /dev/null +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/pojo/enums/OauthErrorType.kt @@ -0,0 +1,37 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.bkrepo.auth.pojo.enums + +enum class OauthErrorType(val value: String) { + INVALID_REQUEST("invalid_request"), + INVALID_CLIENT("invalid_client"), + INVALID_GRANT("invalid_grant"), + UNAUTHORIZED_CLIENT("unauthorized_client"), + UNSUPPORTED_GRANT_TYPE("unsupported_grant_type"), + INVALID_SCOPE("invalid_scope"), +} diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/oauth/OauthAuthorizationServiceImpl.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/oauth/OauthAuthorizationServiceImpl.kt index f51ce3bb52..dbcaf5f48b 100644 --- a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/oauth/OauthAuthorizationServiceImpl.kt +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/oauth/OauthAuthorizationServiceImpl.kt @@ -31,9 +31,13 @@ import cn.hutool.core.codec.Base64Decoder import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.tencent.bkrepo.auth.config.OauthProperties import com.tencent.bkrepo.auth.dao.AccountDao +import com.tencent.bkrepo.auth.dao.repository.OauthTokenRepository +import com.tencent.bkrepo.auth.exception.OauthException import com.tencent.bkrepo.auth.message.AuthMessageCode import com.tencent.bkrepo.auth.model.TAccount import com.tencent.bkrepo.auth.model.TOauthToken +import com.tencent.bkrepo.auth.pojo.enums.OauthErrorType +import com.tencent.bkrepo.auth.pojo.enums.ResourceType import com.tencent.bkrepo.auth.pojo.oauth.AuthorizationGrantType import com.tencent.bkrepo.auth.pojo.oauth.AuthorizeRequest import com.tencent.bkrepo.auth.pojo.oauth.AuthorizedResult @@ -42,8 +46,6 @@ import com.tencent.bkrepo.auth.pojo.oauth.IdToken import com.tencent.bkrepo.auth.pojo.oauth.JsonWebKey import com.tencent.bkrepo.auth.pojo.oauth.JsonWebKeySet import com.tencent.bkrepo.auth.pojo.oauth.OauthToken -import com.tencent.bkrepo.auth.dao.repository.OauthTokenRepository -import com.tencent.bkrepo.auth.pojo.enums.ResourceType import com.tencent.bkrepo.auth.pojo.oauth.OidcConfiguration import com.tencent.bkrepo.auth.pojo.oauth.UserInfo import com.tencent.bkrepo.auth.service.OauthAuthorizationService @@ -54,7 +56,6 @@ import com.tencent.bkrepo.common.api.constant.HttpHeaders import com.tencent.bkrepo.common.api.constant.MediaTypes import com.tencent.bkrepo.common.api.constant.StringPool import com.tencent.bkrepo.common.api.exception.ErrorCodeException -import com.tencent.bkrepo.common.api.message.CommonMessageCode import com.tencent.bkrepo.common.api.util.JsonUtils import com.tencent.bkrepo.common.api.util.Preconditions import com.tencent.bkrepo.common.api.util.toJsonString @@ -88,8 +89,8 @@ class OauthAuthorizationServiceImpl( override fun authorized(authorizeRequest: AuthorizeRequest): AuthorizedResult { with(authorizeRequest) { val userId = SecurityUtils.getUserId() - val client = - accountDao.findById(clientId) ?: throw ErrorCodeException(AuthMessageCode.AUTH_CLIENT_NOT_EXIST) + val client = accountDao.findById(clientId) + ?: throw OauthException(OauthErrorType.INVALID_CLIENT, "client[$clientId] not found]") val code = OauthUtils.generateCode() val userIdKey = "$clientId:$code:userId" @@ -136,7 +137,7 @@ class OauthAuthorizationServiceImpl( } else if (generateTokenRequest.grantType.equals(AuthorizationGrantType.CLIENT_CREDENTIALS.value(), true)) { createClientCredentialsToken(clientId, clientSecret) } else { - throw ErrorCodeException(CommonMessageCode.PARAMETER_INVALID, "grant_type") + throw OauthException(OauthErrorType.UNSUPPORTED_GRANT_TYPE, generateTokenRequest.grantType) } val token = transfer(tOauthToken) @@ -148,9 +149,9 @@ class OauthAuthorizationServiceImpl( Preconditions.checkNotNull(clientId, this::clientId.name) Preconditions.checkNotNull(refreshToken, this::refreshToken.name) var token = oauthTokenRepository.findFirstByAccountIdAndRefreshToken(clientId!!, refreshToken!!) - ?: throw ErrorCodeException(CommonMessageCode.RESOURCE_NOT_FOUND, refreshToken!!) + ?: throw OauthException(OauthErrorType.INVALID_GRANT, "refresh token[$refreshToken] not found") val client = accountDao.findById(clientId!!) - ?: throw ErrorCodeException(CommonMessageCode.RESOURCE_NOT_FOUND, clientId!!) + ?: throw OauthException(OauthErrorType.INVALID_GRANT, "client[$clientId] not found") token = buildOauthToken( userId = token.userId, nonce = OauthUtils.generateRandomString(10), @@ -171,7 +172,8 @@ class OauthAuthorizationServiceImpl( val userIdKey = "$clientId:$code:userId" val openIdKey = "$clientId:$code:openId" val nonceKey = "$clientId:$code:nonce" - val userId = redisOperation.get(userIdKey) ?: throw ErrorCodeException(AuthMessageCode.AUTH_CODE_CHECK_FAILED) + val userId = redisOperation.get(userIdKey) + ?: throw OauthException(OauthErrorType.INVALID_REQUEST, "auth code check failed") val openId = redisOperation.get(openIdKey).toBoolean() val nonce = redisOperation.get(nonceKey) val client = checkClientSecret(clientId, clientSecret, code, generateTokenRequest.codeVerifier) @@ -331,10 +333,11 @@ class OauthAuthorizationServiceImpl( codeVerifier: String? ): TAccount { if (clientSecret.isNullOrBlank() && codeVerifier.isNullOrBlank()) { - throw ErrorCodeException(CommonMessageCode.PARAMETER_MISSING, "clientSecret or codeVerifier") + throw OauthException(OauthErrorType.INVALID_REQUEST, "need clientSecret or codeVerifier") } - val client = accountDao.findById(clientId) ?: throw ErrorCodeException(AuthMessageCode.AUTH_CLIENT_NOT_EXIST) + val client = accountDao.findById(clientId) + ?: throw OauthException(OauthErrorType.INVALID_CLIENT, "client[$clientId] not found") val credential = if (clientSecret.isNullOrBlank()) { client.credentials.find { it.authorizationGrantType == AuthorizationGrantType.AUTHORIZATION_CODE } @@ -345,7 +348,7 @@ class OauthAuthorizationServiceImpl( } } if (credential == null) { - throw ErrorCodeException(AuthMessageCode.AUTH_SECRET_CHECK_FAILED) + throw OauthException(OauthErrorType.UNAUTHORIZED_CLIENT, "auth secret check failed") } if (!code.isNullOrBlank()) { @@ -366,7 +369,7 @@ class OauthAuthorizationServiceImpl( else -> false } if (!pass) { - throw ErrorCodeException(CommonMessageCode.PARAMETER_INVALID, "code_verifier") + throw OauthException(OauthErrorType.INVALID_REQUEST, "code_verifier") } }