View Javadoc
1   /*
2    * Copyright (c) 2002-2022, City of 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.security.SecureRandom;
41  import java.sql.Timestamp;
42  
43  import java.util.ArrayList;
44  import java.util.Collections;
45  import java.util.Random;
46  
47  /**
48   * Utility class used to generate random passwords
49   */
50  public final class PasswordUtil
51  {
52      public static final String PROPERTY_PASSWORD_SIZE = "randomPassword.size";
53      public static final int CONSTANT_DEFAULT_RANDOM_PASSWORD_SIZE = 16;
54      private static final int CONSTANT_NUMBER_LETTERS = 26;
55      private static final int CONSTANT_NUMBER_NUMBERS_BASE10 = 10;
56      private static final int CONSTANT_ASCII_CODE_A_UPPERCASE = 65;
57      private static final int CONSTANT_ASCII_CODE_A_LOWERCASE = 97;
58      private static final int CONSTANT_ASCII_CODE_ZERO = 48;
59      private static final char [ ] CONSTANT_SPECIAL_CHARACTERS = {
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       * 
77       * @return the new password
78       */
79      public static String makePassword( )
80      {
81          // reinitialize password
82          int nPasswordSize = AppPropertiesService.getPropertyInt( PROPERTY_PASSWORD_SIZE, CONSTANT_DEFAULT_RANDOM_PASSWORD_SIZE );
83          int nMinPasswordSize = AdminUserService.getIntegerSecurityParameter( PARAMETER_PASSWORD_MINIMUM_LENGTH );
84  
85          if ( nMinPasswordSize > nPasswordSize )
86          {
87              nPasswordSize = nMinPasswordSize;
88          }
89  
90          return makePassword( nPasswordSize, true, true, true );
91      }
92  
93      /**
94       * Generate a new random password
95       * 
96       * @param nPasswordSize
97       *            the password size
98       * @param bUpperAndLowerCase
99       *            true if the password must contain upper and lower case
100      * @param bNumbers
101      *            if the password must contain numbers
102      * @param bSpecialCaracters
103      *            if the password must contain special characters
104      *
105      * @return the new password
106      */
107     public static String makePassword( int nPasswordSize, boolean bUpperAndLowerCase, boolean bNumbers, boolean bSpecialCaracters )
108     {
109         // reinitialize password
110         Random r = new SecureRandom( );
111 
112         ArrayList<Character> listCharacters = new ArrayList<>( nPasswordSize );
113 
114         // No of Big letters
115         int nNumCapitalLetters = bUpperAndLowerCase ? ( r.nextInt( nPasswordSize - 3 ) + 1 ) : 0; // choose a number between 1 and CONSTANT_PASSWORD_SIZE -1
116 
117         // no on special characters
118         int nNumSpecial = bSpecialCaracters ? ( r.nextInt( nPasswordSize - 2 - nNumCapitalLetters ) + 1 ) : 0; // choose a number beetwen 1 and
119                                                                                                                // CONSTANT_PASSWORD_SIZE - a1
120 
121         // no of nos
122         int nNumNumbers = bNumbers ? ( r.nextInt( nPasswordSize - 1 - nNumCapitalLetters - nNumSpecial ) + 1 ) : 0; // choose a number to complete list of
123                                                                                                                     // CONSTANT_PASSWORD_SIZE characters
124 
125         // no of small
126         int nNumSmallLetters = nPasswordSize - nNumCapitalLetters - nNumSpecial - nNumNumbers; // choose a number to complete list of CONSTANT_PASSWORD_SIZE
127                                                                                                // characters
128 
129         for ( int j = 0; j < nNumCapitalLetters; j++ )
130         {
131             char c1 = (char) ( r.nextInt( CONSTANT_NUMBER_LETTERS ) + CONSTANT_ASCII_CODE_A_UPPERCASE );
132             listCharacters.add( Character.valueOf( c1 ) );
133         }
134 
135         for ( int j = 0; j < nNumSmallLetters; j++ )
136         {
137             char c1 = (char) ( r.nextInt( CONSTANT_NUMBER_LETTERS ) + CONSTANT_ASCII_CODE_A_LOWERCASE );
138             listCharacters.add( Character.valueOf( c1 ) );
139         }
140 
141         for ( int j = 0; j < nNumNumbers; j++ )
142         {
143             char c1 = (char) ( r.nextInt( CONSTANT_NUMBER_NUMBERS_BASE10 - 1 ) + CONSTANT_ASCII_CODE_ZERO );
144             listCharacters.add( Character.valueOf( c1 ) );
145         }
146 
147         for ( int j = 0; j < nNumSpecial; j++ )
148         {
149             char c1 = CONSTANT_SPECIAL_CHARACTERS [r.nextInt( CONSTANT_SPECIAL_CHARACTERS.length )];
150             listCharacters.add( Character.valueOf( c1 ) );
151         }
152 
153         Collections.shuffle( listCharacters, r );
154 
155         StringBuilder sbPassword = new StringBuilder( listCharacters.size( ) );
156 
157         for ( Character myChar : listCharacters )
158         {
159             sbPassword.append( myChar );
160         }
161 
162         return sbPassword.toString( );
163     }
164 
165     /**
166      * Check whether a password contains upper and lower case letters, special characters and numbers.
167      * 
168      * @param strPassword
169      *            The password to check
170      * @return True if the password format is correct, false otherwise
171      */
172     public static boolean checkPasswordFormat( String strPassword )
173     {
174         return checkPasswordFormat( strPassword, true, true, true );
175     }
176 
177     /**
178      * Check whether a password contains upper and lower case letters, special characters and numbers.
179      * 
180      * @param strPassword
181      *            The password to check
182      * @param bUpperAndLowerCase
183      *            true if the password must contain upper and lower case
184      * @param bNumero
185      *            if the password must contain numero
186      * @param bSpecialCaracters
187      *            if the password must contain special characters
188      *
189      * @return True if the password format is correct, false otherwise
190      */
191     public static boolean checkPasswordFormat( String strPassword, boolean bUpperAndLowerCase, boolean bNumero, boolean bSpecialCaracters )
192     {
193         if ( ( strPassword == null ) || strPassword.isEmpty( ) )
194         {
195             return false;
196         }
197 
198         StringBuilder sbRegex = new StringBuilder( CONSTANT_PASSWORD_BEGIN_REGEX );
199 
200         if ( bUpperAndLowerCase )
201         {
202             sbRegex.append( CONSTANT_PASSWORD_REGEX_UPPER_LOWER );
203         }
204 
205         if ( bNumero )
206         {
207             sbRegex.append( CONSTANT_PASSWORD_REGEX_NUM );
208         }
209 
210         if ( bSpecialCaracters )
211         {
212             sbRegex.append( CONSTANT_PASSWORD_REGEX_SPECIAL );
213         }
214 
215         sbRegex.append( CONSTANT_PASSWORD_END_REGEX );
216 
217         return strPassword.matches( sbRegex.toString( ) );
218     }
219 
220     /**
221      * Get the maximum valid date of a password starting from now with the given number of days.
222      * 
223      * @param nNumberDay
224      *            The number of days the password is valid
225      * @return The maximum valid date of a password
226      */
227     public static Timestamp getPasswordMaxValidDate( int nNumberDay )
228     {
229         if ( nNumberDay <= 0 )
230         {
231             return null;
232         }
233 
234         long nMilliSeconds = DateUtil.convertDaysInMiliseconds( nNumberDay );
235         return new Timestamp( new java.util.Date( ).getTime( ) + nMilliSeconds );
236     }
237 }