Let a secure retriever answer the questions the editor asks it
The editor asks every retriever-backed field whether its list is open, and whether the field is required, on endpoints suffixed onto the field's own URL. Only the unsecured retriever had them, so opening an MCP agent's shared session picker asked /secure-retriever/.../open and got a NoResourceFoundException stack in the log. The editor swallowed the failure and fell back to a closed list, which is why nothing looked wrong. Both questions now exist on the secure side too, defaulting the same way, so the answer comes from the retriever rather than from a 404. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
90c97f9aad
commit
18f2ab2fa0
|
|
@ -12,6 +12,28 @@ public interface SecureDynamicFieldRetriever {
|
|||
|
||||
List<RetrieverItem> retrieve(String parameter, Map<String, String> params, LoginEntity user);
|
||||
|
||||
/**
|
||||
* Whether the target field must be filled given the current parameters.
|
||||
*
|
||||
* <p>Answered here as well as on {@link DynamicFieldRetriever} because the editor asks the
|
||||
* question of whichever retriever backs the field, and cannot know which of the two families it
|
||||
* landed on.
|
||||
*/
|
||||
default boolean isRequired(String parameter, Map<String, String> params, LoginEntity user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether {@link #retrieve} returns a closed list or an incomplete one.
|
||||
*
|
||||
* <p>True tells the editor to accept a typed value instead of only what was listed. Closed by
|
||||
* default, for the same reason as the unsecured retriever: a retriever that can enumerate its
|
||||
* own values should keep the user inside them.
|
||||
*/
|
||||
default boolean isOpen(String parameter, Map<String, String> params, LoginEntity user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
record RetrieverItemDescriptor(String label, String description, Map<String, Object> meta) {
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -55,4 +55,26 @@ public class SecureRetrieverController {
|
|||
@AuthenticationPrincipal LoginEntity userDetails) {
|
||||
return resolveRetriever(category).retrieve(parameter, params, userDetails);
|
||||
}
|
||||
|
||||
@GetMapping("/{category}/{parameter}/required")
|
||||
@Operation(summary = "Check secure retriever required",
|
||||
description = "Returns true if the target field should be required given the current parameters.")
|
||||
public boolean isRequired(
|
||||
@PathVariable String category,
|
||||
@PathVariable String parameter,
|
||||
@RequestParam Map<String, String> params,
|
||||
@AuthenticationPrincipal LoginEntity userDetails) {
|
||||
return resolveRetriever(category).isRequired(parameter, params, userDetails);
|
||||
}
|
||||
|
||||
@GetMapping("/{category}/{parameter}/open")
|
||||
@Operation(summary = "Check secure retriever open",
|
||||
description = "Returns true if the values are an incomplete list and the target field accepts a typed value.")
|
||||
public boolean isOpen(
|
||||
@PathVariable String category,
|
||||
@PathVariable String parameter,
|
||||
@RequestParam Map<String, String> params,
|
||||
@AuthenticationPrincipal LoginEntity userDetails) {
|
||||
return resolveRetriever(category).isOpen(parameter, params, userDetails);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,18 @@ public class SecureRetrieverControllerTest {
|
|||
@Autowired
|
||||
private FlowRepository flowRepository;
|
||||
|
||||
@Test
|
||||
public void answersTheOpenAndRequiredQuestionsEveryRetrieverBackedFieldAsks() {
|
||||
// The editor asks these of whichever retriever backs a field. A secure one used to have no
|
||||
// endpoint to answer on, so opening an MCP agent's shared session picker logged a 404 stack.
|
||||
LoginEntity user = new LoginEntity("testuser", "ignored");
|
||||
|
||||
assertFalse(secureRetrieverController.isOpen("ExecutionVariables", "shared",
|
||||
Map.of("kind", "MCP_SESSION"), user));
|
||||
assertFalse(secureRetrieverController.isRequired("ExecutionVariables", "shared",
|
||||
Map.of("kind", "MCP_SESSION"), user));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void flowSubFlowRetrieverReturnsOnlyValidFlowsWhenRequested() {
|
||||
LoginEntity user = new LoginEntity("testuser", "ignored");
|
||||
|
|
|
|||
Loading…
Reference in New Issue