1 /* 2 * Copyright 2022 Google LLC 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * 15 * * Neither the name of Google LLC nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 package com.google.auth.oauth2; 33 34 import com.google.api.client.http.HttpResponseException; 35 import com.google.auth.Retryable; 36 import java.io.IOException; 37 38 /** 39 * Base class for the standard Auth error response. It extends a default exception while keeping 40 * Json response format 41 */ 42 class GoogleAuthException extends IOException implements Retryable { 43 44 private final boolean isRetryable; 45 private final int retryCount; 46 47 /** 48 * Constructor with all parameters 49 * 50 * @param isRetryable A retry status for the related HTTP request 51 * @param retryCount A number of retries performed for the related HTTP request 52 * @param message The detail message (which is saved for later retrieval by the {@link 53 * #getMessage()} method) 54 * @param cause The cause (which is saved for later retrieval by the {@link #getCause()} method). 55 * (A null value is permitted, and indicates that the cause is nonexistent or unknown.) 56 */ GoogleAuthException(boolean isRetryable, int retryCount, String message, Throwable cause)57 GoogleAuthException(boolean isRetryable, int retryCount, String message, Throwable cause) { 58 super(message, cause); 59 this.isRetryable = isRetryable; 60 this.retryCount = retryCount; 61 } 62 63 /** 64 * Constructor with message defaulted to the cause 65 * 66 * @param isRetryable A retry status for the related HTTP request 67 * @param retryCount A number of retries performed for the related HTTP request 68 * @param cause The cause (which is saved for later retrieval by the {@link #getCause()} method). 69 * (A null value is permitted, and indicates that the cause is nonexistent or unknown.) If the 70 * cause has retry information, it is going to be skipped in favor of the {@code retryCount} 71 * parameter 72 */ GoogleAuthException(boolean isRetryable, int retryCount, Throwable cause)73 GoogleAuthException(boolean isRetryable, int retryCount, Throwable cause) { 74 super(cause); 75 this.isRetryable = isRetryable; 76 this.retryCount = retryCount; 77 } 78 79 /** 80 * Constructor without explicit retry count. 81 * 82 * @param isRetryable A retry status for the related HTTP request 83 * @param cause The cause (which is saved for later retrieval by the {@link #getCause()} method). 84 * (A null value is permitted, and indicates that the cause is nonexistent or unknown.) 85 */ GoogleAuthException(boolean isRetryable, Throwable cause)86 GoogleAuthException(boolean isRetryable, Throwable cause) { 87 super(cause); 88 this.isRetryable = isRetryable; 89 this.retryCount = 0; 90 } 91 92 /** 93 * Constructor without retry info 94 * 95 * @param cause The cause (which is saved for later retrieval by the {@link #getCause()} method). 96 * (A null value is permitted, and indicates that the cause is nonexistent or unknown.) 97 */ GoogleAuthException(Throwable cause)98 GoogleAuthException(Throwable cause) { 99 this(false, cause); 100 } 101 102 /** A default Constructor */ GoogleAuthException()103 GoogleAuthException() { 104 super(); 105 this.isRetryable = false; 106 this.retryCount = 0; 107 } 108 109 /** 110 * Creates an instance of the exception based on {@link HttpResponseException} and a custom error 111 * message. 112 * 113 * @see #createWithTokenEndpointResponseException(HttpResponseException, String) 114 * @param responseException an instance of {@link HttpResponseException} 115 * @param message The detail message (which is saved for later retrieval by the {@link 116 * #getMessage()} method) 117 * @return new instance of {@link GoogleAuthException} 118 */ createWithTokenEndpointResponseException( HttpResponseException responseException, String message)119 static GoogleAuthException createWithTokenEndpointResponseException( 120 HttpResponseException responseException, String message) { 121 int responseStatus = responseException.getStatusCode(); 122 boolean isRetryable = 123 OAuth2Utils.TOKEN_ENDPOINT_RETRYABLE_STATUS_CODES.contains(responseStatus); 124 int retryCount = responseException.getAttemptCount() - 1; 125 126 if (message == null) { 127 return new GoogleAuthException(isRetryable, retryCount, responseException); 128 } else { 129 return new GoogleAuthException(isRetryable, retryCount, message, responseException); 130 } 131 } 132 133 /** 134 * Creates an instance of the exception based on {@link HttpResponseException} returned by Google 135 * token endpoint. It uses response status code information to populate the {@code #isRetryable} 136 * property and a number of performed attempts to populate the {@code #retryCount} property 137 * 138 * @param responseException an instance of {@link HttpResponseException} 139 * @return new instance of {@link GoogleAuthException} 140 */ createWithTokenEndpointResponseException( HttpResponseException responseException)141 static GoogleAuthException createWithTokenEndpointResponseException( 142 HttpResponseException responseException) { 143 return GoogleAuthException.createWithTokenEndpointResponseException(responseException, null); 144 } 145 146 /** 147 * Creates an instance of the exception based on {@link IOException} and a custom error message. 148 * 149 * @see #createWithTokenEndpointIOException(IOException) 150 * @param ioException an instance of {@link IOException} 151 * @param message The detail message (which is saved for later retrieval by the {@link 152 * #getMessage()} method) 153 * @return new instance of {@link GoogleAuthException} 154 */ createWithTokenEndpointIOException( IOException ioException, String message)155 static GoogleAuthException createWithTokenEndpointIOException( 156 IOException ioException, String message) { 157 158 if (message == null) { 159 // TODO: temporarily setting retry Count to service account default to remove a direct 160 // dependency, to be reverted after release 161 return new GoogleAuthException( 162 true, ServiceAccountCredentials.DEFAULT_NUMBER_OF_RETRIES, ioException); 163 } else { 164 return new GoogleAuthException( 165 true, ServiceAccountCredentials.DEFAULT_NUMBER_OF_RETRIES, message, ioException); 166 } 167 } 168 169 /** 170 * Creates an instance of the exception based on {@link IOException} returned by Google token 171 * endpoint. It uses response status code information to populate the {@code #isRetryable} 172 * property and a number of performed attempts to populate the {@code #retryCount} property 173 * 174 * @see #createWithTokenEndpointIOException(IOException) 175 * @param ioException an instance of {@link IOException} 176 * @return new instance of {@link GoogleAuthException} 177 */ createWithTokenEndpointIOException(IOException ioException)178 static GoogleAuthException createWithTokenEndpointIOException(IOException ioException) { 179 return GoogleAuthException.createWithTokenEndpointIOException(ioException, null); 180 } 181 182 /** Returns true if the error is retryable, false otherwise */ 183 @Override isRetryable()184 public boolean isRetryable() { 185 return isRetryable; 186 } 187 188 /** Returns number of reties performed for the related HTTP request */ 189 @Override getRetryCount()190 public int getRetryCount() { 191 return retryCount; 192 } 193 } 194