Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *  Copyright: 2014 Regents of the University of California and the
4
 *              National Center for Ecological Analysis and Synthesis
5
 *  Purpose: To test the Access Controls in metacat by JUnit
6
 *
7
 *   '$Author: slaughter $'
8
 *     '$Date: $'
9
 * '$Revision:$'
10
 *
11
 * This program is free software; you can redistribute it and/or modify
12
 * it under the terms of the GNU General Public License as published by
13
 * the Free Software Foundation; either version 2 of the License, or
14
 * (at your option) any later version.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
 * GNU General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU General Public License
22
 * along with this program; if not, write to the Free Software
23
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24
 */
25

    
26
package edu.ucsb.nceas.metacat.admin.upgrade.dataone;
27

    
28
import java.io.ByteArrayInputStream;
29
import java.io.InputStream;
30
import java.util.ArrayList;
31
import java.util.List;
32
import java.util.Vector;
33

    
34
import junit.framework.Test;
35
import junit.framework.TestSuite;
36

    
37
import org.dataone.client.v2.CNode;
38
import org.dataone.client.v2.itk.D1Client;
39
import org.dataone.service.exceptions.ServiceFailure;
40
import org.dataone.service.types.v1.AccessPolicy;
41
import org.dataone.service.types.v1.AccessRule;
42
import org.dataone.service.types.v1.Identifier;
43
import org.dataone.service.types.v1.Permission;
44
import org.dataone.service.types.v1.Session;
45
import org.dataone.service.types.v1.Subject;
46
import org.dataone.service.types.v2.SystemMetadata;
47
import org.junit.Before;
48

    
49
import edu.ucsb.nceas.metacat.IdentifierManager;
50
import edu.ucsb.nceas.metacat.client.MetacatAuthException;
51
import edu.ucsb.nceas.metacat.client.MetacatInaccessibleException;
52
import edu.ucsb.nceas.metacat.dataone.MNodeService;
53
import edu.ucsb.nceas.metacat.dataone.D1NodeServiceTest;
54
import edu.ucsb.nceas.metacat.dataone.SyncAccessPolicy;
55
import edu.ucsb.nceas.metacat.util.DocumentUtil;
56
import edu.ucsb.nceas.utilities.access.AccessControlInterface;
57

    
58
/**
59
 * A JUnit test for testing syncing access policies between MN -> CN after local
60
 * update by metacat services
61
 */
