View Javadoc
1   /*
2    * Copyright (c) 2002-2014, Mairie de Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  package fr.paris.lutece.util.password;
35  
36  import fr.paris.lutece.portal.service.admin.AdminUserService;
37  import fr.paris.lutece.portal.service.util.AppPropertiesService;
38  import fr.paris.lutece.util.date.DateUtil;
39  
40  import java.sql.Timestamp;
41  
42  import java.util.ArrayList;
43  import java.util.Collections;
44  import java.util.Random;
45  
46  
47  /**
48   * Utility class used to generate random passwords
49   */
50  public final class PasswordUtil
51  {
52      private static final String PROPERTY_PASSWORD_SIZE = "randomPassword.size";
53      private static final int CONSTANT_NUMBER_LETTERS = 26;
54      private static final int CONSTANT_NUMBER_NUMBERS_BASE10 = 10;
55      private static final int CONSTANT_ASCII_CODE_A_UPPERCASE = 65;
56      private static final int CONSTANT_ASCII_CODE_A_LOWERCASE = 97;
57      private static final int CONSTANT_ASCII_CODE_ZERO = 48;
58      private static final char[] CONSTANT_SPECIAL_CHARACTERS = 
59          {
60              '!', ',', ':', '?', '$', '-', '@', '}', '{', '(', ')', '*', '+', '=', '[', ']', '%', '.',
61          };
62      private static final String CONSTANT_PASSWORD_BEGIN_REGEX = "^";
63      private static final String CONSTANT_PASSWORD_REGEX_NUM = "(?=.*[0-9])";
64      private static final String CONSTANT_PASSWORD_REGEX_SPECIAL = "(?=.*[^a-zA-Z0-9])";
65      private static final String CONSTANT_PASSWORD_REGEX_UPPER_LOWER = "(?=.*[a-z])(?=.*[A-Z])";
66      private static final String CONSTANT_PASSWORD_END_REGEX = "(.*)$";
67      private static final String PARAMETER_PASSWORD_MINIMUM_LENGTH = "password_minimum_length";
68  
69      /** Private Constructor */
70      private PasswordUtil(  )
71      {
72      }
73  
74      /**
75       * Generate a new random password
76       * @return the new password
77       */
78      public static String makePassword(  )
79      {
80          // reinitialize password
81          int nPasswordSize = AppPropertiesService.getPropertyInt( PROPERTY_PASSWORD_SIZE, 8 );
82          int nMinPasswordSize = AdminUserService.getIntegerSecurityParameter( PARAMETER_PASSWORD_MINIMUM_LENGTH );
83  
84          if ( nMinPasswordSize > nPasswordSize )
85          {
86              nPasswordSize = nMinPasswordSize;
87          }
88  
89          return makePassword( nPasswordSize, true, true, true );
90      }
91  
92      /**
93       * Generate a new random password
94       * @param nPasswordSize the password size
95       * @param bUpperAndLowerCase true if the password must contain upper and
96       *            lower case
97       * @param bNumbers if the password must contain numbers
98       * @param bSpecialCaracters if the password must contain special characters
99       *
100      * @return the new password
101      */
102     public static String makePassword( int nPasswordSize, boolean bUpperAndLowerCase, boolean bNumbers,
103         boolean bSpecialCaracters )
104     {
105         // reinitialize password
106         Random r = new Random(  );
107 
108         ArrayList<Character> listCharacters = new ArrayList<Character>( nPasswordSize );
109 
110         // No of Big letters
111         int nNumCapitalLetters = bUpperAndLowerCase ? ( r.nextInt( nPasswordSize - 3 ) + 1 ) : 0; // choose a number between 1 and CONSTANT_PASSWORD_SIZE -1
112 
113         // no on special characters
114         int nNumSpecial = bSpecialCaracters ? ( r.nextInt( nPasswordSize - 2 - nNumCapitalLetters ) + 1 ) : 0; // choose a number beetwen 1 and CONSTANT_PASSWORD_SIZE - a1
115 
116         // no of nos
117         int nNumNumbers = bNumbers ? ( r.nextInt( nPasswordSize - 1 - nNumCapitalLetters - nNumSpecial ) + 1 ) : 0; // choose a number to complete list of CONSTANT_PASSWORD_SIZE characters
118 
119         // no of small
120         int nNumSmallLetters = nPasswordSize - nNumCapitalLetters - nNumSpecial - nNumNumbers; // choose a number to complete list of CONSTANT_PASSWORD_SIZE characters
121 
122         for ( int j = 0; j < nNumCapitalLetters; j++ )
123         {
124             char c1 = (char) ( r.nextInt( CONSTANT_NUMBER_LETTERS ) + CONSTANT_ASCII_CODE_A_UPPERCASE );
125             listCharacters.add( Character.valueOf( c1 ) );
126         }
127 
128         for ( int j = 0; j < nNumSmallLetters; j++ )
129         {
130             char c1 = (char) ( r.nextInt( CONSTANT_NUMBER_LETTERS ) + CONSTANT_ASCII_CODE_A_LOWERCASE );
131             listCharacters.add( Character.valueOf( c1 ) );
132         }
133 
134         for ( int j = 0; j < nNumNumbers; j++ )
135         {
136             char c1 = (char) ( r.nextInt( CONSTANT_NUMBER_NUMBERS_BASE10 - 1 ) + CONSTANT_ASCII_CODE_ZERO );
137             listCharacters.add( Character.valueOf( c1 ) );
138         }
139 
140         for ( int j = 0; j < nNumSpecial; j++ )
141         {
142             char c1 = CONSTANT_SPECIAL_CHARACTERS[r.nextInt( CONSTANT_SPECIAL_CHARACTERS.length )];
143             listCharacters.add( Character.valueOf( c1 ) );
144         }
145 
146         Collections.shuffle( listCharacters );
147 
148         StringBuilder sbPassword = new StringBuilder( listCharacters.size(  ) );
149 
150         for ( Character myChar : listCharacters )
151         {
152             sbPassword.append( myChar );
153         }
154 
155         return sbPassword.toString(  );
156     }
157 
158     /**
159      * Check whether a password contains upper and lower case letters, special
160      * characters and numbers.
161      * @param strPassword The password to check
162      * @return True if the password format is correct, false otherwise
163      */
164     public static boolean checkPasswordFormat( String strPassword )
165     {
166         return checkPasswordFormat( strPassword, true, true, true );
167     }
168 
169     /**
170      * Check whether a password contains upper and lower case letters, special
171      * characters and numbers.
172      * @param strPassword The password to check
173      * @param bUpperAndLowerCase true if the password must contain upper and lower case
174      * @param bNumero if the password must contain numero
175      * @param bSpecialCaracters if the password must contain special characters
176      *
177      * @return True if the password format is correct, false otherwise
178      */
179     public static boolean checkPasswordFormat( String strPassword, boolean bUpperAndLowerCase, boolean bNumero,
180         boolean bSpecialCaracters )
181     {
182         if ( ( strPassword == null ) || strPassword.isEmpty(  ) )
183         {
184             return false;
185         }
186 
187         StringBuilder sbRegex = new StringBuilder( CONSTANT_PASSWORD_BEGIN_REGEX );
188 
189         if ( bUpperAndLowerCase )
190         {
191             sbRegex.append( CONSTANT_PASSWORD_REGEX_UPPER_LOWER );
192         }
193 
194         if ( bNumero )
195         {
196             sbRegex.append( CONSTANT_PASSWORD_REGEX_NUM );
197         }
198 
199         if ( bSpecialCaracters )
200         {
201             sbRegex.append( CONSTANT_PASSWORD_REGEX_SPECIAL );
202         }
203 
204         sbRegex.append( CONSTANT_PASSWORD_END_REGEX );
205 
206         return strPassword.matches( sbRegex.toString(  ) );
207     }
208 
209     /**
210      * Get the maximum valid date of a password starting from now with the given
211      * number of days.
212      * @param nNumberDay The number of days the password is valid
213      * @return The maximum valid date of a password
214      */
215     public static Timestamp getPasswordMaxValidDate( int nNumberDay )
216     {
217         if ( nNumberDay <= 0 )
218         {
219             return null;
220         }
221 
222         long nMilliSeconds = DateUtil.convertDaysInMiliseconds( nNumberDay );
223         Timestamp maxValidDate = new Timestamp( new java.util.Date(  ).getTime(  ) + nMilliSeconds );
224 
225         return maxValidDate;
226     }
227 }