/* ====================================================================
*
* Copyright (C) 2007 - 2016 GeoSolutions S.A.S.
* http://www.geo-solutions.it
*
* GPLv3 + Classpath exception
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.
*
* ====================================================================
*
* This software consists of voluntary contributions made by developers
* of GeoSolutions. For more information on GeoSolutions, please see
* <http://www.geo-solutions.it/>.
*
*/
package it.geosolutions.geostore.services.rest.impl;
import it.geosolutions.geostore.core.model.Attribute;
import it.geosolutions.geostore.core.model.Category;
import it.geosolutions.geostore.core.model.Resource;
import it.geosolutions.geostore.core.model.SecurityRule;
import it.geosolutions.geostore.core.model.User;
import it.geosolutions.geostore.core.model.enums.DataType;
import it.geosolutions.geostore.core.model.enums.Role;
import it.geosolutions.geostore.services.ResourceService;
import it.geosolutions.geostore.services.SecurityService;
import it.geosolutions.geostore.services.dto.ShortAttribute;
import it.geosolutions.geostore.services.dto.search.BaseField;
import it.geosolutions.geostore.services.dto.search.FieldFilter;
import it.geosolutions.geostore.services.dto.search.SearchFilter;
import it.geosolutions.geostore.services.dto.search.SearchOperator;
import it.geosolutions.geostore.services.exception.BadRequestServiceEx;
import it.geosolutions.geostore.services.exception.DuplicatedResourceNameServiceEx;
import it.geosolutions.geostore.services.exception.InternalErrorServiceEx;
import it.geosolutions.geostore.services.exception.NotFoundServiceEx;
import it.geosolutions.geostore.services.rest.RESTResourceService;
import it.geosolutions.geostore.services.rest.exception.BadRequestWebEx;
import it.geosolutions.geostore.services.rest.exception.ConflictWebEx;
import it.geosolutions.geostore.services.rest.exception.ForbiddenErrorWebEx;
import it.geosolutions.geostore.services.rest.exception.InternalErrorWebEx;
import it.geosolutions.geostore.services.rest.exception.NotFoundWebEx;
import it.geosolutions.geostore.services.rest.model.RESTCategory;
import it.geosolutions.geostore.services.rest.model.RESTResource;
import it.geosolutions.geostore.services.rest.model.ResourceList;
import it.geosolutions.geostore.services.rest.model.SecurityRuleList;
import it.geosolutions.geostore.services.rest.model.ShortAttributeList;
import it.geosolutions.geostore.services.rest.model.ShortResourceList;
import it.geosolutions.geostore.services.rest.utils.Convert;
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.core.SecurityContext;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
/**
* Class RESTResourceServiceImpl.
*
* @author ETj (etj at geo-solutions.it)
* @author Tobia di Pisa (tobia.dipisa at geo-solutions.it)
* @author DamianoG
*/
public class RESTResourceServiceImpl extends RESTServiceImpl implements RESTResourceService {
private final static Logger LOGGER = Logger.getLogger(RESTResourceServiceImpl.class);
private ResourceService resourceService;
/**
* @param resourceService
*/
public void setResourceService(ResourceService resourceService) {
this.resourceService = resourceService;
}
/* (non-Javadoc)
* @see it.geosolutions.geostore.services.rest.impl.RESTServiceImpl#getSecurityService()
*/
@Override
protected SecurityService getSecurityService() {
return resourceService;
}
/*
* (non-Javadoc)
*
* @see it.geosolutions.geostore.services.rest.RESTResourceService#insert(it.geosolutions.geostore.core.model.Resource)
*/
@Override
public long insert(SecurityContext sc, RESTResource resource) {
if (resource == null)
throw new BadRequestWebEx("Resource is null");
if (resource.getId() != null)
throw new BadRequestWebEx("Id should be null");
if (resource.getCategory() == null)
throw new BadRequestWebEx("Category should be not null");
User authUser = extractAuthUser(sc);
// This list holds the security rules for this resources
// By default, when a resource is inserted, 2 rules are created :
// - one is related to the User that inserts the rule
// - the other one is related to its group
List<SecurityRule> securities = new ArrayList<>();
// User Security rule: the user that insert the resource (the "owner") is allowed to Read and Write the resources
SecurityRule userSecurityRule = new SecurityRule();
userSecurityRule.setCanRead(true);
userSecurityRule.setCanWrite(true);
userSecurityRule.setUser(authUser);
securities.add(userSecurityRule);
Resource r = Convert.convertResource(resource);
r.setSecurity(securities);
try {
long id = resourceService.insert(r);
return id;
} catch (BadRequestServiceEx ex) {
throw new BadRequestWebEx(ex.getMessage());
} catch (NotFoundServiceEx e) {
throw new NotFoundWebEx(e.getMessage());
} catch (DuplicatedResourceNameServiceEx e) {
throw new ConflictWebEx(e.getMessage());
}
}
/**
* Updates a resource. Name, Description and Metadata will be replaced if not null.<br/>
* Category can not be changed; category element may exist in the input resource provided it is the same as in the original resource.<br/>
* Attribute list will be updated only if it exists in the input resource.<br/>
* <P/>
* TODO: attribute list behaviour should be checked: read comments in source.
*
* @see it.geosolutions.geostore.services.rest.RESTResourceService#update(long, it.geosolutions.geostore.core.model.Resource)
*/
@Override
public long update(SecurityContext sc, long id, RESTResource resource) throws NotFoundWebEx,
BadRequestWebEx {
try {
if (resource == null)
throw new BadRequestWebEx("Resource is null");
resource.setId(id);
Resource old = resourceService.get(id);
if (old == null)
throw new NotFoundWebEx("Resource not found");
if (resource.getCategory() != null) {
RESTCategory newrc = resource.getCategory();
Category oldrc = old.getCategory();
if ((newrc.getId() != null && !newrc.getId().equals(oldrc.getId()))
|| (newrc.getName() != null && !newrc.getName().equals(oldrc.getName()))) {
LOGGER.info("Trying to change category old(" + oldrc.getId() + ":"
+ oldrc.getName() + ") new(" + newrc.getId() + ":" + newrc.getName()
+ ")");
throw new BadRequestWebEx("Can't change resource category");
}
}
//
// Authorization check.
//
boolean canUpdate = false;
User authUser = extractAuthUser(sc);
canUpdate = resourceAccessWrite(authUser, old.getId());
if (canUpdate) {
if (resource.getDescription() != null)
old.setDescription(resource.getDescription());
if (resource.getName() != null)
old.setName(resource.getName());
if (resource.getMetadata() != null)
old.setMetadata(resource.getMetadata());
try {
resourceService.update(old);
} catch (DuplicatedResourceNameServiceEx e) {
throw new ConflictWebEx(e.getMessage());
}
//
// Check Attribute list
//
// 1) no Attributes element --> no change
// 2a) empty attributes element --> remove all attributes
// 2b) attributes element with children --> update attributes list
if (resource.getAttribute() == null) {
if (LOGGER.isDebugEnabled())
LOGGER.debug("Attribute list is null: no change in the attrib list");
} else {
if (LOGGER.isDebugEnabled())
LOGGER.debug("Attribute list is " + resource.getAttribute().size());
List<Attribute> attributes = Convert.convertAttributeList(resource
.getAttribute());
resourceService.updateAttributes(id, attributes);
}
return id;
} else
throw new ForbiddenErrorWebEx("Can't update resource");
} catch (NotFoundServiceEx ex) {
LOGGER.warn("Resource not found (" + id + "): " + ex.getMessage(), ex);
throw new NotFoundWebEx("Resource not found");
}
}
/*
* (non-Javadoc)
*
* @see it.geosolutions.geostore.services.rest.RESTResourceService#delete(long)
*/
@Override
public void delete(SecurityContext sc, long id) throws NotFoundWebEx {
//
// Authorization check.
//
boolean canDelete = false;
User authUser = extractAuthUser(sc);
canDelete = resourceAccessWrite(authUser, id);
if (canDelete) {
boolean ret = resourceService.delete(id);
if (!ret)
throw new NotFoundWebEx("Resource not found");
} else
throw new ForbiddenErrorWebEx("This user cannot delete this resource !");
}
/*
* (non-Javadoc)
*
* @see it.geosolutions.geostore.services.rest.RESTResourceService#delete(long)
*/
@Override
public void deleteResources(SecurityContext sc, SearchFilter filter) throws BadRequestWebEx,
InternalErrorWebEx {
try {
resourceService.deleteResources(filter);
} catch (BadRequestServiceEx e) {
if (LOGGER.isEnabledFor(Level.ERROR))
LOGGER.error(e.getMessage());
throw new BadRequestWebEx(e.getMessage());
} catch (InternalErrorServiceEx e) {
if (LOGGER.isEnabledFor(Level.ERROR))
LOGGER.error(e.getMessage());
throw new InternalErrorWebEx(e.getMessage());
}
}
@Override
public Resource get(SecurityContext sc, long id, boolean fullResource) throws NotFoundWebEx {
//
// Authorization check.
//
boolean canRead = false;
User authUser = extractAuthUser(sc);
canRead = resourceAccessRead(authUser, id);
if(!canRead){
throw new ForbiddenErrorWebEx("This user cannot read this resource !");
}
if (fullResource) {
if (LOGGER.isDebugEnabled())
LOGGER.debug("Retrieving a full resource");
List<Resource> resourcesFull;
try {
SearchFilter filter = new FieldFilter(BaseField.ID, Long.toString(id),
SearchOperator.EQUAL_TO);
resourcesFull = resourceService.getResourcesFull(filter, authUser);
} catch (Exception ex) {
LOGGER.error(ex.getMessage(), ex);
throw new InternalErrorWebEx("Internal error");
}
if (resourcesFull.isEmpty())
throw new NotFoundWebEx("Resource not found");
if (LOGGER.isDebugEnabled())
LOGGER.debug("DATA is " + resourcesFull.get(0).getData());
return resourcesFull.get(0);
} else {
Resource ret = resourceService.get(id);
if (ret == null)
throw new NotFoundWebEx("Resource not found");
// CHECKME
if (ret.getData() != null) {
LOGGER.warn("Resource has data attached. It should not. Please check.");
ret.setData(null); // make the data transmission lighter
}
return ret;
}
}
/*
* (non-Javadoc)
*
* @see it.geosolutions.geostore.services.rest.RESTResourceService#getList(java.lang.String, java.lang.Integer, java.lang.Integer)
*/
@Override
public ShortResourceList getList(SecurityContext sc, String nameLike, Integer page,
Integer entries) throws BadRequestWebEx {
User authUser = extractAuthUser(sc);
nameLike = nameLike.replaceAll("[*]", "%");
try {
return new ShortResourceList(resourceService.getList(nameLike, page, entries, authUser));
} catch (BadRequestServiceEx ex) {
throw new BadRequestWebEx(ex.getMessage());
}
}
/*
* (non-Javadoc)
*
* @see it.geosolutions.geostore.services.rest.RESTResourceService#getList(java.lang.Integer, java.lang.Integer)
*/
@Override
public ShortResourceList getAll(SecurityContext sc, Integer page, Integer entries)
throws BadRequestWebEx {
User authUser = extractAuthUser(sc);
try {
return new ShortResourceList(resourceService.getAll(page, entries, authUser));
} catch (BadRequestServiceEx ex) {
throw new BadRequestWebEx(ex.getMessage());
}
}
/*
* (non-Javadoc)
*
* @see it.geosolutions.geostore.services.rest.RESTResourceService#getCount(java.lang.String)
*/
@Override
public long getCount(SecurityContext sc, String nameLike) {
nameLike = nameLike.replaceAll("[*]", "%");
return resourceService.getCount(nameLike);
}
/**
* @param id
* @return long
* @throws BadRequestWebEx
*/
@SuppressWarnings("unused")
private long parseId(SecurityContext sc, String id) throws BadRequestWebEx {
try {
return Long.parseLong(id);
} catch (Exception e) {
LOGGER.info("Bad id requested '" + id + "'");
throw new BadRequestWebEx("Bad id");
}
}
/*
* (non-Javadoc)
*
* @see it.geosolutions.geostore.services.rest.RESTResourceService#getAttributes(long)
*/
@Override
public ShortAttributeList getAttributes(SecurityContext sc, long id) throws NotFoundWebEx {
Resource resource = resourceService.get(id);
if (resource == null)
throw new NotFoundWebEx("Resource not found");
//
// Authorization check.
//
boolean canRead = false;
User authUser = extractAuthUser(sc);
canRead = resourceAccessRead(authUser, id);
if (canRead) {
return new ShortAttributeList(resourceService.getAttributes(id));
} else {
throw new ForbiddenErrorWebEx(
"This user cannot read this resource so neither its attributes!");
}
}
/*
* (non-Javadoc)
*
* @see it.geosolutions.geostore.services.rest.RESTResourceService#getAttribute(long, java.lang.String)
*/
@Override
public String getAttribute(SecurityContext sc, long id, String name) throws NotFoundWebEx {
Resource resource = resourceService.get(id);
if (resource == null)
throw new NotFoundWebEx("Resource not found");
//
// Authorization check.
//
boolean canRead = false;
User authUser = extractAuthUser(sc);
canRead = resourceAccessRead(authUser, id);
if (canRead) {
ShortAttribute shAttribute = resourceService.getAttribute(id, name);
if (shAttribute == null)
throw new NotFoundWebEx("Resource attribute not found");
return shAttribute.getValue();
} else {
throw new ForbiddenErrorWebEx(
"This user cannot read this resource so neither its attributes!");
}
}
/*
* (non-Javadoc)
*
* @see it.geosolutions.geostore.services.rest.RESTResourceService#insertAttribute(long, java.lang.String, java.lang.String,it.geosolutions.geostore.core.model.enums.DataType)
*/
@Override
public long updateAttribute(SecurityContext sc, long id, String name, String value, DataType type)
throws NotFoundWebEx, InternalErrorWebEx {
Resource resource = resourceService.get(id);
if (resource == null)
throw new NotFoundWebEx("Resource not found");
//
// Authorization check.
//
boolean canUpdate = false;
try {
User authUser = extractAuthUser(sc);
canUpdate = resourceAccessWrite(authUser, resource.getId());
if (canUpdate){
ShortAttribute a = resourceService.getAttribute(id, name);
//if the attribute exists, will be updated
if(a!=null){
return resourceService.updateAttribute(id, name, value);
}else{
//create the attribute if missing
if(type != null){
return resourceService.insertAttribute(id, name, value, type);
}else{
return resourceService.insertAttribute(id, name, value, DataType.STRING);
}
}
} else {
throw new InternalErrorServiceEx("This user cannot access this resource !");
}
} catch (InternalErrorServiceEx ex) {
throw new InternalErrorWebEx(ex.getMessage());
}
}
/*
* (non-Javadoc)
*
* @see it.geosolutions.geostore.services.rest.RESTResourceService#insertAttribute(long, java.lang.String, java.lang.String)
*/
public long updateAttribute(SecurityContext sc, long id,String name, String value) {
return updateAttribute(sc, id, name, value, null);
}
/*
* (non-Javadoc)
*
* @see it.geosolutions.geostore.services.rest.RESTResourceService#getResourceByFilter(it.geosolutions.geostore.services.dto.SearchFilter)
*/
@Override
public ShortResourceList getResources(SecurityContext sc, SearchFilter filter) {
User authUser = extractAuthUser(sc);
try {
return new ShortResourceList(resourceService.getResources(filter, authUser));
} catch (BadRequestServiceEx e) {
if (LOGGER.isInfoEnabled())
LOGGER.info(e.getMessage());
throw new BadRequestWebEx(e.getMessage());
} catch (InternalErrorServiceEx e) {
if (LOGGER.isInfoEnabled())
LOGGER.info(e.getMessage());
throw new InternalErrorWebEx(e.getMessage());
}
}
/*
* (non-Javadoc)
*
* @see it.geosolutions.geostore.services.rest.RESTResourceService#getResourcesList(javax.ws.rs.core.SecurityContext,
* it.geosolutions.geostore.services.dto.search.SearchFilter, java.lang.Integer, java.lang.Integer, boolean, boolean)
*/
@Override
public ResourceList getResourcesList(SecurityContext sc, Integer page, Integer entries,
boolean includeAttributes, boolean includeData, SearchFilter filter) {
User authUser = extractAuthUser(sc);
try {
return new ResourceList(resourceService.getResources(filter, page, entries, includeAttributes, includeData, authUser));
} catch (BadRequestServiceEx e) {
if (LOGGER.isInfoEnabled())
LOGGER.info(e.getMessage());
throw new BadRequestWebEx(e.getMessage());
} catch (InternalErrorServiceEx e) {
if (LOGGER.isInfoEnabled())
LOGGER.info(e.getMessage());
throw new InternalErrorWebEx(e.getMessage());
}
}
@Override
public void updateSecurityRules(SecurityContext sc, long id,
SecurityRuleList securityRules) {
//
// Authorization check.
//
boolean canWrite = false;
User authUser = extractAuthUser(sc);
canWrite = resourceAccessWrite(authUser, id);
if (canWrite) {
ShortAttribute owner = resourceService.getAttribute(id, "owner");
if((authUser.getRole() == Role.ADMIN) || (owner == null) || (owner.getValue().equals(authUser.getName()))) {
try {
resourceService.updateSecurityRules(id, Convert
.convertSecurityRuleList(securityRules.getList(), id));
} catch (BadRequestServiceEx e) {
if (LOGGER.isInfoEnabled())
LOGGER.info(e.getMessage());
throw new BadRequestWebEx(e.getMessage());
} catch (InternalErrorServiceEx e) {
if (LOGGER.isInfoEnabled())
LOGGER.info(e.getMessage());
throw new InternalErrorWebEx(e.getMessage());
} catch (NotFoundServiceEx e) {
if (LOGGER.isInfoEnabled())
LOGGER.info(e.getMessage());
throw new NotFoundWebEx(e.getMessage());
}
} else {
throw new ForbiddenErrorWebEx(
"This user cannot update this resource permissions!");
}
} else {
throw new ForbiddenErrorWebEx(
"This user cannot write this resource so neither its permissions!");
}
}
@Override
public SecurityRuleList getSecurityRules(SecurityContext sc, long id) {
//
// Authorization check.
//
boolean canRead = false;
User authUser = extractAuthUser(sc);
canRead = resourceAccessRead(authUser, id);
if (canRead) {
ShortAttribute owner = resourceService.getAttribute(id, "owner");
if((authUser.getRole() == Role.ADMIN) || (owner == null) || (owner.getValue().equals(authUser.getName()))) {
try {
return new SecurityRuleList(resourceService.getSecurityRules(id));
} catch (BadRequestServiceEx e) {
if (LOGGER.isInfoEnabled())
LOGGER.info(e.getMessage());
throw new BadRequestWebEx(e.getMessage());
} catch (InternalErrorServiceEx e) {
if (LOGGER.isInfoEnabled())
LOGGER.info(e.getMessage());
throw new InternalErrorWebEx(e.getMessage());
}
} else {
throw new ForbiddenErrorWebEx(
"This user cannot read this resource permissions!");
}
} else {
throw new ForbiddenErrorWebEx(
"This user cannot read this resource so neither its permissions!");
}
}
}