From 436d2babb914e2d43d275e2157ecb819fced399b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Falconnier?= Date: Sun, 6 Aug 2023 11:32:29 +0200 Subject: [PATCH] Add MDM recovery password configurations --- goztl.go | 14 +- mdm_blueprints.go | 32 ++-- mdm_blueprints_test.go | 54 +++--- mdm_recovery_password_configs.go | 194 +++++++++++++++++++ mdm_recovery_password_configs_test.go | 266 ++++++++++++++++++++++++++ 5 files changed, 515 insertions(+), 45 deletions(-) create mode 100644 mdm_recovery_password_configs.go create mode 100644 mdm_recovery_password_configs_test.go diff --git a/goztl.go b/goztl.go index 599b815..fc1ead5 100644 --- a/goztl.go +++ b/goztl.go @@ -15,7 +15,7 @@ import ( ) const ( - libraryVersion = "0.1.36" + libraryVersion = "0.1.37" userAgent = "goztl/" + libraryVersion mediaType = "application/json" ) @@ -38,11 +38,12 @@ type Client struct { Tags TagsService Taxonomies TaxonomiesService // MDM - MDMArtifacts MDMArtifactsService - MDMBlueprints MDMBlueprintsService - MDMBlueprintArtifacts MDMBlueprintArtifactsService - MDMFileVaultConfigs MDMFileVaultConfigsService - MDMProfiles MDMProfilesService + MDMArtifacts MDMArtifactsService + MDMBlueprints MDMBlueprintsService + MDMBlueprintArtifacts MDMBlueprintArtifactsService + MDMFileVaultConfigs MDMFileVaultConfigsService + MDMProfiles MDMProfilesService + MDMRecoveryPasswordConfigs MDMRecoveryPasswordConfigsService // Monolith MonolithCatalogs MonolithCatalogsService MonolithConditions MonolithConditionsService @@ -160,6 +161,7 @@ func NewClient(httpClient *http.Client, bu string, token string, opts ...ClientO c.MDMBlueprintArtifacts = &MDMBlueprintArtifactsServiceOp{client: c} c.MDMFileVaultConfigs = &MDMFileVaultConfigsServiceOp{client: c} c.MDMProfiles = &MDMProfilesServiceOp{client: c} + c.MDMRecoveryPasswordConfigs = &MDMRecoveryPasswordConfigsServiceOp{client: c} // Monolith c.MonolithCatalogs = &MonolithCatalogsServiceOp{client: c} c.MonolithConditions = &MonolithConditionsServiceOp{client: c} diff --git a/mdm_blueprints.go b/mdm_blueprints.go index e04e030..f07bb97 100644 --- a/mdm_blueprints.go +++ b/mdm_blueprints.go @@ -29,15 +29,16 @@ var _ MDMBlueprintsService = &MDMBlueprintsServiceOp{} // MDMBlueprint represents a Zentral MDM blueprint type MDMBlueprint struct { - ID int `json:"id,omitempty"` - Name string `json:"name"` - InventoryInterval int `json:"inventory_interval"` - CollectApps int `json:"collect_apps"` - CollectCertificates int `json:"collect_certificates"` - CollectProfiles int `json:"collect_profiles"` - FileVaultConfigID *int `json:"filevault_config"` - Created Timestamp `json:"created_at,omitempty"` - Updated Timestamp `json:"updated_at,omitempty"` + ID int `json:"id,omitempty"` + Name string `json:"name"` + InventoryInterval int `json:"inventory_interval"` + CollectApps int `json:"collect_apps"` + CollectCertificates int `json:"collect_certificates"` + CollectProfiles int `json:"collect_profiles"` + FileVaultConfigID *int `json:"filevault_config"` + RecoveryPasswordConfigID *int `json:"recovery_password_config"` + Created Timestamp `json:"created_at,omitempty"` + Updated Timestamp `json:"updated_at,omitempty"` } func (mb MDMBlueprint) String() string { @@ -46,12 +47,13 @@ func (mb MDMBlueprint) String() string { // MDMBlueprintRequest represents a request to create or update a MDM blueprint type MDMBlueprintRequest struct { - Name string `json:"name"` - InventoryInterval int `json:"inventory_interval"` - CollectApps int `json:"collect_apps"` - CollectCertificates int `json:"collect_certificates"` - CollectProfiles int `json:"collect_profiles"` - FileVaultConfigID *int `json:"filevault_config"` + Name string `json:"name"` + InventoryInterval int `json:"inventory_interval"` + CollectApps int `json:"collect_apps"` + CollectCertificates int `json:"collect_certificates"` + CollectProfiles int `json:"collect_profiles"` + FileVaultConfigID *int `json:"filevault_config"` + RecoveryPasswordConfigID *int `json:"recovery_password_config"` } type listMBOptions struct { diff --git a/mdm_blueprints_test.go b/mdm_blueprints_test.go index 0c90c16..a97bba2 100644 --- a/mdm_blueprints_test.go +++ b/mdm_blueprints_test.go @@ -21,6 +21,7 @@ var mbListJSONResponse = ` "collect_certificates": 1, "collect_profiles": 2, "filevault_config": null, + "recovery_password_config": null, "created_at": "2022-07-22T01:02:03.444444", "updated_at": "2022-07-22T01:02:03.444444" } @@ -36,6 +37,7 @@ var mbGetJSONResponse = ` "collect_certificates": 1, "collect_profiles": 2, "filevault_config": 3, + "recovery_password_config": 4, "created_at": "2022-07-22T01:02:03.444444", "updated_at": "2022-07-22T01:02:03.444444" } @@ -63,6 +65,7 @@ var mbUpdateJSONResponse = ` "collect_certificates": 1, "collect_profiles": 2, "filevault_config": 3, + "recovery_password_config": 4, "created_at": "2022-07-22T01:02:03.444444", "updated_at": "2022-07-22T01:02:03.444444" } @@ -118,15 +121,16 @@ func TestMDMBlueprintsService_GetByID(t *testing.T) { } want := &MDMBlueprint{ - ID: 4, - Name: "Default", - InventoryInterval: 77777, - CollectApps: 0, - CollectCertificates: 1, - CollectProfiles: 2, - FileVaultConfigID: Int(3), - Created: Timestamp{referenceTime}, - Updated: Timestamp{referenceTime}, + ID: 4, + Name: "Default", + InventoryInterval: 77777, + CollectApps: 0, + CollectCertificates: 1, + CollectProfiles: 2, + FileVaultConfigID: Int(3), + RecoveryPasswordConfigID: Int(4), + Created: Timestamp{referenceTime}, + Updated: Timestamp{referenceTime}, } if !cmp.Equal(got, want) { t.Errorf("MDMBlueprints.GetByID returned %+v, want %+v", got, want) @@ -217,12 +221,13 @@ func TestMDMBlueprintsService_Update(t *testing.T) { defer teardown() updateRequest := &MDMBlueprintRequest{ - Name: "Default", - InventoryInterval: 77777, - CollectApps: 0, - CollectCertificates: 1, - CollectProfiles: 2, - FileVaultConfigID: Int(3), + Name: "Default", + InventoryInterval: 77777, + CollectApps: 0, + CollectCertificates: 1, + CollectProfiles: 2, + FileVaultConfigID: Int(3), + RecoveryPasswordConfigID: Int(4), } mux.HandleFunc("/mdm/blueprints/4/", func(w http.ResponseWriter, r *http.Request) { @@ -245,15 +250,16 @@ func TestMDMBlueprintsService_Update(t *testing.T) { } want := &MDMBlueprint{ - ID: 4, - Name: "Default", - InventoryInterval: 77777, - CollectApps: 0, - CollectCertificates: 1, - CollectProfiles: 2, - FileVaultConfigID: Int(3), - Created: Timestamp{referenceTime}, - Updated: Timestamp{referenceTime}, + ID: 4, + Name: "Default", + InventoryInterval: 77777, + CollectApps: 0, + CollectCertificates: 1, + CollectProfiles: 2, + FileVaultConfigID: Int(3), + RecoveryPasswordConfigID: Int(4), + Created: Timestamp{referenceTime}, + Updated: Timestamp{referenceTime}, } if !cmp.Equal(got, want) { t.Errorf("MDMBlueprints.Update returned %+v, want %+v", got, want) diff --git a/mdm_recovery_password_configs.go b/mdm_recovery_password_configs.go new file mode 100644 index 0000000..86e6996 --- /dev/null +++ b/mdm_recovery_password_configs.go @@ -0,0 +1,194 @@ +package goztl + +import ( + "context" + "fmt" + "net/http" +) + +const mrpcBasePath = "mdm/recovery_password_configs/" + +// MDMRecoveryPasswordConfigsService is an interface for interfacing with the MDM recovery password configuration +// endpoints of the Zentral API +type MDMRecoveryPasswordConfigsService interface { + List(context.Context, *ListOptions) ([]MDMRecoveryPasswordConfig, *Response, error) + GetByID(context.Context, int) (*MDMRecoveryPasswordConfig, *Response, error) + GetByName(context.Context, string) (*MDMRecoveryPasswordConfig, *Response, error) + Create(context.Context, *MDMRecoveryPasswordConfigRequest) (*MDMRecoveryPasswordConfig, *Response, error) + Update(context.Context, int, *MDMRecoveryPasswordConfigRequest) (*MDMRecoveryPasswordConfig, *Response, error) + Delete(context.Context, int) (*Response, error) +} + +// MDMRecoveryPasswordConfigsServiceOp handles communication with the MDM recovery password configurations related +// methods of the Zentral API. +type MDMRecoveryPasswordConfigsServiceOp struct { + client *Client +} + +var _ MDMRecoveryPasswordConfigsService = &MDMRecoveryPasswordConfigsServiceOp{} + +// MDMRecoveryPasswordConfig represents a Zentral MDM recovery password configuration +type MDMRecoveryPasswordConfig struct { + ID int `json:"id"` + Name string `json:"name"` + DynamicPassword bool `json:"dynamic_password"` + StaticPassword *string `json:"static_password"` + RotationIntervalDays int `json:"rotation_interval_days"` + RotateFirmwarePassword bool `json:"rotate_firmware_password"` + Created Timestamp `json:"created_at,omitempty"` + Updated Timestamp `json:"updated_at,omitempty"` +} + +func (mrpc MDMRecoveryPasswordConfig) String() string { + return Stringify(mrpc) +} + +// MDMRecoveryPasswordConfigRequest represents a request to create or update a MDM recovery password configuration +type MDMRecoveryPasswordConfigRequest struct { + Name string `json:"name"` + DynamicPassword bool `json:"dynamic_password"` + StaticPassword *string `json:"static_password"` + RotationIntervalDays int `json:"rotation_interval_days"` + RotateFirmwarePassword bool `json:"rotate_firmware_password"` +} + +type listMRPCOptions struct { + Name string `url:"name,omitempty"` +} + +// List lists all the MDM recovery password configurations. +func (s *MDMRecoveryPasswordConfigsServiceOp) List(ctx context.Context, opt *ListOptions) ([]MDMRecoveryPasswordConfig, *Response, error) { + return s.list(ctx, opt, nil) +} + +// GetByID retrieves a MDM recovery password configuration by id. +func (s *MDMRecoveryPasswordConfigsServiceOp) GetByID(ctx context.Context, mrpcID int) (*MDMRecoveryPasswordConfig, *Response, error) { + if mrpcID < 1 { + return nil, nil, NewArgError("mrpcID", "cannot be less than 1") + } + + path := fmt.Sprintf("%s%d/", mrpcBasePath, mrpcID) + + req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil) + if err != nil { + return nil, nil, err + } + + mrpc := new(MDMRecoveryPasswordConfig) + + resp, err := s.client.Do(ctx, req, mrpc) + if err != nil { + return nil, resp, err + } + + return mrpc, resp, err +} + +// GetByName retrieves a MDM recovery password configuration by name. +func (s *MDMRecoveryPasswordConfigsServiceOp) GetByName(ctx context.Context, name string) (*MDMRecoveryPasswordConfig, *Response, error) { + if len(name) < 1 { + return nil, nil, NewArgError("name", "cannot be blank") + } + + listMRPCOpt := &listMRPCOptions{Name: name} + + mrpcs, resp, err := s.list(ctx, nil, listMRPCOpt) + if err != nil { + return nil, resp, err + } + if len(mrpcs) < 1 { + return nil, resp, nil + } + + return &mrpcs[0], resp, err +} + +// Create a new MDM recovery password configuration. +func (s *MDMRecoveryPasswordConfigsServiceOp) Create(ctx context.Context, createRequest *MDMRecoveryPasswordConfigRequest) (*MDMRecoveryPasswordConfig, *Response, error) { + if createRequest == nil { + return nil, nil, NewArgError("createRequest", "cannot be nil") + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, mrpcBasePath, createRequest) + if err != nil { + return nil, nil, err + } + + mrpc := new(MDMRecoveryPasswordConfig) + resp, err := s.client.Do(ctx, req, mrpc) + if err != nil { + return nil, resp, err + } + + return mrpc, resp, err +} + +// Update a MDM recovery password configuration. +func (s *MDMRecoveryPasswordConfigsServiceOp) Update(ctx context.Context, mrpcID int, updateRequest *MDMRecoveryPasswordConfigRequest) (*MDMRecoveryPasswordConfig, *Response, error) { + if mrpcID < 1 { + return nil, nil, NewArgError("mrpcID", "cannot be less than 1") + } + + if updateRequest == nil { + return nil, nil, NewArgError("updateRequest", "cannot be nil") + } + + path := fmt.Sprintf("%s%d/", mrpcBasePath, mrpcID) + + req, err := s.client.NewRequest(ctx, http.MethodPut, path, updateRequest) + if err != nil { + return nil, nil, err + } + + mrpc := new(MDMRecoveryPasswordConfig) + resp, err := s.client.Do(ctx, req, mrpc) + if err != nil { + return nil, resp, err + } + + return mrpc, resp, err +} + +// Delete a MDM recovery password configuration. +func (s *MDMRecoveryPasswordConfigsServiceOp) Delete(ctx context.Context, mrpcID int) (*Response, error) { + if mrpcID < 1 { + return nil, NewArgError("mrpcID", "cannot be less than 1") + } + + path := fmt.Sprintf("%s%d/", mrpcBasePath, mrpcID) + + req, err := s.client.NewRequest(ctx, http.MethodDelete, path, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(ctx, req, nil) + + return resp, err +} + +// Helper method for listing MDM recovery password configurations +func (s *MDMRecoveryPasswordConfigsServiceOp) list(ctx context.Context, opt *ListOptions, mrpcOpt *listMRPCOptions) ([]MDMRecoveryPasswordConfig, *Response, error) { + path := mrpcBasePath + path, err := addOptions(path, opt) + if err != nil { + return nil, nil, err + } + path, err = addOptions(path, mrpcOpt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil) + if err != nil { + return nil, nil, err + } + + var mrpcs []MDMRecoveryPasswordConfig + resp, err := s.client.Do(ctx, req, &mrpcs) + if err != nil { + return nil, resp, err + } + + return mrpcs, resp, err +} diff --git a/mdm_recovery_password_configs_test.go b/mdm_recovery_password_configs_test.go new file mode 100644 index 0000000..d207324 --- /dev/null +++ b/mdm_recovery_password_configs_test.go @@ -0,0 +1,266 @@ +package goztl + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/assert" +) + +var mrpcListJSONResponse = ` +[ + { + "id": 4, + "name": "Default", + "dynamic_password": true, + "static_password": null, + "rotation_interval_days": 90, + "rotate_firmware_password": true, + "created_at": "2022-07-22T01:02:03.444444", + "updated_at": "2022-07-22T01:02:03.444444" + } +] +` + +var mrpcGetJSONResponse = ` +{ + "id": 4, + "name": "Default", + "dynamic_password": false, + "static_password": "12345678", + "rotation_interval_days": 0, + "rotate_firmware_password": false, + "created_at": "2022-07-22T01:02:03.444444", + "updated_at": "2022-07-22T01:02:03.444444" +} +` + +var mrpcCreateJSONResponse = ` +{ + "id": 4, + "name": "Default", + "dynamic_password": false, + "static_password": "12345678", + "rotation_interval_days": 0, + "rotate_firmware_password": false, + "created_at": "2022-07-22T01:02:03.444444", + "updated_at": "2022-07-22T01:02:03.444444" +} +` + +var mrpcUpdateJSONResponse = ` +{ + "id": 4, + "name": "Default", + "dynamic_password": true, + "rotation_interval_days": 90, + "rotate_firmware_password": true, + "created_at": "2022-07-22T01:02:03.444444", + "updated_at": "2022-07-22T01:02:03.444444" +} +` + +func TestMDMRecoveryPasswordConfigsService_List(t *testing.T) { + client, mux, teardown := setup() + defer teardown() + + mux.HandleFunc("/mdm/recovery_password_configs/", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", "application/json") + fmt.Fprint(w, mrpcListJSONResponse) + }) + + ctx := context.Background() + got, _, err := client.MDMRecoveryPasswordConfigs.List(ctx, nil) + if err != nil { + t.Errorf("MDMRecoveryPasswordConfigs.List returned error: %v", err) + } + + want := []MDMRecoveryPasswordConfig{ + { + ID: 4, + Name: "Default", + DynamicPassword: true, + RotationIntervalDays: 90, + RotateFirmwarePassword: true, + Created: Timestamp{referenceTime}, + Updated: Timestamp{referenceTime}, + }, + } + if !cmp.Equal(got, want) { + t.Errorf("MDMRecoveryPasswordConfigs.List returned %+v, want %+v", got, want) + } +} + +func TestMDMRecoveryPasswordConfigsService_GetByID(t *testing.T) { + client, mux, teardown := setup() + defer teardown() + + mux.HandleFunc("/mdm/recovery_password_configs/4/", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", "application/json") + fmt.Fprint(w, mrpcGetJSONResponse) + }) + + ctx := context.Background() + got, _, err := client.MDMRecoveryPasswordConfigs.GetByID(ctx, 4) + if err != nil { + t.Errorf("MDMRecoveryPasswordConfigs.GetByID returned error: %v", err) + } + + want := &MDMRecoveryPasswordConfig{ + ID: 4, + Name: "Default", + DynamicPassword: false, + StaticPassword: String("12345678"), + RotationIntervalDays: 0, + RotateFirmwarePassword: false, + Created: Timestamp{referenceTime}, + Updated: Timestamp{referenceTime}, + } + if !cmp.Equal(got, want) { + t.Errorf("MDMRecoveryPasswordConfigs.GetByID returned %+v, want %+v", got, want) + } +} + +func TestMDMRecoveryPasswordConfigsService_GetByName(t *testing.T) { + client, mux, teardown := setup() + defer teardown() + + mux.HandleFunc("/mdm/recovery_password_configs/", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", "application/json") + testQueryArg(t, r, "name", "Default") + fmt.Fprint(w, mrpcListJSONResponse) + }) + + ctx := context.Background() + got, _, err := client.MDMRecoveryPasswordConfigs.GetByName(ctx, "Default") + if err != nil { + t.Errorf("MDMRecoveryPasswordConfigs.GetByName returned error: %v", err) + } + + want := &MDMRecoveryPasswordConfig{ + ID: 4, + Name: "Default", + DynamicPassword: true, + RotationIntervalDays: 90, + RotateFirmwarePassword: true, + Created: Timestamp{referenceTime}, + Updated: Timestamp{referenceTime}, + } + if !cmp.Equal(got, want) { + t.Errorf("MDMRecoveryPasswordConfigs.GetByName returned %+v, want %+v", got, want) + } +} + +func TestMDMRecoveryPasswordConfigsService_Create(t *testing.T) { + client, mux, teardown := setup() + defer teardown() + + createRequest := &MDMRecoveryPasswordConfigRequest{ + Name: "Default", + DynamicPassword: false, + StaticPassword: String("12345678"), + RotationIntervalDays: 0, + RotateFirmwarePassword: false, + } + + mux.HandleFunc("/mdm/recovery_password_configs/", func(w http.ResponseWriter, r *http.Request) { + v := new(MDMRecoveryPasswordConfigRequest) + err := json.NewDecoder(r.Body).Decode(v) + if err != nil { + t.Fatal(err) + } + testMethod(t, r, "POST") + testHeader(t, r, "Accept", "application/json") + testHeader(t, r, "Content-Type", "application/json") + assert.Equal(t, createRequest, v) + + fmt.Fprint(w, mrpcCreateJSONResponse) + }) + + ctx := context.Background() + got, _, err := client.MDMRecoveryPasswordConfigs.Create(ctx, createRequest) + if err != nil { + t.Errorf("MDMRecoveryPasswordConfigs.Create returned error: %v", err) + } + + want := &MDMRecoveryPasswordConfig{ + ID: 4, + Name: "Default", + DynamicPassword: false, + StaticPassword: String("12345678"), + RotationIntervalDays: 0, + RotateFirmwarePassword: false, + Created: Timestamp{referenceTime}, + Updated: Timestamp{referenceTime}, + } + if !cmp.Equal(got, want) { + t.Errorf("MDMRecoveryPasswordConfigs.Create returned %+v, want %+v", got, want) + } +} + +func TestMDMRecoveryPasswordConfigsService_Update(t *testing.T) { + client, mux, teardown := setup() + defer teardown() + + updateRequest := &MDMRecoveryPasswordConfigRequest{ + Name: "Default", + DynamicPassword: true, + RotationIntervalDays: 90, + RotateFirmwarePassword: true, + } + + mux.HandleFunc("/mdm/recovery_password_configs/4/", func(w http.ResponseWriter, r *http.Request) { + v := new(MDMRecoveryPasswordConfigRequest) + err := json.NewDecoder(r.Body).Decode(v) + if err != nil { + t.Fatal(err) + } + testMethod(t, r, "PUT") + testHeader(t, r, "Accept", "application/json") + testHeader(t, r, "Content-Type", "application/json") + assert.Equal(t, updateRequest, v) + fmt.Fprint(w, mrpcUpdateJSONResponse) + }) + + ctx := context.Background() + got, _, err := client.MDMRecoveryPasswordConfigs.Update(ctx, 4, updateRequest) + if err != nil { + t.Errorf("MDMRecoveryPasswordConfigs.Update returned error: %v", err) + } + + want := &MDMRecoveryPasswordConfig{ + ID: 4, + Name: "Default", + DynamicPassword: true, + RotationIntervalDays: 90, + RotateFirmwarePassword: true, + Created: Timestamp{referenceTime}, + Updated: Timestamp{referenceTime}, + } + if !cmp.Equal(got, want) { + t.Errorf("MDMRecoveryPasswordConfigs.Update returned %+v, want %+v", got, want) + } +} + +func TestMDMRecoveryPasswordConfigsService_Delete(t *testing.T) { + client, mux, teardown := setup() + defer teardown() + + mux.HandleFunc("/mdm/recovery_password_configs/4/", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := context.Background() + _, err := client.MDMRecoveryPasswordConfigs.Delete(ctx, 4) + if err != nil { + t.Errorf("MDMRecoveryPasswordConfigs.Delete returned error: %v", err) + } +}