Project

General

Profile

1
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
import java.security.cert.X509Certificate;
8
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
import org.apache.http.HttpResponse;
22
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
import org.dataone.client.auth.CertificateManager;
27
import org.dataone.configuration.Settings;
28
import org.dataone.service.exceptions.ServiceFailure;
29
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
import org.dataone.service.types.v2.SystemMetadata;
38
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
    private static final String USERWITHCERT = "CN=Jing Tao,OU=NCEAS,O=UCSB,ST=California,C=US";
60
    private static final String EMLFILE = "test/restfiles/knb-lter-gce.109.6.xml";
61
    private static final String INTRUSTCERTFILE = "test/test-credentials/test-user.pem";
62
    private static final String IDXPATH = "//response/result/doc/str[@name='id']/text()";
63
    private static final String TITLEPATH = "//response/result/doc/str[@name='title']/text()";
64
    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
    
66
    /**
67
     * Build the test suite
68
     * @return
69
     */
70
    public static Test suite() {
71
      
72
      TestSuite suite = new TestSuite();
73
      suite.addTest(new SolrQueryAccessFilterTest("testPublicReadable"));
74
      suite.addTest(new SolrQueryAccessFilterTest("testOnlyUserReadable"));
75
      suite.addTest(new SolrQueryAccessFilterTest("testGroupReadable"));
76
      suite.addTest(new SolrQueryAccessFilterTest("testOnlyRightHolderReadable"));
77
      suite.addTest(new SolrQueryAccessFilterTest("testDistrustCertificate"));
78
      
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
        Thread.sleep(10000);
118
        Session querySession = getSession(Constants.SUBJECT_PUBLIC, null);
119
        InputStream input = query(querySession, id);
120
        Document doc = generateDoc(input);
121
        String resultId  = extractElementValue(doc, IDXPATH);
122
        assertTrue("In the testPublicReadable method, the query result should have the id "+id.getValue()+ " rather than "+resultId, resultId.equals(id.getValue()));
123
        String title = extractElementValue(doc, TITLEPATH);
124
        assertTrue(title.equals(TITLE));
125
        Session querySession2 = getSession(QUERYUSER, null);
126
        input = query(querySession2, id);
127
        doc = generateDoc(input);
128
        String resultId2 = extractElementValue(doc, IDXPATH);
129
        assertTrue("In the testPublicReadable method, the query result should have the id "+id.getValue()+ " rather than "+resultId2, resultId2.equals(id.getValue()));
130
        title = extractElementValue(doc, TITLEPATH);
131
        assertTrue(title.equals(TITLE));
132
        
133
        archive(session, id);
134
        input = query(querySession2, id);
135
        doc = generateDoc(input);
136
        String resultId3 = extractElementValue(doc, IDXPATH);
137
        assertTrue("In the testPublicReadable method, the query result should be null since the document was archived. ", resultId3 == null);
138
    }
139
    
140
    
141
    /**
142
     * Test to query a document which can only be read by a specified user
143
     */
144
    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
        
153
        Thread.sleep(10000);
154
        Session querySession = getSession(Constants.SUBJECT_PUBLIC, null);
155
        InputStream input = query(querySession, id);
156
        Document doc = generateDoc(input);
157
        String resultId = extractElementValue(doc, IDXPATH);
158
        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
        input = query(querySession2, id);
161
        doc = generateDoc(input);
162
        resultId = extractElementValue(doc, IDXPATH);
163
        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
        String title = extractElementValue(doc, TITLEPATH);
165
        assertTrue(title.equals(TITLE));
166
        archive(session, id);
167
    }
168
    
169
    /**
170
     * Test to query a document which can be read by a specified group
171
     */
172
    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
        InputStream input = query(querySession, id);
183
        Document doc = generateDoc(input);
184
        String resultId = extractElementValue(doc, IDXPATH);
185
        assertTrue("In the testGroupReadable method, the query result id should be null for the public ", resultId == null);
186
        Session querySession2 = getSession(QUERYUSER, null);
187
        input = query(querySession2, id);
188
        doc = generateDoc(input);
189
        resultId = extractElementValue(doc, IDXPATH);
190
        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
        input = query(querySession3, id);
194
        doc = generateDoc(input);
195
        resultId = extractElementValue(doc, IDXPATH);
196
        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
        String title = extractElementValue(doc, TITLEPATH);
198
        assertTrue(title.equals(TITLE));
199
        archive(session, id);
200
    }
201
    
202
    
203
    /**
204
     * Test to query a document which only can be read by the rightHolder
205
     */
206
    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
        InputStream input = query(querySession, id);
217
        Document doc = generateDoc(input);
218
        String resultId = extractElementValue(doc, IDXPATH);
219
        assertTrue("In the testOnlyRightHolderReadable method, the query result id should be null for the public ", resultId == null);
220
        Session querySession2 = getSession(QUERYUSER, null);
221
        input = query(querySession2, id);
222
        doc = generateDoc(input);
223
        resultId = extractElementValue(doc, IDXPATH);
224
        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
        input = query(querySession3, id);
228
        doc = generateDoc(input);
229
        resultId = extractElementValue(doc, IDXPATH);
230
        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
        input = query(querySession4, id);
233
        doc = generateDoc(input);
234
        resultId = extractElementValue(doc, IDXPATH);
235
        assertTrue("In the testOnlyRightHolderReadable method, the query result for the creator "+CREATEUSER+" should be "+id.getValue(), id.getValue().equals(resultId));
236
        String title = extractElementValue(doc, TITLEPATH);
237
        assertTrue(title.equals(TITLE));
238
        archive(session, id);
239
    }
240
    
241
    /**
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
        Session querySession = getSession(USERWITHCERT, null);
257
        
258
        InputStream input = query(querySession, id);
259
        Document doc = generateDoc(input);
260
        String resultId = extractElementValue(doc, IDXPATH);
261
        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
        
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
        org.dataone.service.types.v2.Node node = MNodeService.getInstance(request).getCapabilities();
267
        CertificateManager.getInstance().setCertificateLocation(INTRUSTCERTFILE);
268
        String baseURL = node.getBaseURL();
269
        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
        MNode mnNode = D1Client.getMN(baseURL);
278
        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
        archive(session, id);
291
        
292
    }
293
    
294
    /*
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
     * Archive the given id.
340
     */
341
    private void archive(Session session, Identifier id) throws Exception {
342
        MNodeService.getInstance(request).archive(session, id);
343
    }
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
    private InputStream query(Session session, Identifier id) throws Exception{
373
        String query = generateQuery(id.getValue());
374
        MNodeService service = MNodeService.getInstance(request);
375
        service.setSession(session);
376
        InputStream input = service.query(session, SOLR, query);
377
        return input;
378
    }
379
    
380
    /*
381
     * 
382
     */
383
    private Document generateDoc(InputStream input) throws Exception {
384
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
385
        DocumentBuilder builder = factory.newDocumentBuilder();
386
        Document doc = builder.parse(new InputSource(input));
387
        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
        XPathFactory xPathfactory = XPathFactory.newInstance();
396
        XPath xpath = xPathfactory.newXPath();
397
        XPathExpression expr = xpath.compile(path);
398
        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
        System.out.println("the value of the element " + path+ " is ====== "+id);
414
        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
    
428
  
429
}
(8-8/8)