package cc.blynk.server.api.http.logic; import cc.blynk.core.http.BaseHttpHandler; import cc.blynk.core.http.MediaType; import cc.blynk.core.http.Response; import cc.blynk.core.http.annotation.*; import cc.blynk.server.Holder; import cc.blynk.server.api.http.pojo.TokenUser; import cc.blynk.server.api.http.pojo.TokensPool; import cc.blynk.server.core.BlockingIOProcessor; import cc.blynk.server.core.dao.UserDao; import cc.blynk.server.core.model.AppName; import cc.blynk.server.core.model.auth.User; import cc.blynk.server.notifications.mail.MailWrapper; import cc.blynk.utils.FileLoaderUtil; import cc.blynk.utils.IPUtils; import cc.blynk.utils.validators.BlynkEmailValidator; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.util.UUID; import static cc.blynk.core.http.Response.*; /** * The Blynk project * Created by Andrew Zakordonets * Date : 12/05/2015. */ @Path("/") @ChannelHandler.Sharable public class ResetPasswordLogic extends BaseHttpHandler { private static final Logger log = LogManager.getLogger(ResetPasswordLogic.class); private final UserDao userDao; private final TokensPool tokensPool; private final String emailBody; private final MailWrapper mailWrapper; private final String resetPassUrl; private final String pageContent; private final BlockingIOProcessor blockingIOProcessor; private static final String RESET_PASS_STATIC_PATH = "static/reset/"; public ResetPasswordLogic(Holder holder) { super(holder, ""); this.userDao = holder.userDao; this.tokensPool = new TokensPool(60 * 60 * 1000); this.emailBody = FileLoaderUtil.readFileAsString(RESET_PASS_STATIC_PATH + "reset-email.html"); this.mailWrapper = holder.mailWrapper; String netInterface = holder.props.getProperty("net.interface", "eth"); String host = holder.props.getProperty("server.host", IPUtils.resolveHostIP(netInterface)); this.resetPassUrl = "http://" + host + "/landing?token="; this.pageContent = FileLoaderUtil.readFileAsString(RESET_PASS_STATIC_PATH + "enterNewPassword.html"); this.blockingIOProcessor = holder.blockingIOProcessor; } private static String generateToken() { return (UUID.randomUUID().toString() + UUID.randomUUID().toString()).replace("-", ""); } @POST @Consumes(value = MediaType.APPLICATION_FORM_URLENCODED) @Path("resetPassword") public Response sendResetPasswordEmail(@Context ChannelHandlerContext ctx, @FormParam("email") String email, @FormParam("appName") String appName) { if (BlynkEmailValidator.isNotValidEmail(email)) { return badRequest(email + " email has not valid format."); } final String trimmedEmail = email.trim().toLowerCase(); appName = (appName == null ? AppName.BLYNK : appName); User user = userDao.getByName(trimmedEmail, appName); if (user == null) { return badRequest("Sorry, this account is not exists."); } String token = generateToken(); log.info("{} trying to reset pass.", trimmedEmail); TokenUser userToken = new TokenUser(trimmedEmail, appName); tokensPool.addToken(token, userToken); String message = emailBody.replace("{RESET_URL}", resetPassUrl + token); log.info("Sending token to {} address", trimmedEmail); blockingIOProcessor.execute(() -> { Response response; try { mailWrapper.sendHtml(trimmedEmail, "Password reset request for Blynk app.", message); log.info("{} mail sent.", trimmedEmail); response = ok("Email was sent."); } catch (Exception e) { log.info("Error sending mail for {}. Reason : {}", trimmedEmail, e.getMessage()); response = badRequest("Error sending reset email."); } ctx.writeAndFlush(response); }); return noResponse(); } @GET @Path("landing") public Response generateResetPage(@QueryParam("token") String token) { TokenUser user = tokensPool.getUser(token); if (user == null) { return badRequest("Your token was not found or it is outdated. Please try again."); } log.info("{} landed.", user.email); String page = pageContent.replace("{EMAIL}", user.email).replace("{TOKEN}", token); return ok(page, MediaType.TEXT_HTML); } @POST @Consumes(value = MediaType.APPLICATION_FORM_URLENCODED) @Path("updatePassword") public Response updatePassword(@FormParam("password") String password, @FormParam("token") String token) { TokenUser tokenUser = tokensPool.getUser(token); if (tokenUser == null) { return badRequest("Invalid token. Please repeat all steps."); } log.info("Resetting pass for {}", tokenUser.email); User user = userDao.getByName(tokenUser.email, tokenUser.appName); if (user == null) { log.warn("No user with email {}", tokenUser.email); return notFound(); } user.pass = password; user.lastModifiedTs = System.currentTimeMillis(); log.info("{} password was reset.", user.email); tokensPool.removeToken(token); return ok("Password was successfully reset."); } }