Moved the loading of commands.properties to the IAM plugin

This commit is contained in:
Prachi Damle 2013-12-27 23:01:10 -08:00
parent dd8dcd9492
commit e5b4a1d869
7 changed files with 207 additions and 170 deletions

View File

@ -190,10 +190,6 @@ 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() {
@ -201,7 +197,6 @@ 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;
}
@ -238,39 +233,6 @@ 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("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) {
@ -283,28 +245,8 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
_apiNameCmdClassMap.put(apiName, apiCmdList);
}
apiCmdList.add(cmdClass);
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) {
@ -319,92 +261,6 @@ 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;
AclEntityType[] entityTypes = null;
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()));
}
APICommand at = cmdClass.getAnnotation(APICommand.class);
entityTypes = at.entityType();
}
AclPolicyPermissionVO apiPermission = null;
PermissionScope permissionScope = PermissionScope.ACCOUNT;
switch (role) {
case User:
permissionScope = PermissionScope.ACCOUNT;
break;
case Admin:
permissionScope = PermissionScope.ALL;
break;
case DomainAdmin:
permissionScope = PermissionScope.DOMAIN;
break;
case ResourceAdmin:
permissionScope = PermissionScope.DOMAIN;
break;
}
if (entityTypes == null || entityTypes.length == 0) {
apiPermission = new AclPolicyPermissionVO(role.ordinal() + 1, apiName, null, null, permissionScope,
new Long(-1), Permission.Allow);
if (apiPermission != null) {
if (isReadCommand) {
apiPermission.setAccessType(AccessType.ListEntry);
}
_aclPermissionDao.persist(apiPermission);
}
} else {
for (AclEntityType entityType : entityTypes) {
apiPermission = new AclPolicyPermissionVO(role.ordinal() + 1, apiName, entityType.toString(), null,
permissionScope, new Long(-1), Permission.Allow);
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" })

View File

@ -16,52 +16,205 @@
// under the License.
package org.apache.cloudstack.acl;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.ejb.Local;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import org.apache.log4j.Logger;
import org.apache.cloudstack.acl.api.AclApiService;
import org.apache.cloudstack.acl.APIChecker;
import org.apache.cloudstack.acl.AclEntityType;
import org.apache.cloudstack.acl.PermissionScope;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.BaseListCmd;
import org.apache.cloudstack.iam.api.AclPolicy;
import org.apache.cloudstack.iam.api.AclPolicyPermission.Permission;
import org.apache.cloudstack.iam.api.IAMService;
import com.cloud.api.ApiServerService;
import com.cloud.exception.PermissionDeniedException;
import com.cloud.user.Account;
import com.cloud.user.AccountService;
import com.cloud.user.User;
import com.cloud.utils.PropertiesUtil;
import com.cloud.utils.component.AdapterBase;
import com.cloud.utils.component.PluggableService;
import com.cloud.utils.exception.CloudRuntimeException;
// This is the Role Based API access checker that grab's the account's roles
// based on the set of roles, access is granted if any of the role has access to the api
//This is the Role Based API access checker that grab's the account's roles
//based on the set of roles, access is granted if any of the role has access to the api
@Local(value=APIChecker.class)
public class RoleBasedAPIAccessChecker extends AdapterBase implements APIChecker {
protected static final Logger s_logger = Logger.getLogger(RoleBasedAPIAccessChecker.class);
@Inject AccountService _accountService;
@Inject AclApiService _aclService;
@Inject
AccountService _accountService;
@Inject
ApiServerService _apiServer;
@Inject
IAMService _iamSrv;
Set<String> commandsPropertiesOverrides = new HashSet<String>();
Map<RoleType, Set<String>> commandsPropertiesRoleBasedApisMap = new HashMap<RoleType, Set<String>>();
List<PluggableService> _services;
protected RoleBasedAPIAccessChecker() {
super();
}
for (RoleType roleType : RoleType.values()) {
commandsPropertiesRoleBasedApisMap.put(roleType, new HashSet<String>());
}
}
@Override
public boolean checkAccess(User user, String commandName)
throws PermissionDeniedException {
public boolean checkAccess(User user, String commandName) throws PermissionDeniedException {
Account account = _accountService.getAccount(user.getAccountId());
if (account == null) {
throw new PermissionDeniedException("The account id=" + user.getAccountId() + "for user id=" + user.getId() + "is null");
throw new PermissionDeniedException("The account id=" + user.getAccountId() + "for user id=" + user.getId()
+ "is null");
}
List<AclPolicy> policies = _aclService.listAclPolicies(account.getAccountId());
List<AclPolicy> policies = _iamSrv.listAclPolicies(account.getAccountId());
boolean isAllowed = _aclService.isAPIAccessibleForPolicies(commandName, policies);
boolean isAllowed = _iamSrv.isAPIAccessibleForPolicies(commandName, policies);
if (!isAllowed) {
throw new PermissionDeniedException("The API does not exist or is blacklisted. api: " + commandName);
}
return isAllowed;
}
}
@Override
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
super.configure(name, params);
processMapping(PropertiesUtil.processConfigFile(new String[] { "commands.properties" }));
return true;
}
@Override
public boolean start() {
// drop all default policy api permissions - we reload them every time
// to include any changes done to the @APICommand or
// commands.properties.
for (RoleType role : RoleType.values()) {
_iamSrv.resetAclPolicy(role.ordinal() + 1);
}
for (PluggableService service : _services) {
for (Class<?> cmdClass : service.getCommands()) {
APICommand command = cmdClass.getAnnotation(APICommand.class);
if (!commandsPropertiesOverrides.contains(command.name())) {
for (RoleType role : command.authorized()) {
addDefaultAclPolicyPermission(command.name(), cmdClass, role);
}
}
}
}
// read commands.properties and load api acl permissions -
// commands.properties overrides any @APICommand authorization
for (String apiName : commandsPropertiesOverrides) {
Class<?> cmdClass = _apiServer.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);
}
}
}
return super.start();
}
private void processMapping(Map<String, String> configMap) {
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());
}
}
}
public List<PluggableService> getServices() {
return _services;
}
@Inject
public void setServices(List<PluggableService> _services) {
this._services = _services;
}
private void addDefaultAclPolicyPermission(String apiName, Class<?> cmdClass, RoleType role) {
AccessType accessType = null;
AclEntityType[] entityTypes = null;
if (cmdClass != null) {
BaseCmd cmdObj;
try {
cmdObj = (BaseCmd) cmdClass.newInstance();
if (cmdObj instanceof BaseListCmd) {
accessType = AccessType.ListEntry;
}
} catch (Exception e) {
throw new CloudRuntimeException(String.format(
"%s is claimed as an API command, but it cannot be instantiated", cmdClass.getName()));
}
APICommand at = cmdClass.getAnnotation(APICommand.class);
entityTypes = at.entityType();
}
PermissionScope permissionScope = PermissionScope.ACCOUNT;
switch (role) {
case User:
permissionScope = PermissionScope.ACCOUNT;
break;
case Admin:
permissionScope = PermissionScope.ALL;
break;
case DomainAdmin:
permissionScope = PermissionScope.DOMAIN;
break;
case ResourceAdmin:
permissionScope = PermissionScope.DOMAIN;
break;
}
if (entityTypes == null || entityTypes.length == 0) {
_iamSrv.addAclPermissionToAclPolicy(new Long(role.ordinal()) + 1, null, permissionScope.toString(), new Long(-1),
apiName, accessType.toString(), Permission.Allow);
} else {
for (AclEntityType entityType : entityTypes) {
_iamSrv.addAclPermissionToAclPolicy(new Long(role.ordinal()) + 1, entityType.toString(), permissionScope.toString(), new Long(-1),
apiName, accessType.toString(), Permission.Allow);
}
}
}
}

