KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > opencrx > security > layer > application > OpenCrxSecurity_1


1 /*
2  * ====================================================================
3  * Project: opencrx, http://www.opencrx.org/
4  * Name: $Id: OpenCrxSecurity_1.java,v 1.16 2006/02/03 17:11:55 wfro Exp $
5  * Description: OpenCrxSecurity_1
6  * Revision: $Revision: 1.16 $
7  * Owner: CRIXP AG, Switzerland, http://www.crixp.com
8  * Date: $Date: 2006/02/03 17:11:55 $
9  * ====================================================================
10  *
11  * This software is published under the BSD license
12  * as listed below.
13  *
14  * Copyright (c) 2004, CRIXP Corp., Switzerland
15  * All rights reserved.
16  *
17  * Redistribution and use in source and binary forms, with or without
18  * modification, are permitted provided that the following conditions
19  * are met:
20  *
21  * * Redistributions of source code must retain the above copyright
22  * notice, this list of conditions and the following disclaimer.
23  *
24  * * Redistributions in binary form must reproduce the above copyright
25  * notice, this list of conditions and the following disclaimer in
26  * the documentation and/or other materials provided with the
27  * distribution.
28  *
29  * * Neither the name of CRIXP Corp. nor the names of the contributors
30  * to openCRX may be used to endorse or promote products derived
31  * from this software without specific prior written permission
32  *
33  *
34  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
35  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
36  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
37  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
39  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
40  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
41  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
43  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
45  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46  * POSSIBILITY OF SUCH DAMAGE.
47  *
48  * ------------------
49  *
50  * This product includes software developed by the Apache Software
51  * Foundation (http://www.apache.org/).
52  *
53  * This product includes software developed by contributors to
54  * openMDX (http://www.openmdx.org/)
55  */

56 package org.opencrx.security.layer.application;
57
58 import java.util.Set JavaDoc;
59
60 import org.opencrx.kernel.generic.OpenCrxException;
61 import org.opencrx.kernel.generic.SecurityKeys;
62 import org.openmdx.application.log.AppLog;
63 import org.openmdx.base.exception.ServiceException;
64 import org.openmdx.base.text.conversion.Base64;
65 import org.openmdx.compatibility.base.application.configuration.Configuration;
66 import org.openmdx.compatibility.base.dataprovider.cci.AttributeSelectors;
67 import org.openmdx.compatibility.base.dataprovider.cci.AttributeSpecifier;
68 import org.openmdx.compatibility.base.dataprovider.cci.DataproviderObject;
69 import org.openmdx.compatibility.base.dataprovider.cci.DataproviderObject_1_0;
70 import org.openmdx.compatibility.base.dataprovider.cci.DataproviderOperations;
71 import org.openmdx.compatibility.base.dataprovider.cci.DataproviderReply;
72 import org.openmdx.compatibility.base.dataprovider.cci.DataproviderRequest;
73 import org.openmdx.compatibility.base.dataprovider.cci.RequestCollection;
74 import org.openmdx.compatibility.base.dataprovider.cci.ServiceHeader;
75 import org.openmdx.compatibility.base.dataprovider.cci.SharedConfigurationEntries;
76 import org.openmdx.compatibility.base.dataprovider.cci.SystemAttributes;
77 import org.openmdx.compatibility.base.dataprovider.layer.application.ProvidingUid_1;
78 import org.openmdx.compatibility.base.dataprovider.spi.Layer_1_0;
79 import org.openmdx.compatibility.base.naming.Path;
80 import org.openmdx.compatibility.base.query.FilterOperators;
81 import org.openmdx.compatibility.base.query.FilterProperty;
82 import org.openmdx.compatibility.base.query.Quantors;
83 import org.openmdx.kernel.exception.BasicException;
84 import org.openmdx.kernel.id.UUIDs;
85 import org.openmdx.model1.accessor.basic.cci.Model_1_0;
86
87 /**
88  * This plugin implements the models org:openmdx:security:realm1,
89  * org:openmdx:security1:authorization1, org:openmdx:security:authentication1.
90  *
91  * It must be able to manage the following classes:
92  * - realm1: Group, Permission, Principal, Policy, Privilege, Realm, Role
93  * - authorization1: Policy, Privilege
94  * - authentication1: Password
95  *
96  * This plugin delegates all object retrievals, updates and creations to the
97  * persistence plugin which is typically the database plugin
98  * (e.g. super.get(header, request), super.replace(header, request)). An
99  * LDAP implementation of this plugin must delegate requests to an LDAP
100  * service.
101  *
102  */

