Commit fd383165 authored by Matija Obreza's avatar Matija Obreza

Merge branch '396-problem-with-ssr-accession-page' into 'master'

Resolve "Problem with SSR accession page"

Closes #396

See merge request genesys-pgr/genesys-server!315
parents 16b3feab 43b2af7b
......@@ -21,6 +21,7 @@ import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import org.genesys.blocks.model.SelfCleaning;
import org.genesys.blocks.model.UuidModel;
import org.genesys.blocks.security.model.AclAwareModel;
import org.genesys.blocks.util.ClassAclOid;
import org.genesys.catalog.model.traits.Descriptor;
import org.genesys.catalog.model.traits.DescriptorList;
import org.genesys.catalog.model.vocab.ControlledVocabulary;
......@@ -48,6 +49,8 @@ import java.util.Set;
@Document(indexName = "catalog")
public class Partner extends UuidModel implements SelfCleaning, AclAwareModel {
private static final ClassAclOid<Partner> PARENT_OID = ClassAclOid.forClass(Partner.class);
/** The Constant serialVersionUID. */
private static final long serialVersionUID = 7972197553356837382L;
......@@ -118,6 +121,11 @@ public class Partner extends UuidModel implements SelfCleaning, AclAwareModel {
private void preupdate() {
trimStringsToNull();
}
@Override
public AclAwareModel aclParentObject() {
return PARENT_OID;
}
/**
* Instantiates a new partner.
......
......@@ -193,6 +193,9 @@ public class DatasetServiceImpl implements DatasetService {
dataset = datasetRepository.save(dataset);
// Make dataset publicly not-readable
aclService.makePubliclyReadable(dataset, false);
try {
final Path datasetPath = Paths.get(datasetRepositoryPath, dataset.getUuid().toString());
final Partner partner = dataset.getOwner();
......@@ -702,6 +705,9 @@ public class DatasetServiceImpl implements DatasetService {
aclService.makePubliclyReadable(datasetFile, true);
}
}
// Make dataset publicly readable
aclService.makePubliclyReadable(loaded, true);
return lazyLoad(datasetRepository.save(loaded));
}
......@@ -767,6 +773,9 @@ public class DatasetServiceImpl implements DatasetService {
aclService.makePubliclyReadable(datasetFile, false);
}
}
// Make dataset publicly not-readable
aclService.makePubliclyReadable(loaded, false);
return lazyLoad(datasetRepository.save(loaded));
}
......
......@@ -139,4 +139,17 @@ public class AdminController {
aclService.cleanupAcl();
return true;
}
@PostMapping(value = "/institutes-acl")
public void aclMakeInstitutesPublic() throws Exception {
LOG.warn("Adding ACL for FaoInstitutes");
instituteRepository.findAll().forEach(institute -> {
// LOG.warn("Making FaoInstitute {} public", institute.getCode());
aclService.createOrUpdatePermissions(institute);
});
LOG.warn("Added ACL to existing FaoInstitutes");
}
}
......@@ -15,24 +15,20 @@
*/
package org.genesys2.server.api.v1;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.genesys.blocks.model.JsonViews;
import org.genesys.blocks.oauth.model.OAuthClient;
import org.genesys.blocks.oauth.service.OAuthClientDetailsService;
import org.genesys.blocks.security.model.AclEntry;
import org.genesys.blocks.security.model.AclObjectIdentity;
import org.genesys.blocks.security.model.AclSid;
import org.genesys.blocks.security.serialization.AclEntriesToPermissions;
import org.genesys.blocks.security.serialization.SidPermissions;
import org.genesys.blocks.security.service.CustomAclService;
import org.genesys.blocks.security.service.CustomAclService.AclObjectIdentityExt;
import org.genesys2.server.api.ApiBaseController;
import org.genesys2.server.exception.NotFoundElement;
import org.genesys2.server.model.UserRole;
......@@ -42,7 +38,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
......@@ -51,9 +47,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.swagger.annotations.Api;
......@@ -90,7 +84,6 @@ public class PermissionController {
* @param sidPermissions the sid permissions
* @return the acl object identity
*/
@Transactional
@PostMapping(value = "/permissions/{clazz}/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
@JsonView(JsonViews.Minimal.class)
public AclObjectIdentityExt addPermission(@PathVariable(value = "clazz") final String className, @PathVariable("id") final long id,
......@@ -104,6 +97,27 @@ public class PermissionController {
return lazyLoadForJson(aclService.setPermissions(objectIdentity, sid, sidPermissions));
}
/**
* Adds the permission.
*
* @param className the class name
* @param id the id
* @param sidPermissions the sid permissions
* @return the acl object identity
*/
@DeleteMapping(value = "/permissions/{clazz}/{id}/{sid}", produces = MediaType.APPLICATION_JSON_VALUE)
@JsonView(JsonViews.Minimal.class)
public AclObjectIdentityExt deletePermissionsForSid(@PathVariable(value = "clazz") final String className, @PathVariable("id") final long id,
@PathVariable("sid") final String sid) {
final AclObjectIdentity objectIdentity = aclService.ensureObjectIdentity(id, className);
LOG.info("Removing permissions for {}", sid);
final AclSid aclSid = aclService.getSid(aclService.getSidId(sid));
return lazyLoadForJson(aclService.removePermissions(objectIdentity, aclSid));
}
/**
* Update inheriting status
*
......@@ -111,7 +125,6 @@ public class PermissionController {
* @param id the id
* @return updated acl object identity
*/
@Transactional
@JsonView(JsonViews.Minimal.class)
@PostMapping(value = "/update-inheriting/{inheriting}/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public AclObjectIdentityExt updateInheriting(@PathVariable(value = "inheriting") final boolean inheriting,
......@@ -131,7 +144,6 @@ public class PermissionController {
* @param parentId the parentId
* @return updated acl object identity
*/
@Transactional
@JsonView(JsonViews.Minimal.class)
@PostMapping(value = "/update-parent/{id}/{parentId}", produces = MediaType.APPLICATION_JSON_VALUE)
public AclObjectIdentityExt updateParentObject(@PathVariable(name = "id") final long id, @PathVariable(name = "parentId") final long parentId) {
......@@ -149,7 +161,6 @@ public class PermissionController {
* @param id the id
* @return the acl object identity
*/
@Transactional(readOnly = true)
@GetMapping(value = "/permissions/{clazz}/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
@JsonView(JsonViews.Minimal.class)
public AclObjectIdentityExt permissions(@PathVariable(value = "clazz") final String className, @PathVariable("id") final long id) {
......@@ -167,32 +178,11 @@ public class PermissionController {
* @return the acl object identity
*/
protected AclObjectIdentityExt lazyLoadForJson(final AclObjectIdentity objectIdentity) {
AclObjectIdentityExt _aclObjectIdentity = new AclObjectIdentityExt(objectIdentity);
if (objectIdentity != null && objectIdentity.getAclEntries() != null) {
objectIdentity.getAclEntries().size();
List<AclEntry> inheritedEntries = inherited(objectIdentity, new ArrayList<>(), new HashSet<>());
_aclObjectIdentity.inherited.addAll(inheritedEntries);
// lazy load for JSON
_aclObjectIdentity.inherited.forEach(entry -> entry.getAclSid().getId());
AclObjectIdentityExt ext = aclService.loadObjectIdentityExt(objectIdentity);
if (ext == null) {
throw new NotFoundElement("No such ACL object");
}
return _aclObjectIdentity;
}
private List<AclEntry> inherited(AclObjectIdentity objectIdentity, List<AclEntry> aclEntries, Set<AclObjectIdentity> handled) {
if (objectIdentity == null || handled.contains(objectIdentity)) {
return aclEntries;
}
aclEntries.addAll(objectIdentity.getAclEntries());
handled.add(objectIdentity);
if (objectIdentity.getParentObject() != null) {
inherited(objectIdentity.getParentObject(), aclEntries, handled);
}
return aclEntries;
return ext;
}
/**
......@@ -201,7 +191,6 @@ public class PermissionController {
* @param id the internal ID of aclObjectIdentity
* @return the acl object identity
*/
@Transactional(readOnly = true)
@GetMapping(value = "/permissions/{aclObjectIdentityId}", produces = MediaType.APPLICATION_JSON_VALUE)
@JsonView(JsonViews.Minimal.class)
public AclObjectIdentityExt permissions(@PathVariable(value = "aclObjectIdentityId") final long id) {
......@@ -274,18 +263,4 @@ public class PermissionController {
}
return oauthMap;
}
/**
* Wraps {@link AclObjectIdentity} and adds list of inherited permissions.
*/
private static class AclObjectIdentityExt {
@JsonUnwrapped
public AclObjectIdentity original;
@JsonSerialize(converter = AclEntriesToPermissions.class)
public List<AclEntry> inherited = new ArrayList<>();
public AclObjectIdentityExt(AclObjectIdentity source) {
this.original = source;
}
}
}
......@@ -15,16 +15,25 @@
*/
package org.genesys2.server.component.listener;
import org.genesys.blocks.security.model.AclAwareModel;
import org.genesys.blocks.security.service.CustomAclService;
import org.genesys.blocks.util.ClassAclOid;
import org.genesys.blocks.util.CurrentApplicationContext;
import org.genesys.catalog.model.Partner;
import org.genesys.catalog.persistence.PartnerRepository;
import org.genesys.catalog.persistence.dataset.DatasetRepository;
import org.genesys2.server.component.security.AsAdminInvoker;
import org.genesys2.server.model.impl.FaoInstitute;
import org.genesys2.server.persistence.SubsetRepository;
import org.genesys2.server.persistence.kpi.ExecutionRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import com.google.common.collect.Lists;
/**
* Declare sorts of things that upgrade the existing database
*
......@@ -39,6 +48,9 @@ public class ApplicationUpgrades implements InitializingBean {
@Autowired
protected AsAdminInvoker asAdminInvoker;
@Autowired
protected CurrentApplicationContext springContext;
/*
* (non-Javadoc)
......@@ -47,6 +59,9 @@ public class ApplicationUpgrades implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
kpiEnsureACL();
aclEnsureClassOIDs();
aclMakePartnersPublic();
aclMakeDraftsPrivate();
}
@Autowired
......@@ -64,5 +79,60 @@ public class ApplicationUpgrades implements InitializingBean {
return true;
});
}
private void aclEnsureClassOIDs() throws Exception {
asAdminInvoker.invoke(() -> {
LOG.warn("Making some Entities publicly readable by default");
for (Class<? extends AclAwareModel> clazz : Lists.newArrayList(Partner.class, FaoInstitute.class)) {
LOG.warn("Making {} publicly readable by default", clazz.getName());
aclService.createOrUpdatePermissions(ClassAclOid.forClass(clazz));
aclService.makePubliclyReadable(ClassAclOid.forClass(clazz), true);
}
return true;
});
}
@Autowired
private PartnerRepository partnerRepository;
private void aclMakePartnersPublic() throws Exception {
asAdminInvoker.invoke(() -> {
LOG.warn("Making Partners publicly readable");
partnerRepository.findAll().forEach(partner -> {
LOG.warn("Making Partner {} publicly readable", partner.getShortName());
aclService.createOrUpdatePermissions(partner);
});
return true;
});
}
@Autowired
private DatasetRepository datasetRepository;
@Autowired
private SubsetRepository subsetRepository;
private void aclMakeDraftsPrivate() throws Exception {
asAdminInvoker.invoke(() -> {
LOG.warn("Making non-published Datasets and Subsets private, and published publicly readable");
datasetRepository.findAll().forEach(dataset -> {
LOG.warn("Setting ACL for Dataset {}", dataset.getTitle());
aclService.makePubliclyReadable(dataset, dataset.isPublished());
});
subsetRepository.findAll().forEach(subset -> {
LOG.warn("Setting ACL for Subset {}", subset.getTitle());
aclService.makePubliclyReadable(subset, subset.isPublished());
});
LOG.warn("Datasets and Subsets are protected.");
return true;
});
}
}
......@@ -34,6 +34,7 @@ import org.genesys.blocks.model.BasicModel;
import org.genesys.blocks.model.EntityId;
import org.genesys.blocks.model.JsonViews;
import org.genesys.blocks.security.model.AclAwareModel;
import org.genesys.blocks.util.ClassAclOid;
import org.genesys.catalog.model.Partner;
import org.genesys.custom.elasticsearch.IgnoreField;
import org.genesys2.server.model.genesys.PDCIStatistics;
......@@ -52,6 +53,8 @@ import com.fasterxml.jackson.databind.ObjectMapper;
@Table(name = "faoinstitute", uniqueConstraints = @UniqueConstraint(columnNames = { "code" }), indexes = { @Index(columnList = "code", name = "code_FAOINSTITUTE") })
public class FaoInstitute extends BasicModel implements GeoReferencedEntity, AclAwareModel, EntityId {
private static final ClassAclOid<FaoInstitute> DEFAULT_PARENT_OID = ClassAclOid.forClass(FaoInstitute.class);
private static final long serialVersionUID = -8773002513838748431L;
private static final int LEN_ACRONYM = 50;
......@@ -140,7 +143,7 @@ public class FaoInstitute extends BasicModel implements GeoReferencedEntity, Acl
@Override
public AclAwareModel aclParentObject(){
return this.owner;
return this.owner == null ? DEFAULT_PARENT_OID : this.owner;
}
public FaoInstitute() {
......
......@@ -498,4 +498,16 @@ public class AdminController {
aclService.cleanupAcl();
return "redirect:/admin/";
}
@PostMapping(value = "/institutes-acl")
public void aclMakeInstitutesPublic() throws Exception {
LOG.warn("Adding ACL for FaoInstitutes");
instituteRepository.findAll().forEach(institute -> {
// LOG.warn("Making FaoInstitute {} public", institute.getCode());
aclService.createOrUpdatePermissions(institute);
});
LOG.warn("Added ACL to existing FaoInstitutes");
}
}
......@@ -32,6 +32,7 @@ import javax.validation.Valid;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.genesys.blocks.security.service.CustomAclService;
import org.genesys2.server.component.security.SecurityUtils;
import org.genesys2.server.exception.InvalidApiUsageException;
import org.genesys2.server.exception.NotFoundElement;
......@@ -117,6 +118,9 @@ public class SubsetServiceImpl implements SubsetService {
@Autowired
private PlatformTransactionManager transactionManager;
@Autowired
private CustomAclService aclService;
/**
* {@inheritDoc}
*/
......@@ -169,7 +173,13 @@ public class SubsetServiceImpl implements SubsetService {
final Subset subset = new Subset();
copyValues(subset, source);
subset.setState(PublishState.DRAFT);
return lazyLoad(subsetRepository.save(subset));
Subset loaded = subsetRepository.save(subset);
// Make Subset publicly not-readable
aclService.makePubliclyReadable(loaded, false);
return lazyLoad(loaded);
}
/**
......@@ -404,6 +414,9 @@ public class SubsetServiceImpl implements SubsetService {
}
loaded.setState(PublishState.PUBLISHED);
// Make dataset publicly readable
aclService.makePubliclyReadable(loaded, true);
return lazyLoad(subsetRepository.save(loaded));
}
......@@ -449,6 +462,9 @@ public class SubsetServiceImpl implements SubsetService {
}
loaded.setState(PublishState.DRAFT);
// Make Subset publicly not-readable
aclService.makePubliclyReadable(loaded, false);
return lazyLoad(subsetRepository.save(loaded));
}
......
......@@ -189,5 +189,11 @@
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>
<form method="post" action="<c:url value="/admin/institutes-acl" />">
<input type="submit" class="btn btn-default" class="btn btn-default" value="Make FaoInstitutes ACL" />
<!-- CSRF protection -->
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>
</body>
</html>
......@@ -29,6 +29,7 @@ import org.genesys.catalog.persistence.dataset.DatasetRepository;
import org.genesys.catalog.persistence.traits.DescriptorRepository;
import org.genesys.catalog.service.DatasetService;
import org.genesys.catalog.service.ShortFilterService;
import org.genesys.filerepository.persistence.RepositoryFilePersistence;
import org.genesys.test.config.ApplicationConfig;
import org.genesys2.server.model.PublishState;
import org.genesys2.server.persistence.FaoInstituteRepository;
......@@ -74,6 +75,8 @@ public abstract class AbstractApiTest extends AbstractTest {
protected ShortFilterService shortFilterService;
@Autowired
private FaoInstituteRepository instituteRepository;
@Autowired
protected RepositoryFilePersistence repositoryFilePersistence;
protected MockMvc mockMvc;
@Rule
......@@ -97,6 +100,7 @@ public abstract class AbstractApiTest extends AbstractTest {
@Transactional
public void cleanup() throws Exception {
datasetRepository.deleteAll();
repositoryFilePersistence.deleteAll();
partnerRepository.deleteAll();
instituteRepository.deleteAll();
super.cleanup();
......
package org.genesys.test.base;
import java.util.ArrayList;
import java.util.List;
import org.genesys.blocks.auditlog.persistence.AuditLogRepository;
import org.genesys.blocks.persistence.ClassPKRepository;
import org.genesys.blocks.security.model.AclAwareModel;
import org.genesys.blocks.security.persistence.AclEntryPersistence;
import org.genesys.blocks.security.persistence.AclObjectIdentityPersistence;
import org.genesys.blocks.security.persistence.AclSidPersistence;
import org.genesys.blocks.security.service.CustomAclService;
import org.genesys.blocks.util.ClassAclOid;
import org.genesys.catalog.model.Partner;
import org.genesys.catalog.persistence.PartnerRepository;
import org.genesys.filerepository.model.RepositoryFolder;
import org.genesys.filerepository.persistence.RepositoryFolderRepository;
import org.genesys2.server.component.security.AsAdminInvoker;
import org.genesys2.server.model.impl.FaoInstitute;
import org.genesys2.server.persistence.FaoInstituteRepository;
import org.junit.After;
import org.junit.Before;
import org.slf4j.Logger;
......@@ -9,6 +25,8 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import com.google.common.collect.Lists;
/**
* Do not touch
*
......@@ -22,19 +40,68 @@ public abstract class AbstractTest {
private AuditLogRepository auditLogRepo;
@Autowired
private ClassPKRepository classPKRepository;
@Autowired
private AclObjectIdentityPersistence aclOidRepository;
@Autowired
private AclEntryPersistence aclEntryRepository;
@Autowired
private AclSidPersistence aclSidRepository;
@Autowired
private AsAdminInvoker asAdminInvoker;
@Autowired
private CustomAclService aclService;
@Autowired
private RepositoryFolderRepository repositoryFolderRepository;
@Autowired
private PartnerRepository partnerRepository;
@Autowired
private FaoInstituteRepository instituteRepository;
@Transactional
@Before
public void beforeTest() throws Exception {
cleanup();
asAdminInvoker.invoke(() -> {
LOG.info("Making some Entities publicly readable by default");
for (Class<? extends AclAwareModel> clazz : Lists.newArrayList(Partner.class, FaoInstitute.class)) {
LOG.trace("Making {} publicly readable by default", clazz.getName());
aclService.createOrUpdatePermissions(ClassAclOid.forClass(clazz));
aclService.makePubliclyReadable(ClassAclOid.forClass(clazz), true);
}
return true;
});
}
@Transactional
@After
public void cleanup() throws Exception {
deleteFolders(repositoryFolderRepository.findAll());
partnerRepository.deleteAll();
instituteRepository.deleteAll();
auditLogRepo.deleteAll();
aclEntryRepository.deleteAll();
aclOidRepository.deleteAll();
aclSidRepository.deleteAll();
classPKRepository.deleteAll();
}
protected void deleteFolders(List<RepositoryFolder> toDelete) {
List<RepositoryFolder> pending = new ArrayList<RepositoryFolder>();
for (RepositoryFolder folder : toDelete) {
try {
repositoryFolderRepository.delete(folder);
} catch (Throwable e) {
LOG.debug("Folder not deleted {}: {}", folder, e.getMessage()); //, e);
pending.add(folder);
}
}
if (pending.size() > 0) {
deleteFolders(pending);
}
}
}
......@@ -36,6 +36,7 @@ import org.genesys.catalog.service.DescriptorService;
import org.genesys.catalog.service.PartnerService;
import org.genesys.catalog.service.ShortFilterService;
import org.genesys.catalog.service.VocabularyService;
import org.genesys.filerepository.persistence.RepositoryFolderRepository;
import org.genesys.test.base.AbstractServiceTest;
import org.genesys2.server.api.model.AccessionHeaderJson;
import org.genesys2.server.model.genesys.Accession;
......@@ -45,6 +46,7 @@ import org.genesys2.server.persistence.FaoInstituteRepository;
import org.genesys2.server.service.InstituteService;
import org.genesys2.server.service.worker.AccessionUploader;
import org.junit.After;
import org.junit.Before;
import org.springframework.beans.factory.annotation.Autowired;
import com.fasterxml.jackson.databind.ObjectMapper;
......@@ -122,17 +124,27 @@ public abstract class CatalogServiceTest extends AbstractServiceTest {
@Autowired
protected AccessionRepository accessionRepository;
@Autowired
private RepositoryFolderRepository repositoryFolderRepository;
protected Partner partner;
protected Partner partner2;
@Before
@Override
public void beforeTest() throws Exception {
super.beforeTest();
partner = setupPartner(PARTNER_1, PARTNER_ACRONYM_1, Sets.newHashSet(URL_1));
partner2 = setupPartner(PARTNER_2, PARTNER_ACRONYM_2, Sets.newHashSet(URL_1));
}
@After
@Override
public void cleanup() throws Exception {
deleteFolders(repositoryFolderRepository.findAll());
partnerRepository.deleteAll();
accessionRepository.deleteAll();
instituteRepository.deleteAll();
partner = setupPartner(PARTNER_1, PARTNER_ACRONYM_1, Sets.newHashSet(URL_1));