Skip to content

Conversation

@getvictor
Copy link
Member

@getvictor getvictor commented Oct 20, 2025

Related issue: Resolves #34533

This is the first sub-task out of several. Changes file will be added in a subsequent PR.

Checklist for submitter

If some of the following don't apply, delete the relevant line.

  • Input data is properly validated, SELECT * is avoided, SQL injection is prevented (using placeholders for values in statements)

Testing

  • Added/updated automated tests
  • QA'd all new/changed functionality manually

New Fleet configuration settings

  • Setting(s) is/are explicitly excluded from GitOps

Summary by CodeRabbit

  • New Features

    • Added Okta Conditional Access support (IDP, ACS URL, audience, certificate) and exposed conditional access in AppConfig/API
    • App activity logging for adding/removing Okta conditional access
  • Bug Fixes

    • Fixed typo in conditional access validation messaging
  • Tests

    • Added tests for Okta Conditional Access lifecycle, license gating, and GitOps export exclusion
  • Documentation

    • Added audit-log entries for Okta conditional access add/delete

@getvictor
Copy link
Member Author

@coderabbitai full review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 20, 2025

✅ Actions performed

Full review triggered.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 20, 2025

Walkthrough

Pass global ConditionalAccess settings through validation and add Okta conditional-access fields to AppConfig/API; validation and ModifyAppConfig updated, tests and activity entries added for Okta lifecycle, and team-level validation calls updated to use the global settings.

Changes

Cohort / File(s) Summary
AppConfig / Conditional Access model
server/fleet/app.go, tools/cloner-check/generated_files/appconfig.txt
Added ConditionalAccessSettings with Microsoft Entra and Okta fields; exposed AppConfig.ConditionalAccess and added OktaConfigured(); updated deep copy behavior.
AppConfig endpoint & modify logic
server/service/appconfig.go
Move ConditionalAccess into AppConfig payload, trim Okta fields, enforce "all-or-none" Okta partial updates, validate ACS URL and PEM certificate content, gate Okta fields by premium license, and emit added/deleted Okta activities.
Validation logic
server/fleet/integrations.go
ValidateConditionalAccessIntegration signature updated to accept *ConditionalAccessSettings; refactored to check whether Microsoft Entra or Okta is configured (entraConfigured / oktaConfigured) and error if enabling without any configured integration.
Team-level callers
ee/server/service/teams.go
Three call sites (ModifyTeam, createTeamFromSpec, editTeamFromSpec) now pass appCfg.ConditionalAccess into ValidateConditionalAccessIntegration.
Activities / Audit
server/fleet/activities.go, docs/Contributing/reference/audit-logs.md
Added two new activities: added_conditional_access_okta and deleted_conditional_access_okta; documented them in audit logs reference.
Tests (integration & core)
server/service/integration_enterprise_test.go, server/service/integration_core_test.go
Added TestAppConfigOktaConditionalAccess (enterprise tests) covering Okta set/get/clear and activity assertions; added license-gating assertion for Okta conditional_access in TestAppConfig.
CLI/generate tests
cmd/fleetctl/fleetctl/generate_gitops_test.go
Added test(s) ensuring generated GitOps/org settings omit conditional_access Okta/Microsoft fields; duplicate test was introduced.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant UI as API Caller
participant Service as fleet Service (ModifyTeam / ModifyAppConfig)
participant AppCfg as AppConfig (ConditionalAccessSettings)
participant Validator as ValidateConditionalAccessIntegration
participant Integrations as Conditional Access Integrations store

UI->>Service: request to create/edit team or update AppConfig
Service->>AppCfg: load current AppConfig (includes ConditionalAccess)
Service->>Validator: ValidateConditionalAccessIntegration(ctx, AppCfg.ConditionalAccess, currentEnabled, newEnabled)
Validator->>Integrations: ConditionalAccessMicrosoftGet(ctx)
Integrations-->>Validator: Entra integration info
Validator->>AppCfg: call AppCfg.ConditionalAccess.OktaConfigured()
alt any integration configured
    Validator-->>Service: validation OK
else none configured & enabling requested
    Validator-->>Service: return error (no integration configured)
