/**
 * Unit test for {@link UserRoleDaoUserRoleListService}.
 *
 * @author mlowery
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
    locations = {
      "classpath:/repository.spring.xml",
      "classpath:/repository-test-override.spring.xml"
    })
@SuppressWarnings("nls")
public class UserRoleDaoUserRoleListServiceTest implements ApplicationContextAware {
  public static final int DEFAULT_ROLE_COUNT = 4;
  public static final int DEFAULT_USER_COUNT = 1; // admin
  public static final String MAIN_TENANT_1 = "maintenant1";
  public static final String MAIN_TENANT_2 = "maintenant2";

  public static final String PASSWORD_1 = "password1"; // $NON-NLS-1$
  public static final String PASSWORD_2 = "password2"; // $NON-NLS-1$
  public static final String PASSWORD_3 = "password3"; // $NON-NLS-1$
  public static final String PASSWORD_4 = "password4"; // $NON-NLS-1$
  public static final String PASSWORD_5 = "password5"; // $NON-NLS-1$
  public static final String PASSWORD_6 = "password6"; // $NON-NLS-1$
  public static final String PASSWORD_7 = "password7"; // $NON-NLS-1$
  public static final String PASSWORD_8 = "password8"; // $NON-NLS-1$
  public static final String PASSWORD_9 = "password9"; // $NON-NLS-1$
  public static final String PASSWORD_10 = "password10"; // $NON-NLS-1$
  public static final String PASSWORD_11 = "password11"; // $NON-NLS-1$
  public static final String PASSWORD_12 = "password12"; // $NON-NLS-1$
  public static final String PASSWORD_13 = "password13"; // $NON-NLS-1$
  public static final String PASSWORD_14 = "password14"; // $NON-NLS-1$

  public static final String USER_1 = "admin"; // $NON-NLS-1$
  public static final String USER_2 = "jim"; // $NON-NLS-1$
  public static final String USER_3 = "sally"; // $NON-NLS-1$
  public static final String USER_4 = "suzy"; // $NON-NLS-1$
  public static final String USER_5 = "nancy"; // $NON-NLS-1$
  public static final String USER_6 = "john"; // $NON-NLS-1$
  public static final String USER_7 = "jane"; // $NON-NLS-1$
  public static final String USER_8 = "jerry"; // $NON-NLS-1$
  public static final String USER_9 = "tom"; // $NON-NLS-1$
  public static final String USER_10 = "johny"; // $NON-NLS-1$
  public static final String USER_11 = "mary"; // $NON-NLS-1$
  public static final String USER_12 = "jill"; // $NON-NLS-1$
  public static final String USER_13 = "jack"; // $NON-NLS-1$
  public static final String USER_14 = "jeremy"; // $NON-NLS-1$

  public static final String UNKNOWN_USER = "******"; // $NON-NLS-1$

  public static final ITenant UNKNOWN_TENANT = new Tenant("unknownTenant", true); // $NON-NLS-1$

  public static final String ROLE_1 = "SalesMgr"; // $NON-NLS-1$
  public static final String ROLE_2 = "IT"; // $NON-NLS-1$

  public static final String ROLE_3 = "Sales"; // $NON-NLS-1$
  public static final String ROLE_4 = "Developer"; // $NON-NLS-1$
  public static final String ROLE_5 = "CEO"; // $NON-NLS-1$
  public static final String ROLE_6 = "Finance"; // $NON-NLS-1$
  public static final String ROLE_7 = "Marketing"; // $NON-NLS-1$
  public static final String ROLE_8 = "RegionalMgr"; // $NON-NLS-1$
  public static final String ROLE_9 = "CTO"; // $NON-NLS-1$
  public static final String ROLE_10 = "CFO"; // $NON-NLS-1$
  public static final String ROLE_11 = "CMO"; // $NON-NLS-1$
  public static final String ROLE_12 = "CIO"; // $NON-NLS-1$
  public static final String ROLE_13 = "COO"; // $NON-NLS-1$
  public static final String ROLE_14 = "CSO"; // $NON-NLS-1$

  public static final String UNKNOWN_ROLE = "unknownRole"; // $NON-NLS-1$

  public static final String USER_DESCRIPTION_1 = "User Description 1"; // $NON-NLS-1$
  public static final String USER_DESCRIPTION_2 = "User Description 2"; // $NON-NLS-1$
  public static final String USER_DESCRIPTION_3 = "User Description 3"; // $NON-NLS-1$
  public static final String USER_DESCRIPTION_4 = "User Description 4"; // $NON-NLS-1$
  public static final String USER_DESCRIPTION_5 = "User Description 5"; // $NON-NLS-1$
  public static final String USER_DESCRIPTION_6 = "User Description 6"; // $NON-NLS-1$
  public static final String USER_DESCRIPTION_7 = "User Description 7"; // $NON-NLS-1$
  public static final String USER_DESCRIPTION_8 = "User Description 8"; // $NON-NLS-1$
  public static final String USER_DESCRIPTION_9 = "User Description 9"; // $NON-NLS-1$
  public static final String USER_DESCRIPTION_10 = "User Description 10"; // $NON-NLS-1$
  public static final String USER_DESCRIPTION_11 = "User Description 11"; // $NON-NLS-1$
  public static final String USER_DESCRIPTION_12 = "User Description 12"; // $NON-NLS-1$
  public static final String USER_DESCRIPTION_13 = "User Description 13"; // $NON-NLS-1$
  public static final String USER_DESCRIPTION_14 = "User Description 14"; // $NON-NLS-1$

  public static final String ROLE_DESCRIPTION_1 = "Role Description 1"; // $NON-NLS-1$
  public static final String ROLE_DESCRIPTION_2 = "Role Description 2"; // $NON-NLS-1$
  public static final String ROLE_DESCRIPTION_3 = "Role Description 3"; // $NON-NLS-1$
  public static final String ROLE_DESCRIPTION_4 = "Role Description 4"; // $NON-NLS-1$
  public static final String ROLE_DESCRIPTION_5 = "Role Description 5"; // $NON-NLS-1$
  public static final String ROLE_DESCRIPTION_6 = "Role Description 6"; // $NON-NLS-1$
  public static final String ROLE_DESCRIPTION_7 = "Role Description 7"; // $NON-NLS-1$
  public static final String ROLE_DESCRIPTION_8 = "Role Description 8"; // $NON-NLS-1$

  public static final String ROLE_DESCRIPTION_9 = "Role Description 9"; // $NON-NLS-1$
  public static final String ROLE_DESCRIPTION_10 = "Role Description 10"; // $NON-NLS-1$
  public static final String ROLE_DESCRIPTION_11 = "Role Description 11"; // $NON-NLS-1$
  public static final String ROLE_DESCRIPTION_12 = "Role Description 12"; // $NON-NLS-1$
  public static final String ROLE_DESCRIPTION_13 = "Role Description 13"; // $NON-NLS-1$
  public static final String ROLE_DESCRIPTION_14 = "Role Description 14"; // $NON-NLS-1$

  NameFactory NF = NameFactoryImpl.getInstance();
  Name P_PRINCIPAL_NAME = NF.create(Name.NS_REP_URI, "principalName"); // $NON-NLS-1$
  private boolean startupCalled;
  String pPrincipalName;
  IUserRoleDao userRoleDao;
  private ITenantManager tenantManager;
  private String repositoryAdminUsername;
  private String tenantAdminAuthorityName;
  private String tenantAuthenticatedAuthorityName;
  private String sysAdminAuthorityName;
  private String sysAdminUserName;
  private JcrTemplate testJcrTemplate;
  private IBackingRepositoryLifecycleManager manager;
  private IAuthorizationPolicy authorizationPolicy;
  private MicroPlatform mp;
  private IRepositoryFileDao repositoryFileDao;
  private IUnifiedRepository repo;
  private Repository repository = null;
  private ITenant systemTenant = null;
  private ITenantedPrincipleNameResolver tenantedRoleNameUtils;
  private ITenantedPrincipleNameResolver tenantedUserNameUtils;
  private IRoleAuthorizationPolicyRoleBindingDao roleAuthorizationPolicyRoleBindingDao;

  @BeforeClass
  public static void setUpClass() throws Exception {
    // folder cannot be deleted at teardown shutdown hooks have not yet necessarily completed
    // parent folder must match jcrRepository.homeDir bean property in
    // repository-test-override.spring.xml
    FileUtils.deleteDirectory(new File("/tmp/jackrabbit-test-TRUNK"));
    PentahoSessionHolder.setStrategyName(PentahoSessionHolder.MODE_GLOBAL);
  }

  @AfterClass
  public static void tearDownClass() throws Exception {
    PentahoSessionHolder.setStrategyName(PentahoSessionHolder.MODE_INHERITABLETHREADLOCAL);
  }

  @Before
  public void setUp() throws Exception {
    mp = new MicroPlatform();
    // used by DefaultPentahoJackrabbitAccessControlHelper
    mp.defineInstance("tenantedUserNameUtils", tenantedUserNameUtils);
    mp.defineInstance("tenantedRoleNameUtils", tenantedRoleNameUtils);
    mp.defineInstance(IAuthorizationPolicy.class, authorizationPolicy);
    mp.defineInstance(ITenantManager.class, tenantManager);
    mp.define(ITenant.class, Tenant.class);
    mp.defineInstance(
        "roleAuthorizationPolicyRoleBindingDaoTarget", roleAuthorizationPolicyRoleBindingDao);
    mp.defineInstance("repositoryAdminUsername", repositoryAdminUsername);

    // Start the micro-platform
    mp.start();
    logout();
    startupCalled = true;
  }

  private void cleanupUserAndRoles(String userName, ITenant tenant) {
    login(
        userName,
        tenant,
        new String[] {tenantAdminAuthorityName, tenantAuthenticatedAuthorityName});
    for (IPentahoRole role : userRoleDao.getRoles()) {
      userRoleDao.deleteRole(role);
    }
    for (IPentahoUser user : userRoleDao.getUsers()) {
      userRoleDao.deleteUser(user);
    }
    logout();
  }

  /* private void deleteUserRoleAndTenant(ITenant parentTenant, List<ITenant> tenants) {
    try {
      if(tenants != null && tenants.size() > 0) {
        for(ITenant tenant: tenants) {
          login("admin", tenant, true);
          for(IPentahoRole role:userRoleDao.getRoles())  {
            userRoleDao.deleteRole(role);
          }
          for(IPentahoUser user:userRoleDao.getUsers())  {
            userRoleDao.deleteUser(user);
          }
          deleteUserRoleAndTenant(tenant, tenantManager.getChildTenants(tenant));
          logout();
        }
      } else {
        tenantManager.deleteTenant(parentTenant);
      }
    } catch (Throwable e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } //$NON-NLS-1$ //$NON-NLS-2$
  }*/
  @After
  public void tearDown() throws Exception {
    // Deleting all user and roles and tenant
    // null out fields to get back memory
    authorizationPolicy = null;
    loginAsRepositoryAdmin();
    SimpleJcrTestUtils.deleteItem(
        testJcrTemplate, ServerRepositoryPaths.getPentahoRootFolderPath());
    logout();
    repositoryAdminUsername = null;
    sysAdminAuthorityName = null;
    sysAdminUserName = null;
    tenantAdminAuthorityName = null;
    tenantAuthenticatedAuthorityName = null;
    authorizationPolicy = null;
    testJcrTemplate = null;
    if (startupCalled) {
      manager.shutdown();
    }

    // null out fields to get back memory
    repo = null;
    tenantManager = null;
  }

  protected void loginAsRepositoryAdmin() {
    StandaloneSession pentahoSession = new StandaloneSession(repositoryAdminUsername);
    pentahoSession.setAuthenticated(repositoryAdminUsername);
    final GrantedAuthority[] repositoryAdminAuthorities =
        new GrantedAuthority[] {new GrantedAuthorityImpl(sysAdminAuthorityName)};
    final String password = "******";
    UserDetails repositoryAdminUserDetails =
        new User(
            repositoryAdminUsername, password, true, true, true, true, repositoryAdminAuthorities);
    Authentication repositoryAdminAuthentication =
        new UsernamePasswordAuthenticationToken(
            repositoryAdminUserDetails, password, repositoryAdminAuthorities);
    PentahoSessionHolder.setSession(pentahoSession);
    // this line necessary for Spring Security's MethodSecurityInterceptor
    SecurityContextHolder.getContext().setAuthentication(repositoryAdminAuthentication);
  }

  protected void logout() {
    PentahoSessionHolder.removeSession();
    SecurityContextHolder.getContext().setAuthentication(null);
  }

  protected void login(final String username, final ITenant tenant, String[] roles) {
    StandaloneSession pentahoSession = new StandaloneSession(username);
    pentahoSession.setAuthenticated(tenant.getId(), username);
    PentahoSessionHolder.setSession(pentahoSession);
    pentahoSession.setAttribute(IPentahoSession.TENANT_ID_KEY, tenant.getId());
    final String password = "******";

    List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>();

    for (String roleName : roles) {
      authList.add(new GrantedAuthorityImpl(roleName));
    }
    GrantedAuthority[] authorities = authList.toArray(new GrantedAuthority[0]);
    UserDetails userDetails = new User(username, password, true, true, true, true, authorities);
    Authentication auth =
        new UsernamePasswordAuthenticationToken(userDetails, password, authorities);
    PentahoSessionHolder.setSession(pentahoSession);
    // this line necessary for Spring Security's MethodSecurityInterceptor
    SecurityContextHolder.getContext().setAuthentication(auth);
  }

  @Override
  public void setApplicationContext(final ApplicationContext applicationContext)
      throws BeansException {
    manager =
        (IBackingRepositoryLifecycleManager)
            applicationContext.getBean("backingRepositoryLifecycleManager");
    SessionFactory jcrSessionFactory =
        (SessionFactory) applicationContext.getBean("jcrSessionFactory");
    testJcrTemplate = new JcrTemplate(jcrSessionFactory);
    testJcrTemplate.setAllowCreate(true);
    testJcrTemplate.setExposeNativeSession(true);
    repositoryAdminUsername = (String) applicationContext.getBean("repositoryAdminUsername");
    tenantAuthenticatedAuthorityName =
        (String) applicationContext.getBean("singleTenantAuthenticatedAuthorityName");
    tenantAdminAuthorityName =
        (String) applicationContext.getBean("singleTenantAdminAuthorityName");
    sysAdminAuthorityName = (String) applicationContext.getBean("superAdminAuthorityName");
    sysAdminUserName = (String) applicationContext.getBean("superAdminUserName");
    authorizationPolicy = (IAuthorizationPolicy) applicationContext.getBean("authorizationPolicy");
    tenantManager = (ITenantManager) applicationContext.getBean("tenantMgrTxn");
    repositoryFileDao = (IRepositoryFileDao) applicationContext.getBean("repositoryFileDao");
    userRoleDao = (IUserRoleDao) applicationContext.getBean("userRoleDaoTxn");
    TestPrincipalProvider.userRoleDao = userRoleDao;
    repo = (IUnifiedRepository) applicationContext.getBean("unifiedRepository");
    repository = (Repository) applicationContext.getBean("jcrRepository");
    tenantedUserNameUtils =
        (ITenantedPrincipleNameResolver) applicationContext.getBean("tenantedUserNameUtils");
    tenantedRoleNameUtils =
        (ITenantedPrincipleNameResolver) applicationContext.getBean("tenantedRoleNameUtils");
    roleAuthorizationPolicyRoleBindingDao =
        (IRoleAuthorizationPolicyRoleBindingDao)
            applicationContext.getBean("roleAuthorizationPolicyRoleBindingDaoTarget");
  }

  @Test
  public void testGetAllAuthorities() {
    loginAsRepositoryAdmin();
    ITenant systemTenant =
        tenantManager.createTenant(
            null,
            ServerRepositoryPaths.getPentahoRootFolderName(),
            tenantAdminAuthorityName,
            tenantAuthenticatedAuthorityName,
            "Anonymous");
    userRoleDao.createUser(
        systemTenant, sysAdminUserName, "password", "", new String[] {tenantAdminAuthorityName});
    login(
        sysAdminUserName,
        systemTenant,
        new String[] {tenantAdminAuthorityName, tenantAuthenticatedAuthorityName});
    ITenant mainTenant_1 =
        tenantManager.createTenant(
            systemTenant,
            MAIN_TENANT_1,
            tenantAdminAuthorityName,
            tenantAuthenticatedAuthorityName,
            "Anonymous");
    userRoleDao.createUser(
        mainTenant_1, "admin", "password", "", new String[] {tenantAdminAuthorityName});
    ITenant mainTenant_2 =
        tenantManager.createTenant(
            systemTenant,
            MAIN_TENANT_2,
            tenantAdminAuthorityName,
            tenantAuthenticatedAuthorityName,
            "Anonymous");
    userRoleDao.createUser(
        mainTenant_2, "admin", "password", "", new String[] {tenantAdminAuthorityName});

    login(
        "admin",
        mainTenant_1,
        new String[] {tenantAdminAuthorityName, tenantAuthenticatedAuthorityName});
    IPentahoRole pentahoRole =
        userRoleDao.createRole(mainTenant_1, ROLE_1, ROLE_DESCRIPTION_1, null);
    pentahoRole = userRoleDao.createRole(mainTenant_1, ROLE_2, ROLE_DESCRIPTION_2, null);
    pentahoRole = userRoleDao.createRole(mainTenant_1, ROLE_3, ROLE_DESCRIPTION_3, null);
    logout();
    login(
        "admin",
        mainTenant_2,
        new String[] {tenantAdminAuthorityName, tenantAuthenticatedAuthorityName});
    pentahoRole = userRoleDao.createRole(mainTenant_2, ROLE_4, ROLE_DESCRIPTION_4, null);
    pentahoRole = userRoleDao.createRole(mainTenant_2, ROLE_5, ROLE_DESCRIPTION_5, null);
    pentahoRole = userRoleDao.createRole(mainTenant_2, ROLE_6, ROLE_DESCRIPTION_6, null);
    pentahoRole = userRoleDao.createRole(mainTenant_2, ROLE_7, ROLE_DESCRIPTION_7, null);

    List<String> systemRoles = Arrays.asList(new String[] {"Admin"});
    List<String> extraRoles = Arrays.asList(new String[] {"Authenticated", "Anonymous"});
    String adminRole = "Admin";

    UserRoleDaoUserDetailsService userDetailsService = new UserRoleDaoUserDetailsService();
    UserRoleDaoUserRoleListService service =
        new UserRoleDaoUserRoleListService(
            userRoleDao,
            userDetailsService,
            tenantedUserNameUtils,
            systemRoles,
            extraRoles,
            adminRole);
    userDetailsService.setUserRoleDao(userRoleDao);
    logout();
    login(
        "admin",
        mainTenant_1,
        new String[] {tenantAdminAuthorityName, tenantAuthenticatedAuthorityName});

    List<String> allRolesForDefaultTenant = service.getAllRoles();
    List<String> allRolesForTenant = service.getAllRoles(mainTenant_2);
    assertTrue(allRolesForDefaultTenant.size() == 3 + DEFAULT_ROLE_COUNT);
    assertTrue(allRolesForTenant.size() == 2);

    logout();
    login(
        "admin",
        mainTenant_2,
        new String[] {tenantAdminAuthorityName, tenantAuthenticatedAuthorityName});
    allRolesForDefaultTenant = service.getAllRoles();
    allRolesForTenant = service.getAllRoles(mainTenant_1);
    assertTrue(allRolesForDefaultTenant.size() == 4 + DEFAULT_ROLE_COUNT);
    assertTrue(allRolesForTenant.size() == 2);

    allRolesForTenant = service.getAllRoles(mainTenant_2);
    assertTrue(allRolesForTenant.size() == 4 + DEFAULT_ROLE_COUNT);

    allRolesForTenant = service.getAllRoles(mainTenant_1);
    assertTrue(allRolesForTenant.size() == 2);

    logout();
    login(
        "admin",
        mainTenant_1,
        new String[] {tenantAdminAuthorityName, tenantAuthenticatedAuthorityName});

    allRolesForTenant = service.getAllRoles(mainTenant_1);
    assertTrue(allRolesForTenant.size() == 3 + DEFAULT_ROLE_COUNT);

    allRolesForTenant = service.getAllRoles(mainTenant_2);
    assertTrue(allRolesForTenant.size() == 2);

    cleanupUserAndRoles("admin", mainTenant_1);
    cleanupUserAndRoles("admin", mainTenant_2);
    cleanupUserAndRoles(sysAdminUserName, systemTenant);
    // tenantManager.deleteTenant(mainTenant_1);
    // tenantManager.deleteTenant(mainTenant_2);
    // tenantManager.deleteTenant(systemTenant);
  }

  @Test
  public void testGetAllUsernames() {
    loginAsRepositoryAdmin();
    ITenant systemTenant =
        tenantManager.createTenant(
            null,
            ServerRepositoryPaths.getPentahoRootFolderName(),
            tenantAdminAuthorityName,
            tenantAuthenticatedAuthorityName,
            "Anonymous");
    userRoleDao.createUser(
        systemTenant, sysAdminUserName, "password", "", new String[] {tenantAdminAuthorityName});
    login(
        sysAdminUserName,
        systemTenant,
        new String[] {tenantAdminAuthorityName, tenantAuthenticatedAuthorityName});
    ITenant mainTenant_1 =
        tenantManager.createTenant(
            systemTenant,
            MAIN_TENANT_1,
            tenantAdminAuthorityName,
            tenantAuthenticatedAuthorityName,
            "Anonymous");
    userRoleDao.createUser(
        mainTenant_1, "admin", "password", "", new String[] {tenantAdminAuthorityName});
    ITenant mainTenant_2 =
        tenantManager.createTenant(
            systemTenant,
            MAIN_TENANT_2,
            tenantAdminAuthorityName,
            tenantAuthenticatedAuthorityName,
            "Anonymous");
    userRoleDao.createUser(
        mainTenant_2, "admin", "password", "", new String[] {tenantAdminAuthorityName});

    login(
        "admin",
        mainTenant_1,
        new String[] {tenantAdminAuthorityName, tenantAuthenticatedAuthorityName});
    IPentahoUser pentahoUser =
        userRoleDao.createUser(mainTenant_1, USER_2, PASSWORD_2, USER_DESCRIPTION_2, null);
    pentahoUser =
        userRoleDao.createUser(mainTenant_1, USER_3, PASSWORD_3, USER_DESCRIPTION_3, null);
    pentahoUser =
        userRoleDao.createUser(
            null,
            tenantedUserNameUtils.getPrincipleId(mainTenant_1, USER_4),
            PASSWORD_4,
            USER_DESCRIPTION_4,
            null);
    pentahoUser = userRoleDao.createUser(null, USER_5, PASSWORD_5, USER_DESCRIPTION_5, null);
    pentahoUser =
        userRoleDao.createUser(
            null,
            tenantedUserNameUtils.getPrincipleId(mainTenant_1, USER_6),
            PASSWORD_6,
            USER_DESCRIPTION_6,
            null);
    logout();
    login(
        "admin",
        mainTenant_2,
        new String[] {tenantAdminAuthorityName, tenantAuthenticatedAuthorityName});
    pentahoUser =
        userRoleDao.createUser(mainTenant_2, USER_7, PASSWORD_7, USER_DESCRIPTION_7, null);
    pentahoUser = userRoleDao.createUser(null, USER_8, PASSWORD_8, USER_DESCRIPTION_8, null);
    UserRoleDaoUserDetailsService userDetailsService = new UserRoleDaoUserDetailsService();
    userDetailsService.setUserRoleDao(userRoleDao);

    List<String> systemRoles = Arrays.asList(new String[] {"Admin"});
    List<String> extraRoles = Arrays.asList(new String[] {"Authenticated", "Anonymous"});
    String adminRole = "Admin";

    UserRoleDaoUserRoleListService service =
        new UserRoleDaoUserRoleListService(
            userRoleDao,
            userDetailsService,
            tenantedUserNameUtils,
            systemRoles,
            extraRoles,
            adminRole);
    service.setUserRoleDao(userRoleDao);
    service.setUserDetailsService(userDetailsService);

    logout();
    login(
        "admin",
        mainTenant_1,
        new String[] {tenantAdminAuthorityName, tenantAuthenticatedAuthorityName});
    List<String> allUserForDefaultTenant = service.getAllUsers();
    List<String> allUserForTenant = service.getAllUsers(mainTenant_2);

    assertTrue(allUserForDefaultTenant.size() == 5 + DEFAULT_USER_COUNT);
    assertTrue(allUserForTenant.size() == 0);
    logout();
    login(
        "admin",
        mainTenant_2,
        new String[] {tenantAdminAuthorityName, tenantAuthenticatedAuthorityName});
    allUserForDefaultTenant = service.getAllUsers();
    allUserForTenant = service.getAllUsers(mainTenant_1);

    assertTrue(allUserForDefaultTenant.size() == 2 + DEFAULT_USER_COUNT);
    assertTrue(allUserForTenant.size() == 0);

    allUserForTenant = service.getAllUsers(mainTenant_1);

    assertTrue(allUserForTenant.size() == 0);
    allUserForTenant = service.getAllUsers(mainTenant_2);
    assertTrue(allUserForTenant.size() == 2 + DEFAULT_USER_COUNT);
    logout();

    login(
        "admin",
        mainTenant_1,
        new String[] {tenantAdminAuthorityName, tenantAuthenticatedAuthorityName});
    allUserForTenant = service.getAllUsers(mainTenant_1);
    assertTrue(allUserForTenant.size() == 5 + DEFAULT_USER_COUNT);

    allUserForTenant = service.getAllUsers(mainTenant_2);
    assertTrue(allUserForTenant.size() == 0);

    cleanupUserAndRoles("admin", mainTenant_1);
    cleanupUserAndRoles("admin", mainTenant_2);
    cleanupUserAndRoles(sysAdminUserName, systemTenant);
  }

  @Test
  public void testGetAuthoritiesForUser() {
    loginAsRepositoryAdmin();
    ITenant systemTenant =
        tenantManager.createTenant(
            null,
            ServerRepositoryPaths.getPentahoRootFolderName(),
            tenantAdminAuthorityName,
            tenantAuthenticatedAuthorityName,
            "Anonymous");
    userRoleDao.createUser(
        systemTenant, sysAdminUserName, "password", "", new String[] {tenantAdminAuthorityName});
    login(
        sysAdminUserName,
        systemTenant,
        new String[] {tenantAdminAuthorityName, tenantAuthenticatedAuthorityName});
    ITenant mainTenant_1 =
        tenantManager.createTenant(
            systemTenant,
            MAIN_TENANT_1,
            tenantAdminAuthorityName,
            tenantAuthenticatedAuthorityName,
            "Anonymous");
    userRoleDao.createUser(
        mainTenant_1, "admin", "password", "", new String[] {tenantAdminAuthorityName});
    ITenant mainTenant_2 =
        tenantManager.createTenant(
            systemTenant,
            MAIN_TENANT_2,
            tenantAdminAuthorityName,
            tenantAuthenticatedAuthorityName,
            "Anonymous");
    userRoleDao.createUser(
        mainTenant_2, "admin", "password", "", new String[] {tenantAdminAuthorityName});

    login(
        "admin",
        mainTenant_1,
        new String[] {tenantAdminAuthorityName, tenantAuthenticatedAuthorityName});
    IPentahoUser pentahoUser =
        userRoleDao.createUser(mainTenant_1, USER_2, PASSWORD_2, USER_DESCRIPTION_2, null);
    pentahoUser =
        userRoleDao.createUser(
            null,
            tenantedUserNameUtils.getPrincipleId(mainTenant_1, USER_3),
            PASSWORD_3,
            USER_DESCRIPTION_3,
            null);
    pentahoUser = userRoleDao.createUser(null, USER_4, PASSWORD_4, USER_DESCRIPTION_4, null);
    logout();

    login(
        "admin",
        mainTenant_2,
        new String[] {tenantAdminAuthorityName, tenantAuthenticatedAuthorityName});
    pentahoUser =
        userRoleDao.createUser(mainTenant_2, USER_5, PASSWORD_5, USER_DESCRIPTION_5, null);
    pentahoUser =
        userRoleDao.createUser(
            null,
            tenantedUserNameUtils.getPrincipleId(mainTenant_2, USER_6),
            PASSWORD_6,
            USER_DESCRIPTION_6,
            null);

    logout();

    login(
        "admin",
        mainTenant_1,
        new String[] {tenantAdminAuthorityName, tenantAuthenticatedAuthorityName});
    IPentahoRole pentahoRole =
        userRoleDao.createRole(mainTenant_1, ROLE_1, ROLE_DESCRIPTION_1, null);
    pentahoRole =
        userRoleDao.createRole(
            null,
            tenantedRoleNameUtils.getPrincipleId(mainTenant_1, ROLE_2),
            ROLE_DESCRIPTION_2,
            null);
    pentahoRole = userRoleDao.createRole(null, ROLE_3, ROLE_DESCRIPTION_3, null);
    logout();

    login(
        "admin",
        mainTenant_2,
        new String[] {tenantAdminAuthorityName, tenantAuthenticatedAuthorityName});
    pentahoRole = userRoleDao.createRole(mainTenant_2, ROLE_4, ROLE_DESCRIPTION_4, null);
    userRoleDao.setUserRoles(null, USER_5, new String[] {ROLE_4});
    userRoleDao.setUserRoles(
        null, tenantedUserNameUtils.getPrincipleId(mainTenant_2, USER_6), new String[] {ROLE_4});
    logout();
    login(
        "admin",
        mainTenant_1,
        new String[] {tenantAdminAuthorityName, tenantAuthenticatedAuthorityName});
    userRoleDao.setUserRoles(mainTenant_1, USER_2, new String[] {ROLE_1, ROLE_2, ROLE_3});

    List<String> systemRoles = Arrays.asList(new String[] {"Admin"});

    try {
      userRoleDao.setUserRoles(mainTenant_1, USER_3, new String[] {ROLE_2, ROLE_3, ROLE_4});
      fail("Exception should be thrown");
    } catch (Throwable th) {
      assertNotNull(th);
    }

    try {
      userRoleDao.setUserRoles(mainTenant_1, USER_4, new String[] {ROLE_2, ROLE_4});
      fail("Exception should be thrown");
    } catch (Throwable th) {
      assertNotNull(th);
    }
    UserRoleDaoUserDetailsService userDetailsService = new UserRoleDaoUserDetailsService();
    userDetailsService.setUserRoleDao(userRoleDao);
    userDetailsService.setDefaultRole(tenantAuthenticatedAuthorityName);

    List<String> extraRoles = Arrays.asList(new String[] {"Authenticated", "Anonymous"});
    String adminRole = "Admin";

    UserRoleDaoUserRoleListService service =
        new UserRoleDaoUserRoleListService(
            userRoleDao,
            userDetailsService,
            tenantedUserNameUtils,
            systemRoles,
            extraRoles,
            adminRole);
    service.setUserDetailsService(userDetailsService);

    logout();
    login(
        "admin",
        mainTenant_1,
        new String[] {tenantAdminAuthorityName, tenantAuthenticatedAuthorityName});
    List<String> rolesForUser_2 = service.getRolesForUser(mainTenant_1, USER_2);
    List<String> rolesForUser_2_1 = service.getRolesForUser(null, USER_2);
    List<String> rolesForUser_2_1_1 =
        service.getRolesForUser(null, tenantedUserNameUtils.getPrincipleId(mainTenant_1, USER_2));
    List<String> rolesForUser_3 = service.getRolesForUser(mainTenant_1, USER_3);
    List<String> rolesForUser_4 = service.getRolesForUser(mainTenant_1, USER_4);

    assertTrue(rolesForUser_2.size() == 4);
    assertTrue(rolesForUser_2_1.size() == 4);
    assertTrue(rolesForUser_2_1_1.size() == 4);
    assertTrue(rolesForUser_3.size() == 3);
    assertTrue(rolesForUser_4.size() == 2);

    cleanupUserAndRoles("admin", mainTenant_1);
    cleanupUserAndRoles("admin", mainTenant_2);
    cleanupUserAndRoles(sysAdminUserName, systemTenant);
  }

  @Test
  public void testGetUsernamesInRole() {
    loginAsRepositoryAdmin();
    ITenant systemTenant =
        tenantManager.createTenant(
            null,
            ServerRepositoryPaths.getPentahoRootFolderName(),
            tenantAdminAuthorityName,
            tenantAuthenticatedAuthorityName,
            "Anonymous");
    userRoleDao.createUser(
        systemTenant, sysAdminUserName, "password", "", new String[] {tenantAdminAuthorityName});
    login(
        sysAdminUserName,
        systemTenant,
        new String[] {tenantAdminAuthorityName, tenantAuthenticatedAuthorityName});
    ITenant mainTenant_1 =
        tenantManager.createTenant(
            systemTenant,
            MAIN_TENANT_1,
            tenantAdminAuthorityName,
            tenantAuthenticatedAuthorityName,
            "Anonymous");
    userRoleDao.createUser(
        mainTenant_1, "admin", "password", "", new String[] {tenantAdminAuthorityName});
    ITenant mainTenant_2 =
        tenantManager.createTenant(
            systemTenant,
            MAIN_TENANT_2,
            tenantAdminAuthorityName,
            tenantAuthenticatedAuthorityName,
            "Anonymous");
    userRoleDao.createUser(
        mainTenant_2, "admin", "password", "", new String[] {tenantAdminAuthorityName});

    login(
        "admin",
        mainTenant_1,
        new String[] {tenantAdminAuthorityName, tenantAuthenticatedAuthorityName});
    IPentahoUser pentahoUser =
        userRoleDao.createUser(mainTenant_1, USER_2, PASSWORD_2, USER_DESCRIPTION_2, null);
    pentahoUser = userRoleDao.createUser(null, USER_3, PASSWORD_3, USER_DESCRIPTION_3, null);
    pentahoUser =
        userRoleDao.createUser(
            null,
            tenantedUserNameUtils.getPrincipleId(mainTenant_1, USER_4),
            PASSWORD_4,
            USER_DESCRIPTION_4,
            null);
    pentahoUser =
        userRoleDao.createUser(mainTenant_1, USER_5, PASSWORD_5, USER_DESCRIPTION_5, null);
    pentahoUser =
        userRoleDao.createUser(mainTenant_1, USER_6, PASSWORD_6, USER_DESCRIPTION_6, null);
    logout();
    login(
        "admin",
        mainTenant_2,
        new String[] {tenantAdminAuthorityName, tenantAuthenticatedAuthorityName});
    pentahoUser =
        userRoleDao.createUser(mainTenant_2, USER_7, PASSWORD_7, USER_DESCRIPTION_7, null);
    pentahoUser =
        userRoleDao.createUser(mainTenant_2, USER_8, PASSWORD_8, USER_DESCRIPTION_8, null);
    logout();
    login(
        "admin",
        mainTenant_1,
        new String[] {tenantAdminAuthorityName, tenantAuthenticatedAuthorityName});
    IPentahoRole pentahoRole =
        userRoleDao.createRole(mainTenant_1, ROLE_1, ROLE_DESCRIPTION_1, null);
    pentahoRole = userRoleDao.createRole(null, ROLE_2, ROLE_DESCRIPTION_2, null);
    pentahoRole =
        userRoleDao.createRole(
            null,
            tenantedRoleNameUtils.getPrincipleId(mainTenant_1, ROLE_3),
            ROLE_DESCRIPTION_3,
            null);
    logout();
    login(
        "admin",
        mainTenant_2,
        new String[] {tenantAdminAuthorityName, tenantAuthenticatedAuthorityName});
    pentahoRole = userRoleDao.createRole(mainTenant_2, ROLE_4, ROLE_DESCRIPTION_4, null);
    logout();
    login(
        "admin",
        mainTenant_1,
        new String[] {tenantAdminAuthorityName, tenantAuthenticatedAuthorityName});
    userRoleDao.setRoleMembers(null, ROLE_1, new String[] {USER_2, USER_3, USER_4});
    userRoleDao.setRoleMembers(mainTenant_1, ROLE_2, new String[] {USER_5, USER_6, USER_7});
    userRoleDao.setRoleMembers(
        null,
        tenantedRoleNameUtils.getPrincipleId(mainTenant_1, ROLE_3),
        new String[] {USER_2, USER_4, USER_6});
    logout();
    login(
        "admin",
        mainTenant_2,
        new String[] {tenantAdminAuthorityName, tenantAuthenticatedAuthorityName});
    userRoleDao.setRoleMembers(null, ROLE_4, new String[] {USER_3, USER_5, USER_7});
    logout();
    login(
        "admin",
        mainTenant_1,
        new String[] {tenantAdminAuthorityName, tenantAuthenticatedAuthorityName});
    UserRoleDaoUserDetailsService userDetailsService = new UserRoleDaoUserDetailsService();
    userDetailsService.setUserRoleDao(userRoleDao);
    userDetailsService.setDefaultRole(tenantAuthenticatedAuthorityName);
    List<String> systemRoles = new ArrayList<String>();
    systemRoles.add("Admin");

    List<String> extraRoles = Arrays.asList(new String[] {"Authenticated", "Anonymous"});
    String adminRole = "Admin";

    UserRoleDaoUserRoleListService service =
        new UserRoleDaoUserRoleListService(
            userRoleDao,
            userDetailsService,
            tenantedUserNameUtils,
            systemRoles,
            extraRoles,
            adminRole);

    List<String> usersInRole_1 = service.getUsersInRole(mainTenant_1, ROLE_1);
    List<String> usersInRole_2 = service.getUsersInRole(null, ROLE_2);
    List<String> usersInRole_3 =
        service.getUsersInRole(null, tenantedRoleNameUtils.getPrincipleId(mainTenant_1, ROLE_3));

    logout();
    login(
        "admin",
        mainTenant_2,
        new String[] {tenantAdminAuthorityName, tenantAuthenticatedAuthorityName});

    List<String> usersInRole_4 = service.getUsersInRole(mainTenant_2, ROLE_4);

    assertTrue(usersInRole_1.size() == 3);
    assertTrue(usersInRole_2.size() == 2);
    assertTrue(usersInRole_3.size() == 3);
    assertTrue(usersInRole_4.size() == 1);

    logout();

    cleanupUserAndRoles("admin", mainTenant_1);
    cleanupUserAndRoles("admin", mainTenant_2);
    cleanupUserAndRoles(sysAdminUserName, systemTenant);
  }

  private static void traverseNodes(Node node, int currentLevel) throws Exception {
    System.out.println(node.getPath());
    NodeIterator children = node.getNodes();
    while (children.hasNext()) {
      traverseNodes(children.nextNode(), currentLevel + 1);
    }
  }
}
public abstract class AbstractJcrBackedUserRoleDao implements IUserRoleDao {

