001/* 002 * (C) Copyright 2014 Nuxeo SA (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl-2.1.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Nicolas Chapurlat <nchapurlat@nuxeo.com> 016 */ 017 018package org.nuxeo.ecm.platform.usermanager; 019 020import java.io.Serializable; 021import java.util.ArrayList; 022import java.util.Collections; 023import java.util.HashMap; 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.schema.types.resolver.ObjectResolver; 032import org.nuxeo.runtime.api.Framework; 033import org.nuxeo.runtime.api.login.LoginComponent; 034 035/** 036 * This {@link ObjectResolver} allows to manage integrity for fields containing group or user references. 037 * <p> 038 * References should have a prefix. NuxeoPrincipal.PREFIX for users, NuxeoGroup.PREFIX for groups. 039 * </p> 040 * <p> 041 * If only user or group are configured, the prefix is not needed but still supported. If noth user and group are 042 * configured, reference without prefix are resolved as user first. 043 * </p> 044 * <p> 045 * To use it, put the following code in your schema XSD : 046 * </p> 047 * 048 * <pre> 049 * {@code 050 * <!-- user or group resolver --> 051 * <xs:simpleType name="userOrGroupReference"> 052 * <xs:restriction base="xs:string" ref:resolver="userManagerResolver" /> 053 * </xs:simpleType> 054 * 055 * <!-- user resolver --> 056 * <xs:simpleType name="userReference"> 057 * <xs:restriction base="xs:string" ref:resolver="userManagerResolver" ref:type="user" /> 058 * </xs:simpleType> 059 * 060 * <!-- group resolver --> 061 * <xs:simpleType name="groupReference"> 062 * <xs:restriction base="xs:string" ref:resolver="userManagerResolver" ref:type="group" /> 063 * </xs:simpleType> 064 * } 065 * </pre> 066 * 067 * @since 7.1 068 */ 069public class UserManagerResolver implements ObjectResolver { 070 071 private static final long serialVersionUID = 1L; 072 073 public static final String INPUT_PARAM_FILTER = "type"; 074 075 public static final String FILTER_GROUP = "group"; 076 077 public static final String FILTER_USER = "user"; 078 079 public static final String NAME = "userManagerResolver"; 080 081 public static final String PARAM_INCLUDE_USERS = "includeUsers"; 082 083 public static final String PARAM_INCLUDE_GROUPS = "includeGroups"; 084 085 private Map<String, Serializable> parameters; 086 087 private boolean includingUsers = true; 088 089 private boolean includingGroups = true; 090 091 private transient UserManager userManager; 092 093 public UserManager getUserManager() { 094 if (userManager == null) { 095 userManager = Framework.getService(UserManager.class); 096 } 097 return userManager; 098 } 099 100 private List<Class<?>> managedClasses = null; 101 102 @Override 103 public List<Class<?>> getManagedClasses() { 104 if (managedClasses == null) { 105 managedClasses = new ArrayList<Class<?>>(); 106 if (includingUsers) { 107 managedClasses.add(NuxeoPrincipal.class); 108 } 109 if (includingGroups) { 110 managedClasses.add(NuxeoGroup.class); 111 } 112 } 113 return managedClasses; 114 } 115 116 @Override 117 public void configure(Map<String, String> parameters) throws IllegalStateException { 118 if (this.parameters != null) { 119 throw new IllegalStateException("cannot change configuration, may be already in use somewhere"); 120 } 121 if (FILTER_USER.equals(parameters.get(INPUT_PARAM_FILTER))) { 122 includingGroups = false; 123 } else if (FILTER_GROUP.equals(parameters.get(INPUT_PARAM_FILTER))) { 124 includingUsers = false; 125 } 126 this.parameters = new HashMap<String, Serializable>(); 127 this.parameters.put(PARAM_INCLUDE_GROUPS, includingGroups); 128 this.parameters.put(PARAM_INCLUDE_USERS, includingUsers); 129 } 130 131 @Override 132 public String getName() throws IllegalStateException { 133 checkConfig(); 134 return UserManagerResolver.NAME; 135 } 136 137 @Override 138 public Map<String, Serializable> getParameters() throws IllegalStateException { 139 checkConfig(); 140 return Collections.unmodifiableMap(parameters); 141 } 142 143 @Override 144 public boolean validate(Object value) throws IllegalStateException { 145 checkConfig(); 146 return fetch(value) != null; 147 } 148 149 @Override 150 public Object fetch(Object value) throws IllegalStateException { 151 checkConfig(); 152 if (value != null && value instanceof String) { 153 String name = (String) value; 154 boolean userPrefix = name.startsWith(NuxeoPrincipal.PREFIX); 155 boolean groupPrefix = name.startsWith(NuxeoGroup.PREFIX); 156 if (includingUsers && !includingGroups) { 157 if (userPrefix) { 158 name = name.substring(NuxeoPrincipal.PREFIX.length()); 159 } 160 if (LoginComponent.SYSTEM_USERNAME.equals(name)) { 161 return new SystemPrincipal(name); 162 } 163 return getUserManager().getPrincipal(name); 164 } else if (!includingUsers && includingGroups) { 165 if (groupPrefix) { 166 name = name.substring(NuxeoGroup.PREFIX.length()); 167 } 168 return getUserManager().getGroup(name); 169 } else { 170 if (userPrefix) { 171 name = name.substring(NuxeoPrincipal.PREFIX.length()); 172 if (LoginComponent.SYSTEM_USERNAME.equals(name)) { 173 return new SystemPrincipal(name); 174 } 175 return getUserManager().getPrincipal(name); 176 } else if (groupPrefix) { 177 name = name.substring(NuxeoGroup.PREFIX.length()); 178 return getUserManager().getGroup(name); 179 } else { 180 if (LoginComponent.SYSTEM_USERNAME.equals(name)) { 181 return new SystemPrincipal(name); 182 } 183 NuxeoPrincipal principal = getUserManager().getPrincipal(name); 184 if (principal != null) { 185 return principal; 186 } else { 187 return getUserManager().getGroup(name); 188 } 189 } 190 } 191 } 192 return null; 193 } 194 195 @SuppressWarnings("unchecked") 196 @Override 197 public <T> T fetch(Class<T> type, Object value) throws IllegalStateException { 198 checkConfig(); 199 Object principal = fetch(value); 200 if (type.isInstance(principal)) { 201 return (T) principal; 202 } 203 return null; 204 } 205 206 @Override 207 public Serializable getReference(Object entity) throws IllegalStateException { 208 checkConfig(); 209 if (entity != null) { 210 if (entity instanceof NuxeoPrincipal && includingUsers) { 211 return NuxeoPrincipal.PREFIX + ((NuxeoPrincipal) entity).getName(); 212 } else if (entity instanceof NuxeoGroup && includingGroups) { 213 return NuxeoGroup.PREFIX + ((NuxeoGroup) entity).getName(); 214 } 215 } 216 return null; 217 } 218 219 @Override 220 public String getConstraintErrorMessage(Object invalidValue, Locale locale) throws IllegalStateException { 221 checkConfig(); 222 if (isIncludingUsers() && isIncludingGroups()) { 223 return Helper.getConstraintErrorMessage(this, "any", invalidValue, locale); 224 } else if (!isIncludingUsers() && isIncludingGroups()) { 225 return Helper.getConstraintErrorMessage(this, "group", invalidValue, locale); 226 } else if (isIncludingUsers() && !isIncludingGroups()) { 227 return Helper.getConstraintErrorMessage(this, "user", invalidValue, locale); 228 } 229 return String.format("%s cannot resolve reference %s", getName(), invalidValue); 230 } 231 232 public boolean isIncludingUsers() throws IllegalStateException { 233 checkConfig(); 234 return includingUsers; 235 } 236 237 public boolean isIncludingGroups() throws IllegalStateException { 238 checkConfig(); 239 return includingGroups; 240 } 241 242 private void checkConfig() throws IllegalStateException { 243 if (parameters == null) { 244 throw new IllegalStateException( 245 "you should call #configure(Map<String, String>) before. Please get this resolver throught ExternalReferenceService which is in charge of resolver configuration."); 246 } 247 } 248 249}