diff --git a/.travis.yml b/.travis.yml index e358d2576..9bcf99945 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,3 @@ language: java jdk: - - oraclejdk7 + - oraclejdk8 diff --git a/jetty-debug.xml b/jetty-debug.xml index 3c3645caa..4f1008997 100644 --- a/jetty-debug.xml +++ b/jetty-debug.xml @@ -34,10 +34,11 @@ 8443 30000 - /.keystore - + /.keystore changeit changeit + /.keystore + changeit diff --git a/pom.xml b/pom.xml index b58750bf4..8b1e45c23 100755 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.jbei ice war - 4.3.6 + 4.4.0 ice Inventory of Composable Elements (ICE) for Synthetic Biology @@ -37,9 +37,9 @@ provided - postgresql + org.postgresql postgresql - 9.1-901.jdbc4 + 9.4-1201-jdbc41 com.h2database @@ -55,7 +55,7 @@ com.google.code.gson gson - 2.2.4 + 2.3.1 compile @@ -79,34 +79,19 @@ 4.3.10.Final - commons-cli - commons-cli - 1.2 - - - commons-dbcp - commons-dbcp - 1.4 - - - commons-pool - commons-pool - 1.6 + commons-codec + commons-codec + 1.10 commons-io commons-io 2.4 - - org.mod4j.org.apache.commons - lang - 2.1.0 - org.apache.commons commons-email - 1.3.1 + 1.3.3 com.opencsv @@ -116,18 +101,41 @@ ch.qos.logback logback-classic - 1.0.13 + 1.1.3 org.biojava core - 1.8.5 + 1.9.2 org.sbolstandard libSBOLj 2.0-beta + + org.apache.httpcomponents + httpcore + 4.2.2 + + + org.apache.httpcomponents + httpclient + 4.2.1 + + + + org.mockito + mockito-all + 1.10.19 + test + + + + com.google.guava + guava + 18.0 + target @@ -164,10 +172,10 @@ true org.apache.maven.plugins maven-compiler-plugin - 2.3.2 + 3.3 - 1.7 - 1.7 + 1.8 + 1.8 @@ -184,7 +192,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 2.9.1 + 2.10.3 diff --git a/src/main/java/org/jbei/auth/Authorization.java b/src/main/java/org/jbei/auth/Authorization.java new file mode 100644 index 000000000..7aa8ffd8b --- /dev/null +++ b/src/main/java/org/jbei/auth/Authorization.java @@ -0,0 +1,36 @@ +package org.jbei.auth; + +/** + * An object specifying if a request is valid, and for which user. + * + * @author wcmorrell + * @version 1.0 + */ +public interface Authorization { + + /** + * Pre-made invalid authorization object. + */ + public static final Authorization INVALID = new Authorization() { + @Override + public boolean isValid() { + return false; + } + + @Override + public String getUserId() { + return null; + } + }; + + /** + * @return the user ID valid for the request + */ + public String getUserId(); + + /** + * @return {@code true} only if the request is validated + */ + public boolean isValid(); + +} \ No newline at end of file diff --git a/src/main/java/org/jbei/auth/KeyTable.java b/src/main/java/org/jbei/auth/KeyTable.java new file mode 100644 index 000000000..e1537d56d --- /dev/null +++ b/src/main/java/org/jbei/auth/KeyTable.java @@ -0,0 +1,20 @@ +/** + * + */ +package org.jbei.auth; + +import java.security.Key; + +/** + * @author wcmorrell + * @version 1.0 + */ +public interface KeyTable { + + /** + * @param keyId + * @return the matching Key object, or {@code null} if not found + */ + public abstract Key getKey(final String keyId); + +} diff --git a/src/main/java/org/jbei/auth/MemoryKeyTable.java b/src/main/java/org/jbei/auth/MemoryKeyTable.java new file mode 100644 index 000000000..d5b013e2a --- /dev/null +++ b/src/main/java/org/jbei/auth/MemoryKeyTable.java @@ -0,0 +1,62 @@ +/** + * + */ +package org.jbei.auth; + +import java.security.Key; +import java.util.HashMap; +import java.util.Map; + +/** + * Simple KeyTable stores mapping of KeyID => Key in memory. + * + * @author wcmorrell + * @version 1.0 + */ +public class MemoryKeyTable extends HashMap implements KeyTable { + + private static final long serialVersionUID = 460597881793784549L; + + /** + * Calls super HashMap constructor. + */ + public MemoryKeyTable() { + super(); + } + + /** + * Calls super HashMap constructor. + * + * @param initialCapacity the initial capacity + * @param loadFactor the load factor + * @throws IllegalArgumentException if the initial capacity is negative or the load factor is nonpositive + */ + public MemoryKeyTable(final int initialCapacity, final float loadFactor) { + super(initialCapacity, loadFactor); + } + + /** + * Calls super HashMap constructor. + * + * @param initialCapacity the initial capacity. + * @throws IllegalArgumentException if the initial capacity is negative. + */ + public MemoryKeyTable(final int initialCapacity) { + super(initialCapacity); + } + + /** + * Calls super HashMap constructor. + * + * @param m the map whose mappings are to be placed in this map + * @throws NullPointerException if the specified map is null + */ + public MemoryKeyTable(final Map m) { + super(m); + } + + @Override + public Key getKey(final String keyId) { + return get(keyId); + } +} diff --git a/src/main/java/org/jbei/auth/hmac/DefaultHmacSignature.java b/src/main/java/org/jbei/auth/hmac/DefaultHmacSignature.java new file mode 100644 index 000000000..7e6c400a3 --- /dev/null +++ b/src/main/java/org/jbei/auth/hmac/DefaultHmacSignature.java @@ -0,0 +1,55 @@ +package org.jbei.auth.hmac; + +import org.apache.commons.codec.binary.Base64; + +import javax.crypto.Mac; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * TODO + * + * @author wcmorrell + * @version 1.0 + */ +public final class DefaultHmacSignature implements HmacSignature { + + private final Mac mac; + private final String userId; + + private String signature = null; + + /** + * @param mac + * @param userId + */ + public DefaultHmacSignature(final Mac mac, final String userId) { + this.mac = mac; + this.userId = userId; + } + + @Override + public InputStream filterInput(final InputStream stream) { + return new HmacInputStream(stream, mac); + } + + @Override + public OutputStream filterOutput(final OutputStream stream) { + return new HmacOutputStream(stream, mac); + } + + @Override + public String generateSignature() { + if (signature == null) { + final byte[] rawSignature = mac.doFinal(); + signature = Base64.encodeBase64String(rawSignature); + } + return signature; + } + + @Override + public String getUserId() { + return userId; + } + +} \ No newline at end of file diff --git a/src/main/java/org/jbei/auth/hmac/HmacAuthorizor.java b/src/main/java/org/jbei/auth/hmac/HmacAuthorizor.java new file mode 100644 index 000000000..a42072334 --- /dev/null +++ b/src/main/java/org/jbei/auth/hmac/HmacAuthorizor.java @@ -0,0 +1,105 @@ +/** + * + */ +package org.jbei.auth.hmac; + +import org.apache.commons.lang3.StringUtils; +import org.jbei.auth.Authorization; +import org.jbei.auth.KeyTable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.http.HttpServletRequest; +import java.security.SignatureException; +import java.util.Map; + +/** + * Creates {@link Authorization} objects based on HMAC values in HTTP Authorization headers. + * + * @author wcmorrell + * @version 1.0 + */ +public class HmacAuthorizor { + + private static final Logger log = LoggerFactory.getLogger(HmacAuthorizor.class); + + private final HmacSignatureFactory factory; + + /** + * @param table where to look up keyId => key + */ + public HmacAuthorizor(final KeyTable table) { + factory = new HmacSignatureFactory(table); + } + + /** + * Creates an initial signature object based on the request and Authorization header value. + * + * @param auth + * @param method + * @param host + * @param path + * @param params + * @return an initial signature, without request body bytes appended; or {@code null}. + */ + public HmacSignature initSignature(final String auth, final String method, final String host, + final String path, final Map> params) { + final String[] parts = StringUtils.split(auth, ':'); + if (parts == null || parts.length == 0) { + log.debug("No Authorization header found on request"); + } else if ("1".equals(parts[0]) && parts.length == 4) { + try { + return factory.buildSignature(parts[1], parts[2], method, host, path, params); + } catch (final SignatureException e) { + log.error("Cannot initialize signature", e); + } + } else { + log.warn("Unknown Authorization header format: " + auth); + } + return null; + } + + /** + * Validates a request based on HTTP Authorization header. + * + * @param request the incoming request + * @return {@link Authorization} object + */ + public Authorization validate(final HttpServletRequest request) { + final String auth = request.getHeader("Authorization"); + final String[] parts = StringUtils.split(auth, ':'); + if (parts == null || parts.length == 0) { + log.debug("No Authorization header found on request"); + } else if ("1".equals(parts[0]) && parts.length == 4) { + return version1(request, parts[1], parts[2], parts[3]); + } else { + log.warn("Unknown Authorization header format: " + auth); + } + return Authorization.INVALID; + } + + private Authorization version1(final HttpServletRequest request, final String keyId, + final String userId, final String signature) { + try { + final HmacSignature hmac = factory.buildSignature(request, keyId, userId); + if (hmac == null) { + // no valid keyId + } else if (signature.equals(hmac.generateSignature())) { + return new Authorization() { + @Override + public boolean isValid() { + return true; + } + + @Override + public String getUserId() { + return userId; + } + }; + } + } catch (final SignatureException e) { + log.warn("Signature failed in HmacAuthorizor: " + e.getMessage()); + } + return Authorization.INVALID; + } +} diff --git a/src/main/java/org/jbei/auth/hmac/HmacInputStream.java b/src/main/java/org/jbei/auth/hmac/HmacInputStream.java new file mode 100644 index 000000000..40f6ee2ed --- /dev/null +++ b/src/main/java/org/jbei/auth/hmac/HmacInputStream.java @@ -0,0 +1,56 @@ +package org.jbei.auth.hmac; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.crypto.Mac; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.util.Arrays; + +/** + * Decorates an {@link InputStream} to pass all read bytes to a {@link Mac}. + * + * @author wcmorrell + * @version 1.0 + * @since 1.0 + */ +public final class HmacInputStream extends FilterInputStream { + + private static final Charset UTF8 = Charset.forName("UTF-8"); + private static final Logger log = LoggerFactory.getLogger(HmacInputStream.class); + + private final Mac mac; + + /** + * @param stream the wrapped stream + * @param mac the digest to update + */ + public HmacInputStream(final InputStream stream, final Mac mac) { + super(stream); + this.mac = mac; + } + + @Override + public int read() throws IOException { + final int data = super.read(); + if (data != -1) { + mac.update((byte) data); + } + return data; + } + + @Override + public int read(final byte[] data, final int offset, final int length) throws IOException { + final int read = super.read(data, offset, length); + if (read != -1) { + final String debug = new String(Arrays.copyOfRange(data, offset, offset + read), UTF8); + mac.update(data, offset, read); + log.debug("Stream data: " + debug); + } + return read; + } + +} \ No newline at end of file diff --git a/src/main/java/org/jbei/auth/hmac/HmacOutputStream.java b/src/main/java/org/jbei/auth/hmac/HmacOutputStream.java new file mode 100644 index 000000000..c42c6ed4e --- /dev/null +++ b/src/main/java/org/jbei/auth/hmac/HmacOutputStream.java @@ -0,0 +1,40 @@ +package org.jbei.auth.hmac; + +import javax.crypto.Mac; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * Decorates an {@link OutputStream} to pass all written bytes through a {@link Mac}. + * + * @author wcmorrell + * @version 1.0 + * @since 1.0 + */ +public final class HmacOutputStream extends FilterOutputStream { + + private final Mac mac; + + /** + * @param stream the stream to wrap + * @param mac the digest to update + */ + public HmacOutputStream(final OutputStream stream, final Mac mac) { + super(stream); + this.mac = mac; + } + + @Override + public void write(final int data) throws IOException { + mac.update((byte) data); + super.write(data); + } + + @Override + public void write(final byte[] data, final int offset, final int length) throws IOException { + mac.update(data, offset, length); + super.write(data, offset, length); + } + +} \ No newline at end of file diff --git a/src/main/java/org/jbei/auth/hmac/HmacSignature.java b/src/main/java/org/jbei/auth/hmac/HmacSignature.java new file mode 100644 index 000000000..7cf5801d7 --- /dev/null +++ b/src/main/java/org/jbei/auth/hmac/HmacSignature.java @@ -0,0 +1,41 @@ +/** + * + */ +package org.jbei.auth.hmac; + +import java.io.InputStream; +import java.io.OutputStream; + +/** + * Utility class to handle generating HMAC signatures in REST requests. A signature should be + * created by concatenating information on a request, then signing with a shared SecretKey. + * + * @author wcmorrell + * @version 1.0 + * @since 1.0 + */ +public interface HmacSignature { + + /** + * @param stream an InputStream which data should pass through signature calculation + * @return a wrapped stream to be used instead of the original stream + */ + public abstract InputStream filterInput(final InputStream stream); + + /** + * @param stream an OutputStream which data should pass through signature calculation + * @return a wrapped stream to be used instead of the original stream + */ + public abstract OutputStream filterOutput(final OutputStream stream); + + /** + * @return compute the signature string + */ + public abstract String generateSignature(); + + /** + * @return the user ID attached to the signature + */ + public abstract String getUserId(); + +} diff --git a/src/main/java/org/jbei/auth/hmac/HmacSignatureFactory.java b/src/main/java/org/jbei/auth/hmac/HmacSignatureFactory.java new file mode 100644 index 000000000..f053203d3 --- /dev/null +++ b/src/main/java/org/jbei/auth/hmac/HmacSignatureFactory.java @@ -0,0 +1,269 @@ +/** + * + */ +package org.jbei.auth.hmac; + +import com.google.common.net.PercentEscaper; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.Header; +import org.apache.http.client.methods.HttpRequestBase; +import org.jbei.auth.KeyTable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.crypto.KeyGenerator; +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import javax.servlet.http.HttpServletRequest; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; +import java.nio.charset.Charset; +import java.security.*; +import java.util.*; + +/** + * Generates {@link HmacSignature} objects for use in authenticating requests to a REST service. By + * default this class will generate {@link HmacSignature} objects conforming to version 1 of the + * JBEI authentication specification. An HTTP Authorization header is set with the format + * {@code Version:KeyId:UserId:Signature}, with: + *
    + *
  • {@code Version = 1},
  • + *
  • {@code KeyId} is a string identifying the key used to sign the request,
  • + *
  • {@code UserId} is a string identifying the user (if any) the request is submitted on behalf + * of,
  • + *
  • {@code Signature} is a Base64-encoded string of the request content signed with the SHA-1 + * HMAC algorithm (specified in RFC 2104)
  • + *
+ * This class builds objects to generate the {@code Signature} portion of the header, given a + * request object and a {@code UserId}. The {@code Signature} is generated by constructing a string + * containing the following separated by a newline character: + *
    + *
  • {@code UserId}
  • + *
  • the HTTP Method (e.g. {@code GET}, {@code POST})
  • + *
  • the HTTP Host
  • + *
  • the request path
  • + *
  • the query string, sorted by natural UTF-8 byte ordering of parameter names
  • + *
  • the content of the request entity body
  • + *
+ * This constructed string is then signed with the key used to initialize this object. + * + * @author wcmorrell + * @version 1.0 + * @since 1.0 + */ +public class HmacSignatureFactory { + + private static final Charset UTF8 = Charset.forName("UTF-8"); + private static final Comparator QUERY_COMPARATOR = new Comparator() { + @Override + public int compare(final String a, final String b) { + return a.substring(0, a.indexOf("=")).compareTo(b.substring(0, b.indexOf("="))); + } + }; + private static final Logger log = LoggerFactory.getLogger(HmacSignatureFactory.class); + private static final PercentEscaper ESCAPER = new PercentEscaper("-_.~", false); + private static final String HMAC = "HmacSHA1"; + private static final String NEWLINE = "\n"; + + private final KeyTable table; + + /** + * Convenience method to create a new random key for signing requests. + * + * @return a SecretKey instance + * @throws NoSuchAlgorithmException if the system has no registered security providers able to generate the key + */ + public static Key createKey() throws NoSuchAlgorithmException { + final KeyGenerator keyGenerator = KeyGenerator.getInstance(HMAC); + keyGenerator.init(512, new SecureRandom()); + return keyGenerator.generateKey(); + } + + /** + * Convenience method to decode a key stored in Base64 encoding. + * + * @param encodedKey the encoded key String + * @return a SecretKey object for the decoded key + */ + public static Key decodeKey(final String encodedKey) { + return new SecretKeySpec(Base64.decodeBase64(encodedKey), HMAC); + } + + /** + * Convenience method to encode a key to a Base64 String. + * + * @param key the key to encode + * @return a Base64 String representation of the key + */ + public static String encodeKey(final Key key) { + return Base64.encodeBase64String(key.getEncoded()); + } + + /** + * Constructor initializes factory with the secret used to sign requests. + * + * @param table object used to look up keys for signing + */ + public HmacSignatureFactory(final KeyTable table) { + this.table = table; + } + + /** + * @param request a request received via Servlet API + * @param keyId the key identifier signing the request + * @param userId the user creating the request + * @return an {@link HmacSignature} initialized with the request headers; the request stream may + * need to be passed through {@link HmacSignature#filterInput(InputStream)} to calculate + * the correct signature + * @throws SignatureException if there is an error setting up the signature + */ + public HmacSignature buildSignature(final HttpServletRequest request, final String keyId, + final String userId) throws SignatureException { + try { + final Mac mac = Mac.getInstance(HMAC); + final Key key = table.getKey(keyId); + if (key != null) { + mac.init(key); + mac.update((buildRequestString(userId, request)).getBytes(UTF8)); + return new DefaultHmacSignature(mac, userId); + } + return null; + } catch (final InvalidKeyException | NoSuchAlgorithmException e) { + throw new SignatureException("Failed to initialize signature"); + } + } + + /** + * @param request a request to be sent via HttpClient API + * @param keyId the key identifier signing the request + * @param userId the user creating the request + * @return an {@link HmacSignature} initialized with the request headers; the request stream may + * need to be passed through {@link HmacSignature#filterOutput(OutputStream)} to + * calculate the correct signature + * @throws SignatureException if there is an error setting up the signature + */ + public HmacSignature buildSignature(final HttpRequestBase request, final String keyId, + final String userId) throws SignatureException { + try { + final Mac mac = Mac.getInstance(HMAC); + final Key key = table.getKey(keyId); + if (key != null) { + mac.init(key); + mac.update((buildRequestString(userId, request)).getBytes(UTF8)); + return new DefaultHmacSignature(mac, userId); + } + return null; + } catch (final InvalidKeyException | NoSuchAlgorithmException e) { + throw new SignatureException("Failed to initialize signature"); + } + } + + /** + * Builds initial signature object from raw individual components. + * + * @param keyId + * @param userId + * @param method + * @param host + * @param path + * @param params + * @return an {@link HmacSignature} initialized with the request headers; the request stream may + * need to be passed through {@link HmacSignature#filterOutput(OutputStream)} to + * calculate the correct signature + * @throws SignatureException + */ + public HmacSignature buildSignature(final String keyId, final String userId, + final String method, final String host, final String path, + final Map> params) throws SignatureException { + try { + final Mac mac = Mac.getInstance(HMAC); + final Key key = table.getKey(keyId); + if (key != null) { + mac.init(key); + mac.update((buildRequestString(userId, method, host, path, + extractAndSortParams(params))).getBytes(UTF8)); + return new DefaultHmacSignature(mac, userId); + } + return null; + } catch (final InvalidKeyException | NoSuchAlgorithmException e) { + throw new SignatureException("Failed to initialize signature"); + } + + } + + private List extractAndSortParams(final Map> params) { + final List encParams = new ArrayList(); + for (final Map.Entry> entry : params.entrySet()) { + final String name = ESCAPER.escape(entry.getKey()); + for (final String value : entry.getValue()) { + encParams.add(name + "=" + ESCAPER.escape(value)); + } + } + Collections.sort(encParams, QUERY_COMPARATOR); + return encParams; + } + + private List extractAndSortParams(final HttpServletRequest request) { + final List encParams = new ArrayList(); + for (final Map.Entry entry : request.getParameterMap().entrySet()) { + final String name = ESCAPER.escape(entry.getKey()); + for (final String value : entry.getValue()) { + encParams.add(name + "=" + ESCAPER.escape(value)); + } + } + Collections.sort(encParams, QUERY_COMPARATOR); + return encParams; + } + + private List extractAndSortParams(final HttpRequestBase request) { + final List encParams = new ArrayList(); + final String query = request.getURI().getRawQuery(); + if (query != null) { + // split on ampersand (&) + for (final String parameter : StringUtils.split(request.getURI().getRawQuery(), "&")) { + encParams.add(parameter); + } + } + Collections.sort(encParams, QUERY_COMPARATOR); + return encParams; + } + + private String buildRequestString(final String userId, final String method, final String host, + final String path, final List params) { + final StringBuilder buffer = new StringBuilder(); + final String sortedParams = StringUtils.join(params.iterator(), "&"); + buffer.append(userId).append(NEWLINE); + buffer.append(method).append(NEWLINE); + buffer.append(host).append(NEWLINE); + buffer.append(path).append(NEWLINE); + buffer.append(sortedParams).append(NEWLINE); + debugRequestString(buffer); + return buffer.toString(); + } + + private String buildRequestString(final String userId, final HttpServletRequest request) { + return buildRequestString(userId, request.getMethod(), request.getHeader("Host"), + request.getRequestURI(), extractAndSortParams(request)); + } + + private String buildRequestString(final String userId, final HttpRequestBase request) { + final Header host = request.getFirstHeader("Host"); + final URI uri = request.getURI(); + return buildRequestString(userId, request.getMethod(), host.getValue(), uri.getRawPath(), + extractAndSortParams(request)); + } + + private void debugRequestString(final StringBuilder buffer) { + if (log.isDebugEnabled()) { + final StringBuilder debug = new StringBuilder(); + debug.append("Constructed request string:").append(NEWLINE); + debug.append("-----BEGIN-----").append(NEWLINE); + debug.append(buffer).append(NEWLINE); + debug.append("----- END -----").append(NEWLINE); + log.debug(debug.toString()); + } + } + +} diff --git a/src/main/java/org/jbei/ice/ControllerException.java b/src/main/java/org/jbei/ice/ControllerException.java deleted file mode 100755 index 2e76b80b9..000000000 --- a/src/main/java/org/jbei/ice/ControllerException.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.jbei.ice; - -/** - * Exception class for controllers. - * - * @author Zinovii Dmytriv - */ -public class ControllerException extends Exception { - private static final long serialVersionUID = -4198825232452614310L; - - /** - * Base constructor. - */ - public ControllerException() { - } - - /** - * Create a new ControllerException with a message. - * - * @param message - */ - public ControllerException(String message) { - super(message); - } - - /** - * Create a new ControllerException with a {@link Throwable}. - * - * @param cause - */ - public ControllerException(Throwable cause) { - super(cause); - } - - /** - * Create a new ControllerException with a message and a {@link Throwable}. - * - * @param message - * @param cause - */ - public ControllerException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/src/main/java/org/jbei/ice/lib/account/AccountController.java b/src/main/java/org/jbei/ice/lib/account/AccountController.java index df89777c3..70d260736 100755 --- a/src/main/java/org/jbei/ice/lib/account/AccountController.java +++ b/src/main/java/org/jbei/ice/lib/account/AccountController.java @@ -1,15 +1,12 @@ package org.jbei.ice.lib.account; -import org.apache.commons.lang.StringUtils; -import org.jbei.ice.ControllerException; +import org.apache.commons.lang3.StringUtils; import org.jbei.ice.lib.account.authentication.AuthenticationException; import org.jbei.ice.lib.account.authentication.IAuthentication; -import org.jbei.ice.lib.account.authentication.InvalidCredentialsException; import org.jbei.ice.lib.account.authentication.LocalAuthentication; import org.jbei.ice.lib.account.model.Account; import org.jbei.ice.lib.account.model.AccountPreferences; import org.jbei.ice.lib.common.logging.Logger; -import org.jbei.ice.lib.dao.DAOException; import org.jbei.ice.lib.dao.DAOFactory; import org.jbei.ice.lib.dao.hibernate.AccountDAO; import org.jbei.ice.lib.dao.hibernate.AccountPreferencesDAO; @@ -25,10 +22,9 @@ /** * ABI to manipulate {@link Account} objects. - *

- * This class contains methods that wrap {@link org.jbei.ice.lib.dao.hibernate.AccountDAO} to manipulate {@link - * Account} - * objects. + *

+ * This class contains methods that wrap {@link org.jbei.ice.lib.dao.hibernate.AccountDAO} to + * manipulate {@link Account} objects. * * @author Timothy Ham, Zinovii Dmytriv, Hector Plahar */ @@ -40,28 +36,43 @@ public class AccountController { private final AccountDAO dao; private final AccountPreferencesDAO accountPreferencesDAO; + /** + * Default constructor. + */ public AccountController() { dao = DAOFactory.getAccountDAO(); accountPreferencesDAO = DAOFactory.getAccountPreferencesDAO(); } - public AccountTransfer updateAccount(String requester, long userId, AccountTransfer transfer) { - Account account = dao.get(userId); - if (!account.getEmail().equalsIgnoreCase(requester) && !isAdministrator(requester)) + /** + * @param requester + * @param userId + * @param transfer + * @return updated account object + */ + public AccountTransfer updateAccount(final String requester, final long userId, + final AccountTransfer transfer) { + final Account account = dao.get(userId); + if (!account.getEmail().equalsIgnoreCase(requester) && !isAdministrator(requester)) { return null; + } // if transfer has password then it is a password change - if (!StringUtils.isEmpty(transfer.getFirstName())) + if (!StringUtils.isEmpty(transfer.getFirstName())) { account.setFirstName(transfer.getFirstName()); + } - if (!StringUtils.isEmpty(transfer.getLastName())) + if (!StringUtils.isEmpty(transfer.getLastName())) { account.setLastName(transfer.getLastName()); + } - if (!StringUtils.isEmpty(transfer.getDescription())) + if (!StringUtils.isEmpty(transfer.getDescription())) { account.setDescription(transfer.getDescription()); + } - if (!StringUtils.isEmpty(transfer.getInstitution())) + if (!StringUtils.isEmpty(transfer.getInstitution())) { account.setInstitution(transfer.getInstitution()); + } return dao.update(account).toDataTransferObject(); } @@ -72,7 +83,7 @@ public AccountTransfer updateAccount(String requester, long userId, AccountTrans * @param id Database id of account * @return Account for the id */ - public Account get(long id) { + public Account get(final long id) { return dao.get(id); } @@ -80,47 +91,58 @@ public Account get(long id) { * Reset a user's password * * @param targetEmail email address of user account to be changed - * @return true if the user account is found with email specified in the parameter and password for it is - * successfully reset, false otherwise + * @return true if the user account is found with email specified in the parameter and password + * for it is successfully reset, false otherwise */ - public boolean resetPassword(String targetEmail) { + public boolean resetPassword(final String targetEmail) { Account account = getByEmail(targetEmail); - if (account == null) + if (account == null) { return false; + } try { - String newPassword = Utils.generateUUID().substring(24); - String encryptedNewPassword = AccountUtils.encryptNewUserPassword(newPassword, account.getSalt()); + final String newPassword = Utils.generateUUID().substring(24); + final String encryptedNewPassword = AccountUtils.encryptNewUserPassword(newPassword, + account.getSalt()); account.setPassword(encryptedNewPassword); account = dao.update(account); - AccountTransfer transfer = account.toDataTransferObject(); + final AccountTransfer transfer = account.toDataTransferObject(); transfer.setPassword(newPassword); - String url = Utils.getConfigValue(ConfigurationKey.URI_PREFIX); + final String url = Utils.getConfigValue(ConfigurationKey.URI_PREFIX); String projectName = Utils.getConfigValue(ConfigurationKey.PROJECT_NAME); - if (StringUtils.isEmpty(projectName)) + if (StringUtils.isEmpty(projectName)) { projectName = "ICE"; - String subject = projectName + " Password Reset"; + } + final String subject = projectName + " Password Reset"; String name = account.getFirstName(); if (StringUtils.isBlank(name)) { name = account.getLastName(); } - SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, MMM d, yyyy 'at' HH:mm aaa, z"); - - StringBuilder builder = new StringBuilder(); - builder.append("Dear ").append(name).append(",\n\n") - .append("The password for your ").append(projectName) - .append(" account (").append(targetEmail).append(") was reset on ") - .append(dateFormat.format(new Date())).append(".\nYour new temporary password is\n\n") - .append(newPassword).append("\n\n") - .append("Please go to the following link to login and change your password.\n\n") - .append("https://").append(url).append("/profile/").append(account.getId()) - .append("\n\nThank you."); + final SimpleDateFormat dateFormat = new SimpleDateFormat( + "EEE, MMM d, yyyy 'at' HH:mm aaa, z"); + + final StringBuilder builder = new StringBuilder(); + builder.append("Dear ") + .append(name) + .append(",\n\n") + .append("The password for your ") + .append(projectName) + .append(" account (") + .append(targetEmail) + .append(") was reset on ") + .append(dateFormat.format(new Date())) + .append(".\nYour new temporary password is\n\n") + .append(newPassword) + .append("\n\n") + .append("Please go to the following link to login and change your password.\n\n") + .append("https://").append(url).append("/profile/").append(account.getId()) + .append("\n\nThank you."); Emailer.send(account.getEmail(), subject, builder.toString()); - } catch (Exception ex) { + } catch (final Exception ex) { Logger.error(ex); return false; } @@ -130,34 +152,46 @@ public boolean resetPassword(String targetEmail) { /** * Updates account password associated the account email. It encrypts it before associating it * with the account + * + * @param userId + * @param transfer + * @return updated account object */ - public AccountTransfer updatePassword(String userId, AccountTransfer transfer) { - Account userAccount = getByEmail(transfer.getEmail()); - if (userAccount == null) - throw new IllegalArgumentException("Could not retrieve account by id " + transfer.getEmail()); + public AccountTransfer updatePassword(final String userId, final AccountTransfer transfer) { + final Account userAccount = getByEmail(transfer.getEmail()); + if (userAccount == null) { + throw new IllegalArgumentException("Could not retrieve account by id " + + transfer.getEmail()); + } - if (!isAdministrator(userId) && !userAccount.getEmail().equalsIgnoreCase(userId)) + if (!isAdministrator(userId) && !userAccount.getEmail().equalsIgnoreCase(userId)) { return null; + } - userAccount.setPassword(AccountUtils.encryptNewUserPassword(transfer.getPassword(), userAccount.getSalt())); + userAccount.setPassword(AccountUtils.encryptNewUserPassword(transfer.getPassword(), + userAccount.getSalt())); return dao.update(userAccount).toDataTransferObject(); } /** - * validates the account dto to ensure that the fields required (especially by the database) - * are present + * validates the account dto to ensure that the fields required (especially by the database) are + * present * * @param accountTransfer account dto for validation */ - private boolean validateRequiredAccountFields(AccountTransfer accountTransfer) { - if (accountTransfer.getFirstName() == null || accountTransfer.getFirstName().trim().isEmpty()) + private boolean validateRequiredAccountFields(final AccountTransfer accountTransfer) { + if (accountTransfer.getFirstName() == null + || accountTransfer.getFirstName().trim().isEmpty()) { return false; + } - if (accountTransfer.getLastName() == null || accountTransfer.getLastName().trim().isEmpty()) + if (accountTransfer.getLastName() == null || accountTransfer.getLastName().trim().isEmpty()) { return false; + } - if (accountTransfer.getEmail() == null || accountTransfer.getEmail().trim().isEmpty()) + if (accountTransfer.getEmail() == null || accountTransfer.getEmail().trim().isEmpty()) { return false; + } return true; } @@ -170,22 +204,22 @@ private boolean validateRequiredAccountFields(AccountTransfer accountTransfer) { * @param sendEmail whether to send account information (including password by email) * @return generated password */ - public AccountTransfer createNewAccount(AccountTransfer info, boolean sendEmail) { + public AccountTransfer createNewAccount(final AccountTransfer info, final boolean sendEmail) { // validate fields required by the database validateRequiredAccountFields(info); - String email = info.getEmail().trim(); + final String email = info.getEmail().trim(); if (getByEmail(email) != null) { Logger.error("Account with id \"" + email + "\" already exists"); return null; } // generate salt and encrypt password before storing - String salt = Utils.generateSaltForUserAccount(); - String newPassword = Utils.generateUUID().substring(24); - String encryptedPassword = AccountUtils.encryptNewUserPassword(newPassword, salt); + final String salt = Utils.generateSaltForUserAccount(); + final String newPassword = Utils.generateUUID().substring(24); + final String encryptedPassword = AccountUtils.encryptNewUserPassword(newPassword, salt); - Account account = AccountUtils.fromDTO(info); + final Account account = AccountUtils.fromDTO(info); account.setPassword(encryptedPassword); account.setSalt(salt); account.setCreationTime(Calendar.getInstance().getTime()); @@ -196,24 +230,25 @@ public AccountTransfer createNewAccount(AccountTransfer info, boolean sendEmail) return info; } - String subject = "Account created successfully"; - StringBuilder stringBuilder = new StringBuilder(); + final String subject = "Account created successfully"; + final StringBuilder stringBuilder = new StringBuilder(); String name = account.getFirstName(); if (StringUtils.isBlank(name)) { name = account.getLastName(); - if (StringUtils.isBlank(name)) + if (StringUtils.isBlank(name)) { name = email; + } } stringBuilder.append("Dear ").append(name).append(", ") - .append("\n\nThank you for creating a ") - .append(Utils.getConfigValue(ConfigurationKey.PROJECT_NAME)) - .append(" account. \nBy accessing ") - .append("this site with the password provided at the bottom ") - .append("you agree to the following terms:\n\n"); + .append("\n\nThank you for creating a ") + .append(Utils.getConfigValue(ConfigurationKey.PROJECT_NAME)) + .append(" account. \nBy accessing ") + .append("this site with the password provided at the bottom ") + .append("you agree to the following terms:\n\n"); - String terms = "Biological Parts IP Disclaimer: \n\n" + final String terms = "Biological Parts IP Disclaimer: \n\n" + "The JBEI Registry of Biological Parts Software is licensed under a standard BSD\n" + "license. Permission or license to use the biological parts registered in\n" + "the JBEI Registry of Biological Parts is not included in the BSD license\n" @@ -222,26 +257,28 @@ public AccountTransfer createNewAccount(AccountTransfer info, boolean sendEmail) + "Biological Parts will not infringe any patent or other proprietary right."; stringBuilder.append(terms); - stringBuilder.append("\n\nYour new password is: ") - .append(newPassword) - .append("\nYour login id is: ") - .append(info.getEmail()) - .append("\n\n"); + stringBuilder.append("\n\nYour new password is: ").append(newPassword) + .append("\nYour login id is: ").append(info.getEmail()).append("\n\n"); - String server = Utils.getConfigValue(ConfigurationKey.URI_PREFIX); - stringBuilder.append("Please remember to change your password by going to your profile page at \n\n") - .append("https://").append(server).append("/profile/").append(account.getId()) - .append("\n\nThank you."); + final String server = Utils.getConfigValue(ConfigurationKey.URI_PREFIX); + stringBuilder + .append("Please remember to change your password by going to your profile page at \n\n") + .append("https://").append(server).append("/profile/").append(account.getId()) + .append("\n\nThank you."); Emailer.send(info.getEmail(), subject, stringBuilder.toString()); info.setPassword(newPassword); return info; } + /** + * @return new admin account + */ public Account createAdminAccount() { Account adminAccount = getByEmail(ADMIN_ACCOUNT_EMAIL); - if (adminAccount != null) + if (adminAccount != null) { return adminAccount; + } adminAccount = new Account(); adminAccount.setEmail(ADMIN_ACCOUNT_EMAIL); @@ -250,11 +287,12 @@ public Account createAdminAccount() { adminAccount.setInitials(""); adminAccount.setInstitution(""); adminAccount.setSalt(Utils.generateSaltForUserAccount()); - adminAccount.setPassword(AccountUtils.encryptNewUserPassword(ADMIN_ACCOUNT_PASSWORD, adminAccount.getSalt())); + adminAccount.setPassword(AccountUtils.encryptNewUserPassword(ADMIN_ACCOUNT_PASSWORD, + adminAccount.getSalt())); adminAccount.setDescription("Administrator Account"); adminAccount.setIp(""); - Date currentTime = Calendar.getInstance().getTime(); + final Date currentTime = Calendar.getInstance().getTime(); adminAccount.setCreationTime(currentTime); adminAccount.setModificationTime(currentTime); adminAccount.setLastLoginTime(currentTime); @@ -268,19 +306,29 @@ public Account createAdminAccount() { * @param email unique identifier for account, typically email * @return {@link Account} */ - public Account getByEmail(String email) { + public Account getByEmail(final String email) { return dao.getByEmail(email); } - public long getAccountId(String email) { - Account account = dao.getByEmail(email); - if (account == null) + /** + * @param email an account identifier (usually email) + * @return database identifier of account matching account identifier (email) + * @throws IllegalArgumentException for an invalid account identifier + */ + public long getAccountId(final String email) { + final Account account = dao.getByEmail(email); + if (account == null) { throw new IllegalArgumentException("No account found with email " + email); + } return account.getId(); } - public AccountTransfer getAccountBySessionKey(String sessionKey) { - String userId = SessionHandler.getUserIdBySession(sessionKey); + /** + * @param sessionKey + * @return Account object matching a session key, or {@code null} + */ + public AccountTransfer getAccountBySessionKey(final String sessionKey) { + final String userId = SessionHandler.getUserIdBySession(sessionKey); if (userId == null) { Logger.warn("Could not retrieve user id for session " + sessionKey); return null; @@ -301,66 +349,67 @@ public AccountTransfer getAccountBySessionKey(String sessionKey) { * @param account * @return {@link Account} that has been saved. */ - public Account save(Account account) { + public Account save(final Account account) { account.setModificationTime(Calendar.getInstance().getTime()); - if (account.getSalt() == null || account.getSalt().isEmpty()) + if (account.getSalt() == null || account.getSalt().isEmpty()) { account.setSalt(Utils.generateSaltForUserAccount()); + } return dao.create(account); } + /** + * @param account + * @return {@code true} if an administrator account + */ + public boolean isAdministrator(final Account account) { + return isAdministrator(account.getEmail()); + } + /** * Check in the database if an account is a moderator. * * @param userId unique account identifier for user * @return True, if the account is a moderator. */ - public boolean isAdministrator(String userId) { - if (StringUtils.isEmpty(userId)) + public boolean isAdministrator(final String userId) { + if (StringUtils.isEmpty(userId)) { return false; + } - Account account = this.getByEmail(userId); + final Account account = getByEmail(userId); return account != null && account.getType() == AccountType.ADMIN; } /** * Authenticate a user in the database. - *

- * Using the {@link org.jbei.ice.lib.account.authentication.IAuthentication} specified in the settings file, - * authenticate - * the - * user, and return the sessionData + *

+ * Using the {@link org.jbei.ice.lib.account.authentication.IAuthentication} specified in the + * settings file, authenticate the user, and return the sessionData * * @param login * @param password * @param ip IP Address of the user. - * @throws InvalidCredentialsException + * @return the account identifier (email) on a successful login, otherwise {@code null} */ - public String authenticate(String login, String password, String ip) throws InvalidCredentialsException { - IAuthentication authentication = new LocalAuthentication(); + public String authenticate(final String login, final String password, final String ip) { + final IAuthentication authentication = new LocalAuthentication(); String email; try { email = authentication.authenticates(login.trim(), password); if (email == null) { - try { - Thread.sleep(2000); // sets 2 seconds delay on login to prevent login/password brute force hacking - } catch (InterruptedException ie) { - Logger.warn(ie.getMessage()); - } + loginFailureCooldown(); return null; } - } catch (AuthenticationException e2) { - try { - Thread.sleep(2000); // sets 2 seconds delay on login to prevent login/password brute force hacking - } catch (InterruptedException ie) { - Logger.warn(ie.getMessage()); - } + } catch (final AuthenticationException e2) { + loginFailureCooldown(); return null; } - Account account = dao.getByEmail(email); + final Account account = dao.getByEmail(email); if (account != null) { - AccountPreferences accountPreferences = accountPreferencesDAO.getAccountPreferences(account); + AccountPreferences accountPreferences = accountPreferencesDAO + .getAccountPreferences(account); if (accountPreferences == null) { accountPreferences = new AccountPreferences(); @@ -378,38 +427,63 @@ public String authenticate(String login, String password, String ip) throws Inva return null; } + private void loginFailureCooldown() { + // sets 2 seconds delay on login to prevent login/password brute force hacking + try { + Thread.sleep(2000); + } catch (final InterruptedException ie) { + Logger.warn(ie.getMessage()); + } + } + + /** + * Attempts to load the ICE Authentication Backend from database configuration. + * + * @return an IAuthentication backend + */ + public IAuthentication getAuthenticationBackend() { + final String clazzName = Utils.getConfigValue(ConfigurationKey.AUTHENTICATION_BACKEND); + try { + final Class clazz = Class.forName(clazzName); + if (IAuthentication.class.isAssignableFrom(clazz)) { + return (IAuthentication) clazz.newInstance(); + } + } catch (final ClassNotFoundException e) { + Logger.error("Failed to load class " + clazzName); + } catch (final InstantiationException e) { + Logger.error("Failed to instantiate class " + clazzName); + } catch (final IllegalAccessException e) { + Logger.error("Inaccessible class " + clazzName); + } + return new LocalAuthentication(); + } + /** * Authenticate a user in the database. - *

- * Using the {@link org.jbei.ice.lib.account.authentication.IAuthentication} specified in the settings file, - * authenticate - * the - * user, and return the sessionData + *

+ * Using the {@link org.jbei.ice.lib.account.authentication.IAuthentication} specified in the + * settings file, authenticate the user, and return the sessionData * * @param transfer user information containing the email and password to be used for authentication * If the sessionId field is set, it may or may not be used as the user's session id * @return {@link AccountTransfer} */ - public AccountTransfer authenticate(AccountTransfer transfer) { - String email; - try { - email = authenticate(transfer.getEmail(), transfer.getPassword(), ""); - } catch (InvalidCredentialsException e) { - Logger.error(e); - return null; - } + public AccountTransfer authenticate(final AccountTransfer transfer) { + final String email = authenticate(transfer.getEmail(), transfer.getPassword(), ""); - if (email == null) + if (email == null) { return null; + } - Account account = dao.getByEmail(email); - if (account == null) + final Account account = dao.getByEmail(email); + if (account == null) { return null; + } - AccountTransfer info = account.toDataTransferObject(); + final AccountTransfer info = account.toDataTransferObject(); info.setLastLogin(account.getLastLoginTime().getTime()); info.setId(account.getId()); - boolean isAdmin = isAdministrator(email); + final boolean isAdmin = isAdministrator(email); info.setAdmin(isAdmin); info.setSessionId(SessionHandler.createSessionForUser(email, transfer.getSessionId())); return info; @@ -420,7 +494,7 @@ public AccountTransfer authenticate(AccountTransfer transfer) { * * @param sessionKey unique session identifier */ - public void invalidate(String sessionKey) { + public void invalidate(final String sessionKey) { SessionHandler.invalidateSession(sessionKey); } @@ -429,15 +503,24 @@ public void invalidate(String sessionKey) { * * @param accountPreferences */ - public void saveAccountPreferences(AccountPreferences accountPreferences) { + public void saveAccountPreferences(final AccountPreferences accountPreferences) { accountPreferencesDAO.create(accountPreferences); } - public ArrayList getMatchingAccounts(String userId, String query, int limit) { - Set matches = dao.getMatchingAccounts(query, limit); - ArrayList result = new ArrayList<>(); - for (Account match : matches) { - AccountTransfer info = new AccountTransfer(); + /** + * @param userId + * @param query + * @param limit + * @return accounts matching the query + */ + public List getMatchingAccounts(final String userId, final String query, + final int limit) { + // TODO account object is never used? + getByEmail(userId); + final Set matches = dao.getMatchingAccounts(query, limit); + final ArrayList result = new ArrayList<>(); + for (final Account match : matches) { + final AccountTransfer info = new AccountTransfer(); info.setEmail(match.getEmail()); info.setFirstName(match.getFirstName()); info.setLastName(match.getLastName()); @@ -446,44 +529,56 @@ public ArrayList getMatchingAccounts(String userId, String quer return result; } - public AccountResults retrieveAccounts(String userId, int start, int limit, String sort, boolean asc) { + /** + * @param userId + * @param start + * @param limit + * @param sort + * @param asc + * @return window of results to all accounts + */ + public AccountResults retrieveAccounts(final String userId, final int start, final int limit, + final String sort, final boolean asc) { if (!isAdministrator(userId)) { Logger.warn(userId + " attempting to retrieve all user accounts without admin privileges"); return null; } - AccountResults results = new AccountResults(); - EntryController entryController = new EntryController(); - List accounts = dao.getAccounts(start, limit, sort, asc); + final AccountResults results = new AccountResults(); + final EntryController entryController = new EntryController(); + final List accounts = dao.getAccounts(start, limit, sort, asc); - ArrayList infos = new ArrayList<>(); - for (Account userAccount : accounts) { - AccountTransfer info = userAccount.toDataTransferObject(); - long count = entryController.getNumberOfOwnerEntries(userId, userAccount.getEmail()); + final List infos = new ArrayList<>(); + for (final Account userAccount : accounts) { + final AccountTransfer info = userAccount.toDataTransferObject(); + final long count = entryController.getNumberOfOwnerEntries(userId, + userAccount.getEmail()); info.setUserEntryCount(count); info.setAdmin(isAdministrator(userAccount.getEmail())); infos.add(info); } results.getResults().addAll(infos); - long count = dao.getAccountsCount(); + final long count = dao.getAccountsCount(); results.setResultCount(count); return results; } - public void removeMemberFromGroup(long id, String email) throws ControllerException { - Account account = getByEmail(email); - if (account == null) - throw new ControllerException("Could not find account " + email); + /** + * @param id + * @param email + */ + public void removeMemberFromGroup(final long id, final String email) { + final Account account = getByEmail(email); + if (account == null) { + throw new IllegalArgumentException("Could not find account " + email); + } - Group group = DAOFactory.getGroupDAO().get(id); - if (group == null) - throw new ControllerException("Could not find group " + id); - account.getGroups().remove(group); - try { - dao.update(account); - } catch (DAOException e) { - throw new ControllerException(e); + final Group group = DAOFactory.getGroupDAO().get(id); + if (group == null) { + throw new IllegalArgumentException("Could not find group " + id); } + account.getGroups().remove(group); + dao.update(account); } } diff --git a/src/main/java/org/jbei/ice/lib/account/AccountUtils.java b/src/main/java/org/jbei/ice/lib/account/AccountUtils.java index 3fa9e1bcf..42bd7a331 100755 --- a/src/main/java/org/jbei/ice/lib/account/AccountUtils.java +++ b/src/main/java/org/jbei/ice/lib/account/AccountUtils.java @@ -1,15 +1,16 @@ package org.jbei.ice.lib.account; -import java.security.NoSuchAlgorithmException; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.KeySpec; -import javax.crypto.SecretKeyFactory; -import javax.crypto.spec.PBEKeySpec; - +import org.apache.commons.lang3.StringUtils; import org.jbei.ice.lib.account.model.Account; import org.jbei.ice.lib.common.logging.Logger; import org.jbei.ice.lib.utils.Utils; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; + /** * Utility class for account management * @@ -30,7 +31,7 @@ public static String encryptPassword(String password, String userSalt) { } public static String encryptNewUserPassword(String password, String salt) { - if (password == null || password.trim().isEmpty() || salt == null || salt.trim().isEmpty()) + if (StringUtils.isEmpty(password) || StringUtils.isEmpty(salt)) throw new IllegalArgumentException("Password and salt cannot be empty"); KeySpec spec = new PBEKeySpec(password.toCharArray(), salt.getBytes(), 20000, 160); diff --git a/src/main/java/org/jbei/ice/lib/account/PreferencesController.java b/src/main/java/org/jbei/ice/lib/account/PreferencesController.java index c3658e1cd..291a8c937 100644 --- a/src/main/java/org/jbei/ice/lib/account/PreferencesController.java +++ b/src/main/java/org/jbei/ice/lib/account/PreferencesController.java @@ -1,14 +1,7 @@ package org.jbei.ice.lib.account; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; - -import org.jbei.ice.ControllerException; import org.jbei.ice.lib.account.model.Account; import org.jbei.ice.lib.account.model.Preference; -import org.jbei.ice.lib.dao.DAOException; import org.jbei.ice.lib.dao.DAOFactory; import org.jbei.ice.lib.dao.hibernate.AccountDAO; import org.jbei.ice.lib.dao.hibernate.PreferencesDAO; @@ -17,6 +10,11 @@ import org.jbei.ice.lib.dto.user.PreferenceKey; import org.jbei.ice.lib.dto.user.UserPreferences; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; + /** * Controller for managing user preferences. * @@ -32,15 +30,9 @@ public PreferencesController() { accountDAO = DAOFactory.getAccountDAO(); } - public HashMap retrieveAccountPreferences(Account account, ArrayList keys) - throws ControllerException { + public HashMap retrieveAccountPreferences(Account account, ArrayList keys) { HashMap preferences = new HashMap<>(); - ArrayList results; - try { - results = dao.getAccountPreferences(account, keys); - } catch (DAOException de) { - throw new ControllerException(de); - } + ArrayList results = dao.getAccountPreferences(account, keys); for (Preference preference : results) { PreferenceKey key = PreferenceKey.fromString(preference.getKey()); @@ -101,22 +93,9 @@ public HashMap retrieveUserPreferenceList(Account account, List< return dao.retrievePreferenceValues(account, values); } - // really an update - public boolean saveSetting(Account account, String key, String value) throws ControllerException { - try { - return dao.createOrUpdatePreference(account, key, value) != null; - } catch (DAOException e) { - throw new ControllerException(e); - } - } - - public Preference createPreference(Account account, String key, String value) throws ControllerException { + public Preference createPreference(Account account, String key, String value) { Preference preference = new Preference(account, key.toUpperCase(), value); - try { - return dao.create(preference); - } catch (DAOException e) { - throw new ControllerException(e); - } + return dao.create(preference); } public PreferenceInfo updatePreference(String requesterEmail, long userId, String key, String value) { diff --git a/src/main/java/org/jbei/ice/lib/account/SessionHandler.java b/src/main/java/org/jbei/ice/lib/account/SessionHandler.java index ec73026b2..e0e4a7316 100644 --- a/src/main/java/org/jbei/ice/lib/account/SessionHandler.java +++ b/src/main/java/org/jbei/ice/lib/account/SessionHandler.java @@ -1,13 +1,13 @@ package org.jbei.ice.lib.account; +import org.apache.commons.lang3.StringUtils; + import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; -import org.apache.commons.lang.StringUtils; - /** * Handler for user sessions. * diff --git a/src/main/java/org/jbei/ice/lib/bulkupload/BulkCSVUpload.java b/src/main/java/org/jbei/ice/lib/bulkupload/BulkCSVUpload.java index 0b06cfd29..fafc0ac4d 100644 --- a/src/main/java/org/jbei/ice/lib/bulkupload/BulkCSVUpload.java +++ b/src/main/java/org/jbei/ice/lib/bulkupload/BulkCSVUpload.java @@ -3,8 +3,9 @@ import com.opencsv.CSVParser; import org.apache.commons.io.IOUtils; import org.apache.commons.io.LineIterator; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.jbei.ice.lib.common.logging.Logger; +import org.jbei.ice.lib.dao.DAOFactory; import org.jbei.ice.lib.dto.StorageLocation; import org.jbei.ice.lib.dto.bulkupload.EntryField; import org.jbei.ice.lib.dto.bulkupload.SampleField; @@ -14,6 +15,7 @@ import org.jbei.ice.lib.dto.sample.PartSample; import org.jbei.ice.lib.dto.sample.SampleType; import org.jbei.ice.lib.entry.EntryUtil; +import org.jbei.ice.lib.entry.model.Entry; import java.io.FileInputStream; import java.io.IOException; @@ -71,7 +73,6 @@ public final long processUpload() throws IOException { } return uploadId; - } } @@ -198,6 +199,7 @@ List getBulkUploadDataFromFile(InputStream inputStream) throws I for (int i = 0; i < valuesArray.length; i += 1) { HeaderValue headerForColumn = headers.get(i); + // process sample information if (headerForColumn.isSampleField()) { // todo : move to another method if (partSample == null) @@ -236,6 +238,14 @@ List getBulkUploadDataFromFile(InputStream inputStream) throws I // todo break; + case EXISTING_PART_NUMBER: + Entry entry = DAOFactory.getEntryDAO().getByPartNumber(value); + if (entry == null) + throw new IOException("Could not locate part number \"" + value + "\" for linking"); + PartData toLink = entry.toDataTransferObject(); + data.getLinkedParts().add(toLink); + break; + default: partData = EntryUtil.setPartDataFromField(partData, value, field, isSubType); } diff --git a/src/main/java/org/jbei/ice/lib/bulkupload/BulkEntryCreator.java b/src/main/java/org/jbei/ice/lib/bulkupload/BulkEntryCreator.java index 50599582e..4c8e9d292 100644 --- a/src/main/java/org/jbei/ice/lib/bulkupload/BulkEntryCreator.java +++ b/src/main/java/org/jbei/ice/lib/bulkupload/BulkEntryCreator.java @@ -1,7 +1,7 @@ package org.jbei.ice.lib.bulkupload; import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.jbei.ice.ApplicationController; import org.jbei.ice.lib.access.Permission; import org.jbei.ice.lib.account.AccountController; @@ -17,10 +17,7 @@ import org.jbei.ice.lib.dto.entry.PartData; import org.jbei.ice.lib.dto.entry.Visibility; import org.jbei.ice.lib.dto.sample.PartSample; -import org.jbei.ice.lib.entry.EntryController; -import org.jbei.ice.lib.entry.EntryCreator; -import org.jbei.ice.lib.entry.EntryEditor; -import org.jbei.ice.lib.entry.EntryFactory; +import org.jbei.ice.lib.entry.*; import org.jbei.ice.lib.entry.attachment.Attachment; import org.jbei.ice.lib.entry.model.Entry; import org.jbei.ice.lib.entry.model.Strain; @@ -419,6 +416,7 @@ public boolean createEntries(String userId, long draftId, List d // check permissions authorization.expectWrite(userId, draft); SampleService sampleService = new SampleService(); + EntryAuthorization entryAuthorization = new EntryAuthorization(); for (PartWithSample partWithSample : data) { if (partWithSample == null) @@ -441,27 +439,41 @@ public boolean createEntries(String userId, long draftId, List d if (partData.getLinkedParts() != null && partData.getLinkedParts().size() > 0) { // create linked PartData linked = partData.getLinkedParts().get(0); - Entry linkedEntry = InfoToModelFactory.infoToEntry(linked); - if (linkedEntry != null) { - linkedEntry.setVisibility(Visibility.DRAFT.getValue()); - linkedEntry.setOwner(account.getFullName()); - linkedEntry.setOwnerEmail(account.getEmail()); - linkedEntry = entryDAO.create(linkedEntry); - linked.setId(linkedEntry.getId()); - linked.setModificationTime(linkedEntry.getModificationTime().getTime()); + // for existing the link already....exists so just verify + if (linked.getId() == 0) { + Entry linkedEntry = InfoToModelFactory.infoToEntry(linked); + if (linkedEntry != null) { + linkedEntry.setVisibility(Visibility.DRAFT.getValue()); + linkedEntry.setOwner(account.getFullName()); + linkedEntry.setOwnerEmail(account.getEmail()); + linkedEntry = entryDAO.create(linkedEntry); - addWritePermission(account, linkedEntry); + linked.setId(linkedEntry.getId()); + linked.setModificationTime(linkedEntry.getModificationTime().getTime()); - // check for attachments and sequences for linked entry - saveFiles(linked, linkedEntry, files); + addWritePermission(account, linkedEntry); - // link to main entry in the database - entry.getLinkedEntries().add(linkedEntry); + // check for attachments and sequences for linked entry + saveFiles(linked, linkedEntry, files); + entry.getLinkedEntries().add(linkedEntry); + } + } + + entry = entryDAO.create(entry); + + // attempt to get linked entry and add + if (linked.getId() != 0) { + Entry linkedEntry = entryDAO.get(linked.getId()); + if (linkedEntry != null && entryAuthorization.canWrite(userId, entry)) { + EntryLinks links = new EntryLinks(userId, entry.getId()); + links.addLink(linked, LinkType.CHILD); + } } + } else { + entry = entryDAO.create(entry); } - entry = entryDAO.create(entry); // check for pi String piEmail = entry.getPrincipalInvestigatorEmail(); if (StringUtils.isNotEmpty(piEmail)) { diff --git a/src/main/java/org/jbei/ice/lib/bulkupload/BulkUploadController.java b/src/main/java/org/jbei/ice/lib/bulkupload/BulkUploadController.java index dd8ab284c..14ca7e3f9 100755 --- a/src/main/java/org/jbei/ice/lib/bulkupload/BulkUploadController.java +++ b/src/main/java/org/jbei/ice/lib/bulkupload/BulkUploadController.java @@ -1,8 +1,7 @@ package org.jbei.ice.lib.bulkupload; import org.apache.commons.io.FileUtils; -import org.apache.commons.lang.StringUtils; -import org.jbei.ice.ControllerException; +import org.apache.commons.lang3.StringUtils; import org.jbei.ice.lib.access.Permission; import org.jbei.ice.lib.access.PermissionException; import org.jbei.ice.lib.access.PermissionsController; @@ -383,7 +382,7 @@ public BulkUploadInfo submitBulkImportDraft(String userId, long draftId) throws return null; } - public boolean revertSubmitted(Account account, long uploadId) throws ControllerException { + public boolean revertSubmitted(Account account, long uploadId) { boolean isAdmin = accountController.isAdministrator(account.getEmail()); if (!isAdmin) { Logger.warn(account.getEmail() + " attempting to revert submitted bulk upload " diff --git a/src/main/java/org/jbei/ice/lib/bulkupload/BulkUploadValidation.java b/src/main/java/org/jbei/ice/lib/bulkupload/BulkUploadValidation.java index c23610dd5..2e79d0b87 100755 --- a/src/main/java/org/jbei/ice/lib/bulkupload/BulkUploadValidation.java +++ b/src/main/java/org/jbei/ice/lib/bulkupload/BulkUploadValidation.java @@ -1,6 +1,6 @@ package org.jbei.ice.lib.bulkupload; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.jbei.ice.lib.dao.DAOFactory; import org.jbei.ice.lib.dto.bulkupload.EntryField; import org.jbei.ice.lib.dto.entry.EntryType; diff --git a/src/main/java/org/jbei/ice/lib/bulkupload/BulkZipUpload.java b/src/main/java/org/jbei/ice/lib/bulkupload/BulkZipUpload.java index 326b05e3f..1710c33c6 100644 --- a/src/main/java/org/jbei/ice/lib/bulkupload/BulkZipUpload.java +++ b/src/main/java/org/jbei/ice/lib/bulkupload/BulkZipUpload.java @@ -1,7 +1,7 @@ package org.jbei.ice.lib.bulkupload; import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.jbei.ice.lib.common.logging.Logger; import org.jbei.ice.lib.dto.entry.EntryType; import org.jbei.ice.lib.dto.entry.PartData; diff --git a/src/main/java/org/jbei/ice/lib/bulkupload/FileBulkUpload.java b/src/main/java/org/jbei/ice/lib/bulkupload/FileBulkUpload.java index 411ae19d6..e28aec4bc 100644 --- a/src/main/java/org/jbei/ice/lib/bulkupload/FileBulkUpload.java +++ b/src/main/java/org/jbei/ice/lib/bulkupload/FileBulkUpload.java @@ -48,24 +48,15 @@ public long process() throws IOException { throw new IOException("Unsupported file type " + fileName); } -// protected final BulkCSVUpload getUploadType(String userId, Path csvFilePath, EntryType addType) { -// switch (addType) { -// default: -// return new BulkCSVUpload(userId, csvFilePath, addType); -// -// case ARABIDOPSIS: -// return new ArabidopsisSeedUpload(userId, csvFilePath); -// } -// } - /** * Creates a CSV template for download based on the the type of entries * - * @param addType entry type that is to be uploaded - * @param linked type that is linked to this entry + * @param addType entry type that is to be uploaded + * @param linked optional type that is linked to this entry. Should be one of {@link EntryType} or null + * @param linkToExisting true, if addType is to be linked to an existing entry * @return byte array of the template or null if the headers for the type cannot be retrieved/is unsupported */ - public static byte[] getCSVTemplateBytes(EntryType addType, EntryType linked) { + public static byte[] getCSVTemplateBytes(EntryType addType, EntryType linked, boolean linkToExisting) { List headers = BulkCSVUploadHeaders.getHeadersForType(addType); if (headers == null) return null; @@ -85,17 +76,23 @@ public static byte[] getCSVTemplateBytes(EntryType addType, EntryType linked) { } // check linked - if (linked != null) { - headers = BulkCSVUploadHeaders.getHeadersForType(linked); - if (headers != null) { - for (int i = 0; i < headers.size(); i++) { - sb.append(","); - sb.append('"'); - EntryField header = headers.get(i); - sb.append(linked.getDisplay()).append(" ").append(header.getLabel()); - if (header.isRequired()) - sb.append("*"); - sb.append('"'); + if (linkToExisting) { + sb.append(","); + sb.append('"'); + sb.append("Existing Part Number"); + sb.append('"'); + } else { + if (linked != null) { + headers = BulkCSVUploadHeaders.getHeadersForType(linked); + if (headers != null) { + for (EntryField header : headers) { + sb.append(","); + sb.append('"'); + sb.append(linked.getDisplay()).append(" ").append(header.getLabel()); + if (header.isRequired()) + sb.append("*"); + sb.append('"'); + } } } } diff --git a/src/main/java/org/jbei/ice/lib/common/logging/Logger.java b/src/main/java/org/jbei/ice/lib/common/logging/Logger.java index 425860267..78f3b44ea 100755 --- a/src/main/java/org/jbei/ice/lib/common/logging/Logger.java +++ b/src/main/java/org/jbei/ice/lib/common/logging/Logger.java @@ -1,17 +1,15 @@ package org.jbei.ice.lib.common.logging; -import java.text.SimpleDateFormat; -import java.util.Date; -import javax.mail.MessagingException; - -import org.jbei.ice.ControllerException; import org.jbei.ice.lib.dao.DAOException; import org.jbei.ice.lib.dto.ConfigurationKey; import org.jbei.ice.lib.utils.Emailer; import org.jbei.ice.lib.utils.Utils; - import org.slf4j.LoggerFactory; +import javax.mail.MessagingException; +import java.text.SimpleDateFormat; +import java.util.Date; + /** * Logger for ICE. * @@ -50,7 +48,7 @@ public static void debug(String message) { } private static void sendEmail(String message, Throwable e) { - if (e instanceof MessagingException || e instanceof ControllerException || e instanceof DAOException || true) { + if (e instanceof MessagingException || e instanceof DAOException || true) { // if error is "Can't send email", there is no need to try to send email return; } diff --git a/src/main/java/org/jbei/ice/lib/dao/hibernate/ParameterDAO.java b/src/main/java/org/jbei/ice/lib/dao/hibernate/ParameterDAO.java index 4c121ec60..2edcffd46 100644 --- a/src/main/java/org/jbei/ice/lib/dao/hibernate/ParameterDAO.java +++ b/src/main/java/org/jbei/ice/lib/dao/hibernate/ParameterDAO.java @@ -1,8 +1,17 @@ package org.jbei.ice.lib.dao.hibernate; +import org.hibernate.Criteria; +import org.hibernate.criterion.Projections; +import org.hibernate.criterion.Restrictions; +import org.jbei.ice.lib.dto.entry.CustomField; +import org.jbei.ice.lib.entry.model.Entry; import org.jbei.ice.lib.entry.model.Parameter; +import java.util.List; + /** + * Data accessor object for handling {@link Parameter}s + * * @author Hector Plahar */ public class ParameterDAO extends HibernateRepository { @@ -11,4 +20,16 @@ public class ParameterDAO extends HibernateRepository { public Parameter get(long id) { return super.get(Parameter.class, id); } + + // filter by key value pairs + public List filter(List fields) { + Criteria criteria = currentSession().createCriteria(Parameter.class); + for (CustomField field : fields) { + criteria.add( + Restrictions.and( + Restrictions.eq("key", field.getName()), Restrictions.eq("value", field.getValue()))); + } + criteria.setProjection(Projections.property("entry")); + return criteria.list(); + } } diff --git a/src/main/java/org/jbei/ice/lib/dto/ConfigurationKey.java b/src/main/java/org/jbei/ice/lib/dto/ConfigurationKey.java index 666b15c87..6de1c7ea4 100755 --- a/src/main/java/org/jbei/ice/lib/dto/ConfigurationKey.java +++ b/src/main/java/org/jbei/ice/lib/dto/ConfigurationKey.java @@ -34,7 +34,9 @@ public enum ConfigurationKey implements IDataTransferModel { PASSWORD_CHANGE_ALLOWED("YES"), PROFILE_EDIT_ALLOWED("YES"), JOIN_WEB_OF_REGISTRIES("NO"), - WEB_OF_REGISTRIES_MASTER("registry.jbei.org"); + WEB_OF_REGISTRIES_MASTER("registry.jbei.org"), + + AUTHENTICATION_BACKEND("org.jbei.ice.lib.account.authentication.LocalAuthentication"); private String defaultValue; diff --git a/src/main/java/org/jbei/ice/lib/dto/bulkupload/EntryField.java b/src/main/java/org/jbei/ice/lib/dto/bulkupload/EntryField.java index d6160f8c4..97dff7370 100755 --- a/src/main/java/org/jbei/ice/lib/dto/bulkupload/EntryField.java +++ b/src/main/java/org/jbei/ice/lib/dto/bulkupload/EntryField.java @@ -43,7 +43,8 @@ public enum EntryField implements IDataTransferModel { GENERATION("Generation", false), SENT_TO_ABRC("Sent to ABRC?", false), PLANT_TYPE("Plant Type", false), - PARENTS("Parents", false); + PARENTS("Parents", false), + EXISTING_PART_NUMBER("Existing Part Number", false); private String label; private boolean required; diff --git a/src/main/java/org/jbei/ice/lib/dto/entry/CustomField.java b/src/main/java/org/jbei/ice/lib/dto/entry/CustomField.java index 87a2d4c7a..faf34b119 100755 --- a/src/main/java/org/jbei/ice/lib/dto/entry/CustomField.java +++ b/src/main/java/org/jbei/ice/lib/dto/entry/CustomField.java @@ -3,7 +3,7 @@ import org.jbei.ice.lib.dao.IDataTransferModel; /** - * User created fields for entry + * User created key/value pair fields for an entry. * * @author Hector Plahar */ @@ -17,6 +17,17 @@ public class CustomField implements IDataTransferModel { public CustomField() { } + public CustomField(String name, String value) { + this.name = name; + this.value = value; + } + + public CustomField(long partId, String name, String value) { + this.partId = partId; + this.name = name; + this.value = value; + } + public CustomField(long id, long partId, String name, String value) { this.id = id; this.name = name; diff --git a/src/main/java/org/jbei/ice/lib/dto/entry/CustomFields.java b/src/main/java/org/jbei/ice/lib/dto/entry/CustomFields.java index ee831ef9b..3e3eb0ca3 100644 --- a/src/main/java/org/jbei/ice/lib/dto/entry/CustomFields.java +++ b/src/main/java/org/jbei/ice/lib/dto/entry/CustomFields.java @@ -76,6 +76,20 @@ public List getFieldsForPart(String userId, long partId) { return result; } + public List getPartsByFields(String userId, List fields) { + // todo : performance + List entries = dao.filter(fields); + List parts = new ArrayList<>(); + for (Entry entry : entries) { + if (!authorization.canRead(userId, entry)) + continue; + + parts.add(entry.toDataTransferObject()); + } + + return parts; + } + /** * Deletes the custom field specified by the unique identifier in the parameter. * The user must have write privileges on field associated with entry diff --git a/src/main/java/org/jbei/ice/lib/dto/entry/PartData.java b/src/main/java/org/jbei/ice/lib/dto/entry/PartData.java index f09c6d1a3..8a3f5ec9b 100755 --- a/src/main/java/org/jbei/ice/lib/dto/entry/PartData.java +++ b/src/main/java/org/jbei/ice/lib/dto/entry/PartData.java @@ -16,6 +16,7 @@ public class PartData implements IDataTransferModel { private int index; private String recordId; + private String versionId; private String name; private String owner; private String ownerEmail; @@ -78,6 +79,14 @@ public void setRecordId(String recordId) { this.recordId = recordId; } + public String getVersionId() { + return versionId; + } + + public void setVersionId(String versionId) { + this.versionId = versionId; + } + public EntryType getType() { return type; } diff --git a/src/main/java/org/jbei/ice/lib/entry/EntryController.java b/src/main/java/org/jbei/ice/lib/entry/EntryController.java index 98180a262..aea72dacc 100755 --- a/src/main/java/org/jbei/ice/lib/entry/EntryController.java +++ b/src/main/java/org/jbei/ice/lib/entry/EntryController.java @@ -1,7 +1,7 @@ package org.jbei.ice.lib.entry; import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.jbei.ice.ApplicationController; import org.jbei.ice.lib.access.PermissionsController; import org.jbei.ice.lib.account.AccountController; diff --git a/src/main/java/org/jbei/ice/lib/entry/EntryCreator.java b/src/main/java/org/jbei/ice/lib/entry/EntryCreator.java index a3f9ea514..2ae2f7cea 100644 --- a/src/main/java/org/jbei/ice/lib/entry/EntryCreator.java +++ b/src/main/java/org/jbei/ice/lib/entry/EntryCreator.java @@ -1,6 +1,6 @@ package org.jbei.ice.lib.entry; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.jbei.ice.ApplicationController; import org.jbei.ice.lib.access.Permission; import org.jbei.ice.lib.account.model.Account; diff --git a/src/main/java/org/jbei/ice/lib/entry/EntryUtil.java b/src/main/java/org/jbei/ice/lib/entry/EntryUtil.java index ad7886bee..b1bc0aa7b 100755 --- a/src/main/java/org/jbei/ice/lib/entry/EntryUtil.java +++ b/src/main/java/org/jbei/ice/lib/entry/EntryUtil.java @@ -1,7 +1,7 @@ package org.jbei.ice.lib.entry; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.jbei.ice.lib.dto.ConfigurationKey; import org.jbei.ice.lib.dto.bulkupload.EntryField; import org.jbei.ice.lib.dto.entry.*; diff --git a/src/main/java/org/jbei/ice/lib/entry/attachment/AttachmentController.java b/src/main/java/org/jbei/ice/lib/entry/attachment/AttachmentController.java index 548dfa1b8..dc250c4a3 100755 --- a/src/main/java/org/jbei/ice/lib/entry/attachment/AttachmentController.java +++ b/src/main/java/org/jbei/ice/lib/entry/attachment/AttachmentController.java @@ -1,10 +1,8 @@ package org.jbei.ice.lib.entry.attachment; -import org.apache.commons.lang.StringUtils; -import org.jbei.ice.ControllerException; +import org.apache.commons.lang3.StringUtils; import org.jbei.ice.lib.access.PermissionException; import org.jbei.ice.lib.account.model.Account; -import org.jbei.ice.lib.dao.DAOException; import org.jbei.ice.lib.dao.DAOFactory; import org.jbei.ice.lib.dao.hibernate.AttachmentDAO; import org.jbei.ice.lib.dao.hibernate.EntryDAO; @@ -45,7 +43,7 @@ public AttachmentController() { * This file identifier is assigned on upload * * @return Attachment file if one is found with the identifier, null otherwise (including if the user making - * the request does not have read permissions on the entry that this attachment is associated with) + * the request does not have read permissions on the entry that this attachment is associated with) */ public File getAttachmentByFileId(String userId, String fileId) { Attachment attachment = dao.getByFileId(fileId); @@ -76,13 +74,10 @@ public String getFileName(String userId, String fileId) { * @param attachment attachment * @param inputStream The data stream of the file. * @return Saved attachment. - * @throws ControllerException */ - public Attachment save(Account account, Attachment attachment, InputStream inputStream) throws ControllerException { - if (attachment.getEntry().getVisibility() != Visibility.TRANSFERRED.getValue() && - !entryAuthorization.canWrite(account.getEmail(), attachment.getEntry())) { - throw new ControllerException("No permissions to save attachment!"); - } + public Attachment save(Account account, Attachment attachment, InputStream inputStream) { + if (attachment.getEntry().getVisibility() != Visibility.TRANSFERRED.getValue()) + entryAuthorization.expectWrite(account.getEmail(), attachment.getEntry()); if (attachment.getFileId() == null || attachment.getFileId().isEmpty()) { String fileId = Utils.generateUUID(); @@ -94,12 +89,7 @@ public Attachment save(Account account, Attachment attachment, InputStream input String dataDir = Utils.getConfigValue(ConfigurationKey.DATA_DIRECTORY); File attachmentDir = Paths.get(dataDir, attachmentDirName).toFile(); - - try { - return dao.save(attachmentDir, attachment, inputStream); - } catch (DAOException e) { - throw new ControllerException("Failed to save attachment!", e); - } + return dao.save(attachmentDir, attachment, inputStream); } /** diff --git a/src/main/java/org/jbei/ice/lib/entry/model/Entry.java b/src/main/java/org/jbei/ice/lib/entry/model/Entry.java index 440e5522e..da0d8a73a 100755 --- a/src/main/java/org/jbei/ice/lib/entry/model/Entry.java +++ b/src/main/java/org/jbei/ice/lib/entry/model/Entry.java @@ -1,6 +1,7 @@ package org.jbei.ice.lib.entry.model; import org.apache.lucene.analysis.core.LowerCaseFilterFactory; +import org.apache.lucene.analysis.pattern.PatternReplaceFilterFactory; import org.apache.lucene.analysis.standard.StandardTokenizerFactory; import org.hibernate.annotations.Type; import org.hibernate.search.annotations.*; @@ -83,6 +84,10 @@ tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class), filters = { @TokenFilterDef(factory = LowerCaseFilterFactory.class), + @TokenFilterDef(factory = PatternReplaceFilterFactory.class, params = { + @org.hibernate.search.annotations.Parameter(name = "pattern", value = "[_-]"), + @org.hibernate.search.annotations.Parameter(name = "replacement", value = " ") + }) }) @Table(name = "entries") @SequenceGenerator(name = "sequence", sequenceName = "entries_id_seq", allocationSize = 1) @@ -127,11 +132,12 @@ public class Entry implements IDataModel { private String alias; @Column(name = "name", length = 127) - @Field(store = Store.YES, boost = @Boost(4f)) + @Field(store = Store.YES, boost = @Boost(2f)) private String name; @Column(name = "part_number", length = 127) @Field(boost = @Boost(2f), store = Store.YES) + @Analyzer(definition = "customanalyzer") private String partNumber; @Column(name = "keywords", length = 127) diff --git a/src/main/java/org/jbei/ice/lib/entry/sequence/SequenceAnalysisController.java b/src/main/java/org/jbei/ice/lib/entry/sequence/SequenceAnalysisController.java index 222895868..442299934 100755 --- a/src/main/java/org/jbei/ice/lib/entry/sequence/SequenceAnalysisController.java +++ b/src/main/java/org/jbei/ice/lib/entry/sequence/SequenceAnalysisController.java @@ -4,7 +4,6 @@ import org.jbei.ice.lib.dao.DAOFactory; import org.jbei.ice.lib.dao.hibernate.TraceSequenceDAO; import org.jbei.ice.lib.dto.ConfigurationKey; -import org.jbei.ice.lib.entry.EntryAuthorization; import org.jbei.ice.lib.entry.model.Entry; import org.jbei.ice.lib.entry.model.Plasmid; import org.jbei.ice.lib.models.Sequence; @@ -36,13 +35,11 @@ public class SequenceAnalysisController { private final TraceSequenceDAO traceDao; - private final EntryAuthorization entryAuthorization; public static final String TRACES_DIR_NAME = "traces"; public SequenceAnalysisController() { traceDao = DAOFactory.getTraceSequenceDAO(); - entryAuthorization = new EntryAuthorization(); } /** diff --git a/src/main/java/org/jbei/ice/lib/entry/sequence/SequenceController.java b/src/main/java/org/jbei/ice/lib/entry/sequence/SequenceController.java index e1e795e7c..1746d10f9 100755 --- a/src/main/java/org/jbei/ice/lib/entry/sequence/SequenceController.java +++ b/src/main/java/org/jbei/ice/lib/entry/sequence/SequenceController.java @@ -1,10 +1,8 @@ package org.jbei.ice.lib.entry.sequence; import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.jbei.ice.ApplicationController; -import org.jbei.ice.ControllerException; -import org.jbei.ice.lib.access.PermissionException; import org.jbei.ice.lib.access.PermissionsController; import org.jbei.ice.lib.account.model.Account; import org.jbei.ice.lib.common.logging.Logger; @@ -194,10 +192,8 @@ protected Sequence update(String userId, Sequence sequence) { * Delete the {@link Sequence} in the database, then rebuild the search index. * * @param sequence - * @throws ControllerException - * @throws PermissionException */ - public void delete(Account account, Sequence sequence) throws ControllerException, PermissionException { + public void delete(Account account, Sequence sequence) { authorization.expectWrite(account.getEmail(), sequence.getEntry()); String tmpDir = new ConfigurationController().getPropertyValue(ConfigurationKey.TEMPORARY_DIRECTORY); dao.deleteSequence(sequence, tmpDir); @@ -234,14 +230,13 @@ public static DNASequence parse(String sequence) { * @param sequence * @param formatter * @return Text of a formatted sequence. - * @throws ControllerException */ - public String compose(Sequence sequence, IFormatter formatter) throws ControllerException { + public String compose(Sequence sequence, IFormatter formatter) { ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); try { formatter.format(sequence, byteStream); } catch (FormatterException | IOException e) { - throw new ControllerException(e); + Logger.error(e); } return byteStream.toString(); } diff --git a/src/main/java/org/jbei/ice/lib/entry/sequence/composers/formatters/SBOLVisitor.java b/src/main/java/org/jbei/ice/lib/entry/sequence/composers/formatters/SBOLVisitor.java index 83501bb6e..40b2c73b8 100644 --- a/src/main/java/org/jbei/ice/lib/entry/sequence/composers/formatters/SBOLVisitor.java +++ b/src/main/java/org/jbei/ice/lib/entry/sequence/composers/formatters/SBOLVisitor.java @@ -1,13 +1,6 @@ package org.jbei.ice.lib.entry.sequence.composers.formatters; -import java.net.URI; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.UUID; - +import org.apache.commons.lang3.StringUtils; import org.jbei.ice.lib.common.logging.Logger; import org.jbei.ice.lib.dto.ConfigurationKey; import org.jbei.ice.lib.entry.model.Entry; @@ -15,13 +8,10 @@ import org.jbei.ice.lib.models.Sequence; import org.jbei.ice.lib.models.SequenceFeature; import org.jbei.ice.lib.utils.Utils; +import org.sbolstandard.core.*; -import org.apache.commons.lang.StringUtils; -import org.sbolstandard.core.DnaComponent; -import org.sbolstandard.core.DnaSequence; -import org.sbolstandard.core.SBOLFactory; -import org.sbolstandard.core.SequenceAnnotation; -import org.sbolstandard.core.StrandType; +import java.net.URI; +import java.util.*; /** * @author Hector Plahar diff --git a/src/main/java/org/jbei/ice/lib/experiment/ExperimentController.java b/src/main/java/org/jbei/ice/lib/experiment/ExperimentController.java index 979c59971..65f5a92db 100644 --- a/src/main/java/org/jbei/ice/lib/experiment/ExperimentController.java +++ b/src/main/java/org/jbei/ice/lib/experiment/ExperimentController.java @@ -1,6 +1,6 @@ package org.jbei.ice.lib.experiment; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.jbei.ice.lib.dao.DAOFactory; import org.jbei.ice.lib.dao.hibernate.EntryDAO; import org.jbei.ice.lib.dao.hibernate.ExperimentDAO; diff --git a/src/main/java/org/jbei/ice/lib/folder/FolderController.java b/src/main/java/org/jbei/ice/lib/folder/FolderController.java index c10b6e34e..48eecae27 100755 --- a/src/main/java/org/jbei/ice/lib/folder/FolderController.java +++ b/src/main/java/org/jbei/ice/lib/folder/FolderController.java @@ -1,6 +1,6 @@ package org.jbei.ice.lib.folder; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.jbei.ice.lib.access.Permission; import org.jbei.ice.lib.access.PermissionException; import org.jbei.ice.lib.access.PermissionsController; diff --git a/src/main/java/org/jbei/ice/lib/group/GroupController.java b/src/main/java/org/jbei/ice/lib/group/GroupController.java index ba7a2190a..2a8f655e8 100755 --- a/src/main/java/org/jbei/ice/lib/group/GroupController.java +++ b/src/main/java/org/jbei/ice/lib/group/GroupController.java @@ -1,6 +1,6 @@ package org.jbei.ice.lib.group; -import org.jbei.ice.ControllerException; +import org.jbei.ice.lib.access.PermissionException; import org.jbei.ice.lib.account.AccountController; import org.jbei.ice.lib.account.AccountTransfer; import org.jbei.ice.lib.account.AccountType; @@ -177,16 +177,16 @@ public boolean updateGroup(String userId, UserGroup user) { return group != null; } - public UserGroup deleteGroup(Account account, UserGroup user) throws ControllerException { + public UserGroup deleteGroup(Account account, UserGroup user) { if (user.getType() == GroupType.PUBLIC && account.getType() != AccountType.ADMIN) { String errMsg = "Non admin " + account.getEmail() + " attempting to delete public group"; Logger.error(errMsg); - throw new ControllerException(errMsg); + throw new PermissionException(errMsg); } Group group = dao.get(user.getId()); if (group == null) { - throw new ControllerException("Could not find group to delete"); + throw new IllegalArgumentException("Could not find group to delete"); } if (group.getMembers() != null) { @@ -308,10 +308,6 @@ public ArrayList retrieveGroupMembers(String uuid, boolean incl return new ArrayList<>(result); } - public long retrieveGroupMemberCount(String uuid) throws ControllerException { - return dao.getMemberCount(uuid); - } - public AccountResults getAvailableAccounts(String userId, int offset, int limit, boolean asc, String sort) { Account account = accountController.getByEmail(userId); Set accounts = new HashSet<>(); diff --git a/src/main/java/org/jbei/ice/lib/message/MessageController.java b/src/main/java/org/jbei/ice/lib/message/MessageController.java index cd851333b..d88af552f 100644 --- a/src/main/java/org/jbei/ice/lib/message/MessageController.java +++ b/src/main/java/org/jbei/ice/lib/message/MessageController.java @@ -1,15 +1,9 @@ package org.jbei.ice.lib.message; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -import org.jbei.ice.ControllerException; import org.jbei.ice.lib.account.AccountTransfer; import org.jbei.ice.lib.account.AccountType; import org.jbei.ice.lib.account.model.Account; import org.jbei.ice.lib.common.logging.Logger; -import org.jbei.ice.lib.dao.DAOException; import org.jbei.ice.lib.dao.DAOFactory; import org.jbei.ice.lib.dao.hibernate.AccountDAO; import org.jbei.ice.lib.dao.hibernate.MessageDAO; @@ -18,6 +12,10 @@ import org.jbei.ice.lib.dto.message.MessageList; import org.jbei.ice.lib.group.Group; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + /** * @author Hector Plahar */ @@ -37,18 +35,13 @@ public MessageController() { * @param account account for user making the request. * @param id identifier for message to be marked as read * @return number of unread messages after marking message as read - * @throws ControllerException */ - public int markMessageAsRead(Account account, long id) throws ControllerException { - try { - Message message = dao.retrieveMessage(id); - message.setRead(true); - message.setDateRead(new Date(System.currentTimeMillis())); - dao.update(message); - return getNewMessageCount(account); - } catch (DAOException de) { - throw new ControllerException(de); - } + public int markMessageAsRead(Account account, long id) { + Message message = dao.retrieveMessage(id); + message.setRead(true); + message.setDateRead(new Date()); + dao.update(message); + return getNewMessageCount(account); } /** @@ -58,11 +51,10 @@ public int markMessageAsRead(Account account, long id) throws ControllerExceptio * @param sender account for user sending the message * @param info details of message including recipient(s) * @return false if the message fails to be sent to all the intended recipients - * @throws ControllerException */ - public boolean sendMessage(Account sender, MessageInfo info) throws ControllerException { + public boolean sendMessage(Account sender, MessageInfo info) { if (info == null || info.getAccounts().isEmpty() && info.getUserGroups().isEmpty()) - throw new ControllerException("Cannot send message"); + return false; boolean success = true; Message message = new Message(); @@ -98,11 +90,7 @@ public boolean sendMessage(Account sender, MessageInfo info) throws ControllerEx if (message.getDestinationAccounts().isEmpty() && message.getDestinationGroups().isEmpty()) return false; - try { - dao.create(message); - } catch (DAOException e) { - throw new ControllerException(e); - } + dao.create(message); return success; } @@ -135,11 +123,7 @@ public MessageList retrieveMessages(String requester, String owner, int start, i return messageList; } - public int getNewMessageCount(Account account) throws ControllerException { - try { - return dao.retrieveNewMessageCount(account); - } catch (DAOException de) { - throw new ControllerException(de); - } + public int getNewMessageCount(Account account) { + return dao.retrieveNewMessageCount(account); } } diff --git a/src/main/java/org/jbei/ice/lib/net/RemoteAccessController.java b/src/main/java/org/jbei/ice/lib/net/RemoteAccessController.java index a75be5941..d41a45ef3 100644 --- a/src/main/java/org/jbei/ice/lib/net/RemoteAccessController.java +++ b/src/main/java/org/jbei/ice/lib/net/RemoteAccessController.java @@ -1,6 +1,6 @@ package org.jbei.ice.lib.net; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.jbei.ice.lib.access.RemotePermission; import org.jbei.ice.lib.account.AccountTransfer; import org.jbei.ice.lib.common.logging.Logger; @@ -26,6 +26,7 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.UUID; /** @@ -56,14 +57,14 @@ public RemoteAccessController() { * null on exception */ @SuppressWarnings("unchecked") - public ArrayList getAvailableFolders(long partnerId) { + public List getAvailableFolders(long partnerId) { RemotePartner partner = this.remotePartnerDAO.get(partnerId); if (partner == null) return null; try { String restPath = "/rest/folders/public"; - return (ArrayList) iceRestClient.get(partner.getUrl(), restPath, ArrayList.class); + return iceRestClient.get(partner.getUrl(), restPath, ArrayList.class); } catch (Exception e) { Logger.error(e); return null; @@ -76,7 +77,7 @@ public Setting getMasterVersion() { return new Setting("version", ConfigurationKey.APPLICATION_VERSION.getDefaultValue()); // retrieve version - return (Setting) iceRestClient.get(value, "/rest/config/version"); + return iceRestClient.get(value, "/rest/config/version", Setting.class); } public void addPermission(String requester, RemoteAccessPermission permission) { @@ -116,11 +117,11 @@ public AccountTransfer getRemoteUser(long remoteId, String email) { if (partner == null) return null; - Object result = iceRestClient.get(partner.getUrl(), "/rest/users/" + email, AccountTransfer.class); + AccountTransfer result = iceRestClient.get(partner.getUrl(), "/rest/users/" + email, AccountTransfer.class); if (result == null) return null; - return (AccountTransfer) result; + return result; } public FolderDetails getPublicFolderEntries(long remoteId, long folderId, String sort, boolean asc, int offset, @@ -136,33 +137,33 @@ public FolderDetails getPublicFolderEntries(long remoteId, long folderId, String queryParams.put("limit", limit); queryParams.put("asc", asc); queryParams.put("sort", sort); - Object result = iceRestClient.get(partner.getUrl(), restPath, FolderDetails.class, queryParams); + FolderDetails result = iceRestClient.get(partner.getUrl(), restPath, FolderDetails.class, queryParams); if (result == null) return null; - return (FolderDetails) result; + return result; } catch (Exception e) { Logger.error("Error getting public folder entries from \"" + partner.getUrl() + "\": " + e.getMessage()); return null; } } - public ArrayList getRemotePartSamples(long remoteId, long partId) { + public List getRemotePartSamples(long remoteId, long partId) { RemotePartner partner = this.remotePartnerDAO.get(remoteId); if (partner == null) return null; String restPath = "/rest/parts/" + partId + "/samples"; - return (ArrayList) iceRestClient.get(partner.getUrl(), restPath, ArrayList.class); + return iceRestClient.get(partner.getUrl(), restPath, ArrayList.class); } - public ArrayList getRemotePartComments(long remoteId, long partId) { + public List getRemotePartComments(long remoteId, long partId) { RemotePartner partner = this.remotePartnerDAO.get(remoteId); if (partner == null) return null; String restPath = "/rest/parts/" + partId + "/comments"; - return (ArrayList) iceRestClient.get(partner.getUrl(), restPath, ArrayList.class); + return iceRestClient.get(partner.getUrl(), restPath, ArrayList.class); } public void transferEntries(String userId, long remoteId, EntrySelection selection) { @@ -188,18 +189,18 @@ public FeaturedDNASequence getRemoteSequence(long remoteId, long partId) { } } - public ArrayList getRemoteTraces(long remoteId, long partId) { + public List getRemoteTraces(long remoteId, long partId) { RemotePartner partner = this.remotePartnerDAO.get(remoteId); if (partner == null) return null; try { String restPath = "/rest/parts/" + partId + "/traces"; - Object result = iceRestClient.get(partner.getUrl(), restPath, ArrayList.class); + ArrayList result = iceRestClient.get(partner.getUrl(), restPath, ArrayList.class); if (result == null) return null; - return (ArrayList) result; + return result; } catch (Exception e) { Logger.error(e.getMessage()); return null; diff --git a/src/main/java/org/jbei/ice/lib/net/RemoteContact.java b/src/main/java/org/jbei/ice/lib/net/RemoteContact.java index 874ce4620..49b5ec026 100644 --- a/src/main/java/org/jbei/ice/lib/net/RemoteContact.java +++ b/src/main/java/org/jbei/ice/lib/net/RemoteContact.java @@ -1,6 +1,6 @@ package org.jbei.ice.lib.net; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.jbei.ice.lib.account.AccountController; import org.jbei.ice.lib.account.TokenHash; import org.jbei.ice.lib.common.logging.Logger; @@ -187,7 +187,7 @@ protected boolean apiKeyValidates(String myURL, RegistryPartner registryPartner) HashMap queryParams = new HashMap<>(); queryParams.put("url", myURL); - RegistryPartner response = (RegistryPartner) restClient.get(registryPartner.getUrl(), "/accesstoken/web", + RegistryPartner response = restClient.get(registryPartner.getUrl(), "/accesstoken/web", RegistryPartner.class, queryParams); if (response == null) { // todo : should retry up to a certain number of times Logger.error("Could not validate request"); diff --git a/src/main/java/org/jbei/ice/lib/net/RemoteEntries.java b/src/main/java/org/jbei/ice/lib/net/RemoteEntries.java index 57c1455a1..28b86a74e 100644 --- a/src/main/java/org/jbei/ice/lib/net/RemoteEntries.java +++ b/src/main/java/org/jbei/ice/lib/net/RemoteEntries.java @@ -20,6 +20,7 @@ import java.io.File; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; /** * Entries that are on other registry instances other than this instance. @@ -64,7 +65,7 @@ public WebEntries getPublicEntries(String userId, long remoteId, int offset, int queryParams.put("limit", limit); queryParams.put("asc", asc); queryParams.put("sort", sort); - details = (FolderDetails) iceRestClient.get(partner.getUrl(), restPath, FolderDetails.class, queryParams); + details = iceRestClient.get(partner.getUrl(), restPath, FolderDetails.class, queryParams); if (details == null) return null; } catch (Exception e) { @@ -80,7 +81,7 @@ public WebEntries getPublicEntries(String userId, long remoteId, int offset, int } @SuppressWarnings("unchecked") - public ArrayList getEntryAttachments(String userId, long remoteId, long entryId) { + public List getEntryAttachments(String userId, long remoteId, long entryId) { if (!hasRemoteAccessEnabled()) return null; @@ -89,7 +90,7 @@ public ArrayList getEntryAttachments(String userId, long remoteI return null; String path = "/rest/parts/" + entryId + "/attachments"; - return (ArrayList) iceRestClient.get(partner.getUrl(), path, ArrayList.class); + return iceRestClient.get(partner.getUrl(), path, ArrayList.class); } public FeaturedDNASequence getEntrySequence(String userId, long remoteId, long entryId) { @@ -101,7 +102,7 @@ public FeaturedDNASequence getEntrySequence(String userId, long remoteId, long e return null; String path = "/rest/parts/" + entryId + "/sequence"; - return (FeaturedDNASequence) iceRestClient.get(partner.getUrl(), path, FeaturedDNASequence.class); + return iceRestClient.get(partner.getUrl(), path, FeaturedDNASequence.class); } public void transferEntries(String userId, long remoteId, EntrySelection selection) { @@ -117,7 +118,7 @@ public PartData getPublicEntry(String userId, long remoteId, long entryId) { if (partner == null || partner.getPartnerStatus() != RemotePartnerStatus.APPROVED) return null; - return (PartData) iceRestClient.get(partner.getUrl(), "/rest/parts/" + entryId, PartData.class); + return iceRestClient.get(partner.getUrl(), "/rest/parts/" + entryId, PartData.class); } public PartData getPublicEntryTooltip(String userId, long remoteId, long entryId) { @@ -129,7 +130,7 @@ public PartData getPublicEntryTooltip(String userId, long remoteId, long entryId return null; String path = "/rest/parts/" + entryId + "/tooltip"; - return (PartData) iceRestClient.get(partner.getUrl(), path, PartData.class); + return iceRestClient.get(partner.getUrl(), path, PartData.class); } public PartStatistics getPublicEntryStatistics(String userId, long remoteId, long entryId) { @@ -141,7 +142,7 @@ public PartStatistics getPublicEntryStatistics(String userId, long remoteId, lon return null; String path = "/rest/parts/" + entryId + "/statistics"; - return (PartStatistics) iceRestClient.get(partner.getUrl(), path, PartStatistics.class); + return iceRestClient.get(partner.getUrl(), path, PartStatistics.class); } public FeaturedDNASequence getPublicEntrySequence(String userId, long remoteId, long entryId) { @@ -153,7 +154,7 @@ public FeaturedDNASequence getPublicEntrySequence(String userId, long remoteId, return null; String path = "/rest/parts/" + entryId + "/sequence"; - return (FeaturedDNASequence) iceRestClient.get(partner.getUrl(), path, FeaturedDNASequence.class); + return iceRestClient.get(partner.getUrl(), path, FeaturedDNASequence.class); } public File getPublicAttachment(String userId, long remoteId, String fileId) { diff --git a/src/main/java/org/jbei/ice/lib/net/RemoteTransfer.java b/src/main/java/org/jbei/ice/lib/net/RemoteTransfer.java index 6c90d1831..be1cf7aa6 100644 --- a/src/main/java/org/jbei/ice/lib/net/RemoteTransfer.java +++ b/src/main/java/org/jbei/ice/lib/net/RemoteTransfer.java @@ -1,6 +1,6 @@ package org.jbei.ice.lib.net; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.jbei.ice.lib.common.logging.Logger; import org.jbei.ice.lib.dao.DAOFactory; import org.jbei.ice.lib.dao.hibernate.EntryDAO; @@ -98,7 +98,7 @@ public void transferEntries(long remoteId, List entries) { for (PartData data : entries) { try { - Object object = client.put(url, "/rest/parts/transfer", data, PartData.class); + PartData object = client.put(url, "/rest/parts/transfer", data, PartData.class); if (object == null) { exceptionCount += 1; continue; diff --git a/src/main/java/org/jbei/ice/lib/net/TransferredParts.java b/src/main/java/org/jbei/ice/lib/net/TransferredParts.java index c254b1271..b358c9218 100644 --- a/src/main/java/org/jbei/ice/lib/net/TransferredParts.java +++ b/src/main/java/org/jbei/ice/lib/net/TransferredParts.java @@ -1,6 +1,6 @@ package org.jbei.ice.lib.net; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.jbei.ice.lib.common.logging.Logger; import org.jbei.ice.lib.dao.DAOFactory; import org.jbei.ice.lib.dao.hibernate.EntryDAO; diff --git a/src/main/java/org/jbei/ice/lib/net/WebOfRegistriesTask.java b/src/main/java/org/jbei/ice/lib/net/WebOfRegistriesTask.java index ef3732f05..11853d688 100644 --- a/src/main/java/org/jbei/ice/lib/net/WebOfRegistriesTask.java +++ b/src/main/java/org/jbei/ice/lib/net/WebOfRegistriesTask.java @@ -1,6 +1,6 @@ package org.jbei.ice.lib.net; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.jbei.ice.lib.account.TokenHash; import org.jbei.ice.lib.common.logging.Logger; import org.jbei.ice.lib.dao.DAOFactory; @@ -75,13 +75,13 @@ public void execute() { // contacts the master node for other ice instances protected RegistryPartner requestToJoin(String masterUrl, RegistryPartner partner) { IceRestClient restClient = IceRestClient.getInstance(); - return (RegistryPartner) restClient.post(masterUrl, "/rest/web/partner/remote", partner, RegistryPartner.class); + return restClient.post(masterUrl, "/rest/web/partner/remote", partner, RegistryPartner.class); } @SuppressWarnings("unchecked") protected List getWebOfRegistryPartners(String url) { IceRestClient restClient = IceRestClient.getInstance(); - return (ArrayList) restClient.get(url, "/rest/web/partners", ArrayList.class); + return restClient.get(url, "/rest/web/partners", ArrayList.class); } /** diff --git a/src/main/java/org/jbei/ice/lib/parsers/ABIParser.java b/src/main/java/org/jbei/ice/lib/parsers/ABIParser.java index a30bb8a6e..be5e94f22 100755 --- a/src/main/java/org/jbei/ice/lib/parsers/ABIParser.java +++ b/src/main/java/org/jbei/ice/lib/parsers/ABIParser.java @@ -1,10 +1,9 @@ package org.jbei.ice.lib.parsers; +import org.biojava.bio.symbol.SymbolList; import org.jbei.ice.lib.parsers.abi.ABITrace; import org.jbei.ice.lib.vo.DNASequence; -import org.biojava.bio.symbol.SymbolList; - /** * Parse ABI sequence trace file by wrappying BioJava. * @@ -19,12 +18,6 @@ public String getName() { return ABI_PARSER; } - @Override - public Boolean hasErrors() { - // This parser cannot succeed with errors, so always return false, or fail. - return false; - } - @Override public DNASequence parse(byte[] bytes) throws InvalidFormatParserException { DNASequence DNASequence = null; diff --git a/src/main/java/org/jbei/ice/lib/parsers/AbstractParser.java b/src/main/java/org/jbei/ice/lib/parsers/AbstractParser.java index 0b2a93259..f36f3a19a 100755 --- a/src/main/java/org/jbei/ice/lib/parsers/AbstractParser.java +++ b/src/main/java/org/jbei/ice/lib/parsers/AbstractParser.java @@ -1,13 +1,12 @@ package org.jbei.ice.lib.parsers; +import org.apache.commons.io.IOUtils; +import org.jbei.ice.lib.vo.DNASequence; + import java.io.File; import java.io.FileInputStream; import java.io.IOException; -import org.jbei.ice.lib.vo.DNASequence; - -import org.apache.commons.io.IOUtils; - /** * This class provides skeletal implementation of {@link IDNAParser} interface. * diff --git a/src/main/java/org/jbei/ice/lib/parsers/IDNAParser.java b/src/main/java/org/jbei/ice/lib/parsers/IDNAParser.java index e1975950c..5860814cd 100755 --- a/src/main/java/org/jbei/ice/lib/parsers/IDNAParser.java +++ b/src/main/java/org/jbei/ice/lib/parsers/IDNAParser.java @@ -1,11 +1,11 @@ package org.jbei.ice.lib.parsers; +import org.jbei.ice.lib.vo.DNASequence; + import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; -import org.jbei.ice.lib.vo.DNASequence; - /** * An object that parser sequences and generates an annotated {@link DNASequence} object. * @@ -19,13 +19,6 @@ public interface IDNAParser { */ String getName(); - /** - * Return true if parsing was completed, but there maybe errors in the parsing. - * - * @return True if parsing has errors. - */ - Boolean hasErrors(); - /** * Parse the given bytes to {@link DNASequence} annotated sequence. * @@ -44,7 +37,7 @@ public interface IDNAParser { * @throws IOException * @throws InvalidFormatParserException */ - DNASequence parse(File file) throws FileNotFoundException, IOException, InvalidFormatParserException; + DNASequence parse(File file) throws IOException, InvalidFormatParserException; /** * Parse the given string to {@link DNASequence} annotated sequence. diff --git a/src/main/java/org/jbei/ice/lib/parsers/PlainParser.java b/src/main/java/org/jbei/ice/lib/parsers/PlainParser.java index f1da9a9e9..d9884f0dc 100755 --- a/src/main/java/org/jbei/ice/lib/parsers/PlainParser.java +++ b/src/main/java/org/jbei/ice/lib/parsers/PlainParser.java @@ -1,12 +1,10 @@ package org.jbei.ice.lib.parsers; -import org.jbei.ice.lib.vo.DNASequence; - import org.biojava.bio.BioException; import org.biojava.bio.seq.DNATools; -import org.biojava.bio.symbol.IllegalSymbolException; import org.biojava.bio.symbol.SimpleSymbolList; import org.biojava.bio.symbol.SymbolList; +import org.jbei.ice.lib.vo.DNASequence; /** * Parser to handle file with simply nucleotide sequences. Technically these files are not FASTA @@ -17,12 +15,6 @@ public class PlainParser extends AbstractParser { private static final String PLAIN_PARSER = "Plain"; - @Override - public Boolean hasErrors() { - // This parser cannot succeed with errors, so always return false, or fail. - return false; - } - @Override public DNASequence parse(String textSequence) throws InvalidFormatParserException { textSequence = cleanSequence(textSequence); @@ -31,8 +23,6 @@ public DNASequence parse(String textSequence) throws InvalidFormatParserExceptio try { sl = new SimpleSymbolList(DNATools.getDNA().getTokenization("token"), textSequence .replaceAll("\\s+", "").replaceAll("[\\.|~]", "-").replaceAll("[0-9]", "")); - } catch (IllegalSymbolException e) { - throw new InvalidFormatParserException("Couldn't parse Plain sequence!", e); } catch (BioException e) { throw new InvalidFormatParserException("Couldn't parse Plain sequence!", e); } diff --git a/src/main/java/org/jbei/ice/lib/parsers/fasta/FastaParser.java b/src/main/java/org/jbei/ice/lib/parsers/fasta/FastaParser.java index 266ff1f59..0f9bb33f8 100755 --- a/src/main/java/org/jbei/ice/lib/parsers/fasta/FastaParser.java +++ b/src/main/java/org/jbei/ice/lib/parsers/fasta/FastaParser.java @@ -1,19 +1,18 @@ package org.jbei.ice.lib.parsers.fasta; -import java.io.BufferedReader; -import java.io.StringReader; -import java.util.LinkedList; - +import org.biojava.bio.BioException; +import org.biojavax.bio.seq.RichSequence; +import org.biojavax.bio.seq.RichSequence.IOTools; +import org.biojavax.bio.seq.RichSequenceIterator; import org.jbei.ice.lib.parsers.AbstractParser; import org.jbei.ice.lib.parsers.InvalidFormatParserException; import org.jbei.ice.lib.vo.DNAFeature; import org.jbei.ice.lib.vo.DNASequence; import org.jbei.ice.lib.vo.FeaturedDNASequence; -import org.biojava.bio.BioException; -import org.biojavax.bio.seq.RichSequence; -import org.biojavax.bio.seq.RichSequence.IOTools; -import org.biojavax.bio.seq.RichSequenceIterator; +import java.io.BufferedReader; +import java.io.StringReader; +import java.util.LinkedList; /** * Parse FASTA files. @@ -28,12 +27,6 @@ public String getName() { return FASTA_PARSER; } - @Override - public Boolean hasErrors() { - //This parser cannot succeed with errors, so always return false, or fail. - return false; - } - @Override public FeaturedDNASequence parse(String textSequence) throws InvalidFormatParserException { textSequence = cleanSequence(textSequence); diff --git a/src/main/java/org/jbei/ice/lib/parsers/genbank/IceGenbankParser.java b/src/main/java/org/jbei/ice/lib/parsers/genbank/IceGenbankParser.java index 0cef66d1e..55066f752 100755 --- a/src/main/java/org/jbei/ice/lib/parsers/genbank/IceGenbankParser.java +++ b/src/main/java/org/jbei/ice/lib/parsers/genbank/IceGenbankParser.java @@ -58,7 +58,6 @@ public class IceGenbankParser extends AbstractParser { private static final Pattern startStopPattern = Pattern.compile("[<>]*(\\d+)\\.\\.[<>]*(\\d+)"); private static final Pattern startOnlyPattern = Pattern.compile("\\d+"); - private Boolean hasErrors = false; private List errors = new ArrayList<>(); @Override @@ -66,11 +65,6 @@ public String getName() { return ICE_GENBANK_PARSER; } - @Override - public Boolean hasErrors() { - return hasErrors; - } - public List getErrors() { return errors; } @@ -351,7 +345,6 @@ private FeaturesTag parseFeaturesTag(final Tag tag) throws InvalidFormatParserEx } } catch (final NumberFormatException e) { getErrors().add("Could not parse feature " + line); - hasErrors = true; continue; } @@ -456,7 +449,6 @@ private DNAFeature parseQualifiers(final String block, DNAFeature dnaFeature) { chunk = line.split("="); if (chunk.length < 2) { getErrors().add("Skipping bad genbank qualifier " + line); - hasErrors = true; dnaFeatureNote = null; } else { final String putativeName = chunk[0].trim().substring(1); diff --git a/src/main/java/org/jbei/ice/lib/parsers/sbol/SBOLParser.java b/src/main/java/org/jbei/ice/lib/parsers/sbol/SBOLParser.java index 836132c3c..66e873043 100755 --- a/src/main/java/org/jbei/ice/lib/parsers/sbol/SBOLParser.java +++ b/src/main/java/org/jbei/ice/lib/parsers/sbol/SBOLParser.java @@ -1,17 +1,17 @@ package org.jbei.ice.lib.parsers.sbol; -import java.io.ByteArrayInputStream; -import java.io.IOException; - +import org.jbei.ice.lib.common.logging.Logger; import org.jbei.ice.lib.parsers.AbstractParser; import org.jbei.ice.lib.parsers.InvalidFormatParserException; import org.jbei.ice.lib.vo.DNASequence; - import org.sbolstandard.core.SBOLDocument; import org.sbolstandard.core.SBOLFactory; import org.sbolstandard.core.SBOLRootObject; import org.sbolstandard.core.SBOLValidationException; +import java.io.ByteArrayInputStream; +import java.io.IOException; + /** * Parse SBOL (v 1.1) files that are imported by the user * @@ -26,12 +26,6 @@ public String getName() { return SBOL_PARSER; } - @Override - public Boolean hasErrors() { - // This parser cannot succeed with errors, so always return false, or fail. - return false; - } - @Override public DNASequence parse(String textSequence) throws InvalidFormatParserException { try { @@ -47,6 +41,7 @@ public DNASequence parse(String textSequence) throws InvalidFormatParserExceptio return visitor.getFeaturedDNASequence(); } catch (SBOLValidationException | IOException e) { + Logger.error(e); throw new InvalidFormatParserException("Could not parse SBOL file!", e); } } diff --git a/src/main/java/org/jbei/ice/lib/search/HibernateSearch.java b/src/main/java/org/jbei/ice/lib/search/HibernateSearch.java index 27a644412..487461b26 100755 --- a/src/main/java/org/jbei/ice/lib/search/HibernateSearch.java +++ b/src/main/java/org/jbei/ice/lib/search/HibernateSearch.java @@ -1,6 +1,6 @@ package org.jbei.ice.lib.search; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.index.AtomicReaderContext; import org.apache.lucene.index.DocsEnum; @@ -35,7 +35,8 @@ import java.util.*; /** - * Apache Lucene full text library functionality in Hibernate + * Apache Lucene full text library functionality in Hibernate. + * Implemented as a singleton * * @author Hector Plahar */ @@ -58,54 +59,6 @@ public static HibernateSearch getInstance() { return SingletonHolder.INSTANCE; } - protected BooleanQuery generateQueriesForType(FullTextSession fullTextSession, HashSet fields, - BooleanQuery booleanQuery, String term, BooleanClause.Occur occur, BioSafetyOption option, - HashMap userBoost) { - QueryBuilder qb = fullTextSession.getSearchFactory().buildQueryBuilder().forEntity(Entry.class).get(); - if (!StringUtils.isEmpty(term)) { - // generate term queries for each search term - for (String field : fields) { - Query query; - - if (occur == BooleanClause.Occur.MUST) - query = qb.phrase().withSlop(3).onField(field).sentence(term).createQuery(); - else if (term.contains("*")) { - if (!field.equals("name")) - continue; - query = qb.keyword().wildcard().onField(field).matching(term).createQuery(); - } else - query = qb.keyword().fuzzy().withEditDistanceUpTo(1).onField(field).ignoreFieldBridge().matching( - term).createQuery(); - - Float boost = userBoost.get(field); - if (boost != null) - query.setBoost(boost); - - booleanQuery.add(query, BooleanClause.Occur.SHOULD); - } - - // visibility (using must not because "must for visibility ok" adds it as the query and affects the match - // the security filter takes care of other values not to be included such as "transferred" and "deleted" - Query visibilityQuery = qb.keyword().onField("visibility") - .matching(Visibility.DRAFT.getValue()).createQuery(); - booleanQuery.add(visibilityQuery, BooleanClause.Occur.MUST_NOT); - - Query visibilityQuery2 = qb.keyword().onField("visibility") - .matching(Visibility.DELETED.getValue()).createQuery(); - booleanQuery.add(visibilityQuery2, BooleanClause.Occur.MUST_NOT); - - // bio-safety level - if (option != null) { - TermContext levelContext = qb.keyword(); - Query biosafetyQuery = - levelContext.onField("bioSafetyLevel").ignoreFieldBridge() - .matching(option.getValue()).createQuery(); - booleanQuery.add(biosafetyQuery, BooleanClause.Occur.MUST); - } - } - return booleanQuery; - } - public SearchResults executeSearchNoTerms(String userId, HashMap blastResults, SearchQuery searchQuery) { ArrayList entryTypes = searchQuery.getEntryTypes(); if (entryTypes == null || entryTypes.isEmpty()) { @@ -271,15 +224,12 @@ public DocIdSet getDocIdSet(AtomicReaderContext context, Bits acceptDocs) throws SearchResults results = new SearchResults(); results.setResultCount(blastResults.size()); - - // sort only if the sort is a blast sort that cannot be handled by the query filter -// SearchResults.sort(searchQuery.getParameters().getSortField(), filtered); results.setResults(list); return results; } - public SearchResults executeSearch(String userId, HashMap terms, - SearchQuery searchQuery, HashMap userBoost, + public SearchResults executeSearch(String userId, HashMap terms, + SearchQuery searchQuery, HashMap blastResults) { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); int resultCount; @@ -292,14 +242,14 @@ public SearchResults executeSearch(String userId, HashMap[] classes = SearchFieldFactory.classesForTypes(searchQuery.getEntryTypes()); // generate queries for terms filtering stop words - for (Map.Entry entry : terms.entrySet()) { + for (Map.Entry entry : terms.entrySet()) { String term = cleanQuery(entry.getKey()); if (term.trim().isEmpty() || StandardAnalyzer.STOP_WORDS_SET.contains(term)) continue; BioSafetyOption safetyOption = searchQuery.getBioSafetyOption(); booleanQuery = generateQueriesForType(fullTextSession, fields, booleanQuery, term, entry.getValue(), - safetyOption, userBoost); + safetyOption); } // check for blast search results filter @@ -375,6 +325,52 @@ public SearchResults executeSearch(String userId, HashMap fields, + BooleanQuery booleanQuery, String term, QueryType type, + BioSafetyOption option) { + QueryBuilder qb = fullTextSession.getSearchFactory().buildQueryBuilder().forEntity(Entry.class).get(); + if (!StringUtils.isEmpty(term)) { + // generate term queries for each search term + Query query; + String[] queryFields = fields.toArray(new String[fields.size()]); + if (type == QueryType.PHRASE) { + // phrase types are for quotes so slop is omitted + for (String field : fields) { + booleanQuery.add(qb.phrase().onField(field).sentence(term).createQuery(), BooleanClause.Occur.SHOULD); + } + } else { + // term + if (term.contains("*")) { + query = qb.keyword().wildcard().onFields(SearchFieldFactory.getCommonFields()).matching(term).createQuery(); + booleanQuery.add(query, BooleanClause.Occur.SHOULD); + } else { + query = qb.keyword().fuzzy().onFields(queryFields).ignoreFieldBridge().matching(term).createQuery(); + booleanQuery.add(query, BooleanClause.Occur.MUST); + } + } + + // visibility (using must not because "must for visibility ok" adds it as the query and affects the match + // the security filter takes care of other values not to be included such as "transferred" and "deleted" + Query visibilityQuery = qb.keyword().onField("visibility") + .matching(Visibility.DRAFT.getValue()).createQuery(); + booleanQuery.add(visibilityQuery, BooleanClause.Occur.MUST_NOT); + + Query visibilityQuery2 = qb.keyword().onField("visibility") + .matching(Visibility.DELETED.getValue()).createQuery(); + booleanQuery.add(visibilityQuery2, BooleanClause.Occur.MUST_NOT); + + // bio-safety level + if (option != null) { + TermContext levelContext = qb.keyword(); + Query biosafetyQuery = levelContext.onField("bioSafetyLevel").ignoreFieldBridge() + .matching(option.getValue()).createQuery(); + booleanQuery.add(biosafetyQuery, BooleanClause.Occur.MUST); + } + } + return booleanQuery; + } + + protected Sort getSort(boolean asc, ColumnField sortField) { if (sortField == null) sortField = ColumnField.CREATED; @@ -503,9 +499,6 @@ protected static String cleanQuery(String query) { cleanedQuery = cleanedQuery.endsWith("'") ? cleanedQuery.substring(0, cleanedQuery.length() - 1) : cleanedQuery; cleanedQuery = (cleanedQuery.endsWith("\\") ? cleanedQuery.substring(0, cleanedQuery.length() - 1) : cleanedQuery); -// if (cleanedQuery.startsWith("*") || cleanedQuery.startsWith("?")) { -// cleanedQuery = cleanedQuery.substring(1); -// } return cleanedQuery; } } diff --git a/src/main/java/org/jbei/ice/lib/search/QueryType.java b/src/main/java/org/jbei/ice/lib/search/QueryType.java new file mode 100644 index 000000000..5d08b438c --- /dev/null +++ b/src/main/java/org/jbei/ice/lib/search/QueryType.java @@ -0,0 +1,9 @@ +package org.jbei.ice.lib.search; + +/** + * @author Hector Plahar + */ +public enum QueryType { + PHRASE, + TERM +} diff --git a/src/main/java/org/jbei/ice/lib/search/SearchController.java b/src/main/java/org/jbei/ice/lib/search/SearchController.java index b2179976b..881d723a3 100755 --- a/src/main/java/org/jbei/ice/lib/search/SearchController.java +++ b/src/main/java/org/jbei/ice/lib/search/SearchController.java @@ -1,7 +1,6 @@ package org.jbei.ice.lib.search; -import org.apache.commons.lang.StringUtils; -import org.apache.lucene.search.BooleanClause; +import org.apache.commons.lang3.StringUtils; import org.jbei.ice.lib.account.AccountController; import org.jbei.ice.lib.common.logging.Logger; import org.jbei.ice.lib.dao.DAOFactory; @@ -59,7 +58,7 @@ public SearchResults runWebSearch(SearchQuery query) { continue; try { - SearchResults results = (SearchResults) client.post(partner.getUrl(), "/rest/search", query, + SearchResults results = client.post(partner.getUrl(), "/rest/search", query, SearchResults.class); if (results == null) continue; @@ -70,7 +69,8 @@ public SearchResults runWebSearch(SearchQuery query) { resultsList.add(result); } - total += results.getResults().size(); + // up to 50 returned from each partner, but total size may be greater + total += results.getResultCount(); } catch (Exception e) { Logger.warn("Exception contacting partner " + partner.getUrl() + " : " + e.getMessage()); } @@ -114,7 +114,7 @@ public SearchResults runLocalSearch(String userId, SearchQuery query) { try { blastResults = BlastPlus.runBlast(query.getBlastQuery()); } catch (BlastException e) { - return null; + Logger.error("Exception running blast " + e.getMessage()); } } @@ -130,25 +130,13 @@ public SearchResults runLocalSearch(String userId, SearchQuery query) { // text query (may also include blast) // no filter type indicates a term or phrase query HibernateSearch hibernateSearch = HibernateSearch.getInstance(); - // List boostFields = Arrays.asList(SearchBoostField.values()); -// HashMap results = new PreferencesController().retrieveUserPreferenceList(account, boostFields); - HashMap mapping = new HashMap<>(); -// for (Map.Entry entry : results.entrySet()) { -// try { -// String field = SearchBoostField.valueOf(entry.getKey()).getField(); -// mapping.put(field, Float.valueOf(entry.getValue())); -// } catch (IllegalArgumentException nfe) { -// Logger.warn(nfe.getMessage()); -// } -// } if (!StringUtils.isEmpty(queryString)) { - HashMap terms = parseQueryString(queryString); - return hibernateSearch.executeSearch(userId, terms, query, mapping, blastResults); + HashMap terms = parseQueryString(queryString); + return hibernateSearch.executeSearch(userId, terms, query, blastResults); } else { return hibernateSearch.executeSearchNoTerms(userId, blastResults, query); } - } /** @@ -189,8 +177,8 @@ else if (type == IndexType.BLAST) { * @return a mapping of the phrases and terms to clauses that indicate how the matches should appear * in the document. Phrases must appear in the result document */ - HashMap parseQueryString(String queryString) { - HashMap terms = new HashMap<>(); + HashMap parseQueryString(String queryString) { + HashMap terms = new HashMap<>(); if (queryString == null || queryString.trim().length() == 0) return terms; @@ -201,7 +189,7 @@ HashMap parseQueryString(String queryString) { char c = queryString.charAt(i); if (c == '\"' || c == '\'') { if (startedPhrase) { - terms.put(builder.toString(), BooleanClause.Occur.MUST); + terms.put(builder.toString(), QueryType.PHRASE); builder = new StringBuilder(); startedPhrase = false; } else { @@ -216,7 +204,7 @@ HashMap parseQueryString(String queryString) { continue; if (!startedPhrase) { - terms.put(builder.toString(), BooleanClause.Occur.SHOULD); + terms.put(builder.toString(), QueryType.TERM); builder = new StringBuilder(); continue; } @@ -226,9 +214,9 @@ HashMap parseQueryString(String queryString) { } if (builder.length() > 0) { if (startedPhrase) - terms.put(builder.toString(), BooleanClause.Occur.MUST); + terms.put(builder.toString(), QueryType.PHRASE); else - terms.put(builder.toString(), BooleanClause.Occur.SHOULD); + terms.put(builder.toString(), QueryType.TERM); } return terms; diff --git a/src/main/java/org/jbei/ice/lib/search/blast/BlastPlus.java b/src/main/java/org/jbei/ice/lib/search/blast/BlastPlus.java index 038a81bb8..1e3a70fa8 100644 --- a/src/main/java/org/jbei/ice/lib/search/blast/BlastPlus.java +++ b/src/main/java/org/jbei/ice/lib/search/blast/BlastPlus.java @@ -2,8 +2,8 @@ import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.math.NumberUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.math.NumberUtils; import org.biojava.bio.seq.DNATools; import org.biojava.bio.seq.RNATools; import org.biojava.bio.symbol.IllegalSymbolException; diff --git a/src/main/java/org/jbei/ice/lib/search/filter/SearchFieldFactory.java b/src/main/java/org/jbei/ice/lib/search/filter/SearchFieldFactory.java index 1b77001b0..4cc6add80 100644 --- a/src/main/java/org/jbei/ice/lib/search/filter/SearchFieldFactory.java +++ b/src/main/java/org/jbei/ice/lib/search/filter/SearchFieldFactory.java @@ -38,6 +38,7 @@ public class SearchFieldFactory { commonFields.add("links.link"); commonFields.add("links.url"); commonFields.add("selectionMarkers.name"); + commonFields.add("parameters.value"); commonFields.add("fundingSource"); commonFields.add("principalInvestigator"); @@ -59,6 +60,10 @@ public class SearchFieldFactory { seedFields.add("plantType"); } + public static String[] getCommonFields() { + return commonFields.toArray(new String[commonFields.size()]); + } + public static HashSet entryFields(List types) { HashSet fields = new HashSet<>(); diff --git a/src/main/java/org/jbei/ice/lib/utils/Emailer.java b/src/main/java/org/jbei/ice/lib/utils/Emailer.java index ffe6a668c..c95e9d55b 100755 --- a/src/main/java/org/jbei/ice/lib/utils/Emailer.java +++ b/src/main/java/org/jbei/ice/lib/utils/Emailer.java @@ -1,12 +1,11 @@ package org.jbei.ice.lib.utils; -import org.jbei.ice.lib.common.logging.Logger; -import org.jbei.ice.lib.dto.ConfigurationKey; - -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.mail.Email; import org.apache.commons.mail.EmailException; import org.apache.commons.mail.SimpleEmail; +import org.jbei.ice.lib.common.logging.Logger; +import org.jbei.ice.lib.dto.ConfigurationKey; /** * Utility methods for email. diff --git a/src/main/java/org/jbei/ice/lib/utils/Utils.java b/src/main/java/org/jbei/ice/lib/utils/Utils.java index 40b25fba2..8598bc379 100755 --- a/src/main/java/org/jbei/ice/lib/utils/Utils.java +++ b/src/main/java/org/jbei/ice/lib/utils/Utils.java @@ -1,6 +1,6 @@ package org.jbei.ice.lib.utils; -import org.apache.commons.lang.RandomStringUtils; +import org.apache.commons.lang3.RandomStringUtils; import org.jbei.ice.lib.common.logging.Logger; import org.jbei.ice.lib.config.ConfigurationController; import org.jbei.ice.lib.dto.ConfigurationKey; diff --git a/src/main/java/org/jbei/ice/services/rest/AccessTokenResource.java b/src/main/java/org/jbei/ice/services/rest/AccessTokenResource.java index eee543624..57c2aa6e8 100644 --- a/src/main/java/org/jbei/ice/services/rest/AccessTokenResource.java +++ b/src/main/java/org/jbei/ice/services/rest/AccessTokenResource.java @@ -20,18 +20,18 @@ public class AccessTokenResource extends RestResource { private final AccountController accountController = new AccountController(); /** - * Creates a new access token for the user referenced in the parameter, after - * the credentials (username and password) are validated. If one already exists, it is - * invalidated + * Creates a new access token for the user referenced in the parameter, after the credentials + * (username and password) are validated. If one already exists, it is invalidated * - * @param transfer wraps username and password + * @param transfer + * wraps username and password * @return account information including a valid session id if credentials validate */ @POST @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - public Response create(AccountTransfer transfer) { - AccountTransfer info = accountController.authenticate(transfer); + public Response create(final AccountTransfer transfer) { + final AccountTransfer info = accountController.authenticate(transfer); if (info == null) { Logger.warn("Authentication failed for user " + transfer.getEmail()); return respond(Response.Status.UNAUTHORIZED); @@ -42,20 +42,20 @@ public Response create(AccountTransfer transfer) { } /** - * Invalidates the specified session information. - * - * @param sessionId session identifier to invalidates + * Invalidates the current session information. */ @DELETE public void deleteToken(@HeaderParam(AUTHENTICATION_PARAM_NAME) String sessionId) { - getUserIdFromSessionHeader(sessionId); + // ensure the user is valid + getUserId(); accountController.invalidate(sessionId); } /** * Retrieve account information for user referenced by session id * - * @param sessionId unique session identifier for logged in user + * @param sessionId + * unique session identifier for logged in user * @return account information for session if session is valid, null otherwise */ @GET diff --git a/src/main/java/org/jbei/ice/services/rest/ArrayDataJSONHandler.java b/src/main/java/org/jbei/ice/services/rest/ArrayDataJSONHandler.java index 80c7ec856..e43d9ac84 100644 --- a/src/main/java/org/jbei/ice/services/rest/ArrayDataJSONHandler.java +++ b/src/main/java/org/jbei/ice/services/rest/ArrayDataJSONHandler.java @@ -1,8 +1,15 @@ package org.jbei.ice.services.rest; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import org.jbei.ice.lib.dao.IDataTransferModel; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.ArrayList; import javax.ws.rs.Consumes; import javax.ws.rs.Produces; @@ -12,10 +19,11 @@ import javax.ws.rs.ext.MessageBodyReader; import javax.ws.rs.ext.MessageBodyWriter; import javax.ws.rs.ext.Provider; -import java.io.*; -import java.lang.annotation.Annotation; -import java.lang.reflect.Type; -import java.util.ArrayList; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import org.jbei.ice.lib.dao.IDataTransferModel; /** * @author Hector Plahar @@ -24,39 +32,43 @@ @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public class ArrayDataJSONHandler implements MessageBodyWriter>, - MessageBodyReader> { + MessageBodyReader> { @Override - public boolean isReadable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { + public boolean isReadable(final Class type, final Type genericType, + final Annotation[] annotations, final MediaType mediaType) { return true; } @Override - public ArrayList readFrom(Class> type, Type genericType, - Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, - InputStream entityStream) - throws IOException, WebApplicationException { - Gson gson = new GsonBuilder().create(); + public ArrayList readFrom(final Class> type, + final Type genericType, final Annotation[] annotations, final MediaType mediaType, + final MultivaluedMap httpHeaders, final InputStream entityStream) + throws IOException, WebApplicationException { + final Gson gson = new GsonBuilder().create(); try (Reader in = new InputStreamReader(entityStream)) { return gson.fromJson(in, type); } } @Override - public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { + public boolean isWriteable(final Class type, final Type genericType, + final Annotation[] annotations, final MediaType mediaType) { return true; } @Override - public long getSize(ArrayList iDataTransferModels, Class type, Type genericType, Annotation[] - annotations, MediaType mediaType) { + public long getSize(final ArrayList iDataTransferModels, + final Class type, final Type genericType, final Annotation[] annotations, + final MediaType mediaType) { return 0; } @Override - public void writeTo(ArrayList data, Class type, Type genericType, Annotation[] annotations, - MediaType mediaType, MultivaluedMap httpHeaders, OutputStream entityStream) - throws IOException, WebApplicationException { - Gson gson = new GsonBuilder().setPrettyPrinting().create(); + public void writeTo(final ArrayList data, final Class type, + final Type genericType, final Annotation[] annotations, final MediaType mediaType, + final MultivaluedMap httpHeaders, final OutputStream entityStream) + throws IOException, WebApplicationException { + final Gson gson = new GsonBuilder().setPrettyPrinting().create(); try (Writer out = new OutputStreamWriter(entityStream)) { gson.toJson(data, out); } diff --git a/src/main/java/org/jbei/ice/services/rest/AuthenticationInterceptor.java b/src/main/java/org/jbei/ice/services/rest/AuthenticationInterceptor.java new file mode 100644 index 000000000..ea4e064da --- /dev/null +++ b/src/main/java/org/jbei/ice/services/rest/AuthenticationInterceptor.java @@ -0,0 +1,123 @@ +/** + * + */ +package org.jbei.ice.services.rest; + +import org.apache.commons.lang3.StringUtils; +import org.jbei.auth.KeyTable; +import org.jbei.auth.hmac.HmacAuthorizor; +import org.jbei.auth.hmac.HmacSignature; +import org.jbei.auth.hmac.HmacSignatureFactory; +import org.jbei.ice.lib.common.logging.Logger; +import org.jbei.ice.lib.dao.hibernate.HibernateUtil; +import org.jbei.ice.lib.dto.ConfigurationKey; +import org.jbei.ice.lib.utils.Utils; + +import javax.annotation.Priority; +import javax.ws.rs.Priorities; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.UriInfo; +import javax.ws.rs.ext.ReaderInterceptor; +import javax.ws.rs.ext.ReaderInterceptorContext; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.nio.file.Paths; +import java.security.Key; + +/** + * Filter watches for Authorization headers on incoming requests, and passes along data to build an + * {@link HmacSignature} to validate the request. + * + * @author wcmorrell + * @version 4.2 + * @since 4.2 + */ +@Priority(Priorities.AUTHENTICATION) +public class AuthenticationInterceptor implements ContainerRequestFilter, ReaderInterceptor { + + /** + * Property/Attribute name for mapping the expected value of the generated signature. + */ + public static final String EXPECTED_SIGNATURE = "org.jbei.auth.signature"; + + /** + * Property/Attribute name for mapping the {@link HmacSignature} object for the request. + */ + public static final String HMAC_SIGNATURE = "org.jbei.auth.hmac"; + + // do lookup by using existing configuration DATA_DIRECTORY to find key names => key data + private static final KeyTable TABLE = new KeyTable() { + + // keys stored in /var/lib/tomcat6/data/rest-auth by default + private final File directory; + { + // need to force-create a transaction to get the DATA_DIRECTORY config value + HibernateUtil.beginTransaction(); + directory = Paths.get(Utils.getConfigValue(ConfigurationKey.DATA_DIRECTORY), + "rest-auth").toFile(); + HibernateUtil.commitTransaction(); + } + + @Override + public Key getKey(final String keyId) { + try { + // find file named by keyId in the directory + final File keyFile = new File(directory, keyId); + // collect all lines in the file to a buffer + final StringBuilder encoded = new StringBuilder(); + try (final FileReader reader = new FileReader(keyFile); + final BufferedReader buffered = new BufferedReader(reader);) { + String line; + while ((line = buffered.readLine()) != null) { + encoded.append(line); + } + // after reading all lines, decode value into a Key object + return HmacSignatureFactory.decodeKey(encoded.toString()); + } + } catch (final Throwable t) { + Logger.error("Failed to load rest-auth key " + keyId); + } + return null; + } + + }; + + private static final HmacAuthorizor AUTHORIZOR = new HmacAuthorizor(TABLE); + + @Override + public Object aroundReadFrom(final ReaderInterceptorContext context) throws IOException, + WebApplicationException { + final Object hmac = context.getProperty(HMAC_SIGNATURE); + if (hmac != null && hmac instanceof HmacSignature) { + context.setInputStream(((HmacSignature) hmac).filterInput(context.getInputStream())); + } + return context.proceed(); + } + + @Override + public void filter(final ContainerRequestContext requestContext) throws IOException { + final String hmac = requestContext.getHeaderString("Authorization"); + final String[] parts = StringUtils.split(hmac, ':'); + if (parts == null || parts.length == 0) { + Logger.debug("No Authorization header found on request"); + } else if (!"1".equals(parts[0]) || parts.length != 4) { + Logger.debug("Unknown Authorization header format"); + } else { + final UriInfo uriInfo = requestContext.getUriInfo(); + final MultivaluedMap params = uriInfo.getQueryParameters(false); + final String method = requestContext.getMethod(); + final String host = requestContext.getHeaderString("Host"); + final String path = uriInfo.getAbsolutePath().getPath(); // uriInfo path is relative + final HmacSignature sig = AUTHORIZOR.initSignature(hmac, method, host, path, params); + requestContext.setProperty(HMAC_SIGNATURE, sig); + requestContext.setProperty(EXPECTED_SIGNATURE, parts[3]); + + } + } + +} diff --git a/src/main/java/org/jbei/ice/services/rest/BulkUploadResource.java b/src/main/java/org/jbei/ice/services/rest/BulkUploadResource.java index 834af28af..433db2c28 100644 --- a/src/main/java/org/jbei/ice/services/rest/BulkUploadResource.java +++ b/src/main/java/org/jbei/ice/services/rest/BulkUploadResource.java @@ -1,9 +1,32 @@ package org.jbei.ice.services.rest; +import java.io.File; +import java.io.InputStream; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.DefaultValue; +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 javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import org.glassfish.jersey.media.multipart.FormDataParam; + import org.jbei.ice.lib.access.AuthorizationException; import org.jbei.ice.lib.bulkupload.BulkEntryCreator; import org.jbei.ice.lib.bulkupload.BulkUploadController; @@ -43,19 +66,26 @@ public class BulkUploadResource extends RestResource { private BulkUploadController controller = new BulkUploadController(); private BulkEntryCreator creator = new BulkEntryCreator(); + /** + * @param info + * @param id + * @param offset + * @param limit + * @return model object for bulk upload + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{id}") - public BulkUploadInfo read(@Context UriInfo info, @PathParam("id") long id, - @DefaultValue("0") @QueryParam("offset") int offset, - @DefaultValue("50") @QueryParam("limit") int limit, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String sessionId) { - String userId = getUserIdFromSessionHeader(sessionId); + public BulkUploadInfo read(@Context final UriInfo info, @PathParam("id") final long id, + @DefaultValue("0") @QueryParam("offset") final int offset, + @DefaultValue("50") @QueryParam("limit") final int limit) { + final String userId = getUserId(); try { - Logger.info(userId + ": retrieving bulk import with id \"" + id + "\" [" + offset + ", " + limit + "]"); + Logger.info(userId + ": retrieving bulk import with id \"" + id + "\" [" + offset + + ", " + limit + "]"); return controller.getBulkImport(userId, id, offset, limit); - } catch (Exception e) { + } catch (final Exception e) { return null; } } @@ -73,7 +103,7 @@ public Response getPartNumbersForUpload( @QueryParam("token") String token, @DefaultValue("8") @QueryParam("limit") int limit, @HeaderParam("X-ICE-Authentication-SessionId") String sessionId) { - getUserIdFromSessionHeader(sessionId); + String userId = getUserId(); ArrayList results = controller.getMatchingPartNumbersForLinks(uploadType, token, limit); return super.respond(results); } @@ -81,87 +111,107 @@ public Response getPartNumbersForUpload( /** * Retrieves permissions associated with an upload * - * @param sessionId unique session identifier for the user making request - * @param id unique identifier for the upload + * @param id + * unique identifier for the upload * @return retrieved permissions for specified upload if user has required permissions */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/permissions") - public Response getUploadPermissions(@PathParam("id") long id, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String sessionId) { - String userId = getUserIdFromSessionHeader(sessionId); - List permissionList = controller.getUploadPermissions(userId, id); + public Response getUploadPermissions(@PathParam("id") final long id) { + final String userId = getUserId(); + final List permissionList = controller.getUploadPermissions(userId, id); return super.respond(permissionList); } /** * Add upload permission * - * @param sessionId unique session identifier for the user making request - * @param id unique identifier for the upload + * @param id + * unique identifier for the upload + * @param accessPermission + * model object for permissions applied to upload + * @return Response with the added permission */ @POST @Path("/{id}/permissions") - public Response addPermission(@PathParam("id") long id, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String sessionId, - AccessPermission accessPermission) { - String userId = getUserIdFromSessionHeader(sessionId); - AccessPermission permission = controller.addPermission(userId, id, accessPermission); + public Response addPermission(@PathParam("id") final long id, + final AccessPermission accessPermission) { + final String userId = getUserId(); + final AccessPermission permission = controller.addPermission(userId, id, accessPermission); return super.respond(permission); } + /** + * @param id + * @param permissionId + * @return Response with success or failure of permissions delete + */ @DELETE @Path("/{id}/permissions/{pid}") - public Response removePermission(@PathParam("id") long id, - @PathParam("pid") long permissionId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String sessionId) { - String userId = getUserIdFromSessionHeader(sessionId); - boolean success = controller.deletePermission(userId, id, permissionId); + public Response removePermission(@PathParam("id") final long id, + @PathParam("pid") final long permissionId) { + final String userId = getUserId(); + final boolean success = controller.deletePermission(userId, id, permissionId); return super.respond(success); } + /** + * @param uploadId + * @param fileInputStream + * @param entryId + * @param contentDispositionHeader + * @return Response with sequence information if upload is successful + */ @POST @Path("/{id}/sequence") @Consumes(MediaType.MULTIPART_FORM_DATA) @Produces(MediaType.APPLICATION_JSON) - public Response uploadSequenceFile(@PathParam("id") long uploadId, - @FormDataParam("file") InputStream fileInputStream, - @FormDataParam("entryId") long entryId, - @FormDataParam("file") FormDataContentDisposition contentDispositionHeader, - @HeaderParam("X-ICE-Authentication-SessionId") String sessionId) { + public Response uploadSequenceFile(@PathParam("id") final long uploadId, + @FormDataParam("file") final InputStream fileInputStream, + @FormDataParam("entryId") final long entryId, + @FormDataParam("file") final FormDataContentDisposition contentDispositionHeader) { try { - String fileName = contentDispositionHeader.getFileName(); - String userId = super.getUserIdFromSessionHeader(sessionId); - String sequence = IOUtils.toString(fileInputStream); - SequenceInfo sequenceInfo = controller.addSequence(userId, uploadId, entryId, sequence, fileName); - if (sequenceInfo == null) + final String fileName = contentDispositionHeader.getFileName(); + final String userId = getUserId(); + final String sequence = IOUtils.toString(fileInputStream); + final SequenceInfo sequenceInfo = controller.addSequence(userId, uploadId, entryId, + sequence, fileName); + if (sequenceInfo == null) { return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); + } return Response.status(Response.Status.OK).entity(sequenceInfo).build(); - } catch (Exception e) { + } catch (final Exception e) { Logger.error(e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); } } + /** + * @param uploadId + * @param fileInputStream + * @param entryId + * @param contentDispositionHeader + * @return Response with information on attachment upload + */ @POST @Path("/{id}/attachment") @Consumes(MediaType.MULTIPART_FORM_DATA) @Produces(MediaType.APPLICATION_JSON) - public Response uploadAttachmentFile(@PathParam("id") long uploadId, - @FormDataParam("file") InputStream fileInputStream, - @FormDataParam("entryId") long entryId, - @FormDataParam("file") FormDataContentDisposition contentDispositionHeader, - @HeaderParam("X-ICE-Authentication-SessionId") String sessionId) { + public Response uploadAttachmentFile(@PathParam("id") final long uploadId, + @FormDataParam("file") final InputStream fileInputStream, + @FormDataParam("entryId") final long entryId, + @FormDataParam("file") final FormDataContentDisposition contentDispositionHeader) { try { - String fileName = contentDispositionHeader.getFileName(); - String userId = super.getUserIdFromSessionHeader(sessionId); - AttachmentInfo attachmentInfo = controller.addAttachment(userId, uploadId, entryId, fileInputStream, - fileName); - if (attachmentInfo == null) + final String fileName = contentDispositionHeader.getFileName(); + final String userId = getUserId(); + final AttachmentInfo attachmentInfo = controller.addAttachment(userId, uploadId, + entryId, fileInputStream, fileName); + if (attachmentInfo == null) { return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); + } return Response.status(Response.Status.OK).entity(attachmentInfo).build(); - } catch (Exception e) { + } catch (final Exception e) { Logger.error(e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); } @@ -170,131 +220,167 @@ public Response uploadAttachmentFile(@PathParam("id") long uploadId, /** * Creates or updates a list of entries that are to be associated with the specified bulk upload * + * @param id + * @param info + * * @return wrapper around list of created or updated entries */ @PUT @Produces(MediaType.APPLICATION_JSON) @Path("/{id}") - public BulkUploadInfo updateList(@HeaderParam(value = "X-ICE-Authentication-SessionId") String sessionId, - @PathParam("id") long id, BulkUploadInfo info) { - String userId = getUserIdFromSessionHeader(sessionId); + public BulkUploadInfo updateList(@PathParam("id") final long id, final BulkUploadInfo info) { + final String userId = getUserId(); return creator.createOrUpdateEntries(userId, id, info.getEntryList()); } + /** + * @return Response with pending upload information + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/pending") - public Response getPendingUploads(@HeaderParam(value = "X-ICE-Authentication-SessionId") String sessionId) { + public Response getPendingUploads() { try { - String userId = getUserIdFromSessionHeader(sessionId); - HashMap> pending = controller.getPendingImports(userId); + final String userId = getUserId(); + final HashMap> pending = controller + .getPendingImports(userId); return Response.status(Response.Status.OK).entity(pending).build(); - } catch (AuthorizationException ae) { + } catch (final AuthorizationException ae) { return respond(Response.Status.INTERNAL_SERVER_ERROR); } } + /** + * @param id + * @param info + * @return Response with upload information on renamed upload + */ @PUT @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/name") - public Response updateName(@PathParam("id") long id, BulkUploadInfo info, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String sessionId) { - String userId = getUserIdFromSessionHeader(sessionId); - Logger.info(userId + ": updating bulk upload name for " + info.getId() + " with value " + info.getName()); - BulkUploadInfo result = creator.renameBulkUpload(userId, id, info.getName()); - if (result == null) + public Response updateName(@PathParam("id") final long id, final BulkUploadInfo info) { + final String userId = getUserId(); + Logger.info(userId + ": updating bulk upload name for " + info.getId() + " with value " + + info.getName()); + final BulkUploadInfo result = creator.renameBulkUpload(userId, id, info.getName()); + if (result == null) { return respond(Response.Status.INTERNAL_SERVER_ERROR); + } return respond(Response.Status.OK, result); } + /** + * @param id + * @param info + * @return Response with updated bulk upload info + */ @PUT @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/status") - public Response updateStatus( - @PathParam("id") long id, - BulkUploadInfo info, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String sessionId) { - String userId = getUserIdFromSessionHeader(sessionId); - Logger.info(userId + ": updating bulk upload status for " + info.getId() + " to " + info.getStatus()); - BulkUploadInfo resp = creator.updateStatus(userId, id, info.getStatus()); - if (resp == null) + public Response updateStatus(@PathParam("id") final long id, final BulkUploadInfo info) { + final String userId = getUserId(); + Logger.info(userId + ": updating bulk upload status for " + info.getId() + " to " + + info.getStatus()); + final BulkUploadInfo resp = creator.updateStatus(userId, id, info.getStatus()); + if (resp == null) { return super.respond(Response.Status.BAD_REQUEST); + } return super.respond(resp); } + /** + * @param uploadId + * @param data + * @return Response with created part data + */ @PUT @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/entry") - public Response createEntry(@PathParam("id") long uploadId, - PartData data, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String sessionId) { + public Response createEntry(@PathParam("id") final long uploadId, final PartData data) { try { - String userId = getUserIdFromSessionHeader(sessionId); + final String userId = getUserId(); Logger.info(userId + ": adding entry to upload \"" + uploadId + "\""); - PartData result = creator.createEntry(userId, uploadId, data); + final PartData result = creator.createEntry(userId, uploadId, data); return respond(result); - } catch (Exception e) { + } catch (final Exception e) { return super.respond(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage()); } } + /** + * @param uploadId + * @param entryId + * @param data + * @return Response with updated part data + */ @POST @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/entry/{entryId}") - public Response updateEntry(@PathParam("id") long uploadId, - @PathParam("entryId") long entryId, - PartData data, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String sessionId) { + public Response updateEntry(@PathParam("id") final long uploadId, + @PathParam("entryId") final long entryId, final PartData data) { try { - String userId = getUserIdFromSessionHeader(sessionId); - Logger.info(userId + ": updating entry \"" + entryId + "\" for upload \"" + uploadId + "\""); - PartData result = creator.updateEntry(userId, uploadId, entryId, data); + final String userId = getUserId(); + Logger.info(userId + ": updating entry \"" + entryId + "\" for upload \"" + uploadId + + "\""); + final PartData result = creator.updateEntry(userId, uploadId, entryId, data); return respond(result); - } catch (Exception e) { + } catch (final Exception e) { return super.respond(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage()); } } + /** + * @param info + * @return a bulk upload info object + */ @PUT @Produces(MediaType.APPLICATION_JSON) - public BulkUploadInfo create(BulkUploadInfo info, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String sessionId) { - String userId = getUserIdFromSessionHeader(sessionId); + public BulkUploadInfo create(final BulkUploadInfo info) { + final String userId = getUserId(); Logger.info(userId + ": creating bulk upload draft"); return controller.create(userId, info); } + /** + * @return all bulk uploads for the current user + */ @GET @Produces(MediaType.APPLICATION_JSON) - public ArrayList query(@HeaderParam(value = "X-ICE-Authentication-SessionId") String sessionId) { - String userId = getUserIdFromSessionHeader(sessionId); + public ArrayList query() { + final String userId = getUserId(); Logger.info(userId + ": retrieving bulk upload drafts"); return controller.retrieveByUser(userId, userId); } + /** + * @param fileInputStream + * @param type + * @param contentDispositionHeader + * @return Response with the id of the imported bulk upload + */ @POST @Path("file") @Consumes(MediaType.MULTIPART_FORM_DATA) @Produces(MediaType.APPLICATION_JSON) - public Response post(@FormDataParam("file") InputStream fileInputStream, - @FormDataParam("type") String type, - @FormDataParam("file") FormDataContentDisposition contentDispositionHeader, - @HeaderParam("X-ICE-Authentication-SessionId") String sessionId) { + public Response post(@FormDataParam("file") final InputStream fileInputStream, + @FormDataParam("type") final String type, + @FormDataParam("file") final FormDataContentDisposition contentDispositionHeader) { try { - String userId = getUserIdFromSessionHeader(sessionId); - String fileName = userId + "-" + contentDispositionHeader.getFileName(); - File file = Paths.get(Utils.getConfigValue(ConfigurationKey.DATA_DIRECTORY), + final String userId = getUserId(); + final String fileName = userId + "-" + contentDispositionHeader.getFileName(); + final File file = Paths.get(Utils.getConfigValue(ConfigurationKey.DATA_DIRECTORY), "bulk-import", fileName).toFile(); FileUtils.copyInputStreamToFile(fileInputStream, file); - EntryType addType = EntryType.valueOf(type.toUpperCase()); - FileBulkUpload bulkUpload = new FileBulkUpload(userId, file.toPath(), addType); + final EntryType addType = EntryType.valueOf(type.toUpperCase()); + final FileBulkUpload bulkUpload = new FileBulkUpload(userId, file.toPath(), addType); // converted to string because there is no messagebodywriter for json for long - String importId = Long.toString(bulkUpload.process()); + final String importId = Long.toString(bulkUpload.process()); return Response.status(Response.Status.OK).entity(importId).build(); } catch (IOException e) { Logger.error(e); - return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()) + .build(); } } @@ -302,46 +388,56 @@ public Response post(@FormDataParam("file") InputStream fileInputStream, @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/entry/{entryId}") public Response deleteEntry(@PathParam("id") long uploadId, - @PathParam("entryId") long entryId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String sessionId) { + @PathParam("entryId") long entryId) { try { - String userId = getUserIdFromSessionHeader(sessionId); - if (controller.deleteEntry(userId, uploadId, entryId)) + final String userId = getUserId(); + if (controller.deleteEntry(userId, uploadId, entryId)) { return Response.ok().build(); + } return Response.serverError().build(); - } catch (Exception e) { + } catch (final Exception e) { return super.respond(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage()); } } + /** + * @param uploadId + * @param entryId + * @return OK response if sequence is deleted + */ @DELETE @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/entry/{entryId}/sequence") - public Response deleteEntrySequence(@PathParam("id") long uploadId, - @PathParam("entryId") long entryId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String sessionId) { + public Response deleteEntrySequence(@PathParam("id") final long uploadId, + @PathParam("entryId") final long entryId) { try { - String userId = getUserIdFromSessionHeader(sessionId); - if (new SequenceController().deleteSequence(userId, entryId)) + final String userId = getUserId(); + if (new SequenceController().deleteSequence(userId, entryId)) { return Response.ok().build(); + } return Response.serverError().build(); - } catch (Exception e) { + } catch (final Exception e) { return super.respond(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage()); } } + /** + * @param uploadId + * @param entryId + * @return OK response if attachment is deleted + */ @DELETE @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/entry/{entryId}/attachment") - public Response deleteEntryAttachment(@PathParam("id") long uploadId, - @PathParam("entryId") long entryId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String sessionId) { + public Response deleteEntryAttachment(@PathParam("id") final long uploadId, + @PathParam("entryId") final long entryId) { try { - String userId = getUserIdFromSessionHeader(sessionId); - if (controller.deleteAttachment(userId, uploadId, entryId)) + final String userId = getUserId(); + if (controller.deleteAttachment(userId, uploadId, entryId)) { return Response.ok().build(); + } return Response.serverError().build(); - } catch (Exception e) { + } catch (final Exception e) { return super.respond(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage()); } } diff --git a/src/main/java/org/jbei/ice/services/rest/ConfigResource.java b/src/main/java/org/jbei/ice/services/rest/ConfigResource.java index cd02200a6..fdd9e72d6 100644 --- a/src/main/java/org/jbei/ice/services/rest/ConfigResource.java +++ b/src/main/java/org/jbei/ice/services/rest/ConfigResource.java @@ -24,57 +24,71 @@ public class ConfigResource extends RestResource { /** * Retrieves list of system settings available * - * @param sessionId Session Id for user - * @return list of retrieved system settings that can be changed (including those with no values) + * @return list of retrieved system settings that can be changed (including those with no + * values) */ @GET @Produces(MediaType.APPLICATION_JSON) - public ArrayList get(@HeaderParam(value = "X-ICE-Authentication-SessionId") String sessionId) { - String userId = getUserIdFromSessionHeader(sessionId); + public ArrayList get() { + final String userId = getUserId(); return controller.retrieveSystemSettings(userId); } + /** + * @param uriInfo + * @return the version setting of this ICE instance + */ @GET @Path("/version") @Produces(MediaType.APPLICATION_JSON) - public Setting getVersion(@Context UriInfo uriInfo) { - String url = uriInfo.getBaseUri().getAuthority(); + public Setting getVersion(@Context final UriInfo uriInfo) { + final String url = uriInfo.getBaseUri().getAuthority(); return controller.getSystemVersion(url); } /** * Retrieves the value for the specified config key * + * @param key config key * @return setting containing the passed key and associated value if found */ @GET @Path("/{key}") @Produces(MediaType.APPLICATION_JSON) - public Setting getConfig(@PathParam("key") String key) { + public Setting getConfig(@PathParam("key") final String key) { return controller.getPropertyValue(key); } + /** + * @return Response specifying success or failure of re-index + */ @PUT @Path("/lucene") - public Response buildLuceneIndex(@HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String userId = getUserIdFromSessionHeader(userAgentHeader); - boolean success = searchController.rebuildIndexes(userId, IndexType.LUCENE); + public Response buildLuceneIndex() { + final String userId = getUserId(); + final boolean success = searchController.rebuildIndexes(userId, IndexType.LUCENE); return super.respond(success); } + /** + * @return Response specifying success or failure of re-index + */ @PUT @Path("/blast") - public Response buildBlastIndex(@HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String userId = getUserIdFromSessionHeader(userAgentHeader); - boolean success = searchController.rebuildIndexes(userId, IndexType.BLAST); + public Response buildBlastIndex() { + final String userId = getUserId(); + final boolean success = searchController.rebuildIndexes(userId, IndexType.BLAST); return super.respond(success); } + /** + * @param setting a config value to update + * @return the updated config key:value + */ @PUT @Produces(MediaType.APPLICATION_JSON) - public Setting update(@HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader, - Setting setting) { - String userId = getUserIdFromSessionHeader(userAgentHeader); + public Setting update(final Setting setting) { + final String userId = getUserId(); return controller.updateSetting(userId, setting); } } diff --git a/src/main/java/org/jbei/ice/services/rest/CustomFieldResource.java b/src/main/java/org/jbei/ice/services/rest/CustomFieldResource.java index f841a2726..1d7177508 100644 --- a/src/main/java/org/jbei/ice/services/rest/CustomFieldResource.java +++ b/src/main/java/org/jbei/ice/services/rest/CustomFieldResource.java @@ -4,8 +4,8 @@ import org.jbei.ice.lib.dto.entry.CustomFields; import javax.ws.rs.*; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; +import javax.ws.rs.core.*; +import java.util.ArrayList; import java.util.List; /** @@ -18,6 +18,31 @@ public class CustomFieldResource extends RestResource { private CustomFields fields = new CustomFields(); + @GET + @Path("/parts") + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + public Response getPartByCustomFields( + @Context UriInfo uriInfo, + @HeaderParam(value = "X-ICE-Authentication-SessionId") String sid) { + MultivaluedMap queryParams = uriInfo.getQueryParameters(); + if (queryParams.isEmpty()) + return super.respond(new ArrayList<>()); + + String userId = getUserId(sid); + + List fieldList = new ArrayList<>(); + for (String key : queryParams.keySet()) { + List values = queryParams.get(key); + // currently disallowing multiple values + // todo : disjunction for same values + CustomField field = new CustomField(key, values.get(0)); + fieldList.add(field); + } + + return super.respond(fields.getPartsByFields(userId, fieldList)); + } + /** * Creates a new custom field and associated it with the specified entry. * There are two ways to specify the entry to associate the field with: @@ -38,7 +63,7 @@ public Response create( @HeaderParam(value = "X-ICE-Authentication-SessionId") String sid, @QueryParam(value = "partId") long partId, CustomField customField) { - String userId = getUserIdFromSessionHeader(sid); + String userId = getUserId(sid); if (partId > 0 && customField.getPartId() > 0 && partId != customField.getPartId()) { throw new WebApplicationException("Inconsistent part Ids", Response.Status.BAD_REQUEST); } @@ -46,6 +71,9 @@ public Response create( if (partId <= 0) partId = customField.getPartId(); + if (partId <= 0) + throw new WebApplicationException("Invalid part id", Response.Status.BAD_REQUEST); + if (fields.createField(userId, partId, customField) > 0) return super.respond(Response.Status.CREATED); return super.respond(false); @@ -57,7 +85,7 @@ public Response create( public Response get( @HeaderParam(value = "X-ICE-Authentication-SessionId") String sid, @PathParam(value = "id") long id) { - String userId = getUserIdFromSessionHeader(sid); + String userId = getUserId(sid); return super.respond(fields.getField(userId, id)); } @@ -67,7 +95,7 @@ public Response get( public Response list( @HeaderParam(value = "X-ICE-Authentication-SessionId") String sid, @QueryParam(value = "partId") long partId) { - String userId = getUserIdFromSessionHeader(sid); + String userId = getUserId(sid); List result = fields.getFieldsForPart(userId, partId); return super.respond(result); } @@ -79,7 +107,7 @@ public Response list( public Response update(@HeaderParam(value = "X-ICE-Authentication-SessionId") String sid, @PathParam(value = "id") long id, CustomField customField) { - String userId = getUserIdFromSessionHeader(sid); + String userId = getUserId(sid); return respond(fields.updateField(userId, id, customField)); } @@ -88,7 +116,7 @@ public Response update(@HeaderParam(value = "X-ICE-Authentication-SessionId") St public Response delete( @HeaderParam(value = "X-ICE-Authentication-SessionId") String sid, @PathParam(value = "id") long id) { - String userId = getUserIdFromSessionHeader(sid); + String userId = getUserId(sid); boolean success = fields.deleteField(userId, id); return super.respond(success); } diff --git a/src/main/java/org/jbei/ice/services/rest/ExtensionToMimeType.java b/src/main/java/org/jbei/ice/services/rest/ExtensionToMimeType.java new file mode 100644 index 000000000..14da2b737 --- /dev/null +++ b/src/main/java/org/jbei/ice/services/rest/ExtensionToMimeType.java @@ -0,0 +1,26 @@ +package org.jbei.ice.services.rest; + +/** + * Helper class which provides utility methods for converting extensions to mime types + * + * @author Hector Plahar + */ +public class ExtensionToMimeType { + + /** + * Determines the appropriate mime type (from list of hardcoded values) using the provided extension. + * + * @param extension file extension used to determine mimetype. Should not include the . + *

example: csv not .csv + * @return the appropriate content mimet ype or a default + */ + public static String getMimeType(String extension) { + switch (extension) { + case "csv": + return "text/csv"; + + default: + return "application/octet-stream"; + } + } +} diff --git a/src/main/java/org/jbei/ice/services/rest/FileResource.java b/src/main/java/org/jbei/ice/services/rest/FileResource.java index b711bdd5a..263979a35 100644 --- a/src/main/java/org/jbei/ice/services/rest/FileResource.java +++ b/src/main/java/org/jbei/ice/services/rest/FileResource.java @@ -2,7 +2,7 @@ import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import org.glassfish.jersey.media.multipart.FormDataParam; import org.jbei.ice.lib.account.SessionHandler; @@ -44,6 +44,9 @@ public class FileResource extends RestResource { private SequenceController sequenceController = new SequenceController(); private AttachmentController attachmentController = new AttachmentController(); + /** + * @return Response with attachment info on uploaded file + */ @POST @Path("attachment") @Consumes(MediaType.MULTIPART_FORM_DATA) @@ -52,38 +55,45 @@ public Response post(@FormDataParam("file") InputStream fileInputStream, @FormDataParam("file") FormDataContentDisposition contentDispositionHeader, @HeaderParam("X-ICE-Authentication-SessionId") String sessionId) { try { - getUserIdFromSessionHeader(sessionId); - String fileName = contentDispositionHeader.getFileName(); - String fileId = Utils.generateUUID(); - File attachmentFile = Paths.get(Utils.getConfigValue(ConfigurationKey.DATA_DIRECTORY), + final String fileName = contentDispositionHeader.getFileName(); + final String fileId = Utils.generateUUID(); + final File attachmentFile = Paths.get( + Utils.getConfigValue(ConfigurationKey.DATA_DIRECTORY), AttachmentController.attachmentDirName, fileId).toFile(); FileUtils.copyInputStreamToFile(fileInputStream, attachmentFile); - AttachmentInfo info = new AttachmentInfo(); + final AttachmentInfo info = new AttachmentInfo(); info.setFileId(fileId); info.setFilename(fileName); return Response.status(Response.Status.OK).entity(info).build(); - } catch (IOException e) { + } catch (final IOException e) { Logger.error(e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); } } + protected Response addHeaders(Response.ResponseBuilder response, String fileName) { + response.header("Content-Disposition", "attachment; filename=\"" + fileName + "\""); + int dotIndex = fileName.lastIndexOf('.') + 1; + if (dotIndex == 0) + return response.build(); + + String mimeType = ExtensionToMimeType.getMimeType(fileName.substring(dotIndex)); + response.header("Content-Type", mimeType + "; name=\"" + fileName + "\""); + return response.build(); + } + /** * Retrieves a temp file by fileId */ @GET @Path("tmp/{fileId}") - public Response getTmpFile(@PathParam("fileId") String fileId) { - File tmpFile = Paths.get(Utils.getConfigValue(ConfigurationKey.TEMPORARY_DIRECTORY), fileId).toFile(); - if (tmpFile == null || !tmpFile.exists()) + public Response getTmpFile(@PathParam("fileId") final String fileId) { + final File tmpFile = Paths.get(Utils.getConfigValue(ConfigurationKey.TEMPORARY_DIRECTORY), + fileId).toFile(); + if (tmpFile == null || !tmpFile.exists()) { return super.respond(Response.Status.NOT_FOUND); - - Response.ResponseBuilder response = Response.ok(tmpFile); - if (tmpFile.getName().endsWith(".csv")) { - response.header("Content-Type", "text/csv; name=\"" + tmpFile.getName() + "\""); } - response.header("Content-Disposition", "attachment; filename=\"" + tmpFile.getName() + "\""); - return response.build(); + return addHeaders(Response.ok(tmpFile), tmpFile.getName()); } @GET @@ -91,23 +101,17 @@ public Response getTmpFile(@PathParam("fileId") String fileId) { public Response getAttachment(@PathParam("fileId") String fileId, @QueryParam("sid") String sid, @HeaderParam("X-ICE-Authentication-SessionId") String sessionId) { - try { - if (StringUtils.isEmpty(sessionId)) - sessionId = sid; - - String userId = getUserIdFromSessionHeader(sessionId); - File file = attachmentController.getAttachmentByFileId(userId, fileId); - if (file == null) - return respond(Response.Status.NOT_FOUND); + if (StringUtils.isEmpty(sessionId)) + sessionId = sid; - String name = attachmentController.getFileName(userId, fileId); - Response.ResponseBuilder response = Response.ok(file); - response.header("Content-Disposition", "attachment; filename=\"" + name + "\""); - return response.build(); - } catch (Exception e) { - Logger.error(e); - return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); + String userId = getUserId(sessionId); + File file = attachmentController.getAttachmentByFileId(userId, fileId); + if (file == null) { + return respond(Response.Status.NOT_FOUND); } + + String name = attachmentController.getFileName(userId, fileId); + return addHeaders(Response.ok(file), name); } @GET @@ -116,42 +120,43 @@ public Response getRemoteAttachment(@PathParam("id") long partnerId, @PathParam("fileId") String fileId, @QueryParam("sid") String sid, @HeaderParam("X-ICE-Authentication-SessionId") String sessionId) { - String userId = getUserIdFromSessionHeader(sessionId); + String userId = getUserId(sessionId); RemoteEntries entries = new RemoteEntries(); File file = entries.getPublicAttachment(userId, partnerId, fileId); if (file == null) return respond(Response.Status.NOT_FOUND); - Response.ResponseBuilder response = Response.ok(file); - response.header("Content-Disposition", "attachment; filename=\"remoteAttachment\""); - return response.build(); + return addHeaders(Response.ok(file), "remoteAttachment"); } @GET @Path("upload/{type}") - public Response getUploadCSV(@PathParam("type") String type, @QueryParam("link") String linkedType) { - final EntryType entryAddType = EntryType.nameToType(type); - final EntryType linked; - if (linkedType != null) + public Response getUploadCSV(@PathParam("type") final String type, + @QueryParam("link") final String linkedType) { + EntryType entryAddType = EntryType.nameToType(type); + EntryType linked; + if (linkedType != null) { linked = EntryType.nameToType(linkedType); - else + } else { linked = null; + } - StreamingOutput stream = new StreamingOutput() { + final StreamingOutput stream = new StreamingOutput() { @Override - public void write(OutputStream output) throws IOException, WebApplicationException { - byte[] template = FileBulkUpload.getCSVTemplateBytes(entryAddType, linked); + public void write(final OutputStream output) throws IOException, WebApplicationException { + byte[] template = FileBulkUpload.getCSVTemplateBytes(entryAddType, linked, + "existing".equalsIgnoreCase(linkedType)); ByteArrayInputStream stream = new ByteArrayInputStream(template); IOUtils.copy(stream, output); } }; String filename = type.toLowerCase(); - if (linkedType != null) + if (linkedType != null) { filename += ("_" + linkedType.toLowerCase()); + } - return Response.ok(stream).header("Content-Disposition", "attachment;filename=" - + filename + "_csv_upload.csv").build(); + return addHeaders(Response.ok(stream), filename + "_csv_upload.csv"); } @GET @@ -164,18 +169,19 @@ public Response downloadSequence( if (StringUtils.isEmpty(sessionId)) sessionId = sid; - final String userId = getUserIdFromSessionHeader(sessionId); + final String userId = getUserId(sessionId); final ByteArrayWrapper wrapper = sequenceController.getSequenceFile(userId, partId, downloadType); StreamingOutput stream = new StreamingOutput() { @Override - public void write(OutputStream output) throws IOException, WebApplicationException { - ByteArrayInputStream stream = new ByteArrayInputStream(wrapper.getBytes()); + public void write(final OutputStream output) throws IOException, + WebApplicationException { + final ByteArrayInputStream stream = new ByteArrayInputStream(wrapper.getBytes()); IOUtils.copy(stream, output); } }; - return Response.ok(stream).header("Content-Disposition", "attachment;filename=" + wrapper.getName()).build(); + return addHeaders(Response.ok(stream), wrapper.getName()); } @GET @@ -183,20 +189,13 @@ public void write(OutputStream output) throws IOException, WebApplicationExcepti public Response getTraceSequenceFile(@PathParam("fileId") String fileId, @QueryParam("sid") String sid, @HeaderParam("X-ICE-Authentication-SessionId") String sessionId) { - try { - SequenceAnalysisController sequenceAnalysisController = new SequenceAnalysisController(); - TraceSequence traceSequence = sequenceAnalysisController.getTraceSequenceByFileId(fileId); - if (traceSequence != null) { - File file = sequenceAnalysisController.getFile(traceSequence); - Response.ResponseBuilder response = Response.ok(file); - response.header("Content-Disposition", "attachment; filename=\"" + traceSequence.getFilename() + "\""); - return response.build(); - } - return Response.serverError().build(); - } catch (Exception e) { - Logger.error(e); - return Response.serverError().build(); + final SequenceAnalysisController sequenceAnalysisController = new SequenceAnalysisController(); + final TraceSequence traceSequence = sequenceAnalysisController.getTraceSequenceByFileId(fileId); + if (traceSequence != null) { + final File file = sequenceAnalysisController.getFile(traceSequence); + return addHeaders(Response.ok(file), traceSequence.getFilename()); } + return Response.serverError().build(); } @GET @@ -204,41 +203,34 @@ public Response getTraceSequenceFile(@PathParam("fileId") String fileId, @Path("sbolVisual/{rid}") public Response getSBOLVisual(@PathParam("rid") String recordId, @HeaderParam("X-ICE-Authentication-SessionId") String sessionId) { - try { - String tmpDir = Utils.getConfigValue(ConfigurationKey.TEMPORARY_DIRECTORY); - Entry entry = DAOFactory.getEntryDAO().getByRecordId(recordId); - Sequence sequence = entry.getSequence(); - String hash = sequence.getFwdHash(); - String fileId; + final String tmpDir = Utils.getConfigValue(ConfigurationKey.TEMPORARY_DIRECTORY); + final Entry entry = DAOFactory.getEntryDAO().getByRecordId(recordId); + final Sequence sequence = entry.getSequence(); + final String hash = sequence.getFwdHash(); + final File png = Paths.get(tmpDir, hash + ".png").toFile(); - if (Paths.get(tmpDir, hash + ".png").toFile().exists()) { - fileId = (hash + ".png"); - File file = Paths.get(tmpDir, fileId).toFile(); - - Response.ResponseBuilder response = Response.ok(file); - response.header("Content-Disposition", "attachment; filename=" + entry.getPartNumber() + ".png"); - return response.build(); - } else { - URI uri = PigeonSBOLv.generatePigeonVisual(sequence); - if (uri != null) { - IOUtils.copy(uri.toURL().openStream(), - new FileOutputStream(tmpDir + File.separatorChar + hash + ".png")); - fileId = (hash + ".png"); - File file = Paths.get(tmpDir, fileId).toFile(); + if (png.exists()) { + return addHeaders(Response.ok(png), entry.getPartNumber() + ".png"); + } - Response.ResponseBuilder response = Response.ok(file); - response.header("Content-Disposition", "attachment; filename=" + entry.getPartNumber() + ".png"); - return response.build(); - } + final URI uri = PigeonSBOLv.generatePigeonVisual(sequence); + if (uri != null) { + try (final InputStream in = uri.toURL().openStream(); + final OutputStream out = new FileOutputStream(png);) { + IOUtils.copy(in, out); + } catch (IOException e) { + Logger.error(e); + return respond(false); } - } catch (Exception e) { - Logger.error(e); - return null; + + return addHeaders(Response.ok(png), entry.getPartNumber() + ".png"); } - return null; + return respond(false); } - // this creates an entry if an id is not specified in the form data + /** + * this creates an entry if an id is not specified in the form data + */ @POST @Path("sequence") @Consumes(MediaType.MULTIPART_FORM_DATA) @@ -249,27 +241,29 @@ public Response uploadSequence(@FormDataParam("file") InputStream fileInputStrea @FormDataParam("file") FormDataContentDisposition contentDispositionHeader, @HeaderParam("X-ICE-Authentication-SessionId") String sessionId) { try { - if (entryType == null) + if (entryType == null) { return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); + } - String fileName = contentDispositionHeader.getFileName(); - String userId = SessionHandler.getUserIdBySession(sessionId); - String sequence = IOUtils.toString(fileInputStream); - SequenceInfo sequenceInfo = sequenceController.parseSequence(userId, recordId, entryType, sequence, - fileName); - if (sequenceInfo == null) + final String fileName = contentDispositionHeader.getFileName(); + final String userId = SessionHandler.getUserIdBySession(sessionId); + final String sequence = IOUtils.toString(fileInputStream); + final SequenceInfo sequenceInfo = sequenceController.parseSequence(userId, recordId, + entryType, sequence, fileName); + if (sequenceInfo == null) { return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); + } return Response.status(Response.Status.OK).entity(sequenceInfo).build(); - } catch (Exception e) { + } catch (final Exception e) { Logger.error(e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); } } /** - * Extracts the csv information and writes it to the temp dir and returns the file uuid. - * Then the client is expected to make another rest call with the uuid is a separate window. - * This workaround is due to not being able to download files using XHR or sumsuch + * Extracts the csv information and writes it to the temp dir and returns the file uuid. Then + * the client is expected to make another rest call with the uuid in a separate window. This + * workaround is due to not being able to download files using XHR or sumsuch */ @POST @Path("csv") @@ -278,15 +272,17 @@ public Response uploadSequence(@FormDataParam("file") InputStream fileInputStrea public Response downloadCSV( @HeaderParam("X-ICE-Authentication-SessionId") String sessionId, EntrySelection selection) { - String userId = super.getUserIdFromSessionHeader(sessionId); + String userId = super.getUserId(sessionId); EntriesAsCSV entriesAsCSV = new EntriesAsCSV(); boolean success = entriesAsCSV.setSelectedEntries(userId, selection); if (!success) return super.respond(false); - File file = entriesAsCSV.getFilePath().toFile(); - if (file.exists()) + final File file = entriesAsCSV.getFilePath().toFile(); + if (file.exists()) { return Response.ok(new Setting("key", file.getName())).build(); + } + return respond(false); } } diff --git a/src/main/java/org/jbei/ice/services/rest/FolderResource.java b/src/main/java/org/jbei/ice/services/rest/FolderResource.java index 51d6417d7..f55559c0b 100644 --- a/src/main/java/org/jbei/ice/services/rest/FolderResource.java +++ b/src/main/java/org/jbei/ice/services/rest/FolderResource.java @@ -1,7 +1,24 @@ package org.jbei.ice.services.rest; +import java.util.ArrayList; +import java.util.List; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.DefaultValue; +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 javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + import org.jbei.ice.lib.access.PermissionsController; -import org.jbei.ice.lib.account.SessionHandler; import org.jbei.ice.lib.common.logging.Logger; import org.jbei.ice.lib.dto.entry.PartData; import org.jbei.ice.lib.dto.folder.FolderDetails; @@ -12,14 +29,6 @@ import org.jbei.ice.lib.folder.*; import org.jbei.ice.lib.shared.ColumnField; -import javax.ws.rs.*; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; -import java.util.ArrayList; -import java.util.List; - /** * @author Hector Plahar */ @@ -30,123 +39,154 @@ public class FolderResource extends RestResource { private FolderContentRetriever retriever = new FolderContentRetriever(); private PermissionsController permissionsController = new PermissionsController(); + /** + * @return all collections visible to current user + */ @GET @Produces(MediaType.APPLICATION_JSON) - public Collection retrieveCollection( - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String sid = getUserIdFromSessionHeader(userAgentHeader); + public Collection retrieveCollection() { + final String sid = getUserId(); return controller.getFolderStats(sid); } + /** + * @param folder + * @return Response with info on created folder + */ @PUT @Produces(MediaType.APPLICATION_JSON) - public FolderDetails create( - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader, - FolderDetails folder) { - String sid = getUserIdFromSessionHeader(userAgentHeader); + public FolderDetails create(final FolderDetails folder) { + final String sid = getUserId(); return controller.createPersonalFolder(sid, folder); } - // TODO : allow api key as well + /** + * TODO allow api key as well + * + * @return all public collections + */ @GET @Path("/public") @Produces(MediaType.APPLICATION_JSON) - public ArrayList getPublicFolders( - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { + public ArrayList getPublicFolders() { return controller.getPublicFolders(); } + /** + * @param folderType + * @return all collections of a type + */ @GET @Path("/{type}") @Produces(MediaType.APPLICATION_JSON) public ArrayList getSubFolders( @DefaultValue("personal") @PathParam("type") String folderType, - @DefaultValue("false") @QueryParam("canEdit") boolean canEdit, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String sid = getUserIdFromSessionHeader(userAgentHeader); + @DefaultValue("false") @QueryParam("canEdit") boolean canEdit) { + final String sid = getUserId(); if (canEdit) return new Folders().getCanEditFolders(sid); switch (folderType) { - case "personal": - return controller.getUserFolders(sid); + case "personal": + return controller.getUserFolders(sid); - case "available": - return controller.getAvailableFolders(sid); + case "available": + return controller.getAvailableFolders(sid); - case "drafts": - return controller.getBulkUploadDrafts(sid); + case "drafts": + return controller.getBulkUploadDrafts(sid); - case "pending": - return controller.getPendingBulkUploads(sid); + case "pending": + return controller.getPendingBulkUploads(sid); - case "shared": - return controller.getSharedUserFolders(sid); + case "shared": + return controller.getSharedUserFolders(sid); - default: - return new ArrayList<>(); + default: + return new ArrayList<>(); } } + /** + * @param folderId + * @param details + * @return Response with updated collection info + */ @PUT @Path("/{id}") - public Response update(@PathParam("id") long folderId, - FolderDetails details, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String userId = getUserIdFromSessionHeader(userAgentHeader); - FolderDetails resp = controller.update(userId, folderId, details); + public Response update(@PathParam("id") final long folderId, final FolderDetails details) { + final String userId = getUserId(); + final FolderDetails resp = controller.update(userId, folderId, details); return super.respond(Response.Status.OK, resp); } + /** + * @param folderId + * @param folderType + * @return the details of the deleted collection + */ @DELETE @Path("/{id}") - public FolderDetails deleteFolder(@PathParam("id") long folderId, - @QueryParam("type") String folderType, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String userId = getUserIdFromSessionHeader(userAgentHeader); - FolderType type = FolderType.valueOf(folderType); + public FolderDetails deleteFolder(@PathParam("id") final long folderId, + @QueryParam("type") final String folderType) { + final String userId = getUserId(); + final FolderType type = FolderType.valueOf(folderType); return controller.delete(userId, folderId, type); } + /** + * @param entrySelection + * @return Response with updated collection details + */ @POST @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) @Path("/transfer") - public Response addSelectedEntriesToFolder( - @HeaderParam(value = "X-ICE-Authentication-SessionId") String sessionId, - EntrySelection entrySelection) { - String userId = getUserIdFromSessionHeader(sessionId); - FolderContent folderContent = new FolderContent(); - List result = folderContent.addEntrySelection(userId, entrySelection); + public Response addSelectedEntriesToFolder(final EntrySelection entrySelection) { + final String userId = getUserId(); + final FolderContent folderContent = new FolderContent(); + final List result = folderContent.addEntrySelection(userId, entrySelection); return super.respond(result); } + /** + * @param entrySelection + * @param folderId + * @return Response indicating success or failure + */ @PUT @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/entries") - public Response removeEntriesFromFolder( - EntrySelection entrySelection, - @PathParam("id") long folderId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String sessionId) { - String userId = getUserIdFromSessionHeader(sessionId); - if (controller.removeFolderContents(userId, folderId, entrySelection)) + public Response removeEntriesFromFolder(final EntrySelection entrySelection, + @PathParam("id") final long folderId) { + final String userId = getUserId(); + if (controller.removeFolderContents(userId, folderId, entrySelection)) { return respond(Response.Status.OK); + } return respond(Response.Status.INTERNAL_SERVER_ERROR); } + /** + * @param uriInfo + * @param folderId + * @param offset + * @param limit + * @param sort + * @param asc + * @return details of the selected collection + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/entries") - public FolderDetails read(@Context UriInfo uriInfo, - @PathParam("id") String folderId, - @DefaultValue("0") @QueryParam("offset") int offset, - @DefaultValue("15") @QueryParam("limit") int limit, - @DefaultValue("created") @QueryParam("sort") String sort, - @DefaultValue("false") @QueryParam("asc") boolean asc, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { + public FolderDetails read(@Context final UriInfo uriInfo, + @PathParam("id") final String folderId, + @DefaultValue("0") @QueryParam("offset") final int offset, + @DefaultValue("15") @QueryParam("limit") final int limit, + @DefaultValue("created") @QueryParam("sort") final String sort, + @DefaultValue("false") @QueryParam("asc") final boolean asc) { - ColumnField field = ColumnField.valueOf(sort.toUpperCase()); + final ColumnField field = ColumnField.valueOf(sort.toUpperCase()); if (folderId.equalsIgnoreCase("public")) { // return public entries @@ -154,118 +194,148 @@ public FolderDetails read(@Context UriInfo uriInfo, return controller.getPublicEntries(field, offset, limit, asc); } - String userId = SessionHandler.getUserIdBySession(userAgentHeader); + final String userId = getUserId(); try { - long id = Long.decode(folderId); + final long id = Long.decode(folderId); Logger.info("Retrieving folder " + id + " entries"); return controller.retrieveFolderContents(userId, id, field, asc, offset, limit); - } catch (NumberFormatException nfe) { + } catch (final NumberFormatException nfe) { } - EntryController entryController = new EntryController(); - FolderDetails details = new FolderDetails(); + final EntryController entryController = new EntryController(); + final FolderDetails details = new FolderDetails(); log(userId, "retrieving " + folderId + " entries"); switch (folderId) { - case "personal": - List entries = entryController.retrieveOwnerEntries(userId, userId, field, - asc, offset, limit); - long count = entryController.getNumberOfOwnerEntries(userId, userId); - details.getEntries().addAll(entries); - details.setCount(count); - return details; - - case "available": - FolderDetails retrieved = entryController.retrieveVisibleEntries(userId, field, asc, offset, limit); - details.setEntries(retrieved.getEntries()); - details.setCount(entryController.getNumberOfVisibleEntries(userId)); - return details; - - case "shared": - List data = entryController.getEntriesSharedWithUser(userId, field, asc, offset, limit); - details.setEntries(data); - details.setCount(entryController.getNumberOfEntriesSharedWithUser(userId)); - return details; - - case "drafts": - return retriever.getDraftEntries(userId, field, asc, offset, limit); - - case "deleted": - return retriever.getDeletedEntries(userId, field, asc, offset, limit); - - case "pending": - return retriever.getPendingEntries(userId, field, asc, offset, limit); - - case "transferred": - return retriever.getTransferredEntries(userId, field, asc, offset, limit); - - default: - return null; + case "personal": + final List entries = entryController.retrieveOwnerEntries(userId, userId, + field, asc, offset, limit); + final long count = entryController.getNumberOfOwnerEntries(userId, userId); + details.getEntries().addAll(entries); + details.setCount(count); + return details; + + case "available": + final FolderDetails retrieved = entryController.retrieveVisibleEntries(userId, field, + asc, offset, limit); + details.setEntries(retrieved.getEntries()); + details.setCount(entryController.getNumberOfVisibleEntries(userId)); + return details; + + case "shared": + final List data = entryController.getEntriesSharedWithUser(userId, field, + asc, offset, limit); + details.setEntries(data); + details.setCount(entryController.getNumberOfEntriesSharedWithUser(userId)); + return details; + + case "drafts": + return retriever.getDraftEntries(userId, field, asc, offset, limit); + + case "deleted": + return retriever.getDeletedEntries(userId, field, asc, offset, limit); + + case "pending": + return retriever.getPendingEntries(userId, field, asc, offset, limit); + + case "transferred": + return retriever.getTransferredEntries(userId, field, asc, offset, limit); + + default: + return null; } } + /** + * @param folderId + * @return Response with permissions on a collection + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/permissions") - public Response getFolderPermissions( - @PathParam("id") long folderId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String userId = getUserIdFromSessionHeader(userAgentHeader); + public Response getFolderPermissions(@PathParam("id") final long folderId) { + final String userId = getUserId(); return respond(controller.getPermissions(userId, folderId)); } + /** + * @param info + * @param folderId + * @param permissions + * @return details of the modified collection + */ @PUT @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/permissions") - public FolderDetails setPermissions(@Context UriInfo info, @PathParam("id") long folderId, - ArrayList permissions, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String userId = getUserIdFromSessionHeader(userAgentHeader); + public FolderDetails setPermissions(@Context final UriInfo info, + @PathParam("id") final long folderId, final ArrayList permissions) { + final String userId = getUserId(); return permissionsController.setFolderPermissions(userId, folderId, permissions); } + /** + * @param info + * @param folderId + * @param permission + * @return the added permission + */ @POST @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/permissions") - public AccessPermission addPermission(@Context UriInfo info, @PathParam("id") long folderId, - AccessPermission permission, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String userId = getUserIdFromSessionHeader(userAgentHeader); + public AccessPermission addPermission(@Context final UriInfo info, + @PathParam("id") final long folderId, final AccessPermission permission) { + final String userId = getUserId(); return controller.createFolderPermission(userId, folderId, permission); } + /** + * @param info + * @param partId + * @param permissionId + * @return Response for success or failure + */ @DELETE @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/permissions/{permissionId}") - public Response removePermission(@Context UriInfo info, - @PathParam("id") long partId, - @PathParam("permissionId") long permissionId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String userId = getUserIdFromSessionHeader(userAgentHeader); + public Response removePermission(@Context final UriInfo info, + @PathParam("id") final long partId, @PathParam("permissionId") final long permissionId) { + final String userId = getUserId(); permissionsController.removeFolderPermission(userId, partId, permissionId); return Response.ok().build(); } + /** + * @param info + * @param folderId + * @return Response for success or failure + */ @PUT @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/permissions/public") - public Response enablePublicAccess(@Context UriInfo info, @PathParam("id") long folderId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String userId = getUserIdFromSessionHeader(userAgentHeader); - if (controller.enablePublicReadAccess(userId, folderId)) + public Response enablePublicAccess(@Context final UriInfo info, + @PathParam("id") final long folderId) { + final String userId = getUserId(); + if (controller.enablePublicReadAccess(userId, folderId)) { return respond(Response.Status.OK); + } return respond(Response.Status.INTERNAL_SERVER_ERROR); } + /** + * @param info + * @param folderId + * @return Response for success or failure + */ @DELETE @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/permissions/public") - public Response disablePublicAccess(@Context UriInfo info, @PathParam("id") long folderId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String userId = getUserIdFromSessionHeader(userAgentHeader); - if (controller.disablePublicReadAccess(userId, folderId)) + public Response disablePublicAccess(@Context final UriInfo info, + @PathParam("id") final long folderId) { + final String userId = getUserId(); + if (controller.disablePublicReadAccess(userId, folderId)) { return respond(Response.Status.OK); + } return respond(Response.Status.INTERNAL_SERVER_ERROR); } } diff --git a/src/main/java/org/jbei/ice/services/rest/GroupResource.java b/src/main/java/org/jbei/ice/services/rest/GroupResource.java index 854fde4be..e8df5afdb 100644 --- a/src/main/java/org/jbei/ice/services/rest/GroupResource.java +++ b/src/main/java/org/jbei/ice/services/rest/GroupResource.java @@ -1,8 +1,8 @@ package org.jbei.ice.services.rest; import java.util.ArrayList; + import javax.ws.rs.GET; -import javax.ws.rs.HeaderParam; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; @@ -22,34 +22,43 @@ public class GroupResource extends RestResource { private GroupController groupController = new GroupController(); + /** + * @param id + * @return Response with group info + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{id}") - public Response getGroup(@PathParam("id") long id, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String sessionId) { - String userId = getUserIdFromSessionHeader(sessionId); - UserGroup group = groupController.getGroupById(userId, id); + public Response getGroup(@PathParam("id") final long id) { + final String userId = getUserId(); + final UserGroup group = groupController.getGroupById(userId, id); return respond(group); } + /** + * @param id + * @return Response with group members + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/members") - public Response getGroupMembers(@PathParam("id") long id, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String sessionId) { - String userId = getUserIdFromSessionHeader(sessionId); - ArrayList members = groupController.getGroupMembers(userId, id); + public Response getGroupMembers(@PathParam("id") final long id) { + final String userId = getUserId(); + final ArrayList members = groupController.getGroupMembers(userId, id); return respond(members); } + /** + * @param id + * @param group + * @return response with success or failure + */ @PUT @Produces(MediaType.APPLICATION_JSON) @Path("/{id}") - public Response updateGroup(@PathParam("id") long id, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String sessionId, - UserGroup group) { - String userId = getUserIdFromSessionHeader(sessionId); - boolean success = groupController.updateGroup(userId, group); + public Response updateGroup(@PathParam("id") final long id, final UserGroup group) { + final String userId = getUserId(); + final boolean success = groupController.updateGroup(userId, group); return respond(success); } } diff --git a/src/main/java/org/jbei/ice/services/rest/IceRestClient.java b/src/main/java/org/jbei/ice/services/rest/IceRestClient.java index 01d31d8de..a6ca7ad5e 100644 --- a/src/main/java/org/jbei/ice/services/rest/IceRestClient.java +++ b/src/main/java/org/jbei/ice/services/rest/IceRestClient.java @@ -10,7 +10,6 @@ import javax.ws.rs.client.*; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import java.util.HashMap; import java.util.Map; /** @@ -36,12 +35,13 @@ protected IceRestClient() { client = ClientBuilder.newClient(clientConfig); } - public Object get(String url, String path, Class clazz) { + public T get(String url, String path, Class clazz) { WebTarget target = client.target("https://" + url).path(path); return target.request(MediaType.APPLICATION_JSON_TYPE).buildGet().invoke(clazz); } - public Object get(String url, String path, Class clazz, HashMap queryParams) { + @Override + public T get(String url, String path, Class clazz, Map queryParams) { WebTarget target = client.target("https://" + url).path(path); if (queryParams != null) { for (Map.Entry entry : queryParams.entrySet()) { @@ -56,7 +56,8 @@ public Object get(String url, String path) { return target.request(MediaType.APPLICATION_JSON_TYPE).buildGet().invoke(); } - public Object post(String url, String resourcePath, Object object, Class responseClass) { + @Override + public T post(String url, String resourcePath, Object object, Class responseClass) { WebTarget target = client.target("https://" + url).path(resourcePath); Invocation.Builder invocationBuilder = target.request(MediaType.APPLICATION_JSON_TYPE); Response postResponse = invocationBuilder.post(Entity.entity(object, MediaType.APPLICATION_JSON_TYPE)); @@ -65,7 +66,7 @@ public Object post(String url, String resourcePath, Object object, Class resp return null; } - public Object put(String url, String resourcePath, Object object, Class responseClass) { + public T put(String url, String resourcePath, Object object, Class responseClass) { WebTarget target = client.target("https://" + url).path(resourcePath); Invocation.Builder invocationBuilder = target.request(MediaType.APPLICATION_JSON_TYPE); Response putResponse = invocationBuilder.put(Entity.entity(object, MediaType.APPLICATION_JSON_TYPE)); diff --git a/src/main/java/org/jbei/ice/services/rest/MessageResource.java b/src/main/java/org/jbei/ice/services/rest/MessageResource.java index 2e5546d02..b069882d0 100644 --- a/src/main/java/org/jbei/ice/services/rest/MessageResource.java +++ b/src/main/java/org/jbei/ice/services/rest/MessageResource.java @@ -2,7 +2,6 @@ import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; -import javax.ws.rs.HeaderParam; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; @@ -20,13 +19,16 @@ public class MessageResource extends RestResource { private MessageController controller = new MessageController(); + /** + * @param offset + * @param limit + * @return list of messages + */ @GET @Produces(MediaType.APPLICATION_JSON) - public MessageList get( - @DefaultValue("0") @QueryParam("offset") int offset, - @DefaultValue("15") @QueryParam("limit") int limit, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String userId = getUserIdFromSessionHeader(userAgentHeader); + public MessageList get(@DefaultValue("0") @QueryParam("offset") final int offset, + @DefaultValue("15") @QueryParam("limit") final int limit) { + final String userId = getUserId(); Logger.info(userId + ": retrieving available messages"); return controller.retrieveMessages(userId, userId, offset, limit); } diff --git a/src/main/java/org/jbei/ice/services/rest/PartResource.java b/src/main/java/org/jbei/ice/services/rest/PartResource.java index 02e44ead0..6e258f320 100644 --- a/src/main/java/org/jbei/ice/services/rest/PartResource.java +++ b/src/main/java/org/jbei/ice/services/rest/PartResource.java @@ -4,12 +4,11 @@ import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; import org.apache.commons.io.FileUtils; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import org.glassfish.jersey.media.multipart.FormDataParam; import org.jbei.ice.lib.access.PermissionException; import org.jbei.ice.lib.access.PermissionsController; -import org.jbei.ice.lib.account.SessionHandler; import org.jbei.ice.lib.common.logging.Logger; import org.jbei.ice.lib.dto.ConfigurationKey; import org.jbei.ice.lib.dto.History; @@ -55,6 +54,12 @@ public class PartResource extends RestResource { private ExperimentController experimentController = new ExperimentController(); private SampleService sampleService = new SampleService(); + /** + * @param val + * @param field + * @param limit + * @return list of autocomplete values for a field + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/autocomplete") @@ -66,6 +71,11 @@ public ArrayList autoComplete(@QueryParam("val") String val, return new ArrayList<>(result); } + /** + * @param token + * @param limit + * @return list of autocomplete values for parts + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/autocomplete/partid") @@ -75,293 +85,378 @@ public ArrayList autoComplete(@QueryParam("token") String token, } /** - * Retrieves a part using any of the unique identifiers. e.g. Part number, synthetic id, or global unique - * identifier + * Retrieves a part using any of the unique identifiers. e.g. Part number, synthetic id, or + * global unique identifier + * + * @param info + * @param id + * @return Response with part data */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{id}") - public Response read(@Context UriInfo info, - @PathParam("id") String id, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String sessionId) { - String userId = SessionHandler.getUserIdBySession(sessionId); + public Response read(@Context final UriInfo info, @PathParam("id") final String id) { + final String userId = getUserId(); try { log(userId, "retrieving details for " + id); - EntryType type = EntryType.nameToType(id); + final EntryType type = EntryType.nameToType(id); PartData data; - if (type != null) + if (type != null) { data = controller.getPartDefaults(userId, type); - else + } else { data = controller.retrieveEntryDetails(userId, id); + } return super.respond(data); - } catch (PermissionException pe) { + } catch (final PermissionException pe) { // todo : have a generic error entity returned return Response.status(Response.Status.FORBIDDEN).build(); } } + /** + * @param id + * @return part data with tooltip information + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/tooltip") - public PartData getTooltipDetails(@PathParam("id") String id, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String userId = SessionHandler.getUserIdBySession(userAgentHeader); + public PartData getTooltipDetails(@PathParam("id") final String id) { + final String userId = getUserId(); return controller.retrieveEntryTipDetails(userId, id); } + /** + * @param info + * @param id + * @return permissions on the part + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/permissions") - public ArrayList getPermissions(@Context UriInfo info, @PathParam("id") String id, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String userId = getUserIdFromSessionHeader(userAgentHeader); + public List getPermissions(@Context final UriInfo info, + @PathParam("id") final String id) { + final String userId = getUserId(); return retriever.getEntryPermissions(userId, id); } + /** + * @param info + * @param partId + * @param permissions + * @return part data with permission information + */ @PUT @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/permissions") - public PartData setPermissions(@Context UriInfo info, @PathParam("id") long partId, - ArrayList permissions, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String userId = getUserIdFromSessionHeader(userAgentHeader); + public PartData setPermissions(@Context final UriInfo info, @PathParam("id") final long partId, + final ArrayList permissions) { + final String userId = getUserId(); return permissionsController.setEntryPermissions(userId, partId, permissions); } + /** + * @param partId + * @return Response with studies on a part + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/experiments") - public Response getPartExperiments(@PathParam("id") long partId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String userId = getUserIdFromSessionHeader(userAgentHeader); - ArrayList studies = experimentController.getPartStudies(userId, partId); - if (studies == null) + public Response getPartExperiments(@PathParam("id") final long partId) { + final String userId = getUserId(); + final List studies = experimentController.getPartStudies(userId, partId); + if (studies == null) { return respond(Response.Status.INTERNAL_SERVER_ERROR); + } return respond(Response.Status.OK, studies); } + /** + * @param partId + * @param study + * @return response with study information + */ @POST @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/experiments") - public Response getPartExperiments(@PathParam("id") long partId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader, - Study study) { - String userId = getUserIdFromSessionHeader(userAgentHeader); - study = experimentController.createStudy(userId, partId, study); - return respond(Response.Status.OK, study); + public Response getPartExperiments(@PathParam("id") final long partId, final Study study) { + final String userId = getUserId(); + final Study created = experimentController.createStudy(userId, partId, study); + return respond(Response.Status.OK, created); } + /** + * @param info + * @param partId + * @return Response for success or failure + */ @PUT @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/permissions/public") - public Response enablePublicAccess(@Context UriInfo info, @PathParam("id") long partId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String userId = getUserIdFromSessionHeader(userAgentHeader); - if (permissionsController.enablePublicReadAccess(userId, partId)) + public Response enablePublicAccess(@Context final UriInfo info, + @PathParam("id") final long partId) { + final String userId = getUserId(); + if (permissionsController.enablePublicReadAccess(userId, partId)) { return respond(Response.Status.OK); + } return respond(Response.Status.INTERNAL_SERVER_ERROR); } + /** + * @param info + * @param partId + * @return Response for success or failure + */ @DELETE @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/permissions/public") - public Response disablePublicAccess(@Context UriInfo info, @PathParam("id") long partId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String userId = getUserIdFromSessionHeader(userAgentHeader); - if (permissionsController.disablePublicReadAccess(userId, partId)) + public Response disablePublicAccess(@Context final UriInfo info, + @PathParam("id") final long partId) { + final String userId = getUserId(); + if (permissionsController.disablePublicReadAccess(userId, partId)) { return respond(Response.Status.OK); + } return respond(Response.Status.INTERNAL_SERVER_ERROR); } + /** + * @param info + * @param partId + * @param permission + * @return the created permission + */ @POST @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/permissions") - public AccessPermission createPermission(@Context UriInfo info, @PathParam("id") long partId, - AccessPermission permission, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String userId = getUserIdFromSessionHeader(userAgentHeader); + public AccessPermission createPermission(@Context final UriInfo info, + @PathParam("id") final long partId, + final AccessPermission permission) { + final String userId = getUserId(); return permissionsController.createPermission(userId, partId, permission); } + /** + * @param info + * @param partId + * @param permissionId + * @return Response for success or failure + */ @DELETE @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/permissions/{permissionId}") - public Response removePermission(@Context UriInfo info, - @PathParam("id") long partId, - @PathParam("permissionId") long permissionId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String userId = getUserIdFromSessionHeader(userAgentHeader); + public Response removePermission(@Context final UriInfo info, + @PathParam("id") final long partId, + @PathParam("permissionId") final long permissionId) { + final String userId = getUserId(); permissionsController.removeEntryPermission(userId, partId, permissionId); return super.respond(true); } + /** + * @param partId + * @return statistics on part + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/statistics") - public PartStatistics getStatistics(@PathParam("id") long partId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String userId = SessionHandler.getUserIdBySession(userAgentHeader); + public PartStatistics getStatistics(@PathParam("id") final long partId) { + final String userId = getUserId(); return controller.retrieveEntryStatistics(userId, partId); } + /** + * @param info + * @param partId + * @return comments on part + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/comments") - public ArrayList getComments(@Context UriInfo info, @PathParam("id") long partId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String userId = SessionHandler.getUserIdBySession(userAgentHeader); + public List getComments(@Context final UriInfo info, + @PathParam("id") final long partId) { + final String userId = getUserId(); return controller.retrieveEntryComments(userId, partId); } + /** + * @param partId + * @param userComment + * @return the created comment + */ @POST @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/comments") - public Response createComment(@PathParam("id") long partId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader, - UserComment userComment) { -// if(userComment == null || userComment.getMessage() == null) -// throw new Web + public Response createComment(@PathParam("id") final long partId, + final UserComment userComment) { // todo : check for null - String userId = getUserIdFromSessionHeader(userAgentHeader); + final String userId = getUserId(); log(userId, "adding comment to entry " + partId); - UserComment comment = controller.createEntryComment(userId, partId, userComment); + final UserComment comment = controller.createEntryComment(userId, partId, userComment); return respond(comment); } + /** + * @param partId + * @param commentId + * @param userComment + * @return the updated comment + */ @PUT @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/comments/{commentId}") - public UserComment updateComment(@PathParam("id") long partId, - @PathParam("commentId") long commentId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader, - UserComment userComment) { - String userId = getUserIdFromSessionHeader(userAgentHeader); + public UserComment updateComment(@PathParam("id") final long partId, + @PathParam("commentId") final long commentId, + final UserComment userComment) { + final String userId = getUserId(); return controller.updateEntryComment(userId, partId, commentId, userComment); } + /** + * @param partId + * @param attachment + * @return created attachment info + */ @POST @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) @Path("/{id}/attachments") - public AttachmentInfo addAttachment(@PathParam("id") long partId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader, - AttachmentInfo attachment) { + public AttachmentInfo addAttachment(@PathParam("id") final long partId, + final AttachmentInfo attachment) { // todo : check for null - String userId = getUserIdFromSessionHeader(userAgentHeader); - AttachmentController attachmentController = new AttachmentController(); + final String userId = getUserId(); + final AttachmentController attachmentController = new AttachmentController(); return attachmentController.addAttachmentToEntry(userId, partId, attachment); } + /** + * @param partId + * @return all attachments on a part + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/attachments") - public ArrayList getAttachments(@PathParam("id") long partId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String userId = SessionHandler.getUserIdBySession(userAgentHeader); + public List getAttachments(@PathParam("id") final long partId) { + final String userId = getUserId(); return attachmentController.getByEntry(userId, partId); } + /** + * @param info + * @param partId + * @param attachmentId + * @return A response for success or failure + */ @DELETE @Path("/{id}/attachments/{attachmentId}") - public Response deleteAttachment(@Context UriInfo info, - @PathParam("id") long partId, - @PathParam("attachmentId") long attachmentId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String userId = getUserIdFromSessionHeader(userAgentHeader); - if (!attachmentController.delete(userId, partId, attachmentId)) + public Response deleteAttachment(@Context final UriInfo info, + @PathParam("id") final long partId, + @PathParam("attachmentId") final long attachmentId) { + final String userId = getUserId(); + if (!attachmentController.delete(userId, partId, attachmentId)) { return Response.notModified().build(); // todo : use 404 ? + } return Response.ok().build(); } + /** + * @return history entries for the part + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/history") - public ArrayList getHistory(@Context UriInfo info, - @PathParam("id") long partId, - @QueryParam("sid") String sessionId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - if (StringUtils.isEmpty(userAgentHeader)) - userAgentHeader = sessionId; - String userId = getUserIdFromSessionHeader(userAgentHeader); + public ArrayList getHistory(@Context final UriInfo info, + @PathParam("id") final long partId, + @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader, + @QueryParam("sid") final String sid) { + String sessionId = StringUtils.isEmpty(userAgentHeader) ? sid : userAgentHeader; + final String userId = getUserId(sessionId); return controller.getHistory(userId, partId); } + /** + * @param partId + * @param historyId + * @return Response for success or failure + */ @DELETE @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/history/{historyId}") - public Response delete(@PathParam("id") long partId, - @PathParam("historyId") long historyId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String userId = getUserIdFromSessionHeader(userAgentHeader); - boolean success = controller.deleteHistory(userId, partId, historyId); + public Response delete(@PathParam("id") final long partId, + @PathParam("historyId") final long historyId) { + final String userId = getUserId(); + final boolean success = controller.deleteHistory(userId, partId, historyId); return super.respond(success); } + /** + * @param info + * @param partId + * @param sessionId + * @return traces for the part + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/traces") - public ArrayList getTraces(@Context UriInfo info, - @PathParam("id") long partId, - @QueryParam("sid") String sessionId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - if (StringUtils.isEmpty(userAgentHeader)) - userAgentHeader = sessionId; - String userId = SessionHandler.getUserIdBySession(userAgentHeader); + public ArrayList getTraces(@Context final UriInfo info, + @PathParam("id") final long partId, + @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader, + @QueryParam("sid") final String sid) { + String sessionId = StringUtils.isEmpty(userAgentHeader) ? sid : userAgentHeader; + final String userId = getUserId(sessionId); return controller.getTraceSequences(userId, partId); } @POST @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/traces") - public Response addTraceSequence(@PathParam("id") long partId, - @FormDataParam("file") InputStream fileInputStream, - @FormDataParam("file") FormDataContentDisposition contentDispositionHeader, - @QueryParam("sid") String sessionId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - if (StringUtils.isEmpty(userAgentHeader)) - userAgentHeader = sessionId; - String userId = getUserIdFromSessionHeader(userAgentHeader); - String fileName = contentDispositionHeader.getFileName(); - String tmpDir = Utils.getConfigValue(ConfigurationKey.TEMPORARY_DIRECTORY); - File file = Paths.get(tmpDir, fileName).toFile(); + public Response addTraceSequence(@PathParam("id") final long partId, + @FormDataParam("file") final InputStream fileInputStream, + @FormDataParam("file") final FormDataContentDisposition contentDispositionHeader, + @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader, + @QueryParam("sid") final String sid) { + String sessionId = StringUtils.isEmpty(userAgentHeader) ? sid : userAgentHeader; + final String userId = getUserId(sessionId); + final String fileName = contentDispositionHeader.getFileName(); + final String tmpDir = Utils.getConfigValue(ConfigurationKey.TEMPORARY_DIRECTORY); + final File file = Paths.get(tmpDir, fileName).toFile(); try { FileUtils.copyInputStreamToFile(fileInputStream, file); - } catch (IOException e) { + } catch (final IOException e) { Logger.error(e); return respond(Response.Status.INTERNAL_SERVER_ERROR); } - boolean success = controller.addTraceSequence(userId, partId, file, fileName); + final boolean success = controller.addTraceSequence(userId, partId, file, fileName); return respond(success); } @DELETE @Path("/{id}/traces/{traceId}") - public Response deleteTrace(@Context UriInfo info, @PathParam("id") long partId, - @PathParam("traceId") long traceId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String userId = getUserIdFromSessionHeader(userAgentHeader); - if (!controller.deleteTraceSequence(userId, partId, traceId)) + public Response deleteTrace(@Context final UriInfo info, + @PathParam("id") final long partId, + @PathParam("traceId") final long traceId) { + final String userId = getUserId(); + if (!controller.deleteTraceSequence(userId, partId, traceId)) { return super.respond(Response.Status.UNAUTHORIZED); + } return super.respond(Response.Status.OK); } @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/samples") - public ArrayList getSamples(@Context UriInfo info, @PathParam("id") long partId, + public ArrayList getSamples(@Context UriInfo info, + @PathParam("id") long partId, @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String userId = SessionHandler.getUserIdBySession(userAgentHeader); + String userId = getUserId(userAgentHeader); return sampleService.retrieveEntrySamples(userId, partId); } @POST @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/samples") - public ArrayList addSample(@Context UriInfo info, @PathParam("id") long partId, - @QueryParam("strainNamePrefix") String strainNamePrefix, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader, - PartSample partSample) { - String userId = SessionHandler.getUserIdBySession(userAgentHeader); + public List addSample(@PathParam("id") final long partId, + @QueryParam("strainNamePrefix") final String strainNamePrefix, + final PartSample partSample) { + final String userId = getUserId(); log(userId, "creating sample for part " + partId); sampleService.createSample(userId, partId, partSample, strainNamePrefix); return sampleService.retrieveEntrySamples(userId, partId); @@ -373,7 +468,7 @@ public ArrayList addSample(@Context UriInfo info, @PathParam("id") l public Response deleteSample(@Context UriInfo info, @PathParam("id") long partId, @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader, @PathParam("sampleId") long sampleId) { - String userId = SessionHandler.getUserIdBySession(userAgentHeader); + String userId = getUserId(userAgentHeader); boolean success = sampleService.delete(userId, partId, sampleId); return super.respond(success); } @@ -381,63 +476,58 @@ public Response deleteSample(@Context UriInfo info, @PathParam("id") long partId @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/sequence") - public Response getSequence(@PathParam("id") long partId, - @QueryParam("sid") String sessionId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - if (StringUtils.isEmpty(userAgentHeader)) - userAgentHeader = sessionId; - - String userId = SessionHandler.getUserIdBySession(userAgentHeader); - FeaturedDNASequence sequence = sequenceController.retrievePartSequence(userId, partId); - if (sequence == null) + public Response getSequence(@PathParam("id") final long partId, + @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader, + @QueryParam("sid") final String sessionId) { + final String userId = getUserId(sessionId); + final FeaturedDNASequence sequence = sequenceController.retrievePartSequence(userId, partId); + if (sequence == null) { return Response.status(Response.Status.NO_CONTENT).build(); + } return Response.status(Response.Status.OK).entity(sequence).build(); } @POST @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/sequence") - public FeaturedDNASequence updateSequence(@PathParam("id") long partId, - @QueryParam("sid") String sessionId, - FeaturedDNASequence sequence, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - if (StringUtils.isEmpty(userAgentHeader)) - userAgentHeader = sessionId; - - String userId = getUserIdFromSessionHeader(userAgentHeader); + public FeaturedDNASequence updateSequence(@PathParam("id") final long partId, + @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader, + @QueryParam("sid") final String sessionId, + FeaturedDNASequence sequence) { + final String userId = getUserId(sessionId); return sequenceController.updateSequence(userId, partId, sequence); } @DELETE @Path("/{id}/sequence") - public Response deleteSequence(@PathParam("id") long partId, - @QueryParam("sid") String sessionId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String userId = getUserIdFromSessionHeader(userAgentHeader); - try { - if (sequenceController.deleteSequence(userId, partId)) - return Response.ok().build(); - return Response.serverError().build(); - } catch (RuntimeException e) { - Logger.error(e); - return Response.serverError().build(); + public Response deleteSequence(@PathParam("id") final long partId, + @HeaderParam(value = "X-ICE-Authentication-SessionId") String sid, + @QueryParam("sid") final String sessionId) { + if (StringUtils.isEmpty(sid)) + sid = sessionId; + final String userId = getUserId(sid); + if (sequenceController.deleteSequence(userId, partId)) { + return Response.ok().build(); } + return Response.serverError().build(); } @POST @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - public PartData create(@Context UriInfo info, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader, - PartData partData) { - String userId = getUserIdFromSessionHeader(userAgentHeader); - EntryCreator creator = new EntryCreator(); - long id = creator.createPart(userId, partData); + public PartData create(@Context UriInfo info, PartData partData) { + final String userId = getUserId(); + final EntryCreator creator = new EntryCreator(); + final long id = creator.createPart(userId, partData); log(userId, "created entry " + id); partData.setId(id); return partData; } + /** + * @param partData + * @return created part data + */ @PUT @Path("/transfer") @Consumes(MediaType.APPLICATION_JSON) @@ -448,38 +538,49 @@ public Response transfer(PartData partData) { return super.respond(response); } + /** + * @param info + * @param partId + * @param partData + * @return updated part data + */ @PUT @Path("/{id}") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - public PartData update(@Context UriInfo info, - @PathParam("id") long partId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader, - PartData partData) { - String userId = getUserIdFromSessionHeader(userAgentHeader); - long id = controller.updatePart(userId, partId, partData); + public PartData update(@Context final UriInfo info, @PathParam("id") final long partId, + final PartData partData) { + final String userId = getUserId(); + final long id = controller.updatePart(userId, partId, partData); log(userId, "updated entry " + id); partData.setId(id); return partData; } + /** + * @param id + */ @DELETE @Path("/{id}") - public void delete(@PathParam("id") long id) { + public void delete(@PathParam("id") final long id) { Logger.info("Deleting part " + id); + // TODO this does nothing but log? } + /** + * @param list + * @return Response for success or failure + */ @POST @Path("/trash") @Consumes(MediaType.APPLICATION_JSON) - public Response moveToTrash(ArrayList list, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String userId = getUserIdFromSessionHeader(userAgentHeader); - Type fooType = new TypeToken>() { + public Response moveToTrash(final ArrayList list) { + final String userId = getUserId(); + final Type fooType = new TypeToken>() { }.getType(); - Gson gson = new GsonBuilder().create(); - ArrayList data = gson.fromJson(gson.toJsonTree(list), fooType); - boolean success = controller.moveEntriesToTrash(userId, data); + final Gson gson = new GsonBuilder().create(); + final ArrayList data = gson.fromJson(gson.toJsonTree(list), fooType); + final boolean success = controller.moveEntriesToTrash(userId, data); return respond(success); } @@ -488,17 +589,15 @@ public Response moveToTrash(ArrayList list, * * @param partId id of entry whose link we are removing * @param linkedPart - * @param sessionId - * @return + * @return Response for success or failure */ @DELETE @Path("/{id}/links/{linkedId}") - public Response deleteLink(@PathParam("id") long partId, - @PathParam("linkedId") long linkedPart, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String sessionId) { - String userId = getUserIdFromSessionHeader(sessionId); + public Response deleteLink(@PathParam("id") final long partId, + @PathParam("linkedId") final long linkedPart) { + final String userId = getUserId(); log(userId, "removing link " + linkedPart + " from " + partId); - boolean success = controller.removeLink(userId, partId, linkedPart); + final boolean success = controller.removeLink(userId, partId, linkedPart); return respond(success); } @@ -518,7 +617,7 @@ public Response createLink(@PathParam("id") long partId, @QueryParam("type") @DefaultValue("CHILD") LinkType type, @HeaderParam(value = "X-ICE-Authentication-SessionId") String sessionId, PartData partData) { - String userId = getUserIdFromSessionHeader(sessionId); + String userId = getUserId(sessionId); log(userId, "adding entry link " + partData.getId() + " to " + partId); EntryLinks entryLinks = new EntryLinks(userId, partId); return super.respond(entryLinks.addLink(partData, type)); @@ -527,10 +626,9 @@ public Response createLink(@PathParam("id") long partId, @PUT @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - public Response updateEntries(@HeaderParam(value = "X-ICE-Authentication-SessionId") String sessionId, - @QueryParam(value = "visibility") Visibility visibility, + public Response updateEntries(@QueryParam(value = "visibility") Visibility visibility, List entryIds) { - String userId = getUserIdFromSessionHeader(sessionId); + String userId = getUserId(); log(userId, "updating visibility of " + entryIds.size() + " entries to " + visibility); Entries entries = new Entries(); List arrayList = new ArrayList<>(); diff --git a/src/main/java/org/jbei/ice/services/rest/PermissionResource.java b/src/main/java/org/jbei/ice/services/rest/PermissionResource.java index e2f9f7686..0a4ccb7d3 100644 --- a/src/main/java/org/jbei/ice/services/rest/PermissionResource.java +++ b/src/main/java/org/jbei/ice/services/rest/PermissionResource.java @@ -1,9 +1,9 @@ package org.jbei.ice.services.rest; import java.util.ArrayList; + import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; -import javax.ws.rs.HeaderParam; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; @@ -21,16 +21,22 @@ public class PermissionResource extends RestResource { private PermissionsController controller = new PermissionsController(); + /** + * @param val + * @param limit + * @return matching groups and users for autocomplete widget + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/autocomplete") - public Response autoComplete(@QueryParam("val") String val, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader, - @DefaultValue("8") @QueryParam("limit") int limit) { - String userId = getUserIdFromSessionHeader(userAgentHeader); - ArrayList result = controller.getMatchingGroupsOrUsers(userId, val, limit); - if (result == null) + public Response autoComplete(@QueryParam("val") final String val, + @DefaultValue("8") @QueryParam("limit") final int limit) { + final String userId = getUserId(); + final ArrayList result = controller.getMatchingGroupsOrUsers(userId, val, + limit); + if (result == null) { return super.respond(Response.Status.INTERNAL_SERVER_ERROR); + } return super.respond(Response.Status.OK, result); } } diff --git a/src/main/java/org/jbei/ice/services/rest/RemoteAccessResource.java b/src/main/java/org/jbei/ice/services/rest/RemoteAccessResource.java index 3358f63c5..4333b24b4 100644 --- a/src/main/java/org/jbei/ice/services/rest/RemoteAccessResource.java +++ b/src/main/java/org/jbei/ice/services/rest/RemoteAccessResource.java @@ -1,9 +1,10 @@ package org.jbei.ice.services.rest; import java.util.ArrayList; +import java.util.List; + import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; -import javax.ws.rs.HeaderParam; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; @@ -20,9 +21,8 @@ import org.jbei.ice.lib.vo.FeaturedDNASequence; /** - * REST resource for sending/retrieving messages from remote - * ICE instances. Local instances access this resource which contacts the remote - * resource on its behalf + * REST resource for sending/retrieving messages from remote ICE instances. Local instances access + * this resource which contacts the remote resource on its behalf * * @author Hector Plahar */ @@ -32,77 +32,110 @@ public class RemoteAccessResource extends RestResource { private RemoteAccessController controller = new RemoteAccessController(); /** - * @param remoteId unique identifier for remote partner being accessed + * @param remoteId + * unique identifier for remote partner being accessed * @return list of available folders that are available on the registry */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/available") - public ArrayList readRemoteUser(@PathParam("id") long remoteId) { + public List readRemoteUser(@PathParam("id") long remoteId) { return controller.getAvailableFolders(remoteId); } + /** + * @param remoteId + * @param email + * @return user from remote ICE + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/users/{email}") - public AccountTransfer getRemoteUser(@PathParam("id") long remoteId, - @PathParam("email") String email) { + public AccountTransfer getRemoteUser(@PathParam("id") final long remoteId, + @PathParam("email") final String email) { return controller.getRemoteUser(remoteId, email); } + /** + * @param remoteId + * @param partId + * @return sequence from remote ICE + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{entryId}/sequence") - public Response getSequence(@PathParam("id") long remoteId, - @PathParam("entryId") long partId) { - FeaturedDNASequence sequence = controller.getRemoteSequence(remoteId, partId); - if (sequence == null) + public Response getSequence(@PathParam("id") final long remoteId, + @PathParam("entryId") final long partId) { + final FeaturedDNASequence sequence = controller.getRemoteSequence(remoteId, partId); + if (sequence == null) { return Response.status(Response.Status.NO_CONTENT).build(); + } return Response.status(Response.Status.OK).entity(sequence).build(); } + /** + * @param remoteId + * @param partId + * @return traces from remote ICE + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/parts/{entryId}/traces") public Response getSequenceTraces(@PathParam("id") long remoteId, @PathParam("entryId") long partId) { - ArrayList traces = controller.getRemoteTraces(remoteId, partId); - if (traces == null) + List traces = controller.getRemoteTraces(remoteId, partId); + if (traces == null) { return Response.status(Response.Status.NO_CONTENT).build(); + } return Response.status(Response.Status.OK).entity(traces).build(); } + /** + * @param remoteId + * @param folderId + * @param offset + * @param limit + * @param sort + * @param asc + * @return public folders from remote ICE + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/folders/{folderId}") - public FolderDetails getPublicFolderEntries( - @PathParam("id") long remoteId, - @PathParam("folderId") long folderId, - @DefaultValue("0") @QueryParam("offset") int offset, - @DefaultValue("15") @QueryParam("limit") int limit, - @DefaultValue("created") @QueryParam("sort") String sort, - @DefaultValue("false") @QueryParam("asc") boolean asc, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { + public FolderDetails getPublicFolderEntries(@PathParam("id") final long remoteId, + @PathParam("folderId") final long folderId, + @DefaultValue("0") @QueryParam("offset") final int offset, + @DefaultValue("15") @QueryParam("limit") final int limit, + @DefaultValue("created") @QueryParam("sort") final String sort, + @DefaultValue("false") @QueryParam("asc") final boolean asc) { return controller.getPublicFolderEntries(remoteId, folderId, sort, asc, offset, limit); } + /** + * @param remoteId + * @param partId + * @return part samples from remote ICE + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/parts/{partId}/samples") public Response getRemotePartSamples(@PathParam("id") long remoteId, - @PathParam("partId") long partId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - ArrayList result = controller.getRemotePartSamples(remoteId, partId); + @PathParam("partId") long partId) { + List result = controller.getRemotePartSamples(remoteId, partId); return super.respond(result); } + /** + * @param remoteId + * @param partId + * @return comments from remote ICE + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/parts/{partId}/comments") public Response getRemotePartComments(@PathParam("id") long remoteId, - @PathParam("partId") long partId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - ArrayList result = controller.getRemotePartComments(remoteId, partId); + @PathParam("partId") long partId) { + List result = controller.getRemotePartComments(remoteId, partId); return super.respond(result); } } diff --git a/src/main/java/org/jbei/ice/services/rest/RestClient.java b/src/main/java/org/jbei/ice/services/rest/RestClient.java index 021108b23..30a5f8445 100644 --- a/src/main/java/org/jbei/ice/services/rest/RestClient.java +++ b/src/main/java/org/jbei/ice/services/rest/RestClient.java @@ -1,6 +1,6 @@ package org.jbei.ice.services.rest; -import java.util.HashMap; +import java.util.Map; /** * Parent Rest Client class @@ -12,7 +12,7 @@ public abstract class RestClient { protected final String AUTHENTICATION_PARAM_NAME = "X-ICE-Authentication-SessionId"; protected final String WOR_PARTNER_TOKEN_HEADER = "X-ICE-WOR-Token"; - public abstract Object get(String url, String path, Class clazz, HashMap queryParams); + public abstract T get(String url, String path, Class clazz, Map queryParams); - public abstract Object post(String url, String resourcePath, Object object, Class responseClass); + public abstract T post(String url, String resourcePath, Object object, Class responseClass); } diff --git a/src/main/java/org/jbei/ice/services/rest/RestResource.java b/src/main/java/org/jbei/ice/services/rest/RestResource.java index 729966422..4438d565d 100644 --- a/src/main/java/org/jbei/ice/services/rest/RestResource.java +++ b/src/main/java/org/jbei/ice/services/rest/RestResource.java @@ -1,12 +1,26 @@ package org.jbei.ice.services.rest; +import org.apache.commons.lang3.StringUtils; +import org.jbei.auth.KeyTable; +import org.jbei.auth.hmac.HmacAuthorizor; +import org.jbei.auth.hmac.HmacSignature; +import org.jbei.auth.hmac.HmacSignatureFactory; import org.jbei.ice.lib.account.SessionHandler; import org.jbei.ice.lib.common.logging.Logger; +import org.jbei.ice.lib.dao.hibernate.HibernateUtil; +import org.jbei.ice.lib.dto.ConfigurationKey; +import org.jbei.ice.lib.utils.Utils; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.HeaderParam; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.nio.file.Paths; +import java.security.Key; /** * Parent class for all rest resource objects @@ -18,37 +32,143 @@ public class RestResource { protected final String AUTHENTICATION_PARAM_NAME = "X-ICE-Authentication-SessionId"; protected final String WOR_PARTNER_TOKEN = "X-ICE-WOR-Token"; + // do lookup by using existing configuration DATA_DIRECTORY to find key names => key data + private static final KeyTable TABLE = new KeyTable() { + + // keys stored in /var/lib/tomcat6/data/rest-auth by default + private final File directory; + + { + // need to force-create a transaction to get the DATA_DIRECTORY config value + HibernateUtil.beginTransaction(); + directory = Paths.get(Utils.getConfigValue(ConfigurationKey.DATA_DIRECTORY), + "rest-auth").toFile(); + HibernateUtil.commitTransaction(); + } + + @Override + public Key getKey(final String keyId) { + try { + // find file named by keyId in the directory + final File keyFile = new File(directory, keyId); + // collect all lines in the file to a buffer + final StringBuilder encoded = new StringBuilder(); + try (final FileReader reader = new FileReader(keyFile); + final BufferedReader buffered = new BufferedReader(reader);) { + String line; + while ((line = buffered.readLine()) != null) { + encoded.append(line); + } + // after reading all lines, decode value into a Key object + return HmacSignatureFactory.decodeKey(encoded.toString()); + } + } catch (final Throwable t) { + Logger.error("Failed to load rest-auth key " + keyId); + } + return null; + } + + }; + + private static final HmacAuthorizor AUTHORIZOR = new HmacAuthorizor(TABLE); + + @HeaderParam(value = AUTHENTICATION_PARAM_NAME) + private String sessionId; + + @HeaderParam(value = "Authorization") + private String hmacHeader; + @Context - protected UriInfo uriInfo; + private HttpServletRequest request; + + /** + * Extract the User ID from header values in the resource request. + * + * @return a string User ID + * @throws WebApplicationException for unauthorized access + */ + protected String getUserId() { + return getUserId(sessionId); + } + + /** + * Extract the User ID from a query parameter value or header values in the resource request. + * + * @param sessionId a session ID sent via query parameters + * @return a string User ID + * @throws WebApplicationException for unauthorized access + */ + protected String getUserId(final String sessionId) { + String userId = SessionHandler.getUserIdBySession(sessionId); + if (!StringUtils.isEmpty(userId)) + return userId; + + final Object hmac = request.getAttribute(AuthenticationInterceptor.HMAC_SIGNATURE); + final Object valid = request.getAttribute(AuthenticationInterceptor.EXPECTED_SIGNATURE); + if (hmac != null && hmac instanceof HmacSignature) { + final HmacSignature generated = (HmacSignature) hmac; + if (generated.generateSignature().equals(valid)) { + // TODO validation of meaningful userId + // e.g. "admin" account on EDD won't mean anything to ICE + userId = generated.getUserId(); + } + } - protected String getUserIdFromSessionHeader(String sessionHeader) { - String userId = SessionHandler.getUserIdBySession(sessionHeader); if (userId == null) throw new WebApplicationException(Response.Status.UNAUTHORIZED); return userId; } - protected Response respond(Response.Status status, Object obj) { - if (obj == null) + /** + * Create a {@link Response} object from an entity object. + * + * @param status HTTP status code + * @param obj entity in response + * @return a Response object for the resource request, uses 500 error response if entity is + * {@code null} + */ + protected Response respond(final Response.Status status, final Object obj) { + if (obj == null) { return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); + } return Response.status(status).entity(obj).build(); } - protected Response respond(Object object) { - if (object == null) + /** + * Create a {@link Response} object from an entity object. + * + * @param object entity in response + * @return a 404 NOT FOUND if object is {@code null}, else a 200 OK response with the entity + */ + protected Response respond(final Object object) { + if (object == null) { return Response.status(Response.Status.NOT_FOUND).build(); + } return Response.status(Response.Status.OK).entity(object).build(); } - protected Response respond(Response.Status status) { + /** + * Create an empty {@link Response} object. + * + * @param status HTTP status code to use on the Response + * @return a Response object for the resource request + */ + protected Response respond(final Response.Status status) { return Response.status(status).build(); } - protected Response respond(boolean success) { - if (success) + /** + * Create an empty {@link Response} object. + * + * @param success success/failure flag + * @return a 200 OK response if success is {@code true}, otherwise a 500 error response + */ + protected Response respond(final boolean success) { + if (success) { return Response.status(Response.Status.OK).build(); + } return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); } @@ -58,9 +178,8 @@ protected Response respond(boolean success) { * @param userId unique user identifier * @param message log message */ - protected void log(String userId, String message) { - if (userId == null) - userId = "Unknown"; - Logger.info(userId + ": " + message); + protected void log(final String userId, final String message) { + final String who = (userId == null) ? "Unknown" : userId; + Logger.info(who + ": " + message); } } diff --git a/src/main/java/org/jbei/ice/services/rest/SampleResource.java b/src/main/java/org/jbei/ice/services/rest/SampleResource.java index 74f523365..64b1bdfe1 100644 --- a/src/main/java/org/jbei/ice/services/rest/SampleResource.java +++ b/src/main/java/org/jbei/ice/services/rest/SampleResource.java @@ -1,6 +1,5 @@ package org.jbei.ice.services.rest; -import org.apache.commons.lang.StringUtils; import org.jbei.ice.lib.common.logging.Logger; import org.jbei.ice.lib.dto.StorageLocation; import org.jbei.ice.lib.dto.sample.PartSample; @@ -27,127 +26,156 @@ public class SampleResource extends RestResource { private RequestRetriever requestRetriever = new RequestRetriever(); private SampleService sampleService = new SampleService(); + /** + * @param token + * @return Response with matching part sample + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("{token}") - public Response getSampleByToken(@PathParam("token") String token) { + public Response getSampleByToken(@PathParam("token") String token, + @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { try { - ArrayList result = sampleService.getSamplesByBarcode(null, token); + String userId = getUserId(userAgentHeader); + ArrayList result = sampleService.getSamplesByBarcode(userId, token); return super.respond(result); - } catch (Exception e) { + } catch (final Exception e) { Logger.error(e); return super.respond(false); } } + /** + * @param offset + * @param limit + * @param sort + * @param asc + * @param filter + * @param status + * @return Response with matching samples + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/requests") - public Response getRequests( - @DefaultValue("0") @QueryParam("offset") int offset, - @DefaultValue("15") @QueryParam("limit") int limit, - @DefaultValue("requested") @QueryParam("sort") String sort, - @DefaultValue("false") @QueryParam("asc") boolean asc, - @QueryParam("filter") String filter, - @QueryParam("status") String status, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String userId = getUserIdFromSessionHeader(userAgentHeader); + public Response getRequests(@DefaultValue("0") @QueryParam("offset") final int offset, + @DefaultValue("15") @QueryParam("limit") final int limit, + @DefaultValue("requested") @QueryParam("sort") final String sort, + @DefaultValue("false") @QueryParam("asc") final boolean asc, + @QueryParam("filter") final String filter, + @QueryParam("status") final SampleRequestStatus status) { + final String userId = getUserId(); Logger.info(userId + ": retrieving sample requests"); - SampleRequestStatus requestStatus = null; - if (!StringUtils.isEmpty(status)) { - try { - requestStatus = SampleRequestStatus.valueOf(status); - } catch (Exception e) { - requestStatus = null; - } - } - UserSamples samples = requestRetriever.getRequests(userId, offset, limit, sort, asc, requestStatus, filter); + final UserSamples samples = requestRetriever.getRequests(userId, offset, limit, sort, asc, + status, filter); return super.respond(Response.Status.OK, samples); } /** - * Sets the status of sample requests. Must have admin privs to set the sample for others - * This is intended for requesting samples + * Sets the status of sample requests. Must have admin privs to set the sample for others This + * is intended for requesting samples * - * @param sessionId session identifier + * @param status + * @param requestIds + * @return Response success or failure */ @PUT @Path("/requests") - public Response setRequestStatus( - @HeaderParam(value = "X-ICE-Authentication-SessionId") String sessionId, - @QueryParam("status") SampleRequestStatus status, - ArrayList requestIds) { - String userId = getUserIdFromSessionHeader(sessionId); + public Response setRequestStatus(@QueryParam("status") final SampleRequestStatus status, + final ArrayList requestIds) { + final String userId = getUserId(); try { - if (requestIds == null || requestIds.isEmpty()) + if (requestIds == null || requestIds.isEmpty()) { return super.respond(Response.Status.OK); + } - ArrayList sampleRequestIds = new ArrayList<>(); - for (Number number : requestIds) + final ArrayList sampleRequestIds = new ArrayList<>(); + for (final Number number : requestIds) { sampleRequestIds.add(number.longValue()); + } - boolean success = requestRetriever.setRequestsStatus(userId, sampleRequestIds, status); + final boolean success = requestRetriever.setRequestsStatus(userId, sampleRequestIds, + status); return super.respond(success); - } catch (Exception e) { + } catch (final Exception e) { Logger.error(e); return super.respond(Response.Status.INTERNAL_SERVER_ERROR); } } + /** + * @param requestId + * @return Response with the removed sample + */ @DELETE @Path("/requests/{id}") - public Response deleteSampleRequest(@HeaderParam(value = "X-ICE-Authentication-SessionId") String sessionId, - @PathParam("id") long requestId) { - String userId = getUserIdFromSessionHeader(sessionId); + public Response deleteSampleRequest(@PathParam("id") final long requestId) { + final String userId = getUserId(); return respond(Response.Status.OK, requestRetriever.removeSampleFromCart(userId, requestId)); } + /** + * @param requestId + * @param status + * @return Response with the updated sample request + */ @PUT @Path("/requests/{id}") - public Response updateSampleRequest( - @HeaderParam(value = "X-ICE-Authentication-SessionId") String sessionId, - @PathParam("id") long requestId, - @QueryParam("status") SampleRequestStatus status) { - String userId = getUserIdFromSessionHeader(sessionId); - SampleRequest request = requestRetriever.updateStatus(userId, requestId, status); + public Response updateSampleRequest(@PathParam("id") final long requestId, + @QueryParam("status") final SampleRequestStatus status) { + final String userId = getUserId(); + final SampleRequest request = requestRetriever.updateStatus(userId, requestId, status); return respond(Response.Status.OK, request); } + /** + * @param offset + * @param limit + * @param sort + * @param asc + * @param uid + * @param status + * @return response with the matching sample requests + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/requests/{userId}") - public Response getUserRequests( - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader, - @DefaultValue("0") @QueryParam("offset") int offset, - @DefaultValue("15") @QueryParam("limit") int limit, - @DefaultValue("requested") @QueryParam("sort") String sort, - @DefaultValue("false") @QueryParam("asc") boolean asc, - @PathParam("userId") long uid, - @DefaultValue("IN_CART") @QueryParam("status") SampleRequestStatus status) { - String userId = getUserIdFromSessionHeader(userAgentHeader); + public Response getUserRequests(@DefaultValue("0") @QueryParam("offset") final int offset, + @DefaultValue("15") @QueryParam("limit") final int limit, + @DefaultValue("requested") @QueryParam("sort") final String sort, + @DefaultValue("false") @QueryParam("asc") final boolean asc, + @PathParam("userId") final long uid, + @DefaultValue("IN_CART") @QueryParam("status") final SampleRequestStatus status) { + final String userId = getUserId(); Logger.info(userId + ": retrieving sample requests for user"); - UserSamples userSamples = requestRetriever.getUserSamples(userId, status, offset, limit, sort, asc); + final UserSamples userSamples = requestRetriever.getUserSamples(userId, status, offset, + limit, sort, asc); return super.respond(Response.Status.OK, userSamples); } + /** + * @param request + * @return Response with the added sample requests + */ @POST @Produces(MediaType.APPLICATION_JSON) @Path("/requests") - public ArrayList addRequest( - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader, - SampleRequest request) { - String userId = getUserIdFromSessionHeader(userAgentHeader); + public ArrayList addRequest(final SampleRequest request) { + final String userId = getUserId(); log(userId, "add sample request to cart for " + request.getPartData().getId()); return requestRetriever.placeSampleInCart(userId, request); } + /** + * @param type + * @return Response with the current sample requests + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/storage/{type}") public Response getSampleStorageType( @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader, @DefaultValue("IN_CART") @QueryParam("type") String type) { - String userId = getUserIdFromSessionHeader(userAgentHeader); + String userId = getUserId(userAgentHeader); List locations = sampleService.getStorageLocations(userId, type); return respond(locations); } diff --git a/src/main/java/org/jbei/ice/services/rest/SearchResource.java b/src/main/java/org/jbei/ice/services/rest/SearchResource.java index a7d17890f..88da6eef0 100644 --- a/src/main/java/org/jbei/ice/services/rest/SearchResource.java +++ b/src/main/java/org/jbei/ice/services/rest/SearchResource.java @@ -1,7 +1,6 @@ package org.jbei.ice.services.rest; -import org.apache.commons.lang.StringUtils; -import org.jbei.ice.lib.account.SessionHandler; +import org.apache.commons.lang3.StringUtils; import org.jbei.ice.lib.common.logging.Logger; import org.jbei.ice.lib.dto.entry.EntryType; import org.jbei.ice.lib.dto.search.SearchQuery; @@ -16,8 +15,8 @@ import java.util.List; /** - * REST resource for searching. Supports keyword search with query params for filtering and - * advanced search + * REST resource for searching. Supports keyword search with query params for filtering and advanced + * search * * @author Hector Plahar */ @@ -27,24 +26,26 @@ public class SearchResource extends RestResource { private SearchController controller = new SearchController(); /** - * Advanced Search. The use of post is mostly for the sequence string for - * blast which can get very long and results in a 413 status code if - * sent via GET + * Advanced Search. The use of post is mostly for the sequence string for blast which can get + * very long and results in a 413 status code if sent via GET + * + * @param searchWeb + * whether to perform a web of registry search or not + * @param query + * parameters to the search * * @return results of the search */ @POST @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - public Response search( - @HeaderParam(value = "X-ICE-Authentication-SessionId") String sessionHeader, - @DefaultValue("false") @QueryParam("webSearch") boolean searchWeb, - SearchQuery query) { - String userId = SessionHandler.getUserIdBySession(sessionHeader); + public Response search(@DefaultValue("false") @QueryParam("webSearch") final boolean searchWeb, + final SearchQuery query) { + final String userId = getUserId(); try { - SearchResults results = controller.runSearch(userId, query, searchWeb); + final SearchResults results = controller.runSearch(userId, query, searchWeb); return super.respond(Response.Status.OK, results); - } catch (Exception e) { + } catch (final Exception e) { Logger.error(e); return super.respond(Response.Status.INTERNAL_SERVER_ERROR); } @@ -53,41 +54,44 @@ public Response search( /** * Keyword search * - * @param queryString keywords to search on - * @param searchWeb whether to perform a web of registry search or not - * @param offset result start - * @param limit result count upper limit - * @param sort result sort - * @param asc true if return results in ascending order, false otherwise - * @param sessionId user unique session identifier + * @param queryString + * keywords to search on + * @param searchWeb + * whether to perform a web of registry search or not + * @param offset + * result start + * @param limit + * result count upper limit + * @param sort + * result sort + * @param asc + * true if return results in ascending order, false otherwise * @return wrapper around list of search results conforming to query params */ @GET @Produces(MediaType.APPLICATION_JSON) - public Response search( - @QueryParam("q") String queryString, - @DefaultValue("false") @QueryParam("webSearch") boolean searchWeb, - @DefaultValue("0") @QueryParam("offset") int offset, - @DefaultValue("15") @QueryParam("limit") int limit, - @DefaultValue("relevance") @QueryParam("sort") String sort, - @DefaultValue("false") @QueryParam("asc") boolean asc, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String sessionId) { - String userId = SessionHandler.getUserIdBySession(sessionId); + public Response search(@QueryParam("q") final String queryString, + @DefaultValue("false") @QueryParam("webSearch") final boolean searchWeb, + @DefaultValue("0") @QueryParam("offset") final int offset, + @DefaultValue("15") @QueryParam("limit") final int limit, + @DefaultValue("relevance") @QueryParam("sort") final String sort, + @DefaultValue("false") @QueryParam("asc") final boolean asc) { + final String userId = getUserId(); if (StringUtils.isEmpty(userId) && !searchWeb) { return super.respond(Response.Status.FORBIDDEN); } log(userId, "query \'" + queryString + '\''); - SearchQuery query = new SearchQuery(); + final SearchQuery query = new SearchQuery(); query.setQueryString(queryString); - SearchQuery.Parameters parameters = query.getParameters(); + final SearchQuery.Parameters parameters = query.getParameters(); parameters.setRetrieveCount(limit); parameters.setStart(offset); parameters.setSortAscending(asc); parameters.setSortField(ColumnField.valueOf(sort.toUpperCase())); - List types = Arrays.asList(EntryType.values()); + final List types = Arrays.asList(EntryType.values()); query.setEntryTypes(types); return super.respond(controller.runSearch(userId, query, searchWeb)); } diff --git a/src/main/java/org/jbei/ice/services/rest/UserResource.java b/src/main/java/org/jbei/ice/services/rest/UserResource.java index 4653cb2b5..05faa8861 100644 --- a/src/main/java/org/jbei/ice/services/rest/UserResource.java +++ b/src/main/java/org/jbei/ice/services/rest/UserResource.java @@ -1,5 +1,22 @@ package org.jbei.ice.services.rest; +import java.util.ArrayList; +import java.util.List; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DefaultValue; +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 javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + import org.jbei.ice.lib.account.AccountController; import org.jbei.ice.lib.account.AccountTransfer; import org.jbei.ice.lib.account.PreferencesController; @@ -17,14 +34,6 @@ import org.jbei.ice.lib.group.GroupController; import org.jbei.ice.lib.shared.ColumnField; -import javax.ws.rs.*; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; -import java.util.ArrayList; -import java.util.List; - /** * REST Resource for users * @@ -38,21 +47,22 @@ public class UserResource extends RestResource { private RequestRetriever requestRetriever = new RequestRetriever(); /** - * Retrieves list of users that are available to user making request. Availability is - * defined by being in the same group if the user does not have admin privileges. + * Retrieves list of users that are available to user making request. Availability is defined by + * being in the same group if the user does not have admin privileges. * - * @param sessionId unique user session identifier + * @param offset + * @param limit + * @param sort + * @param asc * @return wrapper around list of users */ @GET @Produces(MediaType.APPLICATION_JSON) - public AccountResults get( - @DefaultValue("0") @QueryParam("offset") int offset, - @DefaultValue("15") @QueryParam("limit") int limit, - @DefaultValue("lastName") @QueryParam("sort") String sort, - @DefaultValue("true") @QueryParam("asc") boolean asc, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String sessionId) { - String userId = getUserIdFromSessionHeader(sessionId); + public AccountResults get(@DefaultValue("0") @QueryParam("offset") final int offset, + @DefaultValue("15") @QueryParam("limit") final int limit, + @DefaultValue("lastName") @QueryParam("sort") final String sort, + @DefaultValue("true") @QueryParam("asc") final boolean asc) { + final String userId = getUserId(); log(userId, "retrieving available accounts"); return groupController.getAvailableAccounts(userId, offset, limit, asc, sort); } @@ -60,159 +70,215 @@ public AccountResults get( /** * Retrieves (up to specified limit), the list of users that match the value * - * @param val text to match against users - * @param limit upper limit for number of users to return - * @param sessionId unique user session identifier + * @param val + * text to match against users + * @param limit + * upper limit for number of users to return * @return list of matching users */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/autocomplete") - public ArrayList getAutoCompleteForAvailableAccounts( - @QueryParam("val") String val, - @DefaultValue("8") @QueryParam("limit") int limit, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String sessionId) { - String userId = getUserIdFromSessionHeader(sessionId); + public List getAutoCompleteForAvailableAccounts( + @QueryParam("val") final String val, + @DefaultValue("8") @QueryParam("limit") final int limit) { + final String userId = getUserId(); return controller.getMatchingAccounts(userId, val, limit); } + /** + * @param info + * @param userId + * @return account information for transfer + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{id}") - public AccountTransfer read(@Context UriInfo info, @PathParam("id") String userId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { + public AccountTransfer read(@Context final UriInfo info, @PathParam("id") final String userId) { Account account; - if (userId.matches("\\d+(\\.\\d+)?")) + if (userId.matches("\\d+(\\.\\d+)?")) { account = controller.get(Long.decode(userId)); - else + } else { account = controller.getByEmail(userId); + } - if (account != null) + if (account != null) { return account.toDataTransferObject(); + } return null; } + /** + * @param info + * @param userId + * @return group listing for a user + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/groups") - public ArrayList getProfileGroups(@Context UriInfo info, @PathParam("id") long userId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String userIdString = getUserIdFromSessionHeader(userAgentHeader); + public ArrayList getProfileGroups(@Context final UriInfo info, + @PathParam("id") final long userId) { + final String userIdString = getUserId(); return groupController.retrieveUserGroups(userIdString, userId, false); } + /** + * @param userId + * @param userGroup + * @return created group + */ @PUT @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/groups") - public UserGroup createGroup(@PathParam("id") long userId, - UserGroup userGroup, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String userIdString = getUserIdFromSessionHeader(userAgentHeader); + public UserGroup createGroup(@PathParam("id") final long userId, final UserGroup userGroup) { + final String userIdString = getUserId(); return groupController.createGroup(userIdString, userGroup); } + /** + * @param info + * @param userId + * @param offset + * @param limit + * @param sort + * @param asc + * @return collection for user's part entries + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/entries") - public FolderDetails getProfileEntries(@Context UriInfo info, - @PathParam("id") long userId, - @DefaultValue("0") @QueryParam("offset") int offset, - @DefaultValue("15") @QueryParam("limit") int limit, - @DefaultValue("created") @QueryParam("sort") String sort, - @DefaultValue("false") @QueryParam("asc") boolean asc, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String userIdString = getUserIdFromSessionHeader(userAgentHeader); - EntryController entryController = new EntryController(); - ColumnField field = ColumnField.valueOf(sort.toUpperCase()); - - Account requestAccount = DAOFactory.getAccountDAO().get(userId); - List entries = entryController.retrieveOwnerEntries(userIdString, requestAccount.getEmail(), field, - asc, offset, limit); - long count = entryController.getNumberOfOwnerEntries(userIdString, requestAccount.getEmail()); - FolderDetails details = new FolderDetails(); + public FolderDetails getProfileEntries(@Context final UriInfo info, + @PathParam("id") final long userId, + @DefaultValue("0") @QueryParam("offset") final int offset, + @DefaultValue("15") @QueryParam("limit") final int limit, + @DefaultValue("created") @QueryParam("sort") final String sort, + @DefaultValue("false") @QueryParam("asc") final boolean asc) { + final String userIdString = getUserId(); + final EntryController entryController = new EntryController(); + final ColumnField field = ColumnField.valueOf(sort.toUpperCase()); + + final Account requestAccount = DAOFactory.getAccountDAO().get(userId); + final List entries = entryController.retrieveOwnerEntries(userIdString, + requestAccount.getEmail(), field, asc, offset, limit); + final long count = entryController.getNumberOfOwnerEntries(userIdString, + requestAccount.getEmail()); + final FolderDetails details = new FolderDetails(); details.getEntries().addAll(entries); details.setCount(count); return details; } + /** + * @param info + * @param userId + * @return preferences for a user + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/preferences") - public UserPreferences getUserPreferences(@Context UriInfo info, - @PathParam("id") long userId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String userIdString = getUserIdFromSessionHeader(userAgentHeader); - PreferencesController preferencesController = new PreferencesController(); + public UserPreferences getUserPreferences(@Context final UriInfo info, + @PathParam("id") final long userId) { + final String userIdString = getUserId(); + final PreferencesController preferencesController = new PreferencesController(); return preferencesController.getUserPreferences(userIdString, userId); } + /** + * @param userId + * @param key + * @param value + * @return updated preferences for a user + */ @POST @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/preferences/{key}") - public PreferenceInfo updatePreference( - @PathParam("id") long userId, - @PathParam("key") String key, - @QueryParam("value") String value, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String userIdString = getUserIdFromSessionHeader(userAgentHeader); - PreferencesController preferencesController = new PreferencesController(); + public PreferenceInfo updatePreference(@PathParam("id") final long userId, + @PathParam("key") final String key, @QueryParam("value") final String value) { + final String userIdString = getUserId(); + final PreferencesController preferencesController = new PreferencesController(); return preferencesController.updatePreference(userIdString, userId, key, value); } + /** + * @param info + * @param userId + * @param transfer + * @return updated user information + */ @POST @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) @Path("/{id}") - public AccountTransfer update(@Context UriInfo info, @PathParam("id") long userId, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader, - AccountTransfer transfer) { - String user = getUserIdFromSessionHeader(userAgentHeader); + public AccountTransfer update(@Context final UriInfo info, @PathParam("id") final long userId, + final AccountTransfer transfer) { + final String user = getUserId(); return controller.updateAccount(user, userId, transfer); } + /** + * @param info + * @param transfer + * @return Response for success or failure + */ @POST @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) @Path("/password") - public Response resetPassword(@Context UriInfo info, AccountTransfer transfer) { - boolean success = controller.resetPassword(transfer.getEmail()); - if (!success) + public Response resetPassword(@Context final UriInfo info, final AccountTransfer transfer) { + final boolean success = controller.resetPassword(transfer.getEmail()); + if (!success) { return super.respond(Response.Status.NOT_FOUND); + } return super.respond(Response.Status.OK); } + /** + * @param transfer + * @return updated user information + */ @PUT @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) @Path("/password") - public AccountTransfer updatePassword( - @HeaderParam(value = "X-ICE-Authentication-SessionId") String sessionId, - AccountTransfer transfer) { - String user = getUserIdFromSessionHeader(sessionId); + public AccountTransfer updatePassword(final AccountTransfer transfer) { + final String user = getUserId(); return controller.updatePassword(user, transfer); } + /** + * @param accountTransfer + * @return Response with created user information + */ @PUT @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - public Response createNewUser(AccountTransfer accountTransfer) { - accountTransfer = controller.createNewAccount(accountTransfer, true); - return super.respond(accountTransfer); + public Response createNewUser(final AccountTransfer accountTransfer) { + final AccountTransfer created = controller.createNewAccount(accountTransfer, true); + return super.respond(created); } + /** + * @param userId + * @param offset + * @param limit + * @param sort + * @param asc + * @param uid + * @param status + * @return Response with user's samples + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/samples") - public Response getRequestedSamples(@PathParam("id") long userId, - @DefaultValue("0") @QueryParam("offset") int offset, - @DefaultValue("15") @QueryParam("limit") int limit, - @DefaultValue("requested") @QueryParam("sort") String sort, - @DefaultValue("false") @QueryParam("asc") boolean asc, - @PathParam("userId") long uid, - @DefaultValue("") @QueryParam("status") SampleRequestStatus status, - @HeaderParam(value = "X-ICE-Authentication-SessionId") String userAgentHeader) { - String user = getUserIdFromSessionHeader(userAgentHeader); - return super.respond(Response.Status.OK, requestRetriever.getUserSamples(user, status, offset, limit, sort, - asc)); + public Response getRequestedSamples(@PathParam("id") final long userId, + @DefaultValue("0") @QueryParam("offset") final int offset, + @DefaultValue("15") @QueryParam("limit") final int limit, + @DefaultValue("requested") @QueryParam("sort") final String sort, + @DefaultValue("false") @QueryParam("asc") final boolean asc, + @PathParam("userId") final long uid, + @DefaultValue("") @QueryParam("status") final SampleRequestStatus status) { + final String user = getUserId(); + return super.respond(Response.Status.OK, + requestRetriever.getUserSamples(user, status, offset, limit, sort, asc)); } } diff --git a/src/main/java/org/jbei/ice/services/rest/WebResource.java b/src/main/java/org/jbei/ice/services/rest/WebResource.java index 64276e8fe..3c556c7e6 100644 --- a/src/main/java/org/jbei/ice/services/rest/WebResource.java +++ b/src/main/java/org/jbei/ice/services/rest/WebResource.java @@ -1,6 +1,23 @@ package org.jbei.ice.services.rest; -import org.apache.commons.lang.StringUtils; +import java.util.List; + +import javax.ws.rs.DELETE; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +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 javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.apache.commons.lang3.StringUtils; import org.jbei.ice.lib.dto.entry.AttachmentInfo; import org.jbei.ice.lib.dto.entry.PartData; import org.jbei.ice.lib.dto.entry.PartStatistics; @@ -12,13 +29,6 @@ import org.jbei.ice.lib.net.WoRController; import org.jbei.ice.lib.vo.FeaturedDNASequence; -import javax.ws.rs.*; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; -import java.util.ArrayList; - /** * Resource for web of registries requests * @@ -31,19 +41,17 @@ public class WebResource extends RestResource { private final RemoteEntries remoteEntries = new RemoteEntries(); /** - * Retrieves information on other ice instances that is in a web of registries - * configuration with this instance; also know as registry partners + * Retrieves information on other ice instances that is in a web of registries configuration + * with this instance; also know as registry partners * - * @param approvedOnly if true (default), only instances that have been approved are returned - * @param userAgentHeader session id for user logged in user + * @param approvedOnly + * if true, only instances that have been approved are returned; defaults to true * @return wrapper around the list of registry partners */ @GET @Produces(MediaType.APPLICATION_JSON) - public Response query( - @DefaultValue("true") @QueryParam("approved_only") boolean approvedOnly, - @HeaderParam(AUTHENTICATION_PARAM_NAME) String userAgentHeader) { - getUserIdFromSessionHeader(userAgentHeader); + public Response query(@DefaultValue("true") @QueryParam("approved_only") boolean approvedOnly) { + getUserId(); // ensure valid session or auth header return super.respond(controller.getRegistryPartners(approvedOnly)); } @@ -55,115 +63,143 @@ public Response query( * @param limit maximum number of entries to retrieve * @param sort field to sort on * @param asc sort order - * @param sessionId unique identifier for user making request - * @return OK HTTP status with the list of entries wrapped in a result object + * + * @return Response with public entries from registry partners */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/entries") public Response getWebEntries( - @PathParam("id") long partnerId, - @DefaultValue("0") @QueryParam("offset") int offset, - @DefaultValue("15") @QueryParam("limit") int limit, - @DefaultValue("created") @QueryParam("sort") String sort, - @DefaultValue("false") @QueryParam("asc") boolean asc, - @HeaderParam(AUTHENTICATION_PARAM_NAME) String sessionId) { - String userId = getUserIdFromSessionHeader(sessionId); - WebEntries result = remoteEntries.getPublicEntries(userId, partnerId, offset, limit, sort, asc); + @PathParam("id") final long partnerId, + @DefaultValue("0") @QueryParam("offset") final int offset, + @DefaultValue("15") @QueryParam("limit") final int limit, + @DefaultValue("created") @QueryParam("sort") final String sort, + @DefaultValue("false") @QueryParam("asc") final boolean asc) { + final String userId = getUserId(); + final WebEntries result = remoteEntries.getPublicEntries(userId, partnerId, offset, limit, sort, asc); return super.respond(Response.Status.OK, result); } + /** + * @param partnerId + * @param partId + * @return attachment info on a registry partner entry + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/entries/{entryId}/attachments") - public ArrayList getAttachments( - @PathParam("id") long partnerId, - @PathParam("entryId") long partId, - @HeaderParam(AUTHENTICATION_PARAM_NAME) String userAgentHeader) { - String userId = getUserIdFromSessionHeader(userAgentHeader); + public List getAttachments(@PathParam("id") final long partnerId, + @PathParam("entryId") final long partId) { + final String userId = getUserId(); return remoteEntries.getEntryAttachments(userId, partnerId, partId); } + /** + * @param remoteId + * @param entrySelection + * @return Response for success + */ @POST @Path("/{id}/transfer") - public Response transferEntries( - @PathParam("id") long remoteId, - EntrySelection entrySelection, - @HeaderParam(AUTHENTICATION_PARAM_NAME) String sessionId) { - String userId = super.getUserIdFromSessionHeader(sessionId); + public Response transferEntries(@PathParam("id") final long remoteId, + final EntrySelection entrySelection) { + final String userId = super.getUserId(); remoteEntries.transferEntries(userId, remoteId, entrySelection); return super.respond(Response.Status.OK); } + /** + * @param partnerId + * @param entryId + * @return Response with a specific entry for a registry partner + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/entries/{entryId}") - public Response getWebEntry(@Context UriInfo uriInfo, - @PathParam("id") long partnerId, - @PathParam("entryId") long entryId, - @HeaderParam(AUTHENTICATION_PARAM_NAME) String sessionId) { - String userId = super.getUserIdFromSessionHeader(sessionId); - PartData result = remoteEntries.getPublicEntry(userId, partnerId, entryId); + public Response getWebEntry( + @PathParam("id") final long partnerId, @PathParam("entryId") final long entryId) { + final String userId = super.getUserId(); + final PartData result = remoteEntries.getPublicEntry(userId, partnerId, entryId); return super.respond(Response.Status.OK, result); } + /** + * @param partnerId + * @param entryId + * @return Response with a specific entry tooltip for a registry partner + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/entries/{entryId}/tooltip") - public Response getWebEntryTooltip(@Context UriInfo uriInfo, - @PathParam("id") long partnerId, - @PathParam("entryId") long entryId, - @HeaderParam(AUTHENTICATION_PARAM_NAME) String sessionId) { - String userId = super.getUserIdFromSessionHeader(sessionId); - PartData result = remoteEntries.getPublicEntryTooltip(userId, partnerId, entryId); + public Response getWebEntryTooltip( + @PathParam("id") final long partnerId, @PathParam("entryId") final long entryId) { + final String userId = super.getUserId(); + final PartData result = remoteEntries.getPublicEntryTooltip(userId, partnerId, entryId); return super.respond(Response.Status.OK, result); } + /** + * @param partnerId + * @param entryId + * @return Response with statistics on a specific entry for a registry partner + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/entries/{entryId}/statistics") - public Response getStatistics(@PathParam("id") long partnerId, - @PathParam("entryId") long entryId, - @HeaderParam(AUTHENTICATION_PARAM_NAME) String sessionId) { - String userId = super.getUserIdFromSessionHeader(sessionId); - PartStatistics statistics = remoteEntries.getPublicEntryStatistics(userId, partnerId, entryId); + public Response getStatistics(@PathParam("id") final long partnerId, + @PathParam("entryId") final long entryId) { + final String userId = super.getUserId(); + final PartStatistics statistics = remoteEntries.getPublicEntryStatistics(userId, partnerId, entryId); return super.respond(statistics); } + /** + * @param partnerId + * @param entryId + * @return Response with a sequence on a registry partner entry + */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{id}/entries/{entryId}/sequence") - public Response getWebEntrySequence(@Context UriInfo uriInfo, - @PathParam("id") long partnerId, - @PathParam("entryId") long entryId, - @HeaderParam(AUTHENTICATION_PARAM_NAME) String sessionId) { - String userId = super.getUserIdFromSessionHeader(sessionId); - FeaturedDNASequence result = remoteEntries.getPublicEntrySequence(userId, partnerId, entryId); + public Response getWebEntrySequence( + @PathParam("id") final long partnerId, @PathParam("entryId") final long entryId) { + final String userId = super.getUserId(); + final FeaturedDNASequence result = remoteEntries.getPublicEntrySequence(userId, partnerId, entryId); return super.respond(Response.Status.OK, result); } + /** + * @param info + * @param partner + * @return Response with an added registry partner + */ @POST @Path("/partner") // admin function - public Response addWebPartner(@Context UriInfo info, - @HeaderParam(AUTHENTICATION_PARAM_NAME) String sessionId, - RegistryPartner partner) { - String userId = getUserIdFromSessionHeader(sessionId); - RemoteContact contactRemote = new RemoteContact(); - RegistryPartner registryPartner = contactRemote.addWebPartner(userId, partner); + public Response addWebPartner(final RegistryPartner partner) { + final String userId = getUserId(); + final RemoteContact contactRemote = new RemoteContact(); + final RegistryPartner registryPartner = contactRemote.addWebPartner(userId, partner); return respond(Response.Status.OK, registryPartner); } + /** + * @param info + * @param partnerId + * @return Response with registry partner info + */ @GET @Path("/partner/{id}") - public Response getWebPartner(@Context UriInfo info, - @PathParam("id") long partnerId, - @HeaderParam(AUTHENTICATION_PARAM_NAME) String sessionId) { - String userId = getUserIdFromSessionHeader(sessionId); - RegistryPartner partner = controller.getWebPartner(userId, partnerId); + public Response getWebPartner(@Context final UriInfo info, @PathParam("id") final long partnerId) { + final String userId = getUserId(); + final RegistryPartner partner = controller.getWebPartner(userId, partnerId); return super.respond(Response.Status.OK, partner); } + /** + * @param partner + * @return Response for success or failure + */ @POST @Path("/partner/remote") public Response remoteWebPartnerRequest(RegistryPartner partner) { @@ -187,29 +223,37 @@ public Response getWebPartners(@HeaderParam(AUTHENTICATION_PARAM_NAME) String se @QueryParam("url") String url) { if (StringUtils.isEmpty(sessionId)) return super.respond(controller.getWebPartners(worToken, url)); - String userId = getUserIdFromSessionHeader(sessionId); + final String userId = getUserId(); return super.respond(controller.getWebPartners(userId)); } + /** + * @param url + * @param partner + * @return Response for success or failure + */ @PUT @Path("/partner/{url}") - public Response updateWebPartner( - @PathParam("url") String url, RegistryPartner partner, - @HeaderParam(AUTHENTICATION_PARAM_NAME) String sessionId) { - String userId = getUserIdFromSessionHeader(sessionId); - if (controller.updateWebPartner(userId, url, partner)) + public Response updateWebPartner(@PathParam("url") final String url, + final RegistryPartner partner) { + final String userId = getUserId(); + if (controller.updateWebPartner(userId, url, partner)) { return respond(Response.Status.OK); + } return respond(Response.Status.INTERNAL_SERVER_ERROR); } + /** + * @param url + * @return Response for success or failure + */ @DELETE @Path("/partner/{url}") - public Response removeWebPartner( - @PathParam("url") String url, - @HeaderParam(AUTHENTICATION_PARAM_NAME) String sessionId) { - String userId = getUserIdFromSessionHeader(sessionId); - if (controller.removeWebPartner(userId, url)) + public Response removeWebPartner(@PathParam("url") final String url) { + final String userId = getUserId(); + if (controller.removeWebPartner(userId, url)) { return respond(Response.Status.OK); + } return respond(Response.Status.INTERNAL_SERVER_ERROR); } } diff --git a/src/main/java/org/jbei/ice/services/rest/multipart/IceApplication.java b/src/main/java/org/jbei/ice/services/rest/multipart/IceApplication.java index 2a88b1447..14a2ba6f1 100644 --- a/src/main/java/org/jbei/ice/services/rest/multipart/IceApplication.java +++ b/src/main/java/org/jbei/ice/services/rest/multipart/IceApplication.java @@ -1,12 +1,12 @@ package org.jbei.ice.services.rest.multipart; -import java.util.HashSet; -import java.util.Set; -import javax.ws.rs.core.Application; - +import org.glassfish.jersey.media.multipart.MultiPartFeature; +import org.jbei.ice.services.rest.AuthenticationInterceptor; import org.jbei.ice.services.rest.FileResource; -import org.glassfish.jersey.media.multipart.MultiPartFeature; +import javax.ws.rs.core.Application; +import java.util.HashSet; +import java.util.Set; /** * @author Hector Plahar @@ -15,10 +15,11 @@ public class IceApplication extends Application { @Override public Set> getClasses() { - final Set> classes = new HashSet>(); + final Set> classes = new HashSet<>(); // register resources and features classes.add(MultiPartFeature.class); classes.add(FileResource.class); + classes.add(AuthenticationInterceptor.class); return classes; } } diff --git a/src/main/java/org/jbei/ice/servlet/InfoToModelFactory.java b/src/main/java/org/jbei/ice/servlet/InfoToModelFactory.java index 6b1a34a90..2906a1e63 100755 --- a/src/main/java/org/jbei/ice/servlet/InfoToModelFactory.java +++ b/src/main/java/org/jbei/ice/servlet/InfoToModelFactory.java @@ -1,6 +1,6 @@ package org.jbei.ice.servlet; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.jbei.ice.lib.common.logging.Logger; import org.jbei.ice.lib.dto.bulkupload.EntryField; import org.jbei.ice.lib.dto.entry.*; diff --git a/src/main/java/org/jbei/ice/servlet/ModelToInfoFactory.java b/src/main/java/org/jbei/ice/servlet/ModelToInfoFactory.java index 6c62f05ba..c4e74ab05 100755 --- a/src/main/java/org/jbei/ice/servlet/ModelToInfoFactory.java +++ b/src/main/java/org/jbei/ice/servlet/ModelToInfoFactory.java @@ -1,6 +1,6 @@ package org.jbei.ice.servlet; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.jbei.ice.lib.account.AccountController; import org.jbei.ice.lib.account.model.Account; import org.jbei.ice.lib.common.logging.Logger; diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index beb1c40cc..3810abe3a 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -17,6 +17,9 @@ + + + diff --git a/src/main/webapp/css/ice.css b/src/main/webapp/css/ice.css index 88dba0b6e..6daca8b0f 100755 --- a/src/main/webapp/css/ice.css +++ b/src/main/webapp/css/ice.css @@ -222,7 +222,6 @@ html, body { font-size: 0.90em; padding: 4px; border-radius: 2px 0 0 2px; - border-right: 0; } .entry_add_notes_input { @@ -516,11 +515,6 @@ a:hover { padding-left: 10px; } -.footer_line { - height: 1px; - background-color: #BBBAAA; -} - .required { color: #CC3333; } @@ -811,6 +805,7 @@ td.selected i.font-awesome { .entry_detail_submenu td { padding: 8px; border-left: 1px solid #e5e5e5; + border-bottom: 1px solid #f0f0f0; } .remove_user_button { diff --git a/src/main/webapp/index.jsp b/src/main/webapp/index.jsp index 5a6124cbd..de2096ef2 100644 --- a/src/main/webapp/index.jsp +++ b/src/main/webapp/index.jsp @@ -19,16 +19,14 @@ - + - - - - - - + + + + @@ -78,76 +76,26 @@

- diff --git a/src/main/webapp/scripts/collection/collectionController.js b/src/main/webapp/scripts/collection/collectionController.js index 050c936c7..5a78e0c4e 100644 --- a/src/main/webapp/scripts/collection/collectionController.js +++ b/src/main/webapp/scripts/collection/collectionController.js @@ -452,15 +452,10 @@ angular.module('ice.collection.controller', []) // selected entries $scope.selection = []; $scope.shoppingCartContents = []; - $scope.openShoppingCart = true; samples.userRequests({status: 'IN_CART'}, {userId: $rootScope.user.id}, function (result) { $scope.shoppingCartContents = result.requests; }); - $scope.hidePopovers = function (hide) { - $scope.openShoppingCart = !hide; - }; - $scope.createEntry = { isOpen: false }; diff --git a/src/main/webapp/scripts/controllers.js b/src/main/webapp/scripts/controllers.js index 27731afec..6eccd793a 100755 --- a/src/main/webapp/scripts/controllers.js +++ b/src/main/webapp/scripts/controllers.js @@ -1,7 +1,7 @@ 'use strict'; var iceControllers = angular.module('iceApp.controllers', ['iceApp.services', 'ui.bootstrap', 'angularFileUpload', - 'vr.directives.slider', 'angularMoment']); + 'angularMoment']); iceControllers.controller('ActionMenuController', function ($stateParams, $scope, $window, $rootScope, $location, $cookieStore, Folders, Entry, WebOfRegistries, Files, Selection, Upload, FolderSelection) { $scope.editDisabled = $scope.addToDisabled = $scope.removeDisabled = $scope.moveToDisabled = $scope.deleteDisabled = true; diff --git a/src/main/webapp/scripts/entry/entryController.js b/src/main/webapp/scripts/entry/entryController.js index 7d6a17bcd..909e89794 100644 --- a/src/main/webapp/scripts/entry/entryController.js +++ b/src/main/webapp/scripts/entry/entryController.js @@ -934,7 +934,8 @@ angular.module('ice.entry.controller', []) }) .controller('EntryController', function ($scope, $stateParams, $cookieStore, $location, $modal, $rootScope, - FileUploader, Entry, Folders, EntryService, EntryContextUtil, Selection) { + FileUploader, Entry, Folders, EntryService, EntryContextUtil, Selection, + CustomField) { $scope.partIdEditMode = false; $scope.showSBOL = true; $scope.context = EntryContextUtil.getContext(); @@ -974,12 +975,18 @@ angular.module('ice.entry.controller', []) controller: function ($scope, $modalInstance) { $scope.toDelete = part; $scope.processingDelete = undefined; - $scope.delete = function () { + $scope.errorDeleting = undefined; + + $scope.deleteSequence = function () { $scope.processingDelete = true; + $scope.errorDeleting = false; + entry.deleteSequence({partId: part.id}, function (result) { $scope.processingDelete = false; $modalInstance.close(part); }, function (error) { + $scope.processingDelete = false; + $scope.errorDeleting = true; console.error(error); }) } @@ -1096,6 +1103,8 @@ angular.module('ice.entry.controller', []) Selection.selectEntry(result); $scope.entry = EntryService.convertToUIForm(result); + if ($scope.entry.canEdit) + $scope.newParameter = {edit: false}; $scope.entryFields = EntryService.getFieldsForType(result.type.toLowerCase()); entry.statistics({partId: $stateParams.id}, function (stats) { @@ -1170,6 +1179,37 @@ angular.module('ice.entry.controller', []) } }; + $scope.createCopyOfEntry = function () { + $scope.entryCopy = angular.copy($scope.entry); + $scope.entryCopy.id = 0; + $scope.entryCopy.recordId = undefined; + $scope.entryCopy.name = $scope.entryCopy.name + " (copy)"; + $scope.entryCopy.owner = undefined; + $scope.entryCopy.ownerEmail = undefined; + + // convert arrays of objects to array strings + $scope.entryCopy.links = EntryService.toStringArray($scope.entryCopy.links); + $scope.entryCopy.selectionMarkers = EntryService.toStringArray($scope.entryCopy.selectionMarkers); + + for (var i = 0; i < $scope.entryCopy.linkedParts.length; i += 1) { + $scope.entryCopy.linkedParts[i].links = EntryService.toStringArray($scope.entryCopy.linkedParts[i].links); + $scope.entryCopy.linkedParts[i].selectionMarkers = EntryService.toStringArray($scope.entryCopy.linkedParts[i].selectionMarkers); + } + + // convert the part to a form the server can work with + $scope.entryCopy = EntryService.getTypeData($scope.entryCopy); + console.log($scope.entryCopy); + + // create or update the part depending on whether there is a current part id + entry.create($scope.entryCopy, function (result) { + $scope.$emit("UpdateCollectionCounts"); + $location.path('entry/' + result.id); // todo : or /entry/edit/ + $scope.showSBOL = false; + }, function (error) { + console.error(error); + }); + }; + // check if a selection has been made var menuOption = $stateParams.option; if (menuOption === undefined) { @@ -1297,5 +1337,26 @@ angular.module('ice.entry.controller', []) uploader.onErrorItem = function (item, response, status, headers) { $scope.serverError = true; }; + + // customer parameter add for entry view + $scope.addNewCustomField = function () { + $scope.newParameter.nameInvalid = $scope.newParameter.name == undefined || $scope.newParameter.name == ''; + $scope.newParameter.valueInvalid = $scope.newParameter.value == undefined || $scope.newParameter.value == ''; + if ($scope.newParameter.nameInvalid || $scope.newParameter.valueInvalid) + return; + + $scope.newParameter.partId = $scope.entry.id; + CustomField().createNewCustomField( + $scope.newParameter, + function (result) { + if (!result) + return; + + $scope.entry.parameters.push(result); + $scope.newParameter.edit = false; + }, function (error) { + console.error(error); + }) + } }); diff --git a/src/main/webapp/scripts/entry/entryService.js b/src/main/webapp/scripts/entry/entryService.js index fdb20ce0c..7e62fc6f5 100644 --- a/src/main/webapp/scripts/entry/entryService.js +++ b/src/main/webapp/scripts/entry/entryService.js @@ -449,4 +449,19 @@ angular.module('ice.entry.service', []) return entry; } } - }); + }) + .factory('CustomField', function ($resource, $cookieStore) { + return function () { + + var sessionId = $cookieStore.get("sessionId"); + + return $resource('rest/custom-fields', {}, { + createNewCustomField: { + method: 'POST', + responseType: "json", + headers: {'X-ICE-Authentication-SessionId': sessionId} + } + }); + } + }) +; diff --git a/src/main/webapp/scripts/entry/general-information.html b/src/main/webapp/scripts/entry/general-information.html index 35087ce28..53b1c63a3 100644 --- a/src/main/webapp/scripts/entry/general-information.html +++ b/src/main/webapp/scripts/entry/general-information.html @@ -1,161 +1,163 @@
- -
- -
{{entry.partId}}
-
- - -
- - - -
-
- -
- {{entry[field.schema].toString()}} + +
+ +
{{entry.partId}}
+
+ + +
+ + + +
+ + +
+ {{entry[field.schema].toString()}} -
+
- -
- {{entry[field.schema]}} - ({{entry[field.schema+'Email']}}) - {{entry[field.schema]}} ({{entry[field.schema+'Email']}}) + +
+ {{entry[field.schema]}} + ({{entry[field.schema+'Email']}}) + {{entry[field.schema]}} ({{entry[field.schema+'Email']}}) -
+
- -
{{entry[field.schema] | date:'fullDate' }} + +
{{entry[field.schema] | date:'fullDate' }} -
+
- - -
- + + +
+ -   +   Update Cancel -
+
- -
- + +
+ - + Save Cancel -
+
- -
- + +
+ Save Cancel -
+
- -
- + +
+ Save Cancel -
+
- -
date -
- + +
date +
+ -
+
Save Cancel -
+
- -
- + +
+ Save Cancel -
+
- -
- - + +
+ + Save Cancel -
+
- -
+ +
-
+
- -
+ +
-
+
- -
+ +
-
+
Save Cancel +
+ + +
- - +
-
-
+
+ -
- -
- {{parameter.value}} + +
+ +
+
+ +
+ +
+ + + Save + Cancel + +
+
+ +
+ +
-
- -
-
-
CONTAINS
- -
-
-
-
- -
- - - {{entry.linkedParts[$index+0].type}} {{entry.linkedParts[$index+0].name}} -
-
+ +
+
+
CONTAINS
+ +
+
+
+
+ +
+ + + {{entry.linkedParts[$index+0].type}} {{entry.linkedParts[$index+0].name}} +
+ +
Sequence with {{entry.linkedParts[$index+0].basePairCount | number}} base pairs & {{entry.linkedParts[$index+0].featureCount}} features @@ -269,116 +299,119 @@ No sequence data +
+

+ {{entry.linkedParts[$index+0].shortDescription}}

-

- {{entry.linkedParts[$index+0].shortDescription}}

-
-
-
-
- {{entry.linkedParts[$index+1].partId}} +
+
+ +
+ + + {{entry.linkedParts[$index+1].type}} {{entry.linkedParts[$index+1].name}} +
+ +
+ Sequence with {{entry.linkedParts[$index+1].basePairCount | number}} base pairs & {{entry.linkedParts[$index+1].featureCount}} features +
+

+ {{entry.linkedParts[$index+1].shortDescription}}

-
- - - {{entry.linkedParts[$index+1].type}} {{entry.linkedParts[$index+1].name}} -
- -
- Sequence with {{entry.linkedParts[$index+1].basePairCount | number}} base pairs & {{entry.linkedParts[$index+1].featureCount}} features -
-

- {{entry.linkedParts[$index+1].shortDescription}}

-
- -
-
-
- SEQUENCE - - -
- Create in VectorEditor - -   |   - -   Upload File - -   |   - - Paste Sequence - -
- -
+
+
+
+ SEQUENCE + + +
+ Create in VectorEditor + +   |   + +   Upload File + +   |   + + Paste Sequence + +
+ + +
Delete   |   - - Open in VectorEditor - + + Open in VectorEditor + -   |   +   |   - + - - - - -   |   - - - Hide - Show Pigeon Image - + + + + +   |   + + + Hide + Show Pigeon Image + +
-
- -
- No sequence data provided + +
+ No sequence data provided - +
Supported file formats are @@ -389,7 +422,8 @@ Error parsing file
-
+
+
@@ -400,32 +434,33 @@ Processing file {{processingFile}} - + -
+
- -
-
- + +
+
+ +
-
-
- +
+ +
-
-
-
-
- NOTES +
+
+
+ NOTES +
+ {{entry.longDescription}}
- {{entry.longDescription}}
-
\ No newline at end of file diff --git a/src/main/webapp/scripts/entry/samples.html b/src/main/webapp/scripts/entry/samples.html index e79051079..cf0a74167 100644 --- a/src/main/webapp/scripts/entry/samples.html +++ b/src/main/webapp/scripts/entry/samples.html @@ -10,7 +10,7 @@