Skip to content

Commit f90dbea

Browse files
authored
feat: support BigQuery dataset ACLs in absence of IAM endpoints (#78)
1 parent 75df778 commit f90dbea

21 files changed

+1678
-641
lines changed

Makefile

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,12 @@ fmtcheck:
5151
fmt:
5252
gofmt -w $(GOFMT_FILES)
5353

54-
55-
.PHONY: bin default generate test vet bootstrap fmt fmtcheck
54+
update-resources:
55+
pushd $(CURDIR)/plugin/iamutil && \
56+
go build -o generate ./internal && \
57+
./generate && \
58+
rm generate && \
59+
popd
60+
61+
62+
.PHONY: bin default generate test vet bootstrap fmt fmtcheck update-resources

go.mod

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,15 @@ require (
88
github.com/hashicorp/go-gcp-common v0.5.0
99
github.com/hashicorp/go-hclog v0.12.0
1010
github.com/hashicorp/go-multierror v1.0.0
11+
github.com/hashicorp/go-version v1.2.0 // indirect
1112
github.com/hashicorp/hcl v1.0.0
1213
github.com/hashicorp/vault-plugin-auth-gcp v0.5.1
1314
github.com/hashicorp/vault/api v1.0.5-0.20200215224050-f6547fa8e820
1415
github.com/hashicorp/vault/sdk v0.1.14-0.20200215224050-f6547fa8e820
1516
github.com/mitchellh/mapstructure v1.1.2
17+
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 // indirect
18+
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b // indirect
1619
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
20+
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 // indirect
1721
google.golang.org/api v0.14.0
1822
)

go.sum

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2I
115115
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
116116
github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0=
117117
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
118+
github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E=
119+
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
118120
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
119121
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
120122
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
@@ -175,6 +177,7 @@ github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144T
175177
github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I=
176178
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
177179
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
180+
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
178181
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
179182
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
180183
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -211,6 +214,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90Pveol
211214
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
212215
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480 h1:O5YqonU5IWby+w98jVUG9h7zlCWCcH4RHyPVReBmhzk=
213216
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
217+
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
218+
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
214219
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
215220
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
216221
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -235,6 +240,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
235240
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
236241
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA=
237242
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
243+
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
244+
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
238245
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
239246
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
240247
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -260,10 +267,13 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
260267
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
261268
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e h1:nFYrTHrdrAOpShe27kaFHjsqYSEQ0KWqdWLu3xuZJts=
262269
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
270+
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
263271
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b h1:ag/x1USPSsqHud38I9BAC88qdNLDHHtQ4mlgQIZPPNA=
264272
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
265273
golang.org/x/sys v0.0.0-20191008105621-543471e840be h1:QAcqgptGM8IQBC9K/RC4o+O9YmqEm0diQn9QmZw/0mU=
266274
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
275+
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0=
276+
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
267277
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
268278
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
269279
golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db h1:6/JqlYfC1CCaLnGceQTI+sDGhC9UBSPAsBqI0Gun6kU=
@@ -326,6 +336,7 @@ gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4
326336
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
327337
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
328338
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
339+
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
329340
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
330341
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
331342
honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

plugin/backend.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ type backend struct {
3333
// cache directly.
3434
cache *cache.Cache
3535

36-
iamResources iamutil.IamResourceParser
36+
resources iamutil.ResourceParser
3737

3838
rolesetLock sync.Mutex
3939
}
@@ -49,8 +49,8 @@ func Factory(ctx context.Context, conf *logical.BackendConfig) (logical.Backend,
4949

5050
func Backend() *backend {
5151
var b = &backend{
52-
cache: cache.New(),
53-
iamResources: iamutil.GetEnabledIamResources(),
52+
cache: cache.New(),
53+
resources: iamutil.GetEnabledResources(),
5454
}
5555

5656
b.Backend = &framework.Backend{

plugin/iamutil/api_handle.go

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package iamutil
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"io"
8+
"net/http"
9+
"strings"
10+
11+
"github.com/hashicorp/errwrap"
12+
"google.golang.org/api/googleapi"
13+
)
14+
15+
type ApiHandle struct {
16+
c *http.Client
17+
userAgent string
18+
}
19+
20+
func GetApiHandle(client *http.Client, userAgent string) *ApiHandle {
21+
return &ApiHandle{
22+
c: client,
23+
userAgent: userAgent,
24+
}
25+
}
26+
27+
func (h *ApiHandle) DoGetRequest(ctx context.Context, r Resource, out interface{}) (err error) {
28+
config := r.GetConfig()
29+
req, err := constructRequest(r, &config.GetMethod, nil)
30+
if err != nil {
31+
return errwrap.Wrapf("Unable to construct Get request: {{err}}", err)
32+
}
33+
return h.doRequest(ctx, req, out)
34+
}
35+
36+
func (h *ApiHandle) DoSetRequest(ctx context.Context, r Resource, data io.Reader, out interface{}) error {
37+
config := r.GetConfig()
38+
req, err := constructRequest(r, &config.SetMethod, data)
39+
if err != nil {
40+
return errwrap.Wrapf("Unable to construct Set request: {{err}}", err)
41+
}
42+
return h.doRequest(ctx, req, out)
43+
}
44+
45+
func (h *ApiHandle) doRequest(ctx context.Context, req *http.Request, out interface{}) error {
46+
if req.Header == nil {
47+
req.Header = make(http.Header)
48+
}
49+
if h.userAgent != "" {
50+
req.Header.Set("User-Agent", h.userAgent)
51+
}
52+
53+
resp, err := h.c.Do(req.WithContext(ctx))
54+
if err != nil {
55+
return err
56+
}
57+
defer googleapi.CloseBody(resp)
58+
59+
if err := googleapi.CheckResponse(resp); err != nil {
60+
return err
61+
}
62+
63+
if err := json.NewDecoder(resp.Body).Decode(out); err != nil {
64+
return errwrap.Wrapf("unable to decode JSON resp to output interface: {{err}}", err)
65+
}
66+
return nil
67+
}
68+
69+
func constructRequest(r Resource, restMethod *RestMethod, data io.Reader) (*http.Request, error) {
70+
config := r.GetConfig()
71+
if data == nil && config != nil && config.Service == "cloudresourcemanager" {
72+
// In order to support Resource Manager policies with conditional bindings,
73+
// we need to request the policy version of 3. This request parameter is backwards compatible
74+
// and will return version 1 policies if they are not yet updated to version 3.
75+
requestPolicyVersion3 := `{"options": {"requestedPolicyVersion": 3}}`
76+
data = strings.NewReader(requestPolicyVersion3)
77+
}
78+
req, err := http.NewRequest(
79+
restMethod.HttpMethod,
80+
googleapi.ResolveRelative(restMethod.BaseURL, restMethod.Path),
81+
data)
82+
if err != nil {
83+
return nil, err
84+
}
85+
86+
if req.Header == nil {
87+
req.Header = make(http.Header)
88+
}
89+
if data != nil {
90+
req.Header.Set("Content-Type", "application/json")
91+
}
92+
93+
relId := r.GetRelativeId()
94+
replacementMap := make(map[string]string)
95+
96+
if strings.Contains(restMethod.Path, "{+resource}") {
97+
// +resource is used to represent full relative resource name
98+
if len(config.Parameters) == 1 && config.Parameters[0] == "resource" {
99+
relName := ""
100+
tkns := strings.Split(config.TypeKey, "/")
101+
for _, colId := range tkns {
102+
if colName, ok := relId.IdTuples[colId]; ok {
103+
relName += fmt.Sprintf("%s/%s/", colId, colName)
104+
}
105+
}
106+
replacementMap["resource"] = strings.Trim(relName, "/")
107+
}
108+
} else {
109+
for colId, resId := range relId.IdTuples {
110+
rId, ok := config.CollectionReplacementKeys[colId]
111+
if !ok {
112+
return nil, fmt.Errorf("expected value for collection id %s", colId)
113+
}
114+
replacementMap[rId] = resId
115+
}
116+
}
117+
118+
googleapi.Expand(req.URL, replacementMap)
119+
return req, nil
120+
}

plugin/iamutil/iam_handle_test.go renamed to plugin/iamutil/api_handle_test.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ import (
1414
"google.golang.org/api/option"
1515
)
1616

17-
func TestIamHandle_ServiceAccount(t *testing.T) {
18-
createServiceAccount := func(t *testing.T, httpC *http.Client) *parsedIamResource {
17+
func TestIamResource_ServiceAccount(t *testing.T) {
18+
createServiceAccount := func(t *testing.T, httpC *http.Client) *IamResource {
1919
iamAdmin, err := iam.NewService(context.Background(), option.WithHTTPClient(httpC))
2020
if err != nil {
2121
t.Fatal(err)
@@ -38,15 +38,15 @@ func TestIamHandle_ServiceAccount(t *testing.T) {
3838
t.Fatal(err)
3939
}
4040

41-
rConfig := generatedIamResources["projects/serviceAccounts"]["iam"]["v1"]
41+
rConfig := generatedResources["projects/serviceAccounts"]["iam"]["v1"]
4242

43-
return &parsedIamResource{
43+
return &IamResource{
4444
relativeId: relId,
4545
config: &rConfig,
4646
}
4747
}
4848

49-
deleteServiceAccount := func(t *testing.T, httpC *http.Client, r *parsedIamResource) {
49+
deleteServiceAccount := func(t *testing.T, httpC *http.Client, r *IamResource) {
5050
saName := fmt.Sprintf("projects/%s/serviceAccounts/%s",
5151
r.relativeId.IdTuples["projects"],
5252
r.relativeId.IdTuples["serviceAccounts"])
@@ -64,8 +64,8 @@ func TestIamHandle_ServiceAccount(t *testing.T) {
6464
}
6565

6666
func verifyIamResource_GetSetPolicy(t *testing.T, resourceType string,
67-
getF func(*testing.T, *http.Client) *parsedIamResource,
68-
cleanupF func(*testing.T, *http.Client, *parsedIamResource)) {
67+
getF func(*testing.T, *http.Client) *IamResource,
68+
cleanupF func(*testing.T, *http.Client, *IamResource)) {
6969

7070
_, creds := util.GetTestCredentials(t)
7171
httpC, err := gcputil.GetHttpClient(creds, iam.CloudPlatformScope)
@@ -76,9 +76,9 @@ func verifyIamResource_GetSetPolicy(t *testing.T, resourceType string,
7676
r := getF(t, httpC)
7777
defer cleanupF(t, httpC, r)
7878

79-
h := GetIamHandle(httpC, "")
79+
h := GetApiHandle(httpC, "")
8080

81-
p, err := h.GetIamPolicy(context.Background(), r)
81+
p, err := r.GetIamPolicy(context.Background(), h)
8282
if err != nil {
8383
t.Fatalf("could not get IAM Policy for resource type '%s': %v", resourceType, err)
8484
}
@@ -92,12 +92,12 @@ func verifyIamResource_GetSetPolicy(t *testing.T, resourceType string,
9292
t.Fatalf("could not get IAM Policy for resource type '%s': %v", resourceType, err)
9393
}
9494

95-
changedP, err := h.SetIamPolicy(context.Background(), r, newP)
95+
changedP, err := r.SetIamPolicy(context.Background(), h, newP)
9696
if err != nil {
9797
t.Fatalf("could not set IAM Policy for resource type '%s': %v", resourceType, err)
9898
}
9999

100-
actualP, err := h.GetIamPolicy(context.Background(), r)
100+
actualP, err := r.GetIamPolicy(context.Background(), h)
101101
if err != nil {
102102
t.Fatalf("could not get updated IAM Policy for resource type '%s': %v", resourceType, err)
103103
}

0 commit comments

Comments
 (0)