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}