package org.zstack.tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.zstack.core.db.DatabaseFacade;
import org.zstack.core.db.SimpleQuery;
import org.zstack.core.db.SimpleQuery.Op;
import org.zstack.core.errorcode.ErrorFacade;
import org.zstack.header.apimediator.ApiMessageInterceptionException;
import org.zstack.header.apimediator.ApiMessageInterceptor;
import org.zstack.header.errorcode.OperationFailureException;
import org.zstack.header.exception.CloudRuntimeException;
import org.zstack.header.identity.*;
import org.zstack.header.message.APIMessage;
import org.zstack.header.tag.*;
import org.zstack.identity.QuotaUtil;
import static org.zstack.core.Platform.argerr;
import static org.zstack.core.Platform.operr;
import javax.persistence.TypedQuery;
/**
*/
public class TagApiInterceptor implements ApiMessageInterceptor {
@Autowired
private DatabaseFacade dbf;
@Autowired
private ErrorFacade errf;
@Autowired
private TagManager tagMgr;
@Override
public APIMessage intercept(APIMessage msg) throws ApiMessageInterceptionException {
if (msg instanceof APIDeleteTagMsg) {
validate((APIDeleteTagMsg) msg);
} else if (msg instanceof APICreateTagMsg) {
validate((APICreateTagMsg) msg);
} else if (msg instanceof APIUpdateSystemTagMsg) {
validate((APIUpdateSystemTagMsg) msg);
}
return msg;
}
private void validate(APIUpdateSystemTagMsg msg) {
SystemTagVO vo = dbf.findByUuid(msg.getUuid(), SystemTagVO.class);
try {
tagMgr.validateSystemTag(vo.getResourceUuid(), vo.getResourceType(), msg.getTag());
} catch (OperationFailureException oe) {
throw new ApiMessageInterceptionException(oe.getErrorCode());
}
}
private void validate(APICreateTagMsg msg) {
if (!tagMgr.getManagedEntityNames().contains(msg.getResourceType())) {
throw new ApiMessageInterceptionException(argerr("no resource type[%s] found in tag system", msg.getResourceType()));
}
if (msg instanceof APICreateSystemTagMsg) {
try {
tagMgr.validateSystemTag(msg.getResourceUuid(), msg.getResourceType(), msg.getTag());
} catch (OperationFailureException oe) {
throw new ApiMessageInterceptionException(oe.getErrorCode());
}
checkIfResourceHasThisTagType(msg.getResourceUuid(), msg.getResourceType());
}
}
private void validate(APIDeleteTagMsg msg) {
SimpleQuery<SystemTagVO> q = dbf.createQuery(SystemTagVO.class);
q.add(SystemTagVO_.uuid, Op.EQ, msg.getUuid());
q.add(SystemTagVO_.inherent, Op.EQ, true);
if (q.isExists()) {
throw new ApiMessageInterceptionException(operr("tag[uuid:%s] is an inherent system tag, can not be removed", msg.getUuid()));
}
boolean userTag = dbf.isExist(msg.getUuid(), UserTagVO.class);
boolean sysTag = dbf.isExist(msg.getUuid(), SystemTagVO.class);
if (!new QuotaUtil().isAdminAccount(msg.getSession().getAccountUuid())) {
if (userTag) {
checkAccountForUserTag(msg);
} else if (sysTag) {
checkAccountForSystemTag(msg);
}
}
}
@Transactional(readOnly = true)
private void checkIfResourceHasThisTagType(String resourceUuid, String resourceType) {
String sql = String.format("select count(vo.uuid) from %s vo where " +
" vo.uuid = :resourceUuid", resourceType);
TypedQuery<Long> q = dbf.getEntityManager().createQuery(sql, Long.class);
q.setParameter("resourceUuid", resourceUuid);
Long size = q.getSingleResult();
if (size <= 0) {
throw new ApiMessageInterceptionException(argerr("The argument :'resourceType' doesn't match uuid"));
}
}
@Transactional(readOnly = true)
private void checkAccountForSystemTag(APIDeleteTagMsg msg) {
String sql = "select ref.accountUuid" +
" from SystemTagVO tag, AccountResourceRefVO ref" +
" where tag.resourceUuid = ref.resourceUuid" +
" and tag.uuid = :tuuid";
TypedQuery<String> q = dbf.getEntityManager().createQuery(sql, String.class);
q.setParameter("tuuid", msg.getUuid());
String accountUuid = q.getSingleResult();
if (!msg.getSession().getAccountUuid().equals(accountUuid)) {
throw new ApiMessageInterceptionException(errf.instantiateErrorCode(IdentityErrors.PERMISSION_DENIED,
String.format("permission denied. The system tag[uuid: %s] refer to a resource not belonging to the account[uuid: %s]",
msg.getUuid(), msg.getSession().getAccountUuid())
));
}
}
@Transactional(readOnly = true)
private void checkAccountForUserTag(APIDeleteTagMsg msg) {
String sql = "select ref.accountUuid from UserTagVO tag, AccountResourceRefVO ref where tag.resourceUuid = ref.resourceUuid and tag.uuid = :tuuid";
TypedQuery<String> q = dbf.getEntityManager().createQuery(sql, String.class);
q.setParameter("tuuid", msg.getUuid());
String accountUuid = q.getSingleResult();
if (!msg.getSession().getAccountUuid().equals(accountUuid)) {
throw new ApiMessageInterceptionException(errf.instantiateErrorCode(IdentityErrors.PERMISSION_DENIED,
String.format("permission denied. The user tag[uuid: %s] refer to a resource not belonging to the account[uuid: %s]",
msg.getUuid(), msg.getSession().getAccountUuid())
));
}
}
}