View Javadoc

1   /**
2    * This file Copyright (c) 2003-2011 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.jaas.sp.jcr;
35  
36  import info.magnolia.cms.core.Content;
37  import info.magnolia.cms.security.MgnlUser;
38  import info.magnolia.cms.security.MgnlUserManager;
39  import info.magnolia.cms.security.SecuritySupport;
40  import info.magnolia.cms.security.User;
41  import info.magnolia.cms.security.UserManager;
42  import info.magnolia.cms.security.auth.Entity;
43  import info.magnolia.jaas.principal.EntityImpl;
44  import info.magnolia.jaas.sp.AbstractLoginModule;
45  import info.magnolia.jaas.sp.UserAwareLoginModule;
46  import org.apache.commons.lang.StringUtils;
47  import org.slf4j.Logger;
48  import org.slf4j.LoggerFactory;
49  
50  import javax.security.auth.login.FailedLoginException;
51  import javax.security.auth.login.LoginException;
52  import javax.security.auth.login.AccountNotFoundException;
53  import javax.security.auth.login.AccountLockedException;
54  
55  import java.util.Calendar;
56  import java.util.GregorianCalendar;
57  import java.util.Iterator;
58  import java.util.TimeZone;
59  
60  
61  /**
62   * Authentication module implementation using JCR to retrieve the users.
63   * @author Sameer Charles $Id: JCRAuthenticationModule.java 49089 2011-09-08 14:00:30Z ochytil $
64   */
65  public class JCRAuthenticationModule extends AbstractLoginModule implements UserAwareLoginModule {
66      private static final Logger log = LoggerFactory.getLogger(JCRAuthenticationModule.class);
67  
68      protected User user;
69      
70      /**
71       * Get number of failed login attempts before locking account.
72       */
73      public int getMaxAttempts() {
74          //Workaround until you can get realm from user (5.0).
75          if (this.user instanceof MgnlUser) {
76              Content node = ((MgnlUser) this.user).getUserNode();
77              realm = StringUtils.substringBefore(StringUtils.substringAfter(node.getHandle(), "/"), "/");
78          //If not supported by user manager then lockout is disabled.
79          } else {
80              return 0;
81          }
82          MgnlUserManager manager = (MgnlUserManager) SecuritySupport.Factory.getInstance().getUserManager(realm);
83          return (int)manager.getMaxFailedLoginAttempts();
84      }
85  
86      /**
87       * Get time period for time lockout.
88       */
89      public long getTimeLock(){
90          //Workaround until you can get realm from user (5.0).
91          if (this.user instanceof MgnlUser) {
92              Content node = ((MgnlUser) this.user).getUserNode();
93              realm = StringUtils.substringBefore(StringUtils.substringAfter(node.getHandle(), "/"), "/");
94          } else {
95              //If not supported by user manager then lockout is disabled.
96              return 0;
97          }
98          MgnlUserManager manager = (MgnlUserManager) SecuritySupport.Factory.getInstance().getUserManager(realm);
99          return (long)manager.getLockTimePeriod();
100     }    
101 
102     /**
103      * Releases all associated memory.
104      */
105     public boolean release() {
106         return true;
107     }
108 
109     /**
110      * Checks is the credentials exist in the repository.
111      * @throws LoginException or specific subclasses (which will be handled further for user feedback)
112      */
113     public void validateUser() throws LoginException {
114         initUser();
115 
116         if (this.user == null) {
117             throw new AccountNotFoundException("User account " + this.name + " not found.");
118         }
119 
120         if (!this.user.isEnabled()){
121             throw new AccountLockedException("User account " + this.name + " is locked.");
122         }
123 
124         matchPassword();
125 
126         if (!UserManager.ANONYMOUS_USER.equals(user.getName()) && user instanceof MgnlUser)
127         {
128             ((MgnlUser) user).setLastAccess();
129         }
130     }
131 
132     protected void initUser() {
133         user = getUserManager().getUser(name);
134     }
135 
136     protected void matchPassword() throws LoginException {
137         if(getMaxAttempts() > 0 && !UserManager.ANONYMOUS_USER.equals(user.getName()) && getTimeLock() > 0){
138             //Only MgnlUser is able to use lockout for time period like hard-lock (timeLock is higher than 0).
139             Calendar currentTime = new GregorianCalendar(TimeZone.getDefault());
140             Calendar lockTime = new GregorianCalendar(TimeZone.getDefault());
141             MgnlUser mgnlUser = (MgnlUser) user;
142             if(mgnlUser.getReleaseTime() != null){
143                 lockTime.clear();
144                 lockTime.setTime(mgnlUser.getReleaseTime().getTime());
145             }
146             if(lockTime.after(currentTime) && mgnlUser.getReleaseTime() != null){
147                 throw new LoginException("User account " + this.name + " is locked until " + mgnlUser.getReleaseTime().getTime() + ".");
148             }
149         }
150 
151         String serverPassword = user.getPassword();
152 
153         if (StringUtils.isEmpty(serverPassword)) {
154             throw new FailedLoginException("we do not allow users with no password");
155         }
156 
157         if (!StringUtils.equals(serverPassword, new String(this.pswd))) {
158 
159             if (getMaxAttempts() > 0 && !UserManager.ANONYMOUS_USER.equals(user.getName())){
160                 //Only MgnlUser is able to use lockout i.e. has maxAttempts higher than 0.
161                 MgnlUser mgnlUser = (MgnlUser) user;
162                 mgnlUser.setFailedLoginAttempts(mgnlUser.getFailedLoginAttempts() + 1);
163 
164                 //Hard lock
165                 if (mgnlUser.getFailedLoginAttempts() >= getMaxAttempts() && getTimeLock() <= 0){
166                     mgnlUser.setEnabled(false);
167                     mgnlUser.setFailedLoginAttempts(0);
168                     log.warn("Account " + this.name + " was locked due to high number of failed login attempts.");
169                 //Lock for time period
170                 }else if (mgnlUser.getFailedLoginAttempts() >= getMaxAttempts() && getTimeLock() > 0){
171                     mgnlUser.setFailedLoginAttempts(0);
172                     Calendar calendar = new GregorianCalendar(TimeZone.getDefault());
173                     calendar.add(Calendar.MINUTE, (int)getTimeLock());
174                     mgnlUser.setReleaseTime(calendar);
175                     String minuteString = "minutes";
176                     if(getTimeLock() == 1){
177                         minuteString = "minute";
178                     }
179                     log.warn("Account " + this.name + " was locked for " + getTimeLock() + " " + minuteString + " due to high number of failed login attempts.");                    
180                 }
181             }
182             throw new FailedLoginException("passwords do not match");
183         }
184         if(user instanceof MgnlUser){
185             MgnlUser mgnlUser = (MgnlUser) user;
186             if (getMaxAttempts() > 0 && !UserManager.ANONYMOUS_USER.equals(mgnlUser.getName()) && mgnlUser.getFailedLoginAttempts() > 0){
187                 mgnlUser.setFailedLoginAttempts(0);
188             }
189         }
190     }
191 
192     /**
193      * Override this to support any configured/non-configured user manager.
194      */
195     public UserManager getUserManager() {
196         SecuritySupport securitySupport = SecuritySupport.Factory.getInstance();
197         return securitySupport.getUserManager(this.realm);
198     }
199 
200     /**
201      * Set user details.
202      */
203     public void setEntity() {
204         EntityImpl entity = new EntityImpl();
205         entity.addProperty(Entity.LANGUAGE, this.user.getLanguage());
206         entity.addProperty(Entity.NAME, this.user.getName());
207 
208         String fullName = this.user.getProperty("title");
209         if(fullName != null){
210             entity.addProperty(Entity.FULL_NAME, fullName);
211         }
212         entity.addProperty(Entity.PASSWORD, new String(this.pswd));
213         this.subject.getPrincipals().add(entity);
214 
215         collectGroupNames();
216         collectRoleNames();
217     }
218 
219     /**
220      * Set access control list from the user, roles and groups.
221      */
222     public void setACL() {
223     }
224 
225     /**
226      * Extract all the configured roles from the given node. (which can be the user node or a group node)
227      */
228     public void collectRoleNames() {
229         for (Iterator iter = this.user.getAllRoles().iterator(); iter.hasNext();) {
230             addRoleName((String)iter.next());
231         }
232     }
233 
234     /**
235      * Extract all the configured groups from the given node. (which can be the user node or a group node)
236      */
237     public void collectGroupNames() {
238         for (Iterator iter = this.user.getAllGroups().iterator(); iter.hasNext();) {
239             addGroupName((String) iter.next());
240         }
241     }
242 
243     public User getUser() {
244         return user;
245     }
246 
247 }