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 2014 ForgeRock AS 026 */ 027package org.forgerock.opendj.ldap.controls; 028 029import static com.forgerock.opendj.ldap.CoreMessages.ERR_PWEXPIRED_CONTROL_BAD_OID; 030import static com.forgerock.opendj.ldap.CoreMessages.ERR_PWEXPIRED_CONTROL_INVALID_VALUE; 031 032import org.forgerock.i18n.LocalizableMessage; 033import org.forgerock.opendj.ldap.ByteString; 034import org.forgerock.opendj.ldap.DecodeException; 035import org.forgerock.opendj.ldap.DecodeOptions; 036 037import org.forgerock.util.Reject; 038 039/** 040 * The Netscape password expired response control as defined in 041 * draft-vchu-ldap-pwd-policy. This control indicates to a client that their 042 * password has expired and must be changed. This control always has a value 043 * which is the string {@code "0"}. 044 * 045 * <pre> 046 * Connection connection = ...; 047 * String DN = ...; 048 * char[] password = ...; 049 * 050 * try { 051 * connection.bind(DN, password); 052 * } catch (LdapException e) { 053 * Result result = e.getResult(); 054 * try { 055 * PasswordExpiredResponseControl control = 056 * result.getControl(PasswordExpiredResponseControl.DECODER, 057 * new DecodeOptions()); 058 * if (!(control == null) && control.hasValue()) { 059 * // Password expired 060 * } 061 * } catch (DecodeException de) { 062 * // Failed to decode the response control. 063 * } 064 * } 065 * </pre> 066 * 067 * @see <a 068 * href="http://tools.ietf.org/html/draft-vchu-ldap-pwd-policy">draft-vchu-ldap-pwd-policy 069 * - Password Policy for LDAP Directories </a> 070 */ 071public final class PasswordExpiredResponseControl implements Control { 072 /** 073 * The OID for the Netscape password expired response control. 074 */ 075 public static final String OID = "2.16.840.1.113730.3.4.4"; 076 077 private final boolean isCritical; 078 079 private static final PasswordExpiredResponseControl CRITICAL_INSTANCE = 080 new PasswordExpiredResponseControl(true); 081 private static final PasswordExpiredResponseControl NONCRITICAL_INSTANCE = 082 new PasswordExpiredResponseControl(false); 083 084 /** 085 * A decoder which can be used for decoding the password expired response 086 * control. 087 */ 088 public static final ControlDecoder<PasswordExpiredResponseControl> DECODER = 089 new ControlDecoder<PasswordExpiredResponseControl>() { 090 091 public PasswordExpiredResponseControl decodeControl(final Control control, 092 final DecodeOptions options) throws DecodeException { 093 Reject.ifNull(control); 094 095 if (control instanceof PasswordExpiredResponseControl) { 096 return (PasswordExpiredResponseControl) control; 097 } 098 099 if (!control.getOID().equals(OID)) { 100 final LocalizableMessage message = 101 ERR_PWEXPIRED_CONTROL_BAD_OID.get(control.getOID(), OID); 102 throw DecodeException.error(message); 103 } 104 105 if (control.hasValue()) { 106 try { 107 Integer.parseInt(control.getValue().toString()); 108 } catch (final Exception e) { 109 final LocalizableMessage message = 110 ERR_PWEXPIRED_CONTROL_INVALID_VALUE.get(); 111 throw DecodeException.error(message); 112 } 113 } 114 115 return control.isCritical() ? CRITICAL_INSTANCE : NONCRITICAL_INSTANCE; 116 } 117 118 public String getOID() { 119 return OID; 120 } 121 }; 122 123 private static final ByteString CONTROL_VALUE = ByteString.valueOf("0"); 124 125 /** 126 * Creates a new Netscape password expired response control. 127 * 128 * @return The new control. 129 */ 130 public static PasswordExpiredResponseControl newControl() { 131 return NONCRITICAL_INSTANCE; 132 } 133 134 private PasswordExpiredResponseControl(final boolean isCritical) { 135 this.isCritical = isCritical; 136 } 137 138 /** {@inheritDoc} */ 139 public String getOID() { 140 return OID; 141 } 142 143 /** {@inheritDoc} */ 144 public ByteString getValue() { 145 return CONTROL_VALUE; 146 } 147 148 /** {@inheritDoc} */ 149 public boolean hasValue() { 150 return true; 151 } 152 153 /** {@inheritDoc} */ 154 public boolean isCritical() { 155 return isCritical; 156 } 157 158 /** {@inheritDoc} */ 159 @Override 160 public String toString() { 161 final StringBuilder builder = new StringBuilder(); 162 builder.append("PasswordExpiredResponseControl(oid="); 163 builder.append(getOID()); 164 builder.append(", criticality="); 165 builder.append(isCritical()); 166 builder.append(")"); 167 return builder.toString(); 168 } 169}