  NameFactory NF = NameFactoryImpl.getInstance();

  Name P_PRINCIPAL_NAME = NF.create(Name.NS_REP_URI, "principalName"); // $NON-NLS-1$

  protected ITenantedPrincipleNameResolver tenantedUserNameUtils;

  protected ITenantedPrincipleNameResolver tenantedRoleNameUtils;

  String pPrincipalName = "rep:principalName"; // $NON-NLS-1$

  IRepositoryFileAclDao repositoryFileAclDao;

  IRepositoryFileDao repositoryFileDao;

  String defaultTenant;

  String authenticatedRoleName;

  String tenantAdminRoleName;

  String repositoryAdminUsername;

  IPathConversionHelper pathConversionHelper;

  IRepositoryDefaultAclHandler defaultAclHandler;

  ILockHelper lockHelper;

  List<String> systemRoles;

  List<String> extraRoles;

  HashMap<String, UserManagerImpl> userMgrMap = new HashMap<String, UserManagerImpl>();

  private LRUMap userCache = new LRUMap(4096);

  private UserCache userDetailsCache = new NullUserCache();

  public AbstractJcrBackedUserRoleDao(
      ITenantedPrincipleNameResolver userNameUtils,
      ITenantedPrincipleNameResolver roleNameUtils,
      String authenticatedRoleName,
      String tenantAdminRoleName,
      String repositoryAdminUsername,
      IRepositoryFileAclDao repositoryFileAclDao,
      IRepositoryFileDao repositoryFileDao,
      final IPathConversionHelper pathConversionHelper,
      final ILockHelper lockHelper,
      final IRepositoryDefaultAclHandler defaultAclHandler,
      final List<String> systemRoles,
      final List<String> extraRoles,
      UserCache userDetailsCache)
      throws NamespaceException {
    this.tenantedUserNameUtils = userNameUtils;
    this.tenantedRoleNameUtils = roleNameUtils;
    this.authenticatedRoleName = authenticatedRoleName;
    this.tenantAdminRoleName = tenantAdminRoleName;
    this.repositoryAdminUsername = repositoryAdminUsername;
    this.repositoryFileAclDao = repositoryFileAclDao;
    this.repositoryFileDao = repositoryFileDao;
    this.pathConversionHelper = pathConversionHelper;
    this.lockHelper = lockHelper;
    this.defaultAclHandler = defaultAclHandler;
    this.systemRoles = systemRoles;
    this.extraRoles = extraRoles;
    this.userDetailsCache = userDetailsCache;
  }

