Merged V2.2 to HEAD

7266: Updates to JMX-based admin
   7270: Add JMX-managed attribute 'maxUsers' and related startup property (server.maxusers)


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@8243 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Derek Hulley 2008-02-11 11:21:42 +00:00
parent 17b806c6c0
commit 1f36c62b18
5 changed files with 236 additions and 40 deletions

View File

@ -135,6 +135,8 @@
<bean id="RepoServerMgmt" class="org.alfresco.repo.admin.RepoServerMgmt">
<property name="transactionService"><ref bean="transactionService"/></property>
<property name="authenticationService"><ref bean="authenticationService"/></property>
<property name="maxUsers"><value>${server.maxusers}</value></property>
<property name="singleUserOnly"><value>${server.singleuseronly.name}</value></property>
</bean>

View File

@ -44,6 +44,18 @@ index.tracking.maxRecordSetSize=1000
# Change the failure behaviour of the configuration checker
system.bootstrap.config_check.strict=true
# Server Single User Mode
# note:
# only allow named user (note: if blank or not set then will allow all users)
# assuming maxusers is not set to 0
#server.singleuseronly.name=admin
# Server Max Users - limit number of users with non-expired tickets
# note:
# -1 allows any number of users, assuming not in single-user mode
# 0 prevents further logins, including the ability to enter single-user mode
server.maxusers=-1
#
# Properties to limit resources spent on individual searches
#

View File