103 public class OpenCrxSecurity_1
104   extends ProvidingUid_1 {
105
106     //-------------------------------------------------------------------------
107
public void activate(
108       short id,
109       Configuration configuration,
110       Layer_1_0 delegation
111     ) throws ServiceException, Exception JavaDoc {
112       super.activate(
113         id,
114         configuration,
115         delegation
116       );
117       
118       // model
119
if(configuration.values(SharedConfigurationEntries.MODEL).size() > 0) {
120         this.model = (Model_1_0)configuration.values(SharedConfigurationEntries.MODEL).get(0);
121       }
122       else {
123         throw new ServiceException(
124           BasicException.Code.DEFAULT_DOMAIN,
125           BasicException.Code.INVALID_CONFIGURATION,
126           null,
127           "A model must be configured with options 'modelPackage' and 'packageImpl'"
128         );
129       }
130
131       // realmIdentity
132
if(configuration.values("realmIdentity").size() > 0) {
133           this.realmIdentity = new Path((String JavaDoc)configuration.values("realmIdentity").get(0));
134       }
135       else {
136           throw new ServiceException(
137               BasicException.Code.DEFAULT_DOMAIN,
138               BasicException.Code.INVALID_CONFIGURATION,
139               null,
140               "A realm identity must be configured with option 'realmIdentity'"
141           );
142       }
143       
144     }
145
146     //-------------------------------------------------------------------------
147
private String JavaDoc getPrincipalName(
148         ServiceHeader header
149     ) throws ServiceException {
150         if(header.getPrincipalChain().size() == 0) {
151             return null;
152         }
153         return (String JavaDoc)header.getPrincipalChain().get(0);
154     }
155     
156     //-------------------------------------------------------------------------
157
private DataproviderObject_1_0 getPrincipal(
158         ServiceHeader header,
159         DataproviderRequest request
160     ) throws ServiceException {
161         String JavaDoc principalName = header.getPrincipalChain().size() == 0
162             ? null
163             : (String JavaDoc)header.getPrincipalChain().get(0);
164         String JavaDoc realmName = SecurityKeys.ROOT_PRINCIPAL.equals(principalName)
165             ? "Root"
166             : request.path().get(4);
167         DataproviderObject_1_0 principal = null;
168         try {
169             principal = this.delegation.addGetRequest(
170                 this.realmIdentity.getParent().getDescendant(new String JavaDoc[]{realmName, "principal", principalName}),
171                 AttributeSelectors.ALL_ATTRIBUTES,
172                 new AttributeSpecifier[]{}
173             );
174         }
175         catch(ServiceException e) {
176             AppLog.warning("principal not found in realm", new Object JavaDoc[]{principalName, realmName});
177             e.log();
178         }
179         return principal;
180     }
181     
182     //-------------------------------------------------------------------------
183
protected void completeObject(
184       ServiceHeader header,
185       Set JavaDoc fetchSet,
186       DataproviderObject_1_0 object
187     ) throws ServiceException {
188     }
189     
190     //-------------------------------------------------------------------------
191
private DataproviderReply completeReply(
192       ServiceHeader header,
193       Set JavaDoc fetchSet,
194       DataproviderReply reply
195     ) throws ServiceException {
196       for(int i = 0; i < reply.getObjects().length; i++) {
197           this.completeObject(
198               header,
199               fetchSet,
200               reply.getObjects()[i]
201           );
202       }
203       return reply;
204     }
205     
206     //-------------------------------------------------------------------------
207
private void setQualifier(
208         DataproviderObject obj
209     ) throws ServiceException {
210         if(
211             this.model.isSubtypeOf(obj, "org:openmdx:security:realm1:Principal") ||
212             this.model.isSubtypeOf(obj, "org:openmdx:security:realm1:Realm") ||
213             this.model.isSubtypeOf(obj, "org:openmdx:security:realm1:Permission") ||
214             this.model.isSubtypeOf(obj, "org:openmdx:security:realm1:Role") ||
215             this.model.isSubtypeOf(obj, "org:openmdx:security:realm1:Policy")
216         ) {
217             obj.clearValues("name").add(obj.path().getBase());
218         }
219         else if(
220             this.model.isSubtypeOf(obj, "org:openmdx:security:realm1:Credential")
221         ) {
222            obj.clearValues("id").add(obj.path().getBase());
223         }
224     }
225     
226     //-------------------------------------------------------------------------
227
public DataproviderObject_1_0 retrieveObject(
228         Path identity
229     ) throws ServiceException {
230         return this.delegation.addGetRequest(
231             identity,
232             AttributeSelectors.ALL_ATTRIBUTES,
233             new AttributeSpecifier[]{}
234         );
235     }
236
237     //-------------------------------------------------------------------------
238
private DataproviderObject createResult(
239       DataproviderRequest request,
240       String JavaDoc structName
241     ) {
242       DataproviderObject result = new DataproviderObject(
243         request.path().getDescendant(
244           new String JavaDoc[]{ "reply", UUIDs.getGenerator().next().toString()}
245         )
246       );
247       result.clearValues(SystemAttributes.OBJECT_CLASS).add(
248         structName
249       );
250       return result;
251     }
252
253     //-------------------------------------------------------------------------
254
public void prolog(
255       ServiceHeader header,
256       DataproviderRequest[] requests
257     ) throws ServiceException {
258       super.prolog(
259           header,
260           requests
261       );
262       this.delegation = new RequestCollection(
263           header,
264           this.getDelegation()
265       );
266     }
267
268     //-------------------------------------------------------------------------
269
public void epilog(
270       ServiceHeader header,
271       DataproviderRequest[] requests,
272       DataproviderReply[] replies
273     ) throws ServiceException {
274       super.epilog(
275           header,
276           requests,
277           replies
278       );
279     }
280
281     //-------------------------------------------------------------------------
282
private void setAttributes(
283       ServiceHeader header,
284       DataproviderObject obj,
285       DataproviderObject_1_0 oldValues
286     ) throws ServiceException {
287       this.setQualifier(
288         obj
289       );
290     }
291
292     //-------------------------------------------------------------------------
293
private void changePassword(
294         DataproviderObject_1_0 passwordCredential,
295         DataproviderObject_1_0 changePasswordParams
296     ) throws ServiceException {
297         String JavaDoc oldPassword = changePasswordParams.getValues("oldPassword") != null
298             ? Base64.encode((byte[])changePasswordParams.values("oldPassword").get(0))
299             : null;
300         if((oldPassword != null) && !oldPassword.equals(passwordCredential.values("password").get(0))) {
301             throw new ServiceException(
302                 BasicException.Code.DEFAULT_DOMAIN,
303                 BasicException.Code.ASSERTION_FAILURE,
304                 new BasicException.Parameter[]{
305                     new BasicException.Parameter("credential", passwordCredential)
306                 },
307                 "old password verification mismatch"
308             );
309         }
310         DataproviderObject changedPasswordCredential = new DataproviderObject(
311             passwordCredential
312         );
313         changedPasswordCredential.clearValues("password").add(
314             Base64.encode((byte[])changePasswordParams.values("password").get(0))
315         );
316         this.delegation.addReplaceRequest(
317             changedPasswordCredential
318         );
319     }
320     
321     //-------------------------------------------------------------------------
322
private void checkPermission(
323         ServiceHeader header,
324         DataproviderRequest request
325     ) throws ServiceException {
326         
327         short operation = request.operation();
328         
329         // read allowed for everybody
330
if(
331             operation == DataproviderOperations.OBJECT_RETRIEVAL ||
332             operation == DataproviderOperations.ITERATION_START ||
333             operation == DataproviderOperations.ITERATION_CONTINUATION
334         ) {
335             return;
336         }
337         // 1) All other operations only allowed for admin principals
338
// 2) Principal is allowed to update its principal objects
339
String JavaDoc principalName = this.getPrincipalName(header);
340         if((
341             !principalName.startsWith("admin" + SecurityKeys.ID_SEPARATOR) &&
342             !(request.path().getParent().isLike(PATH_PATTERN_PRINCIPALS) && request.path().getBase().equals(principalName)))
343         ) {
344             throw new ServiceException(
345                 OpenCrxException.DOMAIN,
346                 OpenCrxException.AUTHORIZATION_FAILURE_UPDATE,
347                 new BasicException.Parameter[]{
348                     new BasicException.Parameter("object", request.path()),
349                     new BasicException.Parameter("param0", request.path())
350                 },
351                 "No permission for " + DataproviderOperations.toString(operation) + " on object"
352             );
353         }
354     }
355     
356     //-------------------------------------------------------------------------
357
/**
358      * Update the realm if any object contained in the realm was modified.
359      */

360     private void updateRealm(
361         ServiceHeader header,
362         DataproviderRequest request
363     ) throws ServiceException {
364         if(
365             (request.path().size() >= PATH_PATTERN_REALM_COMPOSITE.size()) &&
366             request.path().getPrefix(PATH_PATTERN_REALM_COMPOSITE.size()).isLike(PATH_PATTERN_REALM_COMPOSITE)
367         ) {
368             try {
369                 Path realmIdentity = request.path().getPrefix(7);
370                 RequestCollection delegation = new RequestCollection(
371                     header,
372                     this.getDelegation()
373                 );
374                 DataproviderObject_1_0 realm = delegation.addGetRequest(
375                     realmIdentity,
376                     AttributeSelectors.SPECIFIED_AND_SYSTEM_ATTRIBUTES,
377                     new AttributeSpecifier[]{}
378                 );
379                 delegation.addReplaceRequest(
380                     new DataproviderObject(realm)
381                 );
382             }
383             catch(ServiceException e) {
384                 // Ignore if realm does not exist
385
if(BasicException.Code.NOT_FOUND != e.getExceptionCode()) {
386                     throw e;
387                 }
388             }
389         }
390     }
391     
392     //-------------------------------------------------------------------------
393
public DataproviderReply remove(
394         ServiceHeader header,
395         DataproviderRequest request
396     ) throws ServiceException {
397         this.checkPermission(
398             header,
399             request
400         );
401         this.updateRealm(
402             header,
403             request
404         );
405         return super.remove(
406             header,
407             request
408         );
409     }
410     
411     //-------------------------------------------------------------------------
412
public DataproviderReply create(
413         ServiceHeader header,
414         DataproviderRequest request
415     ) throws ServiceException {
416         this.checkPermission(
417             header,
418             request
419         );
420         this.setAttributes(
421             header,
422             request.object(),
423             null
424         );
425         this.updateRealm(
426             header,
427             request
428         );
429         return super.create(header, request);
430     }
431     
432     //-------------------------------------------------------------------------
433
public DataproviderReply replace(
434         ServiceHeader header,
435         DataproviderRequest request
436     ) throws ServiceException {
437         this.checkPermission(
438             header,
439             request
440         );
441         this.setAttributes(
442             header,
443             request.object(),
444             this.delegation.addGetRequest(
445               request.path(),
446               AttributeSelectors.ALL_ATTRIBUTES,
447               null
448             )
449         );
450         this.updateRealm(
451             header,
452             request
453         );
454         try {
455             return super.replace(
456                 header,
457                 request
458             );
459         }
460         catch(ServiceException e) {
461             // Ignore CONCURRENT_ACCESS_FAILURE on realms (updateRealm could have
462
// been called before)
463
if(
464                 (e.getExceptionCode() == BasicException.Code.CONCURRENT_ACCESS_FAILURE) &&
465                 request.path().isLike(PATH_PATTERN_REALM)
466             ) {
467                 return new DataproviderReply(request.object());
468             }
469             throw e;
470         }
471     }
472     
473     //-------------------------------------------------------------------------
474
public DataproviderReply get(
475       ServiceHeader header,
476       DataproviderRequest request
477     ) throws ServiceException {
478         this.checkPermission(
479             header,
480             request
481         );
482         return this.completeReply(
483             header,
484             request.attributeSelector() == AttributeSelectors.SPECIFIED_AND_TYPICAL_ATTRIBUTES
485                 ? request.attributeSpecifierAsMap().keySet()
486                 : null,
487             super.get(
488                 header,
489                 request
490             )
491         );
492     }
493
494     //-------------------------------------------------------------------------
495
public DataproviderReply operation(
496       ServiceHeader header,
497       DataproviderRequest request
498     ) throws ServiceException {
499         this.checkPermission(
500             header,
501             request
502         );
503                     
504         String JavaDoc operationName = request.path().get(
505             request.path().size() - 2
506         );
507         DataproviderObject_1_0 source = this.retrieveObject(
508             request.path().getPrefix(request.path().size() - 2)
509         );
510         String JavaDoc sourceClass = (String JavaDoc)source.values(SystemAttributes.OBJECT_CLASS).get(0);
511         DataproviderObject param = request.object();
512
513         // change password
514
DataproviderObject reply = null;
515         if("org:openmdx:security:authentication1:Password".equals(sourceClass)) {
516             if("change".equals(operationName)) {
517                 this.changePassword(
518                     source,
519                     param
520                 );
521                 reply = this.createResult(
522                     request,
523                     "org:openmdx:base:Void"
524                 );
525             }
526         }
527         
528         // reply
529
if(reply != null) {
530             return new DataproviderReply(
531               reply
532             );
533         }
534         else {
535             return super.operation(
536               header,
537               request
538             );
539         }
540     }
541
542     //-------------------------------------------------------------------------
543
public DataproviderReply find(
544       ServiceHeader header,
545       DataproviderRequest request
546     ) throws ServiceException {
547         this.checkPermission(
548             header,
549             request
550         );
551         String JavaDoc principalName = this.getPrincipalName(header);
552         String JavaDoc realmName = principalName.startsWith("admin" + SecurityKeys.ID_SEPARATOR)
553             ? principalName.substring(principalName.indexOf("-") + 1)
554             : "";
555         // Restrict browsing on principals
556
if(request.path().isLike(PATH_PATTERN_PRINCIPALS)) {
557             boolean containsSubjectFilter = false;
558             for(int i = 0; i < request.attributeFilter().length; i++) {
559                 if("subject".equals(request.attributeFilter()[i].name())) {
560                     containsSubjectFilter = true;
561                     break;
562                 }
563             }
564             // Return groups only if requesting principal is not admin-Root
565
// or segment admin
566
if(
567                 !containsSubjectFilter &&
568                 !"Root".equals(realmName) &&
569                 !realmName.equals(request.path().get(request.path().size()-2))
570             ) {
571                 request.addAttributeFilterProperty(
572                     new FilterProperty(
573                         Quantors.THERE_EXISTS,
574                         SystemAttributes.OBJECT_CLASS,
575                         FilterOperators.IS_IN,
576                         new String JavaDoc[]{
577                             "org:opencrx:security:realm1:PrincipalGroup"
578                         }
579                     )
580                 );
581             }
582         }
583         // Restrict browsing on subjects
584
else if(request.path().isLike(PATH_PATTERN_SUBJECTS)) {
585             // Do not restrict Root
586
if(!"Root".equals(realmName)) {
587                 request.addAttributeFilterProperty(
588                     new FilterProperty(
589                         Quantors.FOR_ALL,
590                         SystemAttributes.OBJECT_CLASS,
591                         FilterOperators.IS_IN,
592                         new String JavaDoc[]{}
593                     )
594                 );
595             }
596         }
597         return this.completeReply(
598             header,
599             request.attributeSelector() == AttributeSelectors.SPECIFIED_AND_TYPICAL_ATTRIBUTES
600                 ? request.attributeSpecifierAsMap().keySet()
601                 : null,
602             super.find(
603                 header,
604                 request
605             )
606         );
607     }
608
609     //-------------------------------------------------------------------------
610
// Variables
611
//-------------------------------------------------------------------------
612
private static final Path PATH_PATTERN_PRINCIPALS =
613         new Path("xri:@openmdx:org.openmdx.security.realm1/provider/:*/segment/:*/realm/:*/principal");
614     private static final Path PATH_PATTERN_REALM =
615         new Path("xri:@openmdx:org.openmdx.security.realm1/provider/:*/segment/:*/realm/:*");
616     private static final Path PATH_PATTERN_REALM_COMPOSITE =
617         new Path("xri:@openmdx:org.openmdx.security.realm1/provider/:*/segment/:*/realm/:*/:*");
618     private static final Path PATH_PATTERN_SUBJECTS =
619         new Path("xri:@openmdx:org.opencrx.security.identity1/provider/:*/segment/:*/subject");
620
621     private RequestCollection delegation = null;
622     private Path realmIdentity = null;
623     private Model_1_0 model = null;
624     
625 }
626
627 //--- End of File -----------------------------------------------------------
628
Popular Tags