  public void setRoleMembers(
      Session session,
      final ITenant theTenant,
      final String roleName,
      final String[] memberUserNames)
      throws RepositoryException, NotFoundException {
    List<IPentahoUser> currentRoleMembers = getRoleMembers(session, theTenant, roleName);
    if (tenantAdminRoleName.equals(roleName)
        && (currentRoleMembers != null && currentRoleMembers.size() > 0)
        && memberUserNames.length == 0) {
      throw new RepositoryException(
          Messages.getInstance()
              .getString(
                  "AbstractJcrBackedUserRoleDao.ERROR_0001_LAST_ADMIN_ROLE", tenantAdminRoleName));
    }
    Group jackrabbitGroup = getJackrabbitGroup(theTenant, roleName, session);

    if ((jackrabbitGroup == null)
        || !TenantUtils.isAccessibleTenant(
            theTenant == null
                ? tenantedRoleNameUtils.getTenant(jackrabbitGroup.getID())
                : theTenant)) {
      throw new NotFoundException(
          Messages.getInstance()
              .getString("AbstractJcrBackedUserRoleDao.ERROR_0002_ROLE_NOT_FOUND"));
    }
    HashMap<String, User> currentlyAssignedUsers = new HashMap<String, User>();
    Iterator<Authorizable> currentMembers = jackrabbitGroup.getMembers();
    while (currentMembers.hasNext()) {
      Authorizable member = currentMembers.next();
      if (member instanceof User) {
        currentlyAssignedUsers.put(member.getID(), (User) member);
      }
    }

    HashMap<String, User> finalCollectionOfAssignedUsers = new HashMap<String, User>();
    if (memberUserNames != null) {
      ITenant tenant = theTenant == null ? JcrTenantUtils.getTenant(roleName, false) : theTenant;
      for (String user : memberUserNames) {
        User jackrabbitUser = getJackrabbitUser(tenant, user, session);
        if (jackrabbitUser != null) {
          finalCollectionOfAssignedUsers.put(
              tenantedRoleNameUtils.getPrincipleId(tenant, user), jackrabbitUser);
        }
      }
    }

    ArrayList<String> usersToRemove = new ArrayList<String>(currentlyAssignedUsers.keySet());
    usersToRemove.removeAll(finalCollectionOfAssignedUsers.keySet());

    ArrayList<String> usersToAdd = new ArrayList<String>(finalCollectionOfAssignedUsers.keySet());
    usersToAdd.removeAll(currentlyAssignedUsers.keySet());

    for (String userId : usersToRemove) {
      jackrabbitGroup.removeMember(currentlyAssignedUsers.get(userId));
    }

    for (String userId : usersToAdd) {
      jackrabbitGroup.addMember(finalCollectionOfAssignedUsers.get(userId));

      // Purge the UserDetails cache
      purgeUserFromCache(userId);
    }
  }

