Skip to content

Commit

Permalink
fix: ApiKey Scope NPE and Content Delivery issues (#2029)
Browse files Browse the repository at this point in the history
  • Loading branch information
JanCizmar authored Dec 18, 2023
1 parent eac6e36 commit 8fc8bc9
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class ApiKeyModelAssembler : RepresentationModelAssemblerSupport<ApiKey, ApiKeyM
userFullName = entity.userAccount.name,
projectId = entity.project.id,
projectName = entity.project.name,
scopes = entity.scopesEnum.map { it.value }.toSet(),
scopes = entity.scopesEnum.mapNotNull { it?.value }.toSet(),
expiresAt = entity.expiresAt?.time,
lastUsedAt = entity.lastUsedAt?.time
)
Expand Down
2 changes: 1 addition & 1 deletion backend/data/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ dependencies {
/**
* AZURE
*/
implementation 'com.azure:azure-storage-blob:12.12.0'
implementation 'com.azure:azure-storage-blob:12.25.1'
implementation 'com.azure:azure-identity:1.10.4'
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ package io.tolgee.component.fileStorage

import com.azure.core.util.BinaryData
import com.azure.storage.blob.BlobClient
import com.azure.storage.blob.BlobContainerAsyncClient
import com.azure.storage.blob.BlobContainerClient
import io.tolgee.exceptions.FileStoreException
import software.amazon.awssdk.services.s3.model.NoSuchKeyException
import java.lang.reflect.Field

open class AzureBlobFileStorage(
private val client: BlobContainerClient,
Expand Down Expand Up @@ -49,6 +51,21 @@ open class AzureBlobFileStorage(
}
}

private fun getBlobClient(storageFilePath: String): BlobClient =
client.getBlobClient(storageFilePath)
private fun getBlobClient(storageFilePath: String): BlobClient {
val clientValue = extractClientFromBlobContainerClient()
return TolgeeBlobClient(clientValue.getBlobAsyncClient(storageFilePath))
}

/**
* The Azure blob client uses deprecated method:
* reactor.core.publisher.Mono reactor.core.publisher.Mono.subscriberContext
* It was removed in mono 3.5 (minor version ugh!)
*
* This is a workaround for this issue.
*/
private fun extractClientFromBlobContainerClient(): BlobContainerAsyncClient {
val clientField: Field? = BlobContainerClient::class.java.getDeclaredField("client")
clientField?.isAccessible = true
return clientField?.get(client) as BlobContainerAsyncClient
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package io.tolgee.component.fileStorage

import com.azure.core.annotation.ReturnType
import com.azure.core.annotation.ServiceMethod
import com.azure.core.http.rest.Response
import com.azure.core.util.Context
import com.azure.core.util.FluxUtil
import com.azure.core.util.logging.ClientLogger
import com.azure.storage.blob.BlobAsyncClient
import com.azure.storage.blob.BlobClient
import com.azure.storage.blob.models.BlockBlobItem
import com.azure.storage.blob.options.BlobParallelUploadOptions
import com.azure.storage.blob.options.BlobUploadFromFileOptions
import com.azure.storage.common.implementation.StorageImplUtils
import reactor.core.publisher.Mono
import java.io.UncheckedIOException
import java.time.Duration
import java.util.*

/**
* The Azure blob client uses deprecated method:
* reactor.core.publisher.Mono reactor.core.publisher.Mono.subscriberContext
* It was removed in mono 3.5 (minor version ugh..!)
*
* This class is a workaround for this issue.
*/
class TolgeeBlobClient(val client: BlobAsyncClient) : BlobClient(client) {
private val logger = ClientLogger(BlobClient::class.java)

@ServiceMethod(returns = ReturnType.SINGLE)
override fun uploadWithResponse(
options: BlobParallelUploadOptions,
timeout: Duration?,
context: Context?
): Response<BlockBlobItem> {
Objects.requireNonNull(options)
val upload: Mono<Response<BlockBlobItem>> = client.uploadWithResponse(options)
.contextWrite(FluxUtil.toReactorContext(context))

try {
return StorageImplUtils.blockWithOptionalTimeout(upload, timeout)
} catch (e: UncheckedIOException) {
throw logger.logExceptionAsError(e)
}
}

@ServiceMethod(returns = ReturnType.SINGLE)
override fun uploadFromFileWithResponse(
options: BlobUploadFromFileOptions?,
timeout: Duration?,
context: Context?
): Response<BlockBlobItem> {
val upload: Mono<Response<BlockBlobItem>> =
this.client.uploadFromFileWithResponse(options)
.contextWrite(FluxUtil.toReactorContext(context))
try {
return StorageImplUtils.blockWithOptionalTimeout(upload, timeout)
} catch (e: UncheckedIOException) {
throw logger.logExceptionAsError(e)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ data class ApiKeyDto(
expiresAt = apiKey.expiresAt,
projectId = apiKey.project.id,
userAccountId = apiKey.userAccount.id,
scopes = apiKey.scopesEnum.toSet(),
scopes = apiKey.scopesEnum.filterNotNull().toSet(),
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ package io.tolgee.dtos.response.ApiKeyDTO

import io.swagger.v3.oas.annotations.media.Schema
import io.tolgee.model.ApiKey
import io.tolgee.model.enums.Scope
import io.tolgee.security.PROJECT_API_KEY_PREFIX
import java.util.stream.Collectors

class ApiKeyDTO(
var id: Long = 0,
Expand All @@ -29,7 +27,7 @@ class ApiKeyDTO(
userName = apiKey.userAccount.name,
projectId = apiKey.project.id,
projectName = apiKey.project.name,
scopes = apiKey.scopesEnum.stream().map(Scope::value).collect(Collectors.toSet())
scopes = apiKey.scopesEnum.mapNotNull { it?.value }.toSet()
)
}
}
Expand Down
6 changes: 5 additions & 1 deletion backend/data/src/main/kotlin/io/tolgee/model/ApiKey.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ class ApiKey(
@NotEmpty
@Enumerated(EnumType.STRING)
@field:ElementCollection(targetClass = Scope::class, fetch = FetchType.EAGER)
var scopesEnum: MutableSet<Scope>
/**
* Scope should be never nullable, butthere were entries with null scopes in the production DB, which caused NPEs,
* so to be sure, lets meke it nullable
*/
var scopesEnum: MutableSet<Scope?>
) : StandardAuditModel() {

@field:NotBlank
Expand Down
7 changes: 0 additions & 7 deletions ee/backend/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,6 @@ kotlin {
jvmToolchain(17)
}

hibernate {
enhancement {
lazyInitialization = true
dirtyTracking = true
}
}

dependencyManagement {
imports {
mavenBom org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES
Expand Down

0 comments on commit 8fc8bc9

Please sign in to comment.