Registration.ExtraSettings was a single flat list, but the API-key and client-credentials connect dialogs need different fields whenever a provider offers both paths, because a different create resolver and a different driver sits behind each. Replace it with APIKeyExtraSettings and ClientCredentialsExtraSettings, and split the GraphQL surface to match so a client cannot render one path's settings on the other. This fixes two connectors that could not be connected at all. 1Password declared accountId and region only, which are the client-credentials shape. The API-key dialog therefore rendered those two fields, mapAPIKeyExtraSettingToField returned nil for both so buildExtraFields discarded them, and the SCIM-bridge driver failed on an empty SCIMBridgeURL. The console already mapped scimBridgeUrl, but no registration declared that key, so the branch was dead. It now declares scimBridgeUrl on the API-key path and accountId + region on client credentials. Langfuse declared baseUrl as required, but mapAPIKeyExtraSettingToField had no LANGFUSE case, so buildExtraFields dropped the value the customer typed and the mutation failed with "langfuseBaseUrl is required". Every other extra-settings provider had a case. The GraphQL input field, the settings struct, the probe builder and the driver were all already correct; only the console mapping was missing. buildExtraFields now takes the settings list explicitly instead of reading it off the provider, so each dialog passes its own path's list and cannot silently iterate the other one. Register rejects a settings list for a path the provider does not offer, and an empty or duplicate setting key within one list. A key repeated across the two lists is allowed: that is how a dual-path provider declares a setting both dialogs need. The new resolver tests walk the whole chain the console walks, from the key a Registration declares through the mutation input field to the persisted settings struct, so a key renamed on one side and not the other fails in CI instead of at connect time. Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
89 lines
3.8 KiB
Go
89 lines
3.8 KiB
Go
// Copyright (c) 2026 Probo Inc <hello@probo.com>.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
// SOFTWARE.
|
|
|
|
package provider
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"go.gearno.de/kit/log"
|
|
"go.probo.inc/probo/pkg/accessreview/drivers"
|
|
"go.probo.inc/probo/pkg/coredata"
|
|
)
|
|
|
|
func crispRegistration() *Registration {
|
|
return &Registration{
|
|
Provider: coredata.ConnectorProviderCrisp,
|
|
DisplayName: "Crisp",
|
|
DocumentationURL: accessReviewDocsURL("crisp"),
|
|
// Model B: the plugin token is Probo's own Crisp Marketplace plugin
|
|
// credential, held server-side in bootstrap config, not pasted by
|
|
// the customer. ManagedAPIKey injects it at connect time; the
|
|
// customer supplies only the Website ID. SupportsAPIKey stays false
|
|
// so the provider is hidden from the driver catalog until the
|
|
// operator configures PROBOD_CONNECTOR_CRISP_PLUGIN_TOKEN — it ships
|
|
// deactivated until Crisp validates the production plugin and
|
|
// activates with no code change once the token is set.
|
|
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,
|
|
// the credential being the verbatim "identifier:key" pair.
|
|
// APIKeyBasicAuthUserPass base64-encodes it (the empty-password
|
|
// APIKeyBasicAuth cannot carry the key). A plugin token can serve
|
|
// several websites, so the reviewed website is captured via
|
|
// APIKeyExtraSettings. Every request also needs the non-auth X-Crisp-Tier
|
|
// header (set by the driver/probe/name resolver), so the probe is a
|
|
// custom closure.
|
|
APIKeyBasicAuthUserPass: true,
|
|
APIKeyExtraSettings: []ExtraSetting{
|
|
{Key: "websiteId", Label: "Website ID", Required: true},
|
|
},
|
|
Probe: probeCrisp,
|
|
NewDriver: func(_ context.Context, c *http.Client, conn *coredata.Connector, _ *log.Logger) (drivers.Driver, error) {
|
|
s, err := coredata.ConnectorSettings[coredata.CrispConnectorSettings](conn)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("cannot read crisp connector settings: %w", err)
|
|
}
|
|
|
|
if s.WebsiteID == "" {
|
|
return nil, fmt.Errorf("cannot create crisp driver: website_id is required")
|
|
}
|
|
|
|
return drivers.NewCrispDriver(c, s.WebsiteID), nil
|
|
},
|
|
NewNameResolver: func(ctx context.Context, c *http.Client, conn *coredata.Connector, logger *log.Logger) drivers.NameResolver {
|
|
s, err := coredata.ConnectorSettings[coredata.CrispConnectorSettings](conn)
|
|
if err != nil {
|
|
logger.ErrorCtx(ctx, "cannot read crisp connector settings", log.Error(err))
|
|
|
|
return nil
|
|
}
|
|
|
|
return drivers.NewCrispNameResolver(c, s.WebsiteID)
|
|
},
|
|
}
|
|
}
|