diff --git a/go.mod b/go.mod index e13ccf22..c6932192 100644 --- a/go.mod +++ b/go.mod @@ -3,34 +3,27 @@ module github.com/xenitab/azad-kube-proxy go 1.15 require ( - github.com/Azure/azure-sdk-for-go v46.0.0+incompatible github.com/Azure/azure-sdk-for-go/sdk/azcore v0.13.4 github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.7.0 - github.com/Azure/azure-sdk-for-go/sdk/to v0.1.1 - github.com/Azure/go-autorest/autorest v0.11.4 github.com/alicebob/miniredis/v2 v2.14.1 github.com/coreos/go-oidc v2.1.0+incompatible github.com/go-logr/logr v0.3.0 github.com/go-logr/zapr v0.3.0 github.com/go-playground/validator/v10 v10.4.1 github.com/go-redis/redis/v8 v8.4.4 - github.com/golang/protobuf v1.4.3 // indirect github.com/google/go-cmp v0.5.4 github.com/gorilla/mux v1.8.0 - github.com/jongio/azidext/go/azidext v0.1.0 github.com/kr/pretty v0.2.0 // indirect + github.com/manicminer/hamilton v0.4.0 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pkg/errors v0.9.1 // indirect github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021 // indirect github.com/spf13/pflag v1.0.5 go.uber.org/zap v1.16.0 golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect - golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect - golang.org/x/sys v0.0.0-20201112073958-5cba982894dd // indirect golang.org/x/text v0.3.4 // indirect golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect - google.golang.org/appengine v1.6.5 // indirect google.golang.org/protobuf v1.25.0 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/square/go-jose.v2 v2.2.2 // indirect diff --git a/go.sum b/go.sum index 4837a46d..a55fddf7 100644 --- a/go.sum +++ b/go.sum @@ -1,36 +1,11 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/Azure/azure-sdk-for-go v46.0.0+incompatible h1:4qlEOCDcDQZTGczYGzbGYCdJfVpZLIs8AEo5+MoXBPw= -github.com/Azure/azure-sdk-for-go v46.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go/sdk/azcore v0.10.0/go.mod h1:R+GJZ0mj7yxXtTENNLTzwkwro5zWzrEiZOdpIiN7Ypc= github.com/Azure/azure-sdk-for-go/sdk/azcore v0.13.4 h1:7MfvHEWKfjZSKQNWERlXpHwCRoceEuQef/fB8CWmnQA= github.com/Azure/azure-sdk-for-go/sdk/azcore v0.13.4/go.mod h1:pElNP+u99BvCZD+0jOlhI9OC/NB2IDTOTGZOZH0Qhq8= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.2.0/go.mod h1:/XqWZ+BVfDwHnN6x+Ns+VH2Le0x0Yhks6I2DHkIyGGo= github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.7.0 h1:qD6HKtTRI0AMrXB+G0TJXxbB7c1gnG8zOROK8eKsbI0= github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.7.0/go.mod h1:+5MTVs48VhHs2R1WpIFv7wy7vJ/acthpwlvcd2eN/Ok= -github.com/Azure/azure-sdk-for-go/sdk/internal v0.3.0/go.mod h1:Q+TCQnSr+clUU0JU+xrHZ3slYCxw17AOFdvWFpQXjAY= github.com/Azure/azure-sdk-for-go/sdk/internal v0.5.0 h1:HG1ggl8L3ZkV/Ydanf7lKr5kkhhPGCpWdnr1J6v7cO4= github.com/Azure/azure-sdk-for-go/sdk/internal v0.5.0/go.mod h1:k4KbFSunV/+0hOHL1vyFaPsiYQ1Vmvy1TBpmtvCDLZM= -github.com/Azure/azure-sdk-for-go/sdk/to v0.1.1 h1:xfQtpQrdXC5By+/gOhE6rLRevCw17TLfjSWzkGkT58Y= -github.com/Azure/azure-sdk-for-go/sdk/to v0.1.1/go.mod h1:UL/d4lvWAzSJUuX+19uKdN0ktyjoOyQhgY+HWNgtIYI= -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.11.4 h1:iWJqGEvip7mjibEqC/srXNdo+4wLEPiwlP/7dZLtoPc= -github.com/Azure/go-autorest/autorest v0.11.4/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= -github.com/Azure/go-autorest/autorest/adal v0.9.0 h1:SigMbuFNuKgc1xcGhaeapbh+8fgsu+GxgDRFyg7f5lM= -github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= -github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.0 h1:z20OWOSG5aCye0HEkDp6TPmP17ZcfeMxPi6HnSALa8c= -github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= -github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= -github.com/Azure/go-autorest/autorest/validation v0.3.0 h1:3I9AAI63HfcLtphd9g39ruUwRI+Ca+z/f36KHPFRUss= -github.com/Azure/go-autorest/autorest/validation v0.3.0/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= -github.com/Azure/go-autorest/logger v0.2.0 h1:e4RVHVZKC5p6UANLJHkM4OfR1UKZPj8Wt8Pcx+3oqrE= -github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk= @@ -49,8 +24,6 @@ github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHo github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -98,10 +71,6 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4 github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= -github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= -github.com/jongio/azidext/go/azidext v0.1.0 h1:FlT+pmODYf82hqyQtE5C/Fajdt64wos88k2d7yhnhHk= -github.com/jongio/azidext/go/azidext v0.1.0/go.mod h1:v7DP8YodvY0fd6An/6j1A6OlU8SxPH1L7pjWcE/svik= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -112,6 +81,8 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/manicminer/hamilton v0.4.0 h1:MJH/beNQGgGo6R9hxPaSy+7x/8coTce5zFSOyfKwoXM= +github.com/manicminer/hamilton v0.4.0/go.mod h1:hkIiAJPBfkx3AYunsjna2+o1LujK+Ck8+o3Cim3CAGM= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -164,7 +135,6 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -190,12 +160,13 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb h1:eBmm0M9fYhWpKZLjQUUKka/LtIxf46G4fxeEz5KJr9U= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11 h1:lwlPPsmjDKK0J6eG6xDWd5XPehI0R024zxjDnw3esPA= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -216,8 +187,9 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201112073958-5cba982894dd h1:5CtCZbICpIOFdgO940moixOPjc0178IU44m4EjOO5IY= -golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 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= @@ -244,8 +216,8 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1N golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= diff --git a/pkg/azure/azure.go b/pkg/azure/azure.go index d1ce19eb..7931146b 100644 --- a/pkg/azure/azure.go +++ b/pkg/azure/azure.go @@ -5,12 +5,10 @@ import ( "fmt" "time" - "github.com/Azure/azure-sdk-for-go/sdk/azcore" - "github.com/Azure/azure-sdk-for-go/sdk/azidentity" - "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" - "github.com/Azure/go-autorest/autorest" "github.com/go-logr/logr" - "github.com/jongio/azidext/go/azidext" + "github.com/manicminer/hamilton/auth" + hamiltonClients "github.com/manicminer/hamilton/clients" + hamiltonEnvironments "github.com/manicminer/hamilton/environments" "github.com/xenitab/azad-kube-proxy/pkg/cache" "github.com/xenitab/azad-kube-proxy/pkg/models" ) @@ -32,53 +30,64 @@ type Client struct { tenantID string graphFilter string cacheClient cache.ClientInterface - groupsClient graphrbac.GroupsClient + groups *groups user *user servicePrincipalUser *servicePrincipalUser } // NewAzureClient returns an Azure client or error func NewAzureClient(ctx context.Context, clientID, clientSecret, tenantID, graphFilter string, cacheClient cache.ClientInterface) (*Client, error) { - a := &Client{ - clientID: clientID, - clientSecret: clientSecret, - tenantID: tenantID, - cacheClient: cacheClient, + authConfig := &auth.Config{ + Environment: hamiltonEnvironments.Global, + TenantID: tenantID, + ClientID: clientID, + ClientSecret: clientSecret, + EnableClientSecretAuth: true, } - var err error - - azureCredential, err := a.getAzureCredential(ctx) + authorizer, err := authConfig.NewAuthorizer(ctx) if err != nil { return nil, err } - usersClient, err := a.getAzureADUsersClient(ctx) - if err != nil { - return nil, err - } + usersClient := hamiltonClients.NewUsersClient(tenantID) + usersClient.BaseClient.Authorizer = authorizer - a.groupsClient, err = a.getAzureADGroupsClient(ctx) - if err != nil { - return nil, err - } + servicePrincipalsClient := hamiltonClients.NewServicePrincipalsClient(tenantID) + servicePrincipalsClient.BaseClient.Authorizer = authorizer + + groupsClient := hamiltonClients.NewGroupsClient(tenantID) + groupsClient.BaseClient.Authorizer = authorizer if graphFilter != "" { graphFilter = fmt.Sprintf("startswith(displayName,'%s')", graphFilter) } - a.graphFilter = graphFilter - a.user, err = newUser(ctx, cacheClient, usersClient) + user, err := newUser(ctx, cacheClient, usersClient) if err != nil { return nil, err } - a.servicePrincipalUser, err = newServicePrincipalUser(ctx, azureCredential, cacheClient) + servicePrincipalUser, err := newServicePrincipalUser(ctx, cacheClient, servicePrincipalsClient) if err != nil { return nil, err } - return a, nil + groups, err := newGroups(ctx, cacheClient, groupsClient, graphFilter) + if err != nil { + return nil, err + } + + return &Client{ + clientID: clientID, + clientSecret: clientSecret, + tenantID: tenantID, + graphFilter: graphFilter, + cacheClient: cacheClient, + user: user, + servicePrincipalUser: servicePrincipalUser, + groups: groups, + }, nil } // GetUserGroups ... @@ -104,7 +113,7 @@ func (client *Client) StartSyncGroups(ctx context.Context, syncInterval time.Dur ticker := time.NewTicker(syncInterval) syncChan := make(chan bool) - err := client.syncAzureADGroupsCache(ctx, "initial") + err := client.groups.syncAzureADGroupsCache(ctx, "initial") if err != nil { return nil, nil, err } @@ -116,104 +125,10 @@ func (client *Client) StartSyncGroups(ctx context.Context, syncInterval time.Dur log.Info("Stopped StartSyncTickerAzureADGroups") return case _ = <-ticker.C: - _ = client.syncAzureADGroupsCache(ctx, "ticker") + _ = client.groups.syncAzureADGroupsCache(ctx, "ticker") } } }() return ticker, syncChan, nil } - -func (client *Client) getAllGroups(ctx context.Context) ([]graphrbac.ADGroup, error) { - log := logr.FromContext(ctx) - - var groups []graphrbac.ADGroup - for list, err := client.groupsClient.List(ctx, client.graphFilter); list.NotDone(); err = list.NextWithContext(ctx) { - if err != nil { - log.Error(err, "Unable to list Azure AD groups", "graphFilter", client.graphFilter) - return nil, err - } - for _, group := range list.Values() { - groups = append(groups, group) - } - } - - return groups, nil -} - -func (client *Client) getAzureCredential(ctx context.Context) (*azidentity.ClientSecretCredential, error) { - log := logr.FromContext(ctx) - - cred, err := azidentity.NewClientSecretCredential(client.tenantID, client.clientID, client.clientSecret, nil) - if err != nil { - log.Error(err, "azidentity.NewClientSecretCredential") - return nil, err - } - return cred, nil -} - -func (client *Client) getGraphAuthorizer(ctx context.Context) (autorest.Authorizer, error) { - log := logr.FromContext(ctx) - - cred, err := azidentity.NewClientSecretCredential(client.tenantID, client.clientID, client.clientSecret, nil) - if err != nil { - log.Error(err, "azidentity.NewClientSecretCredential") - return nil, err - } - - authorizer := azidext.NewAzureIdentityCredentialAdapter( - cred, - azcore.AuthenticationPolicyOptions{ - Options: azcore.TokenRequestOptions{ - Scopes: []string{"https://graph.windows.net/.default"}}}) - - return authorizer, nil -} - -func (client *Client) getAzureADGroupsClient(ctx context.Context) (graphrbac.GroupsClient, error) { - groupsClient := graphrbac.NewGroupsClient(client.tenantID) - authorizer, err := client.getGraphAuthorizer(ctx) - if err != nil { - return graphrbac.GroupsClient{}, err - } - - groupsClient.Authorizer = authorizer - - return groupsClient, nil -} - -func (client *Client) getAzureADUsersClient(ctx context.Context) (graphrbac.UsersClient, error) { - usersClient := graphrbac.NewUsersClient(client.tenantID) - authorizer, err := client.getGraphAuthorizer(ctx) - if err != nil { - return graphrbac.UsersClient{}, err - } - - usersClient.Authorizer = authorizer - - return usersClient, nil -} - -func (client *Client) syncAzureADGroupsCache(ctx context.Context, syncReason string) error { - log := logr.FromContext(ctx) - - groups, err := client.getAllGroups(ctx) - if err != nil { - log.Error(err, "Unable to syncronize groups") - return err - } - - for _, group := range groups { - _, found, err := client.cacheClient.GetGroup(ctx, *group.ObjectID) - if err != nil { - return err - } - if !found { - client.cacheClient.SetGroup(ctx, *group.ObjectID, models.Group{Name: *group.DisplayName}) - } - } - - log.Info("Synchronized Azure AD groups to cache", "groupCount", len(groups), "syncReason", syncReason) - - return nil -} diff --git a/pkg/azure/azure_test.go b/pkg/azure/azure_test.go index fa3e759c..89f28030 100644 --- a/pkg/azure/azure_test.go +++ b/pkg/azure/azure_test.go @@ -55,7 +55,7 @@ func TestNewAzureClient(t *testing.T) { tenantID: "", graphFilter: "", cacheClient: memCache, - expectedErrContains: "Client Secret Credential: Invalid tenantID provided.", + expectedErrContains: "no Authorizer could be configured, please check your configuration", }, { clientID: "", @@ -63,7 +63,7 @@ func TestNewAzureClient(t *testing.T) { tenantID: tenantID, graphFilter: "", cacheClient: memCache, - expectedErrContains: "AADSTS7000216", + expectedErrContains: "no Authorizer could be configured, please check your configuration", }, } @@ -118,12 +118,12 @@ func TestGetUserGroups(t *testing.T) { { objectID: "", userType: models.NormalUserType, - expectedErrContains: "Failure responding to request: StatusCode=405", + expectedErrContains: "unexpected status 404 with response:", }, { objectID: "", userType: models.ServicePrincipalUserType, - expectedErrContains: "Status code not 200 OK: 400", + expectedErrContains: "unexpected status 400 with response:", }, } diff --git a/pkg/azure/groups.go b/pkg/azure/groups.go new file mode 100644 index 00000000..ca213974 --- /dev/null +++ b/pkg/azure/groups.go @@ -0,0 +1,61 @@ +package azure + +import ( + "context" + + "github.com/go-logr/logr" + hamiltonClients "github.com/manicminer/hamilton/clients" + hamiltonModels "github.com/manicminer/hamilton/models" + "github.com/xenitab/azad-kube-proxy/pkg/cache" + "github.com/xenitab/azad-kube-proxy/pkg/models" +) + +type groups struct { + cacheClient cache.ClientInterface + groupsClient *hamiltonClients.GroupsClient + graphFilter string +} + +func newGroups(ctx context.Context, cacheClient cache.ClientInterface, groupsClient *hamiltonClients.GroupsClient, graphFilter string) (*groups, error) { + return &groups{ + cacheClient: cacheClient, + groupsClient: groupsClient, + graphFilter: graphFilter, + }, nil +} + +func (groups *groups) getAllGroups(ctx context.Context) (*[]hamiltonModels.Group, error) { + log := logr.FromContext(ctx) + + groupsResponse, responseCode, err := groups.groupsClient.List(ctx, groups.graphFilter) + if err != nil { + log.Error(err, "Unable to get groups", "responseCode", responseCode) + return nil, err + } + + return groupsResponse, nil +} + +func (groups *groups) syncAzureADGroupsCache(ctx context.Context, syncReason string) error { + log := logr.FromContext(ctx) + + groupsResponse, err := groups.getAllGroups(ctx) + if err != nil { + log.Error(err, "Unable to syncronize groups") + return err + } + + for _, group := range *groupsResponse { + _, found, err := groups.cacheClient.GetGroup(ctx, *group.ID) + if err != nil { + return err + } + if !found { + groups.cacheClient.SetGroup(ctx, *group.ID, models.Group{Name: *group.DisplayName}) + } + } + + log.Info("Synchronized Azure AD groups to cache", "groupCount", len(*groupsResponse), "syncReason", syncReason) + + return nil +} diff --git a/pkg/azure/service_principal.go b/pkg/azure/service_principal.go index 75dd3bc4..bc49ce56 100644 --- a/pkg/azure/service_principal.go +++ b/pkg/azure/service_principal.go @@ -2,39 +2,23 @@ package azure import ( "context" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "net/url" - "time" - "github.com/Azure/azure-sdk-for-go/sdk/azcore" - "github.com/Azure/azure-sdk-for-go/sdk/azidentity" "github.com/go-logr/logr" + hamiltonClients "github.com/manicminer/hamilton/clients" "github.com/xenitab/azad-kube-proxy/pkg/cache" "github.com/xenitab/azad-kube-proxy/pkg/models" ) type servicePrincipalUser struct { - azureCredential *azidentity.ClientSecretCredential - cacheClient cache.ClientInterface - msGraphToken *azcore.AccessToken + cacheClient cache.ClientInterface + servicePrincipalsClient *hamiltonClients.ServicePrincipalsClient } -func newServicePrincipalUser(ctx context.Context, azureCredential *azidentity.ClientSecretCredential, cacheClient cache.ClientInterface) (*servicePrincipalUser, error) { - user := &servicePrincipalUser{ - azureCredential: azureCredential, - cacheClient: cacheClient, - } - - var err error - user.msGraphToken, err = user.getMSGraphToken(ctx) - if err != nil { - return nil, err - } - - return user, nil +func newServicePrincipalUser(ctx context.Context, cacheClient cache.ClientInterface, servicePrincipalsClient *hamiltonClients.ServicePrincipalsClient) (*servicePrincipalUser, error) { + return &servicePrincipalUser{ + cacheClient: cacheClient, + servicePrincipalsClient: servicePrincipalsClient, + }, nil } func (user *servicePrincipalUser) getGroups(ctx context.Context, objectID string) ([]models.Group, error) { @@ -63,85 +47,16 @@ func (user *servicePrincipalUser) getGroups(ctx context.Context, objectID string func (user *servicePrincipalUser) getServicePrincipalGroups(ctx context.Context, objectID string) ([]string, error) { log := logr.FromContext(ctx) - url, err := url.Parse(fmt.Sprintf("https://graph.microsoft.com/v1.0/servicePrincipals/%s/transitiveMemberOf/microsoft.graph.group?$select=id", objectID)) - if err != nil { - log.Error(err, "Unable to parse URL") - return nil, err - } - - msGraphToken, err := user.getMSGraphToken(ctx) + groupsResponse, responseCode, err := user.servicePrincipalsClient.ListGroupMemberships(ctx, objectID, "") if err != nil { - log.Error(err, "Unable to get MS Graph Token") + log.Error(err, "Unable to get Azure AD groups for service principal", "objectID", objectID, "responseCode", responseCode) return nil, err } - req := &http.Request{ - Method: "GET", - URL: url, - Header: map[string][]string{ - "Authorization": {fmt.Sprintf("Bearer %s", msGraphToken.Token)}, - "Content-type": {"application/json"}, - }, - } - - res, err := http.DefaultClient.Do(req) - if err != nil { - log.Error(err, "Unable to get Azure AD groups for service principal", "objectID", objectID) - return nil, err - } - - if res.StatusCode != 200 { - err := fmt.Errorf("Status code not 200 OK: %d", res.StatusCode) - log.Error(err, "Unable to get Azure AD groups for service principal", "objectID", objectID) - return nil, err - } - - defer res.Body.Close() - - body, err := ioutil.ReadAll(res.Body) - if err != nil { - log.Error(err, "Unable to read body of response", "objectID", objectID) - return nil, err - } - - type responseValue struct { - ID string `json:"id"` - } - - var responseData struct { - Values []responseValue `json:"value"` - } - - json.Unmarshal(body, &responseData) - var groups []string - for _, group := range responseData.Values { - groups = append(groups, group.ID) + for _, group := range *groupsResponse { + groups = append(groups, *group.ID) } return groups, nil } - -// GetMSGraphToken generates a new token if none exists and an existing if it not expired -func (user *servicePrincipalUser) getMSGraphToken(ctx context.Context) (*azcore.AccessToken, error) { - log := logr.FromContext(ctx) - - generateNewToken := true - if user.msGraphToken != nil { - if user.msGraphToken.ExpiresOn.After(time.Now().Add(-5 * time.Minute)) { - generateNewToken = false - } - } - - if generateNewToken { - token, err := user.azureCredential.GetToken(ctx, azcore.TokenRequestOptions{Scopes: []string{"https://graph.microsoft.com/.default"}}) - if err != nil { - log.Error(err, "client.AzureCredential.GetToken") - return nil, err - } - - return token, nil - } - - return user.msGraphToken, nil -} diff --git a/pkg/azure/user.go b/pkg/azure/user.go index db30c15b..6b2a8de3 100644 --- a/pkg/azure/user.go +++ b/pkg/azure/user.go @@ -3,19 +3,18 @@ package azure import ( "context" - "github.com/Azure/azure-sdk-for-go/sdk/to" - "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" "github.com/go-logr/logr" + hamiltonClients "github.com/manicminer/hamilton/clients" "github.com/xenitab/azad-kube-proxy/pkg/cache" "github.com/xenitab/azad-kube-proxy/pkg/models" ) type user struct { cacheClient cache.ClientInterface - usersClient graphrbac.UsersClient + usersClient *hamiltonClients.UsersClient } -func newUser(ctx context.Context, cacheClient cache.ClientInterface, usersClient graphrbac.UsersClient) (*user, error) { +func newUser(ctx context.Context, cacheClient cache.ClientInterface, usersClient *hamiltonClients.UsersClient) (*user, error) { user := &user{ cacheClient: cacheClient, usersClient: usersClient, @@ -50,13 +49,16 @@ func (user *user) getGroups(ctx context.Context, objectID string) ([]models.Grou func (user *user) getUserGroups(ctx context.Context, objectID string) ([]string, error) { log := logr.FromContext(ctx) - groupsResponse, err := user.usersClient.GetMemberGroups(ctx, objectID, graphrbac.UserGetMemberGroupsParameters{ - SecurityEnabledOnly: to.BoolPtr(false), - }) + groupsResponse, responseCode, err := user.usersClient.ListGroupMemberships(ctx, objectID, "") if err != nil { - log.Error(err, "Unable to get Azure AD groups for user", "objectID", objectID) + log.Error(err, "Unable to get Azure AD groups for user", "objectID", objectID, "responseCode", responseCode) return nil, err } - return *groupsResponse.Value, nil + var groups []string + for _, group := range *groupsResponse { + groups = append(groups, *group.ID) + } + + return groups, nil }