Suring startup load the API permissions from commands.properties and @APICommand annotations

This commit is contained in:
Prachi Damle 2013-11-25 17:01:56 -08:00
parent f231cec5b7
commit cf69731a52
9 changed files with 183 additions and 97 deletions

View File

@ -19,10 +19,12 @@ package org.apache.cloudstack.acl;
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
import org.apache.cloudstack.api.InternalIdentity;
public interface AclPermission extends InternalIdentity {
public interface AclPolicyPermission extends InternalIdentity {
String getAction();
long getAclPolicyId();
String getEntityType();
AccessType getAccessType();

View File

@ -19,7 +19,7 @@ package org.apache.cloudstack.api.response;
import com.google.gson.annotations.SerializedName;
import org.apache.cloudstack.acl.AclEntityType;
import org.apache.cloudstack.acl.AclPermission;
import org.apache.cloudstack.acl.AclPolicyPermission;
import org.apache.cloudstack.acl.PermissionScope;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseResponse;
@ -46,7 +46,7 @@ public class AclPermissionResponse extends BaseResponse {
@SerializedName(ApiConstants.ACL_ALLOW_DENY)
@Param(description = "allow or deny of this permission")
private AclPermission.Permission permission;
private AclPolicyPermission.Permission permission;
public AclEntityType getEntityType() {
return entityType;
@ -80,11 +80,11 @@ public class AclPermissionResponse extends BaseResponse {
this.scopeId = scopeId;
}
public AclPermission.Permission getPermission() {
public AclPolicyPermission.Permission getPermission() {
return permission;
}
public void setPermission(AclPermission.Permission permission) {
public void setPermission(AclPolicyPermission.Permission permission) {
this.permission = permission;
}

View File

@ -325,7 +325,8 @@
<bean id="AclGroupRoleMapDaoImpl" class="org.apache.cloudstack.acl.dao.AclGroupRoleMapDaoImpl"/>
<bean id="AclApiPermissionDaoImpl" class="org.apache.cloudstack.acl.dao.AclApiPermissionDaoImpl"/>
<bean id="AclEntityPermissionDaoImpl" class="org.apache.cloudstack.acl.dao.AclEntityPermissionDaoImpl"/>
<bean id="AclRolePermissionDaoImpl" class="org.apache.cloudstack.acl.dao.AclRolePermissionDaoImpl"/>
<bean id="AclRolePermissionDaoImpl" class="org.apache.cloudstack.acl.dao.AclRolePermissionDaoImpl"/>
<bean id="AclPolicyPermissionDaoImpl" class="org.apache.cloudstack.acl.dao.AclPolicyPermissionDaoImpl"/>
<bean id="databaseIntegrityChecker" class="com.cloud.upgrade.DatabaseIntegrityChecker" />

View File

@ -32,14 +32,17 @@ import org.apache.cloudstack.acl.SecurityChecker.AccessType;
import com.cloud.utils.db.GenericDao;
@Entity
@Table(name = ("acl_permission"))
public class AclPermissionVO implements AclPermission {
@Table(name = ("acl_policy_permission"))
public class AclPolicyPermissionVO implements AclPolicyPermission {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private long id;
@Column(name = "policy_id")
private long aclPolicyId;
@Column(name = "action")
private String action;
@ -67,12 +70,14 @@ public class AclPermissionVO implements AclPermission {
@Column(name = GenericDao.CREATED_COLUMN)
private Date created;
public AclPermissionVO() {
public AclPolicyPermissionVO() {
}
public AclPermissionVO(String action, String entityType, AccessType accessType, PermissionScope scope,
public AclPolicyPermissionVO(long aclPolicyId, String action, String entityType, AccessType accessType,
PermissionScope scope,
Long scopeId, Permission permission) {
this.aclPolicyId = aclPolicyId;
this.action = action;
this.entityType = entityType;
this.accessType = accessType;
@ -86,6 +91,11 @@ public class AclPermissionVO implements AclPermission {
return id;
}
@Override
public long getAclPolicyId() {
return aclPolicyId;
}
@Override
public String getEntityType() {

View File

@ -16,9 +16,11 @@
// under the License.
package org.apache.cloudstack.acl.dao;
import org.apache.cloudstack.acl.AclPermissionVO;
import org.apache.cloudstack.acl.AclPolicyPermissionVO;
import com.cloud.utils.db.GenericDao;
public interface AclPermissionDao extends GenericDao<AclPermissionVO, Long> {
public interface AclPolicyPermissionDao extends GenericDao<AclPolicyPermissionVO, Long> {
}

View File

@ -20,12 +20,14 @@ import java.util.Map;
import javax.naming.ConfigurationException;
import org.apache.cloudstack.acl.AclPermissionVO;
import org.apache.cloudstack.acl.AclPolicyPermissionVO;
import com.cloud.utils.db.GenericDaoBase;
public class AclPermissionDaoImpl extends GenericDaoBase<AclPermissionVO, Long> implements AclPermissionDao {
public class AclPolicyPermissionDaoImpl extends GenericDaoBase<AclPolicyPermissionVO, Long> implements
AclPolicyPermissionDao {
public AclPermissionDaoImpl()
public AclPolicyPermissionDaoImpl()
{
}

View File

@ -52,14 +52,13 @@ import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.cloudstack.acl.APIChecker;
import org.apache.cloudstack.acl.AclPermissionVO;
import org.apache.cloudstack.acl.AclPolicyPermissionMapVO;
import org.apache.cloudstack.acl.AclPolicyPermissionVO;
import org.apache.cloudstack.acl.PermissionScope;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.acl.AclPermission.Permission;
import org.apache.cloudstack.acl.AclPolicyPermission.Permission;
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
import org.apache.cloudstack.acl.dao.AclPermissionDao;
import org.apache.cloudstack.acl.dao.AclPolicyPermissionMapDao;
import org.apache.cloudstack.acl.dao.AclPolicyPermissionDao;
import org.apache.cloudstack.affinity.AffinityGroupVMMapVO;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseAsyncCmd;
@ -149,12 +148,14 @@ import com.cloud.user.UserAccount;
import com.cloud.user.UserVO;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.Pair;
import com.cloud.utils.PropertiesUtil;
import com.cloud.utils.StringUtils;
import com.cloud.utils.component.ComponentContext;
import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.component.PluggableService;
import com.cloud.utils.concurrency.NamedThreadFactory;
import com.cloud.utils.db.EntityManager;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.db.TransactionLegacy;
import com.cloud.utils.exception.CloudRuntimeException;
@ -180,9 +181,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
List<PluggableService> _pluggableServices;
List<APIChecker> _apiAccessCheckers;
@Inject
private AclPermissionDao _aclPermissionDao;
@Inject
private AclPolicyPermissionMapDao _aclPolicyPermissionMapDao;
private AclPolicyPermissionDao _aclPermissionDao;
@Inject
protected ApiAsyncJobDispatcher _asyncDispatcher;
@ -190,6 +189,10 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
private static final DateFormat _dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
private static Map<String, List<Class<?>>> _apiNameCmdClassMap = new HashMap<String, List<Class<?>>>();
private static Set<String> commandsPropertiesOverrides = new HashSet<String>();
private static Map<RoleType, Set<String>> commandsPropertiesRoleBasedApisMap = new HashMap<RoleType, Set<String>>();
private static ExecutorService _executor = new ThreadPoolExecutor(10, 150, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new NamedThreadFactory("ApiServer"));
public ApiServer() {
@ -197,6 +200,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
@Override
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
processMapping(PropertiesUtil.processConfigFile(new String[] { "commands.properties" }));
return true;
}
@ -233,6 +237,40 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
}
}
// drop all default policy api permissions - we reload them every time
// to include any chanegs done to the @APICommand or
// commands.properties.
SearchBuilder<AclPolicyPermissionVO> sb = _aclPermissionDao.createSearchBuilder();
sb.and("policyId", sb.entity().getAclPolicyId(), SearchCriteria.Op.EQ);
sb.and("resourceType", sb.entity().getEntityType(), SearchCriteria.Op.NULL);
sb.and("scope", sb.entity().getScope(), SearchCriteria.Op.EQ);
sb.done();
SearchCriteria<AclPolicyPermissionVO> permissionSC = sb.create();
for (RoleType role : RoleType.values()) {
permissionSC.setParameters("policyId", role.ordinal() + 1);
switch (role) {
case User:
permissionSC.setParameters("scope", PermissionScope.ACCOUNT.toString());
break;
case Admin:
permissionSC.setParameters("scope", PermissionScope.ALL.toString());
break;
case DomainAdmin:
permissionSC.setParameters("scope", PermissionScope.DOMAIN.toString());
break;
case ResourceAdmin:
permissionSC.setParameters("scope", PermissionScope.DOMAIN.toString());
break;
}
_aclPermissionDao.expunge(permissionSC);
}
for(Class<?> cmdClass: cmdClasses) {
APICommand at = cmdClass.getAnnotation(APICommand.class);
if (at == null) {
@ -246,52 +284,27 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
}
apiCmdList.add(cmdClass);
boolean isReadCommand = false;
BaseCmd cmdObj;
try {
cmdObj = (BaseCmd) cmdClass.newInstance();
if (cmdObj instanceof BaseListCmd) {
isReadCommand = true;
}
} catch (Exception e) {
}
for (RoleType role : at.authorized()) {
AclPermissionVO apiPermission = null;
switch (role) {
case User:
apiPermission = new AclPermissionVO(apiName, null, null, PermissionScope.ACCOUNT, null,
Permission.Allow);
break;
case Admin:
apiPermission = new AclPermissionVO(apiName, null, null, PermissionScope.ALL, null,
Permission.Allow);
break;
case DomainAdmin:
apiPermission = new AclPermissionVO(apiName, null, null, PermissionScope.DOMAIN, null,
Permission.Allow);
break;
case ResourceAdmin:
apiPermission = new AclPermissionVO(apiName, null, null, PermissionScope.DOMAIN, null,
Permission.Allow);
break;
}
if (apiPermission != null) {
if (isReadCommand) {
apiPermission.setAccessType(AccessType.ListEntry);
}
_aclPermissionDao.persist(apiPermission);
AclPolicyPermissionMapVO policyPermMapEntry = new AclPolicyPermissionMapVO(role.ordinal() + 1,
apiPermission.getId());
_aclPolicyPermissionMapDao.persist(policyPermMapEntry);
if (!commandsPropertiesOverrides.contains(apiName)) {
for (RoleType role : at.authorized()) {
addDefaultAclPolicyPermission(apiName, cmdClass, role);
}
}
}
// read commands.properties and load api acl permissions -
// commands.properties overrides any @APICommand authorization
for (String apiName : commandsPropertiesOverrides) {
Class<?> cmdClass = getCmdClass(apiName);
for (RoleType role : RoleType.values()) {
if (commandsPropertiesRoleBasedApisMap.get(role).contains(apiName)) {
// insert permission for this role for this api
addDefaultAclPolicyPermission(apiName, cmdClass, role);
}
}
}
encodeApiResponse = Boolean.valueOf(_configDao.getValue(Config.EncodeApiResponse.key()));
String jsonType = _configDao.getValue(Config.JavaScriptDefaultContentType.key());
if (jsonType != null) {
@ -306,6 +319,74 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
return true;
}
private void processMapping(Map<String, String> configMap) {
for (RoleType roleType : RoleType.values()) {
commandsPropertiesRoleBasedApisMap.put(roleType, new HashSet<String>());
}
for (Map.Entry<String, String> entry : configMap.entrySet()) {
String apiName = entry.getKey();
String roleMask = entry.getValue();
commandsPropertiesOverrides.add(apiName);
try {
short cmdPermissions = Short.parseShort(roleMask);
for (RoleType roleType : RoleType.values()) {
if ((cmdPermissions & roleType.getValue()) != 0)
commandsPropertiesRoleBasedApisMap.get(roleType).add(apiName);
}
} catch (NumberFormatException nfe) {
s_logger.info("Malformed key=value pair for entry: " + entry.toString());
}
}
}
private void addDefaultAclPolicyPermission(String apiName, Class<?> cmdClass, RoleType role) {
boolean isReadCommand = false;
if (cmdClass != null) {
BaseCmd cmdObj;
try {
cmdObj = (BaseCmd) cmdClass.newInstance();
if (cmdObj instanceof BaseListCmd) {
isReadCommand = true;
}
} catch (Exception e) {
throw new CloudRuntimeException(String.format(
"%s is claimed as an API command, but it cannot be instantiated", cmdClass.getName()));
}
}
AclPolicyPermissionVO apiPermission = null;
switch (role) {
case User:
apiPermission = new AclPolicyPermissionVO(role.ordinal() + 1, apiName, null, null, PermissionScope.ACCOUNT,
null, Permission.Allow);
break;
case Admin:
apiPermission = new AclPolicyPermissionVO(role.ordinal() + 1, apiName, null, null, PermissionScope.ALL,
null, Permission.Allow);
break;
case DomainAdmin:
apiPermission = new AclPolicyPermissionVO(role.ordinal() + 1, apiName, null, null, PermissionScope.DOMAIN,
null, Permission.Allow);
break;
case ResourceAdmin:
apiPermission = new AclPolicyPermissionVO(role.ordinal() + 1, apiName, null, null, PermissionScope.DOMAIN,
null, Permission.Allow);
break;
}
if (apiPermission != null) {
if (isReadCommand) {
apiPermission.setAccessType(AccessType.ListEntry);
}
_aclPermissionDao.persist(apiPermission);
}
}
// NOTE: handle() only handles over the wire (OTW) requests from integration.api.port 8096
// If integration api port is not configured, actual OTW requests will be received by ApiServlet
@SuppressWarnings({ "unchecked", "rawtypes" })
@ -945,7 +1026,8 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
else {
// determine the cmd class based on calling context
ResponseView view = ResponseView.Restricted;
if (_accountMgr.isRootAdmin(CallContext.current().getCallingAccount().getId())) {
if (CallContext.current() != null
&& _accountMgr.isRootAdmin(CallContext.current().getCallingAccount().getId())) {
view = ResponseView.Full;
}
for (Class<?> cmdClass : cmdList) {

View File

@ -28,7 +28,7 @@ import javax.persistence.Id;
import javax.persistence.Table;
import org.apache.cloudstack.acl.AclEntityType;
import org.apache.cloudstack.acl.AclPermission;
import org.apache.cloudstack.acl.AclPolicyPermission;
import org.apache.cloudstack.acl.PermissionScope;
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
@ -95,7 +95,7 @@ public class AclPolicyJoinVO extends BaseViewVO implements ControlledViewEntity
@Column(name = "permission_allow_deny")
@Enumerated(value = EnumType.STRING)
private AclPermission.Permission permissionAllowDeny;
private AclPolicyPermission.Permission permissionAllowDeny;
@Column(name = GenericDao.REMOVED_COLUMN)
private Date removed;
@ -206,7 +206,7 @@ public class AclPolicyJoinVO extends BaseViewVO implements ControlledViewEntity
return permissionAccessType;
}
public AclPermission.Permission getPermissionAllowDeny() {
public AclPolicyPermission.Permission getPermissionAllowDeny() {
return permissionAllowDeny;
}

View File

@ -355,32 +355,23 @@ CREATE TABLE `acl_group_policy_map` (
CONSTRAINT `fk_acl_group_policy_map__policy_id` FOREIGN KEY (`policy_id`) REFERENCES `acl_policy` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `acl_permission` (
CREATE TABLE `acl_policy_permission` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`policy_id` bigint(20) unsigned NOT NULL,
`action` varchar(100) NOT NULL,
`resource_type` varchar(100) NOT NULL,
`scope_id` bigint(20) unsigned NOT NULL,
`resource_type` varchar(100) DEFAULT NULL,
`scope_id` bigint(20) unsigned,
`scope` varchar(40) DEFAULT NULL,
`access_type` varchar(40) NOT NULL,
`access_type` varchar(40) DEFAULT NULL,
`permission` varchar(40) NOT NULL COMMENT 'Allow or Deny',
`removed` datetime DEFAULT NULL COMMENT 'date the permission was revoked',
`created` datetime DEFAULT NULL COMMENT 'date the permission was granted',
PRIMARY KEY (`id`),
UNIQUE KEY `id` (`id`)
UNIQUE KEY `id` (`id`),
KEY `fk_acl_policy_permission__policy_id` (`policy_id`),
CONSTRAINT `fk_acl_policy_permission__policy_id` FOREIGN KEY (`policy_id`) REFERENCES `acl_policy` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
CREATE TABLE `acl_policy_permission_map` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`policy_id` bigint(20) unsigned NOT NULL,
`permission_id` bigint(20) unsigned NOT NULL,
`removed` datetime DEFAULT NULL COMMENT 'date the permission was removed from the policy',
`created` datetime DEFAULT NULL COMMENT 'date the permission was added to the policy',
PRIMARY KEY (`id`),
KEY `fk_acl_policy_permission_map__policy_id` (`policy_id`),
KEY `fk_acl_policy_permission_map__permission_id` (`permission_id`),
CONSTRAINT `fk_acl_policy_permission_map__policy_id` FOREIGN KEY (`policy_id`) REFERENCES `acl_policy` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_acl_policy_permission_map__permission_id` FOREIGN KEY (`permission_id`) REFERENCES `acl_permission` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
INSERT IGNORE INTO `cloud`.`acl_policy` (id, name, description, uuid, domain_id, account_id, created, policy_type) VALUES (1, 'NORMAL', 'Domain user role', UUID(), 1, 1, Now(), 'Static');
INSERT IGNORE INTO `cloud`.`acl_policy` (id, name, description, uuid, domain_id, account_id, created, policy_type) VALUES (2, 'ADMIN', 'Root admin role', UUID(), 1, 1, Now(), 'Static');
@ -412,12 +403,12 @@ CREATE OR REPLACE VIEW `cloud`.`acl_policy_view` AS
account.uuid account_uuid,
account.account_name account_name,
account.type account_type,
acl_permission.action permission_action,
acl_permission.resource_type permission_entity_type,
acl_permission.scope permission_scope,
acl_permission.scope_id permission_scope_id,
acl_permission.access_type permission_access_type,
acl_permission.permission permission_allow_deny
acl_policy_permission.action permission_action,
acl_policy_permission.resource_type permission_entity_type,
acl_policy_permission.scope permission_scope,
acl_policy_permission.scope_id permission_scope_id,
acl_policy_permission.access_type permission_access_type,
acl_policy_permission.permission permission_allow_deny
from
`cloud`.`acl_policy`
inner join
@ -425,9 +416,7 @@ CREATE OR REPLACE VIEW `cloud`.`acl_policy_view` AS
inner join
`cloud`.`account` ON acl_policy.account_id = account.id
left join
`cloud`.`acl_policy_permission_map` ON acl_policy.id = acl_policy_permission_map.policy_id
left join
`cloud`.`acl_permission` ON acl_permission.id = acl_policy_permission_map.permission_id;
`cloud`.`acl_policy_permission` ON acl_policy.id = acl_policy_permission.policy_id;
CREATE OR REPLACE VIEW `cloud`.`acl_group_view` AS
@ -463,8 +452,6 @@ CREATE OR REPLACE VIEW `cloud`.`acl_group_view` AS
left join
`cloud`.`acl_policy` ON acl_group_policy_map.policy_id = acl_policy.id
left join
`cloud`.`acl_policy_permission_map` ON acl_group.id = acl_policy_permission_map.policy_id
left join
`cloud`.`acl_group_account_map` ON acl_group.id = acl_group_account_map.group_id
left join
`cloud`.`account` member_account ON acl_group_account_map.account_id = member_account.id;