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.types.v1.AccessPolicy;
29
import org.dataone.service.types.v1.AccessRule;
30
import org.dataone.service.types.v1.Identifier;
31
import org.dataone.service.types.v1.Permission;
32
import org.dataone.service.types.v1.Person;
33
import org.dataone.service.types.v1.Session;
34
import org.dataone.service.types.v1.Subject;
35
import org.dataone.service.types.v1.SubjectInfo;
36
import org.dataone.service.types.v2.SystemMetadata;
37
import org.dataone.service.util.Constants;
38
import org.junit.Before;
39
import org.w3c.dom.Document;
40
import org.w3c.dom.Node;
41
import org.w3c.dom.NodeList;
42
import org.xml.sax.InputSource;
43

    
44

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

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