Java tutorial
/* * ============================================================= * Copyright (C) 2007-2011 Edgenius (http://www.edgenius.com) * ============================================================= * License Information: http://www.edgenius.com/licensing/edgenius/2.0/ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2.0 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * http://www.gnu.org/licenses/gpl.txt * * **************************************************************** */ package com.edgenius.wiki.gwt.server; import java.util.ArrayList; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TimeZone; import java.util.TreeMap; import javax.servlet.http.HttpSession; import org.apache.commons.lang.RandomStringUtils; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.mail.SimpleMailMessage; import org.springframework.security.captcha.CaptchaServiceProxy; import com.edgenius.core.Constants; import com.edgenius.core.Constants.REGISTER_METHOD; import com.edgenius.core.Global; import com.edgenius.core.SecurityValues; import com.edgenius.core.SecurityValues.OPERATIONS; import com.edgenius.core.SecurityValues.RESOURCE_TYPES; import com.edgenius.core.SecurityValues.SYSTEM_ROLES; import com.edgenius.core.UserSetting; import com.edgenius.core.UserSetting.QuickNote; import com.edgenius.core.model.Permission; import com.edgenius.core.model.Resource; import com.edgenius.core.model.Role; import com.edgenius.core.model.User; import com.edgenius.core.service.MailService; import com.edgenius.core.service.MessageService; import com.edgenius.core.service.RoleService; import com.edgenius.core.service.UserExistsException; import com.edgenius.core.service.UserOverLimitedException; import com.edgenius.core.util.CodecUtil; import com.edgenius.core.util.DateUtil; import com.edgenius.core.util.ServletUtils; import com.edgenius.core.util.TimeZoneUtil; import com.edgenius.core.util.WebUtil; import com.edgenius.wiki.WikiConstants; import com.edgenius.wiki.gwt.client.model.ActivityModel; import com.edgenius.wiki.gwt.client.model.CaptchaCodeModel; import com.edgenius.wiki.gwt.client.model.JsInfoModel; import com.edgenius.wiki.gwt.client.model.PageItemModel; import com.edgenius.wiki.gwt.client.model.PermissionListModel; import com.edgenius.wiki.gwt.client.model.PermissionModel; import com.edgenius.wiki.gwt.client.model.QuickNoteModel; import com.edgenius.wiki.gwt.client.model.RoleListModel; import com.edgenius.wiki.gwt.client.model.RoleModel; import com.edgenius.wiki.gwt.client.model.SpaceModel; import com.edgenius.wiki.gwt.client.model.UserListModel; import com.edgenius.wiki.gwt.client.model.UserModel; import com.edgenius.wiki.gwt.client.model.UserProfileModel; import com.edgenius.wiki.gwt.client.server.SecurityController; import com.edgenius.wiki.gwt.client.server.utils.ErrorCode; import com.edgenius.wiki.gwt.client.server.utils.SharedConstants; import com.edgenius.wiki.gwt.server.handler.GWTSpringController; import com.edgenius.wiki.model.ActivityLog; import com.edgenius.wiki.model.Friend; import com.edgenius.wiki.model.History; import com.edgenius.wiki.model.Page; import com.edgenius.wiki.model.Space; import com.edgenius.wiki.security.UserSignUpDiabledException; import com.edgenius.wiki.security.service.SecurityService; import com.edgenius.wiki.service.ActivityLogService; import com.edgenius.wiki.service.FriendService; import com.edgenius.wiki.service.PageService; import com.edgenius.wiki.service.RenderService; import com.edgenius.wiki.service.SecurityDummy; import com.edgenius.wiki.service.SettingService; import com.edgenius.wiki.service.SpaceService; import com.edgenius.wiki.service.ThemeService; import com.edgenius.wiki.service.TouchService; import com.edgenius.wiki.util.WikiUtil; /** * @author Dapeng.Ni */ @SuppressWarnings("serial") public class SecurityControllerImpl extends GWTSpringController implements SecurityController { private static final Logger log = LoggerFactory.getLogger(SecurityControllerImpl.class); private static final int OWNER_TYPE_ROLE = 1; private static final int OWNER_TYPE_USER = 2; private static final int USER_QUICK_NOTE_MAX_VERSION = 5; private SecurityService securityService; private RenderService renderService; private PageService pageService; private SpaceService spaceService; private ThemeService themeService; private FriendService friendService; private SecurityDummy securityDummy; private RoleService roleService; private MailService mailService; private TouchService touchService; private CaptchaServiceProxy captchaService; private SettingService settingService; private ActivityLogService activityLog; private MessageService messageService; public UserModel getUser(String username) { User user; if (StringUtils.isBlank(username)) { //get current login user: username = ServletUtils.getRequest().getRemoteUser(); } user = userReadingService.getUserByName(username); UserModel model = UserUtil.copyUserToModel(null, user); //user status is not sync in copyUserModel(), sync here then.. model.setStatus(user.getSetting().getStatus()); return model; } public UserModel getUser(Integer userUid) { User user; if (userUid == null || userUid <= 0) { //get current login user: String username = ServletUtils.getRequest().getRemoteUser(); user = userReadingService.getUserByName(username); } else { user = userReadingService.getUser(userUid); } UserModel model = UserUtil.copyUserToModel(null, user); UserUtil.copyContactsToModel(user, model, true, messageService); //user status is not sync in copyUserModel(), sync here then.. model.setStatus(user.getSetting().getStatus()); return model; } public UserListModel getSpaceGroupUsers(String senderSpaceUname, String receiverSpaceUname) { UserListModel model = new UserListModel(); Friend frd = friendService.getFriendship(senderSpaceUname, receiverSpaceUname); if (frd == null) { model.errorCode = ErrorCode.FRIEND_NOT_MAKE; } else if (frd.isPending()) { model.errorCode = ErrorCode.FRIEND_PENDING; } else if (frd.isRejected()) { model.errorCode = ErrorCode.FRIEND_REJECT; } if (!StringUtils.isBlank(model.errorCode)) { return model; } List<User> users = friendService.getSpaceGroupUsers(receiverSpaceUname); for (User user : users) { model.add(UserUtil.copyUserToModel(null, user)); } return model; } public String savePassword(Integer userUid, String password) { User user = null; if (userUid == null || userUid <= 0) { // get current login user: user = WikiUtil.getUser(); //reload from database again user = userReadingService.getUser(user.getUid()); } else { //sys admin should use this one user = userReadingService.getUser(userUid); } if (user != null && !user.isAnonymous()) { if (Global.EncryptPassword) { String algorithm = Global.PasswordEncodingAlgorithm; if (algorithm == null) { algorithm = "MD5"; } user.setPassword(CodecUtil.encodePassword(password, algorithm)); } else user.setPassword(password); userService.updateUser(user); return null; } return "failed"; } public UserModel saveProfile(UserModel model) { User user = null; String username = ServletUtils.getRequest().getRemoteUser(); if (model.getUid() == null || model.getUid() <= 0) { // get current login user: user = userReadingService.getUserByName(username); if (user != null) { //reload from database again user = userReadingService.getUser(user.getUid()); } } else { user = userReadingService.getUser(model.getUid()); } if (user != null) { //check if it is current login user if (StringUtils.equalsIgnoreCase(username, user.getUsername())) { model.setLogin(true); } else { model.setLogin(false); } //profile does not update username,password and uid UserUtil.copyProfileToUser(model, user); //also update user Lucene index userService.updateUserWithIndex(user); //need update setting as UserSettting.contact is updated. settingService.saveOrUpdateUserSetting(user, user.getSetting()); //copy back - as UserSettting.contact may need to sort and group UserUtil.copyContactsToModel(user, model, true, messageService); //user status is not sync in copyUserModel(), sync here then.. model.setStatus(user.getSetting().getStatus()); return model; } return model; } /** * New user Sign up */ public UserModel createUser(UserModel model, boolean login) { if (!WikiUtil.captchaValid(captchaService, model)) return null; User user = new User(); UserUtil.copyModelToUser(model, user); String plainPassword = user.getPassword(); if (Global.EncryptPassword) { String algorithm = Global.PasswordEncodingAlgorithm; if (algorithm == null) { // should only happen for test case if (log.isDebugEnabled()) { log.debug("assuming testcase, setting algorithm to 'MD5'"); } algorithm = "MD5"; } user.setPassword(CodecUtil.encodePassword(user.getPassword(), algorithm)); } //now, only signup and approval 2 method, here treat signup method is as default method. //So only Global.registerMethod=="approval", the enable is false, otherwise, user always be enabled. boolean enable = !REGISTER_METHOD.approval.name().equals(Global.registerMethod); user.setEnabled(enable); // Set the default user role on this new user user.setRoles(roleService.getDefaultRole()); user.setCreatedDate(new Date()); try { userService.saveUser(user); //for compatible, not directly use Global.registerMethod(if it is null, treat as signup) activityLog.logUserSignup(user, enable ? REGISTER_METHOD.signup : REGISTER_METHOD.approval); //set back user uid model.setUid(user.getUid()); if (login && enable) { //login flag use for admin: if admin add user, then need not do login and send account email securityService.login(user.getUsername(), model.getPassword()); } try { // Send create account email SimpleMailMessage msg = new SimpleMailMessage(); msg.setFrom(Global.DefaultNotifyMail); msg.setTo(user.getContact().getEmail()); Map<String, Object> map = new HashMap<String, Object>(); map.put(WikiConstants.ATTR_USER, user); map.put(WikiConstants.ATTR_PAGE_LINK, WebUtil.getHostAppURL()); if (login) { if (enable) { mailService.sendPlainMail(msg, WikiConstants.MAIL_TEMPL_SIGNUP_NOTIFICATION, map); } else { //if user needs approval, then send waiting approval email UserSetting setting = user.getSetting(); setting.setRequireSignupApproval(true); settingService.saveOrUpdateUserSetting(user, setting); mailService.sendPlainMail(msg, WikiConstants.MAIL_TEMPL_SIGNUP_WAIT_APPROVAL_USER, map); mailService.sendPlainToSystemAdmins(WikiConstants.MAIL_TEMPL_SIGNUP_WAIT_APPROVAL_ADMIN, map); //redirect - if user signup from popup dialog, this "force" will cause page redirect to approval page, //otherwise, it only try to refresh current page URL by GwtClientUtil.reload(); model.setForceRedirerct(true); model.setRedirUrl("signupwaitapproval.do"); } } else { //this is admin add new user, send user name and password as well. map.put(WikiConstants.ATTR_PASSWORD, plainPassword); mailService.sendPlainMail(msg, WikiConstants.MAIL_TEMPL_USER_ADDED_NOTIFICATION, map); } log.info("Email sent to " + user.getFullname() + " for signup notify."); } catch (Exception e) { log.error("Failed to send sign-up email:" + user.getContact().getEmail(), e); } } catch (UserExistsException e) { log.warn(e.getMessage()); // redisplay the unencrypted passwords user.setPassword(user.getConfirmPassword()); model.errorCode = ErrorCode.USER_ALREADY_EXIST_ERR; } catch (UserSignUpDiabledException e) { log.warn(e.getMessage()); model.errorCode = ErrorCode.USER_SIGNUP_DISABLED; } catch (UserOverLimitedException e) { log.error("User is over license limitation.", e); model.errorCode = ErrorCode.USER_OVER_LIMITED; } return model; } /** * Get all roles, users and anonymous user permission matrix. For security Panel display. */ public PermissionListModel getResourcePermissions(int resourceTypeOrdinal, String resourceName) { PermissionListModel listModel = new PermissionListModel(); RESOURCE_TYPES resourceType = RESOURCE_TYPES.values()[resourceTypeOrdinal]; //Please note: this method actually call by {macro:friends}, if user without admin permission use this macro //in page, and this user does not have admin permission. It may lead the whole page can not be access anymore if (resourceType == RESOURCE_TYPES.INSTANCE) { //check if current user has instance admin permission securityDummy.checkInstanceAdmin(); } else if (resourceType == RESOURCE_TYPES.SPACE) { securityDummy.checkSpaceAdmin(resourceName); } else if (resourceType == RESOURCE_TYPES.PAGE) { Page page = pageService.getCurrentPageByUuid(resourceName); if (page == null) return null; securityDummy.checkPageRestrict(page.getSpace().getUnixName(), resourceName); } Map<PermissionModel, PermissionModel> permSet = new HashMap<PermissionModel, PermissionModel>(); boolean foundAnonymous = false; List<Permission> permList = securityService.getResourcePermission(resourceType, resourceName); // So far does not user Mask concept, it is too complex for common user. // if(resourceType == RESOURCE_TYPES.PAGE){ // String masks = securityService.getResourceMasks(resourceName); // PermissionModel model = new PermissionModel(); // model.mask = true; // //for hashcode use // model.ownerUid = "-999"; // model.ownerType = -999; // if(!StringUtils.isBlank(masks)){ // String[] maskIdxStr = masks.split(","); // int[] maskIdx = new int[maskIdxStr.length]; // for (int idx =0;idx<maskIdxStr.length;idx++) { // maskIdx[idx] = NumberUtils.toInt(maskIdxStr[idx], -1); // } // for (int idx : maskIdx) { // if(idx == -1) // continue; // model.operations[idx] = true; // } // }else{ // //default, all masks are true // Arrays.fill(model.operations, true); // } // permSet.put(model, model); // } int resType = -1; for (Permission permission : permList) { if (resType == -1) //initial value at first time during loop resType = permission.getResource().getType().ordinal(); Set<Role> roles = permission.getRoles(); List<String> dead = permission.getDeadRoleUserList(); if (roles != null) { for (Role role : roles) { if (SYSTEM_ROLES.ANONYMOUS.getName().equalsIgnoreCase(role.getName())) { foundAnonymous = true; } if (SYSTEM_ROLES.ADMIN.getName().equalsIgnoreCase(role.getName())) { //if system does allow show admin permission control, then skip all permission for role of Admin. if (!Global.EnableAdminPermControl) { continue; } } PermissionModel model = new PermissionModel(); model.ownerType = OWNER_TYPE_ROLE; model.ownerName = role.getName(); model.ownerDisplayName = role.getDisplayName(); model.ownerDesc = role.getDescription(); model.resourceType = resType; model.roleType = role.getType(); //get same owner's permission PermissionModel exist = permSet.get(model); if (exist != null) { //update exist, don't create new one: to group same role/user's permission exist.operations[permission.getOperation().ordinal()] = true; if (dead != null && dead.contains(role.getName())) exist.dead[permission.getOperation().ordinal()] = true; else exist.dead[permission.getOperation().ordinal()] = false; } else { model.operations[permission.getOperation().ordinal()] = true; if (dead != null && dead.contains(role.getName())) model.dead[permission.getOperation().ordinal()] = true; else model.dead[permission.getOperation().ordinal()] = false; model.resourceName = resourceName; permSet.put(model, model); } } } Set<User> users = permission.getUsers(); if (users != null) { for (User user : users) { PermissionModel model = new PermissionModel(); model.ownerType = OWNER_TYPE_USER; model.ownerName = user.getUsername(); model.ownerDisplayName = user.getFullname(); model.ownerDesc = user.getFullname(); model.resourceType = resType; PermissionModel exist = permSet.get(model); if (exist != null) { exist.operations[permission.getOperation().ordinal()] = true; if (dead != null && dead.contains(Role.USER_PREFIX + user.getUsername())) exist.dead[permission.getOperation().ordinal()] = true; else exist.dead[permission.getOperation().ordinal()] = false; } else { model.operations[permission.getOperation().ordinal()] = true; if (dead != null && dead.contains(Role.USER_PREFIX + user.getUsername())) model.dead[permission.getOperation().ordinal()] = true; else model.dead[permission.getOperation().ordinal()] = false; model.resourceName = resourceName; permSet.put(model, model); } } } } //always show anonymous group for SPACE and INSTANCE! if (permList.size() > 0 && !foundAnonymous && resourceType != RESOURCE_TYPES.PAGE) { Role anony = roleService.getRoleByName(SecurityValues.SYSTEM_ROLES.ANONYMOUS.getName()); PermissionModel model = new PermissionModel(); model.ownerType = OWNER_TYPE_ROLE; model.ownerName = anony.getName(); model.roleType = anony.getType(); model.ownerDisplayName = anony.getDisplayName(); model.ownerDesc = anony.getDescription(); model.resourceName = resourceName; model.resourceType = resType; permSet.put(model, model); } listModel.list = new ArrayList<PermissionModel>(permSet.values()); return listModel; } /** * */ public PermissionListModel updatePermission(int resourceTypeOrdinal, ArrayList<PermissionModel> changedModelList) { if (changedModelList == null || changedModelList.size() == 0) return null; String resourceName = changedModelList.get(0).resourceName; RESOURCE_TYPES resourceType = RESOURCE_TYPES.values()[resourceTypeOrdinal]; if (resourceType == RESOURCE_TYPES.INSTANCE) { //check if current user has instance admin permission securityDummy.checkInstanceAdmin(); } else if (resourceType == RESOURCE_TYPES.SPACE) { securityDummy.checkSpaceAdmin(resourceName); } else if (resourceType == RESOURCE_TYPES.PAGE) { Page page = pageService.getCurrentPageByUuid(resourceName); if (page == null) return null; securityDummy.checkPageRestrict(page.getSpace().getUnixName(), resourceName); } //!!!Use set so that remove duplicated user/role name Set<String> roleNames = new HashSet<String>(); Set<String> userNames = new HashSet<String>(); String masks = ""; //find out unique role/user from changeModelList, which is for each operation on each role/user for (PermissionModel model : changedModelList) { if (model.mask) { masks += model.operation + ","; } else if (model.ownerType == OWNER_TYPE_ROLE) { roleNames.add(model.ownerName); } else { userNames.add(model.ownerName); } } //masks: currently it is useless if (masks.length() != 0) { Resource res = securityService.getResourceByName(resourceName, resourceType); if (res == null) { res = securityService.saveResource(resourceName, resourceType); } res.setMasks(masks); securityService.updateResource(res); } if (resourceType == RESOURCE_TYPES.SPACE) { //check friendship between new added role and this space role //NOTE: resourceName is spaceUname if resourceType is RESOURCE_TYPES.SPACE friendService.makeFriendsWithSpaceGroups(resourceName, new ArrayList<String>(roleNames)); } boolean dirty = false; //some roles permission have potential updated, so here update them. for (String roleName : roleNames) { Role role = roleService.getRoleByName(roleName); //get this role all permission, and update according to incoming values Set<Permission> perm = role.getPermissions(); dirty = updatePermission(changedModelList, perm, null, role); roleService.saveRole(role); if (isUpdateInstance(changedModelList, null, role)) { //users may be in UserCache. And if any permission modified, these modification must be immediately //update to those cached users. Here will refresh permissions if (SYSTEM_ROLES.ANONYMOUS.getName().equalsIgnoreCase(role.getName())) { //for anonymous user, it has a user in useCache as well, its username is SYSTEM_ROLES.ANONYMOUS.getName() or just null userService.removeUserFromCache(WikiUtil.getAnonymous()); } else if (SYSTEM_ROLES.USERS.getName().equals(role.getName())) { //here is performance consideration: user of ROLE registered may be huge number, just reset all user cacahe then. userService.removeUserFromCache(null); } else { Set<User> users = role.getUsers(); if (users != null) { if (users.size() > 1000) { //still performance consideratino, too many users in this role, then reset userCache to empty anyway. userService.removeUserFromCache(null); } else { for (User user : users) { userService.removeUserFromCache(user); } } } } } } //some users permission have potential updated, so here update them. for (String username : userNames) { User user = userReadingService.getUserByName(username); if (user != null) { //user may from cache, here do reload from DB user = userReadingService.getUser(user.getUid()); Set<Permission> perm = user.getPermissions(); dirty = updatePermission(changedModelList, perm, user, null); //skip UserMethodBeforeAdvise to do security check: as space admin may not instance admin, //and not the user whose permission is updated. userService.interalUpdateUser(user); if (isUpdateInstance(changedModelList, user, null)) { //User value should be in UserCache. Here get user and refresh its instancePermission, then put back to cache. userService.removeUserFromCache(user); } } } //At this case that resource is page. The resource will be new one if first time update. //Any permission won't be reflect to this resource. It means resource can not get back corresponding Permission //so, here must re-load(refresh) Resource object from database. //!!! This method also need call before securityService.resetSpaceReadingCache() and resetPageReadingCache(); Resource resource = securityService.getResourceByName(resourceName, resourceType); securityService.refreshResource(resource); if (resourceType == RESOURCE_TYPES.SPACE) { //so far, resource already is with latest users/roles list, so refresh //resourceName is spaceUname if resourceType is RESOURCE_TYPES.SPACE friendService.refreshSpaceGroupUsers(resource); } //some permission updated, refresh permission cache if (dirty) { securityService.resetPolicyCache(resourceType, resourceName); if (resourceType == RESOURCE_TYPES.SPACE || resourceType == RESOURCE_TYPES.PAGE) { //need check if update spaceReadingCache for (Iterator<PermissionModel> iter = changedModelList.iterator(); iter.hasNext();) { PermissionModel model = iter.next(); if (model.operation == OPERATIONS.READ.ordinal()) { if (resourceType == RESOURCE_TYPES.SPACE) { securityService.resetSpaceReadingCache(resourceName); } else if (resourceType == RESOURCE_TYPES.PAGE) { securityService.resetPageReadingCache(resourceName); } break; } } //update SensitiveTouchInfo if (resourceType == RESOURCE_TYPES.SPACE) { touchService.touchSpace(resourceName); } else if (resourceType == RESOURCE_TYPES.PAGE) { touchService.touchPage(resourceName); } } } return getResourcePermissions(resourceTypeOrdinal, resourceName); } public UserModel checkLogin(JsInfoModel jsModel) { User user = WikiUtil.getUser(); UserModel userModel = UserUtil.copyUserToModel(null, user); if (user.isAnonymous() || user.getSetting().getTimeZone() == null) { //jsModel.timezoneOffset format is hour.minutes, such as 6.30 TimeZone timezone = TimeZoneUtil.guessTimeZone(jsModel.timezoneOffset); HttpSession session = WebUtil.getRequest().getSession(); session.setAttribute(Constants.TIMEZONE, timezone); log.info("User timezone is set to {} with timezone offset {}", timezone.getDisplayName(), jsModel.timezoneOffset); } return userModel; } public RoleModel saveRole(String name, String desc) { name = StringUtils.trimToEmpty(name); RoleModel model = new RoleModel(); Role role = null; //need check if this role already exist role = roleService.getRoleByName(Role.GROUP_ROLE_PREFIX + name.toUpperCase()); if (role == null) { //it is better won't ask user create system default role name, although it is allowed in database role = roleService.getRoleByName(Role.SYSTEM_ROLE_PREFIX + name.toUpperCase()); } if (role != null) { model.errorCode = ErrorCode.ROLE_NAME_CONFLICT_ERR; return model; } role = new Role(); role.setName(Role.GROUP_ROLE_PREFIX + name.toUpperCase()); role.setType(Role.TYPE_GROUP); role.setDisplayName(name); role.setDescription(desc); WikiUtil.setTouchedInfo(userReadingService, role); roleService.createRole(role); model.setUid(role.getUid()); model.setName(role.getName()); model.setDisplayName(name); model.setDesc(desc); return model; } public RoleListModel getRoleList(int roleType) { List<Role> roles = roleService.getRoles(roleType, null); List<RoleModel> modelList = new ArrayList<RoleModel>(); for (Role role : roles) { //if system does allow show admin permission control, then skip all role of Admin. if (SYSTEM_ROLES.ADMIN.getName().equalsIgnoreCase(role.getName())) { if (!Global.EnableAdminPermControl) { continue; } } modelList.add(UserUtil.copyRoleToModel(role)); } RoleListModel model = new RoleListModel(); model.total = roles.size(); model.addAll(modelList); return model; } public Integer addUsersToRole(int roleUid, ArrayList<String> usernameList) { if (usernameList == null || usernameList.size() == 0) { return null; } Role role = roleService.getRole(roleUid); if (role == null) return null; boolean needSave = false; for (String username : usernameList) { User user = userReadingService.getUserByName(username); if (user != null) { //as above user maybe come from cache, so read it again to confirm it is Hibernate PO user = userReadingService.getUser(user.getUid()); Set<User> users = role.getUsers(); boolean has = false; for (User existUser : users) { if (existUser.equals(user)) { has = true; break; } } if (!has) { user.getRoles().add(role); users.add(user); needSave = true; } //It needs reset user from user cache, otherwise, user roles won't be updated userReadingService.removeUserFromCache(user); } } if (needSave) { roleService.saveRole(role); return roleUid; } else { return null; } } //JDK1.6 @Override public UserProfileModel getUserContributed(String username, int type) { User viewer = WikiUtil.getUser(); UserProfileModel profile = new UserProfileModel(); if (type == 0 || (type & SharedConstants.SPACE) > 0) { //this indicate to client side to say spaceList is refreshed. profile.spaces = new ArrayList<SpaceModel>(); List<Space> spaces = spaceService.getUserAllCreatedSpaces(username, -1, viewer); for (Space space : spaces) { SpaceModel model = new SpaceModel(); SpaceUtil.copySpaceToModel(space, model, viewer, themeService); profile.spaces.add(model); } } //Now, history and page are same if (type == 0 || (type & SharedConstants.PAGE) > 0 || (type & SharedConstants.HISTORY) > 0) { //this indicate to client side to say pageList is refreshed. profile.pages = new ArrayList<PageItemModel>(); List<Page> pages = pageService.getUserAllContributedPages(username, -1, viewer); List<History> histories = pageService.getUserAllContributedHistories(username, viewer); Map<String, PageItemModel> map = new HashMap<String, PageItemModel>(); //merge them by pageUUID for (Page page : pages) { PageItemModel item = map.get(page.getPageUuid()); if (item == null) { item = PageUtil.copyToPageItem(page); //sort versionHistory creator->2,...5->current etc. item.versionHistory = new HashMap<Integer, PageItemModel>(); map.put(page.getPageUuid(), item); } if ((page.getCreator() != null && StringUtils.equals(page.getCreator().getUsername(), username)) || (page.getCreator() == null && (username == null || User.ANONYMOUS_USERNAME.equalsIgnoreCase(username)))) { PageItemModel ver = new PageItemModel(); ver.uid = page.getUid(); ver.modifiedDate = DateUtil.getLocalDate(viewer, page.getCreatedDate()); item.versionHistory.put(0, ver); } if ((page.getModifier() != null && StringUtils.equals(page.getModifier().getUsername(), username)) || (page.getModifier() == null && (username == null || User.ANONYMOUS_USERNAME.equalsIgnoreCase(username)))) { PageItemModel ver = new PageItemModel(); ver.uid = page.getUid(); ver.modifiedDate = DateUtil.getLocalDate(viewer, page.getModifiedDate()); item.versionHistory.put(Integer.MAX_VALUE, ver); } } for (History history : histories) { PageItemModel item = map.get(history.getPageUuid()); if (item == null) { item = PageUtil.copyToPageItem(history); item.versionHistory = new HashMap<Integer, PageItemModel>(); map.put(history.getPageUuid(), item); } PageItemModel ver = new PageItemModel(); ver.uid = history.getUid(); ver.modifiedDate = DateUtil.getLocalDate(viewer, history.getModifiedDate()); item.versionHistory.put(history.getVersion(), ver); } //default sort pages by space name TreeMap<String, PageItemModel> sortedPages = new TreeMap<String, PageItemModel>( new Comparator<String>() { public int compare(String o1, String o2) { int ret = o1.compareTo(o2); //don't overwrite same spaceUname pages return ret == 0 ? 1 : ret; } }); for (PageItemModel item : map.values()) { sortedPages.put(item.spaceUname, item); } profile.pages = new ArrayList<PageItemModel>(sortedPages.values()); } if (type == 0 || (type & SharedConstants.ACTIVITY) > 0) { //this indicate to client side to say spaceList is refreshed. profile.activities = new ArrayList<ActivityModel>(); User user = userReadingService.getUserByName(username); //TODO: now only get first 15 activities; List<ActivityLog> msgs = activityLog.getUserActivities(0, 15, user, WikiUtil.getUser()); for (ActivityLog msg : msgs) { ActivityModel act = new ActivityModel(); act.activity = msg.getMessage(); profile.activities.add(act); } } return profile; } public UserProfileModel getUserProfile(String username) { User viewer = WikiUtil.getUser(); UserProfileModel profile = new UserProfileModel(); User user = userReadingService.getUserByName(username); profile.profile = UserUtil.copyUserToModel(viewer, user); //user status is not sync in copyUserModel(), sync here then.. profile.profile.setStatus(user.getSetting().getStatus()); //hide private contact info UserUtil.copyContactsToModel(user, profile.profile, false, messageService); if (user.isAnonymous() || user.equals(WikiUtil.getUser()) || WikiUtil.getUser().isAnonymous()) { profile.profile.setFollowing(-1); } else { profile.profile.setFollowing(userReadingService.isFollowing(viewer, user) ? 1 : 0); } //get user network - need refresh from database again as user cache doesn't save followers/following user = userReadingService.getUser(user.getUid()); List<User> followers = user.getFollowers(); if (followers != null) { for (User fer : followers) { profile.followers.add(UserUtil.copyUserToModel(viewer, fer)); } } List<User> following = user.getFollowings(); if (following != null) { for (User fer : following) { profile.following.add(UserUtil.copyUserToModel(viewer, fer)); } } return profile; } //JDK1.6 @Override public int sendForgetPassword(String email) { User user = userReadingService.getUserByEmail(email); if (user == null) { //email does not exist return SharedConstants.RET_NO_EMAIL; } //reset this user password and send out String newPass = RandomStringUtils.randomAlphabetic(8); //so far, it is plain text - just for email body. It will be encrypted in UserSerivce.resetPassword() String plainPass = newPass; user.setPassword(newPass); //TODO: Does it need warranty only email send out, the password can be reset? //if so, I need change 2 things. Use mailEngine instead of mailService, change mailEngine throw exception //rather than catch all Exception userService.resetPassword(user, newPass); //after above method, the password is encrypted one, so need keep plain one and send email //send email try { // Send create account email SimpleMailMessage msg = new SimpleMailMessage(); msg.setFrom(Global.DefaultNotifyMail); msg.setTo(user.getContact().getEmail()); Map<String, Object> map = new HashMap<String, Object>(); map.put(WikiConstants.ATTR_PASSWORD, plainPass); map.put(WikiConstants.ATTR_USER, user); mailService.sendPlainMail(msg, WikiConstants.MAIL_TEMPL_FORGET_PASSWORD_NOTIFICATION, map); log.info("Email sent to " + user.getFullname() + " for password reset."); } catch (Exception e) { log.error("Failed to send reset passowrd email:" + user.getContact().getEmail(), e); return SharedConstants.RET_SEND_MAIL_FAILED; } return 0; } public int captchaValid(CaptchaCodeModel captcha) { if (!WikiUtil.captchaValid(captchaService, captcha)) return -1; return 0; } public boolean saveUserStatus(Integer userUid, String text) { User user = userReadingService.getUser(userUid); if (user != null) { UserSetting setting = user.getSetting(); setting.setStatus(text); settingService.saveOrUpdateUserSetting(user, setting); activityLog.logUserStatusUpdate(user); return true; } return false; } public QuickNoteModel saveUserQuickNote(String content) { User user = WikiUtil.getUser(); QuickNoteModel model = new QuickNoteModel(); if (!user.isAnonymous()) { UserSetting setting = user.getSetting(); List<QuickNote> notes = setting.getQuickNotes(); int version = 1; if (notes == null || notes.size() == 0) { notes = new ArrayList<QuickNote>(); setting.setQuickNotes(notes); } else { for (QuickNote note : notes) { if (version < note.getVersion()) { //get max version number version = note.getVersion(); } } version++; int diff = notes.size() - USER_QUICK_NOTE_MAX_VERSION + 1; //remove old version for (Iterator<QuickNote> iter = notes.iterator(); iter.hasNext() && diff > 0; diff--) { iter.next(); iter.remove(); } } QuickNote note = new QuickNote(); note.setCreateDate(new Date()); note.setNote(content); note.setVersion(version); notes.add(note); settingService.saveOrUpdateUserSetting(user, setting); log.info("User {} quick note is saved", user.getUsername()); model.renderContent = renderService.renderHTML(content); model.content = content; model.version = version; model.createDate = note.getCreateDate(); } return model; } public UserModel followUser(String username, boolean following) { User user = userReadingService.getUserByName(username); User myself = WikiUtil.getUser(); UserModel model = UserUtil.copyUserToModel(myself, user); if (following) { userService.follow(myself, user); model.setFollowing(1); } else { userService.unfollow(myself, user); model.setFollowing(0); } return model; } //******************************************************************** // private methods //******************************************************************** /* * Check if given user/role's instance permission has been updated in this request <code>changedModelList</code> */ private boolean isUpdateInstance(List<PermissionModel> changedModelList, User user, Role role) { boolean dirty = false; //retrieve all new changed operations for this role for (Iterator<PermissionModel> iter = changedModelList.iterator(); iter.hasNext();) { PermissionModel model = iter.next(); if (model.resourceType != RESOURCE_TYPES.INSTANCE.ordinal()) continue; //skip other permission not belong to this user/role if (user != null) { if (model.ownerType != OWNER_TYPE_USER || !model.ownerName.equalsIgnoreCase(user.getUsername())) continue; } else if (role != null) { if (model.ownerType != OWNER_TYPE_ROLE || !model.ownerName.equalsIgnoreCase(role.getName())) continue; } dirty = true; break; } return dirty; } /* * Update special user or role permissions */ private boolean updatePermission(List<PermissionModel> changedModelList, Set<Permission> perm, User user, Role role) { boolean dirty = false; //retrieve all new changed operations for this role for (Iterator<PermissionModel> iter = changedModelList.iterator(); iter.hasNext();) { PermissionModel model = iter.next(); //skip other permission not belong to this user/role if (user != null) { if (model.ownerType != OWNER_TYPE_USER || !model.ownerName.equalsIgnoreCase(user.getUsername())) continue; } else if (role != null) { if (model.ownerType != OWNER_TYPE_ROLE || !model.ownerName.equalsIgnoreCase(role.getName())) continue; } log.info("change model :" + model); boolean found = false; Resource resource = null; //compare with existed operations, if found, then it means this operation status must switch off, must remove for (Iterator<Permission> permIter = perm.iterator(); permIter.hasNext();) { Permission permission = permIter.next(); if (model.operation == permission.getOperation().ordinal() && model.resourceType == permission.getResource().getType().ordinal() && StringUtils .equalsIgnoreCase(model.resourceName, permission.getResource().getResource())) { permIter.remove(); dirty = true; if (user != null) { log.info("User " + user.getUsername() + " remove permission: " + permission); Set<User> users = permission.getUsers(); for (Iterator<User> uIter = users.iterator(); uIter.hasNext();) { if (user.getUid().equals(uIter.next().getUid())) { uIter.remove(); } } } else if (role != null) { log.info("Role " + role.getName() + " remove permission:" + permission); Set<Role> roles = permission.getRoles(); for (Iterator<Role> rIter = roles.iterator(); rIter.hasNext();) { if (role.getUid().equals(rIter.next().getUid())) { rIter.remove(); } } } found = true; // removed , not necessary update anymore // securityService.saveUpdatePermission(permission); break; } } //if could not find the operation, it means a new operation is tick (space,instance) on or off(page): create new permission if (!found) { OPERATIONS operation = getOperation(model.operation); Permission newPerm = securityService.getPermissionByOperationResource(operation, model.resourceName); if (newPerm == null) { //only page,otherwise, Permission must already exists newPerm = new Permission(); newPerm.setOperation(operation); //for this iterator loop, it must be same resource, so just get once resource = securityService.getResourceByName(model.resourceName, RESOURCE_TYPES.values()[model.resourceType]); if (resource == null) { resource = securityService.saveResource(model.resourceName, RESOURCE_TYPES.values()[model.resourceType]); } newPerm.setResource(resource); } if (user != null) { Set<User> users = newPerm.getUsers(); if (users == null) users = new HashSet<User>(); users.add(user); newPerm.setUsers(users); log.info("User " + user.getUsername() + " add permission:" + newPerm); } else if (role != null) { Set<Role> roles = newPerm.getRoles(); if (roles == null) roles = new HashSet<Role>(); roles.add(role); newPerm.setRoles(roles); log.info("Role " + role.getName() + " add permission:" + newPerm); } dirty = true; securityService.saveUpdatePermission(newPerm); perm.add(newPerm); } } return dirty; } /** * @param operation * @return */ private OPERATIONS getOperation(int operation) { for (SecurityValues.OPERATIONS oper : SecurityValues.OPERATIONS.values()) { if (oper.ordinal() == operation) return oper; } return null; } //******************************************************************** // Set / Get //******************************************************************** public void setSecurityService(SecurityService securityService) { this.securityService = securityService; } public void setRoleService(RoleService roleService) { this.roleService = roleService; } public void setCaptchaService(CaptchaServiceProxy captchaService) { this.captchaService = captchaService; } public void setMailService(MailService mailService) { this.mailService = mailService; } public void setSecurityDummy(SecurityDummy securityDummy) { this.securityDummy = securityDummy; } public void setFriendService(FriendService friendService) { this.friendService = friendService; } public TouchService getTouchService() { return touchService; } public void setTouchService(TouchService touchService) { this.touchService = touchService; } public void setPageService(PageService pageService) { this.pageService = pageService; } public void setActivityLog(ActivityLogService activityLog) { this.activityLog = activityLog; } public void setSpaceService(SpaceService spaceService) { this.spaceService = spaceService; } public void setThemeService(ThemeService themeService) { this.themeService = themeService; } /** * @param settingService the settingService to set */ public void setSettingService(SettingService settingService) { this.settingService = settingService; } public void setRenderService(RenderService renderService) { this.renderService = renderService; } public void setMessageService(MessageService messageService) { this.messageService = messageService; } }