  private void setUserRolesForNewUser(
      Session session, final ITenant theTenant, final String userName, final String[] roles)
      throws RepositoryException, NotFoundException {
    Set<String> roleSet = new HashSet<String>();
    if (roles != null) {
      roleSet.addAll(Arrays.asList(roles));
    }
    roleSet.add(authenticatedRoleName);

    User jackrabbitUser = getJackrabbitUser(theTenant, userName, session);

    if ((jackrabbitUser == null)
        || !TenantUtils.isAccessibleTenant(
            theTenant == null
                ? tenantedUserNameUtils.getTenant(jackrabbitUser.getID())
                : theTenant)) {
      throw new NotFoundException(
          Messages.getInstance()
              .getString("AbstractJcrBackedUserRoleDao.ERROR_0003_USER_NOT_FOUND"));
    }

    HashMap<String, Group> finalCollectionOfAssignedGroups = new HashMap<String, Group>();
    ITenant tenant = theTenant == null ? JcrTenantUtils.getTenant(userName, true) : theTenant;
    for (String role : roleSet) {
      Group jackrabbitGroup = getJackrabbitGroup(tenant, role, session);
      if (jackrabbitGroup != null) {
        finalCollectionOfAssignedGroups.put(
            tenantedRoleNameUtils.getPrincipleId(tenant, role), jackrabbitGroup);
      }
    }

    ArrayList<String> groupsToAdd = new ArrayList<String>(finalCollectionOfAssignedGroups.keySet());

    for (String groupId : groupsToAdd) {
      finalCollectionOfAssignedGroups.get(groupId).addMember(jackrabbitUser);
      // Purge the UserDetails cache
      purgeUserFromCache(userName);
    }
  }

