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