001/*
002 * Licensed to DuraSpace under one or more contributor license agreements.
003 * See the NOTICE file distributed with this work for additional information
004 * regarding copyright ownership.
005 *
006 * DuraSpace licenses this file to you under the Apache License,
007 * Version 2.0 (the "License"); you may not use this file except in
008 * compliance with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.fcrepo.integration.http.api;
019
020import static javax.ws.rs.core.Response.Status.CREATED;
021import static javax.ws.rs.core.Response.Status.NO_CONTENT;
022import static javax.ws.rs.core.Response.Status.OK;
023import static javax.ws.rs.core.Response.Status.PRECONDITION_FAILED;
024import static org.junit.Assert.assertEquals;
025import static org.junit.Assert.assertNotNull;
026
027import java.io.IOException;
028
029import org.apache.http.client.methods.CloseableHttpResponse;
030import org.apache.http.client.methods.HttpGet;
031import org.apache.http.client.methods.HttpHead;
032import org.apache.http.client.methods.HttpPatch;
033import org.apache.http.client.methods.HttpPut;
034import org.apache.http.entity.StringEntity;
035import org.junit.Test;
036import org.springframework.test.context.TestExecutionListeners;
037
038/**
039 * @author dbernstein
040 */
041@TestExecutionListeners(
042        listeners = { TestIsolationExecutionListener.class },
043        mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS)
044public class StateTokensIT extends AbstractResourceIT {
045
046    private static final String X_STATE_TOKEN_HEADER = "X-State-Token";
047    private static final String X_IF_STATE_TOKEN_HEADER = "X-If-State-Token";
048
049    @Test
050    public void testGetHasStateTokenRDFSource() throws IOException {
051        final String id = getRandomUniqueId();
052        createObjectAndClose(id);
053        try (final CloseableHttpResponse response = execute(new HttpGet(serverAddress + id))) {
054            assertEquals(OK.getStatusCode(), getStatus(response));
055            assertNotNull(X_STATE_TOKEN_HEADER, response.getFirstHeader(X_STATE_TOKEN_HEADER).getValue());
056        }
057    }
058
059    @Test
060    public void testHeadHasStateTokenRDFSource() throws IOException {
061        final String id = getRandomUniqueId();
062        createObjectAndClose(id);
063        try (final CloseableHttpResponse response = execute(new HttpHead(serverAddress + id))) {
064            assertEquals(OK.getStatusCode(), getStatus(response));
065            assertNotNull(X_STATE_TOKEN_HEADER, response.getFirstHeader(X_STATE_TOKEN_HEADER).getValue());
066        }
067    }
068
069    @Test
070    public void testAclGetHasStateTokenRDFSource() throws IOException {
071        final String id = getRandomUniqueId();
072        createObjectAndClose(id);
073        final String aclPid = id + "/fcr:acl";
074
075        final HttpPut method = putObjMethod(aclPid, "text/turtle",
076                                            "<#auth>  a <http://www.w3.org/ns/auth/acl#Authorization> .");
077
078        try (final CloseableHttpResponse response = execute(method)) {
079            assertEquals(CREATED.getStatusCode(), getStatus(response));
080        }
081
082        try (final CloseableHttpResponse response = execute(new HttpGet(serverAddress + aclPid))) {
083            assertEquals(OK.getStatusCode(), getStatus(response));
084            assertNotNull(X_STATE_TOKEN_HEADER, response.getFirstHeader(X_STATE_TOKEN_HEADER).getValue());
085        }
086    }
087
088    @Test
089    public void testAclHeadHasStateTokenRDFSource() throws IOException {
090        final String id = getRandomUniqueId();
091        createObjectAndClose(id);
092        final String aclPid = id + "/fcr:acl";
093
094        final HttpPut method = putObjMethod(aclPid, "text/turtle",
095                                            "<#auth>  a <http://www.w3.org/ns/auth/acl#Authorization> .");
096
097        try (final CloseableHttpResponse response = execute(method)) {
098            assertEquals(CREATED.getStatusCode(), getStatus(response));
099        }
100
101        try (final CloseableHttpResponse response = execute(new HttpHead(serverAddress + aclPid))) {
102            assertEquals(OK.getStatusCode(), getStatus(response));
103            assertNotNull(X_STATE_TOKEN_HEADER, response.getFirstHeader(X_STATE_TOKEN_HEADER).getValue());
104        }
105    }
106
107    @Test
108    public void testLdpcvGetHasStateTokenRDFSource() throws IOException {
109        final String id = getRandomUniqueId();
110        createObjectAndClose(id);
111        try (final CloseableHttpResponse response = execute(new HttpGet(serverAddress + id + "/fcr:versions"))) {
112            assertEquals(OK.getStatusCode(), getStatus(response));
113            assertNotNull(X_STATE_TOKEN_HEADER, response.getFirstHeader(X_STATE_TOKEN_HEADER).getValue());
114        }
115    }
116
117    @Test
118    public void testLdpcvHeadHasStateTokenRDFSource() throws IOException {
119        final String id = getRandomUniqueId();
120        createObjectAndClose(id);
121        try (final CloseableHttpResponse response = execute(new HttpHead(serverAddress + id + "/fcr:versions"))) {
122            assertEquals(OK.getStatusCode(), getStatus(response));
123            assertNotNull(X_STATE_TOKEN_HEADER, response.getFirstHeader(X_STATE_TOKEN_HEADER).getValue());
124        }
125    }
126
127    @Test
128    public void testGetHasStateTokenNonRDFSource() throws IOException {
129        final String id = getRandomUniqueId();
130        final String location = serverAddress + id + "/binary";
131        final HttpPut method = putDSMethod(id, "binary", "foo");
132        try (final CloseableHttpResponse response = execute(method)) {
133            assertEquals(CREATED.getStatusCode(), getStatus(response));
134        }
135
136        try (final CloseableHttpResponse response = execute(new HttpGet(location))) {
137            assertEquals(OK.getStatusCode(), getStatus(response));
138            assertNotNull(X_STATE_TOKEN_HEADER, response.getFirstHeader(X_STATE_TOKEN_HEADER).getValue());
139        }
140    }
141
142    @Test
143    public void testHeadHasStateTokenNonRDFSource() throws IOException {
144        final String id = getRandomUniqueId();
145        final String location = serverAddress + id + "/binary";
146        final HttpPut method = putDSMethod(id, "binary", "foo");
147        try (final CloseableHttpResponse response = execute(method)) {
148            assertEquals(CREATED.getStatusCode(), getStatus(response));
149        }
150
151        try (final CloseableHttpResponse response = execute(new HttpHead(location))) {
152            assertEquals(OK.getStatusCode(), getStatus(response));
153            assertNotNull(X_STATE_TOKEN_HEADER, response.getFirstHeader(X_STATE_TOKEN_HEADER).getValue());
154        }
155    }
156
157    @Test
158    public void testPutWithStateTokenOnNonRDFSource() throws IOException {
159        final String id = getRandomUniqueId();
160        final String location = serverAddress + id + "/binary";
161        //create a binary
162        final HttpPut method = putDSMethod(id, "binary", "foo");
163        try (final CloseableHttpResponse response = execute(method)) {
164            assertEquals(CREATED.getStatusCode(), getStatus(response));
165        }
166
167        //get state token
168        final String stateToken;
169        try (final CloseableHttpResponse response = execute(new HttpHead(location))) {
170            assertEquals(OK.getStatusCode(), getStatus(response));
171            stateToken = response.getFirstHeader(X_STATE_TOKEN_HEADER).getValue();
172        }
173
174        //perform put with valid X-If-State-Token
175        final HttpPut validPut = putDSMethod(id, "binary", "bar");
176        validPut.addHeader(X_IF_STATE_TOKEN_HEADER, stateToken);
177        try (final CloseableHttpResponse response = execute(validPut)) {
178            assertEquals(NO_CONTENT.getStatusCode(), getStatus(response));
179        }
180
181        //perform put with an invalid X-If-State-Token
182        final HttpPut invalidPut = putDSMethod(id, "binary", "boo");
183        invalidPut.addHeader(X_IF_STATE_TOKEN_HEADER, "invalid_token");
184        try (final CloseableHttpResponse response = execute(invalidPut)) {
185            assertEquals(PRECONDITION_FAILED.getStatusCode(), getStatus(response));
186        }
187
188    }
189
190    @Test
191    public void testPatchWithStateTokenOnRDFSource() throws IOException {
192        final String id = getRandomUniqueId();
193        final String location = serverAddress + id;
194        //create a resource
195        final HttpPut method = putObjMethod(id);
196        try (final CloseableHttpResponse response = execute(method)) {
197            assertEquals(CREATED.getStatusCode(), getStatus(response));
198        }
199
200        //get state token
201        final String stateToken;
202        try (final CloseableHttpResponse response = execute(new HttpHead(location))) {
203            assertEquals(OK.getStatusCode(), getStatus(response));
204            stateToken = response.getFirstHeader(X_STATE_TOKEN_HEADER).getValue();
205        }
206
207        //perform patch with an invalid X-If-State-Token
208        final HttpPatch invalidPatch = patchObjMethod(id);
209        invalidPatch.addHeader(X_IF_STATE_TOKEN_HEADER, "invalid_token");
210        invalidPatch.setHeader("Content-Type", "application/sparql-update");
211        invalidPatch.setEntity(new StringEntity("INSERT { <> a <http://example.org/test> } WHERE {}"));
212
213        try (final CloseableHttpResponse response = execute(invalidPatch)) {
214            assertEquals(PRECONDITION_FAILED.getStatusCode(), getStatus(response));
215        }
216
217
218        //perform patch with valid X-If-State-Token
219        final HttpPatch validPatch = patchObjMethod(id);
220        validPatch.addHeader(X_IF_STATE_TOKEN_HEADER, stateToken);
221        validPatch.setHeader("Content-Type", "application/sparql-update");
222        validPatch.setEntity(new StringEntity("INSERT { <> a <http://example.org/test> } WHERE {}"));
223        try (final CloseableHttpResponse response = execute(validPatch)) {
224            assertEquals(NO_CONTENT.getStatusCode(), getStatus(response));
225        }
226    }
227
228    @Test
229    public void testPatchWithStateTokenOnAcl() throws IOException {
230        final String id = getRandomUniqueId();
231        createObjectAndClose(id);
232        final String aclPid = id + "/fcr:acl";
233        final String location = serverAddress + aclPid;
234        final HttpPut method = putObjMethod(aclPid, "text/turtle",
235                                            "<#auth>  a <http://www.w3.org/ns/auth/acl#Authorization> .");
236
237        try (final CloseableHttpResponse response = execute(method)) {
238            assertEquals(CREATED.getStatusCode(), getStatus(response));
239        }
240
241        final String stateToken;
242        try (final CloseableHttpResponse response = execute(new HttpHead(location))) {
243            assertEquals(OK.getStatusCode(), getStatus(response));
244            stateToken = response.getFirstHeader(X_STATE_TOKEN_HEADER).getValue();
245        }
246
247        //perform patch with an invalid X-If-State-Token
248        final HttpPatch invalidPatch = patchObjMethod(aclPid);
249        invalidPatch.addHeader(X_IF_STATE_TOKEN_HEADER, "invalid_token");
250        invalidPatch.setHeader("Content-Type", "application/sparql-update");
251        invalidPatch.setEntity(new StringEntity("INSERT { <> a <http://example.org/test> } WHERE {}"));
252
253        try (final CloseableHttpResponse response = execute(invalidPatch)) {
254            assertEquals(PRECONDITION_FAILED.getStatusCode(), getStatus(response));
255        }
256
257        //perform patch with valid X-If-State-Token
258        final HttpPatch validPatch = patchObjMethod(aclPid);
259        validPatch.addHeader(X_IF_STATE_TOKEN_HEADER, stateToken);
260        validPatch.setHeader("Content-Type", "application/sparql-update");
261        validPatch.setEntity(new StringEntity("INSERT { <> a <http://example.org/test> } WHERE {}"));
262        try (final CloseableHttpResponse response = execute(validPatch)) {
263            assertEquals(NO_CONTENT.getStatusCode(), getStatus(response));
264        }
265    }
266}