1/*2 * Copyright 2000-2001,2004 The Apache Software Foundation.3 * 4 * Licensed under the Apache License, Version 2.0 (the "License");5 * you may not use this file except in compliance with the License.6 * You may obtain a copy of the License at7 * 8 * http://www.apache.org/licenses/LICENSE-2.09 * 10 * Unless required by applicable law or agreed to in writing, software11 * distributed under the License is distributed on an "AS IS" BASIS,12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.13 * See the License for the specific language governing permissions and14 * limitations under the License.15 */1617packageorg.apache.jetspeed.services.security;
1819import java.util.ArrayList;
20import java.util.HashMap;
21import java.util.Iterator;
22import java.util.List;
2324import javax.servlet.ServletConfig;
2526import org.apache.jetspeed.om.security.JetspeedUser;
27import org.apache.jetspeed.om.security.JetspeedUserFactory;
28import org.apache.jetspeed.om.security.UserNamePrincipal;
29import org.apache.jetspeed.portal.Portlet;
30import org.apache.jetspeed.services.JetspeedPortalAccessController;
31import org.apache.jetspeed.services.JetspeedSecurity;
32import org.apache.jetspeed.services.JetspeedUserManagement;
33import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
34import org.apache.jetspeed.services.logging.JetspeedLogger;
35import org.apache.jetspeed.services.rundata.JetspeedRunData;
36import org.apache.turbine.om.security.User;
37import org.apache.turbine.services.InitializationException;
38import org.apache.turbine.services.TurbineBaseService;
39import org.apache.turbine.services.TurbineServices;
40import org.apache.turbine.services.resources.ResourceService;
4142/***43 * <p>This is an implementation of the <code>JetspeedSecurityService</code> interface.44 *45 *46 * @author <a href="mailto:david@bluesunrise.com">David Sean Taylor</a>47 * @author <a href="mailto:sgala@hisitech.com">Santiago Gala</a>48 * @version $Id: JetspeedDBSecurityService.java,v 1.25 2004/03/31 04:49:10 morciuch Exp $49 */5051publicclassJetspeedDBSecurityServiceextends TurbineBaseService
52 implements JetspeedSecurityService53 {
54/***55 * Static initialization of the logger for this class56 */57privatestaticfinalJetspeedLogger logger = JetspeedLogFactoryService.getLogger(JetspeedDBSecurityService.class.getName());
5859privatefinalstatic String CONFIG_CASEINSENSITIVE_USERNAME = "caseinsensitive.username";
60privatefinalstatic String CONFIG_CASEINSENSITIVE_PASSWORD = "caseinsensitive.password";
61privatefinalstatic String CONFIG_CASEINSENSITIVE_UPPER = "caseinsensitive.upper";
62privatefinalstatic String CONFIG_LOGON_STRIKE_COUNT = "logon.strike.count";
63privatefinalstatic String CONFIG_LOGON_STRIKE_MAX = "logon.strike.max";
64privatefinalstatic String CONFIG_LOGON_STRIKE_INTERVAL = "logon.strike.interval";
65privatefinalstatic String CONFIG_LOGON_AUTO_DISABLE = "logon.auto.disable";
66privatefinalstatic String CONFIG_ACTIONS_ANON_DISABLE = "actions.anon.disable";
67privatefinalstatic String CONFIG_ACTIONS_ALLUSERS_DISABLE = "actions.allusers.disable";
68privatefinalstatic String CONFIG_ACTIONS_ADMIN_ROLES = "admin.roles";
6970privatefinalstatic String CONFIG_NEWUSER_ROLES = "newuser.roles";
71privatefinalstatic String CONFIG_DEFAULT_PERMISSION_LOGGEDIN = "permission.default.loggedin";
72privatefinalstatic String CONFIG_DEFAULT_PERMISSION_ANONYMOUS = "permission.default.anonymous";
73privatefinalstatic String CONFIG_ANONYMOUS_USER = "user.anonymous";
74privatefinalstatic String [] DEFAULT_PERMISSIONS = {""};
75privatefinalstatic String [] DEFAULT_CONFIG_NEWUSER_ROLES =
76 { "user" };
77privatefinalstatic String [] DEFAULT_ADMIN_ROLES =
78 { "admin" };
7980 String roles[] = null;
81boolean caseInsensitiveUsername = false;
82boolean caseInsensitivePassword = false;
83boolean caseInsensitiveUpper = true;
84boolean actionsAnonDisable = true;
85boolean actionsAllUsersDisable = false;
86 String anonymousUser = "anon";
87 String[] adminRoles = null;
8889int strikeCount = 3; // 3 within the interval90int strikeMax = 20; // 20 total failures 91long strikeInterval = 300; // five minutes9293boolean autoLogonDisable = false;
9495privatestatic HashMap users = new HashMap();
9697privatestatic Object sem = new Object();
9899/***100 * This is the early initialization method called by the 101 * Turbine <code>Service</code> framework102 * @param conf The <code>ServletConfig</code>103 * @exception throws a <code>InitializationException</code> if the service104 * fails to initialize105 */106publicsynchronizedvoid init(ServletConfig conf) throws InitializationException
107 {
108// already initialized109if (getInit()) return;
110111super.init(conf);
112113// get configuration parameters from Jetspeed Resources114 ResourceService serviceConf = ((TurbineServices)TurbineServices.getInstance())
115 .getResources(JetspeedSecurityService.SERVICE_NAME);
116117try118 {
119 roles = serviceConf.getStringArray(CONFIG_NEWUSER_ROLES);
120 adminRoles = serviceConf.getStringArray(CONFIG_ACTIONS_ADMIN_ROLES);
121 }
122catch (Exception e)
123 {}
124125if (null == roles || roles.length == 0)
126 {
127 roles = DEFAULT_CONFIG_NEWUSER_ROLES;
128 }
129130if (null == adminRoles || adminRoles.length == 0)
131 {
132 adminRoles = DEFAULT_ADMIN_ROLES;
133 }
134135 caseInsensitiveUsername = serviceConf.getBoolean(CONFIG_CASEINSENSITIVE_USERNAME, caseInsensitiveUsername);
136 caseInsensitivePassword = serviceConf.getBoolean(CONFIG_CASEINSENSITIVE_PASSWORD, caseInsensitivePassword);
137 caseInsensitiveUpper = serviceConf.getBoolean(CONFIG_CASEINSENSITIVE_UPPER, caseInsensitiveUpper);
138139 strikeCount = serviceConf.getInt(CONFIG_LOGON_STRIKE_COUNT, strikeCount);
140 strikeInterval = serviceConf.getLong(CONFIG_LOGON_STRIKE_INTERVAL, strikeInterval);
141 strikeMax = serviceConf.getInt(CONFIG_LOGON_STRIKE_MAX, strikeMax);
142143 autoLogonDisable = serviceConf.getBoolean(CONFIG_LOGON_AUTO_DISABLE, autoLogonDisable);
144 actionsAnonDisable = serviceConf.getBoolean(CONFIG_ACTIONS_ANON_DISABLE, actionsAnonDisable);
145 actionsAllUsersDisable = serviceConf.getBoolean(CONFIG_ACTIONS_ALLUSERS_DISABLE, actionsAllUsersDisable);
146147 anonymousUser = serviceConf.getString(CONFIG_ANONYMOUS_USER, anonymousUser);
148149// initialization done150 setInit(true);
151 }
152153154//////////////////////////////////////////////////////////////////////////155//156// Required JetspeedSecurity Functions157//158// Required Features provided by default JetspeedSecurity159//160//////////////////////////////////////////////////////////////////////////161162/*163 * Factory to create a new JetspeedUser, using JetspeedUserFactory.164 * The class that is created by the default JetspeedUserFactory is configured165 * in the JetspeedSecurity properties:166 *167 * services.JetspeedSecurity.user.class=168 * org.apache.jetspeed.om.security.BaseJetspeedUser169 *170 * @return JetspeedUser a newly created user that implements JetspeedUser.171 */172publicJetspeedUser getUserInstance()
173 {
174try175 {
176return JetspeedUserFactory.getInstance();
177 }
178catch (UserException e)
179 {
180returnnull;
181 }
182 }
183184//////////////////////////////////////////////////////////////////////////185//186// Optional JetspeedSecurity Features 187//188// Features are not required to be implemented by Security Provider189//190//////////////////////////////////////////////////////////////////////////191192/*193 * During logon, the username can be case sensitive or case insensitive.194 *195 * Given a username, converts the username to either lower or upper case.196 * This optional feature is configurable from the JetspeedSecurity.properties:197 *198 * <code>services.JetspeedSecurity.caseinsensitive.username = true/false</code>199 * <code>services.JetspeedSecurity.caseinsensitive.upper = true/false</code>200 *201 * If <code>caseinsensitive.username</code> is true, 202 * then conversion is enabled and the username will be converted before 203 * being sent to the Authentication provider.204 *205 * @param username The username to be converted depending on configuration.206 * @return The converted username.207 *208 */209public String convertUserName(String username)
210 {
211if (caseInsensitiveUsername)
212 {
213 username = (caseInsensitiveUpper) ? username.toUpperCase() : username.toLowerCase();
214 }
215return username;
216 }
217218/*219 * During logon, the password can be case sensitive or case insensitive.220 *221 * Given a password, converts the password to either lower or upper case.222 * This optional feature is configurable from the JetspeedSecurity.properties:223 *224 * <code>services.JetspeedSecurity.caseinsensitive.password = true/false</code>225 * <code>services.JetspeedSecurity.caseinsensitive.upper = true/false</code>226 *227 * If <code>caseinsensitive.password</code> is true, 228 * then conversion is enabled and the password will be converted before 229 * being sent to the Authentication provider.230 *231 * @param password The password to be converted depending on configuration.232 * @return The converted password.233 *234 */235public String convertPassword(String password)
236 {
237if (caseInsensitivePassword)
238 {
239 password = (caseInsensitiveUpper) ? password.toUpperCase() : password.toLowerCase();
240 }
241return password;
242 }
243244/*245 * Logon Failure / Account Disabling Feature246 *247 * Checks and tracks failed user-logon attempts.248 * If the user fails to logon after a configurable number of logon attempts,249 * then the user's account will be disabled.250 *251 * This optional feature is configurable from the JetspeedSecurity.properties:252 *253 * <code>services.JetspeedSecurity.logon.auto.disable=false</code>254 *255 * The example setting below allows for 3 logon strikes per 300 seconds.256 * When the strike.count is exceeded over the strike.interval, the account257 * is disabled. The strike.max is the cumulative maximum.258 *259 * <code>services.JetspeedSecurity.logon.strike.count=3</code>260 * <code>services.JetspeedSecurity.logon.strike.interval=300</code>261 * <code>services.JetspeedSecurity.logon.strike.max=10</code>262 *263 * These settings are not persisted, and in a distributed environment are 264 * only tracked per node.265 *266 * @param username The username to be checked.267 * @return True if the strike count reached the maximum threshold and the268 * user's account was disabled, otherwise False.269 *270 */271publicboolean checkDisableAccount(String username)
272 {
273 username = convertUserName(username);
274275// TODO: make this work across a cluster of servers276UserLogonStats stat = (UserLogonStats)users.get(username);
277if (stat == null)
278 {
279 stat = newUserLogonStats(username);
280synchronized (sem)
281 {
282 users.put(username, stat);
283 }
284 }
285boolean disabled = stat.failCheck(strikeCount, strikeInterval, strikeMax);
286287if (disabled)
288 {
289try290 {
291// disable the account292JetspeedUser user = (JetspeedUser)JetspeedSecurity.getUser(username);
293if (user != null)
294 {
295 user.setDisabled(true);
296 JetspeedSecurity.saveUser(user);
297 }
298 }
299catch (Exception e)
300 {
301 logger.error("Could not disable user: " + username, e);
302 }
303 }
304return disabled;
305 }
306307/*308 * Logon Failure / Account Disabling Feature309 * 310 * Returns state of the the logon failure / account disabling feature.311 * 312 * If the user fails to logon after a configurable number of logon attempts,313 * then the user's account will be disabled.314 *315 * @see JetspeedSecurityService#checkLogonFailures316 *317 * @return True if the feature is enabled, false if the feature is disabled.318 *319 */320publicboolean isDisableAccountCheckEnabled()
321 {
322return autoLogonDisable;
323 }
324325326/*327 * Logon Failure / Account Disabling Feature328 * 329 * Resets counters for the logon failure / account disabling feature.330 * 331 * If the user fails to logon after a configurable number of logon attempts,332 * then the user's account will be disabled.333 *334 * @see JetspeedSecurityService#checkLogonFailures335 *336 * @param username The username to reset the logon failure counters.337 *338 */339publicvoid resetDisableAccountCheck(String username)
340 {
341// TODO: make this work across a cluster of servers342 username = convertUserName(username);
343UserLogonStats stat = (UserLogonStats)users.get(username);
344if (stat == null)
345 {
346 stat = newUserLogonStats(username);
347synchronized (sem)
348 {
349 users.put(username, stat);
350 }
351 }
352 stat.reset();
353 }
354355356//////////////////////////////////////////////////////////////////////////357//358// Optional JetspeedSecurity Helpers359//360//////////////////////////////////////////////////////////////////////////361362/***363 * Helper to UserManagement.364 * Retrieves a <code>JetspeedUser</code> given the primary principle username.365 * The principal can be any valid Jetspeed Security Principal:366 * <code>org.apache.jetspeed.om.security.UserNamePrincipal</code>367 * <code>org.apache.jetspeed.om.security.UserIdPrincipal</code>368 * 369 * The security service may optionally check the current user context370 * to determine if the requestor has permission to perform this action.371 *372 * @param username The username principal.373 * @return a <code>JetspeedUser</code> associated to the principal identity.374 * @exception UserException when the security provider has a general failure retrieving a user.375 * @exception UnknownUserException when the security provider cannot match376 * the principal identity to a user.377 * @exception InsufficientPrivilegeException when the requestor is denied due to insufficient privilege 378 */379380publicJetspeedUser getUser(String username)
381 throws JetspeedSecurityException382 {
383return JetspeedUserManagement.getUser(newUserNamePrincipal(username));
384 }
385386387/***388 * Helper to PortalAuthorization.389 * Gets a <code>JetspeedUser</code> from rundata, authorize user to perform the secured action on390 * the given <code>Portlet</code> resource. If the user does not have391 * sufficient privilege to perform the action on the resource, the check returns false,392 * otherwise when sufficient privilege is present, checkPermission returns true.393 *394 * @param rundata request that the user is taken from rundatas395 * @param action the secured action to be performed on the resource by the user. 396 * @param portlet the portlet resource.397 * @return boolean true if the user has sufficient privilege.398 */399publicboolean checkPermission(JetspeedRunData runData, String action, Portlet portlet)
400 {
401return JetspeedPortalAccessController.checkPermission(runData.getJetspeedUser(),
402 portlet,
403 action);
404 }
405406/***407 * Helper to PortalAuthorization.408 * Gets a <code>JetspeedUser</code> from rundata, authorize user to perform the secured action on409 * the given <code>Entry</code> resource. If the user does not have410 * sufficient privilege to perform the action on the resource, the check returns false,411 * otherwise when sufficient privilege is present, checkPermission returns true.412 *413 * @param rundata request that the user is taken from rundatas414 * @param action the secured action to be performed on the resource by the user. 415 * @param entry the portal entry resource.416 * @return boolean true if the user has sufficient privilege.417 public boolean checkPermission(JetspeedRunData runData, String action, RegistryEntry entry)418 {419 return JetspeedPortalAccessController.checkPermission(runData.getJetspeedUser(),420 entry,421 action);422 }423 */424425/*426 * Security configuration setting to disable all action buttons for the Anon user427 * This setting is readonly and is edited in the JetspeedSecurity deployment428 * 429 *430 * @return True if the feature actions are disabled for the anon user431 *432 */433publicboolean areActionsDisabledForAnon()
434 {
435return actionsAnonDisable;
436 }
437438/*439 * Security configuration setting to disable all action buttons for all users440 * This setting is readonly and is edited in the JetspeedSecurity deployment441 * 442 *443 * @return True if the feature actions are disabled for the all users444 *445 */446publicboolean areActionsDisabledForAllUsers()
447 {
448return actionsAllUsersDisable;
449 }
450451/*452 * Gets the name of the anonymous user account if applicable453 * 454 *455 * @return String the name of the anonymous user account456 *457 */458public String getAnonymousUserName()
459 {
460return anonymousUser;
461 }
462463/*464 * Gets the list of administrative roles465 * 466 * @return list of admin roles467 */468public List getAdminRoles()
469 {
470 List result = new ArrayList();
471for (int i = 0; i < adminRoles.length; i++)
472 {
473 result.add(adminRoles[i]);
474 }
475476return result;
477 }
478479/***480 * Returns true if user has administrative role481 * 482 * @param user483 * @return true if user has administrative role484 */485publicboolean hasAdminRole(User user)
486 {
487 String username = user.getUserName();
488try489 {
490 List adminRoles = getAdminRoles();
491for (Iterator it = adminRoles.iterator(); it.hasNext();)
492 {
493if (JetspeedSecurity.hasRole(username, (String)it.next()))
494 {
495returntrue;
496 }
497 }
498 }
499catch (Exception e)
500 {
501 logger.error(e);
502 }
503504return false;
505 }
506507 }
508