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.D1Client;
23
import org.dataone.client.D1RestClient;
24
import org.dataone.client.MNode;
25
import org.dataone.client.ObjectFormatCache;
26
import org.dataone.client.RestClient;
27
import org.dataone.client.auth.CertificateManager;
28
import org.dataone.configuration.Settings;
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.v1.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.v1.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
        input = mnNode.query(SOLR, generateQuery(id.getValue()));
279
        doc = generateDoc(input);
280
        String resultId2 = extractElementValue(doc, IDXPATH);
281
        assertTrue("In the testDistrustCertificate method, the query result id should be null", resultId2==null);
282
        archive(session, id);
283
        
284
    }
285
    
286
    /*
287
     * constructs a "fake" session with the specified subject and groups.
288
     * 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.
289
     * @return
290
     */
291
    private Session getSession(String subjectValue, String[]groups) throws Exception {
292
        Session session = new Session();
293
        Subject subject = new Subject();
294
        subject.setValue(subjectValue);
295
        session.setSubject(subject);
296
        if(groups != null) {
297
            Person person = new Person();
298
            person.setSubject(subject);
299
            person.setVerified(new Boolean(true));
300
            List<Subject>groupSubjects = new ArrayList<Subject>();
301
            for(String group: groups) {
302
                Subject groupSub = new Subject();
303
                groupSub.setValue(group);
304
                groupSubjects.add(groupSub);
305
            }
306
            person.setIsMemberOfList(groupSubjects);
307
            SubjectInfo subjectInfo = new SubjectInfo();
308
            subjectInfo.addPerson(person);
309
            session.setSubjectInfo(subjectInfo);
310
        }
311
        return session;
312
    }
313
    
314
    /*
315
     * Create a data object in the dataone server. 
316
     * Return the identifier of the created object
317
     */
318
    private void createObject(Session session, Identifier id, File object, SystemMetadata sysmeta) throws Exception {
319
        MNodeService.getInstance(request).create(session, id, new FileInputStream(object), sysmeta);
320
        
321
    }
322
    
323
    private Identifier generateIdentifier() {
324
        Identifier guid = new Identifier();
325
        long random = Math.round(Math.random()*10000);
326
        guid.setValue("test." + System.currentTimeMillis()+(new Long(random)).toString());
327
        return guid;
328
    }
329
    
330
    /*
331
     * Archive the given id.
332
     */
333
    private void archive(Session session, Identifier id) throws Exception {
334
        MNodeService.getInstance(request).archive(session, id);
335
    }
336
    
337
    
338
    
339
    /*
340
     * Generate system metadata for the file
341
     */
342
    private SystemMetadata generateSystemMetadata(Identifier id, Subject owner, File objectFile, String[] allowedSubjects) throws Exception{
343
        SystemMetadata sysmeta = createSystemMetadata(id, owner, new FileInputStream(objectFile));
344
        AccessPolicy accessPolicy = null;
345
        if(allowedSubjects != null && allowedSubjects.length >0) {
346
            accessPolicy = new AccessPolicy();
347
            for(int i=0; i<allowedSubjects.length; i++) {
348
                AccessRule allow = new AccessRule();
349
                allow.addPermission(Permission.READ);
350
                Subject subject =  new Subject();
351
                subject.setValue(allowedSubjects[i]);
352
                allow.addSubject(subject);
353
                accessPolicy.addAllow(allow);
354
            }
355
        }
356
        sysmeta.setAccessPolicy(accessPolicy);
357
        sysmeta.setFormatId(ObjectFormatCache.getInstance().getFormat(EML201NAMESPACE).getFormatId());
358
        return sysmeta;
359
    }
360
    
361
    /*
362
     * Query the server to find the doc which matches the specified id
363
     */
364
    private InputStream query(Session session, Identifier id) throws Exception{
365
        String query = generateQuery(id.getValue());
366
        MNodeService service = MNodeService.getInstance(request);
367
        service.setSession(session);
368
        InputStream input = service.query(SOLR, query);
369
        return input;
370
    }
371
    
372
    /*
373
     * 
374
     */
375
    private Document generateDoc(InputStream input) throws Exception {
376
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
377
        DocumentBuilder builder = factory.newDocumentBuilder();
378
        Document doc = builder.parse(new InputSource(input));
379
        return doc;
380
    }
381
    
382
    /*
383
     * Extract the return id from the query result input stream
384
     */
385
    private String extractElementValue(Document doc, String path) throws Exception {
386
        String id = null;
387
        XPathFactory xPathfactory = XPathFactory.newInstance();
388
        XPath xpath = xPathfactory.newXPath();
389
        XPathExpression expr = xpath.compile(path);
390
        Object result = expr.evaluate(doc, XPathConstants.NODESET);
391
        System.out.println("================ result is "+result);
392
        if(result != null) {
393
            NodeList nodes = (NodeList) result;
394
            if(nodes != null) {
395
                System.out.println("the length of nodes is "+nodes.getLength());
396
                Node node = nodes.item(0);
397
                if(node != null) {
398
                    id = node.getNodeValue();
399
                }
400
                
401
            }
402
            
403
        }
404
       
405
        System.out.println("the value of the element " + path+ " is ====== "+id);
406
        return id;
407

    
408
    
409
    }
410
    /*
411
     * Make a query string which will query "id= the specified id".
412
     * @param id
413
     * @return
414
     */
415
    private String generateQuery(String id) {
416
        String query = "q=id:"+id+"&fl=id,title";
417
        return query;
418
    }
419
    
420
  
421
}
(6-6/6)