From 85c4f0a1674d77c5392877b192a4ed16da2c8d0e Mon Sep 17 00:00:00 2001 From: Andrew Reed <andrew@smallstep.com> Date: Thu, 19 Dec 2024 16:33:11 -0600 Subject: [PATCH] Add device resource --- .gitignore | 1 + internal/apiclient/v20250101/api.gen.go | 1193 ++++++++++++++++++----- internal/provider/device/model.go | 186 ++++ internal/provider/device/resource.go | 327 +++++++ internal/provider/utils/utils.go | 4 + 5 files changed, 1442 insertions(+), 269 deletions(-) create mode 100644 internal/provider/device/model.go create mode 100644 internal/provider/device/resource.go diff --git a/.gitignore b/.gitignore index 84b94cb..fb12105 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,4 @@ website/vendor api.crt api.key +v0.5.1 diff --git a/internal/apiclient/v20250101/api.gen.go b/internal/apiclient/v20250101/api.gen.go index af489ad..da38b1e 100644 --- a/internal/apiclient/v20250101/api.gen.go +++ b/internal/apiclient/v20250101/api.gen.go @@ -48,38 +48,32 @@ const ( AuthorityTypeManaged AuthorityType = "managed" ) -// Defines values for DeviceOs. +// Defines values for DeviceOS. const ( - DeviceOsIOS DeviceOs = "iOS" - DeviceOsLinux DeviceOs = "Linux" - DeviceOsMacOS DeviceOs = "macOS" - DeviceOsTvOS DeviceOs = "tvOS" - DeviceOsVisionOS DeviceOs = "visionOS" - DeviceOsWatchOS DeviceOs = "watchOS" - DeviceOsWindows DeviceOs = "Windows" + IOS DeviceOS = "iOS" + Linux DeviceOS = "Linux" + MacOS DeviceOS = "macOS" + TvOS DeviceOS = "tvOS" + VisionOS DeviceOS = "visionOS" + WatchOS DeviceOS = "watchOS" + Windows DeviceOS = "Windows" ) // Defines values for DeviceOwnership. const ( - DeviceOwnershipCompany DeviceOwnership = "company" - DeviceOwnershipUser DeviceOwnership = "user" + Company DeviceOwnership = "company" + User DeviceOwnership = "user" ) -// Defines values for DeviceRequestOs. +// Defines values for DevicePatchRemove. const ( - DeviceRequestOsIOS DeviceRequestOs = "iOS" - DeviceRequestOsLinux DeviceRequestOs = "Linux" - DeviceRequestOsMacOS DeviceRequestOs = "macOS" - DeviceRequestOsTvOS DeviceRequestOs = "tvOS" - DeviceRequestOsVisionOS DeviceRequestOs = "visionOS" - DeviceRequestOsWatchOS DeviceRequestOs = "watchOS" - DeviceRequestOsWindows DeviceRequestOs = "Windows" -) - -// Defines values for DeviceRequestOwnership. -const ( - DeviceRequestOwnershipCompany DeviceRequestOwnership = "company" - DeviceRequestOwnershipUser DeviceRequestOwnership = "user" + DisplayId DevicePatchRemove = "displayId" + DisplayName DevicePatchRemove = "displayName" + Metadata DevicePatchRemove = "metadata" + Os DevicePatchRemove = "os" + Ownership DevicePatchRemove = "ownership" + Serial DevicePatchRemove = "serial" + Tags DevicePatchRemove = "tags" ) // Defines values for NewAuthorityType. @@ -353,10 +347,14 @@ type Device struct { Connected bool `json:"connected"` // DisplayId An opaque identifier that may be used to link this device to an external inventory. - DisplayId *string `json:"displayId,omitempty"` + // This field may be populated with a value derived from data synced from your team's MDMs. + // Setting this value explicitly will mask any MDM-derived value. + DisplayId *DeviceDisplayId `json:"displayId,omitempty"` // DisplayName A friendly name for the device. - DisplayName *string `json:"displayName,omitempty"` + // This field may be populated with a value derived from data synced from your team's MDMs. + // Setting this value explicitly will mask any MDM-derived value. + DisplayName *DeviceDisplayName `json:"displayName,omitempty"` // EnrolledAt Timestamp in RFC3339 format when the device first connected to Smallstep. Read only. EnrolledAt *time.Time `json:"enrolledAt,omitempty"` @@ -371,70 +369,149 @@ type Device struct { LastSeen *time.Time `json:"lastSeen,omitempty"` // Metadata A set of key-value pairs available as template data when a provisioner with a webhook is used to issue a certificate to a device. - Metadata *[]DeviceMetadata `json:"metadata,omitempty"` + Metadata *DeviceMetadata `json:"metadata,omitempty"` // Os The device operating system. - Os *DeviceOs `json:"os,omitempty"` + // This field may be populated with a value derived from data synced from your team's MDMs. + // Setting this value explicitly will mask any MDM-derived value. + Os *DeviceOS `json:"os,omitempty"` // Ownership Whether the device is owned by the user or the company. + // This field may be populated with a value derived from data synced from your team's MDMs. + // Setting this value explicitly will mask any MDM-derived value. Ownership *DeviceOwnership `json:"ownership,omitempty"` // PermanentIdentifier The cryptographic identity of the device. High-assurance devices are only issued certificates when this identifier is attested by a trusted source. All devices with the same permanent identifier appear as a single device in the Smallstep API. For Windows and Linux devices this is the hash of the TPM endorsement key and for Apple devices it is the serial number. PermanentIdentifier string `json:"permanentIdentifier"` // Serial The serial number of the device. - Serial *string `json:"serial,omitempty"` + // This field may be populated with a value derived from data synced from your team's MDMs. + // Setting this value explicitly will mask any MDM-derived value. + Serial *DeviceSerial `json:"serial,omitempty"` // Tags A set of tags that can be used to group devices. - Tags *[]string `json:"tags,omitempty"` + Tags *DeviceTags `json:"tags,omitempty"` User *DeviceUser `json:"user,omitempty"` } -// DeviceOs The device operating system. -type DeviceOs string +// DeviceDisplayId An opaque identifier that may be used to link this device to an external inventory. +// This field may be populated with a value derived from data synced from your team's MDMs. +// Setting this value explicitly will mask any MDM-derived value. +type DeviceDisplayId = string -// DeviceOwnership Whether the device is owned by the user or the company. -type DeviceOwnership string +// DeviceDisplayName A friendly name for the device. +// This field may be populated with a value derived from data synced from your team's MDMs. +// Setting this value explicitly will mask any MDM-derived value. +type DeviceDisplayName = string -// DeviceMetadata defines model for deviceMetadata. -type DeviceMetadata struct { +// DeviceMetadata A set of key-value pairs available as template data when a provisioner with a webhook is used to issue a certificate to a device. +type DeviceMetadata = []DeviceMetadataItem + +// DeviceMetadataItem defines model for deviceMetadataItem. +type DeviceMetadataItem struct { Key string `json:"key"` Value string `json:"value"` } +// DeviceOS The device operating system. +// This field may be populated with a value derived from data synced from your team's MDMs. +// Setting this value explicitly will mask any MDM-derived value. +type DeviceOS string + +// DeviceOwnership Whether the device is owned by the user or the company. +// This field may be populated with a value derived from data synced from your team's MDMs. +// Setting this value explicitly will mask any MDM-derived value. +type DeviceOwnership string + +// DevicePatch Data that can be modified on an existing device. +type DevicePatch struct { + // DisplayId An opaque identifier that may be used to link this device to an external inventory. + // This field may be populated with a value derived from data synced from your team's MDMs. + // Setting this value explicitly will mask any MDM-derived value. + DisplayId *DeviceDisplayId `json:"displayId,omitempty"` + + // DisplayName A friendly name for the device. + // This field may be populated with a value derived from data synced from your team's MDMs. + // Setting this value explicitly will mask any MDM-derived value. + DisplayName *DeviceDisplayName `json:"displayName,omitempty"` + + // Metadata A set of key-value pairs available as template data when a provisioner with a webhook is used to issue a certificate to a device. + Metadata *DeviceMetadata `json:"metadata,omitempty"` + + // Os The device operating system. + // This field may be populated with a value derived from data synced from your team's MDMs. + // Setting this value explicitly will mask any MDM-derived value. + Os *DeviceOS `json:"os,omitempty"` + + // Ownership Whether the device is owned by the user or the company. + // This field may be populated with a value derived from data synced from your team's MDMs. + // Setting this value explicitly will mask any MDM-derived value. + Ownership *DeviceOwnership `json:"ownership,omitempty"` + + // Remove Some device fields may be populated with a value derived from data synced from your team's MDMs. + // Any data that is explicitly set by API or with the Smallstep dashboard will mask MDM-derived values. + // Use this list to specify fields that should be unset. If the device includes data synced from an MDM + // this will result in the fields being populated with values derived from the MDM data. Otherwise the + // removed fields will be empty. + Remove *[]DevicePatchRemove `json:"remove,omitempty"` + + // Serial The serial number of the device. + // This field may be populated with a value derived from data synced from your team's MDMs. + // Setting this value explicitly will mask any MDM-derived value. + Serial *DeviceSerial `json:"serial,omitempty"` + + // Tags A set of tags that can be used to group devices. + Tags *DeviceTags `json:"tags,omitempty"` +} + +// DevicePatchRemove defines model for DevicePatch.Remove. +type DevicePatchRemove string + // DeviceRequest Data that can be added to a device record. type DeviceRequest struct { // DisplayId An opaque identifier that may be used to link this device to an external inventory. - DisplayId *string `json:"displayId,omitempty"` + // This field may be populated with a value derived from data synced from your team's MDMs. + // Setting this value explicitly will mask any MDM-derived value. + DisplayId *DeviceDisplayId `json:"displayId,omitempty"` // DisplayName A friendly name for the device. - DisplayName *string `json:"displayName,omitempty"` + // This field may be populated with a value derived from data synced from your team's MDMs. + // Setting this value explicitly will mask any MDM-derived value. + DisplayName *DeviceDisplayName `json:"displayName,omitempty"` // Metadata A set of key-value pairs available as template data when a provisioner with a webhook is used to issue a certificate to a device. - Metadata *[]DeviceMetadata `json:"metadata,omitempty"` + Metadata *DeviceMetadata `json:"metadata,omitempty"` // Os The device operating system. - Os *DeviceRequestOs `json:"os,omitempty"` + // This field may be populated with a value derived from data synced from your team's MDMs. + // Setting this value explicitly will mask any MDM-derived value. + Os *DeviceOS `json:"os,omitempty"` // Ownership Whether the device is owned by the user or the company. - Ownership *DeviceRequestOwnership `json:"ownership,omitempty"` + // This field may be populated with a value derived from data synced from your team's MDMs. + // Setting this value explicitly will mask any MDM-derived value. + Ownership *DeviceOwnership `json:"ownership,omitempty"` // PermanentIdentifier The cryptographic identity of the device. High-assurance devices are only issued certificates when this identifier is attested by a trusted source. All devices with the same permanent identifier appear as a single device in the Smallstep API. For Windows and Linux devices this is the hash of the TPM endorsement key and for Apple devices it is the serial number. PermanentIdentifier string `json:"permanentIdentifier"` // Serial The serial number of the device. - Serial *string `json:"serial,omitempty"` + // This field may be populated with a value derived from data synced from your team's MDMs. + // Setting this value explicitly will mask any MDM-derived value. + Serial *DeviceSerial `json:"serial,omitempty"` // Tags A set of tags that can be used to group devices. - Tags *[]string `json:"tags,omitempty"` + Tags *DeviceTags `json:"tags,omitempty"` User *DeviceUser `json:"user,omitempty"` } -// DeviceRequestOs The device operating system. -type DeviceRequestOs string +// DeviceSerial The serial number of the device. +// This field may be populated with a value derived from data synced from your team's MDMs. +// Setting this value explicitly will mask any MDM-derived value. +type DeviceSerial = string -// DeviceRequestOwnership Whether the device is owned by the user or the company. -type DeviceRequestOwnership string +// DeviceTags A set of tags that can be used to group devices. +type DeviceTags = []string // DeviceUser defines model for deviceUser. type DeviceUser struct { @@ -442,6 +519,7 @@ type DeviceUser struct { DisplayName *string `json:"displayName,omitempty"` // Email Email of the user the device is assigned to. + // Setting this value will mask any value synced from an MDM. Email string `json:"email"` } @@ -920,6 +998,9 @@ type AuthorityDomainOrID = string // AuthorityID defines model for authorityID. type AuthorityID = string +// DeviceID defines model for deviceID. +type DeviceID = string + // Pagination defines model for pagination. type Pagination struct { // After Fetch a page of results other than the first page. @@ -1146,6 +1227,33 @@ type PostDevicesParams struct { Accept *Accept `json:"Accept,omitempty"` } +// DeleteDeviceParams defines parameters for DeleteDevice. +type DeleteDeviceParams struct { + // XRequestId A request ID provided by the client. If not provided, the server will generate one. Will be reflected in responses. + XRequestId *RequestID `json:"X-Request-Id,omitempty"` + + // Accept The content type the client is willing to accept. Also includes API version. + Accept *Accept `json:"Accept,omitempty"` +} + +// GetDeviceParams defines parameters for GetDevice. +type GetDeviceParams struct { + // XRequestId A request ID provided by the client. If not provided, the server will generate one. Will be reflected in responses. + XRequestId *RequestID `json:"X-Request-Id,omitempty"` + + // Accept The content type the client is willing to accept. Also includes API version. + Accept *Accept `json:"Accept,omitempty"` +} + +// PatchDeviceParams defines parameters for PatchDevice. +type PatchDeviceParams struct { + // XRequestId A request ID provided by the client. If not provided, the server will generate one. Will be reflected in responses. + XRequestId *RequestID `json:"X-Request-Id,omitempty"` + + // Accept The content type the client is willing to accept. Also includes API version. + Accept *Accept `json:"Accept,omitempty"` +} + // GetPlatformsParams defines parameters for GetPlatforms. type GetPlatformsParams struct { // Pagination Paginate over a list of objects. Example: `?pagination[first]=30&pagination[after]=MTIzNA==`, which after encoding would be `?pagination%5Bfirst%5D=30&pagination%5Bafter%5D=MTIzNA==` @@ -1188,6 +1296,9 @@ type PostAuthorityRootJSONRequestBody PostAuthorityRootJSONBody // PostDevicesJSONRequestBody defines body for PostDevices for application/json ContentType. type PostDevicesJSONRequestBody = DeviceRequest +// PatchDeviceJSONRequestBody defines body for PatchDevice for application/json ContentType. +type PatchDeviceJSONRequestBody = DevicePatch + // PutPlatformJSONRequestBody defines body for PutPlatform for application/json ContentType. type PutPlatformJSONRequestBody = NewPlatform @@ -1850,6 +1961,17 @@ type ClientInterface interface { PostDevices(ctx context.Context, params *PostDevicesParams, body PostDevicesJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // DeleteDevice request + DeleteDevice(ctx context.Context, deviceID DeviceID, params *DeleteDeviceParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetDevice request + GetDevice(ctx context.Context, deviceID DeviceID, params *GetDeviceParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // PatchDeviceWithBody request with any body + PatchDeviceWithBody(ctx context.Context, deviceID DeviceID, params *PatchDeviceParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + PatchDevice(ctx context.Context, deviceID DeviceID, params *PatchDeviceParams, body PatchDeviceJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // GetPlatforms request GetPlatforms(ctx context.Context, params *GetPlatformsParams, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -2147,6 +2269,54 @@ func (c *Client) PostDevices(ctx context.Context, params *PostDevicesParams, bod return c.Client.Do(req) } +func (c *Client) DeleteDevice(ctx context.Context, deviceID DeviceID, params *DeleteDeviceParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteDeviceRequest(c.Server, deviceID, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) GetDevice(ctx context.Context, deviceID DeviceID, params *GetDeviceParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetDeviceRequest(c.Server, deviceID, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) PatchDeviceWithBody(ctx context.Context, deviceID DeviceID, params *PatchDeviceParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPatchDeviceRequestWithBody(c.Server, deviceID, params, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) PatchDevice(ctx context.Context, deviceID DeviceID, params *PatchDeviceParams, body PatchDeviceJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPatchDeviceRequest(c.Server, deviceID, params, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + func (c *Client) GetPlatforms(ctx context.Context, params *GetPlatformsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewGetPlatformsRequest(c.Server, params) if err != nil { @@ -3297,6 +3467,199 @@ func NewPostDevicesRequestWithBody(server string, params *PostDevicesParams, con return req, nil } +// NewDeleteDeviceRequest generates requests for DeleteDevice +func NewDeleteDeviceRequest(server string, deviceID DeviceID, params *DeleteDeviceParams) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "deviceID", runtime.ParamLocationPath, deviceID) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/devices/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("DELETE", queryURL.String(), nil) + if err != nil { + return nil, err + } + + if params != nil { + + if params.XRequestId != nil { + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithLocation("simple", false, "X-Request-Id", runtime.ParamLocationHeader, *params.XRequestId) + if err != nil { + return nil, err + } + + req.Header.Set("X-Request-Id", headerParam0) + } + + if params.Accept != nil { + var headerParam1 string + + headerParam1, err = runtime.StyleParamWithLocation("simple", false, "Accept", runtime.ParamLocationHeader, *params.Accept) + if err != nil { + return nil, err + } + + req.Header.Set("Accept", headerParam1) + } + + } + + return req, nil +} + +// NewGetDeviceRequest generates requests for GetDevice +func NewGetDeviceRequest(server string, deviceID DeviceID, params *GetDeviceParams) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "deviceID", runtime.ParamLocationPath, deviceID) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/devices/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + if params != nil { + + if params.XRequestId != nil { + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithLocation("simple", false, "X-Request-Id", runtime.ParamLocationHeader, *params.XRequestId) + if err != nil { + return nil, err + } + + req.Header.Set("X-Request-Id", headerParam0) + } + + if params.Accept != nil { + var headerParam1 string + + headerParam1, err = runtime.StyleParamWithLocation("simple", false, "Accept", runtime.ParamLocationHeader, *params.Accept) + if err != nil { + return nil, err + } + + req.Header.Set("Accept", headerParam1) + } + + } + + return req, nil +} + +// NewPatchDeviceRequest calls the generic PatchDevice builder with application/json body +func NewPatchDeviceRequest(server string, deviceID DeviceID, params *PatchDeviceParams, body PatchDeviceJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewPatchDeviceRequestWithBody(server, deviceID, params, "application/json", bodyReader) +} + +// NewPatchDeviceRequestWithBody generates requests for PatchDevice with any type of body +func NewPatchDeviceRequestWithBody(server string, deviceID DeviceID, params *PatchDeviceParams, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "deviceID", runtime.ParamLocationPath, deviceID) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/devices/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("PATCH", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + if params != nil { + + if params.XRequestId != nil { + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithLocation("simple", false, "X-Request-Id", runtime.ParamLocationHeader, *params.XRequestId) + if err != nil { + return nil, err + } + + req.Header.Set("X-Request-Id", headerParam0) + } + + if params.Accept != nil { + var headerParam1 string + + headerParam1, err = runtime.StyleParamWithLocation("simple", false, "Accept", runtime.ParamLocationHeader, *params.Accept) + if err != nil { + return nil, err + } + + req.Header.Set("Accept", headerParam1) + } + + } + + return req, nil +} + // NewGetPlatformsRequest generates requests for GetPlatforms func NewGetPlatformsRequest(server string, params *GetPlatformsParams) (*http.Request, error) { var err error @@ -3553,6 +3916,17 @@ type ClientWithResponsesInterface interface { PostDevicesWithResponse(ctx context.Context, params *PostDevicesParams, body PostDevicesJSONRequestBody, reqEditors ...RequestEditorFn) (*PostDevicesResponse, error) + // DeleteDeviceWithResponse request + DeleteDeviceWithResponse(ctx context.Context, deviceID DeviceID, params *DeleteDeviceParams, reqEditors ...RequestEditorFn) (*DeleteDeviceResponse, error) + + // GetDeviceWithResponse request + GetDeviceWithResponse(ctx context.Context, deviceID DeviceID, params *GetDeviceParams, reqEditors ...RequestEditorFn) (*GetDeviceResponse, error) + + // PatchDeviceWithBodyWithResponse request with any body + PatchDeviceWithBodyWithResponse(ctx context.Context, deviceID DeviceID, params *PatchDeviceParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchDeviceResponse, error) + + PatchDeviceWithResponse(ctx context.Context, deviceID DeviceID, params *PatchDeviceParams, body PatchDeviceJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchDeviceResponse, error) + // GetPlatformsWithResponse request GetPlatformsWithResponse(ctx context.Context, params *GetPlatformsParams, reqEditors ...RequestEditorFn) (*GetPlatformsResponse, error) @@ -4008,6 +4382,83 @@ func (r PostDevicesResponse) StatusCode() int { return 0 } +type DeleteDeviceResponse struct { + Body []byte + HTTPResponse *http.Response + JSON400 *N400 + JSON401 *N401 + JSON404 *N401 + JSON500 *N500 +} + +// Status returns HTTPResponse.Status +func (r DeleteDeviceResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DeleteDeviceResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type GetDeviceResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *Device + JSON400 *N400 + JSON401 *N401 + JSON404 *N404 + JSON500 *N500 +} + +// Status returns HTTPResponse.Status +func (r GetDeviceResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetDeviceResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type PatchDeviceResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *Device + JSON400 *N400 + JSON401 *N401 + JSON404 *N404 + JSON500 *N500 +} + +// Status returns HTTPResponse.Status +func (r PatchDeviceResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PatchDeviceResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + type GetPlatformsResponse struct { Body []byte HTTPResponse *http.Response @@ -4267,6 +4718,41 @@ func (c *ClientWithResponses) PostDevicesWithResponse(ctx context.Context, param return ParsePostDevicesResponse(rsp) } +// DeleteDeviceWithResponse request returning *DeleteDeviceResponse +func (c *ClientWithResponses) DeleteDeviceWithResponse(ctx context.Context, deviceID DeviceID, params *DeleteDeviceParams, reqEditors ...RequestEditorFn) (*DeleteDeviceResponse, error) { + rsp, err := c.DeleteDevice(ctx, deviceID, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseDeleteDeviceResponse(rsp) +} + +// GetDeviceWithResponse request returning *GetDeviceResponse +func (c *ClientWithResponses) GetDeviceWithResponse(ctx context.Context, deviceID DeviceID, params *GetDeviceParams, reqEditors ...RequestEditorFn) (*GetDeviceResponse, error) { + rsp, err := c.GetDevice(ctx, deviceID, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetDeviceResponse(rsp) +} + +// PatchDeviceWithBodyWithResponse request with arbitrary body returning *PatchDeviceResponse +func (c *ClientWithResponses) PatchDeviceWithBodyWithResponse(ctx context.Context, deviceID DeviceID, params *PatchDeviceParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchDeviceResponse, error) { + rsp, err := c.PatchDeviceWithBody(ctx, deviceID, params, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePatchDeviceResponse(rsp) +} + +func (c *ClientWithResponses) PatchDeviceWithResponse(ctx context.Context, deviceID DeviceID, params *PatchDeviceParams, body PatchDeviceJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchDeviceResponse, error) { + rsp, err := c.PatchDevice(ctx, deviceID, params, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePatchDeviceResponse(rsp) +} + // GetPlatformsWithResponse request returning *GetPlatformsResponse func (c *ClientWithResponses) GetPlatformsWithResponse(ctx context.Context, params *GetPlatformsParams, reqEditors ...RequestEditorFn) (*GetPlatformsResponse, error) { rsp, err := c.GetPlatforms(ctx, params, reqEditors...) @@ -5227,6 +5713,161 @@ func ParsePostDevicesResponse(rsp *http.Response) (*PostDevicesResponse, error) return response, nil } +// ParseDeleteDeviceResponse parses an HTTP response from a DeleteDeviceWithResponse call +func ParseDeleteDeviceResponse(rsp *http.Response) (*DeleteDeviceResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DeleteDeviceResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest N400 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest N401 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest N401 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest N500 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseGetDeviceResponse parses an HTTP response from a GetDeviceWithResponse call +func ParseGetDeviceResponse(rsp *http.Response) (*GetDeviceResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetDeviceResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest Device + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest N400 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest N401 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest N404 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest N500 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParsePatchDeviceResponse parses an HTTP response from a PatchDeviceWithResponse call +func ParsePatchDeviceResponse(rsp *http.Response) (*PatchDeviceResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PatchDeviceResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest Device + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest N400 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest N401 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest N404 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest N500 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + // ParseGetPlatformsResponse parses an HTTP response from a GetPlatformsWithResponse call func ParseGetPlatformsResponse(rsp *http.Response) (*GetPlatformsResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) @@ -5324,228 +5965,242 @@ func ParsePutPlatformResponse(rsp *http.Response) (*PutPlatformResponse, error) // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", - "+QAA", + "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", + "AAD//5PVeIeXDgEA", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/internal/provider/device/model.go b/internal/provider/device/model.go new file mode 100644 index 0000000..9655181 --- /dev/null +++ b/internal/provider/device/model.go @@ -0,0 +1,186 @@ +package device + +import ( + "context" + "time" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + v20250101 "github.com/smallstep/terraform-provider-smallstep/internal/apiclient/v20250101" + "github.com/smallstep/terraform-provider-smallstep/internal/provider/utils" +) + +// type name for both resources and data sources +const typeName = "smallstep_device" + +type Model struct { + ID types.String `tfsdk:"id"` + PermanentIdentifier types.String `tfsdk:"permanent_identifier"` + DisplayName types.String `tfsdk:"display_name"` + DisplayID types.String `tfsdk:"display_id"` + Serial types.String `tfsdk:"serial"` + OS types.String `tfsdk:"os"` + Ownership types.String `tfsdk:"ownership"` + User types.Object `tfsdk:"user"` + Tags types.Set `tfsdk:"tags"` + Metadata types.Map `tfsdk:"metadata"` + ApprovedAt types.String `tfsdk:"approved_at"` + EnrolledAt types.String `tfsdk:"enrolled_at"` + LastSeen types.String `tfsdk:"last_seen"` + Connected types.Bool `tfsdk:"connected"` + HighAssurance types.Bool `tfsdk:"high_assurance"` +} + +type UserModel struct { + DisplayName types.String `tfsdk:"display_name"` + Email types.String `tfsdk:"email"` +} + +func (user *UserModel) AsAPI(ctx context.Context) (*v20250101.DeviceUser, diag.Diagnostics) { + d := diag.Diagnostics{} + + if user == nil { + return nil, d + } + + return &v20250101.DeviceUser{ + DisplayName: user.DisplayName.ValueStringPointer(), + Email: user.Email.ValueString(), + }, d +} + +var userAttrTypes = map[string]attr.Type{ + "display_name": types.StringType, + "email": types.StringType, +} + +func fromAPI(ctx context.Context, device *v20250101.Device, state utils.AttributeGetter) (*Model, diag.Diagnostics) { + var diags diag.Diagnostics + + model := &Model{ + ID: types.StringValue(device.Id), + PermanentIdentifier: types.StringValue(device.PermanentIdentifier), + Connected: types.BoolValue(device.Connected), + HighAssurance: types.BoolValue(device.HighAssurance), + } + + displayName, d := utils.ToOptionalString(ctx, device.DisplayName, state, path.Root("display_name")) + diags.Append(d...) + model.DisplayName = displayName + + displayID, d := utils.ToOptionalString(ctx, device.DisplayId, state, path.Root("display_id")) + diags.Append(d...) + model.DisplayID = displayID + + serial, d := utils.ToOptionalString(ctx, device.Serial, state, path.Root("serial")) + diags.Append(d...) + model.Serial = serial + + os, d := utils.ToOptionalString(ctx, device.Os, state, path.Root("os")) + diags.Append(d...) + model.OS = os + + ownership, d := utils.ToOptionalString(ctx, device.Ownership, state, path.Root("ownership")) + diags.Append(d...) + model.Ownership = ownership + + // user + if device.User != nil { + userDisplayName, d := utils.ToOptionalString(ctx, device.User.DisplayName, state, path.Root("user").AtName("display_name")) + diags.Append(d...) + + user, d := basetypes.NewObjectValue(userAttrTypes, map[string]attr.Value{ + "display_name": userDisplayName, + "email": types.StringValue(device.User.Email), + }) + diags.Append(d...) + model.User = user + } else { + model.User = basetypes.NewObjectNull(userAttrTypes) + } + + // model.Tags + tags, d := utils.ToOptionalSet(ctx, device.Tags, state, path.Root("tags")) + diags.Append(d...) + model.Tags = tags + + if device.Metadata != nil { + meta := map[string]attr.Value{} + for _, md := range *device.Metadata { + meta[md.Key] = types.StringValue(md.Value) + } + metadata, d := types.MapValue(types.StringType, meta) + diags.Append(d...) + model.Metadata = metadata + } + + if device.ApprovedAt != nil { + model.ApprovedAt = types.StringValue((*device.ApprovedAt).Format(time.RFC3339)) + } + if device.EnrolledAt != nil { + model.EnrolledAt = types.StringValue((*device.EnrolledAt).Format(time.RFC3339)) + } + if device.LastSeen != nil { + model.LastSeen = types.StringValue((*device.LastSeen).Format(time.RFC3339)) + } + + return model, diags +} + +func toAPI(ctx context.Context, m *Model) (*v20250101.DeviceRequest, diag.Diagnostics) { + // user + diags := diag.Diagnostics{} + + d := &v20250101.DeviceRequest{ + PermanentIdentifier: m.PermanentIdentifier.ValueString(), + DisplayId: m.DisplayID.ValueStringPointer(), + DisplayName: m.DisplayName.ValueStringPointer(), + Serial: m.Serial.ValueStringPointer(), + } + + if os := m.OS.ValueStringPointer(); os != nil { + d.Os = utils.Ref(v20250101.DeviceRequestOs(m.OS.ValueString())) + } + if ownership := m.Ownership.ValueStringPointer(); ownership != nil { + d.Ownership = utils.Ref(v20250101.DeviceRequestOwnership(m.Ownership.ValueString())) + } + + if !m.Metadata.IsNull() { + meta := map[string]types.String{} + diag := m.Metadata.ElementsAs(ctx, &meta, false) + diags.Append(diag...) + + var metadata []v20250101.DeviceMetadata + for k, v := range meta { + metadata = append(metadata, v20250101.DeviceMetadata{ + Key: k, + Value: v.ValueString(), + }) + } + d.Metadata = &metadata + } + + if !m.Tags.IsNull() { + var tags []string + diag := m.Tags.ElementsAs(ctx, &tags, false) + diags.Append(diag...) + d.Tags = &tags + } + + if !m.User.IsNull() { + user := &UserModel{} + diag := m.User.As(ctx, &user, basetypes.ObjectAsOptions{ + UnhandledUnknownAsEmpty: true, + }) + diags.Append(diag...) + d.User = &v20250101.DeviceUser{ + DisplayName: user.DisplayName.ValueStringPointer(), + Email: user.Email.ValueString(), + } + } + + return d, nil +} diff --git a/internal/provider/device/resource.go b/internal/provider/device/resource.go new file mode 100644 index 0000000..6f129ce --- /dev/null +++ b/internal/provider/device/resource.go @@ -0,0 +1,327 @@ +package device + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + v20250101 "github.com/smallstep/terraform-provider-smallstep/internal/apiclient/v20250101" + "github.com/smallstep/terraform-provider-smallstep/internal/provider/utils" +) + +var _ resource.ResourceWithImportState = (*Resource)(nil) + +func NewResource() resource.Resource { + return &Resource{} +} + +type Resource struct { + client *v20250101.Client +} + +func (r *Resource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + device, props, err := utils.Describe("device") + if err != nil { + resp.Diagnostics.AddError( + "Parse Smallstep OpenAPI Device Schema", + err.Error(), + ) + return + } + + deviceUser, userProps, err := utils.Describe("deviceUser") + if err != nil { + resp.Diagnostics.AddError( + "Parse Smallstep OpenAPI Device User Schema", + err.Error(), + ) + return + } + + resp.Schema = schema.Schema{ + MarkdownDescription: device, + + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + MarkdownDescription: props["id"], + Computed: true, + }, + "permanent_identifier": schema.StringAttribute{ + MarkdownDescription: props["permanent_identifier"], + Required: true, + }, + "serial": schema.StringAttribute{ + MarkdownDescription: props["permanent_identifier"], + Optional: true, + Computed: true, + }, + "display_name": schema.StringAttribute{ + MarkdownDescription: props["displayName"], + Optional: true, + Computed: true, + }, + "display_id": schema.StringAttribute{ + MarkdownDescription: props["displayId"], + Optional: true, + Computed: true, + }, + "os": schema.StringAttribute{ + MarkdownDescription: props["os"], + Optional: true, + Computed: true, + }, + "ownership": schema.StringAttribute{ + MarkdownDescription: props["ownership"], + Optional: true, + Computed: true, + }, + "metadata": schema.MapAttribute{ + MarkdownDescription: props["metadata"], + Optional: true, + Computed: true, + }, + "tags": schema.SetAttribute{ + MarkdownDescription: props["tags"], + Optional: true, + Computed: true, + }, + "user": schema.SingleNestedAttribute{ + MarkdownDescription: deviceUser, + Optional: true, + Computed: true, + Attributes: map[string]schema.Attribute{ + "display_name": schema.StringAttribute{ + MarkdownDescription: userProps["displayName"], + Optional: true, + Computed: true, + }, + "email": schema.StringAttribute{ + MarkdownDescription: userProps["email"], + Required: true, + }, + }, + }, + }, + } +} + +func (r *Resource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = typeName +} + +// Configure adds the Smallstep API client to the resource. +func (r *Resource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + // Prevent panic if the provider has not been configured. + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*v20250101.Client) + + if !ok { + resp.Diagnostics.AddError( + "Get Smallstep API client from provider", + fmt.Sprintf("Expected *v20250101.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + return + } + + r.client = client +} + +func (r *Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + state := &Model{} + + resp.Diagnostics.Append(req.State.Get(ctx, state)...) + if resp.Diagnostics.HasError() { + return + } + + httpResp, err := r.client.GetAccount(ctx, state.ID.ValueString(), &v20250101.GetAccountParams{}) + if err != nil { + resp.Diagnostics.AddError( + "Smallstep API Client Error", + fmt.Sprintf("Failed to read account %q: %v", state.ID.ValueString(), err), + ) + return + } + defer httpResp.Body.Close() + + if httpResp.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } + if httpResp.StatusCode != http.StatusOK { + reqID := httpResp.Header.Get("X-Request-Id") + resp.Diagnostics.AddError( + "Smallstep API Response Error", + fmt.Sprintf("Request %q received status %d reading account %s: %s", reqID, httpResp.StatusCode, state.ID.String(), utils.APIErrorMsg(httpResp.Body)), + ) + return + } + + account := &v20250101.Account{} + if err := json.NewDecoder(httpResp.Body).Decode(account); err != nil { + resp.Diagnostics.AddError( + "Smallstep API Client Error", + fmt.Sprintf("Failed to unmarshal account %s: %v", state.ID.String(), err), + ) + return + } + + remote, d := fromAPI(ctx, account, req.State) + resp.Diagnostics.Append(d...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &remote)...) +} + +func (a *Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + plan := &Model{} + resp.Diagnostics.Append(req.Plan.Get(ctx, plan)...) + if resp.Diagnostics.HasError() { + return + } + + reqBody, diags := toAPI(ctx, plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + httpResp, err := a.client.PostAccounts(ctx, &v20250101.PostAccountsParams{}, *reqBody) + if err != nil { + resp.Diagnostics.AddError( + "Smallstep API Client Error", + fmt.Sprintf("Failed to create account: %v", err), + ) + return + } + defer httpResp.Body.Close() + + if httpResp.StatusCode != http.StatusCreated { + reqID := httpResp.Header.Get("X-Request-Id") + resp.Diagnostics.AddError( + "Smallstep API Response Error", + fmt.Sprintf("Request %q received status %d creating account: %s", reqID, httpResp.StatusCode, utils.APIErrorMsg(httpResp.Body)), + ) + return + } + + account := &v20250101.Account{} + if err := json.NewDecoder(httpResp.Body).Decode(account); err != nil { + resp.Diagnostics.AddError( + "Smallstep API Client Error", + fmt.Sprintf("Failed to unmarshal account: %v", err), + ) + return + } + + model, diags := fromAPI(ctx, account, req.Plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + diags = resp.State.Set(ctx, model) + resp.Diagnostics.Append(diags...) +} + +func (r *Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + plan := &Model{} + diags := req.Plan.Get(ctx, plan) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + var accountID string + req.State.GetAttribute(ctx, path.Root("id"), &accountID) + + account, diags := toAPI(ctx, plan) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + account.Id = &accountID + + httpResp, err := r.client.PutAccount(ctx, accountID, &v20250101.PutAccountParams{}, *account) + if err != nil { + resp.Diagnostics.AddError( + "Smallstep API Client Error", + err.Error(), + ) + return + } + defer httpResp.Body.Close() + + if httpResp.StatusCode != http.StatusOK { + reqID := httpResp.Header.Get("X-Request-Id") + resp.Diagnostics.AddError( + "Smallstep API Response Error", + fmt.Sprintf("Request %q received status %d updating account: %s", reqID, httpResp.StatusCode, utils.APIErrorMsg(httpResp.Body)), + ) + return + } + + account = &v20250101.Account{} + if err := json.NewDecoder(httpResp.Body).Decode(account); err != nil { + resp.Diagnostics.AddError( + "Smallstep API Client Error", + fmt.Sprintf("Failed to parse account update response: %v", err), + ) + return + } + + model, diags := fromAPI(ctx, account, req.Plan) + resp.Diagnostics.Append(diags...) + + diags = resp.State.Set(ctx, model) + resp.Diagnostics.Append(diags...) +} + +func (r *Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + state := &Model{} + diags := req.State.Get(ctx, state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + accountID := state.ID.ValueString() + if accountID == "" { + resp.Diagnostics.AddError( + "Invalid Delete Account Request", + "Account ID is required", + ) + return + } + + httpResp, err := r.client.DeleteAccount(ctx, accountID, &v20250101.DeleteAccountParams{}) + if err != nil { + resp.Diagnostics.AddError( + "Smallstep API Client Error", + fmt.Sprintf("Failed to delete account: %v", err), + ) + return + } + defer httpResp.Body.Close() + + if httpResp.StatusCode != http.StatusNoContent { + reqID := httpResp.Header.Get("X-Request-Id") + resp.Diagnostics.AddError( + "Smallstep API Response Error", + fmt.Sprintf("Request %q received status %d updating account: %s", reqID, httpResp.StatusCode, utils.APIErrorMsg(httpResp.Body)), + ) + return + } +} + +func (r *Resource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} diff --git a/internal/provider/utils/utils.go b/internal/provider/utils/utils.go index 12770ae..5963fd7 100644 --- a/internal/provider/utils/utils.go +++ b/internal/provider/utils/utils.go @@ -38,6 +38,10 @@ func Deref[T dereferencable](v *T) (r T) { return } +func Ref[T any](v T) *T { + return &v +} + func ToIntPointer(i64 *int64) *int { if i64 == nil { return nil -- GitLab