  private void purgeUserFromCache(String userName) {
    userDetailsCache.removeUserFromCache(getTenantedUserNameUtils().getPrincipleName(userName));
  }

  public void setUserRoles(
      Session session, final ITenant theTenant, final String userName, final String[] roles)
      throws RepositoryException, NotFoundException {
    if (hasAdminRole(getUserRoles(theTenant, userName)) && (roles.length == 0)) {
      throw new RepositoryException(
          Messages.getInstance()
              .getString("AbstractJcrBackedUserRoleDao.ERROR_0005_LAST_ADMIN_USER", userName));
    }

    Set<String> roleSet = new HashSet<String>();
    if (roles != null) {
      roleSet.addAll(Arrays.asList(roles));
    }
    roleSet.add(authenticatedRoleName);

    User jackrabbitUser = getJackrabbitUser(theTenant, userName, session);

    if ((jackrabbitUser == null)
        || !TenantUtils.isAccessibleTenant(
            theTenant == null
                ? tenantedUserNameUtils.getTenant(jackrabbitUser.getID())
                : theTenant)) {
      throw new NotFoundException(
          Messages.getInstance()
              .getString("AbstractJcrBackedUserRoleDao.ERROR_0003_USER_NOT_FOUND"));
    }
    HashMap<String, Group> currentlyAssignedGroups = new HashMap<String, Group>();
    Iterator<Group> currentGroups = jackrabbitUser.memberOf();
    while (currentGroups.hasNext()) {
      Group currentGroup = currentGroups.next();
      currentlyAssignedGroups.put(currentGroup.getID(), currentGroup);
    }

    HashMap<String, Group> finalCollectionOfAssignedGroups = new HashMap<String, Group>();
    ITenant tenant = theTenant == null ? JcrTenantUtils.getTenant(userName, true) : theTenant;
    for (String role : roleSet) {
      Group jackrabbitGroup = getJackrabbitGroup(tenant, role, session);
      if (jackrabbitGroup != null) {
        finalCollectionOfAssignedGroups.put(
            tenantedRoleNameUtils.getPrincipleId(tenant, role), jackrabbitGroup);
      }
    }

    ArrayList<String> groupsToRemove = new ArrayList<String>(currentlyAssignedGroups.keySet());
    groupsToRemove.removeAll(finalCollectionOfAssignedGroups.keySet());

    ArrayList<String> groupsToAdd = new ArrayList<String>(finalCollectionOfAssignedGroups.keySet());
    groupsToAdd.removeAll(currentlyAssignedGroups.keySet());

    for (String groupId : groupsToRemove) {
      currentlyAssignedGroups.get(groupId).removeMember(jackrabbitUser);
    }

    for (String groupId : groupsToAdd) {
      finalCollectionOfAssignedGroups.get(groupId).addMember(jackrabbitUser);
    }

    // Purge the UserDetails cache
    purgeUserFromCache(userName);
  }

