
org.sakaiproject.authz.impl.DbAuthzGroupService Maven / Gradle / Ivy
/**********************************************************************************
* $URL$
* $Id$
***********************************************************************************
*
* Copyright (c) 2003, 2004, 2005, 2006, 2006 2007, 2007, 2008 Sakai Foundation
*
* Licensed under the Educational Community License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.opensource.org/licenses/ECL-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
**********************************************************************************/
package org.sakaiproject.authz.impl;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sakaiproject.authz.api.*;
import org.sakaiproject.db.api.SqlReader;
import org.sakaiproject.db.api.SqlService;
import org.sakaiproject.entity.api.Entity;
import org.sakaiproject.entity.api.EntityManager;
import org.sakaiproject.entity.api.Reference;
import org.sakaiproject.event.api.Event;
import org.sakaiproject.event.api.NotificationService;
import org.sakaiproject.javax.PagingPosition;
import org.sakaiproject.memory.api.Cache;
import org.sakaiproject.memory.api.MemoryService;
import org.sakaiproject.site.api.SiteService;
import org.sakaiproject.time.api.Time;
import org.sakaiproject.user.api.UserNotDefinedException;
import org.sakaiproject.util.BaseDbFlatStorage;
import org.sakaiproject.util.BaseResourceProperties;
import org.sakaiproject.util.BaseResourcePropertiesEdit;
import org.sakaiproject.util.StringUtil;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
*
* DbAuthzGroupService is an extension of the BaseAuthzGroupService with database storage.
*
*/
public abstract class DbAuthzGroupService extends BaseAuthzGroupService implements Observer
{
/** To avoide the dreaded ORA-01795 and the like, we need to limit to <1000 the items in each in(?, ?, ...) clause, connecting them with ORs. */
protected final static int MAX_IN_CLAUSE = 999;
/** Our log (commons). */
private static Log M_log = LogFactory.getLog(DbAuthzGroupService.class);
/** All the event functions we know exist on the db. */
protected Collection m_functionCache = new HashSet();
/** All the event role names we know exist on the db. */
protected Collection m_roleNameCache = new HashSet();
/** Table name for realms. */
protected String m_realmTableName = "SAKAI_REALM";
/** Table name for realm properties. */
protected String m_realmPropTableName = "SAKAI_REALM_PROPERTY";
/** ID field for realm. */
protected String m_realmIdFieldName = "REALM_ID";
/** AuthzGroup dbid field. */
protected String m_realmDbidField = "REALM_KEY";
/** All "fields" for realm reading. */
protected String[] m_realmReadFieldNames = {"REALM_ID", "PROVIDER_ID",
"(select MAX(ROLE_NAME) from SAKAI_REALM_ROLE where ROLE_KEY = MAINTAIN_ROLE)", "CREATEDBY", "MODIFIEDBY", "CREATEDON", "MODIFIEDON",
"REALM_KEY"};
/** All "fields" for realm update. */
protected String[] m_realmUpdateFieldNames = {"REALM_ID", "PROVIDER_ID",
"MAINTAIN_ROLE = (select MAX(ROLE_KEY) from SAKAI_REALM_ROLE where ROLE_NAME = ?)", "CREATEDBY", "MODIFIEDBY", "CREATEDON", "MODIFIEDON"};
/** All "fields" for realm insert. */
protected String[] m_realmInsertFieldNames = {"REALM_ID", "PROVIDER_ID", "MAINTAIN_ROLE", "CREATEDBY", "MODIFIEDBY", "CREATEDON", "MODIFIEDON"};
/*************************************************************************************************************************************************
* Dependencies
************************************************************************************************************************************************/
/** All "field values" for realm insert. */
protected String[] m_realmInsertValueNames = {"?", "?", "(select MAX(ROLE_KEY) from SAKAI_REALM_ROLE where ROLE_NAME = ?)", "?", "?", "?", "?"};
/** map of database handlers. */
protected Map databaseBeans;
/** The database handler we are using. */
protected DbAuthzGroupSql dbAuthzGroupSql;
/** If true, we do our locks in the remote database, otherwise we do them here. */
protected boolean m_useExternalLocks = true;
/** Configuration: to run the ddl on init or not. */
protected boolean m_autoDdl = false;
/**
* Configuration: Whether or not to automatically promote non-provided users with same status
* and role to provided
*/
protected boolean m_promoteUsersToProvided = true;
private MemoryService m_memoryService;
// KNL-600 CACHING for the realm role groups
private Cache m_realmRoleGRCache;
private Cache authzUserGroupIdsCache;
private Cache maintainRolesCache;
/** KNL-1325 provide a more efficent refreshAuthzGroup */
public static final String REFRESH_MAX_TIME_PROPKEY = "authzgroup.refresh.max.time";
public static final String REFRESH_INTERVAL_PROPKEY = "authzgroup.refresh.interval";
/**
* Number of seconds before running refreshAuthzGroupTask again to clear queue,
* defaults to 60 (1 minute)
*/
private long refreshTaskInterval = 60;
/**
* Number of seconds an authz group refresh is allowed to take
* if threshold is reached delay processing the queue
*/
private long refreshMaxTime = 15;
/** Executor used to schedule processing */
private ScheduledExecutorService refreshScheduler;
/** Queue of authzgroups to refresh used by refreshAuthzGroupTask */
private Map refreshQueue;
public void setDatabaseBeans(Map databaseBeans)
{
this.databaseBeans = databaseBeans;
}
/*************************************************************************************************************************************************
* Configuration
************************************************************************************************************************************************/
/**
* returns the bean which contains database dependent code.
*/
public DbAuthzGroupSql getDbAuthzGroupSql()
{
return dbAuthzGroupSql;
}
/**
* sets which bean containing database dependent code should be used depending on the database vendor.
*/
public void setDbAuthzGroupSql(String vendor) throws Exception
{
this.dbAuthzGroupSql = (databaseBeans.containsKey(vendor) ? databaseBeans.get(vendor) : databaseBeans.get("default"));
}
public void setMemoryService(MemoryService memoryService) {
this.m_memoryService = memoryService;
}
/**
* @return the ServerConfigurationService collaborator.
*/
protected abstract SqlService sqlService();
/**
* Configuration: set the external locks value.
*
* @param value
* The external locks value.
*/
public void setExternalLocks(String value)
{
m_useExternalLocks = Boolean.valueOf(value).booleanValue();
}
/**
* Configuration: to run the ddl on init or not.
*
* @param value
* the auto ddl value.
*/
public void setAutoDdl(String value)
{
m_autoDdl = Boolean.valueOf(value).booleanValue();
}
/*************************************************************************************************************************************************
* Init and Destroy
************************************************************************************************************************************************/
/**
* Configuration: Whether or not to automatically promote non-provided users with same status
* and role to provided
*
* @param promoteUsersToProvided
* 'true' to promote non-provided users, 'false' to maintain their non-provided status
*/
public void setPromoteUsersToProvided(boolean promoteUsersToProvided)
{
m_promoteUsersToProvided = promoteUsersToProvided;
}
public void setRefreshTaskInterval(long refreshTaskInterval) {
M_log.info(REFRESH_INTERVAL_PROPKEY + " changed from " + this.refreshTaskInterval + " to " + refreshTaskInterval);
this.refreshTaskInterval = refreshTaskInterval;
}
public void setRefreshMaxTime(long refreshMaxTime) {
M_log.info(REFRESH_MAX_TIME_PROPKEY + " changed from " + this.refreshMaxTime + " to " + refreshMaxTime);
this.refreshMaxTime = refreshMaxTime;
}
/**
* Final initialization, once all dependencies are set.
*/
public void init()
{
try
{
// The observer will be notified whenever there are new events. Priority observers get notified first, before normal observers.
eventTrackingService().addPriorityObserver(this);
// if we are auto-creating our schema, check and create
if (m_autoDdl)
{
sqlService().ddl(this.getClass().getClassLoader(), "sakai_realm");
}
super.init();
setDbAuthzGroupSql(sqlService().getVendor());
// pre-cache role and function names
cacheRoleNames();
cacheFunctionNames();
m_realmRoleGRCache = m_memoryService.newCache("org.sakaiproject.authz.impl.DbAuthzGroupService.realmRoleGroupCache");
M_log.info("init(): table: " + m_realmTableName + " external locks: " + m_useExternalLocks);
authzUserGroupIdsCache = m_memoryService.newCache("org.sakaiproject.authz.impl.DbAuthzGroupService.authzUserGroupIdsCache");
maintainRolesCache = m_memoryService.newCache("org.sakaiproject.authz.impl.DbAuthzGroupService.maintainRolesCache");
//get the set of maintain roles and cache them on startup
getMaintainRoles();
refreshTaskInterval = initConfig(REFRESH_INTERVAL_PROPKEY, serverConfigurationService().getString(REFRESH_INTERVAL_PROPKEY), refreshTaskInterval);
refreshMaxTime = initConfig(REFRESH_MAX_TIME_PROPKEY, serverConfigurationService().getString(REFRESH_MAX_TIME_PROPKEY), refreshMaxTime);
refreshQueue = Collections.synchronizedMap(new HashMap());
refreshScheduler = Executors.newSingleThreadScheduledExecutor();
refreshScheduler.scheduleWithFixedDelay(
new RefreshAuthzGroupTask(),
120, // minimally wait 2 mins for sakai to start
refreshTaskInterval, // delay before running again
TimeUnit.SECONDS
);
}
catch (Exception t)
{
M_log.warn("init(): ", t);
}
}
private long initConfig(String propkey, String scsValue, long currentValue) {
if (!"".equals(scsValue)) {
try {
long parsedVal = Long.parseLong(scsValue);
M_log.info("initConfig() " + propkey + " changed from " + currentValue + " to " + parsedVal);
return parsedVal;
} catch (NumberFormatException e) {
M_log.error("initConfig() " + propkey + " value cannot be parsed");
}
}
return currentValue;
}
/*************************************************************************************************************************************************
* BaseAuthzGroupService extensions
************************************************************************************************************************************************/
/**
* Returns to uninitialized state.
*/
public void destroy()
{
refreshScheduler.shutdown();
authzUserGroupIdsCache.close();
// done with event watching
eventTrackingService().deleteObserver(this);
maintainRolesCache.close();
M_log.info(this +".destroy()");
}
/**
* Construct a Storage object.
*
* @return The new storage object.
*/
protected Storage newStorage()
{
DbStorage storage = new DbStorage(entityManager(), siteService);
storage.setPromoteUsersToProvided(m_promoteUsersToProvided);
return storage;
} // newStorage
/**
* Check / assure this role name is defined.
*
* @param name
* the role name.
*/
protected void checkRoleName(String name)
{
if (name == null) return;
name = name.intern();
// check the cache to see if the role name already exists
if (getRealmRoleKey(name) != null) return;
// see if we have it in the db
String statement = dbAuthzGroupSql.getCountRealmRoleSql();
Object[] fields = new Object[1];
fields[0] = name;
List results = sqlService().dbRead(statement, fields, new SqlReader()
{
public Object readSqlResultRecord(ResultSet result)
{
try
{
int count = result.getInt(1);
return Integer.valueOf(count);
}
catch (SQLException ignore)
{
return null;
}
}
});
boolean rv = false;
if (!results.isEmpty())
{
rv = ((Integer) results.get(0)).intValue() > 0;
}
// write if we didn't find it
if (!rv)
{
statement = dbAuthzGroupSql.getInsertRealmRoleSql();
// write, but if it fails, we don't really care - it will fail if another app server has just written this role name
sqlService().dbWriteFailQuiet(null, statement, fields);
}
synchronized (m_roleNameCache)
{
//Get realm role Key
statement = dbAuthzGroupSql.getSelectRealmRoleKeySql();
results = sqlService().dbRead(statement, fields, new SqlReader() {
public Object readSqlResultRecord(ResultSet result) {
try {
String name = result.getString(1);
Integer key = result.getInt(2);
RealmRole realmRole = new RealmRole(name, key);
m_roleNameCache.add(realmRole);
}
catch (SQLException ignore) {
}
return null;
}
});
}
}
/**
* Read all the role records, caching them
*/
protected void cacheRoleNames()
{
synchronized (m_roleNameCache)
{
String statement = dbAuthzGroupSql.getSelectRealmRoleSql();
List results = sqlService().dbRead(statement, null, new SqlReader()
{
public Object readSqlResultRecord(ResultSet result)
{
try
{
String name = result.getString(1);
Integer key = result.getInt(2);
RealmRole realmRole = new RealmRole(name, key);
m_roleNameCache.add(realmRole);
}
catch (SQLException ignore)
{
}
return null;
}
});
}
}
/**
* Check / assure this function name is defined.
*
* @param name
* the role name.
*/
protected void checkFunctionName(String name)
{
if (name == null) return;
name = name.intern();
// check the cache to see if the function name already exists
if (m_functionCache.contains(name)) return;
// see if we have this on the db
String statement = dbAuthzGroupSql.getCountRealmFunctionSql();
Object[] fields = new Object[1];
fields[0] = name;
List results = sqlService().dbRead(statement, fields, new SqlReader()
{
public Object readSqlResultRecord(ResultSet result)
{
try
{
int count = result.getInt(1);
return Integer.valueOf(count);
}
catch (SQLException ignore)
{
return null;
}
}
});
boolean rv = false;
if (!results.isEmpty())
{
rv = ((Integer) results.get(0)).intValue() > 0;
}
// write if we didn't find it
if (!rv)
{
statement = dbAuthzGroupSql.getInsertRealmFunctionSql();
// write, but if it fails, we don't really care - it will fail if another app server has just written this function
sqlService().dbWriteFailQuiet(null, statement, fields);
}
// cache the existance of the function name
synchronized (m_functionCache)
{
m_functionCache.add(name);
}
}
/*************************************************************************************************************************************************
* Storage implementation
************************************************************************************************************************************************/
/**
* Read all the function records, caching them
*/
protected void cacheFunctionNames()
{
synchronized (m_functionCache)
{
String statement = dbAuthzGroupSql.getSelectRealmFunction1Sql();
List results = sqlService().dbRead(statement, null, new SqlReader()
{
public Object readSqlResultRecord(ResultSet result)
{
try
{
String name = result.getString(1);
m_functionCache.add(name);
}
catch (SQLException ignore)
{
}
return null;
}
});
}
}
/**
* Form a SQL IN() clause, but break it up with ORs to keep the size of each IN below 100
*
* @param size
* The size
* @param field
* The field name
* @return a SQL IN() with ORs clause this large.
*/
protected String orInClause(int size, String field)
{
// Note: to avoide the dreaded ORA-01795 and the like, we need to limit to <1000 the items in each in(?, ?, ...) clause, connecting them with
// ORs -ggolden
int ors = size / MAX_IN_CLAUSE;
int leftover = size - (ors * MAX_IN_CLAUSE);
StringBuilder buf = new StringBuilder();
// enclose them all in parens if we have > 1
if (ors > 0)
{
buf.append(" (");
}
buf.append(" " + field + " IN ");
// do all the full MAX_IN_CLAUSE '?' in/ors
if (ors > 0)
{
for (int i = 0; i < ors; i++)
{
buf.append("(?");
for (int j = 1; j < MAX_IN_CLAUSE; j++)
{
buf.append(",?");
}
buf.append(")");
if (i < ors - 1)
{
buf.append(" OR " + field + " IN ");
}
}
}
// add one more for the extra
if (leftover > 0)
{
if (ors > 0)
{
buf.append(" OR " + field + " IN ");
}
buf.append("(?");
for (int i = 1; i < leftover; i++)
{
buf.append(",?");
}
buf.append(")");
}
// enclose them all in parens if we have > 1
if (ors > 0)
{
buf.append(" )");
}
return buf.toString();
}
/**
* Get value for query & return that; needed for mssql which doesn't support select stmts in VALUES clauses
* Note that MSSQL support was removed in KNL-880, so this is a no-op.
*
* @param sqlQuery
* @param bindParameter
* @return value if mssql, bindparameter if not (basically a no-op for others)
*/
protected Object getValueForSubquery(String sqlQuery, Object bindParameter)
{
return bindParameter;
}
private Integer getRealmRoleKey(String roleName) {
Iterator itr = m_roleNameCache.iterator();
while (itr.hasNext()) {
RealmRole realmRole = (RealmRole) itr.next();
if (realmRole != null && realmRole.getName().equals(roleName)) {
return realmRole.getKey();
}
}
return null;
}
public void update(Observable arg0, Object arg) {
if (arg == null || !(arg instanceof Event))
return;
Event event = (Event) arg;
// check the event function against the functions we have notifications watching for
String function = event.getEvent();
if (SECURE_UPDATE_AUTHZ_GROUP.equals(function)
|| SECURE_UPDATE_OWN_AUTHZ_GROUP.equals(function)
|| SECURE_REMOVE_AUTHZ_GROUP.equals(function)
|| SECURE_JOIN_AUTHZ_GROUP.equals(function)
|| SECURE_UNJOIN_AUTHZ_GROUP.equals(function)
|| SECURE_ADD_AUTHZ_GROUP.equals(function)) {
// Get the resource ID
String realmId = extractEntityId(event.getResource());
if (realmId != null) {
for (String user : getAuthzUsersInGroups(new HashSet(Arrays.asList(realmId)))) {
authzUserGroupIdsCache.remove(user);
}
if (M_log.isDebugEnabled()) {
M_log.debug("DbAuthzGroupService update(): clear realm role cache for " + realmId);
}
m_realmRoleGRCache.remove(realmId);
} else {
// This should never happen as the events we generate should always have
// a /realm/ prefix on the resource.
M_log.warn("DBAuthzGroupService update(): failed to extract realm ID from "+ event.getResource());
}
}
}
/**
* based on value from RealmRoleGroupCache
* transform a Map object into a Map object
* KNL-1037
*/
private Map getMemberMap(Map mMap, Map,?> roleMap)
{
Map rv = new HashMap();
for (Map.Entry entry : mMap.entrySet())
{
String userId = entry.getKey();
MemberWithRoleId m = entry.getValue();
String roleId = m.getRoleId();
if (roleId != null && roleMap != null && roleMap.containsKey(roleId))
{
Role role = (Role) roleMap.get(roleId);
rv.put(userId, new BaseMember(role, m.isActive(), m.isProvided(), userId, userDirectoryService()));
}
}
return rv;
}
/**
* transform a Map object into a Map object
* to be used in RealmRoleGroupCache
* KNL-1037
*/
private Map getMemberWithRoleIdMap(Map userGrants)
{
Map rv = new HashMap();
for (Map.Entry entry : userGrants.entrySet())
{
String userId = entry.getKey();
Member member = entry.getValue();
rv.put(userId, new MemberWithRoleId(member));
}
return rv;
}
/**
* Step through queue and call refreshAuthzGroup on all groups queued up for
* a refresh
*/
protected class RefreshAuthzGroupTask implements Runnable {
@Override
public void run() {
if (M_log.isDebugEnabled()) M_log.debug("RefreshAuthzGroupTask.run() refreshing " + refreshQueue.size() + " realms");
if (refreshQueue.size() > 0) {
long numberRefreshed = 0;
long timeRefreshed = 0;
long longestRefreshed = 0;
String longestName = null;
List queueList = new ArrayList(refreshQueue.values());
Iterator it = queueList.iterator();
while (it.hasNext()) {
AuthzGroup azGroup = it.next();
String azGroupId = azGroup.getId();
if (M_log.isDebugEnabled()) M_log.debug("RefreshAuthzGroupTask.run() start refresh of azgroup: " + azGroupId);
numberRefreshed++;
long time = 0;
long start = System.currentTimeMillis();
try {
((DbStorage) m_storage).refreshAuthzGroupInternal((BaseAuthzGroup) azGroup);
} catch (Throwable e) {
M_log.error("RefreshAuthzGroupTask.run() Problem refreshing azgroup: " + azGroupId, e);
} finally {
time = (System.currentTimeMillis() - start);
refreshQueue.remove(azGroupId);
if (M_log.isDebugEnabled()) M_log.debug("RefreshAuthzGroupTask.run() refresh of azgroup: " + azGroupId + " took " + time/1e3 + " seconds");
}
timeRefreshed += time;
if (time > longestRefreshed) {
longestRefreshed = time;
longestName = azGroupId;
}
if (it.hasNext() && (time > (refreshMaxTime * 1000L))) {
M_log.warn("RefreshAuthzGroupTask.run() " + azGroupId + " took " + time/1e3 +
" seconds which is longer than the maximum allowed of " + refreshMaxTime +
" seconds, delay processing the rest of the queue");
break;
}
}
M_log.info("RefreshAuthzGroupTask.run() refreshed " + numberRefreshed + " realms in " + timeRefreshed/1e3 +
" seconds, longest realm was " + longestName + " at " + longestRefreshed/1e3 + " seconds");
}
}
}
/**
* Covers for the BaseXmlFileStorage, providing AuthzGroup and RealmEdit parameters
*/
protected class DbStorage extends BaseDbFlatStorage implements Storage, SqlReader
{
private static final String REALM_USER_GRANTS_CACHE = "REALM_USER_GRANTS_CACHE";
private static final String REALM_ROLES_CACHE = "REALM_ROLES_CACHE";
private boolean promoteUsersToProvided = true;
private EntityManager entityManager;
private SiteService siteService;
/**
* Construct.
*/
public DbStorage(EntityManager entityManager, SiteService siteService)
{
super(m_realmTableName, m_realmIdFieldName, m_realmReadFieldNames, m_realmPropTableName, m_useExternalLocks, null, sqlService());
m_reader = this;
setDbidField(m_realmDbidField);
setWriteFields(m_realmUpdateFieldNames, m_realmInsertFieldNames, m_realmInsertValueNames);
setLocking(false);
this.entityManager = entityManager;
this.siteService = siteService;
// setSortField(m_realmSortField, null);
}
/**
* Configure whether or not users with same status and role will be "promoted" to
* being provided.
*
* @param promoteUsersToProvided Whether or not to promote non-provided users
*/
public void setPromoteUsersToProvided(boolean promoteUsersToProvided) {
this.promoteUsersToProvided = promoteUsersToProvided;
}
public boolean check(String id)
{
return super.checkResource(id);
}
public AuthzGroup get(String id)
{
return get(null, id);
}
protected AuthzGroup get(Connection conn, String id)
{
// read the base
BaseAuthzGroup rv = (BaseAuthzGroup) super.getResource(conn, id);
completeGet(conn, rv, false);
return rv;
}
/**
* Complete the read process once the basic realm info has been read
*
* @param realm
* The real to complete
*/
public void completeGet(BaseAuthzGroup realm)
{
completeGet(null, realm, false);
}
/**
* Complete the read process once the basic realm info has been read
*
* @param conn
* optional SQL connection to use.
* @param realm
* The real to complete.
* @param updateProvider
* if true, update and store the provider info.
*/
protected void completeGet(Connection conn, final BaseAuthzGroup realm, boolean updateProvider)
{
if (realm == null) return;
if (!realm.m_lazy) return;
realm.m_lazy = false;
// update the db and realm with latest provider
if (updateProvider)
{
refreshAuthzGroup(realm);
}
// read the properties
if (((BaseResourceProperties) realm.m_properties).isLazy())
{
((BaseResourcePropertiesEdit) realm.m_properties).setLazy(false);
super.readProperties(conn, realm.getKey(), realm.m_properties);
}
Map realmRoleGRCache = (Map)m_realmRoleGRCache.get(realm.getId());
if (M_log.isDebugEnabled()) {
M_log.debug("DbAuthzGroupService: found " + realm.getId() + " in cache? " + (realmRoleGRCache != null));
}
if (realmRoleGRCache != null) {
// KNL-1037 read the cached role and membership information
Map roles = new HashMap();
// dehydrate to SimpleRoles, which can be stored in a distributed Terracotta cache
Map roleProperties = realmRoleGRCache.get(REALM_ROLES_CACHE);
for (java.util.Map.Entry mapEntry : roleProperties.entrySet()) {
roles.put(mapEntry.getKey(), new BaseRole(mapEntry.getValue()));
}
Map userGrants = new HashMap();
Map userGrantsWithRoleIdMap = (Map) realmRoleGRCache.get(REALM_USER_GRANTS_CACHE);
userGrants.putAll(getMemberMap(userGrantsWithRoleIdMap, roles));
realm.m_roles = roles;
realm.m_userGrants = userGrants;
} else {
// KNL-1183
refreshAuthzGroup(realm);
// read the roles and role functions
String sql = dbAuthzGroupSql.getSelectRealmRoleFunctionSql();
Object fields[] = new Object[1];
fields[0] = realm.getId();
m_sql.dbRead(conn, sql, fields, new SqlReader()
{
public Object readSqlResultRecord(ResultSet result)
{
try
{
// get the fields
String roleName = result.getString(1);
String functionName = result.getString(2);
// make the role if needed
BaseRole role = (BaseRole) realm.m_roles.get(roleName);
if (role == null)
{
role = new BaseRole(roleName);
realm.m_roles.put(role.getId(), role);
}
// add the function to the role
role.allowFunction(functionName);
return null;
}
catch (SQLException ignore)
{
return null;
}
}
});
// read the role descriptions
sql = dbAuthzGroupSql.getSelectRealmRoleDescriptionSql();
m_sql.dbRead(conn, sql, fields, new SqlReader()
{
public Object readSqlResultRecord(ResultSet result)
{
try
{
// get the fields
String roleName = result.getString(1);
String description = result.getString(2);
boolean providerOnly = "1".equals(result.getString(3));
// find the role - create it if needed
// Note: if the role does not yet exist, it has no functions
BaseRole role = (BaseRole) realm.m_roles.get(roleName);
if (role == null)
{
role = new BaseRole(roleName);
realm.m_roles.put(role.getId(), role);
}
// set the description
role.setDescription(description);
// set the provider only flag
role.setProviderOnly(providerOnly);
return null;
}
catch (SQLException ignore)
{
return null;
}
}
});
// read the role grants
sql = dbAuthzGroupSql.getSelectRealmRoleGroup1Sql();
m_sql.dbRead(conn, sql, fields, new SqlReader()
{
public Object readSqlResultRecord(ResultSet result)
{
try
{
// get the fields
String roleName = result.getString(1);
String userId = result.getString(2);
String active = result.getString(3);
String provided = result.getString(4);
// give the user one and only one role grant - there should be no second...
BaseMember grant = (BaseMember) realm.m_userGrants.get(userId);
if (grant == null)
{
// find the role - if it does not exist, create it for this grant
// NOTE: it would have no functions or description
BaseRole role = (BaseRole) realm.m_roles.get(roleName);
if (role == null)
{
role = new BaseRole(roleName);
realm.m_roles.put(role.getId(), role);
}
grant = new BaseMember(role, "1".equals(active), "1".equals(provided), userId, userDirectoryService());
realm.m_userGrants.put(userId, grant);
}
else
{
M_log.warn("completeGet: additional user - role grant: " + userId + " " + roleName);
}
return null;
}
catch (SQLException ignore)
{
return null;
}
}
});
Map payLoad = new HashMap();
// rehydrate from SimpleRole, which can be stored in a Terracotta cache
Map roleProperties = new HashMap();
for (java.util.Map.Entry entry : ((Map) realm.m_roles).entrySet()) {
roleProperties.put(entry.getKey(), entry.getValue().exportToSimpleRole());
}
Map membersWithRoleIds = getMemberWithRoleIdMap(realm.m_userGrants);
payLoad.put(REALM_ROLES_CACHE, roleProperties);
payLoad.put(REALM_USER_GRANTS_CACHE, membersWithRoleIds);
m_realmRoleGRCache.put(realm.getId(), payLoad);
}
}
/**
* {@inheritDoc}
*/
public List getAuthzGroups(String criteria, PagingPosition page)
{
List rv = null;
if (criteria != null)
{
criteria = "%" + criteria + "%";
String where = "( UPPER(REALM_ID) like ? or UPPER(PROVIDER_ID) like ? )";
Object[] fields = new Object[2];
fields[0] = criteria.toUpperCase();
fields[1] = criteria.toUpperCase();
// paging
if (page != null)
{
// adjust to the size of the set found
// page.validate(rv.size());
rv = getSelectedResources(where, fields, page.getFirst(), page.getLast());
}
else
{
rv = getSelectedResources(where, fields);
}
}
else
{
// paging
if (page != null)
{
// adjust to the size of the set found
// page.validate(rv.size());
rv = getAllResources(page.getFirst(), page.getLast());
}
else
{
rv = getAllResources();
}
}
return rv;
}
/**
* {@inheritDoc}
*/
public List getAuthzUserGroupIds(ArrayList authzGroupIds, String userid)
{
if (authzGroupIds == null || userid == null || authzGroupIds.size() < 1)
return new ArrayList(); // empty list
// first consult the cache
UserAndGroups uag = (UserAndGroups) authzUserGroupIdsCache.get(userid);
if (uag != null) {
List result = uag.getRealmQuery(new HashSet(authzGroupIds));
if (M_log.isDebugEnabled()) M_log.debug(uag);
if (result != null) {
// hit
return result;
}
// miss
}
// not in the cache
String inClause = orInClause( authzGroupIds.size(), "SAKAI_REALM.REALM_ID" );
String statement = dbAuthzGroupSql.getSelectRealmUserGroupSql( inClause );
Object[] fields = new Object[authzGroupIds.size()+1];
for ( int i=0; i(authzGroupIds), dbResult);
authzUserGroupIdsCache.put(userid, uag);
return dbResult;
}
/**
* {@inheritDoc}
*/
public int countAuthzGroups(String criteria)
{
int rv = 0;
if (criteria != null)
{
criteria = "%" + criteria + "%";
String where = "( UPPER(REALM_ID) like ? or UPPER(PROVIDER_ID) like ? )";
Object[] fields = new Object[2];
fields[0] = criteria.toUpperCase();
fields[1] = criteria.toUpperCase();
rv = countSelectedResources(where, fields);
}
else
{
rv = countAllResources();
}
return rv;
}
/**
* {@inheritDoc}
*/
public Collection getAuthzUsersInGroups(Set groupIds)
{
if (groupIds == null || groupIds.isEmpty()) {
return new ArrayList(); // empty list
}
// make a big where condition for groupIds with ORs
String inClause = orInClause( groupIds.size(), "SR.REALM_ID" );
String statement = dbAuthzGroupSql.getSelectRealmUsersInGroupsSql(inClause);
Object[] fields = groupIds.toArray();
@SuppressWarnings("unchecked")
List results = sqlService().dbRead(statement, fields, null);
return results;
}
/**
* {@inheritDoc}
*/
public Set getProviderIds(String authzGroupId)
{
String statement = dbAuthzGroupSql.getSelectRealmProviderId1Sql();
List results = sqlService().dbRead(statement, new Object[] {authzGroupId}, null);
if (results == null)
{
return new HashSet<>();
}
return new HashSet<>(results);
}
/**
* {@inheritDoc}
*/
public Set getAuthzGroupIds(String providerId)
{
String statement = dbAuthzGroupSql.getSelectRealmIdSql();
List results = sqlService().dbRead(statement, new Object[] {providerId}, null);
if (results == null)
{
return new HashSet();
}
return new HashSet(results);
}
/**
* {@inheritDoc}
*/
public Set getAuthzGroupsIsAllowed(String userId, String lock, Collection azGroups)
{
// further limited to only those authz groups in the azGroups parameter if not null
// if azGroups is not null, but empty, we can short-circut and return an empty set
// or if the lock is null
if (((azGroups != null) && azGroups.isEmpty()) || lock == null)
{
return new HashSet();
}
if ("".equals(lock) || "*".equals(lock)) {
// SPECIAL CASE - return all authzGroup IDs this user is active in (much faster)
String statement = dbAuthzGroupSql.getSelectRealmUserGroupSql("SAKAI_REALM_RL_GR.ACTIVE = '1'");
Object[] fields = new Object[1];
fields[0] = userId;
List dbResult = sqlService().dbRead(statement, fields, null );
return new HashSet(dbResult);
}
// Just like unlock, except we use all realms and get their ids
// Note: consider over all realms just those realms where there's a grant of a role that satisfies the lock
// Ignore realms where anon or auth satisfy the lock.
boolean auth = (userId != null) && (!userDirectoryService().getAnonymousUser().getId().equals(userId));
String sql = dbAuthzGroupSql.getSelectRealmIdSql(azGroups);
int size = 2;
String roleswap = null; // define the roleswap variable
if (azGroups != null)
{
size += azGroups.size();
for (Iterator i = azGroups.iterator(); i.hasNext();)
{
// FIXME - just use the azGroups directly rather than split them up
String[] refs = StringUtil.split(i.next().toString(), Entity.SEPARATOR); // splits the azGroups values so we can look for swapped state
for (int i2 = 0; i2 < refs.length; i2++) // iterate through the groups to see if there is a swapped state in the variable
{
roleswap = securityService().getUserEffectiveRole("/site/" + refs[i2]);
// break from this loop if the user is the current user and a swapped state is found
if (roleswap != null && auth && userId.equals(sessionManager().getCurrentSessionUserId()))
break;
}
if (roleswap!=null)
{
sql = dbAuthzGroupSql.getSelectRealmIdRoleSwapSql(azGroups); // redefine the sql we use if there's a role swap
size++; // increase the "size" by 1 for our new sql
break; // break from the loop
}
}
}
Object[] fields = new Object[size];
fields[0] = lock;
fields[1] = userId;
if (azGroups != null)
{
int pos = 2;
for (Iterator i = azGroups.iterator(); i.hasNext();)
{
fields[pos++] = i.next();
}
if (roleswap!=null) // add in name of the role for the alternate query
{
fields[pos++] = roleswap;
}
}
// Get resultset
List results = m_sql.dbRead(sql, fields, null);
Set rv = new HashSet();
rv.addAll(results);
return rv;
}
/**
* {@inheritDoc}
*/
public AuthzGroup put(String id)
{
BaseAuthzGroup rv = (BaseAuthzGroup) super.putResource(id, fields(id, null, false));
if (rv != null)
{
rv.activate();
}
return rv;
}
/**
* @inheritDoc
*/
public void addNewUser(final AuthzGroup azGroup, final String userId, final String role, final int maxSize) throws GroupFullException
{
// run our save code in a transaction that will restart on deadlock
// if deadlock retry fails, or any other error occurs, a runtime error will be thrown
m_sql.transact(new Runnable()
{
public void run()
{
addNewUserTx(azGroup, userId, role, maxSize);
}
}, "azg:" + azGroup.getId());
}
/**
* The transaction code to save the azg.
*
* @param edit
* The azg to save.
*/
protected void addNewUserTx(AuthzGroup edit, String userId, String role, int maxSize) throws GroupFullException
{
// Assume that users added in this way are always active and never provided
boolean active = true;
boolean provided = false;
String sql;
// Lock the table and count users if required
if (maxSize > 0)
{
// Get the REALM_KEY and lock the realm for update
sql = dbAuthzGroupSql.getSelectRealmUpdate();
Object fields[] = new Object[1];
fields[0] = edit.getId();
List resultsKey = m_sql.dbRead(sql, fields, new SqlReader()
{
public Object readSqlResultRecord(ResultSet result)
{
try
{
int realm_key = result.getInt(1);
return Integer.valueOf(realm_key);
}
catch (Exception e)
{
M_log.warn("addNewUserTx: " + e.toString());
return null;
}
}
});
int realm_key = -1;
if (!resultsKey.isEmpty())
{
realm_key = ((Integer) resultsKey.get(0)).intValue();
}
else
{
// Can't find the REALM_KEY for this REALM (should never happen)
M_log.error("addNewUserTx: can't find realm " + edit.getId());
}
// Count the number of users already in the realm
sql = dbAuthzGroupSql.getSelectRealmSize();
fields[0] = Integer.valueOf(realm_key);
List resultsSize = m_sql.dbRead(sql, fields, new SqlReader()
{
public Object readSqlResultRecord(ResultSet result)
{
try
{
int count = result.getInt(1);
return Integer.valueOf(count);
}
catch (Exception e)
{
M_log.warn("addNewUserTx: " + e.toString());
return null;
}
}
});
int currentSize = resultsSize.isEmpty() ? -1 : ((Integer) resultsSize.get(0)).intValue();
if ((currentSize < 0) || (currentSize >= maxSize))
{
// We can't add the user - group already full, or we can't find the size
throw new GroupFullException(edit.getId());
}
}
// Add the user to SAKAI_REALM_RL_GR
sql = dbAuthzGroupSql.getInsertRealmRoleGroup1Sql();
Object fields[] = new Object[5];
fields[0] = edit.getId();
fields[1] = userId;
fields[2] = role;
fields[3] = active ? "1" : "0";
fields[4] = provided ? "1" : "0";
m_sql.dbWrite(sql, fields);
// update the main realm table for new modified time and last-modified-by
super.commitResource(edit, fields(edit.getId(), ((BaseAuthzGroup) edit), true), null);
}
/**
* @inheritDoc
*/
public void removeUser(final AuthzGroup azGroup, final String userId)
{
// run our save code in a transaction that will restart on deadlock
// if deadlock retry fails, or any other error occurs, a runtime error will be thrown
m_sql.transact(new Runnable()
{
public void run()
{
removeUserTx(azGroup, userId);
}
}, "azg:" + azGroup.getId());
}
/**
* The transaction code to save the azg.
*
* @param edit
* The azg to save.
*/
protected void removeUserTx(AuthzGroup edit, String userId)
{
// Remove the user from SAKAI_REALM_RL_GR
String sql = dbAuthzGroupSql.getDeleteRealmRoleGroup4Sql();
Object fields[] = new Object[2];
fields[0] = edit.getId();
fields[1] = userId;
m_sql.dbWrite(sql, fields);
// update the main realm table for new modified time and last-modified-by
super.commitResource(edit, fields(edit.getId(), ((BaseAuthzGroup) edit), true), null);
}
/**
* @inheritDoc
*/
public void save(final AuthzGroup edit)
{
// pre-check the roles and functions to make sure they are all defined
for (Iterator iRoles = ((BaseAuthzGroup) edit).m_roles.values().iterator(); iRoles.hasNext();)
{
Role role = (Role) iRoles.next();
// make sure the role name is defined / define it
checkRoleName(role.getId());
for (Iterator iFunctions = role.getAllowedFunctions().iterator(); iFunctions.hasNext();)
{
String function = (String) iFunctions.next();
// make sure the role name is defined / define it
checkFunctionName(function);
}
}
// run our save code in a transaction that will restart on deadlock
// if deadlock retry fails, or any other error occurs, a runtime error will be thrown
m_sql.transact(new Runnable()
{
public void run()
{
saveTx(edit);
}
}, "azg:" + edit.getId());
// update with the provider
refreshAuthzGroup((BaseAuthzGroup) edit);
}
/**
* The transaction code to save the azg.
*
* @param edit
* The azg to save.
*/
protected void saveTx(AuthzGroup edit)
{
// update SAKAI_REALM_RL_FN: read, diff with the edit, add and delete
save_REALM_RL_FN(edit);
// update SAKAI_REALM_RL_GR
save_REALM_RL_GR(edit);
// update SAKAI_REALM_PROVIDER
save_REALM_PROVIDER(edit);
// update SAKAI_REALM_ROLE_DESC
save_REALM_ROLE_DESC(edit);
// update the main realm table and properties
super.commitResource(edit, fields(edit.getId(), ((BaseAuthzGroup) edit), true), edit.getProperties(), ((BaseAuthzGroup) edit).getKey());
}
protected void save_REALM_RL_FN(AuthzGroup azg)
{
// add what we have in the azg, unless we see it in the db
final Set toAdd = new HashSet();
for (Iterator iRoles = ((BaseAuthzGroup) azg).m_roles.values().iterator(); iRoles.hasNext();)
{
Role role = (Role) iRoles.next();
for (Iterator iFunctions = role.getAllowedFunctions().iterator(); iFunctions.hasNext();)
{
String function = (String) iFunctions.next();
toAdd.add(new RoleAndFunction(role.getId(), function));
}
}
// delete anything we see in the db we don't have in the azg
final Set toDelete = new HashSet();
// read what we have there now
String sql = dbAuthzGroupSql.getSelectRealmFunction2Sql();
Object fields[] = new Object[1];
fields[0] = caseId(azg.getId());
m_sql.dbRead(sql, fields, new SqlReader()
{
public Object readSqlResultRecord(ResultSet result)
{
try
{
String role = result.getString(1);
String function = result.getString(2);
RoleAndFunction raf = new RoleAndFunction(role, function);
// if we have it in the set toAdd, we can remove it (it's alredy on the db)
if (toAdd.contains(raf))
{
toAdd.remove(raf);
}
// if we don't have it in the azg, we need to delete it
else
{
toDelete.add(raf);
}
}
catch (Exception e)
{
M_log.warn("save_REALM_RL_FN: " + e.toString());
}
return null;
}
});
fields = new Object[3];
fields[0] = caseId(azg.getId());
// delete what we need to
sql = dbAuthzGroupSql.getDeleteRealmRoleFunction1Sql();
for (RoleAndFunction raf : toDelete)
{
fields[1] = raf.role;
fields[2] = raf.function;
m_sql.dbWrite(sql, fields);
}
// add what we need to
sql = dbAuthzGroupSql.getInsertRealmRoleFunctionSql();
fields[0] = getValueForSubquery(dbAuthzGroupSql.getInsertRealmRoleFunction1Sql(), fields[0]);
for (RoleAndFunction raf : toAdd)
{
fields[1] = getValueForSubquery(dbAuthzGroupSql.getInsertRealmRoleFunction2Sql(), raf.role);
fields[2] = getValueForSubquery(dbAuthzGroupSql.getInsertRealmRoleFunction3Sql(), raf.function);
m_sql.dbWrite(sql, fields);
}
// KNL-1230 need to be able to tell when changes occur in the AZG
HashSet lastChanged = new HashSet();
if (!toAdd.isEmpty()) {
lastChanged.addAll(toAdd);
}
if (!toDelete.isEmpty()) {
lastChanged.addAll(toDelete);
}
((BaseAuthzGroup) azg).m_lastChangedRlFn = lastChanged;
}
protected void save_REALM_RL_GR(AuthzGroup azg)
{
// add what we have in the azg, unless we see it in the db
final Set toAdd = new HashSet();
for (Iterator i = ((BaseAuthzGroup) azg).m_userGrants.entrySet().iterator(); i.hasNext();)
{
Map.Entry entry = (Map.Entry) i.next();
Member grant = (Member) entry.getValue();
toAdd.add(new UserAndRole(grant.getUserId(), grant.getRole().getId(), grant.isActive(), grant.isProvided()));
}
// delete anything we see in the db we don't have in the azg
final Set toDelete = new HashSet();
// read what we have there now
String sql = dbAuthzGroupSql.getSelectRealmRoleGroup2Sql();
Object fields[] = new Object[1];
fields[0] = caseId(azg.getId());
m_sql.dbRead(sql, fields, new SqlReader()
{
public Object readSqlResultRecord(ResultSet result)
{
try
{
String userId = result.getString(1);
String role = result.getString(2);
boolean active = "1".equals(result.getString(3));
boolean provided = "1".equals(result.getString(4));
UserAndRole uar = new UserAndRole(userId, role, active, provided);
// if we have it in the set toAdd, we can remove it (it's alredy on the db)
if (toAdd.contains(uar))
{
toAdd.remove(uar);
}
// if we don't have it in the azg, we need to delete it
else
{
toDelete.add(uar);
}
}
catch (Exception e)
{
M_log.warn("save_REALM_RL_GR: " + e.toString());
}
return null;
}
});
fields = new Object[5];
fields[0] = caseId(azg.getId());
// delete what we need to
sql = dbAuthzGroupSql.getDeleteRealmRoleGroup1Sql();
for (UserAndRole uar : toDelete)
{
fields[1] = uar.role;
fields[2] = uar.userId;
fields[3] = uar.active ? "1" : "0";
fields[4] = uar.provided ? "1" : "0";
m_sql.dbWrite(sql, fields);
}
// add what we need to
sql = dbAuthzGroupSql.getInsertRealmRoleGroup1Sql();
fields[0] = getValueForSubquery(dbAuthzGroupSql.getInsertRealmRoleGroup1_1Sql(), fields[0]);
for (UserAndRole uar : toAdd)
{
fields[1] = uar.userId;
fields[2] = getValueForSubquery(dbAuthzGroupSql.getInsertRealmRoleGroup1_2Sql(), uar.role);
fields[3] = uar.active ? "1" : "0";
fields[4] = uar.provided ? "1" : "0";
m_sql.dbWrite(sql, fields);
}
}
protected void save_REALM_PROVIDER(AuthzGroup azg)
{
// we we are not provider, delete any for this realm
if ((azg.getProviderGroupId() == null) || (m_provider == null))
{
String sql = dbAuthzGroupSql.getDeleteRealmProvider1Sql();
Object[] fields = new Object[1];
fields[0] = caseId(azg.getId());
m_sql.dbWrite(sql, fields);
return;
}
// add what we have in the azg, unless we see it in the db
final Set toAdd = new HashSet();
String[] ids = m_provider.unpackId(azg.getProviderGroupId());
if (ids != null)
{
for (String id : ids)
{
toAdd.add(id);
}
}
// delete anything we see in the db we don't have in the azg
final Set toDelete = new HashSet();
// read what we have there now
String sql = dbAuthzGroupSql.getSelectRealmProviderId2Sql();
Object fields[] = new Object[1];
fields[0] = caseId(azg.getId());
m_sql.dbRead(sql, fields, new SqlReader()
{
public Object readSqlResultRecord(ResultSet result)
{
try
{
String provider = result.getString(1);
// if we have it in the set toAdd, we can remove it (it's alredy on the db)
if (toAdd.contains(provider))
{
toAdd.remove(provider);
}
// if we don't have it in the azg, we need to delete it
else
{
toDelete.add(provider);
}
}
catch (Exception e)
{
M_log.warn("save_REALM_PROVIDER: " + e.toString());
}
return null;
}
});
fields = new Object[2];
fields[0] = caseId(azg.getId());
// delete what we need to
sql = dbAuthzGroupSql.getDeleteRealmProvider2Sql();
for (String provider : toDelete)
{
fields[1] = provider;
m_sql.dbWrite(sql, fields);
}
// add what we need to
sql = dbAuthzGroupSql.getInsertRealmProviderSql();
for (String provider : toAdd)
{
fields[1] = provider;
m_sql.dbWrite(sql, fields);
}
}
protected void save_REALM_ROLE_DESC(AuthzGroup azg)
{
// add what we have in the azg, unless we see it in the db
final Set toAdd = new HashSet();
for (Iterator iRoles = ((BaseAuthzGroup) azg).m_roles.values().iterator(); iRoles.hasNext();)
{
Role role = (Role) iRoles.next();
toAdd.add(new RoleAndDescription(role.getId(), role.getDescription(), role.isProviderOnly()));
}
// delete anything we see in the db we don't have in the azg
final Set toDelete = new HashSet();
// read what we have there now
String sql = dbAuthzGroupSql.getSelectRealmProvider2Sql();
Object fields[] = new Object[1];
fields[0] = caseId(azg.getId());
m_sql.dbRead(sql, fields, new SqlReader()
{
public Object readSqlResultRecord(ResultSet result)
{
try
{
String role = result.getString(1);
String description = result.getString(2);
boolean providerOnly = "1".equals(result.getString(3));
RoleAndDescription rad = new RoleAndDescription(role, description, providerOnly);
// if we have it in the set toAdd, we can remove it (it's alredy on the db)
if (toAdd.contains(rad))
{
toAdd.remove(rad);
}
// if we don't have it in the azg, we need to delete it
else
{
toDelete.add(rad);
}
}
catch (Exception e)
{
M_log.warn("save_REALM_ROLE_DESC: " + e.toString());
}
return null;
}
});
fields = new Object[2];
fields[0] = caseId(azg.getId());
// delete what we need to
sql = dbAuthzGroupSql.getDeleteRealmRoleDescription1Sql();
for (RoleAndDescription rad : toDelete)
{
fields[1] = rad.role;
m_sql.dbWrite(sql, fields);
}
fields = new Object[4];
fields[0] = caseId(azg.getId());
// add what we need to
sql = dbAuthzGroupSql.getInsertRealmRoleDescriptionSql();
fields[0] = getValueForSubquery(dbAuthzGroupSql.getInsertRealmRoleDescription1Sql(), fields[0]);
for (RoleAndDescription rad : toAdd)
{
fields[1] = getValueForSubquery(dbAuthzGroupSql.getInsertRealmRoleDescription2Sql(), rad.role);
fields[2] = rad.description;
fields[3] = rad.providerOnly ? "1" : "0";
m_sql.dbWrite(sql, fields);
}
}
public void cancel(AuthzGroup edit)
{
super.cancelResource(edit);
}
public void remove(final AuthzGroup edit)
{
// in a transaction
m_sql.transact(new Runnable()
{
public void run()
{
removeTx(edit);
}
}, "azgRemove:" + edit.getId());
}
/**
* Transaction code for removing the azg.
*/
protected void removeTx(AuthzGroup edit)
{
// delete all the role functions, auth grants, anon grants, role grants, fucntion grants
// and then the realm and release the lock.
// delete the role functions, role grants, provider entries
Object fields[] = new Object[1];
fields[0] = caseId(edit.getId());
String statement = dbAuthzGroupSql.getDeleteRealmRoleFunction2Sql();
m_sql.dbWrite(statement, fields);
statement = dbAuthzGroupSql.getDeleteRealmRoleGroup2Sql();
m_sql.dbWrite(statement, fields);
statement = dbAuthzGroupSql.getDeleteRealmProvider1Sql();
m_sql.dbWrite(statement, fields);
statement = dbAuthzGroupSql.getDeleteRealmRoleDescription2Sql();
m_sql.dbWrite(statement, fields);
// delete the realm and properties
super.removeResource(edit, ((BaseAuthzGroup) edit).getKey());
}
/**
* Get the fields for the database from the edit for this id, and the id again at the end if needed
*
* @param id
* The resource id
* @param edit
* The edit (may be null in a new)
* @param idAgain
* If true, include the id field again at the end, else don't.
* @return The fields for the database.
*/
protected Object[] fields(String id, BaseAuthzGroup edit, boolean idAgain)
{
Object[] rv = new Object[idAgain ? 8 : 7];
rv[0] = caseId(id);
if (idAgain)
{
rv[7] = rv[0];
}
if (edit == null)
{
String current = sessionManager().getCurrentSessionUserId();
// if no current user, since we are working up a new user record, use the user id as creator...
if (current == null) current = "";
Time now = timeService().newTime();
rv[1] = "";
rv[2] = "";
rv[3] = current;
rv[4] = current;
rv[5] = now;
rv[6] = now;
}
else
{
rv[1] = StringUtil.trimToZero(edit.m_providerRealmId);
rv[2] = StringUtil.trimToZero(edit.m_maintainRole);
rv[3] = StringUtil.trimToZero(edit.m_createdUserId);
rv[4] = StringUtil.trimToZero(edit.m_lastModifiedUserId);
rv[5] = edit.getCreatedTime();
rv[6] = edit.getModifiedTime();
}
return rv;
}
/**
* Read from the result one set of fields to create a Resource.
*
* @param result
* The Sql query result.
* @return The Resource object.
*/
public Object readSqlResultRecord(ResultSet result)
{
try
{
String id = result.getString(1);
String providerId = result.getString(2);
String maintainRole = result.getString(3);
String createdBy = result.getString(4);
String modifiedBy = result.getString(5);
java.sql.Timestamp ts = result.getTimestamp(6, sqlService().getCal());
Time createdOn = null;
if (ts != null)
{
createdOn = timeService().newTime(ts.getTime());
}
ts = result.getTimestamp(7, sqlService().getCal());
Time modifiedOn = null;
if (ts != null)
{
modifiedOn = timeService().newTime(ts.getTime());
}
// the special local integer 'db' id field, read after the field list
Integer dbid = Integer.valueOf(result.getInt(8));
// create the Resource from these fields
return new BaseAuthzGroup(DbAuthzGroupService.this,dbid, id, providerId, maintainRole, createdBy, createdOn, modifiedBy, modifiedOn);
}
catch (SQLException e)
{
M_log.warn("readSqlResultRecord: " + e);
return null;
}
}
/**
* {@inheritDoc}
*/
public boolean isAllowed(String userId, String lock, String realmId)
{
if ((lock == null) || (realmId == null)) return false;
Set roles = getEmptyRoles(userId);
Set roleIds = getRealmRoleKeys(roles);
if (M_log.isDebugEnabled())
M_log.debug("isAllowed: userId=" + userId + " lock=" + lock + " realm=" + realmId+
" roles="+ StringUtils.join(roles, ','));
String statement = dbAuthzGroupSql.getCountRealmRoleFunctionSql(roleIds);
Object[] fields = new Object[3 + roleIds.size()];
int pos = 0;
for (Integer roleId : roleIds)
{
fields[pos++] = roleId;
}
fields[pos++] = userId;
fields[pos++] = lock;
fields[pos++] = realmId;
// checks to see if the user is the current user and has the roleswap variable set in the session
String roleswap = securityService().getUserEffectiveRole(realmId);
if (roleswap != null && roles.contains(AUTH_ROLE) && userId.equals(sessionManager().getCurrentSessionUserId()))
{
fields[0] = roleswap; // set the field to the student role for the alternate sql
statement = dbAuthzGroupSql.getCountRoleFunctionSql(); // set the function for our alternate sql
}
List resultsNew = m_sql.dbRead(statement, fields, new SqlReader()
{
public Object readSqlResultRecord(ResultSet result)
{
try
{
int count = result.getInt(1);
return Integer.valueOf(count);
}
catch (SQLException ignore)
{
return null;
}
}
});
boolean rvNew = false;
int countNew = -1;
if (!resultsNew.isEmpty())
{
countNew = ((Integer) resultsNew.get(0)).intValue();
rvNew = countNew > 0;
}
return rvNew;
}
/**
* {@inheritDoc}
*/
public boolean isAllowed(String userId, String lock, Collection realms)
{
if (lock == null) return false;
if (realms == null || realms.size() < 1)
{
M_log.warn("isAllowed(): called with no realms: lock: " + lock + " user: " + userId);
if (M_log.isDebugEnabled())
M_log.debug("isAllowed():", new Exception());
return false;
}
Set roles = getEmptyRoles(userId);
if (M_log.isDebugEnabled())
M_log.debug("isAllowed: userId=" + userId + " lock=" + lock + " realms=" + realms
+ " roles="+ StringUtils.join(roles, ','));
String inClause = orInClause(realms.size(), "SAKAI_REALM.REALM_ID");
Set roleIds = getRealmRoleKeys(roles);
// any of the grant or role realms
String statement = dbAuthzGroupSql.getCountRealmRoleFunctionSql(roleIds, inClause);
Object[] fields = new Object[2 + (2 * realms.size()) + roleIds.size()];
int pos = 0;
// for roleswap
String userSiteRef = null;
String siteRef = null;
// oracle query has different order of parameters
String dbAuthzGroupSqlClassName=dbAuthzGroupSql.getClass().getName();
if(dbAuthzGroupSqlClassName.equals("org.sakaiproject.authz.impl.DbAuthzGroupSqlOracle")) {
fields[pos++] = userId;
}
// populate values for fields
for (String realmId : realms)
{
// These checks for roleswap assume there is at most one of each type of site in the realms collection,
// i.e. one ordinary site and one user site
if (realmId.startsWith(SiteService.REFERENCE_ROOT + Entity.SEPARATOR)) // Starts with /site/
{
if (userId != null && userId.equals(siteService.getSiteUserId(realmId))) {
userSiteRef = realmId;
} else {
siteRef = realmId; // set this variable for potential use later
}
}
fields[pos++] = realmId;
}
fields[pos++] = lock;
if(!dbAuthzGroupSqlClassName.equals("org.sakaiproject.authz.impl.DbAuthzGroupSqlOracle")) {
fields[pos++] = userId;
}
for (String realmId : realms)
{
fields[pos++] = realmId;
}
for (Integer roleId : roleIds)
{
fields[pos++] = roleId;
}
/* Delegated access essentially behaves like roleswap except instead of just specifying which role, you can also specify
* the realm as well. The access map is populated by an Event Listener that listens for dac.checkaccess and is stored in the session
* attribute: delegatedaccess.accessmap. This is a map of: SiteRef -> String[]{realmId, roleId}. Delegated access
* will defer to roleswap if it's set.
*/
String[] delegatedAccessGroupAndRole = getDelegatedAccessRealmRole(siteRef);
boolean delegatedAccess = delegatedAccessGroupAndRole != null && delegatedAccessGroupAndRole.length == 2;
// Would be better to get this initially to make the code more efficient, but the realms collection
// does not have a common order for the site's id which is needed to determine if the session variable exists
// ZQIAN: since the role swap is only done at the site level, for group reference, use its parent site reference instead.
String roleswap = null;
Reference ref = entityManager().newReference(siteRef);
if (SiteService.GROUP_SUBTYPE.equals(ref.getSubType())) {
String containerSiteRef = siteService.siteReference(ref.getContainer());
roleswap = securityService().getUserEffectiveRole(containerSiteRef);
if (roleswap != null) {
siteRef = containerSiteRef;
}
} else {
roleswap = securityService().getUserEffectiveRole(siteRef);
}
List results = null;
// Only check roleswap if the method is being called for the current user
if ( (roleswap != null || delegatedAccess)
&& userId != null && userId.equals(sessionManager().getCurrentSessionUserId())
) {
// First check in the user's own my workspace site realm if it's in the list
// We don't want to change the user's role in their own site, so call the regular function.
// This catches permission checks for entity references such as user dropboxes.
if (userSiteRef != null && isAllowed(userId, lock, userSiteRef))
return true;
// Then check the site where there's a roleswap effective
if (M_log.isDebugEnabled()) M_log.debug("userId="+userId+", siteRef="+siteRef+", roleswap="+roleswap+", delegatedAccess="+delegatedAccess);
Object[] fields2 = new Object[3];
if (roleswap != null) {
fields2[0] = roleswap;
} else if (delegatedAccess
&& delegatedAccessGroupAndRole != null ) {
// set the role for delegated access
fields2[0] = delegatedAccessGroupAndRole[1];
}
fields2[1] = lock;
if (roleswap == null
&& delegatedAccess
&& delegatedAccessGroupAndRole != null
) {
// set the realm for delegated access
fields2[2] = delegatedAccessGroupAndRole[0];
} else {
fields2[2] = siteRef;
}
if (M_log.isDebugEnabled()) M_log.debug("roleswap/dac fields: "+Arrays.toString(fields2));
statement = dbAuthzGroupSql.getCountRoleFunctionSql();
results = m_sql.dbRead(statement, fields2, new SqlReader()
{
public Object readSqlResultRecord(ResultSet result)
{
try
{
int count = result.getInt(1);
return Integer.valueOf(count);
}
catch (SQLException ignore)
{
return null;
}
}
});
boolean rv = false;
int count = -1;
if (!results.isEmpty())
{
count = ((Integer) results.get(0)).intValue();
rv = count > 0;
}
if (rv) // if true, go ahead and return
return true;
// Then check the rest of the realms. For example these could be subfolders under /content/group/...
if(roleswap != null){
for (String realmId : realms)
{
if (realmId == siteRef || realmId == userSiteRef) // we've already checked these so no need to do it again
continue;
fields2[2] = realmId;
results = m_sql.dbRead(statement, fields2, new SqlReader()
{
public Object readSqlResultRecord(ResultSet result)
{
try
{
int count = result.getInt(1);
return Integer.valueOf(count);
}
catch (SQLException ignore)
{
return null;
}
}
});
count = -1;
if (!results.isEmpty())
{
count = ((Integer) results.get(0)).intValue();
rv = count > 0;
}
if (rv) // if true, go ahead and return
return true;
}
}
// No successful results for roleswap
return false;
}
// Regular lookup (not roleswap)
results = m_sql.dbRead(statement, fields, new SqlReader()
{
public Object readSqlResultRecord(ResultSet result)
{
try
{
int count = result.getInt(1);
return Integer.valueOf(count);
}
catch (SQLException ignore)
{
return null;
}
}
});
boolean rv = false;
int count = -1;
if (!results.isEmpty())
{
count = ((Integer) results.get(0)).intValue();
rv = count > 0;
}
return rv;
}
/**
* Delegated access essentially behaves like roleswap except instead of just specifying which role, you can also specify
* the realm as well. The access map is populated by an Event Listener that listens for dac.checkaccess and is stored in the session
* attribute: delegatedaccess.accessmap. This is a map of: SiteRef -> String[]{realmId, roleId}.
* Delegated access will defer to roleswap if it is set.
*
* @param siteRef the site realm id
* @return String[]{realmId, roleId} or null if delegated access is disabled
*/
private String[] getDelegatedAccessRealmRole(String siteRef){
if (M_log.isDebugEnabled()) M_log.debug("getDelegatedAccessRealmRole(siteRef="+siteRef+")");
String[] delegatedAccessGroupAndRole = null;
// first we get the map out of the session (if it exists and is safe)
Map,?> delegatedAccessMap = null;
if (sessionManager().getCurrentSession().getAttribute("delegatedaccess.accessmapflag") != null) {
// only check for the map if the accessmapflag is set
Object delegatedAccessMapObj = sessionManager().getCurrentSession().getAttribute("delegatedaccess.accessmap");
if (delegatedAccessMapObj != null && delegatedAccessMapObj instanceof Map) {
// only read the map value out if it is set and is an actual map
delegatedAccessMap = (Map,?>) delegatedAccessMapObj;
}
//if the siteRef doesn't exist in the map, then that means that we haven't checked delegatedaccess for this user and site.
//if the user doesn't have access, the map will have a null value for that siteRef.
if (siteRef != null
&& (delegatedAccessMap == null || !delegatedAccessMap.containsKey(siteRef))){
/* the delegatedaccess.accessmapflag is set during login and is only set for user's who have some kind of delegated access
* if the user has access somewhere but either the map is null or there isn't any record for this site, then that means
* this site hasn't been checked yet. By posting an event, a DelegatedAccess observer will check this site's access for this user
* and store it in the user's session
*/
eventTrackingService().post(eventTrackingService().newEvent("dac.checkaccess", siteRef, false, NotificationService.NOTI_REQUIRED));
//grab the session after the checkaccess event since the checkaccess event could have modified it
delegatedAccessMapObj = sessionManager().getCurrentSession().getAttribute("delegatedaccess.accessmap");
if (delegatedAccessMapObj != null && delegatedAccessMapObj instanceof Map) {
// only read the map value out if it is set and is an actual map
delegatedAccessMap = (Map,?>) delegatedAccessMapObj;
}
}
if (siteRef != null
&& delegatedAccessMap != null
&& delegatedAccessMap.containsKey(siteRef)
&& delegatedAccessMap.get(siteRef) instanceof String[]) {
if (M_log.isDebugEnabled()) M_log.debug("siteRef="+siteRef+", delegatedAccessMap="+delegatedAccessMap);
delegatedAccessGroupAndRole = (String[]) delegatedAccessMap.get(siteRef);
if (M_log.isInfoEnabled()) {
String dacgarStr = "";
if (delegatedAccessGroupAndRole != null && delegatedAccessGroupAndRole.length > 1) {
dacgarStr = ", GroupAndRole["+delegatedAccessGroupAndRole[0]+", "+delegatedAccessGroupAndRole[1]+"]";
}
M_log.info("delegatedAccessCheck: userId="+sessionManager().getCurrentSessionUserId()+", siteRef="+siteRef+", delegatedAccess="+dacgarStr);
}
}
}
if (M_log.isDebugEnabled()) M_log.debug("getDelegatedAccessRealmRole(siteRef="+siteRef+"): "+Arrays.toString(delegatedAccessGroupAndRole));
return delegatedAccessGroupAndRole;
}
/**
* {@inheritDoc}
*/
public Set getUsersIsAllowed(String lock, Collection realms)
{
if ((lock == null) || (realms == null) || (realms.isEmpty())) return new HashSet();
String sql = dbAuthzGroupSql.getSelectRealmRoleGroupUserIdSql(orInClause(realms.size(), "SR.REALM_ID"), orInClause(realms.size(),
"SR1.REALM_ID"));
Object[] fields = new Object[1 + (2 * realms.size())];
int pos = 0;
for (Iterator i = realms.iterator(); i.hasNext();)
{
String roleRealm = (String) i.next();
fields[pos++] = roleRealm;
}
fields[pos++] = lock;
for (Iterator i = realms.iterator(); i.hasNext();)
{
String roleRealm = (String) i.next();
fields[pos++] = roleRealm;
}
// read the strings
List results = m_sql.dbRead(sql, fields, null);
// prepare the return
Set rv = new HashSet();
rv.addAll(results);
return rv;
}
/**
* {@inheritDoc}
*/
public Set getUsersIsAllowedByGroup(String lock, Collection realms)
{
final Set usersByGroup = new HashSet();
if ((lock == null) || (realms != null && realms.isEmpty())) return usersByGroup;
String sql;
Object[] fields;
if (realms != null) {
sql = dbAuthzGroupSql.getSelectRealmRoleGroupUserIdSql(orInClause(realms.size(), "REALM_ID"));
fields = new Object[realms.size() + 1];
int pos = 0;
fields[pos++] = lock;
for (Iterator i = realms.iterator(); i.hasNext();)
{
String roleRealm = (String) i.next();
fields[pos++] = roleRealm;
}
} else {
sql = dbAuthzGroupSql.getSelectRealmRoleGroupUserIdSql("true");
fields = new Object[1];
fields[0] = lock;
}
// read the strings
m_sql.dbRead(sql, fields, new SqlReader()
{
public Object readSqlResultRecord(ResultSet result)
{
try
{
String[] useringroup = new String[2];
useringroup[0] = result.getString(1);
useringroup[1] = result.getString(2);
usersByGroup.add( useringroup );
}
catch (SQLException ignore)
{
}
return null;
}
});
return usersByGroup;
}
/**
* {@inheritDoc}
*/
public Map getUserCountIsAllowed(String function, Collection azGroups)
{
final Map userCountByGroup = new HashMap();
if ((function == null) || (azGroups != null && azGroups.isEmpty())) return userCountByGroup;
String sql;
Object[] fields;
if (azGroups != null) {
sql = dbAuthzGroupSql.getSelectRealmRoleGroupUserCountSql(orInClause(azGroups.size(), "REALM_ID"));
fields = new Object[azGroups.size() + 1];
int pos = 0;
fields[pos++] = function;
for (Iterator i = azGroups.iterator(); i.hasNext();)
{
String roleRealm = (String) i.next();
fields[pos++] = roleRealm;
}
} else {
sql = dbAuthzGroupSql.getSelectRealmRoleGroupUserCountSql("true");
fields = new Object[1];
fields[0] = function;
}
// read the realm size counts
m_sql.dbRead(sql, fields, new SqlReader()
{
public Object readSqlResultRecord(ResultSet result)
{
try
{
String realm = result.getString(1);
Integer size = result.getInt(2);
userCountByGroup.put(realm, size);
}
catch (SQLException ignore)
{
}
return null;
}
});
return userCountByGroup;
}
/**
* {@inheritDoc}
*/
public Set getAllowedFunctions(String role, Collection realms)
{
if ((role == null) || (realms == null) || (realms.isEmpty())) return new HashSet();
String sql = dbAuthzGroupSql.getSelectRealmFunctionFunctionNameSql(orInClause(realms.size(), "SR.REALM_ID"));
Object[] fields = new Object[1 + realms.size()];
fields[0] = role;
int pos = 1;
for (Iterator i = realms.iterator(); i.hasNext();)
{
String roleRealm = (String) i.next();
fields[pos++] = roleRealm;
}
// read the strings
List results = m_sql.dbRead(sql, fields, null);
// prepare the return
Set rv = new HashSet();
rv.addAll(results);
return rv;
}
/**
* {@inheritDoc}
*/
public void refreshUser(String userId, Map providerGrants)
{
if (userId == null) return;
String sql = dbAuthzGroupSql.getSelectRealmRoleGroup3Sql();
// read this user's grants from all realms
Object[] fields = new Object[1];
fields[0] = userId;
List grants = m_sql.dbRead(sql, fields, new SqlReader()
{
public Object readSqlResultRecord(ResultSet result)
{
try
{
int realmKey = result.getInt(1);
String roleName = result.getString(2);
String active = result.getString(3);
String provided = result.getString(4);
return new RealmAndRole(Integer.valueOf(realmKey), roleName, "1".equals(active), "1".equals(provided));
}
catch (Exception ignore)
{
return null;
}
}
});
// make a map, realm id -> role granted, each for provider and non-provider (or inactive)
Map existing = new HashMap();
Map providedInactive = new HashMap();
Map nonProvider = new HashMap();
for (RealmAndRole rar : grants)
{
// active and provided are the currently stored provider grants
if (rar.provided)
{
if (existing.containsKey(rar.realmId))
{
M_log.warn("refreshUser: duplicate realm id found in provider grants: " + rar.realmId);
}
else
{
existing.put(rar.realmId, rar.role);
// Record inactive status
if (!rar.active) {
providedInactive.put(rar.realmId, rar.role);
}
}
}
// inactive or not provided are the currently stored internal grants - not to be overwritten by provider info
else
{
if (nonProvider.containsKey(rar.realmId))
{
M_log.warn("refreshUser: duplicate realm id found in nonProvider grants: " + rar.realmId);
}
else
{
nonProvider.put(rar.realmId, rar.role);
}
}
}
// compute the user's realm roles based on the new provider information
// same map form as existing, realm id -> role granted
Map target = new HashMap();
// for each realm that has a provider in the map, and does not have a grant for the user,
// add the active provided grant with the map's role.
if ((providerGrants != null) && (providerGrants.size() > 0))
{
// get all the realms that have providers in the map, with their full provider id
// Assemble SQL. Note: distinct must be used because one cannot establish an equijoin between
// SRP.PROVIDER_ID and SR.PROVIDER_ID as the values in SRP.PROVIDER_ID often include
// additional concatenated course values. It may be worth reviewing this strategy.
sql = dbAuthzGroupSql.getSelectRealmProviderSql(orInClause(providerGrants.size(), "SRP.PROVIDER_ID"));
Object[] fieldsx = new Object[providerGrants.size()];
int pos = 0;
for (String providerId : providerGrants.keySet())
{
fieldsx[pos++] = providerId;
}
List realms = m_sql.dbRead(sql, fieldsx, new SqlReader()
{
public Object readSqlResultRecord(ResultSet result)
{
try
{
int id = result.getInt(1);
String provider = result.getString(2);
return new RealmAndProvider(Integer.valueOf(id), provider);
}
catch (Exception ignore)
{
return null;
}
}
});
if ((realms != null) && (realms.size() > 0))
{
for (RealmAndProvider rp : realms)
{
String role = providerGrants.get(rp.providerId);
if (role != null)
{
if (target.containsKey(rp.realmId))
{
M_log.warn("refreshUser: duplicate realm id computed for new grants: " + rp.realmId);
}
else
{
target.put(rp.realmId, role);
}
}
}
}
}
// compute the records we need to delete: every existing not in target or not matching target's role
List toDelete = new Vector();
for (Map.Entry entry : existing.entrySet())
{
Integer realmId = (Integer) entry.getKey();
String role = (String) entry.getValue();
String targetRole = (String) target.get(realmId);
if ((targetRole == null) || (!targetRole.equals(role)))
{
toDelete.add(realmId);
}
}
// compute the records we need to add: every target not in existing, or not matching's existing's role
// we don't insert target grants that would override internal grants
List toInsert = new Vector();
for (Map.Entry entry : target.entrySet())
{
Integer realmId = entry.getKey();
String role = entry.getValue();
String existingRole = (String) existing.get(realmId);
String nonProviderRole = (String) nonProvider.get(realmId);
if ((nonProviderRole == null) && ((existingRole == null) || (!existingRole.equals(role))))
{
boolean active = true;
if (providedInactive.get(realmId) != null) {
active = false;
}
toInsert.add(new RealmAndRole(realmId, role, active, true));
}
}
// if any, do it
if ((toDelete.size() > 0) || (toInsert.size() > 0))
{
// do these each in their own transaction, to avoid possible deadlock
// caused by transactions modifying more than one row at a time.
// delete
sql = dbAuthzGroupSql.getDeleteRealmRoleGroup3Sql();
fields = new Object[2];
fields[1] = userId;
for (Integer realmId : toDelete)
{
fields[0] = realmId;
m_sql.dbWrite(sql, fields);
}
// insert
sql = dbAuthzGroupSql.getInsertRealmRoleGroup2Sql();
fields = new Object[3];
fields[1] = userId;
for (RealmAndRole rar : toInsert)
{
fields[0] = rar.realmId;
fields[2] = getValueForSubquery(dbAuthzGroupSql.getInsertRealmRoleGroup2_1Sql(), rar.role);
m_sql.dbWrite(sql, fields);
}
}
}
/**
* {@inheritDoc}
*/
public void refreshAuthzGroup(BaseAuthzGroup azGroup) {
if (azGroup == null) return;
// Add the AuthzGroup to the queue, keyed on id to eliminate duplicate refreshes
if (M_log.isDebugEnabled()) M_log.debug("refreshAuthzGroup() queue add " + azGroup.getId());
refreshQueue.put(azGroup.getId(), azGroup);
}
/**
* Update the realm with info from the provider
*
* @param realm the realm to be refreshed
*/
protected void refreshAuthzGroupInternal(BaseAuthzGroup realm)
{
if ((realm == null) || (m_provider == null)) return;
if (M_log.isDebugEnabled()) M_log.debug("refreshAuthzGroupInternal() refreshing " + realm.getId());
boolean synchWithContainingRealm = serverConfigurationService().getBoolean("authz.synchWithContainingRealm", true);
// check to see whether this is of group realm or not
// if of Group Realm, get the containing Site Realm
String containingRealmId = null;
AuthzGroup containingRealm = null;
Reference ref = entityManager.newReference(realm.getId());
if (SiteService.APPLICATION_ID.equals(ref.getType())
&& SiteService.GROUP_SUBTYPE.equals(ref.getSubType()))
{
containingRealmId = ref.getContainer();
}
if (containingRealmId != null)
{
String containingRealmRef = siteService.siteReference(containingRealmId);
try
{
containingRealm = getAuthzGroup(containingRealmRef);
}
catch (GroupNotDefinedException e)
{
M_log.warn("refreshAuthzGroupInternal() cannot find containing realm for id: " + containingRealmRef);
}
}
String sql = "";
// Note: the realm is still lazy - we have the realm id but don't need to worry about changing grants
// get the latest userEid -> role name map from the provider
Map target = m_provider.getUserRolesForGroup(realm.getProviderGroupId());
// read the realm's grants
List grants = getGrants(realm);
// make a map, user id -> role granted, each for provider and non-provider (or inactive)
Map existing = new HashMap();
Map providedInactive = new HashMap();
Map nonProvider = new HashMap();
for (UserAndRole uar : grants)
{
// active and provided are the currently stored provider grants
if (uar.provided)
{
if (existing.containsKey(uar.userId))
{
M_log.warn("refreshAuthzGroupInternal() duplicate user id found in provider grants: " + uar.userId);
}
else
{
existing.put(uar.userId, uar.role);
// Record inactive status
if (!uar.active) {
providedInactive.put(uar.userId, uar.role);
}
}
}
// inactive or not provided are the currently stored internal grants - not to be overwritten by provider info
else
{
if (nonProvider.containsKey(uar.userId))
{
M_log.warn("refreshAuthzGroupInternal() duplicate user id found in nonProvider grants: " + uar.userId);
}
else
{
nonProvider.put(uar.userId, uar.role);
}
}
}
// compute the records we need to delete: every existing not in target or not matching target's role
List toDelete = new Vector();
for (Map.Entry entry : existing.entrySet())
{
String userId = entry.getKey();
String role = entry.getValue();
try
{
String userEid = userDirectoryService().getUserEid(userId);
String targetRole = (String) target.get(userEid);
Member cMember = null;
if (containingRealm != null)
{
cMember = containingRealm.getMember(userId);
}
// KNL-1273 - special case - sync role and active status with containing realm grants for this provided user
if (synchWithContainingRealm && cMember != null && targetRole != null)
{
// the sync code in the next loop performs the delete if necessary,
// so we do nothing here except prevent the delete code in this loop
// from running
}
else
{
if ((targetRole == null) || (!targetRole.equals(role)))
{
toDelete.add(userId);
}
}
}
catch (UserNotDefinedException e)
{
M_log.warn("refreshAuthzGroupInternal() cannot find eid for user: " + userId);
}
}
// compute the records we need to add: every target not in existing, or not matching's existing's role
// we don't insert target grants that would override internal grants
List toInsert = new Vector();
for (Map.Entry entry : target.entrySet())
{
String userEid = entry.getKey();
try
{
String userId = userDirectoryService().getUserId(userEid);
String role = entry.getValue();
boolean active = true;
String existingRole = (String) existing.get(userId);
String nonProviderRole = (String) nonProvider.get(userId);
Member cMember = null;
if (containingRealm != null)
{
cMember = containingRealm.getMember(userId);
}
// KNL-1273 - special case - sync role and active status with containing realm grants for this provided user
if (synchWithContainingRealm && cMember != null && nonProviderRole == null)
{
// determines if realm update is required because role or active status differs from containing realm grants
boolean insertRequired = true;
String cMemberRoleId = cMember.getRole() != null ? cMember.getRole().getId() : null;
boolean cMemberActive = cMember.isActive();
// the user has a provided realm entry already, so delete it before inserting if needed
if (existingRole != null)
{
boolean roleEqual = existingRole.equals(cMemberRoleId);
// user is currently active if not in the providedInactive map
boolean currentlyActive = providedInactive.get(userId) == null;
boolean activeEqual = currentlyActive == cMemberActive;
insertRequired = !roleEqual || !activeEqual;
if (insertRequired)
{
toDelete.add(userId);
}
}
if (insertRequired)
{
// Add or update user's role and active status to match containg realm grants
toInsert.add(new UserAndRole(userId, cMemberRoleId, cMemberActive, true));
if ((existingRole != null && !existingRole.equals(cMemberRoleId)) // overriding existing authz group role
||!role.equals(cMemberRoleId)) // overriding provided role
{
M_log.info("refreshAuthzGroupInternal() realm id=" + realm.getId() + ", overrides group role of user eid=" + userEid + ": provided role=" + role + ", with site-level role=" + cMemberRoleId + " and site-level active status=" + cMemberActive);
}
}
}
else
{
if ((nonProviderRole == null) && ((existingRole == null) || (!existingRole.equals(role))))
{
// Check whether this user was inactive in the site previously, if so preserve status
if (providedInactive.get(userId) != null)
{
active = false;
}
// this is either at site level or at the group level but no need to synchronize
toInsert.add(new UserAndRole(userId, role, active, true));
}
}
}
catch (UserNotDefinedException e)
{
M_log.warn("refreshAuthzGroupInternal() cannot find id for user eid: " + userEid);
}
}
if (promoteUsersToProvided)
{
// compute the records we want to promote from non-provided to provider:
// every non-provided user with an equivalent provided entry with the same role
for (Map.Entry entry : nonProvider.entrySet())
{
String userId = entry.getKey();
String role = entry.getValue();
try
{
String userEid = userDirectoryService().getUserEid(userId);
String targetRole = (String) target.get(userEid);
if (role.equals(targetRole))
{
// remove from non-provided and add as provided
toDelete.add(userId);
// Check whether this user was inactive in the site previously, if so preserve status
boolean active = true;
if (providedInactive.get(userId) != null) {
active = false;
}
toInsert.add(new UserAndRole(userId, role, active, true));
}
}
catch (UserNotDefinedException e)
{
M_log.warn("refreshAuthzGroupInternal() cannot find eid for user: " + userId);
}
}
}
// if any, do it
if ((toDelete.size() > 0) || (toInsert.size() > 0))
{
// do these each in their own transaction, to avoid possible deadlock
// caused by transactions modifying more than one row at a time.
// delete
sql = dbAuthzGroupSql.getDeleteRealmRoleGroup4Sql();
Object[] fields = new Object[2];
fields[0] = caseId(realm.getId());
for (String userId : toDelete)
{
fields[1] = userId;
m_sql.dbWrite(sql, fields);
}
// insert
sql = dbAuthzGroupSql.getInsertRealmRoleGroup3Sql();
fields = new Object[5];
fields[0] = caseId(realm.getId());
fields[0] = getValueForSubquery(dbAuthzGroupSql.getInsertRealmRoleGroup3_1Sql(), fields[0]);
for (UserAndRole uar : toInsert)
{
fields[1] = uar.userId;
fields[2] = getValueForSubquery(dbAuthzGroupSql.getInsertRealmRoleGroup3_2Sql(), uar.role);
fields[3] = uar.active ? "1" : "0"; // KNL-1099
fields[4] = uar.provided ? "1" : "0"; // KNL-1099
m_sql.dbWrite(sql, fields);
}
eventTrackingService().post(eventTrackingService().newEvent(SECURE_UPDATE_AUTHZ_GROUP, realm.getReference(), true));
}
if (M_log.isDebugEnabled()) {
M_log.debug("refreshAuthzGroupInternal() deleted: "+ toDelete.size()+ " inserted: "+ toInsert.size()+ " provided: "+ existing.size()+ " nonProvider: "+ nonProvider.size());
}
}
private List getGrants(AuthzGroup realm) {
// read the realm's grants
String sql = dbAuthzGroupSql.getSelectRealmRoleGroup2Sql();
Object[] fields = new Object[1];
fields[0] = caseId(realm.getId());
List grants = m_sql.dbRead(sql, fields, new SqlReader()
{
public Object readSqlResultRecord(ResultSet result)
{
try
{
String userId = result.getString(1);
String roleName = result.getString(2);
String active = result.getString(3);
String provided = result.getString(4);
return new UserAndRole(userId, roleName, "1".equals(active), "1".equals(provided));
}
catch (Exception ignore)
{
return null;
}
}
});
return grants;
}
/**
* {@inheritDoc}
*/
public String getUserRole(String userId, String azGroupId)
{
if ((userId == null) || (azGroupId == null)) return null;
// checks to see if the user is the current user and has the roleswap variable set in the session
String rv = null;
if (userId.equals(sessionManager().getCurrentSessionUserId())) {
rv = securityService().getUserEffectiveRole(azGroupId);
}
// otherwise drop through to the usual check
if (rv == null) {
String sql = dbAuthzGroupSql.getSelectRealmRoleNameSql();
Object[] fields = new Object[2];
fields[0] = azGroupId;
fields[1] = userId;
// read the string
List results = m_sql.dbRead(sql, fields, null);
// prepare the return
if ((results != null) && (!results.isEmpty()))
{
rv = (String) results.get(0);
if (results.size() > 1)
{
M_log.warn("getUserRole: user: " + userId + " multiple roles");
}
}
}
return rv;
}
/**
* {@inheritDoc}
*/
public Map getUserRoles(String userId, Collection azGroupIds)
{
final HashMap rv = new HashMap();
if (userId == null || "".equals(userId))
return rv;
String inClause;
int azgCount = azGroupIds == null ? 0 : azGroupIds.size();
if (azgCount == 0) {
inClause = " 1=1 ";
}
else {
inClause = orInClause(azgCount, "REALM_ID");
}
String sql = dbAuthzGroupSql.getSelectRealmRolesSql(inClause);
Object[] fields = new Object[1 + azgCount];
fields[0] = userId;
if (azgCount > 0) {
int pos = 1;
for (String s : azGroupIds) {
fields[pos++] = s;
}
}
m_sql.dbRead(sql, fields, new SqlReader()
{
public Object readSqlResultRecord(ResultSet result)
{
try
{
String realmId = result.getString(1);
String roleName = result.getString(2);
// ignore if we get an unexpected null -- it's useless to us
if ((realmId != null) && (roleName != null))
{
rv.put(realmId, roleName);
}
}
catch (Exception t)
{
M_log.warn("Serious database error occurred reading result set", t);
}
return null;
}
});
return rv;
}
/**
* {@inheritDoc}
*/
public Map getUsersRole(Collection userIds, String azGroupId)
{
if ((userIds == null) || (userIds.isEmpty()) || (azGroupId == null))
{
return new HashMap();
}
String inClause = orInClause(userIds.size(), "SRRG.USER_ID");
String sql = dbAuthzGroupSql.getSelectRealmUserRoleSql(inClause);
Object[] fields = new Object[1 + userIds.size()];
fields[0] = azGroupId;
int pos = 1;
for (Iterator i = userIds.iterator(); i.hasNext();)
{
fields[pos++] = i.next();
}
// the return
final Map rv = new HashMap();
// read
m_sql.dbRead(sql, fields, new SqlReader()
{
public Object readSqlResultRecord(ResultSet result)
{
try
{
// read the results
String userId = result.getString(1);
String role = result.getString(2);
if ((userId != null) && (role != null))
{
rv.put(userId, role);
}
}
catch (Exception t)
{
}
return null;
}
});
return rv;
}
public Set getMaintainRoles(){
Set maintainRoles = null;
if (maintainRolesCache != null && maintainRolesCache.containsKey("maintainRoles")) {
maintainRoles = (Set) maintainRolesCache.get("maintainRoles");
} else {
String sql = dbAuthzGroupSql.getMaintainRolesSql();
maintainRoles = new HashSet(m_sql.dbRead(sql));
maintainRolesCache.put("maintainRoles", maintainRoles);
}
return maintainRoles;
}
private class UserAndGroups
{
String user;
long total;
long hit;
Map> realmsQuery;
public UserAndGroups(String userid) {
this.user = userid;
this.total = 0;
this.hit = 0;
this.realmsQuery = new HashMap>();
}
void addRealmQuery(Set query, List result) {
if (query == null || query.size() < 1) return;
total++;
Long queryHash = computeRealmQueryHash(query);
if (queryHash != null) {
if (result == null) result = Collections.emptyList();
realmsQuery.put(queryHash, result);
}
}
List getRealmQuery(Set query) {
if (query == null || query.size() < 1) return null;
List result = null;
total++;
Long queryHash = computeRealmQueryHash(query);
if (queryHash != null) {
if (realmsQuery.containsKey(queryHash)) {
result = realmsQuery.get(queryHash);
hit++;
}
}
return result;
}
Long computeRealmQueryHash(Set query) {
if (query == null || query.size() == 0) return null;
long hash = 0;
for (String q : query) {
hash += q.hashCode();
}
return Long.valueOf(hash);
}
@Override
public int hashCode() {
return user.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == null) return false;
if (this == obj) return true;
if (getClass() != obj.getClass())
return false;
UserAndGroups other = (UserAndGroups) obj;
if (user == null) {
if (other.user != null)
return false;
} else if (!user.equals(other.user))
return false;
return true;
}
@Override
public String toString() {
return "UserAndGroups [" + (user != null ? "user=" + user : "") + "]" +
" size=" + realmsQuery.size() + ", total=" + total + ", hits=" + hit + ", hit ratio=" + (hit * 100) / (float) total;
}
}
public class RealmAndProvider
{
public Integer realmId;
public String providerId;
public RealmAndProvider(Integer id, String provider)
{
this.realmId = id;
this.providerId = provider;
}
}
public class RealmAndRole
{
public Integer realmId;
public String role;
boolean active;
boolean provided;
public RealmAndRole(Integer id, String role, boolean active, boolean provided)
{
this.realmId = id;
this.role = role;
this.active = active;
this.provided = provided;
}
public boolean equals(Object obj)
{
if (!(obj instanceof RealmAndRole)) return false;
if (this == obj) return true;
RealmAndRole other = (RealmAndRole) obj;
if (StringUtil.different(this.role, other.role)) return false;
if (this.provided != other.provided) return false;
if (this.active != other.active) return false;
if (((this.realmId == null) && (other.realmId != null)) || ((this.realmId != null) && (other.realmId == null))
|| ((this.realmId != null) && (other.realmId != null) && (!this.realmId.equals(other.realmId)))) return false;
return true;
}
public int hashCode()
{
return (this.role + Boolean.valueOf(this.provided).toString() + Boolean.valueOf(this.active).toString() + this.realmId).hashCode();
}
}
public class UserAndRole
{
public String userId;
public String role;
boolean active;
boolean provided;
public UserAndRole(String userId, String role, boolean active, boolean provided)
{
this.userId = userId;
this.role = role;
this.active = active;
this.provided = provided;
}
public boolean equals(Object obj)
{
if (!(obj instanceof UserAndRole)) return false;
if (this == obj) return true;
UserAndRole other = (UserAndRole) obj;
if (StringUtil.different(this.role, other.role)) return false;
if (this.provided != other.provided) return false;
if (this.active != other.active) return false;
if (StringUtil.different(this.userId, other.userId)) return false;
return true;
}
public int hashCode()
{
return (this.role + Boolean.valueOf(this.provided).toString() + Boolean.valueOf(this.active).toString() + this.userId).hashCode();
}
}
public class RoleAndFunction
{
public String role;
public String function;
public RoleAndFunction(String role, String function)
{
this.role = role;
this.function = function;
}
public boolean equals(Object obj)
{
if (!(obj instanceof RoleAndFunction)) return false;
if (this == obj) return true;
RoleAndFunction other = (RoleAndFunction) obj;
if (StringUtil.different(this.role, other.role)) return false;
if (StringUtil.different(this.function, other.function)) return false;
return true;
}
public int hashCode()
{
return (this.role + this.function).hashCode();
}
}
public class RoleAndDescription
{
public String role;
public String description;
public boolean providerOnly;
public RoleAndDescription(String role, String description, boolean providerOnly)
{
this.role = role;
this.description = description;
this.providerOnly = providerOnly;
}
public boolean equals(Object obj)
{
if (!(obj instanceof RoleAndDescription)) return false;
if (this == obj) return true;
RoleAndDescription other = (RoleAndDescription) obj;
if (StringUtil.different(this.role, other.role)) return false;
if (StringUtil.different(this.description, other.description)) return false;
if (this.providerOnly != other.providerOnly) return false;
return true;
}
public int hashCode()
{
return (this.role + this.description + Boolean.valueOf(this.providerOnly).toString()).hashCode();
}
}
} // DbStorage
private Set getRealmRoleKeys(Set roles) {
Set roleIds = new HashSet();
for(String role: roles) {
Integer realmRoleKey = getRealmRoleKey(role);
// If the role hasn't yet been used then it won't exist and so we can't lookup it's ID.
if (realmRoleKey != null) {
roleIds.add(realmRoleKey);
}
}
return roleIds;
}
class RealmRole implements Comparable{
private String name;
private Integer key;
RealmRole(String name) {
this.name = name;
}
RealmRole(String name, Integer key) {
this.name = name;
this.key = key;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getKey() {
return key;
}
public void setKey(Integer key) {
this.key = key;
}
public int compareTo(RealmRole realmRole) {
return this.name.compareToIgnoreCase(realmRole.name);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy