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