Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86ec18adc3 | ||
|
|
a96d8b9373 | ||
|
|
d8471243e3 | ||
|
|
f3819aa37a | ||
|
|
24a9c0b090 | ||
|
|
3aff7f5aba | ||
|
|
7197294c2c | ||
|
|
fe1b80eadc | ||
|
|
933fc23905 | ||
|
|
073c3e1904 | ||
|
|
22b62c202f |
No files matched your search
@@ -1,17 +1,63 @@
|
||||
module github.com/hashicorp/boundary-plugin-host-azure
|
||||
|
||||
go 1.16
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/Azure/azure-sdk-for-go v58.0.0+incompatible
|
||||
github.com/Azure/go-autorest/autorest v0.11.21
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.8
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
|
||||
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
|
||||
github.com/hashicorp/boundary/sdk v0.0.12-0.20211028194642-6eba8a478703
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1
|
||||
github.com/Azure/azure-sdk-for-go v59.2.0+incompatible
|
||||
github.com/Azure/go-autorest/autorest v0.11.22
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.9
|
||||
github.com/hashicorp/boundary/sdk v0.0.12-0.20211112235128-46a1edcb3b7b
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.2
|
||||
github.com/kr/pretty v0.3.0
|
||||
github.com/manicminer/hamilton v0.32.0
|
||||
github.com/manicminer/hamilton v0.33.0
|
||||
github.com/mitchellh/mapstructure v1.4.2
|
||||
github.com/stretchr/testify v1.7.0
|
||||
google.golang.org/protobuf v1.27.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.14 // indirect
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 // indirect
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
|
||||
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
|
||||
github.com/Azure/go-autorest/logger v0.2.1 // indirect
|
||||
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dimchansky/utfbom v1.1.1 // indirect
|
||||
github.com/fatih/color v1.12.0 // indirect
|
||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/eventlogger v0.1.1-0.20211106154408-4ff8da3a890c // indirect
|
||||
github.com/hashicorp/eventlogger/filters/encrypt v0.1.6-0.20211027211326-5db60a48f239 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-hclog v0.16.2 // indirect
|
||||
github.com/hashicorp/go-kms-wrapping v0.6.6 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.0 // indirect
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.1 // indirect
|
||||
github.com/hashicorp/go-sockaddr v1.0.2 // indirect
|
||||
github.com/hashicorp/go-uuid v1.0.2 // indirect
|
||||
github.com/hashicorp/go-version v1.3.0 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.8 // indirect
|
||||
github.com/mattn/go-isatty v0.0.13 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/pointerstructure v1.2.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.6.2 // indirect
|
||||
github.com/ryanuber/go-glob v1.0.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c // indirect
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
|
||||
golang.org/x/text v0.3.6 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6 // indirect
|
||||
google.golang.org/grpc v1.40.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
)
|
||||
@@ -34,28 +34,28 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/Azure/azure-sdk-for-go v36.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/azure-sdk-for-go v58.0.0+incompatible h1:Cw16jiP4dI+CK761aq44ol4RV5dUiIIXky1+EKpoiVM=
|
||||
github.com/Azure/azure-sdk-for-go v58.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/azure-sdk-for-go v59.2.0+incompatible h1:mbxiZy1K820hQ+dI+YIO/+a0wQDYqOu18BAGe4lXjVk=
|
||||
github.com/Azure/azure-sdk-for-go v59.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
|
||||
github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8GNUx2nRB378IPt/1p0=
|
||||
github.com/Azure/go-autorest/autorest v0.10.1/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
|
||||
github.com/Azure/go-autorest/autorest v0.11.17/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw=
|
||||
github.com/Azure/go-autorest/autorest v0.11.21 h1:w77zY/9RnUAWcIQyDC0Fc89mCvwftR8F+zsR/OH6enk=
|
||||
github.com/Azure/go-autorest/autorest v0.11.21/go.mod h1:Do/yuMSW/13ayUkcVREpsMHGG+MvV81uzSCFgYPj4tM=
|
||||
github.com/Azure/go-autorest/autorest v0.11.19/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA=
|
||||
github.com/Azure/go-autorest/autorest v0.11.22 h1:bXiQwDjrRmBQOE67bwlvUKAC1EU1yZTPQ38c+bstZws=
|
||||
github.com/Azure/go-autorest/autorest v0.11.22/go.mod h1:BAWYUWGPEtKPzjVkp0Q6an0MJcJDsoh5Z1BFAEFs4Xs=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.8.1/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.11/go.mod h1:nBKAnTomx8gDtl+3ZCJv2v0KACFHWTB2drffI1B68Pk=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.14 h1:G8hexQdV5D4khOXrWG2YuLCFKhWYmWD8bHYaXN5ophk=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.14/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.4.2/go.mod h1:90gmfKdlmKgfjUpnCEpOJzsUEjrWDSLwHIG73tSXddM=
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 h1:TzPg6B6fTZ0G1zBf3T54aI7p3cAT6u//TOXGPmFMOXg=
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.8/go.mod h1:kxyKZTSfKh8OVFWPAgOgQ/frrJgeYQJPyR5fLFmXko4=
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.9 h1:Y2CgdzitFDsdMwYMzf9LIZWrrTFysqbRc7b94XVVJ78=
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.9/go.mod h1:hg3/1yw0Bq87O3KvvnJoAh34/0zbP7SFizX/qN5JvjU=
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.3.1/go.mod h1:ZG5p860J94/0kI9mNJVoIoLgXcirM2gF5i2kWloofxw=
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 h1:dMOmEJfkLKW/7JsokJqkyoYSgmR08hi9KrhjZb+JALY=
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.4.2/go.mod h1:7qkJkT+j6b+hIpzMOwPChJhTqS8VbsqqgULzMNRugoM=
|
||||
@@ -75,7 +75,6 @@ github.com/Azure/go-autorest/autorest/validation v0.2.0/go.mod h1:3EEqHnBxQGHXRY
|
||||
github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac=
|
||||
github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E=
|
||||
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
||||
github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
|
||||
github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg=
|
||||
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
|
||||
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
||||
@@ -166,7 +165,6 @@ github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGE
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
|
||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||
github.com/frankban/quicktest v1.10.0 h1:Gfh+GAJZOAoKZsIZeZbdn2JF10kN1XHNvjsvQK8gVkE=
|
||||
github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
@@ -253,19 +251,19 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
|
||||
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=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/hashicorp/boundary/sdk v0.0.12-0.20211028194642-6eba8a478703 h1:i7VpfFTZSWN0TQ5SHlrhe4stSPrfq+4bIu915oB7O54=
|
||||
github.com/hashicorp/boundary/sdk v0.0.12-0.20211028194642-6eba8a478703/go.mod h1:rgZ7YvzX8AM1Vd681nAb+MpSApWNmUUnbyycpVWlylk=
|
||||
github.com/hashicorp/boundary/sdk v0.0.12-0.20211112235128-46a1edcb3b7b h1:qURjJaF3/TUbDcPCUAg9PG9a95NCgPrrcrOsurBJ7Rc=
|
||||
github.com/hashicorp/boundary/sdk v0.0.12-0.20211112235128-46a1edcb3b7b/go.mod h1:Krt7KLGTnDPQTaVlgSygV5SeqWDrOfbYyVBmB0KILD4=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/eventlogger v0.1.0 h1:S6xc4gZVzewuDUP4R4Ngko419h/CGDuV/b4ADL3XLik=
|
||||
github.com/hashicorp/eventlogger v0.1.0/go.mod h1:a3IXf1aEJfpCPzseTOrwKj4fVW/Qn3oEmpQeaIznzH0=
|
||||
github.com/hashicorp/eventlogger/filters/encrypt v0.1.5 h1:kNkH4G6zzWlZSoI1I+B/ud4chVKTPL516C6jB7dRdlE=
|
||||
github.com/hashicorp/eventlogger/filters/encrypt v0.1.5/go.mod h1:8rcez7Kw1zanB0/074qnOuGu7zxmNh9Xr2ZI+K4xVIA=
|
||||
github.com/hashicorp/eventlogger v0.1.1-0.20211106154408-4ff8da3a890c h1:u1Zq+LfTXaagmJCHK3XH/fi7pk7oRY933VjGNG7/Ynw=
|
||||
github.com/hashicorp/eventlogger v0.1.1-0.20211106154408-4ff8da3a890c/go.mod h1:NaXU8p/pl5a2RX/N0/yncinT3Iw5CLkbF4JRxAVnk3c=
|
||||
github.com/hashicorp/eventlogger/filters/encrypt v0.1.6-0.20211027211326-5db60a48f239 h1:Yh9tY0lige+y0trmjQeT9NRDo6+YvtNAzbmUNOsIUzI=
|
||||
github.com/hashicorp/eventlogger/filters/encrypt v0.1.6-0.20211027211326-5db60a48f239/go.mod h1:8rcez7Kw1zanB0/074qnOuGu7zxmNh9Xr2ZI+K4xVIA=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
@@ -305,8 +303,9 @@ github.com/hashicorp/go-secure-stdlib/configutil v0.1.2 h1:ai0P91rxlyGWkYUH/zFr7
|
||||
github.com/hashicorp/go-secure-stdlib/configutil v0.1.2/go.mod h1:EN1DJMjv9y5e/uRAuP2WPibAox5KHEiq2BjZl00aNPQ=
|
||||
github.com/hashicorp/go-secure-stdlib/listenerutil v0.1.1 h1:HlTofNQN49DTzPCMVDppp+AVxMNVWaHUElpUYC0TRRE=
|
||||
github.com/hashicorp/go-secure-stdlib/listenerutil v0.1.1/go.mod h1:irE9ILwhE8VooWdhHhBy0qXzOMRlHKoWoMlr31mAFCE=
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1 h1:78ki3QBevHwYrVxnyVeaEz+7WtifHhauYF23es/0KlI=
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8=
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.2 h1:Tz6v3Jb2DRnDCfifRSjYKG0m8dLdNq6bcDkB41en7nw=
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.2/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8=
|
||||
github.com/hashicorp/go-secure-stdlib/reloadutil v0.1.1 h1:SMGUnbpAcat8rIKHkBPjfv81yC46a8eCNZ2hsR2l1EI=
|
||||
github.com/hashicorp/go-secure-stdlib/reloadutil v0.1.1/go.mod h1:Ch/bf00Qnx77MZd49JRgHYqHQjtEmTgGU2faufpVZb0=
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.1 h1:nd0HIW15E6FG1MsnArYaHfuw9C2zgzM8LxkG5Ty/788=
|
||||
@@ -354,7 +353,6 @@ github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
@@ -371,8 +369,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/manicminer/hamilton v0.32.0 h1:awVHVXOLp9P5QbWaOFqneuWjAvzAk3BYtTEX6AeWB5k=
|
||||
github.com/manicminer/hamilton v0.32.0/go.mod h1:QryxpD/4+cdKuXNi0UjLDvgxYdP0LLmYz7dYU7DAX4U=
|
||||
github.com/manicminer/hamilton v0.33.0 h1:G58jBVHg2qsJqb5cPJ3tKzWj3JTf8gW3r5+4RmnEa0E=
|
||||
github.com/manicminer/hamilton v0.33.0/go.mod h1:QryxpD/4+cdKuXNi0UjLDvgxYdP0LLmYz7dYU7DAX4U=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
@@ -398,8 +396,9 @@ github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eI
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
|
||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.4.2 h1:6h7AQ0yhTcIsmFmnAwQls75jp2Gzs4iB8W7pjMO+rqo=
|
||||
github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A=
|
||||
github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4=
|
||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
@@ -470,10 +469,8 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/assertions v1.0.0 h1:UVQPSSmc3qtTi+zPPkCXvZX9VvW/xT/NsRvKfwY81a8=
|
||||
github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
@@ -482,7 +479,6 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
@@ -525,8 +521,9 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -559,7 +556,6 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
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=
|
||||
@@ -849,7 +845,6 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
|
||||
+118
-146
@@ -9,196 +9,164 @@ import (
|
||||
"github.com/Azure/go-autorest/autorest"
|
||||
"github.com/Azure/go-autorest/autorest/azure/auth"
|
||||
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs"
|
||||
pb "github.com/hashicorp/boundary/sdk/pbs/plugin"
|
||||
hauth "github.com/manicminer/hamilton/auth"
|
||||
"github.com/manicminer/hamilton/environments"
|
||||
"github.com/manicminer/hamilton/msgraph"
|
||||
"github.com/manicminer/hamilton/odata"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
type WrappedHamiltonConfig struct {
|
||||
hauth.Config
|
||||
type AuthorizationInfo struct {
|
||||
HamiltonConfig hauth.Config
|
||||
AuthParams AuthParams
|
||||
}
|
||||
|
||||
type Attributes struct {
|
||||
SubscriptionId string `mapstructure:"subscription_id"`
|
||||
ClientId string `mapstructure:"client_id"`
|
||||
TenantId string `mapstructure:"tenant_id"`
|
||||
DisableCredentialRotation bool `mapstructure:"disable_credential_rotation"`
|
||||
}
|
||||
|
||||
type SecretData struct {
|
||||
SecretValue string `mapstructure:"secret_value"`
|
||||
SecretId string `mapstructure:"secret_id"`
|
||||
CredsLastRotatedTime string `mapstructure:"creds_last_rotated_time"`
|
||||
}
|
||||
|
||||
type AuthParams struct {
|
||||
SubscriptionId string
|
||||
ClientId string
|
||||
ClientObjectId string
|
||||
TenantId string
|
||||
SecretValue string
|
||||
SecretId string
|
||||
CredsLastRotatedTime time.Time
|
||||
}
|
||||
|
||||
func authParams(hc *hostcatalogs.HostCatalog, secrets *structpb.Struct) (map[string]string, error) {
|
||||
if hc == nil {
|
||||
func getAuthorizationInfo(hc *hostcatalogs.HostCatalog) (*AuthorizationInfo, error) {
|
||||
switch {
|
||||
case hc == nil:
|
||||
return nil, errors.New("host catalog is nil")
|
||||
}
|
||||
if hc.GetAttributes() == nil {
|
||||
case hc.GetAttributes() == nil:
|
||||
return nil, errors.New("host catalog attributes is nil")
|
||||
}
|
||||
hcFields := hc.GetAttributes().GetFields()
|
||||
if hcFields == nil {
|
||||
return nil, errors.New("host catalog attributes fields is nil")
|
||||
case hc.GetSecrets() == nil:
|
||||
return nil, errors.New("host catalog secret data is nil")
|
||||
}
|
||||
|
||||
if secrets == nil {
|
||||
return nil, errors.New("secrets is nil")
|
||||
hcFields := hc.GetAttributes().AsMap()
|
||||
var attrs Attributes
|
||||
if err := mapstructure.Decode(hcFields, &attrs); err != nil {
|
||||
return nil, fmt.Errorf("error decoding host catalog attribute fields: %w", err)
|
||||
}
|
||||
pdFields := secrets.GetFields()
|
||||
if pdFields == nil {
|
||||
return nil, errors.New("secrets data fields is nil")
|
||||
if attrs.SubscriptionId == "" {
|
||||
return nil, errors.New("subscription id is empty")
|
||||
}
|
||||
if attrs.TenantId == "" {
|
||||
return nil, errors.New("tenant id is empty")
|
||||
}
|
||||
|
||||
var subscriptionId string
|
||||
if hcFields[constSubscriptionId] == nil {
|
||||
return nil, errors.New("subscription id not provided in incoming data")
|
||||
pdFields := hc.GetSecrets().AsMap()
|
||||
var secrets SecretData
|
||||
if err := mapstructure.Decode(pdFields, &secrets); err != nil {
|
||||
return nil, fmt.Errorf("error decoding host catalog persisted data fields: %w", err)
|
||||
}
|
||||
if subscriptionId = hcFields[constSubscriptionId].GetStringValue(); subscriptionId == "" {
|
||||
return nil, errors.New("subscription id could not be read as a string value")
|
||||
if secrets.SecretValue == "" {
|
||||
return nil, errors.New("secret value is empty")
|
||||
}
|
||||
|
||||
ret := map[string]string{
|
||||
constSubscriptionId: subscriptionId,
|
||||
}
|
||||
|
||||
clientId := hcFields[constClientId]
|
||||
secretValue := pdFields[constSecretValue]
|
||||
secretId := pdFields[constSecretId]
|
||||
tenantId := hcFields[constTenantId]
|
||||
|
||||
switch {
|
||||
case clientId != nil && clientId.GetStringValue() != "" &&
|
||||
secretValue != nil && secretValue.GetStringValue() != "" &&
|
||||
tenantId != nil && tenantId.GetStringValue() != "":
|
||||
ret[constClientId] = clientId.GetStringValue()
|
||||
ret[constSecretValue] = secretValue.GetStringValue()
|
||||
ret[constTenantId] = tenantId.GetStringValue()
|
||||
if secretId != nil {
|
||||
ret[constSecretId] = secretId.GetStringValue()
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func fetchAuthorizer(hc *hostcatalogs.HostCatalog, secrets *structpb.Struct) (string, autorest.Authorizer, error) {
|
||||
aParams, err := authParams(hc, secrets)
|
||||
var lastRotatedTime time.Time
|
||||
var err error
|
||||
if secrets.CredsLastRotatedTime != "" {
|
||||
lastRotatedTime, err = time.Parse(time.RFC3339Nano, secrets.CredsLastRotatedTime)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("error finding authentication params: %w", err)
|
||||
return nil, fmt.Errorf("error parsing last rotated time: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
var authorizer autorest.Authorizer
|
||||
// Switch between currently-understood authorization types
|
||||
{
|
||||
switch {
|
||||
case aParams[constClientId] != "" &&
|
||||
aParams[constSecretValue] != "" &&
|
||||
aParams[constTenantId] != "":
|
||||
return &AuthorizationInfo{
|
||||
HamiltonConfig: hauth.Config{
|
||||
Environment: environments.Global,
|
||||
TenantID: attrs.TenantId,
|
||||
ClientID: attrs.ClientId,
|
||||
ClientSecret: secrets.SecretValue,
|
||||
EnableClientSecretAuth: true,
|
||||
},
|
||||
AuthParams: AuthParams{
|
||||
SubscriptionId: attrs.SubscriptionId,
|
||||
ClientId: attrs.ClientId,
|
||||
TenantId: attrs.TenantId,
|
||||
SecretId: secrets.SecretId,
|
||||
SecretValue: secrets.SecretValue,
|
||||
CredsLastRotatedTime: lastRotatedTime,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *AuthorizationInfo) autorestAuthorizer() (autorest.Authorizer, error) {
|
||||
credsConfig := auth.NewClientCredentialsConfig(
|
||||
aParams[constClientId],
|
||||
aParams[constSecretValue],
|
||||
aParams[constTenantId],
|
||||
a.AuthParams.ClientId,
|
||||
a.AuthParams.SecretValue,
|
||||
a.AuthParams.TenantId,
|
||||
)
|
||||
authorizer, err = credsConfig.Authorizer()
|
||||
authorizer, err := credsConfig.Authorizer()
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("error authorizing to Azure: %w", err)
|
||||
return nil, fmt.Errorf("error authorizing to Azure: %w", err)
|
||||
}
|
||||
|
||||
default:
|
||||
return "", nil, errors.New("no or incomplete authentication information available")
|
||||
}
|
||||
|
||||
if authorizer == nil {
|
||||
return "", nil, fmt.Errorf("authorizer after authorizing to Azure is nil")
|
||||
return nil, errors.New("generated authorizer is nil")
|
||||
}
|
||||
return authorizer, nil
|
||||
}
|
||||
|
||||
return aParams[constSubscriptionId], authorizer, nil
|
||||
func (a *AuthorizationInfo) populateObjectId(ctx context.Context, opt ...Option) error {
|
||||
if a.AuthParams.ClientObjectId != "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getWrappedHamiltonConfig(hc *hostcatalogs.HostCatalog, persisted *pb.HostCatalogPersisted) (*WrappedHamiltonConfig, error) {
|
||||
secrets := hc.GetSecrets()
|
||||
if secrets == nil && persisted != nil {
|
||||
secrets = persisted.GetSecrets()
|
||||
}
|
||||
aParams, err := authParams(hc, secrets)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching authentication params when fetching: %w", err)
|
||||
}
|
||||
authConfig := &WrappedHamiltonConfig{}
|
||||
authConfig.Environment = environments.Global
|
||||
|
||||
switch {
|
||||
case aParams[constClientId] != "" &&
|
||||
aParams[constSecretValue] != "" &&
|
||||
aParams[constTenantId] != "":
|
||||
authConfig.TenantID = aParams[constTenantId]
|
||||
authConfig.ClientID = aParams[constClientId]
|
||||
authConfig.ClientSecret = aParams[constSecretValue]
|
||||
if aParams[constSecretId] != "" {
|
||||
authConfig.SecretId = aParams[constSecretId]
|
||||
}
|
||||
authConfig.EnableClientSecretAuth = true
|
||||
|
||||
default:
|
||||
return nil, errors.New("no or incomplete authentication information available")
|
||||
}
|
||||
|
||||
return authConfig, nil
|
||||
}
|
||||
|
||||
func getObjectId(ctx context.Context, hConfig *WrappedHamiltonConfig, opt ...Option) (string, error) {
|
||||
opts, err := getOpts(opt...)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error parsing ops: %w", err)
|
||||
}
|
||||
|
||||
objId := opts.withObjectId
|
||||
if objId == "" {
|
||||
clientId := opts.withClientId
|
||||
if clientId == "" && hConfig != nil {
|
||||
clientId = hConfig.ClientID
|
||||
}
|
||||
if clientId == "" {
|
||||
return "", fmt.Errorf("neither client id nor object id passed in, cannot continue")
|
||||
if a.AuthParams.ClientId == "" {
|
||||
return errors.New("client id not known at object id lookup time")
|
||||
}
|
||||
// First, find the appId
|
||||
aClient, err := getApplicationsClient(ctx, hConfig)
|
||||
aClient, err := getApplicationsClient(ctx, a)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error getting application client: %w", err)
|
||||
return fmt.Errorf("error getting application client: %w", err)
|
||||
}
|
||||
if aClient == nil {
|
||||
return "", errors.New("applications client is nil when getting object id")
|
||||
return errors.New("applications client is nil when getting object id")
|
||||
}
|
||||
apps, _, err := aClient.List(ctx, odata.Query{
|
||||
Filter: fmt.Sprintf("appId eq '%s'", clientId),
|
||||
Filter: fmt.Sprintf("appId eq '%s'", a.AuthParams.ClientId),
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error listing applications to find objId: %w", err)
|
||||
return fmt.Errorf("error listing applications to find objId: %w", err)
|
||||
}
|
||||
if apps == nil {
|
||||
return "", errors.New("nil apps returned when listing to find objId")
|
||||
return errors.New("nil apps returned when listing to find objId")
|
||||
}
|
||||
if len(*apps) != 1 {
|
||||
return "", fmt.Errorf("unexpected number of apps found, expected 1, found %d", len(*apps))
|
||||
return fmt.Errorf("unexpected number of apps found, expected 1, found %d", len(*apps))
|
||||
}
|
||||
objId = *(*apps)[0].DirectoryObject.ID
|
||||
}
|
||||
return objId, nil
|
||||
a.AuthParams.ClientObjectId = *(*apps)[0].DirectoryObject.ID
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// rotateCredentials creates a new password, then uses a client with that new
|
||||
// password to revoke the old.
|
||||
//
|
||||
// NOTE: the underlying auth config will be modified to use the new credentials!
|
||||
func rotateCredential(ctx context.Context, hConfig *WrappedHamiltonConfig, opt ...Option) (*msgraph.PasswordCredential, error) {
|
||||
if hConfig == nil {
|
||||
return nil, errors.New("empty auth config id")
|
||||
func rotateCredential(ctx context.Context, authzInfo *AuthorizationInfo, opt ...Option) (*msgraph.PasswordCredential, error) {
|
||||
if authzInfo == nil {
|
||||
return nil, errors.New("empty authz info")
|
||||
}
|
||||
if hConfig.SecretId == "" {
|
||||
if authzInfo.AuthParams.SecretId == "" {
|
||||
return nil, errors.New("missing original secret id")
|
||||
}
|
||||
|
||||
// Get the object ID here so we don't have to look it up twice
|
||||
objId, err := getObjectId(ctx, hConfig)
|
||||
if err != nil {
|
||||
// Ensure the object ID is set
|
||||
if err := authzInfo.populateObjectId(ctx); err != nil {
|
||||
return nil, fmt.Errorf("error fetching object id: %w", err)
|
||||
}
|
||||
|
||||
newCred, err := addCredential(ctx, hConfig, WithObjectId(objId))
|
||||
newCred, err := addCredential(ctx, authzInfo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error adding password: %w", err)
|
||||
}
|
||||
@@ -209,19 +177,23 @@ func rotateCredential(ctx context.Context, hConfig *WrappedHamiltonConfig, opt .
|
||||
return nil, errors.New("new credential secret text is nil after adding")
|
||||
}
|
||||
|
||||
if err := removeCredential(ctx, hConfig, WithObjectId(objId)); err != nil {
|
||||
if err := removeCredential(ctx, authzInfo); err != nil {
|
||||
return nil, fmt.Errorf("error removing previous credential: %w", err)
|
||||
}
|
||||
return newCred, nil
|
||||
}
|
||||
|
||||
func addCredential(ctx context.Context, hConfig *WrappedHamiltonConfig, opt ...Option) (*msgraph.PasswordCredential, error) {
|
||||
objId, err := getObjectId(ctx, hConfig, opt...)
|
||||
if err != nil {
|
||||
func addCredential(ctx context.Context, authzInfo *AuthorizationInfo, opt ...Option) (*msgraph.PasswordCredential, error) {
|
||||
if authzInfo == nil {
|
||||
return nil, errors.New("empty authz info")
|
||||
}
|
||||
|
||||
// Ensure the object ID is set
|
||||
if err := authzInfo.populateObjectId(ctx); err != nil {
|
||||
return nil, fmt.Errorf("error fetching object id: %w", err)
|
||||
}
|
||||
|
||||
aClient, err := getApplicationsClient(ctx, hConfig)
|
||||
aClient, err := getApplicationsClient(ctx, authzInfo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting application client: %w", err)
|
||||
}
|
||||
@@ -231,7 +203,7 @@ func addCredential(ctx context.Context, hConfig *WrappedHamiltonConfig, opt ...O
|
||||
|
||||
// Create the new password
|
||||
displayName := fmt.Sprintf("boundary-rotated-%s", time.Now().Format(time.RFC3339))
|
||||
newPass, _, err := aClient.AddPassword(ctx, objId, msgraph.PasswordCredential{
|
||||
newPass, _, err := aClient.AddPassword(ctx, authzInfo.AuthParams.ClientObjectId, msgraph.PasswordCredential{
|
||||
DisplayName: &displayName,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -250,9 +222,9 @@ func addCredential(ctx context.Context, hConfig *WrappedHamiltonConfig, opt ...O
|
||||
return newPass, nil
|
||||
}
|
||||
|
||||
func removeCredential(ctx context.Context, hConfig *WrappedHamiltonConfig, opt ...Option) error {
|
||||
objId, err := getObjectId(ctx, hConfig, opt...)
|
||||
if err != nil {
|
||||
func removeCredential(ctx context.Context, authzInfo *AuthorizationInfo, opt ...Option) error {
|
||||
// Ensure the object ID is set
|
||||
if err := authzInfo.populateObjectId(ctx); err != nil {
|
||||
return fmt.Errorf("error fetching object id: %w", err)
|
||||
}
|
||||
|
||||
@@ -261,12 +233,12 @@ func removeCredential(ctx context.Context, hConfig *WrappedHamiltonConfig, opt .
|
||||
return fmt.Errorf("error parsing ops: %w", err)
|
||||
}
|
||||
|
||||
secretId := hConfig.SecretId
|
||||
secretId := authzInfo.AuthParams.SecretId
|
||||
if opts.withSecretId != "" {
|
||||
secretId = opts.withSecretId
|
||||
}
|
||||
|
||||
aClient, err := getApplicationsClient(ctx, hConfig)
|
||||
aClient, err := getApplicationsClient(ctx, authzInfo)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting application client: %w", err)
|
||||
}
|
||||
@@ -274,7 +246,7 @@ func removeCredential(ctx context.Context, hConfig *WrappedHamiltonConfig, opt .
|
||||
return errors.New("applications client is nil when removing credential")
|
||||
}
|
||||
|
||||
_, err = aClient.RemovePassword(ctx, objId, secretId)
|
||||
_, err = aClient.RemovePassword(ctx, authzInfo.AuthParams.ClientObjectId, secretId)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error removing old password: %w", err)
|
||||
}
|
||||
|
||||
+4
-7
@@ -112,20 +112,17 @@ func getPublicIpAddressesClient(opt ...Option) (*network.PublicIPAddressesClient
|
||||
return &client, nil
|
||||
}
|
||||
|
||||
func getApplicationsClient(ctx context.Context, authConfig *WrappedHamiltonConfig) (*msgraph.ApplicationsClient, error) {
|
||||
if authConfig == nil {
|
||||
func getApplicationsClient(ctx context.Context, authzInfo *AuthorizationInfo) (*msgraph.ApplicationsClient, error) {
|
||||
if authzInfo == nil {
|
||||
return nil, errors.New("empty auth config id when fetching service principals client")
|
||||
}
|
||||
if authConfig.TenantID == "" {
|
||||
return nil, errors.New("empty tenant id in auth config when fetching service principals client")
|
||||
}
|
||||
|
||||
authorizer, err := authConfig.NewAuthorizer(ctx, hauth.MsGraph)
|
||||
authorizer, err := authzInfo.HamiltonConfig.NewAuthorizer(ctx, hauth.MsGraph)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching hauth authorizer when fetching service principals client: %w", err)
|
||||
}
|
||||
|
||||
client := msgraph.NewApplicationsClient(authConfig.TenantID)
|
||||
client := msgraph.NewApplicationsClient(authzInfo.AuthParams.TenantId)
|
||||
client.BaseClient.Authorizer = authorizer
|
||||
|
||||
return client, nil
|
||||
|
||||
+4
-13
@@ -32,9 +32,8 @@ type options struct {
|
||||
withAuthorizer autorest.Authorizer
|
||||
withUserAgent string
|
||||
withBaseUrl string
|
||||
withWrappedHamiltonConfig *WrappedHamiltonConfig
|
||||
withAuthorizationInfo *AuthorizationInfo
|
||||
withClientId string
|
||||
withObjectId string
|
||||
withSecretId string
|
||||
}
|
||||
|
||||
@@ -100,10 +99,10 @@ func WithBaseUrl(with string) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// WithWrappedHamiltonConfig contains the hamilton package authentication config to use
|
||||
func WithWrappedHamiltonConfig(with *WrappedHamiltonConfig) Option {
|
||||
// WithAuthorizationInfo contains the authorization information to use
|
||||
func WithAuthorizationInfo(with *AuthorizationInfo) Option {
|
||||
return func(o *options) error {
|
||||
o.withWrappedHamiltonConfig = with
|
||||
o.withAuthorizationInfo = with
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -116,14 +115,6 @@ func WithClientId(with string) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// WithObjectId contains the object ID to use
|
||||
func WithObjectId(with string) Option {
|
||||
return func(o *options) error {
|
||||
o.withObjectId = with
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithSecretId contains the secret ID to use
|
||||
func WithSecretId(with string) Option {
|
||||
return func(o *options) error {
|
||||
|
||||
+127
-167
@@ -9,10 +9,12 @@ import (
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/profiles/latest/network/mgmt/network"
|
||||
"github.com/Azure/azure-sdk-for-go/profiles/latest/resources/mgmt/resources"
|
||||
"github.com/kr/pretty"
|
||||
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
|
||||
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs"
|
||||
pb "github.com/hashicorp/boundary/sdk/pbs/plugin"
|
||||
)
|
||||
|
||||
@@ -27,11 +29,11 @@ var (
|
||||
)
|
||||
|
||||
func rotateCredFromCallback(ctx context.Context, catalog *hostcatalogs.HostCatalog) (*pb.HostCatalogPersisted, error) {
|
||||
authConfig, err := getWrappedHamiltonConfig(catalog, nil)
|
||||
authzInfo, err := getAuthorizationInfo(catalog)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting hauth config when creating catalog: %w", err)
|
||||
}
|
||||
newPass, err := rotateCredential(ctx, authConfig)
|
||||
newPass, err := rotateCredential(ctx, authzInfo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error rotating credentials when creating catalog: %w", err)
|
||||
}
|
||||
@@ -52,207 +54,96 @@ func rotateCredFromCallback(ctx context.Context, catalog *hostcatalogs.HostCatal
|
||||
}
|
||||
|
||||
func (p *AzurePlugin) OnCreateCatalog(ctx context.Context, req *pb.OnCreateCatalogRequest) (*pb.OnCreateCatalogResponse, error) {
|
||||
catalog := req.GetCatalog()
|
||||
if catalog == nil {
|
||||
return nil, errors.New("catalog is nil")
|
||||
if err := validateCatalog(req.GetCatalog()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
secrets := catalog.GetSecrets()
|
||||
if secrets == nil {
|
||||
// Secrets will have to be provided later, on update
|
||||
return nil, nil
|
||||
if err := validateSecrets(req.GetCatalog().GetSecrets()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sanitizeSecretsFields(secrets)
|
||||
secrets := req.GetCatalog().GetSecrets().AsMap()
|
||||
persistedSecrets := map[string]interface{}{
|
||||
constSecretValue: secrets[constSecretValue],
|
||||
}
|
||||
if _, ok := secrets[constSecretId]; ok {
|
||||
persistedSecrets[constSecretId] = secrets[constSecretId]
|
||||
}
|
||||
persist, err := structpb.NewStruct(persistedSecrets)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.FailedPrecondition, "failed marshaling persisted secrets: %q", err.Error())
|
||||
}
|
||||
|
||||
// Check if rotation is skipped. Always prefer attempting rotation unless we
|
||||
// explicitly are told not to
|
||||
var skipRotation bool
|
||||
if attrs := catalog.GetAttributes(); attrs != nil {
|
||||
if fields := attrs.GetFields(); fields != nil {
|
||||
if field := fields[constDisableCredentialRotation]; field != nil {
|
||||
if field.GetBoolValue() {
|
||||
skipRotation = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if skipRotation {
|
||||
return &pb.OnCreateCatalogResponse{
|
||||
Persisted: &pb.HostCatalogPersisted{
|
||||
Secrets: secrets,
|
||||
Secrets: persist,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
newPersisted, err := rotateCredFromCallback(ctx, catalog)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error formatting new persisted data: %w", err)
|
||||
}
|
||||
ret := &pb.OnCreateCatalogResponse{
|
||||
Persisted: newPersisted,
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (p *AzurePlugin) OnUpdateCatalog(ctx context.Context, req *pb.OnUpdateCatalogRequest) (*pb.OnUpdateCatalogResponse, error) {
|
||||
newCatalog := req.GetNewCatalog()
|
||||
if newCatalog == nil {
|
||||
// Should never happen
|
||||
return nil, errors.New("new catalog is nil")
|
||||
if err := validateCatalog(req.GetNewCatalog()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
currentCatalog := req.GetCurrentCatalog()
|
||||
if currentCatalog == nil {
|
||||
// Should never happen
|
||||
return nil, errors.New("current catalog is nil")
|
||||
return nil, status.Error(codes.FailedPrecondition, "current catalog is nil")
|
||||
}
|
||||
secrets := newCatalog.GetSecrets()
|
||||
secrets := req.GetNewCatalog().GetSecrets()
|
||||
if secrets == nil {
|
||||
// If new secrets weren't passed in, don't rotate what we have on
|
||||
// update.
|
||||
//
|
||||
// TODO: There may be combinations of attributes where those values
|
||||
// changing (or being added) should trigger rotation.
|
||||
return nil, nil
|
||||
return &pb.OnUpdateCatalogResponse{}, nil
|
||||
}
|
||||
if err := validateSecrets(secrets); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sanitizeSecretsFields(secrets)
|
||||
|
||||
// If the old credential had been rotated, attempt to delete the old
|
||||
// credential using itself; if it fails, proceed anyways, since in theory
|
||||
// only Boundary knows this value so simply forgetting it is safe enough (if
|
||||
// not clean).
|
||||
var field *structpb.Value
|
||||
var pdFields map[string]*structpb.Value
|
||||
var err error
|
||||
var authConfig *WrappedHamiltonConfig
|
||||
|
||||
persisted := req.GetPersisted()
|
||||
if persisted == nil {
|
||||
goto after_revocation
|
||||
secretsMap := secrets.AsMap()
|
||||
persistedSecrets := map[string]interface{}{
|
||||
constSecretValue: secretsMap[constSecretValue],
|
||||
}
|
||||
if persisted.GetSecrets() == nil {
|
||||
goto after_revocation
|
||||
if _, ok := secretsMap[constSecretId]; ok {
|
||||
persistedSecrets[constSecretId] = secretsMap[constSecretId]
|
||||
}
|
||||
pdFields = persisted.GetSecrets().GetFields()
|
||||
if pdFields == nil {
|
||||
goto after_revocation
|
||||
}
|
||||
field = pdFields[constCredsLastRotatedTime]
|
||||
if field == nil {
|
||||
goto after_revocation
|
||||
}
|
||||
// If the field exists, it is a timestamp here indicating last rotation time
|
||||
authConfig, err = getWrappedHamiltonConfig(currentCatalog, req.GetPersisted())
|
||||
persist, err := structpb.NewStruct(persistedSecrets)
|
||||
if err != nil {
|
||||
// TODO: warn in some way?
|
||||
goto after_revocation
|
||||
}
|
||||
if err := removeCredential(ctx, authConfig); err != nil {
|
||||
// TODO: warn
|
||||
goto after_revocation
|
||||
return nil, status.Errorf(codes.FailedPrecondition, "failed marshaling persisted secrets: %q", err.Error())
|
||||
}
|
||||
|
||||
after_revocation:
|
||||
// Check if rotation is skipped. Always prefer attempting rotation unless we
|
||||
// explicitly are told not to
|
||||
var skipRotation bool
|
||||
if attrs := newCatalog.GetAttributes(); attrs != nil {
|
||||
if fields := attrs.GetFields(); fields != nil {
|
||||
if field := fields[constDisableCredentialRotation]; field != nil {
|
||||
if field.GetBoolValue() {
|
||||
skipRotation = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if skipRotation {
|
||||
return &pb.OnUpdateCatalogResponse{
|
||||
Persisted: &pb.HostCatalogPersisted{
|
||||
Secrets: secrets,
|
||||
Secrets: persist,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
newPersisted, err := rotateCredFromCallback(ctx, req.GetNewCatalog())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error formatting new persisted data: %w", err)
|
||||
}
|
||||
ret := &pb.OnUpdateCatalogResponse{
|
||||
Persisted: newPersisted,
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (p *AzurePlugin) OnDeleteCatalog(ctx context.Context, req *pb.OnDeleteCatalogRequest) (*pb.OnDeleteCatalogResponse, error) {
|
||||
if req.GetCatalog() == nil {
|
||||
return nil, errors.New("catalog is nil")
|
||||
}
|
||||
if req.GetPersisted() == nil || req.GetPersisted().GetSecrets() == nil {
|
||||
return nil, nil
|
||||
}
|
||||
// Figure out if creds were rotated
|
||||
pdFields := req.GetPersisted().GetSecrets().GetFields()
|
||||
if pdFields == nil {
|
||||
// No data
|
||||
return nil, nil
|
||||
}
|
||||
field := pdFields[constCredsLastRotatedTime]
|
||||
if field == nil {
|
||||
return nil, nil
|
||||
}
|
||||
// The field existing means it is a string containing the last time it was
|
||||
// rotated. Delete the credential using itself.
|
||||
authConfig, err := getWrappedHamiltonConfig(req.GetCatalog(), req.GetPersisted())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting auth config on catalog deletion: %w", err)
|
||||
}
|
||||
if err := removeCredential(ctx, authConfig); err != nil {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error revoking credential on catalog deletion: %w, authConfig is %s", err, pretty.Sprint(authConfig))
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (p *AzurePlugin) OnCreateSet(ctx context.Context, req *pb.OnCreateSetRequest) (*pb.OnCreateSetResponse, error) {
|
||||
// Nothing yet
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (p *AzurePlugin) OnUpdateSet(ctx context.Context, req *pb.OnUpdateSetRequest) (*pb.OnUpdateSetResponse, error) {
|
||||
// Nothing yet
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (p *AzurePlugin) OnDeleteSet(ctx context.Context, req *pb.OnDeleteSetRequest) (*pb.OnDeleteSetResponse, error) {
|
||||
// Nothing yet
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (p *AzurePlugin) ListHosts(ctx context.Context, req *pb.ListHostsRequest) (*pb.ListHostsResponse, error) {
|
||||
if len(req.GetSets()) == 0 {
|
||||
// Nothing to fetch
|
||||
return nil, nil
|
||||
return &pb.ListHostsResponse{}, nil
|
||||
}
|
||||
catalog := req.GetCatalog()
|
||||
if catalog == nil {
|
||||
return nil, errors.New("catalog is nil")
|
||||
return nil, status.Error(codes.FailedPrecondition, "catalog is nil")
|
||||
}
|
||||
catalogAttrs := catalog.GetAttributes()
|
||||
if catalogAttrs == nil {
|
||||
return nil, errors.New("catalog has no attributes")
|
||||
return nil, status.Error(codes.FailedPrecondition, "catalog has no attributes")
|
||||
}
|
||||
|
||||
// Create an authorizer
|
||||
var secrets *structpb.Struct
|
||||
if req.GetPersisted() != nil {
|
||||
secrets = req.GetPersisted().GetSecrets()
|
||||
}
|
||||
subscriptionId, authorizer, err := fetchAuthorizer(catalog, secrets)
|
||||
catalog.Secrets = req.GetPersisted().GetSecrets()
|
||||
authzInfo, err := getAuthorizationInfo(catalog)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching subscription id and authorizer: %w", err)
|
||||
return nil, fmt.Errorf("error fetching authorizationInfo: %w", err)
|
||||
}
|
||||
if subscriptionId == "" {
|
||||
return nil, errors.New("subscription id not discovered")
|
||||
if authzInfo == nil {
|
||||
return nil, status.Error(codes.FailedPrecondition, "authorization info is nil")
|
||||
}
|
||||
authorizer, err := authzInfo.autorestAuthorizer()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching autorest authorizer: %w", err)
|
||||
}
|
||||
if authorizer == nil {
|
||||
return nil, errors.New("fetched authorizer is nil")
|
||||
@@ -260,7 +151,7 @@ func (p *AzurePlugin) ListHosts(ctx context.Context, req *pb.ListHostsRequest) (
|
||||
|
||||
commonOpts := make([]Option, 0, 3)
|
||||
commonOpts = append(commonOpts,
|
||||
WithSubscriptionId(subscriptionId),
|
||||
WithSubscriptionId(authzInfo.AuthParams.SubscriptionId),
|
||||
WithAuthorizer(authorizer),
|
||||
)
|
||||
|
||||
@@ -384,11 +275,11 @@ func (p *AzurePlugin) ListHosts(ctx context.Context, req *pb.ListHostsRequest) (
|
||||
return nil, fmt.Errorf("instance view statuses returned for vm with id %q is null", *res.ID)
|
||||
}
|
||||
var running bool
|
||||
for _, status := range *iv.Statuses {
|
||||
if status.Code == nil {
|
||||
for _, s := range *iv.Statuses {
|
||||
if s.Code == nil {
|
||||
continue
|
||||
}
|
||||
state := strings.ToLower(*status.Code)
|
||||
state := strings.ToLower(*s.Code)
|
||||
prefix := "powerstate/"
|
||||
if !strings.HasPrefix(state, prefix) {
|
||||
continue
|
||||
@@ -504,9 +395,78 @@ func splitId(in, expectedService, expectedResource string) (string, string, erro
|
||||
return splitId[3], splitId[7], nil
|
||||
}
|
||||
|
||||
func sanitizeSecretsFields(in *structpb.Struct) {
|
||||
// Only we are allowed to set this value
|
||||
if fields := in.GetFields(); fields != nil {
|
||||
delete(fields, constCredsLastRotatedTime)
|
||||
// Returns an invalid argument error with the problematic fields included
|
||||
// in the error message.
|
||||
func invalidArgumentError(msg string, f map[string]string) error {
|
||||
var fieldMsgs []string
|
||||
for field, val := range f {
|
||||
fieldMsgs = append(fieldMsgs, fmt.Sprintf("%q: %q", field, val))
|
||||
}
|
||||
if len(fieldMsgs) > 0 {
|
||||
msg = fmt.Sprintf("%s: [%s]", msg, strings.Join(fieldMsgs, ", "))
|
||||
}
|
||||
return status.Error(codes.InvalidArgument, msg)
|
||||
}
|
||||
|
||||
func validateSecrets(s *structpb.Struct) error {
|
||||
if s == nil {
|
||||
return status.Error(codes.InvalidArgument, "Secrets not provided but are required")
|
||||
}
|
||||
var secrets SecretData
|
||||
if err := mapstructure.Decode(s.AsMap(), &secrets); err != nil {
|
||||
return status.Errorf(codes.InvalidArgument, "error decoding catalog secrets: %s", err)
|
||||
}
|
||||
badFields := make(map[string]string)
|
||||
if len(secrets.SecretValue) == 0 {
|
||||
badFields["secrets.secret_value"] = "This field is required."
|
||||
}
|
||||
if len(secrets.CredsLastRotatedTime) != 0 {
|
||||
badFields["secrets.creds_last_rotated_time"] = "This field is reserved and cannot be set."
|
||||
}
|
||||
if len(badFields) > 0 {
|
||||
return invalidArgumentError("Error in the secrets provided", badFields)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var allowedFields = map[string]struct{}{
|
||||
constDisableCredentialRotation: {},
|
||||
constSubscriptionId: {},
|
||||
constTenantId: {},
|
||||
constClientId: {},
|
||||
}
|
||||
|
||||
func validateCatalog(catalog *hostcatalogs.HostCatalog) error {
|
||||
if catalog == nil {
|
||||
return status.Error(codes.InvalidArgument, "catalog is nil")
|
||||
}
|
||||
var attrs Attributes
|
||||
attrMap := catalog.GetAttributes().AsMap()
|
||||
if err := mapstructure.Decode(attrMap, &attrs); err != nil {
|
||||
return status.Errorf(codes.InvalidArgument, "error decoding catalog attributes: %s", err)
|
||||
}
|
||||
badFields := make(map[string]string)
|
||||
if !attrs.DisableCredentialRotation {
|
||||
badFields["attributes.disable_credential_rotation"] = "This field must be set to true."
|
||||
}
|
||||
if len(attrs.SubscriptionId) == 0 {
|
||||
badFields["attributes.subscription_id"] = "This is a required field."
|
||||
}
|
||||
if len(attrs.ClientId) == 0 {
|
||||
badFields["attributes.client_id"] = "This is a required field."
|
||||
}
|
||||
if len(attrs.TenantId) == 0 {
|
||||
badFields["attributes.tenant_id"] = "This is a required field."
|
||||
}
|
||||
|
||||
for f := range attrMap {
|
||||
if _, ok := allowedFields[f]; !ok {
|
||||
badFields[fmt.Sprintf("attributes.%s", f)] = "Unrecognized field."
|
||||
}
|
||||
}
|
||||
|
||||
if len(badFields) > 0 {
|
||||
return invalidArgumentError("Invalid arguments in the new catalog", badFields)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+256
-126
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/manicminer/hamilton/auth"
|
||||
"github.com/manicminer/hamilton/environments"
|
||||
"github.com/manicminer/hamilton/msgraph"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
)
|
||||
@@ -46,116 +47,251 @@ func TestListHosts(t *testing.T) {
|
||||
pretty.Println(lhResp.GetHosts())
|
||||
}
|
||||
|
||||
func TestOnCreateOnDeleteCatalog(t *testing.T) {
|
||||
require := require.New(t)
|
||||
ctx := context.Background()
|
||||
p := &AzurePlugin{}
|
||||
hostCatalog, _ := testGetHostStructs(t)
|
||||
objId, initialAuthConfig, initialCred, cleanup := testCreateInitialSecret(t, hostCatalog)
|
||||
defer cleanup()
|
||||
|
||||
// At this point, remove secrets corresponding to those read in by
|
||||
// testGetHostCatalog as they're our permanent ones
|
||||
hostCatalog.Secrets = nil
|
||||
|
||||
// Give some time -- a lot -- for token to become active
|
||||
time.Sleep(10 * time.Minute)
|
||||
|
||||
// Now, we'll use this credential to simulate creating a host catalog, which
|
||||
// will rotate it. First, ensure we don't error out if creds aren't provided
|
||||
// _yet_ (instead, on update)
|
||||
ccResp, err := p.OnCreateCatalog(ctx, &pb.OnCreateCatalogRequest{
|
||||
Catalog: hostCatalog,
|
||||
})
|
||||
require.NoError(err)
|
||||
require.Nil(ccResp)
|
||||
|
||||
// Now run the same thing with rotation disabled, make sure we get the same
|
||||
// values back
|
||||
secrets := wrapMap(t, map[string]interface{}{
|
||||
constSecretValue: *initialCred.SecretText,
|
||||
constSecretId: *initialCred.KeyId,
|
||||
})
|
||||
hostCatalog.Secrets = secrets
|
||||
hostCatalog.GetAttributes().GetFields()[constDisableCredentialRotation] = structpb.NewBoolValue(true)
|
||||
ccResp, err = p.OnCreateCatalog(ctx, &pb.OnCreateCatalogRequest{
|
||||
Catalog: hostCatalog,
|
||||
})
|
||||
require.NoError(err)
|
||||
require.NotNil(ccResp)
|
||||
require.NotNil(ccResp.GetPersisted())
|
||||
require.NotNil(ccResp.GetPersisted().GetSecrets())
|
||||
newPdFields := ccResp.GetPersisted().GetSecrets().GetFields()
|
||||
require.Equal(*initialCred.KeyId, newPdFields[constSecretId].GetStringValue())
|
||||
require.Equal(*initialCred.SecretText, newPdFields[constSecretValue].GetStringValue())
|
||||
|
||||
// Now we'll try again, actually rotating the creds
|
||||
delete(hostCatalog.GetAttributes().GetFields(), constDisableCredentialRotation)
|
||||
ccResp, err = p.OnCreateCatalog(ctx, &pb.OnCreateCatalogRequest{
|
||||
Catalog: hostCatalog,
|
||||
})
|
||||
|
||||
require.NoError(err)
|
||||
require.NotNil(ccResp)
|
||||
require.NotNil(ccResp.GetPersisted())
|
||||
require.NotNil(ccResp.GetPersisted().GetSecrets())
|
||||
newPdFields = ccResp.GetPersisted().GetSecrets().GetFields()
|
||||
// Use old creds for this removal to give it a high likelihood of success
|
||||
deferAuthConfig := *initialAuthConfig
|
||||
defer func() {
|
||||
_ = removeCredential(
|
||||
ctx,
|
||||
&deferAuthConfig,
|
||||
WithObjectId(objId),
|
||||
)
|
||||
}()
|
||||
|
||||
// Best-effort ensure that this new cred is deleted if OnDelete doesn't work right
|
||||
require.NotEqual(*initialCred.KeyId, newPdFields[constSecretId].GetStringValue())
|
||||
require.NotEqual(*initialCred.SecretText, newPdFields[constSecretValue].GetStringValue())
|
||||
|
||||
// Remove these now prior to OnDelete
|
||||
hostCatalog.Secrets = nil
|
||||
|
||||
// Make sure old creds don't work
|
||||
waitForCredsAuthConfig := *initialAuthConfig
|
||||
// FIXME: Actually don't, because they can still be used for up to a day!??! later?!?!
|
||||
// waitForCreds(t, &waitForCredsAuthConfig, false)
|
||||
|
||||
// Make sure the new creds work, then sleep
|
||||
waitForCredsAuthConfig.SecretId = newPdFields[constSecretId].GetStringValue()
|
||||
waitForCredsAuthConfig.ClientSecret = newPdFields[constSecretValue].GetStringValue()
|
||||
waitForCreds(t, &waitForCredsAuthConfig, true)
|
||||
time.Sleep(10 * time.Minute)
|
||||
|
||||
// Now, let's test OnDelete. First, check the case where we never configured
|
||||
// a credential in the first place.
|
||||
cdResp, err := p.OnDeleteCatalog(ctx, &pb.OnDeleteCatalogRequest{
|
||||
Catalog: hostCatalog,
|
||||
Persisted: &pb.HostCatalogPersisted{},
|
||||
})
|
||||
require.NoError(err)
|
||||
require.Nil(cdResp)
|
||||
|
||||
// Now, use the credentials we got above to revoke this credential we got from OnCreate
|
||||
secrets = wrapMap(t, map[string]interface{}{
|
||||
constSecretValue: waitForCredsAuthConfig.ClientSecret,
|
||||
constSecretId: waitForCredsAuthConfig.SecretId,
|
||||
})
|
||||
cdResp, err = p.OnDeleteCatalog(ctx, &pb.OnDeleteCatalogRequest{
|
||||
Catalog: hostCatalog,
|
||||
Persisted: &pb.HostCatalogPersisted{
|
||||
Secrets: secrets,
|
||||
func TestValidateCatalog(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
catalogAttrs *structpb.Struct
|
||||
wantError bool
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
catalogAttrs: wrapMap(t, map[string]interface{}{
|
||||
constDisableCredentialRotation: true,
|
||||
constClientId: "client_id",
|
||||
constTenantId: "tenant_id",
|
||||
constSubscriptionId: "sub_id",
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "nil",
|
||||
catalogAttrs: nil,
|
||||
wantError: true,
|
||||
},
|
||||
{
|
||||
name: "rotation enabled",
|
||||
catalogAttrs: wrapMap(t, map[string]interface{}{
|
||||
constDisableCredentialRotation: false,
|
||||
constClientId: "client_id",
|
||||
constTenantId: "tenant_id",
|
||||
constSubscriptionId: "sub_id",
|
||||
}),
|
||||
wantError: true,
|
||||
},
|
||||
{
|
||||
name: "no client id",
|
||||
catalogAttrs: wrapMap(t, map[string]interface{}{
|
||||
constDisableCredentialRotation: true,
|
||||
constTenantId: "tenant_id",
|
||||
constSubscriptionId: "sub_id",
|
||||
}),
|
||||
wantError: true,
|
||||
},
|
||||
{
|
||||
name: "no tenant id",
|
||||
catalogAttrs: wrapMap(t, map[string]interface{}{
|
||||
constDisableCredentialRotation: false,
|
||||
constClientId: "client_id",
|
||||
constSubscriptionId: "sub_id",
|
||||
}),
|
||||
wantError: true,
|
||||
},
|
||||
{
|
||||
name: "no subscription id",
|
||||
catalogAttrs: wrapMap(t, map[string]interface{}{
|
||||
constDisableCredentialRotation: false,
|
||||
constClientId: "client_id",
|
||||
constTenantId: "tenant_id",
|
||||
}),
|
||||
wantError: true,
|
||||
},
|
||||
{
|
||||
name: "unrecognized field",
|
||||
catalogAttrs: wrapMap(t, map[string]interface{}{
|
||||
constDisableCredentialRotation: true,
|
||||
constClientId: "client_id",
|
||||
constTenantId: "tenant_id",
|
||||
constSubscriptionId: "sub_id",
|
||||
"unrecognized": "unrecognized",
|
||||
}),
|
||||
wantError: true,
|
||||
},
|
||||
})
|
||||
require.NoError(err)
|
||||
require.Nil(cdResp)
|
||||
// Make sure the creds no longer work
|
||||
// FIXME: Don't, for the same reason
|
||||
// waitForCreds(t, &waitForCredsAuthConfig, false)
|
||||
}
|
||||
|
||||
func waitForCreds(t *testing.T, hConfig *WrappedHamiltonConfig, shouldWork bool) auth.Authorizer {
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got := validateCatalog(&hostcatalogs.HostCatalog{
|
||||
Attributes: tc.catalogAttrs,
|
||||
})
|
||||
if tc.wantError {
|
||||
assert.Error(t, got)
|
||||
} else {
|
||||
assert.NoError(t, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateSecrets(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
catalogSecrets *structpb.Struct
|
||||
wantError bool
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
catalogSecrets: wrapMap(t, map[string]interface{}{
|
||||
constSecretValue: "value",
|
||||
constSecretId: "secret_id",
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "nil secret value",
|
||||
catalogSecrets: nil,
|
||||
wantError: true,
|
||||
},
|
||||
{
|
||||
name: "no secret value",
|
||||
catalogSecrets: wrapMap(t, map[string]interface{}{
|
||||
constSecretId: "secret_id",
|
||||
}),
|
||||
wantError: true,
|
||||
},
|
||||
{
|
||||
name: "secret with last rotated time",
|
||||
catalogSecrets: wrapMap(t, map[string]interface{}{
|
||||
constSecretValue: "value",
|
||||
constCredsLastRotatedTime: "something",
|
||||
}),
|
||||
wantError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got := validateSecrets(tc.catalogSecrets)
|
||||
if tc.wantError {
|
||||
assert.Error(t, got)
|
||||
} else {
|
||||
assert.NoError(t, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateCatalog(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
p := &AzurePlugin{}
|
||||
attrs := wrapMap(t, map[string]interface{}{
|
||||
constDisableCredentialRotation: true,
|
||||
constClientId: "client_id",
|
||||
constTenantId: "tenant_id",
|
||||
constSubscriptionId: "sub_id",
|
||||
})
|
||||
secrets := wrapMap(t, map[string]interface{}{
|
||||
constSecretValue: "secret_value",
|
||||
constSecretId: "secret_id",
|
||||
})
|
||||
|
||||
res, err := p.OnCreateCatalog(ctx, &pb.OnCreateCatalogRequest{Catalog: &hostcatalogs.HostCatalog{
|
||||
Attributes: attrs,
|
||||
Secrets: secrets,
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, res.GetPersisted().GetSecrets().AsMap(), secrets.AsMap())
|
||||
|
||||
secretsWithExtra := wrapMap(t, map[string]interface{}{
|
||||
constSecretValue: "secret_value",
|
||||
constSecretId: "secret_id",
|
||||
"extraField": "extra_value",
|
||||
})
|
||||
res, err = p.OnCreateCatalog(ctx, &pb.OnCreateCatalogRequest{Catalog: &hostcatalogs.HostCatalog{
|
||||
Attributes: attrs,
|
||||
Secrets: secretsWithExtra,
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
// still only persist the fields we care about.
|
||||
assert.Equal(t, res.GetPersisted().GetSecrets().AsMap(), secrets.AsMap())
|
||||
|
||||
secretsWithRotationTime := wrapMap(t, map[string]interface{}{
|
||||
constSecretValue: "secret_value",
|
||||
constSecretId: "secret_id",
|
||||
constCredsLastRotatedTime: "something",
|
||||
})
|
||||
res, err = p.OnCreateCatalog(ctx, &pb.OnCreateCatalogRequest{Catalog: &hostcatalogs.HostCatalog{
|
||||
Attributes: attrs,
|
||||
Secrets: secretsWithRotationTime,
|
||||
}})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestUpdateCatalog(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
p := &AzurePlugin{}
|
||||
|
||||
oldCatalog := &hostcatalogs.HostCatalog{
|
||||
Attributes: wrapMap(t, map[string]interface{}{
|
||||
constDisableCredentialRotation: true,
|
||||
constClientId: "foo",
|
||||
constTenantId: "foo",
|
||||
constSubscriptionId: "foo",
|
||||
}),
|
||||
}
|
||||
|
||||
attrs := wrapMap(t, map[string]interface{}{
|
||||
constDisableCredentialRotation: true,
|
||||
constClientId: "client_id",
|
||||
constTenantId: "tenant_id",
|
||||
constSubscriptionId: "sub_id",
|
||||
})
|
||||
|
||||
res, err := p.OnUpdateCatalog(ctx, &pb.OnUpdateCatalogRequest{CurrentCatalog: oldCatalog,
|
||||
NewCatalog: &hostcatalogs.HostCatalog{
|
||||
Attributes: attrs,
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, res.GetPersisted())
|
||||
|
||||
secrets := wrapMap(t, map[string]interface{}{
|
||||
constSecretValue: "secret_value",
|
||||
constSecretId: "secret_id",
|
||||
})
|
||||
|
||||
res, err = p.OnUpdateCatalog(ctx, &pb.OnUpdateCatalogRequest{CurrentCatalog: oldCatalog,
|
||||
NewCatalog: &hostcatalogs.HostCatalog{
|
||||
Attributes: attrs,
|
||||
Secrets: secrets,
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, res.GetPersisted().GetSecrets().AsMap(), secrets.AsMap())
|
||||
|
||||
secretsWithExtra := wrapMap(t, map[string]interface{}{
|
||||
constSecretValue: "secret_value",
|
||||
constSecretId: "secret_id",
|
||||
"extraField": "extra_value",
|
||||
})
|
||||
res, err = p.OnUpdateCatalog(ctx, &pb.OnUpdateCatalogRequest{CurrentCatalog: oldCatalog,
|
||||
NewCatalog: &hostcatalogs.HostCatalog{
|
||||
Attributes: attrs,
|
||||
Secrets: secretsWithExtra,
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
// still only persist the fields we care about.
|
||||
assert.Equal(t, res.GetPersisted().GetSecrets().AsMap(), secrets.AsMap())
|
||||
|
||||
secretsWithRotationTime := wrapMap(t, map[string]interface{}{
|
||||
constSecretValue: "secret_value",
|
||||
constSecretId: "secret_id",
|
||||
constCredsLastRotatedTime: "something",
|
||||
})
|
||||
res, err = p.OnUpdateCatalog(ctx, &pb.OnUpdateCatalogRequest{CurrentCatalog: oldCatalog,
|
||||
NewCatalog: &hostcatalogs.HostCatalog{
|
||||
Attributes: attrs,
|
||||
Secrets: secretsWithRotationTime,
|
||||
}})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func waitForCreds(t *testing.T, authzInfo *AuthorizationInfo, shouldWork bool) auth.Authorizer {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
ctx := context.Background()
|
||||
@@ -164,9 +300,9 @@ func waitForCreds(t *testing.T, hConfig *WrappedHamiltonConfig, shouldWork bool)
|
||||
environments.Global,
|
||||
auth.MsGraph,
|
||||
auth.TokenVersion2,
|
||||
hConfig.TenantID,
|
||||
hConfig.ClientID,
|
||||
hConfig.ClientSecret)
|
||||
authzInfo.AuthParams.TenantId,
|
||||
authzInfo.AuthParams.ClientId,
|
||||
authzInfo.AuthParams.SecretValue)
|
||||
require.NoError(err)
|
||||
|
||||
for {
|
||||
@@ -230,45 +366,39 @@ func testGetHostStructs(t *testing.T) (*hostcatalogs.HostCatalog, []*hostsets.Ho
|
||||
// leading into a test. This will serve as our "initial secret" for a test. We
|
||||
// also return a few cached items. It is expected that the host catalog contains
|
||||
// Secrets.
|
||||
func testCreateInitialSecret(t *testing.T, hostCatalog *hostcatalogs.HostCatalog) (string, *WrappedHamiltonConfig, *msgraph.PasswordCredential, func()) {
|
||||
func testCreateInitialSecret(t *testing.T, hostCatalog *hostcatalogs.HostCatalog) (*AuthorizationInfo, *msgraph.PasswordCredential, func()) {
|
||||
require := require.New(t)
|
||||
ctx := context.Background()
|
||||
|
||||
var initialCred *msgraph.PasswordCredential
|
||||
var objId string // The object ID of the application
|
||||
|
||||
// Set up the data, get the config
|
||||
persistedData := hostCatalog.GetSecrets()
|
||||
authConfig, err := getWrappedHamiltonConfig(hostCatalog, &pb.HostCatalogPersisted{Secrets: persistedData})
|
||||
require.NoError(err)
|
||||
|
||||
// Pre-cache the object ID
|
||||
objId, err = getObjectId(ctx, authConfig, WithClientId(hostCatalog.GetAttributes().GetFields()[constClientId].GetStringValue()))
|
||||
authzInfo, err := getAuthorizationInfo(hostCatalog)
|
||||
require.NoError(err)
|
||||
|
||||
// Get the credential
|
||||
initialCred, err = addCredential(ctx, authConfig, WithObjectId(objId))
|
||||
initialCred, err = addCredential(ctx, authzInfo)
|
||||
require.NoError(err)
|
||||
require.NotNil(initialCred)
|
||||
require.NotNil(initialCred.KeyId)
|
||||
require.NotNil(initialCred.SecretText)
|
||||
|
||||
origAuthConfig := *authConfig
|
||||
origAuthConfig := *authzInfo
|
||||
origAuthConfig.AuthParams.SecretId = *initialCred.KeyId
|
||||
cleanup := func() {
|
||||
if err := removeCredential(
|
||||
ctx,
|
||||
&origAuthConfig,
|
||||
WithObjectId(objId),
|
||||
WithSecretId(*initialCred.KeyId),
|
||||
); err != nil {
|
||||
// It may not exist because it's been rotated
|
||||
require.Contains(err.Error(), "No password credential found with keyId", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
authConfig.SecretId = *initialCred.KeyId
|
||||
authConfig.ClientSecret = *initialCred.SecretText
|
||||
waitForCreds(t, authConfig, true)
|
||||
authzInfo.AuthParams.SecretId = *initialCred.KeyId
|
||||
authzInfo.AuthParams.SecretValue = *initialCred.SecretText
|
||||
authzInfo.HamiltonConfig.ClientSecret = *initialCred.SecretText
|
||||
waitForCreds(t, authzInfo, true)
|
||||
|
||||
return objId, authConfig, initialCred, cleanup
|
||||
return authzInfo, initialCred, cleanup
|
||||
}
|
||||
Reference in new issue
Block a user