Skip to content

Commit b2874e5

Browse files
paddycarvermodular-magician
authored andcommitted
Add support for Audit Configs
1 parent 6acdbb3 commit b2874e5

40 files changed

+414
-72
lines changed

google/data_source_google_iam_policy.go

+55
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,36 @@ import (
99
"google.golang.org/api/cloudresourcemanager/v1"
1010
)
1111

12+
var auditConfig *schema.Schema = &schema.Schema{
13+
Type: schema.TypeSet,
14+
Optional: true,
15+
Elem: &schema.Resource{
16+
Schema: map[string]*schema.Schema{
17+
"service": {
18+
Type: schema.TypeString,
19+
Required: true,
20+
},
21+
"audit_log_configs": {
22+
Type: schema.TypeSet,
23+
Required: true,
24+
Elem: &schema.Resource{
25+
Schema: map[string]*schema.Schema{
26+
"log_type": {
27+
Type: schema.TypeString,
28+
Required: true,
29+
},
30+
"exempted_members": {
31+
Type: schema.TypeList,
32+
Elem: &schema.Schema{Type: schema.TypeString},
33+
Optional: true,
34+
},
35+
},
36+
},
37+
},
38+
},
39+
},
40+
}
41+
1242
var iamBinding *schema.Schema = &schema.Schema{
1343
Type: schema.TypeSet,
1444
Required: true,
@@ -49,6 +79,7 @@ func dataSourceGoogleIamPolicy() *schema.Resource {
4979
Type: schema.TypeString,
5080
Computed: true,
5181
},
82+
"audit_config": auditConfig,
5283
},
5384
}
5485
}
@@ -58,13 +89,17 @@ func dataSourceGoogleIamPolicy() *schema.Resource {
5889
func dataSourceGoogleIamPolicyRead(d *schema.ResourceData, meta interface{}) error {
5990
var policy cloudresourcemanager.Policy
6091
var bindings []*cloudresourcemanager.Binding
92+
var auditConfigs []*cloudresourcemanager.AuditConfig
6193

6294
// The schema supports multiple binding{} blocks
6395
bset := d.Get("binding").(*schema.Set)
96+
aset := d.Get("audit_config").(*schema.Set)
6497

6598
// All binding{} blocks will be converted and stored in an array
6699
bindings = make([]*cloudresourcemanager.Binding, bset.Len())
100+
auditConfigs = make([]*cloudresourcemanager.AuditConfig, aset.Len())
67101
policy.Bindings = bindings
102+
policy.AuditConfigs = auditConfigs
68103

69104
// Convert each config binding into a cloudresourcemanager.Binding
70105
for i, v := range bset.List() {
@@ -75,6 +110,26 @@ func dataSourceGoogleIamPolicyRead(d *schema.ResourceData, meta interface{}) err
75110
}
76111
}
77112

113+
// Convert each audit_config into a cloudresourcemanager.AuditConfig
114+
for i, v := range aset.List() {
115+
config := v.(map[string]interface{})
116+
// build list of audit configs first
117+
auditLogConfigSet := config["audit_log_configs"].(*schema.Set)
118+
// the array we're going to add to the outgoing resource
119+
auditLogConfigs := make([]*cloudresourcemanager.AuditLogConfig, auditLogConfigSet.Len())
120+
for x, y := range auditLogConfigSet.List() {
121+
logConfig := y.(map[string]interface{})
122+
auditLogConfigs[x] = &cloudresourcemanager.AuditLogConfig{
123+
LogType: logConfig["log_type"].(string),
124+
ExemptedMembers: convertStringArr(logConfig["exempted_members"].([]interface{})),
125+
}
126+
}
127+
policy.AuditConfigs[i] = &cloudresourcemanager.AuditConfig{
128+
Service: config["service"].(string),
129+
AuditLogConfigs: auditLogConfigs,
130+
}
131+
}
132+
78133
// Marshal cloudresourcemanager.Policy to JSON suitable for storing in state
79134
pjson, err := json.Marshal(&policy)
80135
if err != nil {

google/iam.go

+51
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,54 @@ func rolesToMembersMap(bindings []*cloudresourcemanager.Binding) map[string]map[
161161
}
162162
return bm
163163
}
164+
165+
// Merge multiple Audit Configs such that configs with the same service result in
166+
// a single exemption list with combined members
167+
func mergeAuditConfigs(auditConfigs []*cloudresourcemanager.AuditConfig) []*cloudresourcemanager.AuditConfig {
168+
am := auditConfigToServiceMap(auditConfigs)
169+
var ac []*cloudresourcemanager.AuditConfig
170+
for service, auditLogConfigs := range am {
171+
var a cloudresourcemanager.AuditConfig
172+
a.Service = service
173+
a.AuditLogConfigs = make([]*cloudresourcemanager.AuditLogConfig, 0, len(auditLogConfigs))
174+
for k, v := range auditLogConfigs {
175+
var alc cloudresourcemanager.AuditLogConfig
176+
alc.LogType = k
177+
for member := range v {
178+
alc.ExemptedMembers = append(alc.ExemptedMembers, member)
179+
}
180+
a.AuditLogConfigs = append(a.AuditLogConfigs, &alc)
181+
}
182+
if len(a.AuditLogConfigs) > 0 {
183+
ac = append(ac, &a)
184+
}
185+
}
186+
return ac
187+
}
188+
189+
// Build a service map with the log_type and bindings below it
190+
func auditConfigToServiceMap(auditConfig []*cloudresourcemanager.AuditConfig) map[string]map[string]map[string]bool {
191+
ac := make(map[string]map[string]map[string]bool)
192+
// Get each config
193+
for _, c := range auditConfig {
194+
// Initialize service map
195+
if _, ok := ac[c.Service]; !ok {
196+
ac[c.Service] = map[string]map[string]bool{}
197+
}
198+
// loop through audit log configs
199+
for _, lc := range c.AuditLogConfigs {
200+
// Initialize service map
201+
if _, ok := ac[c.Service][lc.LogType]; !ok {
202+
ac[c.Service][lc.LogType] = map[string]bool{}
203+
}
204+
// Get each member (user/principal) for the binding
205+
for _, m := range lc.ExemptedMembers {
206+
// Add the member
207+
if _, ok := ac[c.Service][lc.LogType][m]; !ok {
208+
ac[c.Service][lc.LogType][m] = true
209+
}
210+
}
211+
}
212+
}
213+
return ac
214+
}

google/resource_google_project_iam_policy.go

+77-19
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"encoding/json"
55
"fmt"
66
"log"
7+
"reflect"
78
"sort"
89

910
"github.com/hashicorp/errwrap"
@@ -88,8 +89,8 @@ func resourceGoogleProjectIamPolicyRead(d *schema.ResourceData, meta interface{}
8889
return err
8990
}
9091

91-
// we only marshal the bindings, because only the bindings get set in the config
92-
policyBytes, err := json.Marshal(&cloudresourcemanager.Policy{Bindings: policy.Bindings})
92+
// we only marshal the bindings and audit configs, because only the bindings and audit configs get set in the config
93+
policyBytes, err := json.Marshal(&cloudresourcemanager.Policy{Bindings: policy.Bindings, AuditConfigs: policy.AuditConfigs})
9394
if err != nil {
9495
return fmt.Errorf("Error marshaling IAM policy: %v", err)
9596
}
@@ -157,7 +158,7 @@ func setProjectIamPolicy(policy *cloudresourcemanager.Policy, config *Config, pi
157158
pbytes, _ := json.Marshal(policy)
158159
log.Printf("[DEBUG] Setting policy %#v for project: %s", string(pbytes), pid)
159160
_, err := config.clientResourceManager.Projects.SetIamPolicy(pid,
160-
&cloudresourcemanager.SetIamPolicyRequest{Policy: policy}).Do()
161+
&cloudresourcemanager.SetIamPolicyRequest{Policy: policy, UpdateMask: "bindings,etag,auditConfigs"}).Do()
161162

162163
if err != nil {
163164
return errwrap.Wrapf(fmt.Sprintf("Error applying IAM policy for project %q. Policy is %#v, error is {{err}}", pid, policy), err)
@@ -197,34 +198,67 @@ func jsonPolicyDiffSuppress(k, old, new string, d *schema.ResourceData) bool {
197198
log.Printf("[ERROR] Could not unmarshal new policy %s: %v", new, err)
198199
return false
199200
}
200-
oldPolicy.Bindings = mergeBindings(oldPolicy.Bindings)
201-
newPolicy.Bindings = mergeBindings(newPolicy.Bindings)
202201
if newPolicy.Etag != oldPolicy.Etag {
203202
return false
204203
}
205204
if newPolicy.Version != oldPolicy.Version {
206205
return false
207206
}
208-
if len(newPolicy.Bindings) != len(oldPolicy.Bindings) {
207+
if !compareBindings(oldPolicy.Bindings, newPolicy.Bindings) {
209208
return false
210209
}
211-
sort.Sort(sortableBindings(newPolicy.Bindings))
212-
sort.Sort(sortableBindings(oldPolicy.Bindings))
213-
for pos, newBinding := range newPolicy.Bindings {
214-
oldBinding := oldPolicy.Bindings[pos]
215-
if oldBinding.Role != newBinding.Role {
216-
return false
217-
}
218-
if len(oldBinding.Members) != len(newBinding.Members) {
210+
if !compareAuditConfigs(oldPolicy.AuditConfigs, newPolicy.AuditConfigs) {
211+
return false
212+
}
213+
return true
214+
}
215+
216+
func derefBindings(b []*cloudresourcemanager.Binding) []cloudresourcemanager.Binding {
217+
db := make([]cloudresourcemanager.Binding, len(b))
218+
219+
for i, v := range b {
220+
db[i] = *v
221+
sort.Strings(db[i].Members)
222+
}
223+
return db
224+
}
225+
226+
func compareBindings(a, b []*cloudresourcemanager.Binding) bool {
227+
a = mergeBindings(a)
228+
b = mergeBindings(b)
229+
sort.Sort(sortableBindings(a))
230+
sort.Sort(sortableBindings(b))
231+
return reflect.DeepEqual(derefBindings(a), derefBindings(b))
232+
}
233+
234+
func compareAuditConfigs(a, b []*cloudresourcemanager.AuditConfig) bool {
235+
a = mergeAuditConfigs(a)
236+
b = mergeAuditConfigs(b)
237+
sort.Sort(sortableAuditConfigs(a))
238+
sort.Sort(sortableAuditConfigs(b))
239+
if len(a) != len(b) {
240+
return false
241+
}
242+
for i, v := range a {
243+
if len(v.AuditLogConfigs) != len(b[i].AuditLogConfigs) {
219244
return false
220245
}
221-
sort.Strings(oldBinding.Members)
222-
sort.Strings(newBinding.Members)
223-
for i, newMember := range newBinding.Members {
224-
oldMember := oldBinding.Members[i]
225-
if newMember != oldMember {
246+
sort.Sort(sortableAuditLogConfigs(v.AuditLogConfigs))
247+
sort.Sort(sortableAuditLogConfigs(b[i].AuditLogConfigs))
248+
for x, logConfig := range v.AuditLogConfigs {
249+
if b[i].AuditLogConfigs[x].LogType != logConfig.LogType {
250+
return false
251+
}
252+
sort.Strings(logConfig.ExemptedMembers)
253+
sort.Strings(b[i].AuditLogConfigs[x].ExemptedMembers)
254+
if len(logConfig.ExemptedMembers) != len(b[i].AuditLogConfigs[x].ExemptedMembers) {
226255
return false
227256
}
257+
for pos, mem := range logConfig.ExemptedMembers {
258+
if b[i].AuditLogConfigs[x].ExemptedMembers[pos] != mem {
259+
return false
260+
}
261+
}
228262
}
229263
}
230264
return true
@@ -242,6 +276,30 @@ func (b sortableBindings) Less(i, j int) bool {
242276
return b[i].Role < b[j].Role
243277
}
244278

279+
type sortableAuditConfigs []*cloudresourcemanager.AuditConfig
280+
281+
func (b sortableAuditConfigs) Len() int {
282+
return len(b)
283+
}
284+
func (b sortableAuditConfigs) Swap(i, j int) {
285+
b[i], b[j] = b[j], b[i]
286+
}
287+
func (b sortableAuditConfigs) Less(i, j int) bool {
288+
return b[i].Service < b[j].Service
289+
}
290+
291+
type sortableAuditLogConfigs []*cloudresourcemanager.AuditLogConfig
292+
293+
func (b sortableAuditLogConfigs) Len() int {
294+
return len(b)
295+
}
296+
func (b sortableAuditLogConfigs) Swap(i, j int) {
297+
b[i], b[j] = b[j], b[i]
298+
}
299+
func (b sortableAuditLogConfigs) Less(i, j int) bool {
300+
return b[i].LogType < b[j].LogType
301+
}
302+
245303
func getProjectIamPolicyMutexKey(pid string) string {
246304
return fmt.Sprintf("iam-project-%s", pid)
247305
}

0 commit comments

Comments
 (0)