From 6e7e9fb63740396e09b682aed4159b97c07d5ae7 Mon Sep 17 00:00:00 2001 From: gaohan <1135494872@qq.com> Date: Fri, 26 Feb 2021 16:35:38 +0800 Subject: [PATCH 1/2] Support conditional access policy in obo flow. (#18354) --- eng/versioning/external_dependencies.txt | 1 + .../configuration/AADSampleConfiguration.java | 24 ++--- .../aad/controller/SampleController.java | 2 +- .../src/main/resources/application.yml | 3 + .../sample/aad/config/WebClientConfig.java | 27 +++++ .../controller/CallOboServerController.java | 60 +++++++++++ .../src/main/resources/templates/index.html | 1 + sdk/spring/azure-spring-boot/pom.xml | 7 ++ ...ADOAuth2OboAuthorizedClientRepository.java | 42 +++++++- .../AADAuthenticationFailureHandler.java | 59 ----------- .../AADHandleConditionalAccessFilter.java | 81 ++++++++++++++ .../spring/aad/webapp/AADOAuth2Error.java | 76 ------------- .../aad/webapp/AADWebAppConfiguration.java | 2 +- .../AADWebSecurityConfigurerAdapter.java | 24 ++--- ...ConditionalAccessResponseErrorHandler.java | 100 ------------------ .../spring/autoconfigure/aad/Constants.java | 1 + ...itionalAccessResponseErrorHandlerTest.java | 56 ---------- 17 files changed, 237 insertions(+), 329 deletions(-) create mode 100644 sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-active-directory-webapp/src/main/java/com/azure/spring/sample/aad/config/WebClientConfig.java create mode 100644 sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-active-directory-webapp/src/main/java/com/azure/spring/sample/aad/controller/CallOboServerController.java delete mode 100644 sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapp/AADAuthenticationFailureHandler.java create mode 100644 sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapp/AADHandleConditionalAccessFilter.java delete mode 100644 sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapp/AADOAuth2Error.java delete mode 100644 sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapp/ConditionalAccessResponseErrorHandler.java delete mode 100644 sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/webapp/ConditionalAccessResponseErrorHandlerTest.java diff --git a/eng/versioning/external_dependencies.txt b/eng/versioning/external_dependencies.txt index 89f9243f9a00..1502121e62df 100644 --- a/eng/versioning/external_dependencies.txt +++ b/eng/versioning/external_dependencies.txt @@ -132,6 +132,7 @@ org.springframework:spring-messaging;5.2.10.RELEASE org.springframework:spring-tx;5.2.10.RELEASE org.springframework:spring-web;5.2.10.RELEASE org.springframework:spring-webmvc;5.2.10.RELEASE +org.springframework:spring-webflux;5.2.10.RELEASE # spring-boot-starter-parent is not managed by spring-boot-dependencies or spring-cloud-dependencies. org.springframework.boot:spring-boot-starter-parent;2.3.7.RELEASE diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-active-directory-resource-server-obo/src/main/java/com/azure/spring/sample/aad/configuration/AADSampleConfiguration.java b/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-active-directory-resource-server-obo/src/main/java/com/azure/spring/sample/aad/configuration/AADSampleConfiguration.java index c1f5d8b283bc..41ff9abd115e 100644 --- a/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-active-directory-resource-server-obo/src/main/java/com/azure/spring/sample/aad/configuration/AADSampleConfiguration.java +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-active-directory-resource-server-obo/src/main/java/com/azure/spring/sample/aad/configuration/AADSampleConfiguration.java @@ -15,25 +15,13 @@ public class AADSampleConfiguration { @Bean - public OAuth2AuthorizedClientManager authorizedClientManager( - ClientRegistrationRepository clientRegistrationRepository, - OAuth2AuthorizedClientRepository authorizedClientRepository) { - OAuth2AuthorizedClientProvider authorizedClientProvider = - OAuth2AuthorizedClientProviderBuilder.builder() - .refreshToken() - .build(); - DefaultOAuth2AuthorizedClientManager authorizedClientManager = - new DefaultOAuth2AuthorizedClientManager(clientRegistrationRepository, authorizedClientRepository); - authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider); - return authorizedClientManager; - } - - @Bean - public WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) { - ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client = - new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager); + public static WebClient webClient(ClientRegistrationRepository clientRegistrationRepository, + OAuth2AuthorizedClientRepository authorizedClientRepository) { + ServletOAuth2AuthorizedClientExchangeFilterFunction function = + new ServletOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrationRepository, + authorizedClientRepository); return WebClient.builder() - .apply(oauth2Client.oauth2Configuration()) + .apply(function.oauth2Configuration()) .build(); } } diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-active-directory-resource-server-obo/src/main/java/com/azure/spring/sample/aad/controller/SampleController.java b/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-active-directory-resource-server-obo/src/main/java/com/azure/spring/sample/aad/controller/SampleController.java index 0ca6afc267e1..4c90780e4101 100644 --- a/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-active-directory-resource-server-obo/src/main/java/com/azure/spring/sample/aad/controller/SampleController.java +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-active-directory-resource-server-obo/src/main/java/com/azure/spring/sample/aad/controller/SampleController.java @@ -28,7 +28,7 @@ public class SampleController { private static final String GRAPH_ME_ENDPOINT = "https://graph.microsoft.com/v1.0/me"; - private static final String CUSTOM_LOCAL_FILE_ENDPOINT = "http://localhost:8080/file"; + private static final String CUSTOM_LOCAL_FILE_ENDPOINT = "http://localhost:8082/file"; @Autowired private WebClient webClient; diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-active-directory-resource-server/src/main/resources/application.yml b/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-active-directory-resource-server/src/main/resources/application.yml index e0beedb9ab07..bb6453e448e4 100644 --- a/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-active-directory-resource-server/src/main/resources/application.yml +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-active-directory-resource-server/src/main/resources/application.yml @@ -2,6 +2,9 @@ # In v2.0 tokens, this is always the client ID of the API, while in v1.0 tokens it can be the client ID or the resource URI used in the request. # If you are using v1.0 tokens, configure both to properly complete the audience validation. +server: + port: 8082 + #azure: # activedirectory: # client-id: diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-active-directory-webapp/src/main/java/com/azure/spring/sample/aad/config/WebClientConfig.java b/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-active-directory-webapp/src/main/java/com/azure/spring/sample/aad/config/WebClientConfig.java new file mode 100644 index 000000000000..846c28b5c15c --- /dev/null +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-active-directory-webapp/src/main/java/com/azure/spring/sample/aad/config/WebClientConfig.java @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.sample.aad.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; +import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository; +import org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction; +import org.springframework.web.reactive.function.client.WebClient; + + +@Configuration +public class WebClientConfig { + + @Bean + public static WebClient webClient(ClientRegistrationRepository clientRegistrationRepository, + OAuth2AuthorizedClientRepository authorizedClientRepository) { + ServletOAuth2AuthorizedClientExchangeFilterFunction function = + new ServletOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrationRepository, + authorizedClientRepository); + return WebClient.builder() + .apply(function.oauth2Configuration()) + .build(); + } +} diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-active-directory-webapp/src/main/java/com/azure/spring/sample/aad/controller/CallOboServerController.java b/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-active-directory-webapp/src/main/java/com/azure/spring/sample/aad/controller/CallOboServerController.java new file mode 100644 index 000000000000..1a5f1564fd19 --- /dev/null +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-active-directory-webapp/src/main/java/com/azure/spring/sample/aad/controller/CallOboServerController.java @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.sample.aad.controller; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; +import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.reactive.function.client.WebClient; + + +import static org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient; + +@Controller +public class CallOboServerController { + + private static final Logger LOGGER = LoggerFactory.getLogger(CallOboServerController.class); + + private static final String CUSTOM_LOCAL_FILE_ENDPOINT = "http://localhost:8081/call-custom"; + + @Autowired + private WebClient webClient; + + /** + * Call obo server, combine all the response and return. + * @param obo authorized client for Custom + * @return Response Graph and Custom data. + */ + @GetMapping("/obo") + @ResponseBody + public String callOboServer(@RegisteredOAuth2AuthorizedClient("obo") OAuth2AuthorizedClient obo) { + return callOboEndpoint(obo); + } + + /** + * Call obo local file endpoint + * @param obo Authorized Client + * @return Response string data. + */ + private String callOboEndpoint(OAuth2AuthorizedClient obo) { + if (null != obo) { + String body = webClient + .get() + .uri(CUSTOM_LOCAL_FILE_ENDPOINT) + .attributes(oauth2AuthorizedClient(obo)) + .retrieve() + .bodyToMono(String.class) + .block(); + LOGGER.info("Response from obo server: {}", body); + return "Obo server response " + (null != body ? "success." : "failed."); + } else { + return "Obo server response failed."; + } + } +} diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-active-directory-webapp/src/main/resources/templates/index.html b/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-active-directory-webapp/src/main/resources/templates/index.html index 7fe764b65dcd..edacd62d1799 100644 --- a/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-active-directory-webapp/src/main/resources/templates/index.html +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-active-directory-webapp/src/main/resources/templates/index.html @@ -34,6 +34,7 @@

Azure Active Directory OAuth 2.0 Login with Spring Security

Group2 Message | Graph Client | Arm Client | + Obo Client | diff --git a/sdk/spring/azure-spring-boot/pom.xml b/sdk/spring/azure-spring-boot/pom.xml index bb4d8f5460e7..8a8aaa9ab0f9 100644 --- a/sdk/spring/azure-spring-boot/pom.xml +++ b/sdk/spring/azure-spring-boot/pom.xml @@ -270,6 +270,12 @@ spring-core 5.2.10.RELEASE + + org.springframework + spring-webflux + 5.2.10.RELEASE + true + org.apache.httpcomponents httpclient @@ -302,6 +308,7 @@ org.springframework:spring-core:[5.2.10.RELEASE] org.springframework:spring-web:[5.2.10.RELEASE] org.springframework:spring-jms:[5.2.10.RELEASE] + org.springframework:spring-webflux:[5.2.10.RELEASE] org.springframework.boot:spring-boot-actuator-autoconfigure:[2.3.5.RELEASE] org.springframework.boot:spring-boot-autoconfigure-processor:[2.3.5.RELEASE] org.springframework.boot:spring-boot-autoconfigure:[2.3.5.RELEASE] diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapi/AADOAuth2OboAuthorizedClientRepository.java b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapi/AADOAuth2OboAuthorizedClientRepository.java index 82c74fbaa251..5e649b9265b6 100644 --- a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapi/AADOAuth2OboAuthorizedClientRepository.java +++ b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapi/AADOAuth2OboAuthorizedClientRepository.java @@ -3,28 +3,42 @@ package com.azure.spring.aad.webapi; +import com.azure.spring.autoconfigure.aad.Constants; import com.microsoft.aad.msal4j.ClientCredentialFactory; import com.microsoft.aad.msal4j.ConfidentialClientApplication; import com.microsoft.aad.msal4j.IClientSecret; +import com.microsoft.aad.msal4j.MsalInteractionRequiredException; import com.microsoft.aad.msal4j.OnBehalfOfParameters; import com.microsoft.aad.msal4j.UserAssertion; import com.nimbusds.jwt.JWT; import com.nimbusds.jwt.JWTParser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository; import org.springframework.security.oauth2.core.OAuth2AccessToken; +import org.springframework.security.oauth2.core.OAuth2ErrorCodes; +import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; import org.springframework.security.oauth2.server.resource.authentication.AbstractOAuth2TokenAuthenticationToken; +import org.springframework.util.Assert; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.net.MalformedURLException; +import java.text.ParseException; import java.time.Instant; import java.util.Date; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ExecutionException; /** *

@@ -86,8 +100,18 @@ public T loadAuthorizedClient(String registra oAuth2AccessToken); request.setAttribute(oboAuthorizedClientAttributeName, (T) oAuth2AuthorizedClient); return (T) oAuth2AuthorizedClient; - } catch (Throwable throwable) { - LOGGER.error("Failed to load authorized client.", throwable); + } catch (ExecutionException exception) { + // Handle conditional access policy for obo flow. + // A user interaction is required, but we are in a web API, and therefore, we need to report back to the + // client through a 'WWW-Authenticate' header https://tools.ietf.org/html/rfc6750#section-3.1 + Optional.of(exception) + .map(Throwable::getCause) + .filter(e -> e instanceof MsalInteractionRequiredException) + .map(e -> (MsalInteractionRequiredException) e) + .ifPresent(this::replyForbiddenWithWwwAuthenticateHeader); + LOGGER.error("Failed to load authorized client.", exception); + } catch (InterruptedException | ParseException exception) { + LOGGER.error("Failed to load authorized client.", exception); } return null; } @@ -130,4 +154,18 @@ private String interceptAuthorizationUri(String authorizationUri) { } return null; } + + void replyForbiddenWithWwwAuthenticateHeader(MsalInteractionRequiredException exception) { + ServletRequestAttributes attr = + (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); + HttpServletResponse response = attr.getResponse(); + Assert.notNull(response, "HttpServletResponse should not be null."); + response.setStatus(HttpStatus.FORBIDDEN.value()); + Map parameters = new LinkedHashMap<>(); + parameters.put(Constants.CONDITIONAL_ACCESS_POLICY_CLAIMS, exception.claims()); + parameters.put(OAuth2ParameterNames.ERROR, OAuth2ErrorCodes.INVALID_TOKEN); + parameters.put(OAuth2ParameterNames.ERROR_DESCRIPTION, "The resource server requires higher privileges than " + + "provided by the access token"); + response.addHeader(HttpHeaders.WWW_AUTHENTICATE, Constants.BEARER_PREFIX + parameters.toString()); + } } diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapp/AADAuthenticationFailureHandler.java b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapp/AADAuthenticationFailureHandler.java deleted file mode 100644 index 2a9ddaeb78b9..000000000000 --- a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapp/AADAuthenticationFailureHandler.java +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.azure.spring.aad.webapp; - -import com.azure.spring.autoconfigure.aad.Constants; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.oauth2.core.OAuth2AuthenticationException; -import org.springframework.security.web.authentication.AuthenticationFailureHandler; -import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; -import org.springframework.security.web.savedrequest.DefaultSavedRequest; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.Optional; - -/** - * Redirect URL for handling OAuthentication failure - */ -public class AADAuthenticationFailureHandler implements AuthenticationFailureHandler { - private static final String DEFAULT_FAILURE_URL = "/login?error"; - private final AuthenticationFailureHandler defaultHandler; - - public AADAuthenticationFailureHandler() { - this.defaultHandler = new SimpleUrlAuthenticationFailureHandler(DEFAULT_FAILURE_URL); - } - - @Override - public void onAuthenticationFailure(HttpServletRequest request, - HttpServletResponse response, - AuthenticationException exception) throws IOException, ServletException { - String claims = Optional.of(exception) - .filter(e -> e instanceof OAuth2AuthenticationException) - .map(e -> (OAuth2AuthenticationException) e) - .map(OAuth2AuthenticationException::getError) - .filter(e -> e instanceof AADOAuth2Error) - .map(e -> (AADOAuth2Error) e) - .map(AADOAuth2Error::getClaims) - .orElse(null); - - if (claims == null) { - // Default handle logic - defaultHandler.onAuthenticationFailure(request, response, exception); - } else { - // Handle conditional access policy, step 2. - response.setStatus(302); - request.getSession().setAttribute(Constants.CONDITIONAL_ACCESS_POLICY_CLAIMS, claims); - String redirectUrl = Optional.of(request) - .map(HttpServletRequest::getSession) - .map(s -> s.getAttribute(Constants.SAVED_REQUEST)) - .map(r -> (DefaultSavedRequest) r) - .map(DefaultSavedRequest::getRedirectUrl) - .orElse(null); - response.sendRedirect(redirectUrl); - } - } -} diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapp/AADHandleConditionalAccessFilter.java b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapp/AADHandleConditionalAccessFilter.java new file mode 100644 index 000000000000..8a5a8fff81d7 --- /dev/null +++ b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapp/AADHandleConditionalAccessFilter.java @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.aad.webapp; + +import com.azure.spring.autoconfigure.aad.Constants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.util.StringUtils; +import org.springframework.web.filter.OncePerRequestFilter; +import org.springframework.web.reactive.function.client.WebClientResponseException; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Handle the {@link WebClientResponseException} in On-Behalf-Of flow. + * + *

+ * When the resource-server needs re-acquire token(The request requires higher privileges than provided by the access + * token in On-Behalf-Of flow.), it can sent a 403 with information in the WWW-Authenticate header to web client ,web + * client will throw {@link WebClientResponseException}, web-application can handle this exception to challenge the + * user. + */ +public class AADHandleConditionalAccessFilter extends OncePerRequestFilter { + + private static final Logger LOGGER = LoggerFactory.getLogger(AADHandleConditionalAccessFilter.class); + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, + FilterChain filterChain) throws IOException, ServletException { + try { + filterChain.doFilter(request, response); + } catch (Exception exception) { + Map authParameters = + Optional.of(exception) + .map(Throwable::getCause) + .filter(e -> e instanceof WebClientResponseException) + .map(e -> (WebClientResponseException) e) + .map(WebClientResponseException::getHeaders) + .map(httpHeaders -> httpHeaders.get(HttpHeaders.WWW_AUTHENTICATE)) + .map(list -> list.get(0)) + .map(this::parseAuthParameters) + .orElse(null); + if (authParameters != null && authParameters.containsKey(Constants.CONDITIONAL_ACCESS_POLICY_CLAIMS)) { + request.getSession().setAttribute(Constants.CONDITIONAL_ACCESS_POLICY_CLAIMS, + authParameters.get(Constants.CONDITIONAL_ACCESS_POLICY_CLAIMS)); + response.setStatus(302); + try { + response.sendRedirect(Constants.DEFAULT_AUTHORITY_ENDPOINT_URI); + } catch (IOException e) { + LOGGER.error("Failed to redirect at this response.", exception); + } + return; + } + throw exception; + } + } + + private Map parseAuthParameters(String wwwAuthenticateHeader) { + return Stream.of(wwwAuthenticateHeader) + .filter(header -> !StringUtils.isEmpty(header)) + .filter(header -> header.startsWith(Constants.BEARER_PREFIX)) + .map(str -> str.substring(Constants.BEARER_PREFIX.length() + 1, str.length() - 1)) + .map(str -> str.split(", ")) + .flatMap(Stream::of) + .map(parameter -> parameter.split("=")) + .filter(parameter -> parameter.length > 1) + .collect(Collectors.toMap( + parameters -> parameters[0], + parameters -> parameters[1])); + } +} diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapp/AADOAuth2Error.java b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapp/AADOAuth2Error.java deleted file mode 100644 index bdad533007d8..000000000000 --- a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapp/AADOAuth2Error.java +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.azure.spring.aad.webapp; - -import org.springframework.security.core.SpringSecurityCoreVersion; -import org.springframework.security.oauth2.core.OAuth2Error; - -/** - * Custom error with the error code returned by aad - */ -public class AADOAuth2Error extends OAuth2Error { - private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; - - private final String errorCodes; - - private final String timestamp; - - private final String traceId; - - private final String correlationId; - - private final String subError; - - private final String claims; - - public AADOAuth2Error(String error, String errorDescription, String errorCodes, String timestamp, - String traceId, String correlationId, String errorUri, String subError, String claims) { - super(error, errorDescription, errorUri); - this.errorCodes = errorCodes; - this.timestamp = timestamp; - this.traceId = traceId; - this.correlationId = correlationId; - this.subError = subError; - this.claims = claims; - } - - - public final String getErrorCodes() { - return errorCodes; - } - - public final String getTimestamp() { - return timestamp; - } - - public final String getTraceId() { - return traceId; - } - - public final String getCorrelationId() { - return correlationId; - } - - public final String getSubError() { - return subError; - } - - public final String getClaims() { - return claims; - } - - - @Override - public String toString() { - - return "AADAuthenticationException{" - + ", error_codes='" + errorCodes + '\'' - + ", timestamp='" + timestamp + '\'' - + ", trace_id='" + traceId + '\'' - + ", correlation_id='" + correlationId + '\'' - + ", suberror='" + subError + '\'' - + ", claims='" + claims + '\'' - + '}'; - } -} diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapp/AADWebAppConfiguration.java b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapp/AADWebAppConfiguration.java index 5d81dd95429b..e24505eef3c9 100644 --- a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapp/AADWebAppConfiguration.java +++ b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapp/AADWebAppConfiguration.java @@ -121,6 +121,7 @@ private Set accessTokenScopes() { return result; } + private Set openidScopes() { Set result = new HashSet<>(); result.add("openid"); @@ -198,5 +199,4 @@ protected void configure(HttpSecurity http) throws Exception { .anyRequest().authenticated(); } } - } diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapp/AADWebSecurityConfigurerAdapter.java b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapp/AADWebSecurityConfigurerAdapter.java index 09d00f40a062..b29a4f63ef28 100644 --- a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapp/AADWebSecurityConfigurerAdapter.java +++ b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapp/AADWebSecurityConfigurerAdapter.java @@ -5,7 +5,6 @@ import com.azure.spring.autoconfigure.aad.AADAuthenticationProperties; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.converter.FormHttpMessageConverter; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.oauth2.client.endpoint.DefaultAuthorizationCodeTokenResponseClient; @@ -15,19 +14,16 @@ import org.springframework.security.oauth2.client.oidc.web.logout.OidcClientInitiatedLogoutSuccessHandler; import org.springframework.security.oauth2.client.userinfo.OAuth2UserService; import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver; -import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter; import org.springframework.security.oauth2.core.oidc.user.OidcUser; -import org.springframework.security.web.authentication.AuthenticationFailureHandler; +import org.springframework.security.web.access.ExceptionTranslationFilter; import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; import org.springframework.util.StringUtils; -import org.springframework.web.client.RestTemplate; import java.net.URI; -import java.util.Arrays; /** - * Abstract configuration class, used to make AzureClientRegistrationRepository - * and AuthzCodeGrantRequestEntityConverter take effect. + * Abstract configuration class, used to make AzureClientRegistrationRepository and AuthzCodeGrantRequestEntityConverter + * take effect. */ public abstract class AADWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter { @@ -42,6 +38,9 @@ public abstract class AADWebSecurityConfigurerAdapter extends WebSecurityConfigu protected void configure(HttpSecurity http) throws Exception { // @formatter:off http.oauth2Login() + .authorizationEndpoint() + .authorizationRequestResolver(requestResolver()) + .and() .tokenEndpoint() .accessTokenResponseClient(accessTokenResponseClient()) .and() @@ -51,7 +50,8 @@ protected void configure(HttpSecurity http) throws Exception { .and() .logout() .logoutSuccessHandler(oidcLogoutSuccessHandler()) - .and(); + .and() + .addFilterBefore(new AADHandleConditionalAccessFilter(), ExceptionTranslationFilter.class); // @formatter:off } @@ -68,10 +68,6 @@ protected LogoutSuccessHandler oidcLogoutSuccessHandler() { protected OAuth2AccessTokenResponseClient accessTokenResponseClient() { DefaultAuthorizationCodeTokenResponseClient result = new DefaultAuthorizationCodeTokenResponseClient(); - RestTemplate restTemplate = new RestTemplate(Arrays.asList( - new FormHttpMessageConverter(), new OAuth2AccessTokenResponseHttpMessageConverter())); - restTemplate.setErrorHandler(new ConditionalAccessResponseErrorHandler()); - result.setRestOperations(restTemplate); result.setRequestEntityConverter( new AADOAuth2AuthorizationCodeGrantRequestEntityConverter(repo.getAzureClient())); return result; @@ -80,8 +76,4 @@ protected OAuth2AccessTokenResponseClient a protected OAuth2AuthorizationRequestResolver requestResolver() { return new AADOAuth2AuthorizationRequestResolver(this.repo); } - - protected AuthenticationFailureHandler failureHandler() { - return new AADAuthenticationFailureHandler(); - } } diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapp/ConditionalAccessResponseErrorHandler.java b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapp/ConditionalAccessResponseErrorHandler.java deleted file mode 100644 index 1acce08fb812..000000000000 --- a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/webapp/ConditionalAccessResponseErrorHandler.java +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.azure.spring.aad.webapp; - -import com.nimbusds.oauth2.sdk.token.BearerTokenError; -import org.springframework.core.convert.converter.Converter; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.client.ClientHttpResponse; -import org.springframework.security.oauth2.core.OAuth2AuthorizationException; -import org.springframework.security.oauth2.core.OAuth2Error; -import org.springframework.security.oauth2.core.OAuth2ErrorCodes; -import org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter; -import org.springframework.util.StringUtils; -import org.springframework.web.client.DefaultResponseErrorHandler; -import org.springframework.web.client.ResponseErrorHandler; - -import java.io.IOException; -import java.util.Map; - -/** - * Handle conditional access. - */ -public class ConditionalAccessResponseErrorHandler implements ResponseErrorHandler { - - private final OAuth2ErrorHttpMessageConverter oauth2ErrorConverter = new OAuth2ErrorHttpMessageConverter(); - - private final ResponseErrorHandler defaultErrorHandler = new DefaultResponseErrorHandler(); - - protected ConditionalAccessResponseErrorHandler() { - this.oauth2ErrorConverter.setErrorConverter(new AADOAuth2ErrorConverter()); - } - - @Override - public boolean hasError(ClientHttpResponse response) throws IOException { - return this.defaultErrorHandler.hasError(response); - } - - @Override - public void handleError(ClientHttpResponse response) throws IOException { - - if (!HttpStatus.BAD_REQUEST.equals(response.getStatusCode())) { - this.defaultErrorHandler.handleError(response); - } - - // A Bearer Token Error may be in the WWW-Authenticate response header - OAuth2Error oauth2Error = this.readErrorFromWwwAuthenticate(response.getHeaders()); - if (oauth2Error == null) { - oauth2Error = this.oauth2ErrorConverter.read(OAuth2Error.class, response); - } - /** - * Handle conditional access policy, step 1. - * https://docs.microsoft.com/en-us/azure/active-directory/conditional-access/howto-conditional-access-policy-all-users-mfa - */ - throw new OAuth2AuthorizationException(oauth2Error); - } - - private OAuth2Error readErrorFromWwwAuthenticate(HttpHeaders headers) { - String wwwAuthenticateHeader = headers.getFirst(HttpHeaders.WWW_AUTHENTICATE); - if (!StringUtils.hasText(wwwAuthenticateHeader)) { - return null; - } - - BearerTokenError bearerTokenError; - try { - bearerTokenError = BearerTokenError.parse(wwwAuthenticateHeader); - } catch (Exception ex) { - return null; - } - - String errorCode = bearerTokenError.getCode() != null - ? bearerTokenError.getCode() : OAuth2ErrorCodes.SERVER_ERROR; - String errorDescription = bearerTokenError.getDescription(); - - String errorUri = bearerTokenError.getURI() != null - ? bearerTokenError.getURI().toString() : null; - - return new OAuth2Error(errorCode, errorDescription, errorUri); - } - - - private static class AADOAuth2ErrorConverter implements Converter, OAuth2Error> { - @Override - public OAuth2Error convert(Map parameters) { - String errorCode = parameters.get("error"); - String description = parameters.get("error_description"); - String errorCodes = parameters.get("error_codes"); - String timestamp = parameters.get("timestamp"); - String traceId = parameters.get("trace_id"); - String correlationId = parameters.get("correlation_id"); - String uri = parameters.get("error_uri"); - String subError = parameters.get("suberror"); - String claims = parameters.get("claims"); - - return new AADOAuth2Error(errorCode, description, errorCodes, timestamp, traceId, correlationId, - uri, subError, claims); - } - } -} diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/autoconfigure/aad/Constants.java b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/autoconfigure/aad/Constants.java index 87be1cd2f4e2..e37205c1ca84 100644 --- a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/autoconfigure/aad/Constants.java +++ b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/autoconfigure/aad/Constants.java @@ -19,6 +19,7 @@ public class Constants { public static final Set DEFAULT_AUTHORITY_SET; public static final String ROLE_PREFIX = "ROLE_"; public static final String SAVED_REQUEST = "SPRING_SECURITY_SAVED_REQUEST"; + public static final String DEFAULT_AUTHORITY_ENDPOINT_URI = "/oauth2/authorization/azure"; static { Set authoritySet = new HashSet<>(); diff --git a/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/webapp/ConditionalAccessResponseErrorHandlerTest.java b/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/webapp/ConditionalAccessResponseErrorHandlerTest.java deleted file mode 100644 index 8922efb79d57..000000000000 --- a/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/webapp/ConditionalAccessResponseErrorHandlerTest.java +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.azure.spring.aad.webapp; - -import org.junit.Assert; -import org.junit.Test; -import org.springframework.http.HttpStatus; -import org.springframework.http.client.ClientHttpResponse; -import org.springframework.mock.http.client.MockClientHttpResponse; -import org.springframework.security.oauth2.core.OAuth2AuthorizationException; -import org.springframework.web.client.HttpClientErrorException; -import org.springframework.web.client.ResponseErrorHandler; - -import java.io.IOException; -import java.util.Optional; - -public class ConditionalAccessResponseErrorHandlerTest { - private ResponseErrorHandler azureHandler = new ConditionalAccessResponseErrorHandler(); - private ClientHttpResponse clientHttpResponse = new MockClientHttpResponse(("{\n" - + " \"error\": \"fake_error\",\n" - + " \"error_description\": \" fake_error_description\",\n" - + " \"error_codes\": [\n" - + " 53001\n" - + " ],\n" - + " \"timestamp\": \"fake_timestamp\",\n" - + " \"trace_id\": \"fake_trace_id\",\n" - + " \"correlation_id\": \"fake_correlation_id\",\n" - + " \"error_uri\": \"fake_error_uri\",\n" - + " \"suberror\": \"message_only\",\n" - + " \"claims\": \"{\\\"access_token\\\":{\\\"fake_token\\\":{\\\"essential\\\":true," - + "\\\"values\\\":[\\\"fake_values\\\"]}}}\"\n" - + "}").getBytes(), HttpStatus.BAD_REQUEST); - - @Test - public void azureResponseErrorHandleTest() throws IOException { - AADOAuth2Error error = null; - try { - azureHandler.handleError(clientHttpResponse); - } catch (OAuth2AuthorizationException exception) { - error = (AADOAuth2Error) Optional.of(exception) - .map(OAuth2AuthorizationException::getError).orElse(null); - } - Assert.assertNotNull(error); - } - - @Test - public void defaultErrorHandlerTest() throws IOException { - clientHttpResponse = new MockClientHttpResponse("".getBytes(), HttpStatus.UNAUTHORIZED); - try { - azureHandler.handleError(clientHttpResponse); - } catch (HttpClientErrorException exception) { - Assert.assertNotNull(exception); - } - } -} From cdaea7911c8ff69bf27c65aee18eb30802141116 Mon Sep 17 00:00:00 2001 From: SDKAuto Date: Fri, 26 Feb 2021 08:47:15 +0000 Subject: [PATCH 2/2] CodeGen from PR 13166 in Azure/azure-rest-api-specs Merge b353336f78b47a9a9d83765d68a6b9b87ea76f95 into 89f00c23e9b247304c490504f182aebd0dff4f4e --- .../azure-resourcemanager-kusto/CHANGELOG.md | 3 +- .../azure-resourcemanager-kusto/README.md | 18 +- sdk/kusto/azure-resourcemanager-kusto/pom.xml | 5 + .../resourcemanager/kusto/KustoManager.java | 28 ++- .../AttachedDatabaseConfigurationsImpl.java | 4 +- .../kusto/implementation/ClusterImpl.java | 75 ++++++++ .../ClusterPrincipalAssignmentsImpl.java | 4 +- .../kusto/implementation/ClustersImpl.java | 24 +-- .../implementation/DataConnectionsImpl.java | 4 +- .../DatabasePrincipalAssignmentsImpl.java | 4 +- .../kusto/implementation/DatabasesImpl.java | 8 +- .../KustoManagementClientImpl.java | 4 +- .../kusto/implementation/OperationsImpl.java | 4 +- .../kusto/implementation/Utils.java | 129 ++++++++++++++ .../resourcemanager/kusto/models/Cluster.java | 161 ++++++++++++++++++ 15 files changed, 438 insertions(+), 37 deletions(-) diff --git a/sdk/kusto/azure-resourcemanager-kusto/CHANGELOG.md b/sdk/kusto/azure-resourcemanager-kusto/CHANGELOG.md index ff301aaadbde..fc62d8dc6411 100644 --- a/sdk/kusto/azure-resourcemanager-kusto/CHANGELOG.md +++ b/sdk/kusto/azure-resourcemanager-kusto/CHANGELOG.md @@ -1,7 +1,8 @@ # Release History -## 1.0.0-beta.2 (Unreleased) +## 1.0.0-beta.1 (2021-02-26) +- Azure Resource Manager Kusto client library for Java. This package contains Microsoft Azure SDK for Kusto Management SDK. The Azure Kusto management API provides a RESTful set of web services that interact with Azure Kusto services to manage your clusters and databases. The API enables you to create, update, and delete clusters and databases. Package tag package-2020-09-18. For documentation on how to use this package, please see [Azure Management Libraries for Java](https://aka.ms/azsdk/java/mgmt). ## 1.0.0-beta.1 (2020-12-18) diff --git a/sdk/kusto/azure-resourcemanager-kusto/README.md b/sdk/kusto/azure-resourcemanager-kusto/README.md index 1c4f49078b3e..699a29f67083 100644 --- a/sdk/kusto/azure-resourcemanager-kusto/README.md +++ b/sdk/kusto/azure-resourcemanager-kusto/README.md @@ -4,6 +4,20 @@ Azure Resource Manager Kusto client library for Java. This package contains Microsoft Azure SDK for Kusto Management SDK. The Azure Kusto management API provides a RESTful set of web services that interact with Azure Kusto services to manage your clusters and databases. The API enables you to create, update, and delete clusters and databases. Package tag package-2020-09-18. For documentation on how to use this package, please see [Azure Management Libraries for Java](https://aka.ms/azsdk/java/mgmt). +## We'd love to hear your feedback + +We're always working on improving our products and the way we communicate with our users. So we'd love to learn what's working and how we can do better. + +If you haven't already, please take a few minutes to [complete this short survey][survey] we have put together. + +Thank you in advance for your collaboration. We really appreciate your time! + +## Documentation + +Various documentation is available to help you get started + +- [API reference documentation][docs] + ## Getting started ### Prerequisites @@ -18,7 +32,7 @@ This package contains Microsoft Azure SDK for Kusto Management SDK. The Azure Ku com.azure.resourcemanager azure-resourcemanager-kusto - 1.0.0-beta.1 + 1.0.0-beta.2 ``` [//]: # ({x-version-update-end}) @@ -75,6 +89,8 @@ For details on contributing to this repository, see the [contributing guide](htt 1. Create new Pull Request +[survey]: https://microsoft.qualtrics.com/jfe/form/SV_ehN0lIk2FKEBkwd?Q_CHL=DOCS +[docs]: https://azure.github.io/azure-sdk-for-java/ [jdk]: https://docs.microsoft.com/java/azure/jdk/ [azure_subscription]: https://azure.microsoft.com/free/ [azure_identity]: https://github.com/Azure/azure-sdk-for-java/blob/master/sdk/identity/azure-identity diff --git a/sdk/kusto/azure-resourcemanager-kusto/pom.xml b/sdk/kusto/azure-resourcemanager-kusto/pom.xml index f6cdef2ad56b..ae37871518f5 100644 --- a/sdk/kusto/azure-resourcemanager-kusto/pom.xml +++ b/sdk/kusto/azure-resourcemanager-kusto/pom.xml @@ -41,6 +41,11 @@ + + com.azure + azure-core + 1.13.0 + com.azure azure-core-management diff --git a/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/KustoManager.java b/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/KustoManager.java index e35bcd166bdd..3005908bd061 100644 --- a/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/KustoManager.java +++ b/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/KustoManager.java @@ -180,17 +180,31 @@ public KustoManager authenticate(TokenCredential credential, AzureProfile profil Objects.requireNonNull(credential, "'credential' cannot be null."); Objects.requireNonNull(profile, "'profile' cannot be null."); + StringBuilder userAgentBuilder = new StringBuilder(); + userAgentBuilder + .append("azsdk-java") + .append("-") + .append("com.azure.resourcemanager.kusto") + .append("/") + .append("1.0.0-beta.1"); + if (!Configuration.getGlobalConfiguration().get("AZURE_TELEMETRY_DISABLED", false)) { + userAgentBuilder + .append(" (") + .append(Configuration.getGlobalConfiguration().get("java.version")) + .append("; ") + .append(Configuration.getGlobalConfiguration().get("os.name")) + .append("; ") + .append(Configuration.getGlobalConfiguration().get("os.version")) + .append("; auto-generated)"); + } else { + userAgentBuilder.append(" (auto-generated)"); + } + if (retryPolicy == null) { retryPolicy = new RetryPolicy("Retry-After", ChronoUnit.SECONDS); } List policies = new ArrayList<>(); - policies - .add( - new UserAgentPolicy( - null, - "com.azure.resourcemanager.kusto", - "1.0.0-beta.1", - Configuration.getGlobalConfiguration())); + policies.add(new UserAgentPolicy(userAgentBuilder.toString())); policies.add(new RequestIdPolicy()); HttpPolicyProviders.addBeforeRetryPolicies(policies); policies.add(retryPolicy); diff --git a/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/AttachedDatabaseConfigurationsImpl.java b/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/AttachedDatabaseConfigurationsImpl.java index 41909d2a27aa..69a53ec333e2 100644 --- a/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/AttachedDatabaseConfigurationsImpl.java +++ b/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/AttachedDatabaseConfigurationsImpl.java @@ -32,14 +32,14 @@ public AttachedDatabaseConfigurationsImpl( public PagedIterable listByCluster(String resourceGroupName, String clusterName) { PagedIterable inner = this.serviceClient().listByCluster(resourceGroupName, clusterName); - return inner.mapPage(inner1 -> new AttachedDatabaseConfigurationImpl(inner1, this.manager())); + return Utils.mapPage(inner, inner1 -> new AttachedDatabaseConfigurationImpl(inner1, this.manager())); } public PagedIterable listByCluster( String resourceGroupName, String clusterName, Context context) { PagedIterable inner = this.serviceClient().listByCluster(resourceGroupName, clusterName, context); - return inner.mapPage(inner1 -> new AttachedDatabaseConfigurationImpl(inner1, this.manager())); + return Utils.mapPage(inner, inner1 -> new AttachedDatabaseConfigurationImpl(inner1, this.manager())); } public AttachedDatabaseConfiguration get( diff --git a/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/ClusterImpl.java b/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/ClusterImpl.java index ba154061dd4b..9e4558995472 100644 --- a/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/ClusterImpl.java +++ b/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/ClusterImpl.java @@ -4,16 +4,21 @@ package com.azure.resourcemanager.kusto.implementation; +import com.azure.core.http.rest.PagedIterable; import com.azure.core.management.Region; import com.azure.core.util.Context; import com.azure.resourcemanager.kusto.KustoManager; import com.azure.resourcemanager.kusto.fluent.models.ClusterInner; +import com.azure.resourcemanager.kusto.fluent.models.FollowerDatabaseDefinitionInner; import com.azure.resourcemanager.kusto.models.AzureSku; import com.azure.resourcemanager.kusto.models.Cluster; import com.azure.resourcemanager.kusto.models.ClusterUpdate; +import com.azure.resourcemanager.kusto.models.DiagnoseVirtualNetworkResult; import com.azure.resourcemanager.kusto.models.EngineType; +import com.azure.resourcemanager.kusto.models.FollowerDatabaseDefinition; import com.azure.resourcemanager.kusto.models.Identity; import com.azure.resourcemanager.kusto.models.KeyVaultProperties; +import com.azure.resourcemanager.kusto.models.LanguageExtension; import com.azure.resourcemanager.kusto.models.LanguageExtensionsList; import com.azure.resourcemanager.kusto.models.OptimizedAutoscale; import com.azure.resourcemanager.kusto.models.ProvisioningState; @@ -237,6 +242,76 @@ public Cluster refresh(Context context) { return this; } + public void stop() { + serviceManager.clusters().stop(resourceGroupName, clusterName); + } + + public void stop(Context context) { + serviceManager.clusters().stop(resourceGroupName, clusterName, context); + } + + public void start() { + serviceManager.clusters().start(resourceGroupName, clusterName); + } + + public void start(Context context) { + serviceManager.clusters().start(resourceGroupName, clusterName, context); + } + + public PagedIterable listFollowerDatabases() { + return serviceManager.clusters().listFollowerDatabases(resourceGroupName, clusterName); + } + + public PagedIterable listFollowerDatabases(Context context) { + return serviceManager.clusters().listFollowerDatabases(resourceGroupName, clusterName, context); + } + + public void detachFollowerDatabases(FollowerDatabaseDefinitionInner followerDatabaseToRemove) { + serviceManager.clusters().detachFollowerDatabases(resourceGroupName, clusterName, followerDatabaseToRemove); + } + + public void detachFollowerDatabases(FollowerDatabaseDefinitionInner followerDatabaseToRemove, Context context) { + serviceManager + .clusters() + .detachFollowerDatabases(resourceGroupName, clusterName, followerDatabaseToRemove, context); + } + + public DiagnoseVirtualNetworkResult diagnoseVirtualNetwork() { + return serviceManager.clusters().diagnoseVirtualNetwork(resourceGroupName, clusterName); + } + + public DiagnoseVirtualNetworkResult diagnoseVirtualNetwork(Context context) { + return serviceManager.clusters().diagnoseVirtualNetwork(resourceGroupName, clusterName, context); + } + + public PagedIterable listLanguageExtensions() { + return serviceManager.clusters().listLanguageExtensions(resourceGroupName, clusterName); + } + + public PagedIterable listLanguageExtensions(Context context) { + return serviceManager.clusters().listLanguageExtensions(resourceGroupName, clusterName, context); + } + + public void addLanguageExtensions(LanguageExtensionsList languageExtensionsToAdd) { + serviceManager.clusters().addLanguageExtensions(resourceGroupName, clusterName, languageExtensionsToAdd); + } + + public void addLanguageExtensions(LanguageExtensionsList languageExtensionsToAdd, Context context) { + serviceManager + .clusters() + .addLanguageExtensions(resourceGroupName, clusterName, languageExtensionsToAdd, context); + } + + public void removeLanguageExtensions(LanguageExtensionsList languageExtensionsToRemove) { + serviceManager.clusters().removeLanguageExtensions(resourceGroupName, clusterName, languageExtensionsToRemove); + } + + public void removeLanguageExtensions(LanguageExtensionsList languageExtensionsToRemove, Context context) { + serviceManager + .clusters() + .removeLanguageExtensions(resourceGroupName, clusterName, languageExtensionsToRemove, context); + } + public ClusterImpl withRegion(Region location) { this.innerModel().withLocation(location.toString()); return this; diff --git a/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/ClusterPrincipalAssignmentsImpl.java b/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/ClusterPrincipalAssignmentsImpl.java index 9074c340fdf2..aee1eda168fa 100644 --- a/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/ClusterPrincipalAssignmentsImpl.java +++ b/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/ClusterPrincipalAssignmentsImpl.java @@ -101,14 +101,14 @@ public void delete(String resourceGroupName, String clusterName, String principa public PagedIterable list(String resourceGroupName, String clusterName) { PagedIterable inner = this.serviceClient().list(resourceGroupName, clusterName); - return inner.mapPage(inner1 -> new ClusterPrincipalAssignmentImpl(inner1, this.manager())); + return Utils.mapPage(inner, inner1 -> new ClusterPrincipalAssignmentImpl(inner1, this.manager())); } public PagedIterable list( String resourceGroupName, String clusterName, Context context) { PagedIterable inner = this.serviceClient().list(resourceGroupName, clusterName, context); - return inner.mapPage(inner1 -> new ClusterPrincipalAssignmentImpl(inner1, this.manager())); + return Utils.mapPage(inner, inner1 -> new ClusterPrincipalAssignmentImpl(inner1, this.manager())); } public ClusterPrincipalAssignment getById(String id) { diff --git a/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/ClustersImpl.java b/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/ClustersImpl.java index 150184605c9e..3b1fa952f5a6 100644 --- a/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/ClustersImpl.java +++ b/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/ClustersImpl.java @@ -94,14 +94,14 @@ public PagedIterable listFollowerDatabases( String resourceGroupName, String clusterName) { PagedIterable inner = this.serviceClient().listFollowerDatabases(resourceGroupName, clusterName); - return inner.mapPage(inner1 -> new FollowerDatabaseDefinitionImpl(inner1, this.manager())); + return Utils.mapPage(inner, inner1 -> new FollowerDatabaseDefinitionImpl(inner1, this.manager())); } public PagedIterable listFollowerDatabases( String resourceGroupName, String clusterName, Context context) { PagedIterable inner = this.serviceClient().listFollowerDatabases(resourceGroupName, clusterName, context); - return inner.mapPage(inner1 -> new FollowerDatabaseDefinitionImpl(inner1, this.manager())); + return Utils.mapPage(inner, inner1 -> new FollowerDatabaseDefinitionImpl(inner1, this.manager())); } public void detachFollowerDatabases( @@ -140,32 +140,32 @@ public DiagnoseVirtualNetworkResult diagnoseVirtualNetwork( public PagedIterable listByResourceGroup(String resourceGroupName) { PagedIterable inner = this.serviceClient().listByResourceGroup(resourceGroupName); - return inner.mapPage(inner1 -> new ClusterImpl(inner1, this.manager())); + return Utils.mapPage(inner, inner1 -> new ClusterImpl(inner1, this.manager())); } public PagedIterable listByResourceGroup(String resourceGroupName, Context context) { PagedIterable inner = this.serviceClient().listByResourceGroup(resourceGroupName, context); - return inner.mapPage(inner1 -> new ClusterImpl(inner1, this.manager())); + return Utils.mapPage(inner, inner1 -> new ClusterImpl(inner1, this.manager())); } public PagedIterable list() { PagedIterable inner = this.serviceClient().list(); - return inner.mapPage(inner1 -> new ClusterImpl(inner1, this.manager())); + return Utils.mapPage(inner, inner1 -> new ClusterImpl(inner1, this.manager())); } public PagedIterable list(Context context) { PagedIterable inner = this.serviceClient().list(context); - return inner.mapPage(inner1 -> new ClusterImpl(inner1, this.manager())); + return Utils.mapPage(inner, inner1 -> new ClusterImpl(inner1, this.manager())); } public PagedIterable listSkus() { PagedIterable inner = this.serviceClient().listSkus(); - return inner.mapPage(inner1 -> new SkuDescriptionImpl(inner1, this.manager())); + return Utils.mapPage(inner, inner1 -> new SkuDescriptionImpl(inner1, this.manager())); } public PagedIterable listSkus(Context context) { PagedIterable inner = this.serviceClient().listSkus(context); - return inner.mapPage(inner1 -> new SkuDescriptionImpl(inner1, this.manager())); + return Utils.mapPage(inner, inner1 -> new SkuDescriptionImpl(inner1, this.manager())); } public CheckNameResult checkNameAvailability(String location, ClusterCheckNameRequest clusterName) { @@ -195,27 +195,27 @@ public Response checkNameAvailabilityWithResponse( public PagedIterable listSkusByResource(String resourceGroupName, String clusterName) { PagedIterable inner = this.serviceClient().listSkusByResource(resourceGroupName, clusterName); - return inner.mapPage(inner1 -> new AzureResourceSkuImpl(inner1, this.manager())); + return Utils.mapPage(inner, inner1 -> new AzureResourceSkuImpl(inner1, this.manager())); } public PagedIterable listSkusByResource( String resourceGroupName, String clusterName, Context context) { PagedIterable inner = this.serviceClient().listSkusByResource(resourceGroupName, clusterName, context); - return inner.mapPage(inner1 -> new AzureResourceSkuImpl(inner1, this.manager())); + return Utils.mapPage(inner, inner1 -> new AzureResourceSkuImpl(inner1, this.manager())); } public PagedIterable listLanguageExtensions(String resourceGroupName, String clusterName) { PagedIterable inner = this.serviceClient().listLanguageExtensions(resourceGroupName, clusterName); - return inner.mapPage(inner1 -> new LanguageExtensionImpl(inner1, this.manager())); + return Utils.mapPage(inner, inner1 -> new LanguageExtensionImpl(inner1, this.manager())); } public PagedIterable listLanguageExtensions( String resourceGroupName, String clusterName, Context context) { PagedIterable inner = this.serviceClient().listLanguageExtensions(resourceGroupName, clusterName, context); - return inner.mapPage(inner1 -> new LanguageExtensionImpl(inner1, this.manager())); + return Utils.mapPage(inner, inner1 -> new LanguageExtensionImpl(inner1, this.manager())); } public void addLanguageExtensions( diff --git a/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/DataConnectionsImpl.java b/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/DataConnectionsImpl.java index 15d6b180da3b..2dc381c07ae2 100644 --- a/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/DataConnectionsImpl.java +++ b/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/DataConnectionsImpl.java @@ -38,14 +38,14 @@ public PagedIterable listByDatabase( String resourceGroupName, String clusterName, String databaseName) { PagedIterable inner = this.serviceClient().listByDatabase(resourceGroupName, clusterName, databaseName); - return inner.mapPage(inner1 -> new DataConnectionImpl(inner1, this.manager())); + return Utils.mapPage(inner, inner1 -> new DataConnectionImpl(inner1, this.manager())); } public PagedIterable listByDatabase( String resourceGroupName, String clusterName, String databaseName, Context context) { PagedIterable inner = this.serviceClient().listByDatabase(resourceGroupName, clusterName, databaseName, context); - return inner.mapPage(inner1 -> new DataConnectionImpl(inner1, this.manager())); + return Utils.mapPage(inner, inner1 -> new DataConnectionImpl(inner1, this.manager())); } public DataConnectionValidationListResult dataConnectionValidation( diff --git a/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/DatabasePrincipalAssignmentsImpl.java b/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/DatabasePrincipalAssignmentsImpl.java index 55c5c04c735c..db92d0e02b32 100644 --- a/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/DatabasePrincipalAssignmentsImpl.java +++ b/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/DatabasePrincipalAssignmentsImpl.java @@ -120,14 +120,14 @@ public PagedIterable list( String resourceGroupName, String clusterName, String databaseName) { PagedIterable inner = this.serviceClient().list(resourceGroupName, clusterName, databaseName); - return inner.mapPage(inner1 -> new DatabasePrincipalAssignmentImpl(inner1, this.manager())); + return Utils.mapPage(inner, inner1 -> new DatabasePrincipalAssignmentImpl(inner1, this.manager())); } public PagedIterable list( String resourceGroupName, String clusterName, String databaseName, Context context) { PagedIterable inner = this.serviceClient().list(resourceGroupName, clusterName, databaseName, context); - return inner.mapPage(inner1 -> new DatabasePrincipalAssignmentImpl(inner1, this.manager())); + return Utils.mapPage(inner, inner1 -> new DatabasePrincipalAssignmentImpl(inner1, this.manager())); } public DatabasePrincipalAssignment getById(String id) { diff --git a/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/DatabasesImpl.java b/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/DatabasesImpl.java index b961c2ace1b6..970fc1042b1f 100644 --- a/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/DatabasesImpl.java +++ b/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/DatabasesImpl.java @@ -66,13 +66,13 @@ public Response checkNameAvailabilityWithResponse( public PagedIterable listByCluster(String resourceGroupName, String clusterName) { PagedIterable inner = this.serviceClient().listByCluster(resourceGroupName, clusterName); - return inner.mapPage(inner1 -> new DatabaseImpl(inner1, this.manager())); + return Utils.mapPage(inner, inner1 -> new DatabaseImpl(inner1, this.manager())); } public PagedIterable listByCluster(String resourceGroupName, String clusterName, Context context) { PagedIterable inner = this.serviceClient().listByCluster(resourceGroupName, clusterName, context); - return inner.mapPage(inner1 -> new DatabaseImpl(inner1, this.manager())); + return Utils.mapPage(inner, inner1 -> new DatabaseImpl(inner1, this.manager())); } public Database get(String resourceGroupName, String clusterName, String databaseName) { @@ -154,14 +154,14 @@ public PagedIterable listPrincipals( String resourceGroupName, String clusterName, String databaseName) { PagedIterable inner = this.serviceClient().listPrincipals(resourceGroupName, clusterName, databaseName); - return inner.mapPage(inner1 -> new DatabasePrincipalImpl(inner1, this.manager())); + return Utils.mapPage(inner, inner1 -> new DatabasePrincipalImpl(inner1, this.manager())); } public PagedIterable listPrincipals( String resourceGroupName, String clusterName, String databaseName, Context context) { PagedIterable inner = this.serviceClient().listPrincipals(resourceGroupName, clusterName, databaseName, context); - return inner.mapPage(inner1 -> new DatabasePrincipalImpl(inner1, this.manager())); + return Utils.mapPage(inner, inner1 -> new DatabasePrincipalImpl(inner1, this.manager())); } public DatabasePrincipalListResult addPrincipals( diff --git a/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/KustoManagementClientImpl.java b/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/KustoManagementClientImpl.java index cf1e8404ad1e..12fc58e81f75 100644 --- a/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/KustoManagementClientImpl.java +++ b/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/KustoManagementClientImpl.java @@ -319,7 +319,7 @@ public Mono getLroFinalResultOrError(AsyncPollResponse, if (managementError.getCode() == null || managementError.getMessage() == null) { managementError = null; } - } catch (IOException ioe) { + } catch (IOException | RuntimeException ioe) { logger.logThrowableAsWarning(ioe); } } @@ -348,7 +348,7 @@ private static final class HttpResponseImpl extends HttpResponse { super(null); this.statusCode = statusCode; this.httpHeaders = httpHeaders; - this.responseBody = responseBody.getBytes(StandardCharsets.UTF_8); + this.responseBody = responseBody == null ? null : responseBody.getBytes(StandardCharsets.UTF_8); } public int getStatusCode() { diff --git a/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/OperationsImpl.java b/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/OperationsImpl.java index b51bd4a31330..4a1f09e71f1a 100644 --- a/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/OperationsImpl.java +++ b/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/OperationsImpl.java @@ -28,12 +28,12 @@ public OperationsImpl(OperationsClient innerClient, KustoManager serviceManager) public PagedIterable list() { PagedIterable inner = this.serviceClient().list(); - return inner.mapPage(inner1 -> new OperationImpl(inner1, this.manager())); + return Utils.mapPage(inner, inner1 -> new OperationImpl(inner1, this.manager())); } public PagedIterable list(Context context) { PagedIterable inner = this.serviceClient().list(context); - return inner.mapPage(inner1 -> new OperationImpl(inner1, this.manager())); + return Utils.mapPage(inner, inner1 -> new OperationImpl(inner1, this.manager())); } private OperationsClient serviceClient() { diff --git a/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/Utils.java b/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/Utils.java index 457814f522a1..5865dd53469f 100644 --- a/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/Utils.java +++ b/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/implementation/Utils.java @@ -4,12 +4,20 @@ package com.azure.resourcemanager.kusto.implementation; +import com.azure.core.http.rest.PagedFlux; +import com.azure.core.http.rest.PagedIterable; +import com.azure.core.http.rest.PagedResponse; +import com.azure.core.http.rest.PagedResponseBase; import com.azure.core.util.CoreUtils; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import reactor.core.publisher.Mono; final class Utils { static String getValueFromIdByName(String id, String name) { @@ -64,4 +72,125 @@ static String getValueFromIdByParameterName(String id, String pathTemplate, Stri } return null; } + + static PagedIterable mapPage(PagedIterable pageIterable, Function mapper) { + return new PagedIterableImpl(pageIterable, mapper); + } + + private static final class PagedIterableImpl extends PagedIterable { + + private final PagedIterable pageIterable; + private final Function mapper; + private final Function, PagedResponse> pageMapper; + + private PagedIterableImpl(PagedIterable pageIterable, Function mapper) { + super(new PagedFlux(Mono::empty)); + this.pageIterable = pageIterable; + this.mapper = mapper; + this.pageMapper = + page -> + new PagedResponseBase( + page.getRequest(), + page.getStatusCode(), + page.getHeaders(), + page.getElements().stream().map(mapper).collect(Collectors.toList()), + page.getContinuationToken(), + null); + } + + @Override + public Stream stream() { + return pageIterable.stream().map(mapper); + } + + @Override + public Stream> streamByPage() { + return pageIterable.streamByPage().map(pageMapper); + } + + @Override + public Stream> streamByPage(String continuationToken) { + return pageIterable.streamByPage(continuationToken).map(pageMapper); + } + + @Override + public Stream> streamByPage(int preferredPageSize) { + return pageIterable.streamByPage(preferredPageSize).map(pageMapper); + } + + @Override + public Stream> streamByPage(String continuationToken, int preferredPageSize) { + return pageIterable.streamByPage(continuationToken, preferredPageSize).map(pageMapper); + } + + @Override + public Iterator iterator() { + return new IteratorImpl(pageIterable.iterator(), mapper); + } + + @Override + public Iterable> iterableByPage() { + return new IterableImpl, PagedResponse>(pageIterable.iterableByPage(), pageMapper); + } + + @Override + public Iterable> iterableByPage(String continuationToken) { + return new IterableImpl, PagedResponse>( + pageIterable.iterableByPage(continuationToken), pageMapper); + } + + @Override + public Iterable> iterableByPage(int preferredPageSize) { + return new IterableImpl, PagedResponse>( + pageIterable.iterableByPage(preferredPageSize), pageMapper); + } + + @Override + public Iterable> iterableByPage(String continuationToken, int preferredPageSize) { + return new IterableImpl, PagedResponse>( + pageIterable.iterableByPage(continuationToken, preferredPageSize), pageMapper); + } + } + + private static final class IteratorImpl implements Iterator { + + private final Iterator iterator; + private final Function mapper; + + private IteratorImpl(Iterator iterator, Function mapper) { + this.iterator = iterator; + this.mapper = mapper; + } + + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public S next() { + return mapper.apply(iterator.next()); + } + + @Override + public void remove() { + iterator.remove(); + } + } + + private static final class IterableImpl implements Iterable { + + private final Iterable iterable; + private final Function mapper; + + private IterableImpl(Iterable iterable, Function mapper) { + this.iterable = iterable; + this.mapper = mapper; + } + + @Override + public Iterator iterator() { + return new IteratorImpl(iterable.iterator(), mapper); + } + } } diff --git a/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/models/Cluster.java b/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/models/Cluster.java index b547f4c0c56e..82bbebbaa429 100644 --- a/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/models/Cluster.java +++ b/sdk/kusto/azure-resourcemanager-kusto/src/main/java/com/azure/resourcemanager/kusto/models/Cluster.java @@ -4,9 +4,11 @@ package com.azure.resourcemanager.kusto.models; +import com.azure.core.http.rest.PagedIterable; import com.azure.core.management.Region; import com.azure.core.util.Context; import com.azure.resourcemanager.kusto.fluent.models.ClusterInner; +import com.azure.resourcemanager.kusto.fluent.models.FollowerDatabaseDefinitionInner; import java.util.List; import java.util.Map; @@ -577,4 +579,163 @@ interface WithEngineType { * @return the refreshed resource. */ Cluster refresh(Context context); + + /** + * Stops a Kusto cluster. + * + * @throws com.azure.core.management.exception.ManagementException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + */ + void stop(); + + /** + * Stops a Kusto cluster. + * + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws com.azure.core.management.exception.ManagementException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + */ + void stop(Context context); + + /** + * Starts a Kusto cluster. + * + * @throws com.azure.core.management.exception.ManagementException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + */ + void start(); + + /** + * Starts a Kusto cluster. + * + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws com.azure.core.management.exception.ManagementException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + */ + void start(Context context); + + /** + * Returns a list of databases that are owned by this cluster and were followed by another cluster. + * + * @throws com.azure.core.management.exception.ManagementException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the list Kusto database principals operation response. + */ + PagedIterable listFollowerDatabases(); + + /** + * Returns a list of databases that are owned by this cluster and were followed by another cluster. + * + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws com.azure.core.management.exception.ManagementException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the list Kusto database principals operation response. + */ + PagedIterable listFollowerDatabases(Context context); + + /** + * Detaches all followers of a database owned by this cluster. + * + * @param followerDatabaseToRemove The follower databases properties to remove. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws com.azure.core.management.exception.ManagementException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + */ + void detachFollowerDatabases(FollowerDatabaseDefinitionInner followerDatabaseToRemove); + + /** + * Detaches all followers of a database owned by this cluster. + * + * @param followerDatabaseToRemove The follower databases properties to remove. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws com.azure.core.management.exception.ManagementException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + */ + void detachFollowerDatabases(FollowerDatabaseDefinitionInner followerDatabaseToRemove, Context context); + + /** + * Diagnoses network connectivity status for external resources on which the service is dependent on. + * + * @throws com.azure.core.management.exception.ManagementException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response. + */ + DiagnoseVirtualNetworkResult diagnoseVirtualNetwork(); + + /** + * Diagnoses network connectivity status for external resources on which the service is dependent on. + * + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws com.azure.core.management.exception.ManagementException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response. + */ + DiagnoseVirtualNetworkResult diagnoseVirtualNetwork(Context context); + + /** + * Returns a list of language extensions that can run within KQL queries. + * + * @throws com.azure.core.management.exception.ManagementException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the list of language extension objects. + */ + PagedIterable listLanguageExtensions(); + + /** + * Returns a list of language extensions that can run within KQL queries. + * + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws com.azure.core.management.exception.ManagementException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the list of language extension objects. + */ + PagedIterable listLanguageExtensions(Context context); + + /** + * Add a list of language extensions that can run within KQL queries. + * + * @param languageExtensionsToAdd The language extensions to add. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws com.azure.core.management.exception.ManagementException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + */ + void addLanguageExtensions(LanguageExtensionsList languageExtensionsToAdd); + + /** + * Add a list of language extensions that can run within KQL queries. + * + * @param languageExtensionsToAdd The language extensions to add. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws com.azure.core.management.exception.ManagementException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + */ + void addLanguageExtensions(LanguageExtensionsList languageExtensionsToAdd, Context context); + + /** + * Remove a list of language extensions that can run within KQL queries. + * + * @param languageExtensionsToRemove The language extensions to remove. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws com.azure.core.management.exception.ManagementException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + */ + void removeLanguageExtensions(LanguageExtensionsList languageExtensionsToRemove); + + /** + * Remove a list of language extensions that can run within KQL queries. + * + * @param languageExtensionsToRemove The language extensions to remove. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws com.azure.core.management.exception.ManagementException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + */ + void removeLanguageExtensions(LanguageExtensionsList languageExtensionsToRemove, Context context); }