Project

General

Profile

1 7667 tao
package edu.ucsb.nceas.metacat.dataone;
2
3
import java.io.ByteArrayInputStream;
4
import java.io.File;
5
import java.io.FileInputStream;
6
import java.io.InputStream;
7 7674 tao
import java.security.cert.X509Certificate;
8 7667 tao
import java.util.ArrayList;
9
import java.util.List;
10
11
import javax.xml.parsers.DocumentBuilder;
12
import javax.xml.parsers.DocumentBuilderFactory;
13
import javax.xml.xpath.XPath;
14
import javax.xml.xpath.XPathConstants;
15
import javax.xml.xpath.XPathExpression;
16
import javax.xml.xpath.XPathFactory;
17
18
import junit.framework.Test;
19
import junit.framework.TestSuite;
20
21 7674 tao
import org.apache.http.HttpResponse;
22 8810 leinfelder
import org.dataone.client.v2.itk.D1Client;
23
import org.dataone.client.v2.MNode;
24
import org.dataone.client.v2.formats.ObjectFormatCache;
25
import org.dataone.client.rest.RestClient;
26 7674 tao
import org.dataone.client.auth.CertificateManager;
27 7667 tao
import org.dataone.configuration.Settings;
28 9181 tao
import org.dataone.service.exceptions.ServiceFailure;
29 7667 tao
import org.dataone.service.types.v1.AccessPolicy;
30
import org.dataone.service.types.v1.AccessRule;
31
import org.dataone.service.types.v1.Identifier;
32
import org.dataone.service.types.v1.Permission;
33
import org.dataone.service.types.v1.Person;
34
import org.dataone.service.types.v1.Session;
35
import org.dataone.service.types.v1.Subject;
36
import org.dataone.service.types.v1.SubjectInfo;
37 8810 leinfelder
import org.dataone.service.types.v2.SystemMetadata;
38 7667 tao
import org.dataone.service.util.Constants;
39
import org.junit.Before;
40
import org.w3c.dom.Document;
41
import org.w3c.dom.Node;
42
import org.w3c.dom.NodeList;
43
import org.xml.sax.InputSource;
44
45
46
/**
47
 * A test class to test the access filter mechanism for the solr query
48
 * @author tao
49
 *
50
 */