  public IPentahoRole createRole(
      Session session,
      final ITenant theTenant,
      final String roleName,
      final String description,
      final String[] memberUserNames)
      throws AuthorizableExistsException, RepositoryException {
    ITenant tenant = theTenant;
    String role = roleName;
    if (tenant == null) {
      tenant = JcrTenantUtils.getTenant(roleName, false);
      role = JcrTenantUtils.getPrincipalName(roleName, false);
    }
    if (tenant == null || tenant.getId() == null) {
      tenant = JcrTenantUtils.getCurrentTenant();
    }
    if (!TenantUtils.isAccessibleTenant(tenant)) {
      throw new NotFoundException(
          Messages.getInstance()
              .getString(
                  "AbstractJcrBackedUserRoleDao.ERROR_0006_TENANT_NOT_FOUND", theTenant.getId()));
    }
    String roleId = tenantedRoleNameUtils.getPrincipleId(tenant, role);

    UserManager tenantUserMgr = getUserManager(tenant, session);
    // Intermediate path will always be an empty string. The path is already provided while creating
    // a user manager
    tenantUserMgr.createGroup(new PrincipalImpl(roleId), ""); // $NON-NLS-1$
    setRoleMembers(session, tenant, role, memberUserNames);
    setRoleDescription(session, tenant, role, description);
    return getRole(session, theTenant, roleName);
  }

  public IPentahoUser createUser(
      Session session,
      final ITenant theTenant,
      final String userName,
      final String password,
      final String description,
      final String[] roles)
      throws AuthorizableExistsException, RepositoryException {
    ITenant tenant = theTenant;
    String user = userName;
    if (tenant == null) {
      tenant = JcrTenantUtils.getTenant(userName, true);
      user = JcrTenantUtils.getPrincipalName(userName, true);
    }
    if (tenant == null || tenant.getId() == null) {
      tenant = JcrTenantUtils.getCurrentTenant();
    }
    if (!TenantUtils.isAccessibleTenant(tenant)) {
      throw new NotFoundException(
          Messages.getInstance()
              .getString(
                  "AbstractJcrBackedUserRoleDao.ERROR_0006_TENANT_NOT_FOUND", theTenant.getId()));
    }
    String userId = tenantedUserNameUtils.getPrincipleId(tenant, user);
    UserManager tenantUserMgr = getUserManager(tenant, session);
    tenantUserMgr.createUser(userId, password, new PrincipalImpl(userId), ""); // $NON-NLS-1$
    session.save();
    /**
     * This call is absolutely necessary. setUserRolesForNewUser will never * inspect what roles
     * this user is a part of. Since this is a new user * it will not be a part of new roles
     */
    setUserRolesForNewUser(session, tenant, user, roles);
    setUserDescription(session, tenant, user, description);
    session.save();
    createUserHomeFolder(tenant, user, session);
    session.save();
    this.userDetailsCache.removeUserFromCache(userName);
    return getUser(session, tenant, userName);
  }

  public void deleteRole(Session session, final IPentahoRole role)
      throws NotFoundException, RepositoryException {
    if (canDeleteRole(session, role)) {
      Group jackrabbitGroup = getJackrabbitGroup(role.getTenant(), role.getName(), session);
      if (jackrabbitGroup != null
          && TenantUtils.isAccessibleTenant(
              tenantedRoleNameUtils.getTenant(jackrabbitGroup.getID()))) {
        jackrabbitGroup.remove();
      } else {
        throw new NotFoundException(""); // $NON-NLS-1$
      }
    } else {
      throw new RepositoryException(
          Messages.getInstance()
              .getString("AbstractJcrBackedUserRoleDao.ERROR_0007_ATTEMPTED_SYSTEM_ROLE_DELETE"));
    }
  }

  public void deleteUser(Session session, final IPentahoUser user)
      throws NotFoundException, RepositoryException {
    if (canDeleteUser(session, user)) {
      User jackrabbitUser = getJackrabbitUser(user.getTenant(), user.getUsername(), session);
      if (jackrabbitUser != null
          && TenantUtils.isAccessibleTenant(
              tenantedUserNameUtils.getTenant(jackrabbitUser.getID()))) {

        // [BISERVER-9215] Adding new user with same user name as a previously deleted user,
        // defaults to all previous roles
        Iterator<Group> currentGroups = jackrabbitUser.memberOf();
        while (currentGroups.hasNext()) {
          currentGroups.next().removeMember(jackrabbitUser);
        }
        // [BISERVER-9215]

        jackrabbitUser.remove();
      } else {
        throw new NotFoundException(""); // $NON-NLS-1$
      }
    } else {
      throw new RepositoryException(
          Messages.getInstance()
              .getString(
                  "AbstractJcrBackedUserRoleDao.ERROR_0004_LAST_USER_NEEDED_IN_ROLE",
                  tenantAdminRoleName));
    }
  }

  public List<IPentahoRole> getRoles(Session session) throws RepositoryException {
    return getRoles(session, JcrTenantUtils.getCurrentTenant());
  }

  private IPentahoUser convertToPentahoUser(User jackrabbitUser) throws RepositoryException {
    if (userCache.containsKey(jackrabbitUser.getID())) {
      return (IPentahoUser) userCache.get(jackrabbitUser.getID());
    }
    IPentahoUser pentahoUser = null;
    Value[] propertyValues = null;

    String description = null;
    try {
      propertyValues = jackrabbitUser.getProperty("description"); // $NON-NLS-1$
      description = propertyValues.length > 0 ? propertyValues[0].getString() : null;
    } catch (Exception ex) {
    }

    Credentials credentials = jackrabbitUser.getCredentials();
    String password = null;
    if (credentials instanceof CryptedSimpleCredentials) {
      password = new String(((CryptedSimpleCredentials) credentials).getPassword());
    }

    pentahoUser =
        new PentahoUser(
            tenantedUserNameUtils.getTenant(jackrabbitUser.getID()),
            tenantedUserNameUtils.getPrincipleName(jackrabbitUser.getID()),
            password,
            description,
            !jackrabbitUser.isDisabled());

    userCache.put(jackrabbitUser.getID(), pentahoUser);
    return pentahoUser;
  }

  private IPentahoRole convertToPentahoRole(Group jackrabbitGroup) throws RepositoryException {
    IPentahoRole role = null;
    Value[] propertyValues = null;

    String description = null;
    try {
      propertyValues = jackrabbitGroup.getProperty("description"); // $NON-NLS-1$
      description = propertyValues.length > 0 ? propertyValues[0].getString() : null;
    } catch (Exception ex) {
    }

    role =
        new PentahoRole(
            tenantedRoleNameUtils.getTenant(jackrabbitGroup.getID()),
            tenantedRoleNameUtils.getPrincipleName(jackrabbitGroup.getID()),
            description);
    return role;
  }

