Skip to content

Commit a2306f4

Browse files
author
Prachi Damle
committed
some more poc work
1 parent 0738632 commit a2306f4

12 files changed

Lines changed: 349 additions & 205 deletions

File tree

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.cloud.acl;
2+
3+
import com.cloud.exception.PermissionDeniedException;
4+
import com.cloud.user.Account;
5+
import com.cloud.user.User;
6+
import com.cloud.utils.component.Adapter;
7+
8+
/**
9+
* APIAccessChecker checks the ownership and access control to API requests
10+
*/
11+
public interface APIAccessChecker extends Adapter {
12+
13+
boolean canAccessAPI(User user, String apiCommandName) throws PermissionDeniedException;
14+
}

api/src/com/cloud/acl/Role.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.cloud.acl;
2+
3+
//metadata - consists of default dynamic roles in CS + any custom roles added by user
4+
public interface Role {
5+
6+
public static final short ROOT_ADMIN = 0;
7+
public static final short DOMAIN_ADMIN = 1;
8+
public static final short DOMAIN_USER = 2;
9+
public static final short OWNER = 3;
10+
public static final short PARENT_DOMAIN_ADMIN = 4;
11+
public static final short PARENT_DOMAIN_USER = 5;
12+
public static final short CHILD_DOMAIN_ADMIN = 6;
13+
public static final short CHILD_DOMAIN_USER = 7;
14+
15+
public long getId();
16+
17+
public short getRoleType();
18+
19+
20+
}

api/src/com/cloud/api/ACL.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,6 @@
2626
@Target({ FIELD })
2727
public @interface ACL {
2828

29-
30-
Class<?> resourceType();
29+
boolean checkKeyAccess() default false;
30+
boolean checkValueAccess() default false;
3131
}

api/src/com/cloud/api/Parameter.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,8 @@
4444
int length() default 255;
4545

4646
String since() default "";
47+
48+
Class<?>[] resourceType() default Object.class;
49+
50+
String retrieveMethod() default "getById";
4751
}

api/src/com/cloud/api/commands/DeployVMCmd.java

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import com.cloud.exception.ResourceUnavailableException;
4747
import com.cloud.host.Host;
4848
import com.cloud.hypervisor.Hypervisor.HypervisorType;
49+
import com.cloud.network.IpAddress;
4950
import com.cloud.network.Network;
5051
import com.cloud.network.security.SecurityGroup;
5152
import com.cloud.offering.DiskOffering;
@@ -55,6 +56,7 @@
5556
import com.cloud.user.UserContext;
5657
import com.cloud.uservm.UserVm;
5758