end
Service-->>UI: respond success or validation error

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • sgress454
  • lucasmrod
  • ghernandez345
  • mna

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title Check ✅ Passed The pull request title "Okta conditional access configs" is a clear, concise, and specific description of the main change being implemented. It directly reflects the core objective of adding Okta configuration support for conditional access, which is evident across multiple files including the new ConditionalAccessSettings struct with Okta fields, API endpoint modifications, activity type additions, and comprehensive test coverage. The title is sufficiently descriptive that a teammate reviewing the commit history would immediately understand this changeset concerns Okta conditional access configuration implementation.
Linked Issues Check ✅ Passed The code changes comprehensively address the objectives specified in linked issue #34533 ("Okta config API"). The implementation includes: a ConditionalAccessSettings struct with Okta configuration fields (OktaIDPID, OktaAssertionConsumerServiceURL, OktaAudienceURI, OktaCertificate) in server/fleet/app.go; API modifications in server/service/appconfig.go that expose these settings via PATCH /api/latest/fleet/config with validation and premium license enforcement; extended validation logic in server/fleet/integrations.go to support Okta alongside Microsoft Entra; and critically, activity types ActivityTypeAddedConditionalAccessOkta and ActivityTypeDeletedConditionalAccessOkta in server/fleet/activities.go to provide activity updates for configuring and deleting Okta configs as required. Comprehensive tests in server/service/integration_enterprise_test.go validate the Okta configuration lifecycle, license checks, and activity logging.
Out of Scope Changes Check ✅ Passed All code changes in this pull request are directly scoped to the Okta conditional access configuration implementation as defined in issue #34533. The modifications across eight core files establish the Okta config data model, API endpoints, validation, and activity tracking. Supporting changes include: teams.go passing conditional access config to validation (enabling Okta config usage in team operations), GitOps exclusion tests confirming Okta settings are intentionally omitted from GitOps export (as required by the checklist), audit log documentation for new activity types, and tooling updates. The generated files tracking appconfig changes are automatically maintained. No changes introduce unrelated functionality or departures from the stated objectives.
Description Check ✅ Passed The PR description includes the required related issue reference in the correct format ("Resolves #34533") and addresses the key sections from the template. The submitter has appropriately selected and checked relevant checklist items (input validation, automated tests, manual QA, and GitOps exclusion) while deleting items that don't apply, which aligns with the template instruction to "delete the relevant line" if something doesn't apply. The note explaining that the changes file will be added in a subsequent PR demonstrates awareness of the checklist requirements even though it's deferred. The description covers the primary compliance areas including validation/security, testing, and GitOps configuration handling.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch victor/34533-okta-config

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (12)
server/fleet/app.go (1)

607-609: Document Copy() handling for ConditionalAccess field.

While the ConditionalAccess field contains only value types (making shallow copy safe), the explicit warning comments on lines 241-243 and 619-621 indicate that all new fields should be considered in the Copy() implementation. Consider adding a comment in the Copy() method documenting that this field doesn't require special handling due to its value-type composition.

Add a comment in the Copy() method around line 796:

 	if c.YaraRules != nil {
 		rules := make([]YaraRule, len(c.YaraRules))
 		copy(rules, c.YaraRules)
 		clone.YaraRules = rules
 	}
+
+	// ConditionalAccess: nothing needs cloning (contains only value types)
 
 	return &clone
 }
server/fleet/integrations.go (1)

423-439: Avoid false positives; treat Okta “configured” only when all fields are non-empty

Current oktaConfigured checks only OktaIDPID and doesn’t trim whitespace. A payload with spaces can bypass the “no integration configured” guard; also a single field present doesn’t mean Okta is actually configured.

Apply one of the following:

Option A — stricter inline check:

- oktaConfigured := conditionalAccessSettings.OktaIDPID.Valid &&
-   conditionalAccessSettings.OktaIDPID.Value != ""
+ oktaConfigured := 
+   conditionalAccessSettings.OktaIDPID.Valid &&
+   strings.TrimSpace(conditionalAccessSettings.OktaIDPID.Value) != "" &&
+   conditionalAccessSettings.OktaAssertionConsumerServiceURL.Valid &&
+   strings.TrimSpace(conditionalAccessSettings.OktaAssertionConsumerServiceURL.Value) != "" &&
+   conditionalAccessSettings.OktaAudienceURI.Valid &&
+   strings.TrimSpace(conditionalAccessSettings.OktaAudienceURI.Value) != "" &&
+   conditionalAccessSettings.OktaCertificate.Valid &&
+   strings.TrimSpace(conditionalAccessSettings.OktaCertificate.Value) != ""

