001/*
002 * (C) Copyright 2015 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 *     Nicolas Chapurlat <nchapurlat@nuxeo.com>
018 */
019
020package org.nuxeo.ecm.platform.usermanager.io;
021
022import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE;
023import static org.nuxeo.ecm.core.io.registry.reflect.Instantiations.SINGLETON;
024import static org.nuxeo.ecm.core.io.registry.reflect.Priorities.REFERENCE;
025
026import java.io.IOException;
027import java.io.OutputStream;
028import java.util.Collection;
029
030import javax.inject.Inject;
031
032import org.codehaus.jackson.JsonGenerator;
033import org.nuxeo.ecm.core.api.DocumentModel;
034import org.nuxeo.ecm.core.api.NuxeoGroup;
035import org.nuxeo.ecm.core.api.NuxeoPrincipal;
036import org.nuxeo.ecm.core.api.model.Property;
037import org.nuxeo.ecm.core.io.marshallers.json.ExtensibleEntityJsonWriter;
038import org.nuxeo.ecm.core.io.marshallers.json.OutputStreamWithJsonWriter;
039import org.nuxeo.ecm.core.io.marshallers.json.document.DocumentPropertyJsonWriter;
040import org.nuxeo.ecm.core.io.marshallers.json.enrichers.AbstractJsonEnricher;
041import org.nuxeo.ecm.core.io.registry.Writer;
042import org.nuxeo.ecm.core.io.registry.reflect.Setup;
043import org.nuxeo.ecm.directory.api.DirectoryService;
044import org.nuxeo.ecm.platform.usermanager.UserManager;
045
046import com.thoughtworks.xstream.io.json.JsonWriter;
047
048/**
049 * Convert {@link NuxeoPrincipal} to Json.
050 * <p>
051 * This marshaller is enrichable: register class implementing {@link AbstractJsonEnricher} and managing
052 * {@link NuxeoPrincipal}.
053 * </p>
054 * <p>
055 * This marshaller is also extensible: extend it and simply override
056 * {@link ExtensibleEntityJsonWriter#extend(NuxeoPrincipal, JsonWriter)}.
057 * </p>
058 * <p>
059 * Format is:
060 *
061 * <pre>
062 * {@code
063 * {
064 *   "entity-type":"user",
065 *   "id":"USERNAME",
066 *   "properties":{   <- depending on the user schema / format is managed by {@link DocumentPropertyJsonWriter}
067 *     "firstName":"FIRSTNAME",
068 *     "lastName":"LASTNAME",
069 *     "username":"USERNAME",
070 *     "email":"user@mail.com",
071 *     "company":"COMPANY",
072 *     "password":"", <- ALWAYS EMPTY
073 *     "groups":[
074 *       "GROUP1 NAME OF THE USER",
075 *       "GROUP2 NAME OF THE USER",
076 *       ...
077 *     ]
078 *   },
079 *   "extendedGroups":[
080 *     {
081 *       "name":"GROUP1NAME",
082 *       "label":"GROUP1 DISPLAY NAME",
083 *       "url":"GROUP1 URL"
084 *     },
085 *     ...
086 *   ],
087 *   "isAdministrator":true|false,
088 *   "isAnonymous":false|false
089 *             <-- contextParameters if there are enrichers activated
090 *             <-- additional property provided by extend() method
091 * }
092 * </pre>
093 *
094 * </p>
095 *
096 * @since 7.2
097 */
098@Setup(mode = SINGLETON, priority = REFERENCE)
099public class NuxeoPrincipalJsonWriter extends ExtensibleEntityJsonWriter<NuxeoPrincipal> {
100
101    public static final String ENTITY_TYPE = "user";
102
103    @Inject
104    private UserManager userManager;
105
106    @Inject
107    private DirectoryService directoryService;
108
109    public NuxeoPrincipalJsonWriter() {
110        super(ENTITY_TYPE, NuxeoPrincipal.class);
111    }
112
113    @Override
114    protected void writeEntityBody(NuxeoPrincipal principal, JsonGenerator jg) throws IOException {
115        jg.writeStringField("id", principal.getName());
116        writeProperties(jg, principal);
117        writeExtendedGroups(jg, principal);
118        jg.writeBooleanField("isAdministrator", principal.isAdministrator());
119        jg.writeBooleanField("isAnonymous", principal.isAnonymous());
120    }
121
122    private void writeProperties(JsonGenerator jg, NuxeoPrincipal principal) throws IOException {
123        DocumentModel doc = principal.getModel();
124        if (doc == null) {
125            return;
126        }
127        String userSchema = userManager.getUserSchemaName();
128        Collection<Property> properties = doc.getPropertyObjects(userSchema);
129        if (properties.isEmpty()) {
130            return;
131        }
132        Writer<Property> propertyWriter = registry.getWriter(ctx, Property.class, APPLICATION_JSON_TYPE);
133        jg.writeObjectFieldStart("properties");
134        for (Property property : properties) {
135            String localName = property.getField().getName().getLocalName();
136            if (!localName.equals(getPasswordField())) {
137                jg.writeFieldName(localName);
138                OutputStream out = new OutputStreamWithJsonWriter(jg);
139                propertyWriter.write(property, Property.class, Property.class, APPLICATION_JSON_TYPE, out);
140            }
141        }
142        jg.writeEndObject();
143    }
144
145    private void writeExtendedGroups(JsonGenerator jg, NuxeoPrincipal principal) throws IOException {
146        jg.writeArrayFieldStart("extendedGroups");
147        for (String strGroup : principal.getAllGroups()) {
148            NuxeoGroup group = userManager.getGroup(strGroup);
149            String label = group == null ? strGroup : group.getLabel();
150            jg.writeStartObject();
151            jg.writeStringField("name", strGroup);
152            jg.writeStringField("label", label);
153            jg.writeStringField("url", "group/" + strGroup);
154            jg.writeEndObject();
155        }
156        jg.writeEndArray();
157    }
158
159    private String getPasswordField() {
160        String userDirectoryName = userManager.getUserDirectoryName();
161        return directoryService.getDirectory(userDirectoryName).getPasswordField();
162    }
163
164}