  public List<IPentahoUser> getUsers(Session session) throws RepositoryException {
    return getUsers(session, JcrTenantUtils.getCurrentTenant());
  }

  public void setRoleDescription(
      Session session, final ITenant theTenant, final String roleName, final String description)
      throws NotFoundException, RepositoryException {
    Group jackrabbitGroup = getJackrabbitGroup(theTenant, roleName, session);
    if (jackrabbitGroup != null
        && TenantUtils.isAccessibleTenant(
            theTenant == null
                ? tenantedRoleNameUtils.getTenant(jackrabbitGroup.getID())
                : theTenant)) {
      if (description == null) {
        jackrabbitGroup.removeProperty("description"); // $NON-NLS-1$
      } else {
        jackrabbitGroup.setProperty(
            "description", session.getValueFactory().createValue(description)); // $NON-NLS-1$
      }
    } else {
      throw new NotFoundException(
          Messages.getInstance()
              .getString("AbstractJcrBackedUserRoleDao.ERROR_0002_ROLE_NOT_FOUND"));
    }
  }

  public void setUserDescription(
      Session session, final ITenant theTenant, final String userName, final String description)
      throws NotFoundException, RepositoryException {
    User jackrabbitUser = getJackrabbitUser(theTenant, userName, session);
    if ((jackrabbitUser == null)
        || !TenantUtils.isAccessibleTenant(
            theTenant == null
                ? tenantedUserNameUtils.getTenant(jackrabbitUser.getID())
                : theTenant)) {
      throw new NotFoundException(
          Messages.getInstance()
              .getString("AbstractJcrBackedUserRoleDao.ERROR_0003_USER_NOT_FOUND"));
    }
    if (description == null) {
      jackrabbitUser.removeProperty("description"); // $NON-NLS-1$
    } else {
      jackrabbitUser.setProperty(
          "description", session.getValueFactory().createValue(description)); // $NON-NLS-1$
    }
  }

  public void setPassword(
      Session session, final ITenant theTenant, final String userName, final String password)
      throws NotFoundException, RepositoryException {
    User jackrabbitUser = getJackrabbitUser(theTenant, userName, session);
    if ((jackrabbitUser == null)
        || !TenantUtils.isAccessibleTenant(
            theTenant == null
                ? tenantedUserNameUtils.getTenant(jackrabbitUser.getID())
                : theTenant)) {
      throw new NotFoundException(
          Messages.getInstance()
              .getString("AbstractJcrBackedUserRoleDao.ERROR_0003_USER_NOT_FOUND"));
    }
    jackrabbitUser.changePassword(password);

    /** BISERVER-9906 Clear cache after changing password */
    purgeUserFromCache(userName);
    userCache.remove(jackrabbitUser.getID());
  }

  public ITenantedPrincipleNameResolver getTenantedUserNameUtils() {
    return tenantedUserNameUtils;
  }

  public ITenantedPrincipleNameResolver getTenantedRoleNameUtils() {
    return tenantedRoleNameUtils;
  }

  public List<IPentahoRole> getRoles(Session session, ITenant tenant)
      throws RepositoryException, NamespaceException {
    return getRoles(session, tenant, false);
  }

  public List<IPentahoRole> getRoles(
      Session session, final ITenant theTenant, boolean includeSubtenants)
      throws RepositoryException {
    ArrayList<IPentahoRole> roles = new ArrayList<IPentahoRole>();
    if (TenantUtils.isAccessibleTenant(theTenant)) {
      UserManager userMgr = getUserManager(theTenant, session);
      pPrincipalName = ((SessionImpl) session).getJCRName(P_PRINCIPAL_NAME);
      Iterator<Authorizable> it =
          userMgr.findAuthorizables(pPrincipalName, null, UserManager.SEARCH_TYPE_GROUP);
      while (it.hasNext()) {
        Group group = (Group) it.next();
        IPentahoRole pentahoRole = convertToPentahoRole(group);
        // Exclude the system role from the list of roles to be returned back
        if (!extraRoles.contains(pentahoRole.getName())) {
          if (includeSubtenants) {
            roles.add(pentahoRole);
          } else {
            if (pentahoRole.getTenant() != null && pentahoRole.getTenant().equals(theTenant)) {
              roles.add(pentahoRole);
            }
          }
        }
      }
    }
    return roles;
  }

  public List<IPentahoUser> getUsers(Session session, ITenant tenant) throws RepositoryException {
    return getUsers(session, tenant, false);
  }

  public List<IPentahoUser> getUsers(
      Session session, final ITenant theTenant, boolean includeSubtenants)
      throws RepositoryException {
    ArrayList<IPentahoUser> users = new ArrayList<IPentahoUser>();
    if (TenantUtils.isAccessibleTenant(theTenant)) {
      UserManager userMgr = getUserManager(theTenant, session);
      pPrincipalName = ((SessionImpl) session).getJCRName(P_PRINCIPAL_NAME);
      Iterator<Authorizable> it =
          userMgr.findAuthorizables(pPrincipalName, null, UserManager.SEARCH_TYPE_USER);
      while (it.hasNext()) {
        User user = (User) it.next();
        IPentahoUser pentahoUser = convertToPentahoUser(user);
        if (includeSubtenants) {
          users.add(pentahoUser);
        } else {
          if (pentahoUser.getTenant() != null && pentahoUser.getTenant().equals(theTenant)) {
            users.add(pentahoUser);
          }
        }
      }
    }
    return users;
  }

  public IPentahoRole getRole(Session session, final ITenant tenant, final String name)
      throws RepositoryException {
    Group jackrabbitGroup = getJackrabbitGroup(tenant, name, session);
    return jackrabbitGroup != null
            && TenantUtils.isAccessibleTenant(
                tenant == null ? tenantedRoleNameUtils.getTenant(jackrabbitGroup.getID()) : tenant)
        ? convertToPentahoRole(jackrabbitGroup)
        : null;
  }

  private UserManagerImpl getUserManager(ITenant theTenant, Session session)
      throws RepositoryException {
    Properties tenantProperties = new Properties();
    tenantProperties.put(
        UserManagerImpl.PARAM_USERS_PATH,
        UserManagerImpl.USERS_PATH + theTenant.getRootFolderAbsolutePath());
    tenantProperties.put(
        UserManagerImpl.PARAM_GROUPS_PATH,
        UserManagerImpl.GROUPS_PATH + theTenant.getRootFolderAbsolutePath());
    return new UserManagerImpl((SessionImpl) session, session.getUserID(), tenantProperties);
  }

  public IPentahoUser getUser(Session session, final ITenant tenant, final String name)
      throws RepositoryException {
    User jackrabbitUser = getJackrabbitUser(tenant, name, session);
    return jackrabbitUser != null
            && TenantUtils.isAccessibleTenant(
                tenant == null ? tenantedUserNameUtils.getTenant(jackrabbitUser.getID()) : tenant)
        ? convertToPentahoUser(jackrabbitUser)
        : null;
  }

  private Group getJackrabbitGroup(ITenant theTenant, String name, Session session)
      throws RepositoryException {
    Group jackrabbitGroup = null;
    String roleId = name;
    String roleName = name;
    ITenant tenant = theTenant;

    if (tenant == null) {
      tenant = JcrTenantUtils.getTenant(roleName, false);
      roleName = JcrTenantUtils.getPrincipalName(roleName, false);
    }
    if (tenant == null || tenant.getId() == null) {
      tenant = JcrTenantUtils.getCurrentTenant();
    }
    if (tenant == null || tenant.getId() == null) {
      tenant = JcrTenantUtils.getDefaultTenant();
    }
    roleId = tenantedRoleNameUtils.getPrincipleId(tenant, roleName);

    UserManager userMgr = getUserManager(tenant, session);
    Authorizable authorizable = userMgr.getAuthorizable(roleId);
    if (authorizable instanceof Group) {
      jackrabbitGroup = (Group) authorizable;
    }
    return jackrabbitGroup;
  }

  private User getJackrabbitUser(ITenant theTenant, String name, Session session)
      throws RepositoryException {
    User jackrabbitUser = null;
    String userId = name;
    String userName = name;
    ITenant tenant = theTenant;
    if (tenant == null) {
      tenant = JcrTenantUtils.getTenant(userName, true);
      userName = JcrTenantUtils.getPrincipalName(userName, true);
    }
    if (tenant == null || tenant.getId() == null) {
      tenant = JcrTenantUtils.getCurrentTenant();
    }
    if (tenant == null || tenant.getId() == null) {
      tenant = JcrTenantUtils.getDefaultTenant();
    }

    if (tenant != null) {
      userId = tenantedUserNameUtils.getPrincipleId(tenant, userName);

      UserManager userMgr = getUserManager(tenant, session);
      Authorizable authorizable = userMgr.getAuthorizable(userId);
      if (authorizable instanceof User) {
        jackrabbitUser = (User) authorizable;
      }
    }
    return jackrabbitUser;
  }

  protected boolean tenantExists(String tenantName) {
    return tenantName != null && tenantName.trim().length() > 0;
  }

