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