View File

@ -34,4 +34,24 @@
<module>plugin</module>
<module>server</module>
</modules>
<dependencies>
<dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-utils</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-api</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -27,18 +27,6 @@
<relativePath>../pom.xml</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-utils</artifactId>

View File

@ -72,5 +72,6 @@ public interface IAMService {
List<Long> getGrantedEntities(long accountId, String action, String scope);
AclPolicy resetAclPolicy(long aclPolicyId);
}

View File

@ -579,6 +579,26 @@ public class IAMServiceImpl extends ManagerBase implements IAMService, Manager {
}
@DB
@Override
public AclPolicy resetAclPolicy(long aclPolicyId) {
// get the Acl Policy entity
AclPolicy policy = _aclPolicyDao.findById(aclPolicyId);
if (policy == null) {
throw new InvalidParameterValueException("Unable to find acl policy: " + aclPolicyId
+ "; failed to reset the policy.");
}
SearchBuilder<AclPolicyPermissionVO> sb = _policyPermissionDao.createSearchBuilder();
sb.and("policyId", sb.entity().getAclPolicyId(), SearchCriteria.Op.EQ);
sb.and("scope", sb.entity().getScope(), SearchCriteria.Op.EQ);
sb.done();
SearchCriteria<AclPolicyPermissionVO> permissionSC = sb.create();
permissionSC.setParameters("policyId", aclPolicyId);
_policyPermissionDao.expunge(permissionSC);
return policy;
}
@Override
public boolean isAPIAccessibleForPolicies(String apiName, List<AclPolicy> policies) {

View File

@ -17,7 +17,6 @@
package org.apache.cloudstack.iam;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.when;