Option B — add ConditionalAccessSettings.OktaConfigured() helper and call it here for consistency.

server/service/appconfig.go (5)

136-145: Clear Entra fields when no integration is found

Avoid stale TenantID/Configured values if ConditionalAccessMicrosoftGet returns nil.

 if conditionalAccessIntegration != nil {
   appConfig.ConditionalAccess.MicrosoftEntraTenantID = conditionalAccessIntegration.TenantID
   appConfig.ConditionalAccess.MicrosoftEntraConnectionConfigured = conditionalAccessIntegration.SetupDone
+} else {
+  appConfig.ConditionalAccess.MicrosoftEntraTenantID = ""
+  appConfig.ConditionalAccess.MicrosoftEntraConnectionConfigured = false
 }

486-493: Order-of-ops note: avoid whitespace-only Okta values appearing “configured”

ValidateConditionalAccessIntegration is called before you trim Okta fields. Either:

  • make ValidateConditionalAccessIntegration trim (preferred, see suggestion in fleet/integrations.go), or
  • move the Okta trimming block above this call.

Current behavior can briefly treat " " as configured.


495-506: License gate should trigger only when setting non-empty values

Right now, empty strings with Valid=true trip the premium check, blocking clears on Free. Gate only when any Okta field is being set to a non-empty value.

- oktaFieldsBeingSet := (newAppConfig.ConditionalAccess.OktaIDPID.Set && newAppConfig.ConditionalAccess.OktaIDPID.Valid) ||
-   (newAppConfig.ConditionalAccess.OktaAssertionConsumerServiceURL.Set && newAppConfig.ConditionalAccess.OktaAssertionConsumerServiceURL.Valid) ||
-   (newAppConfig.ConditionalAccess.OktaAudienceURI.Set && newAppConfig.ConditionalAccess.OktaAudienceURI.Valid) ||
-   (newAppConfig.ConditionalAccess.OktaCertificate.Set && newAppConfig.ConditionalAccess.OktaCertificate.Valid)
+ isNonEmpty := func(s optjson.String) bool {
+   return s.Set && s.Valid && strings.TrimSpace(s.Value) != ""
+ }
+ oktaFieldsBeingSet := isNonEmpty(newAppConfig.ConditionalAccess.OktaIDPID) ||
+   isNonEmpty(newAppConfig.ConditionalAccess.OktaAssertionConsumerServiceURL) ||
+   isNonEmpty(newAppConfig.ConditionalAccess.OktaAudienceURI) ||
+   isNonEmpty(newAppConfig.ConditionalAccess.OktaCertificate)

545-561: Tighten ACS URL and certificate validation

  • Require a host in ACS URL and use ParseRequestURI for stricter parsing.
  • Validate PEM is a valid x509 CERTIFICATE. Support multiple PEM blocks.
- acsURL, err := url.Parse(appConfig.ConditionalAccess.OktaAssertionConsumerServiceURL.Value)
- if err != nil || (acsURL.Scheme != "http" && acsURL.Scheme != "https") {
+ acsURL, err := url.ParseRequestURI(appConfig.ConditionalAccess.OktaAssertionConsumerServiceURL.Value)
+ if err != nil || ((acsURL.Scheme != "http" && acsURL.Scheme != "https") || acsURL.Host == "") {
   invalid.Append("conditional_access.okta_assertion_consumer_service_url",
-    "must be a valid URL with http or https scheme")
+    "must be a valid URL with http or https scheme and a host")
 }
 
- block, _ := pem.Decode([]byte(appConfig.ConditionalAccess.OktaCertificate.Value))
- if block == nil {
-   invalid.Append("conditional_access.okta_certificate",
-     "must be valid PEM format")
- }
+ // Validate one or more PEM-encoded CERTIFICATE blocks and parse each
+ rest := []byte(appConfig.ConditionalAccess.OktaCertificate.Value)
+ certCount := 0
+ for {
+   block, r := pem.Decode(rest)
+   if block == nil {
+     break
+   }
+   rest = r
+   if block.Type != "CERTIFICATE" {
+     invalid.Append("conditional_access.okta_certificate", "PEM block must be a CERTIFICATE")
+     break
+   }
+   if _, err := x509.ParseCertificate(block.Bytes); err != nil {
+     invalid.Append("conditional_access.okta_certificate", "must be a valid x509 certificate")
+     break
+   }
+   certCount++
+ }
+ if certCount == 0 {
+   invalid.Append("conditional_access.okta_certificate", "must contain at least one PEM-encoded certificate")
+ }

