Hide managed connectors until fully configured
A managed (Model B) connector like Crisp needs both the Probo-held key and a resource ID (the plugin ID) to connect, but the driver catalog gated visibility on the key alone. A deployment that set the key without the plugin ID (reachable through raw JSON config; the bootstrap env path already requires both) would show Crisp as connectable and then fail every attempt with an internal error. Add a RequiresManagedResourceID flag to the registration and a Registry.ManagedConnectorReady check that requires both before a managed provider enters the catalog, so a half-configured provider stays hidden instead of dead-ending at connect. Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
This commit is contained in:
@@ -37,6 +37,11 @@ func crispRegistration() *Registration {
|
|||||||
// deactivated until Crisp validates the production plugin and
|
// deactivated until Crisp validates the production plugin and
|
||||||
// activates with no code change once the token is set.
|
// activates with no code change once the token is set.
|
||||||
ManagedAPIKey: true,
|
ManagedAPIKey: true,
|
||||||
|
// The per-website plugin API also needs the plugin ID (a distinct value
|
||||||
|
// from the token identifier), supplied via bootstrap alongside the
|
||||||
|
// token. Require it so Crisp stays hidden until both are configured
|
||||||
|
// rather than surfacing as connectable and failing at connect time.
|
||||||
|
RequiresManagedResourceID: true,
|
||||||
// Crisp authenticates with the plugin token presented as HTTP Basic,
|
// Crisp authenticates with the plugin token presented as HTTP Basic,
|
||||||
// the credential being the verbatim "identifier:key" pair.
|
// the credential being the verbatim "identifier:key" pair.
|
||||||
// APIKeyBasicAuthUserPass base64-encodes it (the empty-password
|
// APIKeyBasicAuthUserPass base64-encodes it (the empty-password
|
||||||
|
|||||||
@@ -304,6 +304,31 @@ func (r *Registry) ManagedResourceID(p coredata.ConnectorProvider) (string, bool
|
|||||||
return id, ok
|
return id, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ManagedConnectorReady reports whether a ManagedAPIKey provider is fully
|
||||||
|
// configured for this deployment: its Probo-held key is set and, when the
|
||||||
|
// provider also requires a resource ID (RequiresManagedResourceID, e.g. the
|
||||||
|
// Crisp plugin ID), that is set too. A provider that is not ready is kept out
|
||||||
|
// of the driver catalog, since connecting it would fail at verify time. It is
|
||||||
|
// false for non-managed and unregistered providers.
|
||||||
|
func (r *Registry) ManagedConnectorReady(p coredata.ConnectorProvider) bool {
|
||||||
|
reg, ok := r.Get(p)
|
||||||
|
if !ok || !reg.ManagedAPIKey {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := r.ManagedAPIKey(p); !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if reg.RequiresManagedResourceID {
|
||||||
|
if _, ok := r.ManagedResourceID(p); !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// ProviderOAuth2Scopes returns the OAuth2 scopes the access review
|
// ProviderOAuth2Scopes returns the OAuth2 scopes the access review
|
||||||
// driver for the given provider needs to list user accounts. Returns
|
// driver for the given provider needs to list user accounts. Returns
|
||||||
// nil for providers that do not need any scopes (Notion, Intercom)
|
// nil for providers that do not need any scopes (Notion, Intercom)
|
||||||
|
|||||||
@@ -266,6 +266,35 @@ func TestCrispIsManagedAPIKey(t *testing.T) {
|
|||||||
assert.True(t, reg.ManagedAPIKey)
|
assert.True(t, reg.ManagedAPIKey)
|
||||||
assert.False(t, reg.SupportsAPIKey)
|
assert.False(t, reg.SupportsAPIKey)
|
||||||
assert.True(t, reg.APIKeyBasicAuthUserPass)
|
assert.True(t, reg.APIKeyBasicAuthUserPass)
|
||||||
|
assert.True(t, reg.RequiresManagedResourceID, "crisp needs the plugin ID before it can connect")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestRegistry_ManagedConnectorReady pins that a provider requiring a resource
|
||||||
|
// ID (Crisp's plugin ID) is reported ready, and thus surfaced in the catalog,
|
||||||
|
// only once BOTH the managed key and the resource ID are configured.
|
||||||
|
func TestRegistry_ManagedConnectorReady(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
t.Run("crisp needs both key and resource id", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
r := provider.NewBuiltinRegistry()
|
||||||
|
assert.False(t, r.ManagedConnectorReady(coredata.ConnectorProviderCrisp), "unconfigured")
|
||||||
|
|
||||||
|
r.SetManagedAPIKey(coredata.ConnectorProviderCrisp, "identifier:secret")
|
||||||
|
assert.False(t, r.ManagedConnectorReady(coredata.ConnectorProviderCrisp), "key set but plugin id missing")
|
||||||
|
|
||||||
|
r.SetManagedResourceID(coredata.ConnectorProviderCrisp, "plugin-id")
|
||||||
|
assert.True(t, r.ManagedConnectorReady(coredata.ConnectorProviderCrisp), "key and plugin id set")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("non-managed provider is never ready", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
r := provider.NewBuiltinRegistry()
|
||||||
|
r.SetManagedAPIKey(coredata.ConnectorProviderTally, "some-key")
|
||||||
|
assert.False(t, r.ManagedConnectorReady(coredata.ConnectorProviderTally))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestRegistry_RejectsManagedPlusCustomerCredential pins that a
|
// TestRegistry_RejectsManagedPlusCustomerCredential pins that a
|
||||||
|
|||||||
@@ -124,6 +124,15 @@ type Registration struct {
|
|||||||
// injected key is presented on the wire.
|
// injected key is presented on the wire.
|
||||||
ManagedAPIKey bool
|
ManagedAPIKey bool
|
||||||
|
|
||||||
|
// RequiresManagedResourceID marks a ManagedAPIKey provider that also needs
|
||||||
|
// a Probo-supplied resource ID (Crisp's plugin ID, registered via
|
||||||
|
// (*Registry).SetManagedResourceID) before a connection can succeed. Such a
|
||||||
|
// provider stays out of the driver catalog until BOTH the managed key and
|
||||||
|
// the resource ID are configured, so the operator never sees it as
|
||||||
|
// connectable while a connect attempt would fail at verify time. See
|
||||||
|
// (*Registry).ManagedConnectorReady.
|
||||||
|
RequiresManagedResourceID bool
|
||||||
|
|
||||||
// BuildProbeURL derives a per-connector probe URL when the API host or
|
// BuildProbeURL derives a per-connector probe URL when the API host or
|
||||||
// path depends on connector settings (e.g. a customer subdomain or
|
// path depends on connector settings (e.g. a customer subdomain or
|
||||||
// instance URL). Nil for providers with a static ProbeURL.
|
// instance URL). Nil for providers with a static ProbeURL.
|
||||||
|
|||||||
@@ -575,10 +575,12 @@ func (r *queryResolver) AccessReviewDrivers(ctx context.Context) ([]*types.Conne
|
|||||||
clientCredentialsSupported := reg.SupportsClientCredentials
|
clientCredentialsSupported := reg.SupportsClientCredentials
|
||||||
|
|
||||||
// ManagedAPIKey (Model B, e.g. Crisp) providers are connectable only
|
// ManagedAPIKey (Model B, e.g. Crisp) providers are connectable only
|
||||||
// once the operator configures the Probo-held key; until then they
|
// once the operator configures the Probo-held key (and any required
|
||||||
// stay hidden, so such a provider ships deactivated.
|
// resource ID, e.g. Crisp's plugin ID); until then they stay hidden, so
|
||||||
_, hasManaged := r.providerRegistry.ManagedAPIKey(provider)
|
// such a provider ships deactivated. Gating on full readiness keeps a
|
||||||
apiKeyManaged := reg.ManagedAPIKey && hasManaged
|
// half-configured provider out of the catalog rather than surfacing it
|
||||||
|
// and failing at connect time.
|
||||||
|
apiKeyManaged := r.providerRegistry.ManagedConnectorReady(provider)
|
||||||
|
|
||||||
// Skip providers that cannot be connected in this deployment: no
|
// Skip providers that cannot be connected in this deployment: no
|
||||||
// OAuth client credentials configured and no key-based fallback
|
// OAuth client credentials configured and no key-based fallback
|
||||||
|
|||||||
Reference in New Issue
Block a user