Initial commit

This commit is contained in:
jendib
2013-07-27 18:33:20 +02:00
parent 41cb6dd9ae
commit 9b74bd8194
156 changed files with 72879 additions and 0 deletions
@@ -0,0 +1,14 @@
package com.sismics.docs.rest.constant;
/**
* Base functions.
*
* @author jtremeaux
*/
public enum BaseFunction {
/**
* Allows the user to use the admin fonctions.
*/
ADMIN,
}
@@ -0,0 +1,114 @@
package com.sismics.docs.rest.resource;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Appender;
import org.apache.log4j.Logger;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import com.sismics.docs.core.util.ConfigUtil;
import com.sismics.docs.core.util.jpa.PaginatedList;
import com.sismics.docs.core.util.jpa.PaginatedLists;
import com.sismics.docs.rest.constant.BaseFunction;
import com.sismics.rest.exception.ForbiddenClientException;
import com.sismics.rest.exception.ServerException;
import com.sismics.util.log4j.LogCriteria;
import com.sismics.util.log4j.LogEntry;
import com.sismics.util.log4j.MemoryAppender;
/**
* General app REST resource.
*
* @author jtremeaux
*/
@Path("/app")
public class AppResource extends BaseResource {
/**
* Return the information about the application.
*
* @return Response
* @throws JSONException
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response version() throws JSONException {
ResourceBundle configBundle = ConfigUtil.getConfigBundle();
String currentVersion = configBundle.getString("api.current_version");
String minVersion = configBundle.getString("api.min_version");
JSONObject response = new JSONObject();
response.put("current_version", currentVersion.replace("-SNAPSHOT", ""));
response.put("min_version", minVersion);
response.put("total_memory", Runtime.getRuntime().totalMemory());
response.put("free_memory", Runtime.getRuntime().freeMemory());
return Response.ok().entity(response).build();
}
/**
* Retrieve the application logs.
*
* @param level Filter on logging level
* @param tag Filter on logger name / tag
* @param message Filter on message
* @param limit Page limit
* @param offset Page offset
* @return
* @throws JSONException
*/
@GET
@Path("log")
@Produces(MediaType.APPLICATION_JSON)
public Response log(
@QueryParam("level") String level,
@QueryParam("tag") String tag,
@QueryParam("message") String message,
@QueryParam("limit") Integer limit,
@QueryParam("offset") Integer offset) throws JSONException {
if (!authenticate()) {
throw new ForbiddenClientException();
}
checkBaseFunction(BaseFunction.ADMIN);
// Get the memory appender
Logger logger = Logger.getRootLogger();
Appender appender = logger.getAppender("MEMORY");
if (appender == null || !(appender instanceof MemoryAppender)) {
throw new ServerException("ServerError", "MEMORY appender not configured");
}
MemoryAppender memoryAppender = (MemoryAppender) appender;
// Find the logs
LogCriteria logCriteria = new LogCriteria();
logCriteria.setLevel(StringUtils.stripToNull(level));
logCriteria.setTag(StringUtils.stripToNull(tag));
logCriteria.setMessage(StringUtils.stripToNull(message));
PaginatedList<LogEntry> paginatedList = PaginatedLists.create(limit, offset);
memoryAppender.find(logCriteria, paginatedList);
JSONObject response = new JSONObject();
List<JSONObject> logs = new ArrayList<JSONObject>();
for (LogEntry logEntry : paginatedList.getResultList()) {
JSONObject log = new JSONObject();
log.put("date", logEntry.getTimestamp());
log.put("level", logEntry.getLevel());
log.put("tag", logEntry.getTag());
log.put("message", logEntry.getMessage());
logs.add(log);
}
response.put("total", paginatedList.getResultCount());
response.put("logs", logs);
return Response.ok().entity(response).build();
}
}
@@ -0,0 +1,82 @@
package com.sismics.docs.rest.resource;
import java.security.Principal;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import org.codehaus.jettison.json.JSONException;
import com.sismics.docs.rest.constant.BaseFunction;
import com.sismics.rest.exception.ForbiddenClientException;
import com.sismics.security.IPrincipal;
import com.sismics.security.UserPrincipal;
import com.sismics.util.filter.TokenBasedSecurityFilter;
/**
* Base class of REST resources.
*
* @author jtremeaux
*/
public abstract class BaseResource {
/**
* Injects the HTTP request.
*/
@Context
protected HttpServletRequest request;
/**
* Application key.
*/
@QueryParam("app_key")
protected String appKey;
/**
* Principal of the authenticated user.
*/
protected IPrincipal principal;
/**
* This method is used to check if the user is authenticated.
*
* @return True if the user is authenticated and not anonymous
*/
protected boolean authenticate() {
Principal principal = (Principal) request.getAttribute(TokenBasedSecurityFilter.PRINCIPAL_ATTRIBUTE);
if (principal != null && principal instanceof IPrincipal) {
this.principal = (IPrincipal) principal;
return !this.principal.isAnonymous();
} else {
return false;
}
}
/**
* Checks if the user has a base function. Throw an exception if the check fails.
*
* @param baseFunction Base function to check
* @throws JSONException
*/
protected void checkBaseFunction(BaseFunction baseFunction) throws JSONException {
if (!hasBaseFunction(baseFunction)) {
throw new ForbiddenClientException();
}
}
/**
* Checks if the user has a base function.
*
* @param baseFunction Base function to check
* @return True if the user has the base function
* @throws JSONException
*/
protected boolean hasBaseFunction(BaseFunction baseFunction) throws JSONException {
if (principal == null || !(principal instanceof UserPrincipal)) {
return false;
}
Set<String> baseFunctionSet = ((UserPrincipal) principal).getBaseFunctionSet();
return baseFunctionSet != null && baseFunctionSet.contains(baseFunction.name());
}
}
@@ -0,0 +1,230 @@
package com.sismics.docs.rest.resource;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.NoResultException;
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.MediaType;
import javax.ws.rs.core.Response;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import com.sismics.docs.core.dao.jpa.DocumentDao;
import com.sismics.docs.core.dao.jpa.criteria.DocumentCriteria;
import com.sismics.docs.core.dao.jpa.dto.DocumentDto;
import com.sismics.docs.core.model.jpa.Document;
import com.sismics.docs.core.util.jpa.PaginatedList;
import com.sismics.docs.core.util.jpa.PaginatedLists;
import com.sismics.docs.core.util.jpa.SortCriteria;
import com.sismics.rest.exception.ClientException;
import com.sismics.rest.exception.ForbiddenClientException;
import com.sismics.rest.util.ValidationUtil;
/**
* Document REST resources.
*
* @author bgamard
*/
@Path("/document")
public class DocumentResource extends BaseResource {
/**
* Returns a document.
*
* @param id Document ID
* @return Response
* @throws JSONException
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response get(
@QueryParam("id") String id) throws JSONException {
if (!authenticate()) {
throw new ForbiddenClientException();
}
DocumentDao documentDao = new DocumentDao();
Document documentDb = null;
try {
documentDb = documentDao.getDocument(id, principal.getId());
} catch (NoResultException e) {
throw new ClientException("DocumentNotFound", MessageFormat.format("Document not found: {0}", id));
}
JSONObject document = new JSONObject();
document.put("id", documentDb.getId());
document.put("title", documentDb.getTitle());
document.put("description", documentDb.getDescription());
document.put("create_date", documentDb.getCreateDate().getTime());
return Response.ok().entity(document).build();
}
/**
* Returns all documents.
*
* @param limit Page limit
* @param offset Page offset
* @return Response
* @throws JSONException
*/
@GET
@Path("list")
@Produces(MediaType.APPLICATION_JSON)
public Response list(
@QueryParam("limit") Integer limit,
@QueryParam("offset") Integer offset,
@QueryParam("sort_column") Integer sortColumn,
@QueryParam("asc") Boolean asc) throws JSONException {
if (!authenticate()) {
throw new ForbiddenClientException();
}
JSONObject response = new JSONObject();
List<JSONObject> documents = new ArrayList<JSONObject>();
DocumentDao documentDao = new DocumentDao();
PaginatedList<DocumentDto> paginatedList = PaginatedLists.create(limit, offset);
SortCriteria sortCriteria = new SortCriteria(sortColumn, asc);
DocumentCriteria documentCriteria = new DocumentCriteria();
documentCriteria.setUserId(principal.getId());
documentDao.findByCriteria(paginatedList, documentCriteria, sortCriteria);
for (DocumentDto documentDto : paginatedList.getResultList()) {
JSONObject document = new JSONObject();
document.put("id", documentDto.getId());
document.put("title", documentDto.getTitle());
document.put("description", documentDto.getDescription());
document.put("create_date", documentDto.getCreateTimestamp());
documents.add(document);
}
response.put("total", paginatedList.getResultCount());
response.put("documents", documents);
return Response.ok().entity(response).build();
}
/**
* Creates a new document.
*
* @param title Title
* @param description Description
* @return Response
* @throws JSONException
*/
@PUT
@Produces(MediaType.APPLICATION_JSON)
public Response add(
@FormParam("title") String title,
@FormParam("description") String description) throws JSONException {
if (!authenticate()) {
throw new ForbiddenClientException();
}
// Validate input data
title = ValidationUtil.validateLength(title, "title", 1, 100, false);
description = ValidationUtil.validateLength(description, "description", 0, 4000, true);
// Create the document
DocumentDao documentDao = new DocumentDao();
Document document = new Document();
document.setUserId(principal.getId());
document.setTitle(title);
document.setDescription(description);
String documentId = documentDao.create(document);
JSONObject response = new JSONObject();
response.put("id", documentId);
return Response.ok().entity(response).build();
}
/**
* Updates the document.
*
* @param title Title
* @param description Description
* @return Response
* @throws JSONException
*/
@POST
@Path("{id: [a-z0-9\\-]+}")
@Produces(MediaType.APPLICATION_JSON)
public Response update(
@PathParam("id") String id,
@FormParam("title") String title,
@FormParam("description") String description) throws JSONException {
if (!authenticate()) {
throw new ForbiddenClientException();
}
// Validate input data
title = ValidationUtil.validateLength(title, "title", 1, 100, false);
description = ValidationUtil.validateLength(description, "description", 0, 4000, true);
// Get the document
DocumentDao documentDao = new DocumentDao();
Document document = null;
try {
document = documentDao.getDocument(id, principal.getId());
} catch (NoResultException e) {
throw new ClientException("DocumentNotFound", MessageFormat.format("Document not found: {0}", id));
}
// Update the document
if (title != null) {
document.setTitle(title);
}
if (description != null) {
document.setDescription(description);
}
// Always return ok
JSONObject response = new JSONObject();
response.put("status", "ok");
return Response.ok().entity(response).build();
}
/**
* Deletes a document.
*
* @param id Document ID
* @return Response
* @throws JSONException
*/
@DELETE
@Path("{id: [a-z0-9\\-]+}")
@Produces(MediaType.APPLICATION_JSON)
public Response delete(
@PathParam("id") String id) throws JSONException {
if (!authenticate()) {
throw new ForbiddenClientException();
}
// Get the document
DocumentDao documentDao = new DocumentDao();
Document document = null;
try {
document = documentDao.getDocument(id, principal.getId());
} catch (NoResultException e) {
throw new ClientException("DocumentNotFound", MessageFormat.format("Document not found: {0}", id));
}
// Delete the document
documentDao.delete(document.getId());
// Always return ok
JSONObject response = new JSONObject();
response.put("status", "ok");
return Response.ok().entity(response).build();
}
}
@@ -0,0 +1,200 @@
package com.sismics.docs.rest.resource;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.NoResultException;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
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.MediaType;
import javax.ws.rs.core.Response;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import com.sismics.docs.core.dao.jpa.DocumentDao;
import com.sismics.docs.core.dao.jpa.FileDao;
import com.sismics.docs.core.model.jpa.Document;
import com.sismics.docs.core.model.jpa.File;
import com.sismics.docs.core.util.DirectoryUtil;
import com.sismics.rest.exception.ClientException;
import com.sismics.rest.exception.ForbiddenClientException;
import com.sismics.rest.exception.ServerException;
import com.sismics.rest.util.ValidationUtil;
import com.sismics.util.mime.MimeTypeUtil;
import com.sun.jersey.multipart.FormDataBodyPart;
import com.sun.jersey.multipart.FormDataParam;
/**
* File REST resources.
*
* @author bgamard
*/
@Path("/file")
public class FileResource extends BaseResource {
/**
* Add a file to a document.
*
* @param id Document ID
* @param fileBodyPart File to add
* @return Response
* @throws JSONException
*/
@PUT
@Consumes("multipart/form-data")
@Produces(MediaType.APPLICATION_JSON)
public Response add(
@FormDataParam("id") String documentId,
@FormDataParam("file") FormDataBodyPart fileBodyPart) throws JSONException {
if (!authenticate()) {
throw new ForbiddenClientException();
}
// Validate input data
ValidationUtil.validateRequired(documentId, "id");
ValidationUtil.validateRequired(fileBodyPart, "file");
// Get the document
DocumentDao documentDao = new DocumentDao();
Document document = null;
try {
document = documentDao.getDocument(documentId, principal.getId());
} catch (NoResultException e) {
throw new ClientException("DocumentNotFound", MessageFormat.format("Document not found: {0}", documentId));
}
FileDao fileDao = new FileDao();
InputStream is = fileBodyPart.getValueAs(InputStream.class);
try {
// Create the file
File file = new File();
file.setDocumentId(document.getId());
file.setMimeType(MimeTypeUtil.guessMimeType(is));
String fileId = fileDao.create(file);
// Copy the incoming stream content into the storage directory
Files.copy(is, Paths.get(DirectoryUtil.getStorageDirectory().getPath(), fileId));
// Always return ok
JSONObject response = new JSONObject();
response.put("status", "ok");
response.put("id", fileId);
return Response.ok().entity(response).build();
} catch (Exception e) {
throw new ServerException("FileError", "Error adding a file", e);
}
}
/**
* Returns a file.
*
* @param id Document ID
* @return Response
* @throws JSONException
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response get(
@QueryParam("id") String id) throws JSONException {
if (!authenticate()) {
throw new ForbiddenClientException();
}
FileDao fileDao = new FileDao();
File fileDb = null;
try {
fileDb = fileDao.getFile(id);
} catch (NoResultException e) {
throw new ClientException("FileNotFound", MessageFormat.format("File not found: {0}", id));
}
JSONObject file = new JSONObject();
file.put("id", fileDb.getId());
file.put("mimetype", fileDb.getMimeType());
file.put("document_id", fileDb.getDocumentId());
file.put("create_date", fileDb.getCreateDate().getTime());
return Response.ok().entity(file).build();
}
/**
* Returns files linked to a document.
*
* @param id Document ID
* @return Response
* @throws JSONException
*/
@GET
@Path("list")
@Produces(MediaType.APPLICATION_JSON)
public Response list(
@QueryParam("id") String documentId) throws JSONException {
if (!authenticate()) {
throw new ForbiddenClientException();
}
FileDao fileDao = new FileDao();
List<File> fileList = fileDao.getByDocumentId(documentId);
JSONObject response = new JSONObject();
List<JSONObject> files = new ArrayList<JSONObject>();
for (File fileDb : fileList) {
JSONObject file = new JSONObject();
file.put("id", fileDb.getId());
file.put("mimetype", fileDb.getMimeType());
file.put("document_id", fileDb.getDocumentId());
file.put("create_date", fileDb.getCreateDate().getTime());
files.add(file);
}
response.put("files", files);
return Response.ok().entity(response).build();
}
/**
* Deletes a file.
*
* @param id File ID
* @return Response
* @throws JSONException
*/
@DELETE
@Path("{id: [a-z0-9\\-]+}")
@Produces(MediaType.APPLICATION_JSON)
public Response delete(
@PathParam("id") String id) throws JSONException {
if (!authenticate()) {
throw new ForbiddenClientException();
}
// Get the file
FileDao fileDao = new FileDao();
File file = null;
try {
file = fileDao.getFile(id);
} catch (NoResultException e) {
throw new ClientException("FileNotFound", MessageFormat.format("File not found: {0}", id));
}
// Delete the document
fileDao.delete(file.getId());
// Always return ok
JSONObject response = new JSONObject();
response.put("status", "ok");
return Response.ok().entity(response).build();
}
}
@@ -0,0 +1,46 @@
package com.sismics.docs.rest.resource;
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import com.sismics.docs.core.dao.jpa.LocaleDao;
import com.sismics.docs.core.model.jpa.Locale;
/**
* Locale REST resources.
*
* @author jtremeaux
*/
@Path("/locale")
public class LocaleResource extends BaseResource {
/**
* Returns the list of all locales.
*
* @return Response
* @throws JSONException
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response list() throws JSONException {
LocaleDao localeDao = new LocaleDao();
List<Locale> localeList = localeDao.findAll();
JSONObject response = new JSONObject();
List<JSONObject> items = new ArrayList<JSONObject>();
for (Locale locale : localeList) {
JSONObject item = new JSONObject();
item.put("id", locale.getId());
items.add(item);
}
response.put("locales", items);
return Response.ok().entity(response).build();
}
}
@@ -0,0 +1,57 @@
package com.sismics.docs.rest.resource;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import com.sun.jersey.core.util.ReaderWriter;
/**
* MessageBodyWriter personalized to write JSON despite the text/plain MIME type.
* Used in particuler in return of a posted form, since IE doesn't knw how to read the application/json MIME type.
*
* @author bgamard
*/
@Provider
@Produces(MediaType.TEXT_PLAIN)
public class TextPlainMessageBodyWriter implements
MessageBodyWriter<JSONObject> {
@Override
public boolean isWriteable(Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
return true;
}
@Override
public long getSize(JSONObject array, Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
return -1;
}
@Override
public void writeTo(JSONObject jsonObject, Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders,
OutputStream entityStream) throws IOException,
WebApplicationException {
try {
OutputStreamWriter writer = new OutputStreamWriter(entityStream, ReaderWriter.getCharset(mediaType));
jsonObject.write(writer);
writer.flush();
} catch (JSONException e) {
throw new WebApplicationException(e);
}
}
}
@@ -0,0 +1,45 @@
package com.sismics.docs.rest.resource;
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import com.sismics.docs.core.dao.file.theme.ThemeDao;
/**
* Theme REST resources.
*
* @author jtremeaux
*/
@Path("/theme")
public class ThemeResource extends BaseResource {
/**
* Returns the list of all themes.
*
* @return Response
* @throws JSONException
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response list() throws JSONException {
ThemeDao themeDao = new ThemeDao();
List<String> themeList = themeDao.findAll();
JSONObject response = new JSONObject();
List<JSONObject> items = new ArrayList<JSONObject>();
for (String theme : themeList) {
JSONObject item = new JSONObject();
item.put("id", theme);
items.add(item);
}
response.put("themes", items);
return Response.ok().entity(response).build();
}
}
@@ -0,0 +1,535 @@
package com.sismics.docs.rest.resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.servlet.http.Cookie;
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.MediaType;
import javax.ws.rs.core.NewCookie;
import javax.ws.rs.core.Response;
import org.apache.commons.lang.StringUtils;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import com.sismics.docs.core.constant.Constants;
import com.sismics.docs.core.dao.jpa.AuthenticationTokenDao;
import com.sismics.docs.core.dao.jpa.UserDao;
import com.sismics.docs.core.dao.jpa.dto.UserDto;
import com.sismics.docs.core.model.jpa.AuthenticationToken;
import com.sismics.docs.core.model.jpa.User;
import com.sismics.docs.core.util.jpa.PaginatedList;
import com.sismics.docs.core.util.jpa.PaginatedLists;
import com.sismics.docs.core.util.jpa.SortCriteria;
import com.sismics.docs.rest.constant.BaseFunction;
import com.sismics.rest.exception.ClientException;
import com.sismics.rest.exception.ForbiddenClientException;
import com.sismics.rest.exception.ServerException;
import com.sismics.rest.util.ValidationUtil;
import com.sismics.security.UserPrincipal;
import com.sismics.util.LocaleUtil;
import com.sismics.util.filter.TokenBasedSecurityFilter;
/**
* User REST resources.
*
* @author jtremeaux
*/
@Path("/user")
public class UserResource extends BaseResource {
/**
* Creates a new user.
*
* @param username User's username
* @param password Password
* @param email E-Mail
* @param localeId Locale ID
* @return Response
* @throws JSONException
*/
@PUT
@Produces(MediaType.APPLICATION_JSON)
public Response register(
@FormParam("username") String username,
@FormParam("password") String password,
@FormParam("locale") String localeId,
@FormParam("email") String email) throws JSONException {
if (!authenticate()) {
throw new ForbiddenClientException();
}
checkBaseFunction(BaseFunction.ADMIN);
// Validate the input data
username = ValidationUtil.validateLength(username, "username", 3, 50);
ValidationUtil.validateAlphanumeric(username, "username");
password = ValidationUtil.validateLength(password, "password", 8, 50);
email = ValidationUtil.validateLength(email, "email", 3, 50);
ValidationUtil.validateEmail(email, "email");
// Create the user
User user = new User();
user.setRoleId(Constants.DEFAULT_USER_ROLE);
user.setUsername(username);
user.setPassword(password);
user.setEmail(email);
user.setCreateDate(new Date());
if (localeId == null) {
// Set the locale from the HTTP headers
localeId = LocaleUtil.getLocaleIdFromAcceptLanguage(request.getHeader("Accept-Language"));
}
user.setLocaleId(localeId);
// Create the user
UserDao userDao = new UserDao();
try {
userDao.create(user);
} catch (Exception e) {
if ("AlreadyExistingUsername".equals(e.getMessage())) {
throw new ServerException("AlreadyExistingUsername", "Login already used", e);
} else {
throw new ServerException("UnknownError", "Unknown Server Error", e);
}
}
// Always return OK
JSONObject response = new JSONObject();
response.put("status", "ok");
return Response.ok().entity(response).build();
}
/**
* Updates user informations.
*
* @param password Password
* @param email E-Mail
* @param themeId Theme
* @param localeId Locale ID
* @param displayTitleWeb Display only article titles (web application).
* @param displayTitleMobile Display only article titles (mobile application).
* @param displayUnreadWeb Display only unread titles (web application).
* @param displayUnreadMobile Display only unread titles (mobile application).
* @param firstConnection True if the user hasn't acknowledged the first connection wizard yet.
* @return Response
* @throws JSONException
*/
@POST
@Produces(MediaType.APPLICATION_JSON)
public Response update(
@FormParam("password") String password,
@FormParam("email") String email,
@FormParam("theme") String themeId,
@FormParam("locale") String localeId,
@FormParam("first_connection") Boolean firstConnection) throws JSONException {
if (!authenticate()) {
throw new ForbiddenClientException();
}
// Validate the input data
password = ValidationUtil.validateLength(password, "password", 8, 50, true);
email = ValidationUtil.validateLength(email, "email", null, 100, true);
localeId = ValidationUtil.validateLocale(localeId, "locale", true);
themeId = ValidationUtil.validateTheme(themeId, "theme", true);
// Update the user
UserDao userDao = new UserDao();
User user = userDao.getActiveByUsername(principal.getName());
if (email != null) {
user.setEmail(email);
}
if (themeId != null) {
user.setTheme(themeId);
}
if (localeId != null) {
user.setLocaleId(localeId);
}
if (firstConnection != null && hasBaseFunction(BaseFunction.ADMIN)) {
user.setFirstConnection(firstConnection);
}
user = userDao.update(user);
if (StringUtils.isNotBlank(password)) {
user.setPassword(password);
user = userDao.updatePassword(user);
}
// Always return "ok"
JSONObject response = new JSONObject();
response.put("status", "ok");
return Response.ok().entity(response).build();
}
/**
* Updates user informations.
*
* @param username Username
* @param password Password
* @param email E-Mail
* @param themeId Theme
* @param localeId Locale ID
* @param displayTitleWeb Display only article titles (web application).
* @param displayTitleMobile Display only article titles (mobile application).
* @param displayUnreadWeb Display only unread titles (web application).
* @param displayUnreadMobile Display only unread titles (mobile application).
* @return Response
* @throws JSONException
*/
@POST
@Path("{username: [a-zA-Z0-9_]+}")
@Produces(MediaType.APPLICATION_JSON)
public Response update(
@PathParam("username") String username,
@FormParam("password") String password,
@FormParam("email") String email,
@FormParam("theme") String themeId,
@FormParam("locale") String localeId) throws JSONException {
if (!authenticate()) {
throw new ForbiddenClientException();
}
checkBaseFunction(BaseFunction.ADMIN);
// Validate the input data
password = ValidationUtil.validateLength(password, "password", 8, 50, true);
email = ValidationUtil.validateLength(email, "email", null, 100, true);
localeId = ValidationUtil.validateLocale(localeId, "locale", true);
themeId = ValidationUtil.validateTheme(themeId, "theme", true);
// Check if the user exists
UserDao userDao = new UserDao();
User user = userDao.getActiveByUsername(username);
if (user == null) {
throw new ClientException("UserNotFound", "The user doesn't exist");
}
// Update the user
if (email != null) {
user.setEmail(email);
}
if (themeId != null) {
user.setTheme(themeId);
}
if (localeId != null) {
user.setLocaleId(localeId);
}
user = userDao.update(user);
if (StringUtils.isNotBlank(password)) {
// Change the password
user.setPassword(password);
user = userDao.updatePassword(user);
}
// Always return "ok"
JSONObject response = new JSONObject();
response.put("status", "ok");
return Response.ok().entity(response).build();
}
/**
* Checks if a username is available. Search only on active accounts.
*
* @param username Username to check
* @return Response
*/
@GET
@Path("check_username")
@Produces(MediaType.APPLICATION_JSON)
public Response checkUsername(
@QueryParam("username") String username) throws JSONException {
UserDao userDao = new UserDao();
User user = userDao.getActiveByUsername(username);
JSONObject response = new JSONObject();
if (user != null) {
response.put("status", "ko");
response.put("message", "Username already registered");
} else {
response.put("status", "ok");
}
return Response.ok().entity(response).build();
}
/**
* This resource is used to authenticate the user and create a user ession.
* The "session" is only used to identify the user, no other data is stored in the session.
*
* @param username Username
* @param password Password
* @param longLasted Remember the user next time, create a long lasted session.
* @return Response
*/
@POST
@Path("login")
@Produces(MediaType.APPLICATION_JSON)
public Response login(
@FormParam("username") String username,
@FormParam("password") String password,
@FormParam("remember") boolean longLasted) throws JSONException {
// Validate the input data
username = StringUtils.strip(username);
password = StringUtils.strip(password);
// Get the user
UserDao userDao = new UserDao();
String userId = userDao.authenticate(username, password);
if (userId == null) {
throw new ForbiddenClientException();
}
// Create a new session token
AuthenticationTokenDao authenticationTokenDao = new AuthenticationTokenDao();
AuthenticationToken authenticationToken = new AuthenticationToken();
authenticationToken.setUserId(userId);
authenticationToken.setLongLasted(longLasted);
String token = authenticationTokenDao.create(authenticationToken);
// Cleanup old session tokens
authenticationTokenDao.deleteOldSessionToken(userId);
JSONObject response = new JSONObject();
int maxAge = longLasted ? TokenBasedSecurityFilter.TOKEN_LONG_LIFETIME : -1;
NewCookie cookie = new NewCookie(TokenBasedSecurityFilter.COOKIE_NAME, token, "/", null, null, maxAge, false);
return Response.ok().entity(response).cookie(cookie).build();
}
/**
* Logs out the user and deletes the active session.
*
* @return Response
*/
@POST
@Path("logout")
@Produces(MediaType.APPLICATION_JSON)
public Response logout() throws JSONException {
if (!authenticate()) {
throw new ForbiddenClientException();
}
// Get the value of the session token
String authToken = null;
if (request.getCookies() != null) {
for (Cookie cookie : request.getCookies()) {
if (TokenBasedSecurityFilter.COOKIE_NAME.equals(cookie.getName())) {
authToken = cookie.getValue();
}
}
}
AuthenticationTokenDao authenticationTokenDao = new AuthenticationTokenDao();
AuthenticationToken authenticationToken = null;
if (authToken != null) {
authenticationToken = authenticationTokenDao.get(authToken);
}
// No token : nothing to do
if (authenticationToken == null) {
throw new ForbiddenClientException();
}
// Deletes the server token
try {
authenticationTokenDao.delete(authToken);
} catch (Exception e) {
throw new ServerException("AuthenticationTokenError", "Error deleting authentication token: " + authToken, e);
}
// Deletes the client token in the HTTP response
JSONObject response = new JSONObject();
NewCookie cookie = new NewCookie(TokenBasedSecurityFilter.COOKIE_NAME, null);
return Response.ok().entity(response).cookie(cookie).build();
}
/**
* Delete a user.
*
* @return Response
*/
@DELETE
@Produces(MediaType.APPLICATION_JSON)
public Response delete() throws JSONException {
if (!authenticate()) {
throw new ForbiddenClientException();
}
// Ensure that the admin user is not deleted
// TODO Ensure it exists at least one user left
// Delete the user
UserDao userDao = new UserDao();
userDao.delete(principal.getName());
// Always return ok
JSONObject response = new JSONObject();
response.put("status", "ok");
return Response.ok().entity(response).build();
}
/**
* Deletes a user.
*
* @param username Username
* @return Response
* @throws JSONException
*/
@DELETE
@Path("{username: [a-zA-Z0-9_]+}")
@Produces(MediaType.APPLICATION_JSON)
public Response delete(@PathParam("username") String username) throws JSONException {
if (!authenticate()) {
throw new ForbiddenClientException();
}
checkBaseFunction(BaseFunction.ADMIN);
// Check if the user exists
UserDao userDao = new UserDao();
User user = userDao.getActiveByUsername(username);
if (user == null) {
throw new ClientException("UserNotFound", "The user doesn't exist");
}
// Ensure that the admin user is not deleted
// TODO Ensure it exists at least one user left
// Delete the user
userDao.delete(user.getUsername());
// Always return ok
JSONObject response = new JSONObject();
response.put("status", "ok");
return Response.ok().entity(response).build();
}
/**
* Returns the information about the connected user.
*
* @return Response
* @throws JSONException
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response info() throws JSONException {
JSONObject response = new JSONObject();
if (!authenticate()) {
response.put("anonymous", true);
String localeId = LocaleUtil.getLocaleIdFromAcceptLanguage(request.getHeader("Accept-Language"));
response.put("locale", localeId);
// Check if admin has the default password
UserDao userDao = new UserDao();
User adminUser = userDao.getById("admin");
if (adminUser != null && adminUser.getDeleteDate() == null) {
response.put("is_default_password", Constants.DEFAULT_ADMIN_PASSWORD.equals(adminUser.getPassword()));
}
} else {
response.put("anonymous", false);
UserDao userDao = new UserDao();
User user = userDao.getById(principal.getId());
response.put("username", user.getUsername());
response.put("email", user.getEmail());
response.put("theme", user.getTheme());
response.put("locale", user.getLocaleId());
response.put("first_connection", user.isFirstConnection());
JSONArray baseFunctions = new JSONArray(((UserPrincipal) principal).getBaseFunctionSet());
response.put("base_functions", baseFunctions);
response.put("is_default_password", hasBaseFunction(BaseFunction.ADMIN) && Constants.DEFAULT_ADMIN_PASSWORD.equals(user.getPassword()));
}
return Response.ok().entity(response).build();
}
/**
* Returns the information about a user.
*
* @param username Username
* @return Response
* @throws JSONException
*/
@GET
@Path("{username: [a-zA-Z0-9_]+}")
@Produces(MediaType.APPLICATION_JSON)
public Response view(@PathParam("username") String username) throws JSONException {
if (!authenticate()) {
throw new ForbiddenClientException();
}
checkBaseFunction(BaseFunction.ADMIN);
JSONObject response = new JSONObject();
UserDao userDao = new UserDao();
User user = userDao.getActiveByUsername(username);
if (user == null) {
throw new ClientException("UserNotFound", "The user doesn't exist");
}
response.put("username", user.getUsername());
response.put("email", user.getEmail());
response.put("theme", user.getTheme());
response.put("locale", user.getLocaleId());
return Response.ok().entity(response).build();
}
/**
* Returns all active users.
*
* @param limit Page limit
* @param offset Page offset
* @param sortColumn Sort index
* @param asc If true, ascending sorting, else descending
* @return Response
* @throws JSONException
*/
@GET
@Path("list")
@Produces(MediaType.APPLICATION_JSON)
public Response list(
@QueryParam("limit") Integer limit,
@QueryParam("offset") Integer offset,
@QueryParam("sort_column") Integer sortColumn,
@QueryParam("asc") Boolean asc) throws JSONException {
if (!authenticate()) {
throw new ForbiddenClientException();
}
checkBaseFunction(BaseFunction.ADMIN);
JSONObject response = new JSONObject();
List<JSONObject> users = new ArrayList<JSONObject>();
PaginatedList<UserDto> paginatedList = PaginatedLists.create(limit, offset);
SortCriteria sortCriteria = new SortCriteria(sortColumn, asc);
UserDao userDao = new UserDao();
userDao.findAll(paginatedList, sortCriteria);
for (UserDto userDto : paginatedList.getResultList()) {
JSONObject user = new JSONObject();
user.put("id", userDto.getId());
user.put("username", userDto.getUsername());
user.put("email", userDto.getEmail());
user.put("create_date", userDto.getCreateTimestamp());
users.add(user);
}
response.put("total", paginatedList.getResultCount());
response.put("users", users);
return Response.ok().entity(response).build();
}
}