From 4071dd77345afcadf98d7f5075302f95b6421329 Mon Sep 17 00:00:00 2001 From: Pedro Igor Date: Wed, 18 Oct 2017 20:27:21 -0200 Subject: [PATCH] [KEYCLOAK-5703] - Improving exception handling and parsing server response --- .../client/AuthorizationDeniedException.java | 5 ++ .../resource/AuthorizationResource.java | 13 ++--- .../client/resource/EntitlementResource.java | 22 ++----- .../client/resource/PermissionResource.java | 8 +-- .../client/resource/ProtectedResource.java | 32 ++++++----- .../authorization/client/util/Throwables.java | 57 +++++++++++++++++++ 6 files changed, 93 insertions(+), 44 deletions(-) create mode 100644 authz/client/src/main/java/org/keycloak/authorization/client/util/Throwables.java diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/AuthorizationDeniedException.java b/authz/client/src/main/java/org/keycloak/authorization/client/AuthorizationDeniedException.java index 8c706b3848..0356e0ab69 100644 --- a/authz/client/src/main/java/org/keycloak/authorization/client/AuthorizationDeniedException.java +++ b/authz/client/src/main/java/org/keycloak/authorization/client/AuthorizationDeniedException.java @@ -21,7 +21,12 @@ package org.keycloak.authorization.client; * @author Pedro Igor */ public class AuthorizationDeniedException extends RuntimeException { + public AuthorizationDeniedException(Throwable cause) { super(cause); } + + public AuthorizationDeniedException(String message, Throwable cause) { + super(message, cause); + } } diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/resource/AuthorizationResource.java b/authz/client/src/main/java/org/keycloak/authorization/client/resource/AuthorizationResource.java index 9a03e5c2e6..0f22ebe89e 100644 --- a/authz/client/src/main/java/org/keycloak/authorization/client/resource/AuthorizationResource.java +++ b/authz/client/src/main/java/org/keycloak/authorization/client/resource/AuthorizationResource.java @@ -18,11 +18,11 @@ package org.keycloak.authorization.client.resource; -import org.keycloak.authorization.client.AuthorizationDeniedException; +import static org.keycloak.authorization.client.util.Throwables.handleAndWrapException; + import org.keycloak.authorization.client.representation.AuthorizationRequest; import org.keycloak.authorization.client.representation.AuthorizationResponse; import org.keycloak.authorization.client.util.Http; -import org.keycloak.authorization.client.util.HttpResponseException; import org.keycloak.util.JsonSerialization; /** @@ -44,13 +44,8 @@ public class AuthorizationResource { .authorizationBearer(this.accessToken) .json(JsonSerialization.writeValueAsBytes(request)) .response().json(AuthorizationResponse.class).execute(); - } catch (HttpResponseException e) { - if (403 == e.getStatusCode()) { - throw new AuthorizationDeniedException(e); - } - throw new RuntimeException("Failed to obtain authorization data.", e); - } catch (Exception e) { - throw new RuntimeException("Failed to obtain authorization data.", e); + } catch (Exception cause) { + throw handleAndWrapException("Failed to obtain authorization data", cause); } } } diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/resource/EntitlementResource.java b/authz/client/src/main/java/org/keycloak/authorization/client/resource/EntitlementResource.java index f37b280daf..1103c2d0e8 100644 --- a/authz/client/src/main/java/org/keycloak/authorization/client/resource/EntitlementResource.java +++ b/authz/client/src/main/java/org/keycloak/authorization/client/resource/EntitlementResource.java @@ -1,10 +1,10 @@ package org.keycloak.authorization.client.resource; -import org.keycloak.authorization.client.AuthorizationDeniedException; +import static org.keycloak.authorization.client.util.Throwables.handleAndWrapException; + import org.keycloak.authorization.client.representation.EntitlementRequest; import org.keycloak.authorization.client.representation.EntitlementResponse; import org.keycloak.authorization.client.util.Http; -import org.keycloak.authorization.client.util.HttpResponseException; import org.keycloak.util.JsonSerialization; /** @@ -25,13 +25,8 @@ public class EntitlementResource { return this.http.get("/authz/entitlement/" + resourceServerId) .authorizationBearer(eat) .response().json(EntitlementResponse.class).execute(); - } catch (HttpResponseException e) { - if (403 == e.getStatusCode()) { - throw new AuthorizationDeniedException(e); - } - throw new RuntimeException("Failed to obtain entitlements.", e); - } catch (Exception e) { - throw new RuntimeException("Failed to obtain entitlements.", e); + } catch (Exception cause) { + throw handleAndWrapException("Failed to obtain entitlements", cause); } } @@ -41,13 +36,8 @@ public class EntitlementResource { .authorizationBearer(eat) .json(JsonSerialization.writeValueAsBytes(request)) .response().json(EntitlementResponse.class).execute(); - } catch (HttpResponseException e) { - if (403 == e.getStatusCode()) { - throw new AuthorizationDeniedException(e); - } - throw new RuntimeException("Failed to obtain entitlements.", e); - } catch (Exception e) { - throw new RuntimeException("Failed to obtain entitlements.", e); + } catch (Exception cause) { + throw handleAndWrapException("Failed to obtain entitlements", cause); } } } diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/resource/PermissionResource.java b/authz/client/src/main/java/org/keycloak/authorization/client/resource/PermissionResource.java index 72247b2414..960955f9ca 100644 --- a/authz/client/src/main/java/org/keycloak/authorization/client/resource/PermissionResource.java +++ b/authz/client/src/main/java/org/keycloak/authorization/client/resource/PermissionResource.java @@ -17,13 +17,13 @@ */ package org.keycloak.authorization.client.resource; +import static org.keycloak.authorization.client.util.Throwables.handleAndWrapException; + import org.keycloak.authorization.client.representation.PermissionRequest; import org.keycloak.authorization.client.representation.PermissionResponse; import org.keycloak.authorization.client.util.Http; import org.keycloak.util.JsonSerialization; -import java.io.IOException; - /** * @author Pedro Igor */ @@ -43,8 +43,8 @@ public class PermissionResource { .authorizationBearer(this.pat) .json(JsonSerialization.writeValueAsBytes(request)) .response().json(PermissionResponse.class).execute(); - } catch (IOException e) { - throw new RuntimeException("Error obtaining permission ticket.", e); + } catch (Exception cause) { + throw handleAndWrapException("Error obtaining permission ticket", cause); } } } diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectedResource.java b/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectedResource.java index 91c78a2f44..d52ccc011a 100644 --- a/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectedResource.java +++ b/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectedResource.java @@ -17,13 +17,15 @@ */ package org.keycloak.authorization.client.resource; +import static org.keycloak.authorization.client.util.Throwables.handleAndWrapException; + +import java.util.Set; + import org.keycloak.authorization.client.representation.RegistrationResponse; import org.keycloak.authorization.client.representation.ResourceRepresentation; import org.keycloak.authorization.client.util.Http; import org.keycloak.util.JsonSerialization; -import java.util.Set; - /** * @author Pedro Igor */ @@ -43,8 +45,8 @@ public class ProtectedResource { .authorizationBearer(this.pat) .json(JsonSerialization.writeValueAsBytes(resource)) .response().json(RegistrationResponse.class).execute(); - } catch (Exception e) { - throw new RuntimeException("Could not create resource.", e); + } catch (Exception cause) { + throw handleAndWrapException("Could not create resource", cause); } } @@ -53,8 +55,8 @@ public class ProtectedResource { this.http.put("/authz/protection/resource_set/" + resource.getId()) .authorizationBearer(this.pat) .json(JsonSerialization.writeValueAsBytes(resource)).execute(); - } catch (Exception e) { - throw new RuntimeException("Could not create resource.", e); + } catch (Exception cause) { + throw handleAndWrapException("Could not update resource", cause); } } @@ -63,8 +65,8 @@ public class ProtectedResource { return this.http.get("/authz/protection/resource_set/" + id) .authorizationBearer(this.pat) .response().json(RegistrationResponse.class).execute(); - } catch (Exception e) { - throw new RuntimeException("Could not find resource.", e); + } catch (Exception cause) { + throw handleAndWrapException("Could not find resource", cause); } } @@ -74,8 +76,8 @@ public class ProtectedResource { .authorizationBearer(this.pat) .param("filter", filter) .response().json(Set.class).execute(); - } catch (Exception e) { - throw new RuntimeException("Could not find resource.", e); + } catch (Exception cause) { + throw handleAndWrapException("Could not find resource", cause); } } @@ -84,8 +86,8 @@ public class ProtectedResource { return this.http.get("/authz/protection/resource_set") .authorizationBearer(this.pat) .response().json(Set.class).execute(); - } catch (Exception e) { - throw new RuntimeException("Could not find resource.", e); + } catch (Exception cause) { + throw handleAndWrapException("Could not find resource", cause); } } @@ -94,8 +96,8 @@ public class ProtectedResource { this.http.delete("/authz/protection/resource_set/" + id) .authorizationBearer(this.pat) .execute(); - } catch (Exception e) { - throw new RuntimeException("Could not delete resource.", e); + } catch (Exception cause) { + throw handleAndWrapException("Could not delete resource", cause); } } -} +} \ No newline at end of file diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/util/Throwables.java b/authz/client/src/main/java/org/keycloak/authorization/client/util/Throwables.java new file mode 100644 index 0000000000..d51b27ecc7 --- /dev/null +++ b/authz/client/src/main/java/org/keycloak/authorization/client/util/Throwables.java @@ -0,0 +1,57 @@ +/* + * Copyright 2017 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.authorization.client.util; + +import org.keycloak.authorization.client.AuthorizationDeniedException; + +/** + * @author Pedro Igor + */ +public final class Throwables { + + /** + * Handles an {@code exception} and wraps it into a {@link RuntimeException}. The resulting exception contains + * more details in case the given {@code exception} is of a {@link HttpResponseException}. + * + * @param message the message + * @param exception the root exception + * @return a {@link RuntimeException} wrapping the given {@code exception} + */ + public static RuntimeException handleAndWrapException(String message, Exception exception) { + if (exception instanceof HttpResponseException) { + throw handleAndWrapHttpResponseException(message, HttpResponseException.class.cast(exception)); + } + + return new RuntimeException(message, exception); + } + + private static RuntimeException handleAndWrapHttpResponseException(String message, HttpResponseException exception) { + HttpResponseException hre = HttpResponseException.class.cast(exception); + StringBuilder detail = new StringBuilder(message); + byte[] bytes = hre.getBytes(); + + if (bytes != null) { + detail.append(". Server message: ").append(new String(bytes)); + } + + if (403 == exception.getStatusCode()) { + throw new AuthorizationDeniedException(detail.toString(), exception); + } + + return new RuntimeException(detail.toString(), exception); + } +}