62
public class SyncAccessPolicyTest extends D1NodeServiceTest {
63

    
64
	/**
65
	 * Constructor to build the test
66
	 * 
67
	 * @param name
68
	 *            the name of the test method
69
	 */
70
	public SyncAccessPolicyTest(String name) {
71
		super(name);
72
	}
73

    
74
	/**
75
	 * Establish a testing framework by initializing appropriate objects
76
	 */
77
	@Before
78
	public void setUp() throws Exception {
79
		metacatConnectionNeeded = true;
80
		super.setUp();
81

    
82
	}
83

    
84
	/**
85
	 * Release any objects after tests are complete
86
	 */
87
	public void tearDown() {
88
	}
89

    
90
	/**
91
	 * Create a suite of tests to be run together
92
	 */
93
	public static Test suite() {
94
		TestSuite suite = new TestSuite();
95
		suite.addTest(new SyncAccessPolicyTest("initialize"));
96
		suite.addTest(new SyncAccessPolicyTest("testIsEqual"));
97
		suite.addTest(new SyncAccessPolicyTest("testSyncAccessPolicy"));
98
		suite.addTest(new SyncAccessPolicyTest("testSyncEML201OnlineDataAccessPolicy"));
99

    
100
		return suite;
101
	}
102

    
103
	/**
104
	 * Run an initial test that always passes to check that the test harness is
105
	 * working.
106
	 */
107
	public void initialize() {
108
		assertTrue(1 == 1);
109
	}
110

    
111
	/**
112
	 * constructs a "fake" session with a test subject
113
	 * 
114
	 * @return
115
	 */
116
	@Override
117
	public Session getTestSession() throws Exception {
118
		Session session = new Session();
119
		Subject subject = new Subject();
120
		subject.setValue(anotheruser);
121
		session.setSubject(subject);
122
		return session;
123
	}
124

    
125
	/**
126
	 * Test object creation
127
	 */
128
	public Identifier createTestPid() {
129
		printTestHeader("testCreate");
130
		Identifier pid = null;
131
		try {
132
			Session session = getTestSession();
133
			Identifier guid = new Identifier();
134
			guid.setValue("testSyncAP." + System.currentTimeMillis());
135
			InputStream object = new ByteArrayInputStream(
136
					"test".getBytes("UTF-8"));
137
			SystemMetadata sysmeta = createSystemMetadata(guid,
138
					session.getSubject(), object);
139

    
140
			pid = MNodeService.getInstance(request).create(session, guid,
141
					object, sysmeta);
142
		} catch (Exception e) {
143
			e.printStackTrace();
144
			debug("Error creating pid: " + e.getMessage());
145
			fail();
146
		}
147
		return pid;
148
	}
149

    
150
	/**
151
	 * This test checks that access policy of a pid is synced with the CN after
152
	 * the docid is manually changed using the Metacat api (setaccess).
153
	 */
154
	public void testSyncAccessPolicy() {
155

    
156
		AccessPolicy cnAccessPolicy = null;
157
		AccessPolicy mnAccessPolicy = null;
158
		SystemMetadata cnSysMeta = null;
159
		SystemMetadata mnSysMeta = null;
160
		CNode cn = null;
161

    
162
		String response = null;
163
		debug("\nStarting sync access policy test");
164

    
165
		String localId = null;
166
		try {
167
			Identifier pid = null;
168
			pid = createTestPid();
169
			assertNotNull(pid);
170

    
171
			debug("Inserted new document: " + pid.getValue());
172
			boolean found = false;
173
			int attempts = 0;
174

    
175
			/*
176
			 * Determine the CN for the current host. This test must be run on a
177
			 * registered MN.
178
			 */
179
			try {
180
				cn = D1Client.getCN();
181
			} catch (ServiceFailure sf) {
182
				debug("Unable to get Coordinating node name for this MN");
183
				fail();
184
			}
185

    
186
			// We have to wait until the CN has harvested the new document,
187
			// otherwise we
188
			// will just get an error of "pid not found" when we request the CN
189
			// to
190
			// sync the access policies
191
			// (which is triggered by the Metacat setaccess call).
192

    
193
			debug("Checking for new pid on CN...");
194
			found = false;
195
			for (int i = 0; i < 6; i++) {
196
				attempts = i;
197
				Thread.sleep(1000 * 60);
198
				// Get the test document from the CN
199
				// Get sm, access policy for requested pid from the CN
200
				// Get sm, access policy for requested pid from the CN
201
				try {
202
					cnSysMeta = cn.getSystemMetadata(null, pid);
203
					debug("Got SM from CN");
204

    
205
				} catch (Exception e) {
206
					debug("Error getting system metadata for pid: "
207
							+ pid.getValue() + " from cn: " + e.getMessage());
208
					debug("Will request pid from CN again...");
209
					continue;
210
				}
211

    
212
				found = true;
213
				debug("Document " + pid.getValue() + " has been read from CN.");
214
				break;
215
			}
216

    
217
			if (!found) {
218
				fail("Error, cannot read system metadata for pid: " + pid
219
						+ " after " + attempts + " attempts");
220
			}
221
			
222
			/* Read SM from MN */
223
			try {
224
				mnSysMeta = MNodeService.getInstance(request)
225
						.getSystemMetadata(null, pid);
226
				debug("Got SM from MN");
227
			} catch (Exception e) {
228
				fail("Error getting system metadata for new pid: "
229
						+ pid.getValue() + ". Message: " + e.getMessage());
230
			}
231
			
232
			SyncAccessPolicy syncAP = new SyncAccessPolicy();
233

    
234
			// access policies should match after initial harvest
235
			assertTrue(syncAP.isEqual(cnSysMeta.getAccessPolicy(), mnSysMeta.getAccessPolicy()));
236

    
237
			// find the local id for this pid
238
			try {
239
				localId = IdentifierManager.getInstance().getLocalId(
240
						pid.getValue());
241
			} catch (Exception e) {
242
				fail("Unable to retrieve localId for pid: " + pid.getValue());
243
			}
244

    
245
			debug("Updating permissions of localId: " + localId + ", guid: "
246
					+ pid.getValue() + ", username: " + username
247
					+ " read, allow, allowFirst");
248

    
249
			response = m.login(anotheruser, anotherpassword);
250
			debug("Logging in as user: " + anotheruser);
251
			debug("Adding access for user: " + username);
252

    
253
			/* Update the docid access policy with Metacat api */
254
			try {
255
				response = m.setAccess(localId, username,
256
						AccessControlInterface.READSTRING,
257
						AccessControlInterface.ALLOW,
258
						AccessControlInterface.ALLOWFIRST);
259
			} catch (Exception e) {
260
				debug("Response from setaccess: " + response);
261
				debug("Error setting access for localId: " + e.getMessage());
262
				fail();
263
			}
264

    
265
			debug("Response from setaccess: " + response);
266
			debug("Retrieving updated docid from CN to check if perms were updated...");
267

    
268
			/* Reread SM from MN after updating access */
269
			try {
270
				mnSysMeta = MNodeService.getInstance(request)
271
						.getSystemMetadata(null, pid);
272
				debug("Got SM from MN");
273
			} catch (Exception e) {
274
				fail("Error getting system metadata for new pid: "
275
						+ pid.getValue() + ". Message: " + e.getMessage());
276
			}
277

    
278
			/* Check if the access policy was updated on the MN */
279
			mnAccessPolicy = mnSysMeta.getAccessPolicy();
280
			found = false;
281
			List<Subject> subjectList = null;
282
			List<AccessRule> accessRules = mnAccessPolicy.getAllowList();
283
			debug("Checking that access policy was added to MN");
284

    
285
			try {
286
				debug("Checking " + accessRules.size() + " access rules");
287
				for (AccessRule ar : accessRules) {
288
					subjectList = ar.getSubjectList();
289
					debug("Checking " + ar.sizeSubjectList()
290
							+ " subjects for this access rule");
291
					for (Subject sj : subjectList) {
292
						debug("Checking subject: " + sj.getValue());
293
						if (sj.getValue().equals(username)) {
294
							debug("user " + username + " found");
295
							found = true;
296
						}
297
						debug("MN done with subject: " + sj.getValue());
298
					}
299
				}
300
			} catch (Exception e) {
301
				debug("Error checking access policy: " + e.getMessage());
302
				fail();
303
			}
304

    
305
			if (!found)
306
				debug("user " + username + " not found in access policy");
307

    
308
			// assertTrue(found);
309

    
310
			/* Reread SM from CN */
311
			try {
312
				cnSysMeta = cn.getSystemMetadata(null, pid);
313
				debug("Got SM from CN");
314
			} catch (Exception e) {
315
				fail("Error getting system metadata for pid: " + pid.getValue()
316
						+ " from cn: " + e.getMessage());
317
			}
318

    
319
			debug("Done getting CN SM");
320

    
321
			/* Check if the access policy was updated on the MN */
322
			cnAccessPolicy = cnSysMeta.getAccessPolicy();
323
			found = false;
324
			subjectList = null;
325
			accessRules = cnAccessPolicy.getAllowList();
326
			debug("Checking that access policy was added to CN");
327
			debug("Checking " + accessRules.size() + " access rules");
328

    
329
			for (AccessRule ar : accessRules) {
330
				subjectList = ar.getSubjectList();
331
				debug("Checking " + ar.sizeSubjectList()
332
						+ " subjects for this access rule");
333
				for (Subject sj : subjectList) {
334
					debug("Checking subject: " + sj.getValue());
335
					if (sj.getValue().indexOf(username) >= 0) {
336
						debug("user " + username + " found");
337
						found = true;
338
					}
339
				}
340
			}
341
			if (!found)
342
				debug("user " + username + " not found in access policy");
343
			debug("Done checking access rules for CN");
344

    
345
			assertTrue(found);
346
			debug("Checking privs retrieved from CN");
347
			debug("Getting access policy for pid: " + pid.getValue());
348
			cnAccessPolicy = cnSysMeta.getAccessPolicy();
349
			debug("Diffing access policies (MN,CN) for pid: " + pid.getValue());
350
			debug("Comparing access policies...");
351

    
352
			Boolean apEqual = new Boolean(syncAP.isEqual(mnAccessPolicy,
353
					cnAccessPolicy));
354
			debug("Are access policies equal?: " + apEqual.toString());
355
			assert (apEqual == true);
356
		} catch (Exception e) {
357
			e.printStackTrace();
358
			debug("Error running syncAP test: " + e.getMessage());
359
			fail();
360
		} finally {
361
			if (localId != null)
362
				deleteDocumentId(localId, SUCCESS, true);
363
			try {
364
				m.logout();
365
			} catch (Exception e) {
366
				debug("Error logging out");
367
			}
368
		}
369

    
370
		debug("Done running testSyncAccessPolicy");
371
	}
372

    
373
	/*
374
	 * This test checks that the access policy of an online data object
375
	 * (described in EML 2.0.1 <online> section) is properly synced with the CN
376
	 * when the access policy is changed by modifying the access policy in the
377
	 * <additionalMetadata> section of the parent document.
378
	 */
379
	public void testSyncEML201OnlineDataAccessPolicy() {
380
		String newdocid = null;
381
		String onlineDocid = null;
382
		String onlinetestdatafile2 = "test/onlineDataFile2";
383
		SystemMetadata mnSysMeta = null;
384
		SystemMetadata cnSysMeta = null;
385
		CNode cn = null;
386

    
387
		try {
388
			debug("\nRunning: testSyncEML201OnlineDataAccessPolicy");
389
			String emlVersion = EML2_0_1;
390
			debug("logging in as: username=" + username + " password="
391
					+ password);
392
			m.login(username, password);
393

    
394
			/* Create a data object */
395
			onlineDocid = generateDocumentId();
396
			uploadDocumentId(onlineDocid + ".1", onlinetestdatafile2, SUCCESS,
397
					false);
398
			debug("Inserted new data object with localid: " + onlineDocid
399
					+ ".1");
400
			String accessRule1 = generateOneAccessRule("public", true, true,
401
					false, false, false);
402

    
403
			Vector<String> accessRules = new Vector<String>();
404
			accessRules.add(accessRule1);
405
			String accessDoc = getAccessBlock(accessRules, ALLOWFIRST);
406
			testdocument = getTestEmlDoc("Testing insert", emlVersion,
407
					testEmlInlineBlock1, null, "ecogrid://knb/" + onlineDocid
408
							+ ".1", null, accessDoc, null, null, null, null);
409

    
410
			/* Insert the eml document that describes the data object */
411
			newdocid = generateDocumentId();
412
			insertDocumentId(newdocid + ".1", testdocument, SUCCESS, false);
413

    
414
			debug("Inserted document with localId: " + newdocid + ".1");
415
			/*
416
			 * Determine the D1 pid for the data object so that we can query the
417
			 * CN for it.
418
			 */
419
			String docidWithoutRev = DocumentUtil.getSmartDocId(onlineDocid
420
					+ ".1");
421
			int rev = IdentifierManager.getInstance().getLatestRevForLocalId(
422
					onlineDocid + ".1");
423
			String guid = IdentifierManager.getInstance().getGUID(
424
					docidWithoutRev, rev);
425

    
426
			Identifier pid = new Identifier();
427
			pid.setValue(guid);
428
			try {
429
				cn = D1Client.getCN();
430
			} catch (ServiceFailure sf) {
431
				debug("Unable to get Coordinating node name for this MN");
432
				fail();
433
			}
434

    
435
			/*
436
			 * Wait for the cn to harvest metadata for the data object. We are
437
			 * testing that an update of access rules in the parent eml document
438
			 * for the enclosed online data object will trigger metacat (via
439
			 * SyncAccessPolicy) to update the access policy on the CN, so the
440
			 * data object has to be present on the CN before we continue.
441
			 */
442
			debug("Checking for new data object pid " + pid.getValue()
443
					+ " on CN...");
444
			boolean found = false;
445
			int attempts = 0;
446
			for (int i = 0; i < 6; i++) {
447
				attempts = i;
448
				Thread.sleep(1000 * 60);
449
				// Get sm for data object pid from the CN
450
				try {
451
					cnSysMeta = cn.getSystemMetadata(null, pid);
452
				} catch (Exception e) {
453
					debug("Error getting system metadata for pid: "
454
							+ pid.getValue() + " from cn: " + e.getMessage());
455
					debug("Will request pid from CN again...");
456
					continue;
457
				}
458

    
459
				found = true;
460
				debug("Data object pid " + pid.getValue()
461
						+ " has been read from CN.");
462
				break;
463
			}
464

    
465
			if (!found) {
466
				fail("Error, cannot read system metadata for pid: " + pid
467
						+ " after " + attempts + " attempts");
468
			}
469

    
470
			// update the document access control for online data
471
			accessRule1 = generateOneAccessRule("public", true, true, false,
472
					false, false);
473
			String accessRule2 = generateOneAccessRule(anotheruser, true, true,
474
					false, false, false);
475

    
476
			accessRules = new Vector<String>();
477
			accessRules.add(accessRule1);
478
			accessRules.add(accessRule2);
479
			String accessData = getAccessBlock(accessRules, ALLOWFIRST);
480
			testdocument = getTestEmlDoc("Testing insert", emlVersion,
481
					testEmlInlineBlock1, null, "ecogrid://knb/" + onlineDocid
482
							+ ".1", null, accessDoc, null, null, accessData,
483
					null);
484

    
485
			/*
486
			 * Updating the parent document should trigger metacat to update the
487
			 * access policy on the CN (via EML 201 parser and
488
			 * SyncAccessPolicy).
489
			 */
490
			updateDocumentId(newdocid + ".2", testdocument, SUCCESS, false);
491

    
492
			/* Get MN access policy for data object */
493
			try {
494
				// Get sm, access policy for requested localId
495
				mnSysMeta = IdentifierManager.getInstance().getSystemMetadata(
496
						pid.getValue());
497
			} catch (Exception e) {
498
				debug("Error getting system metadata for new pid: "
499
						+ pid.getValue() + ". Message: " + e.getMessage());
500
				fail();
501
			}
502
			/* Get CN access policy for data object */
503
			try {
504
				cnSysMeta = cn.getSystemMetadata(null, pid);
505
			} catch (Exception e) {
506
				debug("Error getting system metadata for pid: "
507
						+ pid.getValue() + " from cn: " + e.getMessage());
508
				fail();
509
			}
510

    
511
			/* Compare MN and CN access policies */
512
			debug("Getting CN,MN access policy for pid: " + pid.getValue());
513
			AccessPolicy mnAccessPolicy = mnSysMeta.getAccessPolicy();
514
			AccessPolicy cnAccessPolicy = cnSysMeta.getAccessPolicy();
515
			debug("Diffing access policies (MN,CN) for pid: " + pid.getValue());
516

    
517
			SyncAccessPolicy syncAP = new SyncAccessPolicy();
518
			debug("Comparing access policies...");
519

    
520
			Boolean apEqual = new Boolean(syncAP.isEqual(mnAccessPolicy,
521
					cnAccessPolicy));
522

    
523
			debug("Are access policies equal?: " + apEqual.toString());
524
			assert (apEqual == true);
525

    
526
		} catch (MetacatAuthException mae) {
527
			fail("Authorization failed:\n" + mae.getMessage());
528
		} catch (MetacatInaccessibleException mie) {
529
			fail("Metacat Inaccessible:\n" + mie.getMessage());
530
		} catch (Exception e) {
531
			fail("General exception:\n" + e.getMessage());
532
		} finally {
533
			/* Delete the document */
534
			deleteDocumentId(newdocid + ".2", SUCCESS, true);
535
			deleteDocumentId(onlineDocid + ".1", SUCCESS, true);
536

    
537
			// logout
538
			debug("logging out");
539
			try {
540
				m.logout();
541
			} catch (Exception e) {
542
				debug("Error logging out");
543
			}
544
		}
545

    
546
		debug("Done running testSyncEML201OnlineDataAccessPolicy");
547
	}
548

    
549
	public void testIsEqual() {
550
		AccessPolicy ap1 = new AccessPolicy();
551
		AccessRule ar1 = new AccessRule();
552
		ar1.addPermission(Permission.READ);
553
		Subject subject1 = new Subject();
554
		subject1.setValue(username);
555
		ar1.addSubject(subject1);
556
		ap1.addAllow(ar1);
557

    
558
		AccessPolicy ap2 = new AccessPolicy();
559
		AccessRule ar2 = new AccessRule();
560
		ar2.addPermission(Permission.READ);
561
		Subject subject2 = new Subject();
562
		subject2.setValue(username);
563
		ar2.addSubject(subject2);
564
		ap2.addAllow(ar2);
565

    
566
		boolean isEqual = false;
567
		SyncAccessPolicy syncAP = new SyncAccessPolicy();
568

    
569
		// try something that should be true
570
		isEqual = syncAP.isEqual(ap1, ap2);
571
		assertTrue(isEqual);
572

    
573
		// try something that makes them not equal
574
		Subject subject3 = new Subject();
575
		subject3.setValue(anotheruser);
576
		ar2.addSubject(subject3);
577

    
578
		isEqual = syncAP.isEqual(ap1, ap2);
579
		assertFalse(isEqual);
580

    
581
		isEqual = syncAP.isEqual(ap1, null);
582
		assertFalse(isEqual);
583
	}
584
}
(2-2/2)