1 56 57 package org.opencrx.kernel.layer.application; 58 59 import java.io.BufferedReader ; 60 import java.io.ByteArrayInputStream ; 61 import java.io.IOException ; 62 import java.io.InputStreamReader ; 63 import java.io.UnsupportedEncodingException ; 64 import java.security.MessageDigest ; 65 import java.security.NoSuchAlgorithmException ; 66 import java.util.ArrayList ; 67 import java.util.Arrays ; 68 import java.util.HashSet ; 69 import java.util.Iterator ; 70 import java.util.List ; 71 import java.util.Set ; 72 import java.util.StringTokenizer ; 73 74 import org.opencrx.kernel.generic.SecurityKeys; 75 import org.openmdx.application.log.AppLog; 76 import org.openmdx.base.accessor.jmi.cci.RefPackage_1_0; 77 import org.openmdx.base.exception.ServiceException; 78 import org.openmdx.base.text.conversion.Base64; 79 import org.openmdx.compatibility.base.dataprovider.cci.AttributeSelectors; 80 import org.openmdx.compatibility.base.dataprovider.cci.AttributeSpecifier; 81 import org.openmdx.compatibility.base.dataprovider.cci.DataproviderObject; 82 import org.openmdx.compatibility.base.dataprovider.cci.DataproviderObject_1_0; 83 import org.openmdx.compatibility.base.dataprovider.cci.Directions; 84 import org.openmdx.compatibility.base.dataprovider.cci.RequestCollection; 85 import org.openmdx.compatibility.base.dataprovider.cci.ServiceHeader; 86 import org.openmdx.compatibility.base.dataprovider.cci.SystemAttributes; 87 import org.openmdx.compatibility.base.naming.Path; 88 import org.openmdx.compatibility.base.query.FilterOperators; 89 import org.openmdx.compatibility.base.query.FilterProperty; 90 import org.openmdx.compatibility.base.query.Quantors; 91 import org.openmdx.kernel.exception.BasicException; 92 import org.openmdx.kernel.id.UUIDs; 93 import org.openmdx.model1.accessor.basic.cci.Model_1_0; 94 95 public class UserHomes { 96 97 public UserHomes( 99 Model_1_0 model, 100 OpenCrxKernel_1 plugin, 101 RequestCollection delegation, 102 String passwordEncodingAlgorithm, 103 RefPackage_1_0 rootPkg 104 ) { 105 this.model = model; 106 this.plugin = plugin; 107 this.delegation = delegation; 108 this.passwordEncodingAlgorithm = passwordEncodingAlgorithm; 109 this.rootPkg = rootPkg; 110 } 111 112 void refreshCharts( 114 ServiceHeader header, 115 DataproviderObject_1_0 userHome 116 ) throws ServiceException { 117 118 Path chartReference = userHome.path().getChild("chart"); 119 List charts = new ArrayList (); 120 121 charts.addAll( 124 Arrays.asList( 125 this.plugin.getContracts().calculateUserHomeCharts( 126 header, 127 userHome.path(), 128 chartReference 129 ) 130 ) 131 ); 132 charts.addAll( 134 Arrays.asList( 135 this.plugin.activities.calculateUserHomeCharts( 136 header, 137 userHome.path(), 138 chartReference 139 ) 140 ) 141 ); 142 143 Set exclude = new HashSet (); 145 for(Iterator i = charts.iterator(); i.hasNext(); ) { 146 DataproviderObject chart = (DataproviderObject)i.next(); 147 try { 148 DataproviderObject existing = this.plugin.retrieveObjectForModification(chart.path()); 149 existing.attributeNames().clear(); 150 existing.addClones(chart, true); 151 exclude.add(chart.path()); 152 } 153 catch(ServiceException e) { 154 try { 155 if(e.getExceptionCode() == BasicException.Code.NOT_FOUND) { 156 this.delegation.addCreateRequest( 157 chart 158 ); 159 exclude.add(chart.path()); 160 } 161 } 162 catch(ServiceException e0) { 163 AppLog.error(e0.getMessage(), e0.getCause(), 1); 164 } 165 } 166 } 167 168 this.plugin.removeAll( 170 chartReference, 171 null, 172 0, 173 exclude 174 ); 175 } 176 177 private byte[] getPasswordDigest( 179 String password, 180 String algorithm 181 ) { 182 try { 183 MessageDigest md = MessageDigest.getInstance(algorithm); 184 md.update(password.getBytes("UTF-8")); 185 return md.digest(); 186 } 187 catch(NoSuchAlgorithmException e) { 188 } 189 catch(UnsupportedEncodingException e) { 190 } 191 return null; 192 } 193 194 199 private DataproviderObject createPasswordCredential( 200 Path subjectIdentity, 201 List errors 202 ) { 203 String providerName = subjectIdentity.get(2); 204 205 Path passwordIdentity = new Path("xri:@openmdx:org.openmdx.security.authentication1/provider"); 207 passwordIdentity = passwordIdentity.getDescendant( 208 new String []{ 209 providerName, 210 "segment", 211 "Root", "credential", 213 UUIDs.getGenerator().next().toString() 214 } 215 ); 216 DataproviderObject passwordCredential = new DataproviderObject( 217 passwordIdentity 218 ); 219 passwordCredential.values(SystemAttributes.OBJECT_CLASS).add("org:openmdx:security:authentication1:Password"); 220 passwordCredential.values("subject").add(subjectIdentity); 221 passwordCredential.values("locked").add(Boolean.FALSE); 222 try { 223 this.delegation.addCreateRequest(passwordCredential); 224 } 225 catch(Exception e) { 226 ServiceException e0 = new ServiceException(e); 227 AppLog.warning(e0.getMessage(), e0.getCause(), 1); 228 errors.add("can not create password credential"); 229 errors.add("reason is " + e.getMessage()); 230 return null; 231 } 232 return passwordCredential; 233 } 234 235 private int changePassword( 237 Path passwordCredentialIdentity, 238 String oldPassword, 239 String password 240 ) { 241 DataproviderObject changePasswordParams = new DataproviderObject( 242 passwordCredentialIdentity.getChild("change") 243 ); 244 changePasswordParams.values(SystemAttributes.OBJECT_CLASS).add( 245 "org:openmdx:security:authentication1:PasswordChangeParams" 246 ); 247 if(oldPassword != null) { 248 changePasswordParams.values("oldPassword").add(this.getPasswordDigest(oldPassword, this.passwordEncodingAlgorithm)); 249 } 250 if(password == null) { 251 return MISSING_NEW_PASSWORD; 252 } 253 changePasswordParams.values("password").add(this.getPasswordDigest(password, this.passwordEncodingAlgorithm)); 254 try { 255 RequestCollection runAsRootDelegation = this.plugin.getRunAsRootDelegation(); 256 runAsRootDelegation.addOperationRequest(changePasswordParams); 257 } 258 catch(ServiceException e) { 259 AppLog.warning(e.getMessage(), e.getCause(), 1); 260 return CAN_NOT_CHANGE_PASSWORD; 261 } 262 return CHANGE_PASSWORD_OK; 263 } 264 265 public int changePassword( 267 DataproviderObject_1_0 userHome, 268 Path realmIdentity, 269 boolean verifyOldPassword, 270 String oldPassword, 271 String newPassword, 272 String newPasswordVerification 273 ) { 274 if(newPassword == null) { 275 return MISSING_NEW_PASSWORD; 276 } 277 if(newPasswordVerification == null) { 278 return MISSING_NEW_PASSWORD_VERIFICATION; 279 } 280 if(!newPassword.equals(newPasswordVerification)) { 281 return PASSWORD_VERIFICATION_MISMATCH; 282 } 283 if(verifyOldPassword && oldPassword == null) { 284 return MISSING_OLD_PASSWORD; 285 } 286 287 String principalName = userHome.path().getBase(); 289 290 DataproviderObject principal = null; 292 try { 293 principal = 294 new DataproviderObject( 295 this.delegation.addGetRequest( 296 realmIdentity.getDescendant(new String []{"principal", principalName}), 297 AttributeSelectors.ALL_ATTRIBUTES, 298 new AttributeSpecifier[]{} 299 ) 300 ); 301 } 302 catch(Exception e) { 303 ServiceException e0 = new ServiceException(e); 304 AppLog.warning(e0.getMessage(), e0.getCause(), 1); 305 return CAN_NOT_RETRIEVE_REQUESTED_PRINCIPAL; 306 } 307 308 return this.changePassword( 309 (Path)principal.values("credential").get(0), 310 verifyOldPassword ? oldPassword : null, 311 newPassword 312 ); 313 } 314 315 323 DataproviderObject_1_0 createUserHome( 324 Path loginRealmIdentity, 325 Path realmIdentity, 326 Path contactIdentity, 327 Path primaryGroup, 328 String principalName, 329 boolean isAdministrator, 330 String initialPassword, 331 String initialPasswordVerification, 332 List errors 333 ) { 334 if(principalName == null) { 335 errors.add("missing principalName"); 336 return null; 337 } 338 if(contactIdentity == null) { 339 errors.add("missing contact"); 340 return null; 341 } 342 343 Path subjectIdentity = null; 345 try { 346 DataproviderObject_1_0 loginPrincipal = this.plugin.retrieveObjectForModification( 347 loginRealmIdentity.getDescendant(new String []{"principal", principalName}) 348 ); 349 if(loginPrincipal.values("subject").size() == 0) { 350 errors.add("Undefined subject for principal " + loginPrincipal.path()); 351 return null; 352 } 353 subjectIdentity = (Path)loginPrincipal.values("subject").get(0); 354 if((initialPassword != null) && (initialPassword.length() > 0)) { 356 DataproviderObject passwordCredential = this.createPasswordCredential( 357 subjectIdentity, 358 errors 359 ); 360 if(passwordCredential == null) { 361 return null; 362 } 363 this.changePassword( 365 passwordCredential.path(), 366 null, 367 initialPassword 368 ); 369 loginPrincipal.clearValues("credential").add(passwordCredential.path()); 371 } 372 if((loginPrincipal.getValues("credential") == null) || (loginPrincipal.getValues("credential").size() == 0)) { 373 errors.add("No credential specified for principal " + principalName); 374 } 375 } 376 catch(Exception e) { 377 ServiceException e1 = new ServiceException(e); 378 AppLog.warning(e1.getMessage(), e1.getCause(), 1); 379 errors.add("can not retrieve principal " + principalName + " in realm " + loginRealmIdentity); 380 errors.add("reason is " + e.getMessage()); 381 return null; 382 } 383 384 Path userIdentity = this.plugin.createPrincipal( 386 principalName + "." + SecurityKeys.USER_SUFFIX, 387 realmIdentity, 388 SecurityKeys.PRINCIPAL_TYPE_USER, 389 new Path[]{}, 390 subjectIdentity, 391 errors 392 ); 393 if(errors.size() > 0) return null; 394 395 Path groupAdministratorsIdentity = userIdentity.getParent().getChild(SecurityKeys.USER_GROUP_ADMINISTRATORS); 396 Path groupUsersIdentity = userIdentity.getParent().getChild(SecurityKeys.USER_GROUP_USERS); 397 398 this.plugin.createPrincipal( 400 principalName, 401 realmIdentity, 402 SecurityKeys.PRINCIPAL_TYPE_PRINCIPAL, 403 isAdministrator 404 ? new Path[]{ 405 groupAdministratorsIdentity, 406 groupUsersIdentity, 407 userIdentity 408 } 409 : new Path[]{ 410 groupUsersIdentity, 411 userIdentity 412 }, 413 subjectIdentity, 414 errors 415 ); 416 if(errors.size() > 0) return null; 417 418 this.plugin.createPrincipal( 422 principalName, 423 realmIdentity.getParent().getChild("Root"), 424 SecurityKeys.PRINCIPAL_TYPE_PRINCIPAL, 425 new Path[]{}, 426 subjectIdentity, 427 errors 428 ); 429 if(errors.size() > 0) return null; 430 431 this.plugin.createPrincipal( 433 principalName + "." + SecurityKeys.USER_SUFFIX, 434 realmIdentity.getParent().getChild("Root"), 435 SecurityKeys.PRINCIPAL_TYPE_USER, 436 new Path[]{}, 437 subjectIdentity, 438 errors 439 ); 440 if(errors.size() > 0) return null; 441 442 String providerName = contactIdentity.get(2); 443 String segmentName = contactIdentity.get(4); 444 445 initialPassword = initialPassword == null ? "" : initialPassword; 446 initialPasswordVerification = initialPasswordVerification == null ? "" : initialPasswordVerification; 447 if(!initialPassword.equals(initialPasswordVerification)) { 448 errors.add("the passwords you typed do not match"); 449 return null; 450 } 451 452 455 Path userHomeReference = new Path("xri:@openmdx:org.opencrx.kernel.home1"); 456 userHomeReference = userHomeReference.getDescendant( 457 new String []{"provider", providerName, "segment", segmentName, "userHome"} 458 ); 459 DataproviderObject_1_0 userHome = null; 460 try { 461 userHome = this.delegation.addGetRequest( 462 userHomeReference.getChild(principalName), 463 AttributeSelectors.ALL_ATTRIBUTES, 464 new AttributeSpecifier[]{} 465 ); 466 } 467 catch(Exception e) { 468 DataproviderObject newUserHome = new DataproviderObject( 469 userHomeReference.getChild(principalName) 470 ); 471 newUserHome.values(SystemAttributes.OBJECT_CLASS).add("org:opencrx:kernel:home1:UserHome"); 472 newUserHome.values("contact").add(contactIdentity); 473 newUserHome.clearValues("primaryGroup").add(primaryGroup); 474 newUserHome.values("owningUser").add(userIdentity); 476 newUserHome.values("owningGroup").addAll( 478 Arrays.asList( 479 new Path[]{ 480 groupAdministratorsIdentity 481 } 482 ) 483 ); 484 newUserHome.values("accessLevelDelete").add( 486 new Short ((short)0) 487 ); 488 newUserHome.values("accessLevelDelete").add(new Short (SecurityKeys.ACCESS_LEVEL_PRIVATE)); 489 this.plugin.initCharts(newUserHome, null); 490 491 try { 492 this.delegation.addCreateRequest(newUserHome); 493 } 494 catch(Exception e0) { 495 ServiceException e1 = new ServiceException(e0); 496 AppLog.warning(e1.getMessage(), e1.getCause(), 1); 497 errors.add("can not create user home"); 498 errors.add("reason is " + e.getMessage()); 499 return null; 500 } 501 userHome = newUserHome; 502 } 503 return userHome; 504 } 505 506 private DataproviderObject_1_0 retrieveUserHome( 508 Path userHomeSegment, 509 String principalName 510 ) { 511 try { 512 return this.plugin.retrieveObjectFromDelegation( 513 userHomeSegment.getDescendant(new String []{"userHome", principalName}) 514 ); 515 } 516 catch(Exception e) { 517 return null; 518 } 519 } 520 521 private DataproviderObject_1_0 retrieveContact( 523 Path accountSegment, 524 String aliasName, 525 String fullName 526 ) { 527 try { 528 FilterProperty[] filter = null; 529 if(!"-".equals(aliasName)) { 530 filter = new FilterProperty[]{ 531 new FilterProperty( 532 Quantors.THERE_EXISTS, 533 "aliasName", 534 FilterOperators.IS_IN, 535 new String []{aliasName} 536 ) 537 }; 538 } 539 else if(!"-".equals(fullName)) { 540 filter = new FilterProperty[]{ 541 new FilterProperty( 542 Quantors.THERE_EXISTS, 543 "fullName", 544 FilterOperators.IS_IN, 545 new String []{fullName} 546 ) 547 }; 548 } 549 List accounts = this.delegation.addFindRequest( 550 accountSegment.getChild("account"), 551 filter 552 ); 553 if(accounts.size() > 0) { 554 return (DataproviderObject_1_0)accounts.iterator().next(); 555 } 556 else { 557 return null; 558 } 559 } 560 catch(Exception e) { 561 return null; 562 } 563 } 564 565 private DataproviderObject_1_0 retrievePrincipal( 567 Path realm, 568 String principalName 569 ) { 570 try { 571 return this.plugin.retrieveObjectFromDelegation( 572 realm.getDescendant(new String []{"principal", principalName}) 573 ); 574 } 575 catch(Exception e) { 576 return null; 577 } 578 } 579 580 public String importUsers( 582 DataproviderObject_1_0 homeSegment, 583 byte[] item 584 ) throws ServiceException { 585 BufferedReader reader = new BufferedReader ( 586 new InputStreamReader (new ByteArrayInputStream (item)) 587 ); 588 DataproviderObject_1_0 realm = this.plugin.retrieveObjectFromDelegation( 589 new Path("xri:@openmdx:org.openmdx.security.realm1/provider/" + homeSegment.path().get(2) + "/segment/Root/realm/" + homeSegment.path().get(4)) 590 ); 591 DataproviderObject_1_0 accountSegment = this.plugin.retrieveObjectFromDelegation( 592 new Path("xri:@openmdx:org.opencrx.kernel.account1/provider/" + homeSegment.path().get(2) + "/segment/" + homeSegment.path().get(4)) 593 ); 594 Path loginRealmIdentity = 595 new Path("xri:@openmdx:org.openmdx.security.realm1/provider/" + homeSegment.path().get(2) + "/segment/Root/realm/Default"); 596 597 int nCreatedUsers = 0; 598 int nFailedUsersNoPrimaryGroup = 0; 599 int nFailedUsersNoContact = 0; 600 int nFailedUsersOther = 0; 601 int nExistingUsers = 0; 602 try { 603 while(reader.ready()) { 604 String l = reader.readLine(); 605 if(l.startsWith("User")) { 606 StringTokenizer t = new StringTokenizer (l, ";"); 607 String command = t.nextToken(); 608 String principalName = t.nextToken(); 609 String accountAlias = t.nextToken(); 610 String accountFullName = t.nextToken(); 611 String primaryGroupName = t.nextToken(); 612 String password = t.nextToken(); 613 if(this.retrieveUserHome(homeSegment.path(), principalName) == null) { 614 try { 615 DataproviderObject_1_0 contact = this.retrieveContact(accountSegment.path(), accountAlias, accountFullName); 616 if(contact != null) { 617 DataproviderObject_1_0 primaryGroup = this.retrievePrincipal(realm.path(), primaryGroupName); 618 if(primaryGroup != null) { 619 List errors = new ArrayList (); 620 this.createUserHome( 621 loginRealmIdentity, 622 realm.path(), 623 contact.path(), 624 primaryGroup.path(), 625 principalName, 626 false, 627 password, 628 password, 629 errors 630 ); 631 if(errors.size() == 0) { 632 nCreatedUsers++; 633 } 634 else { 635 nFailedUsersOther++; 636 } 637 } 638 else { 639 AppLog.info("Group " + primaryGroup + " for user " + principalName + " not found"); 640 nFailedUsersNoPrimaryGroup++; 641 } 642 } 643 else { 644 AppLog.info("Contact " + accountAlias + "/" + accountFullName + " for user " + principalName + " not found"); 645 nFailedUsersNoContact++; 646 } 647 } 648 catch(Exception e) { 649 new ServiceException(e).log(); 650 nFailedUsersOther++; 651 } 652 } 653 else { 654 nExistingUsers++; 655 } 656 } 657 } 658 } 659 catch(IOException e) { 660 new ServiceException(e).log(); 661 } 662 return 663 "Users=(created:" + nCreatedUsers + ",existing:" + nExistingUsers + ",failed no primary group:" + nFailedUsersNoPrimaryGroup + ",failed no contact:" + nFailedUsersNoContact + ",failed other:" + nFailedUsersOther + ");"; 664 } 665 666 public void encodeEMailAccountPassword( 668 DataproviderObject object, 669 DataproviderObject_1_0 oldValues 670 ) throws ServiceException { 671 String objectClass = (String )object.values(SystemAttributes.OBJECT_CLASS).get(0); 672 if(this.model.isSubtypeOf(objectClass, "org:opencrx:kernel:home1:EMailAccount")) { 673 if(object.getValues("incomingPassword") != null) { 674 try { 675 String password = (String )object.values("incomingPassword").get(0); 676 if((password != null) && !password.startsWith(SecurityKeys.PASSWORD_ENCODING_SCHEME)) { 677 object.clearValues("incomingPassword").add( 678 SecurityKeys.PASSWORD_ENCODING_SCHEME + Base64.encode(password.getBytes("UTF-8")) 679 ); 680 } 681 } catch(UnsupportedEncodingException e) {} 682 } 683 if(object.getValues("outgoingPassword") != null) { 684 try { 685 String password = (String )object.values("outgoingPassword").get(0); 686 if((password != null) && !password.startsWith(SecurityKeys.PASSWORD_ENCODING_SCHEME)) { 687 object.clearValues("outgoingPassword").add( 688 SecurityKeys.PASSWORD_ENCODING_SCHEME + Base64.encode(password.getBytes("UTF-8")) 689 ); 690 } 691 } catch(UnsupportedEncodingException e) {} 692 } 693 } 694 } 695 696 public List completeUserHome( 698 ServiceHeader header, 699 DataproviderObject_1_0 userHome 700 ) throws ServiceException { 701 List additionalFetchedObjects = new ArrayList (); 702 try { 703 List charts = this.delegation.addFindRequest( 704 userHome.path().getChild("chart"), 705 null, 706 AttributeSelectors.ALL_ATTRIBUTES, 707 null, 708 0, Integer.MAX_VALUE, 709 Directions.ASCENDING 710 ); 711 for( 712 int i = 0; 713 i < 4; 714 i++ 715 ) { 716 userHome.values("favoriteChart" + i); 718 userHome.values("favoriteChart" + i + "MimeType"); 719 userHome.values("favoriteChart" + i + "Name"); 720 userHome.values("favoriteChart" + i + "Description"); 721 if(userHome.getValues("chart" + i).size() > 0) { 722 for( 723 Iterator j = charts.iterator(); 724 j.hasNext(); 725 ) { 726 DataproviderObject_1_0 chart = (DataproviderObject_1_0)j.next(); 727 if(chart.path().equals(userHome.getValues("chart" + i).get(0))) { 728 userHome.values("favoriteChart" + i).addAll( 729 chart.values("chart") 730 ); 731 userHome.values("favoriteChart" + i + "MimeType").addAll( 732 chart.values("chartMimeType") 733 ); 734 userHome.values("favoriteChart" + i + "Name").addAll( 735 chart.values("chartName") 736 ); 737 userHome.values("favoriteChart" + i + "Description").addAll( 738 chart.values("description") 739 ); 740 additionalFetchedObjects.add(chart); 741 break; 742 } 743 } 744 } 745 } 746 } 747 catch(ServiceException e) {} 749 return additionalFetchedObjects; 750 } 751 752 private static final short CHANGE_PASSWORD_OK = 0; 756 private static final short MISSING_NEW_PASSWORD = 1; 757 private static final short MISSING_NEW_PASSWORD_VERIFICATION = 2; 758 private static final short PASSWORD_VERIFICATION_MISMATCH = 3; 759 private static final short CAN_NOT_RETRIEVE_REQUESTED_PRINCIPAL = 4; 760 private static final short CAN_NOT_CHANGE_PASSWORD = 5; 761 private static final short MISSING_OLD_PASSWORD = 6; 762 763 private final Model_1_0 model; 764 private final OpenCrxKernel_1 plugin; 765 private final RequestCollection delegation; 766 private final String passwordEncodingAlgorithm; 767 private final RefPackage_1_0 rootPkg; 768 769 } 770 771 | Popular Tags |