001/* 002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 003 * 004 * Copyright (c) 2005 Sun Microsystems Inc. All Rights Reserved 005 * 006 * The contents of this file are subject to the terms 007 * of the Common Development and Distribution License 008 * (the License). You may not use this file except in 009 * compliance with the License. 010 * 011 * You can obtain a copy of the License at 012 * https://opensso.dev.java.net/public/CDDLv1.0.html or 013 * opensso/legal/CDDLv1.0.txt 014 * See the License for the specific language governing 015 * permission and limitations under the License. 016 * 017 * When distributing Covered Code, include this CDDL 018 * Header Notice in each file and include the License file 019 * at opensso/legal/CDDLv1.0.txt. 020 * If applicable, add the following below the CDDL Header, 021 * with the fields enclosed by brackets [] replaced by 022 * your own identifying information: 023 * "Portions Copyrighted [year] [name of copyright owner]" 024 * 025 * $Id: IdUtils.java,v 1.34 2009/11/20 23:52:54 ww203982 Exp $ 026 * 027 * Portions Copyrighted 2011-2017 ForgeRock AS. 028 * Portions Copyrighted 2014 Nomura Research Institute, Ltd 029 */ 030 031package com.sun.identity.idm; 032 033import java.security.AccessController; 034import java.util.Collections; 035import java.util.HashSet; 036import java.util.Iterator; 037import java.util.Map; 038import java.util.Set; 039import java.util.StringTokenizer; 040 041import com.iplanet.am.sdk.AMConstants; 042import com.iplanet.am.sdk.AMDirectoryAccessFactory; 043import com.iplanet.am.sdk.AMException; 044import com.iplanet.am.sdk.AMObject; 045import com.iplanet.am.sdk.AMOrganization; 046import com.iplanet.am.sdk.AMStoreConnection; 047import com.iplanet.am.sdk.common.IDirectoryServices; 048import com.iplanet.am.util.SystemProperties; 049import com.iplanet.sso.SSOException; 050import com.iplanet.sso.SSOToken; 051import com.sun.identity.authentication.service.AuthD; 052import com.sun.identity.common.CaseInsensitiveHashMap; 053import com.sun.identity.common.CaseInsensitiveHashSet; 054import com.sun.identity.common.DNUtils; 055import com.sun.identity.security.AdminTokenAction; 056import com.sun.identity.shared.Constants; 057import com.sun.identity.shared.debug.Debug; 058import com.sun.identity.sm.DNMapper; 059import com.sun.identity.sm.OrgConfigViaAMSDK; 060import com.sun.identity.sm.OrganizationConfigManager; 061import com.sun.identity.sm.SMSEntry; 062import com.sun.identity.sm.SMSException; 063import com.sun.identity.sm.ServiceConfig; 064import com.sun.identity.sm.ServiceConfigManager; 065import com.sun.identity.sm.ServiceManager; 066import org.forgerock.openam.ldap.LDAPUtils; 067import org.forgerock.openam.utils.CollectionUtils; 068import org.forgerock.openam.utils.StringUtils; 069import org.forgerock.opendj.ldap.DN; 070 071/** 072 * The class defines some static utilities used by other components like policy 073 * and auth 074 * 075 * @supported.api 076 */ 077public final class IdUtils { 078 private static Debug debug = AMIdentityRepository.debug; 079 080 private static Map mapSupportedTypes = new CaseInsensitiveHashMap(10); 081 082 public static Set supportedTypes = new HashSet(); 083 084 private static Map mapTypesToServiceNames = new CaseInsensitiveHashMap(); 085 086 protected static Map typesCanBeMemberOf = new CaseInsensitiveHashMap(); 087 088 protected static Map typesCanHaveMembers = new CaseInsensitiveHashMap(); 089 090 protected static Map typesCanAddMembers = new CaseInsensitiveHashMap(); 091 092 // Static map to cache "orgIdentifier" and organization DN 093 private static Map<String, String> orgIdentifierToOrgName = Collections.synchronizedMap( 094 new CaseInsensitiveHashMap<String, String>()); 095 private static Map<String, Boolean> orgStatusCache = Collections.synchronizedMap( 096 new CaseInsensitiveHashMap<String, Boolean>()); 097 098 // Static Set to cache unknown organisation lookups 099 private static Set<String> unknownOrgLookupCache = 100 Collections.synchronizedSet(new CaseInsensitiveHashSet<String>()); 101 102 // ServiceConfigManager for sunidentityrepository service 103 private static String notificationId; 104 105 private static ServiceConfigManager serviceConfigManager; 106 107 // User naming attribute for AMSDK 108 private static String USER_NAMING_ATTR; 109 110 // Organization naming attribute for AMSDK 111 private static String ORG_NAMING_ATTR; 112 113 // SMS Root Suffix 114 private static String ROOT_SUFFIX; 115 116 // DN pointing to the services node 117 private static String SERVICES_SUFFIX; 118 119 // Special Users 120 private static Set specialUsers = new HashSet(); 121 122 static { 123 initialize(); 124 } 125 126 protected static void initialize() { 127 if (ServiceManager.isConfigMigratedTo70()) { 128 // IdRepo service schema exists. Read the supported 129 // entities from there 130 try { 131 SSOToken adminToken = (SSOToken) AccessController 132 .doPrivileged(AdminTokenAction.getInstance()); 133 serviceConfigManager = new ServiceConfigManager(adminToken, 134 IdConstants.REPO_SERVICE, "1.0"); 135 ServiceConfig ss = serviceConfigManager.getGlobalConfig(null); 136 Set typeSchemaNames = ss.getSubConfigNames("*", 137 IdConstants.SUPPORTED_TYPES); 138 if (typeSchemaNames == null || typeSchemaNames.isEmpty()) { 139 loadDefaultTypes(); 140 } else { 141 Iterator it = typeSchemaNames.iterator(); 142 while (it.hasNext()) { 143 String typeSchema = (String) it.next(); 144 IdType idType = new IdType(typeSchema); 145 supportedTypes.add(idType); 146 mapSupportedTypes.put(idType.getName(), idType); 147 ServiceConfig tsc = ss.getSubConfig(typeSchema); 148 Map attributes = tsc.getAttributes(); 149 Set serviceNameSet = (Set) attributes 150 .get(IdConstants.SERVICE_NAME); 151 Set canBeMembersOf = (Set) attributes 152 .get(IdConstants.ATTR_MEMBER_OF); 153 Set canHaveMembers = (Set) attributes 154 .get(IdConstants.ATTR_HAVE_MEMBERS); 155 Set canAddMembers = (Set) attributes 156 .get(IdConstants.ATTR_ADD_MEMBERS); 157 if (serviceNameSet != null && 158 !serviceNameSet.isEmpty()) { 159 mapTypesToServiceNames.put(typeSchema, 160 (String) serviceNameSet.iterator().next()); 161 } 162 if (canBeMembersOf != null && 163 !canBeMembersOf.isEmpty()) { 164 Set memberOfSet = getMemberSet(canBeMembersOf); 165 typesCanBeMemberOf.put(typeSchema, memberOfSet); 166 } 167 if (canHaveMembers != null && 168 !canHaveMembers.isEmpty()) { 169 Set memberSet = getMemberSet(canHaveMembers); 170 typesCanHaveMembers.put(typeSchema, memberSet); 171 } 172 if (canAddMembers != null && 173 !canAddMembers.isEmpty()) 174 { 175 Set memberSet = getMemberSet(canAddMembers); 176 typesCanAddMembers.put(typeSchema, memberSet); 177 } 178 } 179 } 180 } catch (SMSException e) { 181 String installTime = SystemProperties.get( 182 Constants.SYS_PROPERTY_INSTALL_TIME, "false"); 183 if (!installTime.equals("true")) { 184 debug.error( 185 "IdUtils.initialize: Loading default types.", e); 186 } 187 loadDefaultTypes(); 188 } catch (SSOException ssoe) { 189 debug.error("dUtils.initialize: Loading default types", ssoe); 190 loadDefaultTypes(); 191 } 192 } else { 193 loadDefaultTypes(); 194 } 195 196 // Register for SMS notifications to root realm 197 if (notificationId == null) { 198 try { 199 SSOToken adminToken = (SSOToken) AccessController 200 .doPrivileged(AdminTokenAction.getInstance()); 201 if (serviceConfigManager == null) { 202 serviceConfigManager = new ServiceConfigManager(adminToken, 203 IdConstants.REPO_SERVICE, "1.0"); 204 } 205 notificationId = serviceConfigManager.addListener( 206 new IdUtilsListener()); 207 } catch (SMSException e) { 208 String installTime = SystemProperties.get( 209 Constants.SYS_PROPERTY_INSTALL_TIME, "false"); 210 if (!installTime.equals("true")) { 211 debug.error( 212 "IdUtils.initialize: Register notification", e); 213 } 214 } catch (SSOException ssoe) { 215 String installTime = SystemProperties.get( 216 Constants.SYS_PROPERTY_INSTALL_TIME, "false"); 217 if (!installTime.equals("true")) { 218 debug.error( 219 "IdUtils.initialize: Register notification", ssoe); 220 } 221 } 222 } 223 } 224 225 /** 226 * @supported.api 227 * Returns a handle of the Identity object based on 228 * the SSO Token passed in (<code>AMIdentity</code> object of the user 229 * who is authenticated). 230 * 231 * @param token Single sign on token of user. 232 * @return Identity object. 233 * @throws IdRepoException if there are repository related error conditions. 234 * @throws SSOException if user's single sign on token is invalid. 235 */ 236 public static AMIdentity getIdentity(SSOToken token) 237 throws IdRepoException, SSOException { 238 String principal = token.getProperty(Constants.UNIVERSAL_IDENTIFIER); 239 if (principal == null) { 240 // This could happen during co-existence with AM 6.x 241 // and SSOToken created by AM 6.x server. In this case 242 // the principal name would be the DN 243 principal = token.getPrincipal().getName(); 244 } 245 return (getIdentity(token, principal)); 246 } 247 248 /** 249 * @supported.api 250 * 251 * Returns a string which uniquely represents this identity object. 252 * 253 * @param id 254 * <code>AMIdentity</code> object whose string represenation is 255 * needed. 256 * @return universal identifier of <code>id</code>. 257 */ 258 public static String getUniversalId(AMIdentity id) { 259 return id.getUniversalId(); 260 } 261 262 /** 263 * @supported.api 264 * 265 * Returns an <code>AMIdentity</code> object, if provided with a string 266 * identifier for the object. 267 * 268 * @param token SSOToken of the administrator 269 * @param univId String represenation of the identity. 270 * @return Identity object 271 * @throws IdRepoException if the identifier provided is wrong. 272 */ 273 public static AMIdentity getIdentity(SSOToken token, String univId) 274 throws IdRepoException { 275 return (getIdentity(token, univId, null)); 276 } 277 278 /** 279 * Returns an <code>AMIdentity</code> object, given the 280 * DN of an authenticated identity, realm name and identity type. 281 * This interface is mainly for authentication component to get 282 * back the identity of the user. 283 * 284 * @param token SSOToken of the administrator 285 * @param amsdkdn DN of the authenticated user 286 * @param realm realm name where the user was authenticated 287 * @return Identity object or <code>null</code> 288 * @throws IdRepoException if the underly components throws 289 * exception while obtaining the identity object 290 */ 291 public static AMIdentity getIdentity(SSOToken token, String amsdkdn, 292 String realm) throws IdRepoException { 293 if (amsdkdn == null || !LDAPUtils.isDN(amsdkdn)) { 294 Object[] args = { amsdkdn }; 295 throw (new IdRepoException(IdRepoBundle.BUNDLE_NAME, 296 IdRepoErrorCode.ILLEGAL_UNIVERSAL_IDENTIFIER, args)); 297 } 298 DN amsdkdnObject = LDAPUtils.newDN(amsdkdn); 299 300 // Try constructing the identity object 301 if (amsdkdn.toLowerCase().startsWith("id=")) { 302 try { 303 return (new AMIdentity(amsdkdnObject, token)); 304 } catch (IdRepoException ide) { 305 // this could be a AMSDK DN. Follow the AMSDK rules 306 if (debug.messageEnabled()) { 307 debug.message("IdUtils:getIdentity(token, " + 308 amsdkdn + ") got exception: " + ide.getMessage() + 309 "\n\tContinuing with AMSDK DN check"); 310 } 311 } 312 } 313 314 // Check for Special Users 315 initializeSpecialUsers(); 316 if (specialUsers.contains(DNUtils.normalizeDN(amsdkdn))) { 317 return new AMIdentity(amsdkdnObject, token, LDAPUtils.rdnValueFromDn( 318 amsdkdnObject), IdType.USER, ROOT_SUFFIX); 319 } 320 321 // Since "amsdkdn" is not a UUID, check if realm has AMSDK configured 322 // This change is to avoid the issue of IdUtils always checking the 323 // users in AMSDK as IdUtils does not check if AMSDK is configured in 324 // any of the realms. 325 try { 326 if (!ServiceManager.isAMSDKEnabled() || ((realm != null) && 327 !OrgConfigViaAMSDK.isAMSDKConfigured(realm)) || 328 (!ServiceManager.isAMSDKConfigured())) { 329 // Not configured for AMSDK, return 330 return (null); 331 } 332 } catch (SMSException smse) { 333 // Ignore the exception and continue 334 } 335 336 // Initialize root realm suffix, org and user naming attributes 337 initializeForGetIdentity(); 338 339 // Determine if the amsdkdn is valid. Obtain name & type 340 String name = null; 341 IdType type = null; 342 try { 343 // Since we would using AMSDK, get AMDirectoryManager preload 344 // all the attributes and check if it exists 345 IDirectoryServices dsServices = 346 AMDirectoryAccessFactory.getDirectoryServices(); 347 // Preload/cache all the attributes assuming it is a user 348 // Mainly for performance reasons, since getObjectType would 349 // force multiple another directory lookup 350 try { 351 if (amsdkdn.startsWith(USER_NAMING_ATTR)) { 352 dsServices.getAttributes(token, amsdkdn, AMObject.USER); 353 } 354 } catch (Exception e) { 355 // Ignore the exception and continue since this for cache 356 } 357 358 // Getting object type would use the cached attributes 359 int sdkType = dsServices.getObjectType(token, amsdkdn); 360 361 // Convert the sdkType to IdRepo type 362 type = getType(AMStoreConnection.getObjectName(sdkType)); 363 name = AMConstants.CONTAINER_DEFAULT_TEMPLATE_ROLE; 364 if (!type.equals(IdType.REALM)) { 365 name = LDAPUtils.rdnValueFromDn(amsdkdnObject); 366 } 367 } catch (AMException ame) { 368 // Debug the message and return null 369 if (debug.messageEnabled()) { 370 debug.message("IdUtils.getIdentity: Unable to resolve " + 371 "AMSDK DN: " + amsdkdn, ame); 372 } 373 return (null); 374 } catch (SSOException ssoe) { 375 // Debug the message and return null 376 if (debug.messageEnabled()) { 377 debug.message("IdUtils.getIdentity: Unable to resolve " + 378 "AMSDK DN. Got SSOException", ssoe); 379 } 380 return (null); 381 } 382 383 // Need to determine realm for amsdkdn 384 String srealm = ROOT_SUFFIX; 385 if (!amsdkdn.equals(ROOT_SUFFIX) && 386 !amsdkdn.equals(SERVICES_SUFFIX)) { 387 // Need to get the object type and walk up the tree 388 int index = amsdkdn.indexOf(ORG_NAMING_ATTR); 389 if (index == 0) { 390 srealm = OrgConfigViaAMSDK.getRealmForAMSDK(amsdkdn, realm); 391 } else if (index > 0) { 392 srealm = OrgConfigViaAMSDK.getRealmForAMSDK( 393 amsdkdn.substring(index), realm); 394 } 395 if (debug.messageEnabled()) { 396 debug.message("IdUtils.getIdentity:: amsdkdn=" + 397 amsdkdn + " maps to realm=" + srealm); 398 } 399 } else if (amsdkdn.equals(SERVICES_SUFFIX)) { 400 // Since amsdkdn points to services node, 401 // it should be reset to root suffix 402 amsdkdn = ROOT_SUFFIX; 403 } 404 405 return (new AMIdentity(amsdkdnObject, token, name, type, srealm)); 406 } 407 408 /** 409 * Returns the name of service which defines the profile information for 410 * this type. Returns null, if nothing is defined. 411 * 412 * @param type IdType whose service name is needed. 413 * @return Name of the service. 414 */ 415 public static String getServiceName(IdType type) { 416 return (String) mapTypesToServiceNames.get(type.getName()); 417 } 418 419 /** 420 * Returns corresponding <code>IdType</code> object given a type. 421 * 422 * @param type of object to return. 423 * @return Idtype of type. 424 * @throws IdRepoException if there are no corresponding types. 425 */ 426 public static IdType getType(String type) throws IdRepoException { 427 if (type.equalsIgnoreCase("managedrole")) { 428 type = "role"; 429 } else if (type.equalsIgnoreCase("organization") 430 || type.equalsIgnoreCase("organizationalunit")) { 431 type = "realm"; 432 } 433 434 IdType returnType = (IdType) mapSupportedTypes.get(type); 435 if (returnType == null) { 436 Object args[] = { type }; 437 throw new IdRepoException(IdRepoBundle.BUNDLE_NAME, 438 IdRepoErrorCode.NOT_SUPPORTED_TYPE, args); 439 } 440 return returnType; 441 } 442 443 /** 444 * Returns the matching DN from the AM SDK for this entry. This utility is 445 * required by auth. 446 * 447 * @param id <code>AMIdentity</code> object. 448 * @return <code>DN</code> of the object, as represented in the datastore. 449 */ 450 public static String getDN(AMIdentity id) { 451 if (id.getDN() != null) { 452 return id.getDN(); 453 } else { 454 return id.getUniversalId(); 455 } 456 } 457 458 /** 459 * Returns an organization which maps to the identifier used by application 460 * 461 * @param orgIdentifier Organization identifier 462 * @return Organization mapping to that identifier. 463 */ 464 public static String getOrganization(SSOToken token, String orgIdentifier) 465 throws IdRepoException, SSOException { 466 // Check in cache first 467 String id = orgIdentifierToOrgName.get(orgIdentifier); 468 if (id != null) { 469 return (id); 470 } 471 472 String[] args = {orgIdentifier}; 473 // Don't go to the expense of an organisation search if we have failed to look this up already. 474 if (unknownOrgLookupCache.contains(orgIdentifier)) { 475 if (debug.messageEnabled()) { 476 debug.message("IdUtils.getOrganization: orgIdentifier {} found in unknown org lookup cache.", 477 orgIdentifier); 478 } 479 throw new IdRepoException(IdRepoBundle.BUNDLE_NAME, IdRepoErrorCode.NO_MAPPING_FOUND, args); 480 } 481 482 // Compute the organization name 483 if (debug.messageEnabled()) { 484 debug.message("IdUtils:getOrganization Input orgname: " 485 + orgIdentifier); 486 } 487 if (orgIdentifier == null || orgIdentifier.length() == 0 488 || orgIdentifier.equals("/")) { 489 // Return base DN 490 id = DNMapper.orgNameToDN("/"); 491 } else if (orgIdentifier.startsWith("/")) { 492 // If orgIdentifier is in "/" format covert to DN and return 493 id = DNMapper.orgNameToDN(orgIdentifier); 494 try { 495 new OrganizationConfigManager(token, orgIdentifier); 496 } catch (SMSException e) { 497 if (debug.messageEnabled()) { 498 debug.message("IdUtils.getOrganization Exception in getting org name from SMS", e); 499 } 500 unknownOrgLookupCache.add(orgIdentifier); 501 throw new IdRepoException(IdRepoBundle.BUNDLE_NAME, IdRepoErrorCode.NO_MAPPING_FOUND, args); 502 } 503 } else if (LDAPUtils.isDN(orgIdentifier)) { 504 id = orgIdentifier; 505 try { 506 // Search for realms with orgIdentifier name 507 OrganizationConfigManager ocm = 508 new OrganizationConfigManager(token, orgIdentifier); 509 } catch (SMSException smse) { 510 if (debug.messageEnabled()) { 511 debug.message("IdUtils.getOrganization Exception in getting org name from SMS", smse); 512 } 513 unknownOrgLookupCache.add(orgIdentifier); 514 throw new IdRepoException(IdRepoBundle.BUNDLE_NAME, IdRepoErrorCode.NO_MAPPING_FOUND, args); 515 } 516 } else if (ServiceManager.isCoexistenceMode()) { 517 // Return the org DN as determined by AMStoreConnection 518 if (debug.messageEnabled()) { 519 debug.message("IdUtils.getOrganization: getting from AMSDK"); 520 } 521 try { 522 AMStoreConnection amsc = new AMStoreConnection(token); 523 id = amsc.getOrganizationDN(orgIdentifier, null); 524 } catch (AMException ame) { 525 if (debug.messageEnabled()) { 526 debug.message("IdUtils.getOrganization Exception in " 527 + "getting org name from AMSDK", ame); 528 } 529 throw convertAMException(ame); 530 } 531 } else { 532 // Get the realm name from SMS 533 if (debug.messageEnabled()) { 534 debug.message("IdUtils.getOrganization: getting from " + 535 "SMS realms"); 536 } 537 try { 538 boolean foundOrg = false; 539 ServiceManager sm = new ServiceManager(token); 540 // First search for realms with orgIdentifier name 541 OrganizationConfigManager ocm = sm.getOrganizationConfigManager("/"); 542 Set<String> subOrgNames = ocm.getSubOrganizationNames(orgIdentifier, true); 543 if (CollectionUtils.isNotEmpty(subOrgNames)) { 544 if (subOrgNames.size() == 1) { 545 id = DNMapper.orgNameToDN(subOrgNames.iterator().next()); 546 foundOrg = true; 547 } else { 548 for (String subRealmName : subOrgNames) { 549 // check for orgIdentifier 550 StringTokenizer st = new StringTokenizer(subRealmName, "/"); 551 // Need to handle the scenario where multiple 552 // sub-realm with the same name should not be 553 // allowed 554 while (st.hasMoreTokens()) { 555 if (st.nextToken().equalsIgnoreCase(orgIdentifier)) { 556 if (!foundOrg) { 557 id = DNMapper.orgNameToDN(subRealmName); 558 foundOrg = true; 559 } else { 560 throw new IdRepoException(IdRepoBundle.BUNDLE_NAME, 561 IdRepoErrorCode.MULTIPLE_MAPPINGS_FOUND, args); 562 } 563 } 564 } 565 } 566 } 567 } 568 569 // Check if organization name has been determined 570 if (debug.messageEnabled()) { 571 debug.message("IdUtils.getOrganization: getting from " + 572 "SMS realms aliases"); 573 } 574 // perform organization alias search 575 Set<String> vals = new HashSet<>(1); 576 vals.add(orgIdentifier); 577 Set orgAliases = sm.searchOrganizationNames( 578 IdConstants.REPO_SERVICE, 579 IdConstants.ORGANIZATION_ALIAS_ATTR, vals); 580 if (!foundOrg && CollectionUtils.isEmpty(orgAliases)) { 581 if (debug.warningEnabled()) { 582 debug.warning("IdUtils.getOrganization Unable to find Org name for: " + orgIdentifier); 583 } 584 unknownOrgLookupCache.add(orgIdentifier); 585 throw new IdRepoException(IdRepoBundle.BUNDLE_NAME, 586 IdRepoErrorCode.NO_MAPPING_FOUND, args); 587 } else if (CollectionUtils.isNotEmpty(orgAliases) && foundOrg) { 588 // Check to see if there is one alias, which points to the same realm as the one previously found 589 boolean sameRealm = false; 590 if (orgAliases.size() == 1) { 591 String aliasId = DNMapper.orgNameToDN((String)orgAliases.iterator().next()); 592 if (StringUtils.isEqualTo(aliasId, id)) { 593 // The realm has an alias that is equivalent to the realm name 594 sameRealm = true; 595 } 596 } 597 if (!sameRealm) { 598 // Multiple realms should not have the same alias 599 if (debug.warningEnabled()) { 600 debug.warning("IdUtils.getOrganization Multiple " + 601 " matching Orgs found for: " + orgIdentifier); 602 } 603 throw new IdRepoException(IdRepoBundle.BUNDLE_NAME, 604 IdRepoErrorCode.MULTIPLE_MAPPINGS_FOUND, args); 605 } 606 } else if (CollectionUtils.isNotEmpty(orgAliases) && (orgAliases.size() > 1)) { 607 // Multiple realms should not have the same alias 608 if (debug.warningEnabled()) { 609 debug.warning("IdUtils.getOrganization Multiple " + 610 " matching Orgs found for: " + orgIdentifier); 611 } 612 throw new IdRepoException(IdRepoBundle.BUNDLE_NAME, 613 IdRepoErrorCode.MULTIPLE_MAPPINGS_FOUND, args); 614 } 615 if (!foundOrg) { 616 String tmpS = (String) orgAliases.iterator().next(); 617 id = DNMapper.orgNameToDN(tmpS); 618 } 619 } catch (SMSException smse) { 620 if (debug.messageEnabled()) { 621 debug.message("IdUtils.getOrganization Exception in " 622 + "getting org name from SMS", smse); 623 } 624 unknownOrgLookupCache.add(orgIdentifier); 625 throw new IdRepoException(IdRepoBundle.BUNDLE_NAME, IdRepoErrorCode.NO_MAPPING_FOUND, args); 626 } 627 } 628 629 if (debug.messageEnabled()) { 630 debug.message("IdUtils:getOrganization Search for OrgIdentifier:" + 631 orgIdentifier + " returning realm DN: " + id); 632 } 633 634 // Add to cache and return id 635 orgIdentifierToOrgName.put(orgIdentifier, id); 636 return id; 637 } 638 639 /** 640 * Clears the cache containing orgIdentifiers to organization names 641 */ 642 protected static void clearOrganizationNamesCache() { 643 orgIdentifierToOrgName.clear(); 644 orgStatusCache.clear(); 645 unknownOrgLookupCache.clear(); 646 if (debug.messageEnabled()) { 647 debug.message("IdUtils.clearOrganizationNamesCache called"); 648 } 649 } 650 651 /** 652 * Returs true or false, depending on if this organization is enabled or 653 * not. The organization string passed to this method should be an 654 * identifier returned from the method 655 * <code> IdUtils.getOrganization </code>. In the default mode, where 656 * relams are enabled but backward comaptibility is required, this checks 657 * for organization status in the AM enabled Sun DS. Otherwise, it checks 658 * for organization status from the realms tree. 659 * 660 * @param token token SSOToken a valid SSOToken. 661 * @param org name of the organization of interest. 662 * @return <code>true</code> if org is active; 663 * otherwise <code>false</code> 664 * @throws IdRepoException if there are repository related error conditions. 665 * @throws SSOException If user's single sign on token is invalid. 666 */ 667 public static boolean isOrganizationActive(SSOToken token, String org) 668 throws IdRepoException, SSOException { 669 // Check the cache 670 if (orgStatusCache.containsKey(org)) { 671 return orgStatusCache.get(org); 672 } 673 boolean isActive = true; 674 // Need to initialize ServiceManager by creating the constructor 675 if (!ServiceManager.isCoexistenceMode()) { 676 // Pick it up from the realms tree. 677 try { 678 OrganizationConfigManager ocm = new OrganizationConfigManager( 679 token, org); 680 if (ocm == null) { 681 Object[] args = { org }; 682 throw new IdRepoException(IdRepoBundle.BUNDLE_NAME, IdRepoErrorCode.NO_MAPPING_FOUND, args); 683 } 684 Map attributes = ocm.getAttributes(IdConstants.REPO_SERVICE); 685 Set vals = (Set) attributes 686 .get(IdConstants.ORGANIZATION_STATUS_ATTR); 687 if (vals == null || vals.isEmpty()) { 688 isActive = true; 689 } else { 690 String stringActive = (String) vals.iterator().next(); 691 isActive = stringActive.equalsIgnoreCase("Active"); 692 } 693 } catch (SMSException smse) { 694 Object args[] = { org }; 695 throw new IdRepoException(IdRepoBundle.BUNDLE_NAME, IdRepoErrorCode.NO_MAPPING_FOUND, args); 696 } 697 } else if (ServiceManager.isAMSDKEnabled()) { 698 // Return the org DN as determined by AMStoreConnection. 699 try { 700 AMStoreConnection amsc = new AMStoreConnection(token); 701 AMOrganization orgObj = amsc.getOrganization(org); 702 isActive = orgObj.isActivated(); 703 } catch (AMException ame) { 704 throw convertAMException(ame); 705 } 706 } 707 // Add to cache 708 orgStatusCache.put(org, isActive); 709 return isActive; 710 } 711 712 private static void initializeForGetIdentity() { 713 // Initialize root realm, if not already initalized 714 if (ROOT_SUFFIX == null) { 715 ROOT_SUFFIX = SMSEntry.getRootSuffix(); 716 StringBuilder sb = new StringBuilder(100); 717 sb.append(SMSEntry.SERVICES_RDN) 718 .append(SMSEntry.COMMA).append(ROOT_SUFFIX); 719 SERVICES_SUFFIX = DNUtils.normalizeDN(sb.toString()); 720 } 721 722 // Initialize organization and user naming attributes 723 if ((ORG_NAMING_ATTR == null) || (USER_NAMING_ATTR == null)) { 724 try { 725 ORG_NAMING_ATTR = AMStoreConnection.getNamingAttribute( 726 AMObject.ORGANIZATION).toLowerCase() + "="; 727 USER_NAMING_ATTR = AMStoreConnection.getNamingAttribute( 728 AMObject.USER).toLowerCase() + "="; 729 } catch (AMException ame) { 730 if (debug.warningEnabled()) { 731 debug.warning("IdUtils: unable to get naming " + 732 "attribute for org/user. Using \"o\"/\"uid\""); 733 } 734 ORG_NAMING_ATTR = "o="; 735 USER_NAMING_ATTR = "uid="; 736 } 737 } 738 } 739 740 private static void initializeSpecialUsers() { 741 // Populate special users 742 if (specialUsers.isEmpty()) { 743 String susers = SystemProperties.get( 744 Constants.AUTHENTICATION_SPECIAL_USERS, ""); 745 StringTokenizer st = new StringTokenizer( 746 susers, "|"); 747 while (st.hasMoreTokens()) { 748 specialUsers.add(DNUtils.normalizeDN(st.nextToken())); 749 } 750 susers = SystemProperties.get( 751 "com.sun.identity.authentication.super.user", ""); 752 specialUsers.add(DNUtils.normalizeDN(susers)); 753 } 754 } 755 756 /** 757 * Returns an IdRepoException based on an <code>AMException</code> 758 * 759 * @param ame 760 * @return IdRepoException based on ame. 761 */ 762 public static IdRepoException convertAMException(AMException ame) { 763 Object[] args = ame.getMessageArgs(); 764 String eCode = ame.getErrorCode(); 765 IdRepoException ide = null; 766 if (args == null) { 767 ide = new IdRepoException("amProfile", eCode, null); 768 } else { 769 ide = new IdRepoException("amProfile", ame.getErrorCode(), args); 770 } 771 ide.setLDAPErrorCode(ame.getLDAPErrorCode()); 772 return ide; 773 } 774 775 private static void loadDefaultTypes() { 776 supportedTypes.add(IdType.REALM); 777 supportedTypes.add(IdType.AGENT); 778 supportedTypes.add(IdType.USER); 779 supportedTypes.add(IdType.ROLE); 780 supportedTypes.add(IdType.GROUP); 781 supportedTypes.add(IdType.FILTEREDROLE); 782 mapSupportedTypes.put(IdType.REALM.getName(), IdType.REALM); 783 mapSupportedTypes.put(IdType.USER.getName(), IdType.USER); 784 mapSupportedTypes.put(IdType.ROLE.getName(), IdType.ROLE); 785 mapSupportedTypes.put(IdType.FILTEREDROLE.getName(), 786 IdType.FILTEREDROLE); 787 mapSupportedTypes.put(IdType.AGENT.getName(), IdType.AGENT); 788 mapSupportedTypes.put(IdType.GROUP.getName(), IdType.GROUP); 789 Set memberSet = new HashSet(); 790 memberSet.add(IdType.ROLE); 791 memberSet.add(IdType.GROUP); 792 memberSet.add(IdType.FILTEREDROLE); 793 typesCanBeMemberOf.put(IdType.USER.getName(), memberSet); 794 Set memberShipSet = new HashSet(); 795 memberShipSet.add(IdType.USER); 796 typesCanHaveMembers.put(IdType.ROLE.getName(), memberShipSet); 797 typesCanHaveMembers.put(IdType.GROUP.getName(), memberShipSet); 798 typesCanHaveMembers.put(IdType.FILTEREDROLE.getName(), memberShipSet); 799 typesCanAddMembers.put(IdType.GROUP.getName(), memberShipSet); 800 typesCanAddMembers.put(IdType.ROLE.getName(), memberShipSet); 801 } 802 803 private static Set getMemberSet(Set members) { 804 Set memberSet = new HashSet(members.size() * 2); 805 for (Iterator iter = members.iterator(); iter.hasNext();) { 806 String currType = (String) iter.next(); 807 memberSet.add(new IdType(currType)); 808 } 809 return memberSet; 810 } 811 812 /** 813 * Returns the user name extracted from the uuid 814 * if the orgName supplied in the parameter is 815 * not same realm name in uuid then <code>IdRepoException</code> 816 * is thrown 817 * 818 * @param uuid uuid of the user 819 * @param orgName the org user is trying to login to 820 * @return user name 821 * @throws IdRepoException 822 */ 823 public static String getIdentityName(String uuid, String orgName) 824 throws IdRepoException { 825 String username = uuid; 826 // Check uuid 827 if ((uuid != null) && uuid.toLowerCase().startsWith("id=")) { 828 // Could be universal id, get the identity object 829 AMIdentity id = new AMIdentity(null, uuid); 830 username = id.getName(); 831 // Check the realm names 832 String realm = DNUtils.normalizeDN(id.getRealm()); 833 if (!DNUtils.normalizeDN(orgName).equals(realm)) { 834 Object[] args = {uuid, orgName}; 835 throw new IdRepoException(IdRepoBundle.BUNDLE_NAME, IdRepoErrorCode.REALM_NAME_NOT_MATCH_AUTHENTICATION_REALM, 836 args); 837 } 838 } 839 return (username); 840 } 841 842 /** 843 * Gets the AMIdentity of a user with username equal to uName that exists in realm 844 * 845 * @param uName username of the user to get. 846 * @param realm realm the user belongs to. 847 * @return The AMIdentity of user with username equal to uName. 848 */ 849 public static AMIdentity getIdentity(String uName, String realm) { 850 return getIdentity(uName, realm, null); 851 } 852 853 /** 854 * Gets the AMIdentity of a user with username equal to uName that exists in realm. 855 * If no AMIdentity found using username it will fall back to using userSearchAttributes if supplied. 856 * 857 * @param uName username of the user to get. 858 * @param realm realm the user belongs to. 859 * @param userSearchAttributes Alias Search Attribute Name. 860 * @return The AMIdentity of user with username equal to uName. 861 */ 862 public static AMIdentity getIdentity(String uName, String realm, Set<String> userSearchAttributes) { 863 AMIdentity theID = null; 864 865 AMIdentityRepository amIdRepo = getAMIdentityRepository(DNMapper.orgNameToDN(realm)); 866 867 IdSearchControl idsc = new IdSearchControl(); 868 idsc.setRecursive(true); 869 idsc.setAllReturnAttributes(true); 870 // search for the identity 871 Set<AMIdentity> results = Collections.EMPTY_SET; 872 try { 873 idsc.setMaxResults(0); 874 IdSearchResults searchResults = amIdRepo.searchIdentities(IdType.USER, uName, idsc); 875 876 if (searchResults.getSearchResults().isEmpty() && CollectionUtils.isNotEmpty(userSearchAttributes)) { 877 debug.message("IdUtils.getIdentity: searching user identity with alternative attributes {} ", 878 userSearchAttributes); 879 final Map<String, Set<String>> searchAVP = CollectionUtils.toAvPairMap(userSearchAttributes, uName); 880 idsc.setSearchModifiers(IdSearchOpModifier.OR, searchAVP); 881 //workaround as data store always adds 'user-naming-attribute' to searchfilter 882 searchResults = amIdRepo.searchIdentities(IdType.USER, "*", idsc); 883 } 884 885 if (searchResults != null) { 886 results = searchResults.getSearchResults(); 887 } 888 889 if (results == null || results.size() != 1) { 890 throw new IdRepoException("IdUtils" + 891 ".getIdentity : " + 892 "More than one user found"); 893 } 894 theID = results.iterator().next(); 895 } catch (IdRepoException e) { 896 debug.warning("Error searching for user identity"); 897 } catch (SSOException e) { 898 debug.warning("User's ssoToken has expired"); 899 } 900 return theID; 901 } 902 903 /** 904 * Returns <code>AMIdentityRepostiory</code> handle for an organization. 905 * 906 * @param orgDN the organization name. 907 * @return <code>AMIdentityRepostiory</code> object 908 */ 909 public static AMIdentityRepository getAMIdentityRepository(String orgDN) { 910 return AuthD.getAuth().getAMIdentityRepository(orgDN); 911 } 912 913 // SMS service listener to reinitialize if IdRepo service changes 914 static class IdUtilsListener implements com.sun.identity.sm.ServiceListener 915 { 916 public void schemaChanged(String serviceName, String version) { 917 initialize(); 918 } 919 920 public void globalConfigChanged(String serviceName, String version, 921 String groupName, String serviceComponent, int type) { 922 initialize(); 923 clearOrganizationNamesCache(); 924 } 925 926 public void organizationConfigChanged(String serviceName, 927 String version, String orgName, String groupName, 928 String serviceComponent, int type) { 929 clearOrganizationNamesCache(); 930 } 931 } 932 933}