001/* 002 * (C) Copyright 2014-2018 Nuxeo (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; 021 022import java.io.Serializable; 023import java.util.ArrayList; 024import java.util.List; 025import java.util.Locale; 026import java.util.Map; 027 028import org.nuxeo.ecm.core.api.NuxeoGroup; 029import org.nuxeo.ecm.core.api.NuxeoPrincipal; 030import org.nuxeo.ecm.core.api.SystemPrincipal; 031import org.nuxeo.ecm.core.api.security.SecurityConstants; 032import org.nuxeo.ecm.core.schema.types.resolver.AbstractObjectResolver; 033import org.nuxeo.ecm.core.schema.types.resolver.ObjectResolver; 034import org.nuxeo.runtime.api.Framework; 035 036/** 037 * This {@link ObjectResolver} allows to manage integrity for fields containing group or user references. 038 * <p> 039 * References should have a prefix. NuxeoPrincipal.PREFIX for users, NuxeoGroup.PREFIX for groups. 040 * </p> 041 * <p> 042 * If only user or group are configured, the prefix is not needed but still supported. If noth user and group are 043 * configured, reference without prefix are resolved as user first. 044 * </p> 045 * <p> 046 * To use it, put the following code in your schema XSD : 047 * </p> 048 * 049 * <pre> 050 * {@code 051 * <!-- user or group resolver --> 052 * <xs:simpleType name="userOrGroupReference"> 053 * <xs:restriction base="xs:string" ref:resolver="userManagerResolver" /> 054 * </xs:simpleType> 055 * 056 * <!-- user resolver --> 057 * <xs:simpleType name="userReference"> 058 * <xs:restriction base="xs:string" ref:resolver="userManagerResolver" ref:type="user" /> 059 * </xs:simpleType> 060 * 061 * <!-- group resolver --> 062 * <xs:simpleType name="groupReference"> 063 * <xs:restriction base="xs:string" ref:resolver="userManagerResolver" ref:type="group" /> 064 * </xs:simpleType> 065 * } 066 * </pre> 067 * 068 * @since 7.1 069 */ 070public class UserManagerResolver extends AbstractObjectResolver implements ObjectResolver { 071 072 private static final long serialVersionUID = 1L; 073 074 public static final String INPUT_PARAM_FILTER = "type"; 075 076 public static final String FILTER_GROUP = "group"; 077 078 public static final String FILTER_USER = "user"; 079 080 public static final String NAME = "userManagerResolver"; 081 082 public static final String PARAM_INCLUDE_USERS = "includeUsers"; 083 084 public static final String PARAM_INCLUDE_GROUPS = "includeGroups"; 085 086 private boolean includingUsers = true; 087 088 private boolean includingGroups = true; 089 090 private transient UserManager userManager; 091 092 public UserManager getUserManager() { 093 if (userManager == null) { 094 userManager = Framework.getService(UserManager.class); 095 } 096 return userManager; 097 } 098 099 private List<Class<?>> managedClasses = null; 100 101 @Override 102 public List<Class<?>> getManagedClasses() { 103 if (managedClasses == null) { 104 managedClasses = new ArrayList<>(); 105 if (includingUsers) { 106 managedClasses.add(NuxeoPrincipal.class); 107 } 108 if (includingGroups) { 109 managedClasses.add(NuxeoGroup.class); 110 } 111 } 112 return managedClasses; 113 } 114 115 @Override 116 public void configure(Map<String, String> parameters) throws IllegalStateException { 117 super.configure(parameters); 118 if (FILTER_USER.equals(parameters.get(INPUT_PARAM_FILTER))) { 119 includingGroups = false; 120 } else if (FILTER_GROUP.equals(parameters.get(INPUT_PARAM_FILTER))) { 121 includingUsers = false; 122 } 123 this.parameters.put(PARAM_INCLUDE_GROUPS, includingGroups); 124 this.parameters.put(PARAM_INCLUDE_USERS, includingUsers); 125 } 126 127 @Override 128 public String getName() throws IllegalStateException { 129 checkConfig(); 130 return UserManagerResolver.NAME; 131 } 132 133 @Override 134 public Object fetch(Object value) throws IllegalStateException { 135 checkConfig(); 136 if (value instanceof String) { 137 String name = (String) value; 138 boolean userPrefix = name.startsWith(NuxeoPrincipal.PREFIX); 139 boolean groupPrefix = name.startsWith(NuxeoGroup.PREFIX); 140 if (includingUsers && !includingGroups) { 141 if (userPrefix) { 142 name = name.substring(NuxeoPrincipal.PREFIX.length()); 143 } 144 if (SecurityConstants.SYSTEM_USERNAME.equals(name)) { 145 return new SystemPrincipal(null); 146 } 147 return getUserManager().getPrincipal(name, false); 148 } else if (!includingUsers && includingGroups) { 149 if (groupPrefix) { 150 name = name.substring(NuxeoGroup.PREFIX.length()); 151 } 152 return getUserManager().getGroup(name); 153 } else { 154 if (userPrefix) { 155 name = name.substring(NuxeoPrincipal.PREFIX.length()); 156 if (SecurityConstants.SYSTEM_USERNAME.equals(name)) { 157 return new SystemPrincipal(null); 158 } 159 return getUserManager().getPrincipal(name, false); 160 } else if (groupPrefix) { 161 name = name.substring(NuxeoGroup.PREFIX.length()); 162 return getUserManager().getGroup(name); 163 } else { 164 if (SecurityConstants.SYSTEM_USERNAME.equals(name)) { 165 return new SystemPrincipal(null); 166 } 167 NuxeoPrincipal principal = getUserManager().getPrincipal(name, false); 168 if (principal != null) { 169 return principal; 170 } else { 171 return getUserManager().getGroup(name); 172 } 173 } 174 } 175 } 176 return null; 177 } 178 179 @SuppressWarnings("unchecked") 180 @Override 181 public <T> T fetch(Class<T> type, Object value) throws IllegalStateException { 182 checkConfig(); 183 Object principal = fetch(value); 184 if (type.isInstance(principal)) { 185 return (T) principal; 186 } 187 return null; 188 } 189 190 @Override 191 public Serializable getReference(Object entity) throws IllegalStateException { 192 checkConfig(); 193 if (entity != null) { 194 if (entity instanceof NuxeoPrincipal && includingUsers) { 195 return NuxeoPrincipal.PREFIX + ((NuxeoPrincipal) entity).getName(); 196 } else if (entity instanceof NuxeoGroup && includingGroups) { 197 return NuxeoGroup.PREFIX + ((NuxeoGroup) entity).getName(); 198 } 199 } 200 return null; 201 } 202 203 @Override 204 public String getConstraintErrorMessage(Object invalidValue, Locale locale) throws IllegalStateException { 205 checkConfig(); 206 if (isIncludingUsers() && isIncludingGroups()) { 207 return Helper.getConstraintErrorMessage(this, "any", invalidValue, locale); 208 } else if (!isIncludingUsers() && isIncludingGroups()) { 209 return Helper.getConstraintErrorMessage(this, "group", invalidValue, locale); 210 } else if (isIncludingUsers() && !isIncludingGroups()) { 211 return Helper.getConstraintErrorMessage(this, "user", invalidValue, locale); 212 } 213 return String.format("%s cannot resolve reference %s", getName(), invalidValue); 214 } 215 216 public boolean isIncludingUsers() throws IllegalStateException { 217 checkConfig(); 218 return includingUsers; 219 } 220 221 public boolean isIncludingGroups() throws IllegalStateException { 222 checkConfig(); 223 return includingGroups; 224 } 225 226}