Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
VOSpace INAF
vospace-rest
Commits
be9d595e
Commit
be9d595e
authored
Aug 28, 2021
by
Nicola Fulvio Calabria
Browse files
Added LinkNodes validation to CreateNodeController
parent
d86824c8
Changes
8
Hide whitespace changes
Inline
Side-by-side
src/main/java/it/inaf/oats/vospace/CreateNodeController.java
View file @
be9d595e
...
...
@@ -6,6 +6,9 @@
package
it.inaf.oats.vospace
;
import
it.inaf.ia2.aa.data.User
;
import
it.inaf.oats.vospace.exception.InvalidArgumentException
;
import
it.inaf.oats.vospace.exception.InvalidURIException
;
import
net.ivoa.xml.vospace.v2.LinkNode
;
import
net.ivoa.xml.vospace.v2.Node
;
import
org.springframework.http.MediaType
;
import
org.springframework.web.bind.annotation.RequestBody
;
...
...
@@ -14,6 +17,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.web.bind.annotation.PutMapping
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
org.springframework.beans.factory.annotation.Value
;
@RestController
public
class
CreateNodeController
extends
BaseNodeController
{
...
...
@@ -23,6 +27,9 @@ public class CreateNodeController extends BaseNodeController {
@Autowired
private
CreateNodeService
createNodeService
;
@Value
(
"${vospace-authority}"
)
private
String
authority
;
@PutMapping
(
value
=
{
"/nodes"
,
"/nodes/**"
},
consumes
=
{
MediaType
.
APPLICATION_XML_VALUE
,
MediaType
.
TEXT_XML_VALUE
,
MediaType
.
APPLICATION_JSON_VALUE
},
produces
=
{
MediaType
.
APPLICATION_XML_VALUE
,
MediaType
.
TEXT_XML_VALUE
,
MediaType
.
APPLICATION_JSON_VALUE
})
...
...
@@ -32,8 +39,36 @@ public class CreateNodeController extends BaseNodeController {
LOG
.
debug
(
"createNodeController called for node with URI {} and PATH {}"
,
node
.
getUri
(),
path
);
// Get Node path (and validates it too)
String
decodedURIPathFromNode
=
URIUtils
.
returnVosPathFromNodeURI
(
node
.
getUri
(),
authority
);
LOG
.
debug
(
"createNodeController URI: {} decoded as {}"
,
node
.
getUri
(),
decodedURIPathFromNode
);
// Check if payload URI is consistent with http request
if
(!
decodedURIPathFromNode
.
equals
(
path
))
{
throw
new
InvalidURIException
(
decodedURIPathFromNode
,
path
);
}
// validate format of input node
this
.
validateInputNode
(
node
);
return
createNodeService
.
createNode
(
node
,
path
,
principal
);
}
private
void
validateInputNode
(
Node
node
)
{
if
(
node
instanceof
LinkNode
)
{
LinkNode
linkNode
=
(
LinkNode
)
node
;
String
target
=
linkNode
.
getTarget
();
// I validate it here to add context easily
if
(
target
==
null
)
{
throw
new
InvalidArgumentException
(
"LinkNode in payload has no target element specified"
);
}
URIUtils
.
returnVosPathFromNodeURI
(
linkNode
.
getTarget
(),
authority
);
}
}
}
src/main/java/it/inaf/oats/vospace/CreateNodeService.java
View file @
be9d595e
...
...
@@ -10,7 +10,6 @@ import it.inaf.oats.vospace.datamodel.NodeProperties;
import
it.inaf.oats.vospace.datamodel.NodeUtils
;
import
it.inaf.oats.vospace.exception.ContainerNotFoundException
;
import
it.inaf.oats.vospace.exception.DuplicateNodeException
;
import
it.inaf.oats.vospace.exception.InvalidURIException
;
import
it.inaf.oats.vospace.exception.LinkFoundException
;
import
it.inaf.oats.vospace.exception.PermissionDeniedException
;
import
it.inaf.oats.vospace.persistence.NodeDAO
;
...
...
@@ -19,7 +18,6 @@ import net.ivoa.xml.vospace.v2.Property;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.stereotype.Service
;
import
org.springframework.transaction.annotation.EnableTransactionManagement
;
...
...
@@ -30,25 +28,12 @@ public class CreateNodeService {
@Autowired
private
NodeDAO
nodeDao
;
@Value
(
"${vospace-authority}"
)
private
String
authority
;
private
static
final
Logger
LOG
=
LoggerFactory
.
getLogger
(
CreateNodeService
.
class
);
public
Node
createNode
(
Node
node
,
String
path
,
User
principal
)
{
LOG
.
debug
(
"createNodeService called for node with URI {} and PATH {}"
,
node
.
getUri
(),
path
);
// Get Node path (and validates it too)
String
decodedURIPathFromNode
=
URIUtils
.
returnVosPathFromNodeURI
(
node
.
getUri
(),
authority
);
LOG
.
debug
(
"createNodeService URI: {} decoded as {}"
,
node
.
getUri
(),
decodedURIPathFromNode
);
// Check if payload URI is consistent with http request
if
(!
decodedURIPathFromNode
.
equals
(
path
))
{
throw
new
InvalidURIException
(
decodedURIPathFromNode
,
path
);
}
// Check if another node is already present at specified path
// This checks if the user is trying to insert the root node at "/" too
if
(
nodeDao
.
listNode
(
path
).
isPresent
())
{
...
...
src/main/java/it/inaf/oats/vospace/URIUtils.java
View file @
be9d595e
...
...
@@ -51,6 +51,10 @@ public class URIUtils {
String
resultPath
=
null
;
try
{
if
(
nodeURI
==
null
)
throw
new
IllegalArgumentException
(
"URI string is null"
);
URI
uri
=
new
URI
(
nodeURI
);
// Check scheme
...
...
@@ -88,7 +92,7 @@ public class URIUtils {
}
catch
(
URISyntaxException
e
)
{
throw
new
InvalidURIException
(
nodeURI
);
}
}
return
resultPath
;
...
...
src/test/java/it/inaf/oats/vospace/CreateNodeControllerTest.java
View file @
be9d595e
...
...
@@ -30,6 +30,7 @@ import org.springframework.boot.test.mock.mockito.SpyBean;
import
net.ivoa.xml.vospace.v2.ContainerNode
;
import
net.ivoa.xml.vospace.v2.LinkNode
;
import
java.util.List
;
import
java.util.Objects
;
import
java.util.Optional
;
import
static
org
.
junit
.
jupiter
.
api
.
Assertions
.
assertEquals
;
import
static
org
.
junit
.
jupiter
.
api
.
Assertions
.
assertTrue
;
...
...
@@ -44,17 +45,17 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMock
@TestPropertySource
(
properties
=
"spring.main.allow-bean-definition-overriding=true"
)
@AutoConfigureMockMvc
public
class
CreateNodeControllerTest
{
@MockBean
private
NodeDAO
nodeDao
;
@SpyBean
@Autowired
private
CreateNodeController
controller
;
@Autowired
private
MockMvc
mockMvc
;
private
ContainerNode
getContainerParentNode
(
String
path
)
{
ContainerNode
parentNode
=
new
ContainerNode
();
// Set parent node address at /
...
...
@@ -66,7 +67,7 @@ public class CreateNodeControllerTest {
parentNode
.
setProperties
(
List
.
of
(
groups
));
return
parentNode
;
}
private
ContainerNode
getContainerParentNodeWithCreator
(
String
path
)
{
ContainerNode
parentNode
=
new
ContainerNode
();
// Set parent node address at /
...
...
@@ -77,7 +78,7 @@ public class CreateNodeControllerTest {
parentNode
.
setProperties
(
List
.
of
(
creator
));
return
parentNode
;
}
private
LinkNode
getLinkParentNode
(
String
path
)
{
LinkNode
parentNode
=
new
LinkNode
();
// Set parent node address at /
...
...
@@ -89,15 +90,15 @@ public class CreateNodeControllerTest {
parentNode
.
setProperties
(
List
.
of
(
groups
));
return
parentNode
;
}
@Test
public
void
testFromJsonToXml
()
throws
Exception
{
String
requestBody
=
getResourceFileContent
(
"create-unstructured-data-node.json"
);
when
(
nodeDao
.
listNode
(
eq
(
"/"
)))
.
thenReturn
(
Optional
.
of
(
getContainerParentNode
(
"/"
)));
mockMvc
.
perform
(
put
(
"/nodes/mydata1"
)
.
header
(
"Authorization"
,
"Bearer user2_token"
)
.
content
(
requestBody
)
...
...
@@ -105,17 +106,17 @@ public class CreateNodeControllerTest {
.
accept
(
MediaType
.
APPLICATION_XML
))
.
andDo
(
print
())
.
andExpect
(
status
().
isOk
());
verifyArguments
();
}
@Test
public
void
testFromXmlToJson
()
throws
Exception
{
String
requestBody
=
getResourceFileContent
(
"create-unstructured-data-node.xml"
);
when
(
nodeDao
.
listNode
(
eq
(
"/"
)))
.
thenReturn
(
Optional
.
of
(
getContainerParentNode
(
"/"
)));
mockMvc
.
perform
(
put
(
"/nodes/mydata1"
)
.
header
(
"Authorization"
,
"Bearer user2_token"
)
.
content
(
requestBody
)
...
...
@@ -123,18 +124,18 @@ public class CreateNodeControllerTest {
.
accept
(
MediaType
.
APPLICATION_JSON
))
.
andDo
(
print
())
.
andExpect
(
status
().
isOk
());
verifyArguments
();
verify
(
nodeDao
,
times
(
1
)).
createNode
(
any
());
}
@Test
public
void
testFromXmlToXml
()
throws
Exception
{
String
requestBody
=
getResourceFileContent
(
"create-unstructured-data-node.xml"
);
when
(
nodeDao
.
listNode
(
eq
(
"/"
)))
.
thenReturn
(
Optional
.
of
(
getContainerParentNode
(
"/"
)));
mockMvc
.
perform
(
put
(
"/nodes/mydata1"
)
.
header
(
"Authorization"
,
"Bearer user2_token"
)
.
content
(
requestBody
)
...
...
@@ -142,18 +143,18 @@ public class CreateNodeControllerTest {
.
accept
(
MediaType
.
APPLICATION_XML
))
.
andDo
(
print
())
.
andExpect
(
status
().
isOk
());
verifyArguments
();
verify
(
nodeDao
,
times
(
1
)).
createNode
(
any
());
}
@Test
public
void
testFromJsonToJson
()
throws
Exception
{
String
requestBody
=
getResourceFileContent
(
"create-unstructured-data-node.json"
);
when
(
nodeDao
.
listNode
(
eq
(
"/"
)))
.
thenReturn
(
Optional
.
of
(
getContainerParentNode
(
"/"
)));
mockMvc
.
perform
(
put
(
"/nodes/mydata1"
)
.
header
(
"Authorization"
,
"Bearer user2_token"
)
.
content
(
requestBody
)
...
...
@@ -161,18 +162,18 @@ public class CreateNodeControllerTest {
.
accept
(
MediaType
.
APPLICATION_JSON
))
.
andDo
(
print
())
.
andExpect
(
status
().
isOk
());
verifyArguments
();
verify
(
nodeDao
,
times
(
1
)).
createNode
(
any
());
}
@Test
public
void
testURIConsistence
()
throws
Exception
{
String
requestBody
=
getResourceFileContent
(
"create-unstructured-data-node.xml"
);
when
(
nodeDao
.
listNode
(
eq
(
"/"
)))
.
thenReturn
(
Optional
.
of
(
getContainerParentNode
(
"/"
)));
mockMvc
.
perform
(
put
(
"/nodes/mydata2"
)
.
header
(
"Authorization"
,
"Bearer user2_token"
)
.
content
(
requestBody
)
...
...
@@ -180,20 +181,77 @@ public class CreateNodeControllerTest {
.
accept
(
MediaType
.
APPLICATION_XML
))
.
andDo
(
print
())
.
andExpect
(
status
().
is4xxClientError
());
verifyArguments
();
}
@Test
public
void
testCreateInternalLinkNode
()
throws
Exception
{
String
requestBody
=
getResourceFileContent
(
"create-internal-link-node.xml"
);
when
(
nodeDao
.
listNode
(
eq
(
"/"
)))
.
thenReturn
(
Optional
.
of
(
getContainerParentNode
(
"/"
)));
mockMvc
.
perform
(
put
(
"/nodes/myInternalLink"
)
.
header
(
"Authorization"
,
"Bearer user2_token"
)
.
content
(
requestBody
)
.
contentType
(
MediaType
.
APPLICATION_XML
)
.
accept
(
MediaType
.
APPLICATION_XML
))
.
andDo
(
print
())
.
andExpect
(
status
().
isOk
());
verifyLinkArguments
(
"vos://example.com!vospace/myDummyDataNode1"
);
}
@Test
public
void
testCreateExternalLinkNode
()
throws
Exception
{
String
requestBody
=
getResourceFileContent
(
"create-external-link-node.xml"
);
when
(
nodeDao
.
listNode
(
eq
(
"/"
)))
.
thenReturn
(
Optional
.
of
(
getContainerParentNode
(
"/"
)));
mockMvc
.
perform
(
put
(
"/nodes/myExternalLink"
)
.
header
(
"Authorization"
,
"Bearer user2_token"
)
.
content
(
requestBody
)
.
contentType
(
MediaType
.
APPLICATION_XML
)
.
accept
(
MediaType
.
APPLICATION_XML
))
.
andDo
(
print
())
.
andExpect
(
status
().
is4xxClientError
());
verifyLinkArguments
(
"vos://external.com!vospace/myDummyDataNode1"
);
}
@Test
public
void
testCreateLinkNodeNoTarget
()
throws
Exception
{
String
requestBody
=
getResourceFileContent
(
"create-link-node-notarget.xml"
);
when
(
nodeDao
.
listNode
(
eq
(
"/"
)))
.
thenReturn
(
Optional
.
of
(
getContainerParentNode
(
"/"
)));
mockMvc
.
perform
(
put
(
"/nodes/myNoTargetLink"
)
.
header
(
"Authorization"
,
"Bearer user2_token"
)
.
content
(
requestBody
)
.
contentType
(
MediaType
.
APPLICATION_XML
)
.
accept
(
MediaType
.
APPLICATION_XML
))
.
andDo
(
print
())
.
andExpect
(
status
().
is4xxClientError
());
verifyLinkArguments
(
null
);
}
@Test
public
void
testNodeAlreadyExisting
()
throws
Exception
{
String
requestBody
=
getResourceFileContent
(
"create-unstructured-data-node.xml"
);
when
(
nodeDao
.
listNode
(
eq
(
"/"
)))
.
thenReturn
(
Optional
.
of
(
getContainerParentNode
(
"/"
)));
when
(
nodeDao
.
listNode
(
eq
(
"/mydata1"
)))
.
thenReturn
(
Optional
.
of
(
getContainerParentNode
(
"/mydata1"
)));
mockMvc
.
perform
(
put
(
"/nodes/mydata1"
)
.
header
(
"Authorization"
,
"Bearer user2_token"
)
.
content
(
requestBody
)
...
...
@@ -201,17 +259,17 @@ public class CreateNodeControllerTest {
.
accept
(
MediaType
.
APPLICATION_XML
))
.
andDo
(
print
())
.
andExpect
(
status
().
is4xxClientError
());
verifyArguments
();
}
@Test
public
void
testContainerNotFound
()
throws
Exception
{
String
requestBody
=
getResourceFileContent
(
"create-unstructured-data-node.xml"
);
when
(
nodeDao
.
listNode
(
eq
(
"/"
)))
.
thenReturn
(
Optional
.
ofNullable
(
null
));
mockMvc
.
perform
(
put
(
"/nodes/mydata1"
)
.
header
(
"Authorization"
,
"Bearer user2_token"
)
.
content
(
requestBody
)
...
...
@@ -219,17 +277,17 @@ public class CreateNodeControllerTest {
.
accept
(
MediaType
.
APPLICATION_XML
))
.
andDo
(
print
())
.
andExpect
(
status
().
is4xxClientError
());
verifyArguments
();
}
@Test
public
void
testLinkNodeFound
()
throws
Exception
{
String
requestBody
=
getResourceFileContent
(
"create-unstructured-data-node.xml"
);
when
(
nodeDao
.
listNode
(
eq
(
"/"
)))
.
thenReturn
(
Optional
.
of
(
getLinkParentNode
(
"/"
)));
mockMvc
.
perform
(
put
(
"/nodes/mydata1"
)
.
header
(
"Authorization"
,
"Bearer user2_token"
)
.
content
(
requestBody
)
...
...
@@ -237,17 +295,17 @@ public class CreateNodeControllerTest {
.
accept
(
MediaType
.
APPLICATION_XML
))
.
andDo
(
print
())
.
andExpect
(
status
().
is4xxClientError
());
verifyArguments
();
}
@Test
public
void
testPermissionDenied
()
throws
Exception
{
String
requestBody
=
getResourceFileContent
(
"create-unstructured-data-node.xml"
);
when
(
nodeDao
.
listNode
(
eq
(
"/"
)))
.
thenReturn
(
Optional
.
of
(
getContainerParentNode
(
"/"
)));
mockMvc
.
perform
(
put
(
"/nodes/mydata1"
)
.
header
(
"Authorization"
,
"Bearer user1_token"
)
.
content
(
requestBody
)
...
...
@@ -255,17 +313,17 @@ public class CreateNodeControllerTest {
.
accept
(
MediaType
.
APPLICATION_XML
))
.
andDo
(
print
())
.
andExpect
(
status
().
is4xxClientError
());
verifyArguments
();
}
@Test
public
void
testWriteWithOnlyOwnership
()
throws
Exception
{
String
requestBody
=
getResourceFileContent
(
"create-unstructured-data-node.xml"
);
when
(
nodeDao
.
listNode
(
eq
(
"/"
)))
.
thenReturn
(
Optional
.
of
(
getContainerParentNodeWithCreator
(
"/"
)));
mockMvc
.
perform
(
put
(
"/nodes/mydata1"
)
.
header
(
"Authorization"
,
"Bearer user2_token"
)
.
content
(
requestBody
)
...
...
@@ -273,16 +331,16 @@ public class CreateNodeControllerTest {
.
accept
(
MediaType
.
APPLICATION_XML
))
.
andDo
(
print
())
.
andExpect
(
status
().
is2xxSuccessful
());
verifyArguments
();
verify
(
nodeDao
,
times
(
1
)).
createNode
(
any
());
}
@Test
public
void
testWriteOwnerAbsent
()
throws
Exception
{
String
requestBody
=
getResourceFileContent
(
"create-unstructured-data-node.xml"
);
when
(
nodeDao
.
listNode
(
eq
(
"/"
)))
.
thenReturn
(
Optional
.
of
(
getContainerParentNodeWithCreator
(
"/"
)));
...
...
@@ -300,18 +358,18 @@ public class CreateNodeControllerTest {
UnstructuredDataNode
udn
=
(
UnstructuredDataNode
)
node
;
String
creator
=
NodeProperties
.
getNodePropertyByURI
(
udn
,
NodeProperties
.
CREATOR_URI
);
return
(
creator
!=
null
&&
creator
.
equals
(
"user2"
));
return
(
creator
!=
null
&&
creator
.
equals
(
"user2"
));
}));
}
@Test
public
void
testGroupPropertiesAbsent
()
throws
Exception
{
String
requestBody
=
getResourceFileContent
(
"create-unstructured-data-node.xml"
);
ContainerNode
cdn
=
getContainerParentNode
(
"/"
);
when
(
nodeDao
.
listNode
(
eq
(
"/"
)))
.
thenReturn
(
Optional
.
of
(
cdn
));
...
...
@@ -331,16 +389,16 @@ public class CreateNodeControllerTest {
udn
,
NodeProperties
.
GROUP_READ_URI
);
String
groupWrite
=
NodeProperties
.
getNodePropertyByURI
(
udn
,
NodeProperties
.
GROUP_WRITE_URI
);
return
(
groupRead
==
null
&&
groupWrite
.
equals
(
"group1 group2"
));
return
(
groupRead
==
null
&&
groupWrite
.
equals
(
"group1 group2"
));
}));
}
@Test
public
void
testWriteOwnerMismatch
()
throws
Exception
{
String
requestBody
=
getResourceFileContent
(
"create-unstructured-data-node-user1.xml"
);
when
(
nodeDao
.
listNode
(
eq
(
"/"
)))
.
thenReturn
(
Optional
.
of
(
getContainerParentNodeWithCreator
(
"/"
)));
...
...
@@ -354,15 +412,15 @@ public class CreateNodeControllerTest {
.
andExpect
(
status
().
is4xxClientError
());
// assert createNode is not called
verify
(
nodeDao
,
times
(
0
)).
createNode
(
any
());
verify
(
nodeDao
,
times
(
0
)).
createNode
(
any
());
}
@Test
public
void
testSubPath
()
throws
Exception
{
String
requestBody
=
getResourceFileContent
(
"create-unstructured-data-node.xml"
)
.
replace
(
"/mydata1"
,
"/mydata1/anothernode"
);
mockMvc
.
perform
(
put
(
"/nodes/mydata1/anothernode"
)
.
header
(
"Authorization"
,
"Bearer user2_token"
)
.
content
(
requestBody
)
...
...
@@ -373,19 +431,19 @@ public class CreateNodeControllerTest {
// Using ArgumentCaptor for verifying multiple method invocations
ArgumentCaptor
<
String
>
argCaptor
=
ArgumentCaptor
.
forClass
(
String
.
class
);
verify
(
nodeDao
,
times
(
2
)).
listNode
(
argCaptor
.
capture
());
assertEquals
(
"/mydata1/anothernode"
,
argCaptor
.
getAllValues
().
get
(
0
));
assertEquals
(
"/mydata1"
,
argCaptor
.
getAllValues
().
get
(
1
));
}
@Test
public
void
testIllegalChars
()
throws
Exception
{
String
requestBody
=
getResourceFileContent
(
"create-unstructured-data-node.xml"
)
.
replace
(
"/mydata1"
,
"/mydata1/?anothernode"
);
String
message
=
mockMvc
.
perform
(
put
(
new
URI
(
"/nodes/mydata1/%3Fanothernode"
))
.
header
(
"Authorization"
,
"Bearer user2_token"
)
.
content
(
requestBody
)
...
...
@@ -394,10 +452,10 @@ public class CreateNodeControllerTest {
.
andDo
(
print
())
.
andExpect
(
status
().
isBadRequest
())
.
andReturn
().
getResolvedException
().
getMessage
();
assertTrue
(
message
.
contains
(
"contains an illegal character"
));
}
private
void
verifyArguments
()
{
verify
(
controller
).
createNode
(
argThat
(
node
->
{
...
...
@@ -408,7 +466,19 @@ public class CreateNodeControllerTest {
&&
"ivo://ivoa.net/vospace/core#description"
.
equals
(
property
.
getUri
());
}),
any
());
}
private
void
verifyLinkArguments
(
String
target
)
{
verify
(
controller
).
createNode
(
argThat
(
node
->
{
LinkNode
linkNode
=
(
LinkNode
)
node
;
Property
property
=
linkNode
.
getProperties
().
get
(
0
);
return
"vos:LinkNode"
.
equals
(
linkNode
.
getType
())
&&
"test value"
.
equals
(
property
.
getValue
())
&&
"ivo://ivoa.net/vospace/core#description"
.
equals
(
property
.
getUri
())
&&
Objects
.
equals
(
target
,
linkNode
.
getTarget
());
}),
any
());
}
protected
static
String
getResourceFileContent
(
String
fileName
)
throws
Exception
{
try
(
InputStream
in
=
CreateNodeControllerTest
.
class
.
getClassLoader
().
getResourceAsStream
(
fileName
))
{
return
new
String
(
in
.
readAllBytes
(),
StandardCharsets
.
UTF_8
);
...
...
src/test/java/it/inaf/oats/vospace/persistence/NodeDAOTest.java
View file @
be9d595e
...
...
@@ -17,6 +17,7 @@ import java.util.Set;
import
javax.sql.DataSource
;
import
net.ivoa.xml.vospace.v2.ContainerNode
;
import
net.ivoa.xml.vospace.v2.DataNode
;