001/*
002 * (C) Copyright 2013 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl-2.1.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     dmetzler
016 */
017package org.nuxeo.ecm.restapi.jaxrs.io.usermanager;
018
019import java.io.IOException;
020import java.io.InputStream;
021import java.io.Serializable;
022import java.lang.annotation.Annotation;
023import java.lang.reflect.Type;
024import java.util.ArrayList;
025import java.util.List;
026
027import javax.ws.rs.Consumes;
028import javax.ws.rs.WebApplicationException;
029import javax.ws.rs.core.Context;
030import javax.ws.rs.core.MediaType;
031import javax.ws.rs.core.MultivaluedMap;
032import javax.ws.rs.core.Response;
033import javax.ws.rs.ext.MessageBodyReader;
034import javax.ws.rs.ext.Provider;
035
036import org.apache.commons.io.IOUtils;
037import org.apache.commons.logging.Log;
038import org.apache.commons.logging.LogFactory;
039import org.codehaus.jackson.JsonFactory;
040import org.codehaus.jackson.JsonParseException;
041import org.codehaus.jackson.JsonParser;
042import org.codehaus.jackson.JsonToken;
043import org.nuxeo.ecm.core.api.DocumentModel;
044import org.nuxeo.ecm.core.api.NuxeoException;
045import org.nuxeo.ecm.core.api.NuxeoPrincipal;
046import org.nuxeo.ecm.core.api.PropertyException;
047import org.nuxeo.ecm.platform.usermanager.NuxeoPrincipalImpl;
048import org.nuxeo.ecm.platform.usermanager.UserManager;
049import org.nuxeo.ecm.platform.usermanager.io.NuxeoPrincipalJsonReader;
050import org.nuxeo.ecm.webengine.WebException;
051import org.nuxeo.ecm.webengine.jaxrs.coreiodelegate.JsonCoreIODelegate;
052import org.nuxeo.runtime.api.Framework;
053
054/**
055 * Class that knows how to read NuxeoPrincipal from JSON
056 *
057 * @since 5.7.3 @deprecated since 7.10 The Nuxeo JSON marshalling was migrated to nuxeo-core-io. This class is replaced
058 *        by {@link NuxeoPrincipalJsonReader} which is registered by default and available to marshal
059 *        {@link NuxeoPrincipal} from the Nuxeo Rest API thanks to the JAX-RS marshaller {@link JsonCoreIODelegate}
060 */
061@Deprecated
062@Provider
063@Consumes({ "application/json+nxentity", "application/json" })
064public class NuxeoPrincipalReader implements MessageBodyReader<NuxeoPrincipal> {
065
066    protected static final Log log = LogFactory.getLog(NuxeoPrincipalReader.class);
067
068    @Context
069    JsonFactory factory;
070
071    @Override
072    public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
073        return NuxeoPrincipal.class.isAssignableFrom(type);
074    }
075
076    @Override
077    public NuxeoPrincipal readFrom(Class<NuxeoPrincipal> type, Type genericType, Annotation[] annotations,
078            MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
079            throws IOException, WebApplicationException {
080
081        String content = IOUtils.toString(entityStream);
082        if (content.isEmpty()) {
083            throw new WebException("No content in request body", Response.Status.BAD_REQUEST.getStatusCode());
084        }
085
086        return readRequest(content, httpHeaders);
087
088    }
089
090    /**
091     * @param content
092     * @param httpHeaders
093     * @return
094     * @since 5.7.3
095     */
096    private NuxeoPrincipal readRequest(String content, MultivaluedMap<String, String> httpHeaders) {
097        try {
098            JsonParser jp = factory.createJsonParser(content);
099            return readJson(jp, httpHeaders);
100        } catch (NuxeoException | IOException e) {
101            throw new WebApplicationException(e, Response.Status.INTERNAL_SERVER_ERROR);
102        }
103    }
104
105    /**
106     * @param jp
107     * @param httpHeaders
108     * @param request2
109     * @return
110     * @throws IOException
111     * @throws JsonParseException
112     * @since 5.7.3
113     */
114    static NuxeoPrincipal readJson(JsonParser jp, MultivaluedMap<String, String> httpHeaders)
115            throws JsonParseException, IOException {
116        JsonToken tok = jp.nextToken();
117
118        // skip {
119        if (jp.getCurrentToken() == JsonToken.START_OBJECT) {
120            tok = jp.nextToken();
121        }
122        String id = null;
123
124        UserManager um = Framework.getLocalService(UserManager.class);
125        DocumentModel userDoc = null;
126        String schema = um.getUserSchemaName();
127
128        while (tok != JsonToken.END_OBJECT) {
129            String key = jp.getCurrentName();
130            jp.nextToken();
131            if ("id".equals(key)) {
132                id = jp.readValueAs(String.class);
133
134                NuxeoPrincipal principal = um.getPrincipal(id);
135                if (principal == null) {
136                    userDoc = um.getBareUserModel();
137                } else {
138                    userDoc = principal.getModel();
139                }
140            } else if ("extendedGroups".equals(key)) {
141                jp.readValueAsTree();
142            } else if ("properties".equals(key)) {
143                readProperties(jp, userDoc, schema);
144            } else if ("entity-type".equals(key)) {
145                String entityType = jp.readValueAs(String.class);
146                if (!"user".equals(entityType)) {
147                    throw new WebApplicationException(Response.Status.BAD_REQUEST);
148                }
149            }
150            tok = jp.nextToken();
151        }
152
153        NuxeoPrincipal principal = new NuxeoPrincipalImpl(id);
154        principal.setModel(userDoc);
155
156        return principal;
157
158    }
159
160    protected static void readProperties(JsonParser jp, DocumentModel doc, String schemaName) throws PropertyException,
161            JsonParseException, IOException {
162        JsonToken tok = jp.nextToken();
163        while (tok != JsonToken.END_OBJECT) {
164
165            String key = schemaName + ":" + jp.getCurrentName();
166            tok = jp.nextToken();
167            if (tok == JsonToken.START_ARRAY) {
168                doc.setPropertyValue(key, (Serializable) readArrayProperty(jp));
169            } else if (tok == JsonToken.VALUE_NULL) {
170                doc.setPropertyValue(key, (String) null);
171            } else {
172                doc.setPropertyValue(key, jp.getText());
173            }
174            tok = jp.nextToken();
175        }
176    }
177
178    protected static List<Serializable> readArrayProperty(JsonParser jp) throws JsonParseException, IOException {
179        List<Serializable> list = new ArrayList<>();
180        JsonToken tok = jp.nextToken();
181        while (tok != JsonToken.END_ARRAY) {
182            if (tok == JsonToken.START_ARRAY) {
183                list.add((Serializable) readArrayProperty(jp));
184            } else {
185                list.add(jp.getText());
186            }
187            tok = jp.nextToken();
188        }
189        return list;
190    }
191
192}