aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton Tananaev <anton.tananaev@gmail.com>2021-02-27 13:02:34 -0800
committerAnton Tananaev <anton.tananaev@gmail.com>2021-02-27 13:02:34 -0800
commit1499b12fa782a57dbaef294ddd16ba61d2193cc7 (patch)
tree4717cccc2ac137e824ca8f5838d4c1093e731349
parentb9117b29226dae6cf945c2dd961c91c29fcb5d8b (diff)
downloadtraccar-server-1499b12fa782a57dbaef294ddd16ba61d2193cc7.tar.gz
traccar-server-1499b12fa782a57dbaef294ddd16ba61d2193cc7.tar.bz2
traccar-server-1499b12fa782a57dbaef294ddd16ba61d2193cc7.zip
Implement password reset API
-rw-r--r--src/main/java/org/traccar/api/resource/PasswordResource.java81
-rw-r--r--src/main/java/org/traccar/notification/NotificationFormatter.java61
-rw-r--r--src/main/java/org/traccar/notification/TextTemplateFormatter.java91
-rw-r--r--templates/full/passwordReset.vm8
4 files changed, 187 insertions, 54 deletions
diff --git a/src/main/java/org/traccar/api/resource/PasswordResource.java b/src/main/java/org/traccar/api/resource/PasswordResource.java
new file mode 100644
index 000000000..20e8d768d
--- /dev/null
+++ b/src/main/java/org/traccar/api/resource/PasswordResource.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2021 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.api.resource;
+
+import org.apache.velocity.VelocityContext;
+import org.traccar.Context;
+import org.traccar.api.BaseResource;
+import org.traccar.model.User;
+import org.traccar.notification.FullMessage;
+import org.traccar.notification.TextTemplateFormatter;
+
+import javax.annotation.security.PermitAll;
+import javax.mail.MessagingException;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.sql.SQLException;
+import java.util.UUID;
+
+@Path("password")
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+public class PasswordResource extends BaseResource {
+
+ private static final String PASSWORD_RESET_TOKEN = "passwordToken";
+
+ @Path("reset")
+ @PermitAll
+ @POST
+ public Response reset(@FormParam("email") String email) throws SQLException, MessagingException {
+ for (long userId : Context.getUsersManager().getAllItems()) {
+ User user = Context.getUsersManager().getById(userId);
+ if (email.equals(user.getEmail())) {
+ String token = UUID.randomUUID().toString().replaceAll("-", "");
+ user.set(PASSWORD_RESET_TOKEN, token);
+ Context.getUsersManager().updateItem(user);
+ VelocityContext velocityContext = TextTemplateFormatter.prepareContext(null);
+ velocityContext.put("token", token);
+ FullMessage message = TextTemplateFormatter.formatFullMessage(velocityContext, "passwordReset");
+ Context.getMailManager().sendMessage(userId, message.getSubject(), message.getBody());
+ break;
+ }
+ }
+ return Response.ok().build();
+ }
+
+ @Path("update")
+ @PermitAll
+ @POST
+ public Response update(
+ @FormParam("token") String token, @FormParam("password") String password) throws SQLException {
+ for (long userId : Context.getUsersManager().getAllItems()) {
+ User user = Context.getUsersManager().getById(userId);
+ if (token.equals(user.getString(PASSWORD_RESET_TOKEN))) {
+ user.getAttributes().remove(PASSWORD_RESET_TOKEN);
+ user.setPassword(password);
+ Context.getUsersManager().updateItem(user);
+ return Response.ok().build();
+ }
+ }
+ return Response.status(Response.Status.NOT_FOUND).build();
+ }
+
+}
diff --git a/src/main/java/org/traccar/notification/NotificationFormatter.java b/src/main/java/org/traccar/notification/NotificationFormatter.java
index 2f8100226..dabc75b8b 100644
--- a/src/main/java/org/traccar/notification/NotificationFormatter.java
+++ b/src/main/java/org/traccar/notification/NotificationFormatter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2021 Anton Tananaev (anton@traccar.org)
* Copyright 2017 - 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,18 +16,7 @@
*/
package org.traccar.notification;
-import java.io.StringWriter;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Paths;
-import java.util.Locale;
-
-import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
-import org.apache.velocity.exception.ResourceNotFoundException;
-import org.apache.velocity.tools.generic.DateTool;
-import org.apache.velocity.tools.generic.NumberTool;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.traccar.Context;
import org.traccar.model.Device;
import org.traccar.model.Event;
@@ -37,8 +26,6 @@ import org.traccar.reports.ReportUtils;
public final class NotificationFormatter {
- private static final Logger LOGGER = LoggerFactory.getLogger(NotificationFormatter.class);
-
private NotificationFormatter() {
}
@@ -47,8 +34,8 @@ public final class NotificationFormatter {
User user = Context.getPermissionsManager().getUser(userId);
Device device = Context.getIdentityManager().getById(event.getDeviceId());
- VelocityContext velocityContext = new VelocityContext();
- velocityContext.put("user", user);
+ VelocityContext velocityContext = TextTemplateFormatter.prepareContext(user);
+
velocityContext.put("device", device);
velocityContext.put("event", event);
if (position != null) {
@@ -67,52 +54,18 @@ public final class NotificationFormatter {
if (driverUniqueId != null) {
velocityContext.put("driver", Context.getDriversManager().getDriverByUniqueId(driverUniqueId));
}
- velocityContext.put("webUrl", Context.getVelocityEngine().getProperty("web.url"));
- velocityContext.put("dateTool", new DateTool());
- velocityContext.put("numberTool", new NumberTool());
- velocityContext.put("timezone", ReportUtils.getTimezone(userId));
- velocityContext.put("locale", Locale.getDefault());
- return velocityContext;
- }
-
- public static Template getTemplate(Event event, String path) {
- String templateFilePath;
- Template template;
-
- try {
- templateFilePath = Paths.get(path, event.getType() + ".vm").toString();
- template = Context.getVelocityEngine().getTemplate(templateFilePath, StandardCharsets.UTF_8.name());
- } catch (ResourceNotFoundException error) {
- LOGGER.warn("Notification template error", error);
- templateFilePath = Paths.get(path, "unknown.vm").toString();
- template = Context.getVelocityEngine().getTemplate(templateFilePath, StandardCharsets.UTF_8.name());
- }
- return template;
+ return velocityContext;
}
public static FullMessage formatFullMessage(long userId, Event event, Position position) {
VelocityContext velocityContext = prepareContext(userId, event, position);
- String formattedMessage = formatMessage(velocityContext, userId, event, position, "full");
-
- return new FullMessage((String) velocityContext.get("subject"), formattedMessage);
+ return TextTemplateFormatter.formatFullMessage(velocityContext, event.getType());
}
public static String formatShortMessage(long userId, Event event, Position position) {
- return formatMessage(null, userId, event, position, "short");
- }
-
- private static String formatMessage(VelocityContext vc, Long userId, Event event, Position position,
- String templatePath) {
-
- VelocityContext velocityContext = vc;
- if (velocityContext == null) {
- velocityContext = prepareContext(userId, event, position);
- }
- StringWriter writer = new StringWriter();
- getTemplate(event, templatePath).merge(velocityContext, writer);
-
- return writer.toString();
+ VelocityContext velocityContext = prepareContext(userId, event, position);
+ return TextTemplateFormatter.formatShortMessage(velocityContext, event.getType());
}
}
diff --git a/src/main/java/org/traccar/notification/TextTemplateFormatter.java b/src/main/java/org/traccar/notification/TextTemplateFormatter.java
new file mode 100644
index 000000000..c7cac2d4d
--- /dev/null
+++ b/src/main/java/org/traccar/notification/TextTemplateFormatter.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2021 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.notification;
+
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.exception.ResourceNotFoundException;
+import org.apache.velocity.tools.generic.DateTool;
+import org.apache.velocity.tools.generic.NumberTool;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.Context;
+import org.traccar.model.User;
+import org.traccar.reports.ReportUtils;
+
+import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Paths;
+import java.util.Locale;
+
+public final class TextTemplateFormatter {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(TextTemplateFormatter.class);
+
+ private TextTemplateFormatter() {
+ }
+
+ public static VelocityContext prepareContext(User user) {
+
+ VelocityContext velocityContext = new VelocityContext();
+
+ if (user != null) {
+ velocityContext.put("user", user);
+ velocityContext.put("timezone", ReportUtils.getTimezone(user.getId()));
+ }
+
+ velocityContext.put("webUrl", Context.getVelocityEngine().getProperty("web.url"));
+ velocityContext.put("dateTool", new DateTool());
+ velocityContext.put("numberTool", new NumberTool());
+ velocityContext.put("locale", Locale.getDefault());
+
+ return velocityContext;
+ }
+
+ public static Template getTemplate(String name, String path) {
+
+ String templateFilePath;
+ Template template;
+
+ try {
+ templateFilePath = Paths.get(path, name + ".vm").toString();
+ template = Context.getVelocityEngine().getTemplate(templateFilePath, StandardCharsets.UTF_8.name());
+ } catch (ResourceNotFoundException error) {
+ LOGGER.warn("Notification template error", error);
+ templateFilePath = Paths.get(path, "unknown.vm").toString();
+ template = Context.getVelocityEngine().getTemplate(templateFilePath, StandardCharsets.UTF_8.name());
+ }
+ return template;
+ }
+
+ public static FullMessage formatFullMessage(VelocityContext velocityContext, String name) {
+ String formattedMessage = formatMessage(velocityContext, name, "full");
+ return new FullMessage((String) velocityContext.get("subject"), formattedMessage);
+ }
+
+ public static String formatShortMessage(VelocityContext velocityContext, String name) {
+ return formatMessage(velocityContext, name, "short");
+ }
+
+ private static String formatMessage(
+ VelocityContext velocityContext, String name, String templatePath) {
+
+ StringWriter writer = new StringWriter();
+ getTemplate(name, templatePath).merge(velocityContext, writer);
+ return writer.toString();
+ }
+
+}
diff --git a/templates/full/passwordReset.vm b/templates/full/passwordReset.vm
new file mode 100644
index 000000000..fe692ba1d
--- /dev/null
+++ b/templates/full/passwordReset.vm
@@ -0,0 +1,8 @@
+#set($subject = "Password reset")
+<!DOCTYPE html>
+<html>
+<body>
+To reset password please click on the following link:<br>
+<a href="$webUrl?passwordReset=$token">$webUrl?passwordReset=$token</a><br>
+</body>
+</html>