/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.user.server;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import com.google.inject.persist.Transactional;
import org.eclipse.che.api.core.ApiException;
import org.eclipse.che.api.core.ConflictException;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.Page;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.core.model.user.Profile;
import org.eclipse.che.api.core.model.user.User;
import org.eclipse.che.api.core.notification.EventService;
import org.eclipse.che.api.user.server.event.BeforeUserRemovedEvent;
import org.eclipse.che.api.user.server.event.PostUserPersistedEvent;
import org.eclipse.che.api.user.server.event.UserCreatedEvent;
import org.eclipse.che.api.user.server.event.UserRemovedEvent;
import org.eclipse.che.api.user.server.model.impl.ProfileImpl;
import org.eclipse.che.api.user.server.model.impl.UserImpl;
import org.eclipse.che.api.user.server.spi.PreferenceDao;
import org.eclipse.che.api.user.server.spi.ProfileDao;
import org.eclipse.che.api.user.server.spi.UserDao;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import java.util.Set;
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkArgument;
import static java.lang.System.currentTimeMillis;
import static java.util.Objects.requireNonNull;
import static org.eclipse.che.api.user.server.Constants.ID_LENGTH;
import static org.eclipse.che.api.user.server.Constants.PASSWORD_LENGTH;
import static org.eclipse.che.commons.lang.NameGenerator.generate;
/**
* Facade for {@link User} and {@link Profile} related operations.
*
* @author Max Shaposhnik (mshaposhnik@codenvy.com)
* @author Yevhenii Voevodin
* @author Anton Korneta
*/
@Singleton
public class UserManager {
private final UserDao userDao;
private final ProfileDao profileDao;
private final PreferenceDao preferencesDao;
private final Set<String> reservedNames;
private final EventService eventService;
@Inject
public UserManager(UserDao userDao,
ProfileDao profileDao,
PreferenceDao preferencesDao,
EventService eventService,
@Named("che.auth.reserved_user_names") String[] reservedNames) {
this.userDao = userDao;
this.profileDao = profileDao;
this.preferencesDao = preferencesDao;
this.eventService = eventService;
this.reservedNames = Sets.newHashSet(reservedNames);
}
/**
* Creates new user and his profile.
*
* @param newUser
* created user
* @throws NullPointerException
* when {@code newUser} is null
* @throws ConflictException
* when user with such name/email/alias already exists
* @throws ServerException
* when any other error occurs
*/
public User create(User newUser, boolean isTemporary) throws ConflictException, ServerException {
requireNonNull(newUser, "Required non-null user");
if (reservedNames.contains(newUser.getName().toLowerCase())) {
throw new ConflictException(String.format("Username '%s' is reserved", newUser.getName()));
}
final String userId = newUser.getId() != null ? newUser.getId() : generate("user", ID_LENGTH);
final UserImpl user = new UserImpl(userId,
newUser.getEmail(),
newUser.getName(),
firstNonNull(newUser.getPassword(), generate("", PASSWORD_LENGTH)),
newUser.getAliases());
doCreate(user, isTemporary);
eventService.publish(new UserCreatedEvent(user));
return user;
}
@Transactional(rollbackOn = {RuntimeException.class, ApiException.class})
protected void doCreate(UserImpl user, boolean isTemporary) throws ConflictException, ServerException {
userDao.create(user);
eventService.publish(new PostUserPersistedEvent(new UserImpl(user))).propagateException();
profileDao.create(new ProfileImpl(user.getId()));
preferencesDao.setPreferences(user.getId(), ImmutableMap.of("temporary", Boolean.toString(isTemporary),
"codenvy:created", Long.toString(currentTimeMillis())));
}
/**
* Updates user by replacing an existing user entity with a new one.
*
* @param user
* user update
* @throws NullPointerException
* when {@code user} is null
* @throws NotFoundException
* when user with id {@code user.getId()} is not found
* @throws ConflictException
* when user's new alias/email/name is not unique
* @throws ServerException
* when any other error occurs
*/
public void update(User user) throws NotFoundException, ServerException, ConflictException {
requireNonNull(user, "Required non-null user");
userDao.update(new UserImpl(user));
}
/**
* Finds user by given {@code id}.
*
* @param id
* user identifier
* @return user instance
* @throws NullPointerException
* when {@code id} is null
* @throws NotFoundException
* when user doesn't exist
* @throws ServerException
* when any other error occurs
*/
public User getById(String id) throws NotFoundException, ServerException {
requireNonNull(id, "Required non-null id");
return userDao.getById(id);
}
/**
* Finds user by given {@code alias}.
*
* @param alias
* user alias
* @return user instance
* @throws NullPointerException
* when {@code alias} is null
* @throws NotFoundException
* when user doesn't exist
* @throws ServerException
* when any other error occurs
*/
public User getByAlias(String alias) throws NotFoundException, ServerException {
requireNonNull(alias, "Required non-null alias");
return userDao.getByAlias(alias);
}
/**
* Finds user by given {@code name}.
*
* @param name
* user name
* @return user instance
* @throws NullPointerException
* when {@code name} is null
* @throws NotFoundException
* when user doesn't exist
* @throws ServerException
* when any other error occurs
*/
public User getByName(String name) throws NotFoundException, ServerException {
requireNonNull(name, "Required non-null name");
return userDao.getByName(name);
}
/**
* Finds user by given {@code email}.
*
* @param email
* user email
* @return user instance
* @throws NullPointerException
* when {@code email} is null
* @throws NotFoundException
* when user doesn't exist
* @throws ServerException
* when any other error occurs
*/
public User getByEmail(String email) throws NotFoundException, ServerException {
requireNonNull(email, "Required non-null email");
return userDao.getByEmail(email);
}
/**
* Finds all users {@code email}.
*
* @param maxItems
* the maximum number of users to return
* @param skipCount
* the number of users to skip
* @return user instance
* @throws IllegalArgumentException
* when {@code maxItems} or {@code skipCount} is negative
* @throws ServerException
* when any other error occurs
*/
public Page<UserImpl> getAll(int maxItems, long skipCount) throws ServerException {
checkArgument(maxItems >= 0, "The number of items to return can't be negative.");
checkArgument(skipCount >= 0, "The number of items to skip can't be negative.");
return userDao.getAll(maxItems, skipCount);
}
/**
* Returns all users whose email address contains specified {@code emailPart}.
*
* @param emailPart
* fragment of user's email
* @param maxItems
* the maximum number of users to return
* @param skipCount
* the number of users to skip
* @return list of matched users
* @throws NullPointerException
* when {@code emailPart} is null
* @throws IllegalArgumentException
* when {@code maxItems} or {@code skipCount} is negative or
* when {@code skipCount} more than {@value Integer#MAX_VALUE}
* @throws ServerException
* when any other error occurs
*/
public Page<? extends User> getByEmailPart(String emailPart, int maxItems, long skipCount) throws ServerException {
requireNonNull(emailPart, "Required non-null email part");
checkArgument(maxItems >= 0, "The number of items to return can't be negative");
checkArgument(skipCount >= 0 && skipCount <= Integer.MAX_VALUE,
"The number of items to skip can't be negative or greater than " + Integer.MAX_VALUE);
return userDao.getByEmailPart(emailPart, maxItems, skipCount);
}
/**
* Returns all users whose name contains specified {@code namePart}.
*
* @param namePart
* fragment of user's name
* @param maxItems
* the maximum number of users to return
* @param skipCount
* the number of users to skip
* @return list of matched users
* @throws NullPointerException
* when {@code namePart} is null
* @throws IllegalArgumentException
* when {@code maxItems} or {@code skipCount} is negative or
* when {@code skipCount} more than {@value Integer#MAX_VALUE}
* @throws ServerException
* when any other error occurs
*/
public Page<? extends User> getByNamePart(String namePart, int maxItems, long skipCount) throws ServerException {
requireNonNull(namePart, "Required non-null name part");
checkArgument(maxItems >= 0, "The number of items to return can't be negative");
checkArgument(skipCount >= 0 && skipCount <= Integer.MAX_VALUE,
"The number of items to skip can't be negative or greater than " + Integer.MAX_VALUE);
return userDao.getByNamePart(namePart, maxItems, skipCount);
}
/**
* Gets total count of all users
*
* @return user count
* @throws ServerException
* when any error occurs
*/
public long getTotalCount() throws ServerException {
return userDao.getTotalCount();
}
/**
* Removes user by given {@code id}.
*
* @param id
* user identifier
* @throws NullPointerException
* when {@code id} is null
* @throws ConflictException
* when given user cannot be deleted
* @throws ServerException
* when any other error occurs
*/
public void remove(String id) throws ServerException, ConflictException {
requireNonNull(id, "Required non-null id");
doRemove(id);
eventService.publish(new UserRemovedEvent(id));
}
@Transactional(rollbackOn = {RuntimeException.class, ServerException.class})
protected void doRemove(String id) throws ServerException {
UserImpl user;
try {
user = userDao.getById(id);
} catch (NotFoundException ignored) {
return;
}
preferencesDao.remove(id);
profileDao.remove(id);
eventService.publish(new BeforeUserRemovedEvent(user)).propagateException();
userDao.remove(id);
}
}