From c57b1634dfe106593c6d28b4b3e4a57a7bf2ea93 Mon Sep 17 00:00:00 2001 From: Ryan Canty Date: Tue, 4 Feb 2020 16:25:39 -0800 Subject: [PATCH 1/5] feat: support BigQuery dataset ACLs in absence of IAM endpoints --- go.mod | 8 + go.sum | 29 + plugin/iamutil/bigquery_access.go | 71 ++ plugin/iamutil/bigquery_access_test.go | 75 ++ plugin/iamutil/iam_handle.go | 24 +- plugin/iamutil/iam_resource.go | 9 +- plugin/iamutil/iam_resource_parser_test.go | 1 + plugin/iamutil/iam_resources_generated.go | 948 ++++++++++++++------- plugin/iamutil/internal/generate_iam.go | 33 + 9 files changed, 888 insertions(+), 310 deletions(-) create mode 100644 plugin/iamutil/bigquery_access.go create mode 100644 plugin/iamutil/bigquery_access_test.go diff --git a/go.mod b/go.mod index 8b010d3c..75532d0d 100644 --- a/go.mod +++ b/go.mod @@ -3,16 +3,24 @@ module github.com/hashicorp/vault-plugin-secrets-gcp go 1.12 require ( + github.com/Bowery/prompt v0.0.0-20190916142128-fa8279994f75 // indirect + github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185 // indirect + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/hashicorp/errwrap v1.0.0 github.com/hashicorp/go-cleanhttp v0.5.1 github.com/hashicorp/go-gcp-common v0.5.0 github.com/hashicorp/go-hclog v0.12.0 github.com/hashicorp/go-multierror v1.0.0 + github.com/hashicorp/go-version v1.2.0 // indirect github.com/hashicorp/hcl v1.0.0 github.com/hashicorp/vault-plugin-auth-gcp v0.5.1 github.com/hashicorp/vault/api v1.0.5-0.20200215224050-f6547fa8e820 github.com/hashicorp/vault/sdk v0.1.14-0.20200215224050-f6547fa8e820 github.com/mitchellh/mapstructure v1.1.2 + github.com/pkg/errors v0.9.1 // indirect golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 + golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 // indirect + golang.org/x/tools v0.0.0-20200204230316-67a4523381ef // indirect google.golang.org/api v0.14.0 + gopkg.in/yaml.v2 v2.2.8 // indirect ) diff --git a/go.sum b/go.sum index 9de26e65..1cb7c8c5 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= git.apache.org/thrift.git v0.12.0/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= +github.com/Bowery/prompt v0.0.0-20190916142128-fa8279994f75 h1:xGHheKK44eC6K0u5X+DZW/fRaR1LnDdqPHMZMWx5fv8= +github.com/Bowery/prompt v0.0.0-20190916142128-fa8279994f75/go.mod h1:4/6eNcqZ09BZ9wLK3tZOjBA1nDj+B0728nlX5YRlSmQ= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= @@ -25,6 +27,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185 h1:3T8ZyTDp5QxTx3NU48JVb2u+75xc040fofcBaN+6jPA= +github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185/go.mod h1:cFRxtTwTOJkz2x3rQUNCYKWC93yP1VKjR8NUhqFxZNU= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= @@ -68,6 +72,8 @@ github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= @@ -115,6 +121,8 @@ github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2I github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0= github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -139,6 +147,8 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kardianos/govendor v1.0.9 h1:WOH3FcVI9eOgnIZYg96iwUwrL4eOVx+aQ66oyX2R8Yc= +github.com/kardianos/govendor v1.0.9/go.mod h1:yvmR6q9ZZ7nSF5Wvh40v0wfP+3TwwL8zYQp+itoZSVM= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -160,6 +170,10 @@ github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go. github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/gox v1.0.1 h1:x0jD3dcHk9a9xPSDN6YEL4xL6Qz0dvNYm8yZqui5chI= +github.com/mitchellh/gox v1.0.1/go.mod h1:ED6BioOGXMswlXa2zxfh/xdd5QhwYliBFn9V18Ap4z4= +github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= @@ -175,7 +189,10 @@ github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144T github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= @@ -220,6 +237,7 @@ golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTk golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422 h1:QzoH/1pFpZguR8NrRHLcO6jKqfv2zpuSqZLgdm7ZmjI= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -260,6 +278,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e h1:nFYrTHrdrAOpShe27kaFHjsqYSEQ0KWqdWLu3xuZJts= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b h1:ag/x1USPSsqHud38I9BAC88qdNLDHHtQ4mlgQIZPPNA= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be h1:QAcqgptGM8IQBC9K/RC4o+O9YmqEm0diQn9QmZw/0mU= @@ -284,6 +303,11 @@ golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135 h1:5Beo0mZN8dRzgrMMkDp0jc8YXQKx9DiJ2k1dkvGsn5A= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20200116225955-84cebe10344f h1:ILR8hSRYwfkRGnA5a6c/vTjIFGkLibNIFxGiosQo+LM= +golang.org/x/tools v0.0.0-20200116225955-84cebe10344f/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204230316-67a4523381ef h1:mdhEDFpO1Tfj7PXIflIuP1tbXt4rJgHIvbzdh62SARw= +golang.org/x/tools v0.0.0-20200204230316-67a4523381ef/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.0.0-20181220000619-583d854617af/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.2.0/go.mod h1:IfRCZScioGtypHNTlz3gFk67J8uePVW7uDTBzXuIkhU= google.golang.org/api v0.3.0/go.mod h1:IuvZyQh8jgscv8qWfQ4ABd8m7hEudgBFM/EdhA3BnXw= @@ -326,7 +350,12 @@ gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4 gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= +gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/plugin/iamutil/bigquery_access.go b/plugin/iamutil/bigquery_access.go new file mode 100644 index 00000000..7804b5ba --- /dev/null +++ b/plugin/iamutil/bigquery_access.go @@ -0,0 +1,71 @@ +package iamutil + +import ( + "fmt" + "strings" +) + +// NOTE: BigQuery does not conform to the typical REST for IAM policies +// instead it has an access array with bindings on the dataset +// object. https://cloud.google.com/bigquery/docs/reference/rest/v2/datasets#Dataset +type AccessBinding struct { + Role string `json:"role,omitempty"` + UserByEmail string `json:"userByEmail,omitempty"` +} + +type Dataset struct { + Access []*AccessBinding `json:"access,omitempty"` +} + +func (p *Policy) AsDataset() Dataset { + dataset := Dataset{} + if p == nil { + return dataset + } + for _, binding := range p.Bindings { + for _, member := range binding.Members { + var email string + memberSplit := strings.Split(member, ":") + if len(memberSplit) == 2 { + email = memberSplit[1] + } else { + email = member + } + + if email != "" { + dataset.Access = append(dataset.Access, &AccessBinding{ + Role: binding.Role, + UserByEmail: email, + }) + } + } + } + return dataset +} + +func (ds *Dataset) AsPolicy() Policy { + policy := Policy{} + if ds == nil { + return policy + } + bindingMap := make(map[string]*Binding) + for _, accessBinding := range ds.Access { + email := fmt.Sprintf("serviceAccount:%s", accessBinding.UserByEmail) + if binding, ok := bindingMap[accessBinding.Role]; ok { + binding.Members = append(binding.Members, email) + } else { + bindingMap[accessBinding.Role] = &Binding{ + Role: accessBinding.Role, + Members: []string{email}, + } + } + } + for k := range bindingMap { + policy.Bindings = append(policy.Bindings, bindingMap[k]) + } + return policy +} + +func (r *parsedIamResource) IsBigqueryResource() bool { + return r.config.TypeKey == "projects/datasets" && r.config.Service == "bigquery" +} diff --git a/plugin/iamutil/bigquery_access_test.go b/plugin/iamutil/bigquery_access_test.go new file mode 100644 index 00000000..99754fe6 --- /dev/null +++ b/plugin/iamutil/bigquery_access_test.go @@ -0,0 +1,75 @@ +package iamutil + +import ( + "encoding/json" + "testing" +) + +func TestPolicyToDataset(t *testing.T) { + policy, expectedDataset := getTestFixtures() + expectedDatasetBytes, err := json.Marshal(expectedDataset) + if err != nil { + t.Fatal(err) + } + actualDataset := policy.AsDataset() + actualDatasetBytes, err := json.Marshal(actualDataset) + if err != nil { + t.Fatal(err) + } + if string(actualDatasetBytes) != string(expectedDatasetBytes) { + t.Fatalf("%v should be equal to %v", string(actualDatasetBytes), string(expectedDatasetBytes)) + } +} + +func TestDatasetToPolicy(t *testing.T) { + expectedPolicy, dataset := getTestFixtures() + expectedPolicyBytes, err := json.Marshal(expectedPolicy) + if err != nil { + t.Fatal(err) + } + actualPolicy := dataset.AsPolicy() + actualPolicyBytes, err := json.Marshal(actualPolicy) + if err != nil { + t.Fatal(err) + } + if string(actualPolicyBytes) != string(expectedPolicyBytes) { + t.Fatalf("%v should be equal to %v", string(actualPolicyBytes), string(expectedPolicyBytes)) + } +} + +func getTestFixtures() (*Policy, *Dataset) { + policy := &Policy{ + Bindings: []*Binding{ + &Binding{ + Members: []string{ + "serviceAccount:foo@my-projectiam.gserviceaccount.com", + "serviceAccount:bar@my-projectiam.gserviceaccount.com", + }, + Role: "roles/bigquery.dataViewer", + }, + &Binding{ + Members: []string{ + "serviceAccount:baz@my-projectiam.gserviceaccount.com", + }, + Role: "roles/bigquery.dataOwner", + }, + }, + } + dataset := &Dataset{ + Access: []*AccessBinding{ + &AccessBinding{ + Role: "roles/bigquery.dataViewer", + UserByEmail: "foo@my-projectiam.gserviceaccount.com", + }, + &AccessBinding{ + Role: "roles/bigquery.dataViewer", + UserByEmail: "bar@my-projectiam.gserviceaccount.com", + }, + &AccessBinding{ + Role: "roles/bigquery.dataOwner", + UserByEmail: "baz@my-projectiam.gserviceaccount.com", + }, + }, + } + return policy, dataset +} diff --git a/plugin/iamutil/iam_handle.go b/plugin/iamutil/iam_handle.go index 63700bef..ee87efaf 100644 --- a/plugin/iamutil/iam_handle.go +++ b/plugin/iamutil/iam_handle.go @@ -27,8 +27,16 @@ func (h *IamHandle) GetIamPolicy(ctx context.Context, r IamResource) (*Policy, e return nil, errwrap.Wrapf("unable to construct GetIamPolicy request: {{err}}", err) } var p Policy - if err := h.doRequest(ctx, req, &p); err != nil { - return nil, errwrap.Wrapf("unable to get policy: {{err}}", err) + if r.IsBigqueryResource() { + var dataset Dataset + if err := h.doRequest(ctx, req, &dataset); err != nil { + return nil, errwrap.Wrapf("unable to get BigQuery Dataset ACL: {{err}}", err) + } + p = dataset.AsPolicy() + } else { + if err := h.doRequest(ctx, req, &p); err != nil { + return nil, errwrap.Wrapf("unable to get policy: {{err}}", err) + } } return &p, nil } @@ -39,8 +47,16 @@ func (h *IamHandle) SetIamPolicy(ctx context.Context, r IamResource, p *Policy) return nil, errwrap.Wrapf("unable to construct SetIamPolicy request: {{err}}", err) } var out Policy - if err := h.doRequest(ctx, req, &out); err != nil { - return nil, errwrap.Wrapf("unable to set policy: {{err}}", err) + if r.IsBigqueryResource() { + var dataset Dataset + if err := h.doRequest(ctx, req, &dataset); err != nil { + return nil, errwrap.Wrapf("unable to set BigQuery Dataset ACL: {{err}}", err) + } + out = dataset.AsPolicy() + } else { + if err := h.doRequest(ctx, req, &out); err != nil { + return nil, errwrap.Wrapf("unable to set policy: {{err}}", err) + } } return &out, nil } diff --git a/plugin/iamutil/iam_resource.go b/plugin/iamutil/iam_resource.go index a763408b..0d273c26 100644 --- a/plugin/iamutil/iam_resource.go +++ b/plugin/iamutil/iam_resource.go @@ -15,6 +15,7 @@ import ( type IamResource interface { GetIamPolicyRequest() (*http.Request, error) SetIamPolicyRequest(*Policy) (req *http.Request, err error) + IsBigqueryResource() bool } // parsedIamResource implements IamResource. @@ -61,11 +62,15 @@ type RestMethod struct { } func (r *parsedIamResource) SetIamPolicyRequest(p *Policy) (req *http.Request, err error) { - jsonP, err := json.Marshal(p) + var jsonP []byte + if r.IsBigqueryResource() { + jsonP, err = json.Marshal(p.AsDataset()) + } else { + jsonP, err = json.Marshal(p) + } if err != nil { return nil, err } - reqJson := fmt.Sprintf(r.config.SetMethod.RequestFormat, jsonP) if !json.Valid([]byte(reqJson)) { return nil, fmt.Errorf("request format from generated IAM config invalid JSON: %s", reqJson) diff --git a/plugin/iamutil/iam_resource_parser_test.go b/plugin/iamutil/iam_resource_parser_test.go index 88210091..fca0db9f 100644 --- a/plugin/iamutil/iam_resource_parser_test.go +++ b/plugin/iamutil/iam_resource_parser_test.go @@ -183,6 +183,7 @@ func verifyHttpMethod(typeKey string, m *RestMethod) error { case http.MethodGet: case http.MethodPost: case http.MethodPut: + case http.MethodPatch: return nil default: return fmt.Errorf("unexpected HttpMethod %s", m.HttpMethod) diff --git a/plugin/iamutil/iam_resources_generated.go b/plugin/iamutil/iam_resources_generated.go index 1d6d5e9a..059accaa 100644 --- a/plugin/iamutil/iam_resources_generated.go +++ b/plugin/iamutil/iam_resources_generated.go @@ -62,12 +62,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/storage/v1/", + BaseURL: "https://storage.googleapis.com/storage/v1/", Path: "b/{bucket}/iam", }, SetMethod: RestMethod{ HttpMethod: "PUT", - BaseURL: "https://www.googleapis.com/storage/v1/", + BaseURL: "https://storage.googleapis.com/storage/v1/", Path: "b/{bucket}/iam", RequestFormat: `%s`, }, @@ -90,12 +90,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/storage/v1/", + BaseURL: "https://storage.googleapis.com/storage/v1/", Path: "b/{bucket}/o/{object}/iam", }, SetMethod: RestMethod{ HttpMethod: "PUT", - BaseURL: "https://www.googleapis.com/storage/v1/", + BaseURL: "https://storage.googleapis.com/storage/v1/", Path: "b/{bucket}/o/{object}/iam", RequestFormat: `%s`, }, @@ -139,12 +139,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/storage/v1/", + BaseURL: "https://storage.googleapis.com/storage/v1/", Path: "b/{bucket}/iam", }, SetMethod: RestMethod{ HttpMethod: "PUT", - BaseURL: "https://www.googleapis.com/storage/v1/", + BaseURL: "https://storage.googleapis.com/storage/v1/", Path: "b/{bucket}/iam", RequestFormat: `%s`, }, @@ -167,41 +167,18 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/storage/v1/", + BaseURL: "https://storage.googleapis.com/storage/v1/", Path: "b/{bucket}/o/{object}/iam", }, SetMethod: RestMethod{ HttpMethod: "PUT", - BaseURL: "https://www.googleapis.com/storage/v1/", + BaseURL: "https://storage.googleapis.com/storage/v1/", Path: "b/{bucket}/o/{object}/iam", RequestFormat: `%s`, }, }, }, }, - "datasets": { - "genomics": { - "v1": IamRestResource{ - Name: "datasets", - TypeKey: "datasets", - Service: "genomics", - IsPreferredVersion: true, - Parameters: []string{"resource"}, - CollectionReplacementKeys: map[string]string{}, - GetMethod: RestMethod{ - HttpMethod: "POST", - BaseURL: "https://genomics.googleapis.com/", - Path: "v1/{+resource}:getIamPolicy", - }, - SetMethod: RestMethod{ - HttpMethod: "POST", - BaseURL: "https://genomics.googleapis.com/", - Path: "v1/{+resource}:setIamPolicy", - RequestFormat: `{"policy": %s}`, - }, - }, - }, - }, "folders": { "cloudresourcemanager": { "v2": IamRestResource{ @@ -286,6 +263,67 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, }, }, + "organizations/sources": { + "securitycenter": { + "v1": IamRestResource{ + Name: "sources", + TypeKey: "organizations/sources", + Service: "securitycenter", + IsPreferredVersion: true, + Parameters: []string{"resource"}, + CollectionReplacementKeys: map[string]string{}, + GetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://securitycenter.googleapis.com/", + Path: "v1/{+resource}:getIamPolicy", + }, + SetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://securitycenter.googleapis.com/", + Path: "v1/{+resource}:setIamPolicy", + RequestFormat: `{"policy": %s}`, + }, + }, + "v1beta1": IamRestResource{ + Name: "sources", + TypeKey: "organizations/sources", + Service: "securitycenter", + IsPreferredVersion: false, + Parameters: []string{"resource"}, + CollectionReplacementKeys: map[string]string{}, + GetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://securitycenter.googleapis.com/", + Path: "v1beta1/{+resource}:getIamPolicy", + }, + SetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://securitycenter.googleapis.com/", + Path: "v1beta1/{+resource}:setIamPolicy", + RequestFormat: `{"policy": %s}`, + }, + }, + "v1p1beta1": IamRestResource{ + Name: "sources", + TypeKey: "organizations/sources", + Service: "securitycenter", + IsPreferredVersion: false, + Parameters: []string{"resource"}, + CollectionReplacementKeys: map[string]string{}, + GetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://securitycenter.googleapis.com/", + Path: "v1p1beta1/{+resource}:getIamPolicy", + }, + SetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://securitycenter.googleapis.com/", + Path: "v1p1beta1/{+resource}:setIamPolicy", + RequestFormat: `{"policy": %s}`, + }, + }, + }, + }, "projects": { "cloudresourcemanager": { "v1": IamRestResource{ @@ -346,12 +384,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/alpha/projects/", + BaseURL: "https://compute.googleapis.com/compute/alpha/projects/", Path: "{project}/global/backendBuckets/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/alpha/projects/", + BaseURL: "https://compute.googleapis.com/compute/alpha/projects/", Path: "{project}/global/backendBuckets/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, @@ -381,6 +419,29 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, }, }, + "projects/datasets": { + "bigquery": { + "v2": IamRestResource{ + Name: "datasets", + TypeKey: "projects/datasets", + Service: "bigquery", + IsPreferredVersion: true, + Parameters: []string{"resource"}, + CollectionReplacementKeys: map[string]string{}, + GetMethod: RestMethod{ + HttpMethod: "GET", + BaseURL: "https://bigquery.googleapis.com", + Path: "bigquery/v2/{+resource}", + }, + SetMethod: RestMethod{ + HttpMethod: "PATCH", + BaseURL: "https://bigquery.googleapis.com", + Path: "bigquery/v2/{+resource}", + RequestFormat: `%s`, + }, + }, + }, + }, "projects/deployments": { "deploymentmanager": { "alpha": IamRestResource{ @@ -465,12 +526,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/alpha/projects/", + BaseURL: "https://compute.googleapis.com/compute/alpha/projects/", Path: "{project}/global/images/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/alpha/projects/", + BaseURL: "https://compute.googleapis.com/compute/alpha/projects/", Path: "{project}/global/images/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, @@ -487,12 +548,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/beta/projects/", + BaseURL: "https://compute.googleapis.com/compute/beta/projects/", Path: "{project}/global/images/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/beta/projects/", + BaseURL: "https://compute.googleapis.com/compute/beta/projects/", Path: "{project}/global/images/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, @@ -509,12 +570,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/v1/projects/", + BaseURL: "https://compute.googleapis.com/compute/v1/projects/", Path: "{project}/global/images/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/v1/projects/", + BaseURL: "https://compute.googleapis.com/compute/v1/projects/", Path: "{project}/global/images/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, @@ -535,12 +596,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/alpha/projects/", + BaseURL: "https://compute.googleapis.com/compute/alpha/projects/", Path: "{project}/global/instanceTemplates/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/alpha/projects/", + BaseURL: "https://compute.googleapis.com/compute/alpha/projects/", Path: "{project}/global/instanceTemplates/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, @@ -557,12 +618,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/beta/projects/", + BaseURL: "https://compute.googleapis.com/compute/beta/projects/", Path: "{project}/global/instanceTemplates/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/beta/projects/", + BaseURL: "https://compute.googleapis.com/compute/beta/projects/", Path: "{project}/global/instanceTemplates/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, @@ -579,12 +640,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/v1/projects/", + BaseURL: "https://compute.googleapis.com/compute/v1/projects/", Path: "{project}/global/instanceTemplates/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/v1/projects/", + BaseURL: "https://compute.googleapis.com/compute/v1/projects/", Path: "{project}/global/instanceTemplates/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, @@ -635,6 +696,29 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, }, }, + "projects/instances/backups": { + "spanner": { + "v1": IamRestResource{ + Name: "backups", + TypeKey: "projects/instances/backups", + Service: "spanner", + IsPreferredVersion: true, + Parameters: []string{"resource"}, + CollectionReplacementKeys: map[string]string{}, + GetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://spanner.googleapis.com/", + Path: "v1/{+resource}:getIamPolicy", + }, + SetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://spanner.googleapis.com/", + Path: "v1/{+resource}:setIamPolicy", + RequestFormat: `{"policy": %s}`, + }, + }, + }, + }, "projects/instances/databases": { "spanner": { "v1": IamRestResource{ @@ -658,6 +742,29 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, }, }, + "projects/instances/tables": { + "bigtableadmin": { + "v2": IamRestResource{ + Name: "tables", + TypeKey: "projects/instances/tables", + Service: "bigtableadmin", + IsPreferredVersion: true, + Parameters: []string{"resource"}, + CollectionReplacementKeys: map[string]string{}, + GetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://bigtableadmin.googleapis.com/", + Path: "v2/{+resource}:getIamPolicy", + }, + SetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://bigtableadmin.googleapis.com/", + Path: "v2/{+resource}:setIamPolicy", + RequestFormat: `{"policy": %s}`, + }, + }, + }, + }, "projects/interconnects": { "compute": { "alpha": IamRestResource{ @@ -672,12 +779,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/alpha/projects/", + BaseURL: "https://compute.googleapis.com/compute/alpha/projects/", Path: "{project}/global/interconnects/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/alpha/projects/", + BaseURL: "https://compute.googleapis.com/compute/alpha/projects/", Path: "{project}/global/interconnects/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, @@ -698,12 +805,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/alpha/projects/", + BaseURL: "https://compute.googleapis.com/compute/alpha/projects/", Path: "{project}/global/licenseCodes/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/alpha/projects/", + BaseURL: "https://compute.googleapis.com/compute/alpha/projects/", Path: "{project}/global/licenseCodes/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, @@ -724,12 +831,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/alpha/projects/", + BaseURL: "https://compute.googleapis.com/compute/alpha/projects/", Path: "{project}/global/licenses/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/alpha/projects/", + BaseURL: "https://compute.googleapis.com/compute/alpha/projects/", Path: "{project}/global/licenses/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, @@ -746,12 +853,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/beta/projects/", + BaseURL: "https://compute.googleapis.com/compute/beta/projects/", Path: "{project}/global/licenses/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/beta/projects/", + BaseURL: "https://compute.googleapis.com/compute/beta/projects/", Path: "{project}/global/licenses/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, @@ -768,12 +875,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/v1/projects/", + BaseURL: "https://compute.googleapis.com/compute/v1/projects/", Path: "{project}/global/licenses/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/v1/projects/", + BaseURL: "https://compute.googleapis.com/compute/v1/projects/", Path: "{project}/global/licenses/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, @@ -782,91 +889,95 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/locations/autoscalingPolicies": { "dataproc": { - "v1beta2": IamRestResource{ + "v1": IamRestResource{ Name: "autoscalingPolicies", TypeKey: "projects/locations/autoscalingPolicies", Service: "dataproc", - IsPreferredVersion: false, + IsPreferredVersion: true, Parameters: []string{"resource"}, CollectionReplacementKeys: map[string]string{}, GetMethod: RestMethod{ - HttpMethod: "GET", + HttpMethod: "POST", BaseURL: "https://dataproc.googleapis.com/", - Path: "v1beta2/{+resource}:getIamPolicy", + Path: "v1/{+resource}:getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", BaseURL: "https://dataproc.googleapis.com/", - Path: "v1beta2/{+resource}:setIamPolicy", + Path: "v1/{+resource}:setIamPolicy", RequestFormat: `{"policy": %s}`, }, }, - }, - }, - "projects/locations/datasets": { - "healthcare": { - "v1alpha": IamRestResource{ - Name: "datasets", - TypeKey: "projects/locations/datasets", - Service: "healthcare", + "v1beta2": IamRestResource{ + Name: "autoscalingPolicies", + TypeKey: "projects/locations/autoscalingPolicies", + Service: "dataproc", IsPreferredVersion: false, Parameters: []string{"resource"}, CollectionReplacementKeys: map[string]string{}, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://healthcare.googleapis.com/", - Path: "v1alpha/{+resource}:getIamPolicy", + BaseURL: "https://dataproc.googleapis.com/", + Path: "v1beta2/{+resource}:getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://healthcare.googleapis.com/", - Path: "v1alpha/{+resource}:setIamPolicy", + BaseURL: "https://dataproc.googleapis.com/", + Path: "v1beta2/{+resource}:setIamPolicy", RequestFormat: `{"policy": %s}`, }, }, - "v1alpha2": IamRestResource{ - Name: "datasets", - TypeKey: "projects/locations/datasets", - Service: "healthcare", + }, + }, + "projects/locations/connections": { + "bigqueryconnection": { + "v1beta1": IamRestResource{ + Name: "connections", + TypeKey: "projects/locations/connections", + Service: "bigqueryconnection", IsPreferredVersion: true, Parameters: []string{"resource"}, CollectionReplacementKeys: map[string]string{}, GetMethod: RestMethod{ - HttpMethod: "GET", - BaseURL: "https://healthcare.googleapis.com/", - Path: "v1alpha2/{+resource}:getIamPolicy", + HttpMethod: "POST", + BaseURL: "https://bigqueryconnection.googleapis.com/", + Path: "v1beta1/{+resource}:getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://healthcare.googleapis.com/", - Path: "v1alpha2/{+resource}:setIamPolicy", + BaseURL: "https://bigqueryconnection.googleapis.com/", + Path: "v1beta1/{+resource}:setIamPolicy", RequestFormat: `{"policy": %s}`, }, }, }, }, - "projects/locations/datasets/annotationStores": { + "projects/locations/datasets": { "healthcare": { - "v1alpha": IamRestResource{ - Name: "annotationStores", - TypeKey: "projects/locations/datasets/annotationStores", + "v1beta1": IamRestResource{ + Name: "datasets", + TypeKey: "projects/locations/datasets", Service: "healthcare", - IsPreferredVersion: false, + IsPreferredVersion: true, Parameters: []string{"resource"}, CollectionReplacementKeys: map[string]string{}, GetMethod: RestMethod{ - HttpMethod: "POST", + HttpMethod: "GET", BaseURL: "https://healthcare.googleapis.com/", - Path: "v1alpha/{+resource}:getIamPolicy", + Path: "v1beta1/{+resource}:getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", BaseURL: "https://healthcare.googleapis.com/", - Path: "v1alpha/{+resource}:setIamPolicy", + Path: "v1beta1/{+resource}:setIamPolicy", RequestFormat: `{"policy": %s}`, }, }, - "v1alpha2": IamRestResource{ + }, + }, + "projects/locations/datasets/annotationStores": { + "healthcare": { + "v1beta1": IamRestResource{ Name: "annotationStores", TypeKey: "projects/locations/datasets/annotationStores", Service: "healthcare", @@ -874,14 +985,14 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ Parameters: []string{"resource"}, CollectionReplacementKeys: map[string]string{}, GetMethod: RestMethod{ - HttpMethod: "POST", + HttpMethod: "GET", BaseURL: "https://healthcare.googleapis.com/", - Path: "v1alpha2/{+resource}:getIamPolicy", + Path: "v1beta1/{+resource}:getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", BaseURL: "https://healthcare.googleapis.com/", - Path: "v1alpha2/{+resource}:setIamPolicy", + Path: "v1beta1/{+resource}:setIamPolicy", RequestFormat: `{"policy": %s}`, }, }, @@ -889,28 +1000,32 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/locations/datasets/dicomStores": { "healthcare": { - "v1alpha": IamRestResource{ + "v1beta1": IamRestResource{ Name: "dicomStores", TypeKey: "projects/locations/datasets/dicomStores", Service: "healthcare", - IsPreferredVersion: false, + IsPreferredVersion: true, Parameters: []string{"resource"}, CollectionReplacementKeys: map[string]string{}, GetMethod: RestMethod{ HttpMethod: "GET", BaseURL: "https://healthcare.googleapis.com/", - Path: "v1alpha/{+resource}:getIamPolicy", + Path: "v1beta1/{+resource}:getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", BaseURL: "https://healthcare.googleapis.com/", - Path: "v1alpha/{+resource}:setIamPolicy", + Path: "v1beta1/{+resource}:setIamPolicy", RequestFormat: `{"policy": %s}`, }, }, - "v1alpha2": IamRestResource{ - Name: "dicomStores", - TypeKey: "projects/locations/datasets/dicomStores", + }, + }, + "projects/locations/datasets/fhirStores": { + "healthcare": { + "v1beta1": IamRestResource{ + Name: "fhirStores", + TypeKey: "projects/locations/datasets/fhirStores", Service: "healthcare", IsPreferredVersion: true, Parameters: []string{"resource"}, @@ -918,129 +1033,137 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ GetMethod: RestMethod{ HttpMethod: "GET", BaseURL: "https://healthcare.googleapis.com/", - Path: "v1alpha2/{+resource}:getIamPolicy", + Path: "v1beta1/{+resource}:getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", BaseURL: "https://healthcare.googleapis.com/", - Path: "v1alpha2/{+resource}:setIamPolicy", + Path: "v1beta1/{+resource}:setIamPolicy", RequestFormat: `{"policy": %s}`, }, }, }, }, - "projects/locations/datasets/fhirStores": { - "healthcare": { - "v1alpha": IamRestResource{ - Name: "fhirStores", - TypeKey: "projects/locations/datasets/fhirStores", - Service: "healthcare", - IsPreferredVersion: false, + "projects/locations/entryGroups": { + "datacatalog": { + "v1beta1": IamRestResource{ + Name: "entryGroups", + TypeKey: "projects/locations/entryGroups", + Service: "datacatalog", + IsPreferredVersion: true, Parameters: []string{"resource"}, CollectionReplacementKeys: map[string]string{}, GetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://healthcare.googleapis.com/", - Path: "v1alpha/{+resource}:getIamPolicy", + BaseURL: "https://datacatalog.googleapis.com/", + Path: "v1beta1/{+resource}:getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://healthcare.googleapis.com/", - Path: "v1alpha/{+resource}:setIamPolicy", + BaseURL: "https://datacatalog.googleapis.com/", + Path: "v1beta1/{+resource}:setIamPolicy", RequestFormat: `{"policy": %s}`, }, }, - "v1alpha2": IamRestResource{ - Name: "fhirStores", - TypeKey: "projects/locations/datasets/fhirStores", - Service: "healthcare", + }, + }, + "projects/locations/functions": { + "cloudfunctions": { + "v1": IamRestResource{ + Name: "functions", + TypeKey: "projects/locations/functions", + Service: "cloudfunctions", IsPreferredVersion: true, Parameters: []string{"resource"}, CollectionReplacementKeys: map[string]string{}, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://healthcare.googleapis.com/", - Path: "v1alpha2/{+resource}:getIamPolicy", + BaseURL: "https://cloudfunctions.googleapis.com/", + Path: "v1/{+resource}:getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://healthcare.googleapis.com/", - Path: "v1alpha2/{+resource}:setIamPolicy", + BaseURL: "https://cloudfunctions.googleapis.com/", + Path: "v1/{+resource}:setIamPolicy", RequestFormat: `{"policy": %s}`, }, }, }, }, - "projects/locations/datasets/fhirStores/securityLabels": { - "healthcare": { - "v1alpha": IamRestResource{ - Name: "securityLabels", - TypeKey: "projects/locations/datasets/fhirStores/securityLabels", - Service: "healthcare", - IsPreferredVersion: false, + "projects/locations/instances": { + "datafusion": { + "v1beta1": IamRestResource{ + Name: "instances", + TypeKey: "projects/locations/instances", + Service: "datafusion", + IsPreferredVersion: true, Parameters: []string{"resource"}, CollectionReplacementKeys: map[string]string{}, GetMethod: RestMethod{ - HttpMethod: "POST", - BaseURL: "https://healthcare.googleapis.com/", - Path: "v1alpha/{+resource}:getIamPolicy", + HttpMethod: "GET", + BaseURL: "https://datafusion.googleapis.com/", + Path: "v1beta1/{+resource}:getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://healthcare.googleapis.com/", - Path: "v1alpha/{+resource}:setIamPolicy", + BaseURL: "https://datafusion.googleapis.com/", + Path: "v1beta1/{+resource}:setIamPolicy", RequestFormat: `{"policy": %s}`, }, }, - "v1alpha2": IamRestResource{ - Name: "securityLabels", - TypeKey: "projects/locations/datasets/fhirStores/securityLabels", - Service: "healthcare", + }, + }, + "projects/locations/keyRings": { + "cloudkms": { + "v1": IamRestResource{ + Name: "keyRings", + TypeKey: "projects/locations/keyRings", + Service: "cloudkms", IsPreferredVersion: true, Parameters: []string{"resource"}, CollectionReplacementKeys: map[string]string{}, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://healthcare.googleapis.com/", - Path: "v1alpha2/{+resource}:getIamPolicy", + BaseURL: "https://cloudkms.googleapis.com/", + Path: "v1/{+resource}:getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://healthcare.googleapis.com/", - Path: "v1alpha2/{+resource}:setIamPolicy", + BaseURL: "https://cloudkms.googleapis.com/", + Path: "v1/{+resource}:setIamPolicy", RequestFormat: `{"policy": %s}`, }, }, }, }, - "projects/locations/functions": { - "cloudfunctions": { + "projects/locations/keyRings/cryptoKeys": { + "cloudkms": { "v1": IamRestResource{ - Name: "functions", - TypeKey: "projects/locations/functions", - Service: "cloudfunctions", + Name: "cryptoKeys", + TypeKey: "projects/locations/keyRings/cryptoKeys", + Service: "cloudkms", IsPreferredVersion: true, Parameters: []string{"resource"}, CollectionReplacementKeys: map[string]string{}, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://cloudfunctions.googleapis.com/", + BaseURL: "https://cloudkms.googleapis.com/", Path: "v1/{+resource}:getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://cloudfunctions.googleapis.com/", + BaseURL: "https://cloudkms.googleapis.com/", Path: "v1/{+resource}:setIamPolicy", RequestFormat: `{"policy": %s}`, }, }, }, }, - "projects/locations/keyRings": { + "projects/locations/keyRings/importJobs": { "cloudkms": { "v1": IamRestResource{ - Name: "keyRings", - TypeKey: "projects/locations/keyRings", + Name: "importJobs", + TypeKey: "projects/locations/keyRings/importJobs", Service: "cloudkms", IsPreferredVersion: true, Parameters: []string{"resource"}, @@ -1059,135 +1182,219 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, }, }, - "projects/locations/keyRings/cryptoKeys": { - "cloudkms": { + "projects/locations/queues": { + "cloudtasks": { + "v2": IamRestResource{ + Name: "queues", + TypeKey: "projects/locations/queues", + Service: "cloudtasks", + IsPreferredVersion: true, + Parameters: []string{"resource"}, + CollectionReplacementKeys: map[string]string{}, + GetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://cloudtasks.googleapis.com/", + Path: "v2/{+resource}:getIamPolicy", + }, + SetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://cloudtasks.googleapis.com/", + Path: "v2/{+resource}:setIamPolicy", + RequestFormat: `{"policy": %s}`, + }, + }, + "v2beta2": IamRestResource{ + Name: "queues", + TypeKey: "projects/locations/queues", + Service: "cloudtasks", + IsPreferredVersion: false, + Parameters: []string{"resource"}, + CollectionReplacementKeys: map[string]string{}, + GetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://cloudtasks.googleapis.com/", + Path: "v2beta2/{+resource}:getIamPolicy", + }, + SetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://cloudtasks.googleapis.com/", + Path: "v2beta2/{+resource}:setIamPolicy", + RequestFormat: `{"policy": %s}`, + }, + }, + "v2beta3": IamRestResource{ + Name: "queues", + TypeKey: "projects/locations/queues", + Service: "cloudtasks", + IsPreferredVersion: false, + Parameters: []string{"resource"}, + CollectionReplacementKeys: map[string]string{}, + GetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://cloudtasks.googleapis.com/", + Path: "v2beta3/{+resource}:getIamPolicy", + }, + SetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://cloudtasks.googleapis.com/", + Path: "v2beta3/{+resource}:setIamPolicy", + RequestFormat: `{"policy": %s}`, + }, + }, + }, + }, + "projects/locations/registries": { + "cloudiot": { "v1": IamRestResource{ - Name: "cryptoKeys", - TypeKey: "projects/locations/keyRings/cryptoKeys", - Service: "cloudkms", + Name: "registries", + TypeKey: "projects/locations/registries", + Service: "cloudiot", + IsPreferredVersion: true, + Parameters: []string{"resource"}, + CollectionReplacementKeys: map[string]string{}, + GetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://cloudiot.googleapis.com/", + Path: "v1/{+resource}:getIamPolicy", + }, + SetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://cloudiot.googleapis.com/", + Path: "v1/{+resource}:setIamPolicy", + RequestFormat: `{"policy": %s}`, + }, + }, + }, + }, + "projects/locations/registries/groups": { + "cloudiot": { + "v1": IamRestResource{ + Name: "groups", + TypeKey: "projects/locations/registries/groups", + Service: "cloudiot", IsPreferredVersion: true, Parameters: []string{"resource"}, CollectionReplacementKeys: map[string]string{}, GetMethod: RestMethod{ - HttpMethod: "GET", - BaseURL: "https://cloudkms.googleapis.com/", + HttpMethod: "POST", + BaseURL: "https://cloudiot.googleapis.com/", Path: "v1/{+resource}:getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://cloudkms.googleapis.com/", + BaseURL: "https://cloudiot.googleapis.com/", Path: "v1/{+resource}:setIamPolicy", RequestFormat: `{"policy": %s}`, }, }, }, }, - "projects/locations/keyRings/importJobs": { - "cloudkms": { + "projects/locations/services": { + "run": { "v1": IamRestResource{ - Name: "importJobs", - TypeKey: "projects/locations/keyRings/importJobs", - Service: "cloudkms", + Name: "services", + TypeKey: "projects/locations/services", + Service: "run", IsPreferredVersion: true, Parameters: []string{"resource"}, CollectionReplacementKeys: map[string]string{}, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://cloudkms.googleapis.com/", + BaseURL: "https://run.googleapis.com/", Path: "v1/{+resource}:getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://cloudkms.googleapis.com/", + BaseURL: "https://run.googleapis.com/", Path: "v1/{+resource}:setIamPolicy", RequestFormat: `{"policy": %s}`, }, }, - }, - }, - "projects/locations/queues": { - "cloudtasks": { - "v2beta2": IamRestResource{ - Name: "queues", - TypeKey: "projects/locations/queues", - Service: "cloudtasks", + "v1alpha1": IamRestResource{ + Name: "services", + TypeKey: "projects/locations/services", + Service: "run", IsPreferredVersion: false, Parameters: []string{"resource"}, CollectionReplacementKeys: map[string]string{}, GetMethod: RestMethod{ - HttpMethod: "POST", - BaseURL: "https://cloudtasks.googleapis.com/", - Path: "v2beta2/{+resource}:getIamPolicy", + HttpMethod: "GET", + BaseURL: "https://run.googleapis.com/", + Path: "v1alpha1/{+resource}:getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://cloudtasks.googleapis.com/", - Path: "v2beta2/{+resource}:setIamPolicy", + BaseURL: "https://run.googleapis.com/", + Path: "v1alpha1/{+resource}:setIamPolicy", RequestFormat: `{"policy": %s}`, }, }, - "v2beta3": IamRestResource{ - Name: "queues", - TypeKey: "projects/locations/queues", - Service: "cloudtasks", + }, + }, + "projects/locations/tagTemplates": { + "datacatalog": { + "v1beta1": IamRestResource{ + Name: "tagTemplates", + TypeKey: "projects/locations/tagTemplates", + Service: "datacatalog", IsPreferredVersion: true, Parameters: []string{"resource"}, CollectionReplacementKeys: map[string]string{}, GetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://cloudtasks.googleapis.com/", - Path: "v2beta3/{+resource}:getIamPolicy", + BaseURL: "https://datacatalog.googleapis.com/", + Path: "v1beta1/{+resource}:getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://cloudtasks.googleapis.com/", - Path: "v2beta3/{+resource}:setIamPolicy", + BaseURL: "https://datacatalog.googleapis.com/", + Path: "v1beta1/{+resource}:setIamPolicy", RequestFormat: `{"policy": %s}`, }, }, }, }, - "projects/locations/registries": { - "cloudiot": { - "v1": IamRestResource{ - Name: "registries", - TypeKey: "projects/locations/registries", - Service: "cloudiot", + "projects/locations/taxonomies": { + "datacatalog": { + "v1beta1": IamRestResource{ + Name: "taxonomies", + TypeKey: "projects/locations/taxonomies", + Service: "datacatalog", IsPreferredVersion: true, Parameters: []string{"resource"}, CollectionReplacementKeys: map[string]string{}, GetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://cloudiot.googleapis.com/", - Path: "v1/{+resource}:getIamPolicy", + BaseURL: "https://datacatalog.googleapis.com/", + Path: "v1beta1/{+resource}:getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://cloudiot.googleapis.com/", - Path: "v1/{+resource}:setIamPolicy", + BaseURL: "https://datacatalog.googleapis.com/", + Path: "v1beta1/{+resource}:setIamPolicy", RequestFormat: `{"policy": %s}`, }, }, }, }, - "projects/locations/registries/groups": { - "cloudiot": { - "v1": IamRestResource{ - Name: "groups", - TypeKey: "projects/locations/registries/groups", - Service: "cloudiot", + "projects/locations/taxonomies/policyTags": { + "datacatalog": { + "v1beta1": IamRestResource{ + Name: "policyTags", + TypeKey: "projects/locations/taxonomies/policyTags", + Service: "datacatalog", IsPreferredVersion: true, Parameters: []string{"resource"}, CollectionReplacementKeys: map[string]string{}, GetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://cloudiot.googleapis.com/", - Path: "v1/{+resource}:getIamPolicy", + BaseURL: "https://datacatalog.googleapis.com/", + Path: "v1beta1/{+resource}:getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://cloudiot.googleapis.com/", - Path: "v1/{+resource}:setIamPolicy", + BaseURL: "https://datacatalog.googleapis.com/", + Path: "v1beta1/{+resource}:setIamPolicy", RequestFormat: `{"policy": %s}`, }, }, @@ -1249,12 +1456,34 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/alpha/projects/", + BaseURL: "https://compute.googleapis.com/compute/alpha/projects/", + Path: "{project}/global/machineImages/{resource}/getIamPolicy", + }, + SetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://compute.googleapis.com/compute/alpha/projects/", + Path: "{project}/global/machineImages/{resource}/setIamPolicy", + RequestFormat: `{"policy": %s}`, + }, + }, + "beta": IamRestResource{ + Name: "machineImages", + TypeKey: "projects/machineImages", + Service: "compute", + IsPreferredVersion: false, + Parameters: []string{"project", "resource"}, + CollectionReplacementKeys: map[string]string{ + "machineImages": "resource", + "projects": "project", + }, + GetMethod: RestMethod{ + HttpMethod: "GET", + BaseURL: "https://compute.googleapis.com/compute/beta/projects/", Path: "{project}/global/machineImages/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/alpha/projects/", + BaseURL: "https://compute.googleapis.com/compute/beta/projects/", Path: "{project}/global/machineImages/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, @@ -1347,6 +1576,25 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/regions/autoscalingPolicies": { "dataproc": { + "v1": IamRestResource{ + Name: "autoscalingPolicies", + TypeKey: "projects/regions/autoscalingPolicies", + Service: "dataproc", + IsPreferredVersion: true, + Parameters: []string{"resource"}, + CollectionReplacementKeys: map[string]string{}, + GetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://dataproc.googleapis.com/", + Path: "v1/{+resource}:getIamPolicy", + }, + SetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://dataproc.googleapis.com/", + Path: "v1/{+resource}:setIamPolicy", + RequestFormat: `{"policy": %s}`, + }, + }, "v1beta2": IamRestResource{ Name: "autoscalingPolicies", TypeKey: "projects/regions/autoscalingPolicies", @@ -1355,7 +1603,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ Parameters: []string{"resource"}, CollectionReplacementKeys: map[string]string{}, GetMethod: RestMethod{ - HttpMethod: "POST", + HttpMethod: "GET", BaseURL: "https://dataproc.googleapis.com/", Path: "v1beta2/{+resource}:getIamPolicy", }, @@ -1425,12 +1673,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/alpha/projects/", + BaseURL: "https://compute.googleapis.com/compute/alpha/projects/", Path: "{project}/regions/{region}/disks/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/alpha/projects/", + BaseURL: "https://compute.googleapis.com/compute/alpha/projects/", Path: "{project}/regions/{region}/disks/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, @@ -1448,12 +1696,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/beta/projects/", + BaseURL: "https://compute.googleapis.com/compute/beta/projects/", Path: "{project}/regions/{region}/disks/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/beta/projects/", + BaseURL: "https://compute.googleapis.com/compute/beta/projects/", Path: "{project}/regions/{region}/disks/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, @@ -1475,12 +1723,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/alpha/projects/", + BaseURL: "https://compute.googleapis.com/compute/alpha/projects/", Path: "{project}/regions/{region}/interconnectAttachments/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/alpha/projects/", + BaseURL: "https://compute.googleapis.com/compute/alpha/projects/", Path: "{project}/regions/{region}/interconnectAttachments/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, @@ -1544,12 +1792,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/alpha/projects/", + BaseURL: "https://compute.googleapis.com/compute/alpha/projects/", Path: "{project}/regions/{region}/nodeTemplates/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/alpha/projects/", + BaseURL: "https://compute.googleapis.com/compute/alpha/projects/", Path: "{project}/regions/{region}/nodeTemplates/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, @@ -1567,12 +1815,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/beta/projects/", + BaseURL: "https://compute.googleapis.com/compute/beta/projects/", Path: "{project}/regions/{region}/nodeTemplates/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/beta/projects/", + BaseURL: "https://compute.googleapis.com/compute/beta/projects/", Path: "{project}/regions/{region}/nodeTemplates/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, @@ -1590,12 +1838,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/v1/projects/", + BaseURL: "https://compute.googleapis.com/compute/v1/projects/", Path: "{project}/regions/{region}/nodeTemplates/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/v1/projects/", + BaseURL: "https://compute.googleapis.com/compute/v1/projects/", Path: "{project}/regions/{region}/nodeTemplates/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, @@ -1659,12 +1907,58 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/alpha/projects/", + BaseURL: "https://compute.googleapis.com/compute/alpha/projects/", + Path: "{project}/regions/{region}/resourcePolicies/{resource}/getIamPolicy", + }, + SetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://compute.googleapis.com/compute/alpha/projects/", + Path: "{project}/regions/{region}/resourcePolicies/{resource}/setIamPolicy", + RequestFormat: `{"policy": %s}`, + }, + }, + "beta": IamRestResource{ + Name: "resourcePolicies", + TypeKey: "projects/regions/resourcePolicies", + Service: "compute", + IsPreferredVersion: false, + Parameters: []string{"project", "region", "resource"}, + CollectionReplacementKeys: map[string]string{ + "projects": "project", + "regions": "region", + "resourcePolicies": "resource", + }, + GetMethod: RestMethod{ + HttpMethod: "GET", + BaseURL: "https://compute.googleapis.com/compute/beta/projects/", + Path: "{project}/regions/{region}/resourcePolicies/{resource}/getIamPolicy", + }, + SetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://compute.googleapis.com/compute/beta/projects/", + Path: "{project}/regions/{region}/resourcePolicies/{resource}/setIamPolicy", + RequestFormat: `{"policy": %s}`, + }, + }, + "v1": IamRestResource{ + Name: "resourcePolicies", + TypeKey: "projects/regions/resourcePolicies", + Service: "compute", + IsPreferredVersion: true, + Parameters: []string{"project", "region", "resource"}, + CollectionReplacementKeys: map[string]string{ + "projects": "project", + "regions": "region", + "resourcePolicies": "resource", + }, + GetMethod: RestMethod{ + HttpMethod: "GET", + BaseURL: "https://compute.googleapis.com/compute/v1/projects/", Path: "{project}/regions/{region}/resourcePolicies/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/alpha/projects/", + BaseURL: "https://compute.googleapis.com/compute/v1/projects/", Path: "{project}/regions/{region}/resourcePolicies/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, @@ -1686,12 +1980,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/alpha/projects/", + BaseURL: "https://compute.googleapis.com/compute/alpha/projects/", Path: "{project}/regions/{region}/subnetworks/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/alpha/projects/", + BaseURL: "https://compute.googleapis.com/compute/alpha/projects/", Path: "{project}/regions/{region}/subnetworks/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, @@ -1709,12 +2003,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/beta/projects/", + BaseURL: "https://compute.googleapis.com/compute/beta/projects/", Path: "{project}/regions/{region}/subnetworks/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/beta/projects/", + BaseURL: "https://compute.googleapis.com/compute/beta/projects/", Path: "{project}/regions/{region}/subnetworks/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, @@ -1732,12 +2026,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/v1/projects/", + BaseURL: "https://compute.googleapis.com/compute/v1/projects/", Path: "{project}/regions/{region}/subnetworks/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/v1/projects/", + BaseURL: "https://compute.googleapis.com/compute/v1/projects/", Path: "{project}/regions/{region}/subnetworks/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, @@ -1809,6 +2103,29 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, }, }, + "projects/secrets": { + "secretmanager": { + "v1beta1": IamRestResource{ + Name: "secrets", + TypeKey: "projects/secrets", + Service: "secretmanager", + IsPreferredVersion: true, + Parameters: []string{"resource"}, + CollectionReplacementKeys: map[string]string{}, + GetMethod: RestMethod{ + HttpMethod: "GET", + BaseURL: "https://secretmanager.googleapis.com/", + Path: "v1beta1/{+resource}:getIamPolicy", + }, + SetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://secretmanager.googleapis.com/", + Path: "v1beta1/{+resource}:setIamPolicy", + RequestFormat: `{"policy": %s}`, + }, + }, + }, + }, "projects/serviceAccounts": { "iam": { "v1": IamRestResource{ @@ -1846,12 +2163,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/alpha/projects/", + BaseURL: "https://compute.googleapis.com/compute/alpha/projects/", Path: "{project}/global/snapshots/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/alpha/projects/", + BaseURL: "https://compute.googleapis.com/compute/alpha/projects/", Path: "{project}/global/snapshots/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, @@ -1868,12 +2185,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/beta/projects/", + BaseURL: "https://compute.googleapis.com/compute/beta/projects/", Path: "{project}/global/snapshots/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/beta/projects/", + BaseURL: "https://compute.googleapis.com/compute/beta/projects/", Path: "{project}/global/snapshots/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, @@ -1890,12 +2207,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/v1/projects/", + BaseURL: "https://compute.googleapis.com/compute/v1/projects/", Path: "{project}/global/snapshots/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/v1/projects/", + BaseURL: "https://compute.googleapis.com/compute/v1/projects/", Path: "{project}/global/snapshots/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, @@ -2007,56 +2324,6 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, }, }, - "projects/zones/allocations": { - "compute": { - "alpha": IamRestResource{ - Name: "allocations", - TypeKey: "projects/zones/allocations", - Service: "compute", - IsPreferredVersion: false, - Parameters: []string{"project", "zone", "resource"}, - CollectionReplacementKeys: map[string]string{ - "allocations": "resource", - "projects": "project", - "zones": "zone", - }, - GetMethod: RestMethod{ - HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/alpha/projects/", - Path: "{project}/zones/{zone}/allocations/{resource}/getIamPolicy", - }, - SetMethod: RestMethod{ - HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/alpha/projects/", - Path: "{project}/zones/{zone}/allocations/{resource}/setIamPolicy", - RequestFormat: `{"policy": %s}`, - }, - }, - "beta": IamRestResource{ - Name: "allocations", - TypeKey: "projects/zones/allocations", - Service: "compute", - IsPreferredVersion: false, - Parameters: []string{"project", "zone", "resource"}, - CollectionReplacementKeys: map[string]string{ - "allocations": "resource", - "projects": "project", - "zones": "zone", - }, - GetMethod: RestMethod{ - HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/beta/projects/", - Path: "{project}/zones/{zone}/allocations/{resource}/getIamPolicy", - }, - SetMethod: RestMethod{ - HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/beta/projects/", - Path: "{project}/zones/{zone}/allocations/{resource}/setIamPolicy", - RequestFormat: `{"policy": %s}`, - }, - }, - }, - }, "projects/zones/disks": { "compute": { "alpha": IamRestResource{ @@ -2072,12 +2339,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/alpha/projects/", + BaseURL: "https://compute.googleapis.com/compute/alpha/projects/", Path: "{project}/zones/{zone}/disks/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/alpha/projects/", + BaseURL: "https://compute.googleapis.com/compute/alpha/projects/", Path: "{project}/zones/{zone}/disks/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, @@ -2095,12 +2362,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/beta/projects/", + BaseURL: "https://compute.googleapis.com/compute/beta/projects/", Path: "{project}/zones/{zone}/disks/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/beta/projects/", + BaseURL: "https://compute.googleapis.com/compute/beta/projects/", Path: "{project}/zones/{zone}/disks/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, @@ -2118,12 +2385,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/v1/projects/", + BaseURL: "https://compute.googleapis.com/compute/v1/projects/", Path: "{project}/zones/{zone}/disks/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/v1/projects/", + BaseURL: "https://compute.googleapis.com/compute/v1/projects/", Path: "{project}/zones/{zone}/disks/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, @@ -2145,12 +2412,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/alpha/projects/", + BaseURL: "https://compute.googleapis.com/compute/alpha/projects/", Path: "{project}/zones/{zone}/instances/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/alpha/projects/", + BaseURL: "https://compute.googleapis.com/compute/alpha/projects/", Path: "{project}/zones/{zone}/instances/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, @@ -2168,12 +2435,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/beta/projects/", + BaseURL: "https://compute.googleapis.com/compute/beta/projects/", Path: "{project}/zones/{zone}/instances/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/beta/projects/", + BaseURL: "https://compute.googleapis.com/compute/beta/projects/", Path: "{project}/zones/{zone}/instances/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, @@ -2191,12 +2458,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/v1/projects/", + BaseURL: "https://compute.googleapis.com/compute/v1/projects/", Path: "{project}/zones/{zone}/instances/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/v1/projects/", + BaseURL: "https://compute.googleapis.com/compute/v1/projects/", Path: "{project}/zones/{zone}/instances/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, @@ -2218,12 +2485,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/alpha/projects/", + BaseURL: "https://compute.googleapis.com/compute/alpha/projects/", Path: "{project}/zones/{zone}/nodeGroups/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/alpha/projects/", + BaseURL: "https://compute.googleapis.com/compute/alpha/projects/", Path: "{project}/zones/{zone}/nodeGroups/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, @@ -2241,12 +2508,12 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/beta/projects/", + BaseURL: "https://compute.googleapis.com/compute/beta/projects/", Path: "{project}/zones/{zone}/nodeGroups/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/beta/projects/", + BaseURL: "https://compute.googleapis.com/compute/beta/projects/", Path: "{project}/zones/{zone}/nodeGroups/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, @@ -2264,18 +2531,91 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, GetMethod: RestMethod{ HttpMethod: "GET", - BaseURL: "https://www.googleapis.com/compute/v1/projects/", + BaseURL: "https://compute.googleapis.com/compute/v1/projects/", Path: "{project}/zones/{zone}/nodeGroups/{resource}/getIamPolicy", }, SetMethod: RestMethod{ HttpMethod: "POST", - BaseURL: "https://www.googleapis.com/compute/v1/projects/", + BaseURL: "https://compute.googleapis.com/compute/v1/projects/", Path: "{project}/zones/{zone}/nodeGroups/{resource}/setIamPolicy", RequestFormat: `{"policy": %s}`, }, }, }, }, + "projects/zones/reservations": { + "compute": { + "alpha": IamRestResource{ + Name: "reservations", + TypeKey: "projects/zones/reservations", + Service: "compute", + IsPreferredVersion: false, + Parameters: []string{"project", "zone", "resource"}, + CollectionReplacementKeys: map[string]string{ + "projects": "project", + "reservations": "resource", + "zones": "zone", + }, + GetMethod: RestMethod{ + HttpMethod: "GET", + BaseURL: "https://compute.googleapis.com/compute/alpha/projects/", + Path: "{project}/zones/{zone}/reservations/{resource}/getIamPolicy", + }, + SetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://compute.googleapis.com/compute/alpha/projects/", + Path: "{project}/zones/{zone}/reservations/{resource}/setIamPolicy", + RequestFormat: `{"policy": %s}`, + }, + }, + "beta": IamRestResource{ + Name: "reservations", + TypeKey: "projects/zones/reservations", + Service: "compute", + IsPreferredVersion: false, + Parameters: []string{"project", "zone", "resource"}, + CollectionReplacementKeys: map[string]string{ + "projects": "project", + "reservations": "resource", + "zones": "zone", + }, + GetMethod: RestMethod{ + HttpMethod: "GET", + BaseURL: "https://compute.googleapis.com/compute/beta/projects/", + Path: "{project}/zones/{zone}/reservations/{resource}/getIamPolicy", + }, + SetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://compute.googleapis.com/compute/beta/projects/", + Path: "{project}/zones/{zone}/reservations/{resource}/setIamPolicy", + RequestFormat: `{"policy": %s}`, + }, + }, + "v1": IamRestResource{ + Name: "reservations", + TypeKey: "projects/zones/reservations", + Service: "compute", + IsPreferredVersion: true, + Parameters: []string{"project", "zone", "resource"}, + CollectionReplacementKeys: map[string]string{ + "projects": "project", + "reservations": "resource", + "zones": "zone", + }, + GetMethod: RestMethod{ + HttpMethod: "GET", + BaseURL: "https://compute.googleapis.com/compute/v1/projects/", + Path: "{project}/zones/{zone}/reservations/{resource}/getIamPolicy", + }, + SetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://compute.googleapis.com/compute/v1/projects/", + Path: "{project}/zones/{zone}/reservations/{resource}/setIamPolicy", + RequestFormat: `{"policy": %s}`, + }, + }, + }, + }, "providers/notes": { "containeranalysis": { "v1alpha1": IamRestResource{ diff --git a/plugin/iamutil/internal/generate_iam.go b/plugin/iamutil/internal/generate_iam.go index 56dc390f..5c208f48 100644 --- a/plugin/iamutil/internal/generate_iam.go +++ b/plugin/iamutil/internal/generate_iam.go @@ -236,6 +236,13 @@ func generateConfig() error { return err } + // Inject BigQuery here since it doesn't have a IAM setter/getter + config["projects/datasets"] = map[string]map[string]iamutil.IamRestResource{ + "bigquery": { + "v2": bqResource(), + }, + } + if err := writeConfig(config); err != nil { return err } @@ -243,7 +250,33 @@ func generateConfig() error { return nil } +func bqResource() iamutil.IamRestResource { + return iamutil.IamRestResource{ + Name: "datasets", + TypeKey: "projects/datasets", + Service: "bigquery", + IsPreferredVersion: true, + Parameters: []string{"resource"}, + CollectionReplacementKeys: map[string]string{}, + GetMethod: iamutil.RestMethod{ + HttpMethod: "GET", + BaseURL: "https://bigquery.googleapis.com", + Path: "bigquery/v2/{+resource}", + }, + SetMethod: iamutil.RestMethod{ + HttpMethod: "PATCH", + BaseURL: "https://bigquery.googleapis.com", + // NOTE: the bigquery portion of the path needs to be in + // the version since googleapis removes it from the + // BaseURL when resolving + Path: "bigquery/v2/{+resource}", + RequestFormat: "%s", + }, + } +} + func writeConfig(config iamutil.GeneratedResources) error { + tpl, err := template.ParseFiles(fmt.Sprintf("internal/%s", templateFile)) if err != nil { return err From 997668c672d52d1af5c783b453527e283d9403a7 Mon Sep 17 00:00:00 2001 From: Ryan Canty Date: Wed, 25 Mar 2020 16:20:56 -0700 Subject: [PATCH 2/5] Refactored to make Resources not specific to IAM - Also fixed exception with locations/global in tests --- Makefile | 11 +- go.sum | 5 + plugin/backend.go | 6 +- plugin/iamutil/api_handle.go | 117 +++++ ...{iam_handle_test.go => api_handle_test.go} | 22 +- plugin/iamutil/bigquery_access.go | 71 --- plugin/iamutil/dataset_resource.go | 116 +++++ ...ccess_test.go => dataset_resource_test.go} | 8 +- plugin/iamutil/iam_handle.go | 86 ---- plugin/iamutil/iam_resource.go | 130 +----- plugin/iamutil/iam_resource_test.go | 44 +- ...{generate_iam.go => generate_resources.go} | 46 +- .../iamutil/internal/resource_config_template | 14 +- plugin/iamutil/internal/resource_overrides.go | 34 ++ plugin/iamutil/resource.go | 52 +++ ..._resource_parser.go => resource_parser.go} | 28 +- ...parser_test.go => resource_parser_test.go} | 43 +- ...es_generated.go => resources_generated.go} | 433 +++++++++++++----- plugin/path_role_set.go | 4 +- plugin/role_set.go | 14 +- plugin/rollback.go | 16 +- 21 files changed, 795 insertions(+), 505 deletions(-) create mode 100644 plugin/iamutil/api_handle.go rename plugin/iamutil/{iam_handle_test.go => api_handle_test.go} (84%) delete mode 100644 plugin/iamutil/bigquery_access.go create mode 100644 plugin/iamutil/dataset_resource.go rename plugin/iamutil/{bigquery_access_test.go => dataset_resource_test.go} (93%) delete mode 100644 plugin/iamutil/iam_handle.go rename plugin/iamutil/internal/{generate_iam.go => generate_resources.go} (84%) create mode 100644 plugin/iamutil/internal/resource_overrides.go create mode 100644 plugin/iamutil/resource.go rename plugin/iamutil/{iam_resource_parser.go => resource_parser.go} (83%) rename plugin/iamutil/{iam_resource_parser_test.go => resource_parser_test.go} (83%) rename plugin/iamutil/{iam_resources_generated.go => resources_generated.go} (90%) diff --git a/Makefile b/Makefile index b815eafd..6992166d 100644 --- a/Makefile +++ b/Makefile @@ -51,5 +51,12 @@ fmtcheck: fmt: gofmt -w $(GOFMT_FILES) - -.PHONY: bin default generate test vet bootstrap fmt fmtcheck +update-resources: + pushd $(CURDIR)/plugin/iamutil && \ + go build -o generate ./internal && \ + ./generate && \ + rm generate && \ + popd + + +.PHONY: bin default generate test vet bootstrap fmt fmtcheck update-resources diff --git a/go.sum b/go.sum index 1cb7c8c5..3f02d81d 100644 --- a/go.sum +++ b/go.sum @@ -228,6 +228,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90Pveol golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480 h1:O5YqonU5IWby+w98jVUG9h7zlCWCcH4RHyPVReBmhzk= golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -251,6 +253,7 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -283,6 +286,8 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b h1:ag/x1USPSsqHud38I9BAC88qd golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be h1:QAcqgptGM8IQBC9K/RC4o+O9YmqEm0diQn9QmZw/0mU= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db h1:6/JqlYfC1CCaLnGceQTI+sDGhC9UBSPAsBqI0Gun6kU= diff --git a/plugin/backend.go b/plugin/backend.go index e498fcfc..114f61b4 100644 --- a/plugin/backend.go +++ b/plugin/backend.go @@ -33,7 +33,7 @@ type backend struct { // cache directly. cache *cache.Cache - iamResources iamutil.IamResourceParser + resources iamutil.ResourceParser rolesetLock sync.Mutex } @@ -49,8 +49,8 @@ func Factory(ctx context.Context, conf *logical.BackendConfig) (logical.Backend, func Backend() *backend { var b = &backend{ - cache: cache.New(), - iamResources: iamutil.GetEnabledIamResources(), + cache: cache.New(), + resources: iamutil.GetEnabledResources(), } b.Backend = &framework.Backend{ diff --git a/plugin/iamutil/api_handle.go b/plugin/iamutil/api_handle.go new file mode 100644 index 00000000..748015ed --- /dev/null +++ b/plugin/iamutil/api_handle.go @@ -0,0 +1,117 @@ +package iamutil + +import ( + "context" + "encoding/json" + "fmt" + "github.com/hashicorp/errwrap" + "google.golang.org/api/googleapi" + "io" + "net/http" + "strings" +) + +type ApiHandle struct { + c *http.Client + userAgent string +} + +func GetApiHandle(client *http.Client, userAgent string) *ApiHandle { + return &ApiHandle{ + c: client, + userAgent: userAgent, + } +} + +func (h *ApiHandle) DoGetRequest(ctx context.Context, r Resource, out interface{}) (err error) { + config := r.GetConfig() + req, err := constructRequest(r, &config.GetMethod, nil) + if err != nil { + return errwrap.Wrapf("Unable to construct Get request: {{err}}", err) + } + return h.doRequest(ctx, req, out) +} + +func (h *ApiHandle) DoSetRequest(ctx context.Context, r Resource, data io.Reader, out interface{}) error { + config := r.GetConfig() + req, err := constructRequest(r, &config.SetMethod, data) + if err != nil { + return errwrap.Wrapf("Unable to construct Set request: {{err}}", err) + } + return h.doRequest(ctx, req, out) +} + +func (h *ApiHandle) doRequest(ctx context.Context, req *http.Request, out interface{}) error { + if req.Header == nil { + req.Header = make(http.Header) + } + if h.userAgent != "" { + req.Header.Set("User-Agent", h.userAgent) + } + + resp, err := h.c.Do(req.WithContext(ctx)) + if err != nil { + return err + } + defer googleapi.CloseBody(resp) + + if err := googleapi.CheckResponse(resp); err != nil { + return err + } + + if err := json.NewDecoder(resp.Body).Decode(out); err != nil { + return errwrap.Wrapf("unable to decode JSON resp to output interface: {{err}}", err) + } + return nil +} + +func constructRequest(r Resource, restMethod *RestMethod, data io.Reader) (*http.Request, error) { + config := r.GetConfig() + if data == nil && config != nil && config.Service == "cloudresourcemanager" { + // In order to support Resource Manager policies with conditional bindings, + // we need to request the policy version of 3. This request parameter is backwards compatible + // and will return version 1 policies if they are not yet updated to version 3. + requestPolicyVersion3 := `{"options": {"requestedPolicyVersion": 3}}` + data = strings.NewReader(requestPolicyVersion3) + } + req, err := http.NewRequest( + restMethod.HttpMethod, + googleapi.ResolveRelative(restMethod.BaseURL, restMethod.Path), + data) + if err != nil { + return nil, err + } + + if req.Header == nil { + req.Header = make(http.Header) + } + if data != nil { + req.Header.Set("Content-Type", "application/json") + } + + relId := r.GetRelativeId() + replacementMap := make(map[string]string) + + if strings.Contains(restMethod.Path, "{+resource}") { + // +resource is used to represent full relative resource name + if len(config.Parameters) == 1 && config.Parameters[0] == "resource" { + relName := "" + tkns := strings.Split(config.TypeKey, "/") + for _, colId := range tkns { + relName += fmt.Sprintf("%s/%s/", colId, relId.IdTuples[colId]) + } + replacementMap["resource"] = strings.Trim(relName, "/") + } + } else { + for colId, resId := range relId.IdTuples { + rId, ok := config.CollectionReplacementKeys[colId] + if !ok { + return nil, fmt.Errorf("expected value for collection id %s", colId) + } + replacementMap[rId] = resId + } + } + + googleapi.Expand(req.URL, replacementMap) + return req, nil +} diff --git a/plugin/iamutil/iam_handle_test.go b/plugin/iamutil/api_handle_test.go similarity index 84% rename from plugin/iamutil/iam_handle_test.go rename to plugin/iamutil/api_handle_test.go index a296af73..41170fce 100644 --- a/plugin/iamutil/iam_handle_test.go +++ b/plugin/iamutil/api_handle_test.go @@ -14,8 +14,8 @@ import ( "google.golang.org/api/option" ) -func TestIamHandle_ServiceAccount(t *testing.T) { - createServiceAccount := func(t *testing.T, httpC *http.Client) *parsedIamResource { +func TestIamResource_ServiceAccount(t *testing.T) { + createServiceAccount := func(t *testing.T, httpC *http.Client) *IamResource { iamAdmin, err := iam.NewService(context.Background(), option.WithHTTPClient(httpC)) if err != nil { t.Fatal(err) @@ -38,15 +38,15 @@ func TestIamHandle_ServiceAccount(t *testing.T) { t.Fatal(err) } - rConfig := generatedIamResources["projects/serviceAccounts"]["iam"]["v1"] + rConfig := generatedResources["projects/serviceAccounts"]["iam"]["v1"] - return &parsedIamResource{ + return &IamResource{ relativeId: relId, config: &rConfig, } } - deleteServiceAccount := func(t *testing.T, httpC *http.Client, r *parsedIamResource) { + deleteServiceAccount := func(t *testing.T, httpC *http.Client, r *IamResource) { saName := fmt.Sprintf("projects/%s/serviceAccounts/%s", r.relativeId.IdTuples["projects"], r.relativeId.IdTuples["serviceAccounts"]) @@ -64,8 +64,8 @@ func TestIamHandle_ServiceAccount(t *testing.T) { } func verifyIamResource_GetSetPolicy(t *testing.T, resourceType string, - getF func(*testing.T, *http.Client) *parsedIamResource, - cleanupF func(*testing.T, *http.Client, *parsedIamResource)) { + getF func(*testing.T, *http.Client) *IamResource, + cleanupF func(*testing.T, *http.Client, *IamResource)) { _, creds := util.GetTestCredentials(t) httpC, err := gcputil.GetHttpClient(creds, iam.CloudPlatformScope) @@ -76,9 +76,9 @@ func verifyIamResource_GetSetPolicy(t *testing.T, resourceType string, r := getF(t, httpC) defer cleanupF(t, httpC, r) - h := GetIamHandle(httpC, "") + h := GetApiHandle(httpC, "") - p, err := h.GetIamPolicy(context.Background(), r) + p, err := r.GetIamPolicy(context.Background(), h) if err != nil { t.Fatalf("could not get IAM Policy for resource type '%s': %v", resourceType, err) } @@ -92,12 +92,12 @@ func verifyIamResource_GetSetPolicy(t *testing.T, resourceType string, t.Fatalf("could not get IAM Policy for resource type '%s': %v", resourceType, err) } - changedP, err := h.SetIamPolicy(context.Background(), r, newP) + changedP, err := r.SetIamPolicy(context.Background(), h, newP) if err != nil { t.Fatalf("could not set IAM Policy for resource type '%s': %v", resourceType, err) } - actualP, err := h.GetIamPolicy(context.Background(), r) + actualP, err := r.GetIamPolicy(context.Background(), h) if err != nil { t.Fatalf("could not get updated IAM Policy for resource type '%s': %v", resourceType, err) } diff --git a/plugin/iamutil/bigquery_access.go b/plugin/iamutil/bigquery_access.go deleted file mode 100644 index 7804b5ba..00000000 --- a/plugin/iamutil/bigquery_access.go +++ /dev/null @@ -1,71 +0,0 @@ -package iamutil - -import ( - "fmt" - "strings" -) - -// NOTE: BigQuery does not conform to the typical REST for IAM policies -// instead it has an access array with bindings on the dataset -// object. https://cloud.google.com/bigquery/docs/reference/rest/v2/datasets#Dataset -type AccessBinding struct { - Role string `json:"role,omitempty"` - UserByEmail string `json:"userByEmail,omitempty"` -} - -type Dataset struct { - Access []*AccessBinding `json:"access,omitempty"` -} - -func (p *Policy) AsDataset() Dataset { - dataset := Dataset{} - if p == nil { - return dataset - } - for _, binding := range p.Bindings { - for _, member := range binding.Members { - var email string - memberSplit := strings.Split(member, ":") - if len(memberSplit) == 2 { - email = memberSplit[1] - } else { - email = member - } - - if email != "" { - dataset.Access = append(dataset.Access, &AccessBinding{ - Role: binding.Role, - UserByEmail: email, - }) - } - } - } - return dataset -} - -func (ds *Dataset) AsPolicy() Policy { - policy := Policy{} - if ds == nil { - return policy - } - bindingMap := make(map[string]*Binding) - for _, accessBinding := range ds.Access { - email := fmt.Sprintf("serviceAccount:%s", accessBinding.UserByEmail) - if binding, ok := bindingMap[accessBinding.Role]; ok { - binding.Members = append(binding.Members, email) - } else { - bindingMap[accessBinding.Role] = &Binding{ - Role: accessBinding.Role, - Members: []string{email}, - } - } - } - for k := range bindingMap { - policy.Bindings = append(policy.Bindings, bindingMap[k]) - } - return policy -} - -func (r *parsedIamResource) IsBigqueryResource() bool { - return r.config.TypeKey == "projects/datasets" && r.config.Service == "bigquery" -} diff --git a/plugin/iamutil/dataset_resource.go b/plugin/iamutil/dataset_resource.go new file mode 100644 index 00000000..6a630f52 --- /dev/null +++ b/plugin/iamutil/dataset_resource.go @@ -0,0 +1,116 @@ +package iamutil + +import ( + "context" + "encoding/json" + "fmt" + "github.com/hashicorp/errwrap" + "github.com/hashicorp/go-gcp-common/gcputil" + "strings" +) + +// NOTE: BigQuery does not conform to the typical REST for IAM policies +// instead it has an access array with bindings on the dataset +// object. https://cloud.google.com/bigquery/docs/reference/rest/v2/datasets#Dataset +type AccessBinding struct { + Role string `json:"role,omitempty"` + UserByEmail string `json:"userByEmail,omitempty"` +} + +type Dataset struct { + Access []*AccessBinding `json:"access,omitempty"` +} + +// NOTE: DatasetResource implements IamResource. +// This is because bigquery datasets have their own +// ACLs instead of an IAM policy +type DatasetResource struct { + relativeId *gcputil.RelativeResourceName + config *RestResource +} + +func (r *DatasetResource) GetConfig() *RestResource { + return r.config +} + +func (r *DatasetResource) GetRelativeId() *gcputil.RelativeResourceName { + return r.relativeId +} + +func (r *DatasetResource) GetIamPolicy(ctx context.Context, h *ApiHandle) (*Policy, error) { + var dataset Dataset + if err := h.DoGetRequest(ctx, r, &dataset); err != nil { + return nil, errwrap.Wrapf("unable to get BigQuery Dataset ACL: {{err}}", err) + } + p := dataset.AsPolicy() + return &p, nil +} + +func (r *DatasetResource) SetIamPolicy(ctx context.Context, h *ApiHandle, p *Policy) (*Policy, error) { + var jsonP []byte + jsonP, err := json.Marshal(p.AsDataset()) + if err != nil { + return nil, err + } + reqJson := fmt.Sprintf(r.config.SetMethod.RequestFormat, jsonP) + if !json.Valid([]byte(reqJson)) { + return nil, fmt.Errorf("request format from generated BigQuery Dataset config invalid JSON: %s", reqJson) + } + + var dataset Dataset + if err := h.DoSetRequest(ctx, r, strings.NewReader(reqJson), &dataset); err != nil { + return nil, errwrap.Wrapf("unable to set BigQuery Dataset ACL: {{err}}", err) + } + policy := dataset.AsPolicy() + + return &policy, nil +} + +func (p *Policy) AsDataset() Dataset { + ds := Dataset{} + if p == nil { + return ds + } + for _, binding := range p.Bindings { + for _, member := range binding.Members { + var email string + memberSplit := strings.Split(member, ":") + if len(memberSplit) == 2 { + email = memberSplit[1] + } else { + email = member + } + + if email != "" { + ds.Access = append(ds.Access, &AccessBinding{ + Role: binding.Role, + UserByEmail: email, + }) + } + } + } + return ds +} + +func (ds *Dataset) AsPolicy() Policy { + policy := Policy{} + if ds == nil { + return policy + } + bindingMap := make(map[string]*Binding) + for _, accessBinding := range ds.Access { + email := fmt.Sprintf("serviceAccount:%s", accessBinding.UserByEmail) + if binding, ok := bindingMap[accessBinding.Role]; ok { + binding.Members = append(binding.Members, email) + } else { + bindingMap[accessBinding.Role] = &Binding{ + Role: accessBinding.Role, + Members: []string{email}, + } + } + } + for k := range bindingMap { + policy.Bindings = append(policy.Bindings, bindingMap[k]) + } + return policy +} diff --git a/plugin/iamutil/bigquery_access_test.go b/plugin/iamutil/dataset_resource_test.go similarity index 93% rename from plugin/iamutil/bigquery_access_test.go rename to plugin/iamutil/dataset_resource_test.go index 99754fe6..2c6dedb9 100644 --- a/plugin/iamutil/bigquery_access_test.go +++ b/plugin/iamutil/dataset_resource_test.go @@ -22,12 +22,12 @@ func TestPolicyToDataset(t *testing.T) { } func TestDatasetToPolicy(t *testing.T) { - expectedPolicy, dataset := getTestFixtures() + expectedPolicy, ds := getTestFixtures() expectedPolicyBytes, err := json.Marshal(expectedPolicy) if err != nil { t.Fatal(err) } - actualPolicy := dataset.AsPolicy() + actualPolicy := ds.AsPolicy() actualPolicyBytes, err := json.Marshal(actualPolicy) if err != nil { t.Fatal(err) @@ -55,7 +55,7 @@ func getTestFixtures() (*Policy, *Dataset) { }, }, } - dataset := &Dataset{ + ds := &Dataset{ Access: []*AccessBinding{ &AccessBinding{ Role: "roles/bigquery.dataViewer", @@ -71,5 +71,5 @@ func getTestFixtures() (*Policy, *Dataset) { }, }, } - return policy, dataset + return policy, ds } diff --git a/plugin/iamutil/iam_handle.go b/plugin/iamutil/iam_handle.go deleted file mode 100644 index ee87efaf..00000000 --- a/plugin/iamutil/iam_handle.go +++ /dev/null @@ -1,86 +0,0 @@ -package iamutil - -import ( - "context" - "encoding/json" - "net/http" - - "github.com/hashicorp/errwrap" - "google.golang.org/api/googleapi" -) - -type IamHandle struct { - c *http.Client - userAgent string -} - -func GetIamHandle(client *http.Client, userAgent string) *IamHandle { - return &IamHandle{ - c: client, - userAgent: userAgent, - } -} - -func (h *IamHandle) GetIamPolicy(ctx context.Context, r IamResource) (*Policy, error) { - req, err := r.GetIamPolicyRequest() - if err != nil { - return nil, errwrap.Wrapf("unable to construct GetIamPolicy request: {{err}}", err) - } - var p Policy - if r.IsBigqueryResource() { - var dataset Dataset - if err := h.doRequest(ctx, req, &dataset); err != nil { - return nil, errwrap.Wrapf("unable to get BigQuery Dataset ACL: {{err}}", err) - } - p = dataset.AsPolicy() - } else { - if err := h.doRequest(ctx, req, &p); err != nil { - return nil, errwrap.Wrapf("unable to get policy: {{err}}", err) - } - } - return &p, nil -} - -func (h *IamHandle) SetIamPolicy(ctx context.Context, r IamResource, p *Policy) (*Policy, error) { - req, err := r.SetIamPolicyRequest(p) - if err != nil { - return nil, errwrap.Wrapf("unable to construct SetIamPolicy request: {{err}}", err) - } - var out Policy - if r.IsBigqueryResource() { - var dataset Dataset - if err := h.doRequest(ctx, req, &dataset); err != nil { - return nil, errwrap.Wrapf("unable to set BigQuery Dataset ACL: {{err}}", err) - } - out = dataset.AsPolicy() - } else { - if err := h.doRequest(ctx, req, &out); err != nil { - return nil, errwrap.Wrapf("unable to set policy: {{err}}", err) - } - } - return &out, nil -} - -func (h *IamHandle) doRequest(ctx context.Context, req *http.Request, out interface{}) error { - if req.Header == nil { - req.Header = make(http.Header) - } - if h.userAgent != "" { - req.Header.Set("User-Agent", h.userAgent) - } - - resp, err := h.c.Do(req.WithContext(ctx)) - if err != nil { - return err - } - defer googleapi.CloseBody(resp) - - if err := googleapi.CheckResponse(resp); err != nil { - return err - } - - if err := json.NewDecoder(resp.Body).Decode(out); err != nil { - return errwrap.Wrapf("unable to decode JSON resp to output interface: {{err}}", err) - } - return nil -} diff --git a/plugin/iamutil/iam_resource.go b/plugin/iamutil/iam_resource.go index 0d273c26..811245d5 100644 --- a/plugin/iamutil/iam_resource.go +++ b/plugin/iamutil/iam_resource.go @@ -1,73 +1,38 @@ package iamutil import ( + "context" "encoding/json" "fmt" + "github.com/hashicorp/errwrap" "github.com/hashicorp/go-gcp-common/gcputil" - "google.golang.org/api/googleapi" - "io" - "net/http" "strings" ) -// IamResource handles constructing HTTP requests for getting and -// setting IAM policies. -type IamResource interface { - GetIamPolicyRequest() (*http.Request, error) - SetIamPolicyRequest(*Policy) (req *http.Request, err error) - IsBigqueryResource() bool -} - -// parsedIamResource implements IamResource. -type parsedIamResource struct { +// IamResource implements Resource. +type IamResource struct { relativeId *gcputil.RelativeResourceName - config *IamRestResource + config *RestResource } -type IamRestResource struct { - // Name is the base name of the resource - // i.e. for a GCE instance: "instance" - Name string - - // Type Key is the identifying path for the resource, or - // the RESTful resource identifier without resource IDs - // i.e. For a GCE instance: "projects/zones/instances" - TypeKey string - - // Service Information - // Service is the name of the service this resource belongs to. - Service string - - // IsPreferredVersion is true if this version of the API/resource is preferred. - IsPreferredVersion bool - - // IsPreferredVersion is true if this version of the API/resource is preferred. - GetMethod RestMethod - - // IsPreferredVersion is true if this version of the API/resource is preferred. - SetMethod RestMethod - - // Ordered parameters to be replaced in method paths - Parameters []string - - // collection Id --> parameter to be replaced {} name - CollectionReplacementKeys map[string]string +func (r *IamResource) GetConfig() *RestResource { + return r.config } -type RestMethod struct { - HttpMethod string - BaseURL string - Path string - RequestFormat string +func (r *IamResource) GetRelativeId() *gcputil.RelativeResourceName { + return r.relativeId } -func (r *parsedIamResource) SetIamPolicyRequest(p *Policy) (req *http.Request, err error) { - var jsonP []byte - if r.IsBigqueryResource() { - jsonP, err = json.Marshal(p.AsDataset()) - } else { - jsonP, err = json.Marshal(p) +func (r *IamResource) GetIamPolicy(ctx context.Context, h *ApiHandle) (*Policy, error) { + var p Policy + if err := h.DoGetRequest(ctx, r, &p); err != nil { + return nil, errwrap.Wrapf("unable to get policy: {{err}}", err) } + return &p, nil +} + +func (r *IamResource) SetIamPolicy(ctx context.Context, h *ApiHandle, p *Policy) (*Policy, error) { + jsonP, err := json.Marshal(p) if err != nil { return nil, err } @@ -76,60 +41,9 @@ func (r *parsedIamResource) SetIamPolicyRequest(p *Policy) (req *http.Request, e return nil, fmt.Errorf("request format from generated IAM config invalid JSON: %s", reqJson) } - return r.constructRequest(&r.config.SetMethod, strings.NewReader(reqJson)) -} - -var requestPolicyVersion3 = `{"options": {"requestedPolicyVersion": 3}}` - -func (r *parsedIamResource) GetIamPolicyRequest() (*http.Request, error) { - // In order to support Resource Manager policies with conditional bindings, - // we need to request the policy version of 3. This request parameter is backwards compatible - // and will return version 1 policies if they are not yet updated to version 3. - if r.config != nil && r.config.Service == "cloudresourcemanager" { - return r.constructRequest(&r.config.GetMethod, strings.NewReader(requestPolicyVersion3)) - } - return r.constructRequest(&r.config.GetMethod, nil) -} - -func (r *parsedIamResource) constructRequest(restMethod *RestMethod, data io.Reader) (*http.Request, error) { - req, err := http.NewRequest( - restMethod.HttpMethod, - googleapi.ResolveRelative(restMethod.BaseURL, restMethod.Path), - data) - if err != nil { - return nil, err + var policy Policy + if err := h.DoSetRequest(ctx, r, strings.NewReader(reqJson), &policy); err != nil { + return nil, errwrap.Wrapf("unable to set policy: {{err}}", err) } - - if req.Header == nil { - req.Header = make(http.Header) - } - if data != nil { - req.Header.Set("Content-Type", "application/json") - } - - relId := r.relativeId - replacementMap := make(map[string]string) - - if strings.Contains(restMethod.Path, "{+resource}") { - // +resource is used to represent full relative resource name - if len(r.config.Parameters) == 1 && r.config.Parameters[0] == "resource" { - relName := "" - tkns := strings.Split(r.config.TypeKey, "/") - for _, colId := range tkns { - relName += fmt.Sprintf("%s/%s/", colId, relId.IdTuples[colId]) - } - replacementMap["resource"] = strings.Trim(relName, "/") - } - } else { - for colId, resId := range relId.IdTuples { - rId, ok := r.config.CollectionReplacementKeys[colId] - if !ok { - return nil, fmt.Errorf("expected value for collection id %s", colId) - } - replacementMap[rId] = resId - } - } - - googleapi.Expand(req.URL, replacementMap) - return req, nil + return &policy, nil } diff --git a/plugin/iamutil/iam_resource_test.go b/plugin/iamutil/iam_resource_test.go index f37f65fa..d7caa063 100644 --- a/plugin/iamutil/iam_resource_test.go +++ b/plugin/iamutil/iam_resource_test.go @@ -2,13 +2,15 @@ package iamutil import ( "encoding/json" + "fmt" "github.com/hashicorp/go-gcp-common/gcputil" "io/ioutil" + "strings" "testing" ) -func TestParsedIamResource(t *testing.T) { - r := &parsedIamResource{ +func TestIamResource(t *testing.T) { + r := &IamResource{ relativeId: &gcputil.RelativeResourceName{ Name: "b", TypeKey: "f/b", @@ -18,7 +20,7 @@ func TestParsedIamResource(t *testing.T) { }, OrderedCollectionIds: []string{"f", "b"}, }, - config: &IamRestResource{ + config: &RestResource{ Name: "b", TypeKey: "f/b", Service: "agcpservice", @@ -44,7 +46,7 @@ func TestParsedIamResource(t *testing.T) { }, } - getR, err := r.GetIamPolicyRequest() + getR, err := constructRequest(r, &r.config.GetMethod, nil) if err != nil { t.Fatalf("Could not construct GetIamPolicyRequest: %v", err) } @@ -73,7 +75,18 @@ func TestParsedIamResource(t *testing.T) { }, }, } - setR, err := r.SetIamPolicyRequest(expectedP) + + jsonP, err := json.Marshal(expectedP) + if err != nil { + t.Fatalf("Could not json marshal expected policy: %v", err) + } + + reqJson := fmt.Sprintf(r.config.SetMethod.RequestFormat, jsonP) + if !json.Valid([]byte(reqJson)) { + t.Fatalf("Could not formate expected policy: %v", err) + } + + setR, err := constructRequest(r, &r.config.SetMethod, strings.NewReader(reqJson)) if err != nil { t.Fatalf("Could not construct SetIamPolicyRequest: %v", err) } @@ -126,7 +139,7 @@ func TestParsedIamResource(t *testing.T) { } func TestConditionalIamResource(t *testing.T) { - r := &parsedIamResource{ + r := &IamResource{ relativeId: &gcputil.RelativeResourceName{ Name: "projects", TypeKey: "cloudresourcemanager/projects", @@ -135,7 +148,7 @@ func TestConditionalIamResource(t *testing.T) { }, OrderedCollectionIds: []string{"cloudresourcemanager", "projects"}, }, - config: &IamRestResource{ + config: &RestResource{ Name: "projects", TypeKey: "cloudresourcemanager/projects", Service: "cloudresourcemanager", @@ -157,7 +170,7 @@ func TestConditionalIamResource(t *testing.T) { }, } - getR, err := r.GetIamPolicyRequest() + getR, err := constructRequest(r, &r.config.GetMethod, nil) if err != nil { t.Fatalf("Could not construct GetIamPolicyRequest: %v", err) } @@ -218,7 +231,7 @@ func TestConditionalIamResource(t *testing.T) { Condition: &Condition{ Title: "test", Description: "", - Expression: "a==b", + Expression: "a==b", }, }, { @@ -227,7 +240,18 @@ func TestConditionalIamResource(t *testing.T) { }, }, } - setR, err := r.SetIamPolicyRequest(expectedP) + + jsonP, err := json.Marshal(expectedP) + if err != nil { + t.Fatalf("Could not json marshal expected policy: %v", err) + } + + reqJson := fmt.Sprintf(r.config.SetMethod.RequestFormat, jsonP) + if !json.Valid([]byte(reqJson)) { + t.Fatalf("Could not formate expected policy: %v", err) + } + + setR, err := constructRequest(r, &r.config.SetMethod, strings.NewReader(reqJson)) if err != nil { t.Fatalf("Could not construct SetIamPolicyRequest: %v", err) } diff --git a/plugin/iamutil/internal/generate_iam.go b/plugin/iamutil/internal/generate_resources.go similarity index 84% rename from plugin/iamutil/internal/generate_iam.go rename to plugin/iamutil/internal/generate_resources.go index 5c208f48..e6b3a17f 100644 --- a/plugin/iamutil/internal/generate_iam.go +++ b/plugin/iamutil/internal/generate_resources.go @@ -21,7 +21,7 @@ import ( const ( templateFile = "resource_config_template" - outputFile = "iam_resources_generated.go" + outputFile = "resources_generated.go" ) var sanizitedCollectionIds = map[string]string{ @@ -86,7 +86,7 @@ func checkResource(name string, fullPath string, resource *discovery.RestResourc return fmt.Errorf("unable to get schema for setIamPolicy request, could not find policy in schema '%s'", setM.Request.Ref) } - r := iamutil.IamRestResource{ + r := iamutil.RestResource{ Name: name, TypeKey: typeKey, Service: doc.Name, @@ -157,6 +157,9 @@ func parseTypeKeyFromPattern(pattern string) string { typeKey := "" re := regexp.MustCompile("^[a-zA-Z]*[a-z]$") ptn := strings.Trim(pattern, "^$/") + // In a few resources, the Discovery API hardcodes "global" which if set + // as the TypeKey breaks the common pattern in tests. + ptn = strings.ReplaceAll(ptn, "global/", "") tkns := strings.Split(ptn, "/") for _, tkn := range tkns { if re.MatchString(tkn) { @@ -181,13 +184,13 @@ func getPolicyReplacementString(sch *discovery.JsonSchema) string { return "" } -func addToConfig(resourceKey, service, version string, r iamutil.IamRestResource, config iamutil.GeneratedResources) { +func addToConfig(resourceKey, service, version string, r iamutil.RestResource, config iamutil.GeneratedResources) { log.Printf("adding [%s %s %s]", resourceKey, service, version) if _, ok := config[resourceKey]; !ok { - config[resourceKey] = make(map[string]map[string]iamutil.IamRestResource) + config[resourceKey] = make(map[string]map[string]iamutil.RestResource) } if _, ok := config[resourceKey][service]; !ok { - config[resourceKey][service] = make(map[string]iamutil.IamRestResource) + config[resourceKey][service] = make(map[string]iamutil.RestResource) } config[resourceKey][service][version] = r } @@ -236,11 +239,9 @@ func generateConfig() error { return err } - // Inject BigQuery here since it doesn't have a IAM setter/getter - config["projects/datasets"] = map[string]map[string]iamutil.IamRestResource{ - "bigquery": { - "v2": bqResource(), - }, + // Inject overrides that use ACLs instead of IAM policies + for k, v := range resourceOverrides { + config[k] = v } if err := writeConfig(config); err != nil { @@ -250,31 +251,6 @@ func generateConfig() error { return nil } -func bqResource() iamutil.IamRestResource { - return iamutil.IamRestResource{ - Name: "datasets", - TypeKey: "projects/datasets", - Service: "bigquery", - IsPreferredVersion: true, - Parameters: []string{"resource"}, - CollectionReplacementKeys: map[string]string{}, - GetMethod: iamutil.RestMethod{ - HttpMethod: "GET", - BaseURL: "https://bigquery.googleapis.com", - Path: "bigquery/v2/{+resource}", - }, - SetMethod: iamutil.RestMethod{ - HttpMethod: "PATCH", - BaseURL: "https://bigquery.googleapis.com", - // NOTE: the bigquery portion of the path needs to be in - // the version since googleapis removes it from the - // BaseURL when resolving - Path: "bigquery/v2/{+resource}", - RequestFormat: "%s", - }, - } -} - func writeConfig(config iamutil.GeneratedResources) error { tpl, err := template.ParseFiles(fmt.Sprintf("internal/%s", templateFile)) diff --git a/plugin/iamutil/internal/resource_config_template b/plugin/iamutil/internal/resource_config_template index 947cbf47..98ae74f8 100644 --- a/plugin/iamutil/internal/resource_config_template +++ b/plugin/iamutil/internal/resource_config_template @@ -2,17 +2,17 @@ // THIS FILE IS AUTOGENERATED USING go generate. DO NOT EDIT. package iamutil -func GetEnabledIamResources() GeneratedResources { - return generatedIamResources +func GetEnabledResources() GeneratedResources { + return generatedResources } -var generatedIamResources = map[string]map[string]map[string]IamRestResource { +var generatedResources = map[string]map[string]map[string]RestResource { {{ range $typeKey,$serviceMap := . -}} "{{$typeKey}}": { {{ range $service, $versionMap := . -}} "{{$service}}": { {{ range $version, $resource := $versionMap -}} - "{{$version}}": {{template "iam_rest_resource" $resource}}, + "{{$version}}": {{template "rest_resource" $resource}}, {{ end -}} }, {{ end -}} @@ -22,8 +22,8 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource { {{end}} -{{ define "iam_rest_resource" -}} -IamRestResource{ +{{ define "rest_resource" -}} +RestResource{ Name: "{{.Name}}", TypeKey: "{{.TypeKey}}", Service: "{{.Service}}", @@ -54,4 +54,4 @@ RestMethod { Path: "{{ .Path }}", RequestFormat: `{{ .RequestFormat }}`, } -{{- end}} \ No newline at end of file +{{- end}} diff --git a/plugin/iamutil/internal/resource_overrides.go b/plugin/iamutil/internal/resource_overrides.go new file mode 100644 index 00000000..f0096170 --- /dev/null +++ b/plugin/iamutil/internal/resource_overrides.go @@ -0,0 +1,34 @@ +package main + +import ( + "github.com/hashicorp/vault-plugin-secrets-gcp/plugin/iamutil" +) + +var resourceOverrides = map[string]map[string]map[string]iamutil.RestResource{ + "projects/datasets": { + "bigquery": { + "v2": iamutil.RestResource{ + Name: "datasets", + TypeKey: "projects/datasets", + Service: "bigquery", + IsPreferredVersion: true, + Parameters: []string{"resource"}, + CollectionReplacementKeys: map[string]string{}, + GetMethod: iamutil.RestMethod{ + HttpMethod: "GET", + BaseURL: "https://bigquery.googleapis.com", + Path: "bigquery/v2/{+resource}", + }, + SetMethod: iamutil.RestMethod{ + HttpMethod: "PATCH", + BaseURL: "https://bigquery.googleapis.com", + // NOTE: the bigquery portion of the path needs to be in + // the version since googleapis removes it from the + // BaseURL when resolving + Path: "bigquery/v2/{+resource}", + RequestFormat: "%s", + }, + }, + }, + }, +} diff --git a/plugin/iamutil/resource.go b/plugin/iamutil/resource.go new file mode 100644 index 00000000..70cbd621 --- /dev/null +++ b/plugin/iamutil/resource.go @@ -0,0 +1,52 @@ +package iamutil + +import ( + "context" + "github.com/hashicorp/go-gcp-common/gcputil" +) + +// IamResource handles constructing HTTP requests for getting and +// setting IAM policies. +type Resource interface { + GetIamPolicy(context.Context, *ApiHandle) (*Policy, error) + SetIamPolicy(context.Context, *ApiHandle, *Policy) (*Policy, error) + GetConfig() *RestResource + GetRelativeId() *gcputil.RelativeResourceName +} + +type RestResource struct { + // Name is the base name of the resource + // i.e. for a GCE instance: "instance" + Name string + + // Type Key is the identifying path for the resource, or + // the RESTful resource identifier without resource IDs + // i.e. For a GCE instance: "projects/zones/instances" + TypeKey string + + // Service Information + // Service is the name of the service this resource belongs to. + Service string + + // IsPreferredVersion is true if this version of the API/resource is preferred. + IsPreferredVersion bool + + // IsPreferredVersion is true if this version of the API/resource is preferred. + GetMethod RestMethod + + // IsPreferredVersion is true if this version of the API/resource is preferred. + SetMethod RestMethod + + // Ordered parameters to be replaced in method paths + Parameters []string + + // collection Id --> parameter to be replaced {} name + CollectionReplacementKeys map[string]string +} + +type RestMethod struct { + HttpMethod string + BaseURL string + Path string + RequestFormat string +} diff --git a/plugin/iamutil/iam_resource_parser.go b/plugin/iamutil/resource_parser.go similarity index 83% rename from plugin/iamutil/iam_resource_parser.go rename to plugin/iamutil/resource_parser.go index 08d20dfc..f0f3bb24 100644 --- a/plugin/iamutil/iam_resource_parser.go +++ b/plugin/iamutil/resource_parser.go @@ -14,17 +14,17 @@ const ( errorMultipleVersions = `please provide a self-link with version instead; multiple versions of this resource exist, all non-preferred` ) -// IamResourceParser handles parsing resource ID and REST +// ResourceParser handles parsing resource ID and REST // config from a given resource ID or name. -type IamResourceParser interface { - Parse(string) (IamResource, error) +type ResourceParser interface { + Parse(string) (Resource, error) } -// GeneratedResources implements IamResourceParser - a value +// GeneratedResources implements ResourceParser - a value // is generated using internal/generate_iam.go -type GeneratedResources map[string]map[string]map[string]IamRestResource +type GeneratedResources map[string]map[string]map[string]RestResource -func getResourceFromVersions(rawName string, versionMap map[string]IamRestResource) (*IamRestResource, error) { +func getResourceFromVersions(rawName string, versionMap map[string]RestResource) (*RestResource, error) { possibleVer := make([]string, 0, len(versionMap)) for v, config := range versionMap { if config.IsPreferredVersion || len(versionMap) == 1 { @@ -45,10 +45,10 @@ func getResourceFromVersions(rawName string, versionMap map[string]IamRestResour return nil, fmt.Errorf(resourceParsingErrorTmpl, rawName, errorMultipleVersions) } -func (apis GeneratedResources) GetRestConfig(rawName string, fullName *gcputil.FullResourceName, prefix string) (*IamRestResource, error) { +func (apis GeneratedResources) GetRestConfig(rawName string, fullName *gcputil.FullResourceName, prefix string) (*RestResource, error) { relName := fullName.RelativeResourceName if relName == nil { - return nil, fmt.Errorf(resourceParsingErrorTmpl, rawName, fmt.Errorf("unsupported resource type: %s", rawName)) + return nil, fmt.Errorf(resourceParsingErrorTmpl, rawName, fmt.Errorf("relative name does not exist: %s", rawName)) } serviceMap, ok := apis[relName.TypeKey] @@ -80,7 +80,7 @@ func (apis GeneratedResources) GetRestConfig(rawName string, fullName *gcputil.F return nil, fmt.Errorf(resourceParsingErrorTmpl, rawName, errorMultipleServices) } -func (apis GeneratedResources) Parse(rawName string) (IamResource, error) { +func (apis GeneratedResources) Parse(rawName string) (Resource, error) { rUrl, err := url.Parse(rawName) if err != nil { return nil, fmt.Errorf(`resource "%s" is invalid URI`, rawName) @@ -123,8 +123,10 @@ func (apis GeneratedResources) Parse(rawName string) (IamResource, error) { if err != nil { return nil, err } - return &parsedIamResource{ - relativeId: relName, - config: cfg, - }, nil + switch cfg.TypeKey { + case "projects/dataset": + return &DatasetResource{relativeId: relName, config: cfg}, nil + default: + return &IamResource{relativeId: relName, config: cfg}, nil + } } diff --git a/plugin/iamutil/iam_resource_parser_test.go b/plugin/iamutil/resource_parser_test.go similarity index 83% rename from plugin/iamutil/iam_resource_parser_test.go rename to plugin/iamutil/resource_parser_test.go index fca0db9f..5a1e080d 100644 --- a/plugin/iamutil/iam_resource_parser_test.go +++ b/plugin/iamutil/resource_parser_test.go @@ -11,10 +11,12 @@ import ( "github.com/hashicorp/errwrap" ) +var letters = "ABCDEFGHIJKLMNOP" + func TestEnabledIamResources_RelativeName(t *testing.T) { - enabledApis := GetEnabledIamResources() + enabledApis := GetEnabledResources() - for resourceType, services := range generatedIamResources { + for resourceType, services := range generatedResources { if resourceType == "" { continue } @@ -37,7 +39,7 @@ func TestEnabledIamResources_RelativeName(t *testing.T) { } if resource != nil { - if err = verifyResource(resourceType, resource.(*parsedIamResource)); err != nil { + if err = verifyResource(resourceType, resource.(*IamResource)); err != nil { t.Errorf("could not verify resource for relative resource name %q: %sv", testRelName, err) } } @@ -49,9 +51,9 @@ func TestEnabledIamResources_RelativeName(t *testing.T) { } func TestEnabledIamResources_FullName(t *testing.T) { - enabledApis := GetEnabledIamResources() + enabledApis := GetEnabledResources() - for resourceType, services := range generatedIamResources { + for resourceType, services := range generatedResources { if resourceType == "" { continue } @@ -65,7 +67,7 @@ func TestEnabledIamResources_FullName(t *testing.T) { t.Errorf("failed to get resource for full resource name %s (type: %s): %v", testFullName, resourceType, err) continue } - if err = verifyResource(resourceType, resource.(*parsedIamResource)); err != nil { + if err = verifyResource(resourceType, resource.(*IamResource)); err != nil { t.Errorf("could not verify resource for relative resource name %s: %v", testFullName, err) continue } @@ -77,7 +79,7 @@ func TestEnabledIamResources_FullName(t *testing.T) { } } -func constructSelfLink(relName string, cfg IamRestResource) (string, error) { +func constructSelfLink(relName string, cfg RestResource) (string, error) { reqUrl := cfg.GetMethod.BaseURL + cfg.GetMethod.Path _, err := url.Parse(reqUrl) @@ -102,9 +104,9 @@ func constructSelfLink(relName string, cfg IamRestResource) (string, error) { } func TestEnabledIamResources_SelfLink(t *testing.T) { - enabledApis := GetEnabledIamResources() + enabledApis := GetEnabledResources() - for resourceType, services := range generatedIamResources { + for resourceType, services := range generatedResources { for _, versions := range services { for _, cfg := range versions { relName := getFakeId(resourceType) @@ -123,8 +125,10 @@ func TestEnabledIamResources_SelfLink(t *testing.T) { if err != nil { t.Errorf("failed to get resource for self link %s (type: %s): %v", testSelfLink, resourceType, err) } - if err = verifyResource(resourceType, resource.(*parsedIamResource)); err != nil { - t.Errorf("could not verify resource for self link %s: %v", testSelfLink, err) + if r, ok := resource.(*IamResource); ok { + if err = verifyResource(resourceType, r); err != nil { + t.Errorf("could not verify resource for self link %s: %v", testSelfLink, err) + } } } else if resource != nil || err == nil { t.Errorf("expected error for using self link %s (type: %s), got resource:\n %v\n", testSelfLink, resourceType, resource) @@ -135,7 +139,7 @@ func TestEnabledIamResources_SelfLink(t *testing.T) { } } -func expectVersionError(versions map[string]IamRestResource) bool { +func expectVersionError(versions map[string]RestResource) bool { if len(versions) == 1 { return false } @@ -193,7 +197,7 @@ func verifyHttpMethod(typeKey string, m *RestMethod) error { } func TestIamEnabledResources_ValidateGeneratedConfig(t *testing.T) { - for typeKey, services := range generatedIamResources { + for typeKey, services := range generatedResources { for service, versions := range services { for ver, cfg := range versions { if cfg.Service != service { @@ -216,17 +220,19 @@ func getFakeId(resourceType string) string { fakeId := "" for idx, cid := range collectionIds { - fakeId += fmt.Sprintf("%s/aFakeId%d/", cid, idx) + suffix := letters[idx] + fakeId += fmt.Sprintf("%s/aFakeId%s/", cid, string(suffix)) } return strings.Trim(fakeId, "/") } -func verifyResource(rType string, resource *parsedIamResource) error { +func verifyResource(rType string, resource *IamResource) (err error) { + var req *http.Request if resource.relativeId.TypeKey != rType { return fmt.Errorf("expected resource type %s, actual resource has different type %s", rType, resource.relativeId.TypeKey) } - req, err := resource.GetIamPolicyRequest() + req, err = constructRequest(resource, &resource.config.GetMethod, nil) if err != nil { return errwrap.Wrapf("unable to construct GetIamPolicyRequest: {{err}}", err) } @@ -234,7 +240,7 @@ func verifyResource(rType string, resource *parsedIamResource) error { return err } - req, err = resource.SetIamPolicyRequest(nil) + req, err = constructRequest(resource, &resource.config.SetMethod, strings.NewReader("{}")) if err != nil { return errwrap.Wrapf("unable to construct SetIamPolicyRequest: {{err}}", err) } @@ -247,7 +253,8 @@ func verifyResource(rType string, resource *parsedIamResource) error { func verifyConstructRequest(req *http.Request, resourceType string) error { collectionIds := strings.Split(resourceType, "/") for idx := range collectionIds { - rid := fmt.Sprintf("/aFakeId%d", idx) + suffix := letters[idx] + rid := fmt.Sprintf("/aFakeId%s", string(suffix)) if !strings.Contains(req.URL.Path, rid) { return fmt.Errorf("expected expanded request URL %s to contain %s", req.URL.String(), rid) } diff --git a/plugin/iamutil/iam_resources_generated.go b/plugin/iamutil/resources_generated.go similarity index 90% rename from plugin/iamutil/iam_resources_generated.go rename to plugin/iamutil/resources_generated.go index 059accaa..e33030c6 100644 --- a/plugin/iamutil/iam_resources_generated.go +++ b/plugin/iamutil/resources_generated.go @@ -1,14 +1,14 @@ // THIS FILE IS AUTOGENERATED USING go generate. DO NOT EDIT. package iamutil -func GetEnabledIamResources() GeneratedResources { - return generatedIamResources +func GetEnabledResources() GeneratedResources { + return generatedResources } -var generatedIamResources = map[string]map[string]map[string]IamRestResource{ +var generatedResources = map[string]map[string]map[string]RestResource{ "": { "iap": { - "v1": IamRestResource{ + "v1": RestResource{ Name: "v1", TypeKey: "", Service: "iap", @@ -27,7 +27,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "v1beta1": IamRestResource{ + "v1beta1": RestResource{ Name: "v1beta1", TypeKey: "", Service: "iap", @@ -50,7 +50,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "b": { "storage": { - "v1": IamRestResource{ + "v1": RestResource{ Name: "buckets", TypeKey: "b", Service: "storage", @@ -76,7 +76,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "b/o": { "storage": { - "v1": IamRestResource{ + "v1": RestResource{ Name: "objects", TypeKey: "b/o", Service: "storage", @@ -104,7 +104,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "billingAccounts": { "cloudbilling": { - "v1": IamRestResource{ + "v1": RestResource{ Name: "billingAccounts", TypeKey: "billingAccounts", Service: "cloudbilling", @@ -127,7 +127,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "buckets": { "storage": { - "v1": IamRestResource{ + "v1": RestResource{ Name: "buckets", TypeKey: "buckets", Service: "storage", @@ -153,7 +153,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "buckets/objects": { "storage": { - "v1": IamRestResource{ + "v1": RestResource{ Name: "objects", TypeKey: "buckets/objects", Service: "storage", @@ -181,7 +181,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "folders": { "cloudresourcemanager": { - "v2": IamRestResource{ + "v2": RestResource{ Name: "folders", TypeKey: "folders", Service: "cloudresourcemanager", @@ -200,7 +200,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "v2beta1": IamRestResource{ + "v2beta1": RestResource{ Name: "folders", TypeKey: "folders", Service: "cloudresourcemanager", @@ -223,7 +223,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "organizations": { "cloudresourcemanager": { - "v1": IamRestResource{ + "v1": RestResource{ Name: "organizations", TypeKey: "organizations", Service: "cloudresourcemanager", @@ -242,7 +242,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "v1beta1": IamRestResource{ + "v1beta1": RestResource{ Name: "organizations", TypeKey: "organizations", Service: "cloudresourcemanager", @@ -265,7 +265,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "organizations/sources": { "securitycenter": { - "v1": IamRestResource{ + "v1": RestResource{ Name: "sources", TypeKey: "organizations/sources", Service: "securitycenter", @@ -284,7 +284,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "v1beta1": IamRestResource{ + "v1beta1": RestResource{ Name: "sources", TypeKey: "organizations/sources", Service: "securitycenter", @@ -303,7 +303,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "v1p1beta1": IamRestResource{ + "v1p1beta1": RestResource{ Name: "sources", TypeKey: "organizations/sources", Service: "securitycenter", @@ -326,7 +326,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects": { "cloudresourcemanager": { - "v1": IamRestResource{ + "v1": RestResource{ Name: "projects", TypeKey: "projects", Service: "cloudresourcemanager", @@ -347,7 +347,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "v1beta1": IamRestResource{ + "v1beta1": RestResource{ Name: "projects", TypeKey: "projects", Service: "cloudresourcemanager", @@ -372,7 +372,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/backendBuckets": { "compute": { - "alpha": IamRestResource{ + "alpha": RestResource{ Name: "backendBuckets", TypeKey: "projects/backendBuckets", Service: "compute", @@ -398,7 +398,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/configs": { "runtimeconfig": { - "v1beta1": IamRestResource{ + "v1beta1": RestResource{ Name: "configs", TypeKey: "projects/configs", Service: "runtimeconfig", @@ -421,7 +421,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/datasets": { "bigquery": { - "v2": IamRestResource{ + "v2": RestResource{ Name: "datasets", TypeKey: "projects/datasets", Service: "bigquery", @@ -444,7 +444,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/deployments": { "deploymentmanager": { - "alpha": IamRestResource{ + "alpha": RestResource{ Name: "deployments", TypeKey: "projects/deployments", Service: "deploymentmanager", @@ -466,7 +466,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "v2": IamRestResource{ + "v2": RestResource{ Name: "deployments", TypeKey: "projects/deployments", Service: "deploymentmanager", @@ -488,7 +488,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "v2beta": IamRestResource{ + "v2beta": RestResource{ Name: "deployments", TypeKey: "projects/deployments", Service: "deploymentmanager", @@ -514,7 +514,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/images": { "compute": { - "alpha": IamRestResource{ + "alpha": RestResource{ Name: "images", TypeKey: "projects/images", Service: "compute", @@ -536,7 +536,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "beta": IamRestResource{ + "beta": RestResource{ Name: "images", TypeKey: "projects/images", Service: "compute", @@ -558,7 +558,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "v1": IamRestResource{ + "v1": RestResource{ Name: "images", TypeKey: "projects/images", Service: "compute", @@ -584,7 +584,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/instanceTemplates": { "compute": { - "alpha": IamRestResource{ + "alpha": RestResource{ Name: "instanceTemplates", TypeKey: "projects/instanceTemplates", Service: "compute", @@ -606,7 +606,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "beta": IamRestResource{ + "beta": RestResource{ Name: "instanceTemplates", TypeKey: "projects/instanceTemplates", Service: "compute", @@ -628,7 +628,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "v1": IamRestResource{ + "v1": RestResource{ Name: "instanceTemplates", TypeKey: "projects/instanceTemplates", Service: "compute", @@ -654,7 +654,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/instances": { "bigtableadmin": { - "v2": IamRestResource{ + "v2": RestResource{ Name: "instances", TypeKey: "projects/instances", Service: "bigtableadmin", @@ -675,7 +675,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, }, "spanner": { - "v1": IamRestResource{ + "v1": RestResource{ Name: "instances", TypeKey: "projects/instances", Service: "spanner", @@ -698,7 +698,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/instances/backups": { "spanner": { - "v1": IamRestResource{ + "v1": RestResource{ Name: "backups", TypeKey: "projects/instances/backups", Service: "spanner", @@ -719,9 +719,32 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, }, }, + "projects/instances/clusters/backups": { + "bigtableadmin": { + "v2": RestResource{ + Name: "backups", + TypeKey: "projects/instances/clusters/backups", + Service: "bigtableadmin", + IsPreferredVersion: true, + Parameters: []string{"resource"}, + CollectionReplacementKeys: map[string]string{}, + GetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://bigtableadmin.googleapis.com/", + Path: "v2/{+resource}:getIamPolicy", + }, + SetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://bigtableadmin.googleapis.com/", + Path: "v2/{+resource}:setIamPolicy", + RequestFormat: `{"policy": %s}`, + }, + }, + }, + }, "projects/instances/databases": { "spanner": { - "v1": IamRestResource{ + "v1": RestResource{ Name: "databases", TypeKey: "projects/instances/databases", Service: "spanner", @@ -744,7 +767,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/instances/tables": { "bigtableadmin": { - "v2": IamRestResource{ + "v2": RestResource{ Name: "tables", TypeKey: "projects/instances/tables", Service: "bigtableadmin", @@ -767,7 +790,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/interconnects": { "compute": { - "alpha": IamRestResource{ + "alpha": RestResource{ Name: "interconnects", TypeKey: "projects/interconnects", Service: "compute", @@ -793,7 +816,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/licenseCodes": { "compute": { - "alpha": IamRestResource{ + "alpha": RestResource{ Name: "licenseCodes", TypeKey: "projects/licenseCodes", Service: "compute", @@ -819,7 +842,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/licenses": { "compute": { - "alpha": IamRestResource{ + "alpha": RestResource{ Name: "licenses", TypeKey: "projects/licenses", Service: "compute", @@ -841,7 +864,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "beta": IamRestResource{ + "beta": RestResource{ Name: "licenses", TypeKey: "projects/licenses", Service: "compute", @@ -863,7 +886,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "v1": IamRestResource{ + "v1": RestResource{ Name: "licenses", TypeKey: "projects/licenses", Service: "compute", @@ -889,7 +912,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/locations/autoscalingPolicies": { "dataproc": { - "v1": IamRestResource{ + "v1": RestResource{ Name: "autoscalingPolicies", TypeKey: "projects/locations/autoscalingPolicies", Service: "dataproc", @@ -908,7 +931,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "v1beta2": IamRestResource{ + "v1beta2": RestResource{ Name: "autoscalingPolicies", TypeKey: "projects/locations/autoscalingPolicies", Service: "dataproc", @@ -931,7 +954,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/locations/connections": { "bigqueryconnection": { - "v1beta1": IamRestResource{ + "v1beta1": RestResource{ Name: "connections", TypeKey: "projects/locations/connections", Service: "bigqueryconnection", @@ -952,9 +975,32 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, }, }, + "projects/locations/connectivityTests": { + "networkmanagement": { + "v1beta1": RestResource{ + Name: "connectivityTests", + TypeKey: "projects/locations/connectivityTests", + Service: "networkmanagement", + IsPreferredVersion: true, + Parameters: []string{"resource"}, + CollectionReplacementKeys: map[string]string{}, + GetMethod: RestMethod{ + HttpMethod: "GET", + BaseURL: "https://networkmanagement.googleapis.com/", + Path: "v1beta1/{+resource}:getIamPolicy", + }, + SetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://networkmanagement.googleapis.com/", + Path: "v1beta1/{+resource}:setIamPolicy", + RequestFormat: `{"policy": %s}`, + }, + }, + }, + }, "projects/locations/datasets": { "healthcare": { - "v1beta1": IamRestResource{ + "v1beta1": RestResource{ Name: "datasets", TypeKey: "projects/locations/datasets", Service: "healthcare", @@ -977,7 +1023,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/locations/datasets/annotationStores": { "healthcare": { - "v1beta1": IamRestResource{ + "v1beta1": RestResource{ Name: "annotationStores", TypeKey: "projects/locations/datasets/annotationStores", Service: "healthcare", @@ -1000,7 +1046,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/locations/datasets/dicomStores": { "healthcare": { - "v1beta1": IamRestResource{ + "v1beta1": RestResource{ Name: "dicomStores", TypeKey: "projects/locations/datasets/dicomStores", Service: "healthcare", @@ -1023,7 +1069,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/locations/datasets/fhirStores": { "healthcare": { - "v1beta1": IamRestResource{ + "v1beta1": RestResource{ Name: "fhirStores", TypeKey: "projects/locations/datasets/fhirStores", Service: "healthcare", @@ -1044,9 +1090,70 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, }, }, + "projects/locations/domains": { + "managedidentities": { + "v1": RestResource{ + Name: "domains", + TypeKey: "projects/locations/domains", + Service: "managedidentities", + IsPreferredVersion: true, + Parameters: []string{"resource"}, + CollectionReplacementKeys: map[string]string{}, + GetMethod: RestMethod{ + HttpMethod: "GET", + BaseURL: "https://managedidentities.googleapis.com/", + Path: "v1/{+resource}:getIamPolicy", + }, + SetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://managedidentities.googleapis.com/", + Path: "v1/{+resource}:setIamPolicy", + RequestFormat: `{"policy": %s}`, + }, + }, + "v1alpha1": RestResource{ + Name: "domains", + TypeKey: "projects/locations/domains", + Service: "managedidentities", + IsPreferredVersion: false, + Parameters: []string{"resource"}, + CollectionReplacementKeys: map[string]string{}, + GetMethod: RestMethod{ + HttpMethod: "GET", + BaseURL: "https://managedidentities.googleapis.com/", + Path: "v1alpha1/{+resource}:getIamPolicy", + }, + SetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://managedidentities.googleapis.com/", + Path: "v1alpha1/{+resource}:setIamPolicy", + RequestFormat: `{"policy": %s}`, + }, + }, + "v1beta1": RestResource{ + Name: "domains", + TypeKey: "projects/locations/domains", + Service: "managedidentities", + IsPreferredVersion: false, + Parameters: []string{"resource"}, + CollectionReplacementKeys: map[string]string{}, + GetMethod: RestMethod{ + HttpMethod: "GET", + BaseURL: "https://managedidentities.googleapis.com/", + Path: "v1beta1/{+resource}:getIamPolicy", + }, + SetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://managedidentities.googleapis.com/", + Path: "v1beta1/{+resource}:setIamPolicy", + RequestFormat: `{"policy": %s}`, + }, + }, + }, + }, "projects/locations/entryGroups": { "datacatalog": { - "v1beta1": IamRestResource{ + "v1beta1": RestResource{ Name: "entryGroups", TypeKey: "projects/locations/entryGroups", Service: "datacatalog", @@ -1069,7 +1176,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/locations/functions": { "cloudfunctions": { - "v1": IamRestResource{ + "v1": RestResource{ Name: "functions", TypeKey: "projects/locations/functions", Service: "cloudfunctions", @@ -1092,7 +1199,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/locations/instances": { "datafusion": { - "v1beta1": IamRestResource{ + "v1beta1": RestResource{ Name: "instances", TypeKey: "projects/locations/instances", Service: "datafusion", @@ -1112,10 +1219,31 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, }, }, + "memcache": { + "v1beta2": RestResource{ + Name: "instances", + TypeKey: "projects/locations/instances", + Service: "memcache", + IsPreferredVersion: true, + Parameters: []string{"resource"}, + CollectionReplacementKeys: map[string]string{}, + GetMethod: RestMethod{ + HttpMethod: "GET", + BaseURL: "https://memcache.googleapis.com/", + Path: "v1beta2/{+resource}:getIamPolicy", + }, + SetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://memcache.googleapis.com/", + Path: "v1beta2/{+resource}:setIamPolicy", + RequestFormat: `{"policy": %s}`, + }, + }, + }, }, "projects/locations/keyRings": { "cloudkms": { - "v1": IamRestResource{ + "v1": RestResource{ Name: "keyRings", TypeKey: "projects/locations/keyRings", Service: "cloudkms", @@ -1138,7 +1266,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/locations/keyRings/cryptoKeys": { "cloudkms": { - "v1": IamRestResource{ + "v1": RestResource{ Name: "cryptoKeys", TypeKey: "projects/locations/keyRings/cryptoKeys", Service: "cloudkms", @@ -1161,7 +1289,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/locations/keyRings/importJobs": { "cloudkms": { - "v1": IamRestResource{ + "v1": RestResource{ Name: "importJobs", TypeKey: "projects/locations/keyRings/importJobs", Service: "cloudkms", @@ -1182,9 +1310,55 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, }, }, + "projects/locations/namespaces": { + "servicedirectory": { + "v1beta1": RestResource{ + Name: "namespaces", + TypeKey: "projects/locations/namespaces", + Service: "servicedirectory", + IsPreferredVersion: true, + Parameters: []string{"resource"}, + CollectionReplacementKeys: map[string]string{}, + GetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://servicedirectory.googleapis.com/", + Path: "v1beta1/{+resource}:getIamPolicy", + }, + SetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://servicedirectory.googleapis.com/", + Path: "v1beta1/{+resource}:setIamPolicy", + RequestFormat: `{"policy": %s}`, + }, + }, + }, + }, + "projects/locations/namespaces/services": { + "servicedirectory": { + "v1beta1": RestResource{ + Name: "services", + TypeKey: "projects/locations/namespaces/services", + Service: "servicedirectory", + IsPreferredVersion: true, + Parameters: []string{"resource"}, + CollectionReplacementKeys: map[string]string{}, + GetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://servicedirectory.googleapis.com/", + Path: "v1beta1/{+resource}:getIamPolicy", + }, + SetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://servicedirectory.googleapis.com/", + Path: "v1beta1/{+resource}:setIamPolicy", + RequestFormat: `{"policy": %s}`, + }, + }, + }, + }, "projects/locations/queues": { "cloudtasks": { - "v2": IamRestResource{ + "v2": RestResource{ Name: "queues", TypeKey: "projects/locations/queues", Service: "cloudtasks", @@ -1203,7 +1377,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "v2beta2": IamRestResource{ + "v2beta2": RestResource{ Name: "queues", TypeKey: "projects/locations/queues", Service: "cloudtasks", @@ -1222,7 +1396,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "v2beta3": IamRestResource{ + "v2beta3": RestResource{ Name: "queues", TypeKey: "projects/locations/queues", Service: "cloudtasks", @@ -1245,7 +1419,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/locations/registries": { "cloudiot": { - "v1": IamRestResource{ + "v1": RestResource{ Name: "registries", TypeKey: "projects/locations/registries", Service: "cloudiot", @@ -1268,7 +1442,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/locations/registries/groups": { "cloudiot": { - "v1": IamRestResource{ + "v1": RestResource{ Name: "groups", TypeKey: "projects/locations/registries/groups", Service: "cloudiot", @@ -1291,7 +1465,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/locations/services": { "run": { - "v1": IamRestResource{ + "v1": RestResource{ Name: "services", TypeKey: "projects/locations/services", Service: "run", @@ -1310,7 +1484,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "v1alpha1": IamRestResource{ + "v1alpha1": RestResource{ Name: "services", TypeKey: "projects/locations/services", Service: "run", @@ -1333,7 +1507,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/locations/tagTemplates": { "datacatalog": { - "v1beta1": IamRestResource{ + "v1beta1": RestResource{ Name: "tagTemplates", TypeKey: "projects/locations/tagTemplates", Service: "datacatalog", @@ -1356,7 +1530,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/locations/taxonomies": { "datacatalog": { - "v1beta1": IamRestResource{ + "v1beta1": RestResource{ Name: "taxonomies", TypeKey: "projects/locations/taxonomies", Service: "datacatalog", @@ -1379,7 +1553,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/locations/taxonomies/policyTags": { "datacatalog": { - "v1beta1": IamRestResource{ + "v1beta1": RestResource{ Name: "policyTags", TypeKey: "projects/locations/taxonomies/policyTags", Service: "datacatalog", @@ -1402,7 +1576,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/locations/workflowTemplates": { "dataproc": { - "v1": IamRestResource{ + "v1": RestResource{ Name: "workflowTemplates", TypeKey: "projects/locations/workflowTemplates", Service: "dataproc", @@ -1421,7 +1595,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "v1beta2": IamRestResource{ + "v1beta2": RestResource{ Name: "workflowTemplates", TypeKey: "projects/locations/workflowTemplates", Service: "dataproc", @@ -1444,7 +1618,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/machineImages": { "compute": { - "alpha": IamRestResource{ + "alpha": RestResource{ Name: "machineImages", TypeKey: "projects/machineImages", Service: "compute", @@ -1466,7 +1640,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "beta": IamRestResource{ + "beta": RestResource{ Name: "machineImages", TypeKey: "projects/machineImages", Service: "compute", @@ -1492,7 +1666,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/notes": { "containeranalysis": { - "v1alpha1": IamRestResource{ + "v1alpha1": RestResource{ Name: "notes", TypeKey: "projects/notes", Service: "containeranalysis", @@ -1511,7 +1685,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "v1beta1": IamRestResource{ + "v1beta1": RestResource{ Name: "notes", TypeKey: "projects/notes", Service: "containeranalysis", @@ -1534,7 +1708,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/occurrences": { "containeranalysis": { - "v1alpha1": IamRestResource{ + "v1alpha1": RestResource{ Name: "occurrences", TypeKey: "projects/occurrences", Service: "containeranalysis", @@ -1553,7 +1727,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "v1beta1": IamRestResource{ + "v1beta1": RestResource{ Name: "occurrences", TypeKey: "projects/occurrences", Service: "containeranalysis", @@ -1576,7 +1750,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/regions/autoscalingPolicies": { "dataproc": { - "v1": IamRestResource{ + "v1": RestResource{ Name: "autoscalingPolicies", TypeKey: "projects/regions/autoscalingPolicies", Service: "dataproc", @@ -1595,7 +1769,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "v1beta2": IamRestResource{ + "v1beta2": RestResource{ Name: "autoscalingPolicies", TypeKey: "projects/regions/autoscalingPolicies", Service: "dataproc", @@ -1618,7 +1792,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/regions/clusters": { "dataproc": { - "v1": IamRestResource{ + "v1": RestResource{ Name: "clusters", TypeKey: "projects/regions/clusters", Service: "dataproc", @@ -1637,7 +1811,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "v1beta2": IamRestResource{ + "v1beta2": RestResource{ Name: "clusters", TypeKey: "projects/regions/clusters", Service: "dataproc", @@ -1660,7 +1834,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/regions/disks": { "compute": { - "alpha": IamRestResource{ + "alpha": RestResource{ Name: "regionDisks", TypeKey: "projects/regions/disks", Service: "compute", @@ -1683,7 +1857,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "beta": IamRestResource{ + "beta": RestResource{ Name: "regionDisks", TypeKey: "projects/regions/disks", Service: "compute", @@ -1710,7 +1884,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/regions/interconnectAttachments": { "compute": { - "alpha": IamRestResource{ + "alpha": RestResource{ Name: "interconnectAttachments", TypeKey: "projects/regions/interconnectAttachments", Service: "compute", @@ -1737,7 +1911,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/regions/jobs": { "dataproc": { - "v1": IamRestResource{ + "v1": RestResource{ Name: "jobs", TypeKey: "projects/regions/jobs", Service: "dataproc", @@ -1756,7 +1930,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "v1beta2": IamRestResource{ + "v1beta2": RestResource{ Name: "jobs", TypeKey: "projects/regions/jobs", Service: "dataproc", @@ -1779,7 +1953,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/regions/nodeTemplates": { "compute": { - "alpha": IamRestResource{ + "alpha": RestResource{ Name: "nodeTemplates", TypeKey: "projects/regions/nodeTemplates", Service: "compute", @@ -1802,7 +1976,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "beta": IamRestResource{ + "beta": RestResource{ Name: "nodeTemplates", TypeKey: "projects/regions/nodeTemplates", Service: "compute", @@ -1825,7 +1999,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "v1": IamRestResource{ + "v1": RestResource{ Name: "nodeTemplates", TypeKey: "projects/regions/nodeTemplates", Service: "compute", @@ -1852,7 +2026,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/regions/operations": { "dataproc": { - "v1": IamRestResource{ + "v1": RestResource{ Name: "operations", TypeKey: "projects/regions/operations", Service: "dataproc", @@ -1871,7 +2045,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "v1beta2": IamRestResource{ + "v1beta2": RestResource{ Name: "operations", TypeKey: "projects/regions/operations", Service: "dataproc", @@ -1894,7 +2068,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/regions/resourcePolicies": { "compute": { - "alpha": IamRestResource{ + "alpha": RestResource{ Name: "resourcePolicies", TypeKey: "projects/regions/resourcePolicies", Service: "compute", @@ -1917,7 +2091,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "beta": IamRestResource{ + "beta": RestResource{ Name: "resourcePolicies", TypeKey: "projects/regions/resourcePolicies", Service: "compute", @@ -1940,7 +2114,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "v1": IamRestResource{ + "v1": RestResource{ Name: "resourcePolicies", TypeKey: "projects/regions/resourcePolicies", Service: "compute", @@ -1967,7 +2141,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/regions/subnetworks": { "compute": { - "alpha": IamRestResource{ + "alpha": RestResource{ Name: "subnetworks", TypeKey: "projects/regions/subnetworks", Service: "compute", @@ -1990,7 +2164,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "beta": IamRestResource{ + "beta": RestResource{ Name: "subnetworks", TypeKey: "projects/regions/subnetworks", Service: "compute", @@ -2013,7 +2187,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "v1": IamRestResource{ + "v1": RestResource{ Name: "subnetworks", TypeKey: "projects/regions/subnetworks", Service: "compute", @@ -2040,7 +2214,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/regions/workflowTemplates": { "dataproc": { - "v1": IamRestResource{ + "v1": RestResource{ Name: "workflowTemplates", TypeKey: "projects/regions/workflowTemplates", Service: "dataproc", @@ -2059,7 +2233,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "v1beta2": IamRestResource{ + "v1beta2": RestResource{ Name: "workflowTemplates", TypeKey: "projects/regions/workflowTemplates", Service: "dataproc", @@ -2082,7 +2256,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/repos": { "sourcerepo": { - "v1": IamRestResource{ + "v1": RestResource{ Name: "repos", TypeKey: "projects/repos", Service: "sourcerepo", @@ -2105,13 +2279,32 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/secrets": { "secretmanager": { - "v1beta1": IamRestResource{ + "v1": RestResource{ Name: "secrets", TypeKey: "projects/secrets", Service: "secretmanager", IsPreferredVersion: true, Parameters: []string{"resource"}, CollectionReplacementKeys: map[string]string{}, + GetMethod: RestMethod{ + HttpMethod: "GET", + BaseURL: "https://secretmanager.googleapis.com/", + Path: "v1/{+resource}:getIamPolicy", + }, + SetMethod: RestMethod{ + HttpMethod: "POST", + BaseURL: "https://secretmanager.googleapis.com/", + Path: "v1/{+resource}:setIamPolicy", + RequestFormat: `{"policy": %s}`, + }, + }, + "v1beta1": RestResource{ + Name: "secrets", + TypeKey: "projects/secrets", + Service: "secretmanager", + IsPreferredVersion: false, + Parameters: []string{"resource"}, + CollectionReplacementKeys: map[string]string{}, GetMethod: RestMethod{ HttpMethod: "GET", BaseURL: "https://secretmanager.googleapis.com/", @@ -2128,7 +2321,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/serviceAccounts": { "iam": { - "v1": IamRestResource{ + "v1": RestResource{ Name: "serviceAccounts", TypeKey: "projects/serviceAccounts", Service: "iam", @@ -2151,7 +2344,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/snapshots": { "compute": { - "alpha": IamRestResource{ + "alpha": RestResource{ Name: "snapshots", TypeKey: "projects/snapshots", Service: "compute", @@ -2173,7 +2366,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "beta": IamRestResource{ + "beta": RestResource{ Name: "snapshots", TypeKey: "projects/snapshots", Service: "compute", @@ -2195,7 +2388,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "v1": IamRestResource{ + "v1": RestResource{ Name: "snapshots", TypeKey: "projects/snapshots", Service: "compute", @@ -2219,7 +2412,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, }, "pubsub": { - "v1": IamRestResource{ + "v1": RestResource{ Name: "snapshots", TypeKey: "projects/snapshots", Service: "pubsub", @@ -2242,7 +2435,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/subscriptions": { "pubsub": { - "v1": IamRestResource{ + "v1": RestResource{ Name: "subscriptions", TypeKey: "projects/subscriptions", Service: "pubsub", @@ -2261,7 +2454,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "v1beta2": IamRestResource{ + "v1beta2": RestResource{ Name: "subscriptions", TypeKey: "projects/subscriptions", Service: "pubsub", @@ -2284,7 +2477,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/topics": { "pubsub": { - "v1": IamRestResource{ + "v1": RestResource{ Name: "topics", TypeKey: "projects/topics", Service: "pubsub", @@ -2303,7 +2496,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "v1beta2": IamRestResource{ + "v1beta2": RestResource{ Name: "topics", TypeKey: "projects/topics", Service: "pubsub", @@ -2326,7 +2519,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/zones/disks": { "compute": { - "alpha": IamRestResource{ + "alpha": RestResource{ Name: "disks", TypeKey: "projects/zones/disks", Service: "compute", @@ -2349,7 +2542,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "beta": IamRestResource{ + "beta": RestResource{ Name: "disks", TypeKey: "projects/zones/disks", Service: "compute", @@ -2372,7 +2565,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "v1": IamRestResource{ + "v1": RestResource{ Name: "disks", TypeKey: "projects/zones/disks", Service: "compute", @@ -2399,7 +2592,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/zones/instances": { "compute": { - "alpha": IamRestResource{ + "alpha": RestResource{ Name: "instances", TypeKey: "projects/zones/instances", Service: "compute", @@ -2422,7 +2615,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "beta": IamRestResource{ + "beta": RestResource{ Name: "instances", TypeKey: "projects/zones/instances", Service: "compute", @@ -2445,7 +2638,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "v1": IamRestResource{ + "v1": RestResource{ Name: "instances", TypeKey: "projects/zones/instances", Service: "compute", @@ -2472,7 +2665,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/zones/nodeGroups": { "compute": { - "alpha": IamRestResource{ + "alpha": RestResource{ Name: "nodeGroups", TypeKey: "projects/zones/nodeGroups", Service: "compute", @@ -2495,7 +2688,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "beta": IamRestResource{ + "beta": RestResource{ Name: "nodeGroups", TypeKey: "projects/zones/nodeGroups", Service: "compute", @@ -2518,7 +2711,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "v1": IamRestResource{ + "v1": RestResource{ Name: "nodeGroups", TypeKey: "projects/zones/nodeGroups", Service: "compute", @@ -2545,7 +2738,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "projects/zones/reservations": { "compute": { - "alpha": IamRestResource{ + "alpha": RestResource{ Name: "reservations", TypeKey: "projects/zones/reservations", Service: "compute", @@ -2568,7 +2761,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "beta": IamRestResource{ + "beta": RestResource{ Name: "reservations", TypeKey: "projects/zones/reservations", Service: "compute", @@ -2591,7 +2784,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ RequestFormat: `{"policy": %s}`, }, }, - "v1": IamRestResource{ + "v1": RestResource{ Name: "reservations", TypeKey: "projects/zones/reservations", Service: "compute", @@ -2618,7 +2811,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "providers/notes": { "containeranalysis": { - "v1alpha1": IamRestResource{ + "v1alpha1": RestResource{ Name: "notes", TypeKey: "providers/notes", Service: "containeranalysis", @@ -2641,7 +2834,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "services": { "servicemanagement": { - "v1": IamRestResource{ + "v1": RestResource{ Name: "services", TypeKey: "services", Service: "servicemanagement", @@ -2664,7 +2857,7 @@ var generatedIamResources = map[string]map[string]map[string]IamRestResource{ }, "services/consumers": { "servicemanagement": { - "v1": IamRestResource{ + "v1": RestResource{ Name: "consumers", TypeKey: "services/consumers", Service: "servicemanagement", diff --git a/plugin/path_role_set.go b/plugin/path_role_set.go index f32ed7d7..962a8d1c 100644 --- a/plugin/path_role_set.go +++ b/plugin/path_role_set.go @@ -236,7 +236,7 @@ func (b *backend) pathRoleSetDelete(ctx context.Context, req *logical.Request, d return nil, err } - iamHandle := iamutil.GetIamHandle(httpC, useragent.String()) + apiHandle := iamutil.GetApiHandle(httpC, useragent.String()) warnings := make([]string, 0) if rs.AccountId != nil { @@ -250,7 +250,7 @@ func (b *backend) pathRoleSetDelete(ctx context.Context, req *logical.Request, d warnings = append(warnings, w) } - if merr := b.removeBindings(ctx, iamHandle, rs.AccountId.EmailOrId, rs.Bindings); merr != nil { + if merr := b.removeBindings(ctx, apiHandle, rs.AccountId.EmailOrId, rs.Bindings); merr != nil { for _, err := range merr.Errors { w := fmt.Sprintf("unable to delete IAM policy bindings for service account %q (WAL entry to clean-up later has been added): %v", rs.AccountId.EmailOrId, err) warnings = append(warnings, w) diff --git a/plugin/role_set.go b/plugin/role_set.go index e8cfbead..632faeb0 100644 --- a/plugin/role_set.go +++ b/plugin/role_set.go @@ -137,7 +137,7 @@ func (b *backend) saveRoleSetWithNewAccount(ctx context.Context, s logical.Stora return nil, err } - iamHandle := iamutil.GetIamHandle(httpC, useragent.String()) + apiHandle := iamutil.GetApiHandle(httpC, useragent.String()) oldAccount := rs.AccountId oldBindings := rs.Bindings @@ -162,7 +162,7 @@ func (b *backend) saveRoleSetWithNewAccount(ctx context.Context, s logical.Stora binds = newBinds rs.Bindings = newBinds } - walIds, err := rs.updateIamPolicies(ctx, s, b.iamResources, iamHandle, binds) + walIds, err := rs.updateIamPolicies(ctx, s, b.resources, apiHandle, binds) if err != nil { tryDeleteWALs(ctx, s, oldWals...) return nil, err @@ -194,7 +194,7 @@ func (b *backend) saveRoleSetWithNewAccount(ctx context.Context, s logical.Stora // Return any errors as warnings so user knows immediate cleanup failed warnings := make([]string, 0) - if errs := b.removeBindings(ctx, iamHandle, oldAccount.EmailOrId, oldBindings); errs != nil { + if errs := b.removeBindings(ctx, apiHandle, oldAccount.EmailOrId, oldBindings); errs != nil { warnings = make([]string, len(errs.Errors), len(errs.Errors)+2) for idx, err := range errs.Errors { warnings[idx] = fmt.Sprintf("unable to immediately delete old binding (WAL cleanup entry has been added): %v", err) @@ -358,7 +358,7 @@ func (rs *RoleSet) newKeyForTokenGen(ctx context.Context, s logical.Storage, iam return walId, nil } -func (rs *RoleSet) updateIamPolicies(ctx context.Context, s logical.Storage, enabledIamResources iamutil.IamResourceParser, iamHandle *iamutil.IamHandle, rb ResourceBindings) ([]string, error) { +func (rs *RoleSet) updateIamPolicies(ctx context.Context, s logical.Storage, enabledResources iamutil.ResourceParser, apiHandle *iamutil.ApiHandle, rb ResourceBindings) ([]string, error) { wals := make([]string, 0, len(rb)) for rName, roles := range rb { walId, err := framework.PutWAL(ctx, s, walTypeIamPolicy, &walIamPolicy{ @@ -374,12 +374,12 @@ func (rs *RoleSet) updateIamPolicies(ctx context.Context, s logical.Storage, ena return wals, err } - resource, err := enabledIamResources.Parse(rName) + resource, err := enabledResources.Parse(rName) if err != nil { return wals, err } - p, err := iamHandle.GetIamPolicy(ctx, resource) + p, err := resource.GetIamPolicy(ctx, apiHandle) if err != nil { return wals, err } @@ -392,7 +392,7 @@ func (rs *RoleSet) updateIamPolicies(ctx context.Context, s logical.Storage, ena continue } - if _, err := iamHandle.SetIamPolicy(ctx, resource, newP); err != nil { + if _, err := resource.SetIamPolicy(ctx, apiHandle, newP); err != nil { return wals, err } wals = append(wals, walId) diff --git a/plugin/rollback.go b/plugin/rollback.go index 6d85f542..fca1d731 100644 --- a/plugin/rollback.go +++ b/plugin/rollback.go @@ -182,7 +182,7 @@ func (b *backend) serviceAccountPolicyRollback(ctx context.Context, req *logical } } - r, err := b.iamResources.Parse(entry.Resource) + r, err := b.resources.Parse(entry.Resource) if err != nil { return err } @@ -192,12 +192,12 @@ func (b *backend) serviceAccountPolicyRollback(ctx context.Context, req *logical return err } - iamHandle := iamutil.GetIamHandle(httpC, useragent.String()) + apiHandle := iamutil.GetApiHandle(httpC, useragent.String()) if err != nil { return err } - p, err := iamHandle.GetIamPolicy(ctx, r) + p, err := r.GetIamPolicy(ctx, apiHandle) if err != nil { return err } @@ -212,7 +212,7 @@ func (b *backend) serviceAccountPolicyRollback(ctx context.Context, req *logical return nil } - _, err = iamHandle.SetIamPolicy(ctx, r, newP) + _, err = r.SetIamPolicy(ctx, apiHandle, newP) return err } @@ -240,15 +240,15 @@ func (b *backend) deleteTokenGenKey(ctx context.Context, iamAdmin *iam.Service, return nil } -func (b *backend) removeBindings(ctx context.Context, iamHandle *iamutil.IamHandle, email string, bindings ResourceBindings) (allErr *multierror.Error) { +func (b *backend) removeBindings(ctx context.Context, apiHandle *iamutil.ApiHandle, email string, bindings ResourceBindings) (allErr *multierror.Error) { for resName, roles := range bindings { - resource, err := b.iamResources.Parse(resName) + resource, err := b.resources.Parse(resName) if err != nil { allErr = multierror.Append(allErr, errwrap.Wrapf(fmt.Sprintf("unable to delete role binding for resource '%s': {{err}}", resName), err)) continue } - p, err := iamHandle.GetIamPolicy(ctx, resource) + p, err := resource.GetIamPolicy(ctx, apiHandle) if err != nil { allErr = multierror.Append(allErr, errwrap.Wrapf(fmt.Sprintf("unable to delete role binding for resource '%s': {{err}}", resName), err)) continue @@ -261,7 +261,7 @@ func (b *backend) removeBindings(ctx context.Context, iamHandle *iamutil.IamHand if !changed { continue } - if _, err = iamHandle.SetIamPolicy(ctx, resource, newP); err != nil { + if _, err = resource.SetIamPolicy(ctx, apiHandle, newP); err != nil { allErr = multierror.Append(allErr, errwrap.Wrapf(fmt.Sprintf("unable to delete role binding for resource '%s': {{err}}", resName), err)) continue } From f888e454a4cf388c9ddd66c41b5ab653d3ba88d5 Mon Sep 17 00:00:00 2001 From: Ryan Canty Date: Thu, 26 Mar 2020 17:58:11 -0700 Subject: [PATCH 3/5] Added etag, fix test bugs, add more tests --- plugin/iamutil/dataset_resource.go | 74 ++++++--- plugin/iamutil/dataset_resource_test.go | 195 ++++++++++++++++++++++-- plugin/iamutil/iam_resource_test.go | 4 +- 3 files changed, 233 insertions(+), 40 deletions(-) diff --git a/plugin/iamutil/dataset_resource.go b/plugin/iamutil/dataset_resource.go index 6a630f52..5abf0727 100644 --- a/plugin/iamutil/dataset_resource.go +++ b/plugin/iamutil/dataset_resource.go @@ -3,6 +3,7 @@ package iamutil import ( "context" "encoding/json" + "errors" "fmt" "github.com/hashicorp/errwrap" "github.com/hashicorp/go-gcp-common/gcputil" @@ -13,12 +14,14 @@ import ( // instead it has an access array with bindings on the dataset // object. https://cloud.google.com/bigquery/docs/reference/rest/v2/datasets#Dataset type AccessBinding struct { - Role string `json:"role,omitempty"` - UserByEmail string `json:"userByEmail,omitempty"` + Role string `json:"role,omitempty"` + UserByEmail string `json:"userByEmail,omitempty"` + GroupByEmail string `json:"groupByEmail,omitempty"` } type Dataset struct { Access []*AccessBinding `json:"access,omitempty"` + Etag string `json:"etag,omitempty"` } // NOTE: DatasetResource implements IamResource. @@ -42,13 +45,17 @@ func (r *DatasetResource) GetIamPolicy(ctx context.Context, h *ApiHandle) (*Poli if err := h.DoGetRequest(ctx, r, &dataset); err != nil { return nil, errwrap.Wrapf("unable to get BigQuery Dataset ACL: {{err}}", err) } - p := dataset.AsPolicy() - return &p, nil + p := datasetAsPolicy(&dataset) + return p, nil } func (r *DatasetResource) SetIamPolicy(ctx context.Context, h *ApiHandle, p *Policy) (*Policy, error) { var jsonP []byte - jsonP, err := json.Marshal(p.AsDataset()) + ds, err := policyAsDataset(p) + if err != nil { + return nil, err + } + jsonP, err = json.Marshal(ds) if err != nil { return nil, err } @@ -61,56 +68,77 @@ func (r *DatasetResource) SetIamPolicy(ctx context.Context, h *ApiHandle, p *Pol if err := h.DoSetRequest(ctx, r, strings.NewReader(reqJson), &dataset); err != nil { return nil, errwrap.Wrapf("unable to set BigQuery Dataset ACL: {{err}}", err) } - policy := dataset.AsPolicy() + policy := datasetAsPolicy(&dataset) - return &policy, nil + return policy, nil } -func (p *Policy) AsDataset() Dataset { - ds := Dataset{} +func policyAsDataset(p *Policy) (*Dataset, error) { + ds := &Dataset{ + Etag: p.Etag, + } + if p == nil { - return ds + return nil, errwrap.Wrap(errors.New("Policy cannot be nil"), nil) } for _, binding := range p.Bindings { + if binding.Condition != nil { + return nil, errwrap.Wrap(errors.New("Bigquery Datasets do not support conditional IAM"), nil) + } for _, member := range binding.Members { - var email string + var email, iamType string memberSplit := strings.Split(member, ":") if len(memberSplit) == 2 { + iamType = memberSplit[0] email = memberSplit[1] } else { email = member } if email != "" { - ds.Access = append(ds.Access, &AccessBinding{ - Role: binding.Role, - UserByEmail: email, - }) + binding := &AccessBinding{Role: binding.Role} + if iamType == "group" { + binding.GroupByEmail = email + } else { + binding.UserByEmail = email + } + ds.Access = append(ds.Access, binding) } } } - return ds + return ds, nil } -func (ds *Dataset) AsPolicy() Policy { - policy := Policy{} +func datasetAsPolicy(ds *Dataset) *Policy { + policy := &Policy{ + Etag: ds.Etag, + } if ds == nil { return policy } bindingMap := make(map[string]*Binding) for _, accessBinding := range ds.Access { - email := fmt.Sprintf("serviceAccount:%s", accessBinding.UserByEmail) + var iamMember string + + //NOTE: Can either have GroupByEmail or UserByEmail but not both + if accessBinding.GroupByEmail != "" { + iamMember = fmt.Sprintf("group:%s", accessBinding.GroupByEmail) + } else if strings.HasSuffix(accessBinding.UserByEmail, "gserviceaccount.com") { + iamMember = fmt.Sprintf("serviceAccount:%s", accessBinding.UserByEmail) + } else { + iamMember = fmt.Sprintf("user:%s", accessBinding.UserByEmail) + } if binding, ok := bindingMap[accessBinding.Role]; ok { - binding.Members = append(binding.Members, email) + binding.Members = append(binding.Members, iamMember) } else { bindingMap[accessBinding.Role] = &Binding{ Role: accessBinding.Role, - Members: []string{email}, + Members: []string{iamMember}, } } } - for k := range bindingMap { - policy.Bindings = append(policy.Bindings, bindingMap[k]) + for _, v := range bindingMap { + policy.Bindings = append(policy.Bindings, v) } return policy } diff --git a/plugin/iamutil/dataset_resource_test.go b/plugin/iamutil/dataset_resource_test.go index 2c6dedb9..ae15a090 100644 --- a/plugin/iamutil/dataset_resource_test.go +++ b/plugin/iamutil/dataset_resource_test.go @@ -2,43 +2,174 @@ package iamutil import ( "encoding/json" + "fmt" + "github.com/hashicorp/go-gcp-common/gcputil" + "io/ioutil" + "reflect" + "sort" + "strings" "testing" ) func TestPolicyToDataset(t *testing.T) { policy, expectedDataset := getTestFixtures() - expectedDatasetBytes, err := json.Marshal(expectedDataset) + actualDataset, err := policyAsDataset(policy) if err != nil { t.Fatal(err) } - actualDataset := policy.AsDataset() - actualDatasetBytes, err := json.Marshal(actualDataset) - if err != nil { - t.Fatal(err) - } - if string(actualDatasetBytes) != string(expectedDatasetBytes) { - t.Fatalf("%v should be equal to %v", string(actualDatasetBytes), string(expectedDatasetBytes)) + if !datasetEq(actualDataset, expectedDataset) { + t.Fatalf("%v should be equal to %v", actualDataset, expectedDataset) } } func TestDatasetToPolicy(t *testing.T) { expectedPolicy, ds := getTestFixtures() - expectedPolicyBytes, err := json.Marshal(expectedPolicy) + actualPolicy := datasetAsPolicy(ds) + if !policyEq(actualPolicy, expectedPolicy) { + t.Fatalf("%v should be equal to %v", actualPolicy, expectedPolicy) + } +} + +func TestDatasetResource(t *testing.T) { + expectedP := &Policy{ + Etag: "atag", + Bindings: []*Binding{ + { + Members: []string{"user:myuser@google.com", "serviceAccount:myserviceaccount@iam.gserviceaccount.com"}, + Role: "roles/arole", + }, + { + Members: []string{"user:myuser@google.com", "group:mygroup@google.com"}, + Role: "roles/anotherrole", + }, + }, + } + verifyDatasetResourceWithPolicy(t, expectedP) +} + +func TestConditionalDatasetResource(t *testing.T) { + p := &Policy{ + Etag: "atag", + Version: 3, + Bindings: []*Binding{ + { + Members: []string{"user:myuser@google.com", "serviceAccount:myserviceaccount@iam.gserviceaccount.com"}, + Role: "roles/arole", + Condition: &Condition{ + Title: "test", + Description: "", + Expression: "a==b", + }, + }, + { + Members: []string{"user:myuser@google.com"}, + Role: "roles/anotherrole", + }, + }, + } + + _, err := policyAsDataset(p) + if err == nil { + t.Fatalf("Datasets do not support conditions, but error was not triggered") + } +} + +func verifyDatasetResourceWithPolicy(t *testing.T, expectedP *Policy) { + r := testResource() + + getR, err := constructRequest(r, &r.config.GetMethod, nil) if err != nil { - t.Fatal(err) + t.Fatalf("Could not construct GetIamPolicyRequest: %v", err) + } + expectedURLBase := "https://bigquery.googleapis.com/bigquery/v2/projects/project/datasets/dataset" + if getR.URL.String() != expectedURLBase { + t.Fatalf("expected get request URL %s, got %s", expectedURLBase, getR.URL.String()) + } + if getR.Method != "GET" { + t.Fatalf("expected get request method %s, got %s", "GET", getR.Method) + } + if getR.Body != nil { + data, err := ioutil.ReadAll(getR.Body) + t.Fatalf("expected nil get body, actual non-nil body.Read returns %s %v", string(data), err) } - actualPolicy := ds.AsPolicy() - actualPolicyBytes, err := json.Marshal(actualPolicy) + + ds, err := policyAsDataset(expectedP) if err != nil { - t.Fatal(err) + t.Fatalf("Could not convert policy to dataset: %v", err) } - if string(actualPolicyBytes) != string(expectedPolicyBytes) { - t.Fatalf("%v should be equal to %v", string(actualPolicyBytes), string(expectedPolicyBytes)) + + jsonP, err := json.Marshal(ds) + if err != nil { + t.Fatalf("Could not json marshal expected policy: %v", err) } + + reqJson := fmt.Sprintf(r.config.SetMethod.RequestFormat, jsonP) + if !json.Valid([]byte(reqJson)) { + t.Fatalf("Could not format expected policy: %v", err) + } + + setR, err := constructRequest(r, &r.config.SetMethod, strings.NewReader(reqJson)) + if err != nil { + t.Fatalf("Could not construct SetIamPolicyRequest: %v", err) + } + + if setR.URL.String() != expectedURLBase { + t.Fatalf("expected set request URL %s, got %s", expectedURLBase, setR.URL.String()) + } + if setR.Method != "PATCH" { + t.Fatalf("expected set request method %s, got %s", "PATCH", setR.Method) + } + if setR.Header.Get("Content-Type") != "application/json" { + t.Fatalf("expected `Content Type = application/json` header in set request, headers: %+v", setR.Header) + } + if setR.Body == nil { + t.Fatalf("expected non-nil set body, actually nil") + } + data, err := ioutil.ReadAll(setR.Body) + if err != nil { + t.Fatalf("unable to read data from set request: %v", err) + } + + actual := struct { + D *Dataset `json:"access,omitempty"` + P *Policy `json:"policy,omitempty"` + }{} + if err := json.Unmarshal(data, &actual.D); err != nil { + t.Fatalf("unable to read policy from set request body: %v", err) + } + actual.P = datasetAsPolicy(actual.D) + if actual.P.Etag != expectedP.Etag { + t.Fatalf("mismatch set request policy, expected %s, got %s", expectedP.Etag, actual.P.Etag) + } + + if len(actual.P.Bindings) != len(expectedP.Bindings) { + t.Fatalf("mismatch set request policy bindings length, expected %+v, got %+v", expectedP.Bindings, actual.P.Bindings) + } + + if !policyEq(expectedP, actual.P) { + exBytes, _ := json.Marshal(expectedP) + acBytes, _ := json.Marshal(actual.P) + t.Fatalf("Expected policy %v. Got policy %v", string(exBytes), string(acBytes)) + } +} + +// Necessary due to using a map to convert between dataset/policy +// since maps do not retain order +func policyEq(p1 *Policy, p2 *Policy) bool { + sort.SliceStable(p1.Bindings, func(i, j int) bool { return p1.Bindings[i].Role < p1.Bindings[j].Role }) + sort.SliceStable(p2.Bindings, func(i, j int) bool { return p2.Bindings[i].Role < p2.Bindings[j].Role }) + return reflect.DeepEqual(p1, p2) +} + +func datasetEq(d1 *Dataset, d2 *Dataset) bool { + sort.SliceStable(d1.Access, func(i, j int) bool { return d1.Access[i].Role < d1.Access[j].Role }) + sort.SliceStable(d2.Access, func(i, j int) bool { return d2.Access[i].Role < d2.Access[j].Role }) + return reflect.DeepEqual(*d1, *d2) } func getTestFixtures() (*Policy, *Dataset) { policy := &Policy{ + Etag: "atag", Bindings: []*Binding{ &Binding{ Members: []string{ @@ -56,6 +187,7 @@ func getTestFixtures() (*Policy, *Dataset) { }, } ds := &Dataset{ + Etag: "atag", Access: []*AccessBinding{ &AccessBinding{ Role: "roles/bigquery.dataViewer", @@ -73,3 +205,36 @@ func getTestFixtures() (*Policy, *Dataset) { } return policy, ds } + +func testResource() *DatasetResource { + return &DatasetResource{ + relativeId: &gcputil.RelativeResourceName{ + Name: "dataset", + TypeKey: "projects/dataset", + IdTuples: map[string]string{ + "projects": "project", + "datasets": "dataset", + }, + OrderedCollectionIds: []string{"projects", "datasets"}, + }, + config: &RestResource{ + Name: "datasets", + TypeKey: "projects/datasets", + Service: "bigquery", + IsPreferredVersion: true, + Parameters: []string{"resource"}, + CollectionReplacementKeys: map[string]string{}, + GetMethod: RestMethod{ + HttpMethod: "GET", + BaseURL: "https://bigquery.googleapis.com", + Path: "bigquery/v2/{+resource}", + }, + SetMethod: RestMethod{ + HttpMethod: "PATCH", + BaseURL: "https://bigquery.googleapis.com", + Path: "bigquery/v2/{+resource}", + RequestFormat: "%s", + }, + }, + } +} diff --git a/plugin/iamutil/iam_resource_test.go b/plugin/iamutil/iam_resource_test.go index d7caa063..6278d8d8 100644 --- a/plugin/iamutil/iam_resource_test.go +++ b/plugin/iamutil/iam_resource_test.go @@ -66,7 +66,7 @@ func TestIamResource(t *testing.T) { Etag: "atag", Bindings: []*Binding{ { - Members: []string{"user:myuser@google.com", "serviceAccount:myserviceaccount@iam.gserviceaccounts.com"}, + Members: []string{"user:myuser@google.com", "serviceAccount:myserviceaccount@iam.gserviceaccount.com"}, Role: "roles/arole", }, { @@ -226,7 +226,7 @@ func TestConditionalIamResource(t *testing.T) { Version: 3, Bindings: []*Binding{ { - Members: []string{"user:myuser@google.com", "serviceAccount:myserviceaccount@iam.gserviceaccounts.com"}, + Members: []string{"user:myuser@google.com", "serviceAccount:myserviceaccount@iam.gserviceaccount.com"}, Role: "roles/arole", Condition: &Condition{ Title: "test", From 75c7a70c490b783efafa87192ed153a69807bbc3 Mon Sep 17 00:00:00 2001 From: Ryan Canty Date: Fri, 10 Apr 2020 13:10:00 -0700 Subject: [PATCH 4/5] Removed unnecessary errwrap --- plugin/iamutil/dataset_resource.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugin/iamutil/dataset_resource.go b/plugin/iamutil/dataset_resource.go index 5abf0727..e80bd945 100644 --- a/plugin/iamutil/dataset_resource.go +++ b/plugin/iamutil/dataset_resource.go @@ -5,9 +5,10 @@ import ( "encoding/json" "errors" "fmt" + "strings" + "github.com/hashicorp/errwrap" "github.com/hashicorp/go-gcp-common/gcputil" - "strings" ) // NOTE: BigQuery does not conform to the typical REST for IAM policies @@ -79,11 +80,11 @@ func policyAsDataset(p *Policy) (*Dataset, error) { } if p == nil { - return nil, errwrap.Wrap(errors.New("Policy cannot be nil"), nil) + return nil, errors.New("Policy cannot be nil") } for _, binding := range p.Bindings { if binding.Condition != nil { - return nil, errwrap.Wrap(errors.New("Bigquery Datasets do not support conditional IAM"), nil) + return nil, errors.New("Bigquery Datasets do not support conditional IAM") } for _, member := range binding.Members { var email, iamType string From 5c8605104b8bff52c45cb61fb5f96aa46256f0be Mon Sep 17 00:00:00 2001 From: Ryan Canty Date: Fri, 10 Apr 2020 17:25:33 -0700 Subject: [PATCH 5/5] Updated per review comments --- go.mod | 8 ++------ go.sum | 27 ++----------------------- plugin/iamutil/api_handle.go | 9 ++++++--- plugin/iamutil/dataset_resource.go | 13 +++++------- plugin/iamutil/dataset_resource_test.go | 3 ++- plugin/iamutil/iam_resource_test.go | 7 ++++--- plugin/iamutil/resource.go | 12 +++++------ 7 files changed, 27 insertions(+), 52 deletions(-) diff --git a/go.mod b/go.mod index 75532d0d..8188c087 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,6 @@ module github.com/hashicorp/vault-plugin-secrets-gcp go 1.12 require ( - github.com/Bowery/prompt v0.0.0-20190916142128-fa8279994f75 // indirect - github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185 // indirect - github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/hashicorp/errwrap v1.0.0 github.com/hashicorp/go-cleanhttp v0.5.1 github.com/hashicorp/go-gcp-common v0.5.0 @@ -17,10 +14,9 @@ require ( github.com/hashicorp/vault/api v1.0.5-0.20200215224050-f6547fa8e820 github.com/hashicorp/vault/sdk v0.1.14-0.20200215224050-f6547fa8e820 github.com/mitchellh/mapstructure v1.1.2 - github.com/pkg/errors v0.9.1 // indirect + golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 // indirect + golang.org/x/net v0.0.0-20200226121028-0de0cce0169b // indirect golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 // indirect - golang.org/x/tools v0.0.0-20200204230316-67a4523381ef // indirect google.golang.org/api v0.14.0 - gopkg.in/yaml.v2 v2.2.8 // indirect ) diff --git a/go.sum b/go.sum index 3f02d81d..0dc50a1d 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,6 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= git.apache.org/thrift.git v0.12.0/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= -github.com/Bowery/prompt v0.0.0-20190916142128-fa8279994f75 h1:xGHheKK44eC6K0u5X+DZW/fRaR1LnDdqPHMZMWx5fv8= -github.com/Bowery/prompt v0.0.0-20190916142128-fa8279994f75/go.mod h1:4/6eNcqZ09BZ9wLK3tZOjBA1nDj+B0728nlX5YRlSmQ= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= @@ -27,8 +25,6 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185 h1:3T8ZyTDp5QxTx3NU48JVb2u+75xc040fofcBaN+6jPA= -github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185/go.mod h1:cFRxtTwTOJkz2x3rQUNCYKWC93yP1VKjR8NUhqFxZNU= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= @@ -72,8 +68,6 @@ github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= @@ -147,8 +141,6 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kardianos/govendor v1.0.9 h1:WOH3FcVI9eOgnIZYg96iwUwrL4eOVx+aQ66oyX2R8Yc= -github.com/kardianos/govendor v1.0.9/go.mod h1:yvmR6q9ZZ7nSF5Wvh40v0wfP+3TwwL8zYQp+itoZSVM= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -170,10 +162,6 @@ github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go. github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/mitchellh/gox v1.0.1 h1:x0jD3dcHk9a9xPSDN6YEL4xL6Qz0dvNYm8yZqui5chI= -github.com/mitchellh/gox v1.0.1/go.mod h1:ED6BioOGXMswlXa2zxfh/xdd5QhwYliBFn9V18Ap4z4= -github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= @@ -191,8 +179,6 @@ github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= @@ -239,7 +225,6 @@ golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTk golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422 h1:QzoH/1pFpZguR8NrRHLcO6jKqfv2zpuSqZLgdm7ZmjI= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -253,9 +238,10 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -308,11 +294,6 @@ golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135 h1:5Beo0mZN8dRzgrMMkDp0jc8YXQKx9DiJ2k1dkvGsn5A= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20200116225955-84cebe10344f h1:ILR8hSRYwfkRGnA5a6c/vTjIFGkLibNIFxGiosQo+LM= -golang.org/x/tools v0.0.0-20200116225955-84cebe10344f/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204230316-67a4523381ef h1:mdhEDFpO1Tfj7PXIflIuP1tbXt4rJgHIvbzdh62SARw= -golang.org/x/tools v0.0.0-20200204230316-67a4523381ef/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.0.0-20181220000619-583d854617af/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.2.0/go.mod h1:IfRCZScioGtypHNTlz3gFk67J8uePVW7uDTBzXuIkhU= google.golang.org/api v0.3.0/go.mod h1:IuvZyQh8jgscv8qWfQ4ABd8m7hEudgBFM/EdhA3BnXw= @@ -357,10 +338,6 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/plugin/iamutil/api_handle.go b/plugin/iamutil/api_handle.go index 748015ed..5a1dc7e0 100644 --- a/plugin/iamutil/api_handle.go +++ b/plugin/iamutil/api_handle.go @@ -4,11 +4,12 @@ import ( "context" "encoding/json" "fmt" - "github.com/hashicorp/errwrap" - "google.golang.org/api/googleapi" "io" "net/http" "strings" + + "github.com/hashicorp/errwrap" + "google.golang.org/api/googleapi" ) type ApiHandle struct { @@ -98,7 +99,9 @@ func constructRequest(r Resource, restMethod *RestMethod, data io.Reader) (*http relName := "" tkns := strings.Split(config.TypeKey, "/") for _, colId := range tkns { - relName += fmt.Sprintf("%s/%s/", colId, relId.IdTuples[colId]) + if colName, ok := relId.IdTuples[colId]; ok { + relName += fmt.Sprintf("%s/%s/", colId, colName) + } } replacementMap["resource"] = strings.Trim(relName, "/") } diff --git a/plugin/iamutil/dataset_resource.go b/plugin/iamutil/dataset_resource.go index e80bd945..51903284 100644 --- a/plugin/iamutil/dataset_resource.go +++ b/plugin/iamutil/dataset_resource.go @@ -75,13 +75,11 @@ func (r *DatasetResource) SetIamPolicy(ctx context.Context, h *ApiHandle, p *Pol } func policyAsDataset(p *Policy) (*Dataset, error) { - ds := &Dataset{ - Etag: p.Etag, - } - if p == nil { return nil, errors.New("Policy cannot be nil") } + + ds := &Dataset{Etag: p.Etag} for _, binding := range p.Bindings { if binding.Condition != nil { return nil, errors.New("Bigquery Datasets do not support conditional IAM") @@ -111,12 +109,11 @@ func policyAsDataset(p *Policy) (*Dataset, error) { } func datasetAsPolicy(ds *Dataset) *Policy { - policy := &Policy{ - Etag: ds.Etag, - } if ds == nil { - return policy + return &Policy{} } + + policy := &Policy{Etag: ds.Etag} bindingMap := make(map[string]*Binding) for _, accessBinding := range ds.Access { var iamMember string diff --git a/plugin/iamutil/dataset_resource_test.go b/plugin/iamutil/dataset_resource_test.go index ae15a090..72891798 100644 --- a/plugin/iamutil/dataset_resource_test.go +++ b/plugin/iamutil/dataset_resource_test.go @@ -3,12 +3,13 @@ package iamutil import ( "encoding/json" "fmt" - "github.com/hashicorp/go-gcp-common/gcputil" "io/ioutil" "reflect" "sort" "strings" "testing" + + "github.com/hashicorp/go-gcp-common/gcputil" ) func TestPolicyToDataset(t *testing.T) { diff --git a/plugin/iamutil/iam_resource_test.go b/plugin/iamutil/iam_resource_test.go index 6278d8d8..5d37af77 100644 --- a/plugin/iamutil/iam_resource_test.go +++ b/plugin/iamutil/iam_resource_test.go @@ -3,10 +3,11 @@ package iamutil import ( "encoding/json" "fmt" - "github.com/hashicorp/go-gcp-common/gcputil" "io/ioutil" "strings" "testing" + + "github.com/hashicorp/go-gcp-common/gcputil" ) func TestIamResource(t *testing.T) { @@ -83,7 +84,7 @@ func TestIamResource(t *testing.T) { reqJson := fmt.Sprintf(r.config.SetMethod.RequestFormat, jsonP) if !json.Valid([]byte(reqJson)) { - t.Fatalf("Could not formate expected policy: %v", err) + t.Fatalf("Could not format expected policy: %v", err) } setR, err := constructRequest(r, &r.config.SetMethod, strings.NewReader(reqJson)) @@ -248,7 +249,7 @@ func TestConditionalIamResource(t *testing.T) { reqJson := fmt.Sprintf(r.config.SetMethod.RequestFormat, jsonP) if !json.Valid([]byte(reqJson)) { - t.Fatalf("Could not formate expected policy: %v", err) + t.Fatalf("Could not format expected policy: %v", err) } setR, err := constructRequest(r, &r.config.SetMethod, strings.NewReader(reqJson)) diff --git a/plugin/iamutil/resource.go b/plugin/iamutil/resource.go index 70cbd621..72739f85 100644 --- a/plugin/iamutil/resource.go +++ b/plugin/iamutil/resource.go @@ -2,10 +2,11 @@ package iamutil import ( "context" + "github.com/hashicorp/go-gcp-common/gcputil" ) -// IamResource handles constructing HTTP requests for getting and +// Resource handles constructing HTTP requests for getting and // setting IAM policies. type Resource interface { GetIamPolicy(context.Context, *ApiHandle) (*Policy, error) @@ -19,28 +20,27 @@ type RestResource struct { // i.e. for a GCE instance: "instance" Name string - // Type Key is the identifying path for the resource, or + // TypeKey is the identifying path for the resource, or // the RESTful resource identifier without resource IDs // i.e. For a GCE instance: "projects/zones/instances" TypeKey string - // Service Information // Service is the name of the service this resource belongs to. Service string // IsPreferredVersion is true if this version of the API/resource is preferred. IsPreferredVersion bool - // IsPreferredVersion is true if this version of the API/resource is preferred. + // HTTP metadata for getting Policy data in GCP GetMethod RestMethod - // IsPreferredVersion is true if this version of the API/resource is preferred. + // HTTP metadata for setting Policy data in GCP SetMethod RestMethod // Ordered parameters to be replaced in method paths Parameters []string - // collection Id --> parameter to be replaced {} name + // Mapping of collection ids onto the parameter to be replaced CollectionReplacementKeys map[string]string }