Fix documents signing authentication

Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
Émile Ré
2026-01-21 12:24:34 +04:00
parent 25c94d4571
commit ff65303a1a
2 changed files with 131 additions and 124 deletions

View File

@@ -104,29 +104,16 @@ const routes = [
},
{
path: "/",
Component: lazy(() => import("./pages/iam/memberships/ViewerLayoutLoader")),
Fallback: ViewerLayoutLoading,
ErrorBoundary: ErrorBoundary,
children: [
{
index: true,
Component: lazy(
() => import("./pages/iam/memberships/MembershipsPageLoader"),
),
},
{
Component: CenteredLayout,
Component: lazy(() => import("./pages/iam/memberships/ViewerLayoutLoader")),
Fallback: ViewerLayoutLoading,
children: [
{
path: "organizations/new",
index: true,
Component: lazy(
() => import("./pages/iam/organizations/NewOrganizationPage"),
),
},
{
path: "documents/signing-requests",
Component: lazy(
() => import("./pages/DocumentSigningRequestsPage"),
() => import("./pages/iam/memberships/MembershipsPageLoader"),
),
},
{
@@ -135,10 +122,28 @@ const routes = [
() => import("./pages/iam/apiKeys/APIKeysPageLoader"),
),
},
],
{
Component: CenteredLayout,
children: [
{
path: "organizations/new",
Component: lazy(
() => import("./pages/iam/organizations/NewOrganizationPage"),
),
},
],
},
]
},
],
},
{
path: "documents/signing-requests",
ErrorBoundary: ErrorBoundary,
Component: lazy(
() => import("./pages/DocumentSigningRequestsPage"),
),
},
{
path: "/organizations/:organizationId/employee",
Component: lazy(

View File

@@ -65,13 +65,116 @@ func NewMux(
safeRedirect := &saferedirect.SafeRedirect{AllowedHost: baseURL.Host()}
r.Use(authn.NewSessionMiddleware(iamSvc, cookieConfig))
r.Use(authn.NewAPIKeyMiddleware(iamSvc, tokenSecret))
r.Use(authn.NewIdentityPresenceMiddleware())
graphqlHandler := NewGraphQLHandler(iamSvc, proboSvc, customDomainCname, logger)
r.Handle("/graphql", graphqlHandler)
r.Group(func(r chi.Router) {
r.Use(authn.NewSessionMiddleware(iamSvc, cookieConfig))
r.Use(authn.NewAPIKeyMiddleware(iamSvc, tokenSecret))
r.Use(authn.NewIdentityPresenceMiddleware())
r.Handle("/graphql", graphqlHandler)
r.Get("/connectors/initiate", func(w http.ResponseWriter, r *http.Request) {
provider := r.URL.Query().Get("provider")
if provider != "SLACK" {
httpserver.RenderError(w, http.StatusBadRequest, fmt.Errorf("unsupported provider"))
return
}
organizationID, err := gid.ParseGID(r.URL.Query().Get("organization_id"))
if err != nil {
panic(fmt.Errorf("cannot parse organization id: %w", err))
}
apiKey := authn.APIKeyFromContext(r.Context())
if apiKey != nil {
httpserver.RenderError(w, http.StatusBadRequest, fmt.Errorf("api key authentication cannot be used for this endpoint"))
return
}
identity := authn.IdentityFromContext(r.Context())
if identity == nil {
httpserver.RenderError(w, http.StatusUnauthorized, fmt.Errorf("authentication required"))
return
}
session := authn.SessionFromContext(r.Context())
if session == nil {
httpserver.RenderError(w, http.StatusUnauthorized, fmt.Errorf("authentication required"))
return
}
if err := iamSvc.Authorizer.Authorize(r.Context(), iam.AuthorizeParams{
Principal: identity.ID,
Resource: organizationID,
Session: &session.ID,
Action: probo.ActionConnectorInitiate,
}); err != nil {
httpserver.RenderError(w, http.StatusForbidden, err)
return
}
redirectURL, err := connectorRegistry.Initiate(r.Context(), provider, organizationID, r)
if err != nil {
panic(fmt.Errorf("cannot initiate connector: %w", err))
}
// Allow external redirects for Slack OAuth only for now
slackSafeRedirect := &saferedirect.SafeRedirect{AllowedHost: "slack.com"}
slackSafeRedirect.Redirect(w, r, redirectURL, "/", http.StatusSeeOther)
})
r.Get("/connectors/complete", func(w http.ResponseWriter, r *http.Request) {
provider := r.URL.Query().Get("provider")
if provider == "" {
httpserver.RenderError(w, http.StatusBadRequest, fmt.Errorf("missing provider parameter"))
return
}
var connectorProvider coredata.ConnectorProvider
switch provider {
case "SLACK":
connectorProvider = coredata.ConnectorProviderSlack
default:
httpserver.RenderError(w, http.StatusBadRequest, fmt.Errorf("unsupported provider"))
return
}
stateToken := r.URL.Query().Get("state")
if stateToken == "" {
httpserver.RenderError(w, http.StatusBadRequest, fmt.Errorf("missing state parameter"))
return
}
connection, organizationID, err := connectorRegistry.Complete(r.Context(), provider, r)
if err != nil {
panic(fmt.Errorf("cannot complete connector: %w", err))
}
continueURL := r.URL.Query().Get("continue")
svc := proboSvc.WithTenant(organizationID.TenantID())
_, err = svc.Connectors.Create(
r.Context(),
probo.CreateConnectorRequest{
OrganizationID: *organizationID,
Provider: connectorProvider,
Protocol: coredata.ConnectorProtocol(connection.Type()),
Connection: connection,
},
)
if err != nil {
panic(fmt.Errorf("cannot create or update connector: %w", err))
}
if continueURL != "" {
safeRedirect.Redirect(w, r, continueURL, "/", http.StatusSeeOther)
} else {
redirectURL := baseURL.WithPath("/organizations/" + organizationID.String()).MustString()
safeRedirect.Redirect(w, r, redirectURL, "/", http.StatusSeeOther)
}
})
})
r.Get(
"/documents/signing-requests",
@@ -192,107 +295,6 @@ func NewMux(
},
)
r.Get("/connectors/initiate", func(w http.ResponseWriter, r *http.Request) {
provider := r.URL.Query().Get("provider")
if provider != "SLACK" {
httpserver.RenderError(w, http.StatusBadRequest, fmt.Errorf("unsupported provider"))
return
}
organizationID, err := gid.ParseGID(r.URL.Query().Get("organization_id"))
if err != nil {
panic(fmt.Errorf("cannot parse organization id: %w", err))
}
apiKey := authn.APIKeyFromContext(r.Context())
if apiKey != nil {
httpserver.RenderError(w, http.StatusBadRequest, fmt.Errorf("api key authentication cannot be used for this endpoint"))
return
}
identity := authn.IdentityFromContext(r.Context())
if identity == nil {
httpserver.RenderError(w, http.StatusUnauthorized, fmt.Errorf("authentication required"))
return
}
session := authn.SessionFromContext(r.Context())
if session == nil {
httpserver.RenderError(w, http.StatusUnauthorized, fmt.Errorf("authentication required"))
return
}
if err := iamSvc.Authorizer.Authorize(r.Context(), iam.AuthorizeParams{
Principal: identity.ID,
Resource: organizationID,
Session: &session.ID,
Action: probo.ActionConnectorInitiate,
}); err != nil {
httpserver.RenderError(w, http.StatusForbidden, err)
return
}
redirectURL, err := connectorRegistry.Initiate(r.Context(), provider, organizationID, r)
if err != nil {
panic(fmt.Errorf("cannot initiate connector: %w", err))
}
// Allow external redirects for Slack OAuth only for now
slackSafeRedirect := &saferedirect.SafeRedirect{AllowedHost: "slack.com"}
slackSafeRedirect.Redirect(w, r, redirectURL, "/", http.StatusSeeOther)
})
r.Get("/connectors/complete", func(w http.ResponseWriter, r *http.Request) {
provider := r.URL.Query().Get("provider")
if provider == "" {
httpserver.RenderError(w, http.StatusBadRequest, fmt.Errorf("missing provider parameter"))
return
}
var connectorProvider coredata.ConnectorProvider
switch provider {
case "SLACK":
connectorProvider = coredata.ConnectorProviderSlack
default:
httpserver.RenderError(w, http.StatusBadRequest, fmt.Errorf("unsupported provider"))
return
}
stateToken := r.URL.Query().Get("state")
if stateToken == "" {
httpserver.RenderError(w, http.StatusBadRequest, fmt.Errorf("missing state parameter"))
return
}
connection, organizationID, err := connectorRegistry.Complete(r.Context(), provider, r)
if err != nil {
panic(fmt.Errorf("cannot complete connector: %w", err))
}
continueURL := r.URL.Query().Get("continue")
svc := proboSvc.WithTenant(organizationID.TenantID())
_, err = svc.Connectors.Create(
r.Context(),
probo.CreateConnectorRequest{
OrganizationID: *organizationID,
Provider: connectorProvider,
Protocol: coredata.ConnectorProtocol(connection.Type()),
Connection: connection,
},
)
if err != nil {
panic(fmt.Errorf("cannot create or update connector: %w", err))
}
if continueURL != "" {
safeRedirect.Redirect(w, r, continueURL, "/", http.StatusSeeOther)
} else {
redirectURL := baseURL.WithPath("/organizations/" + organizationID.String()).MustString()
safeRedirect.Redirect(w, r, redirectURL, "/", http.StatusSeeOther)
}
})
return r
}