@ -27,7 +27,10 @@ package org.alfresco.repo.admin;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.security.authentication.AuthenticationServiceImpl;
import org.alfresco.repo.transaction.TransactionServiceImpl;
import org.alfresco.service.license.LicenseService;
@ -47,6 +50,8 @@ public class RepoServerMgmt implements RepoServerMgmtMBean, ApplicationContextAw
private TransactionServiceImpl transactionService;
private AuthenticationServiceImpl authenticationService;
// property key should be the same as the one in core-services-context.xml (to allow repo to start in multi-user mode even if the property is not set)
private final static String PROPERTY_KEY_SINGLE_USER_ONLY = "${server.singleuseronly.name}";
public void setTransactionService(TransactionServiceImpl transactionService)
{
@ -71,13 +76,13 @@ public class RepoServerMgmt implements RepoServerMgmtMBean, ApplicationContextAw
{
if (readOnly && isReadOnly())
{
log.info("Alfresco Repository is already READONLY");
log.info("Alfresco is already read-only");
return;
}
if (!readOnly && !isReadOnly())
{
log.info("Alfresco Repository is already WRITABLE");
log.info("Alfresco is already read-write");
return;
}
@ -100,11 +105,11 @@ public class RepoServerMgmt implements RepoServerMgmtMBean, ApplicationContextAw
if (readOnly)
{
log.info("Alfresco Repository set to READONLY");
log.info("Alfresco set to be read-only");
}
else
{
log.info("Alfresco Repository set to WRITABLE");
log.info("Alfresco set to be read-write");
}
}
@ -118,7 +123,7 @@ public class RepoServerMgmt implements RepoServerMgmtMBean, ApplicationContextAw
}
// Note: implementing counts as managed attributes (without params) means that
// certain JMX consoles can create graphs
// certain JMX consoles can monitor
/*
* (non-Javadoc)
@ -165,7 +170,8 @@ public class RepoServerMgmt implements RepoServerMgmtMBean, ApplicationContextAw
public String[] listUserNamesNonExpired()
{
Set<String> userSet = authenticationService.getUsersWithTickets(true);
return userSet.toArray(new String[0]);
SortedSet<String> sorted = new TreeSet<String>(userSet);
return sorted.toArray(new String[0]);
}
/*
@ -175,7 +181,8 @@ public class RepoServerMgmt implements RepoServerMgmtMBean, ApplicationContextAw
public String[] listUserNamesAll()
{
Set<String> userSet = authenticationService.getUsersWithTickets(false);
return userSet.toArray(new String[0]);
SortedSet<String> sorted = new TreeSet<String>(userSet);
return sorted.toArray(new String[0]);
}
/*
@ -184,7 +191,9 @@ public class RepoServerMgmt implements RepoServerMgmtMBean, ApplicationContextAw
*/
public int invalidateTicketsExpired()
{
return authenticationService.invalidateTickets(true);
int count = authenticationService.invalidateTickets(true);
log.info("Expired tickets invalidated: " + count);
return count;
}
/*
@ -193,24 +202,132 @@ public class RepoServerMgmt implements RepoServerMgmtMBean, ApplicationContextAw
*/
public int invalidateTicketsAll()
{
return authenticationService.invalidateTickets(false);
int count = authenticationService.invalidateTickets(false);
log.info("All tickets invalidated: " + count);
return count;
}
/*
* (non-Javadoc)
* @see org.alfresco.mbeans.RepoServerMgmtMBean#allowSingleUserOnly(java.lang.String)
* @see org.alfresco.repo.admin.RepoServerMgmtMBean#invalidateUser(java.lang.String)
*/
public void allowSingleUserOnly(String allowedUsername)
public void invalidateUser(String username)
{
authenticationService.invalidateUserSession(username);
log.info("User invalidated: " + username);
}
/*
* (non-Javadoc)
* @see org.alfresco.repo.admin.RepoServerMgmtMBean#setSingleUserOnly(java.lang.String)
*/
public void setSingleUserOnly(String allowedUsername)
{
int maxUsers = getMaxUsers();
List<String> allowedUsers = null;
if (allowedUsername != null)
if ((allowedUsername != null) && (! allowedUsername.equals("")))
{
if (! allowedUsername.equals(PROPERTY_KEY_SINGLE_USER_ONLY))
{
allowedUsers = new ArrayList<String>(0);
allowedUsers.add(allowedUsername);
invalidateTicketsAll();
if (maxUsers != 0)
{
log.info("Alfresco set to allow single-user (" + allowedUsername + ") logins");
}
else
{
log.info("Alfresco set to allow single-user (" + allowedUsername + ") logins - although further logins are currently prevented (limit = 0)");
}
}
}
else
{
if (maxUsers == -1)
{
log.info("Alfresco set to allow logins (no limit set)");
}
else if (maxUsers == 0)
{
log.info("Alfresco set to allow logins - although further logins are currently prevented (limit = 0)");
}
else if (maxUsers != 0)
{
log.info("Alfresco set to allow logins (limit = " + maxUsers + ")");
}
}
authenticationService.setAllowedUsers(allowedUsers);
}
/*
* (non-Javadoc)
* @see org.alfresco.repo.admin.RepoServerMgmtMBean#getSingleUserOnly()
*/
public String getSingleUserOnly()
{
List<String> allowedUsers = authenticationService.getAllowedUsers();
if (allowedUsers != null)
{
if (allowedUsers.size() > 1)
{
throw new AlfrescoRuntimeException("Unexpected: more than one user allowed");
}
if (allowedUsers.size() == 1)
{
return allowedUsers.get(0);
}
}
return null;
}
/*
* (non-Javadoc)
* @see org.alfresco.repo.admin.RepoServerMgmtMBean#setMaxUsers(int)
*/
public void setMaxUsers(int maxUsers)
{
authenticationService.setMaxUsers(maxUsers);
String singleUserOnlyName = getSingleUserOnly();
if (maxUsers == -1)
{
if ((singleUserOnlyName != null) && (! singleUserOnlyName.equals("")))
{
log.info("Alfresco set to allow logins (no limit set) - although currently restricted to single-user (" + singleUserOnlyName + ")");
}
else
{
log.info("Alfresco set to allow logins (no limit set)");
}
}
else if (maxUsers == 0)
{
log.info("Alfresco set to prevent further logins (limit = 0)");
}
else
{
if ((singleUserOnlyName != null) && (! singleUserOnlyName.equals("")))
{
log.info("Alfresco set to allow logins (limit = " + maxUsers + ") - although currently restricted to single-user (" + singleUserOnlyName + ")");
}
else
{
log.info("Alfresco set to allow logins (limit = " + maxUsers + ")");
}
}
}
/*
* (non-Javadoc)
* @see org.alfresco.repo.admin.RepoServerMgmtMBean#getMaxUsers()
*/
public int getMaxUsers()
{
return authenticationService.getMaxUsers();
}
}

