azkaban-developers

Adding the ability to lockdown Create Projects except for those

4/4/2013 10:10:01 PM

Details

diff --git a/conf/azkaban.properties b/conf/azkaban.properties
index c222cfb..7884f10 100644
--- a/conf/azkaban.properties
+++ b/conf/azkaban.properties
@@ -45,3 +45,5 @@ mail.sender=
 mail.host=
 job.failure.email=
 job.success.email=
+
+lockdown.create.projects=false
\ No newline at end of file
diff --git a/src/java/azkaban/user/Permission.java b/src/java/azkaban/user/Permission.java
index 7f57290..9f26055 100644
--- a/src/java/azkaban/user/Permission.java
+++ b/src/java/azkaban/user/Permission.java
@@ -29,6 +29,7 @@ public class Permission {
 		WRITE(0x0000002),
 		EXECUTE(0x0000004),
 		SCHEDULE(0x0000008),
+		CREATEPROJECTS(0x40000000), // Only used for roles
 		ADMIN(0x8000000);
 		
 		private int numVal;
diff --git a/src/java/azkaban/user/XmlUserManager.java b/src/java/azkaban/user/XmlUserManager.java
index 7c26daf..042a081 100644
--- a/src/java/azkaban/user/XmlUserManager.java
+++ b/src/java/azkaban/user/XmlUserManager.java
@@ -20,6 +20,7 @@ import java.io.File;
 import java.io.IOException;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Set;
 
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
@@ -49,6 +50,7 @@ public class XmlUserManager implements UserManager {
 	public static final String AZKABAN_USERS_TAG = "azkaban-users";
 	public static final String USER_TAG = "user";
 	public static final String ROLE_TAG = "role";
+	public static final String GROUP_TAG = "group";
 	public static final String ROLENAME_ATTR = "name";
 	public static final String ROLEPERMISSIONS_ATTR = "permissions";
 	public static final String USERNAME_ATTR = "username";
@@ -56,12 +58,14 @@ public class XmlUserManager implements UserManager {
 	public static final String ROLES_ATTR = "roles";
 	public static final String PROXY_ATTR = "proxy";
 	public static final String GROUPS_ATTR = "groups";
-
+	public static final String GROUPNAME_ATTR = "name";
+	
 	private String xmlPath;
 
 	private HashMap<String, User> users;
 	private HashMap<String, String> userPassword;
 	private HashMap<String, Role> roles;
+	private HashMap<String, Set<String>> groupRoles;
 	private HashMap<String, HashSet<String>> proxyUserMap;
 
 	/**
@@ -84,8 +88,9 @@ public class XmlUserManager implements UserManager {
 		HashMap<String, User> users = new HashMap<String, User>();
 		HashMap<String, String> userPassword = new HashMap<String, String>();
 		HashMap<String, Role> roles = new HashMap<String, Role>();
+		HashMap<String, Set<String>> groupRoles = new HashMap<String, Set<String>>();
 		HashMap<String, HashSet<String>> proxyUserMap = new HashMap<String, HashSet<String>>();
-
+		
 		// Creating the document builder to parse xml.
 		DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory
 				.newInstance();
@@ -122,6 +127,9 @@ public class XmlUserManager implements UserManager {
 				else if (node.getNodeName().equals(ROLE_TAG)) {
 					parseRoleTag(node, roles);
 				}
+				else if (node.getNodeName().equals(GROUP_TAG)) {
+					parseGroupRoleTag(node, groupRoles);
+				}
 			}
 		}
 
@@ -131,6 +139,7 @@ public class XmlUserManager implements UserManager {
 			this.userPassword = userPassword;
 			this.roles = roles;
 			this.proxyUserMap = proxyUserMap;
+			this.groupRoles = groupRoles;
 		}
 	}
 
@@ -251,9 +260,47 @@ public class XmlUserManager implements UserManager {
 		if (user == null) {
 			throw new UserManagerException("Internal error: User not found.");
 		}
+
+		// Add all the roles the group has to the user
+		resolveGroupRoles(user);
 		return user;
 	}
 
+	private void resolveGroupRoles(User user) {
+		for (String group: user.getGroups()) {
+			Set<String> groupRoleSet = groupRoles.get(group);
+			if (groupRoleSet != null) {
+				for (String role: groupRoleSet) {
+					user.addRole(role);
+				}
+			}
+		}
+	}
+	
+	private void parseGroupRoleTag(Node node, HashMap<String, Set<String>> groupRoles) {
+		NamedNodeMap groupAttrMap = node.getAttributes();
+		Node groupNameAttr = groupAttrMap.getNamedItem(GROUPNAME_ATTR);
+		if (groupNameAttr == null) {
+			throw new RuntimeException(
+					"Error loading role. The role 'name' attribute doesn't exist");
+		}
+		
+		String groupName = groupNameAttr.getNodeValue();
+		Set<String> roleSet = new HashSet<String>();
+		
+		Node roles = groupAttrMap.getNamedItem(ROLES_ATTR);
+		if (roles != null) {
+			String value = roles.getNodeValue();
+			String[] roleSplit = value.split("\\s*,\\s*");
+			for (String role : roleSplit) {
+				roleSet.add(role);
+			}
+		}
+		
+		groupRoles.put( groupName, roleSet );
+		logger.info("Group roles " + groupName + " added.");
+	}
+	
 	@Override
 	public boolean validateUser(String username) {
 		return users.containsKey(username);
@@ -269,7 +316,7 @@ public class XmlUserManager implements UserManager {
 		// Return true. Validation should be added when groups are added to the xml.
 		return true;
 	}
-
+	
 	@Override
 	public boolean validateProxyUser(String proxyUser, User realUser) {
 		if(proxyUserMap.containsKey(realUser.getUserId()) && proxyUserMap.get(realUser.getUserId()).contains(proxyUser)) {
diff --git a/src/java/azkaban/webapp/AzkabanWebServer.java b/src/java/azkaban/webapp/AzkabanWebServer.java
index 7494878..1af216f 100644
--- a/src/java/azkaban/webapp/AzkabanWebServer.java
+++ b/src/java/azkaban/webapp/AzkabanWebServer.java
@@ -144,9 +144,6 @@ public class AzkabanWebServer implements AzkabanServer {
 	private MBeanServer mbeanServer;
 	private ArrayList<ObjectName> registeredMBeans = new ArrayList<ObjectName>();
 
-	
-	
-	
 	/**
 	 * Constructor usually called by tomcat AzkabanServletContext to create the
 	 * initial server
diff --git a/src/java/azkaban/webapp/servlet/IndexServlet.java b/src/java/azkaban/webapp/servlet/IndexServlet.java
index 895e766..cf94e93 100644
--- a/src/java/azkaban/webapp/servlet/IndexServlet.java
+++ b/src/java/azkaban/webapp/servlet/IndexServlet.java
@@ -19,15 +19,23 @@ package azkaban.webapp.servlet;
 import java.io.IOException;
 import java.util.List;
 
+import javax.servlet.ServletConfig;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.apache.log4j.Logger;
+
 //import org.apache.log4j.Logger;
 
+import azkaban.executor.ExecutorManager;
 import azkaban.project.Project;
 import azkaban.project.ProjectManager;
+import azkaban.scheduler.ScheduleManager;
+import azkaban.user.Permission;
+import azkaban.user.Role;
 import azkaban.user.User;
+import azkaban.user.UserManager;
 import azkaban.webapp.AzkabanWebServer;
 import azkaban.webapp.session.Session;
 
@@ -35,11 +43,27 @@ import azkaban.webapp.session.Session;
  * The main page
  */
 public class IndexServlet extends LoginAbstractAzkabanServlet {
-	// private static final Logger logger =
-	// Logger.getLogger(IndexServlet.class.getName());
-
+	private static final Logger logger = Logger.getLogger(IndexServlet.class.getName());
+	private static final String LOCKDOWN_CREATE_PROJECTS_KEY = "lockdown.create.projects";
 	private static final long serialVersionUID = -1;
 
+	private UserManager userManager;
+
+	private boolean lockdownCreateProjects = false;
+	
+	@Override
+	public void init(ServletConfig config) throws ServletException {
+		super.init(config);
+		
+		AzkabanWebServer server = (AzkabanWebServer)getApplication();
+
+		userManager = server.getUserManager();
+		lockdownCreateProjects = server.getServerProps().getBoolean(LOCKDOWN_CREATE_PROJECTS_KEY, false);
+		if (lockdownCreateProjects) {
+			logger.info("Creation of projects is locked down");
+		}
+	}
+	
 	@Override
 	protected void handleGet(HttpServletRequest req, HttpServletResponse resp, Session session) throws ServletException, IOException {
 		
@@ -58,6 +82,11 @@ public class IndexServlet extends LoginAbstractAzkabanServlet {
 
 		ProjectManager manager = ((AzkabanWebServer)getApplication()).getProjectManager();
 		Page page = newPage(req, resp, session, "azkaban/webapp/servlet/velocity/index.vm");
+		
+		if (lockdownCreateProjects && !hasPermissionToCreateProject(user)) {
+			page.add("hideCreateProject", true);
+		}
+		
 		if (hasParam(req, "all")) {
 			List<Project> projects = manager.getProjects();
 			page.add("allProjects", "true");
@@ -98,5 +127,15 @@ public class IndexServlet extends LoginAbstractAzkabanServlet {
 		
 	}
 
-
+	private boolean hasPermissionToCreateProject(User user) {
+		for(String roleName: user.getRoles()) {
+			Role role = userManager.getRole(roleName);
+			Permission perm = role.getPermission();
+			if (perm.isPermissionSet(Permission.Type.ADMIN) || perm.isPermissionSet(Permission.Type.CREATEPROJECTS)) {
+				return true;
+			}
+		}
+		
+		return false;
+	}
 }
diff --git a/src/java/azkaban/webapp/servlet/ProjectManagerServlet.java b/src/java/azkaban/webapp/servlet/ProjectManagerServlet.java
index fe3999c..30cb415 100644
--- a/src/java/azkaban/webapp/servlet/ProjectManagerServlet.java
+++ b/src/java/azkaban/webapp/servlet/ProjectManagerServlet.java
@@ -73,12 +73,15 @@ public class ProjectManagerServlet extends LoginAbstractAzkabanServlet {
 	private static final long serialVersionUID = 1;
 	private static final Logger logger = Logger.getLogger(ProjectManagerServlet.class);
 	private static final NodeLevelComparator NODE_LEVEL_COMPARATOR = new NodeLevelComparator();
+	private static final String LOCKDOWN_CREATE_PROJECTS_KEY = "lockdown.create.projects";
 	
 	private ProjectManager projectManager;
 	private ExecutorManager executorManager;
 	private ScheduleManager scheduleManager;
 	private UserManager userManager;
 
+	private boolean lockdownCreateProjects = false;
+	
 	private static Comparator<Flow> FLOW_ID_COMPARATOR = new Comparator<Flow>() {
 		@Override
 		public int compare(Flow f1, Flow f2) {
@@ -95,6 +98,10 @@ public class ProjectManagerServlet extends LoginAbstractAzkabanServlet {
 		executorManager = server.getExecutorManager();
 		scheduleManager = server.getScheduleManager();
 		userManager = server.getUserManager();
+		lockdownCreateProjects = server.getServerProps().getBoolean(LOCKDOWN_CREATE_PROJECTS_KEY, false);
+		if (lockdownCreateProjects) {
+			logger.info("Creation of projects is locked down");
+		}
 	}
 
 	@Override
@@ -137,7 +144,7 @@ public class ProjectManagerServlet extends LoginAbstractAzkabanServlet {
 		page.add("errorMsg", "No project set.");
 		page.render();
 	}
-
+	
 	@Override
 	protected void handleMultiformPost(HttpServletRequest req, HttpServletResponse resp, Map<String, Object> params, Session session) throws ServletException, IOException {
 		if (params.containsKey("action")) {
@@ -1177,23 +1184,30 @@ public class ProjectManagerServlet extends LoginAbstractAzkabanServlet {
 		logger.info("Create project " + projectName);
 		
 		User user = session.getUser();
-		
+			
 		String status = null;
 		String action = null;
 		String message = null;
 		HashMap<String, Object> params = null;
-		try {
-			projectManager.createProject(projectName, projectDescription, user);
-			status = "success";
-			action = "redirect";
-			String redirect = "manager?project=" + projectName;
-			params = new HashMap<String, Object>();
-			params.put("path", redirect);
-		} catch (ProjectManagerException e) {
-			message = e.getMessage();
+		
+		if (lockdownCreateProjects && !hasPermissionToCreateProject(user)) {
+			message = "User " + user.getUserId() + " doesn't have permission to create projects.";
+			logger.info(message);
 			status = "error";
 		}
-
+		else {
+			try {
+				projectManager.createProject(projectName, projectDescription, user);
+				status = "success";
+				action = "redirect";
+				String redirect = "manager?project=" + projectName;
+				params = new HashMap<String, Object>();
+				params.put("path", redirect);
+			} catch (ProjectManagerException e) {
+				message = e.getMessage();
+				status = "error";
+			}
+		}
 		String response = createJsonResponse(status, message, action, params);
 		try {
 			Writer write = resp.getWriter();
@@ -1321,4 +1335,16 @@ public class ProjectManagerServlet extends LoginAbstractAzkabanServlet {
 		
 		return perm;
 	}
+	
+	private boolean hasPermissionToCreateProject(User user) {
+		for(String roleName: user.getRoles()) {
+			Role role = userManager.getRole(roleName);
+			Permission perm = role.getPermission();
+			if (perm.isPermissionSet(Permission.Type.ADMIN) || perm.isPermissionSet(Permission.Type.CREATEPROJECTS)) {
+				return true;
+			}
+		}
+		
+		return false;
+	}
 }
diff --git a/src/java/azkaban/webapp/servlet/velocity/index.vm b/src/java/azkaban/webapp/servlet/velocity/index.vm
index dc32de9..d884bb7 100644
--- a/src/java/azkaban/webapp/servlet/velocity/index.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/index.vm
@@ -66,8 +66,10 @@
 #end			
 						<input id="searchtextbox" type="text" placeholder="project name containing ..." value=#if($search_term) ${search_term} #else "" #end class="search-input" name="searchterm">
 					</form>
-			
+
+#if (!$hideCreateProject)
 					<a id="create-project-btn" class="btn1 " href="#">Create Project</a>
+#end
 				</div><!-- end .section-hd -->
 			</div>
 			<table id="all-jobs" class="all-jobs job-table project-table">