Skip to content

Commit 7e22520

Browse files
authored
Use user token when requesting from HttpsDataVault on same DS (#8322)
1 parent 5bd4e22 commit 7e22520

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+324
-208
lines changed

CHANGELOG.unreleased.md

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
1313
### Added
1414

1515
### Changed
16+
- When using a zarr link to a wk-served data layer as another layer’s source, the user’s token is used to access the data. [#8322](https://github.com/scalableminds/webknossos/pull/8322/)
1617

1718
### Fixed
1819
- Fixed a bug that would lock a non existing mapping to an empty segmentation layer under certain conditions. [#8401](https://github.com/scalableminds/webknossos/pull/8401)

test/backend/DataVaultTestSuite.scala

+27-14
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package backend
22

3+
import com.scalableminds.util.accesscontext.TokenContext
34
import com.scalableminds.util.tools.Fox
45
import org.scalatestplus.play.PlaySpec
56

@@ -26,6 +27,8 @@ import scala.concurrent.ExecutionContext.{global => globalExecutionContext}
2627
class DataVaultTestSuite extends PlaySpec {
2728

2829
val handleFoxJustification = "Handling Fox in Unit Test Context"
30+
val emptyTokenContext: TokenContext = TokenContext(None)
31+
val dummyDataStoreHost = "example.com"
2932

3033
"Data vault" when {
3134
"using Range requests" when {
@@ -36,10 +39,11 @@ class DataVaultTestSuite extends PlaySpec {
3639
"return correct response" in {
3740
WsTestClient.withClient { ws =>
3841
val uri = new URI("http://storage.googleapis.com/")
39-
val vaultPath = new VaultPath(uri, HttpsDataVault.create(RemoteSourceDescriptor(uri, None), ws))
42+
val vaultPath =
43+
new VaultPath(uri, HttpsDataVault.create(RemoteSourceDescriptor(uri, None), ws, dummyDataStoreHost))
4044
val bytes =
4145
(vaultPath / s"neuroglancer-fafb-data/fafb_v14/fafb_v14_orig/$dataKey")
42-
.readBytes(Some(range))(globalExecutionContext)
46+
.readBytes(Some(range))(globalExecutionContext, emptyTokenContext)
4347
.get(handleFoxJustification)
4448

4549
assert(bytes.length == range.length)
@@ -53,7 +57,9 @@ class DataVaultTestSuite extends PlaySpec {
5357
val vaultPath = new VaultPath(uri, GoogleCloudDataVault.create(RemoteSourceDescriptor(uri, None)))
5458
"return correct response" in {
5559

56-
val bytes = (vaultPath / dataKey).readBytes(Some(range))(globalExecutionContext).get(handleFoxJustification)
60+
val bytes = (vaultPath / dataKey)
61+
.readBytes(Some(range))(globalExecutionContext, emptyTokenContext)
62+
.get(handleFoxJustification)
5763

5864
assert(bytes.length == range.length)
5965
assert(bytes.take(10).sameElements(Array(-1, -40, -1, -32, 0, 16, 74, 70, 73, 70)))
@@ -63,15 +69,15 @@ class DataVaultTestSuite extends PlaySpec {
6369
"requesting a non-existent object" in {
6470
val result =
6571
(vaultPath / s"non-existent-key${UUID.randomUUID}")
66-
.readBytes()(globalExecutionContext)
72+
.readBytes()(globalExecutionContext, emptyTokenContext)
6773
.await(handleFoxJustification)
6874
assertBoxEmpty(result)
6975
}
7076
}
7177
"return failure" when {
7278
"requesting invalid range" in {
7379
val result = (vaultPath / dataKey)
74-
.readBytes(Some(Range.Long(-5, -10, 1)))(globalExecutionContext)
80+
.readBytes(Some(Range.Long(-5, -10, 1)))(globalExecutionContext, emptyTokenContext)
7581
.await(handleFoxJustification)
7682
assertBoxFailure(result)
7783
}
@@ -83,7 +89,7 @@ class DataVaultTestSuite extends PlaySpec {
8389
uri,
8490
Some(GoogleServiceAccountCredential("name", JsString("secret"), "user", "org")))))
8591
val result = (vaultPath / dataKey)
86-
.readBytes(Some(Range.Long(-10, 10, 1)))(globalExecutionContext)
92+
.readBytes(Some(Range.Long(-10, 10, 1)))(globalExecutionContext, emptyTokenContext)
8793
.await(handleFoxJustification)
8894
assertBoxFailure(result)
8995
}
@@ -97,7 +103,9 @@ class DataVaultTestSuite extends PlaySpec {
97103
val vaultPath =
98104
new VaultPath(uri, S3DataVault.create(RemoteSourceDescriptor(uri, None), ws)(globalExecutionContext))
99105
val bytes =
100-
(vaultPath / "s0/5/5/5").readBytes(Some(range))(globalExecutionContext).get(handleFoxJustification)
106+
(vaultPath / "s0/5/5/5")
107+
.readBytes(Some(range))(globalExecutionContext, emptyTokenContext)
108+
.get(handleFoxJustification)
101109
assert(bytes.length == range.length)
102110
assert(bytes.take(10).sameElements(Array(0, 0, 0, 3, 0, 0, 0, 64, 0, 0)))
103111
}
@@ -113,9 +121,10 @@ class DataVaultTestSuite extends PlaySpec {
113121
"return correct response" in {
114122
WsTestClient.withClient { ws =>
115123
val uri = new URI("http://storage.googleapis.com/")
116-
val vaultPath = new VaultPath(uri, HttpsDataVault.create(RemoteSourceDescriptor(uri, None), ws))
124+
val vaultPath =
125+
new VaultPath(uri, HttpsDataVault.create(RemoteSourceDescriptor(uri, None), ws, dummyDataStoreHost))
117126
val bytes = (vaultPath / s"neuroglancer-fafb-data/fafb_v14/fafb_v14_orig/$dataKey")
118-
.readBytes()(globalExecutionContext)
127+
.readBytes()(globalExecutionContext, emptyTokenContext)
119128
.get(handleFoxJustification)
120129

121130
assert(bytes.length == dataLength)
@@ -128,7 +137,8 @@ class DataVaultTestSuite extends PlaySpec {
128137
"return correct response" in {
129138
val uri = new URI("gs://neuroglancer-fafb-data/fafb_v14/fafb_v14_orig")
130139
val vaultPath = new VaultPath(uri, GoogleCloudDataVault.create(RemoteSourceDescriptor(uri, None)))
131-
val bytes = (vaultPath / dataKey).readBytes()(globalExecutionContext).get(handleFoxJustification)
140+
val bytes =
141+
(vaultPath / dataKey).readBytes()(globalExecutionContext, emptyTokenContext).get(handleFoxJustification)
132142

133143
assert(bytes.length == dataLength)
134144
assert(bytes.take(10).sameElements(Array(-1, -40, -1, -32, 0, 16, 74, 70, 73, 70)))
@@ -143,7 +153,7 @@ class DataVaultTestSuite extends PlaySpec {
143153
new VaultPath(uri, S3DataVault.create(RemoteSourceDescriptor(uri, None), ws)(globalExecutionContext))
144154
val bytes =
145155
(vaultPath / "33792-34304_29696-30208_3216-3232")
146-
.readBytes()(globalExecutionContext)
156+
.readBytes()(globalExecutionContext, emptyTokenContext)
147157
.get(handleFoxJustification)
148158
assert(bytes.take(10).sameElements(Array(-87, -95, -85, -94, -101, 124, 115, 100, 113, 111)))
149159
}
@@ -155,7 +165,8 @@ class DataVaultTestSuite extends PlaySpec {
155165
WsTestClient.withClient { ws =>
156166
val s3DataVault = S3DataVault.create(RemoteSourceDescriptor(uri, None), ws)(globalExecutionContext)
157167
val vaultPath = new VaultPath(uri, s3DataVault)
158-
val result = vaultPath.readBytes()(globalExecutionContext).await(handleFoxJustification)
168+
val result =
169+
vaultPath.readBytes()(globalExecutionContext, emptyTokenContext).await(handleFoxJustification)
159170
assertBoxEmpty(result)
160171
}
161172
}
@@ -167,7 +178,8 @@ class DataVaultTestSuite extends PlaySpec {
167178
WsTestClient.withClient { ws =>
168179
val s3DataVault = S3DataVault.create(RemoteSourceDescriptor(uri, None), ws)(globalExecutionContext)
169180
val vaultPath = new VaultPath(uri, s3DataVault)
170-
val result = vaultPath.readBytes()(globalExecutionContext).await(handleFoxJustification)
181+
val result =
182+
vaultPath.readBytes()(globalExecutionContext, emptyTokenContext).await(handleFoxJustification)
171183
assertBoxEmpty(result)
172184
}
173185
}
@@ -207,7 +219,8 @@ class DataVaultTestSuite extends PlaySpec {
207219
"using vault path" when {
208220
class MockDataVault extends DataVault {
209221
override def readBytesAndEncoding(path: VaultPath, range: RangeSpecifier)(
210-
implicit ec: ExecutionContext): Fox[(Array[Byte], Encoding.Value)] = ???
222+
implicit ec: ExecutionContext,
223+
tc: TokenContext): Fox[(Array[Byte], Encoding.Value)] = ???
211224

212225
override def listDirectory(path: VaultPath,
213226
maxItems: Int)(implicit ec: ExecutionContext): Fox[List[VaultPath]] = ???

webknossos-datastore/app/com/scalableminds/webknossos/datastore/controllers/BinaryDataController.scala

+10-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.scalableminds.webknossos.datastore.controllers
22

33
import com.google.inject.Inject
4+
import com.scalableminds.util.accesscontext.TokenContext
45
import com.scalableminds.util.geometry.Vec3Int
56
import com.scalableminds.util.image.{Color, JPEGWriter}
67
import com.scalableminds.util.time.Instant
@@ -102,14 +103,14 @@ class BinaryDataController @Inject()(
102103
datasetDirectoryName,
103104
dataLayerName) ~> NOT_FOUND
104105
magParsed <- Vec3Int.fromMagLiteral(mag).toFox ?~> "malformedMag"
105-
request = DataRequest(
106+
dataRequest = DataRequest(
106107
VoxelPosition(x, y, z, magParsed),
107108
width,
108109
height,
109110
depth,
110111
DataServiceRequestSettings(halfByte = halfByte, appliedAgglomerate = mappingName)
111112
)
112-
(data, indices) <- requestData(dataSource, dataLayer, request)
113+
(data, indices) <- requestData(dataSource, dataLayer, dataRequest)
113114
} yield Ok(data).withHeaders(createMissingBucketsHeaders(indices): _*)
114115
}
115116
}
@@ -147,13 +148,13 @@ class BinaryDataController @Inject()(
147148
(dataSource, dataLayer) <- dataSourceRepository.getDataSourceAndDataLayer(organizationId,
148149
datasetDirectoryName,
149150
dataLayerName) ~> NOT_FOUND
150-
request = DataRequest(
151+
dataRequest = DataRequest(
151152
VoxelPosition(x * cubeSize * mag, y * cubeSize * mag, z * cubeSize * mag, Vec3Int(mag, mag, mag)),
152153
cubeSize,
153154
cubeSize,
154155
cubeSize
155156
)
156-
(data, indices) <- requestData(dataSource, dataLayer, request)
157+
(data, indices) <- requestData(dataSource, dataLayer, dataRequest)
157158
} yield Ok(data).withHeaders(createMissingBucketsHeaders(indices): _*)
158159
}
159160
}
@@ -180,14 +181,14 @@ class BinaryDataController @Inject()(
180181
dataLayerName) ?~> Messages(
181182
"dataSource.notFound") ~> NOT_FOUND
182183
magParsed <- Vec3Int.fromMagLiteral(mag).toFox ?~> "malformedMag"
183-
request = DataRequest(
184+
dataRequest = DataRequest(
184185
VoxelPosition(x, y, z, magParsed),
185186
width,
186187
height,
187188
depth = 1,
188189
DataServiceRequestSettings(appliedAgglomerate = mappingName)
189190
)
190-
(data, _) <- requestData(dataSource, dataLayer, request)
191+
(data, _) <- requestData(dataSource, dataLayer, dataRequest)
191192
intensityRange: Option[(Double, Double)] = intensityMin.flatMap(min => intensityMax.map(max => (min, max)))
192193
layerColor = color.flatMap(Color.fromHTML)
193194
params = ImageCreatorParameters(
@@ -252,10 +253,11 @@ class BinaryDataController @Inject()(
252253
request.body.cuboid(dataLayer),
253254
request.body.segmentId,
254255
request.body.voxelSizeFactorInUnit,
256+
tokenContextForRequest(request),
255257
request.body.mapping,
256258
request.body.mappingType,
257259
request.body.additionalCoordinates,
258-
request.body.findNeighbors
260+
request.body.findNeighbors,
259261
)
260262
// The client expects the ad-hoc mesh as a flat float-array. Three consecutive floats form a 3D point, three
261263
// consecutive 3D points (i.e., nine floats) form a triangle.
@@ -308,7 +310,7 @@ class BinaryDataController @Inject()(
308310
dataSource: DataSource,
309311
dataLayer: DataLayer,
310312
dataRequests: DataRequestCollection
311-
): Fox[(Array[Byte], List[Int])] = {
313+
)(implicit tc: TokenContext): Fox[(Array[Byte], List[Int])] = {
312314
val requests =
313315
dataRequests.map(r => DataServiceDataRequest(dataSource, dataLayer, r.cuboid(dataLayer), r.settings))
314316
binaryDataService.handleDataRequests(requests)

webknossos-datastore/app/com/scalableminds/webknossos/datastore/controllers/ZarrStreamingController.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ class ZarrStreamingController @Inject()(
261261
dataLayerName: String,
262262
mag: String,
263263
coordinates: String,
264-
)(implicit m: MessagesProvider): Fox[Result] =
264+
)(implicit m: MessagesProvider, tc: TokenContext): Fox[Result] =
265265
for {
266266
(dataSource, dataLayer) <- dataSourceRepository.getDataSourceAndDataLayer(organizationId,
267267
datasetDirectoryName,
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package com.scalableminds.webknossos.datastore.dataformats
22

3+
import com.scalableminds.util.accesscontext.TokenContext
34
import com.scalableminds.util.tools.Fox
45
import com.scalableminds.webknossos.datastore.models.requests.DataReadInstruction
6+
57
import scala.concurrent.ExecutionContext
68

79
trait BucketProvider {
8-
def load(readInstruction: DataReadInstruction)(implicit ec: ExecutionContext): Fox[Array[Byte]]
10+
def load(readInstruction: DataReadInstruction)(implicit ec: ExecutionContext, tc: TokenContext): Fox[Array[Byte]]
911
}

webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/DatasetArrayBucketProvider.scala

+7-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.scalableminds.webknossos.datastore.dataformats
22

3+
import com.scalableminds.util.accesscontext.TokenContext
34
import com.scalableminds.util.cache.AlfuCache
45
import com.scalableminds.util.geometry.Vec3Int
56
import com.scalableminds.util.time.Instant
@@ -16,6 +17,7 @@ import com.scalableminds.webknossos.datastore.models.requests.DataReadInstructio
1617
import com.scalableminds.webknossos.datastore.storage.RemoteSourceDescriptorService
1718
import com.typesafe.scalalogging.LazyLogging
1819
import net.liftweb.common.Empty
20+
1921
import scala.concurrent.duration._
2022
import ucar.ma2.{Array => MultiArray}
2123

@@ -32,7 +34,7 @@ class DatasetArrayBucketProvider(dataLayer: DataLayer,
3234
// Cache the DatasetArrays of all mags of this layer
3335
private lazy val datasetArrayCache = AlfuCache[Vec3Int, DatasetArray](maxCapacity = 50)
3436

35-
def load(readInstruction: DataReadInstruction)(implicit ec: ExecutionContext): Fox[Array[Byte]] =
37+
def load(readInstruction: DataReadInstruction)(implicit ec: ExecutionContext, tc: TokenContext): Fox[Array[Byte]] =
3638
for {
3739
datasetArray <- datasetArrayCache.getOrLoad(readInstruction.bucket.mag,
3840
_ => openDatasetArrayWithTimeLogging(readInstruction))
@@ -45,8 +47,8 @@ class DatasetArrayBucketProvider(dataLayer: DataLayer,
4547
dataLayer.elementClass == ElementClass.uint24)
4648
} yield bucketData
4749

48-
private def openDatasetArrayWithTimeLogging(readInstruction: DataReadInstruction)(
49-
implicit ec: ExecutionContext): Fox[DatasetArray] = {
50+
private def openDatasetArrayWithTimeLogging(
51+
readInstruction: DataReadInstruction)(implicit ec: ExecutionContext, tc: TokenContext): Fox[DatasetArray] = {
5052
val before = Instant.now
5153
for {
5254
result <- openDatasetArray(readInstruction).futureBox
@@ -59,8 +61,8 @@ class DatasetArrayBucketProvider(dataLayer: DataLayer,
5961
} yield result
6062
}
6163

62-
private def openDatasetArray(readInstruction: DataReadInstruction)(
63-
implicit ec: ExecutionContext): Fox[DatasetArray] = {
64+
private def openDatasetArray(readInstruction: DataReadInstruction)(implicit ec: ExecutionContext,
65+
tc: TokenContext): Fox[DatasetArray] = {
6466
val magLocatorOpt: Option[MagLocator] =
6567
dataLayer.mags.find(_.mag == readInstruction.bucket.mag)
6668

webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/ChunkReader.scala

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.scalableminds.webknossos.datastore.datareaders
22

3+
import com.scalableminds.util.accesscontext.TokenContext
34
import com.scalableminds.util.tools.Fox
45
import com.scalableminds.util.tools.Fox.box2Fox
56
import com.scalableminds.webknossos.datastore.datavault.VaultPath
@@ -18,7 +19,7 @@ class ChunkReader(header: DatasetHeader) {
1819
def read(path: VaultPath,
1920
chunkShapeFromMetadata: Array[Int],
2021
range: Option[NumericRange[Long]],
21-
useSkipTypingShortcut: Boolean)(implicit ec: ExecutionContext): Fox[MultiArray] =
22+
useSkipTypingShortcut: Boolean)(implicit ec: ExecutionContext, tc: TokenContext): Fox[MultiArray] =
2223
for {
2324
chunkBytesAndShapeBox: Box[(Array[Byte], Option[Array[Int]])] <- readChunkBytesAndShape(path, range).futureBox
2425
chunkShape: Array[Int] = chunkBytesAndShapeBox.toOption.flatMap(_._2).getOrElse(chunkShapeFromMetadata)
@@ -39,7 +40,8 @@ class ChunkReader(header: DatasetHeader) {
3940
// Returns bytes (optional, Fox.empty may later be replaced with fill value)
4041
// and chunk shape (optional, only for data formats where each chunk reports its own shape, e.g. N5)
4142
protected def readChunkBytesAndShape(path: VaultPath, range: Option[NumericRange[Long]])(
42-
implicit ec: ExecutionContext): Fox[(Array[Byte], Option[Array[Int]])] =
43+
implicit ec: ExecutionContext,
44+
tc: TokenContext): Fox[(Array[Byte], Option[Array[Int]])] =
4345
for {
4446
bytes <- path.readBytes(range)
4547
decompressed <- tryo(header.compressorImpl.decompress(bytes)).toFox ?~> "chunk.decompress.failed"

0 commit comments

Comments
 (0)