1package cloudflare
2
3import (
4	"context"
5	"encoding/json"
6	"fmt"
7	"net/http"
8
9	"github.com/pkg/errors"
10)
11
12// DevicePostureRule represents a device posture rule.
13type DevicePostureRule struct {
14	ID          string                   `json:"id,omitempty"`
15	Type        string                   `json:"type"`
16	Name        string                   `json:"name"`
17	Description string                   `json:"description,omitempty"`
18	Schedule    string                   `json:"schedule,omitempty"`
19	Match       []DevicePostureRuleMatch `json:"match,omitempty"`
20	Input       DevicePostureRuleInput   `json:"input,omitempty"`
21}
22
23// DevicePostureRuleMatch represents the conditions that the client must match to run the rule.
24type DevicePostureRuleMatch struct {
25	Platform string `json:"platform,omitempty"`
26}
27
28// DevicePostureRuleInput represents the value to be checked against.
29type DevicePostureRuleInput struct {
30	ID         string `json:"id,omitempty"`
31	Path       string `json:"path,omitempty"`
32	Exists     bool   `json:"exists,omitempty"`
33	Thumbprint string `json:"thumbprint,omitempty"`
34	Sha256     string `json:"sha256,omitempty"`
35	Running    bool   `json:"running,omitempty"`
36	RequireAll bool   `json:"requireAll,omitempty"`
37	Enabled    bool   `json:"enabled,omitempty"`
38	Version    string `json:"version,omitempty"`
39	Operator   string `json:"operator,omitempty"`
40	Domain     string `json:"domain,omitempty"`
41}
42
43// DevicePostureRuleListResponse represents the response from the list
44// device posture rules endpoint.
45type DevicePostureRuleListResponse struct {
46	Result []DevicePostureRule `json:"result"`
47	Response
48	ResultInfo `json:"result_info"`
49}
50
51// DevicePostureRuleDetailResponse is the API response, containing a single
52// device posture rule.
53type DevicePostureRuleDetailResponse struct {
54	Response
55	Result DevicePostureRule `json:"result"`
56}
57
58// DevicePostureRules returns all device posture rules within an account.
59//
60// API reference: https://api.cloudflare.com/#device-posture-rules-list-device-posture-rules
61func (api *API) DevicePostureRules(ctx context.Context, accountID string) ([]DevicePostureRule, ResultInfo, error) {
62	uri := fmt.Sprintf("/%s/%s/devices/posture", AccountRouteRoot, accountID)
63
64	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
65	if err != nil {
66		return []DevicePostureRule{}, ResultInfo{}, err
67	}
68
69	var devicePostureRuleListResponse DevicePostureRuleListResponse
70	err = json.Unmarshal(res, &devicePostureRuleListResponse)
71	if err != nil {
72		return []DevicePostureRule{}, ResultInfo{}, errors.Wrap(err, errUnmarshalError)
73	}
74
75	return devicePostureRuleListResponse.Result, devicePostureRuleListResponse.ResultInfo, nil
76}
77
78// DevicePostureRule returns a single device posture rule based on the rule ID.
79//
80// API reference: https://api.cloudflare.com/#device-posture-rules-device-posture-rules-details
81func (api *API) DevicePostureRule(ctx context.Context, accountID, ruleID string) (DevicePostureRule, error) {
82	uri := fmt.Sprintf(
83		"/%s/%s/devices/posture/%s",
84		AccountRouteRoot,
85		accountID,
86		ruleID,
87	)
88
89	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
90	if err != nil {
91		return DevicePostureRule{}, err
92	}
93
94	var devicePostureRuleDetailResponse DevicePostureRuleDetailResponse
95	err = json.Unmarshal(res, &devicePostureRuleDetailResponse)
96	if err != nil {
97		return DevicePostureRule{}, errors.Wrap(err, errUnmarshalError)
98	}
99
100	return devicePostureRuleDetailResponse.Result, nil
101}
102
103// CreateDevicePostureRule creates a new device posture rule.
104//
105// API reference: https://api.cloudflare.com/#device-posture-rules-create-device-posture-rule
106func (api *API) CreateDevicePostureRule(ctx context.Context, accountID string, rule DevicePostureRule) (DevicePostureRule, error) {
107	uri := fmt.Sprintf("/%s/%s/devices/posture", AccountRouteRoot, accountID)
108
109	res, err := api.makeRequestContext(ctx, http.MethodPost, uri, rule)
110	if err != nil {
111		return DevicePostureRule{}, err
112	}
113
114	var devicePostureRuleDetailResponse DevicePostureRuleDetailResponse
115	err = json.Unmarshal(res, &devicePostureRuleDetailResponse)
116	if err != nil {
117		return DevicePostureRule{}, errors.Wrap(err, errUnmarshalError)
118	}
119
120	return devicePostureRuleDetailResponse.Result, nil
121}
122
123// UpdateDevicePostureRule updates an existing device posture rule.
124//
125// API reference: https://api.cloudflare.com/#device-posture-rules-update-device-posture-rule
126func (api *API) UpdateDevicePostureRule(ctx context.Context, accountID string, rule DevicePostureRule) (DevicePostureRule, error) {
127	if rule.ID == "" {
128		return DevicePostureRule{}, errors.Errorf("device posture rule ID cannot be empty")
129	}
130
131	uri := fmt.Sprintf(
132		"/%s/%s/devices/posture/%s",
133		AccountRouteRoot,
134		accountID,
135		rule.ID,
136	)
137
138	res, err := api.makeRequestContext(ctx, http.MethodPut, uri, rule)
139	if err != nil {
140		return DevicePostureRule{}, err
141	}
142
143	var devicePostureRuleDetailResponse DevicePostureRuleDetailResponse
144	err = json.Unmarshal(res, &devicePostureRuleDetailResponse)
145	if err != nil {
146		return DevicePostureRule{}, errors.Wrap(err, errUnmarshalError)
147	}
148
149	return devicePostureRuleDetailResponse.Result, nil
150}
151
152// DeleteDevicePostureRule deletes a device posture rule.
153//
154// API reference: https://api.cloudflare.com/#device-posture-rules-delete-device-posture-rule
155func (api *API) DeleteDevicePostureRule(ctx context.Context, accountID, ruleID string) error {
156	uri := fmt.Sprintf(
157		"/%s/%s/devices/posture/%s",
158		AccountRouteRoot,
159		accountID,
160		ruleID,
161	)
162
163	_, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil)
164	if err != nil {
165		return err
166	}
167
168	return nil
169}
170