  public List<IPentahoUser> getRoleMembers(
      Session session, final ITenant theTenant, final String roleName) throws RepositoryException {
    List<IPentahoUser> users = new ArrayList<IPentahoUser>();
    Group jackrabbitGroup = getJackrabbitGroup(theTenant, roleName, session);
    if ((jackrabbitGroup != null)
        && TenantUtils.isAccessibleTenant(
            theTenant == null
                ? tenantedRoleNameUtils.getTenant(jackrabbitGroup.getID())
                : theTenant)) {
      Iterator<Authorizable> authorizables = jackrabbitGroup.getMembers();
      while (authorizables.hasNext()) {
        Authorizable authorizable = authorizables.next();
        if (authorizable instanceof User) {
          users.add(convertToPentahoUser((User) authorizable));
        }
      }
    }
    return users;
  }

  public List<IPentahoRole> getUserRoles(
      Session session, final ITenant theTenant, final String userName) throws RepositoryException {
    ArrayList<IPentahoRole> roles = new ArrayList<IPentahoRole>();
    User jackrabbitUser = getJackrabbitUser(theTenant, userName, session);
    if ((jackrabbitUser != null)
        && TenantUtils.isAccessibleTenant(
            theTenant == null
                ? tenantedUserNameUtils.getTenant(jackrabbitUser.getID())
                : theTenant)) {
      Iterator<Group> groups = jackrabbitUser.memberOf();
      while (groups.hasNext()) {
        IPentahoRole role = convertToPentahoRole(groups.next());
        // Exclude the extra role from the list of roles to be returned back
        if (!extraRoles.contains(role.getName())) {
          roles.add(role);
        }
      }
    }
    return roles;
  }

  private RepositoryFile createUserHomeFolder(ITenant theTenant, String username, Session session)
      throws RepositoryException {
    Builder aclsForUserHomeFolder = null;
    Builder aclsForTenantHomeFolder = null;

    if (theTenant == null) {
      theTenant = JcrTenantUtils.getTenant(username, true);
      username = JcrTenantUtils.getPrincipalName(username, true);
    }
    if (theTenant == null || theTenant.getId() == null) {
      theTenant = JcrTenantUtils.getCurrentTenant();
    }
    if (theTenant == null || theTenant.getId() == null) {
      theTenant = JcrTenantUtils.getDefaultTenant();
    }
    RepositoryFile userHomeFolder = null;
    String userId = tenantedUserNameUtils.getPrincipleId(theTenant, username);
    final RepositoryFileSid userSid = new RepositoryFileSid(userId);
    RepositoryFile tenantHomeFolder = null;
    RepositoryFile tenantRootFolder = null;
    RepositoryFileSid ownerSid = null;
    // Get the Tenant Root folder. If the Tenant Root folder does not exist then exit.
    tenantRootFolder =
        JcrRepositoryFileUtils.getFileByAbsolutePath(
            session,
            ServerRepositoryPaths.getTenantRootFolderPath(theTenant),
            pathConversionHelper,
            lockHelper,
            false,
            null);
    if (tenantRootFolder != null) {
      // Try to see if Tenant Home folder exist
      tenantHomeFolder =
          JcrRepositoryFileUtils.getFileByAbsolutePath(
              session,
              ServerRepositoryPaths.getTenantHomeFolderPath(theTenant),
              pathConversionHelper,
              lockHelper,
              false,
              null);

      if (tenantHomeFolder == null) {
        String ownerId = tenantedUserNameUtils.getPrincipleId(theTenant, username);
        ownerSid = new RepositoryFileSid(ownerId, Type.USER);

        String tenantAuthenticatedRoleId =
            tenantedRoleNameUtils.getPrincipleId(theTenant, authenticatedRoleName);
        RepositoryFileSid tenantAuthenticatedRoleSid =
            new RepositoryFileSid(tenantAuthenticatedRoleId, Type.ROLE);

        aclsForTenantHomeFolder =
            new RepositoryFileAcl.Builder(userSid)
                .ace(tenantAuthenticatedRoleSid, EnumSet.of(RepositoryFilePermission.READ));

        aclsForUserHomeFolder =
            new RepositoryFileAcl.Builder(userSid)
                .ace(ownerSid, EnumSet.of(RepositoryFilePermission.ALL));
        tenantHomeFolder =
            internalCreateFolder(
                session,
                tenantRootFolder.getId(),
                new RepositoryFile.Builder(ServerRepositoryPaths.getTenantHomeFolderName())
                    .folder(true)
                    .title(
                        Messages.getInstance()
                            .getString("AbstractJcrBackedUserRoleDao.usersFolderDisplayName"))
                    .build(),
                aclsForTenantHomeFolder.build(),
                "tenant home folder"); //$NON-NLS-1$
      } else {
        String ownerId = tenantedUserNameUtils.getPrincipleId(theTenant, username);
        ownerSid = new RepositoryFileSid(ownerId, Type.USER);
        aclsForUserHomeFolder =
            new RepositoryFileAcl.Builder(userSid)
                .ace(ownerSid, EnumSet.of(RepositoryFilePermission.ALL));
      }

      // now check if user's home folder exist
      userHomeFolder =
          JcrRepositoryFileUtils.getFileByAbsolutePath(
              session,
              ServerRepositoryPaths.getUserHomeFolderPath(theTenant, username),
              pathConversionHelper,
              lockHelper,
              false,
              null);
      if (userHomeFolder == null) {
        userHomeFolder =
            internalCreateFolder(
                session,
                tenantHomeFolder.getId(),
                new RepositoryFile.Builder(username).folder(true).build(),
                aclsForUserHomeFolder.build(),
                "user home folder"); //$NON-NLS-1$
      }
    }
    return userHomeFolder;
  }

  private RepositoryFile internalCreateFolder(
      final Session session,
      final Serializable parentFolderId,
      final RepositoryFile folder,
      final RepositoryFileAcl acl,
      final String versionMessage)
      throws RepositoryException {
    PentahoJcrConstants pentahoJcrConstants = new PentahoJcrConstants(session);
    JcrRepositoryFileUtils.checkoutNearestVersionableFileIfNecessary(
        session, pentahoJcrConstants, parentFolderId);
    Node folderNode =
        JcrRepositoryFileUtils.createFolderNode(
            session, pentahoJcrConstants, parentFolderId, folder);
    // we must create the acl during checkout
    JcrRepositoryFileAclUtils.createAcl(
        session,
        pentahoJcrConstants,
        folderNode.getIdentifier(),
        acl == null ? defaultAclHandler.createDefaultAcl(folder) : acl);
    session.save();
    if (folder.isVersioned()) {
      JcrRepositoryFileUtils.checkinNearestVersionableNodeIfNecessary(
          session, pentahoJcrConstants, folderNode, versionMessage);
    }
    JcrRepositoryFileUtils.checkinNearestVersionableFileIfNecessary(
        session,
        pentahoJcrConstants,
        parentFolderId,
        Messages.getInstance()
            .getString(
                "JcrRepositoryFileDao.USER_0001_VER_COMMENT_ADD_FOLDER",
                folder.getName(),
                (parentFolderId == null
                    ? "root"
                    : parentFolderId.toString()))); // $NON-NLS-1$ //$NON-NLS-2$
    return JcrRepositoryFileUtils.nodeToFile(
        session, pentahoJcrConstants, pathConversionHelper, lockHelper, folderNode);
  }

  /**
   * Checks to see if the removal of the received roles and users would cause the system to have no
   * login associated with the Admin role. This check is to be made before any changes take place
   *
   * @param deleteRoles Roles to be deleted separated with | char
   * @param deleteUsers Users to be deleted separated with | char
   * @return Error message if invalid or null if ok
   * @throws RepositoryException
   */
  private boolean canDeleteUser(Session session, final IPentahoUser user)
      throws RepositoryException {
    boolean userHasAdminRole = false;
    List<IPentahoRole> roles = getUserRoles(null, user.getUsername());
    for (IPentahoRole role : roles) {
      if (tenantAdminRoleName.equals(role.getName())) {
        userHasAdminRole = true;
        break;
      }
    }
    if (userHasAdminRole) {
      List<IPentahoUser> usersWithAdminRole = getRoleMembers(session, null, tenantAdminRoleName);
      if (usersWithAdminRole != null) {

      } else {
        throw new RepositoryException(
            Messages.getInstance()
                .getString(
                    "AbstractJcrBackedUserRoleDao.ERROR_0004_LAST_USER_NEEDED_IN_ROLE",
                    tenantAdminRoleName));
      }
      if (usersWithAdminRole.size() > 1) {
        return true;
      } else if (usersWithAdminRole.size() == 1) {
        return false;
      } else {
        throw new RepositoryException(
            Messages.getInstance()
                .getString(
                    "AbstractJcrBackedUserRoleDao.ERROR_0004_LAST_USER_NEEDED_IN_ROLE",
                    tenantAdminRoleName));
      }
    }
    return true;
  }

  private boolean canDeleteRole(Session session, final IPentahoRole role) {
    return !(role != null && systemRoles.contains(role.getName()));
  }

  private boolean hasAdminRole(List<IPentahoRole> roles) {
    for (IPentahoRole role : roles) {
      if (tenantAdminRoleName.equals(role.getName())) {
        return true;
      }
    }
    return false;
  }

  private boolean hasAdminRole(String[] roles) {
    for (int i = 0; i < roles.length; i++) {
      if (tenantAdminRoleName.equals(roles[i])) {
        return true;
      }
    }
    return false;
  }
}