This project has retired. For details please refer to its Attic page.
DefaultUserSecurityPolicy xref
View Javadoc

1   package org.apache.archiva.redback.policy;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   * http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.archiva.redback.configuration.UserConfiguration;
23  import org.apache.archiva.redback.configuration.UserConfigurationKeys;
24  import org.apache.archiva.redback.users.User;
25  import org.apache.archiva.redback.policy.rules.MustHavePasswordRule;
26  import org.slf4j.Logger;
27  import org.slf4j.LoggerFactory;
28  import org.springframework.context.ApplicationContext;
29  import org.springframework.stereotype.Service;
30  
31  import javax.annotation.PostConstruct;
32  import javax.inject.Inject;
33  import javax.inject.Named;
34  import java.util.ArrayList;
35  import java.util.Calendar;
36  import java.util.Date;
37  import java.util.List;
38  
39  /**
40   * User Security Policy.
41   *
42   * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
43   */
44  @Service("userSecurityPolicy")
45  public class DefaultUserSecurityPolicy
46      implements UserSecurityPolicy
47  {
48      private static final String ENABLEMENT_KEY = "UserSecurityPolicy" + ":ENABLED";
49  
50      private Logger log = LoggerFactory.getLogger( getClass() );
51  
52      private PasswordRule defaultPasswordRule = new MustHavePasswordRule();
53  
54      @Inject
55      @Named(value = "userConfiguration#default")
56      private UserConfiguration config;
57  
58      @Inject
59      @Named(value = "passwordEncoder#sha256")
60      private PasswordEncoder passwordEncoder;
61  
62      @Inject
63      @Named(value = "userValidationSettings")
64      private UserValidationSettings userValidationSettings;
65  
66      @Inject
67      @Named(value = "cookieSettings#rememberMe")
68      private CookieSettings rememberMeCookieSettings;
69  
70      @Inject
71      @Named(value = "cookieSettings#signon")
72      private CookieSettings signonCookieSettings;
73  
74      // TODO use something more generic to be able to do change about container
75      @Inject
76      private ApplicationContext applicationContext;
77  
78      /**
79       * The List of {@link PasswordRule} objects.
80       */
81      @Inject
82      private List<PasswordRule> rules = new ArrayList<PasswordRule>( 0 );
83  
84      private int previousPasswordsCount;
85  
86      private int loginAttemptCount;
87  
88      private int passwordExpirationDays;
89  
90      private boolean passwordExpirationEnabled;
91  
92      private List<String> unlockableAccounts;
93  
94  
95      // ---------------------------------------
96      //  Component lifecycle
97      // ---------------------------------------
98      // TODO move this to constructor
99      @SuppressWarnings("unchecked")
100     @PostConstruct
101     public void initialize()
102     {
103         configurePolicy();
104 
105         configureEncoder();
106 
107         // In some configurations, rules can be unset.
108         if ( rules == null )
109         {
110             // Set rules to prevent downstream NPE.
111             rules = new ArrayList<PasswordRule>( 1 );
112         }
113 
114         if ( rules.isEmpty() )
115         {
116             // there should be at least one rule
117             addPasswordRule( defaultPasswordRule );
118         }
119     }
120 
121     private void configureEncoder()
122     {
123         String encoder = config.getString( UserConfigurationKeys.PASSWORD_ENCODER );
124 
125         if ( encoder != null )
126         {
127             this.passwordEncoder = applicationContext.getBean( "passwordEncoder#" + encoder, PasswordEncoder.class );
128         }
129     }
130 
131     private void configurePolicy()
132     {
133         this.previousPasswordsCount = config.getInt( UserConfigurationKeys.PASSWORD_RETENTION_COUNT );
134         this.loginAttemptCount = config.getInt( UserConfigurationKeys.LOGIN_ATTEMPT_COUNT );
135         this.passwordExpirationEnabled = config.getBoolean( UserConfigurationKeys.PASSWORD_EXPIRATION_ENABLED );
136         this.passwordExpirationDays = config.getInt( UserConfigurationKeys.PASSWORD_EXPIRATION );
137         this.unlockableAccounts = config.getList( UserConfigurationKeys.UNLOCKABLE_ACCOUNTS );
138     }
139 
140 
141     public String getId()
142     {
143         return "Default User Security Policy";
144     }
145 
146     public int getPreviousPasswordsCount()
147     {
148         return previousPasswordsCount;
149     }
150 
151     public List<String> getUnlockableAccounts()
152     {
153         if ( unlockableAccounts == null )
154         {
155             unlockableAccounts = new ArrayList<String>( 0 );
156         }
157         return unlockableAccounts;
158     }
159 
160     /**
161      * Sets a list of accounts which should never be locked by security policy
162      *
163      * @param unlockableAccounts
164      */
165     public void setUnlockableAccounts( List<String> unlockableAccounts )
166     {
167         this.unlockableAccounts = unlockableAccounts;
168     }
169 
170     /**
171      * Sets the count of previous passwords that should be tracked.
172      *
173      * @param count the count of previous passwords to track.
174      */
175     public void setPreviousPasswordsCount( int count )
176     {
177         this.previousPasswordsCount = count;
178     }
179 
180     public int getLoginAttemptCount()
181     {
182         return loginAttemptCount;
183     }
184 
185     public void setLoginAttemptCount( int count )
186     {
187         this.loginAttemptCount = count;
188     }
189 
190     /**
191      * Get the password encoder to be used for password operations
192      *
193      * @return the encoder
194      */
195     public PasswordEncoder getPasswordEncoder()
196     {
197         return passwordEncoder;
198     }
199 
200     public boolean isEnabled()
201     {
202         Boolean bool = (Boolean) PolicyContext.getContext().get( ENABLEMENT_KEY );
203         return bool == null || bool.booleanValue();
204     }
205 
206     public void setEnabled( boolean enabled )
207     {
208         PolicyContext.getContext().put( ENABLEMENT_KEY, Boolean.valueOf( enabled ) );
209     }
210 
211     /**
212      * Add a Specific Rule to the Password Rules List.
213      *
214      * @param rule the rule to add.
215      */
216     public void addPasswordRule( PasswordRule rule )
217     {
218         // TODO: check for duplicates? if so, check should only be based on Rule class name.
219 
220         rule.setUserSecurityPolicy( this );
221         this.rules.add( rule );
222     }
223 
224     /**
225      * Get the Password Rules List.
226      *
227      * @return the list of {@link PasswordRule} objects.
228      */
229     public List<PasswordRule> getPasswordRules()
230     {
231         return this.rules;
232     }
233 
234     /**
235      * Set the Password Rules List.
236      *
237      * @param rules the list of {@link PasswordRule} objects.
238      */
239     public void setPasswordRules( List<PasswordRule> rules )
240     {
241         this.rules.clear();
242 
243         if ( rules == null )
244         {
245             return;
246         }
247 
248         // Intentionally iterating to ensure policy settings in provided rules.
249 
250         for ( PasswordRule rule : rules )
251         {
252             addPasswordRule( rule );
253         }
254     }
255 
256     public void extensionPasswordExpiration( User user )
257         throws MustChangePasswordException
258     {
259         if ( passwordExpirationEnabled && !getUnlockableAccounts().contains( user.getUsername() ) )
260         {
261             Calendar expirationDate = Calendar.getInstance();
262             expirationDate.setTime( user.getLastPasswordChange() );
263             expirationDate.add( Calendar.DAY_OF_MONTH, passwordExpirationDays );
264             Calendar now = Calendar.getInstance();
265 
266             if ( now.after( expirationDate ) )
267             {
268                 log.info( "User '{}' flagged for password expiry (expired on: {})", user.getUsername(),
269                           expirationDate );
270                 user.setPasswordChangeRequired( true );
271                 throw new MustChangePasswordException( "Password Expired, You must change your password.", user );
272             }
273         }
274     }
275 
276     public void extensionExcessiveLoginAttempts( User user )
277         throws AccountLockedException
278     {
279         if ( !getUnlockableAccounts().contains( user.getUsername() ) )
280         {
281             int attempt = user.getCountFailedLoginAttempts();
282             attempt++;
283             user.setCountFailedLoginAttempts( attempt );
284 
285             if ( attempt >= loginAttemptCount )
286             {
287                 log.info( "User '{}' locked due to excessive login attempts: {}", user.getUsername(), attempt );
288                 user.setLocked( true );
289                 throw new AccountLockedException( "Account " + user.getUsername() + " is locked.", user );
290             }
291         }
292     }
293 
294     public void extensionChangePassword( User user )
295         throws PasswordRuleViolationException
296     {
297         extensionChangePassword( user, false );
298     }
299 
300     public void extensionChangePassword( User user, boolean passwordChangeRequired )
301         throws PasswordRuleViolationException
302     {
303         validatePassword( user );
304 
305         // set the current encoded password.
306         user.setEncodedPassword( passwordEncoder.encodePassword( user.getPassword() ) );
307         user.setPassword( null );
308 
309         // push new password onto list of previous password.
310         List<String> previousPasswords = new ArrayList<String>( 1 );
311         previousPasswords.add( user.getEncodedPassword() );
312 
313         if ( !user.getPreviousEncodedPasswords().isEmpty() )
314         {
315             int oldCount = Math.min( previousPasswordsCount - 1, user.getPreviousEncodedPasswords().size() );
316             //modified sublist start index as the previous value results to nothing being added to the list. 
317             List<String> sublist = user.getPreviousEncodedPasswords().subList( 0, oldCount );
318             previousPasswords.addAll( sublist );
319         }
320 
321         user.setPreviousEncodedPasswords( previousPasswords );
322         user.setPasswordChangeRequired( passwordChangeRequired );
323 
324         // Update timestamp for password change.
325         user.setLastPasswordChange( new Date() );
326     }
327 
328     public void validatePassword( User user )
329         throws PasswordRuleViolationException
330     {
331         if ( isEnabled() )
332         {
333             PasswordRuleViolations violations = new PasswordRuleViolations();
334 
335             for ( PasswordRule rule : this.rules )
336             {
337                 if ( rule.isEnabled() )
338                 {
339                     if ( rule.requiresSecurityPolicy() )
340                     {
341                         rule.setUserSecurityPolicy( this );
342                     }
343 
344                     rule.testPassword( violations, user );
345                 }
346             }
347 
348             if ( violations.hasViolations() )
349             {
350                 PasswordRuleViolationException exception = new PasswordRuleViolationException();
351                 exception.setViolations( violations );
352                 throw exception;
353             }
354         }
355 
356         // If you got this far, then ensure that the password is never null.
357         if ( user.getPassword() == null )
358         {
359             user.setPassword( "" );
360         }
361     }
362 
363     public int getPasswordExpirationDays()
364     {
365         return passwordExpirationDays;
366     }
367 
368     public void setPasswordExpirationDays( int passwordExpiry )
369     {
370         this.passwordExpirationDays = passwordExpiry;
371     }
372 
373     public UserValidationSettings getUserValidationSettings()
374     {
375         return userValidationSettings;
376     }
377 
378     public void setUserValidationSettings( UserValidationSettings settings )
379     {
380         this.userValidationSettings = settings;
381     }
382 
383     public CookieSettings getRememberMeCookieSettings()
384     {
385         return rememberMeCookieSettings;
386     }
387 
388     public CookieSettings getSignonCookieSettings()
389     {
390         return signonCookieSettings;
391     }
392 
393     public UserConfiguration getConfig()
394     {
395         return config;
396     }
397 
398     public void setConfig( UserConfiguration config )
399     {
400         this.config = config;
401     }
402 
403 
404     public void setPasswordEncoder( PasswordEncoder passwordEncoder )
405     {
406         this.passwordEncoder = passwordEncoder;
407     }
408 
409     public void setRememberMeCookieSettings( CookieSettings rememberMeCookieSettings )
410     {
411         this.rememberMeCookieSettings = rememberMeCookieSettings;
412     }
413 
414     public void setSignonCookieSettings( CookieSettings signonCookieSettings )
415     {
416         this.signonCookieSettings = signonCookieSettings;
417     }
418 
419     public void setRules( List<PasswordRule> rules )
420     {
421         this.rules = rules;
422     }
423 
424     public void setDefaultPasswordRule( PasswordRule defaultPasswordRule )
425     {
426         this.defaultPasswordRule = defaultPasswordRule;
427     }
428 }