Commit 2a1f7983 authored by Matija Obreza's avatar Matija Obreza
Browse files

REST and AJAX api

parent dae111f7
......@@ -118,7 +118,7 @@ public class Crop extends BusinessModel {
}
private synchronized String translate(String field, Locale locale) {
if (this.i18nJ == null && ! StringUtils.isBlank(this.i18n)) {
if (this.i18nJ == null && !StringUtils.isBlank(this.i18n)) {
ObjectMapper mapper = new ObjectMapper();
try {
this.i18nJ = mapper.readTree(this.i18n);
......@@ -127,6 +127,7 @@ public class Crop extends BusinessModel {
e.printStackTrace();
}
}
return this.i18nJ!=null && this.i18nJ.has(field) && this.i18nJ.get(field).has(locale.getLanguage()) ? this.i18nJ.get(field).get(locale.getLanguage()).textValue() : null;
return this.i18nJ != null && this.i18nJ.has(field) && this.i18nJ.get(field).has(locale.getLanguage()) ? this.i18nJ.get(field).get(locale.getLanguage())
.textValue() : null;
}
}
......@@ -14,7 +14,6 @@
* limitations under the License.
**/
package org.genesys2.server.model.impl;
import javax.persistence.Column;
......@@ -23,6 +22,7 @@ import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import org.genesys2.server.model.BusinessModel;
......@@ -32,7 +32,7 @@ import org.genesys2.server.model.BusinessModel;
* @author mobreza
*/
@Entity
@Table(name = "croprule")
@Table(name = "croprule", uniqueConstraints = { @UniqueConstraint(columnNames = { "cropId", "genus", "species" }) })
public class CropRule extends BusinessModel {
private static final long serialVersionUID = -2336100072991067193L;
......
......@@ -14,7 +14,6 @@
* limitations under the License.
**/
package org.genesys2.server.service;
import java.util.List;
......@@ -22,6 +21,7 @@ import java.util.Locale;
import org.genesys2.server.model.genesys.Taxonomy;
import org.genesys2.server.model.impl.Crop;
import org.genesys2.server.model.impl.CropRule;
import org.genesys2.server.model.impl.CropTaxonomy;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
......@@ -37,4 +37,18 @@ public interface CropService {
void rebuildTaxonomies();
Page<CropTaxonomy> getCropTaxonomies(Crop crop, Pageable pageable);
/**
* Add a {@link Crop} to the system
*
* @param shortName
* @param name
* @param description
* @param i18n
* @return
*/
Crop addCrop(String shortName, String name, String description, String i18n);
CropRule addCropRule(Crop crop, String genus, String species, boolean included);
}
......@@ -40,4 +40,6 @@ public interface TraitService {
List<Metadata> listMetadataByMethod(Method method);
Page<Method> listMethods(Pageable pageable);
}
......@@ -22,8 +22,10 @@ import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.jetty.util.StringUtil;
import org.genesys2.server.model.genesys.Taxonomy;
import org.genesys2.server.model.impl.Crop;
import org.genesys2.server.model.impl.CropRule;
......@@ -198,4 +200,41 @@ public class CropServiceImpl implements CropService {
public Page<CropTaxonomy> getCropTaxonomies(Crop crop, Pageable pageable) {
return cropTaxonomyRepository.findByCrop(crop, pageable);
}
/**
* Register a crop with Genesys
*/
@Override
@PreAuthorize("hasRole('ADMINISTRATOR')")
@Transactional(readOnly = false)
public Crop addCrop(String shortName, String name, String description, String i18n) {
LOG.info("Adding crop " + shortName);
Crop crop = new Crop();
crop.setShortName(shortName);
crop.setName(name);
crop.setDescription(StringUtils.defaultIfBlank(description, null));
crop.setI18n(StringUtils.defaultIfBlank(i18n, null));
cropRepository.save(crop);
LOG.info("Registered crop: " + crop);
return crop;
}
@Override
@PreAuthorize("hasRole('ADMINISTRATOR')")
@Transactional(readOnly = false)
public CropRule addCropRule(Crop crop, String genus, String species, boolean included) {
if (crop == null || StringUtil.isBlank(genus)) {
LOG.debug("Ignoring blank rule");
return null;
}
CropRule rule = new CropRule();
rule.setCrop(crop);
rule.setGenus(genus);
rule.setSpecies(species);
rule.setIncluded(included);
cropRuleRepository.save(rule);
return rule;
}
}
......@@ -49,7 +49,7 @@ import com.fasterxml.jackson.databind.node.JsonNodeType;
@Transactional(readOnly = true)
public class GenesysFilterServiceImpl implements GenesysFilterService {
public static final Log LOG = LogFactory.getLog(GenesysFilterServiceImpl.class);
private static final Log LOG = LogFactory.getLog(GenesysFilterServiceImpl.class);
private ArrayList<GenesysFilterImpl> availableFilters;
......
......@@ -253,7 +253,7 @@ public class GenesysServiceImpl implements GenesysService, TraitService {
public Page<Object[]> statisticsTaxonomyByInstitute(FaoInstitute institute, Pageable pageable) {
return accessionRepository.statisticsTaxonomyInInstitute(institute, pageable);
}
@Override
public Page<Object[]> statisticsCropByInstitute(FaoInstitute institute, Pageable pageable) {
return accessionRepository.statisticsCropInInstitute(institute, pageable);
......@@ -276,7 +276,11 @@ public class GenesysServiceImpl implements GenesysService, TraitService {
@Override
public Page<Accession> listAccessionsByCrop(Crop crop, Pageable pageable) {
return accessionRepository.findByTaxonomy(cropTaxonomyRepository.findTaxonomiesByCrop(crop), pageable);
List<Taxonomy> taxonomies = cropTaxonomyRepository.findTaxonomiesByCrop(crop);
if (taxonomies == null || taxonomies.size() == 0) {
return null;
}
return accessionRepository.findByTaxonomy(taxonomies, pageable);
}
@Override
......@@ -313,6 +317,11 @@ public class GenesysServiceImpl implements GenesysService, TraitService {
return methodRepository.findOne(methodId);
}
@Override
public Page<Method> listMethods(Pageable pageable) {
return methodRepository.findAll(pageable);
}
@Override
public List<Metadata> listMetadataByMethod(Method method) {
return metadataMethodRepository.listMetadataByMethod(method);
......@@ -344,7 +353,7 @@ public class GenesysServiceImpl implements GenesysService, TraitService {
@Override
// Worker threads don't carry this information
//@PreAuthorize("hasRole('ADMINISTRATOR')")
// @PreAuthorize("hasRole('ADMINISTRATOR')")
@Transactional(readOnly = false)
public void saveSvalbards(List<SvalbardData> svalbards) {
svalbardRepository.save(svalbards);
......
package org.genesys2.server.servlet.controller.rest;
import java.io.IOException;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.SerializerProvider;
import org.genesys2.server.model.impl.Crop;
import org.springframework.stereotype.Component;
@Component
public class CropSerializer extends JsonSerializer<Crop> {
@Override
public void serialize(Crop value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
// FIXME Does not do anything!
}
}
\ No newline at end of file
package org.genesys2.server.servlet.controller.rest;
public class ExceptionJson {
public String error;
public ExceptionJson(Throwable e) {
this.error = e.getMessage();
}
}
\ No newline at end of file
package org.genesys2.server.servlet.controller.rest;
import java.util.ArrayList;
import java.util.List;
import net.sf.oval.ConstraintViolation;
public class ModelValidationException extends RuntimeException {
private static final long serialVersionUID = 1923818295664959709L;
private List<CheckFailure> failures = new ArrayList<CheckFailure>();
public ModelValidationException(String message, List<ConstraintViolation> violations) {
super(message);
for (ConstraintViolation cv : violations) {
failures.add(new CheckFailure(cv.getCheckName(), cv.getMessage(), cv.getInvalidValue()));
}
}
public final List<CheckFailure> getFailures() {
return failures;
}
public static class CheckFailure {
public String checkName;
public String message;
public String invalidValue;
public CheckFailure(String checkName, String message, Object invalidValue) {
this.checkName = checkName;
this.message = message;
this.invalidValue = String.valueOf(invalidValue);
}
}
}
package org.genesys2.server.servlet.controller.rest;
import org.genesys2.server.model.genesys.Method;
import org.genesys2.server.model.genesys.Parameter;
import org.genesys2.server.model.impl.Crop;
import org.genesys2.server.model.impl.User;
import org.springframework.data.domain.Page;
public class OAuth2Cleanup {
public static <T> T clean(T t) {
if (t instanceof User) {
User user = (User) t;
user.setId(null);
user.setPassword(null);
}
if (t instanceof Method) {
Method method = (Method) t;
method.setParameter(null);
}
if (t instanceof Parameter) {
Parameter param = (Parameter) t;
param.setCrop(null);
}
if (t instanceof Crop) {
Crop crop = (Crop) t;
crop.setCropRules(null);
}
return t;
}
public static <T> Page<T> clean(Page<T> x) {
for (T m : x.getContent()) {
clean(m);
}
return x;
}
}
......@@ -32,6 +32,7 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
......@@ -50,196 +51,177 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping(value = "/api/v0")
@PreAuthorize("isAuthenticated()")
@RequestMapping(value = { "/api/v0", "/json/v0" })
public class TokenController extends BaseController {
@Autowired
private ConsumerTokenServices tokenServices;
private PasswordEncoder encoder = new StandardPasswordEncoder();
@RequestMapping(
value = ServiceEndpoints.LIST_USER_TOKENS,
method = RequestMethod.GET,
produces = {MediaType.APPLICATION_JSON_VALUE}
)
@ResponseBody
public
Collection<OAuth2AccessToken> listTokensForUser(@PathVariable String username) throws Exception {
if (isAuthenticated()) {
OAuth2Authentication principal = (OAuth2Authentication) SecurityContextHolder.getContext().getAuthentication();
checkResourceOwner(username, principal);
return enhance(tokenServices.findTokensByUserName(username));
} else {
throw new AuthorizationException(EXCEPTION_NOT_AUTHORIZED);
}
}
@RequestMapping(
value = ServiceEndpoints.REVOKE_USER_TOKEN,
method = RequestMethod.DELETE,
produces = {MediaType.APPLICATION_JSON_VALUE}
)
@ResponseBody
public SimpleMessage revokeUserToken(@PathVariable String username,
@PathVariable String token) throws Exception {
if (isAuthenticated()){
OAuth2Authentication principal = (OAuth2Authentication) SecurityContextHolder.getContext().getAuthentication();
checkResourceOwner(username, principal);
String tokenValue = getTokenValue(tokenServices.findTokensByUserName(username), token);
if (tokenValue != null && tokenServices.revokeToken(tokenValue)) {
return new SimpleMessage("ok", "user token revoked");
}
throw new NoSuchTokenException("Token not found");
} else {
throw new AuthorizationException(EXCEPTION_NOT_AUTHORIZED);
}
}
@RequestMapping(
value = ServiceEndpoints.LIST_CLIENT_TOKEN,
method = RequestMethod.GET,
produces = {MediaType.APPLICATION_JSON_VALUE}
)
@ResponseBody
public Collection<OAuth2AccessToken> listTokensForClient(@PathVariable String client)
throws Exception {
if (isAuthenticated()){
OAuth2Authentication principal = (OAuth2Authentication) SecurityContextHolder.getContext().getAuthentication();
checkClient(client, principal);
return enhance(tokenServices.findTokensByClientId(client));
} else {
throw new AuthorizationException(EXCEPTION_NOT_AUTHORIZED);
}
}
@RequestMapping(
value = ServiceEndpoints.REVOKE_CLIENT_TOKEN,
method = RequestMethod.DELETE,
produces = {MediaType.APPLICATION_JSON_VALUE}
)
@ResponseBody
public SimpleMessage revokeClientToken(@PathVariable String client, @PathVariable String token)
throws Exception {
if (isAuthenticated()){
OAuth2Authentication principal = (OAuth2Authentication) SecurityContextHolder.getContext().getAuthentication();
checkClient(client, principal);
String tokenValue = getTokenValue(tokenServices.findTokensByClientId(client), token);
if (tokenValue != null && tokenServices.revokeToken(tokenValue)) {
return new SimpleMessage("ok", "client token revoked");
}
throw new NoSuchTokenException("Token not found");
} else {
throw new AuthorizationException(EXCEPTION_NOT_AUTHORIZED);
}
}
@ExceptionHandler(NoSuchTokenException.class)
public ResponseEntity<Void> handleNoSuchToken(NoSuchTokenException e) {
return new ResponseEntity<Void>(HttpStatus.NOT_FOUND);
}
private String getTokenValue(Collection<OAuth2AccessToken> tokens, String hash) {
for (OAuth2AccessToken token : tokens) {
try {
return token.getValue();
} catch (Exception e) {
// it doesn't match
}
}
return null;
}
private Collection<OAuth2AccessToken> enhance(Collection<OAuth2AccessToken> tokens) {
Collection<OAuth2AccessToken> result = new ArrayList<OAuth2AccessToken>();
for (OAuth2AccessToken prototype : tokens) {
DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(prototype);
Map<String, Object> map = new HashMap<String, Object>(token.getAdditionalInformation());
// The token doesn't have an ID in the token service, but we need one for the endpoint, so add one here
map.put(token.getTokenType(), encoder.encode(token.getValue()));
try {
String clientId = tokenServices.getClientId(token.getValue());
if (clientId != null) {
map.put("client_id", clientId);
}
} catch (InvalidTokenException e) {
// Ignore defensively in case of bugs in token services
}
token.setAdditionalInformation(map);
result.add(token);
}
return result;
}
private void checkResourceOwner(String user, Principal principal) {
if (principal instanceof OAuth2Authentication) {
OAuth2Authentication authentication = (OAuth2Authentication) principal;
if (!authentication.isClientOnly() && !user.equals(principal.getName())) {
throw new AccessDeniedException(String.format("User '%s' cannot obtain tokens for user '%s'",
principal.getName(), user));
}
} else if (!user.equals(principal.getName())) {
throw new AccessDeniedException(String.format("User '%s' cannot obtain tokens for user '%s'",
principal.getName(), user));
}
}
private void checkClient(String client, Principal principal) {
if (principal instanceof OAuth2Authentication) {
OAuth2Authentication authentication = (OAuth2Authentication) principal;
if (!authentication.isClientOnly() || !client.equals(principal.getName()) && !isAdmin(principal)) {
throw new AccessDeniedException(String.format("Client '%s' cannot obtain tokens for client '%s'",
principal.getName(), client));
}
}
}
private boolean isAdmin(Principal principal) {
return AuthorityUtils.authorityListToSet(((Authentication) principal).getAuthorities()).contains("USER_ADMIN");
}
/**
* @param tokenServices the consumerTokenServices to set
*/
public void setTokenServices(ConsumerTokenServices tokenServices) {
this.tokenServices = tokenServices;
}
public static class SimpleMessage implements Serializable {
@Autowired
private ConsumerTokenServices tokenServices;
private PasswordEncoder encoder = new StandardPasswordEncoder();
@RequestMapping(value = ServiceEndpoints.LIST_USER_TOKENS, method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE })
@ResponseBody
public Collection<OAuth2AccessToken> listTokensForUser(@PathVariable String username) throws Exception {
if (isAuthenticated()) {
OAuth2Authentication principal = (OAuth2Authentication) SecurityContextHolder.getContext().getAuthentication();
checkResourceOwner(username, principal);
return enhance(tokenServices.findTokensByUserName(username));
} else {
throw new AuthorizationException(EXCEPTION_NOT_AUTHORIZED);
}
}
@RequestMapping(value = ServiceEndpoints.REVOKE_USER_TOKEN, method = RequestMethod.DELETE, produces = { MediaType.APPLICATION_JSON_VALUE })
@ResponseBody
public SimpleMessage revokeUserToken(@PathVariable String username, @PathVariable String token) throws Exception {
if (isAuthenticated()) {
OAuth2Authentication principal = (OAuth2Authentication) SecurityContextHolder.getContext().getAuthentication();
checkResourceOwner(username, principal);
String tokenValue = getTokenValue(tokenServices.findTokensByUserName(username), token);
if (tokenValue != null && tokenServices.revokeToken(tokenValue)) {
return new SimpleMessage("ok", "user token revoked");
}
throw new NoSuchTokenException("Token not found");
} else {
throw new AuthorizationException(EXCEPTION_NOT_AUTHORIZED);
}
}
@RequestMapping(value = ServiceEndpoints.LIST_CLIENT_TOKEN, method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE })
@ResponseBody
public Collection<OAuth2AccessToken> listTokensForClient(@PathVariable String client) throws Exception {
if (isAuthenticated()) {
OAuth2Authentication principal = (OAuth2Authentication) SecurityContextHolder.getContext().getAuthentication();
checkClient(client, principal);
return enhance(tokenServices.findTokensByClientId(client));
} else {
throw new AuthorizationException(EXCEPTION_NOT_AUTHORIZED);
}
}
@RequestMapping(value = ServiceEndpoints.REVOKE_CLIENT_TOKEN, method = RequestMethod.DELETE, produces = { MediaType.APPLICATION_JSON_VALUE })
@ResponseBody
public SimpleMessage revokeClientToken(@PathVariable String client, @PathVariable String token) throws Exception {
if (isAuthenticated()) {
OAuth2Authentication principal = (OAuth2Authentication) SecurityContextHolder.getContext().getAuthentication();
checkClient(client, principal);
String tokenValue = getTokenValue(tokenServices.findTokensByClientId(client), token);
if (tokenValue != null && tokenServices.revokeToken(tokenValue)) {
return new SimpleMessage("ok", "client token revoked");
}
throw new NoSuchTokenException("Token not found");
} else {
throw new AuthorizationException(EXCEPTION_NOT_AUTHORIZED);
}
}
@ExceptionHandler(NoSuchTokenException.class)
public ResponseEntity<Void> handleNoSuchToken(NoSuchTokenException e) {
return new ResponseEntity<Void>(HttpStatus.NOT_FOUND);
}
private String getTokenValue(Collection<OAuth2AccessToken> tokens, String hash) {
for (OAuth2AccessToken token : tokens) {
try {
return token.getValue();
} catch (Exception e) {
// it doesn't match
}
}
return null;
}
private Collection<OAuth2AccessToken> enhance(Collection<OAuth2AccessToken> tokens) {
Collection<OAuth2AccessToken> result = new ArrayList<OAuth2AccessToken>();
for (OAuth2AccessToken prototype : tokens) {
DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(prototype);
Map<String, Object> map = new HashMap<String, Object>(token.getAdditionalInformation());
// The token doesn't have an ID in the token service, but we need
// one for the endpoint, so add one here
map.put(token.getTokenType(), encoder.encode(token.getValue()));
try {
String clientId = tokenServices.getClientId(token.getValue());
if (clientId != null) {
map.put("client_id", clientId);
}
} catch (InvalidTokenException e) {
// Ignore defensively in case of bugs in token services
}
token.setAdditionalInformation(map);
result.add(token);
}
return result;