From 57bea736da8121553bd538547002e924247b70df Mon Sep 17 00:00:00 2001 From: Oleksandr Sheheda Date: Thu, 21 Oct 2021 20:44:44 +0300 Subject: [PATCH] =?UTF-8?q?#7243=20=F0=9F=8E=89=20Source=20Shopify:=20impl?= =?UTF-8?q?ement=20OAuth=20Java=20part?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- airbyte-api/src/main/openapi/config.yaml | 3 ++ .../source-shopify/source_shopify/spec.json | 4 +- .../java/io/airbyte/oauth/BaseOAuthFlow.java | 30 ++++++++--- .../oauth/OAuthFlowImplementation.java | 18 +++++-- .../airbyte/oauth/flows/AsanaOAuthFlow.java | 6 ++- .../flows/FacebookMarketingOAuthFlow.java | 6 ++- .../airbyte/oauth/flows/ShopifyOAuthFlow.java | 18 +++++-- .../airbyte/oauth/flows/TrelloOAuthFlow.java | 19 +++++-- .../oauth/flows/google/GoogleOAuthFlow.java | 6 ++- .../AsanaOAuthFlowIntegrationTest.java | 4 +- ...bookMarketingOAuthFlowIntegrationTest.java | 8 ++- .../TrelloOAuthFlowIntegrationTest.java | 10 ++-- .../GoogleAdsOAuthFlowIntegrationTest.java | 8 ++- ...ogleAnalyticsOAuthFlowIntegrationTest.java | 8 ++- ...SearchConsoleOAuthFlowIntegrationTest.java | 11 ++-- .../oauth/flows/AsanaOAuthFlowTest.java | 3 +- .../oauth/flows/ShopifyOAuthFlowTest.java | 3 +- .../oauth/flows/TrelloOAuthFlowTest.java | 4 +- .../google/GoogleAnalyticsOAuthFlowTest.java | 50 ++++++++++++------- .../airbyte/server/handlers/OAuthHandler.java | 26 ++++++---- 20 files changed, 176 insertions(+), 69 deletions(-) diff --git a/airbyte-api/src/main/openapi/config.yaml b/airbyte-api/src/main/openapi/config.yaml index 45d3a20f9860..c01af5ae8b04 100644 --- a/airbyte-api/src/main/openapi/config.yaml +++ b/airbyte-api/src/main/openapi/config.yaml @@ -3256,6 +3256,9 @@ components: redirectUrl: description: The url to redirect to after getting the user consent type: string + params: + type: object + additionalProperties: true OAuthConsentRead: type: object required: diff --git a/airbyte-integrations/connectors/source-shopify/source_shopify/spec.json b/airbyte-integrations/connectors/source-shopify/source_shopify/spec.json index e3e02edf0c16..fa48f0348f6a 100644 --- a/airbyte-integrations/connectors/source-shopify/source_shopify/spec.json +++ b/airbyte-integrations/connectors/source-shopify/source_shopify/spec.json @@ -33,7 +33,9 @@ "auth_method": { "type": "string", "const": "access_token", - "enum": ["access_token"], + "enum": [ + "access_token" + ], "default": "access_token", "order": 0 }, diff --git a/airbyte-oauth/src/main/java/io/airbyte/oauth/BaseOAuthFlow.java b/airbyte-oauth/src/main/java/io/airbyte/oauth/BaseOAuthFlow.java index 39386c83d434..942117b46092 100644 --- a/airbyte-oauth/src/main/java/io/airbyte/oauth/BaseOAuthFlow.java +++ b/airbyte-oauth/src/main/java/io/airbyte/oauth/BaseOAuthFlow.java @@ -41,25 +41,39 @@ public BaseOAuthFlow(final ConfigRepository configRepository, final HttpClient h } @Override - public String getSourceConsentUrl(final UUID workspaceId, final UUID sourceDefinitionId, final String redirectUrl) + public String getSourceConsentUrl(final UUID workspaceId, + final UUID sourceDefinitionId, + final String redirectUrl, + final Map params) throws IOException, ConfigNotFoundException { final JsonNode oAuthParamConfig = getSourceOAuthParamConfig(workspaceId, sourceDefinitionId); - return formatConsentUrl(sourceDefinitionId, getClientIdUnsafe(oAuthParamConfig), redirectUrl); + return formatConsentUrl(sourceDefinitionId, getClientIdUnsafe(oAuthParamConfig), redirectUrl, + params); } @Override - public String getDestinationConsentUrl(final UUID workspaceId, final UUID destinationDefinitionId, final String redirectUrl) + public String getDestinationConsentUrl(final UUID workspaceId, + final UUID destinationDefinitionId, + final String redirectUrl, + final Map params) throws IOException, ConfigNotFoundException { - final JsonNode oAuthParamConfig = getDestinationOAuthParamConfig(workspaceId, destinationDefinitionId); - return formatConsentUrl(destinationDefinitionId, getClientIdUnsafe(oAuthParamConfig), redirectUrl); + final JsonNode oAuthParamConfig = getDestinationOAuthParamConfig(workspaceId, + destinationDefinitionId); + return formatConsentUrl(destinationDefinitionId, getClientIdUnsafe(oAuthParamConfig), + redirectUrl, + params); } /** * Depending on the OAuth flow implementation, the URL to grant user's consent may differ, - * especially in the query parameters to be provided. This function should generate such consent URL - * accordingly. + * especially in the query parameters to be provided. This function should generate such consent + * URL accordingly. */ - protected abstract String formatConsentUrl(UUID definitionId, String clientId, String redirectUrl) throws IOException; + protected abstract String formatConsentUrl(UUID definitionId, + String clientId, + String redirectUrl, + Map params) + throws IOException; private static String generateRandomState() { return RandomStringUtils.randomAlphanumeric(7); diff --git a/airbyte-oauth/src/main/java/io/airbyte/oauth/OAuthFlowImplementation.java b/airbyte-oauth/src/main/java/io/airbyte/oauth/OAuthFlowImplementation.java index e43bf3da698c..917243b717d6 100644 --- a/airbyte-oauth/src/main/java/io/airbyte/oauth/OAuthFlowImplementation.java +++ b/airbyte-oauth/src/main/java/io/airbyte/oauth/OAuthFlowImplementation.java @@ -11,14 +11,24 @@ public interface OAuthFlowImplementation { - String getSourceConsentUrl(UUID workspaceId, UUID sourceDefinitionId, String redirectUrl) throws IOException, ConfigNotFoundException; + String getSourceConsentUrl(UUID workspaceId, + UUID sourceDefinitionId, + String redirectUrl, + Map params) + throws IOException, ConfigNotFoundException; - String getDestinationConsentUrl(UUID workspaceId, UUID destinationDefinitionId, String redirectUrl) throws IOException, ConfigNotFoundException; + String getDestinationConsentUrl(UUID workspaceId, + UUID destinationDefinitionId, + String redirectUrl, + Map params) + throws IOException, ConfigNotFoundException; - Map completeSourceOAuth(UUID workspaceId, UUID sourceDefinitionId, Map queryParams, String redirectUrl) + Map completeSourceOAuth(UUID workspaceId, UUID sourceDefinitionId, + Map queryParams, String redirectUrl) throws IOException, ConfigNotFoundException; - Map completeDestinationOAuth(UUID workspaceId, UUID destinationDefinitionId, Map queryParams, String redirectUrl) + Map completeDestinationOAuth(UUID workspaceId, UUID destinationDefinitionId, + Map queryParams, String redirectUrl) throws IOException, ConfigNotFoundException; } diff --git a/airbyte-oauth/src/main/java/io/airbyte/oauth/flows/AsanaOAuthFlow.java b/airbyte-oauth/src/main/java/io/airbyte/oauth/flows/AsanaOAuthFlow.java index e3d53435aa4a..955c2fe6386d 100644 --- a/airbyte-oauth/src/main/java/io/airbyte/oauth/flows/AsanaOAuthFlow.java +++ b/airbyte-oauth/src/main/java/io/airbyte/oauth/flows/AsanaOAuthFlow.java @@ -36,7 +36,11 @@ public AsanaOAuthFlow(ConfigRepository configRepository) { } @Override - protected String formatConsentUrl(UUID definitionId, String clientId, String redirectUrl) throws IOException { + protected String formatConsentUrl(UUID definitionId, + String clientId, + String redirectUrl, + Map params) + throws IOException { try { return new URIBuilder(AUTHORIZE_URL) .addParameter("client_id", clientId) diff --git a/airbyte-oauth/src/main/java/io/airbyte/oauth/flows/FacebookMarketingOAuthFlow.java b/airbyte-oauth/src/main/java/io/airbyte/oauth/flows/FacebookMarketingOAuthFlow.java index 9c459206a7c8..334f84eee4a1 100644 --- a/airbyte-oauth/src/main/java/io/airbyte/oauth/flows/FacebookMarketingOAuthFlow.java +++ b/airbyte-oauth/src/main/java/io/airbyte/oauth/flows/FacebookMarketingOAuthFlow.java @@ -34,7 +34,11 @@ public FacebookMarketingOAuthFlow(final ConfigRepository configRepository) { } @Override - protected String formatConsentUrl(final UUID definitionId, final String clientId, final String redirectUrl) throws IOException { + protected String formatConsentUrl(final UUID definitionId, + final String clientId, + final String redirectUrl, + Map params) + throws IOException { final URIBuilder builder = new URIBuilder() .setScheme("https") .setHost("www.facebook.com") diff --git a/airbyte-oauth/src/main/java/io/airbyte/oauth/flows/ShopifyOAuthFlow.java b/airbyte-oauth/src/main/java/io/airbyte/oauth/flows/ShopifyOAuthFlow.java index e934043dc7a7..cc0f74ad3e45 100644 --- a/airbyte-oauth/src/main/java/io/airbyte/oauth/flows/ShopifyOAuthFlow.java +++ b/airbyte-oauth/src/main/java/io/airbyte/oauth/flows/ShopifyOAuthFlow.java @@ -56,14 +56,22 @@ public void setAuthPrefix(String authPrefix) { } @VisibleForTesting - ShopifyOAuthFlow(ConfigRepository configRepository, HttpClient httpClient, + ShopifyOAuthFlow(ConfigRepository configRepository, + HttpClient httpClient, Supplier stateSupplier) { super(configRepository, httpClient, stateSupplier); } @Override - protected String formatConsentUrl(UUID definitionId, String clientId, String redirectUrl) + protected String formatConsentUrl(UUID definitionId, + String clientId, + String redirectUrl, + Map params) throws IOException { + if (params != null && params.containsKey("shopDomain")) { + authPrefix = String.valueOf(params.get("shopDomain")); + } + String host = authPrefix + ".myshopify.com"; final URIBuilder builder = new URIBuilder() .setScheme("https") @@ -110,8 +118,10 @@ protected String getAccessTokenUrl() { } @Override - protected Map getAccessTokenQueryParameters(String clientId, String clientSecret, - String authCode, String redirectUrl) { + protected Map getAccessTokenQueryParameters(String clientId, + String clientSecret, + String authCode, + String redirectUrl) { return ImmutableMap.builder() .put("client_id", clientId) .put("client_secret", clientSecret) diff --git a/airbyte-oauth/src/main/java/io/airbyte/oauth/flows/TrelloOAuthFlow.java b/airbyte-oauth/src/main/java/io/airbyte/oauth/flows/TrelloOAuthFlow.java index 1273f46e2341..ac158c062931 100644 --- a/airbyte-oauth/src/main/java/io/airbyte/oauth/flows/TrelloOAuthFlow.java +++ b/airbyte-oauth/src/main/java/io/airbyte/oauth/flows/TrelloOAuthFlow.java @@ -49,22 +49,31 @@ public TrelloOAuthFlow(final ConfigRepository configRepository, final HttpTransp this.transport = transport; } - public String getSourceConsentUrl(final UUID workspaceId, final UUID sourceDefinitionId, final String redirectUrl) + public String getSourceConsentUrl(final UUID workspaceId, + final UUID sourceDefinitionId, + final String redirectUrl, + Map params) throws IOException, ConfigNotFoundException { final JsonNode oAuthParamConfig = getSourceOAuthParamConfig(workspaceId, sourceDefinitionId); return getConsentUrl(oAuthParamConfig, redirectUrl); } - public String getDestinationConsentUrl(final UUID workspaceId, final UUID destinationDefinitionId, final String redirectUrl) + public String getDestinationConsentUrl(final UUID workspaceId, + final UUID destinationDefinitionId, + final String redirectUrl, + Map params) throws IOException, ConfigNotFoundException { - final JsonNode oAuthParamConfig = getDestinationOAuthParamConfig(workspaceId, destinationDefinitionId); + final JsonNode oAuthParamConfig = getDestinationOAuthParamConfig(workspaceId, + destinationDefinitionId); return getConsentUrl(oAuthParamConfig, redirectUrl); } - private String getConsentUrl(final JsonNode oAuthParamConfig, final String redirectUrl) throws IOException, ConfigNotFoundException { + private String getConsentUrl(final JsonNode oAuthParamConfig, final String redirectUrl) + throws IOException, ConfigNotFoundException { final String clientKey = getClientIdUnsafe(oAuthParamConfig); final String clientSecret = getClientSecretUnsafe(oAuthParamConfig); - final OAuthGetTemporaryToken oAuthGetTemporaryToken = new OAuthGetTemporaryToken(REQUEST_TOKEN_URL); + final OAuthGetTemporaryToken oAuthGetTemporaryToken = new OAuthGetTemporaryToken( + REQUEST_TOKEN_URL); signer.clientSharedSecret = clientSecret; signer.tokenSharedSecret = null; oAuthGetTemporaryToken.signer = signer; diff --git a/airbyte-oauth/src/main/java/io/airbyte/oauth/flows/google/GoogleOAuthFlow.java b/airbyte-oauth/src/main/java/io/airbyte/oauth/flows/google/GoogleOAuthFlow.java index 1fc029332fe2..bf1e105f06bd 100644 --- a/airbyte-oauth/src/main/java/io/airbyte/oauth/flows/google/GoogleOAuthFlow.java +++ b/airbyte-oauth/src/main/java/io/airbyte/oauth/flows/google/GoogleOAuthFlow.java @@ -35,7 +35,11 @@ public GoogleOAuthFlow(final ConfigRepository configRepository) { } @Override - protected String formatConsentUrl(final UUID definitionId, final String clientId, final String redirectUrl) throws IOException { + protected String formatConsentUrl(final UUID definitionId, + final String clientId, + final String redirectUrl, + Map params) + throws IOException { final URIBuilder builder = new URIBuilder() .setScheme("https") .setHost("accounts.google.com") diff --git a/airbyte-oauth/src/test-integration/java/io.airbyte.oauth.flows/AsanaOAuthFlowIntegrationTest.java b/airbyte-oauth/src/test-integration/java/io.airbyte.oauth.flows/AsanaOAuthFlowIntegrationTest.java index af76c876299d..afd41e067a8d 100644 --- a/airbyte-oauth/src/test-integration/java/io.airbyte.oauth.flows/AsanaOAuthFlowIntegrationTest.java +++ b/airbyte-oauth/src/test-integration/java/io.airbyte.oauth.flows/AsanaOAuthFlowIntegrationTest.java @@ -4,6 +4,7 @@ package io.airbyte.oauth.flows; +import static java.util.Collections.emptyMap; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -81,7 +82,8 @@ public void testFullAsanaOAuthFlow() throws InterruptedException, ConfigNotFound .put("client_id", clientId) .put("client_secret", credentialsJson.get("client_secret").asText()) .build())))); - final String url = asanaOAuthFlow.getSourceConsentUrl(workspaceId, definitionId, REDIRECT_URL); + final String url = asanaOAuthFlow.getSourceConsentUrl(workspaceId, definitionId, REDIRECT_URL, + emptyMap()); LOGGER.info("Waiting for user consent at: {}", url); // TODO: To automate, start a selenium job to navigate to the Consent URL and click on allowing // access... diff --git a/airbyte-oauth/src/test-integration/java/io.airbyte.oauth.flows/FacebookMarketingOAuthFlowIntegrationTest.java b/airbyte-oauth/src/test-integration/java/io.airbyte.oauth.flows/FacebookMarketingOAuthFlowIntegrationTest.java index 3cda4ec1358f..37820b1dad23 100644 --- a/airbyte-oauth/src/test-integration/java/io.airbyte.oauth.flows/FacebookMarketingOAuthFlowIntegrationTest.java +++ b/airbyte-oauth/src/test-integration/java/io.airbyte.oauth.flows/FacebookMarketingOAuthFlowIntegrationTest.java @@ -4,6 +4,7 @@ package io.airbyte.oauth.flows; +import static java.util.Collections.emptyMap; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -80,7 +81,9 @@ public void testFullGoogleOAuthFlow() throws InterruptedException, ConfigNotFoun .put("client_id", credentialsJson.get("client_id").asText()) .put("client_secret", credentialsJson.get("client_secret").asText()) .build())))); - final String url = facebookMarketingOAuthFlow.getSourceConsentUrl(workspaceId, definitionId, REDIRECT_URL); + final String url = facebookMarketingOAuthFlow.getSourceConsentUrl(workspaceId, definitionId, + REDIRECT_URL, + emptyMap()); LOGGER.info("Waiting for user consent at: {}", url); // TODO: To automate, start a selenium job to navigate to the Consent URL and click on allowing // access... @@ -89,7 +92,8 @@ public void testFullGoogleOAuthFlow() throws InterruptedException, ConfigNotFoun limit -= 1; } assertTrue(serverHandler.isSucceeded(), "Failed to get User consent on time"); - final Map params = facebookMarketingOAuthFlow.completeSourceOAuth(workspaceId, definitionId, + final Map params = facebookMarketingOAuthFlow.completeSourceOAuth(workspaceId, + definitionId, Map.of("code", serverHandler.getParamValue()), REDIRECT_URL); LOGGER.info("Response from completing OAuth Flow is: {}", params.toString()); assertTrue(params.containsKey("access_token")); diff --git a/airbyte-oauth/src/test-integration/java/io.airbyte.oauth.flows/TrelloOAuthFlowIntegrationTest.java b/airbyte-oauth/src/test-integration/java/io.airbyte.oauth.flows/TrelloOAuthFlowIntegrationTest.java index 66fb6691347f..44669b8bffd5 100644 --- a/airbyte-oauth/src/test-integration/java/io.airbyte.oauth.flows/TrelloOAuthFlowIntegrationTest.java +++ b/airbyte-oauth/src/test-integration/java/io.airbyte.oauth.flows/TrelloOAuthFlowIntegrationTest.java @@ -4,6 +4,7 @@ package io.airbyte.oauth.flows; +import static java.util.Collections.emptyMap; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -81,7 +82,8 @@ public void testFullGoogleOAuthFlow() throws InterruptedException, ConfigNotFoun .put("client_id", clientId) .put("client_secret", credentialsJson.get("client_secret").asText()) .build())))); - final String url = trelloOAuthFlow.getSourceConsentUrl(workspaceId, definitionId, REDIRECT_URL); + final String url = trelloOAuthFlow.getSourceConsentUrl(workspaceId, definitionId, REDIRECT_URL, + emptyMap()); LOGGER.info("Waiting for user consent at: {}", url); // TODO: To automate, start a selenium job to navigate to the Consent URL and click on allowing // access... @@ -90,8 +92,10 @@ public void testFullGoogleOAuthFlow() throws InterruptedException, ConfigNotFoun limit -= 1; } assertTrue(serverHandler.isSucceeded(), "Failed to get User consent on time"); - final Map params = trelloOAuthFlow.completeSourceOAuth(workspaceId, definitionId, - Map.of("oauth_verifier", serverHandler.getParamValue(), "oauth_token", serverHandler.getResponseQuery().get("oauth_token")), REDIRECT_URL); + final Map params = trelloOAuthFlow.completeSourceOAuth(workspaceId, + definitionId, + Map.of("oauth_verifier", serverHandler.getParamValue(), "oauth_token", + serverHandler.getResponseQuery().get("oauth_token")), REDIRECT_URL); LOGGER.info("Response from completing OAuth Flow is: {}", params.toString()); assertTrue(params.containsKey("token")); assertTrue(params.containsKey("key")); diff --git a/airbyte-oauth/src/test-integration/java/io/airbyte/oauth/flows/google/GoogleAdsOAuthFlowIntegrationTest.java b/airbyte-oauth/src/test-integration/java/io/airbyte/oauth/flows/google/GoogleAdsOAuthFlowIntegrationTest.java index a9c1ddfb31d5..d2f02c056055 100644 --- a/airbyte-oauth/src/test-integration/java/io/airbyte/oauth/flows/google/GoogleAdsOAuthFlowIntegrationTest.java +++ b/airbyte-oauth/src/test-integration/java/io/airbyte/oauth/flows/google/GoogleAdsOAuthFlowIntegrationTest.java @@ -4,6 +4,7 @@ package io.airbyte.oauth.flows.google; +import static java.util.Collections.emptyMap; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -80,7 +81,9 @@ public void testFullGoogleOAuthFlow() throws InterruptedException, ConfigNotFoun .put("client_id", credentialsJson.get("credentials").get("client_id").asText()) .put("client_secret", credentialsJson.get("credentials").get("client_secret").asText()) .build()))))); - final String url = googleAdsOAuthFlow.getSourceConsentUrl(workspaceId, definitionId, REDIRECT_URL); + final String url = googleAdsOAuthFlow.getSourceConsentUrl(workspaceId, definitionId, + REDIRECT_URL, + emptyMap()); LOGGER.info("Waiting for user consent at: {}", url); // TODO: To automate, start a selenium job to navigate to the Consent URL and click on allowing // access... @@ -89,7 +92,8 @@ public void testFullGoogleOAuthFlow() throws InterruptedException, ConfigNotFoun limit -= 1; } assertTrue(serverHandler.isSucceeded(), "Failed to get User consent on time"); - final Map params = googleAdsOAuthFlow.completeSourceOAuth(workspaceId, definitionId, + final Map params = googleAdsOAuthFlow.completeSourceOAuth(workspaceId, + definitionId, Map.of("code", serverHandler.getParamValue()), REDIRECT_URL); LOGGER.info("Response from completing OAuth Flow is: {}", params.toString()); assertTrue(params.containsKey("credentials")); diff --git a/airbyte-oauth/src/test-integration/java/io/airbyte/oauth/flows/google/GoogleAnalyticsOAuthFlowIntegrationTest.java b/airbyte-oauth/src/test-integration/java/io/airbyte/oauth/flows/google/GoogleAnalyticsOAuthFlowIntegrationTest.java index b7683d8c3492..ec40d052b80f 100644 --- a/airbyte-oauth/src/test-integration/java/io/airbyte/oauth/flows/google/GoogleAnalyticsOAuthFlowIntegrationTest.java +++ b/airbyte-oauth/src/test-integration/java/io/airbyte/oauth/flows/google/GoogleAnalyticsOAuthFlowIntegrationTest.java @@ -4,6 +4,7 @@ package io.airbyte.oauth.flows.google; +import static java.util.Collections.emptyMap; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -80,7 +81,9 @@ public void testFullGoogleOAuthFlow() throws InterruptedException, ConfigNotFoun .put("client_id", credentialsJson.get("credentials").get("client_id").asText()) .put("client_secret", credentialsJson.get("credentials").get("client_secret").asText()) .build()))))); - final String url = googleAnalyticsOAuthFlow.getSourceConsentUrl(workspaceId, definitionId, REDIRECT_URL); + final String url = googleAnalyticsOAuthFlow.getSourceConsentUrl(workspaceId, definitionId, + REDIRECT_URL, + emptyMap()); LOGGER.info("Waiting for user consent at: {}", url); // TODO: To automate, start a selenium job to navigate to the Consent URL and click on allowing // access... @@ -89,7 +92,8 @@ public void testFullGoogleOAuthFlow() throws InterruptedException, ConfigNotFoun limit -= 1; } assertTrue(serverHandler.isSucceeded(), "Failed to get User consent on time"); - final Map params = googleAnalyticsOAuthFlow.completeSourceOAuth(workspaceId, definitionId, + final Map params = googleAnalyticsOAuthFlow.completeSourceOAuth(workspaceId, + definitionId, Map.of("code", serverHandler.getParamValue()), REDIRECT_URL); LOGGER.info("Response from completing OAuth Flow is: {}", params.toString()); assertTrue(params.containsKey("credentials")); diff --git a/airbyte-oauth/src/test-integration/java/io/airbyte/oauth/flows/google/GoogleSearchConsoleOAuthFlowIntegrationTest.java b/airbyte-oauth/src/test-integration/java/io/airbyte/oauth/flows/google/GoogleSearchConsoleOAuthFlowIntegrationTest.java index 92812c139bd7..ba6e7661eeb7 100644 --- a/airbyte-oauth/src/test-integration/java/io/airbyte/oauth/flows/google/GoogleSearchConsoleOAuthFlowIntegrationTest.java +++ b/airbyte-oauth/src/test-integration/java/io/airbyte/oauth/flows/google/GoogleSearchConsoleOAuthFlowIntegrationTest.java @@ -4,6 +4,7 @@ package io.airbyte.oauth.flows.google; +import static java.util.Collections.emptyMap; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -78,9 +79,12 @@ public void testFullGoogleOAuthFlow() throws InterruptedException, ConfigNotFoun .withWorkspaceId(workspaceId) .withConfiguration(Jsons.jsonNode(Map.of("authorization", ImmutableMap.builder() .put("client_id", credentialsJson.get("authorization").get("client_id").asText()) - .put("client_secret", credentialsJson.get("authorization").get("client_secret").asText()) + .put("client_secret", + credentialsJson.get("authorization").get("client_secret").asText()) .build()))))); - final String url = googleSearchConsoleOAuthFlow.getSourceConsentUrl(workspaceId, definitionId, REDIRECT_URL); + final String url = googleSearchConsoleOAuthFlow.getSourceConsentUrl(workspaceId, definitionId, + REDIRECT_URL, + emptyMap()); LOGGER.info("Waiting for user consent at: {}", url); // TODO: To automate, start a selenium job to navigate to the Consent URL and click on allowing // access... @@ -89,7 +93,8 @@ public void testFullGoogleOAuthFlow() throws InterruptedException, ConfigNotFoun limit -= 1; } assertTrue(serverHandler.isSucceeded(), "Failed to get User consent on time"); - final Map params = googleSearchConsoleOAuthFlow.completeSourceOAuth(workspaceId, definitionId, + final Map params = googleSearchConsoleOAuthFlow.completeSourceOAuth(workspaceId, + definitionId, Map.of("code", serverHandler.getParamValue()), REDIRECT_URL); LOGGER.info("Response from completing OAuth Flow is: {}", params.toString()); assertTrue(params.containsKey("authorization")); diff --git a/airbyte-oauth/src/test/java/io/airbyte/oauth/flows/AsanaOAuthFlowTest.java b/airbyte-oauth/src/test/java/io/airbyte/oauth/flows/AsanaOAuthFlowTest.java index 4119254b80e9..986644d2f41c 100644 --- a/airbyte-oauth/src/test/java/io/airbyte/oauth/flows/AsanaOAuthFlowTest.java +++ b/airbyte-oauth/src/test/java/io/airbyte/oauth/flows/AsanaOAuthFlowTest.java @@ -4,6 +4,7 @@ package io.airbyte.oauth.flows; +import static java.util.Collections.emptyMap; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; @@ -59,7 +60,7 @@ public void setup() throws IOException, JsonValidationException { @Test public void testGetSourceConcentUrl() throws IOException, InterruptedException, ConfigNotFoundException { final String concentUrl = - asanaoAuthFlow.getSourceConsentUrl(workspaceId, definitionId, REDIRECT_URL); + asanaoAuthFlow.getSourceConsentUrl(workspaceId, definitionId, REDIRECT_URL, emptyMap()); assertEquals(concentUrl, "https://app.asana.com/-/oauth_authorize?client_id=test_client_id&redirect_uri=https%3A%2F%2Fairbyte.io&response_type=code&state=state"); } diff --git a/airbyte-oauth/src/test/java/io/airbyte/oauth/flows/ShopifyOAuthFlowTest.java b/airbyte-oauth/src/test/java/io/airbyte/oauth/flows/ShopifyOAuthFlowTest.java index e2874d275358..8226ae20eccf 100644 --- a/airbyte-oauth/src/test/java/io/airbyte/oauth/flows/ShopifyOAuthFlowTest.java +++ b/airbyte-oauth/src/test/java/io/airbyte/oauth/flows/ShopifyOAuthFlowTest.java @@ -4,6 +4,7 @@ package io.airbyte.oauth.flows; +import static java.util.Collections.emptyMap; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; @@ -70,7 +71,7 @@ public void testGetSourceConsentUrl() .put("client_secret", "test_client_secret") .build()))))); final String actualSourceUrl = shopifyOAuthFlow.getSourceConsentUrl(workspaceId, definitionId, - REDIRECT_URL); + REDIRECT_URL, emptyMap()); final String expectedSourceUrl = String.format( "https://airbyte-integration-test.myshopify.com/admin/oauth/authorize?client_id=%s&redirect_uri=%s&state=%s&%s", getClientId(), diff --git a/airbyte-oauth/src/test/java/io/airbyte/oauth/flows/TrelloOAuthFlowTest.java b/airbyte-oauth/src/test/java/io/airbyte/oauth/flows/TrelloOAuthFlowTest.java index 73040517d9a6..4f34947d0144 100644 --- a/airbyte-oauth/src/test/java/io/airbyte/oauth/flows/TrelloOAuthFlowTest.java +++ b/airbyte-oauth/src/test/java/io/airbyte/oauth/flows/TrelloOAuthFlowTest.java @@ -4,6 +4,7 @@ package io.airbyte.oauth.flows; +import static java.util.Collections.emptyMap; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -75,7 +76,8 @@ public LowLevelHttpResponse execute() throws IOException { @Test public void testGetSourceConcentUrl() throws IOException, InterruptedException, ConfigNotFoundException { final String concentUrl = - trelloOAuthFlow.getSourceConsentUrl(workspaceId, definitionId, REDIRECT_URL); + trelloOAuthFlow.getSourceConsentUrl(workspaceId, definitionId, REDIRECT_URL, + emptyMap()); assertEquals(concentUrl, "https://trello.com/1/OAuthAuthorizeToken?oauth_token=test_token"); } diff --git a/airbyte-oauth/src/test/java/io/airbyte/oauth/flows/google/GoogleAnalyticsOAuthFlowTest.java b/airbyte-oauth/src/test/java/io/airbyte/oauth/flows/google/GoogleAnalyticsOAuthFlowTest.java index 12c6c6366e82..5c8764cb74b8 100644 --- a/airbyte-oauth/src/test/java/io/airbyte/oauth/flows/google/GoogleAnalyticsOAuthFlowTest.java +++ b/airbyte-oauth/src/test/java/io/airbyte/oauth/flows/google/GoogleAnalyticsOAuthFlowTest.java @@ -4,6 +4,7 @@ package io.airbyte.oauth.flows.google; +import static java.util.Collections.emptyMap; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; @@ -62,8 +63,12 @@ private static String getConstantState() { @Test public void testGetConsentUrlEmptyOAuthParameters() { - assertThrows(ConfigNotFoundException.class, () -> googleAnalyticsOAuthFlow.getSourceConsentUrl(workspaceId, definitionId, REDIRECT_URL)); - assertThrows(ConfigNotFoundException.class, () -> googleAnalyticsOAuthFlow.getDestinationConsentUrl(workspaceId, definitionId, REDIRECT_URL)); + assertThrows(ConfigNotFoundException.class, + () -> googleAnalyticsOAuthFlow.getSourceConsentUrl(workspaceId, definitionId, REDIRECT_URL, + emptyMap())); + assertThrows(ConfigNotFoundException.class, + () -> googleAnalyticsOAuthFlow.getDestinationConsentUrl(workspaceId, definitionId, + REDIRECT_URL, emptyMap())); } @Test @@ -73,13 +78,18 @@ public void testGetConsentUrlIncompleteOAuthParameters() throws IOException, Jso .withSourceDefinitionId(definitionId) .withWorkspaceId(workspaceId) .withConfiguration(Jsons.emptyObject()))); - when(configRepository.listDestinationOAuthParam()).thenReturn(List.of(new DestinationOAuthParameter() - .withOauthParameterId(UUID.randomUUID()) - .withDestinationDefinitionId(definitionId) - .withWorkspaceId(workspaceId) - .withConfiguration(Jsons.emptyObject()))); - assertThrows(IllegalArgumentException.class, () -> googleAnalyticsOAuthFlow.getSourceConsentUrl(workspaceId, definitionId, REDIRECT_URL)); - assertThrows(IllegalArgumentException.class, () -> googleAnalyticsOAuthFlow.getDestinationConsentUrl(workspaceId, definitionId, REDIRECT_URL)); + when(configRepository.listDestinationOAuthParam()).thenReturn( + List.of(new DestinationOAuthParameter() + .withOauthParameterId(UUID.randomUUID()) + .withDestinationDefinitionId(definitionId) + .withWorkspaceId(workspaceId) + .withConfiguration(Jsons.emptyObject()))); + assertThrows(IllegalArgumentException.class, + () -> googleAnalyticsOAuthFlow.getSourceConsentUrl(workspaceId, definitionId, REDIRECT_URL, + emptyMap())); + assertThrows(IllegalArgumentException.class, + () -> googleAnalyticsOAuthFlow.getDestinationConsentUrl(workspaceId, definitionId, + REDIRECT_URL, emptyMap())); } @Test @@ -91,7 +101,9 @@ public void testGetSourceConsentUrl() throws IOException, ConfigNotFoundExceptio .withConfiguration(Jsons.jsonNode(Map.of("credentials", ImmutableMap.builder() .put("client_id", getClientId()) .build()))))); - final String actualSourceUrl = googleAnalyticsOAuthFlow.getSourceConsentUrl(workspaceId, definitionId, REDIRECT_URL); + final String actualSourceUrl = googleAnalyticsOAuthFlow.getSourceConsentUrl(workspaceId, + definitionId, REDIRECT_URL, + emptyMap()); final String expectedSourceUrl = String.format( "https://accounts.google.com/o/oauth2/v2/auth?client_id=%s&redirect_uri=%s&response_type=code&scope=%s&access_type=offline&state=%s&include_granted_scopes=true&prompt=consent", getClientId(), @@ -104,17 +116,19 @@ public void testGetSourceConsentUrl() throws IOException, ConfigNotFoundExceptio @Test public void testGetDestinationConsentUrl() throws IOException, ConfigNotFoundException, JsonValidationException { - when(configRepository.listDestinationOAuthParam()).thenReturn(List.of(new DestinationOAuthParameter() - .withOauthParameterId(UUID.randomUUID()) - .withDestinationDefinitionId(definitionId) - .withWorkspaceId(workspaceId) - .withConfiguration(Jsons.jsonNode(Map.of("credentials", ImmutableMap.builder() - .put("client_id", getClientId()) - .build()))))); + when(configRepository.listDestinationOAuthParam()).thenReturn( + List.of(new DestinationOAuthParameter() + .withOauthParameterId(UUID.randomUUID()) + .withDestinationDefinitionId(definitionId) + .withWorkspaceId(workspaceId) + .withConfiguration(Jsons.jsonNode(Map.of("credentials", ImmutableMap.builder() + .put("client_id", getClientId()) + .build()))))); // It would be better to make this comparison agnostic of the order of query params but the URI // class' equals() method // considers URLs with different qparam orders different URIs.. - final String actualDestinationUrl = googleAnalyticsOAuthFlow.getDestinationConsentUrl(workspaceId, definitionId, REDIRECT_URL); + final String actualDestinationUrl = googleAnalyticsOAuthFlow.getDestinationConsentUrl( + workspaceId, definitionId, REDIRECT_URL, emptyMap()); final String expectedDestinationUrl = String.format( "https://accounts.google.com/o/oauth2/v2/auth?client_id=%s&redirect_uri=%s&response_type=code&scope=%s&access_type=offline&state=%s&include_granted_scopes=true&prompt=consent", getClientId(), diff --git a/airbyte-server/src/main/java/io/airbyte/server/handlers/OAuthHandler.java b/airbyte-server/src/main/java/io/airbyte/server/handlers/OAuthHandler.java index 5c5222e65abc..4ab2c4c06150 100644 --- a/airbyte-server/src/main/java/io/airbyte/server/handlers/OAuthHandler.java +++ b/airbyte-server/src/main/java/io/airbyte/server/handlers/OAuthHandler.java @@ -47,11 +47,14 @@ public OAuthHandler(final ConfigRepository configRepository, final TrackingClien public OAuthConsentRead getSourceOAuthConsent(final SourceOauthConsentRequest sourceDefinitionIdRequestBody) throws JsonValidationException, ConfigNotFoundException, IOException { final OAuthFlowImplementation oAuthFlowImplementation = getSourceOAuthFlowImplementation(sourceDefinitionIdRequestBody.getSourceDefinitionId()); - final ImmutableMap metadata = generateSourceMetadata(sourceDefinitionIdRequestBody.getSourceDefinitionId()); - final OAuthConsentRead result = new OAuthConsentRead().consentUrl(oAuthFlowImplementation.getSourceConsentUrl( - sourceDefinitionIdRequestBody.getWorkspaceId(), - sourceDefinitionIdRequestBody.getSourceDefinitionId(), - sourceDefinitionIdRequestBody.getRedirectUrl())); + final ImmutableMap metadata = generateSourceMetadata( + sourceDefinitionIdRequestBody.getSourceDefinitionId()); + final OAuthConsentRead result = new OAuthConsentRead().consentUrl( + oAuthFlowImplementation.getSourceConsentUrl( + sourceDefinitionIdRequestBody.getWorkspaceId(), + sourceDefinitionIdRequestBody.getSourceDefinitionId(), + sourceDefinitionIdRequestBody.getRedirectUrl(), + sourceDefinitionIdRequestBody.getParams())); try { trackingClient.track(sourceDefinitionIdRequestBody.getWorkspaceId(), "Get Oauth Consent URL - Backend", metadata); } catch (final Exception e) { @@ -64,11 +67,14 @@ public OAuthConsentRead getDestinationOAuthConsent(final DestinationOauthConsent throws JsonValidationException, ConfigNotFoundException, IOException { final OAuthFlowImplementation oAuthFlowImplementation = getDestinationOAuthFlowImplementation(destinationDefinitionIdRequestBody.getDestinationDefinitionId()); - final ImmutableMap metadata = generateDestinationMetadata(destinationDefinitionIdRequestBody.getDestinationDefinitionId()); - final OAuthConsentRead result = new OAuthConsentRead().consentUrl(oAuthFlowImplementation.getDestinationConsentUrl( - destinationDefinitionIdRequestBody.getWorkspaceId(), - destinationDefinitionIdRequestBody.getDestinationDefinitionId(), - destinationDefinitionIdRequestBody.getRedirectUrl())); + final ImmutableMap metadata = generateDestinationMetadata( + destinationDefinitionIdRequestBody.getDestinationDefinitionId()); + final OAuthConsentRead result = new OAuthConsentRead().consentUrl( + oAuthFlowImplementation.getDestinationConsentUrl( + destinationDefinitionIdRequestBody.getWorkspaceId(), + destinationDefinitionIdRequestBody.getDestinationDefinitionId(), + destinationDefinitionIdRequestBody.getRedirectUrl(), + destinationDefinitionIdRequestBody.getParams())); try { trackingClient.track(destinationDefinitionIdRequestBody.getWorkspaceId(), "Get Oauth Consent URL - Backend", metadata); } catch (final Exception e) {