azkaban-aplcache

Disable the project upload functionality if the user doesn't

7/19/2017 1:05:11 PM

Details

diff --git a/azkaban-common/src/main/java/azkaban/user/Permission.java b/azkaban-common/src/main/java/azkaban/user/Permission.java
index 834a2c1..9fa44b8 100644
--- a/azkaban-common/src/main/java/azkaban/user/Permission.java
+++ b/azkaban-common/src/main/java/azkaban/user/Permission.java
@@ -191,6 +191,9 @@ public class Permission {
     SCHEDULE(0x0000008),
     METRICS(0x0000010),
     CREATEPROJECTS(0x40000000), // Only used for roles
+    // Users with this permission can upload projects when the property "lockdown.upload.projects"
+    // is turned on
+    UPLOADPROJECTS(0x0008000),
     ADMIN(0x8000000);
 
     private final int numVal;
diff --git a/azkaban-common/src/main/java/azkaban/user/UserUtils.java b/azkaban-common/src/main/java/azkaban/user/UserUtils.java
new file mode 100644
index 0000000..687bcda
--- /dev/null
+++ b/azkaban-common/src/main/java/azkaban/user/UserUtils.java
@@ -0,0 +1,24 @@
+package azkaban.user;
+
+public final class UserUtils {
+  private UserUtils() {
+
+  }
+
+  /**
+   * @return - Returns true if the given user is an ADMIN, or if user has the required permission
+   * for the action requested.
+   */
+  public static boolean hasPermissionforAction(final UserManager userManager, final User user,
+      final Permission.Type type) {
+    for (final String roleName : user.getRoles()) {
+      final Role role = userManager.getRole(roleName);
+      final Permission perm = role.getPermission();
+      if (perm.isPermissionSet(Permission.Type.ADMIN) || perm.isPermissionSet(type)) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+}
diff --git a/azkaban-common/src/test/java/azkaban/user/PermissionTest.java b/azkaban-common/src/test/java/azkaban/user/PermissionTest.java
index 07497b3..0ee4cab 100644
--- a/azkaban-common/src/test/java/azkaban/user/PermissionTest.java
+++ b/azkaban-common/src/test/java/azkaban/user/PermissionTest.java
@@ -16,7 +16,7 @@
 
 package azkaban.user;
 
-import static org.junit.Assert.assertTrue;
+import static org.assertj.core.api.Assertions.assertThat;
 
 import azkaban.user.Permission.Type;
 import org.junit.After;
@@ -47,7 +47,7 @@ public class PermissionTest {
     final Permission perm2 = new Permission();
     perm2.addPermission(Type.READ);
     info("Compare " + perm1.toString() + " and " + perm2.toString());
-    assertTrue(perm1.equals(perm2));
+    assertThat(perm1.equals(perm2)).isTrue();
   }
 
   @Test
@@ -58,7 +58,7 @@ public class PermissionTest {
     final Permission perm2 = new Permission();
     perm2.addPermission(new Type[]{Type.EXECUTE, Type.READ});
     info("Compare " + perm1.toString() + " and " + perm2.toString());
-    assertTrue(perm1.equals(perm2));
+    assertThat(perm1.equals(perm2)).isTrue();
   }
 
   @Test
@@ -70,7 +70,7 @@ public class PermissionTest {
     final Permission perm2 = new Permission();
     perm2.addPermission(new Type[]{Type.READ, Type.WRITE});
     info("Compare " + perm1.toString() + " and " + perm2.toString());
-    assertTrue(perm1.equals(perm2));
+    assertThat(perm1.equals(perm2)).isTrue();
   }
 
   @Test
@@ -82,7 +82,7 @@ public class PermissionTest {
     final Permission perm2 = new Permission();
     perm2.addPermission(new Type[]{Type.READ, Type.WRITE});
     info("Compare " + perm1.toString() + " and " + perm2.toString());
-    assertTrue(perm1.equals(perm2));
+    assertThat(perm1.equals(perm2)).isTrue();
   }
 
   @Test
@@ -94,7 +94,7 @@ public class PermissionTest {
     final String[] array = permission.toStringArray();
     final Permission permission2 = new Permission();
     permission2.addPermissionsByName(array);
-    assertTrue(permission.equals(permission2));
+    assertThat(permission.equals(permission2)).isTrue();
   }
 
   @Test
@@ -105,10 +105,32 @@ public class PermissionTest {
     final int flags = permission.toFlags();
     final Permission permission2 = new Permission(flags);
 
-    assertTrue(permission2.isPermissionSet(Type.READ));
-    assertTrue(permission2.isPermissionSet(Type.WRITE));
+    assertThat(permission2.isPermissionSet(Type.READ)).isTrue();
+    assertThat(permission2.isPermissionSet(Type.WRITE)).isTrue();
 
-    assertTrue(permission.equals(permission2));
+    assertThat(permission.equals(permission2)).isTrue();
+  }
+
+  /**
+   * Verify that the binary bit for UPLOADPROJECTS is not turned on
+   * by setting the other permissions.
+   */
+  @Test
+  public void testUploadProjectFlag() throws Exception {
+    final Permission permission = new Permission();
+    permission.addPermission(new Type[]{Type.UPLOADPROJECTS});
+
+    final int flags = permission.toFlags();
+    final Permission permission2 = new Permission(flags);
+    assertThat(permission2.isPermissionSet(Type.UPLOADPROJECTS)).isTrue();
+    assertThat(permission.equals(permission2)).isTrue();
+
+    permission.removePermissions(new Type[]{Type.UPLOADPROJECTS});
+    final Type[] allPermissions = new Type[]{
+        Type.READ, Type.WRITE, Type.EXECUTE, Type.METRICS, Type.SCHEDULE, Type.CREATEPROJECTS
+    };
+    permission.addPermission(allPermissions);
+    assertThat(permission.isPermissionSet(Type.UPLOADPROJECTS)).isFalse();
   }
 
   /**
diff --git a/azkaban-common/src/test/java/azkaban/user/UserUtilsTest.java b/azkaban-common/src/test/java/azkaban/user/UserUtilsTest.java
new file mode 100644
index 0000000..c087a88
--- /dev/null
+++ b/azkaban-common/src/test/java/azkaban/user/UserUtilsTest.java
@@ -0,0 +1,31 @@
+package azkaban.user;
+
+import static azkaban.user.Permission.Type.UPLOADPROJECTS;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import azkaban.utils.TestUtils;
+import org.junit.Test;
+
+
+public class UserUtilsTest {
+  @Test
+  public void testAdminUserCanUploadProject() throws UserManagerException {
+    final UserManager userManager = TestUtils.createTestXmlUserManager();
+    final User testAdmin = userManager.getUser("testAdmin", "testAdmin");
+    assertThat(UserUtils.hasPermissionforAction(userManager, testAdmin, UPLOADPROJECTS)).isTrue();
+  }
+
+  @Test
+  public void testRegularUserCantUploadProject() {
+    final UserManager userManager = TestUtils.createTestXmlUserManager();
+    final User user = TestUtils.getTestUser();
+    assertThat(UserUtils.hasPermissionforAction(userManager, user, UPLOADPROJECTS)).isFalse();
+  }
+
+  @Test
+  public void testUserWithPermissionsCanUploadProject() throws UserManagerException {
+    final UserManager userManager = TestUtils.createTestXmlUserManager();
+    final User testUpload = userManager.getUser("testUpload", "testUpload");
+    assertThat(UserUtils.hasPermissionforAction(userManager, testUpload, UPLOADPROJECTS)).isTrue();
+  }
+}
diff --git a/azkaban-web-server/src/main/java/azkaban/webapp/servlet/PageUtils.java b/azkaban-web-server/src/main/java/azkaban/webapp/servlet/PageUtils.java
new file mode 100644
index 0000000..f3a7f51
--- /dev/null
+++ b/azkaban-web-server/src/main/java/azkaban/webapp/servlet/PageUtils.java
@@ -0,0 +1,30 @@
+package azkaban.webapp.servlet;
+
+import azkaban.server.session.Session;
+import azkaban.user.Permission;
+import azkaban.user.User;
+import azkaban.user.UserManager;
+import azkaban.user.UserUtils;
+
+
+public final class PageUtils {
+
+  private PageUtils() {
+
+  }
+
+  /**
+   * Method hides the upload button for regular users from relevant pages when the property
+   * "lockdown.upload.projects" is set. The button is displayed for admin users and users with
+   * upload permissions.
+   */
+  public static void hideUploadButtonWhenNeeded(final Page page, final Session session, final UserManager userManager,
+      final Boolean lockdownUploadProjects) {
+    final User user = session.getUser();
+
+    if (lockdownUploadProjects && !UserUtils.hasPermissionforAction(userManager, user,
+        Permission.Type.UPLOADPROJECTS)) {
+      page.add("hideUploadProject", true);
+    }
+  }
+}
diff --git a/azkaban-web-server/src/main/java/azkaban/webapp/servlet/ProjectManagerServlet.java b/azkaban-web-server/src/main/java/azkaban/webapp/servlet/ProjectManagerServlet.java
index 9e7106f..4e05945 100644
--- a/azkaban-web-server/src/main/java/azkaban/webapp/servlet/ProjectManagerServlet.java
+++ b/azkaban-web-server/src/main/java/azkaban/webapp/servlet/ProjectManagerServlet.java
@@ -42,6 +42,7 @@ import azkaban.user.Permission.Type;
 import azkaban.user.Role;
 import azkaban.user.User;
 import azkaban.user.UserManager;
+import azkaban.user.UserUtils;
 import azkaban.utils.JSONUtils;
 import azkaban.utils.Pair;
 import azkaban.utils.Props;
@@ -1077,6 +1078,8 @@ public class ProjectManagerServlet extends LoginAbstractAzkabanServlet {
     final String projectName = getParam(req, "project");
 
     final User user = session.getUser();
+    PageUtils
+        .hideUploadButtonWhenNeeded(page, session, this.userManager, this.lockdownUploadProjects);
     Project project = null;
     try {
       project = this.projectManager.getProject(projectName);
@@ -1234,7 +1237,8 @@ public class ProjectManagerServlet extends LoginAbstractAzkabanServlet {
             "azkaban/webapp/servlet/velocity/permissionspage.vm");
     final String projectName = getParam(req, "project");
     final User user = session.getUser();
-
+    PageUtils
+        .hideUploadButtonWhenNeeded(page, session, this.userManager, this.lockdownUploadProjects);
     Project project = null;
     try {
       project = this.projectManager.getProject(projectName);
@@ -1538,6 +1542,8 @@ public class ProjectManagerServlet extends LoginAbstractAzkabanServlet {
     final String projectName = getParam(req, "project");
 
     final User user = session.getUser();
+    PageUtils
+        .hideUploadButtonWhenNeeded(page, session, this.userManager, this.lockdownUploadProjects);
     Project project = null;
     try {
       project = this.projectManager.getProject(projectName);
@@ -1606,7 +1612,8 @@ public class ProjectManagerServlet extends LoginAbstractAzkabanServlet {
     String message = null;
     HashMap<String, Object> params = null;
 
-    if (this.lockdownCreateProjects && !hasPermissionToCreateProject(user)) {
+    if (this.lockdownCreateProjects &&
+        !UserUtils.hasPermissionforAction(this.userManager, user, Type.CREATEPROJECTS)) {
       message =
           "User " + user.getUserId()
               + " doesn't have permission to create projects.";
@@ -1655,8 +1662,13 @@ public class ProjectManagerServlet extends LoginAbstractAzkabanServlet {
       props.put(ValidatorConfigs.CUSTOM_AUTO_FIX_FLAG_PARAM, "true");
     }
 
-    if (this.lockdownUploadProjects) {
-      registerError(ret, "Project uploading is locked out", resp, 400);
+    if (this.lockdownUploadProjects && !UserUtils
+        .hasPermissionforAction(this.userManager, user, Type.UPLOADPROJECTS)) {
+      final String message =
+          "Project uploading is locked out. Only admin users and users with special permissions can upload projects. "
+              + "User " + user.getUserId() + " doesn't have permission to upload project.";
+      logger.info(message);
+      registerError(ret, message, resp, 403);
     } else if (projectName == null || projectName.isEmpty()) {
       registerError(ret, "No project name found.", resp, 400);
     } else if (project == null) {
@@ -1796,19 +1808,6 @@ public class ProjectManagerServlet extends LoginAbstractAzkabanServlet {
     return perm;
   }
 
-  private boolean hasPermissionToCreateProject(final User user) {
-    for (final String roleName : user.getRoles()) {
-      final Role role = this.userManager.getRole(roleName);
-      final Permission perm = role.getPermission();
-      if (perm.isPermissionSet(Permission.Type.ADMIN)
-          || perm.isPermissionSet(Permission.Type.CREATEPROJECTS)) {
-        return true;
-      }
-    }
-
-    return false;
-  }
-
   private void handleReloadProjectWhitelist(final HttpServletRequest req,
       final HttpServletResponse resp, final Session session) throws IOException {
     final HashMap<String, Object> ret = new HashMap<>();
diff --git a/azkaban-web-server/src/main/java/azkaban/webapp/servlet/ProjectServlet.java b/azkaban-web-server/src/main/java/azkaban/webapp/servlet/ProjectServlet.java
index 5e01bb3..76cb901 100644
--- a/azkaban-web-server/src/main/java/azkaban/webapp/servlet/ProjectServlet.java
+++ b/azkaban-web-server/src/main/java/azkaban/webapp/servlet/ProjectServlet.java
@@ -20,9 +20,9 @@ import azkaban.project.Project;
 import azkaban.project.ProjectManager;
 import azkaban.server.session.Session;
 import azkaban.user.Permission;
-import azkaban.user.Role;
 import azkaban.user.User;
 import azkaban.user.UserManager;
+import azkaban.user.UserUtils;
 import azkaban.utils.Pair;
 import azkaban.webapp.AzkabanWebServer;
 import java.io.IOException;
@@ -160,7 +160,8 @@ public class ProjectServlet extends LoginAbstractAzkabanServlet {
     final Page page =
         newPage(req, resp, session, "azkaban/webapp/servlet/velocity/index.vm");
 
-    if (this.lockdownCreateProjects && !hasPermissionToCreateProject(user)) {
+    if (this.lockdownCreateProjects &&
+        !UserUtils.hasPermissionforAction(this.userManager, user, Permission.Type.CREATEPROJECTS)) {
       page.add("hideCreateProject", true);
     }
 
@@ -220,19 +221,6 @@ public class ProjectServlet extends LoginAbstractAzkabanServlet {
     // TODO Auto-generated method stub
   }
 
-  private boolean hasPermissionToCreateProject(final User user) {
-    for (final String roleName : user.getRoles()) {
-      final Role role = this.userManager.getRole(roleName);
-      final Permission perm = role.getPermission();
-      if (perm.isPermissionSet(Permission.Type.ADMIN)
-          || perm.isPermissionSet(Permission.Type.CREATEPROJECTS)) {
-        return true;
-      }
-    }
-
-    return false;
-  }
-
   /**
    * This class is used to represent a simplified project, which can be returned
    * to end users via REST API. This is done in consideration that the API
diff --git a/azkaban-web-server/src/main/resources/azkaban/webapp/servlet/velocity/projectpageheader.vm b/azkaban-web-server/src/main/resources/azkaban/webapp/servlet/velocity/projectpageheader.vm
index ba9bf38..fe2636d 100644
--- a/azkaban-web-server/src/main/resources/azkaban/webapp/servlet/velocity/projectpageheader.vm
+++ b/azkaban-web-server/src/main/resources/azkaban/webapp/servlet/velocity/projectpageheader.vm
@@ -25,9 +25,11 @@
               <button id="project-delete-btn" class="btn btn-sm btn-danger">
                 <span class="glyphicon glyphicon-trash"></span> Delete Project
               </button>
+#if (!$hideUploadProject)
               <button id="project-upload-btn" class="btn btn-sm btn-primary">
                 <span class="glyphicon glyphicon-upload"></span> Upload
               </button>
+#end
               <a class="btn btn-sm btn-info" href="${context}/manager?project=${project.name}&download=true">
                 <span class="glyphicon glyphicon-download"></span> Download
               </a>
diff --git a/azkaban-web-server/src/test/java/azkaban/fixture/VelocityTemplateTestUtil.java b/azkaban-web-server/src/test/java/azkaban/fixture/VelocityTemplateTestUtil.java
index 3a95cf5..afd8465 100644
--- a/azkaban-web-server/src/test/java/azkaban/fixture/VelocityTemplateTestUtil.java
+++ b/azkaban-web-server/src/test/java/azkaban/fixture/VelocityTemplateTestUtil.java
@@ -6,7 +6,7 @@ import org.apache.velocity.app.VelocityEngine;
 
 
 /**
- * Test utility to render a template.
+ * Test utility to render a template and other helper methods.
  */
 public class VelocityTemplateTestUtil {
 
@@ -27,4 +27,17 @@ public class VelocityTemplateTestUtil {
     engine.mergeTemplate(TEMPLATE_BASE_DIR + templateName + ".vm", "UTF-8", context, stringWriter);
     return stringWriter.getBuffer().toString();
   }
+
+  /**
+   *
+   * @param source the rendered template as a String
+   * @param target the String fragment within the template
+   * @return - boolean
+   */
+  public static boolean ignoreCaseContains(final String source, final String target) {
+    final String sourceNoSpace = source.replaceAll("\\s+", "");
+    final String targetNoSpace = target.replaceAll("\\s+", "");
+    return sourceNoSpace.contains(targetNoSpace);
+  }
+
 }
diff --git a/azkaban-web-server/src/test/java/azkaban/webapp/servlet/ExecutionFlowViewTest.java b/azkaban-web-server/src/test/java/azkaban/webapp/servlet/ExecutionFlowViewTest.java
index 7717717..2771c33 100644
--- a/azkaban-web-server/src/test/java/azkaban/webapp/servlet/ExecutionFlowViewTest.java
+++ b/azkaban-web-server/src/test/java/azkaban/webapp/servlet/ExecutionFlowViewTest.java
@@ -17,12 +17,6 @@ public class ExecutionFlowViewTest {
           + "class=\"btn btn-info btn-sm\" type=\"button\" target=\"_blank\" "
           + "title=\"Analyze job in Dr. Elephant\">Dr. Elephant</a></li>";
 
-  private static boolean ignoreCaseContains(final String source, final String target) {
-    final String sourceNoSpace = source.replaceAll("\\s+", "");
-    final String targetNoSpace = target.replaceAll("\\s+", "");
-    return sourceNoSpace.contains(targetNoSpace);
-  }
-
   /**
    * Test aims to check that the external analyzer button is displayed
    * in the page.
@@ -42,6 +36,7 @@ public class ExecutionFlowViewTest {
 
     final String result =
         VelocityTemplateTestUtil.renderTemplate("executingflowpage", context);
-    assertTrue(ignoreCaseContains(result, EXTERNAL_ANALYZER_ELEMENT));
+    assertTrue(VelocityTemplateTestUtil.
+        ignoreCaseContains(result, EXTERNAL_ANALYZER_ELEMENT));
   }
 }
diff --git a/azkaban-web-server/src/test/java/azkaban/webapp/servlet/PageUtilsTest.java b/azkaban-web-server/src/test/java/azkaban/webapp/servlet/PageUtilsTest.java
new file mode 100644
index 0000000..94c1f5b
--- /dev/null
+++ b/azkaban-web-server/src/test/java/azkaban/webapp/servlet/PageUtilsTest.java
@@ -0,0 +1,88 @@
+package azkaban.webapp.servlet;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+import azkaban.server.session.Session;
+import azkaban.user.User;
+import azkaban.user.UserManager;
+import azkaban.user.UserManagerException;
+import azkaban.utils.TestUtils;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.junit.Test;
+
+
+public class PageUtilsTest {
+  @Test
+  public void testUploadButtonisHiddenWhenGlobalPropertyIsSet()
+      throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException,
+             ClassNotFoundException, InstantiationException, IOException {
+    final HttpServletRequest httpServletRequest = mock(HttpServletRequest.class);
+    final HttpServletResponse httpServletResponse = mock(HttpServletResponse.class);
+    final User user = TestUtils.getTestUser();
+    final Session session = new Session("fake-session-id", user, "127.0.0.1");
+
+    final VelocityEngine velocityEngine = new VelocityEngine();
+    velocityEngine.init("src/test/resources/velocity.properties");
+    final Page page = new Page(httpServletRequest, httpServletResponse, velocityEngine,
+        "azkaban/webapp/servlet/velocity/permissionspage.vm");
+
+    final UserManager userManager = TestUtils.createTestXmlUserManager();
+    PageUtils.hideUploadButtonWhenNeeded(page, session, userManager, true);
+
+    final Field velocityContextField = Page.class.getDeclaredField("context");
+    velocityContextField.setAccessible(true);
+    final VelocityContext velocityContext = (VelocityContext) velocityContextField.get(page);
+    assertThat((boolean) velocityContext.get("hideUploadProject")).isTrue();
+  }
+
+  @Test
+  public void testUploadButtonIsVisibleToAllWhenGlobalPropertyIsNotSet()
+      throws NoSuchFieldException, IllegalAccessException {
+    final HttpServletRequest httpServletRequest = mock(HttpServletRequest.class);
+    final HttpServletResponse httpServletResponse = mock(HttpServletResponse.class);
+    final User user = TestUtils.getTestUser();
+    final Session session = new Session("fake-session-id", user, "127.0.0.1");
+
+    final VelocityEngine velocityEngine = new VelocityEngine();
+    velocityEngine.init("src/test/resources/velocity.properties");
+    final Page page = new Page(httpServletRequest, httpServletResponse, velocityEngine,
+        "azkaban/webapp/servlet/velocity/permissionspage.vm");
+
+    final UserManager userManager = TestUtils.createTestXmlUserManager();
+    PageUtils.hideUploadButtonWhenNeeded(page, session, userManager, false);
+
+    final Field velocityContextField = Page.class.getDeclaredField("context");
+    velocityContextField.setAccessible(true);
+    final VelocityContext velocityContext = (VelocityContext) velocityContextField.get(page);
+    assertThat(velocityContext.containsKey("hideUploadProject")).isFalse();
+  }
+
+  @Test
+  public void testUploadButtonIsEnabledForAdminUser()
+      throws NoSuchFieldException, IllegalAccessException, UserManagerException {
+    final HttpServletRequest httpServletRequest = mock(HttpServletRequest.class);
+    final HttpServletResponse httpServletResponse = mock(HttpServletResponse.class);
+    final UserManager userManager = TestUtils.createTestXmlUserManager();
+    final User testAdmin = userManager.getUser("testAdmin", "testAdmin");
+    final Session session = new Session("fake-session-id", testAdmin, "127.0.0.1");
+
+    final VelocityEngine velocityEngine = new VelocityEngine();
+    velocityEngine.init("src/test/resources/velocity.properties");
+    final Page page = new Page(httpServletRequest, httpServletResponse, velocityEngine,
+        "azkaban/webapp/servlet/velocity/permissionspage.vm");
+
+    PageUtils.hideUploadButtonWhenNeeded(page, session, userManager, false);
+
+    final Field velocityContextField = Page.class.getDeclaredField("context");
+    velocityContextField.setAccessible(true);
+    final VelocityContext velocityContext = (VelocityContext) velocityContextField.get(page);
+    assertThat(velocityContext.get("hideUploadProject")).isNull();
+  }
+}
diff --git a/azkaban-web-server/src/test/java/azkaban/webapp/servlet/ProjectPageHeaderTest.java b/azkaban-web-server/src/test/java/azkaban/webapp/servlet/ProjectPageHeaderTest.java
new file mode 100644
index 0000000..e4e8cbf
--- /dev/null
+++ b/azkaban-web-server/src/test/java/azkaban/webapp/servlet/ProjectPageHeaderTest.java
@@ -0,0 +1,37 @@
+package azkaban.webapp.servlet;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import azkaban.fixture.VelocityContextTestUtil;
+import azkaban.fixture.VelocityTemplateTestUtil;
+import org.apache.velocity.VelocityContext;
+import org.junit.Test;
+
+/**
+ * Test validates the enable/disable feature of the 'Upload' button
+ */
+public class ProjectPageHeaderTest {
+  private static final String UPLOAD_BUTTON
+      = "<button id=\"project-upload-btn\" class=\"btn btn-sm btn-primary\">"
+      + "<span class=\"glyphicon glyphicon-upload\"></span> Upload </button>";
+
+  @Test
+  public void testUploadButtonIsPresent() {
+    final VelocityContext context = VelocityContextTestUtil.getInstance();
+    context.put("hideUploadProject", false);
+
+    final String result =
+        VelocityTemplateTestUtil.renderTemplate("projectpageheader", context);
+    assertThat(VelocityTemplateTestUtil.ignoreCaseContains(result, UPLOAD_BUTTON)).isTrue();
+  }
+
+  @Test
+  public void testUploadButtonIsNotPresent() {
+    final VelocityContext context = VelocityContextTestUtil.getInstance();
+    context.put("hideUploadProject", true);
+
+    final String result =
+        VelocityTemplateTestUtil.renderTemplate("projectpageheader", context);
+    assertThat(VelocityTemplateTestUtil.ignoreCaseContains(result, UPLOAD_BUTTON)).isFalse();
+  }
+}
diff --git a/test/src/test/resources/azkaban/test/azkaban-users.xml b/test/src/test/resources/azkaban/test/azkaban-users.xml
index 75f2d0f..0aa6e34 100644
--- a/test/src/test/resources/azkaban/test/azkaban-users.xml
+++ b/test/src/test/resources/azkaban/test/azkaban-users.xml
@@ -1,5 +1,7 @@
 <azkaban-users>
   <user groups="azkaban" password="testAdmin" roles="admin" username="testAdmin"/>
+  <user password="testUpload" roles="upload" username="testUpload"/>
   <user password="testUser" username="testUser"/>
   <role name="admin" permissions="ADMIN"/>
+  <role name="upload" permissions="UPLOADPROJECTS"/>
 </azkaban-users>