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