Skip to content

Commit

Permalink
fix: Oauth接口错误响应未标准化 TencentBlueKing#2876
Browse files Browse the repository at this point in the history
  • Loading branch information
yaoxuwan authored Dec 27, 2024
1 parent 8af4141 commit 56881ba
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -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()
Original file line number Diff line number Diff line change
@@ -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
)
}
Original file line number Diff line number Diff line change
@@ -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"),
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand All @@ -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),
Expand All @@ -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)
Expand Down Expand Up @@ -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 }
Expand All @@ -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()) {
Expand All @@ -366,7 +369,7 @@ class OauthAuthorizationServiceImpl(
else -> false
}
if (!pass) {
throw ErrorCodeException(CommonMessageCode.PARAMETER_INVALID, "code_verifier")
throw OauthException(OauthErrorType.INVALID_REQUEST, "code_verifier")
}
}

Expand Down

0 comments on commit 56881ba

Please sign in to comment.