001/* 002 * Copyright 2012 Anyware Services 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 */ 016package org.ametys.core.user; 017 018import java.io.InputStream; 019import java.time.ZonedDateTime; 020import java.util.List; 021import java.util.TimeZone; 022import java.util.stream.Collectors; 023import java.util.stream.Stream; 024 025import org.apache.commons.lang3.StringUtils; 026 027import org.ametys.core.user.dataprovider.UserDataProvider; 028import org.ametys.core.user.directory.StoredUser; 029import org.ametys.core.user.directory.UserDirectory; 030import org.ametys.core.util.SizeUtils.ExcludeFromSizeCalculation; 031 032/** 033 * Implementation of the principal abstraction to represent an user with a login and a {@link StoredUser} 034 * @param <T> The type of the factory 035 */ 036public class User <T extends UserFactory> implements java.security.Principal 037{ 038 /** The id of the last name data. This data is of type {@link String} */ 039 public static final String LAST_NAME_DATA_ID = "lastname"; 040 /** The id of the first name data. This data is of type {@link String} */ 041 public static final String FIRST_NAME_DATA_ID = "firstname"; 042 /** The id of the email data. This data is of type {@link String} */ 043 public static final String EMAIL_DATA_ID = "email"; 044 /** The id of the image data. This data is of type {@link UserImage} */ 045 public static final String IMAGE_DATA_ID = "image"; 046 /** The id of the language data. This data is of type String */ 047 public static final String LANGUAGE_DATA_ID = "language"; 048 /** The id of the timezone data. This data is of type {@link TimeZone} or is a TimeZoneId of type {@link String} */ 049 public static final String TIMEZONE_DATA_ID = "timezone"; 050 /** The id of the creation date data. This data is of type {@link ZonedDateTime} */ 051 public static final String CREATION_DATE_DATA_ID = "creation-date"; 052 /** The id of the creation origin data. This data is of type {@link UserCreationOrigin} */ 053 public static final String CREATION_ORIGIN_DATA_ID = "creation-origin"; 054 055 /** 056 * The identity of this principal. 057 */ 058 protected UserIdentity _identity; 059 /** 060 * The user directory this user belongs to. 061 */ 062 @ExcludeFromSizeCalculation 063 protected UserDirectory _userDirectory; 064 065 /** The factory */ 066 @ExcludeFromSizeCalculation 067 protected T _factory; 068 069 private StoredUser _storedUser; 070 071 /** 072 * Enumeration for the user creation origin 073 * 074 */ 075 public enum UserCreationOrigin 076 { 077 /** User created by system */ 078 SYSTEM, 079 /** User created by an administrator */ 080 ADMIN, 081 /** User created by user signup */ 082 USER_SIGNUP, 083 /** When user creation is unknown or not available */ 084 NOT_AVAILABLE 085 } 086 087 /** 088 * Basic structure holding necessary data representing an user profile image 089 */ 090 public static class UserImage 091 { 092 private final InputStream _inputstream; 093 private final String _filename; 094 private final Long _length; 095 private final Long _lastModified; 096 private final String _type; 097 098 /** 099 * Constructor 100 * @param inputstream The image input stream 101 * @param filename The file name or null if unknown 102 * @param length The file length if known 103 * @param lastModified The last modified date 104 * @param type The type name of the source of the image to compute a cache key 105 */ 106 public UserImage(InputStream inputstream, String filename, Long length, Long lastModified, String type) 107 { 108 _inputstream = inputstream; 109 _filename = filename; 110 _length = length; 111 _lastModified = lastModified; 112 _type = type; 113 } 114 115 /** 116 * Retrieve the type name of the source of the image to compute a cache key 117 * @return the type 118 */ 119 public String getType() 120 { 121 return _type; 122 } 123 124 /** 125 * Retrieves the input stream 126 * @return the input stream 127 */ 128 public InputStream getInputstream() 129 { 130 return _inputstream; 131 } 132 133 /** 134 * Retrieves the filename 135 * @return the filename or null if not defined 136 */ 137 public String getFilename() 138 { 139 return _filename; 140 } 141 142 /** 143 * Retrieves the length 144 * @return the length or null or -1 if unknown 145 */ 146 public Long getLength() 147 { 148 return _length; 149 } 150 151 /** 152 * Retrieve the last modified date. 153 * @return The last modified date. Can be null if unknown. 154 */ 155 public Long getLastModified() 156 { 157 return _lastModified; 158 } 159 } 160 161 /** 162 * Construct a new UserPrincipal, associated with a last name, a first name, 163 * an email and identified by a login. 164 * @param storedUser The storedUser. Cannot be null 165 * @param userDirectory The user directory the user belongs to. Cannot be null. 166 * @param factory The factory 167 */ 168 User(StoredUser storedUser, UserDirectory userDirectory, T factory) 169 { 170 _identity = new UserIdentity(storedUser.getIdentifier(), userDirectory.getPopulationId()); 171 _storedUser = storedUser; 172 _userDirectory = userDirectory; 173 _factory = factory; 174 } 175 176 /** 177 * The identity of the user. 178 * 179 * @return The identity. 180 */ 181 public UserIdentity getIdentity() 182 { 183 return _identity; 184 } 185 186 /** 187 * Get the StoredUser of the User 188 * @return The {@link StoredUser} 189 */ 190 public StoredUser getStoredUser() 191 { 192 return _storedUser; 193 } 194 195 @Override 196 public String getName() 197 { 198 return UserIdentity.userIdentityToString(_identity); 199 } 200 201 /** 202 * The user directory this user belongs to. 203 * @return The user directory 204 */ 205 public UserDirectory getUserDirectory() 206 { 207 return _userDirectory; 208 } 209 210 /** 211 * The last name of the user 212 * @return The last name. 213 */ 214 public String getLastName() 215 { 216 return (String) getValue(LAST_NAME_DATA_ID); 217 } 218 219 /** 220 * The first name of the user 221 * @return The first name. 222 */ 223 public String getFirstName() 224 { 225 return (String) getValue(FIRST_NAME_DATA_ID); 226 } 227 228 /** 229 * The email of the user represented by this Principal. 230 * 231 * @return The email. 232 */ 233 public String getEmail() 234 { 235 return (String) getValue(EMAIL_DATA_ID); 236 } 237 238 /** 239 * The fullname of this user. 240 * @return The full name 241 */ 242 public String getFullName() 243 { 244 return _getFullName(true); 245 } 246 247 /** 248 * The fullname to use to display if sort is needed. 249 * Ensure the sort will be on 250 * @return The sortable name 251 */ 252 public String getSortableName() 253 { 254 return _getFullName(false); 255 } 256 257 /** 258 * Get user's avatar from the {@link UserDataProvider} with the biggest priority 259 * @param size The size 260 * @param maxSize The maxSize 261 * @return A {@link UserImage} the user's avatar 262 */ 263 public UserImage getImage(int size, int maxSize) 264 { 265 UserImageAccessor imageAccessor = (UserImageAccessor) getValue(IMAGE_DATA_ID); 266 267 return imageAccessor != null ? imageAccessor.getImage(size, maxSize) : null; 268 } 269 270 /** 271 * Get user's language from the {@link UserDataProvider} with the biggest priority 272 * @return The user's language 273 */ 274 public String getLanguage() 275 { 276 return (String) getValue(LANGUAGE_DATA_ID); 277 } 278 279 /** 280 * Get user's TimeZone from the {@link UserDataProvider} with the biggest priority 281 * @return The user's timezone 282 */ 283 public TimeZone getTimeZone() 284 { 285 Object timezoneFound = getValue(TIMEZONE_DATA_ID); 286 if (timezoneFound instanceof TimeZone timeZone) 287 { 288 return timeZone; 289 } 290 else if (timezoneFound instanceof String timeZoneId && List.of(TimeZone.getAvailableIDs()).contains(timeZoneId)) 291 { 292 return TimeZone.getTimeZone(timeZoneId); 293 } 294 295 return null; 296 } 297 298 /** 299 * Get the last connection date of the user 300 * @return the last connection date or null if the user never logged in 301 */ 302 public ZonedDateTime getLastConnectionDate() 303 { 304 return _factory.getUserStatusManager().getLastConnectionDate(this.getIdentity()).orElse(null); 305 } 306 307 /** 308 * Get the user's creation date 309 * @return the creation date 310 */ 311 public ZonedDateTime getCreationDate() 312 { 313 return (ZonedDateTime) getValue(CREATION_DATE_DATA_ID); 314 } 315 316 /** 317 * Get the user's creation origin 318 * @return the creation origin 319 */ 320 public UserCreationOrigin getCreationOrigin() 321 { 322 return (UserCreationOrigin) getValue(CREATION_ORIGIN_DATA_ID); 323 } 324 325 /** 326 * Get a user's data from the {@link UserDataProvider} with the biggest priority 327 * @param dataId The data id 328 * @return A user's data 329 */ 330 public Object getValue(String dataId) 331 { 332 return _factory.getUserDataProviderEP().getValue(this, dataId); 333 } 334 335 /** 336 * Get the {@link UserDataProvider} with the biggest priority and a value not null for the data requested 337 * @param dataId The data id 338 * @return A {@link UserDataProvider} the user data provider which is going to provide the data requested 339 */ 340 public UserDataProvider getProviderFor(String dataId) 341 { 342 return _factory.getUserDataProviderEP().getProviderFor(this, dataId); 343 } 344 345 /** 346 * The full name of the user represented by this Principal. 347 * @param firstnameThenLastname Define the name order in the full name. Set to <code>true</code> to retrieve the fullname with firstname first and lastname second. Set to <code>false</code> to retrieve the fullname with lastname first and firstname second. 348 * @return The full name. 349 */ 350 protected String _getFullName(boolean firstnameThenLastname) 351 { 352 Stream<String> stream = firstnameThenLastname 353 ? Stream.of(getFirstName(), getLastName()) 354 : Stream.of(getLastName(), getFirstName()); 355 356 String sortableName = stream.filter(StringUtils::isNotEmpty) 357 .collect(Collectors.joining(" ")); 358 359 return StringUtils.defaultIfEmpty(sortableName, _identity.getLogin()); 360 } 361 362 /** 363 * Return a String representation of this object, which exposes only 364 * information that should be public. 365 * 366 * @return A string representing the user. 367 */ 368 @Override 369 public String toString() 370 { 371 StringBuilder sb = new StringBuilder("Principal["); 372 sb.append(_userDirectory.getPopulationId()); 373 sb.append(", "); 374 sb.append(_storedUser.toString()); 375 sb.append("]"); 376 return sb.toString(); 377 } 378 379 /** 380 * Test if two principal are equals. 381 * @return true if the given Object represents the same Principal. 382 */ 383 @Override 384 public boolean equals(Object another) 385 { 386 if (another == null || !(another instanceof User)) 387 { 388 return false; 389 } 390 391 User otherUser = (User) another; 392 393 return _identity != null && _identity.equals(otherUser.getIdentity()); 394 } 395 396 @Override 397 public int hashCode() 398 { 399 return _identity.hashCode(); 400 } 401}