• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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