001/*
002 *   Licensed to the Apache Software Foundation (ASF) under one
003 *   or more contributor license agreements.  See the NOTICE file
004 *   distributed with this work for additional information
005 *   regarding copyright ownership.  The ASF licenses this file
006 *   to you under the Apache License, Version 2.0 (the
007 *   "License"); you may not use this file except in compliance
008 *   with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *   Unless required by applicable law or agreed to in writing,
013 *   software distributed under the License is distributed on an
014 *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *   KIND, either express or implied.  See the License for the
016 *   specific language governing permissions and limitations
017 *   under the License.
018 *
019 */
020
021package org.apache.directory.server.core.authn;
022
023
024import java.nio.charset.StandardCharsets;
025import java.util.Base64;
026
027import org.apache.directory.api.ldap.model.constants.SchemaConstants;
028import org.apache.directory.api.util.Strings;
029
030
031/**
032 * A class to hold the data of historical passwords of a entry.
033 * Note: This class's natural ordering is inconsistent with the equals() method
034 *       hence it is advised not to use this in any implementations of sorted sets
035 *       Instead use Collections.sort() to sort the collection of PasswordHistory objects.
036 *
037 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
038 * @version $Rev$, $Date$
039 */
040public class PasswordHistory implements Comparable<PasswordHistory>
041{
042    /** time when password was last changed */
043    private String time;
044
045    /** the syntax OID that is to be used on the password data */
046    private String syntaxOID = SchemaConstants.OCTET_STRING_SYNTAX;
047
048    /** the length of the password data */
049    private int length;
050
051    /** password octet string */
052    private String data;
053
054    private static final char DELIMITER = '#';
055
056
057    /**
058     * Create a new instance of PasswordHistory
059     * 
060     * @param pwdHistoryVal The history date
061     */
062    public PasswordHistory( String pwdHistoryVal )
063    {
064        int pos = pwdHistoryVal.indexOf( DELIMITER );
065        time = pwdHistoryVal.substring( 0, pos );
066
067        pos++;
068        int nextPos = pwdHistoryVal.indexOf( DELIMITER, pos );
069        syntaxOID = pwdHistoryVal.substring( pos, nextPos );
070
071        nextPos++;
072        pos = pwdHistoryVal.indexOf( DELIMITER, nextPos );
073        length = Integer.parseInt( pwdHistoryVal.substring( nextPos, pos ) );
074
075        data = pwdHistoryVal.substring( pos + 1 );
076    }
077
078
079    /**
080     * Create a new instance of PasswordHistory
081     * 
082     * @param time The time we changed the password
083     * @param password The password to store
084     */
085    public PasswordHistory( String time, byte[] password )
086    {
087        this.time = time;
088        this.data = new String( Base64.getEncoder().encode( password ), StandardCharsets.UTF_8 );
089        this.length = data.length();
090    }
091
092
093    public byte[] getHistoryValue()
094    {
095        StringBuilder sb = new StringBuilder();
096
097        sb.append( time ).append( DELIMITER );
098
099        sb.append( syntaxOID ).append( DELIMITER );
100
101        sb.append( length ).append( DELIMITER );
102
103        sb.append( data );
104
105        return Strings.getBytesUtf8( sb.toString() );
106    }
107
108
109    public String getTime()
110    {
111        return time;
112    }
113
114
115    public String getSyntaxOID()
116    {
117        return syntaxOID;
118    }
119
120
121    public int getLength()
122    {
123        return length;
124    }
125
126
127    public byte[] getPassword()
128    {
129        return Base64.getDecoder().decode( data );
130    }
131
132
133    /**
134     * {@inheritDoc}
135     */
136    @Override
137    public int compareTo( PasswordHistory o )
138    {
139        return o.getTime().compareTo( time );
140    }
141
142
143    /**
144     * @see Object#toString()
145     */
146    @Override
147    public boolean equals( Object o )
148    {
149        if ( !( o instanceof PasswordHistory ) )
150        {
151            return false;
152        }
153
154        PasswordHistory other = ( PasswordHistory ) o;
155
156        return getTime().equals( other.getTime() ) && data.equals( other.data );
157    }
158
159
160    @Override
161    public int hashCode()
162    {
163        final int prime = 31;
164        int result = 1;
165        result = prime * result + ( ( data == null ) ? 0 : data.hashCode() );
166        result = prime * result + length;
167        result = prime * result + ( ( syntaxOID == null ) ? 0 : syntaxOID.hashCode() );
168        result = prime * result + ( ( time == null ) ? 0 : time.hashCode() );
169        return result;
170    }
171
172
173    @Override
174    public String toString()
175    {
176        return "PasswordHistory [time=" + time + ", syntaxOID=" + syntaxOID + ", length=" + length + ", data=" + data
177            + "]";
178    }
179}