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 org.nuxeo.ecm.core.io.marshallers.json.document.DocumentPropertiesJsonReader.DEFAULT_SCHEMA_NAME; 023import static org.nuxeo.ecm.core.io.registry.reflect.Instantiations.SINGLETON; 024import static org.nuxeo.ecm.core.io.registry.reflect.Priorities.REFERENCE; 025import static org.nuxeo.ecm.platform.usermanager.io.NuxeoGroupJsonWriter.ENTITY_TYPE; 026import static org.nuxeo.ecm.platform.usermanager.io.NuxeoGroupJsonWriter.GROUP_LABEL_COMPATIBILITY_FIELD; 027import static org.nuxeo.ecm.platform.usermanager.io.NuxeoGroupJsonWriter.GROUP_NAME_COMPATIBILITY_FIELD; 028import static org.nuxeo.ecm.platform.usermanager.io.NuxeoGroupJsonWriter.MEMBER_GROUPS_FETCH_PROPERTY; 029import static org.nuxeo.ecm.platform.usermanager.io.NuxeoGroupJsonWriter.MEMBER_USERS_FETCH_PROPERTY; 030import static org.nuxeo.ecm.platform.usermanager.io.NuxeoGroupJsonWriter.PARENT_GROUPS_FETCH_PROPERTY; 031 032import java.io.Closeable; 033import java.io.IOException; 034import java.lang.reflect.ParameterizedType; 035import java.util.ArrayList; 036import java.util.Arrays; 037import java.util.List; 038import java.util.Objects; 039 040import javax.inject.Inject; 041 042import org.apache.commons.lang3.StringUtils; 043import org.apache.commons.lang3.reflect.TypeUtils; 044import org.codehaus.jackson.JsonNode; 045import org.nuxeo.ecm.core.api.DocumentModel; 046import org.nuxeo.ecm.core.api.NuxeoGroup; 047import org.nuxeo.ecm.core.api.model.Property; 048import org.nuxeo.ecm.core.io.marshallers.json.EntityJsonReader; 049import org.nuxeo.ecm.core.io.registry.reflect.Setup; 050import org.nuxeo.ecm.platform.usermanager.GroupConfig; 051import org.nuxeo.ecm.platform.usermanager.NuxeoGroupImpl; 052import org.nuxeo.ecm.platform.usermanager.UserManager; 053 054/** 055 * Convert Json as {@link NuxeoGroup}. 056 * <p> 057 * Format is (any additional json property is ignored): 058 * 059 * <pre> 060 * { 061 * "entity-type":"group", 062 * "groupname": "GROUP_NAME", <- deprecated, for backward compatibility 063 * "grouplabel": "GROUP_DISPLAY_NAME", <- deprecated, for backward compatibility 064 * "id": "GROUP_NAME", 065 * "memberUsers": [ 066 * "USERNAME1", 067 * "USERNAME2", 068 * ... 069 * ], 070 * "memberGroups": [ 071 * "GROUPNAME1", 072 * "GROUPNAME2", 073 * ... 074 * ] 075 * } 076 * </pre> 077 * </p> 078 * 079 * @since 7.2 080 */ 081@Setup(mode = SINGLETON, priority = REFERENCE) 082public class NuxeoGroupJsonReader extends EntityJsonReader<NuxeoGroup> { 083 084 @Inject 085 private UserManager userManager; 086 087 public NuxeoGroupJsonReader() { 088 super(ENTITY_TYPE); 089 } 090 091 @Override 092 protected NuxeoGroup readEntity(JsonNode jn) throws IOException { 093 GroupConfig groupConfig = userManager.getGroupConfig(); 094 String id = getStringField(jn, "id"); 095 String groupName = getStringField(jn, GROUP_NAME_COMPATIBILITY_FIELD); 096 if (StringUtils.isBlank(id) || (StringUtils.isNotBlank(groupName) && !id.equals(groupName))) { 097 // backward compatibility if `id` not found or if `groupname` is different 098 id = groupName; 099 } 100 101 DocumentModel groupModel = null; 102 if (StringUtils.isNotBlank(id)) { 103 groupModel = userManager.getGroupModel(id); 104 } 105 if (groupModel == null) { 106 groupModel = userManager.getBareGroupModel(); 107 groupModel.setProperty(groupConfig.schemaName, groupConfig.idField, id); 108 } 109 110 String beforeReadLabel = (String) groupModel.getProperty(groupConfig.schemaName, groupConfig.labelField); 111 112 readProperties(groupModel, groupConfig, jn); 113 readMemberUsers(groupModel, groupConfig, jn); 114 readMemberGroups(groupModel, groupConfig, jn); 115 readParentGroups(groupModel, groupConfig, jn); 116 117 // override the `groupname` that may have been in the `properties` object 118 if (StringUtils.isNotBlank(id)) { 119 groupModel.setProperty(groupConfig.schemaName, groupConfig.idField, id); 120 } 121 122 // override with the compatibility `grouplabel` if needed 123 if (jn.has(GROUP_LABEL_COMPATIBILITY_FIELD)) { 124 String compatLabel = getStringField(jn, GROUP_LABEL_COMPATIBILITY_FIELD); 125 String label = (String) groupModel.getProperty(groupConfig.schemaName, groupConfig.labelField); 126 if (!Objects.equals(label, compatLabel) 127 && (beforeReadLabel == null || Objects.equals(beforeReadLabel, label))) { 128 groupModel.setProperty(groupConfig.schemaName, groupConfig.labelField, compatLabel); 129 } 130 } 131 132 return new NuxeoGroupImpl(groupModel, groupConfig); 133 } 134 135 protected void readProperties(DocumentModel groupModel, GroupConfig groupConfig, JsonNode jn) throws IOException { 136 List<String> excludedProperties = Arrays.asList(groupConfig.membersField, groupConfig.subGroupsField, 137 groupConfig.parentGroupsField); 138 JsonNode propsNode = jn.get("properties"); 139 if (propsNode != null && !propsNode.isNull() && propsNode.isObject()) { 140 ParameterizedType genericType = TypeUtils.parameterize(List.class, Property.class); 141 try (Closeable resource = ctx.wrap().with(DEFAULT_SCHEMA_NAME, groupConfig.schemaName).open()) { 142 List<Property> properties = readEntity(List.class, genericType, propsNode); 143 properties.stream().filter(p -> !excludedProperties.contains(p)).forEach( 144 p -> groupModel.setPropertyValue(p.getName(), p.getValue())); 145 } 146 } 147 } 148 149 protected void readMemberUsers(DocumentModel groupModel, GroupConfig groupConfig, JsonNode jn) { 150 if (jn.has(MEMBER_USERS_FETCH_PROPERTY)) { 151 List<String> users = getArrayStringValues(jn.get(MEMBER_USERS_FETCH_PROPERTY)); 152 groupModel.setProperty(groupConfig.schemaName, groupConfig.membersField, users); 153 } 154 } 155 156 protected void readMemberGroups(DocumentModel groupModel, GroupConfig groupConfig, JsonNode jn) { 157 if (jn.has(MEMBER_GROUPS_FETCH_PROPERTY)) { 158 List<String> groups = getArrayStringValues(jn.get(MEMBER_GROUPS_FETCH_PROPERTY)); 159 groupModel.setProperty(groupConfig.schemaName, groupConfig.subGroupsField, groups); 160 } 161 } 162 163 protected void readParentGroups(DocumentModel groupModel, GroupConfig groupConfig, JsonNode jn) { 164 if (jn.has(PARENT_GROUPS_FETCH_PROPERTY)) { 165 List<String> parents = getArrayStringValues(jn.get(PARENT_GROUPS_FETCH_PROPERTY)); 166 groupModel.setProperty(groupConfig.schemaName, groupConfig.parentGroupsField, parents); 167 } 168 } 169 170 private List<String> getArrayStringValues(JsonNode node) { 171 List<String> values = new ArrayList<>(); 172 if (node != null && !node.isNull() && node.isArray()) { 173 node.getElements().forEachRemaining(n -> { 174 if (n != null && !n.isNull() && n.isTextual()) { 175 values.add(n.getTextValue()); 176 } 177 }); 178 } 179 return values; 180 } 181 182}