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-2016 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 org.forgerock.opendj.server.config.server.PasswordExpirationTimeVirtualAttributeCfg;
023import org.opends.server.api.AuthenticationPolicy;
024import org.opends.server.api.VirtualAttributeProvider;
025import org.opends.server.core.PasswordPolicyState;
026import org.opends.server.core.SearchOperation;
027import org.opends.server.schema.GeneralizedTimeSyntax;
028import org.opends.server.types.*;
029
030import static org.opends.messages.ExtensionMessages.*;
031
032/** Provider for the password expiration time virtual attribute. */
033public class PasswordExpirationTimeVirtualAttributeProvider
034  extends VirtualAttributeProvider<PasswordExpirationTimeVirtualAttributeCfg>
035{
036  /** Debug tracer to log debugging information. */
037  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
038
039  /** Default constructor. */
040  public PasswordExpirationTimeVirtualAttributeProvider()
041  {
042    super();
043  }
044
045  @Override
046  public boolean isMultiValued()
047  {
048    return false;
049  }
050
051  @Override
052  public Attribute getValues(Entry entry, VirtualAttributeRule rule)
053  {
054    // Do not process LDAP operational entries.
055    if (!entry.isSubentry() && !entry.isLDAPSubentry())
056    {
057      long expirationTime = getPasswordExpirationTime(entry);
058      if (expirationTime == -1)
059      {
060        // It does not expire.
061        return Attributes.empty(rule.getAttributeType());
062      }
063      return Attributes.create(rule.getAttributeType(),
064          GeneralizedTimeSyntax.createGeneralizedTimeValue(expirationTime));
065    }
066
067    return Attributes.empty(rule.getAttributeType());
068  }
069
070  @Override
071  public boolean isSearchable(VirtualAttributeRule rule,
072                              SearchOperation searchOperation,
073                              boolean isPreIndexed)
074  {
075    return false;
076  }
077
078  @Override
079  public void processSearch(VirtualAttributeRule rule,
080                            SearchOperation searchOperation)
081  {
082    searchOperation.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
083
084    LocalizableMessage message =
085            ERR_PWDEXPTIME_VATTR_NOT_SEARCHABLE.get(
086            rule.getAttributeType().getNameOrOID());
087    searchOperation.appendErrorMessage(message);
088  }
089
090  @Override
091  public boolean hasValue(Entry entry, VirtualAttributeRule rule)
092  {
093    // Do not process LDAP operational entries.
094    return !entry.isSubentry()
095        && !entry.isLDAPSubentry()
096        && getPasswordExpirationTime(entry) != -1;
097  }
098
099  /**
100   * Utility method to wrap the PasswordPolicyState.getExpirationTime().
101   *
102   * @param entry LDAP entry
103   * @return  Expiration time in milliseconds since the epoch.
104   */
105  private long getPasswordExpirationTime(Entry entry)
106  {
107    // Do not process LDAP operational entries.
108
109    AuthenticationPolicy policy = null;
110
111    try
112    {
113      policy = AuthenticationPolicy.forUser(entry, false);
114    }
115    catch (DirectoryException de)
116    {
117      logger.error(de.getMessageObject());
118
119      logger.traceException(de, "Failed to retrieve password policy for user %s",
120          entry.getName());
121    }
122
123    if (policy == null)
124    {
125      // No authentication policy: debug log this as an error since all
126      // entries should have at least the default password policy.
127      logger.trace("No applicable password policy for user %s", entry.getName());
128    }
129    else if (policy.isPasswordPolicy())
130    {
131      PasswordPolicyState pwpState = null;
132
133      try
134      {
135        pwpState =
136          (PasswordPolicyState) policy.createAuthenticationPolicyState(entry);
137      }
138      catch (DirectoryException de)
139      {
140        logger.error(de.getMessageObject());
141
142        logger.traceException(de, "Failed to retrieve password policy state for user %s",
143            entry.getName());
144      }
145
146      return pwpState.getPasswordExpirationTime();
147    }
148    else
149    {
150      // Not a password policy, could be PTA, etc.
151      logger.trace("Authentication policy %s found for user %s is not a password policy",
152          policy.getDN(), entry.getName());
153    }
154
155    return -1L;
156  }
157}