001package com.nimbusds.common.oauth2;
002
003
004import java.io.IOException;
005import java.nio.charset.StandardCharsets;
006import java.security.MessageDigest;
007import java.security.NoSuchAlgorithmException;
008import javax.servlet.http.HttpServletRequest;
009import javax.servlet.http.HttpServletResponse;
010import javax.ws.rs.WebApplicationException;
011import javax.ws.rs.core.MediaType;
012import javax.ws.rs.core.Response;
013
014import com.nimbusds.oauth2.sdk.token.BearerAccessToken;
015import com.nimbusds.oauth2.sdk.token.BearerTokenError;
016import net.minidev.json.JSONObject;
017import org.apache.logging.log4j.Logger;
018
019
020/**
021 * Master access token validator. Intended for validation of master API access
022 * tokens for the Connect2id server and elsewhere.
023 */
024public interface MasterAccessTokenValidator {
025        
026        
027        /**
028         * Error response: Missing OAuth 2.0 Bearer access token.
029         */
030        ErrorResponse MISSING_BEARER_TOKEN = new ErrorResponse(
031                BearerTokenError.MISSING_TOKEN.getHTTPStatusCode(),
032                BearerTokenError.MISSING_TOKEN.toWWWAuthenticateHeader(),
033                "missing_token",
034                "Unauthorized: Missing Bearer access token");
035        
036        
037        /**
038         * Error response: Invalid OAuth 2.0 Bearer access token.
039         */
040        ErrorResponse INVALID_BEARER_TOKEN = new ErrorResponse(
041                BearerTokenError.INVALID_TOKEN.getHTTPStatusCode(),
042                BearerTokenError.INVALID_TOKEN.toWWWAuthenticateHeader(),
043                BearerTokenError.INVALID_TOKEN.getCode(),
044                "Unauthorized: Invalid Bearer access token");
045        
046        
047        /**
048         * Error response: Web API disabled.
049         */
050        ErrorResponse WEB_API_DISABLED = new ErrorResponse(
051                403,
052                null,
053                "web_api_disabled",
054                "Forbidden: Web API disabled");
055        
056        
057        /**
058         * Bearer token error response.
059         */
060        class ErrorResponse {
061                
062                
063                /**
064                 * The HTTP status code.
065                 */
066                private final int statusCode;
067                
068                
069                /**
070                 * Optional HTTP response {@code WWW-Authenticate} header.
071                 */
072                private final String wwwAuthHeader;
073                
074                
075                /**
076                 * The HTTP body.
077                 */
078                private final String body;
079                
080                
081                /**
082                 * Creates a new bearer token error response.
083                 *
084                 * @param statusCode    The HTTP status code.
085                 * @param wwwAuthHeader The HTTP response
086                 *                      {@code WWW-Authenticate} header,
087                 *                      {@code null} if none.
088                 * @param bodyErrorCode The error code for the body JSON
089                 *                      object.
090                 * @param bodyErrorDesc The error description for the body JSON
091                 *                      object.
092                 */
093                public ErrorResponse(final int statusCode,
094                                     final String wwwAuthHeader,
095                                     final String bodyErrorCode,
096                                     final String bodyErrorDesc) {
097                        
098                        this.statusCode = statusCode;
099                        this.wwwAuthHeader = wwwAuthHeader;
100                        JSONObject bodyErrorObj = new JSONObject();
101                        bodyErrorObj.put("error", bodyErrorCode);
102                        bodyErrorObj.put("error_description", bodyErrorDesc);
103                        this.body = bodyErrorObj.toJSONString();
104                }
105                
106                
107                /**
108                 * Returns a web application exception for this error response.
109                 *
110                 * @return The web application exception.
111                 */
112                public WebApplicationException toWebAppException() {
113                        
114                        Response.ResponseBuilder builder = Response.status(statusCode);
115                        
116                        if (wwwAuthHeader != null) {
117                                builder.header("WWW-Authenticate", wwwAuthHeader);
118                        }
119                        
120                        return new WebApplicationException(
121                                builder.entity(body).type(MediaType.APPLICATION_JSON).build());
122                }
123                
124                
125                /**
126                 * Applies this error response to the specified HTTP servlet
127                 * response.
128                 *
129                 * @param servletResponse The HTTP servlet response. Must not
130                 *                        be {@code null}.
131                 *
132                 * @throws IOException If the error response couldn't be
133                 *                     written.
134                 */
135                public void apply(final HttpServletResponse servletResponse)
136                        throws IOException {
137                        
138                        servletResponse.setStatus(statusCode);
139                        
140                        if (wwwAuthHeader != null) {
141                                servletResponse.setHeader("WWW-Authenticate", wwwAuthHeader);
142                        }
143                        
144                        if (body != null) {
145                                servletResponse.setContentType("application/json");
146                                servletResponse.getWriter().print(body);
147                        }
148                }
149        }
150        
151        
152        /**
153         * Computes the SHA-256 hash of the specified Bearer access token.
154         *
155         * @param token The Bearer access token. Must not be {@code null}.
156         * @param salt  Optional salt to use, {@code null} if none.
157         *
158         * @return The computed SHA-256 hash.
159         */
160        static byte[] computeSHA256(final BearerAccessToken token, final byte[] salt) {
161                
162                try {
163                        MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
164                        if (salt != null) {
165                                sha256.update(salt);
166                        }
167                        return sha256.digest(token.getValue().getBytes(StandardCharsets.UTF_8));
168                } catch (NoSuchAlgorithmException e) {
169                        throw new RuntimeException(e);
170                }
171        }
172        
173        
174        /**
175         * Returns {@code true} if access is disabled (no access token
176         * configured).
177         *
178         * @return {@code true} if access is disabled, else {@code false}.
179         */
180        boolean accessIsDisabled();
181        
182        
183        /**
184         * Gets the optional logger.
185         *
186         * @return The logger, {@code null} if not specified.
187         */
188        Logger getLogger();
189        
190        
191        /**
192         * Sets the optional logger.
193         *
194         * @param log The logger, {@code null} if not specified.
195         */
196        void setLogger(final Logger log);
197        
198        
199        /**
200         * Returns {@code true} if the specified bearer access token is valid.
201         *
202         * @param accessToken The bearer access token to check, {@code null} if
203         *                    not specified.
204         *
205         * @return {@code true} if the specified bearer access token is valid,
206         *         else {@code false}.
207         */
208        boolean isValid(final BearerAccessToken accessToken);
209        
210        
211        /**
212         * Validates a bearer access token passed in the specified HTTP
213         * Authorization header value.
214         *
215         * @param authzHeader The HTTP Authorization header value, {@code null}
216         *                    if not specified.
217         *
218         * @throws WebApplicationException If the header value is {@code null},
219         *                                 the web API is disabled, or the
220         *                                 Bearer access token is missing or
221         *                                 invalid.
222         */
223        void validateBearerAccessToken(final String authzHeader)
224                throws WebApplicationException;
225        
226        
227        /**
228         * Validates a bearer access token passed in the specified HTTP servlet
229         * request.
230         *
231         * @param servletRequest  The HTTP servlet request. Must not be
232         *                        {@code null}.
233         * @param servletResponse The HTTP servlet response. Must not be
234         *                        {@code null}.
235         *
236         * @return {@code true} if the bearer access token was successfully
237         *         validated, {@code false}.
238         *
239         * @throws IOException If the response couldn't be written.
240         */
241        boolean validateBearerAccessToken(final HttpServletRequest servletRequest,
242                                          final HttpServletResponse servletResponse)
243                throws IOException;
244}