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

    
401
    
402
    }
403
    /*
404
     * Make a query string which will query "id= the specified id".
405
     * @param id
406
     * @return
407
     */
408
    private String generateQuery(String id) {
409
        String query = "q=id:"+id+"&fl=id,title";
410
        return query;
411
    }
412
    
413
  
414
}
(6-6/6)