Merged changes made to solve bug #25650

This commit is contained in:
luca.frosini 2023-09-18 16:30:43 +02:00
parent 88c1ee60d8
commit 95b86d8a40
4 changed files with 262 additions and 22 deletions

View File

@ -13,6 +13,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Metadata are added only if requested by the client [#25040]
- Restored Encrypted Property Type and removed Vault [#25041]
- Cleaned RequestInfo in RequestFilter class [#25211]
- Solved bug which allow to create two query template with the same name if a null id was provided [#25650]
## [v4.2.0]

View File

@ -1,23 +1,34 @@
package org.gcube.informationsystem.resourceregistry.queries.templates;
import java.util.HashMap;
import java.util.UUID;
import javax.ws.rs.BadRequestException;
import org.gcube.com.fasterxml.jackson.core.JsonProcessingException;
import org.gcube.com.fasterxml.jackson.databind.JsonNode;
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
import org.gcube.com.fasterxml.jackson.databind.node.ArrayNode;
import org.gcube.com.fasterxml.jackson.databind.node.ObjectNode;
import org.gcube.common.authorization.library.provider.CalledMethodProvider;
import org.gcube.informationsystem.base.reference.AccessType;
import org.gcube.informationsystem.base.reference.IdentifiableElement;
import org.gcube.informationsystem.queries.templates.reference.entities.QueryTemplate;
import org.gcube.informationsystem.resourceregistry.api.exceptions.AlreadyPresentException;
import org.gcube.informationsystem.resourceregistry.api.exceptions.AvailableInAnotherContextException;
import org.gcube.informationsystem.resourceregistry.api.exceptions.NotFoundException;
import org.gcube.informationsystem.resourceregistry.api.exceptions.ResourceRegistryException;
import org.gcube.informationsystem.resourceregistry.api.exceptions.queries.InvalidQueryException;
import org.gcube.informationsystem.resourceregistry.api.exceptions.queries.templates.QueryTemplateAlreadyPresentException;
import org.gcube.informationsystem.resourceregistry.api.exceptions.queries.templates.QueryTemplateNotFoundException;
import org.gcube.informationsystem.resourceregistry.api.exceptions.types.SchemaViolationException;
import org.gcube.informationsystem.resourceregistry.contexts.ContextUtility;
import org.gcube.informationsystem.resourceregistry.contexts.security.QueryTemplatesSecurityContext;
import org.gcube.informationsystem.resourceregistry.contexts.security.SecurityContext;
import org.gcube.informationsystem.resourceregistry.contexts.security.SecurityContext.PermissionMode;
import org.gcube.informationsystem.resourceregistry.instances.base.ElementManagementUtility;
import org.gcube.informationsystem.resourceregistry.instances.base.entities.EntityElementManagement;
import org.gcube.informationsystem.resourceregistry.instances.model.Operation;
import org.gcube.informationsystem.resourceregistry.queries.json.JsonQuery;
import org.gcube.informationsystem.resourceregistry.utils.OrientDBUtility;
import org.gcube.informationsystem.serialization.ElementMapper;
@ -129,7 +140,7 @@ public class QueryTemplateManagement extends EntityElementManagement<QueryTempla
return select;
}
protected void checkIfNameAlreadyExists() throws AlreadyPresentException {
protected void checkIfNameAlreadyExists() throws QueryTemplateAlreadyPresentException {
StringBuffer select = getSelectQuery();
StringBuffer errorMessage = new StringBuffer();
@ -146,7 +157,13 @@ public class QueryTemplateManagement extends EntityElementManagement<QueryTempla
if (resultSet != null) {
try {
if(resultSet.hasNext()) {
throw new AlreadyPresentException(errorMessage.toString());
OResult oResult = resultSet.next();
try {
element = ElementManagementUtility.getElementFromOptional(oResult.getVertex());
} catch (ResourceRegistryException e) {
}
throw new QueryTemplateAlreadyPresentException(errorMessage.toString());
}
}finally {
resultSet.close();
@ -179,7 +196,7 @@ public class QueryTemplateManagement extends EntityElementManagement<QueryTempla
if(resultSet!=null) {
resultSet.close();
}
throw new NotFoundException("Error retrieving " + QueryTemplate.NAME + " with name " + getName());
throw new QueryTemplateNotFoundException("Error retrieving " + QueryTemplate.NAME + " with name " + getName());
}
OResult oResult = resultSet.next();
@ -203,6 +220,80 @@ public class QueryTemplateManagement extends EntityElementManagement<QueryTempla
}
}
@Override
public String createOrUpdate()
throws NotFoundException, AvailableInAnotherContextException, ResourceRegistryException {
ODatabaseDocument current = ContextUtility.getCurrentODatabaseDocumentFromThreadLocal();
try {
oDatabaseDocument = getWorkingContext().getDatabaseDocument(PermissionMode.WRITER);
oDatabaseDocument.begin();
boolean update = false;
setAsEntryPoint();
try {
checkIfNameAlreadyExists();
setOperation(Operation.CREATE);
String calledMethod = CalledMethodProvider.instance.get();
calledMethod = calledMethod.replace("update", "create");
CalledMethodProvider.instance.set(calledMethod);
internalCreate();
} catch(QueryTemplateAlreadyPresentException e) {
String uuidVertexString = element.getProperty(QueryTemplate.ID_PROPERTY).toString();
this.uuid = UUID.fromString(uuidVertexString);
/*
* The service accepts the update if:
* - the JSON does NOT contains the field 'id': because the name is an id too;
* - the JSON contains the field 'id' and the value is null: because some serializators could set to null a missing value;
* - the JSON contains the field 'id' and the value is the same value contained in the vertex in the DB.
*
* In other words, the service refuse the update with Bad Request if
* the JSON contains the field 'id' and the value differs from the value contained in the vertex in the DB.
*/
JsonNode idNode = jsonNode.get(QueryTemplate.ID_PROPERTY);
if(idNode != null && !idNode.isNull() && idNode.isTextual()) {
String jsonID = idNode.asText();
if(uuidVertexString.compareTo(jsonID)!=0) {
throw new ResourceRegistryException("If you provide the id of the " + QueryTemplate.NAME + " it must has the same value of the id contained in the IS (i.e. " + uuidVertexString + ")");
}
}
setOperation(Operation.UPDATE);
update = true;
internalUpdate();
}
oDatabaseDocument.commit();
if(update) {
setReload(true);
}
// TODO Notify to subscriptionNotification
return serializeAsJsonNode().toString();
} catch(ResourceRegistryException e) {
logger.error("Unable to update {} with UUID {}", accessType.getName(), uuid);
if(oDatabaseDocument != null) {
oDatabaseDocument.rollback();
}
throw e;
} catch(Exception e) {
logger.error("Unable to update {} with UUID {}", accessType.getName(), uuid, e);
if(oDatabaseDocument != null) {
oDatabaseDocument.rollback();
}
throw new ResourceRegistryException(e);
} finally {
if(oDatabaseDocument != null) {
oDatabaseDocument.close();
}
if(current!=null) {
current.activateOnCurrentThread();
}
}
}
@Override
protected OVertex reallyCreate() throws AlreadyPresentException, InvalidQueryException, ResourceRegistryException {
try {
@ -218,7 +309,7 @@ public class QueryTemplateManagement extends EntityElementManagement<QueryTempla
throw new ResourceRegistryException("Error Creating " + typeName + " with " + jsonNode, e.getCause());
}
}
@Override
protected OVertex reallyUpdate() throws NotFoundException, ResourceRegistryException {
try {
@ -238,7 +329,9 @@ public class QueryTemplateManagement extends EntityElementManagement<QueryTempla
@Override
protected void reallyDelete() throws NotFoundException, ResourceRegistryException {
logger.debug("Going to delete {} with name {}", accessType.getName(), name);
getElement().delete();
OVertex oVertex = getElement();
uuid = UUID.fromString((String) oVertex.getProperty(IdentifiableElement.ID_PROPERTY));
oVertex.delete();
}
@Override

View File

@ -13,8 +13,8 @@ import org.gcube.common.authorization.utils.secret.JWTSecret;
import org.gcube.common.authorization.utils.secret.Secret;
import org.gcube.common.authorization.utils.secret.SecretUtility;
import org.gcube.common.keycloak.KeycloakClientFactory;
import org.gcube.common.keycloak.KeycloakClientHelper;
import org.gcube.common.keycloak.model.TokenResponse;
import org.gcube.common.scope.api.ScopeProvider;
import org.gcube.informationsystem.model.reference.properties.Metadata;
import org.junit.AfterClass;
import org.junit.BeforeClass;
@ -39,14 +39,13 @@ public class ContextTest {
public static final String NEXTNEXT;
public static final String DEVSEC;
public static final String DEVVRE;
protected static final Properties properties;
protected static final String CLIENT_ID_PROPERTY_KEY = "client_id";
protected static final String CLIENT_SECRET_PROPERTY_KEY = "client_secret";
protected static final String clientID;
protected static final String clientSecret;
public static final String TYPE_PROPERTY_KEY = "type";
public static final String USERNAME_PROPERTY_KEY = "username";
public static final String PASSWORD_PROPERTY_KEY = "password";
public static final String CLIENT_ID_PROPERTY_KEY = "clientId";
static {
GCUBE = "/gcube";
@ -64,16 +63,17 @@ public class ContextTest {
try {
// load the properties file
properties.load(input);
clientID = properties.getProperty(CLIENT_ID_PROPERTY_KEY);
clientSecret = properties.getProperty(CLIENT_SECRET_PROPERTY_KEY);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private enum Type{
USER, CLIENT_ID
};
public static void set(Secret secret) throws Exception {
SecretManagerProvider.instance.reset();
SecretManager secretManager = new SecretManager();
@ -83,15 +83,56 @@ public class ContextTest {
}
public static void setContextByName(String fullContextName) throws Exception {
logger.debug("Going to set credentials for context {}", fullContextName);
Secret secret = getSecretByContextName(fullContextName);
set(secret);
}
private static TokenResponse getJWTAccessToken(String context) throws Exception {
ScopeProvider.instance.set(context);
TokenResponse tr = KeycloakClientFactory.newInstance().queryUMAToken(clientID, clientSecret, context, null);
return tr;
Type type = Type.valueOf(properties.get(TYPE_PROPERTY_KEY).toString());
TokenResponse tr = null;
int index = context.indexOf('/', 1);
String root = context.substring(0, index == -1 ? context.length() : index);
switch (type) {
case CLIENT_ID:
String clientId = properties.getProperty(CLIENT_ID_PROPERTY_KEY);
String clientSecret = properties.getProperty(root);
tr = KeycloakClientFactory.newInstance().queryUMAToken(context, clientId, clientSecret, context, null);
break;
case USER:
default:
String username = properties.getProperty(USERNAME_PROPERTY_KEY);
String password = properties.getProperty(PASSWORD_PROPERTY_KEY);
switch (root) {
case "/gcube":
default:
clientId = "next.d4science.org";
break;
case "/pred4s":
clientId = "pre.d4science.org";
break;
case "/d4science.research-infrastructures.eu":
clientId = "services.d4science.org";
break;
}
clientSecret = null;
tr = KeycloakClientHelper.getTokenForUser(context, username, password);
break;
}
return tr;
}
public static Secret getSecretByContextName(String context) throws Exception {

View File

@ -5,6 +5,9 @@ import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import javax.ws.rs.BadRequestException;
import org.gcube.com.fasterxml.jackson.databind.JavaType;
import org.gcube.com.fasterxml.jackson.databind.JsonNode;
@ -19,6 +22,7 @@ import org.gcube.informationsystem.queries.templates.reference.properties.Templa
import org.gcube.informationsystem.resourceregistry.ContextTest;
import org.gcube.informationsystem.resourceregistry.api.exceptions.AlreadyPresentException;
import org.gcube.informationsystem.resourceregistry.api.exceptions.ResourceRegistryException;
import org.gcube.informationsystem.resourceregistry.api.exceptions.queries.templates.QueryTemplateAlreadyPresentException;
import org.gcube.informationsystem.serialization.ElementMapper;
import org.junit.Assert;
import org.junit.Test;
@ -39,7 +43,9 @@ public class QueryTemplateManagementTest extends ContextTest {
public static void checkUUIDAndMetadata(IdentifiableElement previous, IdentifiableElement got) {
Assert.assertTrue(got.getID()!= null);
Assert.assertTrue(got.getID().compareTo(previous.getID()) == 0);
if(previous.getID()!=null) {
Assert.assertTrue(got.getID().compareTo(previous.getID()) == 0);
}
Metadata gotMetadata = got.getMetadata();
Metadata previousMetadata = previous.getMetadata();
@ -68,10 +74,11 @@ public class QueryTemplateManagementTest extends ContextTest {
}
protected QueryTemplate create(QueryTemplate queryTemplate) throws ResourceRegistryException, IOException {
list();
//list();
QueryTemplateManagement queryTemplateManagement = new QueryTemplateManagement();
queryTemplateManagement.setName(QUERY_TEMPLATE_NAME);
queryTemplateManagement.setForceIncludeMeta(true);
String json = ElementMapper.marshal(queryTemplate);
queryTemplateManagement.setJson(json);
String created = queryTemplateManagement.create();
@ -79,7 +86,7 @@ public class QueryTemplateManagementTest extends ContextTest {
QueryTemplate createdQueryTemplate = ElementMapper.unmarshal(QueryTemplate.class, created);
assertions(queryTemplate, createdQueryTemplate);
list(createdQueryTemplate);
// list(createdQueryTemplate);
return createdQueryTemplate;
}
@ -292,4 +299,102 @@ public class QueryTemplateManagementTest extends ContextTest {
logger.debug("The DB should be now clean");
}
@Test(expected = QueryTemplateAlreadyPresentException.class)
public void doubleCreateTest() throws Exception {
QueryTemplate queryTemplate = getQueryTemplate();
try {
create(queryTemplate);
logger.info("Created {} with name '{}'", QueryTemplate.NAME, queryTemplate.getName());
create(queryTemplate);
throw new Exception("The second create should fails. You should not be here");
} catch (QueryTemplateAlreadyPresentException e) {
logger.info("As expected {} with name '{}' already exists", QueryTemplate.NAME, queryTemplate.getName());
throw e;
}finally {
QueryTemplateManagement queryTemplateManagement = new QueryTemplateManagement();
queryTemplateManagement.setName(QUERY_TEMPLATE_NAME);
queryTemplateManagement.delete();
logger.info("{} with name '{}' successfully deleted", QueryTemplate.NAME, queryTemplate.getName());
logger.debug("The DB should be now clean");
}
}
@Test
public void createUpdateTest() throws Exception {
QueryTemplate queryTemplate = getQueryTemplate();
try {
QueryTemplate createdQueryTemplate = create(queryTemplate);
// test no UUID
logger.info("Created {} with name '{}'", QueryTemplate.NAME, createdQueryTemplate.getName());
QueryTemplateManagement queryTemplateManagement = new QueryTemplateManagement();
queryTemplateManagement.setName(QUERY_TEMPLATE_NAME);
queryTemplateManagement.setForceIncludeMeta(true);
String updatedDescription = "My 1st udpated description";
queryTemplate = getQueryTemplate();
queryTemplate.setDescription(updatedDescription);
String json = ElementMapper.marshal(queryTemplate);
queryTemplateManagement.setJson(json);
String updated = queryTemplateManagement.createOrUpdate();
logger.info("Updated {} is {}", QueryTemplate.NAME, updated);
QueryTemplate updatedQueryTemplate = ElementMapper.unmarshal(QueryTemplate.class, updated);
Assert.assertTrue(updatedQueryTemplate.getDescription().compareTo(updatedDescription)==0);
// test UUID null
queryTemplateManagement = new QueryTemplateManagement();
queryTemplateManagement.setName(QUERY_TEMPLATE_NAME);
queryTemplateManagement.setForceIncludeMeta(true);
updatedDescription = "My 2nd udpated description";
queryTemplate = getQueryTemplate();
queryTemplate.setDescription(updatedDescription);
queryTemplate.setID(null);
json = ElementMapper.marshal(queryTemplate);
queryTemplateManagement.setJson(json);
updated = queryTemplateManagement.createOrUpdate();
logger.info("Updated {} is {}", QueryTemplate.NAME, updated);
updatedQueryTemplate = ElementMapper.unmarshal(QueryTemplate.class, updated);
Assert.assertTrue(updatedQueryTemplate.getDescription().compareTo(updatedDescription)==0);
// test UUID same
queryTemplateManagement = new QueryTemplateManagement();
queryTemplateManagement.setName(QUERY_TEMPLATE_NAME);
queryTemplateManagement.setForceIncludeMeta(true);
updatedDescription = "My 3rd udpated description";
queryTemplate = getQueryTemplate();
queryTemplate.setDescription(updatedDescription);
queryTemplate.setID(createdQueryTemplate.getID());
json = ElementMapper.marshal(queryTemplate);
queryTemplateManagement.setJson(json);
updated = queryTemplateManagement.createOrUpdate();
logger.info("Updated {} is {}", QueryTemplate.NAME, updated);
updatedQueryTemplate = ElementMapper.unmarshal(QueryTemplate.class, updated);
Assert.assertTrue(updatedQueryTemplate.getDescription().compareTo(updatedDescription)==0);
// test another UUID
queryTemplateManagement = new QueryTemplateManagement();
queryTemplateManagement.setName(QUERY_TEMPLATE_NAME);
queryTemplateManagement.setForceIncludeMeta(true);
updatedDescription = "My 4th udpated description";
queryTemplate = getQueryTemplate();
queryTemplate.setDescription(updatedDescription);
queryTemplate.setID(UUID.randomUUID());
json = ElementMapper.marshal(queryTemplate);
queryTemplateManagement.setJson(json);
try {
updated = queryTemplateManagement.createOrUpdate();
throw new Exception("Trying to udpate the " + QueryTemplate.NAME + " using a different " + QueryTemplate.ID_PROPERTY + " must fails. Please check your code");
}catch (ResourceRegistryException e) {
logger.info("Trying to update {} with name '{}' failed as expected : {} ", QueryTemplate.NAME, queryTemplate.getName(), e.getMessage());
}
}finally {
QueryTemplateManagement queryTemplateManagement = new QueryTemplateManagement();
queryTemplateManagement.setName(QUERY_TEMPLATE_NAME);
queryTemplateManagement.delete();
logger.info("{} with name '{}' successfully deleted", QueryTemplate.NAME, queryTemplate.getName());
logger.debug("The DB should be now clean");
}
}
}