Project

General

Profile

1 8452 slaughter
package edu.ucsb.nceas.metacat.dataone;
2
3
/**
4
 *  '$RCSfile$'
5
 *    Purpose: A Class for upgrading the database to version 1.5
6
 *  Copyright: 2000 Regents of the University of California and the
7
 *             National Center for Ecological Analysis and Synthesis
8
 *    Authors: Peter Slaughter
9
 *
10
 *   '$Author: slaughter $'
11
 *     '$Date:$'
12
 * '$Revision:$'
13
 *
14
 * This program is free software; you can redistribute it and/or modify
15
 * it under the terms of the GNU General Public License as published by
16
 * the Free Software Foundation; either version 2 of the License, or
17
 * (at your option) any later version.
18
 *
19
 * This program is distributed in the hope that it will be useful,
20
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
 * GNU General Public License for more details.
23
 *
24
 * You should have received a copy of the GNU General Public License
25
 * along with this program; if not, write to the Free Software
26
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
27
 */
28
29
import java.math.BigInteger;
30
import java.sql.SQLException;
31
import java.util.ArrayList;
32
import java.util.Arrays;
33
import java.util.Date;
34
import java.util.HashMap;
35
import java.util.HashSet;
36 8491 slaughter
import java.util.Iterator;
37 8452 slaughter
import java.util.List;
38
import java.util.Map;
39
import java.util.Set;
40
41
import org.apache.log4j.Logger;
42
import org.dataone.client.CNode;
43
import org.dataone.client.D1Client;
44
import org.dataone.service.exceptions.InvalidRequest;
45
import org.dataone.service.exceptions.InvalidToken;
46
import org.dataone.service.exceptions.NotAuthorized;
47
import org.dataone.service.exceptions.NotFound;
48
import org.dataone.service.exceptions.NotImplemented;
49
import org.dataone.service.exceptions.ServiceFailure;
50
import org.dataone.service.exceptions.VersionMismatch;
51
import org.dataone.service.types.v1.AccessPolicy;
52
import org.dataone.service.types.v1.Identifier;
53
import org.dataone.service.types.v1.ObjectFormatIdentifier;
54
import org.dataone.service.types.v1.ObjectInfo;
55
import org.dataone.service.types.v1.ObjectList;
56
import org.dataone.service.types.v1.Permission;
57
import org.dataone.service.types.v1.Session;
58
import org.dataone.service.types.v1.Subject;
59
import org.dataone.service.types.v1.SystemMetadata;
60
61
import edu.ucsb.nceas.metacat.AccessionNumberException;
62
import edu.ucsb.nceas.metacat.IdentifierManager;
63
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
64 8491 slaughter
import edu.ucsb.nceas.metacat.dataone.D1NodeService;
65 8452 slaughter
import edu.ucsb.nceas.metacat.properties.PropertyService;
66
import edu.ucsb.nceas.metacat.shared.ServiceException;
67
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
68
import edu.ucsb.nceas.utilities.SortedProperties;
69
70
public class SyncAccessPolicy {
71
72
	private static Logger logMetacat = Logger.getLogger(SyncAccessPolicy.class);
73 8491 slaughter
74
	protected static int MAXIMUM_DB_RECORD_COUNT;
75 8452 slaughter
76 8491 slaughter
	static {
77
		try {
78
			MAXIMUM_DB_RECORD_COUNT = Integer.valueOf(PropertyService
79
					.getProperty("database.webResultsetSize"));
80
		} catch (Exception e) {
81
			logMetacat.warn("Could not set MAXIMUM_DB_RECORD_COUNT", e);
82
		}
83
	}
84
85 8452 slaughter
	/**
86
	 * Synchronize access policy (from system metadata) of d1 member node with
87
	 * the corresponding controlling node.
88
	 *
89
	 * @param objList
90
	 *            list of d1 objects to be synced
91
	 * @return syncedIds a list of pids that were synced with the CN
92
	 * @throws ServiceFailure
93
	 * @throws InvalidToken
94
	 * @throws NotAuthorized
95
	 * @throws NotFound
96
	 * @throws NotImplemented
97
	 * @throws McdbDocNotFoundException
98
	 * @throws InvalidRequest
99
	 * @throws VersionMismatch
100
	 * @throws SQLException
101
	 * @throws AccessionNumberException
102
	 * @throws NumberFormatException
103
	 */
104
	public List<Identifier> sync(ObjectList objList) throws ServiceFailure,
105
			InvalidToken, NotAuthorized, NotFound, NotImplemented,
106
			McdbDocNotFoundException, InvalidRequest, VersionMismatch,
107
			NumberFormatException, AccessionNumberException, SQLException {
108
109
		AccessPolicy cnAccessPolicy = null;
110
		String guid = null;
111
		AccessPolicy mnAccessPolicy = null;
112
		Identifier pid = null;
113
		ObjectInfo objInfo = null;
114
		pid = new Identifier();
115
		Session session = null;
116
		List<Identifier> syncedIds = new ArrayList<Identifier>();
117
		SystemMetadata cnSysMeta = null;
118
		SystemMetadata mnSysMeta = null;
119
120
		CNode cn = D1Client.getCN();
121
122 8491 slaughter
		logMetacat.debug("start: " + objList.getStart() + "count: "
123
				+ objList.getCount());
124
		for (int i = objList.getStart(); i < objList.getCount(); i++) {
125 8452 slaughter
126
			objInfo = objList.getObjectInfo(i);
127
			pid = objInfo.getIdentifier();
128 8491 slaughter
129
			logMetacat.debug("Getting SM for pid: " + pid.getValue() + " i: "
130
					+ i);
131 8452 slaughter
			try {
132
				// Get sm, access policy for requested localId
133
				mnSysMeta = IdentifierManager.getInstance().getSystemMetadata(
134
						pid.getValue());
135
136
			} catch (McdbDocNotFoundException e) {
137
				logMetacat.error("Error syncing access policy of pid: "
138
						+ pid.getValue() + " pid not found: " + e.getMessage());
139
			} catch (Exception e) {
140
				logMetacat.error("Error syncing access policy of pid: "
141
						+ pid.getValue() + e.getMessage());
142
			}
143
144 8491 slaughter
			logMetacat
145
					.debug("Getting access policy for pid: " + pid.getValue());
146
147 8452 slaughter
			mnAccessPolicy = mnSysMeta.getAccessPolicy();
148 8491 slaughter
			// logMetacat.debug("pid: " +
149 8452 slaughter
			// mnSysMeta.getIdentifier().toString());
150
151
			// Get sm, access policy for requested pid from the CN
152
153
			try {
154
				cnSysMeta = cn.getSystemMetadata(pid);
155
			} catch (Exception e) {
156
				logMetacat.error("Error getting system metadata for pid: "
157
						+ pid.getValue() + " from cn: " + e.getMessage());
158
			}
159 8491 slaughter
			logMetacat.debug("Getting access policy from CN for pid: "
160
					+ pid.getValue());
161
162 8452 slaughter
			cnAccessPolicy = cnSysMeta.getAccessPolicy();
163 8491 slaughter
			logMetacat.debug("Diffing access policies (MN,CN) for pid: "
164
					+ pid.getValue());
165 8452 slaughter
166 8491 slaughter
			// Compare access policies of MN and CN, and update if different.
167 8452 slaughter
			if (!isEqual(mnAccessPolicy, cnAccessPolicy)) {
168
				try {
169
					BigInteger serialVersion = cnSysMeta.getSerialVersion();
170 8491 slaughter
					logMetacat.debug("Setting access policy from CN for pid: "
171
							+ pid.getValue() + "serial version: "
172
							+ serialVersion.toString());
173
					cn.setAccessPolicy(session, pid, mnAccessPolicy,
174 8452 slaughter
							serialVersion.longValue());
175 8491 slaughter
					logMetacat.debug("Successfully set access policy");
176
					// Add this pid to the list of pids that were successfully
177
					// synced
178 8452 slaughter
					syncedIds.add(pid);
179
				} catch (Exception e) {
180
					logMetacat.error("Error setting access policy of pid: "
181
							+ pid.getValue() + " with cn: " + e.getMessage());
182
				}
183
184
			}
185 8491 slaughter
			logMetacat.debug("Done with pid: " + pid.getValue());
186 8452 slaughter
		}
187
188
		return syncedIds;
189
	}
190
191
	/**
192
	 * Convenience function that accepts a list of guids to sync
193
	 *
194
	 * @param guidsToSync
195
	 *            list of guids to have access policy synced for
196
	 * @return syncedPids - list of pids that were actually synced with the CN
197
	 * @throws NumberFormatException
198
	 * @throws ServiceFailure
199
	 * @throws InvalidToken
200
	 * @throws NotAuthorized
201
	 * @throws NotFound
202
	 * @throws NotImplemented
203
	 * @throws McdbDocNotFoundException
204
	 * @throws InvalidRequest
205
	 * @throws VersionMismatch
206
	 * @throws AccessionNumberException
207
	 * @throws SQLException
208
	 */
209
	public List<Identifier> sync(List<String> guidsToSync)
210
			throws NumberFormatException, ServiceFailure, InvalidToken,
211
			NotAuthorized, NotFound, NotImplemented, McdbDocNotFoundException,
212
			InvalidRequest, VersionMismatch, AccessionNumberException,
213
			SQLException {
214
		List<Identifier> syncedPids = null;
215
		ObjectList objList = new ObjectList();
216
		SystemMetadata sm = new SystemMetadata();
217
218
		int start = 0;
219
		int count = guidsToSync.size();
220
		int total = count;
221
222
		objList.setStart(start);
223
		objList.setCount(count);
224
		objList.setTotal(total);
225
226
		// Convert the guids to d1 objects, as this is what
227
		// IdentifierManager.getInstance().querySystemMetadata returns in
228
		// syncAll, and
229
		// what sync(ObjectList...) expects
230
		for (String guid : guidsToSync) {
231
232
			try {
233
				sm = IdentifierManager.getInstance().getSystemMetadata(guid);
234
			} catch (Exception e) {
235
				logMetacat.error("Error syncing access policy of pid: " + guid
236
						+ e.getMessage());
237
			}
238
239
			logMetacat.debug("Got sm for guid: " + guid);
240
			ObjectInfo oi = new ObjectInfo();
241
242
			Identifier id = new Identifier();
243
			id.setValue(guid);
244
			oi.setIdentifier(id);
245
246
			oi.setDateSysMetadataModified(sm.getDateSysMetadataModified());
247
248
			oi.setChecksum(sm.getChecksum());
249
			oi.setFormatId(sm.getFormatId());
250
			oi.setSize(sm.getSize());
251
			objList.addObjectInfo(oi);
252
		}
253
254
		syncedPids = sync(objList);
255
		return syncedPids;
256
	}
257
258
	public List<Identifier> syncAll() throws ServiceFailure, InvalidToken,
259
			NotAuthorized, NotFound, NotImplemented, McdbDocNotFoundException,
260
			InvalidRequest, VersionMismatch, NumberFormatException,
261
			AccessionNumberException, SQLException, PropertyNotFoundException,
262
			ServiceException {
263
264
		// For the following query parameters - null indicates that the query
265
		// will not be
266
		// constrained by the parameter.
267
		Date startTime = null;
268
		Date endTime = null;
269
		ObjectFormatIdentifier objectFormatId = null;
270
		Boolean replicaStatus = false; // return only pids for which this mn is
271
										// authoritative
272 8491 slaughter
		Integer start = 0;
273
		Integer count = MAXIMUM_DB_RECORD_COUNT;
274 8452 slaughter
275
		ObjectList objsToSync = IdentifierManager.getInstance()
276
				.querySystemMetadata(startTime, endTime, objectFormatId,
277
						replicaStatus, start, count);
278
279
		List<Identifier> syncedIds = sync(objsToSync);
280
281
		return syncedIds;
282
	}
283
284
	/**
285
	 * Compare two d1 system metadata access policies for equivalence.
286
	 *
287
	 * @param ap1
288
	 *            - first access policy in the comparison
289
	 * @param ap2
290
	 *            - second access policy in the comparison
291 8491 slaughter
	 * @return	boolean - true if access policies are equivalent
292 8452 slaughter
	 */
293
	private boolean isEqual(AccessPolicy ap1, AccessPolicy ap2) {
294
295 8491 slaughter
		// Access Policy -> Access Rule -> (Subject, Permission)
296
		// i.e. Subject="slaughter", Permission="read,write,changePermission"
297 8452 slaughter
		// Get the list of access rules for each access policy
298
		List<org.dataone.service.types.v1.AccessRule> allowList1 = ap1
299
				.getAllowList();
300
		List<org.dataone.service.types.v1.AccessRule> allowList2 = ap2
301
				.getAllowList();
302
303
		HashMap<Subject, Set<Permission>> userPerms1 = new HashMap<Subject, Set<Permission>>();
304
		HashMap<Subject, Set<Permission>> userPerms2 = new HashMap<Subject, Set<Permission>>();
305
306
		// Load the permissions from the access rules into a hash of sets, i.e.,
307
		// so that we end up with this:
308 8491 slaughter
		// hash key: set of permissions, i.e.
309 8452 slaughter
		// ----------------------------
310
		// user1: read, write
311
		// user2: read
312
		// user3: read, write, change permissions
313
		// With the permissions in this structure, they can be easily compared
314
		Set<Permission> perms = null;
315
		// Process first access policy
316 8491 slaughter
		// Loop through access rules of this allowList
317 8452 slaughter
		for (org.dataone.service.types.v1.AccessRule accessRule : allowList1) {
318
			for (Subject s : accessRule.getSubjectList()) {
319
				if (userPerms1.containsKey(s)) {
320
					perms = userPerms1.get(s);
321
				} else {
322
					perms = new HashSet<Permission>();
323
				}
324
				for (Permission p : accessRule.getPermissionList()) {
325
					perms.add(p);
326
				}
327 8491 slaughter
				userPerms1.put(s, perms);
328 8452 slaughter
			}
329
		}
330
331
		// Process second access policy
332
		for (org.dataone.service.types.v1.AccessRule accessRule : allowList2) {
333
			for (Subject s : accessRule.getSubjectList()) {
334
				if (userPerms2.containsKey(s)) {
335
					perms = userPerms2.get(s);
336
				} else {
337
					perms = new HashSet<Permission>();
338
				}
339
				for (Permission p : accessRule.getPermissionList()) {
340
					perms.add(p);
341
				}
342 8491 slaughter
				userPerms2.put(s, perms);
343 8452 slaughter
			}
344
		}
345
346
		// Now perform the comparison. This test assumes that the mn perms are
347
		// more
348
		// complete than the cn perms.
349 8491 slaughter
		logMetacat.debug("Performing comparison of access policies");
350 8452 slaughter
		for (Map.Entry<Subject, Set<Permission>> entry : userPerms1.entrySet()) {
351
			// User name
352
			Subject s1 = entry.getKey();
353
			// Perms that the user holds
354 8491 slaughter
			Set<Permission> p1 = entry.getValue();
355
			logMetacat
356
					.debug("Checking access policy of user: " + s1.getValue());
357 8452 slaughter
358 8491 slaughter
			// Does this user exist in both access policies?
359 8452 slaughter
			if (userPerms2.containsKey(s1)) {
360 8491 slaughter
				if (!p1.equals(userPerms2.get(s1))) {
361
					logMetacat.debug("User access policies not equal");
362 8452 slaughter
					return false;
363
				}
364
			} else {
365 8491 slaughter
				logMetacat.debug("User access policy not found on CN");
366 8452 slaughter
				return false;
367
			}
368
		}
369
370
		// All comparisons have been passed, so the two access policies are
371
		// equivalent
372 8491 slaughter
		logMetacat.debug("Access policies are the same");
373 8452 slaughter
		return true;
374
	}
375
376
	public static void main(String[] args) throws Exception {
377
378
		ArrayList<String> guids = null;
379
		SyncAccessPolicy syncAP = new SyncAccessPolicy();
380
381
		// set up the properties based on the test/deployed configuration of the
382
		// workspace
383
		SortedProperties testProperties = new SortedProperties(
384
				"test/test.properties");
385
		testProperties.load();
386
		String metacatContextDir = testProperties
387
				.getProperty("metacat.contextDir");
388
		PropertyService.getInstance(metacatContextDir + "/WEB-INF");
389
390
		if (args.length > 0) {
391
			try {
392
				guids = new ArrayList<String>(Arrays.asList(args[0]
393
						.split("\\s*,\\s*")));
394 8491 slaughter
				logMetacat.debug("Trying to syncing access policy for pids: "
395 8452 slaughter
						+ args[0]);
396
				syncAP.sync(guids);
397
			} catch (Exception e) {
398
				System.err.println("Error syncing pids: " + args[0]
399
						+ " Exception " + e.getMessage());
400
				System.exit(1);
401
			}
402
		}
403
	}
404
}