com.streamreduce.core.service.UserServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.streamreduce.core.service.UserServiceImpl.java

Source

/*
 * Copyright 2012 Nodeable Inc
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.streamreduce.core.service;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.streamreduce.Constants;
import com.streamreduce.InvalidUserAliasException;
import com.streamreduce.core.dao.AccountDAO;
import com.streamreduce.core.dao.DAODatasourceType;
import com.streamreduce.core.dao.EventLogDAO;
import com.streamreduce.core.dao.GenericCollectionDAO;
import com.streamreduce.core.dao.RoleDAO;
import com.streamreduce.core.dao.UserDAO;
import com.streamreduce.core.event.EventId;
import com.streamreduce.core.model.Account;
import com.streamreduce.core.model.Event;
import com.streamreduce.core.model.EventLog;
import com.streamreduce.core.model.Role;
import com.streamreduce.core.model.User;
import com.streamreduce.core.service.exception.AccountNotFoundException;
import com.streamreduce.core.service.exception.UserNotFoundException;
import com.streamreduce.core.service.exception.UsernameUnavailableException;
import com.streamreduce.security.Roles;
import com.streamreduce.util.MessageUtils;
import net.sf.json.JSONObject;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

@Service("userService")
public class UserServiceImpl extends AbstractService implements UserService {

    @Autowired
    private UserDAO userDAO;
    @Autowired
    private RoleDAO roleDAO;
    @Autowired
    private AccountDAO accountDAO;
    @Autowired
    private EventLogDAO eventLogDAO;
    @Autowired
    private GenericCollectionDAO genericCollectionDAO;
    @Autowired
    private SearchService searchService;
    @Autowired
    private EventService eventService;
    @Autowired
    private MessageService messageService;

    private Cache<String, User> superUserCache = CacheBuilder.newBuilder().maximumSize(1)
            .expireAfterAccess(5, TimeUnit.MINUTES).build();

    @Override
    public boolean isUsernameAvailable(String name) {
        User u = userDAO.findUser(name);
        return u == null;
    }

    @Override
    public boolean isAliasAvailable(Account account, String alias) {
        return userDAO.findUserForAlias(account, alias) == null;
    }

    @Override
    public Account getAccount(ObjectId accountId) throws AccountNotFoundException {
        if (accountId == null) {
            throw new AccountNotFoundException("Passed account ID was null.");
        }
        Account a = accountDAO.get(accountId);
        if (a == null) {
            throw new AccountNotFoundException(accountId.toString());
        }
        // Create the event stream entry
        eventService.createEvent(EventId.READ, a, null);
        return a;
    }

    @Override
    public Set<Role> getAccountRoles(ObjectId accountId) throws AccountNotFoundException {
        return roleDAO.findAccountRoles(accountId);
    }

    @Override
    public User getUserById(ObjectId userId) throws UserNotFoundException {
        User u = userDAO.get(userId);
        if (u == null) {
            throw new UserNotFoundException(userId.toString());
        }
        // Create the event stream entry
        eventService.createEvent(EventId.READ, u, null);
        return u;
    }

    public List<User> getUsersById(Set<ObjectId> userIds) {
        List<User> userList = new ArrayList<>();
        for (ObjectId id : userIds) {
            try {
                userList.add(getUserById(id));
            } catch (UserNotFoundException e) {
                // whatever...
            }
        }
        return userList;
    }

    @Override
    public User getUserById(ObjectId userId, Account account) throws UserNotFoundException {
        User u = userDAO.get(userId);
        User user = userDAO.findUserForUsername(account, u.getUsername());
        if (user == null) {
            throw new UserNotFoundException(u.getUsername());
        }
        // Create the event stream entry
        eventService.createEvent(EventId.READ, u, null);
        return user;
    }

    @Override
    public User getUserByAuthenticationToken(String authToken) throws UserNotFoundException {
        User user = userDAO.findByAuthToken(authToken);
        if (user == null) {
            throw new UserNotFoundException(authToken);
        }
        // Create the event stream entry
        eventService.createEvent(EventId.READ, user, null);
        return user;
    }

    @Override
    public User getUser(String username, Account account) throws UserNotFoundException {
        User u = userDAO.findUserForUsername(account, username);
        if (u == null) {
            throw new UserNotFoundException(username);
        }
        // Create the event stream entry
        eventService.createEvent(EventId.READ, u, null);
        return u;
    }

    @Override
    public User getUser(String username) throws UserNotFoundException {
        User u = userDAO.findUser(username);
        if (u == null) {
            throw new UserNotFoundException(username);
        }
        // Create the event stream entry
        eventService.createEvent(EventId.READ, u, null);
        return u;
    }

    @Override
    public User getTargetUser(Account account, String name) throws UserNotFoundException {
        User u = userDAO.findUserInAccount(account, name);
        if (u == null) {
            throw new UserNotFoundException(name);
        }
        // Create the event stream entry
        eventService.createEvent(EventId.READ, u, null);
        return u;
    }

    /**
     * Create an account. Also assign the default product
     *
     * @param account - properly formed account to create
     * @return - the account with the accountId populated
     */
    @Override
    public Account createAccount(Account account) {
        accountDAO.save(account);
        // bootstrap the metric inbox
        createMetricInbox(account);
        // once the inbox has been created, start a river so that we can search on it
        searchService.createRiverForAccount(account);
        // Bootstrap sample messages
        messageService.copyArchivedMessagesToInbox(account);
        // Create the event stream entry
        eventService.createEvent(EventId.CREATE, account, null);
        return account;
    }

    @Override
    public void updateAccount(Account account) {
        accountDAO.save(account);
        // Create the event stream entry
        eventService.createEvent(EventId.UPDATE, account, null);
    }

    @Override
    public void updateUser(User user) {
        validateUserAlias(user);
        userDAO.save(user);
        // Create the event stream entry
        eventService.createEvent(EventId.UPDATE, user, null);
    }

    @Override
    public void resetUserPassword(User user, boolean mobile) {
        userDAO.save(user);
        // Create the event stream entry
        eventService.createEvent(EventId.USER_PASSWORD_RESET_REQUEST, user, null);
    }

    @Override
    public void deleteAccount(ObjectId accountId) {
        try {
            // you can't delete the super user account
            User superUser = getSuperUser();
            if (superUser.getAccount().getId().equals(accountId)) {
                logger.error("You can not delete the Nodeable account");
                return;
            }
            Account toBeDeleted = getAccount(accountId);
            // Create the event stream entry
            eventService.createEvent(EventId.DELETE, toBeDeleted, null);
            accountDAO.deleteById(accountId);
        } catch (AccountNotFoundException anf) {
            logger.error("AccountNotFoundException", anf);
        }
    }

    @Override
    public void deleteUser(User user) {

        // you can't delete the super user
        if (user.getUsername().equals(Constants.NODEABLE_SUPER_USERNAME)) {
            logger.error("You can not delete the Nodebelly user");
            return;
        }
        // Create the event stream entry
        Event event = eventService.createEvent(EventId.DELETE, user, null);
        if (user.getAccount() != null) {
            // Send a user is being deleted message
            messageService.sendNodeableAccountMessage(event, user.getAccount(), null);
        }
        // TODO: remove resoruces they own?
        userDAO.deleteById(user.getId());
    }

    @Override
    public User getUserFromInvite(String inviteKey, String accountId) throws UserNotFoundException {
        User u = userDAO.findInvitedUser(inviteKey, accountId);
        if (u == null) {
            throw new UserNotFoundException(inviteKey + "+" + accountId);
        }
        // Create the event stream entry
        eventService.createEvent(EventId.READ, u, null);
        return u;
    }

    @Override
    public User getUserFromSignupKey(String signupKey, String userId) throws UserNotFoundException {
        User u = userDAO.findUser(signupKey, userId);
        if (u == null) {
            throw new UserNotFoundException(signupKey + "+" + userId);
        }
        // Create the event stream entry
        eventService.createEvent(EventId.READ, u, null);
        return u;
    }

    /**
     * User is already reserved via the #createUserRequest or #createInviteUserRequest calls,
     * this just finishes by firing the event and saving the user again if any changes were made
     *
     * @param user
     * @return
     */
    @Override
    public User createUser(User user) {
        user.setUserStatus(User.UserStatus.ACTIVATED);
        validateUserAlias(user);
        userDAO.save(user);
        // Create the event stream entry
        Event event = eventService.createEvent(EventId.CREATE, user, null);
        // Create a user created message
        messageService.sendNodeableAccountMessage(event, user.getAccount(), null);
        return user;
    }

    /**
     * A new user has requested to sign up. This also sets the AccountOriginator value to true.
     *
     * @param user
     * @return
     * @throws UsernameUnavailableException
     */
    @Override
    public User createUserRequest(User user) throws UsernameUnavailableException {
        if (isUsernameAvailable(user.getUsername())) {
            user.setAccountOriginator(true);
            validateUserAlias(user);
            userDAO.save(user);
        } else {
            throw new UsernameUnavailableException(user.getUsername());
        }
        handleUserRequest(user, false);
        return user;
    }

    @Override
    public void recreateUserRequest(User user) throws UserNotFoundException {
        handleUserRequest(user, true);
    }

    private void handleUserRequest(User user, boolean isRecreate) {
        Map<String, Object> metadata = new HashMap<>();
        metadata.put("userRequestIsNew", !isRecreate);
        // Create the event stream entry
        eventService.createEvent(EventId.CREATE_USER_REQUEST, user, metadata);
    }

    @Override
    public void deleteUserInvite(User user) throws UserNotFoundException {
        userDAO.delete(user);
        // Create the event stream entry
        eventService.createEvent(EventId.DELETE_USER_INVITE_REQUEST, user, null);
    }

    @Override
    public void deleteUsersForAccount(Account account) {
        for (User user : allUsersForAccount(account)) {
            deleteUser(user);
        }
    }

    @Override
    public List<User> allUsersForAccount(Account account) {
        return userDAO.forAccount(account);
    }

    @Override
    /**
     * SOBA-1401 Does not include disabled users (this is our psuedo "deleted" status)
     */
    public List<User> allEnabledUsersForAccount(Account account) {
        return userDAO.allEnabledUsersForAccount(account);
    }

    @Override
    public Set<Role> getUserRoles() {
        Set<Role> roles = new HashSet<>();
        Role r = roleDAO.findRole(Roles.USER_ROLE);
        roles.add(r);
        return roles;
    }

    @Override
    public Set<Role> getAdminRoles() {
        Set<Role> roles = new HashSet<>();
        Role r = roleDAO.findRole(Roles.ADMIN_ROLE);
        roles.add(r);
        r = roleDAO.findRole(Roles.USER_ROLE);
        roles.add(r);
        return roles;
    }

    @Override
    public List<Account> getAccounts() {
        return accountDAO.find().asList();
    }

    @Override
    public void addRole(User user, ObjectId roleId) throws UserNotFoundException {
        Role role = roleDAO.get(roleId);
        // don't allow dupes if the same Id
        if (!(user.getRoles().contains(role))) {
            user.addRole(role);
            updateUser(user);
        }
    }

    @Override
    public void removeRole(User user, ObjectId roleId) throws UserNotFoundException {
        Set<Role> newRoles = new HashSet<>();
        Set<Role> roleSet = user.getRoles();
        for (Role r : roleSet) {
            if (!(r.getId().equals(roleId))) {
                newRoles.add(r);
            }
        }
        user.setRoles(newRoles);
        updateUser(user);
    }

    @Override
    public ObjectId addToEventLog(User user, String name, JSONObject value, Long timestamp) {
        EventLog eventLog = new EventLog.Builder().user(user).keyValue(name, value, timestamp).build();
        eventLogDAO.save(eventLog);
        return eventLog.getId();
        // Fire event?
    }

    @Override
    public void deletePendingUser(User user) {
        userDAO.delete(user);
        // do not fire event, this user has no resources
    }

    /**
     * <p>Returns a copy of a User object representing the Nodeable SuperUser.  A cached copy of the SuperUser user from
     * the data tier is used as the base object from all copies.  Periodically this cached copy is expired and retrieved
     * again from the data tier.</p>
     * <p/>
     * <p>Callers of this method are allowed to modify the User object returned for purposes such as sending a message
     * from the SuperUser into a specific account.  However, instances of the SuperUser User should not be saved with
     * {@link UserService#updateUser(com.streamreduce.core.model.User)} unless the intention is to modify the authoritative
     * persisted SuperUser user.</p>
     *
     * @return A copy of the SuperUser object
     */
    @Override
    public User getSuperUser() {
        try {
            User cachedSuperUser = superUserCache.get("superuser", new Callable<User>() {
                @Override
                public User call() throws Exception {
                    return userDAO.findUser(Constants.NODEABLE_SUPER_USERNAME);
                }
            });
            return new User.Builder(cachedSuperUser).build();
        } catch (ExecutionException e) {
            logger.warn("Exception encountered when accessing superUser from cache. Falling back to UserDAO", e);
            return userDAO.findUser(Constants.NODEABLE_SUPER_USERNAME);
        }
    }

    @Override
    public User getSystemUser() {
        return userDAO.findUser(Constants.NODEABLE_SYSTEM_USERNAME);
    }

    @Override
    public User getAccountAdmin(Account account) {
        List<User> users = allUsersForAccount(account);
        for (User user : users) {
            for (Role role : user.getRoles()) {
                if (role.getName().equals(Roles.ADMIN_ROLE)) {
                    return user;
                }
            }
        }
        return null;
    }

    /**
     * SOBA-1617 -- bootstrap the Metric collections with proper indexes
     *
     * @param account - a valid account
     */
    private void createMetricInbox(Account account) {

        // create a bogus object
        DB db = genericCollectionDAO.getDatabase(DAODatasourceType.MESSAGE);
        DBCollection collection = db.getCollection(MessageUtils.getMetricInboxPath(account));
        BasicDBObject dummyObj = new BasicDBObject();
        collection.insert(dummyObj);

        // add indexes
        collection.ensureIndex("metricGranularity");
        collection.ensureIndex("metricName");

        // remove bogus object
        collection.remove(dummyObj);
    }

    @Override
    public void handleInitialInsightForAccount(Account account) {
        account.setConfigValue(Account.ConfigKey.RECIEVED_INSIGHTS, true);
        updateAccount(account);
    }

    /**
     * Tests that a user alias is filled with only alphanumeric characters, "-", and "_"
     *
     * @param user - The user whose alias is being tested
     * @throws IllegalArgumentException if user.getAlias contains characters other than alphanumeric characters, "_" and
     *                                  "-"
     */
    void validateUserAlias(User user) {
        if (!User.isValidUserAlias(user.getAlias())) {
            throw new InvalidUserAliasException(
                    "User alias contains characters that aren't alphanumeric, dashes, or " + "underscores");
        }
    }
}