/**
  * Retrieve list of iBIOMES users.
  *
  * @param usertype User type (admins, users, or groups)
  * @return User list
  * @throws JargonException
  * @throws IOException
  * @throws Exception
  */
 @RequestMapping(value = "/services/users", method = RequestMethod.GET)
 @ResponseBody
 public UserListMetadata getUsers() throws JargonException, IOException {
   IRODSAccount irodsAccount =
       (IRODSAccount) request.getSession().getAttribute("SPRING_SECURITY_CONTEXT");
   UserAO userAO = irodsAccessObjectFactory.getUserAO(irodsAccount);
   UserListMetadata users = new UserListMetadata(userAO.findAll());
   return users;
 }
 /**
  * Retrieve info for particular iBIOMES user.
  *
  * @param headers
  * @param username
  * @return
  * @throws IOException
  * @throws JargonException
  */
 @RequestMapping(value = "/services/users/{username}", method = RequestMethod.GET)
 @ResponseBody
 public UserMetadata getUser(@PathVariable("username") String username)
     throws JargonException, IOException {
   IRODSAccount irodsAccount =
       (IRODSAccount) request.getSession().getAttribute("SPRING_SECURITY_CONTEXT");
   UserAO userAO = irodsAccessObjectFactory.getUserAO(irodsAccount);
   UserMetadata user = new UserMetadata(userAO.findByName(username));
   return user;
 }
  /**
   * Update user password.
   *
   * @param headers
   * @param username
   * @return
   * @throws IOException
   * @throws JargonException
   */
  @RequestMapping(value = "/services/users/{username}/updatepwd", method = RequestMethod.GET)
  @ResponseBody
  public IBIOMESResponse updateUserPassword(
      @PathVariable("username") String username,
      @RequestParam("oldPwd") String oldPwd,
      @RequestParam("newPwd") String newPwd)
      throws JargonException, IOException {
    IRODSAccount irodsAccount =
        (IRODSAccount) request.getSession().getAttribute("SPRING_SECURITY_CONTEXT");
    UserAO userAO = irodsAccessObjectFactory.getUserAO(irodsAccount);
    try {
      userAO.changeAUserPasswordByThatUser(username, oldPwd, newPwd);

      // create new iRODS account object to reflect changes
      IRODSAccount newAccount =
          IRODSAccount.instance(
              irodsAccount.getHost(),
              irodsAccount.getPort(),
              irodsAccount.getUserName(),
              newPwd,
              irodsAccount.getHomeDirectory(),
              irodsAccount.getZone(),
              irodsAccount.getDefaultStorageResource());

      IRODSSession session = irodsAccessObjectFactory.getIrodsSession();

      // TODO UPDATE SESSION INFO!!! OR SOEMTHING WRONG HERE...

      AuthResponse authResponse = null;
      IRODSAccessObjectFactory accessAO = IRODSAccessObjectFactoryImpl.instance(session);
      try {
        request.getSession().removeAttribute("SPRING_SECURITY_CONTEXT");
        authResponse = accessAO.authenticateIRODSAccount(newAccount);
        session.currentConnection(authResponse.getAuthenticatedIRODSAccount());
        request
            .getSession()
            .setAttribute("SPRING_SECURITY_CONTEXT", authResponse.getAuthenticatedIRODSAccount());
      } catch (JargonException e) {
        e.printStackTrace();
        return new IBIOMESResponse(
            false,
            "Cannot update session information. Exception: " + e.getLocalizedMessage(),
            null);
      }

    } catch (AuthenticationException exc) {
      return new IBIOMESResponse(false, "Current password is not correct", null);
    }

    return new IBIOMESResponse(true, "Password successfully updated", null);
  }
  @GET
  @Path("/{userName}")
  @Produces({"application/xml", "application/json"})
  @Mapped(
      namespaceMap = {
        @XmlNsMap(namespace = "http://irods.org/irods-rest", jsonName = "irods-rest")
      })
  public UserData getUser(
      @HeaderParam("Authorization") final String authorization,
      @PathParam("userName") final String userName,
      @QueryParam("admin") @DefaultValue("false") final boolean isAdmin)
      throws JargonException {

    log.info("getUser()");

    if (authorization == null || authorization.isEmpty()) {
      throw new IllegalArgumentException("null or empty authorization");
    }

    if (userName == null || userName.isEmpty()) {
      throw new IllegalArgumentException("null or empty userName");
    }

    if (getIrodsAccessObjectFactory() == null) {
      throw new IllegalArgumentException("null irodsAccessObjectFactory");
    }

    log.info("as admin?:{}", isAdmin);

    try {
      IRODSAccount irodsAccount =
          RestAuthUtils.getIRODSAccountFromBasicAuthValues(authorization, getRestConfiguration());
      UserAO userAO = getIrodsAccessObjectFactory().getUserAO(irodsAccount);
      log.info("looking up user with name:{}", userName);
      User user = userAO.findByName(userName);
      log.info("user found:{}", user);

      return new UserData(user, getRestConfiguration());
    } finally {
      getIrodsAccessObjectFactory().closeSessionAndEatExceptions();
    }
  }
 /**
  * Create temporary user password.
  *
  * @return
  * @throws JargonException
  */
 @RequestMapping(value = "/services/user/getTmpPassword", method = RequestMethod.GET)
 @ResponseBody
 public IBIOMESResponse getTemporaryPassword() throws JargonException {
   IRODSAccount irodsAccount =
       (IRODSAccount) request.getSession().getAttribute("SPRING_SECURITY_CONTEXT");
   if (irodsAccount.isAnonymousAccount()) {
     return new IBIOMESResponse(true, "", "");
   } else {
     UserAO userAO = irodsAccessObjectFactory.getUserAO(irodsAccount);
     try {
       String password = userAO.getTemporaryPasswordForConnectedUser();
       return new IBIOMESResponse(true, "", password);
     } catch (AuthenticationException exc) {
       return new IBIOMESResponse(
           false,
           "Could not generate temporary password for user '" + irodsAccount.getUserName() + "'",
           null);
     }
   }
 }
  /**
   * Add a user and set the password based on a {@link UserAddByAdminRequest}, which is expected as
   * a JSON structure in the PUT message body. This method will return a JSON structure reflecting
   * {@link UserAddActionResponse} with error or success details.
   *
   * <p>In iRODS, this method will add the user and set a temporary password as described in the
   * JSON request.
   *
   * <p>Note that any Jargon or other exceptions are trapped and returned to the caller in the
   * response.
   *
   * @param authorization BasicAuth info
   * @param userAddByAdminRequest {@link UserAddByAdminRequest}
   * @return {@link UserAddActionResponse}
   */
  @PUT
  @Consumes("application/json")
  @Mapped(
      namespaceMap = {
        @XmlNsMap(namespace = "http://irods.org/irods-rest", jsonName = "irods-rest")
      })
  public UserAddActionResponse addUser(
      @HeaderParam("Authorization") final String authorization,
      final UserAddByAdminRequest userAddByAdminRequest) {
    log.info("addUser()");
    UserAddActionResponse response = new UserAddActionResponse();

    if (userAddByAdminRequest == null) {
      log.error("request not found");
      response.setUserAddActionResponse(UserAddActionResponseCode.ATTRIBUTES_MISSING);
      response.setMessage("null userAddByAdminRequest");
      return response;
    }

    log.info("userAddByAdminRequest:{}", userAddByAdminRequest);

    if (userAddByAdminRequest.getUserName() == null
        || userAddByAdminRequest.getUserName().isEmpty()) {
      log.error("user name missing");
      response.setMessage("User name is missing in the request");
      response.setUserAddActionResponse(UserAddActionResponseCode.ATTRIBUTES_MISSING);
      return response;
    }

    if (userAddByAdminRequest.getTempPassword() == null
        || userAddByAdminRequest.getTempPassword().isEmpty()) {
      log.error("temp password is missing");
      response.setMessage("temp password is missing in the request");
      response.setUserName(userAddByAdminRequest.getUserName());
      response.setUserAddActionResponse(UserAddActionResponseCode.ATTRIBUTES_MISSING);
      return response;
    }

    try {
      IRODSAccount irodsAccount =
          RestAuthUtils.getIRODSAccountFromBasicAuthValues(authorization, getRestConfiguration());

      UserAO userAO = getIrodsAccessObjectFactory().getUserAO(irodsAccount);

      log.info("adding user based on:{}", userAddByAdminRequest);
      User user = new User();
      user.setName(userAddByAdminRequest.getUserName());
      user.setUserDN(userAddByAdminRequest.getDistinguishedName());
      user.setUserType(UserTypeEnum.RODS_USER);
      userAO.addUser(user);
      log.info("user added... set the password");

      userAO.changeAUserPasswordByAnAdmin(user.getName(), userAddByAdminRequest.getTempPassword());
      log.info("password was set to requested value");

      response.setMessage("success");
      response.setUserName(userAddByAdminRequest.getUserName());
      response.setUserAddActionResponse(UserAddActionResponseCode.SUCCESS);
      response.setIrodsEnv(
          ConfigurationUtils.buildIrodsEnvForConfigAndUser(
              getRestConfiguration(), user.getNameWithZone()));
      response.setWebAccessURL(getRestConfiguration().getWebInterfaceURL());
      return response;
    } catch (DuplicateDataException dde) {
      log.error("duplicate data for user add", dde);
      response.setMessage(dde.getMessage());
      response.setUserName(userAddByAdminRequest.getUserName());
      response.setUserAddActionResponse(UserAddActionResponseCode.USER_NAME_IS_TAKEN);
      return response;
    } catch (JargonException je) {
      log.error("Jargon exception in user add", je);
      response.setMessage(je.getMessage());
      response.setUserName(userAddByAdminRequest.getUserName());
      response.setUserAddActionResponse(UserAddActionResponseCode.INTERNAL_ERROR);
      return response;
    } finally {
      getIrodsAccessObjectFactory().closeSessionAndEatExceptions();
    }
  }
  @Test
  public void testAddUserDuplicateByAdmin() throws Exception {

    String testUser = "******";
    String testPassword = "******";
    IRODSAccount irodsAccount =
        testingPropertiesHelper.buildIRODSAccountFromTestProperties(testingProperties);

    IRODSAccessObjectFactory accessObjectFactory = irodsFileSystem.getIRODSAccessObjectFactory();

    UserAO userAO = accessObjectFactory.getUserAO(irodsAccount);
    userAO.deleteUser(testUser);
    User user = new User();
    user.setName(testUser);
    user.setUserType(UserTypeEnum.RODS_USER);
    userAO.addUser(user);

    StringBuilder sb = new StringBuilder();
    sb.append("http://localhost:");
    sb.append(
        testingPropertiesHelper.getPropertyValueAsInt(
            testingProperties, RestTestingProperties.REST_PORT_PROPERTY));
    sb.append("/user/");

    DefaultHttpClientAndContext clientAndContext =
        RestAuthUtils.httpClientSetup(irodsAccount, testingProperties);
    try {

      HttpPut httpPut = new HttpPut(sb.toString());
      httpPut.addHeader("accept", "application/json");
      httpPut.addHeader("Content-Type", "application/json");

      ObjectMapper mapper = new ObjectMapper();
      UserAddByAdminRequest addRequest = new UserAddByAdminRequest();
      addRequest.setDistinguishedName("dn here");
      addRequest.setTempPassword(testPassword);
      addRequest.setUserName(testUser);
      String body = mapper.writeValueAsString(addRequest);

      System.out.println(body);

      httpPut.setEntity(new StringEntity(body));

      HttpResponse response =
          clientAndContext.getHttpClient().execute(httpPut, clientAndContext.getHttpContext());
      HttpEntity entity = response.getEntity();
      Assert.assertEquals(200, response.getStatusLine().getStatusCode());
      String entityData = EntityUtils.toString(entity);

      System.out.println(entityData);

      UserAddActionResponse actual = mapper.readValue(entityData, UserAddActionResponse.class);
      Assert.assertEquals(testUser, actual.getUserName());
      Assert.assertEquals(
          UserAddActionResponse.UserAddActionResponseCode.USER_NAME_IS_TAKEN,
          actual.getUserAddActionResponse());
      Assert.assertEquals(
          UserAddActionResponse.UserAddActionResponseCode.USER_NAME_IS_TAKEN.ordinal(),
          actual.getUserAddActionResponseNumericCode());

    } finally {
      // When HttpClient instance is no longer needed,
      // shut down the connection manager to ensure
      // immediate deallocation of all system resources
      clientAndContext.getHttpClient().getConnectionManager().shutdown();
    }
  }
  @Test
  public void testAddUserByAdminLongDn() throws Exception {

    String testUser = "******";
    String testPassword = "******";
    String testDN =
        "/CN=xxxxxyyxx-64f0-4d49-a0a9-508a8a5328cd/emailAddress=xxxxxxxxxxthismaynotshowup";
    IRODSAccount irodsAccount =
        testingPropertiesHelper.buildIRODSAccountFromTestProperties(testingProperties);

    IRODSAccount userAccount =
        testingPropertiesHelper.buildIRODSAccountForIRODSUserFromTestPropertiesForGivenUser(
            testingProperties, testUser, testPassword);

    IRODSAccessObjectFactory accessObjectFactory = irodsFileSystem.getIRODSAccessObjectFactory();

    UserAO userAO = accessObjectFactory.getUserAO(irodsAccount);

    try {
      userAO.findByName(testUser);
      String homeDir = MiscIRODSUtils.computeHomeDirectoryForIRODSAccount(userAccount);
      IRODSFile userHomeDir =
          irodsFileSystem.getIRODSFileFactory(userAccount).instanceIRODSFile(homeDir);

      for (File homeDirFile : userHomeDir.listFiles()) {
        homeDirFile.delete();
      }
      userAO.deleteUser(testUser);

    } catch (DataNotFoundException dnf) {
      // OK
    }

    StringBuilder sb = new StringBuilder();
    sb.append("http://localhost:");
    sb.append(
        testingPropertiesHelper.getPropertyValueAsInt(
            testingProperties, RestTestingProperties.REST_PORT_PROPERTY));
    sb.append("/user/");

    DefaultHttpClientAndContext clientAndContext =
        RestAuthUtils.httpClientSetup(irodsAccount, testingProperties);
    try {

      HttpPut httpPut = new HttpPut(sb.toString());
      httpPut.addHeader("accept", "application/json");
      httpPut.addHeader("Content-Type", "application/json");

      ObjectMapper mapper = new ObjectMapper();
      UserAddByAdminRequest addRequest = new UserAddByAdminRequest();
      addRequest.setDistinguishedName(testDN);
      addRequest.setTempPassword(testPassword);
      addRequest.setUserName(testUser);
      String body = mapper.writeValueAsString(addRequest);

      System.out.println(body);

      httpPut.setEntity(new StringEntity(body));

      HttpResponse response =
          clientAndContext.getHttpClient().execute(httpPut, clientAndContext.getHttpContext());
      HttpEntity entity = response.getEntity();
      Assert.assertEquals(200, response.getStatusLine().getStatusCode());
      String entityData = EntityUtils.toString(entity);

      System.out.println(entityData);

      UserAddActionResponse actual = mapper.readValue(entityData, UserAddActionResponse.class);
      Assert.assertEquals(testUser, actual.getUserName());
      Assert.assertEquals(
          UserAddActionResponse.UserAddActionResponseCode.SUCCESS,
          actual.getUserAddActionResponse());
      Assert.assertEquals(
          UserAddActionResponse.UserAddActionResponseCode.SUCCESS.ordinal(),
          actual.getUserAddActionResponseNumericCode());

    } finally {
      // When HttpClient instance is no longer needed,
      // shut down the connection manager to ensure
      // immediate deallocation of all system resources
      clientAndContext.getHttpClient().getConnectionManager().shutdown();
    }

    User user = userAO.findByName(testUser);
    Assert.assertNotNull("user not added", user);
    Assert.assertEquals("dn not set", testDN, user.getUserDN());
  }