/*
* NOTE: This copyright does *not* cover user programs that use HQ
* program services by normal system calls through the application
* program interfaces provided as part of the Hyperic Plug-in Development
* Kit or the Hyperic Client Development Kit - this is merely considered
* normal use of the program, and does *not* fall under the heading of
* "derived work".
*
* Copyright (C) [2004-2008], Hyperic, Inc.
* This file is part of HQ.
*
* HQ is free software; you can redistribute it and/or modify
* it under the terms version 2 of the GNU General Public License as
* published by the Free Software Foundation. This program is distributed
* in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*/
package org.hyperic.hq.authz.server.session;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.FlushMode;
import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.hyperic.hibernate.PageInfo;
import org.hyperic.hibernate.dialect.HQDialect;
import org.hyperic.hq.appdef.server.session.Server;
import org.hyperic.hq.appdef.shared.ServerManager;
import org.hyperic.hq.appdef.shared.ServerNotFoundException;
import org.hyperic.hq.authz.shared.AuthzConstants;
import org.hyperic.hq.authz.shared.EdgePermCheck;
import org.hyperic.hq.authz.shared.PermissionManager;
import org.hyperic.hq.context.Bootstrap;
import org.hyperic.hq.dao.HibernateDAO;
import org.hyperic.hq.measurement.server.session.MonitorableType;
import org.hyperic.util.pager.PageControl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
@Repository
public class ResourceDAO
extends HibernateDAO<Resource> {
private Log _log = LogFactory.getLog(ResourceDAO.class);
private PermissionManager permissionManager;
@Autowired
public ResourceDAO(SessionFactory f, PermissionManager permissionManager) {
super(Resource.class, f);
this.permissionManager = permissionManager;
}
Resource create(ResourceType type, Resource prototype, String name, AuthzSubject creator,
Integer instanceId, boolean system) {
Resource resource = createPrivate(type, prototype, name, creator, instanceId, system);
/* add it to the root resource group */
// TODO resolve circular dependencies to remove Bootstrap
ResourceGroupDAO resourceGroupDAO = Bootstrap.getBean(ResourceGroupDAO.class);
ResourceGroup authzGroup = resourceGroupDAO.findRootGroup();
resourceGroupDAO.addMembers(authzGroup, Collections.singleton(resource));
return resource;
}
Resource createPrivate(ResourceType type, Resource prototype, String name,
AuthzSubject creator, Integer instanceId, boolean system) {
if (type == null) {
throw new IllegalArgumentException("ResourceType not set");
}
Resource resource = new Resource(type, prototype, name, creator, instanceId, system);
save(resource);
// Need to flush so that later permission checking can succeed
getSession().flush();
return resource;
}
public Resource findRootResource() {
return findById(AuthzConstants.rootResourceId);
}
public void remove(Resource entity) {
// need this to ensure that the optimistic locking doesn't fail
entity.markDirty();
super.remove(entity);
}
public boolean isOwner(Resource entity, Integer possibleOwner) {
boolean is = false;
if (possibleOwner == null) {
_log.error("possible Owner is NULL. " + "This is probably not what you want.");
/* XXX throw exception instead */
} else {
/* overlord owns every thing */
if (is = possibleOwner.equals(AuthzConstants.overlordId) == false) {
if (_log.isDebugEnabled() && possibleOwner != null) {
_log.debug("User is " + possibleOwner + " owner is " +
entity.getOwner().getId());
}
is = (possibleOwner.equals(entity.getOwner().getId()));
}
}
return is;
}
public Resource findByInstanceId(ResourceType type, Integer id) {
return findByInstanceId(type.getId(), id);
}
@SuppressWarnings("unchecked")
List<Resource> findResourcesOfType(int typeId, PageInfo pInfo) {
String sql = "from Resource r where resourceType.id = :typeId ";
ResourceSortField sort = (ResourceSortField) pInfo.getSort();
sql += " order by " + sort.getSortString("r") + (pInfo.isAscending() ? "" : " DESC");
return pInfo.pageResults(getSession().createQuery(sql).setInteger("typeId", typeId)).list();
}
public Resource findByInstanceId(Integer typeId, Integer id) {
// Resource table is often updated. Manage the instance id and type
// to resource mapping manually to avoid the query cache getting
// invalidated on updates to the resource table.
Cache ridCache = CacheManager.getInstance().getCache("ResourceByInstanceId");
String key = typeId.toString() + id;
Element e = ridCache.get(key);
if (e != null) {
Integer rid = (Integer) e.getObjectValue();
return get(rid);
} else {
String sql = "from Resource where instanceId = ? and " + "resourceType.id = ?";
Resource r = (Resource) getSession().createQuery(sql).setInteger(0, id.intValue())
.setInteger(1, typeId.intValue()).uniqueResult();
if (r != null) {
ridCache.put(new Element(key, r.getId()));
}
return r;
}
}
/**
* Find a Resource by type Id and instance Id, allowing for the query to
* return a stale copy of the resource (for efficiency reasons).
*
* @param typeId The type Id.
* @param id The instance Id.
* @param allowStale <code>true</code> to allow stale copies of an alert
* definition in the query results; <code>false</code> to never allow
* stale copies, potentially always forcing a sync with the database.
* @return The Resource.
*/
public Resource findByInstanceId(Integer typeId, Integer id, boolean allowStale) {
FlushMode oldFlushMode = this.getSession().getFlushMode();
try {
if (allowStale) {
this.getSession().setFlushMode(FlushMode.MANUAL);
}
return findByInstanceId(typeId, id);
} finally {
this.getSession().setFlushMode(oldFlushMode);
}
}
@SuppressWarnings("unchecked")
public List<Resource> findByResource(AuthzSubject subject, Resource r) {
final String[] VIEW_APPDEFS = new String[] { AuthzConstants.platformOpViewPlatform,
AuthzConstants.serverOpViewServer,
AuthzConstants.serviceOpViewService, };
EdgePermCheck wherePermCheck = permissionManager.makePermCheckHql("rez", true);
String hql = "select rez from Resource rez " + wherePermCheck;
Query q = createQuery(hql);
return wherePermCheck.addQueryParameters(q, subject, r, 0, Arrays.asList(VIEW_APPDEFS))
.list();
}
public List<Resource> findChildren(AuthzSubject subject, Resource r) {
final String[] VIEW_APPDEFS = new String[] { AuthzConstants.platformOpViewPlatform,
AuthzConstants.serverOpViewServer,
AuthzConstants.serviceOpViewService, };
EdgePermCheck wherePermCheck = permissionManager.makePermCheckHql("rez", false);
String hql = "select rez from Resource rez " + wherePermCheck;
Query q = createQuery(hql);
return wherePermCheck.addQueryParameters(q, subject, r, 1, Arrays.asList(VIEW_APPDEFS)).list();
}
@SuppressWarnings("unchecked")
public Collection<Resource> findByOwner(AuthzSubject owner, int sortName) {
String sql = "from Resource where owner.id = ?";
switch (sortName) {
case PageControl.SORT_ASC:
sql = sql + " order by name asc";
break;
case PageControl.SORT_DESC:
sql = sql + " order by name desc";
break;
case PageControl.SORT_UNSORTED:
break;
default:
}
return getSession().createQuery(sql).setInteger(0, owner.getId().intValue()).list();
}
@SuppressWarnings("unchecked")
public Collection<Resource> findByOwnerAndType(AuthzSubject owner, ResourceType type) {
String sql = "from Resource where owner.id = ? and resourceType.id = ?";
return getSession().createQuery(sql).setInteger(0, owner.getId().intValue()).setInteger(1,
type.getId().intValue()).list();
}
@SuppressWarnings("unchecked")
public Collection<Resource> findViewableSvcRes_orderName(Integer user, Boolean fSystem) {
ResourceType serviceType = Bootstrap.getBean(ResourceTypeDAO.class).findByName(
AuthzConstants.serviceResType);
Operation op = Bootstrap.getBean(OperationDAO.class).findByTypeAndName(serviceType,
AuthzConstants.serviceOpViewService);
final String sql = new StringBuilder(1024).append("SELECT {res.*} ").append(
"FROM EAM_SUBJECT subject ").append(
"JOIN EAM_SUBJECT_ROLE_MAP subjrolemap on subject.ID = subjrolemap.SUBJECT_ID ")
.append("JOIN EAM_ROLE role on role.ID = subjrolemap.ROLE_ID ").append(
"JOIN EAM_ROLE_RESOURCE_GROUP_MAP rolegrpmap on rolegrpmap.ROLE_ID = role.ID ")
.append("JOIN EAM_RESOURCE res on res.RESOURCE_TYPE_ID = :resourceTypeId ").append(
"AND res.FSYSTEM = :system ").append(
"JOIN EAM_RES_GRP_RES_MAP grpmap on grpmap.RESOURCE_ID = res.ID ").append(
"JOIN EAM_RESOURCE_GROUP resgrp on resgrp.ID = grpmap.RESOURCE_GROUP_ID ").append(
"AND rolegrpmap.RESOURCE_GROUP_ID = resgrp.ID ").append(
"JOIN EAM_ROLE_OPERATION_MAP opmap on role.id = opmap.ROLE_ID ").append(
"AND opmap.OPERATION_ID = :opId ").append("WHERE subject.ID = :subjectId ").append(
"UNION ALL ").append("SELECT {res.*} ").append("FROM EAM_RESOURCE res ").append(
"WHERE res.SUBJECT_ID = :subjectId AND res.RESOURCE_TYPE_ID = :resourceTypeId ")
.append("AND res.FSYSTEM = :system ").toString();
List<Resource> resources = getSession().createSQLQuery(sql)
.addEntity("res", Resource.class).setBoolean("system", fSystem.booleanValue())
.setInteger("opId", op.getId().intValue()).setInteger("subjectId", user.intValue())
.setInteger("resourceTypeId", serviceType.getId().intValue()).list();
// use TreeSet to eliminate dups and sort by Resource
return new TreeSet<Resource>(resources);
}
@SuppressWarnings("unchecked")
public Collection<Resource> findSvcRes_orderName(Boolean fSystem) {
String sql = "select r from Resource r join r.resourceType rt "
+ "where r.system = :system and " + "(rt.name = :resSvcType or "
+ "exists (select rg from ResourceGroup rg " + "join rg.resource r2 "
+ "where r = r2 and rg.groupType = 15)) " + "order by r.sortName ";
return getSession().createQuery(sql).setBoolean("system", fSystem.booleanValue())
.setString("resSvcType", AuthzConstants.serviceResType).list();
}
@SuppressWarnings("unchecked")
public Collection<Resource> findInGroupAuthz_orderName(Integer userId, Integer groupId,
Boolean fSystem) {
String sql = "select distinct r from Resource r " + " join r.resourceGroups rgg"
+ " join r.resourceGroups rg " + " join rg.roles role "
+ " join role.subjects subj " + " join role.operations op " + "where "
+ " r.system = :system and " + " rgg.id = :groupId and "
+ " (subj.id = :subjectId or " + " r.owner.id = :subjectId or "
+ " subj.authDsn = 'covalentAuthzInternalDsn') and "
+ " op.resourceType.id = r.resourceType.id and " + " ("
+ " op.name = 'viewPlatform' or " + " op.name = 'viewServer' or "
+ " op.name = 'viewService' or " + " op.name = 'viewApplication' or "
+ " op.name = 'viewResourceGroup' )" + " order by r.sortName ";
return getSession().createQuery(sql).setBoolean("system", fSystem.booleanValue())
.setInteger("groupId", groupId.intValue()).setInteger("subjectId", userId.intValue())
.list();
}
@SuppressWarnings("unchecked")
public Collection<Resource> findInGroup_orderName(Integer groupId, Boolean fSystem) {
String sql = "select distinct r from Resource r " + " join r.resourceGroups rgg"
+ " join r.resourceGroups rg " + " join rg.roles role "
+ " join role.subjects subj " + " join role.operations op " + "where "
+ " r.system = :system and " + " rgg.id = :groupId and "
+ " (subj.id=1 or r.owner.id=1 or "
+ " subj.authDsn = 'covalentAuthzInternalDsn') and "
+ " op.resourceType.id = r.resourceType.id and "
+ " (op.name = 'viewPlatform' or " + " op.name = 'viewServer' or "
+ " op.name = 'viewService' or " + " op.name = 'viewApplication' or "
+ " op.name='viewResourceGroup' )" + " order by r.sortName ";
return getSession().createQuery(sql).setBoolean("system", fSystem.booleanValue())
.setInteger("groupId", groupId.intValue()).list();
}
@SuppressWarnings("unchecked")
public Collection<Resource> findScopeByOperationBatch(AuthzSubject subjLoc, Resource[] resLocArr,
Operation[] opLocArr) {
StringBuffer sb = new StringBuffer();
sb.append("SELECT DISTINCT r ").append("FROM Resource r ").append(
" join r.resourceGroups g ").append(" join g.roles e ").append(" join e.operations o ")
.append(" join e.subjects s ").append(" WHERE s.id = ? ").append(" AND ( ");
for (int x = 0; x < resLocArr.length; x++) {
if (x > 0)
sb.append(" OR ");
sb.append(" (o.id=").append(opLocArr[x].getId()).append(" AND r.id=").append(
resLocArr[x].getId()).append(") ");
}
sb.append(")");
return getSession().createQuery(sb.toString()).setInteger(0, subjLoc.getId().intValue())
.list();
}
/**
* Returns an ordered list of instance IDs for a given operation.
*/
@SuppressWarnings("unchecked")
public List<Integer> findAllResourcesInstancesForOperation(int opId) {
final String sql = "SELECT r.instanceId FROM Resource r, Operation o "
+ "WHERE o.resourceType = r.resourceType" + " AND o.id = :opId";
return getSession().createQuery(sql).setInteger("opId", opId).list();
}
int reassignResources(int oldOwner, int newOwner) {
return getSession().createQuery(
"UPDATE Resource " + "SET owner.id = :newOwner " + "WHERE owner.id = :oldOwner")
.setInteger("oldOwner", oldOwner).setInteger("newOwner", newOwner).executeUpdate();
}
boolean resourcesExistOfType(String typeName) {
String sql = "select r from Resource r " + "join r.prototype p "
+ "where p.name = :protoName";
return getSession().createQuery(sql).setParameter("protoName", typeName).setMaxResults(1)
.list().isEmpty() == false;
}
@SuppressWarnings("unchecked")
List<Resource> getResourcesByPrototype(Resource proto, String regex) {
String hql = "from Resource r where r.prototype = :proto";
if (regex != null) {
hql += " AND " + getHQDialect().getRegExSQL("r.name", regex, true, false);
}
return getSession().createQuery(hql).setParameter("proto", proto).list();
}
@SuppressWarnings("unchecked")
List<Resource> findResourcesOfPrototype(Resource proto, PageInfo pInfo) {
String sql = "select r from Resource r where r.prototype = :proto";
return pInfo.pageResults(getSession().createQuery(sql).setParameter("proto", proto)).list();
}
Resource findResourcePrototypeByName(String name) {
String sql = "select r from Resource r " + "where r.name = :name "
+ " AND r.resourceType.id in (:platProto, :svrProto, :svcProto)";
return (Resource) getSession().createQuery(sql).setParameter("name", name).setParameter(
"platProto", AuthzConstants.authzPlatformProto).setParameter("svrProto",
AuthzConstants.authzServerProto).setParameter("svcProto",
AuthzConstants.authzServiceProto).uniqueResult();
}
@SuppressWarnings("unchecked")
List<Resource> findAllAppdefPrototypes() {
String sql = "select r from Resource r "
+ "where r.resourceType.id in (:platProto, :svrProto, :svcProto)";
return getSession().createQuery(sql)
.setParameter("platProto", AuthzConstants.authzPlatformProto)
.setParameter("svrProto", AuthzConstants.authzServerProto)
.setParameter("svcProto", AuthzConstants.authzServiceProto)
.list();
}
@SuppressWarnings("unchecked")
List<Resource> findAppdefPrototypes() {
String sql = "select distinct r.prototype from Resource r "
+ "where r.resourceType.id in (:platProto, :svrProto, :svcProto) ";
return getSession().createQuery(sql)
.setParameter("platProto", AuthzConstants.authzPlatform)
.setParameter("svrProto", AuthzConstants.authzServer)
.setParameter("svcProto", AuthzConstants.authzService)
.list();
}
@SuppressWarnings("unchecked")
List<Resource> findByPlugin(String pluginName) {
String sql = "select s.resource from Server s "
+ "where s.serverType.plugin = :pluginName";
return getSession().createQuery(sql)
.setParameter("pluginName", pluginName)
.list();
}
public int getPlatformCountMinusVsphereVmPlatforms() {
String sql = "select count(*) from Resource r " + "where r.resourceType.id = :platProto "
+ "and r.prototype.name != :vspherevm";
return ((Number) getSession().createQuery(sql).setInteger("platProto",
AuthzConstants.authzPlatform.intValue()).setString("vspherevm",
AuthzConstants.platformPrototypeVmwareVsphereVm).uniqueResult()).intValue();
}
@SuppressWarnings("unchecked")
public Collection<Resource> getUnconfiguredResources() {
String hql = new StringBuilder(256)
.append("from Resource r ")
.append("where resourceType.id in (:platformType, :serverType, :serviceType) ")
.append("and not exists (select 1 from Measurement m where r.id = m.resource.id)")
.toString();
Collection<Resource> rtn =
getSession().createQuery(hql)
.setParameter("platformType", AuthzConstants.authzPlatform)
.setParameter("serverType", AuthzConstants.authzServer)
.setParameter("serviceType", AuthzConstants.authzService)
.list();
final ServerManager sMan = Bootstrap.getBean(ServerManager.class);
for (final Iterator<Resource> it=rtn.iterator(); it.hasNext(); ) {
final Resource r = it.next();
if (r == null || r.isInAsyncDeleteState()) {
it.remove();
continue;
}
if (r.getResourceType().getId().equals(AuthzConstants.authzServer)) {
try {
final Server server = sMan.findServerById(r.getInstanceId());
if (server.getServerType().isVirtual()) {
it.remove();
}
} catch (ServerNotFoundException e) {
_log.debug(e,e);
continue;
}
}
}
return rtn;
}
@SuppressWarnings("unchecked")
Collection<Integer> getParentResourceIds(Collection<Resource> resources, ResourceRelation relation, int startdistance,
int maxdistance) {
final List<Resource> resourceList = new ArrayList<Resource>(resources);
final String hql = new StringBuilder(64)
.append("select distinct re.to.id FROM ResourceEdge re ")
.append("WHERE re.distance between :start and :end and re.relation=:relation ")
.append("AND re.from in (:from)")
.toString();
final Query query = getSession().createQuery(hql);
final HQDialect dialect = getHQDialect();
final int batchSize = (dialect.getMaxExpressions() < 0) ? Integer.MAX_VALUE : dialect.getMaxExpressions();
final Collection<Integer> rtn = new ArrayList<Integer>(resources.size());
for (int ii=0; ii<resources.size(); ii+=batchSize) {
final int last = Math.min(resources.size(), batchSize+ii);
rtn.addAll(query.setParameterList("from", resourceList.subList(ii, last))
.setParameter("relation", relation)
.setParameter("start", Math.min(startdistance, maxdistance))
.setParameter("end", Math.max(startdistance, maxdistance))
.list());
}
return rtn;
}
@SuppressWarnings("unchecked")
Map<String, Long> getResourceCountByProtoTypeName(Collection<MonitorableType> types) {
if (types == null || types.isEmpty()) {;
return Collections.emptyMap();
}
final Map<String, String> map = new HashMap<String, String>();
for (final MonitorableType mt : types) {
map.put(mt.getName(), mt.getPlugin());
}
final String hql = new StringBuilder()
.append("select count(*), prototype.name from Resource ")
.append("where prototype.name in (:typeNames) group by prototype.name")
.toString();
final Collection<Object[]> list = getSession().createQuery(hql)
.setParameterList("typeNames", map.keySet())
.list();
final Map<String, Long> rtn = new HashMap<String, Long>();
for (final Object[] objs : list) {
final String pluginName = map.get(objs[1]);
if (pluginName == null) {
continue;
}
Long count = rtn.get(pluginName);
if (count == null) {
rtn.put(pluginName, ((Number) objs[0]).longValue());
} else {
rtn.put(pluginName, count + ((Number) objs[0]).longValue());
}
}
return rtn;
}
@SuppressWarnings("unchecked")
Collection<Resource> getResourcesByProtoTypeName(Collection<String> typeNames) {
if (typeNames == null || typeNames.isEmpty()) {
return Collections.emptyList();
}
final String hql = "from Resource where prototype.name in (:typeNames)";
return getSession().createQuery(hql)
.setParameterList("typeNames", typeNames)
.list();
}
@SuppressWarnings("unchecked")
Collection<Resource> getOrphanedResources() {
final List<Resource> rtn = new ArrayList<Resource>();
// normally i would use a union all in sql instead of three different queries,
// but hibernate keeps giving an NPE when I try that
final String platformHql = new StringBuilder(128)
.append("from Resource r where r.resourceType.id = :platformType and not exists (")
.append("select 1 from Platform p where p.id = r.instanceId")
.append(")")
.toString();
rtn.addAll(createQuery(platformHql).setInteger("platformType", AuthzConstants.authzPlatform).list());
final String serverHql = new StringBuilder(128)
.append("from Resource r where r.resourceType.id = :serverType and not exists (")
.append("select 1 from Server s where s.id = r.instanceId")
.append(")")
.toString();
rtn.addAll(createQuery(serverHql).setInteger("serverType", AuthzConstants.authzServer).list());
final String serviceHql = new StringBuilder(128)
.append("from Resource r where r.resourceType.id = :serviceType and not exists (")
.append("select 1 from Service s where s.id = r.instanceId")
.append(")")
.toString();
rtn.addAll(createQuery(serviceHql).setInteger("serviceType", AuthzConstants.authzService).list());
return rtn;
}
}