59+
5860
@Implementation(description="Creates and automatically starts a virtual machine based on a service offering, disk offering, and template.", responseObject=UserVmResponse.class)
5961
public class DeployVMCmd extends BaseAsyncCreateCmd {
6062
public static final Logger s_logger = Logger.getLogger(DeployVMCmd.class.getName());
@@ -69,13 +71,14 @@ public class DeployVMCmd extends BaseAsyncCreateCmd {
6971
@Parameter(name=ApiConstants.ZONE_ID, type=CommandType.LONG, required=true, description="availability zone for the virtual machine")
7072
private Long zoneId;
7173

74+
@ACL
7275
@IdentityMapper(entityTableName="disk_offering")
73-
@Parameter(name=ApiConstants.SERVICE_OFFERING_ID, type=CommandType.LONG, required=true, description="the ID of the service offering for the virtual machine")
76+
@Parameter(name=ApiConstants.SERVICE_OFFERING_ID, type=CommandType.LONG, required=true, description="the ID of the service offering for the virtual machine", resourceType=ServiceOffering.class)
7477
private Long serviceOfferingId;
7578

76-
@ACL(resourceType=VirtualMachineTemplate.class)
79+
@ACL
7780
@IdentityMapper(entityTableName="vm_template")
78-
@Parameter(name=ApiConstants.TEMPLATE_ID, type=CommandType.LONG, required=true, description="the ID of the template for the virtual machine")
81+
@Parameter(name=ApiConstants.TEMPLATE_ID, type=CommandType.LONG, required=true, description="the ID of the template for the virtual machine",resourceType=VirtualMachineTemplate.class)
7982
private Long templateId;
8083

8184
@Parameter(name=ApiConstants.NAME, type=CommandType.STRING, description="host name for the virtual machine")
@@ -89,18 +92,19 @@ public class DeployVMCmd extends BaseAsyncCreateCmd {
8992
private String accountName;
9093

9194
@IdentityMapper(entityTableName="domain")
92-
@Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.LONG, description="an optional domainId for the virtual machine. If the account parameter is used, domainId must also be used.")
95+
@Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.LONG, description="an optional domainId for the virtual machine. If the account parameter is used, domainId must also be used.", entityType=Domain.class)
9396
private Long domainId;
9497

9598
//Network information
96-
@ACL(resourceType=Network.class)
99+
@ACL
97100
@IdentityMapper(entityTableName="networks")
98-
@Parameter(name=ApiConstants.NETWORK_IDS, type=CommandType.LIST, collectionType=CommandType.LONG, description="list of network ids used by virtual machine. Can't be specified with ipToNetworkList parameter")
101+
@Parameter(name=ApiConstants.NETWORK_IDS, type=CommandType.LIST, collectionType=CommandType.LONG, description="list of network ids used by virtual machine. Can't be specified with ipToNetworkList parameter", resourceType=Network.class)
99102
private List<Long> networkIds;
100103

101104
//DataDisk information
105+
@ACL
102106
@IdentityMapper(entityTableName="disk_offering")
103-
@Parameter(name=ApiConstants.DISK_OFFERING_ID, type=CommandType.LONG, description="the ID of the disk offering for the virtual machine. If the template is of ISO format, the diskOfferingId is for the root disk volume. Otherwise this parameter is used to indicate the offering for the data disk volume. If the templateId parameter passed is from a Template object, the diskOfferingId refers to a DATA Disk Volume created. If the templateId parameter passed is from an ISO object, the diskOfferingId refers to a ROOT Disk Volume created.")
107+
@Parameter(name=ApiConstants.DISK_OFFERING_ID, type=CommandType.LONG, description="the ID of the disk offering for the virtual machine. If the template is of ISO format, the diskOfferingId is for the root disk volume. Otherwise this parameter is used to indicate the offering for the data disk volume. If the templateId parameter passed is from a Template object, the diskOfferingId refers to a DATA Disk Volume created. If the templateId parameter passed is from an ISO object, the diskOfferingId refers to a ROOT Disk Volume created.", resourceType=DiskOffering.class)
104108
private Long diskOfferingId;
105109

106110
@Parameter(name=ApiConstants.SIZE, type=CommandType.LONG, description="the arbitrary size for the DATADISK volume. Mutually exclusive with diskOfferingId")
@@ -118,21 +122,22 @@ public class DeployVMCmd extends BaseAsyncCreateCmd {
118122
@Parameter(name=ApiConstants.SSH_KEYPAIR, type=CommandType.STRING, description="name of the ssh key pair used to login to the virtual machine")
119123
private String sshKeyPairName;
120124

121-
//@ACL(resourceType=Host.class)
125+
122126
@IdentityMapper(entityTableName="host")
123127
@Parameter(name=ApiConstants.HOST_ID, type=CommandType.LONG, description="destination Host ID to deploy the VM to - parameter available for root admin only")
124128
private Long hostId;
125129

126-
//@ACL(resourceType=SecurityGroup.class)
130+
@ACL
127131
@IdentityMapper(entityTableName="security_group")
128-
@Parameter(name=ApiConstants.SECURITY_GROUP_IDS, type=CommandType.LIST, collectionType=CommandType.LONG, description="comma separated list of security groups id that going to be applied to the virtual machine. Should be passed only when vm is created from a zone with Basic Network support. Mutually exclusive with securitygroupnames parameter")
132+
@Parameter(name=ApiConstants.SECURITY_GROUP_IDS, type=CommandType.LIST, collectionType=CommandType.LONG, description="comma separated list of security groups id that going to be applied to the virtual machine. Should be passed only when vm is created from a zone with Basic Network support. Mutually exclusive with securitygroupnames parameter", resourceType=SecurityGroup.class)
129133
private List<Long> securityGroupIdList;
130134

131-
//@ACL(resourceType=SecurityGroup.class)
132-
@Parameter(name=ApiConstants.SECURITY_GROUP_NAMES, type=CommandType.LIST, collectionType=CommandType.STRING, description="comma separated list of security groups names that going to be applied to the virtual machine. Should be passed only when vm is created from a zone with Basic Network support. Mutually exclusive with securitygroupids parameter")
135+
@ACL
136+
@Parameter(name=ApiConstants.SECURITY_GROUP_NAMES, type=CommandType.LIST, collectionType=CommandType.STRING, description="comma separated list of security groups names that going to be applied to the virtual machine. Should be passed only when vm is created from a zone with Basic Network support. Mutually exclusive with securitygroupids parameter", resourceType=SecurityGroup.class)
133137
private List<String> securityGroupNameList;
134138

135-
@Parameter(name = ApiConstants.IP_NETWORK_LIST, type = CommandType.MAP, description = "ip to network mapping. Can't be specified with networkIds parameter. Example: iptonetworklist[0].ip=10.10.10.11&iptonetworklist[0].networkid=204 - requests to use ip 10.10.10.11 in network id=204")
139+
@ACL(checkKeyAccess=true)
140+
@Parameter(name = ApiConstants.IP_NETWORK_LIST, type = CommandType.MAP, description = "ip to network mapping. Can't be specified with networkIds parameter. Example: iptonetworklist[0].ip=10.10.10.11&iptonetworklist[0].networkid=204 - requests to use ip 10.10.10.11 in network id=204",resourceType={Network.class,IpAddress.class})
136141
private Map ipToNetworkList;
137142

138143
@Parameter(name=ApiConstants.IP_ADDRESS, type=CommandType.STRING, description="the ip address for default vm's network")

api/src/com/cloud/domain/Domain.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
/**
2424
* Domain defines the Domain object.
2525
*/
26+
2627
public interface Domain extends OwnedBy {
2728
public static final long ROOT_DOMAIN = 1L;
2829

api/src/com/cloud/network/security/SecurityGroup.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import com.cloud.acl.ControlledEntity;
2020

21+
@doc("")
2122
public interface SecurityGroup extends ControlledEntity {
2223
long getId();
2324

api/src/com/cloud/user/Account.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public enum State {
3434
enabled,
3535
locked
3636
}
37+
3738

3839
public static final short ACCOUNT_TYPE_NORMAL = 0;
3940
public static final short ACCOUNT_TYPE_ADMIN = 1;
@@ -61,4 +62,5 @@ public enum State {
6162
public String getNetworkDomain();
6263

6364
public Long getDefaultZoneId();
65+
6466
}
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
package com.cloud.acl;
2+
3+
import java.io.File;
4+
import java.io.FileInputStream;
5+
import java.io.FileNotFoundException;
6+
import java.io.IOException;
7+
import java.io.InputStream;
8+
import java.util.ArrayList;
9+
import java.util.List;
10+
import java.util.Map;
11+
import java.util.Properties;
12+
13+
import javax.ejb.Local;
14+
import javax.naming.ConfigurationException;
15+
16+
import org.apache.log4j.Logger;
17+
18+
import com.cloud.exception.PermissionDeniedException;
19+
import com.cloud.server.ManagementServer;
20+
import com.cloud.user.Account;
21+
import com.cloud.user.AccountManager;
22+
import com.cloud.user.User;
23+
import com.cloud.utils.PropertiesUtil;
24+
import com.cloud.utils.component.AdapterBase;
25+
import com.cloud.utils.component.ComponentLocator;
26+
import com.cloud.utils.component.Inject;
27+
import com.cloud.utils.component.PluggableService;
28+
29+
/*
30+
* This is the default API access checker that grab's the user's account
31+
* based on the account type, access is granted referring to commands in all *.properties files.
32+
*/
33+
34+
@Local(value=APIAccessChecker.class)
35+
public class StaticRoleBasedAPIAccessChecker extends AdapterBase implements APIAccessChecker {
36+
37+
protected static final Logger s_logger = Logger.getLogger(StaticRoleBasedAPIAccessChecker.class);
38+
public static final short ADMIN_COMMAND = 1;
39+
public static final short DOMAIN_ADMIN_COMMAND = 4;
40+
public static final short RESOURCE_DOMAIN_ADMIN_COMMAND = 2;
41+
public static final short USER_COMMAND = 8;
42+
private static List<String> s_userCommands = null;
43+
private static List<String> s_resellerCommands = null; // AKA domain-admin
44+
private static List<String> s_adminCommands = null;
45+
private static List<String> s_resourceDomainAdminCommands = null;
46+
private static List<String> s_allCommands = null;
47+
private static List<String> s_pluggableServiceCommands = null;
48+
49+
protected @Inject AccountManager _accountMgr;
50+
51+
static {
52+
s_allCommands = new ArrayList<String>();
53+
s_userCommands = new ArrayList<String>();
54+
s_resellerCommands = new ArrayList<String>();
55+
s_adminCommands = new ArrayList<String>();
56+
s_resourceDomainAdminCommands = new ArrayList<String>();
57+
s_pluggableServiceCommands = new ArrayList<String>();
58+
}
59+
60+
@Override
61+
public boolean canAccessAPI(User user, String apiCommandName)
62+
throws PermissionDeniedException{
63+
64+
boolean commandExists = s_allCommands.contains(apiCommandName);
65+
66+
if(commandExists && user != null){
67+
Long accountId = user.getAccountId();
68+
Account userAccount = _accountMgr.getAccount(accountId);
69+
short accountType = userAccount.getType();
70+
return isCommandAvailableForAccount(accountType, apiCommandName);
71+
}
72+
73+
return commandExists;
74+
}
75+
76+
private static boolean isCommandAvailableForAccount(short accountType, String commandName) {
77+
boolean isCommandAvailable = false;
78+
switch (accountType) {
79+
case Account.ACCOUNT_TYPE_ADMIN:
80+
isCommandAvailable = s_adminCommands.contains(commandName);
81+
break;
82+
case Account.ACCOUNT_TYPE_DOMAIN_ADMIN:
83+
isCommandAvailable = s_resellerCommands.contains(commandName);
84+
break;
85+
case Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN:
86+
isCommandAvailable = s_resourceDomainAdminCommands.contains(commandName);
87+
break;
88+
case Account.ACCOUNT_TYPE_NORMAL:
89+
isCommandAvailable = s_userCommands.contains(commandName);
90+
break;
91+
}
92+
return isCommandAvailable;
93+
}
94+
95+
96+
@Override
97+
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
98+
super.configure(name, params);
99+
100+
//load command.properties to build the static map per role.
101+
ComponentLocator locator = ComponentLocator.getLocator(ManagementServer.Name);
102+
String[] apiConfig = ((ManagementServer) ComponentLocator.getComponent(ManagementServer.Name)).getApiConfig();
103+
104+
processConfigFiles(apiConfig, false);
105+
106+
// get commands for all pluggable services
107+
String[] pluggableServicesApiConfigs = getPluggableServicesApiConfigs();
108+
processConfigFiles(pluggableServicesApiConfigs, true);
109+
110+
return true;
111+
}
112+
113+
114+
private String[] getPluggableServicesApiConfigs() {
115+
List<String> pluggableServicesApiConfigs = new ArrayList<String>();
116+
117+
ComponentLocator locator = ComponentLocator.getLocator(ManagementServer.Name);
118+
List<PluggableService> services = locator.getAllPluggableServices();
119+
for (PluggableService service : services) {
120+
pluggableServicesApiConfigs.add(service.getPropertiesFile());
121+
}
122+
return pluggableServicesApiConfigs.toArray(new String[0]);
123+
}
124+
125+
private void processConfigFiles(String[] apiConfig, boolean pluggableServicesConfig) {
126+
try {
127+
Properties preProcessedCommands = new Properties();
128+
if (apiConfig != null) {
129+
for (String configFile : apiConfig) {
130+
File commandsFile = PropertiesUtil.findConfigFile(configFile);
131+
if (commandsFile != null) {
132+
try {
133+
preProcessedCommands.load(new FileInputStream(commandsFile));
134+
} catch (FileNotFoundException fnfex) {
135+
// in case of a file within a jar in classpath, try to open stream using url
136+
InputStream stream = PropertiesUtil.openStreamFromURL(configFile);
137+
if (stream != null) {
138+
preProcessedCommands.load(stream);
139+
} else {
140+
s_logger.error("Unable to find properites file", fnfex);
141+
}
142+
}
143+
}
144+
}
145+
for (Object key : preProcessedCommands.keySet()) {
146+
String preProcessedCommand = preProcessedCommands.getProperty((String) key);
147+
String[] commandParts = preProcessedCommand.split(";");
148+
149+
150+
if (pluggableServicesConfig) {
151+
s_pluggableServiceCommands.add(commandParts[0]);
152+
}
153+
154+
if (commandParts.length > 1) {
155+
try {
156+
short cmdPermissions = Short.parseShort(commandParts[1]);
157+
if ((cmdPermissions & ADMIN_COMMAND) != 0) {
158+
s_adminCommands.add((String) key);
159+
}
160+
if ((cmdPermissions & RESOURCE_DOMAIN_ADMIN_COMMAND) != 0) {
161+
s_resourceDomainAdminCommands.add((String) key);
162+
}
163+
if ((cmdPermissions & DOMAIN_ADMIN_COMMAND) != 0) {
164+
s_resellerCommands.add((String) key);
165+
}
166+
if ((cmdPermissions & USER_COMMAND) != 0) {
167+
s_userCommands.add((String) key);
168+
}
169+
s_allCommands.addAll(s_adminCommands);
170+
s_allCommands.addAll(s_resourceDomainAdminCommands);
171+
s_allCommands.addAll(s_userCommands);
172+
s_allCommands.addAll(s_resellerCommands);
173+
} catch (NumberFormatException nfe) {
174+
s_logger.info("Malformed command.properties permissions value, key = " + key + ", value = " + preProcessedCommand);
175+
}
176+
}
177+
}
178+
179+
}
180+
} catch (FileNotFoundException fnfex) {
181+
s_logger.error("Unable to find properites file", fnfex);
182+
} catch (IOException ioex) {
183+
s_logger.error("Exception loading properties file", ioex);
184+
}
185+
}
186+
187+
188+
}

0 commit comments

Comments
 (0)