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