Address code review feedback
- Return 400 instead of panicking on invalid organization_id - Use generic error message for internal failures - Drop duplicate validation from initiate handler (kept in tx) - Make preserveConnectionFields mutate in place - Remove as type assertions in GoogleWorkspaceConnector - Use sort.Slice instead of sort.SliceStable - Consistent error prefixes in Slack sender Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
This commit is contained in:
@@ -12,7 +12,7 @@
|
|||||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
// PERFORMANCE OF THIS SOFTWARE.
|
// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
import { formatError, type GraphQLError, sprintf } from "@probo/helpers";
|
import { sprintf } from "@probo/helpers";
|
||||||
import { useTranslate } from "@probo/i18n";
|
import { useTranslate } from "@probo/i18n";
|
||||||
import {
|
import {
|
||||||
Badge,
|
Badge,
|
||||||
@@ -128,10 +128,7 @@ export function GoogleWorkspaceConnector(props: {
|
|||||||
if (errors?.length) {
|
if (errors?.length) {
|
||||||
toast({
|
toast({
|
||||||
title: __("Error"),
|
title: __("Error"),
|
||||||
description: formatError(
|
description: errors.map((e) => e.message).join(", "),
|
||||||
__("Failed to disconnect Google Workspace"),
|
|
||||||
errors as GraphQLError[],
|
|
||||||
),
|
|
||||||
variant: "error",
|
variant: "error",
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@@ -146,10 +143,7 @@ export function GoogleWorkspaceConnector(props: {
|
|||||||
onError(error) {
|
onError(error) {
|
||||||
toast({
|
toast({
|
||||||
title: __("Error"),
|
title: __("Error"),
|
||||||
description: formatError(
|
description: error.message,
|
||||||
__("Failed to disconnect Google Workspace"),
|
|
||||||
error as GraphQLError,
|
|
||||||
),
|
|
||||||
variant: "error",
|
variant: "error",
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -179,10 +173,7 @@ export function GoogleWorkspaceConnector(props: {
|
|||||||
if (errors?.length) {
|
if (errors?.length) {
|
||||||
toast({
|
toast({
|
||||||
title: __("Error"),
|
title: __("Error"),
|
||||||
description: formatError(
|
description: errors.map((e) => e.message).join(", "),
|
||||||
__("Failed to update excluded user names"),
|
|
||||||
errors as GraphQLError[],
|
|
||||||
),
|
|
||||||
variant: "error",
|
variant: "error",
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@@ -196,10 +187,7 @@ export function GoogleWorkspaceConnector(props: {
|
|||||||
onError(error) {
|
onError(error) {
|
||||||
toast({
|
toast({
|
||||||
title: __("Error"),
|
title: __("Error"),
|
||||||
description: formatError(
|
description: error.message,
|
||||||
__("Failed to update excluded user names"),
|
|
||||||
error as GraphQLError,
|
|
||||||
),
|
|
||||||
variant: "error",
|
variant: "error",
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ func (c *Connector) LoadOneByOrganizationIDAndProvider(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Widest-scope-wins, tiebreak by most recent updated_at.
|
// Widest-scope-wins, tiebreak by most recent updated_at.
|
||||||
sort.SliceStable(connectors, func(i, j int) bool {
|
sort.Slice(connectors, func(i, j int) bool {
|
||||||
ci, cj := connectorScopeCount(connectors[i]), connectorScopeCount(connectors[j])
|
ci, cj := connectorScopeCount(connectors[i]), connectorScopeCount(connectors[j])
|
||||||
if ci != cj {
|
if ci != cj {
|
||||||
return ci > cj
|
return ci > cj
|
||||||
|
|||||||
@@ -356,7 +356,8 @@ func (s *ConnectorService) Reconnect(
|
|||||||
return fmt.Errorf("cannot reconnect connector: not an OAuth2 connector")
|
return fmt.Errorf("cannot reconnect connector: not an OAuth2 connector")
|
||||||
}
|
}
|
||||||
|
|
||||||
cnnctr.Connection = preserveConnectionFields(req.Connection, cnnctr.Connection)
|
preserveConnectionFields(req.Connection, cnnctr.Connection)
|
||||||
|
cnnctr.Connection = req.Connection
|
||||||
cnnctr.UpdatedAt = time.Now()
|
cnnctr.UpdatedAt = time.Now()
|
||||||
|
|
||||||
return cnnctr.Update(ctx, conn, s.svc.scope, s.svc.encryptionKey)
|
return cnnctr.Update(ctx, conn, s.svc.scope, s.svc.encryptionKey)
|
||||||
@@ -369,11 +370,11 @@ func (s *ConnectorService) Reconnect(
|
|||||||
return cnnctr, nil
|
return cnnctr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// preserveConnectionFields returns newConn with refresh token and Slack
|
// preserveConnectionFields copies refresh token and Slack webhook
|
||||||
// webhook settings copied from oldConn when newConn omits them. Google
|
// settings from oldConn into newConn when newConn omits them. Google
|
||||||
// omits refresh_token on incremental-auth reuse; a Slack access-review
|
// omits refresh_token on incremental-auth reuse; a Slack access-review
|
||||||
// reconnect with no incoming-webhook scope omits the webhook settings.
|
// reconnect with no incoming-webhook scope omits the webhook settings.
|
||||||
func preserveConnectionFields(newConn, oldConn connector.Connection) connector.Connection {
|
func preserveConnectionFields(newConn, oldConn connector.Connection) {
|
||||||
switch n := newConn.(type) {
|
switch n := newConn.(type) {
|
||||||
case *connector.OAuth2Connection:
|
case *connector.OAuth2Connection:
|
||||||
if o, ok := oldConn.(*connector.OAuth2Connection); ok {
|
if o, ok := oldConn.(*connector.OAuth2Connection); ok {
|
||||||
@@ -393,5 +394,4 @@ func preserveConnectionFields(newConn, oldConn connector.Connection) connector.C
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return newConn
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,9 +29,7 @@ import (
|
|||||||
"go.probo.inc/probo/pkg/server/api/authn"
|
"go.probo.inc/probo/pkg/server/api/authn"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var errInvalidReconnectConnector = errors.New("invalid reconnect connector")
|
||||||
errInvalidReconnectConnector = errors.New("invalid reconnect connector")
|
|
||||||
)
|
|
||||||
|
|
||||||
func handleConnectorInitiate(
|
func handleConnectorInitiate(
|
||||||
logger *log.Logger,
|
logger *log.Logger,
|
||||||
@@ -53,7 +51,8 @@ func handleConnectorInitiate(
|
|||||||
|
|
||||||
organizationID, err := gid.ParseGID(r.URL.Query().Get("organization_id"))
|
organizationID, err := gid.ParseGID(r.URL.Query().Get("organization_id"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("cannot parse organization id: %w", err))
|
httpserver.RenderError(w, http.StatusBadRequest, fmt.Errorf("invalid organization_id parameter"))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if authn.APIKeyFromContext(r.Context()) != nil {
|
if authn.APIKeyFromContext(r.Context()) != nil {
|
||||||
@@ -100,7 +99,7 @@ func handleConnectorInitiate(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
logger.ErrorCtx(r.Context(), "cannot look up existing connector", log.Error(err))
|
logger.ErrorCtx(r.Context(), "cannot look up existing connector", log.Error(err))
|
||||||
httpserver.RenderError(w, http.StatusInternalServerError, fmt.Errorf("cannot look up existing connector"))
|
httpserver.RenderError(w, http.StatusInternalServerError, fmt.Errorf("internal error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,9 +145,6 @@ func loadExistingConnector(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := validateReconnectConnector(found, organizationID, provider); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return found, nil
|
return found, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,32 +159,3 @@ func loadExistingConnector(
|
|||||||
return found, err
|
return found, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateReconnectConnector(
|
|
||||||
c *coredata.Connector,
|
|
||||||
organizationID gid.GID,
|
|
||||||
provider string,
|
|
||||||
) error {
|
|
||||||
if c == nil {
|
|
||||||
return fmt.Errorf("%w: connector not found", errInvalidReconnectConnector)
|
|
||||||
}
|
|
||||||
|
|
||||||
var connectorProvider coredata.ConnectorProvider
|
|
||||||
if err := connectorProvider.Scan(provider); err != nil {
|
|
||||||
return fmt.Errorf("%w: unsupported provider: %w", errInvalidReconnectConnector, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.OrganizationID != organizationID {
|
|
||||||
return fmt.Errorf("%w: organization mismatch", errInvalidReconnectConnector)
|
|
||||||
}
|
|
||||||
if c.Provider != connectorProvider {
|
|
||||||
return fmt.Errorf("%w: provider mismatch", errInvalidReconnectConnector)
|
|
||||||
}
|
|
||||||
if c.Protocol != coredata.ConnectorProtocolOAuth2 {
|
|
||||||
return fmt.Errorf("%w: not an OAuth2 connector", errInvalidReconnectConnector)
|
|
||||||
}
|
|
||||||
if c.Connection == nil {
|
|
||||||
return fmt.Errorf("%w: connector has nil connection", errInvalidReconnectConnector)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ func (s *Sender) sendMessage(ctx context.Context, tx pg.Querier, message *coreda
|
|||||||
if errors.Is(err, coredata.ErrResourceNotFound) {
|
if errors.Is(err, coredata.ErrResourceNotFound) {
|
||||||
return nil, nil, fmt.Errorf("cannot send slack message: no connector configured for organization")
|
return nil, nil, fmt.Errorf("cannot send slack message: no connector configured for organization")
|
||||||
}
|
}
|
||||||
return nil, nil, fmt.Errorf("cannot load slack connector: %w", err)
|
return nil, nil, fmt.Errorf("cannot send slack message: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Connection == nil {
|
if c.Connection == nil {
|
||||||
@@ -284,7 +284,7 @@ func (s *Sender) updateMessage(ctx context.Context, tx pg.Querier, updateMessage
|
|||||||
if errors.Is(err, coredata.ErrResourceNotFound) {
|
if errors.Is(err, coredata.ErrResourceNotFound) {
|
||||||
return fmt.Errorf("cannot update slack message: no connector configured for organization")
|
return fmt.Errorf("cannot update slack message: no connector configured for organization")
|
||||||
}
|
}
|
||||||
return fmt.Errorf("cannot load slack connector: %w", err)
|
return fmt.Errorf("cannot update slack message: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Connection == nil {
|
if c.Connection == nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user