/* * (C) Copyright 2016 Nuxeo SA (http://nuxeo.com/) and others. * * 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. * * Contributors: * Gabriel Barata <gbarata@nuxeo.com> */ package org.nuxeo.ecm.restapi.server.jaxrs; import com.google.api.client.auth.oauth2.Credential; import org.codehaus.jackson.map.ObjectMapper; import javax.servlet.http.HttpServletResponse; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.Response.StatusType; import org.nuxeo.ecm.automation.server.jaxrs.RestOperationException; import org.nuxeo.ecm.core.api.DocumentModel; import org.nuxeo.ecm.core.api.NuxeoException; import org.nuxeo.ecm.platform.oauth2.providers.AbstractOAuth2UserEmailProvider; import org.nuxeo.ecm.platform.oauth2.providers.NuxeoOAuth2ServiceProvider; import org.nuxeo.ecm.platform.oauth2.providers.OAuth2ServiceProvider; import org.nuxeo.ecm.platform.oauth2.providers.OAuth2ServiceProviderRegistry; import org.nuxeo.ecm.platform.oauth2.tokens.NuxeoOAuth2Token; import org.nuxeo.ecm.webengine.model.WebObject; import org.nuxeo.ecm.webengine.model.impl.AbstractResource; import org.nuxeo.ecm.webengine.model.impl.ResourceTypeImpl; import org.nuxeo.runtime.api.Framework; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.io.IOException; import java.io.Serializable; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Endpoint to retrieve OAuth2 authentication data * @since 8.4 */ @WebObject(type = "oauth2") public class OAuth2Object extends AbstractResource<ResourceTypeImpl> { /** * Retrieves oauth2 data for a given provider. */ @GET @Path("provider/{providerId}") public Response getProvider(@PathParam("providerId") String providerId, @Context HttpServletRequest request) throws IOException, RestOperationException { NuxeoOAuth2ServiceProvider provider = getProvider(providerId); Map<String,Object> result = new HashMap<>(); result.put("serviceName", provider.getServiceName()); result.put("isAvailable", provider.isProviderAvailable()); result.put("clientId", provider.getClientId()); result.put("authorizationURL", provider.getClientId() == null ? null : provider.getAuthorizationUrl(request)); String username = request.getUserPrincipal().getName(); NuxeoOAuth2Token token = getToken(provider, username); boolean isAuthorized = (token != null); String login = isAuthorized ? token.getServiceLogin() : null; result.put("isAuthorized", isAuthorized); result.put("userId", login); return buildResponse(Status.OK, result); } /** * Retrieves a valid access token for a given provider and the current user. * If expired, the token will be refreshed. */ @GET @Path("provider/{providerId}/token") public Response getToken(@PathParam("providerId") String providerId, @Context HttpServletRequest request) throws IOException, RestOperationException { NuxeoOAuth2ServiceProvider provider = getProvider(providerId); String username = request.getUserPrincipal().getName(); NuxeoOAuth2Token token = getToken(provider, username); if (token == null) { return Response.status(Status.NOT_FOUND).build(); } Credential credential = getCredential(provider, token); if (credential == null) { return Response.status(Status.NOT_FOUND).build(); } Long expiresInSeconds = credential.getExpiresInSeconds(); if (expiresInSeconds != null && expiresInSeconds <= 0) { credential.refreshToken(); } Map<String,Object> result = new HashMap<>(); result.put("token", credential.getAccessToken()); return buildResponse(Status.OK, result); } private NuxeoOAuth2Token getToken(NuxeoOAuth2ServiceProvider provider, String nxuser) { Map<String, Serializable> filter = new HashMap<>(); filter.put("serviceName", provider.getId()); filter.put(NuxeoOAuth2Token.KEY_NUXEO_LOGIN, nxuser); return Framework.doPrivileged(() -> { List<DocumentModel> entries = provider.getCredentialDataStore().query(filter); if (entries != null) { if (entries.size() > 1) { throw new NuxeoException("Found multiple " + provider.getId() + " accounts for " + nxuser); } else if (entries.size() == 1) { return new NuxeoOAuth2Token(entries.get(0)); } } return null; }); } private Credential getCredential(NuxeoOAuth2ServiceProvider provider, NuxeoOAuth2Token token) { return provider.loadCredential( (provider instanceof AbstractOAuth2UserEmailProvider) ? token.getServiceLogin() : token.getNuxeoLogin()); } private NuxeoOAuth2ServiceProvider getProvider(String providerId) throws RestOperationException { OAuth2ServiceProvider provider = Framework.getService(OAuth2ServiceProviderRegistry.class) .getProvider(providerId); if (provider == null || !(provider instanceof NuxeoOAuth2ServiceProvider)) { RestOperationException err = new RestOperationException("Invalid provider: " + providerId); err.setStatus(HttpServletResponse.SC_BAD_REQUEST); throw err; } return (NuxeoOAuth2ServiceProvider) provider; } private Response buildResponse(StatusType status, Object obj) throws IOException { ObjectMapper mapper = new ObjectMapper(); String message = mapper.writeValueAsString(obj); return Response.status(status) .header("Content-Length", message.getBytes("UTF-8").length) .type(MediaType.APPLICATION_JSON + "; charset=UTF-8") .entity(message) .build(); } }