/** * This work was created by participants in the DataONE project, and is * jointly copyrighted by participating institutions in DataONE. For * more information on DataONE, see our web site at http://dataone.org. * * Copyright ${year} * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.dataone.integration.it; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.util.Iterator; import org.apache.commons.io.IOUtils; import org.dataone.client.v1.CNode; import org.dataone.client.v1.types.D1TypeBuilder; import org.dataone.configuration.Settings; import org.dataone.integration.APITestUtils; import org.dataone.integration.ExampleUtilities; import org.dataone.integration.it.testImplementations.AbstractAuthITDataoneIsAuthzd; import org.dataone.service.exceptions.BaseException; import org.dataone.service.exceptions.NotAuthorized; import org.dataone.service.exceptions.NotFound; import org.dataone.service.types.v1.AccessPolicy; import org.dataone.service.types.v1.Identifier; import org.dataone.service.types.v1.Node; import org.dataone.service.types.v1.Permission; import org.dataone.service.types.v1.Subject; import org.dataone.service.types.v1.SystemMetadata; import org.dataone.service.types.v1.util.AccessUtil; import org.dataone.service.util.TypeMarshaller; import org.junit.Test; /** * Test the DataONE Java client methods that focus on CN services. * @author Rob Nahf */ public class CNodeTier2Auth_metacat_IT extends AbstractAuthITDataoneIsAuthzd { private static String currentUrl; @Override protected Iterator getNodeIterator() { return getCoordinatingNodeIterator(); } @Override protected CNode instantiateD1Node(String baseUrl) { return new CNode(baseUrl); } /** * A basic test Tests the dataONE service API isAuthorized() method, checking for Read * permission on the first object returned from the Tier1 listObjects() method. * Anything other than the boolean true is considered a test failure. */ @Test public void testIsAuthorized() { setupClientSubject("testPerson"); Iterator it = getCoordinatingNodeIterator(); while (it.hasNext()) { currentUrl = it.next().getBaseURL(); CNode cn = new CNode(currentUrl); currentUrl = cn.getNodeBaseServiceUrl(); printTestHeader("testIsAuthorized() vs. node: " + currentUrl); try { String objectIdentifier = "TierTesting:" + createNodeAbbreviation(cn.getNodeBaseServiceUrl()) + ":Public_READ" + testObjectSeriesSuffix; Identifier pid = procurePublicReadableTestObject(cn,D1TypeBuilder.buildIdentifier(objectIdentifier)); // Identifier pid = procurePublicReadableTestObject(cn); boolean success = cn.isAuthorized(null, pid, Permission.READ); checkTrue(cn.getLatestRequestUrl(),"isAuthorized response should never be false. [Only true or exception].", success); } catch (BaseException e) { handleFail(cn.getLatestRequestUrl(),e.getClass().getSimpleName() + ": " + e.getDetail_code() + ": " + e.getDescription()); } catch(Exception e) { e.printStackTrace(); handleFail(currentUrl,e.getClass().getName() + ": " + e.getMessage()); } } } /** * Creates an object with no AccessPolicy. changes ownership to different * subject, then tests original rightsholder can't access/change, and the * new one can. */ @Test public void testSetRightsHolder() { Subject inheritorSubject = setupClientSubject("testPerson"); Iterator it = getCoordinatingNodeIterator(); while (it.hasNext()) { currentUrl = it.next().getBaseURL(); CNode cn = new CNode(currentUrl); printTestHeader("testSetRightsHolder(...) vs. node: " + currentUrl); try { Subject ownerSubject = setupClientSubject("testRightsHolder"); // create a new identifier for testing, owned by current subject and with null AP Identifier changeableObject = APITestUtils.buildIdentifier( "TierTesting:setRH:" + ExampleUtilities.generateIdentifier()); changeableObject = createTestObject(cn, changeableObject, null); if (changeableObject != null) { SystemMetadata smd = cn.getSystemMetadata(null, changeableObject); Identifier response = cn.setRightsHolder( null, changeableObject, inheritorSubject, smd.getSerialVersion().longValue()); checkTrue(cn.getLatestRequestUrl(),"1. setRightsHolder(...) returns a Identifier object", response != null); try { cn.isAuthorized(null, changeableObject, Permission.CHANGE_PERMISSION); handleFail(cn.getLatestRequestUrl(), "2. isAuthorized to CHANGE as former rightsHolder should fail."); } catch (NotAuthorized e) { ; // expected } setupClientSubject("testPerson"); try { cn.isAuthorized(null, changeableObject, Permission.CHANGE_PERMISSION); } catch (NotAuthorized na) { handleFail(cn.getLatestRequestUrl(),"3. testPerson should now be able to CHANGE the object"); } try { smd = cn.getSystemMetadata(null, changeableObject); checkTrue(cn.getLatestRequestUrl(), "4. testPerson should be the rightsHolder",smd.getRightsHolder().equals(inheritorSubject)); } catch (NotAuthorized na) { handleFail(cn.getLatestRequestUrl(),"5. testPerson should now be able to get the systemmetadata"); } // clean up step to try to put it back under the testRightsHolder subject. cn.setRightsHolder(null, changeableObject, ownerSubject, smd.getSerialVersion().longValue()); } else { handleFail(cn.getLatestRequestUrl(),"could not create object for testing setRightsHolder"); } } catch (IndexOutOfBoundsException e) { handleFail(cn.getLatestRequestUrl(),"No Objects available to test against"); } catch (BaseException e) { handleFail(cn.getLatestRequestUrl(),e.getDescription()); } catch(Exception e) { e.printStackTrace(); handleFail(currentUrl,e.getClass().getName() + ": " + e.getMessage()); } } } /** * Tests the dataONE service API setAccessPolicy method, and requires a * designated object that the "testRightsHolder" can change. * Outline for the test: find the object, clear the AP, try isAuthorized() * with non-owner (should fail), as owner setAccessPolicy(), try isAuthorized() * with non-owner client who should now have access. * * on the first object returned from the Tier1 listObjects() method. * Anything other than the boolean true is considered a test failure. */ @Test public void testSetAccessPolicy() { boolean origObjectCacheSetting = Settings.getConfiguration().getBoolean("D1Client.useLocalCache"); Settings.getConfiguration().setProperty("D1Client.useLocalCache", false); Iterator it = getCoordinatingNodeIterator(); while (it.hasNext()) { currentUrl = it.next().getBaseURL(); CNode cn = new CNode(currentUrl); currentUrl = cn.getNodeBaseServiceUrl(); printTestHeader("testSetAccessPolicy() vs. node: " + currentUrl); try { Subject ownerSubject = setupClientSubject("testRightsHolder"); // need to procure the target test object and verify // that we are the rightsHolder Identifier changeableObject = D1TypeBuilder.buildIdentifier( String.format("TierTesting:%s:setAccess%s", createNodeAbbreviation(cn.getNodeBaseServiceUrl()), testObjectSeriesSuffix)); SystemMetadata smd = null; try { smd = cn.getSystemMetadata(null,changeableObject); if (!smd.getRightsHolder().equals(ownerSubject)) throw new TestIterationEndingException("the test object should be owned by " + "the client subject"); } catch (NotFound e) { changeableObject = createTestObject(cn, changeableObject, null); } log.info("clear the AccessPolicy"); long serialVersion = smd.getSerialVersion().longValue(); boolean success = cn.setAccessPolicy(null, changeableObject, new AccessPolicy(), serialVersion); // ensure blank policy with isAuthorized(), get() Subject readerSubject = setupClientSubject("testPerson"); try { cn.isAuthorized(null, changeableObject, Permission.READ); handleFail(cn.getLatestRequestUrl(),"1. isAuthorized by the reader should fail"); } catch (NotAuthorized na) { // should fail } try { cn.get(null, changeableObject); handleFail(cn.getLatestRequestUrl(),"2. getting the newly created object as a reader should fail"); } catch (NotAuthorized na) { // this is what we want } // log.info("allow read permission for client-who-is-the-object's-RightsHolder"); setupClientSubject("testRightsHolder"); smd = cn.getSystemMetadata(null, changeableObject); serialVersion = smd.getSerialVersion().longValue(); success = cn.setAccessPolicy(null, changeableObject, AccessUtil.createSingleRuleAccessPolicy(new String[] {readerSubject.getValue()}, new Permission[] {Permission.READ}), serialVersion); checkTrue(cn.getLatestRequestUrl(),"3. testRightsHolder should be able to set the access policy",success); smd = cn.getSystemMetadata(null, changeableObject); ByteArrayOutputStream os = new ByteArrayOutputStream(); TypeMarshaller.marshalTypeToOutputStream(smd.getAccessPolicy(), os); log.info(os.toString()); // test for success log.info("trying isAuthorized as testPerson"); setupClientSubject("testPerson"); try { cn.isAuthorized(null, changeableObject, Permission.READ); } catch (NotAuthorized na) { handleFail(cn.getLatestRequestUrl(),"4. testPerson should be authorized to read this pid '" + changeableObject.getValue() + "'"); } log.info("now trying get() as testPerson"); try { cn.get(null, changeableObject); } catch (NotAuthorized na) { handleFail(cn.getLatestRequestUrl(),"5. testPerson should now be able to get the object"); } log.info("now try to get as a known user with no rights to the object (should not be able)"); setupClientSubject("testSubmitter"); try { InputStream is = cn.get(null, changeableObject); log.info(IOUtils.toString(is)); handleFail(cn.getLatestRequestUrl(),"6. testSubmitter should not be able to get the object"); } catch (NotAuthorized na) { // this is what we want } log.info("now try isAuthorized() on it as a known user with no rights to the object"); try { cn.isAuthorized(null, changeableObject, Permission.READ); handleFail(cn.getLatestRequestUrl(),"7. testSubmitter should not be authorized to read the object"); } catch (NotAuthorized na) { // this is what we want } log.info("finally test access against anonymous client"); setupClientSubject_NoCert(); try { cn.get(null, changeableObject); handleFail(cn.getLatestRequestUrl(),"8. anonymous client (no certificate) should not be" + "able to get the object"); } catch (NotAuthorized na) { // this is what we want } log.info("and test isAuthorized on it with certificateless client"); try { cn.isAuthorized(null, changeableObject, Permission.READ); handleFail(cn.getLatestRequestUrl(),"9. anonymous client (no certificate) should not be " + "able to get successful response from isAuthorized()"); } catch (NotAuthorized na) { // this is what we want } log.info("done."); } catch (TestIterationEndingException e) { handleFail(cn.getLatestRequestUrl(),"No Objects available to test against: " + e.getMessage()); } catch (BaseException e) { handleFail(cn.getLatestRequestUrl(), e.getClass().getSimpleName() + ": " + e.getDetail_code() + ": " + e.getDescription()); } catch (Exception e) { e.printStackTrace(); handleFail(currentUrl, e.getClass().getName() + ": " + e.getMessage()); } } Settings.getConfiguration().setProperty("D1Client.useLocalCache", origObjectCacheSetting); } @Override protected String getTestDescription() { return "Tests CN Tier2 CN_Authorization methods implemented by metacat"; } }