blob: c81d5f5daa05c28b488283f6fb21767127201420 [file] [log] [blame]
Brian Stankeb9170d92016-02-19 14:18:42 -05001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Brian Stankeb9170d92016-02-19 14:18:42 -05003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Jian Li8ae91202016-03-24 14:36:16 -070017package org.onosproject.rest.resources;
Brian Stankeb9170d92016-02-19 14:18:42 -050018
19import com.eclipsesource.json.Json;
20import com.eclipsesource.json.JsonArray;
21import com.eclipsesource.json.JsonObject;
Brian Stankeb9170d92016-02-19 14:18:42 -050022import org.hamcrest.Description;
23import org.hamcrest.Matchers;
24import org.hamcrest.TypeSafeMatcher;
Brian Stankeb9170d92016-02-19 14:18:42 -050025import org.junit.Before;
26import org.junit.Test;
27import org.onlab.osgi.ServiceDirectory;
28import org.onlab.osgi.TestServiceDirectory;
Brian Stankeb9170d92016-02-19 14:18:42 -050029import org.onosproject.codec.CodecService;
30import org.onosproject.codec.impl.CodecManager;
31import org.onosproject.net.key.DeviceKey;
32import org.onosproject.net.key.DeviceKeyAdminService;
33import org.onosproject.net.key.DeviceKeyId;
34import org.onosproject.net.key.DeviceKeyService;
35
Jian Li9d616492016-03-09 10:52:49 -080036import javax.ws.rs.BadRequestException;
Jian Li0ad0c552019-12-16 22:26:21 +090037import javax.ws.rs.InternalServerErrorException;
Jian Li9d616492016-03-09 10:52:49 -080038import javax.ws.rs.NotFoundException;
39import javax.ws.rs.client.Entity;
40import javax.ws.rs.client.WebTarget;
Brian Stankeb9170d92016-02-19 14:18:42 -050041import javax.ws.rs.core.MediaType;
Jian Li9d616492016-03-09 10:52:49 -080042import javax.ws.rs.core.Response;
Brian Stankeb9170d92016-02-19 14:18:42 -050043import java.io.InputStream;
44import java.net.HttpURLConnection;
45import java.util.HashSet;
46
Jian Li9d616492016-03-09 10:52:49 -080047import static org.easymock.EasyMock.anyObject;
48import static org.easymock.EasyMock.createMock;
49import static org.easymock.EasyMock.expect;
50import static org.easymock.EasyMock.expectLastCall;
51import static org.easymock.EasyMock.replay;
52import static org.easymock.EasyMock.verify;
53import static org.hamcrest.Matchers.containsString;
54import static org.hamcrest.Matchers.hasSize;
55import static org.hamcrest.Matchers.is;
56import static org.hamcrest.Matchers.notNullValue;
57import static org.junit.Assert.assertEquals;
58import static org.junit.Assert.assertThat;
59import static org.junit.Assert.fail;
Brian Stankeb9170d92016-02-19 14:18:42 -050060
61/**
62 * Unit tests for device key REST APIs.
63 */
64public class DeviceKeyWebResourceTest extends ResourceTest {
65
66 final DeviceKeyService mockDeviceKeyService = createMock(DeviceKeyService.class);
67 final DeviceKeyAdminService mockDeviceKeyAdminService = createMock(DeviceKeyAdminService.class);
68
69 final HashSet<DeviceKey> deviceKeySet = new HashSet<>();
70
71 private static final String ID = "id";
72 private static final String TYPE = "type";
73 private static final String LABEL = "label";
74 private static final String COMMUNITY_NAME = "community_name";
75 private static final String USERNAME = "username";
76 private static final String PASSWORD = "password";
77
78 private final String deviceKeyId1 = "DeviceKeyId1";
79 private final String deviceKeyId2 = "DeviceKeyId2";
80 private final String deviceKeyId3 = "DeviceKeyId3";
Brian Stankeb8ff6412016-02-25 14:16:19 -050081 private final String deviceKeyId4 = "DeviceKeyId4";
Brian Stankeb9170d92016-02-19 14:18:42 -050082 private final String deviceKeyLabel = "DeviceKeyLabel";
83 private final String deviceKeyCommunityName = "DeviceKeyCommunityName";
84 private final String deviceKeyUsername = "DeviceKeyUsername";
85 private final String deviceKeyPassword = "DeviceKeyPassword";
86
87 private final DeviceKey deviceKey1 = DeviceKey.createDeviceKeyUsingCommunityName(
88 DeviceKeyId.deviceKeyId(deviceKeyId1), deviceKeyLabel, deviceKeyCommunityName);
89 private final DeviceKey deviceKey2 = DeviceKey.createDeviceKeyUsingUsernamePassword(
Brian Stankeb8ff6412016-02-25 14:16:19 -050090 DeviceKeyId.deviceKeyId(deviceKeyId2), null, deviceKeyUsername, deviceKeyPassword);
91 private final DeviceKey deviceKey3 = DeviceKey.createDeviceKeyUsingUsernamePassword(
92 DeviceKeyId.deviceKeyId(deviceKeyId3), null, null, null);
93 private final DeviceKey deviceKey4 = DeviceKey.createDeviceKeyUsingCommunityName(
94 DeviceKeyId.deviceKeyId(deviceKeyId4), null, null);
Brian Stankeb9170d92016-02-19 14:18:42 -050095
96 /**
97 * Initializes test mocks and environment.
98 */
99 @Before
100 public void setUpMocks() {
101 expect(mockDeviceKeyService.getDeviceKeys()).andReturn(deviceKeySet).anyTimes();
102
103 // Register the services needed for the test
104 CodecManager codecService = new CodecManager();
105 codecService.activate();
106 ServiceDirectory testDirectory =
107 new TestServiceDirectory()
108 .add(DeviceKeyService.class, mockDeviceKeyService)
109 .add(DeviceKeyAdminService.class, mockDeviceKeyAdminService)
110 .add(CodecService.class, codecService);
111
Ray Milkey094a1352018-01-22 14:03:54 -0800112 setServiceDirectory(testDirectory);
Brian Stankeb9170d92016-02-19 14:18:42 -0500113 }
114
115 /**
Brian Stankeb9170d92016-02-19 14:18:42 -0500116 * Hamcrest matcher to check that a device key representation in JSON matches
117 * the actual device key.
118 */
119 public static class DeviceKeyJsonMatcher extends TypeSafeMatcher<JsonObject> {
120 private final DeviceKey deviceKey;
121 private String reason = "";
122
123 public DeviceKeyJsonMatcher(DeviceKey deviceKeyValue) {
124 deviceKey = deviceKeyValue;
125 }
126
127 @Override
128 public boolean matchesSafely(JsonObject jsonHost) {
129 // Check the device key id
130 final String jsonId = jsonHost.get(ID).asString();
131 if (!jsonId.equals(deviceKey.deviceKeyId().id().toString())) {
132 reason = ID + " " + deviceKey.deviceKeyId().id().toString();
133 return false;
134 }
135
136 // Check the device key label
Brian Stankeb8ff6412016-02-25 14:16:19 -0500137 final String jsonLabel = (jsonHost.get(LABEL).isNull()) ? null : jsonHost.get(LABEL).asString();
138 if (deviceKey.label() != null) {
139 if ((jsonLabel == null) || !jsonLabel.equals(deviceKey.label())) {
140 reason = LABEL + " " + deviceKey.label();
141 return false;
142 }
Brian Stankeb9170d92016-02-19 14:18:42 -0500143 }
144
145 // Check the device key type
146 final String jsonType = jsonHost.get(TYPE).asString();
147 if (!jsonType.equals(deviceKey.type().toString())) {
148 reason = TYPE + " " + deviceKey.type().toString();
149 return false;
150 }
151
152 if (jsonType.equals(DeviceKey.Type.COMMUNITY_NAME.toString())) {
153 // Check the device key community name
Brian Stankeb8ff6412016-02-25 14:16:19 -0500154 final String jsonCommunityName = jsonHost.get(COMMUNITY_NAME).isNull() ?
155 null : jsonHost.get(COMMUNITY_NAME).asString();
156 if (deviceKey.asCommunityName().name() != null) {
157 if (!jsonCommunityName.equals(deviceKey.asCommunityName().name().toString())) {
158 reason = COMMUNITY_NAME + " " + deviceKey.asCommunityName().name().toString();
159 return false;
160 }
Brian Stankeb9170d92016-02-19 14:18:42 -0500161 }
162 } else if (jsonType.equals(DeviceKey.Type.USERNAME_PASSWORD.toString())) {
163 // Check the device key username
Brian Stankeb8ff6412016-02-25 14:16:19 -0500164 final String jsonUsername = jsonHost.get(USERNAME).isNull() ?
165 null : jsonHost.get(USERNAME).asString();
166 if (deviceKey.asUsernamePassword().username() != null) {
167 if (!jsonUsername.equals(deviceKey.asUsernamePassword().username().toString())) {
168 reason = USERNAME + " " + deviceKey.asUsernamePassword().username().toString();
169 return false;
170 }
Brian Stankeb9170d92016-02-19 14:18:42 -0500171 }
172
173 // Check the device key password
Brian Stankeb8ff6412016-02-25 14:16:19 -0500174 final String jsonPassword = jsonHost.get(PASSWORD).isNull() ?
175 null : jsonHost.get(PASSWORD).asString();
176 if (deviceKey.asUsernamePassword().password() != null) {
177 if (!jsonPassword.equals(deviceKey.asUsernamePassword().password().toString())) {
178 reason = PASSWORD + " " + deviceKey.asUsernamePassword().password().toString();
179 return false;
180 }
Brian Stankeb9170d92016-02-19 14:18:42 -0500181 }
182 } else {
183 reason = "Unknown " + TYPE + " " + deviceKey.type().toString();
184 return false;
185 }
186
187 return true;
188 }
189
190 @Override
191 public void describeTo(Description description) {
192 description.appendText(reason);
193 }
194 }
195
196 /**
197 * Factory to allocate a device key array matcher.
198 *
199 * @param deviceKey device key object we are looking for
200 * @return matcher
201 */
202 private static DeviceKeyJsonMatcher matchesDeviceKey(DeviceKey deviceKey) {
203 return new DeviceKeyJsonMatcher(deviceKey);
204 }
205
206 /**
207 * Hamcrest matcher to check that a device key is represented properly in a JSON
208 * array of device keys.
209 */
210 public static class DeviceKeyJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
211 private final DeviceKey deviceKey;
212 private String reason = "";
213
214 public DeviceKeyJsonArrayMatcher(DeviceKey deviceKeyValue) {
215 deviceKey = deviceKeyValue;
216 }
217
218 @Override
219 public boolean matchesSafely(JsonArray json) {
220 boolean deviceKeyFound = false;
221 final int expectedAttributes = 5;
222 for (int jsonDeviceKeyIndex = 0; jsonDeviceKeyIndex < json.size();
223 jsonDeviceKeyIndex++) {
224
225 final JsonObject jsonHost = json.get(jsonDeviceKeyIndex).asObject();
226
227 // Device keys can have a variable number of attribute so we check
228 // that there is a minimum number.
229 if (jsonHost.names().size() < expectedAttributes) {
230 reason = "Found a device key with the wrong number of attributes";
231 return false;
232 }
233
234 final String jsonDeviceKeyId = jsonHost.get(ID).asString();
235 if (jsonDeviceKeyId.equals(deviceKey.deviceKeyId().id().toString())) {
236 deviceKeyFound = true;
237
238 // We found the correct device key, check the device key attribute values
239 assertThat(jsonHost, matchesDeviceKey(deviceKey));
240 }
241 }
242 if (!deviceKeyFound) {
243 reason = "Device key with id " + deviceKey.deviceKeyId().id().toString() + " was not found";
244 return false;
245 } else {
246 return true;
247 }
248 }
249
250 @Override
251 public void describeTo(Description description) {
252 description.appendText(reason);
253 }
254 }
255
256 /**
257 * Factory to allocate a device key array matcher.
258 *
259 * @param deviceKey device key object we are looking for
260 * @return matcher
261 */
262 private static DeviceKeyJsonArrayMatcher hasDeviceKey(DeviceKey deviceKey) {
263 return new DeviceKeyJsonArrayMatcher(deviceKey);
264 }
265
266 /**
267 * Tests the result of the REST API GET when there are no device keys.
268 */
269 @Test
270 public void testGetDeviceKeysEmptyArray() {
271 replay(mockDeviceKeyService);
Brian Stankeb9170d92016-02-19 14:18:42 -0500272
Jian Li9d616492016-03-09 10:52:49 -0800273 WebTarget wt = target();
274 String response = wt.path("keys").request().get(String.class);
Brian Stankeb9170d92016-02-19 14:18:42 -0500275 assertThat(response, is("{\"keys\":[]}"));
Brian Stankeb8ff6412016-02-25 14:16:19 -0500276
277 verify(mockDeviceKeyService);
Brian Stankeb9170d92016-02-19 14:18:42 -0500278 }
279
280 /**
281 * Tests the result of the REST API GET when device keys are defined.
282 */
283 @Test
284 public void testGetDeviceKeysArray() {
285 replay(mockDeviceKeyService);
Brian Stankeb9170d92016-02-19 14:18:42 -0500286 deviceKeySet.add(deviceKey1);
287 deviceKeySet.add(deviceKey2);
Brian Stankeb8ff6412016-02-25 14:16:19 -0500288 deviceKeySet.add(deviceKey3);
289 deviceKeySet.add(deviceKey4);
Brian Stankeb9170d92016-02-19 14:18:42 -0500290
Jian Li9d616492016-03-09 10:52:49 -0800291 WebTarget wt = target();
292 String response = wt.path("keys").request().get(String.class);
Brian Stankeb9170d92016-02-19 14:18:42 -0500293 assertThat(response, containsString("{\"keys\":["));
294
295 final JsonObject result = Json.parse(response).asObject();
296 assertThat(result, notNullValue());
297
298 assertThat(result.names(), hasSize(1));
299 assertThat(result.names().get(0), is("keys"));
300
301 final JsonArray deviceKeys = result.get("keys").asArray();
302 assertThat(deviceKeys, notNullValue());
Brian Stankeb8ff6412016-02-25 14:16:19 -0500303 assertEquals("Device keys array is not the correct size.", 4, deviceKeys.size());
Brian Stankeb9170d92016-02-19 14:18:42 -0500304
305 assertThat(deviceKeys, hasDeviceKey(deviceKey1));
306 assertThat(deviceKeys, hasDeviceKey(deviceKey2));
Brian Stankeb8ff6412016-02-25 14:16:19 -0500307 assertThat(deviceKeys, hasDeviceKey(deviceKey3));
308 assertThat(deviceKeys, hasDeviceKey(deviceKey4));
309
310 verify(mockDeviceKeyService);
Brian Stankeb9170d92016-02-19 14:18:42 -0500311 }
312
313 /**
314 * Tests the result of the REST API GET using a device key identifier.
315 */
316 @Test
317 public void testGetDeviceKeyById() {
318 deviceKeySet.add(deviceKey1);
319
320 expect(mockDeviceKeyService.getDeviceKey(DeviceKeyId.deviceKeyId(deviceKeyId1)))
321 .andReturn(deviceKey1)
322 .anyTimes();
323 replay(mockDeviceKeyService);
Brian Stankeb9170d92016-02-19 14:18:42 -0500324
Jian Li9d616492016-03-09 10:52:49 -0800325 WebTarget wt = target();
326 String response = wt.path("keys/" + deviceKeyId1).request().get(String.class);
Brian Stankeb9170d92016-02-19 14:18:42 -0500327 final JsonObject result = Json.parse(response).asObject();
328 assertThat(result, notNullValue());
329
330 assertThat(result, matchesDeviceKey(deviceKey1));
Brian Stankeb8ff6412016-02-25 14:16:19 -0500331
332 verify(mockDeviceKeyService);
Brian Stankeb9170d92016-02-19 14:18:42 -0500333 }
334
335 /**
336 * Tests that a GET of a non-existent object throws an exception.
337 */
338 @Test
339 public void testGetNonExistentDeviceKey() {
340
341 expect(mockDeviceKeyService.getDeviceKey(DeviceKeyId.deviceKeyId(deviceKeyId1)))
342 .andReturn(null)
343 .anyTimes();
344 replay(mockDeviceKeyService);
Brian Stankeb9170d92016-02-19 14:18:42 -0500345
Jian Li9d616492016-03-09 10:52:49 -0800346 WebTarget wt = target();
Brian Stankeb9170d92016-02-19 14:18:42 -0500347 try {
Jian Li9d616492016-03-09 10:52:49 -0800348 wt.path("keys/" + deviceKeyId1).request().get(String.class);
Brian Stankeb9170d92016-02-19 14:18:42 -0500349 fail("GET of a non-existent device key did not throw an exception");
Jian Li9d616492016-03-09 10:52:49 -0800350 } catch (NotFoundException ex) {
351 assertThat(ex.getMessage(), containsString("HTTP 404 Not Found"));
Brian Stankeb9170d92016-02-19 14:18:42 -0500352 }
Brian Stankeb8ff6412016-02-25 14:16:19 -0500353
354 verify(mockDeviceKeyService);
Brian Stankeb9170d92016-02-19 14:18:42 -0500355 }
356
357 /**
358 * Tests adding of new device key using POST via JSON stream.
359 */
360 @Test
361 public void testPost() {
362
363 mockDeviceKeyAdminService.addKey(anyObject());
364 expectLastCall();
365
Brian Stankeb9170d92016-02-19 14:18:42 -0500366 replay(mockDeviceKeyAdminService);
367
Jian Li9d616492016-03-09 10:52:49 -0800368 WebTarget wt = target();
Brian Stankeb9170d92016-02-19 14:18:42 -0500369 InputStream jsonStream = DeviceKeyWebResourceTest.class
370 .getResourceAsStream("post-device-key.json");
371
Jian Li9d616492016-03-09 10:52:49 -0800372 Response response = wt.path("keys").request(MediaType.APPLICATION_JSON_TYPE)
373 .post(Entity.json(jsonStream));
Brian Stankeb9170d92016-02-19 14:18:42 -0500374 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
375
376 String location = response.getLocation().getPath();
377 assertThat(location, Matchers.startsWith("/keys/" + deviceKeyId3));
Brian Stankeb8ff6412016-02-25 14:16:19 -0500378
379 verify(mockDeviceKeyAdminService);
Brian Stankeb9170d92016-02-19 14:18:42 -0500380 }
381
382 /**
383 * Tests adding of a null device key using POST via JSON stream.
384 */
385 @Test
386 public void testPostNullDeviceKey() {
387
Brian Stankeb9170d92016-02-19 14:18:42 -0500388 replay(mockDeviceKeyAdminService);
389
Jian Li9d616492016-03-09 10:52:49 -0800390 WebTarget wt = target();
Brian Stankeb9170d92016-02-19 14:18:42 -0500391 try {
Jian Li9d616492016-03-09 10:52:49 -0800392 wt.path("keys").request(MediaType.APPLICATION_JSON_TYPE)
393 .post(Entity.json(null), String.class);
Brian Stankeb9170d92016-02-19 14:18:42 -0500394 fail("POST of null device key did not throw an exception");
Jian Li9d616492016-03-09 10:52:49 -0800395 } catch (BadRequestException ex) {
396 assertThat(ex.getMessage(), containsString("HTTP 400 Bad Request"));
Jian Li0ad0c552019-12-16 22:26:21 +0900397 } catch (InternalServerErrorException ex) {
398 assertThat(ex.getMessage(), containsString("HTTP 500 Internal Server Error"));
Brian Stankeb9170d92016-02-19 14:18:42 -0500399 }
Brian Stankeb8ff6412016-02-25 14:16:19 -0500400
401 verify(mockDeviceKeyAdminService);
Brian Stankeb9170d92016-02-19 14:18:42 -0500402 }
403
404 /**
405 * Tests removing a device key with DELETE request.
406 */
407 @Test
408 public void testDelete() {
409 expect(mockDeviceKeyService.getDeviceKey(DeviceKeyId.deviceKeyId(deviceKeyId2)))
410 .andReturn(deviceKey2)
411 .anyTimes();
412 mockDeviceKeyAdminService.removeKey(anyObject());
413 expectLastCall();
414
415 replay(mockDeviceKeyService);
416 replay(mockDeviceKeyAdminService);
417
Jian Li9d616492016-03-09 10:52:49 -0800418 WebTarget wt = target();
Brian Stankeb9170d92016-02-19 14:18:42 -0500419
Jian Li9d616492016-03-09 10:52:49 -0800420 Response response = wt.path("keys/" + deviceKeyId2)
421 .request(MediaType.APPLICATION_JSON_TYPE)
422 .delete();
Brian Stankeb9170d92016-02-19 14:18:42 -0500423 assertThat(response.getStatus(), is(HttpURLConnection.HTTP_OK));
Brian Stankeb8ff6412016-02-25 14:16:19 -0500424
425 verify(mockDeviceKeyService);
426 verify(mockDeviceKeyAdminService);
Brian Stankeb9170d92016-02-19 14:18:42 -0500427 }
428
429 /**
430 * Tests that a DELETE of a non-existent device key throws an exception.
431 */
432 @Test
433 public void testDeleteNonExistentDeviceKey() {
434 expect(mockDeviceKeyService.getDeviceKey(anyObject()))
435 .andReturn(null)
436 .anyTimes();
437
438 expectLastCall();
439
440 replay(mockDeviceKeyService);
441 replay(mockDeviceKeyAdminService);
442
Jian Li9d616492016-03-09 10:52:49 -0800443 WebTarget wt = target();
Brian Stankeb9170d92016-02-19 14:18:42 -0500444
445 try {
Jian Li9d616492016-03-09 10:52:49 -0800446 wt.path("keys/" + "NON_EXISTENT_DEVICE_KEY").request()
Brian Stankeb9170d92016-02-19 14:18:42 -0500447 .delete(String.class);
448 fail("Delete of a non-existent device key did not throw an exception");
Jian Li9d616492016-03-09 10:52:49 -0800449 } catch (NotFoundException ex) {
450 assertThat(ex.getMessage(), containsString("HTTP 404 Not Found"));
Brian Stankeb9170d92016-02-19 14:18:42 -0500451 }
Brian Stankeb8ff6412016-02-25 14:16:19 -0500452
453 verify(mockDeviceKeyService);
454 verify(mockDeviceKeyAdminService);
Brian Stankeb9170d92016-02-19 14:18:42 -0500455 }
Jian Li0ad0c552019-12-16 22:26:21 +0900456}