001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2012 profiq s.r.o. 025 * Portions Copyright 2012-2014 ForgeRock AS 026 */ 027package org.opends.server.extensions; 028 029import org.forgerock.i18n.LocalizableMessage; 030import org.forgerock.i18n.slf4j.LocalizedLogger; 031import org.forgerock.opendj.ldap.ResultCode; 032import 033 org.opends.server.admin.std.server.PasswordExpirationTimeVirtualAttributeCfg; 034import org.opends.server.api.AuthenticationPolicy; 035import org.opends.server.api.VirtualAttributeProvider; 036import org.opends.server.core.PasswordPolicyState; 037import org.opends.server.core.SearchOperation; 038import org.opends.server.schema.GeneralizedTimeSyntax; 039import org.opends.server.types.*; 040import static org.opends.messages.ExtensionMessages.*; 041 042/** 043 * Provider for the password expiration time virtual attribute. 044 */ 045public class PasswordExpirationTimeVirtualAttributeProvider 046 extends VirtualAttributeProvider<PasswordExpirationTimeVirtualAttributeCfg> 047{ 048 049 /** 050 * Debug tracer to log debugging information. 051 */ 052 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 053 054 /** 055 * Default constructor. 056 */ 057 public PasswordExpirationTimeVirtualAttributeProvider() 058 { 059 super(); 060 } 061 062 /** {@inheritDoc} */ 063 @Override 064 public boolean isMultiValued() 065 { 066 return false; 067 } 068 069 /** {@inheritDoc} */ 070 @Override 071 public Attribute getValues(Entry entry, VirtualAttributeRule rule) 072 { 073 // Do not process LDAP operational entries. 074 if (!entry.isSubentry() && !entry.isLDAPSubentry()) 075 { 076 long expirationTime = getPasswordExpirationTime(entry); 077 if (expirationTime == -1) 078 { 079 // It does not expire. 080 return Attributes.empty(rule.getAttributeType()); 081 } 082 return Attributes.create(rule.getAttributeType(), 083 GeneralizedTimeSyntax.createGeneralizedTimeValue(expirationTime)); 084 } 085 086 return Attributes.empty(rule.getAttributeType()); 087 } 088 089 /** {@inheritDoc} */ 090 @Override 091 public boolean isSearchable(VirtualAttributeRule rule, 092 SearchOperation searchOperation, 093 boolean isPreIndexed) 094 { 095 return false; 096 } 097 098 /** {@inheritDoc} */ 099 @Override 100 public void processSearch(VirtualAttributeRule rule, 101 SearchOperation searchOperation) 102 { 103 searchOperation.setResultCode(ResultCode.UNWILLING_TO_PERFORM); 104 105 LocalizableMessage message = 106 ERR_PWDEXPTIME_VATTR_NOT_SEARCHABLE.get( 107 rule.getAttributeType().getNameOrOID()); 108 searchOperation.appendErrorMessage(message); 109 } 110 111 /** {@inheritDoc} */ 112 @Override 113 public boolean hasValue(Entry entry, VirtualAttributeRule rule) 114 { 115 // Do not process LDAP operational entries. 116 return !entry.isSubentry() 117 && !entry.isLDAPSubentry() 118 && getPasswordExpirationTime(entry) != -1; 119 } 120 121 /** 122 * Utility method to wrap the PasswordPolicyState.getExpirationTime(). 123 * 124 * @param entry LDAP entry 125 * @return Expiration time in milliseconds since the epoch. 126 */ 127 private long getPasswordExpirationTime(Entry entry) 128 { 129 // Do not process LDAP operational entries. 130 131 AuthenticationPolicy policy = null; 132 133 try 134 { 135 policy = AuthenticationPolicy.forUser(entry, false); 136 } 137 catch (DirectoryException de) 138 { 139 logger.error(de.getMessageObject()); 140 141 logger.traceException(de, "Failed to retrieve password policy for user %s", 142 entry.getName()); 143 } 144 145 if (policy == null) 146 { 147 // No authentication policy: debug log this as an error since all 148 // entries should have at least the default password policy. 149 logger.trace("No applicable password policy for user %s", entry.getName()); 150 } 151 else if (policy.isPasswordPolicy()) 152 { 153 PasswordPolicyState pwpState = null; 154 155 try 156 { 157 pwpState = 158 (PasswordPolicyState) policy.createAuthenticationPolicyState(entry); 159 } 160 catch (DirectoryException de) 161 { 162 logger.error(de.getMessageObject()); 163 164 logger.traceException(de, "Failed to retrieve password policy state for user %s", 165 entry.getName()); 166 } 167 168 return pwpState.getPasswordExpirationTime(); 169 170 } 171 else 172 { 173 // Not a password policy, could be PTA, etc. 174 logger.trace("Authentication policy %s found for user %s is not a password policy", 175 policy.getDN(), entry.getName()); 176 } 177 178 return -1L; 179 } 180}