You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by bh...@apache.org on 2016/05/11 09:43:27 UTC

[39/50] [abbrv] git commit: updated refs/heads/4.9-bountycastle-daan to 4817cb6

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4347776a/api/src/org/apache/cloudstack/api/response/UserResponse.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/response/UserResponse.java b/api/src/org/apache/cloudstack/api/response/UserResponse.java
index 36611ae..cc986b3 100644
--- a/api/src/org/apache/cloudstack/api/response/UserResponse.java
+++ b/api/src/org/apache/cloudstack/api/response/UserResponse.java
@@ -20,6 +20,7 @@ import java.util.Date;
 
 import com.google.gson.annotations.SerializedName;
 
+import org.apache.cloudstack.acl.RoleType;
 import org.apache.cloudstack.api.ApiConstants;
 import org.apache.cloudstack.api.BaseResponse;
 import org.apache.cloudstack.api.EntityReference;
@@ -65,6 +66,18 @@ public class UserResponse extends BaseResponse {
     @Param(description = "the account type of the user")
     private Short accountType;
 
+    @SerializedName(ApiConstants.ROLE_ID)
+    @Param(description = "the ID of the role")
+    private String roleId;
+
+    @SerializedName(ApiConstants.ROLE_TYPE)
+    @Param(description = "the type of the role")
+    private String roleType;
+
+    @SerializedName(ApiConstants.ROLE_NAME)
+    @Param(description = "the name of the role")
+    private String roleName;
+
     @SerializedName("domainid")
     @Param(description = "the domain ID of the user")
     private String domainId;
@@ -174,6 +187,20 @@ public class UserResponse extends BaseResponse {
         this.accountType = accountType;
     }
 
+    public void setRoleId(String roleId) {
+        this.roleId = roleId;
+    }
+
+    public void setRoleType(RoleType roleType) {
+        if (roleType != null) {
+            this.roleType = roleType.name();
+        }
+    }
+
+    public void setRoleName(String roleName) {
+        this.roleName = roleName;
+    }
+
     public String getDomainId() {
         return domainId;
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4347776a/api/test/org/apache/cloudstack/acl/RoleTypeTest.java
----------------------------------------------------------------------
diff --git a/api/test/org/apache/cloudstack/acl/RoleTypeTest.java b/api/test/org/apache/cloudstack/acl/RoleTypeTest.java
new file mode 100644
index 0000000..611b761
--- /dev/null
+++ b/api/test/org/apache/cloudstack/acl/RoleTypeTest.java
@@ -0,0 +1,100 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.acl;
+
+import com.cloud.user.Account;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.util.Arrays;
+
+public class RoleTypeTest {
+
+    @Test
+    public void testValidRoleTypeFromString() {
+        for (RoleType roleType : RoleType.values()) {
+            Assert.assertEquals(RoleType.fromString(roleType.name()), roleType);
+        }
+    }
+
+    @Test
+    public void testInvalidRoleTypeFromString() {
+        for (String roleType : Arrays.asList(null, "", "admin", "12345%&^*")) {
+            try {
+                RoleType.fromString(roleType);
+                Assert.fail("Invalid roletype provided, exception was expected");
+            } catch (IllegalStateException e) {
+                Assert.assertEquals(e.getMessage(), "Illegal RoleType name provided");
+            }
+        }
+    }
+
+    @Test
+    public void testDefaultRoleMaskByValue() {
+        Assert.assertEquals(RoleType.fromMask(1), RoleType.Admin);
+        Assert.assertEquals(RoleType.fromMask(2), RoleType.ResourceAdmin);
+        Assert.assertEquals(RoleType.fromMask(4), RoleType.DomainAdmin);
+        Assert.assertEquals(RoleType.fromMask(8), RoleType.User);
+        Assert.assertEquals(RoleType.fromMask(0), RoleType.Unknown);
+    }
+
+    @Test
+    public void testGetByAccountType() {
+        Assert.assertEquals(RoleType.getByAccountType(Account.ACCOUNT_TYPE_NORMAL), RoleType.User);
+        Assert.assertEquals(RoleType.getByAccountType(Account.ACCOUNT_TYPE_ADMIN), RoleType.Admin);
+        Assert.assertEquals(RoleType.getByAccountType(Account.ACCOUNT_TYPE_DOMAIN_ADMIN), RoleType.DomainAdmin);
+        Assert.assertEquals(RoleType.getByAccountType(Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN), RoleType.ResourceAdmin);
+        Assert.assertEquals(RoleType.getByAccountType(Account.ACCOUNT_TYPE_PROJECT), RoleType.Unknown);
+    }
+
+    @Test
+    public void testGetRoleByAccountTypeWhenRoleIdIsProvided() {
+        Assert.assertEquals(RoleType.getRoleByAccountType(123L, Account.ACCOUNT_TYPE_ADMIN), Long.valueOf(123L));
+        Assert.assertEquals(RoleType.getRoleByAccountType(1234L, null), Long.valueOf(1234L));
+    }
+
+    @Test
+    public void testGetRoleByAccountTypeForDefaultAccountTypes() {
+        Assert.assertEquals(RoleType.getRoleByAccountType(null, Account.ACCOUNT_TYPE_ADMIN), (Long) RoleType.Admin.getId());
+        Assert.assertEquals(RoleType.getRoleByAccountType(null, Account.ACCOUNT_TYPE_NORMAL), (Long) RoleType.User.getId());
+        Assert.assertEquals(RoleType.getRoleByAccountType(null, Account.ACCOUNT_TYPE_DOMAIN_ADMIN), (Long) RoleType.DomainAdmin.getId());
+        Assert.assertEquals(RoleType.getRoleByAccountType(null, Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN), (Long) RoleType.ResourceAdmin.getId());
+        Assert.assertEquals(RoleType.getRoleByAccountType(null, Account.ACCOUNT_TYPE_PROJECT), null);
+    }
+
+    @Test
+    public void testGetAccountTypeByRoleWhenRoleIsNull() {
+        for (Short accountType: Arrays.asList(
+                Account.ACCOUNT_TYPE_NORMAL,
+                Account.ACCOUNT_TYPE_ADMIN,
+                Account.ACCOUNT_TYPE_DOMAIN_ADMIN,
+                Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN,
+                Account.ACCOUNT_TYPE_PROJECT,
+                (short) 12345)) {
+            Assert.assertEquals(RoleType.getAccountTypeByRole(null, accountType), accountType);
+        }
+    }
+
+    @Test
+    public void testGetAccountTypeByRole() {
+        Role role = Mockito.mock(Role.class);
+        Mockito.when(role.getRoleType()).thenReturn(RoleType.Admin);
+        Mockito.when(role.getId()).thenReturn(100L);
+        Assert.assertEquals(RoleType.getAccountTypeByRole(role, null), (Short) RoleType.Admin.getAccountType());
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4347776a/api/test/org/apache/cloudstack/acl/RuleTest.java
----------------------------------------------------------------------
diff --git a/api/test/org/apache/cloudstack/acl/RuleTest.java b/api/test/org/apache/cloudstack/acl/RuleTest.java
new file mode 100644
index 0000000..08bd775
--- /dev/null
+++ b/api/test/org/apache/cloudstack/acl/RuleTest.java
@@ -0,0 +1,98 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.acl;
+
+import com.cloud.exception.InvalidParameterValueException;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+public class RuleTest {
+
+    @Test
+    public void testToString() throws Exception {
+        Rule rule = new Rule("someString");
+        Assert.assertEquals(rule.toString(), "someString");
+    }
+
+    @Test
+    public void testMatchesEmpty() throws Exception {
+        Rule rule = new Rule("someString");
+        Assert.assertFalse(rule.matches(""));
+    }
+
+    @Test
+    public void testMatchesNull() throws Exception {
+        Rule rule = new Rule("someString");
+        Assert.assertFalse(rule.matches(null));
+    }
+
+    @Test
+    public void testMatchesSpace() throws Exception {
+        Rule rule = new Rule("someString");
+        Assert.assertFalse(rule.matches(" "));
+    }
+
+    @Test
+    public void testMatchesAPI() throws Exception {
+        Rule rule = new Rule("someApi");
+        Assert.assertTrue(rule.matches("someApi"));
+    }
+
+    @Test
+    public void testMatchesWildcardSuffix() throws Exception {
+        Rule rule = new Rule("list*");
+        Assert.assertTrue(rule.matches("listHosts"));
+    }
+
+    @Test
+    public void testMatchesWildcardPrefix() throws Exception {
+        Rule rule = new Rule("*User");
+        Assert.assertTrue(rule.matches("createUser"));
+    }
+
+    @Test
+    public void testMatchesWildcardMiddle() throws Exception {
+        Rule rule = new Rule("list*s");
+        Assert.assertTrue(rule.matches("listClusters"));
+    }
+
+    @Test
+    public void testValidateRuleWithValidData() throws Exception {
+        for (String rule : Arrays.asList("a", "1", "someApi", "someApi321", "123SomeApi",
+                "prefix*", "*middle*", "*Suffix",
+                "*", "**", "f***", "m0nk3yMa**g1c*")) {
+            Assert.assertEquals(new Rule(rule).toString(), rule);
+        }
+    }
+
+    @Test
+    public void testValidateRuleWithInvalidData() throws Exception {
+        for (String rule : Arrays.asList(null, "", " ", "  ", "\n", "\t", "\r", "\"", "\'",
+                "^someApi$", "^someApi", "some$", "some-Api;", "some,Api",
+                "^", "$", "^$", ".*", "\\w+", "r**l3rd0@Kr3", "j@s1n|+|0\u0237",
+                "[a-z0-9-]+", "^([a-z0-9_\\.-]+)@([\\da-z\\.-]+)\\.([a-z\\.]{2,6})$")) {
+            try {
+                new Rule(rule);
+                Assert.fail("Invalid rule, exception was expected");
+            } catch (InvalidParameterValueException e) {
+                Assert.assertTrue(e.getMessage().startsWith("Only API names and wildcards are allowed"));
+            }
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4347776a/api/test/org/apache/cloudstack/api/command/admin/account/CreateAccountCmdTest.java
----------------------------------------------------------------------
diff --git a/api/test/org/apache/cloudstack/api/command/admin/account/CreateAccountCmdTest.java b/api/test/org/apache/cloudstack/api/command/admin/account/CreateAccountCmdTest.java
index c0a046d..b50b226 100644
--- a/api/test/org/apache/cloudstack/api/command/admin/account/CreateAccountCmdTest.java
+++ b/api/test/org/apache/cloudstack/api/command/admin/account/CreateAccountCmdTest.java
@@ -18,6 +18,7 @@
  */
 package org.apache.cloudstack.api.command.admin.account;
 
+import org.apache.cloudstack.acl.RoleService;
 import org.apache.cloudstack.api.ApiErrorCode;
 import org.apache.cloudstack.api.ServerApiException;
 import org.apache.cloudstack.context.CallContext;
@@ -41,10 +42,13 @@ public class CreateAccountCmdTest {
 
     @Mock
     private AccountService accountService;
+    @Mock
+    private RoleService roleService;
 
     @InjectMocks
     private CreateAccountCmd createAccountCmd = new CreateAccountCmd();
 
+    private long roleId = 1L;
     private short accountType = 1;
     private Long domainId = 1L;
 
@@ -69,7 +73,7 @@ public class CreateAccountCmdTest {
         } catch (ServerApiException e) {
             Assert.assertTrue("Received exception as the mock accountService createUserAccount returns null user", true);
         }
-        Mockito.verify(accountService, Mockito.times(1)).createUserAccount(null, "Test", null, null, null, null, null, accountType, domainId, null, null, null, null);
+        Mockito.verify(accountService, Mockito.times(1)).createUserAccount(null, "Test", null, null, null, null, null, accountType, roleId, domainId, null, null, null, null);
     }
 
     @Test
@@ -82,7 +86,7 @@ public class CreateAccountCmdTest {
             Assert.assertEquals(ApiErrorCode.PARAM_ERROR, e.getErrorCode());
             Assert.assertEquals("Empty passwords are not allowed", e.getMessage());
         }
-        Mockito.verify(accountService, Mockito.never()).createUserAccount(null, null, null, null, null, null, null, accountType, domainId, null, null, null, null);
+        Mockito.verify(accountService, Mockito.never()).createUserAccount(null, null, null, null, null, null, null, accountType, roleId, domainId, null, null, null, null);
     }
 
     @Test
@@ -95,6 +99,6 @@ public class CreateAccountCmdTest {
             Assert.assertEquals(ApiErrorCode.PARAM_ERROR, e.getErrorCode());
             Assert.assertEquals("Empty passwords are not allowed", e.getMessage());
         }
-        Mockito.verify(accountService, Mockito.never()).createUserAccount(null, null, null, null, null, null, null, accountType, domainId, null, null, null, null);
+        Mockito.verify(accountService, Mockito.never()).createUserAccount(null, null, null, null, null, null, null, accountType, roleId, domainId, null, null, null, null);
     }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4347776a/api/test/org/apache/cloudstack/api/command/test/UpdateCfgCmdTest.java
----------------------------------------------------------------------
diff --git a/api/test/org/apache/cloudstack/api/command/test/UpdateCfgCmdTest.java b/api/test/org/apache/cloudstack/api/command/test/UpdateCfgCmdTest.java
index 63c4a03..65b9330 100644
--- a/api/test/org/apache/cloudstack/api/command/test/UpdateCfgCmdTest.java
+++ b/api/test/org/apache/cloudstack/api/command/test/UpdateCfgCmdTest.java
@@ -19,6 +19,7 @@ package org.apache.cloudstack.api.command.test;
 import junit.framework.Assert;
 import junit.framework.TestCase;
 
+import org.apache.cloudstack.acl.RoleService;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -49,21 +50,45 @@ public class UpdateCfgCmdTest extends TestCase {
     }
 
     @Test
+    public void testExecuteForEmptyCfgName() {
+        updateCfgCmd._configService = configService;
+
+        try {
+            updateCfgCmd.execute();
+        } catch (ServerApiException exception) {
+            Assert.assertEquals("Empty configuration name provided", exception.getDescription());
+        }
+    }
+
+    @Test
+    public void testExecuteForRestrictedCfg() {
+        updateCfgCmd._configService = configService;
+        updateCfgCmd.setCfgName(RoleService.EnableDynamicApiChecker.key());
+
+        try {
+            updateCfgCmd.execute();
+        } catch (ServerApiException exception) {
+            Assert.assertEquals("Restricted configuration update not allowed", exception.getDescription());
+        }
+    }
+
+    @Test
     public void testExecuteForEmptyResult() {
         updateCfgCmd._configService = configService;
+        updateCfgCmd.setCfgName("some.cfg");
 
         try {
             updateCfgCmd.execute();
         } catch (ServerApiException exception) {
             Assert.assertEquals("Failed to update config", exception.getDescription());
         }
-
     }
 
     @Test
     public void testExecuteForNullResult() {
 
         updateCfgCmd._configService = configService;
+        updateCfgCmd.setCfgName("some.cfg");
 
         try {
             Mockito.when(configService.updateConfiguration(updateCfgCmd)).thenReturn(null);
@@ -88,6 +113,7 @@ public class UpdateCfgCmdTest extends TestCase {
         Configuration cfg = Mockito.mock(Configuration.class);
         updateCfgCmd._configService = configService;
         updateCfgCmd._responseGenerator = responseGenerator;
+        updateCfgCmd.setCfgName("some.cfg");
 
         try {
             Mockito.when(configService.updateConfiguration(updateCfgCmd)).thenReturn(cfg);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4347776a/client/WEB-INF/classes/resources/messages.properties
----------------------------------------------------------------------
diff --git a/client/WEB-INF/classes/resources/messages.properties b/client/WEB-INF/classes/resources/messages.properties
index fbdedda..5e97907 100644
--- a/client/WEB-INF/classes/resources/messages.properties
+++ b/client/WEB-INF/classes/resources/messages.properties
@@ -1095,9 +1095,17 @@ label.retry.interval=Retry Interval
 label.review=Review
 label.revoke.project.invite=Revoke invitation
 label.role=Role
+label.roles=Roles
+label.roletype=Role Type
+label.add.role=Add Role
+label.edit.role=Edit Role
+label.delete.role=Delete Role
+message.role.ordering.fail=Reordering of rule permissions aborted as the list has changed while you were making changes. Please try again.
 label.root.certificate=Root certificate
 label.root.disk.controller=Root disk controller
 label.root.disk.offering=Root Disk Offering
+label.permission=Permission
+label.rule=Rule
 label.rules=Rules
 label.running.vms=Running VMs
 label.s3.access_key=Access Key

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4347776a/client/pom.xml
----------------------------------------------------------------------
diff --git a/client/pom.xml b/client/pom.xml
index a1e4f60..9bd80b7 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -52,6 +52,11 @@
     </dependency>
     <dependency>
       <groupId>org.apache.cloudstack</groupId>
+      <artifactId>cloud-plugin-acl-dynamic-role-based</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.cloudstack</groupId>
       <artifactId>cloud-plugin-dedicated-resources</artifactId>
       <version>${project.version}</version>
     </dependency>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4347776a/debian/cloudstack-management.install
----------------------------------------------------------------------
diff --git a/debian/cloudstack-management.install b/debian/cloudstack-management.install
index 4e016df..c669382 100644
--- a/debian/cloudstack-management.install
+++ b/debian/cloudstack-management.install
@@ -18,7 +18,6 @@
 /etc/cloudstack/management/catalina.policy
 /etc/cloudstack/management/catalina.properties
 /etc/cloudstack/management/logging.properties
-/etc/cloudstack/management/commands.properties
 /etc/cloudstack/management/ehcache.xml
 /etc/cloudstack/management/server-ssl.xml
 /etc/cloudstack/management/server-nonssl.xml

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4347776a/debian/control
----------------------------------------------------------------------
diff --git a/debian/control b/debian/control
index 05058a8..6e7881b 100644
--- a/debian/control
+++ b/debian/control
@@ -15,7 +15,7 @@ Description: A common package which contains files which are shared by several C
 
 Package: cloudstack-management
 Architecture: all
-Depends: ${misc:Depends}, ${python:Depends}, cloudstack-common (= ${source:Version}), tomcat6, sudo, jsvc, python-mysql.connector, libmysql-java, augeas-tools, mysql-client, adduser, bzip2
+Depends: ${misc:Depends}, ${python:Depends}, cloudstack-common (= ${source:Version}), tomcat6 | tomcat7, sudo, jsvc, python-mysql.connector, libmysql-java, augeas-tools, mysql-client, adduser, bzip2
 Conflicts: cloud-server, cloud-client, cloud-client-ui
 Description: CloudStack server library
  The CloudStack management server

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4347776a/developer/developer-prefill.sql
----------------------------------------------------------------------
diff --git a/developer/developer-prefill.sql b/developer/developer-prefill.sql
index f874118..0dbe180 100644
--- a/developer/developer-prefill.sql
+++ b/developer/developer-prefill.sql
@@ -106,6 +106,11 @@ INSERT INTO `cloud`.`configuration` (category, instance, component, name, value)
             VALUES ('Advanced', 'DEFAULT', 'management-server',
             'direct.agent.load.size', '1000');
 
+-- Enable dynamic RBAC by default for fresh deployments
+INSERT INTO `cloud`.`configuration` (category, instance, component, name, value)
+            VALUES ('Advanced', 'DEFAULT', 'RoleService',
+            'dynamic.apichecker.enabled', 'true');
+
 -- Add developer configuration entry; allows management server to be run as a user other than "cloud"
 INSERT INTO `cloud`.`configuration` (category, instance, component, name, value)
             VALUES ('Advanced', 'DEFAULT', 'management-server',

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4347776a/engine/schema/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml
----------------------------------------------------------------------
diff --git a/engine/schema/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml b/engine/schema/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml
index 67ed2a7..a3d58fc 100644
--- a/engine/schema/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml
+++ b/engine/schema/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml
@@ -113,6 +113,8 @@
   <!--
     DAOs with default configuration
   -->
+  <bean id="roleDaoImpl" class="org.apache.cloudstack.acl.dao.RoleDaoImpl" />
+  <bean id="rolePermissionsDaoImpl" class="org.apache.cloudstack.acl.dao.RolePermissionsDaoImpl" />
   <bean id="accountDaoImpl" class="com.cloud.user.dao.AccountDaoImpl" />
   <bean id="accountDetailsDaoImpl" class="com.cloud.user.AccountDetailsDaoImpl" />
   <bean id="accountJoinDaoImpl" class="com.cloud.api.query.dao.AccountJoinDaoImpl" />

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4347776a/engine/schema/src/com/cloud/upgrade/dao/Upgrade481to490.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade481to490.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade481to490.java
index 9e52134..d1240ec 100644
--- a/engine/schema/src/com/cloud/upgrade/dao/Upgrade481to490.java
+++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade481to490.java
@@ -17,12 +17,21 @@
 
 package com.cloud.upgrade.dao;
 
+import com.cloud.utils.PropertiesUtil;
+import com.cloud.utils.db.ScriptRunner;
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.utils.script.Script;
+import org.apache.cloudstack.acl.RoleType;
 import org.apache.log4j.Logger;
 
 import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
 import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Map;
 
 public class Upgrade481to490 implements DbUpgrade {
     final static Logger s_logger = Logger.getLogger(Upgrade481to490.class);
@@ -53,6 +62,71 @@ public class Upgrade481to490 implements DbUpgrade {
 
     @Override
     public void performDataMigration(Connection conn) {
+        setupRolesAndPermissionsForDynamicChecker(conn);
+    }
+
+    private void migrateAccountsToDefaultRoles(final Connection conn) {
+        try (final PreparedStatement selectStatement = conn.prepareStatement("SELECT `id`, `type` FROM `cloud`.`account`;");
+             final ResultSet selectResultSet = selectStatement.executeQuery()) {
+            while (selectResultSet.next()) {
+                final Long accountId = selectResultSet.getLong(1);
+                final Short accountType = selectResultSet.getShort(2);
+                final Long roleId = RoleType.getByAccountType(accountType).getId();
+                if (roleId < 1L || roleId > 4L) {
+                    s_logger.warn("Skipping role ID migration due to invalid role_id resolved for account id=" + accountId);
+                    continue;
+                }
+                try (final PreparedStatement updateStatement = conn.prepareStatement("UPDATE `cloud`.`account` SET account.role_id = ? WHERE account.id = ? ;")) {
+                    updateStatement.setLong(1, roleId);
+                    updateStatement.setLong(2, accountId);
+                    updateStatement.executeUpdate();
+                } catch (SQLException e) {
+                    s_logger.error("Failed to update cloud.account role_id for account id:" + accountId + " with exception: " + e.getMessage());
+                    throw new CloudRuntimeException("Exception while updating cloud.account role_id", e);
+                }
+            }
+        } catch (SQLException e) {
+            throw new CloudRuntimeException("Exception while migrating existing account table's role_id column to a role based on account type", e);
+        }
+        s_logger.debug("Done migrating existing accounts to use one of default roles based on account type");
+    }
+
+    private void setupRolesAndPermissionsForDynamicChecker(final Connection conn) {
+        final String alterTableSql = "ALTER TABLE `cloud`.`account` " +
+                "ADD COLUMN `role_id` bigint(20) unsigned COMMENT 'role id for this account' AFTER `type`, " +
+                "ADD KEY `fk_account__role_id` (`role_id`), " +
+                "ADD CONSTRAINT `fk_account__role_id` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`);";
+        try (final PreparedStatement pstmt = conn.prepareStatement(alterTableSql)) {
+            pstmt.executeUpdate();
+        } catch (SQLException e) {
+            if (e.getMessage().contains("role_id")) {
+                s_logger.warn("cloud.account table already has the role_id column, skipping altering table and migration of accounts");
+                return;
+            } else {
+                throw new CloudRuntimeException("Unable to create column quota_calculated in table cloud_usage.cloud_usage", e);
+            }
+        }
+
+        migrateAccountsToDefaultRoles(conn);
+
+        final Map<String, String> apiMap = PropertiesUtil.processConfigFile(new String[] { PropertiesUtil.getDefaultApiCommandsFileName() });
+        if (apiMap == null || apiMap.isEmpty()) {
+            if (s_logger.isDebugEnabled()) {
+                s_logger.debug("The commands.properties file and default role permissions were not found. " +
+                        "Assuming new installation, configuring default role-api mappings.");
+            }
+            String script = Script.findScript("", "db/create-default-role-api-mappings.sql");
+            if (script == null) {
+                s_logger.error("Unable to find default role-api mapping sql file, please configure api per role manually");
+                return;
+            }
+            try(final FileReader reader = new FileReader(new File(script))) {
+                ScriptRunner runner = new ScriptRunner(conn, false, true);
+                runner.runScript(reader);
+            } catch (SQLException | IOException e) {
+                s_logger.error("Unable to insert default api-role mappings from file: " + script + ". Please configure api per role manually, giving up!", e);
+            }
+        }
     }
 
     @Override
@@ -61,7 +135,6 @@ public class Upgrade481to490 implements DbUpgrade {
         if (script == null) {
             throw new CloudRuntimeException("Unable to find db/schema-481to490-cleanup.sql");
         }
-
         return new File[] {new File(script)};
     }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4347776a/engine/schema/src/com/cloud/user/AccountVO.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/user/AccountVO.java b/engine/schema/src/com/cloud/user/AccountVO.java
index 0f5a044..a504d2f 100644
--- a/engine/schema/src/com/cloud/user/AccountVO.java
+++ b/engine/schema/src/com/cloud/user/AccountVO.java
@@ -16,8 +16,8 @@
 // under the License.
 package com.cloud.user;
 
-import java.util.Date;
-import java.util.UUID;
+import com.cloud.utils.db.GenericDao;
+import org.apache.cloudstack.acl.RoleType;
 
 import javax.persistence.Column;
 import javax.persistence.Entity;
@@ -27,8 +27,8 @@ import javax.persistence.GeneratedValue;
 import javax.persistence.GenerationType;
 import javax.persistence.Id;
 import javax.persistence.Table;
-
-import com.cloud.utils.db.GenericDao;
+import java.util.Date;
+import java.util.UUID;
 
 @Entity
 @Table(name = "account")
@@ -44,6 +44,9 @@ public class AccountVO implements Account {
     @Column(name = "type")
     private short type = ACCOUNT_TYPE_NORMAL;
 
+    @Column(name = "role_id")
+    private Long roleId;
+
     @Column(name = "domain_id")
     private long domainId;
 
@@ -78,13 +81,21 @@ public class AccountVO implements Account {
         uuid = UUID.randomUUID().toString();
     }
 
-    public AccountVO(String accountName, long domainId, String networkDomain, short type, String uuid) {
+    public AccountVO(final String accountName, final long domainId, final String networkDomain, final short type, final String uuid) {
         this.accountName = accountName;
         this.domainId = domainId;
         this.networkDomain = networkDomain;
         this.type = type;
-        state = State.enabled;
+        this.state = State.enabled;
         this.uuid = uuid;
+        this.roleId = RoleType.getRoleByAccountType(null, type);
+    }
+
+    public AccountVO(final String accountName, final long domainId, final String networkDomain, final short type, final Long roleId, final String uuid) {
+        this(accountName, domainId, networkDomain, type, uuid);
+        if (roleId != null) {
+            this.roleId = roleId;
+        }
     }
 
     public void setNeedsCleanup(boolean value) {
@@ -122,6 +133,14 @@ public class AccountVO implements Account {
         this.type = type;
     }
 
+    public Long getRoleId() {
+        return roleId;
+    }
+
+    public void setRoleId(long roleId) {
+        this.roleId = roleId;
+    }
+
     @Override
     public long getDomainId() {
         return domainId;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4347776a/engine/schema/src/com/cloud/user/dao/AccountDao.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/user/dao/AccountDao.java b/engine/schema/src/com/cloud/user/dao/AccountDao.java
index 4c7ce8e..374c9cc 100644
--- a/engine/schema/src/com/cloud/user/dao/AccountDao.java
+++ b/engine/schema/src/com/cloud/user/dao/AccountDao.java
@@ -41,6 +41,8 @@ public interface AccountDao extends GenericDao<AccountVO, Long> {
 
     List<AccountVO> findActiveAccountsForDomain(Long domain);
 
+    List<AccountVO> findAccountsByRole(Long roleId);
+
     void markForCleanup(long accountId);
 
     List<AccountVO> listAccounts(String accountName, Long domainId, Filter filter);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4347776a/engine/schema/src/com/cloud/user/dao/AccountDaoImpl.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/user/dao/AccountDaoImpl.java b/engine/schema/src/com/cloud/user/dao/AccountDaoImpl.java
index bff6213..2789150 100644
--- a/engine/schema/src/com/cloud/user/dao/AccountDaoImpl.java
+++ b/engine/schema/src/com/cloud/user/dao/AccountDaoImpl.java
@@ -44,7 +44,7 @@ import com.cloud.utils.db.TransactionLegacy;
 public class AccountDaoImpl extends GenericDaoBase<AccountVO, Long> implements AccountDao {
     private static final Logger s_logger = Logger.getLogger(AccountDaoImpl.class);
     private static final String FIND_USER_ACCOUNT_BY_API_KEY = "SELECT u.id, u.username, u.account_id, u.secret_key, u.state, "
-        + "a.id, a.account_name, a.type, a.domain_id, a.state " + "FROM `cloud`.`user` u, `cloud`.`account` a "
+        + "a.id, a.account_name, a.type, a.role_id, a.domain_id, a.state " + "FROM `cloud`.`user` u, `cloud`.`account` a "
         + "WHERE u.account_id = a.id AND u.api_key = ? and u.removed IS NULL";
 
     protected final SearchBuilder<AccountVO> AllFieldsSearch;
@@ -53,6 +53,7 @@ public class AccountDaoImpl extends GenericDaoBase<AccountVO, Long> implements A
     protected final SearchBuilder<AccountVO> CleanupForRemovedAccountsSearch;
     protected final SearchBuilder<AccountVO> CleanupForDisabledAccountsSearch;
     protected final SearchBuilder<AccountVO> NonProjectAccountSearch;
+    protected final SearchBuilder<AccountVO> AccountByRoleSearch;
     protected final GenericSearchBuilder<AccountVO, Long> AccountIdsSearch;
 
     public AccountDaoImpl() {
@@ -96,6 +97,10 @@ public class AccountDaoImpl extends GenericDaoBase<AccountVO, Long> implements A
         AccountIdsSearch.selectFields(AccountIdsSearch.entity().getId());
         AccountIdsSearch.and("ids", AccountIdsSearch.entity().getDomainId(), Op.IN);
         AccountIdsSearch.done();
+
+        AccountByRoleSearch = createSearchBuilder();
+        AccountByRoleSearch.and("roleId", AccountByRoleSearch.entity().getRoleId(), SearchCriteria.Op.EQ);
+        AccountByRoleSearch.done();
     }
 
     @Override
@@ -140,8 +145,9 @@ public class AccountDaoImpl extends GenericDaoBase<AccountVO, Long> implements A
                 AccountVO a = new AccountVO(rs.getLong(6));
                 a.setAccountName(rs.getString(7));
                 a.setType(rs.getShort(8));
-                a.setDomainId(rs.getLong(9));
-                a.setState(State.valueOf(rs.getString(10)));
+                a.setRoleId(rs.getLong(9));
+                a.setDomainId(rs.getLong(10));
+                a.setState(State.valueOf(rs.getString(11)));
 
                 userAcctPair = new Pair<User, Account>(u, a);
             }
@@ -259,6 +265,13 @@ public class AccountDaoImpl extends GenericDaoBase<AccountVO, Long> implements A
     }
 
     @Override
+    public List<AccountVO> findAccountsByRole(Long roleId) {
+        SearchCriteria<AccountVO> sc = AccountByRoleSearch.create();
+        sc.setParameters("roleId", roleId);
+        return listBy(sc);
+    }
+
+    @Override
     public void markForCleanup(long accountId) {
         AccountVO account = findByIdIncludingRemoved(accountId);
         if (!account.getNeedsCleanup()) {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4347776a/engine/schema/src/org/apache/cloudstack/acl/RolePermissionVO.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/org/apache/cloudstack/acl/RolePermissionVO.java b/engine/schema/src/org/apache/cloudstack/acl/RolePermissionVO.java
new file mode 100644
index 0000000..a81cebb
--- /dev/null
+++ b/engine/schema/src/org/apache/cloudstack/acl/RolePermissionVO.java
@@ -0,0 +1,120 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.cloudstack.acl;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import java.util.UUID;
+
+@Entity
+@Table(name = "role_permissions")
+public class RolePermissionVO implements RolePermission {
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Column(name = "id")
+    private long id;
+
+    @Column(name = "uuid")
+    private String uuid;
+
+    @Column(name = "role_id")
+    private long roleId;
+
+    @Column(name = "rule")
+    private String rule;
+
+    @Column(name = "permission", nullable = false)
+    @Enumerated(value = EnumType.STRING)
+    private Permission permission = RolePermission.Permission.DENY;
+
+    @Column(name = "description")
+    private String description;
+
+    @Column(name = "sort_order")
+    private long sortOrder = 0;
+
+    public RolePermissionVO() {
+        this.uuid = UUID.randomUUID().toString();
+    }
+
+    public RolePermissionVO(final long roleId, final String rule, final Permission permission, final String description) {
+        this();
+        this.roleId = roleId;
+        this.rule = rule;
+        this.permission = permission;
+        this.description = description;
+    }
+
+    @Override
+    public long getId() {
+        return id;
+    }
+
+    @Override
+    public String getUuid() {
+        return uuid;
+    }
+
+    public long getRoleId() {
+        return roleId;
+    }
+
+    public void setRoleId(long roleId) {
+        this.roleId = roleId;
+    }
+
+    @Override
+    public Rule getRule() {
+        return new Rule(rule);
+    }
+
+    public void setRule(String rule) {
+        this.rule = rule;
+    }
+
+    @Override
+    public Permission getPermission() {
+        return permission;
+    }
+
+    public void setPermission(Permission permission) {
+        this.permission = permission;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public long getSortOrder() {
+        return sortOrder;
+    }
+
+    public void setSortOrder(long sortOrder) {
+        this.sortOrder = sortOrder;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4347776a/engine/schema/src/org/apache/cloudstack/acl/RoleVO.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/org/apache/cloudstack/acl/RoleVO.java b/engine/schema/src/org/apache/cloudstack/acl/RoleVO.java
new file mode 100644
index 0000000..f3404ab
--- /dev/null
+++ b/engine/schema/src/org/apache/cloudstack/acl/RoleVO.java
@@ -0,0 +1,106 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.cloudstack.acl;
+
+import com.cloud.utils.db.GenericDao;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import java.util.Date;
+import java.util.UUID;
+
+@Entity
+@Table(name = "roles")
+public class RoleVO implements Role {
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Column(name = "id")
+    private long id;
+
+    @Column(name = "uuid")
+    private String uuid;
+
+    @Column(name = "name")
+    private String name;
+
+    @Column(name = "role_type", nullable = false)
+    @Enumerated(value = EnumType.STRING)
+    private RoleType roleType = RoleType.User;
+
+    @Column(name = "description")
+    private String description;
+
+    @Column(name = GenericDao.REMOVED_COLUMN)
+    private Date removed;
+
+    public RoleVO() {
+        this.uuid = UUID.randomUUID().toString();
+    }
+
+    public RoleVO(final String name, final RoleType roleType, final String description) {
+        this();
+        this.name = name;
+        this.roleType = roleType;
+        this.description = description;
+    }
+
+    public RoleVO(final long id, final String name, final RoleType roleType, final String description) {
+        this(name, roleType, description);
+        this.id = id;
+    }
+
+    @Override
+    public String getUuid() {
+        return uuid;
+    }
+
+    @Override
+    public long getId() {
+        return id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public RoleType getRoleType() {
+        return roleType;
+    }
+
+    public void setRoleType(RoleType roleType) {
+        this.roleType = roleType;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4347776a/engine/schema/src/org/apache/cloudstack/acl/dao/RoleDao.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/org/apache/cloudstack/acl/dao/RoleDao.java b/engine/schema/src/org/apache/cloudstack/acl/dao/RoleDao.java
new file mode 100644
index 0000000..e53654d
--- /dev/null
+++ b/engine/schema/src/org/apache/cloudstack/acl/dao/RoleDao.java
@@ -0,0 +1,29 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.cloudstack.acl.dao;
+
+import com.cloud.utils.db.GenericDao;
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.acl.RoleVO;
+
+import java.util.List;
+
+public interface RoleDao extends GenericDao<RoleVO, Long> {
+    List<RoleVO> findAllByName(String roleName);
+    List<RoleVO> findAllByRoleType(RoleType type);
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4347776a/engine/schema/src/org/apache/cloudstack/acl/dao/RoleDaoImpl.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/org/apache/cloudstack/acl/dao/RoleDaoImpl.java b/engine/schema/src/org/apache/cloudstack/acl/dao/RoleDaoImpl.java
new file mode 100644
index 0000000..c7eb496
--- /dev/null
+++ b/engine/schema/src/org/apache/cloudstack/acl/dao/RoleDaoImpl.java
@@ -0,0 +1,61 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.cloudstack.acl.dao;
+
+import com.cloud.utils.db.GenericDaoBase;
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.acl.RoleVO;
+import org.springframework.stereotype.Component;
+
+import javax.ejb.Local;
+import java.util.List;
+
+@Component
+@Local(value = {RoleDao.class})
+public class RoleDaoImpl extends GenericDaoBase<RoleVO, Long> implements RoleDao {
+    private final SearchBuilder<RoleVO> RoleByNameSearch;
+    private final SearchBuilder<RoleVO> RoleByTypeSearch;
+
+    public RoleDaoImpl() {
+        super();
+
+        RoleByNameSearch = createSearchBuilder();
+        RoleByNameSearch.and("roleName", RoleByNameSearch.entity().getName(), SearchCriteria.Op.LIKE);
+        RoleByNameSearch.done();
+
+        RoleByTypeSearch = createSearchBuilder();
+        RoleByTypeSearch.and("roleType", RoleByTypeSearch.entity().getRoleType(), SearchCriteria.Op.EQ);
+        RoleByTypeSearch.done();
+    }
+
+    @Override
+    public List<RoleVO> findAllByName(final String roleName) {
+        SearchCriteria<RoleVO> sc = RoleByNameSearch.create();
+        sc.setParameters("roleName", "%" + roleName + "%");
+        return listBy(sc);
+    }
+
+    @Override
+    public List<RoleVO> findAllByRoleType(final RoleType type) {
+        SearchCriteria<RoleVO> sc = RoleByTypeSearch.create();
+        sc.setParameters("roleType", type);
+        return listBy(sc);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4347776a/engine/schema/src/org/apache/cloudstack/acl/dao/RolePermissionsDao.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/org/apache/cloudstack/acl/dao/RolePermissionsDao.java b/engine/schema/src/org/apache/cloudstack/acl/dao/RolePermissionsDao.java
new file mode 100644
index 0000000..3754491
--- /dev/null
+++ b/engine/schema/src/org/apache/cloudstack/acl/dao/RolePermissionsDao.java
@@ -0,0 +1,49 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.cloudstack.acl.dao;
+
+import com.cloud.utils.db.GenericDao;
+import org.apache.cloudstack.acl.Role;
+import org.apache.cloudstack.acl.RolePermission;
+import org.apache.cloudstack.acl.RolePermissionVO;
+
+import java.util.List;
+
+public interface RolePermissionsDao extends GenericDao<RolePermissionVO, Long> {
+    /**
+     * Adds a new role permission at the end of the list of role permissions
+     * @param item the new role permission
+     * @return returns persisted role permission
+     */
+    RolePermissionVO persist(final RolePermissionVO item);
+
+    /**
+     * Moves an existing role permission under a given parent role permission
+     * @param role the existing role
+     * @param newOrder the new role permissions order
+     * @return returns true on success
+     */
+    boolean update(final Role role, final List<RolePermission> newOrder);
+
+    /**
+     * Returns ordered linked-list of role permission for a given role
+     * @param roleId the ID of the role
+     * @return returns list of role permissions
+     */
+    List<RolePermissionVO> findAllByRoleIdSorted(Long roleId);
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4347776a/engine/schema/src/org/apache/cloudstack/acl/dao/RolePermissionsDaoImpl.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/org/apache/cloudstack/acl/dao/RolePermissionsDaoImpl.java b/engine/schema/src/org/apache/cloudstack/acl/dao/RolePermissionsDaoImpl.java
new file mode 100644
index 0000000..960b652
--- /dev/null
+++ b/engine/schema/src/org/apache/cloudstack/acl/dao/RolePermissionsDaoImpl.java
@@ -0,0 +1,166 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.cloudstack.acl.dao;
+
+import com.cloud.utils.db.Attribute;
+import com.cloud.utils.db.Filter;
+import com.cloud.utils.db.GenericDaoBase;
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.db.Transaction;
+import com.cloud.utils.db.TransactionCallback;
+import com.cloud.utils.db.TransactionStatus;
+import com.cloud.utils.db.UpdateBuilder;
+import com.cloud.utils.exception.CloudRuntimeException;
+import org.apache.cloudstack.acl.Role;
+import org.apache.cloudstack.acl.RolePermission;
+import org.apache.cloudstack.acl.RolePermissionVO;
+import org.apache.log4j.Logger;
+import org.springframework.stereotype.Component;
+
+import javax.ejb.Local;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@Component
+@Local(value = {RolePermissionsDao.class})
+public class RolePermissionsDaoImpl extends GenericDaoBase<RolePermissionVO, Long> implements RolePermissionsDao {
+    protected static final Logger LOGGER = Logger.getLogger(RolePermissionsDaoImpl.class);
+
+    private final SearchBuilder<RolePermissionVO> RolePermissionsSearch;
+    private Attribute sortOrderAttribute;
+
+    public RolePermissionsDaoImpl() {
+        super();
+
+        RolePermissionsSearch = createSearchBuilder();
+        RolePermissionsSearch.and("uuid", RolePermissionsSearch.entity().getUuid(), SearchCriteria.Op.EQ);
+        RolePermissionsSearch.and("roleId", RolePermissionsSearch.entity().getRoleId(), SearchCriteria.Op.EQ);
+        RolePermissionsSearch.and("sortOrder", RolePermissionsSearch.entity().getSortOrder(), SearchCriteria.Op.EQ);
+        RolePermissionsSearch.done();
+
+        sortOrderAttribute = _allAttributes.get("sortOrder");
+
+        assert (sortOrderAttribute != null) : "Couldn't find one of these attributes";
+    }
+
+    private boolean updateSortOrder(final RolePermissionVO permissionBeingMoved, final RolePermissionVO parentPermission) {
+        if (parentPermission != null && permissionBeingMoved.getId() == parentPermission.getId()) {
+            return true;
+        }
+        final List<RolePermissionVO> newOrderedPermissionsList = new ArrayList<>();
+        // Null parent implies item needs to move to the top
+        if (parentPermission == null) {
+            newOrderedPermissionsList.add(permissionBeingMoved);
+        }
+        for (final RolePermissionVO permission : findAllByRoleIdSorted(permissionBeingMoved.getRoleId())) {
+            if (permission.getId() == permissionBeingMoved.getId()) {
+                continue;
+            }
+            newOrderedPermissionsList.add(permission);
+            if (parentPermission != null && permission.getId() == parentPermission.getId()) {
+                newOrderedPermissionsList.add(permissionBeingMoved);
+            }
+        }
+        long sortOrder = 0L;
+        for (final RolePermissionVO permission : newOrderedPermissionsList) {
+            permission.setSortOrder(sortOrder++);
+            if (!update(permission.getId(), permission)) {
+                LOGGER.warn("Failed to update item's sort order with id:" + permission.getId() + " while moving permission with id:" + permissionBeingMoved.getId() + " to a new position");
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public RolePermissionVO persist(final RolePermissionVO item) {
+        item.setSortOrder(0);
+        final List<RolePermissionVO> permissionsList = findAllByRoleIdSorted(item.getRoleId());
+        if (permissionsList != null && permissionsList.size() > 0) {
+            RolePermission lastRule = permissionsList.get(permissionsList.size() - 1);
+            item.setSortOrder(lastRule.getSortOrder() + 1);
+        }
+        return super.persist(item);
+    }
+
+    @Override
+    public boolean update(final Role role, final List<RolePermission> newOrder) {
+        if (role == null || newOrder == null || newOrder.isEmpty()) {
+            return false;
+        }
+        return Transaction.execute(new TransactionCallback<Boolean>() {
+            @Override
+            public Boolean doInTransaction(TransactionStatus status) {
+                final String failMessage = "The role's rule permissions list has changed while you were making updates, aborted re-ordering of rules. Please try again.";
+                final List<RolePermissionVO> currentOrder = findAllByRoleIdSorted(role.getId());
+                if (role.getId() < 1L || newOrder.size() != currentOrder.size()) {
+                    throw new CloudRuntimeException(failMessage);
+                }
+                final Set<Long> newOrderSet = new HashSet<>();
+                for (final RolePermission permission : newOrder) {
+                    if (permission == null) {
+                        continue;
+                    }
+                    newOrderSet.add(permission.getId());
+                }
+                final Set<Long> currentOrderSet = new HashSet<>();
+                for (final RolePermission permission : currentOrder) {
+                    currentOrderSet.add(permission.getId());
+                }
+                if (!newOrderSet.equals(currentOrderSet)) {
+                    throw new CloudRuntimeException(failMessage);
+                }
+                long sortOrder = 0L;
+                for (RolePermission rolePermission : newOrder) {
+                    final SearchCriteria<RolePermissionVO> sc = RolePermissionsSearch.create();
+                    sc.setParameters("uuid", rolePermission.getUuid());
+                    sc.setParameters("roleId", role.getId());
+                    sc.setParameters("sortOrder", rolePermission.getSortOrder());
+
+                    final UpdateBuilder ub = getUpdateBuilder(rolePermission);
+                    ub.set(rolePermission, sortOrderAttribute, sortOrder);
+                    final int result = update(ub, sc, null);
+                    if (result < 1) {
+                        throw new CloudRuntimeException(failMessage);
+                    }
+                    sortOrder++;
+                }
+                return true;
+            }
+        });
+    }
+
+    @Override
+    public List<RolePermissionVO> findAllByRoleIdSorted(final Long roleId) {
+        final SearchCriteria<RolePermissionVO> sc = RolePermissionsSearch.create();
+        if (roleId != null && roleId > 0L) {
+            sc.setParameters("roleId", roleId);
+        }
+        final Filter searchBySorted = new Filter(RolePermissionVO.class, "sortOrder", true, null, null);
+        searchBySorted.addOrderBy(RolePermissionVO.class, "id", true);
+        final List<RolePermissionVO> rolePermissionList = listBy(sc, searchBySorted);
+        if (rolePermissionList == null) {
+            return Collections.emptyList();
+        }
+        return rolePermissionList;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4347776a/plugins/acl/dynamic-role-based/pom.xml
----------------------------------------------------------------------
diff --git a/plugins/acl/dynamic-role-based/pom.xml b/plugins/acl/dynamic-role-based/pom.xml
new file mode 100644
index 0000000..96d7e53
--- /dev/null
+++ b/plugins/acl/dynamic-role-based/pom.xml
@@ -0,0 +1,32 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements. See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership. The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License. You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied. See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
+  http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>cloud-plugin-acl-dynamic-role-based</artifactId>
+  <name>Apache CloudStack Plugin - ACL Dynamic Role Based</name>
+  <parent>
+    <groupId>org.apache.cloudstack</groupId>
+    <artifactId>cloudstack-plugins</artifactId>
+    <version>4.9.0-SNAPSHOT</version>
+    <relativePath>../../pom.xml</relativePath>
+  </parent>
+</project>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4347776a/plugins/acl/dynamic-role-based/resources/META-INF/cloudstack/acl-dynamic-role-based/module.properties
----------------------------------------------------------------------
diff --git a/plugins/acl/dynamic-role-based/resources/META-INF/cloudstack/acl-dynamic-role-based/module.properties b/plugins/acl/dynamic-role-based/resources/META-INF/cloudstack/acl-dynamic-role-based/module.properties
new file mode 100644
index 0000000..dda9998
--- /dev/null
+++ b/plugins/acl/dynamic-role-based/resources/META-INF/cloudstack/acl-dynamic-role-based/module.properties
@@ -0,0 +1,18 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+name=acl-dynamic-role-based
+parent=api

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4347776a/plugins/acl/dynamic-role-based/resources/META-INF/cloudstack/acl-dynamic-role-based/spring-acl-dynamic-role-based-context.xml
----------------------------------------------------------------------
diff --git a/plugins/acl/dynamic-role-based/resources/META-INF/cloudstack/acl-dynamic-role-based/spring-acl-dynamic-role-based-context.xml b/plugins/acl/dynamic-role-based/resources/META-INF/cloudstack/acl-dynamic-role-based/spring-acl-dynamic-role-based-context.xml
new file mode 100644
index 0000000..9ffe465
--- /dev/null
+++ b/plugins/acl/dynamic-role-based/resources/META-INF/cloudstack/acl-dynamic-role-based/spring-acl-dynamic-role-based-context.xml
@@ -0,0 +1,33 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements. See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership. The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License. You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied. See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xmlns:aop="http://www.springframework.org/schema/aop"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+                      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
+                      http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
+                      http://www.springframework.org/schema/context
+                      http://www.springframework.org/schema/context/spring-context-3.0.xsd"
+                      >
+
+    <bean id="DynamicRoleBasedAPIAccessChecker" class="org.apache.cloudstack.acl.DynamicRoleBasedAPIAccessChecker" >
+        <property name="services" value="#{apiCommandsRegistry.registered}" />
+    </bean>
+</beans>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4347776a/plugins/acl/dynamic-role-based/src/org/apache/cloudstack/acl/DynamicRoleBasedAPIAccessChecker.java
----------------------------------------------------------------------
diff --git a/plugins/acl/dynamic-role-based/src/org/apache/cloudstack/acl/DynamicRoleBasedAPIAccessChecker.java b/plugins/acl/dynamic-role-based/src/org/apache/cloudstack/acl/DynamicRoleBasedAPIAccessChecker.java
new file mode 100644
index 0000000..754d4cc
--- /dev/null
+++ b/plugins/acl/dynamic-role-based/src/org/apache/cloudstack/acl/DynamicRoleBasedAPIAccessChecker.java
@@ -0,0 +1,144 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.acl;
+
+import com.cloud.exception.PermissionDeniedException;
+import com.cloud.user.Account;
+import com.cloud.user.AccountService;
+import com.cloud.user.User;
+import com.cloud.utils.component.AdapterBase;
+import com.cloud.utils.component.PluggableService;
+import com.google.common.base.Strings;
+import org.apache.cloudstack.api.APICommand;
+
+import javax.ejb.Local;
+import javax.inject.Inject;
+import javax.naming.ConfigurationException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+@Local(value = APIChecker.class)
+public class DynamicRoleBasedAPIAccessChecker extends AdapterBase implements APIChecker {
+
+    @Inject
+    private AccountService accountService;
+    @Inject
+    private RoleService roleService;
+
+    private List<PluggableService> services;
+    private Map<RoleType, Set<String>> annotationRoleBasedApisMap = new HashMap<>();
+
+    protected DynamicRoleBasedAPIAccessChecker() {
+        super();
+        for (RoleType roleType : RoleType.values()) {
+            annotationRoleBasedApisMap.put(roleType, new HashSet<String>());
+        }
+    }
+
+    private void denyApiAccess(final String commandName) throws PermissionDeniedException {
+        throw new PermissionDeniedException("The API does not exist or is blacklisted for the account's role. " +
+                "The account with is not allowed to request the api: " + commandName);
+    }
+
+    public boolean isDisabled() {
+        return !roleService.isEnabled();
+    }
+
+    @Override
+    public boolean checkAccess(User user, String commandName) throws PermissionDeniedException {
+        if (isDisabled()) {
+            return true;
+        }
+        Account account = accountService.getAccount(user.getAccountId());
+        if (account == null) {
+            throw new PermissionDeniedException("The account id=" + user.getAccountId() + "for user id=" + user.getId() + "is null");
+        }
+
+        final Role accountRole = roleService.findRole(account.getRoleId());
+        if (accountRole == null || accountRole.getId() < 1L) {
+            denyApiAccess(commandName);
+        }
+
+        // Allow all APIs for root admins
+        if (accountRole.getRoleType() == RoleType.Admin && accountRole.getId() == RoleType.Admin.getId()) {
+            return true;
+        }
+
+        // Check against current list of permissions
+        for (final RolePermission permission : roleService.findAllPermissionsBy(accountRole.getId())) {
+            if (permission.getRule().matches(commandName)) {
+                if (RolePermission.Permission.ALLOW.equals(permission.getPermission())) {
+                    return true;
+                } else {
+                    denyApiAccess(commandName);
+                }
+            }
+        }
+
+        // Check annotations
+        if (annotationRoleBasedApisMap.get(accountRole.getRoleType()) != null
+                && annotationRoleBasedApisMap.get(accountRole.getRoleType()).contains(commandName)) {
+            return true;
+        }
+
+        // Default deny all
+        denyApiAccess(commandName);
+        return false;
+    }
+
+    public void addApiToRoleBasedAnnotationsMap(final RoleType roleType, final String commandName) {
+        if (roleType == null || Strings.isNullOrEmpty(commandName)) {
+            return;
+        }
+        final Set<String> commands = annotationRoleBasedApisMap.get(roleType);
+        if (commands != null && !commands.contains(commandName)) {
+            commands.add(commandName);
+        }
+    }
+
+    @Override
+    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
+        super.configure(name, params);
+        return true;
+    }
+
+    @Override
+    public boolean start() {
+        for (PluggableService service : services) {
+            for (Class<?> clz : service.getCommands()) {
+                APICommand command = clz.getAnnotation(APICommand.class);
+                for (RoleType role : command.authorized()) {
+                    addApiToRoleBasedAnnotationsMap(role, command.name());
+                }
+            }
+        }
+        return super.start();
+    }
+
+    public List<PluggableService> getServices() {
+        return services;
+    }
+
+    @Inject
+    public void setServices(List<PluggableService> services) {
+        this.services = services;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4347776a/plugins/acl/dynamic-role-based/test/org/apache/cloudstack/acl/DynamicRoleBasedAPIAccessCheckerTest.java
----------------------------------------------------------------------
diff --git a/plugins/acl/dynamic-role-based/test/org/apache/cloudstack/acl/DynamicRoleBasedAPIAccessCheckerTest.java b/plugins/acl/dynamic-role-based/test/org/apache/cloudstack/acl/DynamicRoleBasedAPIAccessCheckerTest.java
new file mode 100644
index 0000000..12ebbe5
--- /dev/null
+++ b/plugins/acl/dynamic-role-based/test/org/apache/cloudstack/acl/DynamicRoleBasedAPIAccessCheckerTest.java
@@ -0,0 +1,164 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.acl;
+
+import com.cloud.exception.PermissionDeniedException;
+import com.cloud.user.Account;
+import com.cloud.user.AccountService;
+import com.cloud.user.AccountVO;
+import com.cloud.user.User;
+import com.cloud.user.UserVO;
+import junit.framework.TestCase;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+
+import java.lang.reflect.Field;
+import java.util.Collections;
+
+@RunWith(MockitoJUnitRunner.class)
+public class DynamicRoleBasedAPIAccessCheckerTest extends TestCase {
+
+    @Mock
+    private AccountService accountService;
+    @Mock
+    private RoleService roleService;
+
+    private DynamicRoleBasedAPIAccessChecker apiAccessChecker;
+
+    private User getTestUser() {
+        return new UserVO(12L, "some user", "password", "firstName", "lastName",
+                "email@gmail.com", "GMT", "uuid", User.Source.UNKNOWN);
+    }
+
+    private Account getTestAccount() {
+        return new AccountVO("some name", 1L, "network-domain", (short)0, "some-uuid");
+    }
+
+    private Role getTestRole() {
+        return new RoleVO(4L, "SomeRole", RoleType.User, "some description");
+    }
+
+    private void setupMockField(final Object obj, final String fieldName, final Object mock) throws NoSuchFieldException, IllegalAccessException {
+        Field roleDaoField = DynamicRoleBasedAPIAccessChecker.class.getDeclaredField(fieldName);
+        roleDaoField.setAccessible(true);
+        roleDaoField.set(obj, mock);
+    }
+
+    @Override
+    @Before
+    public void setUp() throws NoSuchFieldException, IllegalAccessException {
+        apiAccessChecker = Mockito.spy(new DynamicRoleBasedAPIAccessChecker());
+        setupMockField(apiAccessChecker, "accountService", accountService);
+        setupMockField(apiAccessChecker, "roleService", roleService);
+
+        Mockito.when(accountService.getAccount(Mockito.anyLong())).thenReturn(getTestAccount());
+        Mockito.when(roleService.findRole(Mockito.anyLong())).thenReturn((RoleVO) getTestRole());
+
+        // Enabled plugin
+        Mockito.doReturn(false).when(apiAccessChecker).isDisabled();
+        Mockito.doCallRealMethod().when(apiAccessChecker).checkAccess(Mockito.any(User.class), Mockito.anyString());
+    }
+
+    @Test
+    public void testInvalidAccountCheckAccess() {
+        Mockito.when(accountService.getAccount(Mockito.anyLong())).thenReturn(null);
+        try {
+            apiAccessChecker.checkAccess(getTestUser(), "someApi");
+            fail("Exception was expected");
+        } catch (PermissionDeniedException ignored) {
+        }
+    }
+
+    @Test
+    public void testInvalidAccountRoleCheckAccess() {
+        Mockito.when(roleService.findRole(Mockito.anyLong())).thenReturn(null);
+        try {
+            apiAccessChecker.checkAccess(getTestUser(), "someApi");
+            fail("Exception was expected");
+        } catch (PermissionDeniedException ignored) {
+        }
+    }
+
+    @Test
+    public void testDefaultRootAdminAccess() {
+        Mockito.when(accountService.getAccount(Mockito.anyLong())).thenReturn(new AccountVO("root admin", 1L, null, (short)1, "some-uuid"));
+        Mockito.when(roleService.findRole(Mockito.anyLong())).thenReturn(new RoleVO(1L, "SomeRole", RoleType.Admin, "default root admin role"));
+        assertTrue(apiAccessChecker.checkAccess(getTestUser(), "anyApi"));
+    }
+
+    @Test
+    public void testInvalidRolePermissionsCheckAccess() {
+        Mockito.when(roleService.findAllPermissionsBy(Mockito.anyLong())).thenReturn(Collections.<RolePermission>emptyList());
+        try {
+            apiAccessChecker.checkAccess(getTestUser(), "someApi");
+            fail("Exception was expected");
+        } catch (PermissionDeniedException ignored) {
+        }
+    }
+
+    @Test
+    public void testValidAllowRolePermissionApiCheckAccess() {
+        final String allowedApiName = "someAllowedApi";
+        final RolePermission permission = new RolePermissionVO(1L, allowedApiName, RolePermission.Permission.ALLOW, null);
+        Mockito.when(roleService.findAllPermissionsBy(Mockito.anyLong())).thenReturn(Collections.singletonList(permission));
+        assertTrue(apiAccessChecker.checkAccess(getTestUser(), allowedApiName));
+    }
+
+    @Test
+    public void testValidAllowRolePermissionWildcardCheckAccess() {
+        final String allowedApiName = "someAllowedApi";
+        final RolePermission permission = new RolePermissionVO(1L, "some*", RolePermission.Permission.ALLOW, null);
+        Mockito.when(roleService.findAllPermissionsBy(Mockito.anyLong())).thenReturn(Collections.singletonList(permission));
+        assertTrue(apiAccessChecker.checkAccess(getTestUser(), allowedApiName));
+    }
+
+    @Test
+    public void testValidDenyRolePermissionApiCheckAccess() {
+        final String denyApiName = "someDeniedApi";
+        final RolePermission permission = new RolePermissionVO(1L, denyApiName, RolePermission.Permission.DENY, null);
+        Mockito.when(roleService.findAllPermissionsBy(Mockito.anyLong())).thenReturn(Collections.singletonList(permission));
+        try {
+            apiAccessChecker.checkAccess(getTestUser(), denyApiName);
+            fail("Exception was expected");
+        } catch (PermissionDeniedException ignored) {
+        }
+    }
+
+    @Test
+    public void testValidDenyRolePermissionWildcardCheckAccess() {
+        final String denyApiName = "someDenyApi";
+        final RolePermission permission = new RolePermissionVO(1L, "*Deny*", RolePermission.Permission.DENY, null);
+        Mockito.when(roleService.findAllPermissionsBy(Mockito.anyLong())).thenReturn(Collections.singletonList(permission));
+        try {
+            apiAccessChecker.checkAccess(getTestUser(), denyApiName);
+            fail("Exception was expected");
+        } catch (PermissionDeniedException ignored) {
+        }
+    }
+
+    @Test
+    public void testAnnotationFallbackCheckAccess() {
+        final String allowedApiName = "someApiWithAnnotations";
+        apiAccessChecker.addApiToRoleBasedAnnotationsMap(getTestRole().getRoleType(), allowedApiName);
+        assertTrue(apiAccessChecker.checkAccess(getTestUser(), allowedApiName));
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4347776a/plugins/acl/static-role-based/src/org/apache/cloudstack/acl/StaticRoleBasedAPIAccessChecker.java
----------------------------------------------------------------------
diff --git a/plugins/acl/static-role-based/src/org/apache/cloudstack/acl/StaticRoleBasedAPIAccessChecker.java b/plugins/acl/static-role-based/src/org/apache/cloudstack/acl/StaticRoleBasedAPIAccessChecker.java
index 9221ea4..fc78268 100644
--- a/plugins/acl/static-role-based/src/org/apache/cloudstack/acl/StaticRoleBasedAPIAccessChecker.java
+++ b/plugins/acl/static-role-based/src/org/apache/cloudstack/acl/StaticRoleBasedAPIAccessChecker.java
@@ -41,18 +41,20 @@ import com.cloud.utils.component.PluggableService;
 // based on the account type, access is granted
 public class StaticRoleBasedAPIAccessChecker extends AdapterBase implements APIChecker {
 
-    protected static final Logger s_logger = Logger.getLogger(StaticRoleBasedAPIAccessChecker.class);
+    protected static final Logger LOGGER = Logger.getLogger(StaticRoleBasedAPIAccessChecker.class);
 
-    Set<String> commandPropertyFiles = new HashSet<String>();
-    Set<String> commandsPropertiesOverrides = new HashSet<String>();
-    Map<RoleType, Set<String>> commandsPropertiesRoleBasedApisMap = new HashMap<RoleType, Set<String>>();
-    Map<RoleType, Set<String>> annotationRoleBasedApisMap = new HashMap<RoleType, Set<String>>();
+    private Set<String> commandPropertyFiles = new HashSet<String>();
+    private Set<String> commandsPropertiesOverrides = new HashSet<String>();
+    private Map<RoleType, Set<String>> commandsPropertiesRoleBasedApisMap = new HashMap<RoleType, Set<String>>();
+    private Map<RoleType, Set<String>> annotationRoleBasedApisMap = new HashMap<RoleType, Set<String>>();
+    private List<PluggableService> services;
 
-    List<PluggableService> _services;
     @Inject
-    AccountService _accountService;
+    private AccountService accountService;
+    @Inject
+    private RoleService roleService;
 
-    protected StaticRoleBasedAPIAccessChecker() {
+    public StaticRoleBasedAPIAccessChecker() {
         super();
         for (RoleType roleType : RoleType.values()) {
             commandsPropertiesRoleBasedApisMap.put(roleType, new HashSet<String>());
@@ -60,23 +62,31 @@ public class StaticRoleBasedAPIAccessChecker extends AdapterBase implements APIC
         }
     }
 
+    public boolean isDisabled() {
+        return roleService.isEnabled();
+    }
+
     @Override
     public boolean checkAccess(User user, String commandName) throws PermissionDeniedException {
-        Account account = _accountService.getAccount(user.getAccountId());
+        if (isDisabled()) {
+            return true;
+        }
+
+        Account account = accountService.getAccount(user.getAccountId());
         if (account == null) {
             throw new PermissionDeniedException("The account id=" + user.getAccountId() + "for user id=" + user.getId() + "is null");
         }
 
-        RoleType roleType = _accountService.getRoleType(account);
+        RoleType roleType = accountService.getRoleType(account);
         boolean isAllowed =
             commandsPropertiesOverrides.contains(commandName) ? commandsPropertiesRoleBasedApisMap.get(roleType).contains(commandName) : annotationRoleBasedApisMap.get(
                 roleType).contains(commandName);
 
-        if (!isAllowed) {
-            throw new PermissionDeniedException("The API does not exist or is blacklisted. Role type=" + roleType.toString() + " is not allowed to request the api: " +
-                commandName);
+        if (isAllowed) {
+            return true;
         }
-        return isAllowed;
+
+        throw new PermissionDeniedException("The API does not exist or is blacklisted. Role type=" + roleType.toString() + " is not allowed to request the api: " + commandName);
     }
 
     @Override
@@ -91,7 +101,7 @@ public class StaticRoleBasedAPIAccessChecker extends AdapterBase implements APIC
 
     @Override
     public boolean start() {
-        for (PluggableService service : _services) {
+        for (PluggableService service : services) {
             for (Class<?> clz : service.getCommands()) {
                 APICommand command = clz.getAnnotation(APICommand.class);
                 for (RoleType role : command.authorized()) {
@@ -112,22 +122,22 @@ public class StaticRoleBasedAPIAccessChecker extends AdapterBase implements APIC
             try {
                 short cmdPermissions = Short.parseShort(roleMask);
                 for (RoleType roleType : RoleType.values()) {
-                    if ((cmdPermissions & roleType.getValue()) != 0)
+                    if ((cmdPermissions & roleType.getMask()) != 0)
                         commandsPropertiesRoleBasedApisMap.get(roleType).add(apiName);
                 }
             } catch (NumberFormatException nfe) {
-                s_logger.info("Malformed key=value pair for entry: " + entry.toString());
+                LOGGER.info("Malformed key=value pair for entry: " + entry.toString());
             }
         }
     }
 
     public List<PluggableService> getServices() {
-        return _services;
+        return services;
     }
 
     @Inject
     public void setServices(List<PluggableService> services) {
-        this._services = services;
+        this.services = services;
     }
 
     public Set<String> getCommandPropertyFiles() {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4347776a/plugins/api/discovery/src/org/apache/cloudstack/api/command/user/discovery/ListApisCmd.java
----------------------------------------------------------------------
diff --git a/plugins/api/discovery/src/org/apache/cloudstack/api/command/user/discovery/ListApisCmd.java b/plugins/api/discovery/src/org/apache/cloudstack/api/command/user/discovery/ListApisCmd.java
index be0d5d5..66d8c5d 100644
--- a/plugins/api/discovery/src/org/apache/cloudstack/api/command/user/discovery/ListApisCmd.java
+++ b/plugins/api/discovery/src/org/apache/cloudstack/api/command/user/discovery/ListApisCmd.java
@@ -18,6 +18,7 @@ package org.apache.cloudstack.api.command.user.discovery;
 
 import javax.inject.Inject;
 
+import org.apache.cloudstack.acl.RoleType;
 import org.apache.log4j.Logger;
 
 import org.apache.cloudstack.api.APICommand;
@@ -38,7 +39,7 @@ import com.cloud.user.User;
             description = "lists all available apis on the server, provided by the Api Discovery plugin",
             since = "4.1.0",
             requestHasSensitiveInfo = false,
-            responseHasSensitiveInfo = false)
+            responseHasSensitiveInfo = false, authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
 public class ListApisCmd extends BaseCmd {
 
     public static final Logger s_logger = Logger.getLogger(ListApisCmd.class.getName());

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4347776a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalVlanManagerImpl.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalVlanManagerImpl.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalVlanManagerImpl.java
index 2451b56..aca7565 100644
--- a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalVlanManagerImpl.java
+++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalVlanManagerImpl.java
@@ -37,12 +37,13 @@ import com.cloud.user.dao.UserDao;
 import com.cloud.utils.component.ManagerBase;
 import com.cloud.utils.db.QueryBuilder;
 import com.cloud.utils.db.SearchCriteria;
-import com.cloud.utils.exception.CloudRuntimeException;
-import com.cloud.vm.VirtualMachineProfile;
-import com.google.gson.Gson;
-import org.apache.cloudstack.api.AddBaremetalRctCmd;
-import org.apache.cloudstack.api.DeleteBaremetalRctCmd;
-import org.apache.cloudstack.api.ListBaremetalRctCmd;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.vm.VirtualMachineProfile;
+import com.google.gson.Gson;
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.AddBaremetalRctCmd;
+import org.apache.cloudstack.api.DeleteBaremetalRctCmd;
+import org.apache.cloudstack.api.ListBaremetalRctCmd;
 import org.apache.cloudstack.utils.baremetal.BaremetalUtils;
 import org.springframework.web.client.RestTemplate;
 
@@ -244,12 +245,14 @@ public class BaremetalVlanManagerImpl extends ManagerBase implements BaremetalVl
 
         acnt = new AccountVO();
         acnt.setAccountName(BaremetalUtils.BAREMETAL_SYSTEM_ACCOUNT_NAME);
-        acnt.setUuid(UUID.randomUUID().toString());
-        acnt.setState(Account.State.enabled);
-        acnt.setDomainId(1);
-        acnt = acntDao.persist(acnt);
-
-        UserVO user = new UserVO();
+        acnt.setUuid(UUID.randomUUID().toString());
+        acnt.setState(Account.State.enabled);
+        acnt.setDomainId(1);
+        acnt.setType(RoleType.User.getAccountType());
+        acnt.setRoleId(RoleType.User.getId());
+        acnt = acntDao.persist(acnt);
+
+        UserVO user = new UserVO();
         user.setState(Account.State.enabled);
         user.setUuid(UUID.randomUUID().toString());
         user.setAccountId(acnt.getAccountId());

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4347776a/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/management/MockAccountManager.java
----------------------------------------------------------------------
diff --git a/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/management/MockAccountManager.java b/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/management/MockAccountManager.java
index b5da604..e56766a 100644
--- a/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/management/MockAccountManager.java
+++ b/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/management/MockAccountManager.java
@@ -134,14 +134,14 @@ public class MockAccountManager extends ManagerBase implements AccountManager {
     }
 
     @Override
-    public UserAccount createUserAccount(String arg0, String arg1, String arg2, String arg3, String arg4, String arg5, String arg6, short arg7, Long arg8, String arg9,
+    public UserAccount createUserAccount(String arg0, String arg1, String arg2, String arg3, String arg4, String arg5, String arg6, short arg7, Long roleId, Long arg8, String arg9,
         Map<String, String> arg10, String arg11, String arg12) {
         // TODO Auto-generated method stub
         return null;
     }
 
     @Override
-    public UserAccount createUserAccount(String userName, String password, String firstName, String lastName, String email, String timezone, String accountName, short accountType,
+    public UserAccount createUserAccount(String userName, String password, String firstName, String lastName, String email, String timezone, String accountName, short accountType, Long roleId,
                                          Long domainId, String networkDomain, Map<String, String> details, String accountUUID, String userUUID, User.Source source) {
         // TODO Auto-generated method stub
         return null;
@@ -392,8 +392,8 @@ public class MockAccountManager extends ManagerBase implements AccountManager {
     }
 
     @Override
-    public Account createAccount(String accountName, short accountType, Long domainId, String networkDomain, Map<String, String> details, String uuid) {
-        final AccountVO account = new AccountVO(accountName, domainId, networkDomain, accountType, uuid);
+    public Account createAccount(String accountName, short accountType, Long roleId, Long domainId, String networkDomain, Map<String, String> details, String uuid) {
+        final AccountVO account = new AccountVO(accountName, domainId, networkDomain, accountType, roleId, uuid);
         Transaction.execute(new TransactionCallbackNoReturn() {
             @Override
             public void doInTransactionWithoutResult(TransactionStatus status) {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4347776a/plugins/pom.xml
----------------------------------------------------------------------
diff --git a/plugins/pom.xml b/plugins/pom.xml
index bafe299..1a25819 100755
--- a/plugins/pom.xml
+++ b/plugins/pom.xml
@@ -50,6 +50,7 @@
     <module>api/solidfire-intg-test</module>
     <module>api/discovery</module>
     <module>acl/static-role-based</module>
+    <module>acl/dynamic-role-based</module>
     <module>affinity-group-processors/host-anti-affinity</module>
     <module>affinity-group-processors/explicit-dedication</module>
     <module>deployment-planners/user-concentrated-pod</module>