/* * Copyright (C) 2016-2020 Authlete, Inc. * * 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 com.authlete.jaxrs.server.api; import java.util.LinkedHashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.Consumes; import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.HeaderParam; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import com.authlete.common.api.AuthleteApiFactory; import com.authlete.jaxrs.AccessTokenValidator.Params; import com.authlete.jaxrs.BaseResourceEndpoint; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.neovisionaries.i18n.CountryCode; /** * An endpoint that returns country code information. * * <p> * The API path is <code>/api/country/<i>{countryCode}</i></code> where * <code><i>{countryCode}</i></code> is an ISO 3166-1 alpha-2, alpha-3 * or numeric code (case-insensitive). For example, {@code JP}, * {@code JPN} and {@code 392}. * </p> * * <p> * The response is JSON that contains the following. * </p> * * <blockquote> * <ol> * <li>Country name * <li>ISO 3166-1 alpha-2 code * <li>ISO 3166-1 alpha-3 code * <li>ISO 3166-1 numeric code * <li>Currency * </ol> * </blockquote> * * <p> * Below is an example response from this API. * </p> * * <blockquote> * <pre> * { * "name": "Japan", * "alpha2": "JP", * "alpha3": "JPN", * "numeric": 392, * "currency": "JPY" * } * </pre> * </blockquote> * * @author Takahiko Kawasaki */ @Path("/api/country/{code}") public class CountryEndpoint extends BaseResourceEndpoint { /** * JSON generator. */ private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); @GET public Response get( @HeaderParam(HttpHeaders.AUTHORIZATION) String authorization, @HeaderParam("DPoP") String dpop, @QueryParam("access_token") String accessToken, @PathParam("code") String code, @Context HttpServletRequest request) { // Extract an access token from either the Authorization header or // the request parameters. The Authorization header takes precedence. // See RFC 6750 (Bearer Token Usage) about the standard ways to accept // an access token from a client application. String token = extractAccessToken(authorization, accessToken); return process(request, token, dpop, code); } @POST @Consumes(MediaType.APPLICATION_FORM_URLENCODED) public Response post( @HeaderParam(HttpHeaders.AUTHORIZATION) String authorization, @HeaderParam("DPoP") String dpop, @FormParam("access_token") String accessToken, @PathParam("code") String code, @Context HttpServletRequest request) { // Extract an access token from either the Authorization header or // the request parameters. The Authorization header takes precedence. // See RFC 6750 (Bearer Token Usage) about the standard ways to accept // an access token from a client application. String token = extractAccessToken(authorization, accessToken); return process(request, token, dpop, code); } private Response process( HttpServletRequest request, String accessToken, String dpop, String code) { // Validate the access token. // // validateAccessToken() throws a WebApplicationException when the given // access token is invalid. The response contained in the exception // complies with RFC 6750, so you don't have to build the content of // WWW-Authenticate header in the error response by yourself. // // If you want to get information about the access token (e.g. the subject // of the user and the scopes associated with the access token), use // the object returned from validateAccessToken() method. It is an // instance of AccessTokenInfo class. If you want to get information // even in the case where validateAccessToken() throws an exception, // call AuthleteApi.introspect(IntrospectionRequest) directly. Params params = buildParams(request, accessToken, dpop); validateAccessToken(AuthleteApiFactory.getDefaultApi(), params); // The access token presented by the client application is valid. // Return the requested resource. return getResource(code); } private Params buildParams( HttpServletRequest request, String accessToken, String dpop) { Params params = new Params(); // Access Token params.setAccessToken(accessToken); // Client Certificate params.setClientCertificate(extractClientCertificate(request)); // DPoP params.setDpop(dpop) .setHtm(request.getMethod()) .setHtu(request.getRequestURL().toString()) ; // NOTE: The URL built by request.getRequestURL().toString() may // be invalid behind proxies. return params; } private Response getResource(String code) { // Look up a CountryCode instance that has the ISO 3166-1 code. CountryCode cc = lookup(code); Map<String, Object> data = new LinkedHashMap<String, Object>(); if (cc != null) { // Pack the data into a Map. data.put("name", cc.getName()); data.put("alpha2", cc.getAlpha2()); data.put("alpha3", cc.getAlpha3()); data.put("numeric", cc.getNumeric()); data.put("currency", cc.getCurrency()); } // Convert the data to JSON. String json = GSON.toJson(data); // Create a response of "200 OK". return Response.ok(json, "application/json;charset=UTF-8").build(); } /** * Look up a {@link CountryCode} instance from an ISO 3166-1 code. * * @param code * ISO 3166-1 code (alpha-2, alpha-3, or numeric). * * @return * A {@link CountryCode} instance that corresponds to the * given code. If the given code is not valid, {@code null} * is returned. */ private CountryCode lookup(String code) { if (code == null) { // Not found. return null; } // Interpret the code as an ISO 3166-1 alpha-2 or alpha-3 code. CountryCode cc = CountryCode.getByCodeIgnoreCase(code); if (cc != null) { // Found. return cc; } try { // Interpret the code as an ISO 3166-1 numeric code. return CountryCode.getByCode(Integer.parseInt(code)); } catch (NumberFormatException e) { // Not found. return null; } } }