feat(core): add configurable share access policy

This commit is contained in:
2026-07-10 22:06:20 +02:00
parent 3160a5ed9c
commit 0bf4da0ee9
14 changed files with 224 additions and 17 deletions

View File

@@ -50,6 +50,7 @@ private class AndroidFileSystemService(
file: PickedShareFile,
transferName: String,
senderName: String,
accessPolicy: ShareAccessPolicy,
): Result<Share> = runCatching {
context.contentResolver.openFileDescriptor(Uri.parse(file.value), "r").use { descriptor ->
checkNotNull(descriptor) { "Could not open selected file descriptor" }
@@ -58,6 +59,7 @@ private class AndroidFileSystemService(
displayName = file.displayName,
transferName = transferName,
senderName = senderName,
accessPolicy = accessPolicy,
).getOrThrow()
}
}

View File

@@ -21,16 +21,40 @@ data class CoreEventModel(
val dataJson: String,
)
enum class ShareAccessPolicy {
RequireApproval,
AnyoneWithTransfer,
}
enum class TransferDirection {
Send,
Receive,
}
enum class TransferStatus {
Importing,
Sharing,
Receiving,
Done,
Failed,
Cancelled,
Stopped,
}
data class Transfer(
val localId: String,
val transferId: ULong,
val direction: String,
val status: String,
val direction: TransferDirection,
val status: TransferStatus,
val peerId: String?,
val transferName: String?,
val contentHash: String?,
val fileCount: ULong,
val totalSize: ULong,
val ticket: String?,
val accessPolicy: ShareAccessPolicy,
val createdAt: Long,
val updatedAt: Long,
)
data class Share(
@@ -90,9 +114,21 @@ interface CoreGateway {
suspend fun initialize(appDataDir: String): Result<Unit>
fun shutdown()
suspend fun sharePath(path: String, transferName: String, senderName: String): Result<Share>
suspend fun shareFileDescriptor(fd: Int, displayName: String, transferName: String, senderName: String): Result<Share>
suspend fun shareSecurityScopedFileUrl(fileUrl: String, displayName: String, transferName: String, senderName: String): Result<Share>
suspend fun sharePath(path: String, transferName: String, senderName: String, accessPolicy: ShareAccessPolicy): Result<Share>
suspend fun shareFileDescriptor(
fd: Int,
displayName: String,
transferName: String,
senderName: String,
accessPolicy: ShareAccessPolicy,
): Result<Share>
suspend fun shareSecurityScopedFileUrl(
fileUrl: String,
displayName: String,
transferName: String,
senderName: String,
accessPolicy: ShareAccessPolicy,
): Result<Share>
suspend fun inspectTicket(ticket: String): Result<TicketInspectionModel>
suspend fun receive(ticket: String, outputDir: String, receiverName: String): Result<Unit>
suspend fun receiveWithOutputSink(ticket: String, outputSink: ReceiveOutputSink, receiverName: String): Result<Unit>

View File

@@ -25,6 +25,7 @@ import uniffi.vnidrop.SourceKind
import uniffi.vnidrop.StoredTransfer
import uniffi.vnidrop.TicketInspection
import uniffi.vnidrop.TransferMetadata
import uniffi.vnidrop.TransferAccessMode
import uniffi.vnidrop.VnidropCore
class CoreRepository(
@@ -64,7 +65,12 @@ class CoreRepository(
_state.value = CoreState()
}
override suspend fun sharePath(path: String, transferName: String, senderName: String): Result<Share> =
override suspend fun sharePath(
path: String,
transferName: String,
senderName: String,
accessPolicy: ShareAccessPolicy,
): Result<Share> =
shareSources(
sources = listOf(
ShareSource(
@@ -76,6 +82,7 @@ class CoreRepository(
),
transferName = transferName,
senderName = senderName,
accessPolicy = accessPolicy,
)
override suspend fun shareFileDescriptor(
@@ -83,6 +90,7 @@ class CoreRepository(
displayName: String,
transferName: String,
senderName: String,
accessPolicy: ShareAccessPolicy,
): Result<Share> =
shareSources(
sources = listOf(
@@ -95,6 +103,7 @@ class CoreRepository(
),
transferName = transferName,
senderName = senderName,
accessPolicy = accessPolicy,
)
override suspend fun shareSecurityScopedFileUrl(
@@ -102,6 +111,7 @@ class CoreRepository(
displayName: String,
transferName: String,
senderName: String,
accessPolicy: ShareAccessPolicy,
): Result<Share> =
shareSources(
sources = listOf(
@@ -114,6 +124,7 @@ class CoreRepository(
),
transferName = transferName,
senderName = senderName,
accessPolicy = accessPolicy,
)
override suspend fun inspectTicket(ticket: String): Result<TicketInspectionModel> = runCore {
@@ -170,6 +181,7 @@ class CoreRepository(
sources: List<ShareSource>,
transferName: String,
senderName: String,
accessPolicy: ShareAccessPolicy,
): Result<Share> = runCore {
withPlatformPathAccess(sources) {
requireCore().shareFiles(
@@ -178,6 +190,7 @@ class CoreRepository(
transferId = nextTransferId(),
transferName = transferName.ifBlank { null },
senderName = senderName.ifBlank { null },
accessMode = accessPolicy.toNative(),
),
).toModel()
}.also { share ->
@@ -243,15 +256,46 @@ private fun CoreEvent.toModel(): CoreEventModel = CoreEventModel(
private fun StoredTransfer.toModel(): Transfer = Transfer(
localId = localId,
transferId = transferId,
direction = direction,
status = status,
direction = direction.toTransferDirection(),
status = status.toTransferStatus(),
peerId = peerId,
transferName = transferName,
contentHash = contentHash,
fileCount = fileCount,
totalSize = totalSize,
ticket = ticket,
accessPolicy = accessMode.toModel(),
createdAt = createdAt,
updatedAt = updatedAt,
)
private fun ShareAccessPolicy.toNative(): TransferAccessMode = when (this) {
ShareAccessPolicy.RequireApproval -> TransferAccessMode.APPROVAL_REQUIRED
ShareAccessPolicy.AnyoneWithTransfer -> TransferAccessMode.PUBLIC
}
private fun TransferAccessMode.toModel(): ShareAccessPolicy = when (this) {
TransferAccessMode.APPROVAL_REQUIRED -> ShareAccessPolicy.RequireApproval
TransferAccessMode.PUBLIC -> ShareAccessPolicy.AnyoneWithTransfer
}
private fun String.toTransferDirection(): TransferDirection = when (this) {
"send" -> TransferDirection.Send
"receive" -> TransferDirection.Receive
else -> error("Unknown transfer direction: $this")
}
private fun String.toTransferStatus(): TransferStatus = when (this) {
"importing" -> TransferStatus.Importing
"sharing" -> TransferStatus.Sharing
"receiving" -> TransferStatus.Receiving
"done" -> TransferStatus.Done
"failed" -> TransferStatus.Failed
"cancelled" -> TransferStatus.Cancelled
"stopped" -> TransferStatus.Stopped
else -> error("Unknown transfer status: $this")
}
private fun ShareResult.toModel(): Share = Share(
transferId = transferId,
ticket = ticket,

View File

@@ -30,6 +30,7 @@ interface FileSystemService {
file: PickedShareFile,
transferName: String,
senderName: String,
accessPolicy: ShareAccessPolicy,
): Result<Share>
}

View File

@@ -47,11 +47,13 @@ private class IosFileSystemService : FileSystemService {
file: PickedShareFile,
transferName: String,
senderName: String,
accessPolicy: ShareAccessPolicy,
): Result<Share> = repository.shareSecurityScopedFileUrl(
file.value,
file.displayName,
transferName,
senderName,
accessPolicy,
)
private fun validateSecurityScopedUrl(value: String): FolderAccessStatus {

View File

@@ -33,5 +33,6 @@ private class JvmFileSystemService : FileSystemService {
file: PickedShareFile,
transferName: String,
senderName: String,
): Result<Share> = repository.sharePath(file.value, transferName, senderName)
accessPolicy: ShareAccessPolicy,
): Result<Share> = repository.sharePath(file.value, transferName, senderName, accessPolicy)
}