View File

@ -24,14 +24,17 @@
*/
package org.alfresco.repo.admin;
/**
* Repository Server Management
*
* Note: The attributes/operations below can be clustered (ie. when configured all servers in the cluster will be affected)
*
*/
public interface RepoServerMgmtMBean
{
/**
* Set whether Repository allows writes or not
*
* Note: This operation can be clustered (ie. all servers in the cluster will be affected)
*
* @param readOnly true is READONLY, false is WRITEABLE
*/
public void setReadOnly(boolean readOnly);
@ -39,8 +42,6 @@ public interface RepoServerMgmtMBean
/**
* Does the Repository allows writes or not ?
*
* Note: This operation can be clustered (ie. all servers in the cluster will be affected)
*
* @return boolean true is READONLY, false is WRITEABLE
*/
public boolean isReadOnly();
@ -50,8 +51,6 @@ public interface RepoServerMgmtMBean
*
* This may be higher than the user count, since a user can have more than one ticket/session
*
* Note: This operation can be clustered (ie. all servers in the cluster will be affected)
*
* @return int number of non-expired tickets
*/
public int getTicketCountNonExpired();
@ -61,8 +60,6 @@ public interface RepoServerMgmtMBean
*
* This may be higher than the user count, since a user can have more than one ticket/session
*
* Note: This operation can be clustered (ie. all servers in the cluster will be affected)
*
* @return int number of tickets (non-expired and expired)
*/
public int getTicketCountAll();
@ -72,8 +69,6 @@ public interface RepoServerMgmtMBean
*
* This may be lower than the ticket count, since a user can have more than one ticket/session
*
* Note: This operation can be clustered (ie. all servers in the cluster will be affected)
*
* @return int number of non-expired users
*/
public int getUserCountNonExpired();
@ -83,8 +78,6 @@ public interface RepoServerMgmtMBean
*
* This may be lower than the ticket count, since a user can have more than one ticket/session
*
* Note: This operation can be clustered (ie. all servers in the cluster will be affected)
*
* @return int number of users (non-expired and expired)
*/
public int getUserCountAll();
@ -92,8 +85,6 @@ public interface RepoServerMgmtMBean
/**
* Get set of unique non-expired usernames
*
* Note: This operation can be clustered (ie. all servers in the cluster will be affected)
*
* @return String[] array of non-expired usernames
*/
public String[] listUserNamesNonExpired();
@ -101,8 +92,6 @@ public interface RepoServerMgmtMBean
/**
* Get set of all unique usernames
*
* Note: This operation can be clustered (ie. all servers in the cluster will be affected)
*
* @return String[] array of all usernames (non-expired and expired)
*/
public String[] listUserNamesAll();
@ -110,8 +99,6 @@ public interface RepoServerMgmtMBean
/**
* Invalidate expired tickets
*
* Note: This operation can be clustered (ie. all servers in the cluster will be affected)
*
* @return int count of expired invalidated tickets
*/
public int invalidateTicketsExpired();
@ -125,15 +112,57 @@ public interface RepoServerMgmtMBean
*/
public int invalidateTicketsAll();
/**
* Invalidate given users tickets
*/
public void invalidateUser(String username);
/**
* Set whether Repository allows single user mode or not
*
* If single user mode is set then all tickets will be invalidated first before allowing the
* named user to login (with one or more sessions)
* named user to login (with one or more sessions) assuming maxUsers is not set to 0
*
* Note: This operation can be clustered (ie. all servers in the cluster will be affected)
* Note: This can also be configured at startup. Refer to repository property (server.singleuseronly.name).
*
* @param String allowed username (eg. 'admin') or null to unset (ie. allow all users)
*/
public void allowSingleUserOnly(String allowedUsername);
public void setSingleUserOnly(String allowedUsername);
/**
* If Repository is in single user mode then return the name of the allowed user else return null
*
* @param String allowed username (eg. 'admin') or null (ie. allow all users)
*/
public String getSingleUserOnly();
/**
* Set limit for max users and/or prevent further logins
*
* If number of non-expired logins is greater or equal to the limit then further logins will be prevented
* otherwise valid login attempt will be permitted, unless the system is in single-user mode.
*
* Note:
*
* Max users = 0 prevents further logins (will also prevent single-user mode login)
* Max users = -1 allow logins (without a max limit)
*
* Note: This can also be configured at startup. Refer to repository property (server.maxusers).
*
* @param maxUsers
*/
public void setMaxUsers(int maxUsers);
/**
* Get limit for max users
*
* If number of non-expired logins is greater or equal to the limit then further logins will be prevented
* otherwise valid login attempt will be permitted. However, single-user mode will take precedence.
*
* Max users = 0 prevents further logins
* Max users = -1 allow logins (without a max limit)
*
* @param int maxUsers
*/
public int getMaxUsers();
}

