From 8c12b27f8f4a28925d7fb89abc87a7dc2455077f Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Tue, 13 Feb 2024 15:01:48 -0500 Subject: [PATCH] Add support for grant_scope_ids (#562) --- CHANGELOG.md | 9 +- docs/resources/role.md | 3 +- docs/resources/scope.md | 8 +- examples/resources/boundary_scope/role.tf | 8 +- go.mod | 38 +-- go.sum | 80 ++--- internal/provider/data_source_scope_test.go | 2 +- .../provider/resource_account_ldap_test.go | 2 +- internal/provider/resource_role.go | 85 ++++- ...ource_role_grant_scope_ids_upgrade_test.go | 295 ++++++++++++++++++ internal/provider/resource_role_test.go | 8 +- internal/provider/resource_scope_test.go | 8 +- 12 files changed, 453 insertions(+), 93 deletions(-) create mode 100644 internal/provider/resource_role_grant_scope_ids_upgrade_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index fd78fb51..cf8e6529 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,18 @@ Canonical reference for changes, improvements, and bugfixes for the Boundary Terraform provider. +## Next + +### New and Improved + +* Support the multi-value `grant_scope_ids` field in the role provider + ([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/562)) + ## 1.1.13 (February 1, 2024) ### New and Improved -* Allow dynamic credentials when configuring storage buckets. +* Allow dynamic credentials when configuring storage buckets ([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/549)) ## 1.1.12 (January 8, 2024) diff --git a/docs/resources/role.md b/docs/resources/role.md index c4551eb0..9f3faf9f 100644 --- a/docs/resources/role.md +++ b/docs/resources/role.md @@ -127,7 +127,8 @@ resource "boundary_role" "readonly" { ### Optional - `description` (String) The role description. -- `grant_scope_id` (String) +- `grant_scope_id` (String, Deprecated) For Boundary 0.15+, use `grant_scope_ids` instead. The scope for which the grants in the role should apply. +- `grant_scope_ids` (Set of String) A list of scopes for which the grants in this role should apply, which can include the special values "this", "children", or "descendants" - `grant_strings` (Set of String) A list of stringified grants for the role. - `name` (String) The role name. Defaults to the resource name. - `principal_ids` (Set of String) A list of principal (user or group) IDs to add as principals on the role. diff --git a/docs/resources/scope.md b/docs/resources/scope.md index adb7e0c0..7688c770 100644 --- a/docs/resources/scope.md +++ b/docs/resources/scope.md @@ -53,10 +53,10 @@ resource "boundary_scope" "org" { } resource "boundary_role" "org_admin" { - scope_id = boundary_scope.global.id - grant_scope_id = boundary_scope.org.id - grant_strings = ["ids=*;type=*;actions=*"] - principal_ids = ["u_auth"] + scope_id = boundary_scope.global.id + grant_scope_ids = [boundary_scope.org.id] + grant_strings = ["ids=*;type=*;actions=*"] + principal_ids = ["u_auth"] } ``` diff --git a/examples/resources/boundary_scope/role.tf b/examples/resources/boundary_scope/role.tf index b9f405d0..af85cbdf 100644 --- a/examples/resources/boundary_scope/role.tf +++ b/examples/resources/boundary_scope/role.tf @@ -5,8 +5,8 @@ resource "boundary_scope" "org" { } resource "boundary_role" "org_admin" { - scope_id = boundary_scope.global.id - grant_scope_id = boundary_scope.org.id - grant_strings = ["ids=*;type=*;actions=*"] - principal_ids = ["u_auth"] + scope_id = boundary_scope.global.id + grant_scope_ids = [boundary_scope.org.id] + grant_strings = ["ids=*;type=*;actions=*"] + principal_ids = ["u_auth"] } diff --git a/go.mod b/go.mod index 7aeecb4e..02c92eb4 100644 --- a/go.mod +++ b/go.mod @@ -7,21 +7,21 @@ require ( github.com/hashicorp/boundary v0.15.0 github.com/hashicorp/boundary/api v0.0.45 github.com/hashicorp/boundary/sdk v0.0.42 - github.com/hashicorp/cap v0.4.1 - github.com/hashicorp/cap/ldap v0.0.0-20231012003312-273118a6e3b8 + github.com/hashicorp/cap v0.5.0 + github.com/hashicorp/cap/ldap v0.0.0-20240206183135-ed8f24513744 github.com/hashicorp/go-cty v1.4.1-0.20200723130312-85980079f637 - github.com/hashicorp/go-kms-wrapping/v2 v2.0.15 + github.com/hashicorp/go-kms-wrapping/v2 v2.0.16 github.com/hashicorp/go-secure-stdlib/configutil/v2 v2.0.11 github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8 github.com/hashicorp/go-secure-stdlib/pluginutil/v2 v2.0.6 github.com/hashicorp/terraform-plugin-docs v0.18.0 - github.com/hashicorp/terraform-plugin-sdk/v2 v2.31.0 + github.com/hashicorp/terraform-plugin-sdk/v2 v2.32.0 github.com/jimlambrt/gldap v0.1.9 github.com/kr/pretty v0.3.1 github.com/mitchellh/go-homedir v1.1.0 github.com/stretchr/testify v1.8.4 - golang.org/x/crypto v0.18.0 - mvdan.cc/gofumpt v0.5.0 + golang.org/x/crypto v0.19.0 + mvdan.cc/gofumpt v0.6.0 ) require ( @@ -108,7 +108,7 @@ require ( github.com/hashicorp/nodeenrollment v0.2.9 // indirect github.com/hashicorp/terraform-exec v0.20.0 // indirect github.com/hashicorp/terraform-json v0.21.0 // indirect - github.com/hashicorp/terraform-plugin-go v0.20.0 // indirect + github.com/hashicorp/terraform-plugin-go v0.21.0 // indirect github.com/hashicorp/terraform-plugin-log v0.9.0 // indirect github.com/hashicorp/terraform-registry-address v0.2.3 // indirect github.com/hashicorp/terraform-svchost v0.1.1 // indirect @@ -181,24 +181,24 @@ require ( github.com/yuin/goldmark v1.6.0 // indirect github.com/yuin/goldmark-meta v1.1.0 // indirect github.com/zalando/go-keyring v0.2.3 // indirect - github.com/zclconf/go-cty v1.14.1 // indirect + github.com/zclconf/go-cty v1.14.2 // indirect go.uber.org/atomic v1.11.0 // indirect golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect golang.org/x/mod v0.14.0 // indirect - golang.org/x/net v0.18.0 // indirect - golang.org/x/oauth2 v0.13.0 // indirect - golang.org/x/sync v0.4.0 // indirect - golang.org/x/sys v0.16.0 // indirect - golang.org/x/term v0.16.0 // indirect + golang.org/x/net v0.20.0 // indirect + golang.org/x/oauth2 v0.14.0 // indirect + golang.org/x/sync v0.6.0 // indirect + golang.org/x/sys v0.17.0 // indirect + golang.org/x/term v0.17.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.14.0 // indirect + golang.org/x/tools v0.17.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20231030173426-d783a09b4405 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect - google.golang.org/grpc v1.60.0 // indirect - google.golang.org/protobuf v1.31.0 // indirect + google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect + google.golang.org/grpc v1.61.0 // indirect + google.golang.org/protobuf v1.32.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gorm.io/driver/postgres v1.5.4 // indirect diff --git a/go.sum b/go.sum index 8cf87e22..81513385 100644 --- a/go.sum +++ b/go.sum @@ -134,8 +134,8 @@ github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4Nij github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y= -github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= -github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo= github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k= @@ -237,10 +237,10 @@ github.com/hashicorp/boundary/api v0.0.45 h1:N4ZIjsYf4cwVfcW5sv0WitWiFr8UyudjA/o github.com/hashicorp/boundary/api v0.0.45/go.mod h1:UrsSgLDpFmG6Eq+OIdpC8BSwbHwklsxL/ZCWklr0g5A= github.com/hashicorp/boundary/sdk v0.0.42 h1:iRDdnM6/6q9qLtBP7/cpq/FWQGf69TFSsBQFxgGK8Jw= github.com/hashicorp/boundary/sdk v0.0.42/go.mod h1:QJhm4rky2YMtk9DS5Rj8QJhDKB/6zQbVrm3R0QgdYmQ= -github.com/hashicorp/cap v0.4.1 h1:LVYrTLbPV8W6DPwIm/zC/fbc4UTpCQ7nJhCAPshLuG4= -github.com/hashicorp/cap v0.4.1/go.mod h1:oOoohCNd2JAgfvLz2NpFJTZiZ6CqH9dW8dZ2js52lGA= -github.com/hashicorp/cap/ldap v0.0.0-20231012003312-273118a6e3b8 h1:IKSRg1IONHl9ouMwx/Un1OVwdnlqVGWXRjUEg6VQZuY= -github.com/hashicorp/cap/ldap v0.0.0-20231012003312-273118a6e3b8/go.mod h1:EywAf3IOjdKgFT/mK1YQhHcNXuwDvG4FOuLs1L1odh0= +github.com/hashicorp/cap v0.5.0 h1:YIlAYxdXXtx2IL1JDvP2OyEl55Ooi0yl573kSB9Orlw= +github.com/hashicorp/cap v0.5.0/go.mod h1:IAy00Er+ZFpMo+5x6B4bkO2HgpzgrkfsuDWMmHAuKUE= +github.com/hashicorp/cap/ldap v0.0.0-20240206183135-ed8f24513744 h1:6cU01ORaCUxRXRS0YYnmm4tMfAvFRjamhjgMrgmoWZs= +github.com/hashicorp/cap/ldap v0.0.0-20240206183135-ed8f24513744/go.mod h1:SOJuo/OFD0qKQ7d8qOy+a2B9Tw0/97oHIo+qXxs7HZE= github.com/hashicorp/cli v1.1.6 h1:CMOV+/LJfL1tXCOKrgAX0uRKnzjj/mpmqNXloRSy2K8= github.com/hashicorp/cli v1.1.6/go.mod h1:MPon5QYlgjjo0BSoAiN0ESeT5fRzDjVRp+uioJ0piz4= github.com/hashicorp/dbassert v0.0.0-20231012105025-1bc1bd88e22b h1:rvg3eCucdlpfsnbYXM1Ett4f0reNmIuCruyJUF4iw/0= @@ -275,8 +275,8 @@ github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20231124110655-4315424 github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20231124110655-4315424d22d0/go.mod h1:EHsJEWJkTl6vZ9OUCpfvH8aaLtkmjYdtIpQgzYvw4vI= github.com/hashicorp/go-kms-wrapping/plugin/v2 v2.0.5 h1:jrnDfQm2hCQ0/hEselgqzV4fK16gpZoY0OWGZpVPNHM= github.com/hashicorp/go-kms-wrapping/plugin/v2 v2.0.5/go.mod h1:psh1qKep5ukvuNobFY/hCybuudlkkACpmazOsCgX5Rg= -github.com/hashicorp/go-kms-wrapping/v2 v2.0.15 h1:f3+/VbanXOmVAaDBKwRiVmeL7EX340a4YmaTItMF4Xs= -github.com/hashicorp/go-kms-wrapping/v2 v2.0.15/go.mod h1:0dWtzl2ilqKpavgM3id/kFK9L3tjo6fS4OhbVPSYpnQ= +github.com/hashicorp/go-kms-wrapping/v2 v2.0.16 h1:WZeXfD26QMWYC35at25KgE021SF9L3u9UMHK8fJAdV0= +github.com/hashicorp/go-kms-wrapping/v2 v2.0.16/go.mod h1:ZiKZctjRTLEppuRwrttWkp71VYMbTTCkazK4xT7U/NQ= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= @@ -346,12 +346,12 @@ github.com/hashicorp/terraform-json v0.21.0 h1:9NQxbLNqPbEMze+S6+YluEdXgJmhQykRy github.com/hashicorp/terraform-json v0.21.0/go.mod h1:qdeBs11ovMzo5puhrRibdD6d2Dq6TyE/28JiU4tIQxk= github.com/hashicorp/terraform-plugin-docs v0.18.0 h1:2bINhzXc+yDeAcafurshCrIjtdu1XHn9zZ3ISuEhgpk= github.com/hashicorp/terraform-plugin-docs v0.18.0/go.mod h1:iIUfaJpdUmpi+rI42Kgq+63jAjI8aZVTyxp3Bvk9Hg8= -github.com/hashicorp/terraform-plugin-go v0.20.0 h1:oqvoUlL+2EUbKNsJbIt3zqqZ7wi6lzn4ufkn/UA51xQ= -github.com/hashicorp/terraform-plugin-go v0.20.0/go.mod h1:Rr8LBdMlY53a3Z/HpP+ZU3/xCDqtKNCkeI9qOyT10QE= +github.com/hashicorp/terraform-plugin-go v0.21.0 h1:VSjdVQYNDKR0l2pi3vsFK1PdMQrw6vGOshJXMNFeVc0= +github.com/hashicorp/terraform-plugin-go v0.21.0/go.mod h1:piJp8UmO1uupCvC9/H74l2C6IyKG0rW4FDedIpwW5RQ= github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.31.0 h1:Bl3e2ei2j/Z3Hc2HIS15Gal2KMKyLAZ2om1HCEvK6es= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.31.0/go.mod h1:i2C41tszDjiWfziPQDL5R/f3Zp0gahXe5No/MIO9rCE= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.32.0 h1:7xdO9aOXVmhvMxNAq8UloyyqW0EEzyAY37llSTHJgjo= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.32.0/go.mod h1:LxQzs7AQl/5JE1IGFd6LX8E4A0InRJ/7s245gOmsejA= github.com/hashicorp/terraform-registry-address v0.2.3 h1:2TAiKJ1A3MAkZlH1YI/aTVcLZRu7JseiXNRHbOAyoTI= github.com/hashicorp/terraform-registry-address v0.2.3/go.mod h1:lFHA76T8jfQteVfT7caREqguFrW3c4MFSPhZB7HHgUM= github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ= @@ -669,8 +669,8 @@ github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUei github.com/yuin/goldmark-meta v1.1.0/go.mod h1:U4spWENafuA7Zyg+Lj5RqK/MF+ovMYtBvXi1lBb2VP0= github.com/zalando/go-keyring v0.2.3 h1:v9CUu9phlABObO4LPWycf+zwMG7nlbb3t/B5wa97yms= github.com/zalando/go-keyring v0.2.3/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk= -github.com/zclconf/go-cty v1.14.1 h1:t9fyA35fwjjUMcmL5hLER+e/rEPqrbCK1/OSE4SI9KA= -github.com/zclconf/go-cty v1.14.1/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= +github.com/zclconf/go-cty v1.14.2 h1:kTG7lqmBou0Zkx35r6HJHUQTvaRPr5bIAf3AoHS0izI= +github.com/zclconf/go-cty v1.14.2/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -715,8 +715,8 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= @@ -757,12 +757,12 @@ golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= -golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY= -golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= +golang.org/x/oauth2 v0.14.0 h1:P0Vrf/2538nmC0H+pEQ3MNFRRnVR7RlqyVw+bvm26z0= +golang.org/x/oauth2 v0.14.0/go.mod h1:lAtNWgaWfL4cm7j2OV8TxGi9Qb7ECORx8DktCY74OwM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -771,8 +771,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -808,8 +808,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -818,8 +818,8 @@ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= -golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= -golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -855,8 +855,8 @@ golang.org/x/tools v0.0.0-20210101214203-2dba1e4ea05c/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= -golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= +golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -873,12 +873,12 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98 google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405 h1:I6WNifs6pF9tNdSob2W24JtyxIYjzFB9qDlpUC76q+U= -google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405/go.mod h1:3WDQMjmJk36UQhjQ89emUzb1mdaHcPeeAh4SCBKznB4= -google.golang.org/genproto/googleapis/api v0.0.0-20231030173426-d783a09b4405 h1:HJMDndgxest5n2y77fnErkM62iUsptE/H8p0dC2Huo4= -google.golang.org/genproto/googleapis/api v0.0.0-20231030173426-d783a09b4405/go.mod h1:oT32Z4o8Zv2xPQTg0pbVaPr0MPOH6f14RgXt7zfIpwg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 h1:AB/lmRny7e2pLhFEYIbl5qkDAUt2h0ZRO4wGPhZf+ik= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405/go.mod h1:67X1fPuzjcrkymZzZV1vvkFeTn2Rvc6lYF9MYFGCcwE= +google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ= +google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY= +google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo= +google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= @@ -889,8 +889,8 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= -google.golang.org/grpc v1.60.0 h1:6FQAR0kM31P6MRdeluor2w2gPaS4SVNrD/DNTxrQ15k= -google.golang.org/grpc v1.60.0/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= +google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0= +google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -902,8 +902,8 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -948,7 +948,7 @@ modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM= modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk= mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48= -mvdan.cc/gofumpt v0.5.0 h1:0EQ+Z56k8tXjj/6TQD25BFNKQXpCvT0rnansIc7Ug5E= -mvdan.cc/gofumpt v0.5.0/go.mod h1:HBeVDtMKRZpXyxFciAirzdKklDlGu8aAy1wEbH5Y9js= +mvdan.cc/gofumpt v0.6.0 h1:G3QvahNDmpD+Aek/bNOLrFR2XC6ZAdo62dZu65gmwGo= +mvdan.cc/gofumpt v0.6.0/go.mod h1:4L0wf+kgIPZtcCWXynNS2e6bhmj73umwnuXSZarixzA= nhooyr.io/websocket v1.8.10 h1:mv4p+MnGrLDcPlBoWsvPP7XCzTYMXP9F9eIGoKbgx7Q= nhooyr.io/websocket v1.8.10/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= diff --git a/internal/provider/data_source_scope_test.go b/internal/provider/data_source_scope_test.go index 6f93de2f..e9c5fbfa 100644 --- a/internal/provider/data_source_scope_test.go +++ b/internal/provider/data_source_scope_test.go @@ -42,7 +42,7 @@ resource "boundary_scope" "project" { resource "boundary_role" "org_admin" { scope_id = "global" - grant_scope_id = boundary_scope.org.id + grant_scope_ids = [boundary_scope.org.id] grant_strings = ["ids=*;type=*;actions=*"] principal_ids = ["u_auth"] } diff --git a/internal/provider/resource_account_ldap_test.go b/internal/provider/resource_account_ldap_test.go index 968076c0..a4aba85b 100644 --- a/internal/provider/resource_account_ldap_test.go +++ b/internal/provider/resource_account_ldap_test.go @@ -92,7 +92,7 @@ resource "boundary_user" "alice" { resource "boundary_role" "ldap_principal" { scope_id = boundary_scope.global.id - grant_scope_id = boundary_scope.global.id + grant_scope_ids = [boundary_scope.global.id] grant_strings = ["ids=*;type=*;actions=*"] principal_ids = [boundary_user.alice.id] }` diff --git a/internal/provider/resource_role.go b/internal/provider/resource_role.go index cf73d860..af3e11ec 100644 --- a/internal/provider/resource_role.go +++ b/internal/provider/resource_role.go @@ -19,9 +19,10 @@ import ( ) const ( - roleGrantScopeIdKey = "grant_scope_id" - rolePrincipalIdsKey = "principal_ids" - roleGrantStringsKey = "grant_strings" + roleGrantScopeIdKey = "grant_scope_id" + roleGrantScopeIdsKey = "grant_scope_ids" + rolePrincipalIdsKey = "principal_ids" + roleGrantStringsKey = "grant_strings" ) func resourceRole() *schema.Resource { @@ -71,9 +72,18 @@ func resourceRole() *schema.Resource { Elem: &schema.Schema{Type: schema.TypeString}, }, roleGrantScopeIdKey: { - Type: schema.TypeString, - Optional: true, - Computed: true, + Description: "For Boundary 0.15+, use `grant_scope_ids` instead. The scope for which the grants in the role should apply.", + Type: schema.TypeString, + Optional: true, + Computed: true, + Deprecated: "In Boundary 0.15+, please use `grant_scope_ids` instead. This field will be removed in a future release.", + }, + roleGrantScopeIdsKey: { + Description: `A list of scopes for which the grants in this role should apply, which can include the special values "this", "children", or "descendants"`, + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Computed: true, }, }, } @@ -95,9 +105,13 @@ func setFromRoleResponseMap(d *schema.ResourceData, raw map[string]interface{}) if err := d.Set(roleGrantStringsKey, raw["grant_strings"]); err != nil { return err } + if err := d.Set(roleGrantScopeIdsKey, raw["grant_scope_ids"]); err != nil { + return err + } if err := d.Set(roleGrantScopeIdKey, raw["grant_scope_id"]); err != nil { return err } + d.SetId(raw["id"].(string)) return nil } @@ -126,11 +140,19 @@ func resourceRoleCreate(ctx context.Context, d *schema.ResourceData, meta interf opts = append(opts, roles.WithDescription(descStr)) } - grantScopeIdVal, ok := d.GetOk(roleGrantScopeIdKey) - if ok { + grantScopeIdVal := d.Get(roleGrantScopeIdKey) + if grantScopeIdVal != "" { grantScopeIdStr := grantScopeIdVal.(string) opts = append(opts, roles.WithGrantScopeId(grantScopeIdStr)) } + var grantScopeIds []string + if grantScopeIdsVal, ok := d.GetOk(roleGrantScopeIdsKey); ok { + list := grantScopeIdsVal.(*schema.Set).List() + grantScopeIds = make([]string, 0, len(list)) + for _, i := range list { + grantScopeIds = append(grantScopeIds, i.(string)) + } + } var principalIds []string if principalIdsVal, ok := d.GetOk(rolePrincipalIdsKey); ok { @@ -200,6 +222,18 @@ func resourceRoleCreate(ctx context.Context, d *schema.ResourceData, meta interf } } + if grantScopeIds != nil { + tsgr, err := rc.SetGrantScopes(ctx, tcr.Item.Id, 0, grantScopeIds, roles.WithAutomaticVersioning(true)) + switch { + case err != nil: + diags = append(diags, diag.Diagnostic{Severity: diag.Error, Summary: "error setting grant scopes", Detail: err.Error()}) + case tsgr == nil: + diags = append(diags, diag.Diagnostic{Severity: diag.Error, Summary: "nil role after setting grant scope ids"}) + default: + apiResponse = tsgr.GetResponse().Map + } + } + return diags } @@ -256,12 +290,17 @@ func resourceRoleUpdate(ctx context.Context, d *schema.ResourceData, meta interf var grantScopeId *string if d.HasChange(roleGrantScopeIdKey) { - opts = append(opts, roles.DefaultGrantScopeId()) - grantScopeIdVal, ok := d.GetOk(roleGrantScopeIdKey) - if ok { - grantScopeIdStr := grantScopeIdVal.(string) - grantScopeId = &grantScopeIdStr - opts = append(opts, roles.WithGrantScopeId(grantScopeIdStr)) + // If it is an update to use the multi-value grant_scope_ids, and this + // has changed to "", then don't set it on the API. Instead let it fall + // through and below it'll be updated to be "". + if !d.HasChange(roleGrantScopeIdsKey) { + opts = append(opts, roles.DefaultGrantScopeId()) + grantScopeIdVal, ok := d.GetOk(roleGrantScopeIdKey) + if ok { + grantScopeIdStr := grantScopeIdVal.(string) + grantScopeId = &grantScopeIdStr + opts = append(opts, roles.WithGrantScopeId(grantScopeIdStr)) + } } } @@ -333,6 +372,24 @@ func resourceRoleUpdate(ctx context.Context, d *schema.ResourceData, meta interf } } + if d.HasChange(roleGrantScopeIdsKey) { + var grantScopeIds []string + if grantScopeIdsVal, ok := d.GetOk(roleGrantScopeIdsKey); ok { + grantScopes := grantScopeIdsVal.(*schema.Set).List() + for _, grantScope := range grantScopes { + grantScopeIds = append(grantScopeIds, grantScope.(string)) + } + } + _, err := rc.SetGrantScopes(ctx, d.Id(), 0, grantScopeIds, roles.WithAutomaticVersioning(true)) + if err != nil { + diags = append(diags, diag.Diagnostic{Severity: diag.Error, Summary: "error setting grant scopes", Detail: err.Error()}) + } else { + if err := d.Set(roleGrantScopeIdsKey, grantScopeIds); err != nil { + return diag.FromErr(err) + } + } + } + return diags } diff --git a/internal/provider/resource_role_grant_scope_ids_upgrade_test.go b/internal/provider/resource_role_grant_scope_ids_upgrade_test.go new file mode 100644 index 00000000..03dafc90 --- /dev/null +++ b/internal/provider/resource_role_grant_scope_ids_upgrade_test.go @@ -0,0 +1,295 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package provider + +import ( + "context" + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/boundary/api/roles" + "github.com/hashicorp/boundary/testing/controller" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +var ( + orgRoleWithGrantScopes = ` + resource "boundary_role" "with_grant_scopes" { + name = "with_grant_scopes" + description = "with grant scopes" + principal_ids = [boundary_user.foo.id] + scope_id = boundary_scope.org1.id + depends_on = [boundary_role.org1_admin] + grant_scope_ids = ["this", "children", boundary_scope.proj1.id] + }` + + orgRoleWithGrantScopesUpdate = ` + resource "boundary_role" "with_grant_scopes" { + name = "with_grant_scopes_update" + description = "with grant scopes update" + principal_ids = [boundary_user.foo.id] + scope_id = boundary_scope.org1.id + depends_on = [boundary_role.org1_admin] + grant_scope_ids = ["this"] + }` + + orgRoleWithInvalidGrantScopesUpdate = ` + resource "boundary_role" "with_grant_scopes" { + name = "with_grant_scopes_update" + description = "with grant scopes update" + principal_ids = [boundary_user.foo.id] + scope_id = boundary_scope.org1.id + depends_on = [boundary_role.org1_admin] + grant_scope_ids = ["this", "children", "p_foobar1234"] + }` + + conversionOrgRoleWithGrantScopeId = ` +resource "boundary_role" "with_grant_scope_id" { + name = "grant scope id role" + scope_id = boundary_scope.org1.id + depends_on = [boundary_role.org1_admin] + grant_scope_id = boundary_scope.org1.id +}` + + conversionOrgRoleWithGrantScopeIdUpdate = ` +resource "boundary_role" "with_grant_scope_id" { + name = "grant scope id role" + scope_id = boundary_scope.org1.id + depends_on = [boundary_role.org1_admin] + grant_scope_id = boundary_scope.proj1.id +}` + + conversionOrgRoleWithGrantScopeIdsConversion = ` +resource "boundary_role" "with_grant_scope_id" { + name = "grant scope id role" + scope_id = boundary_scope.org1.id + depends_on = [boundary_role.org1_admin] + grant_scope_ids = ["this", boundary_scope.proj1.id] +}` + + conversionOrgRoleWithGrantScopeIdsUpdate = ` +resource "boundary_role" "with_grant_scope_id" { + name = "grant scope id role" + scope_id = boundary_scope.org1.id + depends_on = [boundary_role.org1_admin] + grant_scope_ids = ["this", "children"] +}` +) + +// TestAccRoleWithGrantScopes exercises creation and update with valid and +// invalid grant scopes +func TestAccRoleWithGrantScopes(t *testing.T) { + tc := controller.NewTestController(t, tcConfig...) + t.Cleanup(tc.Shutdown) + url := tc.ApiAddrs()[0] + + var provider *schema.Provider + resource.Test(t, resource.TestCase{ + IsUnitTest: true, + ProviderFactories: providerFactories(&provider), + CheckDestroy: testAccCheckRoleResourceDestroy(t, provider), + Steps: []resource.TestStep{ + { + // Create with valid grant scopes should create role + Config: testConfig(url, fooOrg, firstProjectFoo, fooUser, orgRoleWithGrantScopes), + Check: resource.ComposeTestCheckFunc( + testAccCheckScopeResourceExists(provider, "boundary_scope.proj1"), + testAccCheckRoleResourceExists(provider, "boundary_role.with_grant_scopes"), + testAccCheckUserResourceExists(provider, "boundary_user.foo"), + testAccCheckRoleResourceGrantScopesSet(provider, "boundary_role.with_grant_scopes", []string{"this", "children", "boundary_scope.proj1"}), + resource.TestCheckResourceAttr("boundary_role.with_grant_scopes", DescriptionKey, "with grant scopes"), + resource.TestCheckResourceAttr("boundary_role.with_grant_scopes", NameKey, "with_grant_scopes"), + ), + }, + importStep("boundary_role.with_grant_scopes"), + { + // Update with invalid grant scopes should update role but return empty plan + // since grant scopes were not set correctly. + Config: testConfig(url, fooOrg, firstProjectFoo, fooUser, orgRoleWithInvalidGrantScopesUpdate), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleResourceExists(provider, "boundary_role.with_grant_scopes"), + resource.TestCheckResourceAttr("boundary_role.with_grant_scopes", DescriptionKey, "with grant scopes update"), + resource.TestCheckResourceAttr("boundary_role.with_grant_scopes", NameKey, "with_grant_scopes_update"), + ), + ExpectError: regexp.MustCompile(`Unable to set grant scopes on role`), + }, + { + // Update again without invalid principal should produce empty plan + Config: testConfig(url, fooOrg, firstProjectFoo, fooUser, orgRoleWithGrantScopesUpdate), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleResourceExists(provider, "boundary_role.with_grant_scopes"), + testAccCheckRoleResourceGrantScopesSet(provider, "boundary_role.with_grant_scopes", []string{"this"}), + resource.TestCheckResourceAttr("boundary_role.with_grant_scopes", DescriptionKey, "with grant scopes update"), + resource.TestCheckResourceAttr("boundary_role.with_grant_scopes", NameKey, "with_grant_scopes_update"), + ), + }, + importStep("boundary_role.with_grant_scopes"), + }, + }) +} + +// TestAccRoleGrantScopeUpgrade exercises some specific upgrade scenarios around +// grant_scope_id -> grant_scope_ids +func TestAccRoleWithGrantScopesUpdate(t *testing.T) { + tc := controller.NewTestController(t, tcConfig...) + t.Cleanup(tc.Shutdown) + url := tc.ApiAddrs()[0] + + var provider *schema.Provider + resource.Test(t, resource.TestCase{ + IsUnitTest: true, + ProviderFactories: providerFactories(&provider), + CheckDestroy: testAccCheckRoleResourceDestroy(t, provider), + Steps: []resource.TestStep{ + { + // Create + Config: testConfig(url, fooOrg, firstProjectFoo, fooUser, conversionOrgRoleWithGrantScopeId), + Check: resource.ComposeTestCheckFunc( + testAccCheckScopeResourceExists(provider, "boundary_scope.org1"), + testAccCheckRoleResourceExists(provider, "boundary_role.with_grant_scope_id"), + testAccCheckUserResourceExists(provider, "boundary_user.foo"), + testAccCheckRoleResourceGrantScopesSet(provider, "boundary_role.with_grant_scope_id", []string{"boundary_scope.org1"}), + testAccCheckRoleResourceGrantScopeSet(provider, "boundary_role.with_grant_scope_id", "boundary_scope.org1"), + ), + }, + importStep("boundary_role.with_grant_scope_id"), + { + Config: testConfig(url, fooOrg, firstProjectFoo, fooUser, conversionOrgRoleWithGrantScopeIdUpdate), + Check: resource.ComposeTestCheckFunc( + testAccCheckScopeResourceExists(provider, "boundary_scope.org1"), + testAccCheckRoleResourceExists(provider, "boundary_role.with_grant_scope_id"), + testAccCheckUserResourceExists(provider, "boundary_user.foo"), + testAccCheckRoleResourceGrantScopesSet(provider, "boundary_role.with_grant_scope_id", []string{"boundary_scope.proj1"}), + testAccCheckRoleResourceGrantScopeSet(provider, "boundary_role.with_grant_scope_id", "boundary_scope.proj1"), + ), + }, + importStep("boundary_role.with_grant_scope_id"), + { + Config: testConfig(url, fooOrg, firstProjectFoo, fooUser, conversionOrgRoleWithGrantScopeIdsConversion), + Check: resource.ComposeTestCheckFunc( + testAccCheckScopeResourceExists(provider, "boundary_scope.org1"), + testAccCheckRoleResourceExists(provider, "boundary_role.with_grant_scope_id"), + testAccCheckUserResourceExists(provider, "boundary_user.foo"), + testAccCheckRoleResourceGrantScopesSet(provider, "boundary_role.with_grant_scope_id", []string{"this", "boundary_scope.proj1"}), + testAccCheckRoleResourceGrantScopeSet(provider, "boundary_role.with_grant_scope_id", ""), + ), + }, + importStep("boundary_role.with_grant_scope_id"), + { + Config: testConfig(url, fooOrg, firstProjectFoo, fooUser, conversionOrgRoleWithGrantScopeIdsUpdate), + Check: resource.ComposeTestCheckFunc( + testAccCheckScopeResourceExists(provider, "boundary_scope.org1"), + testAccCheckRoleResourceExists(provider, "boundary_role.with_grant_scope_id"), + testAccCheckUserResourceExists(provider, "boundary_user.foo"), + testAccCheckRoleResourceGrantScopesSet(provider, "boundary_role.with_grant_scope_id", []string{"this", "children"}), + testAccCheckRoleResourceGrantScopeSet(provider, "boundary_role.with_grant_scope_id", ""), + ), + }, + importStep("boundary_role.with_grant_scope_id"), + }, + }) +} + +func testAccCheckRoleResourceGrantScopeSet(testProvider *schema.Provider, name string, grantScope string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("role resource not found: %s", name) + } + + roleId := rs.Primary.ID + + scopeId := grantScope + if grantScope != "children" && grantScope != "this" && grantScope != "descendants" && grantScope != "" { + scope, ok := s.RootModule().Resources[grantScope] + if !ok { + return fmt.Errorf("scope resource not found: %s", grantScope) + } + + scopeId = scope.Primary.ID + if scopeId == "" { + return fmt.Errorf("role resource ID is not set") + } + } + + md := testProvider.Meta().(*metaData) + rolesClient := roles.NewClient(md.client) + + rr, err := rolesClient.Read(context.Background(), roleId) + if err != nil { + return fmt.Errorf("Got an error when reading role %q: %v", roleId, err) + } + + if rr.Item.GrantScopeId != scopeId { + return fmt.Errorf("grant scope id in state is %s but in boundary is %s", scopeId, rr.Item.GrantScopeId) + } + + return nil + } +} + +func testAccCheckRoleResourceGrantScopesSet(testProvider *schema.Provider, name string, grantScopeIds []string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("role resource not found: %s", name) + } + + id := rs.Primary.ID + if id == "" { + return fmt.Errorf("role resource ID is not set") + } + + scopeIds := []string{} + for _, grantScope := range grantScopeIds { + if grantScope == "children" || grantScope == "this" || grantScope == "descendants" { + scopeIds = append(scopeIds, grantScope) + continue + } + + scope, ok := s.RootModule().Resources[grantScope] + if !ok { + return fmt.Errorf("scope resource not found: %s", grantScope) + } + + scopeId := scope.Primary.ID + if scopeId == "" { + return fmt.Errorf("scope resource ID not set") + } + + scopeIds = append(scopeIds, scopeId) + } + + md := testProvider.Meta().(*metaData) + rolesClient := roles.NewClient(md.client) + + rr, err := rolesClient.Read(context.Background(), id) + if err != nil { + return fmt.Errorf("Got an error when reading role %q: %v", id, err) + } + + // for every grant scope set on the role in the state, ensure each role + // in boundary has the same setings + if len(rr.Item.GrantScopeIds) == 0 { + return fmt.Errorf("no grant scope ids found in boundary") + } + + for _, stateGrantScopeId := range rr.Item.GrantScopeIds { + ok := false + for _, gotScopeId := range scopeIds { + if gotScopeId == stateGrantScopeId { + ok = true + } + } + if !ok { + return fmt.Errorf("grant scope id in state not set in boundary: %s", stateGrantScopeId) + } + } + + return nil + } +} diff --git a/internal/provider/resource_role_test.go b/internal/provider/resource_role_test.go index d4ab6f43..947ab867 100644 --- a/internal/provider/resource_role_test.go +++ b/internal/provider/resource_role_test.go @@ -189,7 +189,7 @@ resource "boundary_role" "with_grants" { func TestAccRoleToOrgToProject(t *testing.T) { tc := controller.NewTestController(t, tcConfig...) - defer tc.Shutdown() + t.Cleanup(tc.Shutdown) url := tc.ApiAddrs()[0] var provider *schema.Provider @@ -243,7 +243,7 @@ func TestAccRoleToOrgToProject(t *testing.T) { func TestAccRoleWithGrants(t *testing.T) { tc := controller.NewTestController(t, tcConfig...) - defer tc.Shutdown() + t.Cleanup(tc.Shutdown) url := tc.ApiAddrs()[0] var provider *schema.Provider @@ -302,7 +302,7 @@ func TestAccRoleWithGrants(t *testing.T) { func TestAccRoleWithPrincipals(t *testing.T) { tc := controller.NewTestController(t, tcConfig...) - defer tc.Shutdown() + t.Cleanup(tc.Shutdown) url := tc.ApiAddrs()[0] var provider *schema.Provider @@ -365,7 +365,7 @@ func TestAccRoleWithPrincipals(t *testing.T) { func TestAccRoleWithGroups(t *testing.T) { tc := controller.NewTestController(t, tcConfig...) - defer tc.Shutdown() + t.Cleanup(tc.Shutdown) url := tc.ApiAddrs()[0] var provider *schema.Provider diff --git a/internal/provider/resource_scope_test.go b/internal/provider/resource_scope_test.go index a7a8b8a6..98de993e 100644 --- a/internal/provider/resource_scope_test.go +++ b/internal/provider/resource_scope_test.go @@ -33,7 +33,7 @@ resource "boundary_scope" "org1" { resource "boundary_role" "org1_admin" { scope_id = boundary_scope.global.id - grant_scope_id = boundary_scope.org1.id + grant_scope_ids = [boundary_scope.org1.id] grant_strings = ["ids=*;type=*;actions=*"] principal_ids = ["u_auth"] } @@ -49,7 +49,7 @@ resource "boundary_scope" "proj1" { resource "boundary_role" "proj1_admin" { scope_id = boundary_scope.org1.id - grant_scope_id = boundary_scope.proj1.id + grant_scope_ids = [boundary_scope.proj1.id] grant_strings = ["ids=*;type=*;actions=*"] principal_ids = ["u_auth"] } @@ -65,7 +65,7 @@ resource "boundary_scope" "proj1" { resource "boundary_role" "proj1_admin" { scope_id = boundary_scope.org1.id - grant_scope_id = boundary_scope.proj1.id + grant_scope_ids = [boundary_scope.proj1.id] grant_strings = ["ids=*;type=*;actions=*"] principal_ids = ["u_auth"] } @@ -80,7 +80,7 @@ resource "boundary_scope" "proj2" { resource "boundary_role" "proj2_admin" { scope_id = boundary_scope.org1.id - grant_scope_id = boundary_scope.proj2.id + grant_scope_ids = [boundary_scope.proj2.id] grant_strings = ["ids=*;type=*;actions=*"] principal_ids = ["u_auth"] }