Compliance Portal Refactor#1476
Conversation
29638d3 to
eeae51b
Compare
eeae51b to
8ddbdd7
Compare
| @@ -63,6 +63,7 @@ type ( | |||
| IAM *iam.Service | |||
| Trust *trust.Service | |||
| ESign *esign.Service | |||
| CustomDomain *management.Service | |||
|
|
||
| func NewSecurityHeadersMiddleware(extraHeaderFields map[string]string) func(next http.Handler) http.Handler { | ||
| return func(next http.Handler) http.Handler { | ||
| return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| "go.probo.inc/probo/pkg/accessreview" | ||
| "go.probo.inc/probo/pkg/agentrun" | ||
| "go.probo.inc/probo/pkg/baseurl" | ||
| "go.probo.inc/probo/pkg/complianceportal/management" | ||
| trust "go.probo.inc/probo/pkg/complianceportal/visitor" |
| @@ -24,10 +24,10 @@ import ( | |||
|
|
|||
| "go.gearno.de/kit/httpserver" | |||
| "go.gearno.de/kit/log" | |||
| trust "go.probo.inc/probo/pkg/complianceportal/visitor" | |||
| @@ -17,8 +17,8 @@ package slack_v1 | |||
| import ( | |||
| "github.com/go-chi/chi/v5" | |||
| "go.gearno.de/kit/log" | |||
| trust "go.probo.inc/probo/pkg/complianceportal/visitor" | |||
Introduce the trust center base domain setting (default probopage.com) so managed default domains can be minted for every compliance page. This configuration is a prerequisite for the domain-ownership migration and the default-domain provisioning that happens at organization creation. Signed-off-by: Bryan Frimin <bryan@probo.com>
Custom domains previously hung off the organization. Attach them to the trust center instead, adding default and custom domain references plus a managed flag, and backfill existing rows. Domains now belong to a compliance page rather than the whole organization. Signed-off-by: Bryan Frimin <bryan@probo.com>
Pull SSL/ACME state out of custom_domains into a dedicated hostname-keyed certificates table, and repoint cached certificates from the domain to the certificate. Custom domains now reference a certificate id, keeping certificate provisioning decoupled from domain business logic. Signed-off-by: Bryan Frimin <bryan@probo.com>
Replace the Provisioner and Renewer with poll-based provision and renew workers orchestrated by a certmanager Service. Certificate operations are now hostname-centric and driven by the certificates table, decoupled from custom-domain business logic. Signed-off-by: Bryan Frimin <bryan@probo.com>
Rename the compliance external URL concept to compliance custom links, and move the public-facing profile fields (description, website, email, headquarter address) off the organization onto the trust center. Backfill managed default domains for pages that lack them. Signed-off-by: Bryan Frimin <bryan@probo.com>
Split the compliance portal into an admin-facing management side and a public-facing visitor side under pkg/complianceportal. Trust-center CRUD, domains, custom links, frameworks, files and accesses move out of pkg/probo, and the visitor read logic moves out of pkg/trust. IAM actions migrate onto compliance-portal scopes. Signed-off-by: Bryan Frimin <bryan@probo.com>
Wire the certificate manager and trust center base domain into IAM so organization creation provisions a managed default domain and certificate atomically. Email presenters in IAM and mailman resolve public URLs through the compliance portal resolver and read profile fields from the trust center. probod initializes the certmanager service and injects the new management and visitor services. Signed-off-by: Bryan Frimin <bryan@probo.com>
Rename the compliancepage HTTP package to complianceportal and drop the slug-based id middleware. Compliance pages are now served exclusively over SNI-based custom-domain HTTPS, with the SNI middleware resolving the page by TLS server name and redirecting secondary domains to the canonical host. Signed-off-by: Bryan Frimin <bryan@probo.com>
Rewire the console and visitor resolvers onto the management and visitor services with compliance-portal authorization. Rename the GraphQL and MCP ComplianceExternalURL type to ComplianceCustomLink, expose trust center profile fields, default and custom domains, public URL, and the managed flag, and drop the profile fields from the organization surface. Signed-off-by: Bryan Frimin <bryan@probo.com>
Follow the new domain model in tests: drop organization profile assertions, add a trust center profile test, and hit the dedicated HTTPS listener with SNI for the visitor API. Mirror the custom link rename and profile field moves in the n8n node operations. Signed-off-by: Bryan Frimin <bryan@probo.com>
Introduce the app-bound useMutation primitive and rename the trust center graph and reference hooks to compliance page, moving the shared reference dialogs alongside them. Sweep the compliance page subpages onto the new mutation hook and the compliance-portal permission strings, and strip the profile fields from the organization settings form. Signed-off-by: Bryan Frimin <bryan@probo.com>
Decompose the brand page into profile, domains, visual identity, and custom link sections, and move frameworks and the NDA card onto the overview page as dedicated fragment components. Remove the standalone domain page and redirect its route to brand, where domains now live. Signed-off-by: Bryan Frimin <bryan@probo.com>
Stop relying on the /trust/{id} path prefix in the public trust app and
serve it from the host root, reading contact and profile data from the
trust center instead of the organization. Mirror the same fragment
ownership change in the compliance portal hero.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Store website, email, and headquarters on the trust center so public and admin surfaces read branding from one place. Drop the trust API organization type and wire console, MCP, CLI, and apps through the updated schema. Signed-off-by: Bryan Frimin <bryan@probo.com>
Register the new provider enum value so compliance portal OAuth clients can be stored and resolved like other IAM OIDC providers. Signed-off-by: Bryan Frimin <bryan@probo.com>
Teach CIMD registration and discovery about per-portal client metadata, and carry portal context through token and ID token issuance for downstream session creation. Signed-off-by: Bryan Frimin <bryan@probo.com>
Build and validate client metadata documents for trust center hostnames so OAuth initiation can publish the correct redirect URIs. Signed-off-by: Bryan Frimin <bryan@probo.com>
Persist portal hostname and OAuth state on session records so the callback can bind an authorization code to the correct trust center. Signed-off-by: Bryan Frimin <bryan@probo.com>
Handle OAuth initiation, callback exchange, and session creation for anonymous trust center visitors through the portal package. Signed-off-by: Bryan Frimin <bryan@probo.com>
Drop the one-time session handoff flow now that trust center visitors authenticate through OAuth against the compliance portal. Signed-off-by: Bryan Frimin <bryan@probo.com>
Relocate the public trust center GraphQL surface, OAuth handlers, and SPA serving into the compliance portal API package and remove the legacy trust v1 server tree. Signed-off-by: Bryan Frimin <bryan@probo.com>
Expose per-portal OAuth client metadata from connect and route OIDC authorization through compliance portal session state. Signed-off-by: Bryan Frimin <bryan@probo.com>
Create IAM sessions from OAuth callbacks with the portal hostname and consent data needed by the compliance portal API. Signed-off-by: Bryan Frimin <bryan@probo.com>
Mount the compliance portal mux on trust center hosts, drop the legacy trust web server, and share response header helpers. Signed-off-by: Bryan Frimin <bryan@probo.com>
| useCreateTrustCenterReferenceMutation, | ||
| useUpdateTrustCenterReferenceMutation, | ||
| } from "#/hooks/graph/TrustCenterReferenceGraph"; | ||
| useCreateCompliancePageReferenceMutation, |
There was a problem hiding this comment.
trust center vs compliance page vs compliance portal
Redirect visitors through the compliance portal OAuth initiate endpoint and remove the verify-magic-link page and routes. Signed-off-by: Bryan Frimin <bryan@probo.com>
Surface dedicated routes when a portal magic link is expired or already used, and add the portal login entry point in console. Signed-off-by: Bryan Frimin <bryan@probo.com>
Update Relay schema paths and HTTP endpoints so the portal app talks to the compliance portal GraphQL and OAuth routes. Signed-off-by: Bryan Frimin <bryan@probo.com>
Extend test helpers for portal OAuth flows and cover connect, callback, and NDA signing against the compliance portal API. Signed-off-by: Bryan Frimin <bryan@probo.com>
Add the complianceportal Go embed target, wire it into CI and release workflows, and document the new build entry point. Signed-off-by: Bryan Frimin <bryan@probo.com>
Pebble skipped real HTTP-01 validation, which hid integration gaps for compliance-page custom domains. step-ca with a Caddy port-80 proxy exercises the same path production uses while keeping issued certs persistent across compose restarts. Signed-off-by: Bryan Frimin <bryan@probo.com>
Update generated test config, loopback HTTPS client, and workflow root CA wiring for the step-ca ACME directory and port-443 trust center listener introduced by the new compose stack. Signed-off-by: Bryan Frimin <bryan@probo.com>
Compliance portal OAuth must call the configured Probo base URL during connect flows; in local dev that endpoint is loopback and was blocked by default SSRF protection on the initiate HTTP client. Signed-off-by: Bryan Frimin <bryan@probo.com>
The compliance portal OAuth callback accepted ID tokens after only parsing claims, without checking the signature, issuer, audience, or expiry. Add RS256 verification helpers to the JOSE package, enforce those checks in ParseIDTokenIdentity, and thread JWKS, issuer, and client ID through the token response and callback handler. Signed-off-by: Bryan Frimin <bryan@probo.com>
| return payload.data?.oidcProviders ?? []; | ||
| } | ||
|
|
||
| export default function PortalLoginPage() { |
There was a problem hiding this comment.
why do we get portal sign in from console?
There was a problem hiding this comment.
23 issues found across 299 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="pkg/server/api/mcp/v1/types/organization.go">
<violation number="1" location="pkg/server/api/mcp/v1/types/organization.go:23">
P2: NewOrganization no longer maps Description from the coredata Organization. If the API Organization struct retains a Description field, callers will receive an empty string instead of the actual value. Either restore the mapping or also remove the field from the struct if it's intentionally being dropped.</violation>
</file>
<file name="apps/console/src/pages/iam/auth/MagicLinkAlreadyUsedPage.tsx">
<violation number="1" location="apps/console/src/pages/iam/auth/MagicLinkAlreadyUsedPage.tsx:38">
P2: Users arriving directly from a stale email link may have no SPA history to go back to. `navigate(-1)` would then navigate outside the app entirely. A safer approach: fall back to a known route like `/login` when `window.history.length` is too shallow, or always navigate to the login/home page.</violation>
</file>
<file name="apps/console/src/pages/organizations/compliance-page/brand/_components/CompliancePageCustomLinkDialog.tsx">
<violation number="1" location="apps/console/src/pages/organizations/compliance-page/brand/_components/CompliancePageCustomLinkDialog.tsx:184">
P2: Editing a URL after manually choosing a Name replaces that Name with the detected social-network name. Clear `nameAutoDetected` when the Name field receives a user edit, so auto-fill only owns untouched names.</violation>
</file>
<file name="pkg/server/api/complianceportal/v1/mux.go">
<violation number="1" location="pkg/server/api/complianceportal/v1/mux.go:122">
P3: Unknown portal paths are always dispatched to `webServer`; `handleCustomDomain404` is unreachable because `/*` matches them first. Route unmatched paths through the custom 404 handler (or remove the unused handler) so the intended error response can be produced.</violation>
</file>
<file name="pkg/certmanager/renew_worker.go">
<violation number="1" location="pkg/certmanager/renew_worker.go:94">
P0: Certificates selected for renewal are never renewed: `Process` succeeds without performing work after `Claim` marks them `Renewing`. Implement renewal and persist the resulting certificate/status, or avoid claiming this worker until processing exists.</violation>
</file>
<file name="pkg/coredata/migrations/20260706T132923Z.sql">
<violation number="1" location="pkg/coredata/migrations/20260706T132923Z.sql:26">
P1: Organizations without a trust center lose their configured custom domain during this migration. Preserve or explicitly migrate these rows instead of deleting all domains that have no `trust_centers` reference.</violation>
</file>
<file name="pkg/server/response_headers.go">
<violation number="1" location="pkg/server/response_headers.go:19">
P2: HSTS preload directive is present but `includeSubDomains` is missing. For the `preload` directive to be effective for browser preload-list submission, `includeSubDomains` is required per hstspreload.org requirements. Either add `includeSubDomains` if all subdomains should enforce HTTPS, or remove the `preload` directive if subdomain coverage is not intended.</violation>
</file>
<file name="apps/compliance-portal/src/components/TopBar/TopBar.tsx">
<violation number="1" location="apps/compliance-portal/src/components/TopBar/TopBar.tsx:114">
P2: Access login returns visitors to the route but drops its query/hash state. Preserve the full in-app location in `continue` so links with parameters resume exactly after OAuth.</violation>
</file>
<file name="contrib/lima/provision.sh">
<violation number="1" location="contrib/lima/provision.sh:242">
P2: The `.env` file for the compliance portal dev server is written to `apps/trust/.env` (line 192), but the npm workspace was renamed from `@probo/trust` to `@probo/compliance-portal`. If the source directory was also renamed, this `.env` won't be picked up by the compliance-portal dev server and `VITE_API_URL` will be unset. Update the path to match the new workspace directory.</violation>
</file>
<file name="pkg/crypto/jose/jose.go">
<violation number="1" location="pkg/crypto/jose/jose.go:120">
P3: Oversized JWK exponents are silently narrowed, so this function can return a key that does not represent the source JWK. Validate that `e` is positive and fits `int` before converting; return an error otherwise.</violation>
<violation number="2" location="pkg/crypto/jose/jose.go:126">
P2: A missing JWKS crashes token verification instead of rejecting the token. Return an error before dereferencing `jwks` so a failed or uninitialized OAuth key-set configuration cannot take down the callback request.</violation>
</file>
<file name="apps/console/src/pages/organizations/compliance-page/brand/_components/CompliancePageCustomLinkList.tsx">
<violation number="1" location="apps/console/src/pages/organizations/compliance-page/brand/_components/CompliancePageCustomLinkList.tsx:33">
P2: Organizations with more than 100 custom links cannot view, edit, or reorder links after the first page, because this list silently requests only the default page. Suggest loading all allowed links or add pagination before making the list management surface depend on this connection.</violation>
</file>
<file name="apps/console/src/pages/iam/auth/MagicLinkExpiredPage.tsx">
<violation number="1" location="apps/console/src/pages/iam/auth/MagicLinkExpiredPage.tsx:38">
P2: `navigate(-1)` on this page can leave users stranded. Users arriving from an expired email link in a new tab have no history to go back to — `navigate(-1)` will either do nothing or exit the app entirely. The page says "Please request a new one" but offers no direct way to do so. Consider navigating to the login/sign-in route as a fallback (e.g., `navigate('/login')`) so users can retry authentication, or provide a "Request new link" action instead.</violation>
</file>
<file name="pkg/server/api/complianceportal/sni_middleware.go">
<violation number="1" location="pkg/server/api/complianceportal/sni_middleware.go:88">
P1: GraphQL mutations submitted through a secondary portal domain can be retried as body-less GET requests after this 301, rather than reaching the canonical `/graphql` endpoint. Use a method-preserving permanent redirect (308) for this host canonicalization.</violation>
</file>
<file name="pkg/coredata/migrations/20260710T085314Z.sql">
<violation number="1" location="pkg/coredata/migrations/20260710T085314Z.sql:31">
P1: Organizations without an existing trust center lose their profile data during this migration: they are excluded from the copy, then their source columns are dropped. Preserve those rows by creating/migrating a trust center first, or retain the organization fields until that backfill can occur.</violation>
</file>
<file name="apps/console/src/pages/organizations/compliance-page/domain/_components/NewCompliancePageDomainDialog.tsx">
<violation number="1" location="apps/console/src/pages/organizations/compliance-page/domain/_components/NewCompliancePageDomainDialog.tsx:153">
P3: Domain help renders the same example twice, reducing the usefulness of the examples list. Restore a distinct second example such as `trust.example.com`.</violation>
</file>
<file name="packages/n8n-node/nodes/Probo/actions/trustCenter/update.operation.ts">
<violation number="1" location="packages/n8n-node/nodes/Probo/actions/trustCenter/update.operation.ts:176">
P2: Clearing a previously-set string field (title, description, websiteUrl, email, headquarterAddress) to empty is silently ignored — the old value persists. The truthy guard (`if (fieldName)`) used here matches `searchEngineIndexing`'s pattern, but that field uses empty string as a distinct '(Unchanged)' sentinel. These new free-form content strings have no such semantic: emptying the input is a valid clear intent. Since n8n provides no way to distinguish untouched-from-cleared for a `default: ''` string parameter, the safest fix is to always include these fields in the input so the API receives the current user intent.</violation>
</file>
<file name="pkg/complianceportal/resolver/resolver.go">
<violation number="1" location="pkg/complianceportal/resolver/resolver.go:81">
P1: A trust center with an inactive custom-domain certificate but no loaded default-domain row publishes that unready custom host as its canonical URL. Fall back to the slug-derived default host instead, so links do not direct users to a domain that cannot yet serve HTTPS.</violation>
</file>
<file name="pkg/coredata/migrations/20260709T090905Z.sql">
<violation number="1" location="pkg/coredata/migrations/20260709T090905Z.sql:98">
P2: Cross-tenant certificate associations are currently valid at the database layer, so a mistaken or insufficiently scoped update can attach another tenant's key/certificate to this domain. Preserve the tenant invariant with a composite tenant-aware reference (or equivalent constraint) when adding this relationship.</violation>
</file>
<file name="GNUmakefile">
<violation number="1" location="GNUmakefile:389">
P1: Production builds omit the compliance portal bundle: `EMBEDDED` and `WITH_APPS` still target `apps/trust`, so the new portal can be deleted by `clean` but never built or embedded. Update those build inputs to the compliance-portal workspace alongside this rename.</violation>
</file>
<file name="pkg/coredata/compliance_framework.go">
<violation number="1" location="pkg/coredata/compliance_framework.go:386">
P1: `LoadWithHiddenByTrustCenterID` now assigns `ROW_NUMBER()` as rank for every row, ignoring the persisted `cf.rank` values that `UpdateRank` and `Insert` store. Any custom ordering set via `UpdateRank` will be silently lost when loading frameworks with this method. If any consumer paginates by `COMPLIANCE_FRAMEWORK_ORDER_RANK` or uses the returned rank for display, the behavior regresses. Consider preserving `cf.rank` for existing linked frameworks (`cf.id IS NOT NULL`) and only generating sequential ranks for unlinked ones, similar to the original logic.</violation>
</file>
<file name="apps/console/src/pages/organizations/compliance-page/domain/_components/DeleteCompliancePageDomainDialog.tsx">
<violation number="1" location="apps/console/src/pages/organizations/compliance-page/domain/_components/DeleteCompliancePageDomainDialog.tsx:43">
P3: `compliancePageId` is a required but unused prop, so callers must supply data this dialog never consumes. Remove it from the prop contract unless it is needed for cache updates.</violation>
</file>
<file name="pkg/iam/oauth2/discovery.go">
<violation number="1" location="pkg/iam/oauth2/discovery.go:86">
P2: Authorization endpoints with provider-required query parameters lose them here, causing their OAuth redirects to fail or select the wrong tenant. Merge generated parameters into `u.Query()` instead of replacing the endpoint query.</violation>
</file>
Note: This PR contains a large number of files. cubic only reviews up to 200 files per PR, so some files may not have been reviewed. cubic prioritizes the most important files to review.
Re-trigger cubic
| return certificate, nil | ||
| } | ||
|
|
||
| func (h *renewHandler) Process(_ context.Context, _ coredata.Certificate) error { |
There was a problem hiding this comment.
P0: Certificates selected for renewal are never renewed: Process succeeds without performing work after Claim marks them Renewing. Implement renewal and persist the resulting certificate/status, or avoid claiming this worker until processing exists.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At pkg/certmanager/renew_worker.go, line 94:
<comment>Certificates selected for renewal are never renewed: `Process` succeeds without performing work after `Claim` marks them `Renewing`. Implement renewal and persist the resulting certificate/status, or avoid claiming this worker until processing exists.</comment>
<file context>
@@ -0,0 +1,127 @@
+ return certificate, nil
+}
+
+func (h *renewHandler) Process(_ context.Context, _ coredata.Certificate) error {
+ return nil
+}
</file context>
| FROM organizations o | ||
| WHERE o.id = tc.organization_id AND o.custom_domain_id IS NOT NULL; | ||
|
|
||
| DELETE FROM custom_domains cd |
There was a problem hiding this comment.
P1: Organizations without a trust center lose their configured custom domain during this migration. Preserve or explicitly migrate these rows instead of deleting all domains that have no trust_centers reference.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At pkg/coredata/migrations/20260706T132923Z.sql, line 26:
<comment>Organizations without a trust center lose their configured custom domain during this migration. Preserve or explicitly migrate these rows instead of deleting all domains that have no `trust_centers` reference.</comment>
<file context>
@@ -0,0 +1,33 @@
+FROM organizations o
+WHERE o.id = tc.organization_id AND o.custom_domain_id IS NOT NULL;
+
+DELETE FROM custom_domains cd
+WHERE NOT EXISTS (
+ SELECT 1 FROM trust_centers tc
</file context>
| RawQuery: r.URL.RawQuery, | ||
| } | ||
|
|
||
| http.Redirect(w, r, target.String(), http.StatusMovedPermanently) |
There was a problem hiding this comment.
P1: GraphQL mutations submitted through a secondary portal domain can be retried as body-less GET requests after this 301, rather than reaching the canonical /graphql endpoint. Use a method-preserving permanent redirect (308) for this host canonicalization.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At pkg/server/api/complianceportal/sni_middleware.go, line 88:
<comment>GraphQL mutations submitted through a secondary portal domain can be retried as body-less GET requests after this 301, rather than reaching the canonical `/graphql` endpoint. Use a method-preserving permanent redirect (308) for this host canonicalization.</comment>
<file context>
@@ -57,16 +58,50 @@ func NewSNIMiddleware(trustSvc *trust.Service) func(next http.Handler) http.Hand
+ RawQuery: r.URL.RawQuery,
+ }
+
+ http.Redirect(w, r, target.String(), http.StatusMovedPermanently)
+
+ return
</file context>
| http.Redirect(w, r, target.String(), http.StatusMovedPermanently) | |
| http.Redirect(w, r, target.String(), http.StatusPermanentRedirect) |
| WHERE tc.organization_id = o.id; | ||
|
|
||
| ALTER TABLE organizations | ||
| DROP COLUMN description, |
There was a problem hiding this comment.
P1: Organizations without an existing trust center lose their profile data during this migration: they are excluded from the copy, then their source columns are dropped. Preserve those rows by creating/migrating a trust center first, or retain the organization fields until that backfill can occur.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At pkg/coredata/migrations/20260710T085314Z.sql, line 31:
<comment>Organizations without an existing trust center lose their profile data during this migration: they are excluded from the copy, then their source columns are dropped. Preserve those rows by creating/migrating a trust center first, or retain the organization fields until that backfill can occur.</comment>
<file context>
@@ -0,0 +1,34 @@
+WHERE tc.organization_id = o.id;
+
+ALTER TABLE organizations
+ DROP COLUMN description,
+ DROP COLUMN website_url,
+ DROP COLUMN email,
</file context>
| host = byID[*trustCenter.CustomDomainID].Domain | ||
| case trustCenter.DefaultDomainID != nil && byID[*trustCenter.DefaultDomainID] != nil: | ||
| host = byID[*trustCenter.DefaultDomainID].Domain | ||
| case trustCenter.CustomDomainID != nil && byID[*trustCenter.CustomDomainID] != nil: |
There was a problem hiding this comment.
P1: A trust center with an inactive custom-domain certificate but no loaded default-domain row publishes that unready custom host as its canonical URL. Fall back to the slug-derived default host instead, so links do not direct users to a domain that cannot yet serve HTTPS.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At pkg/complianceportal/resolver/resolver.go, line 81:
<comment>A trust center with an inactive custom-domain certificate but no loaded default-domain row publishes that unready custom host as its canonical URL. Fall back to the slug-derived default host instead, so links do not direct users to a domain that cannot yet serve HTTPS.</comment>
<file context>
@@ -0,0 +1,146 @@
+ host = byID[*trustCenter.CustomDomainID].Domain
+ case trustCenter.DefaultDomainID != nil && byID[*trustCenter.DefaultDomainID] != nil:
+ host = byID[*trustCenter.DefaultDomainID].Domain
+ case trustCenter.CustomDomainID != nil && byID[*trustCenter.CustomDomainID] != nil:
+ host = byID[*trustCenter.CustomDomainID].Domain
+ }
</file context>
| }, | ||
| ) | ||
|
|
||
| r.Handle("/*", webServer) |
There was a problem hiding this comment.
P3: Unknown portal paths are always dispatched to webServer; handleCustomDomain404 is unreachable because /* matches them first. Route unmatched paths through the custom 404 handler (or remove the unused handler) so the intended error response can be produced.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At pkg/server/api/complianceportal/v1/mux.go, line 122:
<comment>Unknown portal paths are always dispatched to `webServer`; `handleCustomDomain404` is unreachable because `/*` matches them first. Route unmatched paths through the custom 404 handler (or remove the unused handler) so the intended error response can be produced.</comment>
<file context>
@@ -0,0 +1,163 @@
+ },
+ )
+
+ r.Handle("/*", webServer)
+ r.NotFound(handleCustomDomain404)
+ },
</file context>
|
|
||
| return &rsa.PublicKey{ | ||
| N: new(big.Int).SetBytes(nBytes), | ||
| E: int(new(big.Int).SetBytes(eBytes).Int64()), |
There was a problem hiding this comment.
P3: Oversized JWK exponents are silently narrowed, so this function can return a key that does not represent the source JWK. Validate that e is positive and fits int before converting; return an error otherwise.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At pkg/crypto/jose/jose.go, line 120:
<comment>Oversized JWK exponents are silently narrowed, so this function can return a key that does not represent the source JWK. Validate that `e` is positive and fits `int` before converting; return an error otherwise.</comment>
<file context>
@@ -97,3 +98,106 @@ func SignJWT(privateKey *rsa.PrivateKey, kid string, claims any) (string, error)
+
+ return &rsa.PublicKey{
+ N: new(big.Int).SetBytes(nBytes),
+ E: int(new(big.Int).SetBytes(eBytes).Int64()),
+ }, nil
+}
</file context>
| type DeleteCompliancePageDomainDialogProps = PropsWithChildren<{ | ||
| domain: string; | ||
| customDomainId: string; | ||
| compliancePageId: string; |
There was a problem hiding this comment.
P3: compliancePageId is a required but unused prop, so callers must supply data this dialog never consumes. Remove it from the prop contract unless it is needed for cache updates.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/console/src/pages/organizations/compliance-page/domain/_components/DeleteCompliancePageDomainDialog.tsx, line 43:
<comment>`compliancePageId` is a required but unused prop, so callers must supply data this dialog never consumes. Remove it from the prop contract unless it is needed for cache updates.</comment>
<file context>
@@ -40,39 +39,36 @@ const deleteCustomDomainMutation = graphql`
type DeleteCompliancePageDomainDialogProps = PropsWithChildren<{
domain: string;
+ customDomainId: string;
+ compliancePageId: string;
}>;
</file context>
| {" "} | ||
| compliance.example.com, | ||
| trust.example.com | ||
| compliance.example.com |
There was a problem hiding this comment.
P3: Domain help renders the same example twice, reducing the usefulness of the examples list. Restore a distinct second example such as trust.example.com.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/console/src/pages/organizations/compliance-page/domain/_components/NewCompliancePageDomainDialog.tsx, line 153:
<comment>Domain help renders the same example twice, reducing the usefulness of the examples list. Restore a distinct second example such as `trust.example.com`.</comment>
<file context>
@@ -154,7 +150,7 @@ export function NewCompliancePageDomainDialog(props: PropsWithChildren) {
{" "}
compliance.example.com,
- trust.example.com
+ compliance.example.com
</p>
</div>
</file context>
| compliance.example.com | |
| trust.example.com |
| customDomain { | ||
| id | ||
| domain | ||
| managed |
| import("#/pages/organizations/compliance-page/domain/CompliancePageDomainPageLoader"), | ||
| ), | ||
| loader: () => { | ||
| throw redirect("brand"); |
There was a problem hiding this comment.
maybe we clean the route and update the links instead
| updated_at | ||
| ) | ||
| SELECT | ||
| translate( |
There was a problem hiding this comment.
why not use pg function that generates gid?
| SELECT | ||
| translate( | ||
| encode( | ||
| substring(decode(translate(pp.trust_center_id, '-_', '+/'), 'base64') FROM 1 FOR 8) |
There was a problem hiding this comment.
use pg functions that we have already to manipulate gids
| ( | ||
| tc.slug || '.' || COALESCE( | ||
| NULLIF(current_setting('probo.trust_center_base_domain', true), ''), | ||
| 'probopage.com' |
There was a problem hiding this comment.
not sure we want this for self managed instances
| @@ -12,4 +12,13 @@ | |||
| -- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | |||
| -- PERFORMANCE OF THIS SOFTWARE. | |||
|
|
|||
| ALTER TYPE connector_provider ADD VALUE IF NOT EXISTS 'YOUSIGN'; | |||
| ALTER TABLE trust_centers | |||
| ADD COLUMN page_title TEXT; | |||
There was a problem hiding this comment.
Be minded that this used to be org name, which was used in headings like "{{name}}'s trust center", but now could be a full title
| @@ -656,6 +705,7 @@ func (impl *Implm) Run( | |||
| IAM: iamService, | |||
| Trust: trustService, | |||
| ESign: esignService, | |||
| CustomDomain: managementService, | |||
There was a problem hiding this comment.
naming looks weird both Field and value
Move range, ETag, and cache handling out of the files API handler so brand logos and other public assets can reuse the same code path. Signed-off-by: Bryan Frimin <bryan@probo.com>
Serve CIMD logos from dedicated endpoints, expose client metadata branding through GraphQL, and resolve branding from OAuth clients. Signed-off-by: Bryan Frimin <bryan@probo.com>
Drop the portal-login redirect, accept continue URLs for magic links, brand emails from the OAuth client when present, and update e2e coverage. Signed-off-by: Bryan Frimin <bryan@probo.com>
Remove the separate portal login page, show OAuth client branding on sign-in, and preserve authorize continue URLs across auth methods. Signed-off-by: Bryan Frimin <bryan@probo.com>
39a4d5d to
601182d
Compare
Summary by cubic
Moves the public trust center to a dedicated compliance portal with OAuth-based auth and a host-root
/graphql, replacing magic links and slug routing. Adds a trust center base domain and per-portal domains, extracts certificate management into a worker-drivencertificatesflow, and switches local ACME tostep-cawith aCaddyHTTP-01 proxy; also verifies OAuth2 ID tokens.App: console
+2623-1885#/lib/relay/useMutation; migrated dialogs/lists, connection keys, and visibility helpers.App: trust
+67-414/graphql; removed path-prefix helpers and the magic-link verify route; simplified connect flow.Other
+132-108pkg/server/api/complianceportal/v1; updated workflows/configs and pointed frontends to/graphql.certificatestable and added a certmanager worker service.step-ca; added aCaddyport-80 proxy for HTTP-01; set default HTTPS to:443and updated.env/compose.Written for commit 601182d. Summary will update on new commits.