51
public class SolrQueryAccessFilterTest extends D1NodeServiceTest {
52
53
    private static final String SOLR = "solr";
54
    private static final String EML201NAMESPACE = "eml://ecoinformatics.org/eml-2.0.1";
55
    private static final String CREATEUSER = "CN=Christopher Jones A583,O=Google,C=US,DC=cilogon,DC=org";
56
    private static final String QUERYUSER = "CN=ben leinfelder A756,O=Google,C=US,DC=cilogon,DC=org";
57
    private static final String GROUP1 = "CN=PISCO-data-managers,DC=cilogon,DC=org";
58
    private static final String GROUP2 = "CN=dataone-coredev,DC=cilogon,DC=org";
59 7674 tao
    private static final String USERWITHCERT = "CN=Jing Tao,OU=NCEAS,O=UCSB,ST=California,C=US";
60 7667 tao
    private static final String EMLFILE = "test/restfiles/knb-lter-gce.109.6.xml";
61 7675 tao
    private static final String INTRUSTCERTFILE = "test/test-credentials/test-user.pem";
62 7667 tao
    private static final String IDXPATH = "//response/result/doc/str[@name='id']/text()";
63 7692 tao
    private static final String TITLEPATH = "//response/result/doc/str[@name='title']/text()";
64 7693 tao
    private static final String TITLE = "Mollusc population abundance monitoring: Fall 2000 mid-marsh and creekbank infaunal and epifaunal mollusc abundance based on collections from GCE marsh, monitoring sites 1-10";
65 7667 tao
66
    /**
67
     * Build the test suite
68
     * @return
69
     */
70
    public static Test suite() {
71
72
      TestSuite suite = new TestSuite();
73 7686 tao
      suite.addTest(new SolrQueryAccessFilterTest("testPublicReadable"));
74
      suite.addTest(new SolrQueryAccessFilterTest("testOnlyUserReadable"));
75
      suite.addTest(new SolrQueryAccessFilterTest("testGroupReadable"));
76
      suite.addTest(new SolrQueryAccessFilterTest("testOnlyRightHolderReadable"));
77 7674 tao
      suite.addTest(new SolrQueryAccessFilterTest("testDistrustCertificate"));
78 7667 tao
79
      return suite;
80
81
    }
82
83
    /**
84
     * Set up the test fixtures
85
     *
86
     * @throws Exception
87
     */
88
    @Before
89
    public void setUp() throws Exception {
90
      super.setUp();
91
      // set up the configuration for d1client
92
      Settings.getConfiguration().setProperty("D1Client.cnClassName", MockCNode.class.getName());
93
    }
94
95
    /**
96
     * Constructor for the tests
97
     *
98
     * @param name - the name of the test
99
     */
100
    public SolrQueryAccessFilterTest(String name) {
101
      super(name);
102
103
    }
104
105
106
107
    /**
108
     * Test to query a public readable document
109
     */
110
    public void testPublicReadable() throws Exception {
111
        Session session = getSession(CREATEUSER, null);
112
        Identifier id = generateIdentifier();
113
        String[] allowUsers = {Constants.SUBJECT_PUBLIC};
114
        File object = new File(EMLFILE);
115
        SystemMetadata sysmeta = generateSystemMetadata(id, session.getSubject(), object , allowUsers);
116
        createObject(session, id, object, sysmeta);
117 7670 tao
        Thread.sleep(10000);
118 7667 tao
        Session querySession = getSession(Constants.SUBJECT_PUBLIC, null);
119 7692 tao
        InputStream input = query(querySession, id);
120
        Document doc = generateDoc(input);
121
        String resultId  = extractElementValue(doc, IDXPATH);
122 7670 tao
        assertTrue("In the testPublicReadable method, the query result should have the id "+id.getValue()+ " rather than "+resultId, resultId.equals(id.getValue()));
123 7693 tao
        String title = extractElementValue(doc, TITLEPATH);
124
        assertTrue(title.equals(TITLE));
125 7670 tao
        Session querySession2 = getSession(QUERYUSER, null);
126 7692 tao
        input = query(querySession2, id);
127
        doc = generateDoc(input);
128
        String resultId2 = extractElementValue(doc, IDXPATH);
129 7670 tao
        assertTrue("In the testPublicReadable method, the query result should have the id "+id.getValue()+ " rather than "+resultId2, resultId2.equals(id.getValue()));
130 7693 tao
        title = extractElementValue(doc, TITLEPATH);
131
        assertTrue(title.equals(TITLE));
132
133 7668 tao
        archive(session, id);
134 7692 tao
        input = query(querySession2, id);
135
        doc = generateDoc(input);
136
        String resultId3 = extractElementValue(doc, IDXPATH);
137 7686 tao
        assertTrue("In the testPublicReadable method, the query result should be null since the document was archived. ", resultId3 == null);
138 7667 tao
    }
139
140
141
    /**
142
     * Test to query a document which can only be read by a specified user
143
     */
144 7670 tao
    public void testOnlyUserReadable() throws Exception {
145
        Thread.sleep(15000);
146
        Session session = getSession(CREATEUSER, null);
147
        Identifier id = generateIdentifier();
148
        String[] allowUsers = {QUERYUSER};
149
        File object = new File(EMLFILE);
150
        SystemMetadata sysmeta = generateSystemMetadata(id, session.getSubject(), object , allowUsers);
151
        createObject(session, id, object, sysmeta);
152 7667 tao
153 7670 tao
        Thread.sleep(10000);
154
        Session querySession = getSession(Constants.SUBJECT_PUBLIC, null);
155 7692 tao
        InputStream input = query(querySession, id);
156
        Document doc = generateDoc(input);
157
        String resultId = extractElementValue(doc, IDXPATH);
158 7670 tao
        assertTrue("In the testOnlyUserReadable method, the query result id should be null for the public rather than "+resultId, resultId == null);
159
        Session querySession2 = getSession(QUERYUSER, null);
160 7692 tao
        input = query(querySession2, id);
161
        doc = generateDoc(input);
162
        resultId = extractElementValue(doc, IDXPATH);
163 7670 tao
        assertTrue("In the testOnlyUserReadable method, the query result for the user "+QUERYUSER+" should have the id "+id.getValue()+" rather than "+resultId, resultId.equals(id.getValue()));
164 7693 tao
        String title = extractElementValue(doc, TITLEPATH);
165
        assertTrue(title.equals(TITLE));
166 7670 tao
        archive(session, id);
167 7667 tao
    }
168
169
    /**
170
     * Test to query a document which can be read by a specified group
171
     */
172 7670 tao
    public void testGroupReadable() throws Exception {
173
        Thread.sleep(15000);
174
        Session session = getSession(CREATEUSER, null);
175
        Identifier id = generateIdentifier();
176
        String[] allowUsers = {GROUP1, GROUP2};
177
        File object = new File(EMLFILE);
178
        SystemMetadata sysmeta = generateSystemMetadata(id, session.getSubject(), object , allowUsers);
179
        createObject(session, id, object, sysmeta);
180
        Thread.sleep(10000);
181
        Session querySession = getSession(Constants.SUBJECT_PUBLIC, null);
182 7692 tao
        InputStream input = query(querySession, id);
183
        Document doc = generateDoc(input);
184
        String resultId = extractElementValue(doc, IDXPATH);
185 7670 tao
        assertTrue("In the testGroupReadable method, the query result id should be null for the public ", resultId == null);
186
        Session querySession2 = getSession(QUERYUSER, null);
187 7692 tao
        input = query(querySession2, id);
188
        doc = generateDoc(input);
189
        resultId = extractElementValue(doc, IDXPATH);
190 7670 tao
        assertTrue("In the testGroupReadable method, the query result for the user "+QUERYUSER+" which doesn't belong to the group should be null ", resultId == null);
191
        String[]groups = {GROUP1};
192
        Session querySession3 = getSession(QUERYUSER, groups);
193 7692 tao
        input = query(querySession3, id);
194
        doc = generateDoc(input);
195
        resultId = extractElementValue(doc, IDXPATH);
196 7670 tao
        assertTrue("In the testGroupReadable method, the query result for the user "+QUERYUSER+" which belong to the group should have the id "+id.getValue(), resultId.equals(id.getValue()));
197 7693 tao
        String title = extractElementValue(doc, TITLEPATH);
198
        assertTrue(title.equals(TITLE));
199 7670 tao
        archive(session, id);
200 7667 tao
    }
201
202
203
    /**
204
     * Test to query a document which only can be read by the rightHolder
205
     */
206 7670 tao
    public void testOnlyRightHolderReadable() throws Exception {
207
        Thread.sleep(15000);
208
        Session session = getSession(CREATEUSER, null);
209
        Identifier id = generateIdentifier();
210
        String[] allowUsers = null;
211
        File object = new File(EMLFILE);
212
        SystemMetadata sysmeta = generateSystemMetadata(id, session.getSubject(), object , allowUsers);
213
        createObject(session, id, object, sysmeta);
214
        Thread.sleep(10000);
215
        Session querySession = getSession(Constants.SUBJECT_PUBLIC, null);
216 7692 tao
        InputStream input = query(querySession, id);
217
        Document doc = generateDoc(input);
218
        String resultId = extractElementValue(doc, IDXPATH);
219 7670 tao
        assertTrue("In the testOnlyRightHolderReadable method, the query result id should be null for the public ", resultId == null);
220
        Session querySession2 = getSession(QUERYUSER, null);
221 7692 tao
        input = query(querySession2, id);
222
        doc = generateDoc(input);
223
        resultId = extractElementValue(doc, IDXPATH);
224 7670 tao
        assertTrue("In the testOnlyRightHolderReadable method, the query result for the user "+QUERYUSER+" which doesn't belong to the group should be null.", resultId == null);
225
        String[]groups = {GROUP1};
226
        Session querySession3 = getSession(QUERYUSER, groups);
227 7692 tao
        input = query(querySession3, id);
228
        doc = generateDoc(input);
229
        resultId = extractElementValue(doc, IDXPATH);
230 7670 tao
        assertTrue("In the testOnlyRightHolderReadable method, the query result for the user "+QUERYUSER+" which belong to the group should be null.", resultId == null);
231
        Session querySession4 = getSession(CREATEUSER, groups);
232 7692 tao
        input = query(querySession4, id);
233
        doc = generateDoc(input);
234
        resultId = extractElementValue(doc, IDXPATH);
235 7670 tao
        assertTrue("In the testOnlyRightHolderReadable method, the query result for the creator "+CREATEUSER+" should be "+id.getValue(), id.getValue().equals(resultId));
236 7693 tao
        String title = extractElementValue(doc, TITLEPATH);
237
        assertTrue(title.equals(TITLE));
238 7670 tao
        archive(session, id);
239 7667 tao
    }
240
241 7674 tao
    /**
242
     * Test a user with a distrusted certificate.
243
     * @throws Exception
244
     */
245
    public void testDistrustCertificate() throws Exception {
246
        //create a object only be readable by the USERWITHCERT
247
        Session session = getSession(CREATEUSER, null);
248
        Identifier id = generateIdentifier();
249
        String[] allowUsers = {USERWITHCERT};
250
        File object = new File(EMLFILE);
251
        SystemMetadata sysmeta = generateSystemMetadata(id, session.getSubject(), object , allowUsers);
252
        createObject(session, id, object, sysmeta);
253
        Thread.sleep(10000);
254
255
        //use faking session, the user can query the document
256 7675 tao
        Session querySession = getSession(USERWITHCERT, null);
257 7692 tao
258
        InputStream input = query(querySession, id);
259
        Document doc = generateDoc(input);
260
        String resultId = extractElementValue(doc, IDXPATH);
261 7693 tao
        assertTrue("In the testDistrustCertificate method, the query result id should be "+id.getValue(), id.getValue().equals(resultId));
262
        String title = extractElementValue(doc, TITLEPATH);
263
        assertTrue(title.equals(TITLE));
264 7674 tao
265
        //Use the libclient without the session, the user shouldn't query the document since its certificate is distrusted and it will be considered as the public.
266 8810 leinfelder
        org.dataone.service.types.v2.Node node = MNodeService.getInstance(request).getCapabilities();
267 7674 tao
        CertificateManager.getInstance().setCertificateLocation(INTRUSTCERTFILE);
268 7675 tao
        String baseURL = node.getBaseURL();
269 8103 leinfelder
        System.out.println("================The base url is "+baseURL);
270
        if (baseURL.contains("https://localhost")) {
271
        	// force localhost to skip https - most common for devs
272
        	baseURL = baseURL.replace("https", "http");
273
        	baseURL = baseURL.replace("8443", "8080");
274
        	baseURL = baseURL.replace("443", "80");
275
        }
276
        System.out.println("================The MODIFIED base url is "+baseURL);
277 7675 tao
        MNode mnNode = D1Client.getMN(baseURL);
278 9181 tao
        try {
279
            input = mnNode.query(querySession, SOLR, generateQuery(id.getValue()));
280
            fail("Can't reach here since it is an untrusted certificate");
281
        } catch (Exception e) {
282
            System.out.println("The exception is "+e.getMessage());
283
            System.out.println("the exception class is "+e.getClass().getCanonicalName());
284
            assertTrue(e instanceof ServiceFailure);
285
        }
286
287
        //doc = generateDoc(input);
288
        //String resultId2 = extractElementValue(doc, IDXPATH);
289
        //assertTrue("In the testDistrustCertificate method, the query result id should be null", resultId2==null);
290 7674 tao
        archive(session, id);
291
292
    }
293
294 7667 tao
    /*
295
     * constructs a "fake" session with the specified subject and groups.
296
     * If groups is not null, the session will have a subjectinfo which contains the person with the subject and is the member of the groups.
297
     * @return
298
     */
299
    private Session getSession(String subjectValue, String[]groups) throws Exception {
300
        Session session = new Session();
301
        Subject subject = new Subject();
302
        subject.setValue(subjectValue);
303
        session.setSubject(subject);
304
        if(groups != null) {
305
            Person person = new Person();
306
            person.setSubject(subject);
307
            person.setVerified(new Boolean(true));
308
            List<Subject>groupSubjects = new ArrayList<Subject>();
309
            for(String group: groups) {
310
                Subject groupSub = new Subject();
311
                groupSub.setValue(group);
312
                groupSubjects.add(groupSub);
313
            }
314
            person.setIsMemberOfList(groupSubjects);
315
            SubjectInfo subjectInfo = new SubjectInfo();
316
            subjectInfo.addPerson(person);
317
            session.setSubjectInfo(subjectInfo);
318
        }
319
        return session;
320
    }
321
322
    /*
323
     * Create a data object in the dataone server.
324
     * Return the identifier of the created object
325
     */
326
    private void createObject(Session session, Identifier id, File object, SystemMetadata sysmeta) throws Exception {
327
        MNodeService.getInstance(request).create(session, id, new FileInputStream(object), sysmeta);
328
329
    }
330
331
    private Identifier generateIdentifier() {
332
        Identifier guid = new Identifier();
333
        long random = Math.round(Math.random()*10000);
334
        guid.setValue("test." + System.currentTimeMillis()+(new Long(random)).toString());
335
        return guid;
336
    }
337
338
    /*
339 7668 tao
     * Archive the given id.
340 7667 tao
     */
341 7668 tao
    private void archive(Session session, Identifier id) throws Exception {
342
        MNodeService.getInstance(request).archive(session, id);
343 7667 tao
    }
344
345
346
347
    /*
348
     * Generate system metadata for the file
349
     */
350
    private SystemMetadata generateSystemMetadata(Identifier id, Subject owner, File objectFile, String[] allowedSubjects) throws Exception{
351
        SystemMetadata sysmeta = createSystemMetadata(id, owner, new FileInputStream(objectFile));
352
        AccessPolicy accessPolicy = null;
353
        if(allowedSubjects != null && allowedSubjects.length >0) {
354
            accessPolicy = new AccessPolicy();
355
            for(int i=0; i<allowedSubjects.length; i++) {
356
                AccessRule allow = new AccessRule();
357
                allow.addPermission(Permission.READ);
358
                Subject subject =  new Subject();
359
                subject.setValue(allowedSubjects[i]);
360
                allow.addSubject(subject);
361
                accessPolicy.addAllow(allow);
362
            }
363
        }
364
        sysmeta.setAccessPolicy(accessPolicy);
365
        sysmeta.setFormatId(ObjectFormatCache.getInstance().getFormat(EML201NAMESPACE).getFormatId());
366
        return sysmeta;
367
    }
368
369
    /*
370
     * Query the server to find the doc which matches the specified id
371
     */
372 7692 tao
    private InputStream query(Session session, Identifier id) throws Exception{
373 7667 tao
        String query = generateQuery(id.getValue());
374 7670 tao
        MNodeService service = MNodeService.getInstance(request);
375
        service.setSession(session);
376 8810 leinfelder
        InputStream input = service.query(session, SOLR, query);
377 7692 tao
        return input;
378 7667 tao
    }
379
380
    /*
381 7692 tao
     *
382 7667 tao
     */
383 7692 tao
    private Document generateDoc(InputStream input) throws Exception {
384 7667 tao
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
385
        DocumentBuilder builder = factory.newDocumentBuilder();
386
        Document doc = builder.parse(new InputSource(input));
387 7692 tao
        return doc;
388
    }
389
390
    /*
391
     * Extract the return id from the query result input stream
392
     */
393
    private String extractElementValue(Document doc, String path) throws Exception {
394
        String id = null;
395 7667 tao
        XPathFactory xPathfactory = XPathFactory.newInstance();
396
        XPath xpath = xPathfactory.newXPath();
397 7692 tao
        XPathExpression expr = xpath.compile(path);
398 7667 tao
        Object result = expr.evaluate(doc, XPathConstants.NODESET);
399
        System.out.println("================ result is "+result);
400
        if(result != null) {
401
            NodeList nodes = (NodeList) result;
402
            if(nodes != null) {
403
                System.out.println("the length of nodes is "+nodes.getLength());
404
                Node node = nodes.item(0);
405
                if(node != null) {
406
                    id = node.getNodeValue();
407
                }
408
409
            }
410
411
        }
412
413 7692 tao
        System.out.println("the value of the element " + path+ " is ====== "+id);
414 7667 tao
        return id;
415
416
417
    }
418
    /*
419
     * Make a query string which will query "id= the specified id".
420
     * @param id
421
     * @return
422
     */
423
    private String generateQuery(String id) {
424
        String query = "q=id:"+id+"&fl=id,title";
425
        return query;
426
    }
427 7674 tao
428 7675 tao
429 7667 tao
}