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 2010 Sun Microsystems, Inc. 025 * Portions copyright 2013-2014 ForgeRock AS 026 */ 027package org.forgerock.opendj.ldap.controls; 028 029import static com.forgerock.opendj.util.StaticUtils.getExceptionMessage; 030import static com.forgerock.opendj.ldap.CoreMessages.*; 031 032import org.forgerock.i18n.LocalizableMessage; 033import org.forgerock.i18n.slf4j.LocalizedLogger; 034import org.forgerock.opendj.ldap.ByteString; 035import org.forgerock.opendj.ldap.DecodeException; 036import org.forgerock.opendj.ldap.DecodeOptions; 037import org.forgerock.util.Reject; 038 039/** 040 * The Netscape password expiring response control as defined in 041 * draft-vchu-ldap-pwd-policy. This control serves as a warning to clients that 042 * the user's password is about to expire. The only element contained in the 043 * control value is a string representation of the number of seconds until 044 * expiration. 045 * 046 * <pre> 047 * Connection connection = ...; 048 * String DN = ...; 049 * char[] password = ...; 050 * 051 * BindResult result = connection.bind(DN, password); 052 * try { 053 * PasswordExpiringResponseControl control = 054 * result.getControl(PasswordExpiringResponseControl.DECODER, 055 * new DecodeOptions()); 056 * if (!(control == null) && control.hasValue()) { 057 * // Password expires in control.getSecondsUntilExpiration() seconds 058 * } 059 * } catch (DecodeException de) { 060 * // Failed to decode the response control. 061 * } 062 * </pre> 063 * 064 * @see <a 065 * href="http://tools.ietf.org/html/draft-vchu-ldap-pwd-policy">draft-vchu-ldap-pwd-policy 066 * - Password Policy for LDAP Directories </a> 067 */ 068public final class PasswordExpiringResponseControl implements Control { 069 070 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 071 /** 072 * The OID for the Netscape password expiring response control. 073 */ 074 public static final String OID = "2.16.840.1.113730.3.4.5"; 075 076 /** 077 * A decoder which can be used for decoding the password expiring response 078 * control. 079 */ 080 public static final ControlDecoder<PasswordExpiringResponseControl> DECODER = 081 new ControlDecoder<PasswordExpiringResponseControl>() { 082 083 public PasswordExpiringResponseControl decodeControl(final Control control, 084 final DecodeOptions options) throws DecodeException { 085 Reject.ifNull(control); 086 087 if (control instanceof PasswordExpiringResponseControl) { 088 return (PasswordExpiringResponseControl) control; 089 } 090 091 if (!control.getOID().equals(OID)) { 092 final LocalizableMessage message = 093 ERR_PWEXPIRING_CONTROL_BAD_OID.get(control.getOID(), OID); 094 throw DecodeException.error(message); 095 } 096 097 if (!control.hasValue()) { 098 final LocalizableMessage message = ERR_PWEXPIRING_NO_CONTROL_VALUE.get(); 099 throw DecodeException.error(message); 100 } 101 102 int secondsUntilExpiration; 103 try { 104 secondsUntilExpiration = Integer.parseInt(control.getValue().toString()); 105 } catch (final Exception e) { 106 logger.debug(LocalizableMessage.raw("%s", e)); 107 108 final LocalizableMessage message = 109 ERR_PWEXPIRING_CANNOT_DECODE_SECONDS_UNTIL_EXPIRATION 110 .get(getExceptionMessage(e)); 111 throw DecodeException.error(message); 112 } 113 114 return new PasswordExpiringResponseControl(control.isCritical(), 115 secondsUntilExpiration); 116 } 117 118 public String getOID() { 119 return OID; 120 } 121 }; 122 123 /** 124 * Creates a new Netscape password expiring response control with the 125 * provided amount of time until expiration. 126 * 127 * @param secondsUntilExpiration 128 * The length of time in seconds until the password actually 129 * expires. 130 * @return The new control. 131 */ 132 public static PasswordExpiringResponseControl newControl(final int secondsUntilExpiration) { 133 return new PasswordExpiringResponseControl(false, secondsUntilExpiration); 134 } 135 136 /** The length of time in seconds until the password actually expires. */ 137 private final int secondsUntilExpiration; 138 139 private final boolean isCritical; 140 141 private PasswordExpiringResponseControl(final boolean isCritical, 142 final int secondsUntilExpiration) { 143 this.isCritical = isCritical; 144 this.secondsUntilExpiration = secondsUntilExpiration; 145 } 146 147 /** {@inheritDoc} */ 148 public String getOID() { 149 return OID; 150 } 151 152 /** 153 * Returns the length of time in seconds until the password actually 154 * expires. 155 * 156 * @return The length of time in seconds until the password actually 157 * expires. 158 */ 159 public int getSecondsUntilExpiration() { 160 return secondsUntilExpiration; 161 } 162 163 /** {@inheritDoc} */ 164 public ByteString getValue() { 165 return ByteString.valueOf(String.valueOf(secondsUntilExpiration)); 166 } 167 168 /** {@inheritDoc} */ 169 public boolean hasValue() { 170 return true; 171 } 172 173 /** {@inheritDoc} */ 174 public boolean isCritical() { 175 return isCritical; 176 } 177 178 /** {@inheritDoc} */ 179 @Override 180 public String toString() { 181 final StringBuilder builder = new StringBuilder(); 182 builder.append("PasswordExpiringResponseControl(oid="); 183 builder.append(getOID()); 184 builder.append(", criticality="); 185 builder.append(isCritical()); 186 builder.append(", secondsUntilExpiration="); 187 builder.append(secondsUntilExpiration); 188 builder.append(")"); 189 return builder.toString(); 190 } 191}