2016-09-29 02:44:05 +00:00
|
|
|
package service
|
|
|
|
|
|
|
|
|
|
import (
|
2021-09-14 12:11:07 +00:00
|
|
|
"context"
|
2026-04-02 20:56:31 +00:00
|
|
|
"crypto/ecdsa"
|
2026-03-13 20:47:09 +00:00
|
|
|
"crypto/sha256"
|
2023-01-31 14:46:01 +00:00
|
|
|
"crypto/tls"
|
2026-04-02 20:56:31 +00:00
|
|
|
"crypto/x509"
|
2025-11-13 23:10:24 +00:00
|
|
|
"encoding/base64"
|
2026-03-13 20:47:09 +00:00
|
|
|
"encoding/hex"
|
2021-09-15 19:27:53 +00:00
|
|
|
"encoding/json"
|
2022-10-05 12:35:36 +00:00
|
|
|
"fmt"
|
2022-08-30 11:13:09 +00:00
|
|
|
"io"
|
2026-02-28 11:52:21 +00:00
|
|
|
"log/slog"
|
2021-09-15 19:27:53 +00:00
|
|
|
"net/http"
|
2021-07-16 18:28:13 +00:00
|
|
|
"net/http/httptest"
|
|
|
|
|
"os"
|
2024-04-16 21:40:29 +00:00
|
|
|
"sort"
|
2026-04-03 14:58:03 +00:00
|
|
|
"strings"
|
2021-12-23 21:26:55 +00:00
|
|
|
"sync"
|
2016-09-29 02:44:05 +00:00
|
|
|
"testing"
|
2024-10-31 19:24:42 +00:00
|
|
|
"time"
|
2016-09-29 02:44:05 +00:00
|
|
|
|
|
|
|
|
"github.com/WatchBeam/clock"
|
2026-03-13 20:47:09 +00:00
|
|
|
ma "github.com/fleetdm/fleet/v4/ee/maintained-apps"
|
2025-04-04 16:00:46 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/ee/server/scim"
|
2021-11-12 11:18:25 +00:00
|
|
|
eeservice "github.com/fleetdm/fleet/v4/ee/server/service"
|
2025-11-06 23:07:17 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/ee/server/service/condaccess"
|
2025-03-20 16:36:00 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/ee/server/service/digicert"
|
2025-11-04 21:27:15 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/ee/server/service/est"
|
Add SCEP endpoint for host identity. (#30589)
Fixes #30458
Contributor docs PR: https://github.com/fleetdm/fleet/pull/30651
# Checklist for submitter
- We will add changes file later.
- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)
- [x] If database migrations are included, checked table schema to
confirm autoupdate
- For database migrations:
- [x] Checked schema for all modified table for columns that will
auto-update timestamps during migration.
- [x] Confirmed that updating the timestamps is acceptable, and will not
cause unwanted side effects.
- [x] Ensured the correct collation is explicitly set for character
columns (`COLLATE utf8mb4_unicode_ci`).
- [x] Added/updated automated tests
- Did not do manual QA since the SCEP client I have doesn't support ECC.
Will rely on next subtasks for manual QA.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Introduced Host Identity SCEP (Simple Certificate Enrollment Protocol)
support, enabling secure host identity certificate enrollment and
management.
* Added new API endpoints for Host Identity SCEP, including certificate
issuance and retrieval.
* Implemented MySQL-backed storage and management for host identity SCEP
certificates and serials.
* Added new database tables for storing host identity SCEP certificates
and serial numbers.
* Provided utilities for encoding certificates and keys, and handling
ECDSA public keys.
* **Bug Fixes**
* None.
* **Tests**
* Added comprehensive integration and unit tests for Host Identity SCEP
functionality, including certificate issuance, validation, and error
scenarios.
* **Chores**
* Updated test utilities to support unique test names and new SCEP
storage options.
* Extended mock datastore and interfaces for new host identity
certificate methods.
* **Documentation**
* Added comments and documentation for new SCEP-related interfaces,
methods, and database schema changes.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-07-11 14:44:07 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/ee/server/service/hostidentity"
|
2025-07-16 18:08:27 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/ee/server/service/hostidentity/httpsig"
|
speed up macOS profile delivery for initial enrollments (#41960)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #34433
It speeds up the cron, meaning fleetd, bootstrap and now profiles should
be sent within 10 seconds of being known to fleet, compared to the
previous 1 minute.
It's heavily based on my last PR, so the structure and changes are close
to identical, with some small differences.
**I did not do the redis key part in this PR, as I think that should
come in it's own PR, to avoid overlooking logic bugs with that code, and
since this one is already quite sized since we're moving core pieces of
code around.**
# Checklist for submitter
If some of the following don't apply, delete the relevant line.
- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.
## Testing
- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Faster macOS onboarding: device profiles are delivered and installed
as part of DEP enrollment, shortening initial setup.
* Improved profile handling: per-host profile preprocessing, secret
detection, and clearer failure marking.
* **Improvements**
* Consolidated SCEP/NDES error messaging for clearer diagnostics.
* Cron/work scheduling tuned to prioritize Apple MDM profile delivery.
* **Tests**
* Expanded MDM unit and integration tests, including
DeclarativeManagement handling.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-03-19 19:58:10 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/ee/server/service/scep"
|
2026-04-02 20:56:31 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/acl/acmeacl"
|
2026-01-19 14:07:14 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/acl/activityacl"
|
2026-02-25 20:11:03 +00:00
|
|
|
activity_api "github.com/fleetdm/fleet/v4/server/activity/api"
|
2026-01-19 14:07:14 +00:00
|
|
|
activity_bootstrap "github.com/fleetdm/fleet/v4/server/activity/bootstrap"
|
|
|
|
|
"github.com/fleetdm/fleet/v4/server/authz"
|
2021-06-26 04:46:51 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/config"
|
2024-10-31 19:24:42 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/contexts/ctxerr"
|
2022-11-15 14:08:05 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/contexts/license"
|
2023-10-04 20:02:55 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/datastore/cached_mysql"
|
2024-04-24 14:18:58 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/datastore/filesystem"
|
2025-09-26 18:03:50 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/datastore/redis"
|
2025-10-09 14:22:44 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/datastore/redis/redistest"
|
2026-03-13 20:47:09 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/dev_mode"
|
2024-10-31 19:24:42 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/errorstore"
|
2021-06-26 04:46:51 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/fleet"
|
2021-11-12 11:18:25 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/logging"
|
2023-03-28 18:23:15 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/mail"
|
2026-04-02 20:56:31 +00:00
|
|
|
acme_bootstrap "github.com/fleetdm/fleet/v4/server/mdm/acme/bootstrap"
|
2025-11-13 23:10:24 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/mdm/android"
|
|
|
|
|
android_mock "github.com/fleetdm/fleet/v4/server/mdm/android/mock"
|
|
|
|
|
android_service "github.com/fleetdm/fleet/v4/server/mdm/android/service"
|
2023-03-27 19:30:29 +00:00
|
|
|
apple_mdm "github.com/fleetdm/fleet/v4/server/mdm/apple"
|
2026-03-13 20:47:09 +00:00
|
|
|
maintained_apps "github.com/fleetdm/fleet/v4/server/mdm/maintainedapps"
|
2023-06-29 22:31:53 +00:00
|
|
|
microsoft_mdm "github.com/fleetdm/fleet/v4/server/mdm/microsoft"
|
2024-02-26 15:26:00 +00:00
|
|
|
nanodep_storage "github.com/fleetdm/fleet/v4/server/mdm/nanodep/storage"
|
2024-01-12 02:28:48 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/mdm/nanomdm/mdm"
|
|
|
|
|
"github.com/fleetdm/fleet/v4/server/mdm/nanomdm/push"
|
|
|
|
|
nanomdm_push "github.com/fleetdm/fleet/v4/server/mdm/nanomdm/push"
|
2026-04-02 20:56:31 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/mdm/scep/depot"
|
2024-02-22 18:13:46 +00:00
|
|
|
scep_depot "github.com/fleetdm/fleet/v4/server/mdm/scep/depot"
|
2026-02-25 20:11:03 +00:00
|
|
|
fleet_mock "github.com/fleetdm/fleet/v4/server/mock"
|
2023-04-27 12:43:20 +00:00
|
|
|
nanodep_mock "github.com/fleetdm/fleet/v4/server/mock/nanodep"
|
2026-01-06 20:23:07 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/platform/endpointer"
|
2026-01-19 14:07:14 +00:00
|
|
|
common_mysql "github.com/fleetdm/fleet/v4/server/platform/mysql"
|
2021-06-26 04:46:51 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/ptr"
|
2021-11-12 11:18:25 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/service/async"
|
2026-01-19 14:07:14 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/service/middleware/auth"
|
2026-04-02 20:56:31 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/service/middleware/log"
|
2023-01-31 14:46:01 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/service/mock"
|
2024-09-20 14:55:47 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/service/redis_key_value"
|
2024-07-10 13:49:05 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/service/redis_lock"
|
2021-11-23 13:25:43 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/sso"
|
2022-05-18 17:03:00 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/test"
|
2026-01-19 14:07:14 +00:00
|
|
|
"github.com/go-kit/kit/endpoint"
|
2023-01-31 14:46:01 +00:00
|
|
|
"github.com/google/uuid"
|
2021-11-12 11:18:25 +00:00
|
|
|
"github.com/stretchr/testify/assert"
|
2016-09-29 02:44:05 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
2022-04-19 13:35:53 +00:00
|
|
|
"github.com/throttled/throttled/v2"
|
2021-07-16 18:28:13 +00:00
|
|
|
"github.com/throttled/throttled/v2/store/memstore"
|
2025-11-13 23:10:24 +00:00
|
|
|
"google.golang.org/api/androidmanagement/v1"
|
2016-09-29 02:44:05 +00:00
|
|
|
)
|
|
|
|
|
|
2022-11-15 14:08:05 +00:00
|
|
|
func newTestService(t *testing.T, ds fleet.Datastore, rs fleet.QueryResultStore, lq fleet.LiveQueryStore, opts ...*TestServerOpts) (fleet.Service, context.Context) {
|
2022-03-11 15:51:12 +00:00
|
|
|
return newTestServiceWithConfig(t, ds, config.TestConfig(), rs, lq, opts...)
|
2021-07-30 15:45:49 +00:00
|
|
|
}
|
|
|
|
|
|
2022-11-15 14:08:05 +00:00
|
|
|
func newTestServiceWithConfig(t *testing.T, ds fleet.Datastore, fleetConfig config.FleetConfig, rs fleet.QueryResultStore, lq fleet.LiveQueryStore, opts ...*TestServerOpts) (fleet.Service, context.Context) {
|
|
|
|
|
lic := &fleet.LicenseInfo{Tier: fleet.TierFree}
|
2026-02-28 11:52:21 +00:00
|
|
|
logger := slog.New(slog.DiscardHandler)
|
|
|
|
|
writer, err := logging.NewFilesystemLogWriter(t.Context(), fleetConfig.Filesystem.StatusLogFile, logger, fleetConfig.Filesystem.EnableLogRotation,
|
Changes needed before gokit/log to slog transition. (#39527)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #38889
PLEASE READ BELOW before looking at file changes
Before converting individual files/packages to slog, we generally need
to make these 2 changes to make the conversion easier:
- Replace uses of `kitlog.With` since they are not fully compatible with
our kitlog adapter
- Directly use the kitlog adapter logger type instead of the kitlog
interface, which will let us have direct access to the underlying slog
logger: `*logging.Logger`
Note: that I did not replace absolutely all uses of `kitlog.Logger`, but
I did remove all uses of `kitlog.With` except for these due to
complexity:
- server/logging/filesystem.go and the other log writers (webhook,
firehose, kinesis, lambda, pubsub, nats)
- server/datastore/mysql/nanomdm_storage.go (adapter pattern)
- server/vulnerabilities/nvd/* (cascades to CLI tools)
- server/service/osquery_utils/queries.go (callback type signatures
cascade broadly)
- cmd/maintained-apps/ (standalone, so can be transitioned later all at
once)
Most of the changes in this PR follow these patterns:
- `kitlog.Logger` type → `*logging.Logger`
- `kitlog.With(logger, ...)` → `logger.With(...)`
- `kitlog.NewNopLogger() → logging.NewNopLogger()`, including similar
variations such as `logging.NewLogfmtLogger(w)` and
`logging.NewJSONLogger(w)`
- removed many now-unused kitlog imports
Unique changes that the PR review should focus on:
- server/platform/logging/kitlog_adapter.go: Core adapter changes
- server/platform/logging/logging.go: New convenience functions
- server/service/integration_logger_test.go: Test changes for slog
# Checklist for submitter
If some of the following don't apply, delete the relevant line.
- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
- Was added in previous PR
## Testing
- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Refactor**
* Migrated the codebase to a unified internal structured logging system
for more consistent, reliable logs and observability.
* No user-facing functionality changed; runtime behavior and APIs remain
compatible.
* **Tests**
* Updated tests to use the new logging helpers to ensure consistent test
logging and validation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-02-11 16:08:33 +00:00
|
|
|
fleetConfig.Filesystem.EnableLogCompression, 500, 28, 3)
|
2022-03-11 15:51:12 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
2022-12-23 22:04:13 +00:00
|
|
|
osqlogger := &OsqueryLogger{Status: writer, Result: writer}
|
2021-11-23 13:25:43 +00:00
|
|
|
|
2022-06-13 20:29:32 +00:00
|
|
|
var (
|
2025-06-11 17:22:46 +00:00
|
|
|
failingPolicySet fleet.FailingPolicySet = NewMemFailingPolicySet()
|
|
|
|
|
enrollHostLimiter fleet.EnrollHostLimiter = nopEnrollHostLimiter{}
|
|
|
|
|
depStorage nanodep_storage.AllDEPStorage = &nanodep_mock.Storage{}
|
|
|
|
|
mailer fleet.MailService = &mockMailService{SendEmailFn: func(e fleet.Email) error { return nil }}
|
|
|
|
|
c clock.Clock = clock.C
|
speed up macOS profile delivery for initial enrollments (#41960)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #34433
It speeds up the cron, meaning fleetd, bootstrap and now profiles should
be sent within 10 seconds of being known to fleet, compared to the
previous 1 minute.
It's heavily based on my last PR, so the structure and changes are close
to identical, with some small differences.
**I did not do the redis key part in this PR, as I think that should
come in it's own PR, to avoid overlooking logic bugs with that code, and
since this one is already quite sized since we're moving core pieces of
code around.**
# Checklist for submitter
If some of the following don't apply, delete the relevant line.
- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.
## Testing
- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Faster macOS onboarding: device profiles are delivered and installed
as part of DEP enrollment, shortening initial setup.
* Improved profile handling: per-host profile preprocessing, secret
detection, and clearer failure marking.
* **Improvements**
* Consolidated SCEP/NDES error messaging for clearer diagnostics.
* Cron/work scheduling tuned to prioritize Apple MDM profile delivery.
* **Tests**
* Expanded MDM unit and integration tests, including
DeclarativeManagement handling.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-03-19 19:58:10 +00:00
|
|
|
scepConfigService = scep.NewSCEPConfigService(logger, nil)
|
2026-02-28 11:52:21 +00:00
|
|
|
digiCertService = digicert.NewService(digicert.WithLogger(logger))
|
|
|
|
|
estCAService = est.NewService(est.WithLogger(logger))
|
2025-06-11 17:22:46 +00:00
|
|
|
conditionalAccessMicrosoftProxy ConditionalAccessMicrosoftProxy
|
2023-05-31 13:24:22 +00:00
|
|
|
|
2025-09-05 22:31:03 +00:00
|
|
|
mdmStorage fleet.MDMAppleStore
|
|
|
|
|
mdmPusher nanomdm_push.Pusher
|
|
|
|
|
ssoStore sso.SessionStore
|
|
|
|
|
profMatcher fleet.ProfileMatcher
|
|
|
|
|
softwareInstallStore fleet.SoftwareInstallerStore
|
|
|
|
|
bootstrapPackageStore fleet.MDMBootstrapPackageStore
|
|
|
|
|
softwareTitleIconStore fleet.SoftwareTitleIconStore
|
|
|
|
|
distributedLock fleet.Lock
|
|
|
|
|
keyValueStore fleet.KeyValueStore
|
2025-12-18 19:59:46 +00:00
|
|
|
androidService android.Service
|
2022-06-13 20:29:32 +00:00
|
|
|
)
|
2022-05-16 13:44:50 +00:00
|
|
|
if len(opts) > 0 {
|
|
|
|
|
if opts[0].Clock != nil {
|
|
|
|
|
c = opts[0].Clock
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-05-10 15:29:17 +00:00
|
|
|
|
2024-09-20 14:55:47 +00:00
|
|
|
if len(opts) > 0 && opts[0].KeyValueStore != nil {
|
|
|
|
|
keyValueStore = opts[0].KeyValueStore
|
|
|
|
|
}
|
|
|
|
|
|
OpenTelemetry minor improvements (#32324)
Fixes #32313
OpenTelemetry Tracing
- Added tracing to async task collectors: FlushHostsLastSeen,
collectHostsLastSeen, collectLabelQueryExecutions,
collectPolicyQueryExecutions, collectScheduledQueryStats
- Updated HTTP middleware to use OTEL semantic convention for span names
({method} {route})
- Added OTELEnabled() helper to FleetConfig
Optimizations
- Reduced OTEL batch size from 512 to 256 spans to prevent gRPC message
size errors
- Enabled gzip compression for trace exports
NOTE: I tried to improve OTEL instrumentation for cron jobs, but it got
too complicated due to goroutines in `schedule.go` so that effort should
be separate. We do have SQL instrumentation for cron jobs, but we are
missing root spans for cron jobs as a whole.
# Checklist for submitter
If some of the following don't apply, delete the relevant line.
- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
## Testing
- [x] QA'd all new/changed functionality manually
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Expanded OpenTelemetry tracing for async tasks (host last seen, label
membership, policy membership, scheduled query stats) to provide richer
observability.
* More descriptive HTTP span names using “METHOD /route” for clearer
trace analysis.
* **Bug Fixes**
* Improved OTLP gRPC exporter reliability by enabling gzip compression
and reducing export batch size, mitigating intermittent gRPC errors.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-29 00:32:46 +00:00
|
|
|
task := async.NewTask(ds, nil, c, nil)
|
2022-05-16 13:44:50 +00:00
|
|
|
if len(opts) > 0 {
|
|
|
|
|
if opts[0].Task != nil {
|
|
|
|
|
task = opts[0].Task
|
|
|
|
|
} else {
|
|
|
|
|
opts[0].Task = task
|
|
|
|
|
}
|
2022-05-10 15:29:17 +00:00
|
|
|
}
|
|
|
|
|
|
2021-08-26 13:28:53 +00:00
|
|
|
if len(opts) > 0 {
|
|
|
|
|
if opts[0].Logger != nil {
|
|
|
|
|
logger = opts[0].Logger
|
|
|
|
|
}
|
|
|
|
|
if opts[0].License != nil {
|
2022-11-15 14:08:05 +00:00
|
|
|
lic = opts[0].License
|
2021-08-26 13:28:53 +00:00
|
|
|
}
|
2021-11-23 13:25:43 +00:00
|
|
|
if opts[0].Pool != nil {
|
|
|
|
|
ssoStore = sso.NewSessionStore(opts[0].Pool)
|
2023-05-31 13:24:22 +00:00
|
|
|
profMatcher = apple_mdm.NewProfileMatcher(opts[0].Pool)
|
2024-07-10 13:49:05 +00:00
|
|
|
distributedLock = redis_lock.NewLock(opts[0].Pool)
|
2024-09-20 14:55:47 +00:00
|
|
|
keyValueStore = redis_key_value.New(opts[0].Pool)
|
2021-11-23 13:25:43 +00:00
|
|
|
}
|
2023-06-05 19:08:21 +00:00
|
|
|
if opts[0].ProfileMatcher != nil {
|
|
|
|
|
profMatcher = opts[0].ProfileMatcher
|
|
|
|
|
}
|
2021-12-23 21:26:55 +00:00
|
|
|
if opts[0].FailingPolicySet != nil {
|
|
|
|
|
failingPolicySet = opts[0].FailingPolicySet
|
|
|
|
|
}
|
2022-06-13 20:29:32 +00:00
|
|
|
if opts[0].EnrollHostLimiter != nil {
|
|
|
|
|
enrollHostLimiter = opts[0].EnrollHostLimiter
|
|
|
|
|
}
|
2023-03-28 18:23:15 +00:00
|
|
|
if opts[0].UseMailService {
|
2023-04-06 18:21:07 +00:00
|
|
|
mailer, err = mail.NewService(config.TestConfig())
|
|
|
|
|
require.NoError(t, err)
|
2023-03-28 18:23:15 +00:00
|
|
|
}
|
2024-04-24 14:18:58 +00:00
|
|
|
if opts[0].SoftwareInstallStore != nil {
|
|
|
|
|
softwareInstallStore = opts[0].SoftwareInstallStore
|
|
|
|
|
}
|
2024-08-13 12:27:10 +00:00
|
|
|
if opts[0].BootstrapPackageStore != nil {
|
|
|
|
|
bootstrapPackageStore = opts[0].BootstrapPackageStore
|
|
|
|
|
}
|
2025-09-05 22:31:03 +00:00
|
|
|
if opts[0].SoftwareTitleIconStore != nil {
|
|
|
|
|
softwareTitleIconStore = opts[0].SoftwareTitleIconStore
|
|
|
|
|
}
|
2022-07-18 16:44:30 +00:00
|
|
|
|
2022-10-05 22:53:54 +00:00
|
|
|
// allow to explicitly set MDM storage to nil
|
|
|
|
|
mdmStorage = opts[0].MDMStorage
|
2023-04-27 12:43:20 +00:00
|
|
|
if opts[0].DEPStorage != nil {
|
|
|
|
|
depStorage = opts[0].DEPStorage
|
|
|
|
|
}
|
2022-10-05 22:53:54 +00:00
|
|
|
// allow to explicitly set mdm pusher to nil
|
|
|
|
|
mdmPusher = opts[0].MDMPusher
|
2021-08-24 17:35:03 +00:00
|
|
|
}
|
2022-05-10 15:29:17 +00:00
|
|
|
|
2022-11-15 14:08:05 +00:00
|
|
|
ctx := license.NewContext(context.Background(), lic)
|
2022-11-28 19:28:06 +00:00
|
|
|
|
|
|
|
|
cronSchedulesService := fleet.NewCronSchedules()
|
|
|
|
|
|
2024-10-31 19:24:42 +00:00
|
|
|
var eh *errorstore.Handler
|
|
|
|
|
if len(opts) > 0 {
|
|
|
|
|
if opts[0].Pool != nil {
|
2026-02-28 11:52:21 +00:00
|
|
|
eh = errorstore.NewHandler(ctx, opts[0].Pool, logger, time.Minute*10)
|
2024-10-31 19:24:42 +00:00
|
|
|
ctx = ctxerr.NewContext(ctx, eh)
|
|
|
|
|
}
|
|
|
|
|
if opts[0].StartCronSchedules != nil {
|
|
|
|
|
for _, fn := range opts[0].StartCronSchedules {
|
|
|
|
|
err = cronSchedulesService.StartCronSchedule(fn(ctx, ds))
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
}
|
2022-11-28 19:28:06 +00:00
|
|
|
}
|
|
|
|
|
}
|
2025-03-14 17:16:51 +00:00
|
|
|
if len(opts) > 0 && opts[0].SCEPConfigService != nil {
|
|
|
|
|
scepConfigService = opts[0].SCEPConfigService
|
|
|
|
|
}
|
2025-03-20 16:36:00 +00:00
|
|
|
if len(opts) > 0 && opts[0].DigiCertService != nil {
|
|
|
|
|
digiCertService = opts[0].DigiCertService
|
|
|
|
|
}
|
2025-06-11 17:22:46 +00:00
|
|
|
if len(opts) > 0 && opts[0].ConditionalAccessMicrosoftProxy != nil {
|
|
|
|
|
conditionalAccessMicrosoftProxy = opts[0].ConditionalAccessMicrosoftProxy
|
|
|
|
|
fleetConfig.MicrosoftCompliancePartner.ProxyAPIKey = "insecure" // setting this so the feature is "enabled".
|
|
|
|
|
}
|
2022-11-28 19:28:06 +00:00
|
|
|
|
2025-12-18 19:59:46 +00:00
|
|
|
if len(opts) > 0 && opts[0].androidModule != nil {
|
|
|
|
|
androidService = opts[0].androidModule
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-29 22:31:53 +00:00
|
|
|
var wstepManager microsoft_mdm.CertManager
|
2023-07-05 13:06:37 +00:00
|
|
|
if fleetConfig.MDM.WindowsWSTEPIdentityCert != "" && fleetConfig.MDM.WindowsWSTEPIdentityKey != "" {
|
|
|
|
|
rawCert, err := os.ReadFile(fleetConfig.MDM.WindowsWSTEPIdentityCert)
|
2023-06-29 22:31:53 +00:00
|
|
|
require.NoError(t, err)
|
2023-07-05 13:06:37 +00:00
|
|
|
rawKey, err := os.ReadFile(fleetConfig.MDM.WindowsWSTEPIdentityKey)
|
2023-06-29 22:31:53 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
wstepManager, err = microsoft_mdm.NewCertManager(ds, rawCert, rawKey)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-05 22:53:54 +00:00
|
|
|
svc, err := NewService(
|
2022-11-15 14:08:05 +00:00
|
|
|
ctx,
|
2022-10-05 22:53:54 +00:00
|
|
|
ds,
|
|
|
|
|
task,
|
|
|
|
|
rs,
|
2026-02-28 11:52:21 +00:00
|
|
|
logger,
|
2022-10-05 22:53:54 +00:00
|
|
|
osqlogger,
|
|
|
|
|
fleetConfig,
|
|
|
|
|
mailer,
|
|
|
|
|
c,
|
|
|
|
|
ssoStore,
|
|
|
|
|
lq,
|
|
|
|
|
ds,
|
|
|
|
|
failingPolicySet,
|
|
|
|
|
&fleet.NoOpGeoIP{},
|
|
|
|
|
enrollHostLimiter,
|
|
|
|
|
depStorage,
|
|
|
|
|
mdmStorage,
|
|
|
|
|
mdmPusher,
|
2022-11-28 19:28:06 +00:00
|
|
|
cronSchedulesService,
|
2023-06-29 22:31:53 +00:00
|
|
|
wstepManager,
|
2025-03-14 17:16:51 +00:00
|
|
|
scepConfigService,
|
2025-03-20 16:36:00 +00:00
|
|
|
digiCertService,
|
2025-06-11 17:22:46 +00:00
|
|
|
conditionalAccessMicrosoftProxy,
|
2025-10-09 14:22:44 +00:00
|
|
|
keyValueStore,
|
2025-12-18 19:59:46 +00:00
|
|
|
androidService,
|
2022-10-05 22:53:54 +00:00
|
|
|
)
|
2021-07-19 19:48:49 +00:00
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
2022-11-15 14:08:05 +00:00
|
|
|
if lic.IsPremium() {
|
2024-04-24 14:18:58 +00:00
|
|
|
if softwareInstallStore == nil {
|
|
|
|
|
// default to file-based
|
|
|
|
|
dir := t.TempDir()
|
|
|
|
|
store, err := filesystem.NewSoftwareInstallerStore(dir)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
softwareInstallStore = store
|
|
|
|
|
}
|
2025-11-13 23:10:24 +00:00
|
|
|
|
|
|
|
|
var androidModule android.Service
|
|
|
|
|
if len(opts) > 0 {
|
|
|
|
|
androidModule = opts[0].androidModule
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-22 20:11:44 +00:00
|
|
|
svc, err = eeservice.NewService(
|
|
|
|
|
svc,
|
|
|
|
|
ds,
|
2026-02-28 11:52:21 +00:00
|
|
|
logger,
|
2023-02-22 20:11:44 +00:00
|
|
|
fleetConfig,
|
|
|
|
|
mailer,
|
|
|
|
|
c,
|
|
|
|
|
depStorage,
|
2024-05-30 21:18:42 +00:00
|
|
|
apple_mdm.NewMDMAppleCommander(mdmStorage, mdmPusher),
|
2023-04-27 12:43:20 +00:00
|
|
|
ssoStore,
|
2023-05-31 13:24:22 +00:00
|
|
|
profMatcher,
|
2024-04-24 14:18:58 +00:00
|
|
|
softwareInstallStore,
|
2024-08-13 12:27:10 +00:00
|
|
|
bootstrapPackageStore,
|
2025-09-05 22:31:03 +00:00
|
|
|
softwareTitleIconStore,
|
2024-07-10 13:49:05 +00:00
|
|
|
distributedLock,
|
2024-09-20 14:55:47 +00:00
|
|
|
keyValueStore,
|
2025-09-04 16:39:41 +00:00
|
|
|
scepConfigService,
|
|
|
|
|
digiCertService,
|
2025-11-13 23:10:24 +00:00
|
|
|
androidModule,
|
2025-11-04 21:27:15 +00:00
|
|
|
estCAService,
|
2023-02-22 20:11:44 +00:00
|
|
|
)
|
2021-08-26 13:28:53 +00:00
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
2025-11-13 23:10:24 +00:00
|
|
|
|
2021-07-19 19:48:49 +00:00
|
|
|
}
|
2026-02-25 20:11:03 +00:00
|
|
|
|
|
|
|
|
// Set up mock activity service for unit tests. When DBConns is provided,
|
|
|
|
|
// RunServerForTestsWithServiceWithDS will overwrite this with the real bounded context.
|
2026-03-09 02:54:06 +00:00
|
|
|
activityMock := &fleet_mock.MockActivityService{
|
2026-02-25 20:11:03 +00:00
|
|
|
NewActivityFunc: func(_ context.Context, _ *activity_api.User, _ activity_api.ActivityDetails) error {
|
|
|
|
|
return nil
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
svc.SetActivityService(activityMock)
|
|
|
|
|
if len(opts) > 0 {
|
|
|
|
|
opts[0].ActivityMock = activityMock
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 20:56:31 +00:00
|
|
|
// Set up mock ACME service for unit tests. When DBConns is provided,
|
|
|
|
|
// RunServerForTestsWithServiceWithDS will overwrite this with the real service module.
|
|
|
|
|
svc.SetACMEService(&fleet_mock.MockACMEService{})
|
|
|
|
|
|
2022-11-15 14:08:05 +00:00
|
|
|
return svc, ctx
|
2021-07-19 19:48:49 +00:00
|
|
|
}
|
|
|
|
|
|
2022-11-15 14:08:05 +00:00
|
|
|
func newTestServiceWithClock(t *testing.T, ds fleet.Datastore, rs fleet.QueryResultStore, lq fleet.LiveQueryStore, c clock.Clock) (fleet.Service, context.Context) {
|
2021-07-30 15:45:49 +00:00
|
|
|
testConfig := config.TestConfig()
|
2022-11-15 14:08:05 +00:00
|
|
|
return newTestServiceWithConfig(t, ds, testConfig, rs, lq, &TestServerOpts{
|
2021-12-23 21:26:55 +00:00
|
|
|
Clock: c,
|
|
|
|
|
})
|
2016-12-20 21:54:30 +00:00
|
|
|
}
|
|
|
|
|
|
2021-06-06 22:07:29 +00:00
|
|
|
func createTestUsers(t *testing.T, ds fleet.Datastore) map[string]fleet.User {
|
|
|
|
|
users := make(map[string]fleet.User)
|
2024-04-16 21:40:29 +00:00
|
|
|
// Map iteration is random so we sort and iterate using the testUsers keys.
|
|
|
|
|
var keys []string
|
|
|
|
|
for key := range testUsers {
|
|
|
|
|
keys = append(keys, key)
|
|
|
|
|
}
|
|
|
|
|
sort.Strings(keys)
|
Add read replica testing helpers and fix non-sso login bug (#4908)
not set on the INSERT.
- OUT: Only sets the ID on the passed session and returns it. (`CreatedAt`, `AccessedAt`, are not set.)
New version:
```go
func (ds *Datastore) NewSession(ctx context.Context, userID uint, sessionKey string) (*fleet.Session, error) {
sqlStatement := `
INSERT INTO sessions (
user_id,
` + "`key`" + `
)
VALUES(?,?)
`
result, err := ds.writer.ExecContext(ctx, sqlStatement, userID, sessionKey)
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "inserting session")
}
id, _ := result.LastInsertId() // cannot fail with the mysql driver
return ds.sessionByID(ctx, ds.writer, uint(id))
}
```
- IN: Define arguments that are truly used when creating a session.
- OUT: Load and return the fleet.Session struct with all values set (using the `ds.writer` to support read replicas correctly).
PS: The new `NewSession` version mimics what we already do with other entities, like policies (`Datastore.NewGlobalPolicy`).
2022-04-04 23:52:05 +00:00
|
|
|
userID := uint(1)
|
2024-04-16 21:40:29 +00:00
|
|
|
for _, key := range keys {
|
|
|
|
|
u := testUsers[key]
|
2021-06-06 22:07:29 +00:00
|
|
|
user := &fleet.User{
|
2024-04-16 21:40:29 +00:00
|
|
|
ID: userID, // We need to set this in case ds is a mocked Datastore.
|
2021-07-08 15:50:43 +00:00
|
|
|
Name: "Test Name " + u.Email,
|
|
|
|
|
Email: u.Email,
|
2025-04-04 16:00:46 +00:00
|
|
|
GlobalRole: u.GlobalRole,
|
2016-09-29 02:44:05 +00:00
|
|
|
}
|
|
|
|
|
err := user.SetPassword(u.PlaintextPassword, 10, 10)
|
|
|
|
|
require.Nil(t, err)
|
2021-09-14 12:11:07 +00:00
|
|
|
user, err = ds.NewUser(context.Background(), user)
|
2016-09-29 02:44:05 +00:00
|
|
|
require.Nil(t, err)
|
2021-06-24 20:42:29 +00:00
|
|
|
users[user.Email] = *user
|
Add read replica testing helpers and fix non-sso login bug (#4908)
not set on the INSERT.
- OUT: Only sets the ID on the passed session and returns it. (`CreatedAt`, `AccessedAt`, are not set.)
New version:
```go
func (ds *Datastore) NewSession(ctx context.Context, userID uint, sessionKey string) (*fleet.Session, error) {
sqlStatement := `
INSERT INTO sessions (
user_id,
` + "`key`" + `
)
VALUES(?,?)
`
result, err := ds.writer.ExecContext(ctx, sqlStatement, userID, sessionKey)
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "inserting session")
}
id, _ := result.LastInsertId() // cannot fail with the mysql driver
return ds.sessionByID(ctx, ds.writer, uint(id))
}
```
- IN: Define arguments that are truly used when creating a session.
- OUT: Load and return the fleet.Session struct with all values set (using the `ds.writer` to support read replicas correctly).
PS: The new `NewSession` version mimics what we already do with other entities, like policies (`Datastore.NewGlobalPolicy`).
2022-04-04 23:52:05 +00:00
|
|
|
userID++
|
2016-09-29 02:44:05 +00:00
|
|
|
}
|
|
|
|
|
return users
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-04 16:00:46 +00:00
|
|
|
const (
|
|
|
|
|
TestAdminUserEmail = "admin1@example.com"
|
|
|
|
|
TestMaintainerUserEmail = "user1@example.com"
|
|
|
|
|
TestObserverUserEmail = "user2@example.com"
|
|
|
|
|
)
|
|
|
|
|
|
2016-09-29 02:44:05 +00:00
|
|
|
var testUsers = map[string]struct {
|
|
|
|
|
Email string
|
|
|
|
|
PlaintextPassword string
|
2021-05-25 22:46:46 +00:00
|
|
|
GlobalRole *string
|
2016-09-29 02:44:05 +00:00
|
|
|
}{
|
|
|
|
|
"admin1": {
|
2022-05-18 17:03:00 +00:00
|
|
|
PlaintextPassword: test.GoodPassword,
|
2025-04-04 16:00:46 +00:00
|
|
|
Email: TestAdminUserEmail,
|
2021-06-06 22:07:29 +00:00
|
|
|
GlobalRole: ptr.String(fleet.RoleAdmin),
|
2016-09-29 02:44:05 +00:00
|
|
|
},
|
|
|
|
|
"user1": {
|
2022-05-18 17:03:00 +00:00
|
|
|
PlaintextPassword: test.GoodPassword,
|
2025-04-04 16:00:46 +00:00
|
|
|
Email: TestMaintainerUserEmail,
|
2021-06-06 22:07:29 +00:00
|
|
|
GlobalRole: ptr.String(fleet.RoleMaintainer),
|
2016-09-29 02:44:05 +00:00
|
|
|
},
|
|
|
|
|
"user2": {
|
2022-05-18 17:03:00 +00:00
|
|
|
PlaintextPassword: test.GoodPassword,
|
2025-04-04 16:00:46 +00:00
|
|
|
Email: TestObserverUserEmail,
|
2021-06-06 22:07:29 +00:00
|
|
|
GlobalRole: ptr.String(fleet.RoleObserver),
|
2016-09-29 02:44:05 +00:00
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-05 12:35:36 +00:00
|
|
|
func createEnrollSecrets(t *testing.T, count int) []*fleet.EnrollSecret {
|
|
|
|
|
secrets := make([]*fleet.EnrollSecret, count)
|
|
|
|
|
for i := 0; i < count; i++ {
|
|
|
|
|
secrets[i] = &fleet.EnrollSecret{Secret: fmt.Sprintf("testSecret%d", i)}
|
|
|
|
|
}
|
|
|
|
|
return secrets
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-29 02:44:05 +00:00
|
|
|
type mockMailService struct {
|
2021-06-06 22:07:29 +00:00
|
|
|
SendEmailFn func(e fleet.Email) error
|
2016-09-29 02:44:05 +00:00
|
|
|
Invoked bool
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-30 20:45:39 +00:00
|
|
|
func (svc *mockMailService) SendEmail(ctx context.Context, e fleet.Email) error {
|
2016-09-29 02:44:05 +00:00
|
|
|
svc.Invoked = true
|
|
|
|
|
return svc.SendEmailFn(e)
|
|
|
|
|
}
|
2021-07-16 18:28:13 +00:00
|
|
|
|
2024-12-05 14:37:10 +00:00
|
|
|
func (svc *mockMailService) CanSendEmail(smtpSettings fleet.SMTPSettings) bool {
|
|
|
|
|
return smtpSettings.SMTPConfigured
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-28 19:28:06 +00:00
|
|
|
type TestNewScheduleFunc func(ctx context.Context, ds fleet.Datastore) fleet.NewCronScheduleFunc
|
|
|
|
|
|
2025-07-17 11:23:19 +00:00
|
|
|
// HostIdentity combines host identity-related test options
|
|
|
|
|
type HostIdentity struct {
|
|
|
|
|
SCEPStorage scep_depot.Depot
|
|
|
|
|
RequireHTTPMessageSignature bool
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-06 23:07:17 +00:00
|
|
|
// ConditionalAccess combines conditional access-related test options
|
|
|
|
|
type ConditionalAccess struct {
|
|
|
|
|
SCEPStorage scep_depot.Depot
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-19 19:48:49 +00:00
|
|
|
type TestServerOpts struct {
|
2026-02-28 11:52:21 +00:00
|
|
|
Logger *slog.Logger
|
2025-06-11 17:22:46 +00:00
|
|
|
License *fleet.LicenseInfo
|
|
|
|
|
SkipCreateTestUsers bool
|
|
|
|
|
Rs fleet.QueryResultStore
|
|
|
|
|
Lq fleet.LiveQueryStore
|
|
|
|
|
Pool fleet.RedisPool
|
|
|
|
|
FailingPolicySet fleet.FailingPolicySet
|
|
|
|
|
Clock clock.Clock
|
|
|
|
|
Task *async.Task
|
|
|
|
|
EnrollHostLimiter fleet.EnrollHostLimiter
|
|
|
|
|
Is fleet.InstallerStore
|
|
|
|
|
FleetConfig *config.FleetConfig
|
|
|
|
|
MDMStorage fleet.MDMAppleStore
|
|
|
|
|
DEPStorage nanodep_storage.AllDEPStorage
|
|
|
|
|
SCEPStorage scep_depot.Depot
|
|
|
|
|
MDMPusher nanomdm_push.Pusher
|
|
|
|
|
HTTPServerConfig *http.Server
|
|
|
|
|
StartCronSchedules []TestNewScheduleFunc
|
|
|
|
|
UseMailService bool
|
|
|
|
|
APNSTopic string
|
|
|
|
|
ProfileMatcher fleet.ProfileMatcher
|
|
|
|
|
EnableCachedDS bool
|
|
|
|
|
NoCacheDatastore bool
|
|
|
|
|
SoftwareInstallStore fleet.SoftwareInstallerStore
|
|
|
|
|
BootstrapPackageStore fleet.MDMBootstrapPackageStore
|
2025-09-05 22:31:03 +00:00
|
|
|
SoftwareTitleIconStore fleet.SoftwareTitleIconStore
|
2025-06-11 17:22:46 +00:00
|
|
|
KeyValueStore fleet.KeyValueStore
|
|
|
|
|
EnableSCEPProxy bool
|
|
|
|
|
WithDEPWebview bool
|
2026-01-06 20:23:07 +00:00
|
|
|
FeatureRoutes []endpointer.HandlerRoutesFunc
|
2025-06-11 17:22:46 +00:00
|
|
|
SCEPConfigService fleet.SCEPConfigService
|
|
|
|
|
DigiCertService fleet.DigiCertService
|
|
|
|
|
EnableSCIM bool
|
|
|
|
|
ConditionalAccessMicrosoftProxy ConditionalAccessMicrosoftProxy
|
2025-07-17 11:23:19 +00:00
|
|
|
HostIdentity *HostIdentity
|
2025-11-13 23:10:24 +00:00
|
|
|
androidMockClient *android_mock.Client
|
|
|
|
|
androidModule android.Service
|
2025-11-06 23:07:17 +00:00
|
|
|
ConditionalAccess *ConditionalAccess
|
2026-01-19 14:07:14 +00:00
|
|
|
DBConns *common_mysql.DBConnections
|
2026-02-25 20:11:03 +00:00
|
|
|
|
|
|
|
|
// ActivityMock is populated automatically by newTestServiceWithConfig.
|
|
|
|
|
// After setup, tests can use it to intercept or assert on activity creation.
|
2026-03-09 02:54:06 +00:00
|
|
|
ActivityMock *fleet_mock.MockActivityService
|
2026-04-02 20:56:31 +00:00
|
|
|
|
|
|
|
|
ACMECertCA *x509.Certificate
|
|
|
|
|
ACMECertKey *ecdsa.PrivateKey
|
2021-07-19 19:48:49 +00:00
|
|
|
}
|
|
|
|
|
|
2022-05-10 15:29:17 +00:00
|
|
|
func RunServerForTestsWithDS(t *testing.T, ds fleet.Datastore, opts ...*TestServerOpts) (map[string]fleet.User, *httptest.Server) {
|
2023-10-04 20:02:55 +00:00
|
|
|
if len(opts) > 0 && opts[0].EnableCachedDS {
|
|
|
|
|
ds = cached_mysql.New(ds)
|
|
|
|
|
}
|
2022-07-27 19:47:39 +00:00
|
|
|
cfg := config.TestConfig()
|
|
|
|
|
if len(opts) > 0 && opts[0].FleetConfig != nil {
|
|
|
|
|
cfg = *opts[0].FleetConfig
|
|
|
|
|
}
|
2025-03-04 18:04:25 +00:00
|
|
|
svc, ctx := NewTestService(t, ds, cfg, opts...)
|
|
|
|
|
return RunServerForTestsWithServiceWithDS(t, ctx, ds, svc, opts...)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func RunServerForTestsWithServiceWithDS(t *testing.T, ctx context.Context, ds fleet.Datastore, svc fleet.Service,
|
2025-04-29 19:11:31 +00:00
|
|
|
opts ...*TestServerOpts,
|
|
|
|
|
) (map[string]fleet.User, *httptest.Server) {
|
2025-03-04 18:04:25 +00:00
|
|
|
var cfg config.FleetConfig
|
|
|
|
|
if len(opts) > 0 && opts[0].FleetConfig != nil {
|
|
|
|
|
cfg = *opts[0].FleetConfig
|
|
|
|
|
} else {
|
|
|
|
|
cfg = config.TestConfig()
|
|
|
|
|
}
|
2021-08-26 13:28:53 +00:00
|
|
|
users := map[string]fleet.User{}
|
|
|
|
|
if len(opts) == 0 || (len(opts) > 0 && !opts[0].SkipCreateTestUsers) {
|
|
|
|
|
users = createTestUsers(t, ds)
|
2021-07-19 19:48:49 +00:00
|
|
|
}
|
2026-02-28 11:52:21 +00:00
|
|
|
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
|
2021-08-02 22:06:27 +00:00
|
|
|
if len(opts) > 0 && opts[0].Logger != nil {
|
|
|
|
|
logger = opts[0].Logger
|
2021-07-16 18:28:13 +00:00
|
|
|
}
|
2025-11-13 23:10:24 +00:00
|
|
|
|
|
|
|
|
if len(opts) > 0 {
|
|
|
|
|
opts[0].FeatureRoutes = append(opts[0].FeatureRoutes, android_service.GetRoutes(svc, opts[0].androidModule))
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-19 14:07:14 +00:00
|
|
|
// Add activity routes if DBConns is provided
|
|
|
|
|
if len(opts) > 0 && opts[0].DBConns != nil {
|
|
|
|
|
legacyAuthorizer, err := authz.NewAuthorizer()
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
activityAuthorizer := authz.NewAuthorizerAdapter(legacyAuthorizer)
|
2026-02-09 21:29:12 +00:00
|
|
|
activityACLAdapter := activityacl.NewFleetServiceAdapter(svc)
|
2026-02-25 20:11:03 +00:00
|
|
|
activitySvc, activityRoutesFn := activity_bootstrap.New(
|
2026-01-19 14:07:14 +00:00
|
|
|
opts[0].DBConns,
|
|
|
|
|
activityAuthorizer,
|
2026-02-09 21:29:12 +00:00
|
|
|
activityACLAdapter,
|
2026-02-28 11:52:21 +00:00
|
|
|
logger,
|
2026-01-19 14:07:14 +00:00
|
|
|
)
|
2026-02-25 20:11:03 +00:00
|
|
|
svc.SetActivityService(activitySvc)
|
2026-01-19 14:07:14 +00:00
|
|
|
activityAuthMiddleware := func(next endpoint.Endpoint) endpoint.Endpoint {
|
|
|
|
|
return auth.AuthenticatedUser(svc, next)
|
|
|
|
|
}
|
|
|
|
|
opts[0].FeatureRoutes = append(opts[0].FeatureRoutes, activityRoutesFn(activityAuthMiddleware))
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-05 23:52:26 +00:00
|
|
|
var mdmPusher nanomdm_push.Pusher
|
|
|
|
|
if len(opts) > 0 && opts[0].MDMPusher != nil {
|
|
|
|
|
mdmPusher = opts[0].MDMPusher
|
|
|
|
|
}
|
2023-01-16 20:06:30 +00:00
|
|
|
rootMux := http.NewServeMux()
|
|
|
|
|
|
2025-10-09 14:22:44 +00:00
|
|
|
memLimitStore, _ := memstore.New(0)
|
|
|
|
|
var limitStore throttled.GCRAStore = memLimitStore
|
|
|
|
|
var redisPool fleet.RedisPool
|
|
|
|
|
if len(opts) > 0 && opts[0].Pool != nil {
|
|
|
|
|
redisPool = opts[0].Pool
|
|
|
|
|
limitStore = &redis.ThrottledStore{
|
|
|
|
|
Pool: opts[0].Pool,
|
|
|
|
|
KeyPrefix: "ratelimit::",
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
redisPool = redistest.SetupRedis(t, t.Name(), false, false, false) // We are good to initalize a redis pool here as it is only called by integration tests
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 20:56:31 +00:00
|
|
|
// Wire real ACME service module if DBConns is provided (overrides the mock set in newTestServiceWithConfig).
|
|
|
|
|
if len(opts) > 0 && opts[0].DBConns != nil {
|
|
|
|
|
var acmeOpts []acme_bootstrap.ServiceOption
|
|
|
|
|
if opts[0].ACMECertCA != nil && opts[0].ACMECertKey != nil {
|
|
|
|
|
rootCAPool := x509.NewCertPool()
|
|
|
|
|
rootCAPool.AddCert(opts[0].ACMECertCA)
|
|
|
|
|
acmeOpts = append(acmeOpts, acme_bootstrap.WithTestAppleRootCAs(rootCAPool))
|
|
|
|
|
}
|
|
|
|
|
acmeSigner := &acmeCSRSigner{signer: depot.NewSigner(opts[0].SCEPStorage, depot.WithValidityDays(365), depot.WithAllowRenewalDays(14))}
|
|
|
|
|
acmeSvc, acmeRoutes := acme_bootstrap.New(opts[0].DBConns, redisPool, acmeacl.NewFleetDatastoreAdapter(ds, acmeSigner), logger, acmeOpts...)
|
|
|
|
|
svc.SetACMEService(acmeSvc)
|
|
|
|
|
opts[0].FeatureRoutes = append(opts[0].FeatureRoutes, acmeRoutes(log.Logged))
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-16 20:06:30 +00:00
|
|
|
if len(opts) > 0 {
|
|
|
|
|
mdmStorage := opts[0].MDMStorage
|
|
|
|
|
scepStorage := opts[0].SCEPStorage
|
2024-05-30 21:18:42 +00:00
|
|
|
commander := apple_mdm.NewMDMAppleCommander(mdmStorage, mdmPusher)
|
2023-01-16 20:06:30 +00:00
|
|
|
if mdmStorage != nil && scepStorage != nil {
|
2026-01-06 19:04:06 +00:00
|
|
|
vppInstaller := svc.(fleet.AppleMDMVPPInstaller)
|
2026-02-28 11:52:21 +00:00
|
|
|
checkInAndCommand := NewMDMAppleCheckinAndCommandService(ds, commander, vppInstaller, opts[0].License.IsPremium(), logger, redis_key_value.New(redisPool), svc.NewActivity)
|
|
|
|
|
checkInAndCommand.RegisterResultsHandler("InstalledApplicationList", NewInstalledApplicationListResultsHandler(ds, commander, logger, cfg.Server.VPPVerifyTimeout, cfg.Server.VPPVerifyRequestDelay, svc.NewActivity))
|
|
|
|
|
checkInAndCommand.RegisterResultsHandler(fleet.DeviceLocationCmdName, NewDeviceLocationResultsHandler(ds, commander, logger))
|
2026-03-17 14:48:23 +00:00
|
|
|
checkInAndCommand.RegisterResultsHandler(fleet.SetRecoveryLockCmdName, NewSetRecoveryLockResultsHandler(ds, logger, svc.NewActivity))
|
2023-01-16 20:06:30 +00:00
|
|
|
err := RegisterAppleMDMProtocolServices(
|
|
|
|
|
rootMux,
|
2023-03-23 10:30:28 +00:00
|
|
|
cfg.MDM,
|
2023-01-16 20:06:30 +00:00
|
|
|
mdmStorage,
|
|
|
|
|
scepStorage,
|
2026-02-28 11:52:21 +00:00
|
|
|
logger,
|
2025-06-26 21:55:43 +00:00
|
|
|
checkInAndCommand,
|
2024-03-15 18:20:15 +00:00
|
|
|
&MDMAppleDDMService{
|
|
|
|
|
ds: ds,
|
2026-02-28 11:52:21 +00:00
|
|
|
logger: logger,
|
2024-03-15 18:20:15 +00:00
|
|
|
},
|
2024-12-20 21:40:23 +00:00
|
|
|
commander,
|
2025-07-23 11:11:32 +00:00
|
|
|
"https://test-url.com",
|
Added missing OpenTelemetry instrumentation to several API endpoints. (#32960)
Fixes #32331
Manually tested all paths. `/test` path removed in
https://github.com/fleetdm/fleet/pull/32962
Also added support for sending errors to OpenTelemetry, like we do for
APM/Sentry.
# Checklist for submitter
- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.
## Testing
- [x] QA'd all new/changed functionality manually
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added OpenTelemetry tracing across core HTTP endpoints (health,
version, assets, metrics, enroll/root, debug, Apple MDM, SCEP, SCIM)
with dynamic per-request route instrumentation.
* Enhanced error reporting to include OpenTelemetry spans/events with
contextual user/host attributes.
* **Tests**
* Added unit tests validating SCIM and error-handling telemetry, span
naming, and sensitive-data redaction.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-16 16:10:33 +00:00
|
|
|
cfg,
|
2023-01-16 20:06:30 +00:00
|
|
|
)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
}
|
2024-10-09 18:47:27 +00:00
|
|
|
if opts[0].EnableSCEPProxy {
|
2025-03-14 17:16:51 +00:00
|
|
|
var timeout *time.Duration
|
|
|
|
|
if opts[0].SCEPConfigService != nil {
|
speed up macOS profile delivery for initial enrollments (#41960)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #34433
It speeds up the cron, meaning fleetd, bootstrap and now profiles should
be sent within 10 seconds of being known to fleet, compared to the
previous 1 minute.
It's heavily based on my last PR, so the structure and changes are close
to identical, with some small differences.
**I did not do the redis key part in this PR, as I think that should
come in it's own PR, to avoid overlooking logic bugs with that code, and
since this one is already quite sized since we're moving core pieces of
code around.**
# Checklist for submitter
If some of the following don't apply, delete the relevant line.
- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.
## Testing
- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Faster macOS onboarding: device profiles are delivered and installed
as part of DEP enrollment, shortening initial setup.
* Improved profile handling: per-host profile preprocessing, secret
detection, and clearer failure marking.
* **Improvements**
* Consolidated SCEP/NDES error messaging for clearer diagnostics.
* Cron/work scheduling tuned to prioritize Apple MDM profile delivery.
* **Tests**
* Expanded MDM unit and integration tests, including
DeclarativeManagement handling.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-03-19 19:58:10 +00:00
|
|
|
scepConfig, ok := opts[0].SCEPConfigService.(*scep.SCEPConfigService)
|
2025-03-14 17:16:51 +00:00
|
|
|
if ok {
|
|
|
|
|
// In tests, we share the same Timeout pointer between SCEPConfigService and SCEPProxy
|
|
|
|
|
timeout = scepConfig.Timeout
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-10-09 18:47:27 +00:00
|
|
|
err := RegisterSCEPProxy(
|
|
|
|
|
rootMux,
|
|
|
|
|
ds,
|
2026-02-28 11:52:21 +00:00
|
|
|
logger,
|
2025-03-14 17:16:51 +00:00
|
|
|
timeout,
|
Added missing OpenTelemetry instrumentation to several API endpoints. (#32960)
Fixes #32331
Manually tested all paths. `/test` path removed in
https://github.com/fleetdm/fleet/pull/32962
Also added support for sending errors to OpenTelemetry, like we do for
APM/Sentry.
# Checklist for submitter
- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.
## Testing
- [x] QA'd all new/changed functionality manually
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added OpenTelemetry tracing across core HTTP endpoints (health,
version, assets, metrics, enroll/root, debug, Apple MDM, SCEP, SCIM)
with dynamic per-request route instrumentation.
* Enhanced error reporting to include OpenTelemetry spans/events with
contextual user/host attributes.
* **Tests**
* Added unit tests validating SCIM and error-handling telemetry, span
naming, and sensitive-data redaction.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-16 16:10:33 +00:00
|
|
|
&cfg,
|
2024-10-09 18:47:27 +00:00
|
|
|
)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
}
|
2023-01-16 20:06:30 +00:00
|
|
|
}
|
|
|
|
|
|
2024-11-22 15:56:36 +00:00
|
|
|
if len(opts) > 0 && opts[0].WithDEPWebview {
|
2026-02-28 11:52:21 +00:00
|
|
|
frontendHandler := WithMDMEnrollmentMiddleware(svc, logger, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
2024-11-22 15:56:36 +00:00
|
|
|
// do nothing and return 200
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
}))
|
|
|
|
|
rootMux.Handle("/", frontendHandler)
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-06 20:23:07 +00:00
|
|
|
var featureRoutes []endpointer.HandlerRoutesFunc
|
2025-03-04 18:04:25 +00:00
|
|
|
if len(opts) > 0 && len(opts[0].FeatureRoutes) > 0 {
|
|
|
|
|
featureRoutes = opts[0].FeatureRoutes
|
|
|
|
|
}
|
2025-07-16 18:08:27 +00:00
|
|
|
var extra []ExtraHandlerOption
|
|
|
|
|
extra = append(extra, WithLoginRateLimit(throttled.PerMin(1000)))
|
|
|
|
|
|
2025-07-17 11:23:19 +00:00
|
|
|
if len(opts) > 0 && opts[0].HostIdentity != nil {
|
2026-02-28 11:52:21 +00:00
|
|
|
require.NoError(t, hostidentity.RegisterSCEP(rootMux, opts[0].HostIdentity.SCEPStorage, ds, logger, &cfg))
|
2025-07-16 18:08:27 +00:00
|
|
|
var httpSigVerifier func(http.Handler) http.Handler
|
Changes needed before gokit/log to slog transition. (#39527)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #38889
PLEASE READ BELOW before looking at file changes
Before converting individual files/packages to slog, we generally need
to make these 2 changes to make the conversion easier:
- Replace uses of `kitlog.With` since they are not fully compatible with
our kitlog adapter
- Directly use the kitlog adapter logger type instead of the kitlog
interface, which will let us have direct access to the underlying slog
logger: `*logging.Logger`
Note: that I did not replace absolutely all uses of `kitlog.Logger`, but
I did remove all uses of `kitlog.With` except for these due to
complexity:
- server/logging/filesystem.go and the other log writers (webhook,
firehose, kinesis, lambda, pubsub, nats)
- server/datastore/mysql/nanomdm_storage.go (adapter pattern)
- server/vulnerabilities/nvd/* (cascades to CLI tools)
- server/service/osquery_utils/queries.go (callback type signatures
cascade broadly)
- cmd/maintained-apps/ (standalone, so can be transitioned later all at
once)
Most of the changes in this PR follow these patterns:
- `kitlog.Logger` type → `*logging.Logger`
- `kitlog.With(logger, ...)` → `logger.With(...)`
- `kitlog.NewNopLogger() → logging.NewNopLogger()`, including similar
variations such as `logging.NewLogfmtLogger(w)` and
`logging.NewJSONLogger(w)`
- removed many now-unused kitlog imports
Unique changes that the PR review should focus on:
- server/platform/logging/kitlog_adapter.go: Core adapter changes
- server/platform/logging/logging.go: New convenience functions
- server/service/integration_logger_test.go: Test changes for slog
# Checklist for submitter
If some of the following don't apply, delete the relevant line.
- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
- Was added in previous PR
## Testing
- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Refactor**
* Migrated the codebase to a unified internal structured logging system
for more consistent, reliable logs and observability.
* No user-facing functionality changed; runtime behavior and APIs remain
compatible.
* **Tests**
* Updated tests to use the new logging helpers to ensure consistent test
logging and validation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-02-11 16:08:33 +00:00
|
|
|
httpSigVerifier, err := httpsig.Middleware(ds, opts[0].HostIdentity.RequireHTTPMessageSignature,
|
2026-02-28 11:52:21 +00:00
|
|
|
logger.With("component", "http-sig-verifier"))
|
2025-07-16 18:08:27 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
extra = append(extra, WithHTTPSigVerifier(httpSigVerifier))
|
|
|
|
|
}
|
2025-11-06 23:07:17 +00:00
|
|
|
|
|
|
|
|
if len(opts) > 0 && opts[0].ConditionalAccess != nil {
|
2026-02-28 11:52:21 +00:00
|
|
|
require.NoError(t, condaccess.RegisterSCEP(ctx, rootMux, opts[0].ConditionalAccess.SCEPStorage, ds, logger, &cfg))
|
2026-03-13 20:21:16 +00:00
|
|
|
require.NoError(t, condaccess.RegisterIdP(rootMux, ds, logger, &cfg, limitStore))
|
2025-11-06 23:07:17 +00:00
|
|
|
}
|
2026-02-05 18:55:03 +00:00
|
|
|
var carveStore fleet.CarveStore = ds // In tests, we use MySQL as storage for carves.
|
2026-02-28 11:52:21 +00:00
|
|
|
apiHandler := MakeHandler(svc, cfg, logger, limitStore, redisPool, carveStore, featureRoutes, extra...)
|
2023-01-16 20:06:30 +00:00
|
|
|
rootMux.Handle("/api/", apiHandler)
|
2024-10-31 19:24:42 +00:00
|
|
|
var errHandler *errorstore.Handler
|
|
|
|
|
ctxErrHandler := ctxerr.FromContext(ctx)
|
|
|
|
|
if ctxErrHandler != nil {
|
|
|
|
|
errHandler = ctxErrHandler.(*errorstore.Handler)
|
|
|
|
|
}
|
2026-02-28 11:52:21 +00:00
|
|
|
debugHandler := MakeDebugHandler(svc, cfg, logger, errHandler, ds)
|
2024-04-16 12:52:03 +00:00
|
|
|
rootMux.Handle("/debug/", debugHandler)
|
Add CSP to fleet(currently disabled - needs frontend work) (#41395)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #40538
This is the initial iteration of CSP functionality, currently gated
behind FLEET_SERVER_ENABLE_CSP. If disabled, no CSP is served. Nonces
are still injected into pages however a dummy nonce is used and has no
effect.
With this setting turned on things break and will be addressed by mainly
frontend changes in https://github.com/fleetdm/fleet/issues/41577
# 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), JS
inline code is prevented especially for url redirects
- [x] If paths of existing endpoints are modified without backwards
compatibility, checked the frontend/CLI for any necessary changes
## Testing
- [x] Added/updated automated tests
- [x] Where appropriate, [automated tests simulate multiple hosts and
test for host
isolation](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/patterns-backend.md#unit-testing)
(updates to one hosts's records do not affect another)
- [x] QA'd all new/changed functionality manually
---------
Co-authored-by: Gabriel Hernandez <ghernandez345@gmail.com>
2026-03-12 22:06:54 +00:00
|
|
|
rootMux.Handle("/enroll", ServeEndUserEnrollOTA(svc, "", ds, logger, false))
|
2023-01-16 20:06:30 +00:00
|
|
|
|
2025-04-04 16:00:46 +00:00
|
|
|
if len(opts) > 0 && opts[0].EnableSCIM {
|
2026-02-28 11:52:21 +00:00
|
|
|
require.NoError(t, scim.RegisterSCIM(rootMux, ds, svc, logger, &cfg))
|
2025-04-10 19:08:45 +00:00
|
|
|
rootMux.Handle("/api/v1/fleet/scim/details", apiHandler)
|
|
|
|
|
rootMux.Handle("/api/latest/fleet/scim/details", apiHandler)
|
2025-04-04 16:00:46 +00:00
|
|
|
}
|
|
|
|
|
|
2023-01-16 20:06:30 +00:00
|
|
|
server := httptest.NewUnstartedServer(rootMux)
|
|
|
|
|
server.Config = cfg.Server.DefaultHTTPServer(ctx, rootMux)
|
2024-09-17 13:21:54 +00:00
|
|
|
// WriteTimeout is set for security purposes.
|
|
|
|
|
// If we don't set it, (bugy or malignant) clients making long running
|
|
|
|
|
// requests could DDOS Fleet.
|
|
|
|
|
require.NotZero(t, server.Config.WriteTimeout)
|
2022-10-19 10:42:21 +00:00
|
|
|
if len(opts) > 0 && opts[0].HTTPServerConfig != nil {
|
|
|
|
|
server.Config = opts[0].HTTPServerConfig
|
|
|
|
|
// make sure we use the application handler we just created
|
2023-01-16 20:06:30 +00:00
|
|
|
server.Config.Handler = rootMux
|
2022-10-19 10:42:21 +00:00
|
|
|
}
|
2022-10-14 15:00:16 +00:00
|
|
|
server.Start()
|
2021-09-15 19:27:53 +00:00
|
|
|
t.Cleanup(func() {
|
|
|
|
|
server.Close()
|
|
|
|
|
})
|
2021-07-16 18:28:13 +00:00
|
|
|
return users, server
|
|
|
|
|
}
|
2025-03-04 18:04:25 +00:00
|
|
|
|
|
|
|
|
func NewTestService(t *testing.T, ds fleet.Datastore, cfg config.FleetConfig, opts ...*TestServerOpts) (fleet.Service, context.Context) {
|
|
|
|
|
var rs fleet.QueryResultStore
|
|
|
|
|
if len(opts) > 0 && opts[0].Rs != nil {
|
|
|
|
|
rs = opts[0].Rs
|
|
|
|
|
}
|
|
|
|
|
var lq fleet.LiveQueryStore
|
|
|
|
|
if len(opts) > 0 && opts[0].Lq != nil {
|
|
|
|
|
lq = opts[0].Lq
|
|
|
|
|
}
|
|
|
|
|
return newTestServiceWithConfig(t, ds, cfg, rs, lq, opts...)
|
|
|
|
|
}
|
2021-07-30 15:45:49 +00:00
|
|
|
|
2023-04-06 18:21:07 +00:00
|
|
|
func testSESPluginConfig() config.FleetConfig {
|
|
|
|
|
c := config.TestConfig()
|
|
|
|
|
c.Email = config.EmailConfig{EmailBackend: "ses"}
|
|
|
|
|
c.SES = config.SESConfig{
|
|
|
|
|
Region: "us-east-1",
|
|
|
|
|
AccessKeyID: "foo",
|
|
|
|
|
SecretAccessKey: "bar",
|
|
|
|
|
StsAssumeRoleArn: "baz",
|
|
|
|
|
SourceArn: "qux",
|
|
|
|
|
}
|
|
|
|
|
return c
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-30 15:45:49 +00:00
|
|
|
func testKinesisPluginConfig() config.FleetConfig {
|
|
|
|
|
c := config.TestConfig()
|
|
|
|
|
c.Osquery.ResultLogPlugin = "kinesis"
|
|
|
|
|
c.Osquery.StatusLogPlugin = "kinesis"
|
2022-12-23 22:04:13 +00:00
|
|
|
c.Activity.AuditLogPlugin = "kinesis"
|
2021-07-30 15:45:49 +00:00
|
|
|
c.Kinesis = config.KinesisConfig{
|
|
|
|
|
Region: "us-east-1",
|
|
|
|
|
AccessKeyID: "foo",
|
|
|
|
|
SecretAccessKey: "bar",
|
|
|
|
|
StsAssumeRoleArn: "baz",
|
|
|
|
|
StatusStream: "test-status-stream",
|
|
|
|
|
ResultStream: "test-result-stream",
|
2022-12-23 22:04:13 +00:00
|
|
|
AuditStream: "test-audit-stream",
|
2021-07-30 15:45:49 +00:00
|
|
|
}
|
|
|
|
|
return c
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testFirehosePluginConfig() config.FleetConfig {
|
|
|
|
|
c := config.TestConfig()
|
|
|
|
|
c.Osquery.ResultLogPlugin = "firehose"
|
|
|
|
|
c.Osquery.StatusLogPlugin = "firehose"
|
2022-12-23 22:04:13 +00:00
|
|
|
c.Activity.AuditLogPlugin = "firehose"
|
2021-07-30 15:45:49 +00:00
|
|
|
c.Firehose = config.FirehoseConfig{
|
|
|
|
|
Region: "us-east-1",
|
|
|
|
|
AccessKeyID: "foo",
|
|
|
|
|
SecretAccessKey: "bar",
|
|
|
|
|
StsAssumeRoleArn: "baz",
|
|
|
|
|
StatusStream: "test-status-firehose",
|
|
|
|
|
ResultStream: "test-result-firehose",
|
2022-12-23 22:04:13 +00:00
|
|
|
AuditStream: "test-audit-firehose",
|
2021-07-30 15:45:49 +00:00
|
|
|
}
|
|
|
|
|
return c
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testLambdaPluginConfig() config.FleetConfig {
|
|
|
|
|
c := config.TestConfig()
|
|
|
|
|
c.Osquery.ResultLogPlugin = "lambda"
|
|
|
|
|
c.Osquery.StatusLogPlugin = "lambda"
|
2022-12-23 22:04:13 +00:00
|
|
|
c.Activity.AuditLogPlugin = "lambda"
|
2021-07-30 15:45:49 +00:00
|
|
|
c.Lambda = config.LambdaConfig{
|
|
|
|
|
Region: "us-east-1",
|
|
|
|
|
AccessKeyID: "foo",
|
|
|
|
|
SecretAccessKey: "bar",
|
|
|
|
|
StsAssumeRoleArn: "baz",
|
2021-08-02 22:06:27 +00:00
|
|
|
ResultFunction: "result-func",
|
|
|
|
|
StatusFunction: "status-func",
|
2022-12-23 22:04:13 +00:00
|
|
|
AuditFunction: "audit-func",
|
2021-07-30 15:45:49 +00:00
|
|
|
}
|
|
|
|
|
return c
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testPubSubPluginConfig() config.FleetConfig {
|
|
|
|
|
c := config.TestConfig()
|
|
|
|
|
c.Osquery.ResultLogPlugin = "pubsub"
|
|
|
|
|
c.Osquery.StatusLogPlugin = "pubsub"
|
2022-12-23 22:04:13 +00:00
|
|
|
c.Activity.AuditLogPlugin = "pubsub"
|
2021-07-30 15:45:49 +00:00
|
|
|
c.PubSub = config.PubSubConfig{
|
|
|
|
|
Project: "test",
|
|
|
|
|
StatusTopic: "status-topic",
|
|
|
|
|
ResultTopic: "result-topic",
|
2022-12-23 22:04:13 +00:00
|
|
|
AuditTopic: "audit-topic",
|
2021-07-30 15:45:49 +00:00
|
|
|
AddAttributes: false,
|
|
|
|
|
}
|
|
|
|
|
return c
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testStdoutPluginConfig() config.FleetConfig {
|
|
|
|
|
c := config.TestConfig()
|
|
|
|
|
c.Osquery.ResultLogPlugin = "stdout"
|
|
|
|
|
c.Osquery.StatusLogPlugin = "stdout"
|
2022-12-23 22:04:13 +00:00
|
|
|
c.Activity.AuditLogPlugin = "stdout"
|
2021-07-30 15:45:49 +00:00
|
|
|
return c
|
2021-08-02 22:06:27 +00:00
|
|
|
}
|
2021-09-15 19:27:53 +00:00
|
|
|
|
2022-03-11 15:51:12 +00:00
|
|
|
func testUnrecognizedPluginConfig() config.FleetConfig {
|
|
|
|
|
c := config.TestConfig()
|
2022-12-23 22:04:13 +00:00
|
|
|
c.Osquery = config.OsqueryConfig{
|
|
|
|
|
ResultLogPlugin: "bar",
|
|
|
|
|
StatusLogPlugin: "bar",
|
|
|
|
|
}
|
|
|
|
|
c.Activity.AuditLogPlugin = "bar"
|
2022-03-11 15:51:12 +00:00
|
|
|
return c
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-15 19:27:53 +00:00
|
|
|
func assertBodyContains(t *testing.T, resp *http.Response, expected string) {
|
2022-08-30 11:13:09 +00:00
|
|
|
bodyBytes, err := io.ReadAll(resp.Body)
|
2021-09-15 19:27:53 +00:00
|
|
|
require.Nil(t, err)
|
|
|
|
|
bodyString := string(bodyBytes)
|
|
|
|
|
assert.Contains(t, bodyString, expected)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func getJSON(r *http.Response, target interface{}) error {
|
|
|
|
|
return json.NewDecoder(r.Body).Decode(target)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func assertErrorCodeAndMessage(t *testing.T, resp *http.Response, code int, message string) {
|
|
|
|
|
err := &fleet.Error{}
|
|
|
|
|
require.Nil(t, getJSON(resp, err))
|
|
|
|
|
assert.Equal(t, code, err.Code)
|
|
|
|
|
assert.Equal(t, message, err.Message)
|
|
|
|
|
}
|
2021-12-23 21:26:55 +00:00
|
|
|
|
|
|
|
|
type memFailingPolicySet struct {
|
|
|
|
|
mMu sync.RWMutex
|
|
|
|
|
m map[uint][]fleet.PolicySetHost
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var _ fleet.FailingPolicySet = (*memFailingPolicySet)(nil)
|
|
|
|
|
|
|
|
|
|
func NewMemFailingPolicySet() *memFailingPolicySet {
|
|
|
|
|
return &memFailingPolicySet{
|
|
|
|
|
m: make(map[uint][]fleet.PolicySetHost),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AddFailingPoliciesForHost adds the given host to the policy sets.
|
|
|
|
|
func (m *memFailingPolicySet) AddHost(policyID uint, host fleet.PolicySetHost) error {
|
|
|
|
|
m.mMu.Lock()
|
|
|
|
|
defer m.mMu.Unlock()
|
|
|
|
|
|
|
|
|
|
m.m[policyID] = append(m.m[policyID], host)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ListHosts returns the list of hosts present in the policy set.
|
|
|
|
|
func (m *memFailingPolicySet) ListHosts(policyID uint) ([]fleet.PolicySetHost, error) {
|
|
|
|
|
m.mMu.RLock()
|
|
|
|
|
defer m.mMu.RUnlock()
|
|
|
|
|
|
|
|
|
|
hosts := make([]fleet.PolicySetHost, len(m.m[policyID]))
|
2024-10-29 19:17:51 +00:00
|
|
|
copy(hosts, m.m[policyID])
|
2021-12-23 21:26:55 +00:00
|
|
|
return hosts, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// RemoveHosts removes the hosts from the policy set.
|
|
|
|
|
func (m *memFailingPolicySet) RemoveHosts(policyID uint, hosts []fleet.PolicySetHost) error {
|
|
|
|
|
m.mMu.Lock()
|
|
|
|
|
defer m.mMu.Unlock()
|
|
|
|
|
|
|
|
|
|
if _, ok := m.m[policyID]; !ok {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
hostsSet := make(map[uint]struct{})
|
|
|
|
|
for _, host := range hosts {
|
|
|
|
|
hostsSet[host.ID] = struct{}{}
|
|
|
|
|
}
|
|
|
|
|
n := 0
|
|
|
|
|
for _, host := range m.m[policyID] {
|
|
|
|
|
if _, ok := hostsSet[host.ID]; !ok {
|
|
|
|
|
m.m[policyID][n] = host
|
|
|
|
|
n++
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
m.m[policyID] = m.m[policyID][:n]
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// RemoveSet removes a policy set.
|
|
|
|
|
func (m *memFailingPolicySet) RemoveSet(policyID uint) error {
|
|
|
|
|
m.mMu.Lock()
|
|
|
|
|
defer m.mMu.Unlock()
|
|
|
|
|
|
|
|
|
|
delete(m.m, policyID)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ListSets lists all the policy sets.
|
|
|
|
|
func (m *memFailingPolicySet) ListSets() ([]uint, error) {
|
|
|
|
|
m.mMu.RLock()
|
|
|
|
|
defer m.mMu.RUnlock()
|
|
|
|
|
|
|
|
|
|
var policyIDs []uint
|
|
|
|
|
for policyID := range m.m {
|
|
|
|
|
policyIDs = append(policyIDs, policyID)
|
|
|
|
|
}
|
|
|
|
|
return policyIDs, nil
|
|
|
|
|
}
|
2022-06-13 20:29:32 +00:00
|
|
|
|
|
|
|
|
type nopEnrollHostLimiter struct{}
|
|
|
|
|
|
|
|
|
|
func (nopEnrollHostLimiter) CanEnrollNewHost(ctx context.Context) (bool, error) {
|
|
|
|
|
return true, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (nopEnrollHostLimiter) SyncEnrolledHostIDs(ctx context.Context) error {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2023-01-31 14:46:01 +00:00
|
|
|
|
|
|
|
|
func newMockAPNSPushProviderFactory() (*mock.APNSPushProviderFactory, *mock.APNSPushProvider) {
|
|
|
|
|
provider := &mock.APNSPushProvider{}
|
|
|
|
|
provider.PushFunc = mockSuccessfulPush
|
|
|
|
|
factory := &mock.APNSPushProviderFactory{}
|
|
|
|
|
factory.NewPushProviderFunc = func(*tls.Certificate) (push.PushProvider, error) {
|
|
|
|
|
return provider, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return factory, provider
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-20 17:47:11 +00:00
|
|
|
func mockSuccessfulPush(_ context.Context, pushes []*mdm.Push) (map[string]*push.Response, error) {
|
2023-01-31 14:46:01 +00:00
|
|
|
res := make(map[string]*push.Response, len(pushes))
|
|
|
|
|
for _, p := range pushes {
|
|
|
|
|
res[p.Token.String()] = &push.Response{
|
|
|
|
|
Id: uuid.New().String(),
|
|
|
|
|
Err: nil,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return res, nil
|
|
|
|
|
}
|
2023-03-27 19:30:29 +00:00
|
|
|
|
2023-10-06 22:04:33 +00:00
|
|
|
func mdmConfigurationRequiredEndpoints() []struct {
|
2023-05-17 14:16:26 +00:00
|
|
|
method, path string
|
|
|
|
|
deviceAuthenticated bool
|
|
|
|
|
premiumOnly bool
|
|
|
|
|
} {
|
|
|
|
|
return []struct {
|
|
|
|
|
method, path string
|
|
|
|
|
deviceAuthenticated bool
|
|
|
|
|
premiumOnly bool
|
|
|
|
|
}{
|
|
|
|
|
{"POST", "/api/latest/fleet/mdm/apple/enqueue", false, false},
|
|
|
|
|
{"GET", "/api/latest/fleet/mdm/apple/commandresults", false, false},
|
|
|
|
|
{"GET", "/api/latest/fleet/mdm/apple/installers/1", false, false},
|
|
|
|
|
{"DELETE", "/api/latest/fleet/mdm/apple/installers/1", false, false},
|
|
|
|
|
{"GET", "/api/latest/fleet/mdm/apple/installers", false, false},
|
|
|
|
|
{"GET", "/api/latest/fleet/mdm/apple/devices", false, false},
|
|
|
|
|
{"GET", "/api/latest/fleet/mdm/apple/profiles", false, false},
|
|
|
|
|
{"GET", "/api/latest/fleet/mdm/apple/profiles/1", false, false},
|
|
|
|
|
{"DELETE", "/api/latest/fleet/mdm/apple/profiles/1", false, false},
|
|
|
|
|
{"GET", "/api/latest/fleet/mdm/apple/profiles/summary", false, false},
|
|
|
|
|
{"PATCH", "/api/latest/fleet/mdm/hosts/1/unenroll", false, false},
|
2024-03-13 14:27:29 +00:00
|
|
|
{"DELETE", "/api/latest/fleet/hosts/1/mdm", false, false},
|
2023-07-14 15:53:03 +00:00
|
|
|
{"GET", "/api/latest/fleet/mdm/hosts/1/profiles", false, true},
|
2024-03-13 14:27:29 +00:00
|
|
|
{"GET", "/api/latest/fleet/hosts/1/configuration_profiles", false, true},
|
2023-05-17 14:16:26 +00:00
|
|
|
{"POST", "/api/latest/fleet/mdm/hosts/1/lock", false, false},
|
|
|
|
|
{"POST", "/api/latest/fleet/mdm/hosts/1/wipe", false, false},
|
|
|
|
|
{"PATCH", "/api/latest/fleet/mdm/apple/settings", false, false},
|
|
|
|
|
{"GET", "/api/latest/fleet/mdm/apple", false, false},
|
2024-03-13 14:27:29 +00:00
|
|
|
{"GET", "/api/latest/fleet/apns", false, false},
|
2023-05-17 14:16:26 +00:00
|
|
|
{"GET", apple_mdm.EnrollPath + "?token=test", false, false},
|
|
|
|
|
{"GET", apple_mdm.InstallerPath + "?token=test", false, false},
|
2024-03-13 14:27:29 +00:00
|
|
|
{"GET", "/api/latest/fleet/mdm/setup/eula/0982A979-B1C9-4BDF-B584-5A37D32A1172", false, true},
|
|
|
|
|
{"GET", "/api/latest/fleet/setup_experience/eula/0982A979-B1C9-4BDF-B584-5A37D32A1172", false, true},
|
|
|
|
|
{"DELETE", "/api/latest/fleet/mdm/setup/eula/token", false, true},
|
|
|
|
|
{"DELETE", "/api/latest/fleet/setup_experience/eula/token", false, true},
|
|
|
|
|
{"GET", "/api/latest/fleet/mdm/setup/eula/metadata", false, true},
|
|
|
|
|
{"GET", "/api/latest/fleet/setup_experience/eula/metadata", false, true},
|
2024-02-07 12:24:24 +00:00
|
|
|
{"GET", "/api/latest/fleet/mdm/apple/setup/eula/0982A979-B1C9-4BDF-B584-5A37D32A1172", false, false},
|
2023-05-17 14:16:26 +00:00
|
|
|
{"DELETE", "/api/latest/fleet/mdm/apple/setup/eula/token", false, false},
|
|
|
|
|
{"GET", "/api/latest/fleet/mdm/apple/setup/eula/metadata", false, false},
|
|
|
|
|
{"GET", "/api/latest/fleet/mdm/apple/enrollment_profile", false, false},
|
2024-03-13 14:27:29 +00:00
|
|
|
{"GET", "/api/latest/fleet/enrollment_profiles/automatic", false, false},
|
2023-05-17 14:16:26 +00:00
|
|
|
{"POST", "/api/latest/fleet/mdm/apple/enrollment_profile", false, false},
|
2024-03-13 14:27:29 +00:00
|
|
|
{"POST", "/api/latest/fleet/enrollment_profiles/automatic", false, false},
|
2023-05-17 14:16:26 +00:00
|
|
|
{"DELETE", "/api/latest/fleet/mdm/apple/enrollment_profile", false, false},
|
2024-03-13 14:27:29 +00:00
|
|
|
{"DELETE", "/api/latest/fleet/enrollment_profiles/automatic", false, false},
|
2023-05-17 14:16:26 +00:00
|
|
|
{"POST", "/api/latest/fleet/device/%s/migrate_mdm", true, true},
|
2023-05-31 13:24:22 +00:00
|
|
|
{"POST", "/api/latest/fleet/mdm/apple/profiles/preassign", false, true},
|
|
|
|
|
{"POST", "/api/latest/fleet/mdm/apple/profiles/match", false, true},
|
2023-11-01 14:13:12 +00:00
|
|
|
{"POST", "/api/latest/fleet/mdm/commands/run", false, false},
|
2024-03-13 14:27:29 +00:00
|
|
|
{"POST", "/api/latest/fleet/commands/run", false, false},
|
2023-11-01 14:13:12 +00:00
|
|
|
{"GET", "/api/latest/fleet/mdm/commandresults", false, false},
|
2024-03-13 14:27:29 +00:00
|
|
|
{"GET", "/api/latest/fleet/commands/results", false, false},
|
2023-11-01 14:13:12 +00:00
|
|
|
{"GET", "/api/latest/fleet/mdm/commands", false, false},
|
2024-03-13 14:27:29 +00:00
|
|
|
{"GET", "/api/latest/fleet/commands", false, false},
|
2023-10-06 22:04:33 +00:00
|
|
|
{"POST", "/api/fleet/orbit/disk_encryption_key", false, false},
|
2023-11-29 14:32:42 +00:00
|
|
|
{"GET", "/api/latest/fleet/mdm/profiles/1", false, false},
|
2024-03-13 14:27:29 +00:00
|
|
|
{"GET", "/api/latest/fleet/configuration_profiles/1", false, false},
|
|
|
|
|
// TODO: those endpoints accept multipart/form data that gets
|
2023-11-15 15:58:59 +00:00
|
|
|
// parsed before the MDM check, we need to refactor this
|
|
|
|
|
// function to return more information to the caller, or find a
|
|
|
|
|
// better way to test these endpoints.
|
2024-10-18 17:38:26 +00:00
|
|
|
// {"POST", "/api/latest/fleet/mdm/profiles", false, false},
|
|
|
|
|
// {"POST", "/api/latest/fleet/configuration_profiles", false, false},
|
|
|
|
|
// {"POST", "/api/latest/fleet/mdm/setup/eula"},
|
|
|
|
|
// {"POST", "/api/latest/fleet/setup_experience/eula"},
|
|
|
|
|
// {"POST", "/api/latest/fleet/mdm/bootstrap", false, true},
|
|
|
|
|
// {"POST", "/api/latest/fleet/bootstrap", false, true},
|
2023-11-29 14:32:42 +00:00
|
|
|
{"GET", "/api/latest/fleet/mdm/profiles", false, false},
|
2024-03-13 14:27:29 +00:00
|
|
|
{"GET", "/api/latest/fleet/configuration_profiles", false, false},
|
|
|
|
|
{"GET", "/api/latest/fleet/mdm/manual_enrollment_profile", false, true},
|
|
|
|
|
{"GET", "/api/latest/fleet/enrollment_profiles/manual", false, true},
|
|
|
|
|
{"GET", "/api/latest/fleet/mdm/bootstrap/1/metadata", false, true},
|
|
|
|
|
{"GET", "/api/latest/fleet/bootstrap/1/metadata", false, true},
|
|
|
|
|
{"DELETE", "/api/latest/fleet/mdm/bootstrap/1", false, true},
|
|
|
|
|
{"DELETE", "/api/latest/fleet/bootstrap/1", false, true},
|
|
|
|
|
{"GET", "/api/latest/fleet/mdm/bootstrap?token=1", false, true},
|
|
|
|
|
{"GET", "/api/latest/fleet/bootstrap?token=1", false, true},
|
|
|
|
|
{"GET", "/api/latest/fleet/mdm/bootstrap/summary", false, true},
|
|
|
|
|
{"GET", "/api/latest/fleet/mdm/apple/bootstrap/summary", false, true},
|
|
|
|
|
{"GET", "/api/latest/fleet/bootstrap/summary", false, true},
|
|
|
|
|
{"PATCH", "/api/latest/fleet/mdm/apple/setup", false, true},
|
|
|
|
|
{"PATCH", "/api/latest/fleet/setup_experience", false, true},
|
2024-10-09 19:38:13 +00:00
|
|
|
{"POST", "/api/fleet/orbit/setup_experience/status", false, true},
|
2026-03-02 20:14:19 +00:00
|
|
|
{"POST", "/api/latest/fleet/software/web_apps", false, true},
|
2023-03-27 19:30:29 +00:00
|
|
|
}
|
|
|
|
|
}
|
2023-06-22 12:17:37 +00:00
|
|
|
|
2025-04-29 19:11:31 +00:00
|
|
|
func windowsMDMConfigurationRequiredEndpoints() []string {
|
|
|
|
|
return []string{
|
|
|
|
|
"/api/fleet/orbit/disk_encryption_key",
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-02 20:14:19 +00:00
|
|
|
func androidMDMConfigurationRequiredEndpoints() []string {
|
|
|
|
|
return []string{
|
|
|
|
|
"/api/latest/fleet/software/web_apps",
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-22 12:17:37 +00:00
|
|
|
// getURLSchemas returns a list of all valid URI schemas
|
|
|
|
|
func getURISchemas() []string {
|
|
|
|
|
return []string{
|
|
|
|
|
"aaa",
|
|
|
|
|
"aaas",
|
|
|
|
|
"about",
|
|
|
|
|
"acap",
|
|
|
|
|
"acct",
|
|
|
|
|
"acd",
|
|
|
|
|
"acr",
|
|
|
|
|
"adiumxtra",
|
|
|
|
|
"adt",
|
|
|
|
|
"afp",
|
|
|
|
|
"afs",
|
|
|
|
|
"aim",
|
|
|
|
|
"amss",
|
|
|
|
|
"android",
|
|
|
|
|
"appdata",
|
|
|
|
|
"apt",
|
|
|
|
|
"ar",
|
|
|
|
|
"ark",
|
|
|
|
|
"at",
|
|
|
|
|
"attachment",
|
|
|
|
|
"aw",
|
|
|
|
|
"barion",
|
|
|
|
|
"bb",
|
|
|
|
|
"beshare",
|
|
|
|
|
"bitcoin",
|
|
|
|
|
"bitcoincash",
|
|
|
|
|
"blob",
|
|
|
|
|
"bolo",
|
|
|
|
|
"browserext",
|
|
|
|
|
"cabal",
|
|
|
|
|
"calculator",
|
|
|
|
|
"callto",
|
|
|
|
|
"cap",
|
|
|
|
|
"cast",
|
|
|
|
|
"casts",
|
|
|
|
|
"chrome",
|
|
|
|
|
"chrome-extension",
|
|
|
|
|
"cid",
|
|
|
|
|
"coap",
|
|
|
|
|
"coap+tcp",
|
|
|
|
|
"coap+ws",
|
|
|
|
|
"coaps",
|
|
|
|
|
"coaps+tcp",
|
|
|
|
|
"coaps+ws",
|
|
|
|
|
"com-eventbrite-attendee",
|
|
|
|
|
"content",
|
|
|
|
|
"content-type",
|
|
|
|
|
"crid",
|
|
|
|
|
"cstr",
|
|
|
|
|
"cvs",
|
|
|
|
|
"dab",
|
|
|
|
|
"dat",
|
|
|
|
|
"data",
|
|
|
|
|
"dav",
|
|
|
|
|
"dhttp",
|
|
|
|
|
"diaspora",
|
|
|
|
|
"dict",
|
|
|
|
|
"did",
|
|
|
|
|
"dis",
|
|
|
|
|
"dlna-playcontainer",
|
|
|
|
|
"dlna-playsingle",
|
|
|
|
|
"dns",
|
|
|
|
|
"dntp",
|
|
|
|
|
"doi",
|
|
|
|
|
"dpp",
|
|
|
|
|
"drm",
|
|
|
|
|
"drop",
|
|
|
|
|
"dtmi",
|
|
|
|
|
"dtn",
|
|
|
|
|
"dvb",
|
|
|
|
|
"dvx",
|
|
|
|
|
"dweb",
|
|
|
|
|
"ed2k",
|
|
|
|
|
"eid",
|
|
|
|
|
"elsi",
|
|
|
|
|
"embedded",
|
|
|
|
|
"ens",
|
|
|
|
|
"ethereum",
|
|
|
|
|
"example",
|
|
|
|
|
"facetime",
|
|
|
|
|
"fax",
|
|
|
|
|
"feed",
|
|
|
|
|
"feedready",
|
|
|
|
|
"fido",
|
|
|
|
|
"file",
|
|
|
|
|
"filesystem",
|
|
|
|
|
"finger",
|
|
|
|
|
"first-run-pen-experience",
|
|
|
|
|
"fish",
|
|
|
|
|
"fm",
|
|
|
|
|
"ftp",
|
|
|
|
|
"fuchsia-pkg",
|
|
|
|
|
"geo",
|
|
|
|
|
"gg",
|
|
|
|
|
"git",
|
|
|
|
|
"gitoid",
|
|
|
|
|
"gizmoproject",
|
|
|
|
|
"go",
|
|
|
|
|
"gopher",
|
|
|
|
|
"graph",
|
|
|
|
|
"grd",
|
|
|
|
|
"gtalk",
|
|
|
|
|
"h323",
|
|
|
|
|
"ham",
|
|
|
|
|
"hcap",
|
|
|
|
|
"hcp",
|
|
|
|
|
"http",
|
|
|
|
|
"https",
|
|
|
|
|
"hxxp",
|
|
|
|
|
"hxxps",
|
|
|
|
|
"hydrazone",
|
|
|
|
|
"hyper",
|
|
|
|
|
"iax",
|
|
|
|
|
"icap",
|
|
|
|
|
"icon",
|
|
|
|
|
"im",
|
|
|
|
|
"imap",
|
|
|
|
|
"info",
|
|
|
|
|
"iotdisco",
|
|
|
|
|
"ipfs",
|
|
|
|
|
"ipn",
|
|
|
|
|
"ipns",
|
|
|
|
|
"ipp",
|
|
|
|
|
"ipps",
|
|
|
|
|
"irc",
|
|
|
|
|
"irc6",
|
|
|
|
|
"ircs",
|
|
|
|
|
"iris",
|
|
|
|
|
"iris.beep",
|
|
|
|
|
"iris.lwz",
|
|
|
|
|
"iris.xpc",
|
|
|
|
|
"iris.xpcs",
|
|
|
|
|
"isostore",
|
|
|
|
|
"itms",
|
|
|
|
|
"jabber",
|
|
|
|
|
"jar",
|
|
|
|
|
"jms",
|
|
|
|
|
"keyparc",
|
|
|
|
|
"lastfm",
|
|
|
|
|
"lbry",
|
|
|
|
|
"ldap",
|
|
|
|
|
"ldaps",
|
|
|
|
|
"leaptofrogans",
|
|
|
|
|
"lorawan",
|
|
|
|
|
"lpa",
|
|
|
|
|
"lvlt",
|
|
|
|
|
"magnet",
|
|
|
|
|
"mailserver",
|
|
|
|
|
"mailto",
|
|
|
|
|
"maps",
|
|
|
|
|
"market",
|
|
|
|
|
"matrix",
|
|
|
|
|
"message",
|
|
|
|
|
"microsoft.windows.camera",
|
|
|
|
|
"microsoft.windows.camera.multipicker",
|
|
|
|
|
"microsoft.windows.camera.picker",
|
|
|
|
|
"mid",
|
|
|
|
|
"mms",
|
|
|
|
|
"modem",
|
|
|
|
|
"mongodb",
|
|
|
|
|
"moz",
|
|
|
|
|
"ms-access",
|
|
|
|
|
"ms-appinstaller",
|
|
|
|
|
"ms-browser-extension",
|
|
|
|
|
"ms-calculator",
|
|
|
|
|
"ms-drive-to",
|
|
|
|
|
"ms-enrollment",
|
|
|
|
|
"ms-excel",
|
|
|
|
|
"ms-eyecontrolspeech",
|
|
|
|
|
"ms-gamebarservices",
|
|
|
|
|
"ms-gamingoverlay",
|
|
|
|
|
"ms-getoffice",
|
|
|
|
|
"ms-help",
|
|
|
|
|
"ms-infopath",
|
|
|
|
|
"ms-inputapp",
|
|
|
|
|
"ms-launchremotedesktop",
|
|
|
|
|
"ms-lockscreencomponent-config",
|
|
|
|
|
"ms-media-stream-id",
|
|
|
|
|
"ms-meetnow",
|
|
|
|
|
"ms-mixedrealitycapture",
|
|
|
|
|
"ms-mobileplans",
|
|
|
|
|
"ms-newsandinterests",
|
|
|
|
|
"ms-officeapp",
|
|
|
|
|
"ms-people",
|
|
|
|
|
"ms-project",
|
|
|
|
|
"ms-powerpoint",
|
|
|
|
|
"ms-publisher",
|
|
|
|
|
"ms-remotedesktop",
|
|
|
|
|
"ms-remotedesktop-launch",
|
|
|
|
|
"ms-restoretabcompanion",
|
|
|
|
|
"ms-screenclip",
|
|
|
|
|
"ms-screensketch",
|
|
|
|
|
"ms-search",
|
|
|
|
|
"ms-search-repair",
|
|
|
|
|
"ms-secondary-screen-controller",
|
|
|
|
|
"ms-secondary-screen-setup",
|
|
|
|
|
"ms-settings",
|
|
|
|
|
"ms-settings-airplanemode",
|
|
|
|
|
"ms-settings-bluetooth",
|
|
|
|
|
"ms-settings-camera",
|
|
|
|
|
"ms-settings-cellular",
|
|
|
|
|
"ms-settings-cloudstorage",
|
|
|
|
|
"ms-settings-connectabledevices",
|
|
|
|
|
"ms-settings-displays-topology",
|
|
|
|
|
"ms-settings-emailandaccounts",
|
|
|
|
|
"ms-settings-language",
|
|
|
|
|
"ms-settings-location",
|
|
|
|
|
"ms-settings-lock",
|
|
|
|
|
"ms-settings-nfctransactions",
|
|
|
|
|
"ms-settings-notifications",
|
|
|
|
|
"ms-settings-power",
|
|
|
|
|
"ms-settings-privacy",
|
|
|
|
|
"ms-settings-proximity",
|
|
|
|
|
"ms-settings-screenrotation",
|
|
|
|
|
"ms-settings-wifi",
|
|
|
|
|
"ms-settings-workplace",
|
|
|
|
|
"ms-spd",
|
|
|
|
|
"ms-stickers",
|
|
|
|
|
"ms-sttoverlay",
|
|
|
|
|
"ms-transit-to",
|
|
|
|
|
"ms-useractivityset",
|
|
|
|
|
"ms-virtualtouchpad",
|
|
|
|
|
"ms-visio",
|
|
|
|
|
"ms-walk-to",
|
|
|
|
|
"ms-whiteboard",
|
|
|
|
|
"ms-whiteboard-cmd",
|
|
|
|
|
"ms-word",
|
|
|
|
|
"msnim",
|
|
|
|
|
"msrp",
|
|
|
|
|
"msrps",
|
|
|
|
|
"mss",
|
|
|
|
|
"mt",
|
|
|
|
|
"mtqp",
|
|
|
|
|
"mumble",
|
|
|
|
|
"mupdate",
|
|
|
|
|
"mvn",
|
|
|
|
|
"news",
|
|
|
|
|
"nfs",
|
|
|
|
|
"ni",
|
|
|
|
|
"nih",
|
|
|
|
|
"nntp",
|
|
|
|
|
"notes",
|
|
|
|
|
"num",
|
|
|
|
|
"ocf",
|
|
|
|
|
"oid",
|
|
|
|
|
"onenote",
|
|
|
|
|
"onenote-cmd",
|
|
|
|
|
"opaquelocktoken",
|
|
|
|
|
"openpgp4fpr",
|
|
|
|
|
"otpauth",
|
|
|
|
|
"p1",
|
|
|
|
|
"pack",
|
|
|
|
|
"palm",
|
|
|
|
|
"paparazzi",
|
|
|
|
|
"payment",
|
|
|
|
|
"payto",
|
|
|
|
|
"pkcs11",
|
|
|
|
|
"platform",
|
|
|
|
|
"pop",
|
|
|
|
|
"pres",
|
|
|
|
|
"prospero",
|
|
|
|
|
"proxy",
|
|
|
|
|
"pwid",
|
|
|
|
|
"psyc",
|
|
|
|
|
"pttp",
|
|
|
|
|
"qb",
|
|
|
|
|
"query",
|
|
|
|
|
"quic-transport",
|
|
|
|
|
"redis",
|
|
|
|
|
"rediss",
|
|
|
|
|
"reload",
|
|
|
|
|
"res",
|
|
|
|
|
"resource",
|
|
|
|
|
"rmi",
|
|
|
|
|
"rsync",
|
|
|
|
|
"rtmfp",
|
|
|
|
|
"rtmp",
|
|
|
|
|
"rtsp",
|
|
|
|
|
"rtsps",
|
|
|
|
|
"rtspu",
|
|
|
|
|
"sarif",
|
|
|
|
|
"secondlife",
|
|
|
|
|
"secret-token",
|
|
|
|
|
"service",
|
|
|
|
|
"session",
|
|
|
|
|
"sftp",
|
|
|
|
|
"sgn",
|
|
|
|
|
"shc",
|
|
|
|
|
"shttp",
|
|
|
|
|
"sieve",
|
|
|
|
|
"simpleledger",
|
|
|
|
|
"simplex",
|
|
|
|
|
"sip",
|
|
|
|
|
"sips",
|
|
|
|
|
"skype",
|
|
|
|
|
"smb",
|
|
|
|
|
"smp",
|
|
|
|
|
"sms",
|
|
|
|
|
"smtp",
|
|
|
|
|
"snews",
|
|
|
|
|
"snmp",
|
|
|
|
|
"soap.beep",
|
|
|
|
|
"soap.beeps",
|
|
|
|
|
"soldat",
|
|
|
|
|
"spiffe",
|
|
|
|
|
"spotify",
|
|
|
|
|
"ssb",
|
|
|
|
|
"ssh",
|
|
|
|
|
"starknet",
|
|
|
|
|
"steam",
|
|
|
|
|
"stun",
|
|
|
|
|
"stuns",
|
|
|
|
|
"submit",
|
|
|
|
|
"svn",
|
|
|
|
|
"swh",
|
|
|
|
|
"swid",
|
|
|
|
|
"swidpath",
|
|
|
|
|
"tag",
|
|
|
|
|
"taler",
|
|
|
|
|
"teamspeak",
|
|
|
|
|
"tel",
|
|
|
|
|
"teliaeid",
|
|
|
|
|
"telnet",
|
|
|
|
|
"tftp",
|
|
|
|
|
"things",
|
|
|
|
|
"thismessage",
|
|
|
|
|
"tip",
|
|
|
|
|
"tn3270",
|
|
|
|
|
"tool",
|
|
|
|
|
"turn",
|
|
|
|
|
"turns",
|
|
|
|
|
"tv",
|
|
|
|
|
"udp",
|
|
|
|
|
"unreal",
|
|
|
|
|
"upt",
|
|
|
|
|
"urn",
|
|
|
|
|
"ut2004",
|
|
|
|
|
"uuid-in-package",
|
|
|
|
|
"v-event",
|
|
|
|
|
"vemmi",
|
|
|
|
|
"ventrilo",
|
|
|
|
|
"ves",
|
|
|
|
|
"videotex",
|
|
|
|
|
"vnc",
|
|
|
|
|
"view-source",
|
|
|
|
|
"vscode",
|
|
|
|
|
"vscode-insiders",
|
|
|
|
|
"vsls",
|
|
|
|
|
"w3",
|
|
|
|
|
"wais",
|
|
|
|
|
"web3",
|
|
|
|
|
"wcr",
|
|
|
|
|
"webcal",
|
|
|
|
|
"web+ap",
|
|
|
|
|
"wifi",
|
|
|
|
|
"wpid",
|
|
|
|
|
"ws",
|
|
|
|
|
"wss",
|
|
|
|
|
"wtai",
|
|
|
|
|
"wyciwyg",
|
|
|
|
|
"xcon",
|
|
|
|
|
"xcon-userid",
|
|
|
|
|
"xfire",
|
|
|
|
|
"xmlrpc.beep",
|
|
|
|
|
"xmlrpc.beeps",
|
|
|
|
|
"xmpp",
|
|
|
|
|
"xri",
|
|
|
|
|
"ymsgr",
|
|
|
|
|
"z39.50",
|
|
|
|
|
"z39.50r",
|
|
|
|
|
"z39.50s",
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-11-13 23:10:24 +00:00
|
|
|
|
|
|
|
|
func createAndroidDeviceID(name string) string {
|
|
|
|
|
return "enterprises/mock-enterprise-id/devices/" + name
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-02 17:27:20 +00:00
|
|
|
func statusReportMessageWithEnterpriseSpecificID(t *testing.T, deviceInfo androidmanagement.Device, enterpriseSpecificID string) *android.PubSubMessage {
|
2026-02-06 15:46:25 +00:00
|
|
|
return messageWithAndroidIdentifiers(t, android.PubSubStatusReport, deviceInfo, enterpriseSpecificID, "")
|
2025-12-02 17:27:20 +00:00
|
|
|
}
|
|
|
|
|
|
2025-11-13 23:10:24 +00:00
|
|
|
func enrollmentMessageWithEnterpriseSpecificID(t *testing.T, deviceInfo androidmanagement.Device, enterpriseSpecificID string) *android.PubSubMessage {
|
2026-02-06 15:46:25 +00:00
|
|
|
return messageWithAndroidIdentifiers(t, android.PubSubEnrollment, deviceInfo, enterpriseSpecificID, "")
|
2025-12-02 17:27:20 +00:00
|
|
|
}
|
|
|
|
|
|
2026-02-06 15:46:25 +00:00
|
|
|
func statusReportMessageWithSerialNumber(t *testing.T, deviceInfo androidmanagement.Device, serialNumber string) *android.PubSubMessage {
|
|
|
|
|
return messageWithAndroidIdentifiers(t, android.PubSubStatusReport, deviceInfo, "", serialNumber)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func enrollmentMessageWithSerialNumber(t *testing.T, deviceInfo androidmanagement.Device, serialNumber string) *android.PubSubMessage {
|
|
|
|
|
return messageWithAndroidIdentifiers(t, android.PubSubEnrollment, deviceInfo, "", serialNumber)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func messageWithAndroidIdentifiers(t *testing.T, notificationType android.NotificationType, deviceInfo androidmanagement.Device, enterpriseSpecificID, serialNumber string) *android.PubSubMessage {
|
|
|
|
|
if serialNumber == "" && enterpriseSpecificID == "" || serialNumber != "" && enterpriseSpecificID != "" {
|
|
|
|
|
t.Fatalf("exactly one of serialNumber or enterpriseSpecificID must be provided")
|
|
|
|
|
}
|
2025-11-13 23:10:24 +00:00
|
|
|
deviceInfo.HardwareInfo = &androidmanagement.HardwareInfo{
|
2026-02-06 15:46:25 +00:00
|
|
|
Brand: "TestBrand",
|
|
|
|
|
Model: "TestModel",
|
|
|
|
|
Hardware: "test-hardware",
|
|
|
|
|
}
|
|
|
|
|
if enterpriseSpecificID != "" {
|
|
|
|
|
deviceInfo.Ownership = android_service.DeviceOwnershipPersonallyOwned
|
|
|
|
|
deviceInfo.HardwareInfo.EnterpriseSpecificId = enterpriseSpecificID
|
|
|
|
|
deviceInfo.HardwareInfo.SerialNumber = enterpriseSpecificID
|
|
|
|
|
}
|
|
|
|
|
if serialNumber != "" {
|
|
|
|
|
deviceInfo.Ownership = android_service.DeviceOwnershipCompanyOwned
|
|
|
|
|
deviceInfo.HardwareInfo.SerialNumber = serialNumber
|
2025-11-13 23:10:24 +00:00
|
|
|
}
|
|
|
|
|
deviceInfo.SoftwareInfo = &androidmanagement.SoftwareInfo{
|
|
|
|
|
AndroidBuildNumber: "test-build",
|
|
|
|
|
AndroidVersion: "1",
|
|
|
|
|
}
|
|
|
|
|
deviceInfo.MemoryInfo = &androidmanagement.MemoryInfo{
|
|
|
|
|
TotalRam: int64(8 * 1024 * 1024 * 1024), // 8GB RAM in bytes
|
|
|
|
|
TotalInternalStorage: int64(64 * 1024 * 1024 * 1024), // 64GB system partition
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
deviceInfo.MemoryEvents = []*androidmanagement.MemoryEvent{
|
|
|
|
|
{
|
|
|
|
|
EventType: "EXTERNAL_STORAGE_DETECTED",
|
|
|
|
|
ByteCount: int64(64 * 1024 * 1024 * 1024), // 64GB external/built-in storage total capacity
|
|
|
|
|
CreateTime: "2024-01-15T09:00:00Z",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
EventType: "INTERNAL_STORAGE_MEASURED",
|
|
|
|
|
ByteCount: int64(10 * 1024 * 1024 * 1024), // 10GB free in system partition
|
|
|
|
|
CreateTime: "2024-01-15T10:00:00Z",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
EventType: "EXTERNAL_STORAGE_MEASURED",
|
|
|
|
|
ByteCount: int64(25 * 1024 * 1024 * 1024), // 25GB free in external/built-in storage
|
|
|
|
|
CreateTime: "2024-01-15T10:00:00Z",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data, err := json.Marshal(deviceInfo)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
encodedData := base64.StdEncoding.EncodeToString(data)
|
|
|
|
|
|
|
|
|
|
return &android.PubSubMessage{
|
|
|
|
|
Attributes: map[string]string{
|
2025-12-02 17:27:20 +00:00
|
|
|
"notificationType": string(notificationType),
|
2025-11-13 23:10:24 +00:00
|
|
|
},
|
|
|
|
|
Data: encodedData,
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-03-13 20:47:09 +00:00
|
|
|
|
|
|
|
|
type fmaTestState struct {
|
|
|
|
|
version string
|
|
|
|
|
installerBytes []byte
|
|
|
|
|
sha256 string
|
|
|
|
|
installerPath string
|
Override patch policy query (#42322)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #41815
### Changes
- Extracted patch policy creation to `pkg/patch_policy`
- Added a `patch_query` column to the `software_installers` table
- By default that column is empty, and patch policies will generate with
the default query if so
- On app manifest ingestion, the appropriate entry in
`software_installers` will save the override "patch" query from the
manifest in patch_query
# Checklist for submitter
If some of the following don't apply, delete the relevant line.
- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.
- [ ] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements), JS
inline code is prevented especially for url redirects, and untrusted
data interpolated into shell scripts/commands is validated against shell
metacharacters.
- [ ] If paths of existing endpoints are modified without backwards
compatibility, checked the frontend/CLI for any necessary changes
## Testing
- [x] Added/updated automated tests
- [ ] Where appropriate, [automated tests simulate multiple hosts and
test for host
isolation](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/patterns-backend.md#unit-testing)
(updates to one hosts's records do not affect another)
- [ ] QA'd all new/changed functionality manually
- Relied on integration test for FMA version pinning
## Database migrations
- [x] Checked schema for all modified table for columns that will
auto-update timestamps during migration.
- [ ] Confirmed that updating the timestamps is acceptable, and will not
cause unwanted side effects.
- [x] Ensured the correct collation is explicitly set for character
columns (`COLLATE utf8mb4_unicode_ci`).
2026-03-25 14:32:41 +00:00
|
|
|
patchQuery string
|
2026-03-13 20:47:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *fmaTestState) ComputeSHA(b []byte) {
|
|
|
|
|
h := sha256.New()
|
|
|
|
|
h.Write(b)
|
|
|
|
|
s.sha256 = hex.EncodeToString(h.Sum(nil))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func startFMAServers(t *testing.T, ds fleet.Datastore, states map[string]*fmaTestState) {
|
|
|
|
|
if len(states) == 0 {
|
|
|
|
|
states = make(map[string]*fmaTestState, 1)
|
|
|
|
|
states["/zoom/windows.json"] = &fmaTestState{
|
|
|
|
|
version: "1.0",
|
|
|
|
|
installerBytes: []byte("xyz"),
|
|
|
|
|
installerPath: "/zoom.msi",
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
statesByInstallerPath := make(map[string]*fmaTestState, len(states))
|
|
|
|
|
for _, state := range states {
|
|
|
|
|
state.ComputeSHA(state.installerBytes)
|
|
|
|
|
statesByInstallerPath[state.installerPath] = state
|
|
|
|
|
}
|
|
|
|
|
var downloadMu sync.Mutex
|
|
|
|
|
|
|
|
|
|
// Mock installer server — routes by path to serve per-FMA bytes.
|
|
|
|
|
installerServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
downloadMu.Lock()
|
|
|
|
|
defer downloadMu.Unlock()
|
|
|
|
|
|
|
|
|
|
state, found := statesByInstallerPath[r.URL.Path]
|
|
|
|
|
if !found {
|
|
|
|
|
http.NotFound(w, r)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
_, _ = w.Write(state.installerBytes)
|
|
|
|
|
}))
|
|
|
|
|
|
2026-03-13 22:10:55 +00:00
|
|
|
// call Refresh directly (instead of SyncApps) since we're using the server above and not the file server
|
|
|
|
|
// created in SyncApps
|
|
|
|
|
err := maintained_apps.Refresh(t.Context(), ds, slog.New(slog.DiscardHandler))
|
|
|
|
|
require.NoError(t, err)
|
2026-03-13 20:47:09 +00:00
|
|
|
|
|
|
|
|
manifestServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
var state *fmaTestState
|
|
|
|
|
state, found := states[r.URL.Path]
|
|
|
|
|
if !found {
|
|
|
|
|
http.NotFound(w, r)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
versions := []*ma.FMAManifestApp{
|
|
|
|
|
{
|
Override patch policy query (#42322)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #41815
### Changes
- Extracted patch policy creation to `pkg/patch_policy`
- Added a `patch_query` column to the `software_installers` table
- By default that column is empty, and patch policies will generate with
the default query if so
- On app manifest ingestion, the appropriate entry in
`software_installers` will save the override "patch" query from the
manifest in patch_query
# Checklist for submitter
If some of the following don't apply, delete the relevant line.
- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.
- [ ] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements), JS
inline code is prevented especially for url redirects, and untrusted
data interpolated into shell scripts/commands is validated against shell
metacharacters.
- [ ] If paths of existing endpoints are modified without backwards
compatibility, checked the frontend/CLI for any necessary changes
## Testing
- [x] Added/updated automated tests
- [ ] Where appropriate, [automated tests simulate multiple hosts and
test for host
isolation](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/patterns-backend.md#unit-testing)
(updates to one hosts's records do not affect another)
- [ ] QA'd all new/changed functionality manually
- Relied on integration test for FMA version pinning
## Database migrations
- [x] Checked schema for all modified table for columns that will
auto-update timestamps during migration.
- [ ] Confirmed that updating the timestamps is acceptable, and will not
cause unwanted side effects.
- [x] Ensured the correct collation is explicitly set for character
columns (`COLLATE utf8mb4_unicode_ci`).
2026-03-25 14:32:41 +00:00
|
|
|
Version: state.version,
|
|
|
|
|
Queries: ma.FMAQueries{
|
Create default patch policy query in FMA manifest (#42559)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #42492
Includes changes from running ingestions on all FMAs
# Checklist for submitter
If some of the following don't apply, delete the relevant line.
- [ ] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.
- [ ] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements), JS
inline code is prevented especially for url redirects, and untrusted
data interpolated into shell scripts/commands is validated against shell
metacharacters.
- [ ] If paths of existing endpoints are modified without backwards
compatibility, checked the frontend/CLI for any necessary changes
## Testing
- [x] Added/updated automated tests
- [ ] Where appropriate, [automated tests simulate multiple hosts and
test for host
isolation](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/patterns-backend.md#unit-testing)
(updates to one hosts's records do not affect another)
- [x] QA'd all new/changed functionality manually
2026-03-30 21:25:58 +00:00
|
|
|
Exists: "SELECT 1 FROM osquery_info;",
|
|
|
|
|
Patched: state.patchQuery,
|
Override patch policy query (#42322)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #41815
### Changes
- Extracted patch policy creation to `pkg/patch_policy`
- Added a `patch_query` column to the `software_installers` table
- By default that column is empty, and patch policies will generate with
the default query if so
- On app manifest ingestion, the appropriate entry in
`software_installers` will save the override "patch" query from the
manifest in patch_query
# Checklist for submitter
If some of the following don't apply, delete the relevant line.
- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.
- [ ] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements), JS
inline code is prevented especially for url redirects, and untrusted
data interpolated into shell scripts/commands is validated against shell
metacharacters.
- [ ] If paths of existing endpoints are modified without backwards
compatibility, checked the frontend/CLI for any necessary changes
## Testing
- [x] Added/updated automated tests
- [ ] Where appropriate, [automated tests simulate multiple hosts and
test for host
isolation](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/patterns-backend.md#unit-testing)
(updates to one hosts's records do not affect another)
- [ ] QA'd all new/changed functionality manually
- Relied on integration test for FMA version pinning
## Database migrations
- [x] Checked schema for all modified table for columns that will
auto-update timestamps during migration.
- [ ] Confirmed that updating the timestamps is acceptable, and will not
cause unwanted side effects.
- [x] Ensured the correct collation is explicitly set for character
columns (`COLLATE utf8mb4_unicode_ci`).
2026-03-25 14:32:41 +00:00
|
|
|
},
|
2026-03-13 20:47:09 +00:00
|
|
|
InstallerURL: installerServer.URL + state.installerPath,
|
|
|
|
|
InstallScriptRef: "foobaz",
|
|
|
|
|
UninstallScriptRef: "foobaz",
|
|
|
|
|
SHA256: state.sha256,
|
|
|
|
|
DefaultCategories: []string{"Productivity"},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
manifest := ma.FMAManifestFile{
|
|
|
|
|
Versions: versions,
|
|
|
|
|
Refs: map[string]string{"foobaz": "Hello World!"},
|
|
|
|
|
}
|
|
|
|
|
require.NoError(t, json.NewEncoder(w).Encode(manifest))
|
|
|
|
|
}))
|
|
|
|
|
t.Cleanup(func() {
|
|
|
|
|
manifestServer.Close()
|
|
|
|
|
installerServer.Close()
|
|
|
|
|
})
|
|
|
|
|
dev_mode.SetOverride("FLEET_DEV_MAINTAINED_APPS_BASE_URL", manifestServer.URL, t)
|
|
|
|
|
}
|
2026-04-02 20:56:31 +00:00
|
|
|
|
|
|
|
|
// acmeCSRSigner adapts a depot.Signer to the acme.CSRSigner interface.
|
|
|
|
|
type acmeCSRSigner struct {
|
|
|
|
|
signer *depot.Signer
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *acmeCSRSigner) SignCSR(_ context.Context, csr *x509.CertificateRequest) (*x509.Certificate, error) {
|
|
|
|
|
return a.signer.Signx509CSR(csr)
|
|
|
|
|
}
|
2026-04-03 14:58:03 +00:00
|
|
|
|
|
|
|
|
// mockRoundTripper is a custom http.RoundTripper that redirects requests to a mock server.
|
|
|
|
|
type mockRoundTripper struct {
|
|
|
|
|
mockServer string
|
|
|
|
|
origBaseURL string
|
|
|
|
|
next http.RoundTripper
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (rt *mockRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
|
|
|
if strings.Contains(req.URL.String(), rt.origBaseURL) {
|
|
|
|
|
path := strings.TrimPrefix(req.URL.Path, "/")
|
|
|
|
|
newURL := fmt.Sprintf("%s/%s", rt.mockServer, path)
|
|
|
|
|
newReq, err := http.NewRequestWithContext(req.Context(), req.Method, newURL, req.Body) //nolint:gosec // test helper, URL is from mock server
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
newReq.Header = req.Header
|
|
|
|
|
return rt.next.RoundTrip(newReq)
|
|
|
|
|
}
|
|
|
|
|
return rt.next.RoundTrip(req)
|
|
|
|
|
}
|