Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Basic Android MDM on/off functionality #26309

Merged
merged 50 commits into from
Feb 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
1dda21f
Added feature migrations
getvictor Jan 24, 2025
d42de45
Add feature schema.sql, and fix linting errors.
getvictor Jan 24, 2025
88db932
Setting up android directories.
getvictor Jan 24, 2025
e7c4263
WIP
getvictor Jan 27, 2025
534d50e
Temporarily skipping failing test.
getvictor Jan 28, 2025
a52b3e3
Hooked up DB for android enteprises.
getvictor Jan 28, 2025
464fca3
WIP
getvictor Jan 28, 2025
e4bf250
Things mostly working.
getvictor Jan 29, 2025
61fe90b
Updating android schema.
getvictor Jan 29, 2025
acc0c73
Split off Android service.
getvictor Jan 30, 2025
16be7ef
Added devices.delete to tools/android
getvictor Jan 30, 2025
1614233
Added architecture test.
getvictor Jan 31, 2025
655a44d
Minor cleanup.
getvictor Feb 3, 2025
0cb3c92
Merge remote-tracking branch 'origin/main' into victor/android-poc
getvictor Feb 5, 2025
ac001b5
Improved arch_test.go
getvictor Feb 7, 2025
9ac4983
Improved archtest
getvictor Feb 9, 2025
7c43db3
Merge remote-tracking branch 'origin/main' into victor/android-poc
getvictor Feb 9, 2025
29b39e8
Applied refactorings from main.
getvictor Feb 10, 2025
fe58e34
Minor fixes
getvictor Feb 10, 2025
759c08f
Merge remote-tracking branch 'origin/victor/android-poc' into victor/…
getvictor Feb 11, 2025
5b4c3de
WIP preparing for real Android implementation
getvictor Feb 11, 2025
5a61ea4
Merge remote-tracking branch 'origin/main' into victor/26218-android-…
getvictor Feb 11, 2025
35d9fde
Refactoring
getvictor Feb 11, 2025
a47516e
Refactoring
getvictor Feb 11, 2025
3d93714
Refactoring, still
getvictor Feb 11, 2025
caaa56f
Refactoring, still
getvictor Feb 11, 2025
e1471d4
Refactoring, done?
getvictor Feb 11, 2025
f79f67f
Cleanup
getvictor Feb 11, 2025
0b041d9
Removed AndroidCron const.
getvictor Feb 12, 2025
0f916e2
Fix issues in manual testing.
getvictor Feb 12, 2025
e9b6d11
Added signup_url
getvictor Feb 12, 2025
0ddc271
Added basic connect functionality. Does not return HTML page at this …
getvictor Feb 12, 2025
abfa94b
Fixes and start work on deleting enterprise.
getvictor Feb 12, 2025
b11d5ff
Update appConfig cloner.
getvictor Feb 12, 2025
4d1f723
Rolling back migration-related split.
getvictor Feb 13, 2025
7235ab4
Add an empty line
getvictor Feb 13, 2025
e7b18da
Moved android package under MDM
getvictor Feb 13, 2025
5f06d46
Merge remote-tracking branch 'origin/main' into victor/26218-android-…
getvictor Feb 13, 2025
00f5acb
Fixing issues
getvictor Feb 13, 2025
8f4c8ef
Cleanup
getvictor Feb 13, 2025
f7e2b00
Moved routes type.
getvictor Feb 13, 2025
cefd330
Merge remote-tracking branch 'origin/victor/26218-android-turn-on' in…
getvictor Feb 13, 2025
0b9a70b
Fixing up the merge.
getvictor Feb 13, 2025
e41516b
Cleanup
getvictor Feb 13, 2025
9e4b912
Finished delete enterprise functionality.
getvictor Feb 13, 2025
0e09daf
Merge remote-tracking branch 'origin/main' into victor/26218-basic-fu…
getvictor Feb 13, 2025
a662600
Merge to main fixes.
getvictor Feb 13, 2025
eb3b2dc
Fixes after manual testing.
getvictor Feb 13, 2025
df0c94f
Merge remote-tracking branch 'origin/main' into victor/26218-basic-fu…
getvictor Feb 14, 2025
832496e
Fix merge from main.
getvictor Feb 14, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
"ndes_scep_proxy": null
},
"mdm": {
"android_enabled_and_configured": false,
"apple_bm_terms_expired": false,
"apple_server_url": "",
"apple_bm_enabled_and_configured": false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"ndes_scep_proxy": null
},
"mdm": {
"android_enabled_and_configured": false,
"apple_bm_terms_expired": false,
"apple_server_url": "",
"apple_bm_enabled_and_configured": false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ spec:
ndes_scep_proxy: null
zendesk: null
mdm:
android_enabled_and_configured: false
apple_bm_terms_expired: false
apple_server_url: ""
apple_bm_enabled_and_configured: false
Expand Down
1 change: 1 addition & 0 deletions cmd/fleetctl/testdata/expectedGetConfigAppConfigYaml.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ spec:
ndes_scep_proxy: null
zendesk: null
mdm:
android_enabled_and_configured: false
apple_bm_terms_expired: false
apple_server_url: ""
apple_bm_enabled_and_configured: false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"enable_software_inventory": false
},
"mdm": {
"android_enabled_and_configured": false,
"apple_business_manager": null,
"apple_server_url": "",
"volume_purchasing_program": null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ spec:
ndes_scep_proxy: null
zendesk: null
mdm:
android_enabled_and_configured: false
apple_business_manager: null
apple_server_url: ""
volume_purchasing_program: null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ spec:
ndes_scep_proxy: null
zendesk: null
mdm:
android_enabled_and_configured: false
apple_business_manager:
apple_server_url: ""
volume_purchasing_program:
Expand Down
1 change: 1 addition & 0 deletions cmd/fleetctl/testdata/macosSetupExpectedAppConfigSet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ spec:
ndes_scep_proxy: null
zendesk: null
mdm:
android_enabled_and_configured: false
apple_business_manager:
apple_server_url: ""
volume_purchasing_program:
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ require (
github.com/github/smimesign v0.2.0
github.com/go-git/go-git/v5 v5.13.0
github.com/go-ini/ini v1.67.0
github.com/go-json-experiment/json v0.0.0-20250211171154-1ae217ad3535
github.com/go-kit/kit v0.12.0
github.com/go-kit/log v0.2.1
github.com/go-ole/go-ole v1.2.6
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,8 @@ github.com/go-git/go-git/v5 v5.13.0/go.mod h1:Wjo7/JyVKtQgUNdXYXIepzWfJQkUEIGvkv
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-json-experiment/json v0.0.0-20250211171154-1ae217ad3535 h1:yE7argOs92u+sSCRgqqe6eF+cDaVhSPlioy1UkA0p/w=
github.com/go-json-experiment/json v0.0.0-20250211171154-1ae217ad3535/go.mod h1:BWmvoE1Xia34f3l/ibJweyhrT+aROb/FQ6d+37F0e2s=
github.com/go-kit/kit v0.4.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.7.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
Expand Down
18 changes: 18 additions & 0 deletions server/archtest/archtest.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type PackageTest struct {
pkgs []string
includeRegex *regexp.Regexp
ignorePkgs map[string]struct{}
ignoreXTests map[string]struct{}
withTests bool
}

Expand Down Expand Up @@ -49,6 +50,20 @@ func (pt *PackageTest) IgnorePackages(pkgs ...string) *PackageTest {
return pt
}

func (pt *PackageTest) IgnoreXTests(pkgs ...string) *PackageTest {
if pt.ignoreXTests == nil {
pt.ignoreXTests = make(map[string]struct{}, len(pkgs))
}
cleanPkgs := make([]string, 0, len(pkgs))
for _, p := range pkgs {
cleanPkgs = append(cleanPkgs, strings.TrimSuffix(p, "_test"))
}
for _, p := range pt.expandPackages(cleanPkgs) {
pt.ignoreXTests[p] = struct{}{}
}
return pt
}

func (pt *PackageTest) WithTests() *PackageTest {
pt.withTests = true
return pt
Expand Down Expand Up @@ -147,6 +162,9 @@ func (pt *PackageTest) read(pChan chan<- *packageDependency, topDependency *pack
}

// XTestImports are packages with _test suffix that are in the same directory as the package.
if _, ignore := pt.ignoreXTests[dep.name]; ignore {
continue
}
for _, i := range pkg.XTestImports {
queue.PushBack(&packageDependency{name: i, parent: dep.asXTest()})
}
Expand Down
10 changes: 10 additions & 0 deletions server/authz/policy.rego
Original file line number Diff line number Diff line change
Expand Up @@ -1019,3 +1019,13 @@ allow {
subject.global_role == [admin, maintainer, gitops][_]
action == write
}

##
# Android
##
# Global admins can connect enteprise.
allow {
object.type == "android_enterprise"
subject.global_role == admin
action == write
}
11 changes: 11 additions & 0 deletions server/datastore/mysql/app_configs.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"strings"
"time"

"github.com/fleetdm/fleet/v4/server/contexts/ctxdb"
"github.com/fleetdm/fleet/v4/server/contexts/ctxerr"
"github.com/fleetdm/fleet/v4/server/fleet"
"github.com/jmoiron/sqlx"
Expand Down Expand Up @@ -108,6 +109,16 @@ func (ds *Datastore) insertOrReplaceConfigAsset(ctx context.Context, tx sqlx.Ext
return nil
}

func (ds *Datastore) SetAndroidEnabledAndConfigured(ctx context.Context, configured bool) error {
ctx = ctxdb.RequirePrimary(ctx, true)
appConfig, err := ds.AppConfig(ctx)
if err != nil {
return err
}
appConfig.MDM.AndroidEnabledAndConfigured = configured
return ds.SaveAppConfig(ctx, appConfig)
}

func (ds *Datastore) VerifyEnrollSecret(ctx context.Context, secret string) (*fleet.EnrollSecret, error) {
var s fleet.EnrollSecret
err := sqlx.GetContext(ctx, ds.reader(ctx), &s, "SELECT team_id FROM enroll_secrets WHERE secret = ?", secret)
Expand Down
2 changes: 1 addition & 1 deletion server/datastore/mysql/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ CREATE TABLE `app_config_json` (
PRIMARY KEY (`id`)
) /*!50100 TABLESPACE `innodb_system` */ ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
INSERT INTO `app_config_json` VALUES (1,'{\"mdm\": {\"ios_updates\": {\"deadline\": null, \"minimum_version\": null}, \"macos_setup\": {\"script\": null, \"software\": null, \"bootstrap_package\": null, \"macos_setup_assistant\": null, \"enable_end_user_authentication\": false, \"enable_release_device_manually\": false}, \"macos_updates\": {\"deadline\": null, \"minimum_version\": null}, \"ipados_updates\": {\"deadline\": null, \"minimum_version\": null}, \"macos_settings\": {\"custom_settings\": null}, \"macos_migration\": {\"mode\": \"\", \"enable\": false, \"webhook_url\": \"\"}, \"windows_updates\": {\"deadline_days\": null, \"grace_period_days\": null}, \"apple_server_url\": \"\", \"windows_settings\": {\"custom_settings\": null}, \"apple_bm_terms_expired\": false, \"apple_business_manager\": null, \"enable_disk_encryption\": false, \"enabled_and_configured\": false, \"end_user_authentication\": {\"idp_name\": \"\", \"metadata\": \"\", \"entity_id\": \"\", \"issuer_uri\": \"\", \"metadata_url\": \"\"}, \"volume_purchasing_program\": null, \"windows_migration_enabled\": false, \"windows_enabled_and_configured\": false, \"apple_bm_enabled_and_configured\": false}, \"scripts\": null, \"features\": {\"enable_host_users\": true, \"enable_software_inventory\": false}, \"org_info\": {\"org_name\": \"\", \"contact_url\": \"\", \"org_logo_url\": \"\", \"org_logo_url_light_background\": \"\"}, \"integrations\": {\"jira\": null, \"zendesk\": null, \"google_calendar\": null, \"ndes_scep_proxy\": null}, \"sso_settings\": {\"idp_name\": \"\", \"metadata\": \"\", \"entity_id\": \"\", \"enable_sso\": false, \"issuer_uri\": \"\", \"metadata_url\": \"\", \"idp_image_url\": \"\", \"enable_jit_role_sync\": false, \"enable_sso_idp_login\": false, \"enable_jit_provisioning\": false}, \"agent_options\": {\"config\": {\"options\": {\"logger_plugin\": \"tls\", \"pack_delimiter\": \"/\", \"logger_tls_period\": 10, \"distributed_plugin\": \"tls\", \"disable_distributed\": false, \"logger_tls_endpoint\": \"/api/osquery/log\", \"distributed_interval\": 10, \"distributed_tls_max_attempts\": 3}, \"decorators\": {\"load\": [\"SELECT uuid AS host_uuid FROM system_info;\", \"SELECT hostname AS hostname FROM system_info;\"]}}, \"overrides\": {}}, \"fleet_desktop\": {\"transparency_url\": \"\"}, \"smtp_settings\": {\"port\": 587, \"domain\": \"\", \"server\": \"\", \"password\": \"\", \"user_name\": \"\", \"configured\": false, \"enable_smtp\": false, \"enable_ssl_tls\": true, \"sender_address\": \"\", \"enable_start_tls\": true, \"verify_ssl_certs\": true, \"authentication_type\": \"0\", \"authentication_method\": \"0\"}, \"server_settings\": {\"server_url\": \"\", \"enable_analytics\": false, \"query_report_cap\": 0, \"scripts_disabled\": false, \"deferred_save_host\": false, \"live_query_disabled\": false, \"ai_features_disabled\": false, \"query_reports_disabled\": false}, \"webhook_settings\": {\"interval\": \"0s\", \"activities_webhook\": {\"destination_url\": \"\", \"enable_activities_webhook\": false}, \"host_status_webhook\": {\"days_count\": 0, \"destination_url\": \"\", \"host_percentage\": 0, \"enable_host_status_webhook\": false}, \"vulnerabilities_webhook\": {\"destination_url\": \"\", \"host_batch_size\": 0, \"enable_vulnerabilities_webhook\": false}, \"failing_policies_webhook\": {\"policy_ids\": null, \"destination_url\": \"\", \"host_batch_size\": 0, \"enable_failing_policies_webhook\": false}}, \"host_expiry_settings\": {\"host_expiry_window\": 0, \"host_expiry_enabled\": false}, \"vulnerability_settings\": {\"databases_path\": \"\"}, \"activity_expiry_settings\": {\"activity_expiry_window\": 0, \"activity_expiry_enabled\": false}}','2020-01-01 01:01:01','2020-01-01 01:01:01');
INSERT INTO `app_config_json` VALUES (1,'{\"mdm\": {\"ios_updates\": {\"deadline\": null, \"minimum_version\": null}, \"macos_setup\": {\"script\": null, \"software\": null, \"bootstrap_package\": null, \"macos_setup_assistant\": null, \"enable_end_user_authentication\": false, \"enable_release_device_manually\": false}, \"macos_updates\": {\"deadline\": null, \"minimum_version\": null}, \"ipados_updates\": {\"deadline\": null, \"minimum_version\": null}, \"macos_settings\": {\"custom_settings\": null}, \"macos_migration\": {\"mode\": \"\", \"enable\": false, \"webhook_url\": \"\"}, \"windows_updates\": {\"deadline_days\": null, \"grace_period_days\": null}, \"apple_server_url\": \"\", \"windows_settings\": {\"custom_settings\": null}, \"apple_bm_terms_expired\": false, \"apple_business_manager\": null, \"enable_disk_encryption\": false, \"enabled_and_configured\": false, \"end_user_authentication\": {\"idp_name\": \"\", \"metadata\": \"\", \"entity_id\": \"\", \"issuer_uri\": \"\", \"metadata_url\": \"\"}, \"volume_purchasing_program\": null, \"windows_migration_enabled\": false, \"android_enabled_and_configured\": false, \"windows_enabled_and_configured\": false, \"apple_bm_enabled_and_configured\": false}, \"scripts\": null, \"features\": {\"enable_host_users\": true, \"enable_software_inventory\": false}, \"org_info\": {\"org_name\": \"\", \"contact_url\": \"\", \"org_logo_url\": \"\", \"org_logo_url_light_background\": \"\"}, \"integrations\": {\"jira\": null, \"zendesk\": null, \"google_calendar\": null, \"ndes_scep_proxy\": null}, \"sso_settings\": {\"idp_name\": \"\", \"metadata\": \"\", \"entity_id\": \"\", \"enable_sso\": false, \"issuer_uri\": \"\", \"metadata_url\": \"\", \"idp_image_url\": \"\", \"enable_jit_role_sync\": false, \"enable_sso_idp_login\": false, \"enable_jit_provisioning\": false}, \"agent_options\": {\"config\": {\"options\": {\"logger_plugin\": \"tls\", \"pack_delimiter\": \"/\", \"logger_tls_period\": 10, \"distributed_plugin\": \"tls\", \"disable_distributed\": false, \"logger_tls_endpoint\": \"/api/osquery/log\", \"distributed_interval\": 10, \"distributed_tls_max_attempts\": 3}, \"decorators\": {\"load\": [\"SELECT uuid AS host_uuid FROM system_info;\", \"SELECT hostname AS hostname FROM system_info;\"]}}, \"overrides\": {}}, \"fleet_desktop\": {\"transparency_url\": \"\"}, \"smtp_settings\": {\"port\": 587, \"domain\": \"\", \"server\": \"\", \"password\": \"\", \"user_name\": \"\", \"configured\": false, \"enable_smtp\": false, \"enable_ssl_tls\": true, \"sender_address\": \"\", \"enable_start_tls\": true, \"verify_ssl_certs\": true, \"authentication_type\": \"0\", \"authentication_method\": \"0\"}, \"server_settings\": {\"server_url\": \"\", \"enable_analytics\": false, \"query_report_cap\": 0, \"scripts_disabled\": false, \"deferred_save_host\": false, \"live_query_disabled\": false, \"ai_features_disabled\": false, \"query_reports_disabled\": false}, \"webhook_settings\": {\"interval\": \"0s\", \"activities_webhook\": {\"destination_url\": \"\", \"enable_activities_webhook\": false}, \"host_status_webhook\": {\"days_count\": 0, \"destination_url\": \"\", \"host_percentage\": 0, \"enable_host_status_webhook\": false}, \"vulnerabilities_webhook\": {\"destination_url\": \"\", \"host_batch_size\": 0, \"enable_vulnerabilities_webhook\": false}, \"failing_policies_webhook\": {\"policy_ids\": null, \"destination_url\": \"\", \"host_batch_size\": 0, \"enable_failing_policies_webhook\": false}}, \"host_expiry_settings\": {\"host_expiry_window\": 0, \"host_expiry_enabled\": false}, \"vulnerability_settings\": {\"databases_path\": \"\"}, \"activity_expiry_settings\": {\"activity_expiry_window\": 0, \"activity_expiry_enabled\": false}}','2020-01-01 01:01:01','2020-01-01 01:01:01');
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `calendar_events` (
Expand Down
3 changes: 3 additions & 0 deletions server/fleet/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,9 @@ type MDM struct {

VolumePurchasingProgram optjson.Slice[MDMAppleVolumePurchasingProgramInfo] `json:"volume_purchasing_program"`

// AndroidEnabledAndConfigured is set to true if Fleet successfully bound to an Android Management Enterprise
AndroidEnabledAndConfigured bool `json:"android_enabled_and_configured"`

/////////////////////////////////////////////////////////////////
// WARNING: If you add to this struct make sure it's taken into
// account in the AppConfig Clone implementation!
Expand Down
5 changes: 5 additions & 0 deletions server/fleet/datastore.go
Original file line number Diff line number Diff line change
Expand Up @@ -1962,6 +1962,11 @@ type Datastore interface {
// ExpandEmbeddedSecretsAndUpdatedAt is like ExpandEmbeddedSecrets but also
// returns the latest updated_at time of the secrets used in the expansion.
ExpandEmbeddedSecretsAndUpdatedAt(ctx context.Context, document string) (string, *time.Time, error)

// /////////////////////////////////////////////////////////////////////////////
// Android

SetAndroidEnabledAndConfigured(ctx context.Context, configured bool) error
}

// MDMAppleStore wraps nanomdm's storage and adds methods to deal with
Expand Down
8 changes: 6 additions & 2 deletions server/mdm/android/android.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package android

type SignupDetails struct {
Url string `json:"url,omitempty"`
Name string `json:"name,omitempty"`
Url string
Name string
}

type Enterprise struct {
Expand All @@ -19,6 +19,10 @@ func (e Enterprise) IsValid() bool {
return e.EnterpriseID != ""
}

func (e Enterprise) AuthzType() string {
return "android_enterprise"
}

type EnrollmentToken struct {
Value string `json:"value"`
}
Expand Down
2 changes: 2 additions & 0 deletions server/mdm/android/arch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ func TestAllAndroidPackageDependencies(t *testing.T) {
t.Parallel()
archtest.NewPackageTest(t, "github.com/fleetdm/fleet/v4/server/mdm/android...").
OnlyInclude(regexp.MustCompile(`^github\.com/fleetdm/`)).
WithTests().
IgnoreXTests("github.com/fleetdm/fleet/v4/server/fleet"). // ignore fleet_test package
IgnorePackages(
"github.com/fleetdm/fleet/v4/server/datastore/mysql/common_mysql",
"github.com/fleetdm/fleet/v4/server/service/externalsvc", // TODO(#26218): remove this dependency on Jira and Zendesk
Expand Down
4 changes: 3 additions & 1 deletion server/mdm/android/datastore.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
type Datastore interface {
CreateEnterprise(ctx context.Context) (uint, error)
GetEnterpriseByID(ctx context.Context, ID uint) (*Enterprise, error)
GetEnterprise(ctx context.Context) (*Enterprise, error)
UpdateEnterprise(ctx context.Context, enterprise *Enterprise) error
ListEnterprises(ctx context.Context) ([]*Enterprise, error)
DeleteEnterprises(ctx context.Context) error
DeleteOtherEnterprises(ctx context.Context, ID uint) error
}
35 changes: 28 additions & 7 deletions server/mdm/android/mysql/enterprises.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,20 @@ func (ds *Datastore) GetEnterpriseByID(ctx context.Context, id uint) (*android.E
case errors.Is(err, sql.ErrNoRows):
return nil, notFound("Android enterprise").WithID(id)
case err != nil:
return nil, ctxerr.Wrap(ctx, err, "selecting enterprise")
return nil, ctxerr.Wrap(ctx, err, "getting enterprise by id")
}
return &enterprise, nil
}

func (ds *Datastore) GetEnterprise(ctx context.Context) (*android.Enterprise, error) {
stmt := `SELECT id, enterprise_id FROM android_enterprises WHERE enterprise_id != '' LIMIT 1`
var enterprise android.Enterprise
err := sqlx.GetContext(ctx, ds.reader(ctx), &enterprise, stmt)
switch {
case errors.Is(err, sql.ErrNoRows):
return nil, notFound("Android enterprise")
case err != nil:
return nil, ctxerr.Wrap(ctx, err, "getting active enterprise")
}
return &enterprise, nil
}
Expand All @@ -51,12 +64,20 @@ func (ds *Datastore) UpdateEnterprise(ctx context.Context, enterprise *android.E
return nil
}

func (ds *Datastore) ListEnterprises(ctx context.Context) ([]*android.Enterprise, error) {
stmt := `SELECT id, signup_name, enterprise_id FROM android_enterprises`
var enterprises []*android.Enterprise
err := sqlx.SelectContext(ctx, ds.reader(ctx), &enterprises, stmt)
func (ds *Datastore) DeleteOtherEnterprises(ctx context.Context, id uint) error {
stmt := `DELETE FROM android_enterprises WHERE id != ?`
_, err := ds.Writer(ctx).ExecContext(ctx, stmt, id)
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "selecting enterprises")
return ctxerr.Wrap(ctx, err, "deleting other enterprises")
}
return enterprises, nil
return nil
}

func (ds *Datastore) DeleteEnterprises(ctx context.Context) error {
stmt := `DELETE FROM android_enterprises`
_, err := ds.Writer(ctx).ExecContext(ctx, stmt)
if err != nil {
return ctxerr.Wrap(ctx, err, "deleting all enterprises")
}
return nil
}
62 changes: 59 additions & 3 deletions server/mdm/android/mysql/enterprises_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ func TestEnterprise(t *testing.T) {
}{
{"CreateGetEnterprise", testCreateGetEnterprise},
{"UpdateEnterprise", testUpdateEnterprise},
{"DeleteEnterprises", testDeleteEnterprises},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
Expand Down Expand Up @@ -65,10 +66,65 @@ func testUpdateEnterprise(t *testing.T, ds *mysql.Datastore) {
require.NoError(t, err)
assert.Equal(t, enterprise, result)

enterprises, err := ds.ListEnterprises(testCtx())
result, err = ds.GetEnterprise(testCtx())
require.NoError(t, err)
assert.Len(t, enterprises, 1)
assert.Equal(t, enterprise, enterprises[0])
assert.Equal(t, enterprise.ID, result.ID)
assert.Equal(t, enterprise.EnterpriseID, result.EnterpriseID)
}

func testDeleteEnterprises(t *testing.T, ds *mysql.Datastore) {
err := ds.DeleteEnterprises(testCtx())
require.NoError(t, err)
err = ds.DeleteOtherEnterprises(testCtx(), 9999)
require.NoError(t, err)

enterprise := createEnterprise(t, ds)
result, err := ds.GetEnterpriseByID(testCtx(), enterprise.ID)
require.NoError(t, err)
assert.Equal(t, enterprise, result)

// Create enteprise without enterprise_id
id, err := ds.CreateEnterprise(testCtx())
require.NoError(t, err)
assert.NotZero(t, id)

tempEnterprise := &android.Enterprise{
ID: id, // start with an invalid ID
SignupName: "signupUrls/C97372c91c6a85139",
EnterpriseID: "",
}
err = ds.UpdateEnterprise(testCtx(), tempEnterprise)
require.NoError(t, err)

err = ds.DeleteOtherEnterprises(testCtx(), enterprise.ID)
require.NoError(t, err)
result, err = ds.GetEnterpriseByID(testCtx(), enterprise.ID)
require.NoError(t, err)
assert.Equal(t, enterprise, result)
_, err = ds.GetEnterpriseByID(testCtx(), tempEnterprise.ID)
assert.True(t, fleet.IsNotFound(err))

err = ds.DeleteEnterprises(testCtx())
require.NoError(t, err)
_, err = ds.GetEnterpriseByID(testCtx(), enterprise.ID)
assert.True(t, fleet.IsNotFound(err))

}

func createEnterprise(t *testing.T, ds *mysql.Datastore) *android.Enterprise {
enterprise := &android.Enterprise{
ID: 9999, // start with an invalid ID
SignupName: "signupUrls/C97372c91c6a85139",
EnterpriseID: "LC04bp524j",
}
id, err := ds.CreateEnterprise(testCtx())
require.NoError(t, err)
assert.NotZero(t, id)

enterprise.ID = id
err = ds.UpdateEnterprise(testCtx(), enterprise)
require.NoError(t, err)
return enterprise
}

func testCtx() context.Context {
Expand Down
Loading
Loading