708-711: Add activities for Okta config add/remove (per PR objective)

PR objective mentions activities for configuring and deleting Okta configs. Consider emitting activities when Okta becomes configured/unconfigured (transition) similar to Conditional Access enable/disable events.

I can draft a minimal patch that computes before/after OktaConfigured() and emits ActivityTypeConfiguredOktaConditionalAccess / ActivityTypeDeletedOktaConditionalAccess (or new types if needed). Want me to open a follow-up?

Also applies to: 969-994

server/service/integration_enterprise_test.go (5)

21346-21355: Build JSON payloads with json.Marshal instead of fmt.Sprintf.

Safer and clearer, especially for multiline PEM and future field additions. Use a small helper to emit nulls when pointers are nil.

Example:

func oktaPayload(idp, acs, aud, cert *string) []byte {
	body := map[string]any{
		"conditional_access": map[string]any{
			"okta_idp_id":                           idp,
			"okta_assertion_consumer_service_url":   acs,
			"okta_audience_uri":                     aud,
			"okta_certificate":                      cert,
		},
	}
	b, _ := json.Marshal(body)
	return b
}

Usage:

- s.DoRaw("PATCH", "/api/latest/fleet/config", []byte(fmt.Sprintf(`{ ... "okta_certificate": %q ... }`, validCert)), http.StatusOK)
+ s.DoRaw("PATCH", "/api/latest/fleet/config", oktaPayload(&idp, &acs, &aud, &validCert), http.StatusOK)

- s.DoRaw("PATCH", "/api/latest/fleet/config", []byte(`{ "conditional_access": { "okta_idp_id": "..." } }`), http.StatusUnprocessableEntity)
+ s.DoRaw("PATCH", "/api/latest/fleet/config", oktaPayload(&idp, nil, nil, nil), http.StatusUnprocessableEntity)

Also applies to: 21330-21345, 21365-21374, 21375-21384


21324-21329: Strengthen the baseline assertions (or drop them if relying on explicit setup).

If you keep the baseline check, assert all four Okta fields are empty to catch partial state pollution.

- require.False(t, acResp.ConditionalAccess.OktaIDPID.Valid)
+ require.False(t, acResp.ConditionalAccess.OktaIDPID.Valid)
+ require.False(t, acResp.ConditionalAccess.OktaAssertionConsumerServiceURL.Valid)
+ require.False(t, acResp.ConditionalAccess.OktaAudienceURI.Valid)
+ require.False(t, acResp.ConditionalAccess.OktaCertificate.Valid)

21365-21374: Add a case for invalid URL scheme (http) to harden validation coverage.

Right now you cover a malformed URL; add an http-scheme ACS URL which should also be rejected.

badACS := "http://dev-579.okta.com/sso/saml2/0oaqu0748bP2ELUlJ5d7"
s.DoRaw("PATCH", "/api/latest/fleet/config",
	oktaPayload(&idp, &badACS, &aud, &validCert),
	http.StatusUnprocessableEntity)

21330-21345: Consider checking error messages for partial updates.

Asserting 422 is good; also assert the response message mentions that all 4 Okta fields must be provided together to avoid silent regressions in error text.


21300-21430: Optional: structure as subtests and factor constants.

Use t.Run per scenario and extract the repeated URLs into constants to reduce duplication and ease future changes.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1647819 and 5b29b95.

