From 6b0e1e91ebfc8f02dc0556fe61f192fea0994779 Mon Sep 17 00:00:00 2001 From: Haytham Abuelfutuh Date: Mon, 1 Mar 2021 20:12:46 -0800 Subject: [PATCH] Support Google OAuth2 and other OIdC providers (#147) * Move scopes to config Signed-off-by: Haytham Abuelfutuh * missed Signed-off-by: Haytham Abuelfutuh * wip Signed-off-by: Haytham Abuelfutuh * Update handlers.go Signed-off-by: Haytham Abuelfutuh * go get propeller at v0.5.12 (#146) Signed-off-by: Haytham Abuelfutuh * Update to using latest flytepropeller v0.5.13 (#148) Signed-off-by: Haytham Abuelfutuh * Update propeller to 0.5.14 (#149) * Update propeller * cleanup Signed-off-by: Haytham Abuelfutuh * Filter executions by user (#150) Signed-off-by: Haytham Abuelfutuh * Update CI post migration Signed-off-by: Haytham Abuelfutuh * Update codecov link Signed-off-by: Haytham Abuelfutuh * Publish raw events (#151) Signed-off-by: Haytham Abuelfutuh * fix test Signed-off-by: Haytham Abuelfutuh * wip Signed-off-by: Haytham Abuelfutuh * Fix token retrieval from cookies Signed-off-by: Haytham Abuelfutuh * Fix unit tests and lint Signed-off-by: Haytham Abuelfutuh * Move to const Signed-off-by: Haytham Abuelfutuh * Revert Auth config Signed-off-by: Haytham Abuelfutuh * Revert kube config path Signed-off-by: Haytham Abuelfutuh * Use access token when posting to IdP Signed-off-by: Haytham Abuelfutuh * Ignore refresh token error Signed-off-by: Haytham Abuelfutuh * Fix tests Signed-off-by: Haytham Abuelfutuh * Expose openId metadata endpoint and expose scopes in /config endpoint Signed-off-by: Haytham Abuelfutuh * mod tidy Signed-off-by: Haytham Abuelfutuh * rename Signed-off-by: Haytham Abuelfutuh * lint Signed-off-by: Haytham Abuelfutuh * unit tests Signed-off-by: Haytham Abuelfutuh Co-authored-by: Yee Hing Tong Co-authored-by: Katrina Rogan Co-authored-by: tnsetting <38207208+tnsetting@users.noreply.github.com> --- flyteadmin/cmd/entrypoints/serve.go | 6 +- flyteadmin/flyteadmin_config.yaml | 11 ++-- flyteadmin/go.sum | 64 ------------------- flyteadmin/pkg/auth/auth_context.go | 56 ++++++++++------ flyteadmin/pkg/auth/config/config.go | 5 ++ flyteadmin/pkg/auth/constants.go | 9 ++- flyteadmin/pkg/auth/cookie.go | 36 ++++++++++- flyteadmin/pkg/auth/cookie_manager.go | 60 ++++++++++------- flyteadmin/pkg/auth/cookie_manager_test.go | 29 ++++++--- flyteadmin/pkg/auth/handlers.go | 50 ++++++++------- flyteadmin/pkg/auth/handlers_test.go | 19 ++++-- flyteadmin/pkg/auth/interfaces/context.go | 3 +- flyteadmin/pkg/auth/interfaces/cookie.go | 2 +- .../mocks/authentication_context.go | 56 ++++++++++++---- flyteadmin/pkg/auth/token.go | 11 +++- flyteadmin/pkg/config/config.go | 6 ++ flyteadmin/pkg/rpc/config/flyte_client.go | 6 +- .../pkg/rpc/config/flyte_client_test.go | 1 + 18 files changed, 257 insertions(+), 173 deletions(-) diff --git a/flyteadmin/cmd/entrypoints/serve.go b/flyteadmin/cmd/entrypoints/serve.go index c36d523b2..1325e95c3 100644 --- a/flyteadmin/cmd/entrypoints/serve.go +++ b/flyteadmin/cmd/entrypoints/serve.go @@ -153,8 +153,12 @@ func newHTTPServer(ctx context.Context, cfg *config.ServerConfig, authContext in if authContext.GetUserInfoURL() != nil && authContext.GetUserInfoURL().String() != "" { mux.HandleFunc("/me", auth.GetMeEndpointHandler(ctx, authContext)) } + + // The metadata endpoint is an RFC-defined constant, but we need a leading / for the handler to pattern match correctly. + mux.HandleFunc(fmt.Sprintf("/%s", auth.OIdCMetadataEndpoint), auth.GetOIdCMetadataEndpointRedirectHandler(ctx, authContext)) + // The metadata endpoint is an RFC-defined constant, but we need a leading / for the handler to pattern match correctly. - mux.HandleFunc(fmt.Sprintf("/%s", auth.MetadataEndpoint), auth.GetMetadataEndpointRedirectHandler(ctx, authContext)) + mux.HandleFunc(fmt.Sprintf("/%s", auth.OAuth2MetadataEndpoint), auth.GetOAuth2MetadataEndpointRedirectHandler(ctx, authContext)) // This option translates HTTP authorization data (cookies) into a gRPC metadata field gwmuxOptions = append(gwmuxOptions, runtime.WithMetadata(auth.GetHTTPRequestCookieToMetadataHandler(authContext))) diff --git a/flyteadmin/flyteadmin_config.yaml b/flyteadmin/flyteadmin_config.yaml index e62521ada..0b55257d8 100644 --- a/flyteadmin/flyteadmin_config.yaml +++ b/flyteadmin/flyteadmin_config.yaml @@ -8,10 +8,13 @@ server: grpcServerReflection: true security: secure: false - ssl: - certificateFile: "/path/to/server.pem" - keyFile: "/path/to/server.key" useAuth: false + allowCors: true + allowedOrigins: + # Accepting all domains for Sandbox installation + - "*" + allowedHeaders: + - "Content-Type" oauth: clientId: yourclientid clientSecretFile: "/path/to/oauth/secret" @@ -73,7 +76,7 @@ notifications: accountId: "bar" emailer: subject: "Notice: Execution \"{{ name }}\" has {{ phase }} in \"{{ domain }}\"." - sender: "flyte-notifications@example.com" + sender: "flyte-notifications@example.com" body: > Execution \"{{ name }}\" has {{ phase }} in \"{{ domain }}\". View details at diff --git a/flyteadmin/go.sum b/flyteadmin/go.sum index 5d22baedf..6137e385d 100644 --- a/flyteadmin/go.sum +++ b/flyteadmin/go.sum @@ -455,16 +455,11 @@ github.com/lyft/apimachinery v0.0.0-20191031200210-047e3ea32d7f/go.mod h1:llRdnz github.com/lyft/datacatalog v0.2.1/go.mod h1:ktrPvzTDUwHO5Lv0hLH38zLHnOJ++rGoAO0iQ/sIPJ4= github.com/lyft/flyteidl v0.17.0/go.mod h1:/zQXxuHO11u/saxTTZc8oYExIGEShXB+xCB1/F1Cu20= github.com/lyft/flyteidl v0.18.9/go.mod h1:/zQXxuHO11u/saxTTZc8oYExIGEShXB+xCB1/F1Cu20= -github.com/lyft/flyteidl v0.18.11 h1:24NaFYWxANhRbwKfvkgu8axGTWUcl1tgZBqNJutKNJ8= -github.com/lyft/flyteidl v0.18.11/go.mod h1:/zQXxuHO11u/saxTTZc8oYExIGEShXB+xCB1/F1Cu20= -github.com/lyft/flyteidl v0.18.14-0.20210224014048-73cf22d60fd5 h1:m2CBZEr1RC69bZe8VG3xryNfe+jOBUTKze9Zj6sJCms= -github.com/lyft/flyteidl v0.18.14-0.20210224014048-73cf22d60fd5/go.mod h1:JTJC2VqrpEWM/76lPF2Dj9l4FA8FjZh67U0RYuq/Aes= github.com/lyft/flyteidl v0.18.14 h1:c5tBItxLZA77HNRdYiHEZ9UyVUq9I3wiYKDuF/Om/xs= github.com/lyft/flyteidl v0.18.14/go.mod h1:JTJC2VqrpEWM/76lPF2Dj9l4FA8FjZh67U0RYuq/Aes= github.com/lyft/flyteplugins v0.5.20/go.mod h1:1G2YORr5JZhfl4dyHGIO4/+ShNMul5DPnoBEdpJfuNc= github.com/lyft/flytepropeller v0.5.14 h1:6S93Q1tTnoc5iFKe4Sw4qZzQHZgfuTQd6zUdxaZGAik= github.com/lyft/flytepropeller v0.5.14/go.mod h1:P63eiQpUI/57ifXNoHc/NSszU8PYLcJ+fujI4wQ99rI= -github.com/lyft/flytestdlib v0.3.0 h1:nIkX4MlyYdcLLzaF35RI2P5BhARt+qMgHoFto8eVNzU= github.com/lyft/flytestdlib v0.3.0/go.mod h1:LJPPJlkFj+wwVWMrQT3K5JZgNhZi2mULsCG4ZYhinhU= github.com/lyft/flytestdlib v0.3.9 h1:NaKp9xkeWWwhVvqTOcR/FqlASy1N2gu/kN7PVe4S7YI= github.com/lyft/flytestdlib v0.3.9/go.mod h1:LJPPJlkFj+wwVWMrQT3K5JZgNhZi2mULsCG4ZYhinhU= @@ -479,13 +474,11 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= @@ -496,7 +489,6 @@ github.com/mattn/go-sqlite3 v2.0.1+incompatible h1:xQ15muvnzGBHpIpdrNi1DA5x0+TcB github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= @@ -511,7 +503,6 @@ github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8m github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/ncw/swift v1.0.49 h1:eQaKIjSt/PXLKfYgzg01nevmO+CMXfXGRhB1gOhDs7E= github.com/ncw/swift v1.0.49/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= github.com/ncw/swift v1.0.50 h1:E01b5bVIssNhx2KnzAjMWEXkKrb8ytTqCDWY7lqmWjA= github.com/ncw/swift v1.0.50/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= @@ -523,7 +514,6 @@ github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:v github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw= @@ -531,7 +521,6 @@ github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+ github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.8.1 h1:C5Dqfs/LeauYDX0jJXIe2SWmwCbGzx9yF8C8xy3Lh34= @@ -547,7 +536,6 @@ github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR github.com/pierrec/lz4 v0.0.0-20190327172049-315a67e90e41/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -565,18 +553,14 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.3.0 h1:miYCvYqFXtl/J9FIy8eNpBfYthAEFg+Ys0XyUVEcDsc= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.5.0 h1:Ctq0iGpCmr3jeP77kbF2UxgvRwzWWz+4Bh9/vJTyg1A= github.com/prometheus/client_golang v1.5.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.1.0 h1:ElTg5tNp4DqfV7UQjDqv2+RJlNzsDtvNAWccbItceIE= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -586,7 +570,6 @@ github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7q github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.7.0 h1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= @@ -596,7 +579,6 @@ github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.0.10 h1:QJQN3jYQhkamO4mhfUWqdDH2asK7ONOI9MTWjyAxNKM= github.com/prometheus/procfs v0.0.10/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= @@ -610,7 +592,6 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/satori/uuid v1.2.0 h1:6TFY4nxn5XwBx0gDfzbEMCNT6k4N/4FNIuN8RACZ0KI= github.com/satori/uuid v1.2.0/go.mod h1:B8HLsPLik/YNn6KKWVMDJ8nzCL8RP5WyfsnmvnAEwIU= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= @@ -628,12 +609,10 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v0.0.6 h1:breEStsVwemnKh2/s6gMvSdMEkwW0sK8vGStnlVBMCs= github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= @@ -646,22 +625,18 @@ github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.6.2 h1:7aKfF+e8/k68gda3LOjo5RxiUqddoFxVq4BKBPrxk5E= github.com/spf13/viper v1.6.2/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= 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 v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -679,7 +654,6 @@ github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= @@ -690,7 +664,6 @@ go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qL go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2 h1:75k/FF0Q2YM8QYo07VPddOLBslDt1MZOdEslOHvmzAs= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -720,9 +693,7 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200117160349-530e935923ad h1:Jh8cai0fqIK+f6nG0UgPW5wFk8wmiMhM3AyciDBdtQg= golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 h1:3zb4D3T4G8jdExgVU/95+vQXfpEPiMdCaZgmGVxjNHM= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -757,7 +728,6 @@ golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKG golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -789,24 +759,17 @@ golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa h1:F+8P+gmewFQYRk6JoLQLwjBCTu3mcIURZfNkVweuRKA= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= @@ -816,11 +779,8 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -856,18 +816,13 @@ golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 h1:1/DFK4b7JH8DmkqhUk48onnSfrPzImPoVxuomtbT2nk= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 h1:sfkvUWPNGwSV+8/fNqctR5lS2AqCSqYwXdrjCxp/dXo= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775 h1:TC0v2RSO1u2kn1ZugjrFXkRZAEaqMN/RW+OTZkBzmLE= golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d h1:nc5K6ox/4lTFbMVSL9WRR81ixkcwXThoiF6yf+R9scA= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k= @@ -876,7 +831,6 @@ golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fq golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -925,16 +879,12 @@ golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb h1:iKlO7ROJc6SttHKlxzwGytRtBUqX4VARrNTgP2YLX5M= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200818005847-188abfa75333 h1:a6ryybeZHQf5qnBc6IwRfVnI/75UmdtJo71f0//8Dqo= -golang.org/x/tools v0.0.0-20200818005847-188abfa75333/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -954,7 +904,6 @@ google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsb google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.16.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.17.0 h1:0q95w+VuFtv4PAx4PZVQdBMmYbaCHbnfKaEiDIcVyag= google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.20.0 h1:jz2KixHX7EcCPiQrySzPdnYT7DbINAypCqKZ1Z7GM40= @@ -982,15 +931,12 @@ google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150 h1:VPpdpQkGvFicX9yo4G5oxZPi9ALBnEOZblPSa/Wa2m4= google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= google.golang.org/genproto v0.0.0-20200205142000-a86caf926a67/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce h1:1mbrb1tUU+Zmt5C94IGKADBTJZjZXAd+BubWi7r9EiI= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672 h1:jiDSspVssiikoRPFHT6pYrL+CL6/yIc3b9AuHO/4xik= google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940 h1:MRHtG0U6SnaUb+s+LhNE1qt1FQ1wlhqr5E4usBKC0uA= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= @@ -1002,21 +948,16 @@ google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ij google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.24.0 h1:vb/1TCsVn3DcJlQ0Gs1yB1pKI6Do2/QNwxdKqmc/b0s= google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0 h1:bO/TA4OxCOummhSf10siHuG7vJOiwh7SpRpFZDkOgl4= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1030,7 +971,6 @@ gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.51.1 h1:GyboHr4UqMiLUybYjd22ZjQIKEJEpgtLXtuGbR21Oho= gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.54.0 h1:oM5ElzbIi7gwLnNbPX2M25ED1vSAK3B6dex50eS/6Fs= gopkg.in/ini.v1 v1.54.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= @@ -1065,7 +1005,6 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= @@ -1081,7 +1020,6 @@ k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8 k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v0.4.0 h1:lCJCxf/LIowc2IGS9TPjWDyXY4nOmdGdfcwwDQCOURQ= k8s.io/klog v0.4.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= @@ -1093,7 +1031,6 @@ k8s.io/kube-openapi v0.0.0-20200204173128-addea2498afe/go.mod h1:GRQhZsXIAJ1xR0C k8s.io/utils v0.0.0-20190506122338-8fab8cb257d5/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -k8s.io/utils v0.0.0-20200124190032-861946025e34 h1:HjlUD6M0K3P8nRXmr2B9o4F9dUy9TCj/aEpReeyi6+k= k8s.io/utils v0.0.0-20200124190032-861946025e34/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20200229041039-0a110f9eb7ab h1:I3f2hcBrepGRXI1z4sukzAb8w1R4eqbsHrAsx06LGYM= k8s.io/utils v0.0.0-20200229041039-0a110f9eb7ab/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= @@ -1112,7 +1049,6 @@ sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:w sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18= sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= sigs.k8s.io/testing_frameworks v0.1.1/go.mod h1:VVBKrHmJ6Ekkfz284YKhQePcdycOzNH9qL6ht1zEr/U= -sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/flyteadmin/pkg/auth/auth_context.go b/flyteadmin/pkg/auth/auth_context.go index b6f757480..95bc2c307 100644 --- a/flyteadmin/pkg/auth/auth_context.go +++ b/flyteadmin/pkg/auth/auth_context.go @@ -22,15 +22,16 @@ const ( // Please see the comment on the corresponding AuthenticationContext for more information. type Context struct { - oauth2 *oauth2.Config - claims config.Claims - cookieManager interfaces.CookieHandler - oidcProvider *oidc.Provider - options config.OAuthOptions - userInfoURL *url.URL - baseURL *url.URL - metadataURL *url.URL - httpClient *http.Client + oauth2 *oauth2.Config + claims config.Claims + cookieManager interfaces.CookieHandler + oidcProvider *oidc.Provider + options config.OAuthOptions + userInfoURL *url.URL + baseURL *url.URL + oauth2MetadataURL *url.URL + oidcMetadataURL *url.URL + httpClient *http.Client } func (c Context) OAuth2Config() *oauth2.Config { @@ -65,8 +66,12 @@ func (c Context) GetBaseURL() *url.URL { return c.baseURL } -func (c Context) GetMetadataURL() *url.URL { - return c.metadataURL +func (c Context) GetOAuth2MetadataURL() *url.URL { + return c.oauth2MetadataURL +} + +func (c Context) GetOIdCMetadataURL() *url.URL { + return c.oidcMetadataURL } const ( @@ -85,6 +90,7 @@ func NewAuthenticationContext(ctx context.Context, options config.OAuthOptions) if err != nil { return Context{}, errors.Wrapf(ErrAuthContext, err, "Error creating OAuth2 library configuration") } + result.oauth2 = &oauth2Config // Construct the cookie manager object. @@ -92,23 +98,27 @@ func NewAuthenticationContext(ctx context.Context, options config.OAuthOptions) if err != nil { return Context{}, errors.Wrapf(ErrConfigFileRead, err, "Could not read hash key file") } + blockKeyBytes, err := ioutil.ReadFile(options.CookieBlockKeyFile) if err != nil { return Context{}, errors.Wrapf(ErrConfigFileRead, err, "Could not read block key file") } + cookieManager, err := NewCookieManager(ctx, string(hashKeyBytes), string(blockKeyBytes)) if err != nil { logger.Errorf(ctx, "Error creating cookie manager %s", err) return Context{}, errors.Wrapf(ErrAuthContext, err, "Error creating cookie manager") } + result.cookieManager = cookieManager // Construct an oidc Provider, which needs its own http Client. oidcCtx := oidc.ClientContext(ctx, &http.Client{}) provider, err := oidc.NewProvider(oidcCtx, options.Claims.Issuer) if err != nil { - return Context{}, errors.Wrapf(ErrAuthContext, err, "Error creating oidc provider") + return Context{}, errors.Wrapf(ErrAuthContext, err, "Error creating oidc provider w/ issuer [%v]", options.Claims.Issuer) } + result.oidcProvider = provider // TODO: Convert all the URLs in this config to the config.URL type @@ -120,16 +130,25 @@ func NewAuthenticationContext(ctx context.Context, options config.OAuthOptions) logger.Errorf(ctx, "Error parsing base URL %s", err) return Context{}, errors.Wrapf(ErrAuthContext, err, "Error parsing IDP base URL") } + logger.Infof(ctx, "Base IDP URL is %s", base) result.baseURL = base - metadataURL, err := url.Parse(MetadataEndpoint) + result.oauth2MetadataURL, err = url.Parse(OAuth2MetadataEndpoint) if err != nil { - logger.Errorf(ctx, "Error parsing metadata URL %s", err) + logger.Errorf(ctx, "Error parsing oauth2 metadata URL %s", err) return Context{}, errors.Wrapf(ErrAuthContext, err, "Error parsing metadata URL") } - logger.Infof(ctx, "Metadata endpoint is %s", metadataURL) - result.metadataURL = metadataURL + + logger.Infof(ctx, "Metadata endpoint is %s", result.oauth2MetadataURL) + + result.oidcMetadataURL, err = url.Parse(OIdCMetadataEndpoint) + if err != nil { + logger.Errorf(ctx, "Error parsing oidc metadata URL %s", err) + return Context{}, errors.Wrapf(ErrAuthContext, err, "Error parsing metadata URL") + } + + logger.Infof(ctx, "Metadata endpoint is %s", result.oidcMetadataURL) // Construct the URL object for the user info endpoint if applicable if options.IdpUserInfoEndpoint != "" { @@ -158,14 +177,13 @@ func GetOauth2Config(options config.OAuthOptions) (oauth2.Config, error) { if err != nil { return oauth2.Config{}, err } + secret := strings.TrimSuffix(string(secretBytes), "\n") return oauth2.Config{ RedirectURL: options.CallbackURL, ClientID: options.ClientID, ClientSecret: secret, - // Offline access needs to be specified in order to return a refresh token in the exchange. - // TODO: Second parameter is IDP specific - move to config. Also handle case where a refresh token is not allowed - Scopes: []string{OidcScope, OfflineAccessType, ProfileScope}, + Scopes: options.Scopes, Endpoint: oauth2.Endpoint{ AuthURL: options.AuthorizeURL, TokenURL: options.TokenURL, diff --git a/flyteadmin/pkg/auth/config/config.go b/flyteadmin/pkg/auth/config/config.go index dde837a01..dcc5d6d39 100644 --- a/flyteadmin/pkg/auth/config/config.go +++ b/flyteadmin/pkg/auth/config/config.go @@ -58,6 +58,11 @@ type OAuthOptions struct { // into the realm of authorization rather than authentication. DisableForHTTP bool `json:"disableForHttp"` DisableForGrpc bool `json:"disableForGrpc"` + + // Provides a list of scopes to request from the IDP when authenticating. Default value requests claims that should + // be supported by any OIdC server. Refer to https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims for + // a complete list. Other providers might support additional scopes that you can define in a config. + Scopes []string `json:"scopes"` } type Claims struct { diff --git a/flyteadmin/pkg/auth/constants.go b/flyteadmin/pkg/auth/constants.go index 707ecb77e..58540510f 100644 --- a/flyteadmin/pkg/auth/constants.go +++ b/flyteadmin/pkg/auth/constants.go @@ -3,15 +3,14 @@ package auth // OAuth2 Parameters const CsrfFormKey = "state" const AuthorizationResponseCodeType = "code" -const OidcScope = "openid" -const ProfileScope = "profile" const RefreshToken = "refresh_token" const DefaultAuthorizationHeader = "authorization" const BearerScheme = "Bearer" // https://tools.ietf.org/html/rfc8414 // This should be defined without a leading slash. If there is one, the url library's ResolveReference will make it a root path -const MetadataEndpoint = ".well-known/oauth-authorization-server" +const OAuth2MetadataEndpoint = ".well-known/oauth-authorization-server" -// IDP specific -const OfflineAccessType = "offline_access" +// https://openid.net/specs/openid-connect-discovery-1_0.html +// This should be defined without a leading slash. If there is one, the url library's ResolveReference will make it a root path +const OIdCMetadataEndpoint = ".well-known/openid-configuration" diff --git a/flyteadmin/pkg/auth/cookie.go b/flyteadmin/pkg/auth/cookie.go index 848e20f35..70e7436b3 100644 --- a/flyteadmin/pkg/auth/cookie.go +++ b/flyteadmin/pkg/auth/cookie.go @@ -17,13 +17,18 @@ import ( const ( // #nosec - accessTokenCookieName = "flyte_jwt" + accessTokenCookieName = "flyte_at" // #nosec - refreshTokenCookieName = "flyte_refresh" + idTokenCookieName = "flyte_idt" + // #nosec + refreshTokenCookieName = "flyte_rt" // #nosec csrfStateCookieName = "flyte_csrf_state" // #nosec redirectURLCookieName = "flyte_redirect_location" + + // #nosec + idTokenExtra = "id_token" ) const ( @@ -54,6 +59,33 @@ func NewSecureCookie(cookieName, value string, hashKey, blockKey []byte) (http.C return http.Cookie{}, errors.Wrapf(ErrSecureCookie, err, "Error creating secure cookie") } +func retrieveSecureCookie(ctx context.Context, request *http.Request, cookieName string, hashKey, blockKey []byte) (string, error) { + cookie, err := request.Cookie(cookieName) + if err != nil { + logger.Infof(ctx, "Could not detect existing cookie [%v]. Error: %v", cookieName, err) + return "", errors.Wrapf(ErrTokenNil, err, "Failure to retrieve cookie [%v]", cookieName) + } + + if cookie == nil { + logger.Infof(ctx, "Retrieved empty cookie [%v].", cookieName) + return "", errors.Errorf(ErrTokenNil, "Retrieved empty cookie [%v]", cookieName) + } + + logger.Debugf(ctx, "Existing [%v] cookie found", cookieName) + token, err := ReadSecureCookie(ctx, *cookie, hashKey, blockKey) + if err != nil { + logger.Errorf(ctx, "Error reading existing secure cookie [%v]. Error: %s", cookieName, err) + return "", errors.Errorf(ErrTokenNil, "Error reading existing secure cookie [%v]. Error: %s", cookieName, err) + } + + if len(token) == 0 { + logger.Errorf(ctx, "Read empty token from secure cookie [%v].", cookieName) + return "", errors.Errorf(ErrTokenNil, "Read empty token from secure cookie [%v].", cookieName) + } + + return token, nil +} + func ReadSecureCookie(ctx context.Context, cookie http.Cookie, hashKey, blockKey []byte) (string, error) { var s = securecookie.New(hashKey, blockKey) var value string diff --git a/flyteadmin/pkg/auth/cookie_manager.go b/flyteadmin/pkg/auth/cookie_manager.go index 7f228b8a0..f490d6465 100644 --- a/flyteadmin/pkg/auth/cookie_manager.go +++ b/flyteadmin/pkg/auth/cookie_manager.go @@ -20,6 +20,8 @@ const ( ErrB64Decoding errors.ErrorCode = "BINARY_DECODING_FAILED" // #nosec ErrTokenNil errors.ErrorCode = "EMPTY_OAUTH_TOKEN" + // #nosec + ErrNoIDToken errors.ErrorCode = "NO_ID_TOKEN_IN_RESPONSE" ) func NewCookieManager(ctx context.Context, hashKeyEncoded, blockKeyEncoded string) (CookieManager, error) { @@ -29,6 +31,7 @@ func NewCookieManager(ctx context.Context, hashKeyEncoded, blockKeyEncoded strin if err != nil { return CookieManager{}, errors.Wrapf(ErrB64Decoding, err, "Error decoding hash key bytes") } + blockKey, err := base64.RawStdEncoding.DecodeString(blockKeyEncoded) if err != nil { return CookieManager{}, errors.Wrapf(ErrB64Decoding, err, "Error decoding block key bytes") @@ -41,32 +44,30 @@ func NewCookieManager(ctx context.Context, hashKeyEncoded, blockKeyEncoded strin } // TODO: Separate refresh token from access token, remove named returns, and use stdlib errors. -func (c CookieManager) RetrieveTokenValues(ctx context.Context, request *http.Request) (accessToken string, +// RetrieveTokenValues retrieves id, access and refresh tokens from cookies if they exist. The existence of a refresh token +// in a cookie is optional and hence failure to find or read that cookie is tolerated. An error is returned in case of failure +// to retrieve and read either the id or the access tokens. +func (c CookieManager) RetrieveTokenValues(ctx context.Context, request *http.Request) (idToken, accessToken, refreshToken string, err error) { - jwtCookie, err := request.Cookie(accessTokenCookieName) - if err != nil || jwtCookie == nil { - logger.Errorf(ctx, "Could not detect existing access token cookie") - return - } - logger.Debugf(ctx, "Existing JWT cookie found") - accessToken, err = ReadSecureCookie(ctx, *jwtCookie, c.hashKey, c.blockKey) + idToken, err = retrieveSecureCookie(ctx, request, idTokenCookieName, c.hashKey, c.blockKey) if err != nil { - logger.Errorf(ctx, "Error reading existing secure JWT cookie %s", err) - return + return "", "", "", err } - refreshCookie, err := request.Cookie(refreshTokenCookieName) - if err != nil || refreshCookie == nil { - logger.Debugf(ctx, "Could not detect existing access token cookie") - return + accessToken, err = retrieveSecureCookie(ctx, request, accessTokenCookieName, c.hashKey, c.blockKey) + if err != nil { + return "", "", "", err } - logger.Debugf(ctx, "Existing refresh cookie found") - refreshToken, err = ReadSecureCookie(ctx, *refreshCookie, c.hashKey, c.blockKey) + + refreshToken, err = retrieveSecureCookie(ctx, request, refreshTokenCookieName, c.hashKey, c.blockKey) if err != nil { - logger.Errorf(ctx, "Error reading existing secure refresh cookie %s", err) - return + // Refresh tokens are optional. Depending on the auth url (IdP specific) we might or might not receive a refresh + // token. In case we do not, we will just have to redirect to IdP whenever access/id tokens expire. + logger.Infof(ctx, "Refresh token doesn't exist or failed to read it. Ignoring this error. Error: %v", err) + err = nil } + return } @@ -76,22 +77,37 @@ func (c CookieManager) SetTokenCookies(ctx context.Context, writer http.Response return errors.Errorf(ErrTokenNil, "Attempting to set cookies with nil token") } - jwtCookie, err := NewSecureCookie(accessTokenCookieName, token.AccessToken, c.hashKey, c.blockKey) + atCookie, err := NewSecureCookie(accessTokenCookieName, token.AccessToken, c.hashKey, c.blockKey) if err != nil { - logger.Errorf(ctx, "Error generating encrypted JWT cookie %s", err) + logger.Errorf(ctx, "Error generating encrypted accesstoken cookie %s", err) return err } - http.SetCookie(writer, &jwtCookie) + + http.SetCookie(writer, &atCookie) + + if idTokenRaw, converted := token.Extra(idTokenExtra).(string); converted { + idCookie, err := NewSecureCookie(idTokenCookieName, idTokenRaw, c.hashKey, c.blockKey) + if err != nil { + logger.Errorf(ctx, "Error generating encrypted id token cookie %s", err) + return err + } + + http.SetCookie(writer, &idCookie) + } else { + logger.Errorf(ctx, "Response does not contain an id_token.") + return errors.Errorf(ErrNoIDToken, "Response does not contain an id_token.") + } // Set the refresh cookie if there is a refresh token if token.RefreshToken != "" { refreshCookie, err := NewSecureCookie(refreshTokenCookieName, token.RefreshToken, c.hashKey, c.blockKey) if err != nil { - logger.Errorf(ctx, "Error generating encrypted refresh cookie %s", err) + logger.Errorf(ctx, "Error generating encrypted refresh token cookie %s", err) return err } http.SetCookie(writer, &refreshCookie) } + return nil } diff --git a/flyteadmin/pkg/auth/cookie_manager_test.go b/flyteadmin/pkg/auth/cookie_manager_test.go index 44f052ee9..28b377e07 100644 --- a/flyteadmin/pkg/auth/cookie_manager_test.go +++ b/flyteadmin/pkg/auth/cookie_manager_test.go @@ -21,18 +21,23 @@ func TestCookieManager_SetTokenCookies(t *testing.T) { manager, err := NewCookieManager(ctx, hashKeyEncoded, blockKeyEncoded) assert.NoError(t, err) - token := oauth2.Token{ + token := &oauth2.Token{ AccessToken: "access", RefreshToken: "refresh", } + token = token.WithExtra(map[string]interface{}{ + "id_token": "id token", + }) + w := httptest.NewRecorder() - err = manager.SetTokenCookies(ctx, w, &token) + err = manager.SetTokenCookies(ctx, w, token) assert.NoError(t, err) fmt.Println(w.Header().Get("Set-Cookie")) c := w.Result().Cookies() - assert.Equal(t, "flyte_jwt", c[0].Name) - assert.Equal(t, "flyte_refresh", c[1].Name) + assert.Equal(t, "flyte_at", c[0].Name) + assert.Equal(t, "flyte_idt", c[1].Name) + assert.Equal(t, "flyte_rt", c[2].Name) } func TestCookieManager_RetrieveTokenValues(t *testing.T) { @@ -44,23 +49,29 @@ func TestCookieManager_RetrieveTokenValues(t *testing.T) { manager, err := NewCookieManager(ctx, hashKeyEncoded, blockKeyEncoded) assert.NoError(t, err) - token := oauth2.Token{ + token := &oauth2.Token{ AccessToken: "access", RefreshToken: "refresh", } + token = token.WithExtra(map[string]interface{}{ + "id_token": "id token", + }) + w := httptest.NewRecorder() - err = manager.SetTokenCookies(ctx, w, &token) + err = manager.SetTokenCookies(ctx, w, token) assert.NoError(t, err) cookies := w.Result().Cookies() req, err := http.NewRequest("GET", "/api/v1/projects", nil) assert.NoError(t, err) - req.AddCookie(cookies[0]) - req.AddCookie(cookies[1]) + for _, c := range cookies { + req.AddCookie(c) + } - access, refresh, err := manager.RetrieveTokenValues(ctx, req) + idToken, access, refresh, err := manager.RetrieveTokenValues(ctx, req) assert.NoError(t, err) + assert.Equal(t, "id token", idToken) assert.Equal(t, "access", access) assert.Equal(t, "refresh", refresh) } diff --git a/flyteadmin/pkg/auth/handlers.go b/flyteadmin/pkg/auth/handlers.go index 3ea1092ff..c9a83386d 100644 --- a/flyteadmin/pkg/auth/handlers.go +++ b/flyteadmin/pkg/auth/handlers.go @@ -17,7 +17,6 @@ import ( "github.com/lyft/flytestdlib/contextutils" "github.com/lyft/flytestdlib/errors" "github.com/lyft/flytestdlib/logger" - "golang.org/x/oauth2" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" @@ -40,28 +39,26 @@ func RefreshTokensIfExists(ctx context.Context, authContext interfaces.Authentic return func(writer http.ResponseWriter, request *http.Request) { // Since we only do one thing if there are no errors anywhere along the chain, we can save code by just // using one variable and checking for errors at the end. - var err error - accessToken, refreshToken, err := authContext.CookieManager().RetrieveTokenValues(ctx, request) - - if err == nil && accessToken != "" && refreshToken != "" { - _, e := ParseAndValidate(ctx, authContext.Claims(), accessToken, authContext.OidcProvider()) - err = e - if err != nil && errors.IsCausedBy(err, ErrTokenExpired) { - logger.Debugf(ctx, "Expired access token found, attempting to refresh") + idToken, accessToken, refreshToken, err := authContext.CookieManager().RetrieveTokenValues(ctx, request) + if err == nil { + _, err = ParseAndValidate(ctx, authContext.Claims(), idToken, authContext.OidcProvider()) + if err != nil && errors.IsCausedBy(err, ErrTokenExpired) && len(refreshToken) > 0 { + logger.Debugf(ctx, "Expired id token found, attempting to refresh") newToken, e := GetRefreshedToken(ctx, authContext.OAuth2Config(), accessToken, refreshToken) err = e if err == nil { - logger.Debugf(ctx, "Access token refreshed. Saving new tokens into cookies.") + logger.Debugf(ctx, "Tokens are refreshed. Saving new tokens into cookies.") err = authContext.CookieManager().SetTokenCookies(ctx, writer, newToken) } } } if err != nil { - logger.Errorf(ctx, "Non-expiration error in refresh token handler %s, redirecting to login handler", err) + logger.Errorf(ctx, "Non-expiration error in refresh token handler, redirecting to login handler. Error: %s", err) handlerFunc(writer, request) return } + redirectURL := getAuthFlowEndRedirect(ctx, authContext, request) http.Redirect(writer, request, redirectURL, http.StatusTemporaryRedirect) } @@ -101,11 +98,7 @@ func GetCallbackHandler(ctx context.Context, authContext interfaces.Authenticati return } - // TODO: The second parameter is IDP specific but seems to be convention, make configurable anyways. - // The second parameter is necessary to get the initial refresh token - offlineAccessParam := oauth2.SetAuthURLParam(RefreshToken, OfflineAccessType) - - token, err := authContext.OAuth2Config().Exchange(ctx, authorizationCode, offlineAccessParam) + token, err := authContext.OAuth2Config().Exchange(ctx, authorizationCode) if err != nil { logger.Errorf(ctx, "Error when exchanging code %s", err) writer.WriteHeader(http.StatusForbidden) @@ -180,6 +173,7 @@ func GetAuthenticationInterceptor(authContext interfaces.AuthenticationContext) return ctx, status.Errorf(codes.Unauthenticated, "no email or empty email found") } } + if token != nil { newCtx := WithUserEmail(context.WithValue(ctx, bearerTokenContextKey, token), token.Subject) newCtx = WithAuditFields(newCtx, token.Audience, token.IssuedAt) @@ -214,9 +208,8 @@ func WithAuditFields(ctx context.Context, clientIds []string, tokenIssuedAt time func GetHTTPRequestCookieToMetadataHandler(authContext interfaces.AuthenticationContext) HTTPRequestToMetadataAnnotator { return func(ctx context.Context, request *http.Request) metadata.MD { // TODO: Improve error handling - accessToken, _, _ := authContext.CookieManager().RetrieveTokenValues(ctx, request) - if accessToken == "" { - + idToken, _, _, _ := authContext.CookieManager().RetrieveTokenValues(ctx, request) + if len(idToken) == 0 { // If no token was found in the cookies, look for an authorization header, starting with a potentially // custom header set in the Config object if authContext.Options().HTTPAuthorizationHeader != "" { @@ -233,7 +226,7 @@ func GetHTTPRequestCookieToMetadataHandler(authContext interfaces.Authentication return nil } return metadata.MD{ - DefaultAuthorizationHeader: []string{fmt.Sprintf("%s %s", BearerScheme, accessToken)}, + DefaultAuthorizationHeader: []string{fmt.Sprintf("%s %s", BearerScheme, idToken)}, } } } @@ -254,14 +247,14 @@ func GetHTTPMetadataTaggingHandler(authContext interfaces.AuthenticationContext) func GetMeEndpointHandler(ctx context.Context, authCtx interfaces.AuthenticationContext) http.HandlerFunc { idpUserInfoEndpoint := authCtx.GetUserInfoURL().String() return func(writer http.ResponseWriter, request *http.Request) { - access, _, err := authCtx.CookieManager().RetrieveTokenValues(ctx, request) + _, accessToken, _, err := authCtx.CookieManager().RetrieveTokenValues(ctx, request) if err != nil { http.Error(writer, "Error decoding identify token, try /login in again", http.StatusUnauthorized) return } // TODO: Investigate improving transparency of errors. The errors from this call may be just a local error, or may // be an error from the HTTP request to the IDP. In the latter case, consider passing along the error code/msg. - userInfo, err := postToIdp(ctx, authCtx.GetHTTPClient(), idpUserInfoEndpoint, access) + userInfo, err := postToIdp(ctx, authCtx.GetHTTPClient(), idpUserInfoEndpoint, accessToken) if err != nil { logger.Errorf(ctx, "Error getting user info from IDP %s", err) http.Error(writer, "Error getting user info from IDP", http.StatusFailedDependency) @@ -284,9 +277,18 @@ func GetMeEndpointHandler(ctx context.Context, authCtx interfaces.Authentication // This returns a handler that will redirect (303) to the well-known metadata endpoint for the OAuth2 authorization server // See https://tools.ietf.org/html/rfc8414 for more information. -func GetMetadataEndpointRedirectHandler(ctx context.Context, authCtx interfaces.AuthenticationContext) http.HandlerFunc { +func GetOAuth2MetadataEndpointRedirectHandler(ctx context.Context, authCtx interfaces.AuthenticationContext) http.HandlerFunc { + return func(writer http.ResponseWriter, request *http.Request) { + metadataURL := authCtx.GetBaseURL().ResolveReference(authCtx.GetOAuth2MetadataURL()) + http.Redirect(writer, request, metadataURL.String(), http.StatusSeeOther) + } +} + +// This returns a handler that will redirect (303) to the well-known metadata endpoint for the OAuth2 authorization server +// See https://tools.ietf.org/html/rfc8414 for more information. +func GetOIdCMetadataEndpointRedirectHandler(ctx context.Context, authCtx interfaces.AuthenticationContext) http.HandlerFunc { return func(writer http.ResponseWriter, request *http.Request) { - metadataURL := authCtx.GetBaseURL().ResolveReference(authCtx.GetMetadataURL()) + metadataURL := authCtx.GetBaseURL().ResolveReference(authCtx.GetOIdCMetadataURL()) http.Redirect(writer, request, metadataURL.String(), http.StatusSeeOther) } } diff --git a/flyteadmin/pkg/auth/handlers_test.go b/flyteadmin/pkg/auth/handlers_test.go index 8681f8a12..4f551ed60 100644 --- a/flyteadmin/pkg/auth/handlers_test.go +++ b/flyteadmin/pkg/auth/handlers_test.go @@ -49,12 +49,19 @@ func TestGetHTTPRequestCookieToMetadataHandler(t *testing.T) { assert.NoError(t, err) mockAuthCtx := mocks.AuthenticationContext{} mockAuthCtx.On("CookieManager").Return(&cookieManager) + mockAuthCtx.OnOptions().Return(config.OAuthOptions{}) handler := GetHTTPRequestCookieToMetadataHandler(&mockAuthCtx) req, err := http.NewRequest("GET", "/api/v1/projects", nil) assert.NoError(t, err) - jwtCookie, err := NewSecureCookie(accessTokenCookieName, "a.b.c", cookieManager.hashKey, cookieManager.blockKey) + + accessTokenCookie, err := NewSecureCookie(accessTokenCookieName, "a.b.c", cookieManager.hashKey, cookieManager.blockKey) + assert.NoError(t, err) + req.AddCookie(&accessTokenCookie) + + idCookie, err := NewSecureCookie(idTokenCookieName, "a.b.c", cookieManager.hashKey, cookieManager.blockKey) assert.NoError(t, err) - req.AddCookie(&jwtCookie) + req.AddCookie(&idCookie) + assert.Equal(t, "Bearer a.b.c", handler(ctx, req)["authorization"][0]) } @@ -92,12 +99,12 @@ func TestGetMetadataEndpointRedirectHandler(t *testing.T) { ctx := context.Background() baseURL, err := url.Parse("http://www.google.com") assert.NoError(t, err) - metadataPath, err := url.Parse(MetadataEndpoint) + metadataPath, err := url.Parse(OAuth2MetadataEndpoint) assert.NoError(t, err) mockAuthCtx := mocks.AuthenticationContext{} - mockAuthCtx.On("GetBaseURL").Return(baseURL) - mockAuthCtx.On("GetMetadataURL").Return(metadataPath) - handler := GetMetadataEndpointRedirectHandler(ctx, &mockAuthCtx) + mockAuthCtx.OnGetBaseURL().Return(baseURL) + mockAuthCtx.OnGetOAuth2MetadataURL().Return(metadataPath) + handler := GetOAuth2MetadataEndpointRedirectHandler(ctx, &mockAuthCtx) req, err := http.NewRequest("GET", "/xyz", nil) assert.NoError(t, err) w := httptest.NewRecorder() diff --git a/flyteadmin/pkg/auth/interfaces/context.go b/flyteadmin/pkg/auth/interfaces/context.go index 485357349..6d5452fc1 100644 --- a/flyteadmin/pkg/auth/interfaces/context.go +++ b/flyteadmin/pkg/auth/interfaces/context.go @@ -21,6 +21,7 @@ type AuthenticationContext interface { Options() config.OAuthOptions GetUserInfoURL() *url.URL GetBaseURL() *url.URL - GetMetadataURL() *url.URL + GetOAuth2MetadataURL() *url.URL + GetOIdCMetadataURL() *url.URL GetHTTPClient() *http.Client } diff --git a/flyteadmin/pkg/auth/interfaces/cookie.go b/flyteadmin/pkg/auth/interfaces/cookie.go index a76dc36ee..27232094c 100644 --- a/flyteadmin/pkg/auth/interfaces/cookie.go +++ b/flyteadmin/pkg/auth/interfaces/cookie.go @@ -8,7 +8,7 @@ import ( ) type CookieHandler interface { - RetrieveTokenValues(ctx context.Context, request *http.Request) (accessToken string, refreshToken string, err error) + RetrieveTokenValues(ctx context.Context, request *http.Request) (idToken, accessToken, refreshToken string, err error) SetTokenCookies(ctx context.Context, writer http.ResponseWriter, token *oauth2.Token) error DeleteCookies(ctx context.Context, writer http.ResponseWriter) } diff --git a/flyteadmin/pkg/auth/interfaces/mocks/authentication_context.go b/flyteadmin/pkg/auth/interfaces/mocks/authentication_context.go index 34b6ef044..32870207e 100644 --- a/flyteadmin/pkg/auth/interfaces/mocks/authentication_context.go +++ b/flyteadmin/pkg/auth/interfaces/mocks/authentication_context.go @@ -157,26 +157,60 @@ func (_m *AuthenticationContext) GetHTTPClient() *http.Client { return r0 } -type AuthenticationContext_GetMetadataURL struct { +type AuthenticationContext_GetOAuth2MetadataURL struct { *mock.Call } -func (_m AuthenticationContext_GetMetadataURL) Return(_a0 *url.URL) *AuthenticationContext_GetMetadataURL { - return &AuthenticationContext_GetMetadataURL{Call: _m.Call.Return(_a0)} +func (_m AuthenticationContext_GetOAuth2MetadataURL) Return(_a0 *url.URL) *AuthenticationContext_GetOAuth2MetadataURL { + return &AuthenticationContext_GetOAuth2MetadataURL{Call: _m.Call.Return(_a0)} } -func (_m *AuthenticationContext) OnGetMetadataURL() *AuthenticationContext_GetMetadataURL { - c := _m.On("GetMetadataURL") - return &AuthenticationContext_GetMetadataURL{Call: c} +func (_m *AuthenticationContext) OnGetOAuth2MetadataURL() *AuthenticationContext_GetOAuth2MetadataURL { + c := _m.On("GetOAuth2MetadataURL") + return &AuthenticationContext_GetOAuth2MetadataURL{Call: c} } -func (_m *AuthenticationContext) OnGetMetadataURLMatch(matchers ...interface{}) *AuthenticationContext_GetMetadataURL { - c := _m.On("GetMetadataURL", matchers...) - return &AuthenticationContext_GetMetadataURL{Call: c} +func (_m *AuthenticationContext) OnGetOAuth2MetadataURLMatch(matchers ...interface{}) *AuthenticationContext_GetOAuth2MetadataURL { + c := _m.On("GetOAuth2MetadataURL", matchers...) + return &AuthenticationContext_GetOAuth2MetadataURL{Call: c} } -// GetMetadataURL provides a mock function with given fields: -func (_m *AuthenticationContext) GetMetadataURL() *url.URL { +// GetOAuth2MetadataURL provides a mock function with given fields: +func (_m *AuthenticationContext) GetOAuth2MetadataURL() *url.URL { + ret := _m.Called() + + var r0 *url.URL + if rf, ok := ret.Get(0).(func() *url.URL); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*url.URL) + } + } + + return r0 +} + +type AuthenticationContext_GetOIdCMetadataURL struct { + *mock.Call +} + +func (_m AuthenticationContext_GetOIdCMetadataURL) Return(_a0 *url.URL) *AuthenticationContext_GetOIdCMetadataURL { + return &AuthenticationContext_GetOIdCMetadataURL{Call: _m.Call.Return(_a0)} +} + +func (_m *AuthenticationContext) OnGetOIdCMetadataURL() *AuthenticationContext_GetOIdCMetadataURL { + c := _m.On("GetOIdCMetadataURL") + return &AuthenticationContext_GetOIdCMetadataURL{Call: c} +} + +func (_m *AuthenticationContext) OnGetOIdCMetadataURLMatch(matchers ...interface{}) *AuthenticationContext_GetOIdCMetadataURL { + c := _m.On("GetOIdCMetadataURL", matchers...) + return &AuthenticationContext_GetOIdCMetadataURL{Call: c} +} + +// GetOIdCMetadataURL provides a mock function with given fields: +func (_m *AuthenticationContext) GetOIdCMetadataURL() *url.URL { ret := _m.Called() var r0 *url.URL diff --git a/flyteadmin/pkg/auth/token.go b/flyteadmin/pkg/auth/token.go index 6eb7a4189..642222c87 100644 --- a/flyteadmin/pkg/auth/token.go +++ b/flyteadmin/pkg/auth/token.go @@ -38,12 +38,14 @@ func GetRefreshedToken(ctx context.Context, oauth *oauth2.Config, accessToken, r return newToken, nil } -func ParseAndValidate(ctx context.Context, claims config.Claims, accessToken string, +func ParseAndValidate(ctx context.Context, claims config.Claims, rawIDToken string, provider *oidc.Provider) (*oidc.IDToken, error) { - var verifier = provider.Verifier(&oidc.Config{ClientID: claims.Audience}) + var verifier = provider.Verifier(&oidc.Config{ + ClientID: claims.Audience, + }) - idToken, err := verifier.Verify(ctx, accessToken) + idToken, err := verifier.Verify(ctx, rawIDToken) if err != nil { logger.Debugf(ctx, "JWT parsing with claims failed %s", err) flyteErr := errors.Wrapf(ErrJwtValidation, err, "jwt parse with claims failed") @@ -51,8 +53,10 @@ func ParseAndValidate(ctx context.Context, claims config.Claims, accessToken str if strings.Contains(err.Error(), "token is expired") { return idToken, errors.Wrapf(ErrTokenExpired, flyteErr, "token is expired") } + return idToken, flyteErr } + return idToken, nil } @@ -66,6 +70,7 @@ func GetAndValidateTokenObjectFromContext(ctx context.Context, claims config.Cla logger.Debugf(ctx, "Could not retrieve bearer token from metadata %v", err) return nil, errors.Wrapf(ErrJwtValidation, err, "Could not retrieve bearer token from metadata") } + if tokenStr == "" { logger.Debugf(ctx, "Found Bearer scheme but token was blank") return nil, errors.Errorf(ErrJwtValidation, "Bearer token is blank") diff --git a/flyteadmin/pkg/config/config.go b/flyteadmin/pkg/config/config.go index 8809c80ee..512be6446 100644 --- a/flyteadmin/pkg/config/config.go +++ b/flyteadmin/pkg/config/config.go @@ -52,6 +52,12 @@ var defaultServerConfig = &ServerConfig{ // Please see the comments in this struct's definition for more information HTTPAuthorizationHeader: "flyte-authorization", GrpcAuthorizationHeader: "flyte-authorization", + // Default claims that should be supported by any OIdC server. Refer to https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims + // for a complete list. + Scopes: []string{ + "openid", + "profile", + }, }, }, } diff --git a/flyteadmin/pkg/rpc/config/flyte_client.go b/flyteadmin/pkg/rpc/config/flyte_client.go index eaf78731c..f28a02ea5 100644 --- a/flyteadmin/pkg/rpc/config/flyte_client.go +++ b/flyteadmin/pkg/rpc/config/flyte_client.go @@ -12,22 +12,26 @@ import ( const ( clientID = "client_id" redirectURI = "redirect_uri" + scopes = "scopes" authMetadataKey = "authorization_metadata_key" ) func HandleFlyteCliConfigFunc(ctx context.Context, cfg *config.ServerConfig) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - configValues := map[string]string{ + configValues := map[string]interface{}{ clientID: cfg.ThirdPartyConfig.FlyteClientConfig.ClientID, redirectURI: cfg.ThirdPartyConfig.FlyteClientConfig.RedirectURI, + scopes: cfg.Security.Oauth.Scopes, authMetadataKey: cfg.Security.Oauth.GrpcAuthorizationHeader, } + configJSON, err := json.Marshal(configValues) if err != nil { logger.Infof(ctx, "Failed to marshal flyte_client config to JSON with err: %v", err) w.WriteHeader(http.StatusInternalServerError) return } + _, err = w.Write(configJSON) if err != nil { logger.Warningf(ctx, "Failed to write config json [%+v] with err: %v", string(configJSON), err) diff --git a/flyteadmin/pkg/rpc/config/flyte_client_test.go b/flyteadmin/pkg/rpc/config/flyte_client_test.go index 47b2bd333..2c6bbe529 100644 --- a/flyteadmin/pkg/rpc/config/flyte_client_test.go +++ b/flyteadmin/pkg/rpc/config/flyte_client_test.go @@ -44,5 +44,6 @@ func TestHandleFlyteCliConfigFunc(t *testing.T) { clientID: testClientID, redirectURI: testRedirectURI, authMetadataKey: testAuthMetadataKey, + scopes: "", }, responseBodyMap) }