View File

@ -30,6 +30,7 @@ import java.util.Set;
import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.cmr.security.PermissionService;
public class AuthenticationServiceImpl implements AuthenticationService
{
@ -49,7 +50,8 @@ public class AuthenticationServiceImpl implements AuthenticationService
// SysAdmin cache - used to cluster certain JMX operations
private SimpleCache<String, Object> sysAdminCache;
private final static String KEY_SYSADMIN_ALLOWED_USERS = "sysAdminCache.authAllowedUsers";
private final static String KEY_SYSADMIN_ALLOWED_USERS = "sysAdminCache.authAllowedUsers"; // List<String>
private final static String KEY_SYSADMIN_MAX_USERS = "sysAdminCache.authMaxUsers"; // Integer
public AuthenticationServiceImpl()
@ -130,6 +132,14 @@ public class AuthenticationServiceImpl implements AuthenticationService
{
throw new AuthenticationException("Username not allowed: " + userName);
}
Integer maxUsers = (Integer)sysAdminCache.get(KEY_SYSADMIN_MAX_USERS);
if ((maxUsers != null) && (maxUsers != -1) && (ticketComponent.getUsersWithTickets(true).size() >= maxUsers))
{
throw new AuthenticationException("Max users exceeded: " + maxUsers);
}
authenticationComponent.authenticate(userName, password);
}
catch(AuthenticationException ae)
@ -167,6 +177,24 @@ public class AuthenticationServiceImpl implements AuthenticationService
sysAdminCache.put(KEY_SYSADMIN_ALLOWED_USERS, allowedUsers);
}
@SuppressWarnings("unchecked")
public List<String> getAllowedUsers()
{
return (List<String>)sysAdminCache.get(KEY_SYSADMIN_ALLOWED_USERS);
}
public void setMaxUsers(int maxUsers)
{
sysAdminCache.put(KEY_SYSADMIN_MAX_USERS, new Integer(maxUsers));
}
@SuppressWarnings("unchecked")
public int getMaxUsers()
{
Integer maxUsers = (Integer)sysAdminCache.get(KEY_SYSADMIN_MAX_USERS);
return (maxUsers == null ? -1 : maxUsers.intValue());
}
public void invalidateTicket(String ticket) throws AuthenticationException
{
ticketComponent.invalidateTicketById(ticket);
@ -219,8 +247,16 @@ public class AuthenticationServiceImpl implements AuthenticationService
return authenticationComponent.isSystemUserName(getCurrentUserName());
}
@SuppressWarnings("unchecked")
public void authenticateAsGuest() throws AuthenticationException
{
List<String> allowedUsers = (List<String>)sysAdminCache.get(KEY_SYSADMIN_ALLOWED_USERS);
if ((allowedUsers != null) && (! allowedUsers.contains(PermissionService.GUEST_AUTHORITY)))
{
throw new AuthenticationException("Guest authentication is not allowed");
}
authenticationComponent.setGuestUserAsCurrentUser();
ticketComponent.clearCurrentTicket();
}