📒 Files selected for processing (6)
  • ee/server/service/teams.go (3 hunks)
  • server/fleet/app.go (2 hunks)
  • server/fleet/integrations.go (1 hunks)
  • server/service/appconfig.go (4 hunks)
  • server/service/integration_core_test.go (1 hunks)
  • server/service/integration_enterprise_test.go (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

⚙️ CodeRabbit configuration file

When reviewing SQL queries that are added or modified, ensure that appropriate filtering criteria are applied—especially when a query is intended to return data for a specific entity (e.g., a single host). Check for missing WHERE clauses or incorrect filtering that could lead to incorrect or non-deterministic results (e.g., returning the first row instead of the correct one). Flag any queries that may return unintended results due to lack of precise scoping.

Files:

  • server/fleet/app.go
  • server/fleet/integrations.go
  • ee/server/service/teams.go
  • server/service/integration_enterprise_test.go
  • server/service/integration_core_test.go
  • server/service/appconfig.go
🔇 Additional comments (7)
ee/server/service/teams.go (1)

284-291: LGTM! Consistent parameter passing for conditional access validation.

All three call sites to ValidateConditionalAccessIntegration have been consistently updated to pass appCfg.ConditionalAccess, enabling validation to consider global conditional access configuration (Microsoft Entra and Okta settings) during team creation and modification operations.

Also applies to: 1162-1169, 1479-1486

server/fleet/app.go (1)

96-101: Validation rule is properly enforced.

The validation requirement that all four Okta fields must be set together or all must be empty is correctly implemented in server/service/appconfig.go (lines 521-543). The code counts valid, non-empty Okta fields and rejects partial configurations. Additional validation also ensures the ACS URL is a valid HTTP/HTTPS URL and the certificate is valid PEM format. Comprehensive integration tests in server/service/integration_enterprise_test.go verify this behavior across multiple scenarios.

server/service/appconfig.go (4)

16-16: Import looks good


200-206: Response wiring LGTM

Including ConditionalAccess directly on AppConfig and passing through related fields looks correct.

Also applies to: 208-215


507-520: applyOptString helper LGTM


521-544: All-or-none Okta field requirement LGTM

Simple and clear.

server/fleet/integrations.go (1)

406-415: Call sites verified: signature change fully applied

All 4 invocations of ValidateConditionalAccessIntegration have been correctly updated with the new ConditionalAccessSettings parameter:

  • server/service/appconfig.go:488 — appConfig flow
  • ee/server/service/teams.go:284, 1162, 1479 — team flows (3 sites)

Each call properly passes all five parameters in the correct order: context, service interface, conditional access settings, old enabled value, and new enabled value.

@codecov
Copy link

codecov bot commented Oct 20, 2025

Codecov Report

❌ Patch coverage is 77.65363% with 40 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.20%. Comparing base (6726bb1) to head (51ea46a).
⚠️ Report is 59 commits behind head on main.

Files with missing lines Patch % Lines
server/service/appconfig.go 80.66% 20 Missing and 9 partials ⚠️
...vityFeed/GlobalActivityItem/GlobalActivityItem.tsx 0.00% 6 Missing ⚠️
server/fleet/app.go 75.00% 2 Missing and 1 partial ⚠️
server/fleet/integrations.go 75.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##             main   #34566    +/-   ##
========================================
  Coverage   64.20%   64.20%            
========================================
  Files        2063     2063            
  Lines      208401   208552   +151     
  Branches     6977     7077   +100     
========================================
+ Hits       133796   133907   +111     
- Misses      64105    64135    +30     
- Partials    10500    10510    +10     
Flag Coverage Δ
backend 65.31% <80.34%> (+<0.01%) ⬆️
frontend 53.70% <0.00%> (-0.02%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@getvictor
Copy link
Member Author

@coderabbitai full review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 21, 2025

✅ Actions performed

Full review triggered.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (3)
server/service/integration_enterprise_test.go (3)

19110-19112: Nice: test isolation via t.Cleanup.

This addresses prior state‑leak concerns. LGTM.


21489-21491: Good cleanup for Okta test.

Per‑test cleanup avoids cross‑test interference. LGTM.


21508-21527: Do not embed a real third‑party certificate — generate a self‑signed PEM at runtime.

Avoid shipping a real Okta PEM (includes org/email metadata). Generate a self‑signed test cert instead.

Apply:

- // Valid PEM certificate for testing (real Okta certificate)
- validCert := `-----BEGIN CERTIFICATE-----
- ...
- -----END CERTIFICATE-----`
+ // Valid PEM certificate for testing (self‑signed, generated at runtime)
+ validCert := genTestPEMCert(t)

Add once in this file (outside the test):

// test-only helper
func genTestPEMCert(t *testing.T) string {
	t.Helper()
	priv, err := rsa.GenerateKey(rand.Reader, 2048)
	require.NoError(t, err)

	tmpl := x509.Certificate{
		SerialNumber: big.NewInt(1),
		NotBefore:    time.Now().Add(-time.Hour),
		NotAfter:     time.Now().Add(365 * 24 * time.Hour),
		KeyUsage:     x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
		ExtKeyUsage:  []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		Subject:      pkix.Name{CommonName: "test.example"},
	}

	der, err := x509.CreateCertificate(rand.Reader, &tmpl, &tmpl, &priv.PublicKey, priv)
	require.NoError(t, err)

	var buf bytes.Buffer
	require.NoError(t, pem.Encode(&buf, &pem.Block{Type: "CERTIFICATE", Bytes: der}))
	return buf.String()
}

Required imports at top:

import (
	"bytes"
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"math/big"
	"time"
)
🧹 Nitpick comments (5)
server/service/integration_enterprise_test.go (2)

21529-21533: Use obvious placeholders for example Okta URLs.

Prefer non‑realistic hostnames to avoid implying affiliation.

-idp := "https://www.okta.com/saml2/service-provider/spaubuaqdunfbsmoxyhl"
-acs := "https://dev-579.okta.com/sso/saml2/0oaqu0748bP2ELUlJ5d7"
-aud := "https://www.okta.com/saml2/service-provider/spaubuaqdunfbsmoxyhl"
+idp := "https://example.okta.com/saml2/service-provider/spa-example"
+acs := "https://example.okta.com/sso/saml2/0oabcdef123456"
+aud := idp

21555-21563: Tighten assertions: check Valid=true for all Okta fields after set.

Currently only OktaIDPID.Valid is asserted. Add the rest to catch partial state.

 s.DoJSON("GET", "/api/latest/fleet/config", nil, http.StatusOK, &acResp)
-require.True(t, acResp.ConditionalAccess.OktaIDPID.Valid)
+require.True(t, acResp.ConditionalAccess.OktaIDPID.Valid)
+require.True(t, acResp.ConditionalAccess.OktaAssertionConsumerServiceURL.Valid)
+require.True(t, acResp.ConditionalAccess.OktaAudienceURI.Valid)
+require.True(t, acResp.ConditionalAccess.OktaCertificate.Valid)
 assert.Equal(t, idp, acResp.ConditionalAccess.OktaIDPID.Value)
 assert.Equal(t, acs, acResp.ConditionalAccess.OktaAssertionConsumerServiceURL.Value)
 assert.Equal(t, aud, acResp.ConditionalAccess.OktaAudienceURI.Value)
 assert.Equal(t, validCert, acResp.ConditionalAccess.OktaCertificate.Value)
server/fleet/app.go (1)

104-114: Tiny dedupe in OktaConfigured().

Consider a small helper isSet := func(s optjson.String) bool { return s.Valid && s.Value != "" } to reduce repetition.

server/service/appconfig.go (2)

558-613: Okta input validation: solid; two small considerations.

  • ACS URL: http/https + host validation is good.
  • Certificate: robust multi-PEM parse with x509 check is good.

Consider:

  • AudienceURI format: many SAML setups use URNs; if you plan to enforce syntax, explicitly allow urn:… and http(s), or keep length-only checks (current approach).
  • maxCertLength 8KB may be tight if multiple certs are pasted; bump to 16KB to avoid surprising rejects.

Would you like me to adjust AudienceURI validation and increase the certificate size cap?


1057-1090: Activities for Okta changes: distinguish add vs edit.

You emit AddedConditionalAccessOkta for both add and edit. Consider a dedicated EditedConditionalAccessOkta to keep the audit trail precise.

I can add a new activity type and wire tests; want a patch?

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6726bb1 and f02889c.

📒 Files selected for processing (10)
  • cmd/fleetctl/fleetctl/generate_gitops_test.go (2 hunks)
  • docs/Contributing/reference/audit-logs.md (1 hunks)
  • ee/server/service/teams.go (3 hunks)
  • server/fleet/activities.go (2 hunks)
  • server/fleet/app.go (3 hunks)
  • server/fleet/integrations.go (1 hunks)
  • server/service/appconfig.go (5 hunks)
  • server/service/integration_core_test.go (1 hunks)
  • server/service/integration_enterprise_test.go (5 hunks)
  • tools/cloner-check/generated_files/appconfig.txt (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

⚙️ CodeRabbit configuration file

When reviewing SQL queries that are added or modified, ensure that appropriate filtering criteria are applied—especially when a query is intended to return data for a specific entity (e.g., a single host). Check for missing WHERE clauses or incorrect filtering that could lead to incorrect or non-deterministic results (e.g., returning the first row instead of the correct one). Flag any queries that may return unintended results due to lack of precise scoping.

Files:

  • server/fleet/activities.go
  • ee/server/service/teams.go
  • server/fleet/integrations.go
  • cmd/fleetctl/fleetctl/generate_gitops_test.go
  • server/fleet/app.go
  • server/service/integration_enterprise_test.go
  • server/service/appconfig.go
  • server/service/integration_core_test.go
🧠 Learnings (1)
📚 Learning: 2025-10-21T16:04:18.019Z
Learnt from: getvictor
PR: fleetdm/fleet#34566
File: server/service/integration_core_test.go:7500-7511
Timestamp: 2025-10-21T16:04:18.019Z
Learning: Okta conditional access app config in Fleet is Premium-gated and supported both on-prem and in Fleet Cloud; the Cloud-only enforcement applies to the Microsoft compliance partner endpoints, not to the Okta settings.

Applied to files:

  • server/service/integration_core_test.go
🔇 Additional comments (27)
server/fleet/activities.go (3)

227-228: LGTM!

The new activity types are properly registered in ActivityDetailsList and positioned logically alongside related conditional access activities.


2792-2801: Verify the dual-purpose design of the "added" activity.

The documentation states "Generated when Okta is configured or edited for conditional access," suggesting this single activity type handles both initial configuration and subsequent edits. This differs from other resources (e.g., scripts, profiles, software) which have separate "added" and "edited" activity types.

Please confirm this design is intentional for Okta conditional access. If edits should be tracked separately from initial configuration, consider adding an ActivityTypeEditedConditionalAccessOkta activity type.


2803-2812: LGTM!

The deletion activity implementation is correct and follows the established pattern for conditional access integrations.

docs/Contributing/reference/audit-logs.md (1)

2070-2081: Documentation correctly reflects the implementation.

The audit log entries accurately document the new Okta conditional access activities and are properly positioned among related entries. The "configured or edited" wording is consistent with the code implementation in activities.go.

server/service/integration_core_test.go (1)

7597-7607: Thanks for covering the license guard.

This check keeps the Okta conditional-access settings aligned with the existing premium-only behavior, mirroring the other licensing safeguards in the suite.

ee/server/service/teams.go (3)

284-289: LGTM! Consistent parameter addition for conditional access validation.

The addition of appCfg.ConditionalAccess as a parameter to ValidateConditionalAccessIntegration correctly passes the global conditional access settings for validation. The error handling is already in place and appropriate.


1162-1169: LGTM! Consistent parameter addition for conditional access validation.

The addition of appCfg.ConditionalAccess as a parameter to ValidateConditionalAccessIntegration correctly passes the global conditional access settings for validation during team creation from spec.


1479-1486: LGTM! Consistent parameter addition for conditional access validation.

The addition of appCfg.ConditionalAccess as a parameter to ValidateConditionalAccessIntegration correctly passes the global conditional access settings for validation during team editing from spec.

cmd/fleetctl/fleetctl/generate_gitops_test.go (2)

20-20: LGTM! Appropriate test dependency added.

The assert package import is correctly added to support the new test assertions.


762-810: LGTM! Comprehensive test coverage for Okta config exclusion.

The test thoroughly verifies that Okta conditional access configurations are excluded from GitOps output by:

  1. Setting up Okta fields in the app config
  2. Checking the absence of the conditional_access key in the YAML map
  3. Verifying the YAML string doesn't contain any Okta-related field names

This provides good coverage for the intended behavior.

tools/cloner-check/generated_files/appconfig.txt (1)

187-193: LGTM! Generated file updated with new ConditionalAccessSettings structure.

The generated declarations correctly reflect the addition of AppConfig.ConditionalAccess and its nested fields for Microsoft Entra and Okta configuration. The structure is consistent with the existing patterns in this file.

server/service/integration_enterprise_test.go (3)

19212-19215: LGTM: Entra defaults asserted clearly.


19249-19252: LGTM: Post‑delete Entra defaults re‑validated.


21634-21639: ****

No separate "Edited" or "Updated" activity type exists for Okta conditional access. The ActivityTypeAddedConditionalAccessOkta type is designed to handle both add and edit scenarios, as documented: "Generated when Okta is configured or edited for conditional access." The test correctly uses this type for the edit operation, consistent with production code behavior in server/service/appconfig.go.

Likely an incorrect or invalid review comment.

server/fleet/app.go (3)

88-102: Okta CA shape looks good; invariants are clear.

Using optjson.String enables partial PATCH semantics; comment about “all-or-none” aligns with service-level validation. LGTM.


619-622: AppConfig: new ConditionalAccess field.

Public surface change acknowledged. Ensure call sites deep-copy or treat as read-only (Copy() handles this below). LGTM.

If any external serializers rely on a previous response shape for ConditionalAccess, please confirm no breaking changes in clients.


816-821: Copy(): deep copy of ConditionalAccess is sufficient.

Struct contains only value fields; shallow struct copy is fine. LGTM.

server/fleet/integrations.go (1)

405-441: Enable CA only if Entra or Okta is configured.

Logic is correct; nil receiver on OktaConfigured() safely returns false. Clear error path. LGTM.

server/service/appconfig.go (9)

16-16: Import added.

strings import is needed for trimming Okta fields. LGTM.


136-156: Populate Microsoft Entra fields into AppConfig.ConditionalAccess.

Initialization + clear-on-delete is correct. One question: ConditionalAccess is included in AppConfig for all viewers; do we intend to expose Okta cert/URIs and Entra tenant status to non-admins?

If not intended, gate ConditionalAccess similar to SMTP/SSO for non-admins.


211-217: Response assembly includes ConditionalAccess.

Consistent with new embedding approach. LGTM.


219-226: Response fields unchanged in behavior.

No concerns.


497-504: Nil-guard for ConditionalAccess in Modify flow.

Good defensive init for pointer fields.


505-518: Whitespace normalization + partial update helper.

applyOptString is a neat pattern; trimming only when Valid is appropriate. LGTM.


519-533: License gate on Okta updates.

Allows clears on Free (by setting fields to empty/null) but requires Premium when setting non-empty values. Confirm this policy is desired.

If clears should also be gated, extend oktaFieldsBeingSet to consider Set with Valid=false as a mutation.


534-557: All-or-none enforcement for Okta fields.

Counting non-empty Valid fields after applying updates is correct for PATCH behavior. LGTM.


614-621: Validation call wired to include ConditionalAccess settings.

Correctly passes Okta/Entra state into the guard. LGTM.

@getvictor getvictor marked this pull request as ready for review October 21, 2025 20:05
@getvictor getvictor requested review from a team, lukeheath and rachaelshaw as code owners October 21, 2025 20:05
Copy link
Member

@jacobshandling jacobshandling left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

frontend/* LGTM (did not review other changes)

if err := svc.NewActivity(
ctx,
authz.UserFromContext(ctx),
fleet.ActivityTypeAddedConditionalAccessOkta{},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So there's no updated or changed activity type? Just added and deleted?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we are using the same type for added or changed.

"must be a valid URL with http or https scheme and a host")
}

// Validate one or more PEM-encoded CERTIFICATE blocks and parse each
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably add a test for a case where there are multiple certificate blocks

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Can I do that in a follow up PR? So product/frontend doesn't have to re-approve just for a test change?
I will also try to refactor this section into its own method for readability.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up PR sounds good 👍

Copy link
Member

@dantecatalfamo dantecatalfamo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple of small changes for a follow-up PR, otherwise LGTM 👍

@getvictor getvictor merged commit 0db1b47 into main Oct 24, 2025
48 checks passed
@getvictor getvictor deleted the victor/34533-okta-config branch October 24, 2025 15:11
mna pushed a commit that referenced this pull request Oct 27, 2025
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #34533

This is the first sub-task out of several. Changes file will be added in
a subsequent PR.

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)

## Testing

- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually

## New Fleet configuration settings

- [x] Setting(s) is/are explicitly **excluded** from GitOps

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added Okta Conditional Access support (IDP, ACS URL, audience,
certificate) and exposed conditional access in AppConfig/API
  * App activity logging for adding/removing Okta conditional access

* **Bug Fixes**
  * Fixed typo in conditional access validation messaging

* **Tests**
* Added tests for Okta Conditional Access lifecycle, license gating, and
GitOps export exclusion

* **Documentation**
  * Added audit-log entries for Okta conditional access add/delete
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Okta config API

5 participants