Java tutorial
/* * Copyright 2008 Jeff Dwyer * * 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.apress.progwt.server.service.impl; import java.io.Serializable; import java.util.Date; import java.util.List; import net.sf.ehcache.Cache; import net.sf.ehcache.Element; import org.apache.commons.lang.RandomStringUtils; import org.apache.log4j.Logger; import org.openid4java.discovery.DiscoveryException; import org.openid4java.discovery.UrlIdentifier; import org.springframework.beans.factory.annotation.Required; import org.springframework.context.MessageSource; import org.springframework.security.Authentication; import org.springframework.security.AuthenticationManager; import org.springframework.security.context.SecurityContextHolder; import org.springframework.security.providers.UsernamePasswordAuthenticationToken; import org.springframework.security.providers.dao.SaltSource; import org.springframework.security.providers.dao.UserCache; import org.springframework.security.providers.encoding.PasswordEncoder; import org.springframework.security.userdetails.UserDetails; import org.springframework.security.userdetails.UsernameNotFoundException; import org.springframework.transaction.annotation.Transactional; import com.apress.progwt.client.domain.ProcessType; import com.apress.progwt.client.domain.RatingType; import com.apress.progwt.client.domain.User; import com.apress.progwt.client.domain.dto.UserAndToken; import com.apress.progwt.client.exception.BusinessException; import com.apress.progwt.client.exception.SiteException; import com.apress.progwt.server.dao.SchoolDAO; import com.apress.progwt.server.dao.UserDAO; import com.apress.progwt.server.domain.ServerSideUser; import com.apress.progwt.server.service.PermissionDeniedException; import com.apress.progwt.server.service.UserService; import com.apress.progwt.server.web.domain.CreateUserRequestCommand; /** * * * TODO I feel like we shouldn't have to be updating the UserCache * ourselves. Why is this? Right now it's flaky because we need to remove * users too. * * @author Jeff Dwyer * */ @Transactional public class UserServiceImpl implements UserService { /** * match with applicationContext-acegi-security.xml <bean * id="anonymousProcessingFilter"> */ public static final String ANONYMOUS = "anonymousUser"; private static final long CANCELLED_SUBSCRIPTION_ID = 1; private static final Logger log = Logger.getLogger(UserServiceImpl.class); public static final String SAMPLE_TAG_TITLE = "Sample Movies"; public static String normalizeUrl(String username) throws SiteException { if (username == null) { throw new RuntimeException("Invalid openID: " + username); } try { String rtn; // http || https if (username.startsWith("http")) { rtn = UrlIdentifier.normalize(username).toExternalForm(); } else { rtn = UrlIdentifier.normalize("http://" + username).toExternalForm(); } System.out.println("rtn |" + rtn + "|"); if (rtn.equals("http://")) { throw new DiscoveryException("Invalid openID: " + username); } else { return rtn; } } catch (DiscoveryException e) { log.error("Invalid OpenID " + username + " " + e); throw new SiteException("Invalid openID: " + username); } } private int maxUsers; private MessageSource messageSource; private PasswordEncoder passwordEncoder; private SaltSource saltSource; private SchoolDAO schoolDAO; private int startingInvitations; private String tokenSalt; private UserCache userCache; private UserDAO userDAO; private Cache userTokenCache; private AuthenticationManager authMgr; /** * don't let it go negative */ public void addInvitationsTo(User inviter, int num) { int current = inviter.getInvitations(); int newV = current + num; if (newV >= 0) { inviter.setInvitations(newV); } save(inviter); } public void changePassword(String oldPassword, String newPassword) { User user = getCurrentUser(); Authentication oldAuth = new UsernamePasswordAuthenticationToken(user.getUsername(), oldPassword); authMgr.authenticate(oldAuth); createPassWord(user, newPassword); log.debug("password changed, saving"); save(user); log.debug("remove from cache"); userCache.removeUserFromCache(user.getUsername()); log.debug("change security context"); Authentication newAuthentication = new UsernamePasswordAuthenticationToken(user.getUsername(), newPassword); authMgr.authenticate(newAuthentication); SecurityContextHolder.getContext().setAuthentication(newAuthentication); } public boolean couldBeOpenID(String username) { return username.contains(".") || username.contains("="); } public User createUser(CreateUserRequestCommand comm) throws SiteException { // duplicate username as nickname if (comm.isStandard()) { return createUser(comm.getUsername(), comm.getPassword(), comm.getEmail(), comm.getUsername()); } // use nickname different than openid else if (comm.isOpenID()) { return createUser(comm.getOpenIDusernameDoNormalization(), null, comm.getEmail(), comm.getOpenIDnickname()); } else { throw new RuntimeException("Command Neither standard nor open"); } } private User createUser(String username, String userpass, String email, String nickname) throws BusinessException { return createUser(username, userpass, email, false, new Date(), nickname); } public User createUser(String username, String userpass, String email, boolean superV) throws BusinessException { return createUser(username, userpass, email, superV, new Date(), username); } /** * lowercase usernames before creation * * @return * @throws BusinessException */ public User createUser(String username, String userpass, String email, boolean superV, Date dateCreated, String nickname) throws BusinessException { // hmm a bit odd having the logic catch in the // if (log.isDebugEnabled()) { log.debug("u: " + username + " p " + userpass); } User user = new User(); user.setUsername(username.toLowerCase()); user.setNickname(user.getUsername()); user.setEmail(email); user.setSupervisor(superV); user.setEnabled(true); user.setInvitations(startingInvitations); user.setDateCreated(dateCreated); user = save(user); if (userpass != null) { createPassWord(user, userpass); } User createdU = save(user); createdU = setup(createdU); // important. otherwise we were getting directed to the user page // in a logged in, but not // authenticated state, despite our redirect:/index.html SecurityContextHolder.getContext().setAuthentication(null); return createdU; } private void createPassWord(User user, String userpass) { Object salt = saltSource.getSalt(new ServerSideUser(user)); user.setPassword(passwordEncoder.encodePassword(userpass, salt)); } public void delete(Integer id) throws PermissionDeniedException { if (getCurrentUser().isSupervisor()) { User user = userDAO.getUserForId(id); userDAO.delete(user); userCache.removeUserFromCache(user.getUsername()); } else { throw new PermissionDeniedException("You don't have rights to do that."); } } /** * Return if the command has a unique username */ public boolean exists(String username) { try { userDAO.loadUserByUsername(username); return true; } catch (UsernameNotFoundException e) { return false; } } public boolean existsNickname(String nickname) { try { userDAO.getUserByNicknameFetchAll(nickname); return true; } catch (UsernameNotFoundException e) { return false; } } public List<User> getAllUsers() { return userDAO.getAllUsers(); } public User getCurrentUser() throws UsernameNotFoundException { return getCurrentUser(true); } public User getCurrentUser(boolean useCache) throws UsernameNotFoundException { log.debug("getCurrentUser"); Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (null == auth) { throw new UsernameNotFoundException("No Authentications"); } Object obj = auth.getPrincipal(); String username = ""; if (obj instanceof UserDetails) { username = ((UserDetails) obj).getUsername(); } else { username = obj.toString(); } if (username.equals(ANONYMOUS)) { log.debug("Anonymous return null user"); return null; } log.debug("loadUserByUsername " + username); ServerSideUser serverUser = null; if (useCache) { serverUser = (ServerSideUser) userCache.getUserFromCache(username); } User u; if (serverUser == null) { u = userDAO.getUserByUsername(username); userCache.putUserInCache(new ServerSideUser(u)); } else { u = serverUser.getUser(); } return u; } public UserAndToken getCurrentUserAndToken() { User currentUser = getCurrentUser(); return new UserAndToken(currentUser, getToken(currentUser)); } /** * Question, what should we return for user == null? We'll avoid * putting it in the cache, and just return another random string. * There's no way to get this value again and that's probably what we * want. */ public String getToken(User user) { if (user == null) { return RandomStringUtils.randomAscii(10); } Element e = userTokenCache.get(user); if (e != null) { String token = (String) e.getValue(); log.debug("Found existing token for: " + user + " token: " + token); return token; } else { String token = RandomStringUtils.randomAscii(10); log.debug("No existing token for: " + user + " new token: " + token); Element newElement = new Element(user, (Serializable) token); userTokenCache.put(newElement); return token; } } public List<User> getTopUsers(int max) { List<User> users = userDAO.getAllUsers(max); // if(log.isDebugEnabled()){ // for (User user : users) { // log.info(user.getUsername()+" "+user.isSupervisor()); // } // } return users; } public User getUserByNicknameFullFetch(String nickname) { return userDAO.getUserByNicknameFetchAll(nickname); } /** * only openID users are allowed '.' || '=' and all openID usernames * must have a '.' || '=' so, if it's got a '.' || '=' * janrain.normalize() before the lookup * * @throws DiscoveryException */ public User getUserWithNormalization(String username) throws UsernameNotFoundException, SiteException { if (couldBeOpenID(username)) { return userDAO.getUserByUsername(normalizeUrl(username)); // return userDAO.getUserByUsername(com.janrain.openid.Util // .normalizeUrl(username)); } else { return userDAO.getUserByUsername(username); } } private String gm(String messageName) { return messageSource.getMessage(messageName, null, null); } public boolean nowAcceptingSignups() { return userDAO.getUserCount() < maxUsers; } public User save(User user) { User rtn = userDAO.save(user); userCache.removeUserFromCache(rtn.getUsername()); return rtn; } @Required public void setMaxUsers(int maxUsers) { this.maxUsers = maxUsers; } @Required public void setMessageSource(MessageSource messageSource) { this.messageSource = messageSource; } @Required public void setPasswordEncoder(PasswordEncoder passwordEncoder) { this.passwordEncoder = passwordEncoder; } @Required public void setSaltSource(SaltSource saltSource) { this.saltSource = saltSource; } @Required public void setSchoolDAO(SchoolDAO schoolDAO) { this.schoolDAO = schoolDAO; } @Required public void setStartingInvitations(int startingInvitations) { this.startingInvitations = startingInvitations; } private User setup(User createdU) { for (ProcessType processType : schoolDAO.getDefaultProcessTypes()) { createdU.getProcessTypes().add(processType); } for (RatingType ratingType : schoolDAO.getDefaultRatingTypes()) { createdU.getRatingTypes().add(ratingType); } return save(createdU); } @Required public void setAuthMgr(AuthenticationManager authMgr) { this.authMgr = authMgr; } @Required public void setUserCache(UserCache userCache) { this.userCache = userCache; } @Required public void setUserDAO(UserDAO userDAO) { this.userDAO = userDAO; } @Required public void setUserTokenCache(Cache userTokenCache) { this.userTokenCache = userTokenCache; } /** * TODO LOW AOP this security concern */ public void toggleEnabled(Integer id) throws PermissionDeniedException { log.info("toggleEnabled " + getCurrentUser().getUsername() + " " + getCurrentUser().isSupervisor()); if (getCurrentUser().isSupervisor()) { User user = userDAO.getUserForId(id); user.setEnabled(!user.isEnabled()); save(user); } else { throw new PermissionDeniedException("You don't have rights to do that."); } } public void toggleSupervisor(Integer id) throws PermissionDeniedException { if (getCurrentUser().isSupervisor()) { System.out.println("ID " + id); User user = userDAO.getUserForId(id); user.setSupervisor(!user.isSupervisor()); save(user); } else { throw new PermissionDeniedException("You don't have rights to do that."); } } }