diff --git a/modules/gplazma2-oidc/src/main/java/org/dcache/gplazma/oidc/profiles/WlcgProfileScope.java b/modules/gplazma2-oidc/src/main/java/org/dcache/gplazma/oidc/profiles/WlcgProfileScope.java index 431a0f68698..2aefebce80c 100644 --- a/modules/gplazma2-oidc/src/main/java/org/dcache/gplazma/oidc/profiles/WlcgProfileScope.java +++ b/modules/gplazma2-oidc/src/main/java/org/dcache/gplazma/oidc/profiles/WlcgProfileScope.java @@ -76,6 +76,12 @@ public enum Operation { */ STAGE("storage.stage", true, LIST, READ_METADATA, DOWNLOAD, Activity.STAGE), + /** + * Query the status of a file, including whether it is online or nearline (tape). This + * scope allows clients to poll for the status of a file without requiring read access. + */ + POLL("storage.poll", true, READ_METADATA), + /** * "Read" or query information about job status and attributes. */ @@ -147,7 +153,7 @@ public WlcgProfileScope(String scope) { checkScopeValid(operation != null, "Unknown operation %s", operationLabel); if (colon == -1) { - checkScopeValid(!operation.isPathRequired(), "Path must be specified"); + checkScopeValid(!operation.isPathRequired(), "Path must be specified for \"%s\"", operationLabel); path = "/"; } else { String scopePath = scope.substring(colon + 1); diff --git a/modules/gplazma2-oidc/src/test/java/org/dcache/gplazma/oidc/profiles/WlcgProfileScopeTest.java b/modules/gplazma2-oidc/src/test/java/org/dcache/gplazma/oidc/profiles/WlcgProfileScopeTest.java index d23ff310f6f..4733b5392bb 100644 --- a/modules/gplazma2-oidc/src/test/java/org/dcache/gplazma/oidc/profiles/WlcgProfileScopeTest.java +++ b/modules/gplazma2-oidc/src/test/java/org/dcache/gplazma/oidc/profiles/WlcgProfileScopeTest.java @@ -60,6 +60,11 @@ public void shouldIdentifyStorageStageScope() { assertTrue(WlcgProfileScope.isWlcgProfileScope("storage.stage:/")); } + @Test + public void shouldIdentifyStoragePollScope() { + assertTrue(WlcgProfileScope.isWlcgProfileScope("storage.poll:/")); + } + @Test public void shouldNotIdentifyStorageWriteScope() { assertFalse(WlcgProfileScope.isWlcgProfileScope("storage.write:/")); @@ -81,6 +86,11 @@ public void shouldRejectStorageStageScopeWithoutPath() { new WlcgProfileScope("storage.stage"); } + @Test(expected = InvalidScopeException.class) + public void shouldRejectStoragePollScopeWithoutPath() { + new WlcgProfileScope("storage.poll"); + } + @Test(expected = InvalidScopeException.class) public void shouldRejectStorageModifyScopeWithoutPath() { new WlcgProfileScope("storage.modify"); @@ -162,6 +172,34 @@ public void shouldParseStageScopeWithNonRootResourcePath() { assertThat(auth.getActivity(), containsInAnyOrder(LIST, READ_METADATA, DOWNLOAD, Activity.STAGE)); } + @Test + public void shouldParsePollScopeWithRootResourcePath() { + WlcgProfileScope scope = new WlcgProfileScope("storage.poll:/"); + + Optional maybeAuth = scope.authorisation(FsPath.create("/VOs/wlcg")); + + assertTrue(maybeAuth.isPresent()); + + Authorisation auth = maybeAuth.get(); + + assertThat(auth.getPath(), equalTo(FsPath.create("/VOs/wlcg"))); + assertThat(auth.getActivity(), containsInAnyOrder(READ_METADATA)); + } + + @Test + public void shouldParsePollScopeWithNonRootResourcePath() { + WlcgProfileScope scope = new WlcgProfileScope("storage.poll:/foo"); + + Optional maybeAuth = scope.authorisation(FsPath.create("/VOs/wlcg")); + + assertTrue(maybeAuth.isPresent()); + + Authorisation auth = maybeAuth.get(); + + assertThat(auth.getPath(), equalTo(FsPath.create("/VOs/wlcg/foo"))); + assertThat(auth.getActivity(), containsInAnyOrder(READ_METADATA)); + } + @Test(expected=InvalidScopeException.class) public void shouldRejectReadScopeWithRelativeResourcePath() { new WlcgProfileScope("storage.read:foo"); @@ -172,6 +210,11 @@ public void shouldRejectStageScopeWithRelativeResourcePath() { new WlcgProfileScope("storage.stage:foo"); } + @Test(expected=InvalidScopeException.class) + public void shouldRejectPollScopeWithRelativeResourcePath() { + new WlcgProfileScope("storage.poll:foo"); + } + @Test public void shouldParseComputeReadScope() { WlcgProfileScope scope = new WlcgProfileScope("compute.read");