/*******************************************************************************
* Copyright (c) 2013 aegif.
*
* This file is part of NemakiWare.
*
* NemakiWare is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* NemakiWare 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 NemakiWare.
* If not, see <http://www.gnu.org/licenses/>.
*
* Contributors:
* linzhixing(https://github.com/linzhixing) - initial API and implementation
******************************************************************************/
package jp.aegif.nemaki.rest;
import java.text.SimpleDateFormat;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import jp.aegif.nemaki.businesslogic.PrincipalService;
import jp.aegif.nemaki.common.ErrorCode;
import jp.aegif.nemaki.model.User;
import jp.aegif.nemaki.util.AuthenticationUtil;
import jp.aegif.nemaki.util.PropertyManager;
import jp.aegif.nemaki.util.constant.PropertyKey;
import org.apache.chemistry.opencmis.commons.server.CallContext;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.mindrot.jbcrypt.BCrypt;
import org.springframework.stereotype.Component;
@Component
@Path("/repo/{repositoryId}/user/")
public class UserResource extends ResourceBase {
PrincipalService principalService;
public void setPrincipalService(PrincipalService principalService) {
this.principalService = principalService;
}
SolrResource solrResource;
public void setSolrResource(SolrResource value){
this.solrResource = value;
}
private PropertyManager propertyManager;
public void setPropertyManager(PropertyManager propertyManager) {
this.propertyManager = propertyManager;
}
@SuppressWarnings("unchecked")
@GET
@Path("/list")
@Produces(MediaType.APPLICATION_JSON)
public String list(@PathParam("repositoryId") String repositoryId) {
boolean status = true;
JSONObject result = new JSONObject();
JSONArray listJSON = new JSONArray();
JSONArray errMsg = new JSONArray();
// Get all users list
List<User> userList;
try {
userList = principalService.getUsers(repositoryId);
for (User user : userList) {
JSONObject userJSON = convertUserToJson(user);
listJSON.add(userJSON);
}
result.put("users", listJSON);
} catch (Exception e) {
status = false;
e.printStackTrace();
addErrMsg(errMsg, ITEM_ALLUSERS, ErrorCode.ERR_LIST);
}
result = makeResult(status, result, errMsg);
return result.toJSONString();
}
@SuppressWarnings("unchecked")
@GET
@Path("/show/{id}")
@Produces(MediaType.APPLICATION_JSON)
public String show(@PathParam("repositoryId") String repositoryId, @PathParam("id") String userId) {
boolean status = true;
JSONObject result = new JSONObject();
JSONArray errMsg = new JSONArray();
// Validation
if (StringUtils.isBlank(userId)) {
status = false;
addErrMsg(errMsg, ITEM_USERID, ErrorCode.ERR_MANDATORY);
}
User user = principalService.getUserById(repositoryId, userId);
if (user == null) {
status = false;
addErrMsg(errMsg, ITEM_USER, ErrorCode.ERR_NOTFOUND);
} else {
result.put("user", convertUserToJson(user));
}
result = makeResult(status, result, errMsg);
return result.toJSONString();
}
/**
* Search user by id TODO Use Solr
*
* @param query
* @return
*/
@SuppressWarnings("unchecked")
@GET
@Path("/search")
@Produces(MediaType.APPLICATION_JSON)
public String search(@PathParam("repositoryId") String repositoryId, @QueryParam("query") String query) {
boolean status = true;
JSONObject result = new JSONObject();
JSONArray errMsg = new JSONArray();
List<User> users;
JSONArray queriedUsers = new JSONArray();
users = principalService.getUsers(repositoryId);
for (User user : users) {
if (user.getUserId().startsWith(query) || user.getName().startsWith(query)) {
JSONObject userJSON = convertUserToJson(user);
queriedUsers.add(userJSON);
}
}
if (queriedUsers.isEmpty()) {
status = false;
addErrMsg(errMsg, ITEM_USER, ErrorCode.ERR_NOTFOUND);
} else {
result.put("result", queriedUsers);
}
result = makeResult(status, result, errMsg);
return result.toJSONString();
}
@POST
@Path("/create/{id}")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public String create(@PathParam("repositoryId") String repositoryId, @PathParam("id") String userId,
@FormParam(FORM_USERNAME) String name, @FormParam(FORM_PASSWORD) String password,
@FormParam(FORM_FIRSTNAME) String firstName, @FormParam(FORM_LASTNAME) String lastName,
@FormParam(FORM_EMAIL) String email, @Context HttpServletRequest httpRequest) {
boolean status = true;
JSONObject result = new JSONObject();
JSONArray errMsg = new JSONArray();
// Validation
status = validateNewUser(status, errMsg, userId, name, firstName, lastName, password, repositoryId);
// Create a user
if (status) {
// initialize mandatory but space-allowed parameters
if (StringUtils.isBlank(lastName))
lastName = "";
if (StringUtils.isBlank(email))
email = "";
// Generate a password hash
String passwordHash = BCrypt.hashpw(password, BCrypt.gensalt());
User user = new User(userId, name, firstName, lastName, email, passwordHash);
setFirstSignature(httpRequest, user);
// TODO Error handling
principalService.createUser(repositoryId, user);
}
result = makeResult(status, result, errMsg);
return result.toJSONString();
}
@PUT
@Path("/changePassword/{id}")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public String changePassword(@PathParam("repositoryId") String repositoryId, @PathParam("id") String userId,
@FormParam(FORM_OLDPASSWORD) String oldPassword, @FormParam(FORM_NEWPASSWORD) String newPassword,
@Context HttpServletRequest httpRequest) {
boolean status = true;
JSONObject result = new JSONObject();
JSONArray errMsg = new JSONArray();
User user = principalService.getUserById(repositoryId, userId);
// Validation
status = checkAuthorityForUser(status, errMsg, httpRequest, userId, repositoryId);
if (status) {
if (!AuthenticationUtil.passwordMatches(oldPassword, user.getPasswordHash())) {
status = false;
addErrMsg(errMsg, ITEM_USER, ErrorCode.ERR_WRONGPASSWORD);
}
// Edit & Update
if (status) {
// Edit the user info
String passwordHash = BCrypt.hashpw(newPassword, BCrypt.gensalt());
user.setPasswordHash(passwordHash);
setModifiedSignature(httpRequest, user);
try {
principalService.updateUser(repositoryId, user);
} catch (Exception e) {
e.printStackTrace();
status = false;
addErrMsg(errMsg, ITEM_USER, ErrorCode.ERR_UPDATE);
}
setModifiedSignature(httpRequest, user);
try {
principalService.updateUser(repositoryId, user);
} catch (Exception e) {
e.printStackTrace();
status = false;
addErrMsg(errMsg, ITEM_USER, ErrorCode.ERR_UPDATE);
}
if(status){
String solrUserId = propertyManager.readValue(PropertyKey.SOLR_NEMAKI_USERID);
if(user.getUserId().equals(solrUserId)){
JSONObject capResult = solrResource.changeAdminPasswordImpl(repositoryId, newPassword, oldPassword, httpRequest);
if (capResult.get(ITEM_STATUS).toString() != SUCCESS){
// TODO: Error handling
status = false;
addErrMsg(errMsg, ITEM_USER, capResult.get(ITEM_ERROR).toString());
}
}
}
}
}
makeResult(status, result, errMsg);
return result.toJSONString();
}
@PUT
@Path("/update/{id}")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public String update(@PathParam("repositoryId") String repositoryId, @PathParam("id") String userId,
@FormParam(FORM_USERNAME) String name, @FormParam(FORM_FIRSTNAME) String firstName,
@FormParam(FORM_LASTNAME) String lastName, @FormParam(FORM_EMAIL) String email,
@FormParam("addFavorites") String addFavorites, @FormParam("removeFavorites") String removeFavorites,
@FormParam(FORM_PASSWORD) String password, @Context HttpServletRequest httpRequest) {
boolean status = true;
JSONObject result = new JSONObject();
JSONArray errMsg = new JSONArray();
// Existing user
User user = principalService.getUserById(repositoryId, userId);
// Validation
status = checkAuthorityForUser(status, errMsg, httpRequest, userId, repositoryId);
// status = validateUser(status, errMsg, userId, name, firstName,
// lastName);
// Edit & Update
if (status) {
// Edit the user info
// if a parameter is not input, it won't be modified.
if (userId != null)
user.setUserId(userId);
if (name != null)
user.setName(name);
if (firstName != null)
user.setFirstName(firstName);
if (lastName != null)
user.setLastName(lastName);
if (email != null)
user.setEmail(email);
if (addFavorites != null) {
try {
JSONArray l = (JSONArray) (new JSONParser().parse(addFavorites));
Set<String> fs = user.getFavorites();
if (CollectionUtils.isEmpty(fs)) {
fs = new HashSet<String>();
}
fs.addAll(l);
user.setFavorites(fs);
System.out.println();
// fs.addAll(l);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (removeFavorites != null) {
try {
JSONArray l = (JSONArray) (new JSONParser().parse(removeFavorites));
user.getFavorites().removeAll(l);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (StringUtils.isNotBlank(password)) {
// TODO Error handling
user = principalService.getUserById(repositoryId, userId);
// Edit & Update
if (status) {
// Edit the user info
String passwordHash = BCrypt.hashpw(password, BCrypt.gensalt());
user.setPasswordHash(passwordHash);
setModifiedSignature(httpRequest, user);
try {
principalService.updateUser(repositoryId, user);
} catch (Exception e) {
e.printStackTrace();
status = false;
addErrMsg(errMsg, ITEM_USER, ErrorCode.ERR_UPDATE);
}
}
}
setModifiedSignature(httpRequest, user);
try {
principalService.updateUser(repositoryId, user);
} catch (Exception e) {
e.printStackTrace();
status = false;
addErrMsg(errMsg, ITEM_USER, ErrorCode.ERR_UPDATE);
}
}
makeResult(status, result, errMsg);
return result.toJSONString();
}
@DELETE
@Path("/delete/{id}")
@Produces(MediaType.APPLICATION_JSON)
public String delete(@PathParam("repositoryId") String repositoryId, @PathParam("id") String userId,
@Context HttpServletRequest httpRequest) {
boolean status = true;
JSONObject result = new JSONObject();
JSONArray errMsg = new JSONArray();
// Existing user
User user = principalService.getUserById(repositoryId, userId);
if (user == null) {
status = false;
addErrMsg(errMsg, ITEM_USER, ErrorCode.ERR_NOTFOUND);
}
// Validation
status = checkAuthorityForUser(status, errMsg, httpRequest, userId, repositoryId);
// Delete a user
if (status) {
try {
principalService.deleteUser(repositoryId, user.getId());
} catch (Exception ex) {
ex.printStackTrace();
status = false;
addErrMsg(errMsg, ITEM_USER, ErrorCode.ERR_DELETE);
}
} else {
status = false;
addErrMsg(errMsg, ITEM_USER, ErrorCode.ERR_DELETE);
}
result = makeResult(status, result, errMsg);
return result.toJSONString();
}
private boolean validateUser(boolean status, JSONArray errMsg, String userId, String userName, String firstName,
String lastName) {
if (StringUtils.isBlank(userId)) {
status = false;
addErrMsg(errMsg, ITEM_USERID, ErrorCode.ERR_MANDATORY);
}
if (StringUtils.isBlank(userName)) {
status = false;
addErrMsg(errMsg, ITEM_USERNAME, ErrorCode.ERR_MANDATORY);
}
return status;
}
private boolean validateNewUser(boolean status, JSONArray errMsg, String userId, String userName, String firstName,
String lastName, String password, String repositoryId) {
status = validateUser(status, errMsg, userId, userName, firstName, lastName);
// userID uniqueness
User user = principalService.getUserById(repositoryId, userId);
if (user != null) {
status = false;
addErrMsg(errMsg, ITEM_USERID, ErrorCode.ERR_ALREADYEXISTS);
}
if (StringUtils.isBlank(password)) {
status = false;
addErrMsg(errMsg, ITEM_PASSWORD, ErrorCode.ERR_MANDATORY);
}
return status;
}
@SuppressWarnings("unchecked")
private JSONObject convertUserToJson(User user) {
SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
String created = new String();
try {
if (user.getCreated() != null) {
created = sdf.format(user.getCreated().getTime());
}
} catch (Exception ex) {
ex.printStackTrace();
}
String modified = new String();
try {
if (user.getModified() != null) {
modified = sdf.format(user.getModified().getTime());
}
} catch (Exception ex) {
ex.printStackTrace();
}
JSONObject userJSON = new JSONObject();
userJSON.clear();
userJSON.put(ITEM_USERID, user.getUserId());
userJSON.put(ITEM_USERNAME, user.getName());
userJSON.put(ITEM_FIRSTNAME, user.getFirstName());
userJSON.put(ITEM_LASTNAME, user.getLastName());
userJSON.put(ITEM_EMAIL, user.getEmail());
userJSON.put(ITEM_TYPE, user.getType());
userJSON.put(ITEM_CREATOR, user.getCreator());
userJSON.put(ITEM_CREATED, created);
userJSON.put(ITEM_MODIFIER, user.getModifier());
userJSON.put(ITEM_MODIFIED, modified);
boolean isAdmin = (user.isAdmin() == null) ? false : user.isAdmin();
userJSON.put(ITEM_IS_ADMIN, isAdmin);
JSONArray jfs = new JSONArray();
Set<String> ufs = user.getFavorites();
if (CollectionUtils.isNotEmpty(ufs)) {
Iterator<String> ufsItr = ufs.iterator();
while (ufsItr.hasNext()) {
jfs.add(ufsItr.next());
}
}
userJSON.put("favorites", jfs);
return userJSON;
}
private boolean checkAuthorityForUser(boolean status, JSONArray errMsg, HttpServletRequest httpRequest,
String resoureId, String repositoryId) {
CallContext callContext = (CallContext) httpRequest.getAttribute("CallContext");
String userId = callContext.getUsername();
String password = callContext.getPassword();
if (!userId.equals(resoureId) && !isAdminOperaiton(repositoryId, userId, password) && !isSystemUser(repositoryId, resoureId)) {
status = false;
addErrMsg(errMsg, ITEM_USER, ErrorCode.ERR_NOTAUTHENTICATED);
}
return status;
}
private boolean isSystemUser(String repositoryId, String userId){
boolean result = false;
User user = principalService.getUserById(repositoryId, userId);
result = user.isAdmin();
if(result) return true;
String solrUserId = propertyManager.readValue(PropertyKey.SOLR_NEMAKI_USERID);
result = user.getUserId().equals(solrUserId);
if(result) return true;
return result;
}
private boolean isAdminOperaiton(String repositoryId, String userId, String password) {
if (StringUtils.isBlank(userId) || StringUtils.isBlank(password)) {
return false;
}
User user = principalService.getUserById(repositoryId, userId);
boolean isAdmin = (user.isAdmin() == null) ? false : user.isAdmin();
if (isAdmin) {
// password check
boolean match = BCrypt.checkpw(password, user.getPasswordHash());
if (match)
return true;
}
return false;
}
}