From cd1d80896b16c84ae0a8a4631a9dba6955e9e344 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Mon, 20 Feb 2023 14:33:36 -0800 Subject: Implement command notificator --- src/main/java/org/traccar/model/Notification.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'src/main/java/org/traccar/model') diff --git a/src/main/java/org/traccar/model/Notification.java b/src/main/java/org/traccar/model/Notification.java index 95e446132..b6a6e4cf5 100644 --- a/src/main/java/org/traccar/model/Notification.java +++ b/src/main/java/org/traccar/model/Notification.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2023 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. @@ -46,6 +46,16 @@ public class Notification extends ScheduledModel { this.type = type; } + private long commandId; + + public long getCommandId() { + return commandId; + } + + public void setCommandId(long commandId) { + this.commandId = commandId; + } + private String notificators; public String getNotificators() { -- cgit v1.2.3 From 754ff3a85baf2faaa1b962f16f52ebd0e12e6436 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Tue, 21 Feb 2023 09:52:22 -0800 Subject: Add lock attribute --- src/main/java/org/traccar/model/Position.java | 1 + src/main/java/org/traccar/protocol/TeraTrackProtocolDecoder.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'src/main/java/org/traccar/model') diff --git a/src/main/java/org/traccar/model/Position.java b/src/main/java/org/traccar/model/Position.java index 1286db5f2..41cfeaf2e 100644 --- a/src/main/java/org/traccar/model/Position.java +++ b/src/main/java/org/traccar/model/Position.java @@ -83,6 +83,7 @@ public class Position extends Message { public static final String KEY_OPERATOR = "operator"; public static final String KEY_COMMAND = "command"; public static final String KEY_BLOCKED = "blocked"; + public static final String KEY_LOCK = "lock"; public static final String KEY_DOOR = "door"; public static final String KEY_AXLE_WEIGHT = "axleWeight"; public static final String KEY_G_SENSOR = "gSensor"; diff --git a/src/main/java/org/traccar/protocol/TeraTrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/TeraTrackProtocolDecoder.java index 313210f63..423ae3ffe 100644 --- a/src/main/java/org/traccar/protocol/TeraTrackProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TeraTrackProtocolDecoder.java @@ -63,7 +63,7 @@ public class TeraTrackProtocolDecoder extends BaseProtocolDecoder { position.setSpeed(UnitsConverter.knotsFromKph(Integer.parseInt(json.getString("Speed")))); position.set(Position.KEY_ODOMETER, Integer.parseInt(json.getString("Mileage"))); - position.set(Position.KEY_BLOCKED, json.getString("LockOpen").equals("0")); + position.set(Position.KEY_LOCK, json.getString("LockOpen").equals("0")); position.set(Position.KEY_DRIVER_UNIQUE_ID, json.getString("CardNo")); position.set(Position.KEY_ALARM, json.getString("LowPower").equals("1") ? Position.ALARM_LOW_POWER : null); position.set(Position.KEY_BATTERY_LEVEL, Integer.parseInt(json.getString("Power"))); -- cgit v1.2.3 From e6960c3d49025eb63243052e4c7aedde9955fe40 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sat, 25 Mar 2023 07:37:46 -0700 Subject: Support Wetrust GPS tracker --- src/main/java/org/traccar/model/Position.java | 2 ++ .../java/org/traccar/protocol/FifotrackProtocolDecoder.java | 8 ++++---- .../java/org/traccar/protocol/Gl200TextProtocolDecoder.java | 2 +- src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java | 12 +++++++++++- .../java/org/traccar/protocol/HuabaoProtocolDecoder.java | 2 +- .../java/org/traccar/protocol/MeiligaoProtocolDecoder.java | 2 +- src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java | 2 +- src/main/java/org/traccar/protocol/Vt200ProtocolDecoder.java | 2 +- .../java/org/traccar/protocol/Gt06ProtocolDecoderTest.java | 4 ++++ .../java/org/traccar/protocol/TzoneProtocolDecoderTest.java | 3 ++- 10 files changed, 28 insertions(+), 11 deletions(-) (limited to 'src/main/java/org/traccar/model') diff --git a/src/main/java/org/traccar/model/Position.java b/src/main/java/org/traccar/model/Position.java index 41cfeaf2e..2bd71f383 100644 --- a/src/main/java/org/traccar/model/Position.java +++ b/src/main/java/org/traccar/model/Position.java @@ -90,6 +90,7 @@ public class Position extends Message { public static final String KEY_ICCID = "iccid"; public static final String KEY_PHONE = "phone"; public static final String KEY_SPEED_LIMIT = "speedLimit"; + public static final String KEY_DRIVING_TIME = "drivingTime"; public static final String KEY_DTCS = "dtcs"; public static final String KEY_OBD_SPEED = "obdSpeed"; // knots @@ -98,6 +99,7 @@ public class Position extends Message { public static final String KEY_RESULT = "result"; public static final String KEY_DRIVER_UNIQUE_ID = "driverUniqueId"; + public static final String KEY_CARD = "card"; // Start with 1 not 0 public static final String PREFIX_TEMP = "temp"; diff --git a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java index a9d77b46e..e0dd1d62d 100644 --- a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java @@ -308,11 +308,11 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder { } if (parser.hasNext()) { - String rfid = parser.next(); - if (rfid.matches("\\p{XDigit}+")) { - position.set(Position.KEY_DRIVER_UNIQUE_ID, String.valueOf(Integer.parseInt(rfid, 16))); + String value = parser.next(); + if (value.matches("\\p{XDigit}+")) { + position.set(Position.KEY_DRIVER_UNIQUE_ID, String.valueOf(Integer.parseInt(value, 16))); } else { - position.set("driverLicense", rfid); + position.set(Position.KEY_CARD, value); } } diff --git a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java index 28308ab77..c7713bdc2 100644 --- a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java @@ -680,7 +680,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_HOURS, UnitsConverter.msFromHours(Double.parseDouble(values[index - 1]))); } if (BitUtil.check(reportMask, 12)) { - position.set("drivingHours", Double.parseDouble(values[index++])); + position.set(Position.KEY_DRIVING_TIME, Double.parseDouble(values[index++])); } if (BitUtil.check(reportMask, 13)) { position.set("idleHours", Double.parseDouble(values[index++])); diff --git a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java index 5b639ddfc..02a629103 100644 --- a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java @@ -119,6 +119,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { SPACE10X, STANDARD, OBD6, + WETRUST, } private Variant variant; @@ -833,7 +834,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { String data = buf.readCharSequence(buf.readUnsignedByte(), StandardCharsets.US_ASCII).toString(); buf.readUnsignedByte(); // alarm buf.readUnsignedByte(); // swiped - position.set("driverLicense", data.trim()); + position.set(Position.KEY_CARD, data.trim()); } else if (variant == Variant.BENWAY) { int mask = buf.readUnsignedShort(); position.set(Position.KEY_IGNITION, BitUtil.check(mask, 8 + 7)); @@ -869,6 +870,13 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { } position.set(Position.PREFIX_TEMP + 1, temperature); position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 10); + } else if (variant == Variant.WETRUST) { + position.set(Position.KEY_ODOMETER, buf.readUnsignedInt()); + position.set(Position.KEY_CARD, buf.readCharSequence( + buf.readUnsignedByte(), StandardCharsets.US_ASCII).toString()); + position.set(Position.KEY_ALARM, buf.readUnsignedByte() > 0 ? Position.ALARM_GENERAL : null); + position.set("cardStatus", buf.readUnsignedByte()); + position.set(Position.KEY_DRIVING_TIME, buf.readUnsignedShort()); } } @@ -1391,6 +1399,8 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { variant = Variant.SPACE10X; } else if (header == 0x7878 && type == MSG_STATUS && length == 0x13) { variant = Variant.OBD6; + } else if (header == 0x7878 && type == MSG_GPS_LBS_1 && length == 0x29) { + variant = Variant.WETRUST; } else { variant = Variant.STANDARD; } diff --git a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java index 7227c5584..5e73967d5 100644 --- a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java @@ -608,7 +608,7 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { break; case 0xED: stringValue = buf.readCharSequence(length, StandardCharsets.US_ASCII).toString(); - position.set("driverLicense", stringValue.trim()); + position.set(Position.KEY_CARD, stringValue.trim()); break; case 0xEE: position.set(Position.KEY_RSSI, buf.readUnsignedByte()); diff --git a/src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java b/src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java index f3b56973a..1f8c4d2da 100644 --- a/src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java @@ -282,7 +282,7 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_RSSI, parser.nextHexInt()); position.set(Position.KEY_ODOMETER, parser.nextHexLong()); position.set(Position.KEY_SATELLITES, parser.nextHexInt()); - position.set("driverLicense", parser.next()); + position.set(Position.KEY_CARD, parser.next()); position.set(Position.KEY_ODOMETER, parser.nextLong()); position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next()); diff --git a/src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java b/src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java index ba9b41654..f0b1e709d 100644 --- a/src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java @@ -234,7 +234,7 @@ public class TzoneProtocolDecoder extends BaseProtocolDecoder { } else if (type == 1) { - position.set("driverLicense", buf.readCharSequence( + position.set(Position.KEY_CARD, buf.readCharSequence( blockEnd - buf.readerIndex(), StandardCharsets.UTF_8).toString()); } diff --git a/src/main/java/org/traccar/protocol/Vt200ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Vt200ProtocolDecoder.java index a8fc801e7..1ad15f39c 100644 --- a/src/main/java/org/traccar/protocol/Vt200ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Vt200ProtocolDecoder.java @@ -123,7 +123,7 @@ public class Vt200ProtocolDecoder extends BaseProtocolDecoder { position.set("tripStart", decodeDate(buf).getTime()); position.set("tripEnd", decodeDate(buf).getTime()); - position.set("drivingTime", buf.readUnsignedShort()); + position.set(Position.KEY_DRIVING_TIME, buf.readUnsignedShort()); position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedInt()); position.set(Position.KEY_ODOMETER_TRIP, buf.readUnsignedInt()); diff --git a/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java index 3b4cb158e..5dc6b803e 100644 --- a/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java @@ -17,6 +17,10 @@ public class Gt06ProtocolDecoderTest extends ProtocolTest { verifyNull(decoder, binary( "78780D01086471700328358100093F040D0A")); + verifyAttribute(decoder, binary( + "78782912170316053b3bcf015b51220af1201105d56100000000000000000000869c0130010000000238d1af0d0a"), + Position.KEY_DRIVING_TIME, 0); + verifyAttribute(decoder, binary( "78781219012ed042cc00954d00040419000056fe290d0a"), Position.KEY_ALARM, Position.ALARM_LOW_BATTERY); diff --git a/src/test/java/org/traccar/protocol/TzoneProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TzoneProtocolDecoderTest.java index 8fdc8c23c..d90e5e07e 100644 --- a/src/test/java/org/traccar/protocol/TzoneProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TzoneProtocolDecoderTest.java @@ -2,6 +2,7 @@ package org.traccar.protocol; import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; +import org.traccar.model.Position; public class TzoneProtocolDecoderTest extends ProtocolTest { @@ -12,7 +13,7 @@ public class TzoneProtocolDecoderTest extends ProtocolTest { verifyAttribute(decoder, binary( "545a00d424240153011300000863835029944118170316023b180016040485c73d2479187e170316023b1800000000060c000000000d1cc0406303019904aa00000000008a012520205e544f4e474c4f4d245049544f4f4e244d522e5e5e3f3b363030373634333132303130303134323234323d3139303631393538313032363d3f2b2020202020202020202020202032322020202020202020202020203120202020202020202020202030303234363238202031303730302020202020202020202020202020202020202020203f00030080000006e80e0d0a"), - "driverLicense", "% ^TONGLOM$PITOON$MR.^^?;6007643120100142242=190619581026=?+ 22 1 0024628 10700 ?"); + Position.KEY_CARD, "% ^TONGLOM$PITOON$MR.^^?;6007643120100142242=190619581026=?+ 22 1 0024628 10700 ?"); verifyAttributes(decoder, binary( "545a003724240407020200000180322000001610160b151019100000000c010a07320101088600007dca000baa102837016a0114025500000169e80d0a")); -- cgit v1.2.3 From 040fa7c83b67b0c6541348c4ecd3979c7a80ebc5 Mon Sep 17 00:00:00 2001 From: Dan Date: Sat, 1 Apr 2023 23:10:18 +0100 Subject: Expose OIDC state on /server endpoint --- .../java/org/traccar/api/security/OpenIDProvider.java | 2 +- src/main/java/org/traccar/model/Server.java | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) (limited to 'src/main/java/org/traccar/model') diff --git a/src/main/java/org/traccar/api/security/OpenIDProvider.java b/src/main/java/org/traccar/api/security/OpenIDProvider.java index cd3fa4dde..80d84dfbd 100644 --- a/src/main/java/org/traccar/api/security/OpenIDProvider.java +++ b/src/main/java/org/traccar/api/security/OpenIDProvider.java @@ -61,7 +61,7 @@ import com.nimbusds.openid.connect.sdk.AuthenticationRequest; import com.nimbusds.openid.connect.sdk.claims.UserInfo; public class OpenIDProvider { - private final Boolean force; + public final Boolean force; private final ClientID clientId; private final Secret clientSecret; private URI callbackUrl; diff --git a/src/main/java/org/traccar/model/Server.java b/src/main/java/org/traccar/model/Server.java index 73645721b..4c6e10b5e 100644 --- a/src/main/java/org/traccar/model/Server.java +++ b/src/main/java/org/traccar/model/Server.java @@ -16,13 +16,16 @@ package org.traccar.model; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +import org.traccar.Main; +import org.traccar.api.security.OpenIDProvider; import org.traccar.storage.QueryIgnore; import org.traccar.storage.StorageName; @StorageName("tc_servers") @JsonIgnoreProperties(ignoreUnknown = true) public class Server extends ExtendedModel implements UserRestrictions { - + private boolean registration; public boolean getRegistration() { @@ -261,4 +264,15 @@ public class Server extends ExtendedModel implements UserRestrictions { this.newServer = newServer; } + @QueryIgnore + public boolean getOidcEnabled() { + OpenIDProvider oidc = Main.getInjector().getInstance(OpenIDProvider.class); + return oidc != null; + } + + @QueryIgnore + public boolean getOidcForce() { + OpenIDProvider oidc = Main.getInjector().getInstance(OpenIDProvider.class); + return oidc != null && oidc.force; + } } -- cgit v1.2.3 From faf5567add4cf343cc38b3c7dcb297c7cbed88bc Mon Sep 17 00:00:00 2001 From: Dan Date: Mon, 3 Apr 2023 12:12:38 +0100 Subject: First pass --- src/main/java/org/traccar/MainModule.java | 8 +- .../org/traccar/api/resource/ServerResource.java | 7 + .../org/traccar/api/resource/SessionResource.java | 26 +-- .../org/traccar/api/security/LoginService.java | 17 +- .../org/traccar/api/security/OpenIDProvider.java | 245 --------------------- src/main/java/org/traccar/config/Keys.java | 68 +++--- .../java/org/traccar/database/OpenIdProvider.java | 183 +++++++++++++++ src/main/java/org/traccar/model/Server.java | 28 ++- 8 files changed, 270 insertions(+), 312 deletions(-) delete mode 100644 src/main/java/org/traccar/api/security/OpenIDProvider.java create mode 100644 src/main/java/org/traccar/database/OpenIdProvider.java (limited to 'src/main/java/org/traccar/model') diff --git a/src/main/java/org/traccar/MainModule.java b/src/main/java/org/traccar/MainModule.java index 7def97657..7b06b4840 100644 --- a/src/main/java/org/traccar/MainModule.java +++ b/src/main/java/org/traccar/MainModule.java @@ -33,6 +33,7 @@ import org.traccar.broadcast.NullBroadcastService; import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.database.LdapProvider; +import org.traccar.database.OpenIdProvider; import org.traccar.database.StatisticsManager; import org.traccar.forward.EventForwarder; import org.traccar.forward.EventForwarderJson; @@ -88,7 +89,6 @@ import org.traccar.storage.MemoryStorage; import org.traccar.storage.Storage; import org.traccar.web.WebServer; import org.traccar.api.security.LoginService; -import org.traccar.api.security.OpenIDProvider; import javax.annotation.Nullable; import javax.inject.Singleton; @@ -174,9 +174,9 @@ public class MainModule extends AbstractModule { @Singleton @Provides - public static OpenIDProvider provideOpenIDProvider(Config config, LoginService loginService, Storage storage) { - if (config.getBoolean(Keys.OIDC_ENABLE)) { - return new OpenIDProvider(config, loginService, storage); + public static OpenIdProvider provideOpenIDProvider(Config config, LoginService loginService) { + if (config.hasKey(Keys.OPENID_CLIENTID)) { + return new OpenIdProvider(config, loginService); } return null; } diff --git a/src/main/java/org/traccar/api/resource/ServerResource.java b/src/main/java/org/traccar/api/resource/ServerResource.java index 4b7ee9189..9b4d82a66 100644 --- a/src/main/java/org/traccar/api/resource/ServerResource.java +++ b/src/main/java/org/traccar/api/resource/ServerResource.java @@ -16,6 +16,7 @@ package org.traccar.api.resource; import org.traccar.api.BaseResource; +import org.traccar.database.OpenIdProvider; import org.traccar.helper.model.UserUtil; import org.traccar.mail.MailManager; import org.traccar.geocoder.Geocoder; @@ -55,6 +56,10 @@ public class ServerResource extends BaseResource { @Inject private MailManager mailManager; + @Inject + @Nullable + private OpenIdProvider openIdProvider; + @Inject @Nullable private Geocoder geocoder; @@ -65,6 +70,8 @@ public class ServerResource extends BaseResource { Server server = storage.getObject(Server.class, new Request(new Columns.All())); server.setEmailEnabled(mailManager.getEmailEnabled()); server.setGeocoderEnabled(geocoder != null); + server.setOpenIdEnabled(openIdProvider != null); + server.setOpenIdForce(openIdProvider != null && openIdProvider.force); User user = permissionsService.getUser(getUserId()); if (user != null) { if (user.getAdministrator()) { diff --git a/src/main/java/org/traccar/api/resource/SessionResource.java b/src/main/java/org/traccar/api/resource/SessionResource.java index 515d7374a..3de20b8c7 100644 --- a/src/main/java/org/traccar/api/resource/SessionResource.java +++ b/src/main/java/org/traccar/api/resource/SessionResource.java @@ -17,8 +17,8 @@ package org.traccar.api.resource; import org.traccar.api.BaseResource; import org.traccar.api.security.LoginService; -import org.traccar.api.security.OpenIDProvider; import org.traccar.api.signature.TokenManager; +import org.traccar.database.OpenIdProvider; import org.traccar.helper.DataConverter; import org.traccar.helper.LogAction; import org.traccar.helper.ServletHelper; @@ -28,6 +28,7 @@ import org.traccar.storage.query.Columns; import org.traccar.storage.query.Condition; import org.traccar.storage.query.Request; +import com.nimbusds.oauth2.sdk.ParseException; import javax.annotation.security.PermitAll; import javax.inject.Inject; import javax.servlet.http.Cookie; @@ -65,7 +66,7 @@ public class SessionResource extends BaseResource { private LoginService loginService; @Inject - private OpenIDProvider openIdProvider; + private OpenIdProvider openIdProvider; @Inject private TokenManager tokenManager; @@ -169,28 +170,17 @@ public class SessionResource extends BaseResource { @Path("openid/auth") @GET public Response openIdAuth() throws IOException { - if (openIdProvider == null) { - throw new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build()); - } - - return Response.seeOther( - openIdProvider.createAuthRequest() - ).build(); + return Response.seeOther(openIdProvider.createAuthUri()).build(); } @PermitAll @Path("openid/callback") @GET - public Response requestToken() throws IOException, StorageException { - if (openIdProvider == null) { - throw new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build()); - } - - // Get full request URI - StringBuilder requestURL = new StringBuilder(request.getRequestURL().toString()); + public Response requestToken() throws IOException, StorageException, ParseException { + StringBuilder requestUrl = new StringBuilder(request.getRequestURL().toString()); String queryString = request.getQueryString(); - String requestURI = requestURL.append('?').append(queryString).toString(); + String requestUri = requestUrl.append('?').append(queryString).toString(); - return openIdProvider.handleCallback(URI.create(requestURI), request); + return openIdProvider.handleCallback(URI.create(requestUri), request); } } diff --git a/src/main/java/org/traccar/api/security/LoginService.java b/src/main/java/org/traccar/api/security/LoginService.java index 3d4f42b20..7b51667c8 100644 --- a/src/main/java/org/traccar/api/security/LoginService.java +++ b/src/main/java/org/traccar/api/security/LoginService.java @@ -89,17 +89,24 @@ public class LoginService { return null; } - public User lookup(String email) throws StorageException { + public User login(String email, String name, Boolean administrator) throws StorageException { User user = storage.getObject(User.class, new Request( - new Columns.All(), - new Condition.Equals("email", email))); + new Columns.All(), + new Condition.Equals("email", email))); if (user != null) { checkUserEnabled(user); return user; + } else { + user = new User(); + user.setName(name); + user.setEmail(email); + user.setFixedEmail(true); + user.setAdministrator(administrator); + user.setId(storage.addObject(user, new Request(new Columns.Exclude("id")))); + checkUserEnabled(user); + return user; } - - return null; } private void checkUserEnabled(User user) throws SecurityException { diff --git a/src/main/java/org/traccar/api/security/OpenIDProvider.java b/src/main/java/org/traccar/api/security/OpenIDProvider.java deleted file mode 100644 index 1e18fde43..000000000 --- a/src/main/java/org/traccar/api/security/OpenIDProvider.java +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright 2017 - 2023 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.security; - -import org.traccar.config.Config; -import org.traccar.config.Keys; -import org.traccar.api.resource.SessionResource; -import org.traccar.model.User; -import org.traccar.storage.Storage; -import org.traccar.storage.StorageException; -import org.traccar.storage.query.Request; -import org.traccar.storage.query.Columns; -import org.traccar.helper.LogAction; -import org.traccar.helper.ServletHelper; - -import java.net.URI; -import java.net.URISyntaxException; -import java.util.Date; -import java.util.List; -import java.io.IOException; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Response; -import com.google.inject.Inject; - -import com.nimbusds.oauth2.sdk.http.HTTPResponse; -import com.nimbusds.oauth2.sdk.AuthorizationCode; -import com.nimbusds.oauth2.sdk.ResponseType; -import com.nimbusds.oauth2.sdk.Scope; -import com.nimbusds.oauth2.sdk.AuthorizationGrant; -import com.nimbusds.oauth2.sdk.TokenRequest; -import com.nimbusds.oauth2.sdk.TokenResponse; -import com.nimbusds.oauth2.sdk.AuthorizationCodeGrant; -import com.nimbusds.oauth2.sdk.AuthorizationErrorResponse; -import com.nimbusds.oauth2.sdk.ParseException; -import com.nimbusds.oauth2.sdk.AuthorizationResponse; -import com.nimbusds.oauth2.sdk.auth.Secret; -import com.nimbusds.oauth2.sdk.auth.ClientSecretBasic; -import com.nimbusds.oauth2.sdk.auth.ClientAuthentication; -import com.nimbusds.oauth2.sdk.token.BearerAccessToken; -import com.nimbusds.oauth2.sdk.id.State; -import com.nimbusds.oauth2.sdk.id.ClientID; -import com.nimbusds.openid.connect.sdk.OIDCTokenResponse; -import com.nimbusds.openid.connect.sdk.Nonce; -import com.nimbusds.openid.connect.sdk.OIDCTokenResponseParser; -import com.nimbusds.openid.connect.sdk.UserInfoResponse; -import com.nimbusds.openid.connect.sdk.UserInfoRequest; -import com.nimbusds.openid.connect.sdk.AuthenticationRequest; - -import com.nimbusds.openid.connect.sdk.claims.UserInfo; - -public class OpenIDProvider { - public final Boolean force; - private final ClientID clientId; - private final Secret clientSecret; - private URI callbackUrl; - private URI authUrl; - private URI tokenUrl; - private URI userInfoUrl; - private URI baseUrl; - private final String adminGroup; - - private Config config; - private LoginService loginService; - private Storage storage; - - @Inject - public OpenIDProvider(Config config, LoginService loginService, Storage storage) { - force = config.getBoolean(Keys.OIDC_FORCE); - clientId = new ClientID(config.getString(Keys.OIDC_CLIENTID)); - clientSecret = new Secret(config.getString(Keys.OIDC_CLIENTSECRET)); - - this.config = config; - this.storage = storage; - this.loginService = loginService; - - try { - callbackUrl = new URI(config.getString(Keys.WEB_URL, "") + "/api/session/openid/callback"); - authUrl = new URI(config.getString(Keys.OIDC_AUTHURL, "")); - tokenUrl = new URI(config.getString(Keys.OIDC_TOKENURL, "")); - userInfoUrl = new URI(config.getString(Keys.OIDC_USERINFOURL, "")); - baseUrl = new URI(config.getString(Keys.WEB_URL, "")); - } catch (URISyntaxException e) { - } - - adminGroup = config.getString(Keys.OIDC_ADMINGROUP); - } - - public URI createAuthRequest() { - AuthenticationRequest request = new AuthenticationRequest.Builder( - new ResponseType("code"), - new Scope("openid", "profile", "email", "groups"), - this.clientId, - this.callbackUrl) - .endpointURI(this.authUrl) - .state(new State()) - .nonce(new Nonce()) - .build(); - - return request.toURI(); - } - - private OIDCTokenResponse getToken(AuthorizationCode code) { - // Credentials to authenticate us to the token endpoint - ClientAuthentication clientAuth = new ClientSecretBasic(this.clientId, this.clientSecret); - AuthorizationGrant codeGrant = new AuthorizationCodeGrant(code, this.callbackUrl); - - TokenRequest request = new TokenRequest(this.tokenUrl, clientAuth, codeGrant); - TokenResponse tokenResponse; - - try { - HTTPResponse tokenReq = request.toHTTPRequest().send(); - tokenResponse = OIDCTokenResponseParser.parse(tokenReq); - if (!tokenResponse.indicatesSuccess()) { - return null; - } - - return (OIDCTokenResponse) tokenResponse.toSuccessResponse(); - } catch (IOException e) { - return null; - } catch (ParseException e) { - return null; - } - } - - private AuthorizationCode parseCallback(URI requri) throws WebApplicationException { - AuthorizationResponse response; - - try { - response = AuthorizationResponse.parse(requri); - } catch (ParseException e) { - return null; - } - - if (!response.indicatesSuccess()) { - AuthorizationErrorResponse error = response.toErrorResponse(); - throw new WebApplicationException(Response.status(403).entity(error.getErrorObject().getDescription()).build()); - } - - return response.toSuccessResponse().getAuthorizationCode(); - } - - private UserInfo getUserInfo(BearerAccessToken token) { - UserInfoResponse userInfoResponse; - - try { - HTTPResponse httpResponse = new UserInfoRequest(this.userInfoUrl, token) - .toHTTPRequest() - .send(); - - userInfoResponse = UserInfoResponse.parse(httpResponse); - } catch (IOException e) { - return null; - } catch (ParseException e) { - return null; - } - - if (!userInfoResponse.indicatesSuccess()) { - // User info request failed - usually from expiring - return null; - } - - return userInfoResponse.toSuccessResponse().getUserInfo(); - } - - private User createUser(String name, String email, Boolean administrator) throws StorageException { - User user = new User(); - - user.setName(name); - user.setEmail(email); - user.setFixedEmail(true); - user.setDeviceLimit(this.config.getInteger(Keys.USERS_DEFAULT_DEVICE_LIMIT)); - - int expirationDays = this.config.getInteger(Keys.USERS_DEFAULT_EXPIRATION_DAYS); - - if (expirationDays > 0) { - user.setExpirationTime(new Date(System.currentTimeMillis() + expirationDays * 86400000L)); - } - - if (administrator) { - user.setAdministrator(true); - } - - user.setId(this.storage.addObject(user, new Request(new Columns.Exclude("id")))); - - return user; - } - - public Response handleCallback(URI requri, HttpServletRequest request) throws StorageException, WebApplicationException { - // Parse callback - AuthorizationCode authCode = this.parseCallback(requri); - - if (authCode == null) { - return Response.status(403).entity( "Invalid OpenID Connect callback.").build(); - } - - // Get token from IDP - OIDCTokenResponse tokens = this.getToken(authCode); - - if (tokens == null) { - return Response.status(403).entity("Unable to authenticate with the OpenID Connect provider. Please try again.").build(); - } - - BearerAccessToken bearerToken = tokens.getOIDCTokens().getBearerAccessToken(); - - // Get user info from IDP - UserInfo idpUser = this.getUserInfo(bearerToken); - - if (idpUser == null) { - return Response.status(500).entity("Failed to access OpenID Connect user info endpoint. Please contact your administrator.").build(); - } - - String email = idpUser.getEmailAddress(); - String name = idpUser.getName(); - - // Check if user exists - User user = this.loginService.lookup(email); - - // If user does not exist, create one - if (user == null) { - List groups = idpUser.getStringListClaim("groups"); - Boolean administrator = groups.contains(this.adminGroup); - user = this.createUser(name, email, administrator); - } - - // Set user session and redirect to homepage - request.getSession().setAttribute(SessionResource.USER_ID_KEY, user.getId()); - LogAction.login(user.getId(), ServletHelper.retrieveRemoteAddress(request)); - return Response.seeOther( - baseUrl).build(); - } -} diff --git a/src/main/java/org/traccar/config/Keys.java b/src/main/java/org/traccar/config/Keys.java index ace4c36af..a666667d4 100644 --- a/src/main/java/org/traccar/config/Keys.java +++ b/src/main/java/org/traccar/config/Keys.java @@ -610,61 +610,67 @@ public final class Keys { "ldap.adminGroup", List.of(KeyType.CONFIG)); - /** - * OIDC enable. + * Force OpenID Connect authentication. When enabled, the Traccar login page will be skipped + * and users are redirected to the OpenID Connect provider. */ - public static final ConfigKey OIDC_ENABLE = new BooleanConfigKey( - "oidc.enable", + public static final ConfigKey OPENID_FORCE = new BooleanConfigKey( + "openid.force", List.of(KeyType.CONFIG)); /** - * Force OIDC authentication. + * OpenID Connect Client ID. + * This is a unique ID assigned to each application you register with your identity provider. + * Required to enable SSO. */ - public static final ConfigKey OIDC_FORCE = new BooleanConfigKey( - "oidc.force", + public static final ConfigKey OPENID_CLIENTID = new StringConfigKey( + "openid.clientId", List.of(KeyType.CONFIG)); /** - * OIDC Client ID. + * OpenID Connect Client Secret. + * This is a secret assigned to each application you register with your identity provider. + * Required to enable SSO. */ - public static final ConfigKey OIDC_CLIENTID = new StringConfigKey( - "oidc.clientId", + public static final ConfigKey OPENID_CLIENTSECRET = new StringConfigKey( + "openid.clientSecret", List.of(KeyType.CONFIG)); /** - * OIDC Client Secret. + * OpenID Connect Authorization URL. + * This can usually be found in the documentation of your identity provider or by using the well-known + * configuration endpoint, eg. https://auth.example.com//.well-known/openid-configuration + * Required to enable SSO. */ - public static final ConfigKey OIDC_CLIENTSECRET = new StringConfigKey( - "oidc.clientSecret", + public static final ConfigKey OPENID_AUTHURL = new StringConfigKey( + "openid.authUrl", List.of(KeyType.CONFIG)); - /** - * OIDC Authorization URL. + * OpenID Connect Token URL. + * This can be found in the same ways at openid.authUrl. + * Required to enable SSO. */ - public static final ConfigKey OIDC_AUTHURL = new StringConfigKey( - "oidc.authUrl", - List.of(KeyType.CONFIG)); - /** - * OIDC Token URL. - */ - public static final ConfigKey OIDC_TOKENURL = new StringConfigKey( - "oidc.tokenUrl", + public static final ConfigKey OPENID_TOKENURL = new StringConfigKey( + "openid.tokenUrl", List.of(KeyType.CONFIG)); /** - * OIDC User Info URL. + * OpenID Connect User Info URL. + * This can be found in the same ways at openid.authUrl. + * Required to enable SSO. */ - public static final ConfigKey OIDC_USERINFOURL = new StringConfigKey( - "oidc.userInfoUrl", + public static final ConfigKey OPENID_USERINFOURL = new StringConfigKey( + "openid.userInfoUrl", List.of(KeyType.CONFIG)); /** - * OIDC group to grant admin access. + * OpenID Connect group to grant admin access. + * Defaults to admins. */ - public static final ConfigKey OIDC_ADMINGROUP = new StringConfigKey( - "oidc.adminGroup", - List.of(KeyType.CONFIG)); + public static final ConfigKey OPENID_ADMINGROUP = new StringConfigKey( + "openid.adminGroup", + List.of(KeyType.CONFIG), + "admins"); /** * If no data is reported by a device for the given amount of time, status changes from online to unknown. Value is @@ -1629,7 +1635,7 @@ public final class Keys { List.of(KeyType.CONFIG)); /** - * Public URL for the web app. Used for notification and report link. + * Public URL for the web app. Used for notification, report link and OpenID Connect. * If not provided, Traccar will attempt to get a URL from the server IP address, but it might be a local address. */ public static final ConfigKey WEB_URL = new StringConfigKey( diff --git a/src/main/java/org/traccar/database/OpenIdProvider.java b/src/main/java/org/traccar/database/OpenIdProvider.java new file mode 100644 index 000000000..fc1409d14 --- /dev/null +++ b/src/main/java/org/traccar/database/OpenIdProvider.java @@ -0,0 +1,183 @@ +/* + * Copyright 2023 Daniel Raper (me@danr.uk) + * + * 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.database; + +import org.traccar.config.Config; +import org.traccar.config.Keys; +import org.traccar.api.resource.SessionResource; +import org.traccar.api.security.LoginService; +import org.traccar.model.User; +import org.traccar.storage.StorageException; +import org.traccar.helper.LogAction; +import org.traccar.helper.ServletHelper; + +import java.net.URI; +import java.net.URISyntaxException; +import java.io.IOException; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response; +import com.google.inject.Inject; + +import com.nimbusds.oauth2.sdk.http.HTTPResponse; +import com.nimbusds.oauth2.sdk.AuthorizationCode; +import com.nimbusds.oauth2.sdk.ResponseType; +import com.nimbusds.oauth2.sdk.Scope; +import com.nimbusds.oauth2.sdk.AuthorizationGrant; +import com.nimbusds.oauth2.sdk.TokenRequest; +import com.nimbusds.oauth2.sdk.TokenResponse; +import com.nimbusds.oauth2.sdk.AuthorizationCodeGrant; +import com.nimbusds.oauth2.sdk.AuthorizationErrorResponse; +import com.nimbusds.oauth2.sdk.ParseException; +import com.nimbusds.oauth2.sdk.AuthorizationResponse; +import com.nimbusds.oauth2.sdk.auth.Secret; +import com.nimbusds.oauth2.sdk.auth.ClientSecretBasic; +import com.nimbusds.oauth2.sdk.auth.ClientAuthentication; +import com.nimbusds.oauth2.sdk.token.BearerAccessToken; +import com.nimbusds.oauth2.sdk.id.State; +import com.nimbusds.oauth2.sdk.id.ClientID; +import com.nimbusds.openid.connect.sdk.OIDCTokenResponse; +import com.nimbusds.openid.connect.sdk.Nonce; +import com.nimbusds.openid.connect.sdk.OIDCTokenResponseParser; +import com.nimbusds.openid.connect.sdk.UserInfoResponse; +import com.nimbusds.openid.connect.sdk.UserInfoRequest; +import com.nimbusds.openid.connect.sdk.AuthenticationRequest; + +import com.nimbusds.openid.connect.sdk.claims.UserInfo; + +public class OpenIdProvider { + public final Boolean force; + private final ClientID clientId; + private final Secret clientSecret; + private URI callbackUrl; + private URI authUrl; + private URI tokenUrl; + private URI userInfoUrl; + private URI baseUrl; + private final String adminGroup; + + private LoginService loginService; + + @Inject + public OpenIdProvider(Config config, LoginService loginService) { + force = config.getBoolean(Keys.OPENID_FORCE); + clientId = new ClientID(config.getString(Keys.OPENID_CLIENTID)); + clientSecret = new Secret(config.getString(Keys.OPENID_CLIENTSECRET)); + + this.loginService = loginService; + + try { + callbackUrl = new URI(config.getString(Keys.WEB_URL, "") + "/api/session/openid/callback"); + authUrl = new URI(config.getString(Keys.OPENID_AUTHURL, "")); + tokenUrl = new URI(config.getString(Keys.OPENID_TOKENURL, "")); + userInfoUrl = new URI(config.getString(Keys.OPENID_USERINFOURL, "")); + baseUrl = new URI(config.getString(Keys.WEB_URL, "")); + } catch (URISyntaxException e) { + } + + adminGroup = config.getString(Keys.OPENID_ADMINGROUP); + } + + public URI createAuthUri() { + AuthenticationRequest request = new AuthenticationRequest.Builder( + new ResponseType("code"), + new Scope("openid", "profile", "email", "groups"), + clientId, + callbackUrl) + .endpointURI(authUrl) + .state(new State()) + .nonce(new Nonce()) + .build(); + + return request.toURI(); + } + + private OIDCTokenResponse getToken(AuthorizationCode code) { + // Credentials to authenticate us to the token endpoint + ClientAuthentication clientAuth = new ClientSecretBasic(clientId, clientSecret); + AuthorizationGrant codeGrant = new AuthorizationCodeGrant(code, callbackUrl); + + TokenRequest request = new TokenRequest(tokenUrl, clientAuth, codeGrant); + TokenResponse tokenResponse; + + try { + HTTPResponse tokenReq = request.toHTTPRequest().send(); + tokenResponse = OIDCTokenResponseParser.parse(tokenReq); + if (!tokenResponse.indicatesSuccess()) { + return null; + } + + return (OIDCTokenResponse) tokenResponse.toSuccessResponse(); + } catch (IOException e) { + return null; + } catch (ParseException e) { + return null; + } + } + + private UserInfo getUserInfo(BearerAccessToken token) throws IOException, ParseException { + UserInfoResponse userInfoResponse; + + HTTPResponse httpResponse = new UserInfoRequest(userInfoUrl, token) + .toHTTPRequest() + .send(); + + userInfoResponse = UserInfoResponse.parse(httpResponse); + + if (!userInfoResponse.indicatesSuccess()) { + // User info request failed - usually from expiring + return null; + } + + return userInfoResponse.toSuccessResponse().getUserInfo(); + } + + public Response handleCallback(URI requestUri, HttpServletRequest request) throws StorageException, ParseException, IOException, WebApplicationException { + AuthorizationResponse response = AuthorizationResponse.parse(requestUri); + + if (!response.indicatesSuccess()) { + AuthorizationErrorResponse error = response.toErrorResponse(); + throw new WebApplicationException(Response.status(403).entity(error.getErrorObject().getDescription()).build()); + } + + AuthorizationCode authCode = response.toSuccessResponse().getAuthorizationCode(); + + if (authCode == null) { + return Response.status(403).entity( "Invalid OpenID Connect callback.").build(); + } + + OIDCTokenResponse tokens = getToken(authCode); + + if (tokens == null) { + return Response.status(403).entity("Unable to authenticate with the OpenID Connect provider. Please try again.").build(); + } + + BearerAccessToken bearerToken = tokens.getOIDCTokens().getBearerAccessToken(); + + UserInfo userInfo = getUserInfo(bearerToken); + + if (userInfo == null) { + return Response.status(500).entity("Failed to access OpenID Connect user info endpoint. Please contact your administrator.").build(); + } + + User user = loginService.login(userInfo.getEmailAddress(), userInfo.getName(), userInfo.getStringListClaim("groups").contains(adminGroup)); + + request.getSession().setAttribute(SessionResource.USER_ID_KEY, user.getId()); + LogAction.login(user.getId(), ServletHelper.retrieveRemoteAddress(request)); + return Response.seeOther( + baseUrl).build(); + } +} diff --git a/src/main/java/org/traccar/model/Server.java b/src/main/java/org/traccar/model/Server.java index 4c6e10b5e..b790ca472 100644 --- a/src/main/java/org/traccar/model/Server.java +++ b/src/main/java/org/traccar/model/Server.java @@ -17,15 +17,13 @@ package org.traccar.model; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import org.traccar.Main; -import org.traccar.api.security.OpenIDProvider; import org.traccar.storage.QueryIgnore; import org.traccar.storage.StorageName; @StorageName("tc_servers") @JsonIgnoreProperties(ignoreUnknown = true) public class Server extends ExtendedModel implements UserRestrictions { - + private boolean registration; public boolean getRegistration() { @@ -264,15 +262,27 @@ public class Server extends ExtendedModel implements UserRestrictions { this.newServer = newServer; } + private boolean openIdEnabled; + + @QueryIgnore + public boolean getOpenIdEnabled() { + return openIdEnabled; + } + + @QueryIgnore + public void setOpenIdEnabled(boolean openIdEnabled) { + this.openIdEnabled = openIdEnabled; + } + + private boolean openIdForce; + @QueryIgnore - public boolean getOidcEnabled() { - OpenIDProvider oidc = Main.getInjector().getInstance(OpenIDProvider.class); - return oidc != null; + public boolean getOpenIdForce() { + return openIdForce; } @QueryIgnore - public boolean getOidcForce() { - OpenIDProvider oidc = Main.getInjector().getInstance(OpenIDProvider.class); - return oidc != null && oidc.force; + public void setOpenIdForce(boolean openIdForce) { + this.openIdForce = openIdForce; } } -- cgit v1.2.3 From c2637180549e69fc302f91f4685036e2697ae7bf Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sat, 22 Apr 2023 09:11:06 -0700 Subject: Fix report scheduling --- src/main/java/org/traccar/model/Calendar.java | 13 ++++++++++--- src/main/java/org/traccar/schedule/TaskReports.java | 12 +++++------- src/test/java/org/traccar/calendar/CalendarTest.java | 15 +++++++-------- 3 files changed, 22 insertions(+), 18 deletions(-) (limited to 'src/main/java/org/traccar/model') diff --git a/src/main/java/org/traccar/model/Calendar.java b/src/main/java/org/traccar/model/Calendar.java index 62c51cc4a..03f1995ba 100644 --- a/src/main/java/org/traccar/model/Calendar.java +++ b/src/main/java/org/traccar/model/Calendar.java @@ -34,6 +34,7 @@ import java.time.Duration; import java.util.Collection; import java.util.Date; import java.util.List; +import java.util.stream.Collectors; @StorageName("tc_calendars") public class Calendar extends ExtendedModel { @@ -68,16 +69,22 @@ public class Calendar extends ExtendedModel { return calendar; } - public Collection findEvents(Date date) { + private Collection findEvents(Date date) { if (calendar != null) { - Period period = new Period(new DateTime(date), Duration.ZERO); - Filter filter = new Filter<>(new PeriodRule<>(period)); + var filter = new Filter(new PeriodRule<>(new Period(new DateTime(date), Duration.ZERO))); return filter.filter(calendar.getComponents(CalendarComponent.VEVENT)); } else { return List.of(); } } + public Collection findPeriods(Date date) { + var calendarDate = new net.fortuna.ical4j.model.Date(date); + return findEvents(date).stream() + .flatMap((event) -> event.getConsumedTime(calendarDate, calendarDate).stream()) + .collect(Collectors.toSet()); + } + public boolean checkMoment(Date date) { return !findEvents(date).isEmpty(); } diff --git a/src/main/java/org/traccar/schedule/TaskReports.java b/src/main/java/org/traccar/schedule/TaskReports.java index 004a6078c..176b6d537 100644 --- a/src/main/java/org/traccar/schedule/TaskReports.java +++ b/src/main/java/org/traccar/schedule/TaskReports.java @@ -18,7 +18,7 @@ package org.traccar.schedule; import com.google.inject.Injector; import com.google.inject.servlet.RequestScoper; import com.google.inject.servlet.ServletScopes; -import net.fortuna.ical4j.model.component.VEvent; +import net.fortuna.ical4j.model.Period; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.model.BaseModel; @@ -77,16 +77,14 @@ public class TaskReports implements ScheduleTask { Calendar calendar = storage.getObject(Calendar.class, new Request( new Columns.All(), new Condition.Equals("id", report.getCalendarId()))); - var lastEvents = calendar.findEvents(lastCheck); - var currentEvents = calendar.findEvents(currentCheck); + var lastEvents = calendar.findPeriods(lastCheck); + var currentEvents = calendar.findPeriods(currentCheck); if (!lastEvents.isEmpty() && currentEvents.isEmpty()) { - VEvent event = lastEvents.iterator().next(); - Date from = event.getStartDate().getDate(); - Date to = event.getEndDate().getDate(); + Period period = lastEvents.iterator().next(); RequestScoper scope = ServletScopes.scopeRequest(Collections.emptyMap()); try (RequestScoper.CloseableScope ignored = scope.open()) { - executeReport(report, from, to); + executeReport(report, period.getStart(), period.getEnd()); } } } diff --git a/src/test/java/org/traccar/calendar/CalendarTest.java b/src/test/java/org/traccar/calendar/CalendarTest.java index 6f4b30370..4106f89a9 100644 --- a/src/test/java/org/traccar/calendar/CalendarTest.java +++ b/src/test/java/org/traccar/calendar/CalendarTest.java @@ -11,6 +11,7 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; public class CalendarTest { @@ -45,14 +46,12 @@ public class CalendarTest { calendar.setData(calendarString.getBytes()); DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ssX"); - Date date = format.parse("2016-12-13 22:59:59+05"); - assertTrue(!calendar.checkMoment(date)); - date = format.parse("2016-12-13 23:00:01+05"); - assertTrue(calendar.checkMoment(date)); + assertFalse(calendar.checkMoment(format.parse("2016-12-13 22:59:59+05"))); + assertTrue(calendar.checkMoment(format.parse("2016-12-13 23:00:01+05"))); + assertTrue(calendar.checkMoment(format.parse("2016-12-13 06:59:59+05"))); + assertFalse(calendar.checkMoment(format.parse("2016-12-13 07:00:01+05"))); - date = format.parse("2016-12-13 06:59:59+05"); - assertTrue(calendar.checkMoment(date)); - date = format.parse("2016-12-13 07:00:01+05"); - assertTrue(!calendar.checkMoment(date)); + var periods = calendar.findPeriods(format.parse("2016-12-13 06:59:59+05")); + assertFalse(periods.isEmpty()); } } -- cgit v1.2.3 From 30bafaed42e74863c5ca68a33c87f39d1e2de93d Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sun, 23 Apr 2023 08:29:22 -0700 Subject: Move geofenceIds to position --- schema/changelog-5.8.xml | 18 ++++++++ schema/changelog-master.xml | 1 + src/main/java/org/traccar/BasePipelineFactory.java | 2 + .../java/org/traccar/handler/GeofenceHandler.java | 52 ++++++++++++++++++++++ .../handler/events/GeofenceEventHandler.java | 41 +++++------------ .../handler/events/OverspeedEventHandler.java | 4 +- src/main/java/org/traccar/model/Device.java | 21 +-------- src/main/java/org/traccar/model/Position.java | 16 +++++++ swagger.json | 12 ++--- 9 files changed, 109 insertions(+), 58 deletions(-) create mode 100644 schema/changelog-5.8.xml create mode 100644 src/main/java/org/traccar/handler/GeofenceHandler.java (limited to 'src/main/java/org/traccar/model') diff --git a/schema/changelog-5.8.xml b/schema/changelog-5.8.xml new file mode 100644 index 000000000..ec54bf17f --- /dev/null +++ b/schema/changelog-5.8.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + diff --git a/schema/changelog-master.xml b/schema/changelog-master.xml index 1587cc789..dd2bcc8a7 100644 --- a/schema/changelog-master.xml +++ b/schema/changelog-master.xml @@ -38,5 +38,6 @@ + diff --git a/src/main/java/org/traccar/BasePipelineFactory.java b/src/main/java/org/traccar/BasePipelineFactory.java index 38b077980..d04fed383 100644 --- a/src/main/java/org/traccar/BasePipelineFactory.java +++ b/src/main/java/org/traccar/BasePipelineFactory.java @@ -33,6 +33,7 @@ import org.traccar.handler.DistanceHandler; import org.traccar.handler.EngineHoursHandler; import org.traccar.handler.FilterHandler; import org.traccar.handler.GeocoderHandler; +import org.traccar.handler.GeofenceHandler; import org.traccar.handler.GeolocationHandler; import org.traccar.handler.HemisphereHandler; import org.traccar.handler.MotionHandler; @@ -149,6 +150,7 @@ public abstract class BasePipelineFactory extends ChannelInitializer { DistanceHandler.class, RemoteAddressHandler.class, FilterHandler.class, + GeofenceHandler.class, GeocoderHandler.class, SpeedLimitHandler.class, MotionHandler.class, diff --git a/src/main/java/org/traccar/handler/GeofenceHandler.java b/src/main/java/org/traccar/handler/GeofenceHandler.java new file mode 100644 index 000000000..fe15cef8e --- /dev/null +++ b/src/main/java/org/traccar/handler/GeofenceHandler.java @@ -0,0 +1,52 @@ +/* + * Copyright 2023 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.handler; + +import io.netty.channel.ChannelHandler; +import org.traccar.BaseDataHandler; +import org.traccar.config.Config; +import org.traccar.helper.model.GeofenceUtil; +import org.traccar.model.Position; +import org.traccar.session.cache.CacheManager; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.List; + +@Singleton +@ChannelHandler.Sharable +public class GeofenceHandler extends BaseDataHandler { + + private final Config config; + private final CacheManager cacheManager; + + @Inject + public GeofenceHandler(Config config, CacheManager cacheManager) { + this.config = config; + this.cacheManager = cacheManager; + } + + @Override + protected Position handlePosition(Position position) { + + List geofenceIds = GeofenceUtil.getCurrentGeofences(config, cacheManager, position); + if (!geofenceIds.isEmpty()) { + position.setGeofenceIds(geofenceIds); + } + return position; + } + +} diff --git a/src/main/java/org/traccar/handler/events/GeofenceEventHandler.java b/src/main/java/org/traccar/handler/events/GeofenceEventHandler.java index 9414f4b31..0ab9ca217 100644 --- a/src/main/java/org/traccar/handler/events/GeofenceEventHandler.java +++ b/src/main/java/org/traccar/handler/events/GeofenceEventHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2023 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. @@ -17,20 +17,14 @@ package org.traccar.handler.events; import io.netty.channel.ChannelHandler; import org.traccar.config.Config; -import org.traccar.helper.model.GeofenceUtil; import org.traccar.helper.model.PositionUtil; import org.traccar.model.Calendar; -import org.traccar.model.Device; import org.traccar.model.Event; import org.traccar.model.Geofence; import org.traccar.model.Position; import org.traccar.session.ConnectionManager; import org.traccar.session.cache.CacheManager; import org.traccar.storage.Storage; -import org.traccar.storage.StorageException; -import org.traccar.storage.query.Columns; -import org.traccar.storage.query.Condition; -import org.traccar.storage.query.Request; import javax.inject.Inject; import javax.inject.Singleton; @@ -59,36 +53,21 @@ public class GeofenceEventHandler extends BaseEventHandler { @Override protected Map analyzePosition(Position position) { - Device device = cacheManager.getObject(Device.class, position.getDeviceId()); - if (device == null) { - return null; - } - if (!PositionUtil.isLatest(cacheManager, position) || !position.getValid()) { + if (!PositionUtil.isLatest(cacheManager, position)) { return null; } - List currentGeofences = GeofenceUtil.getCurrentGeofences(config, cacheManager, position); List oldGeofences = new ArrayList<>(); - if (device.getGeofenceIds() != null) { - oldGeofences.addAll(device.getGeofenceIds()); + Position lastPosition = cacheManager.getPosition(position.getDeviceId()); + if (lastPosition != null && lastPosition.getGeofenceIds() != null) { + oldGeofences.addAll(lastPosition.getGeofenceIds()); } - List newGeofences = new ArrayList<>(currentGeofences); - newGeofences.removeAll(oldGeofences); - oldGeofences.removeAll(currentGeofences); - - - if (!oldGeofences.isEmpty() || !newGeofences.isEmpty()) { - device.setGeofenceIds(currentGeofences.isEmpty() ? null : currentGeofences); - - try { - storage.updateObject(device, new Request( - new Columns.Include("geofenceIds"), - new Condition.Equals("id", device.getId()))); - } catch (StorageException e) { - throw new RuntimeException("Update device geofences error", e); - } - connectionManager.updateDevice(true, device); + List newGeofences = new ArrayList<>(); + if (position.getGeofenceIds() != null) { + newGeofences.addAll(position.getGeofenceIds()); + newGeofences.removeAll(oldGeofences); + oldGeofences.removeAll(position.getGeofenceIds()); } Map events = new HashMap<>(); diff --git a/src/main/java/org/traccar/handler/events/OverspeedEventHandler.java b/src/main/java/org/traccar/handler/events/OverspeedEventHandler.java index 4d6aa8857..40f1c7442 100644 --- a/src/main/java/org/traccar/handler/events/OverspeedEventHandler.java +++ b/src/main/java/org/traccar/handler/events/OverspeedEventHandler.java @@ -84,8 +84,8 @@ public class OverspeedEventHandler extends BaseEventHandler { double geofenceSpeedLimit = 0; long overspeedGeofenceId = 0; - if (device.getGeofenceIds() != null) { - for (long geofenceId : device.getGeofenceIds()) { + if (position.getGeofenceIds() != null) { + for (long geofenceId : position.getGeofenceIds()) { Geofence geofence = cacheManager.getObject(Geofence.class, geofenceId); if (geofence != null) { double currentSpeedLimit = geofence.getDouble(Keys.EVENT_OVERSPEED_LIMIT.getKey()); diff --git a/src/main/java/org/traccar/model/Device.java b/src/main/java/org/traccar/model/Device.java index b8c87921d..b7cffac49 100644 --- a/src/main/java/org/traccar/model/Device.java +++ b/src/main/java/org/traccar/model/Device.java @@ -15,14 +15,12 @@ */ package org.traccar.model; -import java.util.Date; -import java.util.List; -import java.util.stream.Collectors; - import com.fasterxml.jackson.annotation.JsonIgnore; import org.traccar.storage.QueryIgnore; import org.traccar.storage.StorageName; +import java.util.Date; + @StorageName("tc_devices") public class Device extends GroupedModel implements Disableable { @@ -83,21 +81,6 @@ public class Device extends GroupedModel implements Disableable { this.positionId = positionId; } - private List geofenceIds; - - @QueryIgnore - public List getGeofenceIds() { - return geofenceIds; - } - - public void setGeofenceIds(List geofenceIds) { - if (geofenceIds != null) { - this.geofenceIds = geofenceIds.stream().map(Number::longValue).collect(Collectors.toList()); - } else { - this.geofenceIds = null; - } - } - private String phone; public String getPhone() { diff --git a/src/main/java/org/traccar/model/Position.java b/src/main/java/org/traccar/model/Position.java index 2bd71f383..3ed340703 100644 --- a/src/main/java/org/traccar/model/Position.java +++ b/src/main/java/org/traccar/model/Position.java @@ -16,6 +16,8 @@ package org.traccar.model; import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; import com.fasterxml.jackson.annotation.JsonIgnore; import org.traccar.storage.QueryIgnore; @@ -309,6 +311,20 @@ public class Position extends Message { this.network = network; } + private List geofenceIds; + + public List getGeofenceIds() { + return geofenceIds; + } + + public void setGeofenceIds(List geofenceIds) { + if (geofenceIds != null) { + this.geofenceIds = geofenceIds.stream().map(Number::longValue).collect(Collectors.toList()); + } else { + this.geofenceIds = null; + } + } + @JsonIgnore @QueryIgnore @Override diff --git a/swagger.json b/swagger.json index dde673e19..3d3842b67 100644 --- a/swagger.json +++ b/swagger.json @@ -2725,6 +2725,12 @@ "type": "object", "properties": {} }, + "geofenceIds": { + "type": "array", + "items": { + "type": "integer" + } + }, "attributes": { "type": "object", "properties": {} @@ -2927,12 +2933,6 @@ "category": { "type": "string" }, - "geofenceIds": { - "type": "array", - "items": { - "type": "integer" - } - }, "attributes": { "type": "object", "properties": {} -- cgit v1.2.3 From e44e9f860b11a50b3e39e3895ea9ea5624282b78 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Mon, 15 May 2023 12:06:14 -0700 Subject: Add HTML branding overrides --- src/main/java/org/traccar/api/MediaFilter.java | 11 +-- src/main/java/org/traccar/model/ExtendedModel.java | 8 ++- .../java/org/traccar/web/CharResponseWrapper.java | 82 ++++++++++++++++++++++ src/main/java/org/traccar/web/OverrideFilter.java | 71 +++++++++++++++++++ src/main/java/org/traccar/web/WebModule.java | 4 +- 5 files changed, 163 insertions(+), 13 deletions(-) create mode 100644 src/main/java/org/traccar/web/CharResponseWrapper.java create mode 100644 src/main/java/org/traccar/web/OverrideFilter.java (limited to 'src/main/java/org/traccar/model') diff --git a/src/main/java/org/traccar/api/MediaFilter.java b/src/main/java/org/traccar/api/MediaFilter.java index ab75bdc5d..e6556189a 100644 --- a/src/main/java/org/traccar/api/MediaFilter.java +++ b/src/main/java/org/traccar/api/MediaFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2018 - 2023 Anton Tananaev (anton@traccar.org) * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -32,7 +32,6 @@ import javax.inject.Inject; import javax.inject.Singleton; import javax.servlet.Filter; import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; @@ -57,10 +56,6 @@ public class MediaFilter implements Filter { this.permissionsServiceProvider = permissionsServiceProvider; } - @Override - public void init(FilterConfig filterConfig) throws ServletException { - } - @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { @@ -99,8 +94,4 @@ public class MediaFilter implements Filter { } } - @Override - public void destroy() { - } - } diff --git a/src/main/java/org/traccar/model/ExtendedModel.java b/src/main/java/org/traccar/model/ExtendedModel.java index 7a61eda8c..6a4f502f7 100644 --- a/src/main/java/org/traccar/model/ExtendedModel.java +++ b/src/main/java/org/traccar/model/ExtendedModel.java @@ -89,14 +89,18 @@ public class ExtendedModel extends BaseModel { } } - public String getString(String key) { + public String getString(String key, String defaultValue) { if (attributes.containsKey(key)) { return attributes.get(key).toString(); } else { - return null; + return defaultValue; } } + public String getString(String key) { + return getString(key, null); + } + public double getDouble(String key) { if (attributes.containsKey(key)) { Object value = attributes.get(key); diff --git a/src/main/java/org/traccar/web/CharResponseWrapper.java b/src/main/java/org/traccar/web/CharResponseWrapper.java new file mode 100644 index 000000000..0e7976ce0 --- /dev/null +++ b/src/main/java/org/traccar/web/CharResponseWrapper.java @@ -0,0 +1,82 @@ +/* + * Copyright 2023 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.web; + +import javax.servlet.ServletOutputStream; +import javax.servlet.WriteListener; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpServletResponseWrapper; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +public class CharResponseWrapper extends HttpServletResponseWrapper { + + private final ByteArrayOutputStream capture; + private ServletOutputStream output; + + public CharResponseWrapper(HttpServletResponse response) { + super(response); + capture = new ByteArrayOutputStream(response.getBufferSize()); + } + + @Override + public ServletOutputStream getOutputStream() { + if (output == null) { + output = new ServletOutputStream() { + @Override + public boolean isReady() { + return true; + } + + @Override + public void setWriteListener(WriteListener writeListener) { + } + + @Override + public void write(int b) { + capture.write(b); + } + + @Override + public void flush() throws IOException { + capture.flush(); + } + + @Override + public void close() throws IOException { + capture.close(); + } + }; + } + return output; + } + + @Override + public void flushBuffer() throws IOException { + super.flushBuffer(); + if (output != null) { + output.flush(); + } + } + + public String getCaptureAsString() throws IOException { + if (output != null) { + output.close(); + } + return capture.toString(getCharacterEncoding()); + } + +} diff --git a/src/main/java/org/traccar/web/OverrideFilter.java b/src/main/java/org/traccar/web/OverrideFilter.java new file mode 100644 index 000000000..cb69dfbfa --- /dev/null +++ b/src/main/java/org/traccar/web/OverrideFilter.java @@ -0,0 +1,71 @@ +/* + * Copyright 2023 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.web; + +import com.google.inject.Provider; +import org.traccar.api.security.PermissionsService; +import org.traccar.model.Server; +import org.traccar.storage.StorageException; + +import javax.inject.Inject; +import javax.inject.Singleton; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@Singleton +public class OverrideFilter implements Filter { + + private final Provider permissionsServiceProvider; + + @Inject + public OverrideFilter(Provider permissionsServiceProvider) { + this.permissionsServiceProvider = permissionsServiceProvider; + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + + CharResponseWrapper wrapper = new CharResponseWrapper((HttpServletResponse) response); + + chain.doFilter(request, wrapper); + + Server server; + try { + server = permissionsServiceProvider.get().getServer(); + } catch (StorageException e) { + throw new RuntimeException(e); + } + + String title = server.getString("serverTitle", "Traccar"); + String description = server.getString("serverDescription", "Traccar GPS Tracking System"); + String colorPrimary = server.getString("serverColorPrimary", "#1a237e"); + + String alteredContent = wrapper.getCaptureAsString() + .replace("${title}", title) + .replace("${description}", description) + .replace("${colorPrimary}", colorPrimary); + + response.setContentLength(alteredContent.length()); + response.getWriter().write(alteredContent); + } + +} diff --git a/src/main/java/org/traccar/web/WebModule.java b/src/main/java/org/traccar/web/WebModule.java index 0722c5d1e..f06abf0b5 100644 --- a/src/main/java/org/traccar/web/WebModule.java +++ b/src/main/java/org/traccar/web/WebModule.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2022 - 2023 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. @@ -23,6 +23,8 @@ public class WebModule extends ServletModule { @Override protected void configureServlets() { + filter("/").through(OverrideFilter.class); + filter("/manifest.json").through(OverrideFilter.class); filter("/api/*").through(ThrottlingFilter.class); filter("/api/media/*").through(MediaFilter.class); serve("/api/socket").with(AsyncSocketServlet.class); -- cgit v1.2.3 From e94e7f55aae161cf2fd30bbb2ca217f23627b074 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Mon, 22 May 2023 17:21:52 -0700 Subject: Add SMS enabled flag --- src/main/java/org/traccar/api/resource/ServerResource.java | 8 +++++++- src/main/java/org/traccar/model/Server.java | 14 +++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) (limited to 'src/main/java/org/traccar/model') diff --git a/src/main/java/org/traccar/api/resource/ServerResource.java b/src/main/java/org/traccar/api/resource/ServerResource.java index 6a3b8919e..f334b7a21 100644 --- a/src/main/java/org/traccar/api/resource/ServerResource.java +++ b/src/main/java/org/traccar/api/resource/ServerResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2023 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. @@ -25,6 +25,7 @@ import org.traccar.helper.LogAction; import org.traccar.model.Server; import org.traccar.model.User; import org.traccar.session.cache.CacheManager; +import org.traccar.sms.SmsManager; import org.traccar.storage.StorageException; import org.traccar.storage.query.Columns; import org.traccar.storage.query.Condition; @@ -56,6 +57,10 @@ public class ServerResource extends BaseResource { @Inject private MailManager mailManager; + @Inject + @Nullable + private SmsManager smsManager; + @Inject @Nullable private OpenIdProvider openIdProvider; @@ -69,6 +74,7 @@ public class ServerResource extends BaseResource { public Server get() throws StorageException { Server server = storage.getObject(Server.class, new Request(new Columns.All())); server.setEmailEnabled(mailManager.getEmailEnabled()); + server.setTextEnabled(smsManager != null); server.setGeocoderEnabled(geocoder != null); server.setOpenIdEnabled(openIdProvider != null); server.setOpenIdForce(openIdProvider != null && openIdProvider.getForce()); diff --git a/src/main/java/org/traccar/model/Server.java b/src/main/java/org/traccar/model/Server.java index b790ca472..6442186b6 100644 --- a/src/main/java/org/traccar/model/Server.java +++ b/src/main/java/org/traccar/model/Server.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2023 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. @@ -228,6 +228,18 @@ public class Server extends ExtendedModel implements UserRestrictions { private boolean geocoderEnabled; + private boolean textEnabled; + + @QueryIgnore + public void setTextEnabled(boolean textEnabled) { + this.textEnabled = textEnabled; + } + + @QueryIgnore + public Boolean getTextEnabled() { + return textEnabled; + } + @QueryIgnore public void setGeocoderEnabled(boolean geocoderEnabled) { this.geocoderEnabled = geocoderEnabled; -- cgit v1.2.3 From 6cc15fb06c9490e349847e043eea7c188e88794e Mon Sep 17 00:00:00 2001 From: merabtenei Date: Sun, 28 May 2023 21:38:14 +0100 Subject: fix NullPointerException when attribute key is defined and value is set to null, caused by calling .toString() on null. --- src/main/java/org/traccar/model/ExtendedModel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main/java/org/traccar/model') diff --git a/src/main/java/org/traccar/model/ExtendedModel.java b/src/main/java/org/traccar/model/ExtendedModel.java index 6a4f502f7..d3d247fdd 100644 --- a/src/main/java/org/traccar/model/ExtendedModel.java +++ b/src/main/java/org/traccar/model/ExtendedModel.java @@ -90,7 +90,7 @@ public class ExtendedModel extends BaseModel { } public String getString(String key, String defaultValue) { - if (attributes.containsKey(key)) { + if (attributes.containsKey(key) && attributes.get(key) != null) { return attributes.get(key).toString(); } else { return defaultValue; -- cgit v1.2.3 From 46dabfabdc7b6cc12cbad9863e38ab1ff97c40a5 Mon Sep 17 00:00:00 2001 From: merabtenei Date: Mon, 29 May 2023 16:03:12 +0100 Subject: fix return null value --- src/main/java/org/traccar/model/ExtendedModel.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/main/java/org/traccar/model') diff --git a/src/main/java/org/traccar/model/ExtendedModel.java b/src/main/java/org/traccar/model/ExtendedModel.java index d3d247fdd..0a0923ba1 100644 --- a/src/main/java/org/traccar/model/ExtendedModel.java +++ b/src/main/java/org/traccar/model/ExtendedModel.java @@ -90,8 +90,9 @@ public class ExtendedModel extends BaseModel { } public String getString(String key, String defaultValue) { - if (attributes.containsKey(key) && attributes.get(key) != null) { - return attributes.get(key).toString(); + if (attributes.containsKey(key)) { + Object value = attributes.containsKey(key); + return value != null ? value.toString() : null; } else { return defaultValue; } -- cgit v1.2.3 From d244b4bc4999ba3e3dca607bf797c3f7d7f578ff Mon Sep 17 00:00:00 2001 From: merabtenei Date: Mon, 29 May 2023 18:07:31 +0100 Subject: fix typo: replaced .containsKey with .get --- src/main/java/org/traccar/model/ExtendedModel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main/java/org/traccar/model') diff --git a/src/main/java/org/traccar/model/ExtendedModel.java b/src/main/java/org/traccar/model/ExtendedModel.java index 0a0923ba1..d5cd094da 100644 --- a/src/main/java/org/traccar/model/ExtendedModel.java +++ b/src/main/java/org/traccar/model/ExtendedModel.java @@ -91,7 +91,7 @@ public class ExtendedModel extends BaseModel { public String getString(String key, String defaultValue) { if (attributes.containsKey(key)) { - Object value = attributes.containsKey(key); + Object value = attributes.get(key); return value != null ? value.toString() : null; } else { return defaultValue; -- cgit v1.2.3 From d2909b90af2344176d2018bdbb2f410ea6e44465 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Thu, 22 Jun 2023 06:41:05 -0700 Subject: Decode Fifotrack engine hours --- src/main/java/org/traccar/model/Position.java | 2 +- src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'src/main/java/org/traccar/model') diff --git a/src/main/java/org/traccar/model/Position.java b/src/main/java/org/traccar/model/Position.java index 3ed340703..6685cab95 100644 --- a/src/main/java/org/traccar/model/Position.java +++ b/src/main/java/org/traccar/model/Position.java @@ -42,7 +42,7 @@ public class Position extends Message { public static final String KEY_ODOMETER = "odometer"; // meters public static final String KEY_ODOMETER_SERVICE = "serviceOdometer"; // meters public static final String KEY_ODOMETER_TRIP = "tripOdometer"; // meters - public static final String KEY_HOURS = "hours"; + public static final String KEY_HOURS = "hours"; // milliseconds public static final String KEY_STEPS = "steps"; public static final String KEY_HEART_RATE = "heartRate"; public static final String KEY_INPUT = "input"; diff --git a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java index e0dd1d62d..14b33b67f 100644 --- a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java @@ -64,7 +64,7 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder { .number("(d+),") // course .number("(-?d+),") // altitude .number("(d+),") // odometer - .number("d+,") // runtime + .number("(d+),") // engine hours .number("(x+),") // status .number("(x+)?,") // input .number("(x+)?,") // output @@ -290,6 +290,7 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder { position.setAltitude(parser.nextInt()); position.set(Position.KEY_ODOMETER, parser.nextLong()); + position.set(Position.KEY_HOURS, parser.nextLong() * 1000); long status = parser.nextHexLong(); position.set(Position.KEY_RSSI, BitUtil.between(status, 3, 8)); -- cgit v1.2.3 From 914cc6e85ba8696727edff2d8a600ae695bc410d Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sat, 1 Jul 2023 16:04:07 -0700 Subject: Add calendar based filtering --- schema/changelog-5.9.xml | 27 +++++++++++++++++++ schema/changelog-master.xml | 1 + .../traccar/api/security/PermissionsService.java | 12 ++++----- .../java/org/traccar/handler/FilterHandler.java | 12 +++++++-- src/main/java/org/traccar/model/Device.java | 16 ++++++++++-- src/main/java/org/traccar/model/Geofence.java | 16 ++++++++++-- src/main/java/org/traccar/model/Notification.java | 14 +++++++++- src/main/java/org/traccar/model/Report.java | 16 ++++++++++-- src/main/java/org/traccar/model/Schedulable.java | 22 ++++++++++++++++ .../java/org/traccar/model/ScheduledModel.java | 30 ---------------------- .../org/traccar/session/cache/CacheManager.java | 27 ++++++++++--------- 11 files changed, 136 insertions(+), 57 deletions(-) create mode 100644 schema/changelog-5.9.xml create mode 100644 src/main/java/org/traccar/model/Schedulable.java delete mode 100644 src/main/java/org/traccar/model/ScheduledModel.java (limited to 'src/main/java/org/traccar/model') diff --git a/schema/changelog-5.9.xml b/schema/changelog-5.9.xml new file mode 100644 index 000000000..50a3d3aaa --- /dev/null +++ b/schema/changelog-5.9.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + diff --git a/schema/changelog-master.xml b/schema/changelog-master.xml index dd2bcc8a7..331d5ec78 100644 --- a/schema/changelog-master.xml +++ b/schema/changelog-master.xml @@ -39,5 +39,6 @@ + diff --git a/src/main/java/org/traccar/api/security/PermissionsService.java b/src/main/java/org/traccar/api/security/PermissionsService.java index 18a376601..38bf48f30 100644 --- a/src/main/java/org/traccar/api/security/PermissionsService.java +++ b/src/main/java/org/traccar/api/security/PermissionsService.java @@ -24,7 +24,7 @@ import org.traccar.model.Group; import org.traccar.model.GroupedModel; import org.traccar.model.ManagedUser; import org.traccar.model.Notification; -import org.traccar.model.ScheduledModel; +import org.traccar.model.Schedulable; import org.traccar.model.Server; import org.traccar.model.User; import org.traccar.model.UserRestrictions; @@ -137,13 +137,13 @@ public class PermissionsService { } } } - if (object instanceof ScheduledModel) { - ScheduledModel after = ((ScheduledModel) object); + if (object instanceof Schedulable) { + Schedulable after = ((Schedulable) object); if (after.getCalendarId() > 0) { - ScheduledModel before = null; + Schedulable before = null; if (!addition) { before = storage.getObject(after.getClass(), new Request( - new Columns.Include("calendarId"), new Condition.Equals("id", after.getId()))); + new Columns.Include("calendarId"), new Condition.Equals("id", object.getId()))); } if (before == null || before.getCalendarId() != after.getCalendarId()) { checkPermission(Calendar.class, userId, after.getCalendarId()); @@ -156,7 +156,7 @@ public class PermissionsService { Notification before = null; if (!addition) { before = storage.getObject(after.getClass(), new Request( - new Columns.Include("commandId"), new Condition.Equals("id", after.getId()))); + new Columns.Include("commandId"), new Condition.Equals("id", object.getId()))); } if (before == null || before.getCommandId() != after.getCommandId()) { checkPermission(Command.class, userId, after.getCommandId()); diff --git a/src/main/java/org/traccar/handler/FilterHandler.java b/src/main/java/org/traccar/handler/FilterHandler.java index 2623c3486..028e4cd09 100644 --- a/src/main/java/org/traccar/handler/FilterHandler.java +++ b/src/main/java/org/traccar/handler/FilterHandler.java @@ -25,6 +25,7 @@ import org.traccar.config.Keys; import org.traccar.database.StatisticsManager; import org.traccar.helper.UnitsConverter; import org.traccar.helper.model.AttributeUtil; +import org.traccar.model.Calendar; import org.traccar.model.Device; import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; @@ -259,9 +260,16 @@ public class FilterHandler extends ChannelInboundHandlerAdapter { } } + Device device = cacheManager.getObject(Device.class, deviceId); + if (device.getCalendarId() > 0) { + Calendar calendar = cacheManager.getObject(Calendar.class, device.getCalendarId()); + if (!calendar.checkMoment(position.getFixTime())) { + filterType.append("Calendar "); + } + } + if (filterType.length() > 0) { - String uniqueId = cacheManager.getObject(Device.class, deviceId).getUniqueId(); - LOGGER.info("Position filtered by {}filters from device: {}", filterType, uniqueId); + LOGGER.info("Position filtered by {}filters from device: {}", filterType, device.getUniqueId()); return true; } diff --git a/src/main/java/org/traccar/model/Device.java b/src/main/java/org/traccar/model/Device.java index b7cffac49..2c582328e 100644 --- a/src/main/java/org/traccar/model/Device.java +++ b/src/main/java/org/traccar/model/Device.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2023 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. @@ -22,7 +22,19 @@ import org.traccar.storage.StorageName; import java.util.Date; @StorageName("tc_devices") -public class Device extends GroupedModel implements Disableable { +public class Device extends GroupedModel implements Disableable, Schedulable { + + private long calendarId; + + @Override + public long getCalendarId() { + return calendarId; + } + + @Override + public void setCalendarId(long calendarId) { + this.calendarId = calendarId; + } private String name; diff --git a/src/main/java/org/traccar/model/Geofence.java b/src/main/java/org/traccar/model/Geofence.java index 9259028fb..ca6293651 100644 --- a/src/main/java/org/traccar/model/Geofence.java +++ b/src/main/java/org/traccar/model/Geofence.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2023 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. @@ -26,7 +26,19 @@ import org.traccar.storage.StorageName; import java.text.ParseException; @StorageName("tc_geofences") -public class Geofence extends ScheduledModel { +public class Geofence extends ExtendedModel implements Schedulable { + + private long calendarId; + + @Override + public long getCalendarId() { + return calendarId; + } + + @Override + public void setCalendarId(long calendarId) { + this.calendarId = calendarId; + } private String name; diff --git a/src/main/java/org/traccar/model/Notification.java b/src/main/java/org/traccar/model/Notification.java index b6a6e4cf5..6dcd9c9de 100644 --- a/src/main/java/org/traccar/model/Notification.java +++ b/src/main/java/org/traccar/model/Notification.java @@ -24,7 +24,19 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import org.traccar.storage.StorageName; @StorageName("tc_notifications") -public class Notification extends ScheduledModel { +public class Notification extends ExtendedModel implements Schedulable { + + private long calendarId; + + @Override + public long getCalendarId() { + return calendarId; + } + + @Override + public void setCalendarId(long calendarId) { + this.calendarId = calendarId; + } private boolean always; diff --git a/src/main/java/org/traccar/model/Report.java b/src/main/java/org/traccar/model/Report.java index 1556ecc9e..2ee7ae288 100644 --- a/src/main/java/org/traccar/model/Report.java +++ b/src/main/java/org/traccar/model/Report.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2022 - 2023 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. @@ -18,7 +18,19 @@ package org.traccar.model; import org.traccar.storage.StorageName; @StorageName("tc_reports") -public class Report extends ScheduledModel { +public class Report extends ExtendedModel implements Schedulable { + + private long calendarId; + + @Override + public long getCalendarId() { + return calendarId; + } + + @Override + public void setCalendarId(long calendarId) { + this.calendarId = calendarId; + } private String type; diff --git a/src/main/java/org/traccar/model/Schedulable.java b/src/main/java/org/traccar/model/Schedulable.java new file mode 100644 index 000000000..331e77583 --- /dev/null +++ b/src/main/java/org/traccar/model/Schedulable.java @@ -0,0 +1,22 @@ +/* + * Copyright 2018 - 2023 Anton Tananaev (anton@traccar.org) + * Copyright 2018 Andrey Kunitsyn (andrey@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.model; + +public interface Schedulable { + long getCalendarId(); + void setCalendarId(long calendarId); +} diff --git a/src/main/java/org/traccar/model/ScheduledModel.java b/src/main/java/org/traccar/model/ScheduledModel.java deleted file mode 100644 index 9e6a4b9a6..000000000 --- a/src/main/java/org/traccar/model/ScheduledModel.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2018 Anton Tananaev (anton@traccar.org) - * Copyright 2018 Andrey Kunitsyn (andrey@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.model; - -public class ScheduledModel extends ExtendedModel { - - private long calendarId; - - public long getCalendarId() { - return calendarId; - } - - public void setCalendarId(long calendarId) { - this.calendarId = calendarId; - } -} diff --git a/src/main/java/org/traccar/session/cache/CacheManager.java b/src/main/java/org/traccar/session/cache/CacheManager.java index 9d2350012..24abd7347 100644 --- a/src/main/java/org/traccar/session/cache/CacheManager.java +++ b/src/main/java/org/traccar/session/cache/CacheManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2022 - 2023 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. @@ -31,7 +31,7 @@ import org.traccar.model.GroupedModel; import org.traccar.model.Maintenance; import org.traccar.model.Notification; import org.traccar.model.Position; -import org.traccar.model.ScheduledModel; +import org.traccar.model.Schedulable; import org.traccar.model.Server; import org.traccar.model.User; import org.traccar.storage.Storage; @@ -244,8 +244,8 @@ public class CacheManager implements BroadcastInterface { if (((GroupedModel) before).getGroupId() != ((GroupedModel) object).getGroupId()) { invalidate = true; } - } else if (object instanceof ScheduledModel) { - if (((ScheduledModel) before).getCalendarId() != ((ScheduledModel) object).getCalendarId()) { + } else if (object instanceof Schedulable) { + if (((Schedulable) before).getCalendarId() != ((Schedulable) object).getCalendarId()) { invalidate = true; } } @@ -308,6 +308,12 @@ public class CacheManager implements BroadcastInterface { new Columns.All(), new Condition.Equals("id", deviceId))); if (device != null) { addObject(deviceId, device); + if (device.getCalendarId() > 0) { + var calendar = storage.getObject(Calendar.class, new Request( + new Columns.All(), new Condition.Equals("id", device.getCalendarId()))); + links.computeIfAbsent(Calendar.class, k -> new LinkedHashSet<>()).add(calendar.getId()); + addObject(deviceId, calendar); + } int groupDepth = 0; long groupId = device.getGroupId(); @@ -326,13 +332,12 @@ public class CacheManager implements BroadcastInterface { links.put(clazz, objects.stream().map(BaseModel::getId).collect(Collectors.toSet())); for (var object : objects) { addObject(deviceId, object); - if (object instanceof ScheduledModel) { - var scheduled = (ScheduledModel) object; + if (object instanceof Schedulable) { + var scheduled = (Schedulable) object; if (scheduled.getCalendarId() > 0) { var calendar = storage.getObject(Calendar.class, new Request( new Columns.All(), new Condition.Equals("id", scheduled.getCalendarId()))); - links.computeIfAbsent(Notification.class, k -> new LinkedHashSet<>()) - .add(calendar.getId()); + links.computeIfAbsent(Calendar.class, k -> new LinkedHashSet<>()).add(calendar.getId()); addObject(deviceId, calendar); } } @@ -350,14 +355,12 @@ public class CacheManager implements BroadcastInterface { .filter(Notification::getAlways) .collect(Collectors.toList()); for (var notification : notifications) { - links.computeIfAbsent(Notification.class, k -> new LinkedHashSet<>()) - .add(notification.getId()); + links.computeIfAbsent(Notification.class, k -> new LinkedHashSet<>()).add(notification.getId()); addObject(deviceId, notification); if (notification.getCalendarId() > 0) { var calendar = storage.getObject(Calendar.class, new Request( new Columns.All(), new Condition.Equals("id", notification.getCalendarId()))); - links.computeIfAbsent(Notification.class, k -> new LinkedHashSet<>()) - .add(calendar.getId()); + links.computeIfAbsent(Calendar.class, k -> new LinkedHashSet<>()).add(calendar.getId()); addObject(deviceId, calendar); } } -- cgit v1.2.3 From dcef13b1fc7cf052463e11532365b3ad1f948b99 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Wed, 19 Jul 2023 07:45:13 -0700 Subject: Trim important fields --- src/main/java/org/traccar/model/Device.java | 4 ++-- src/main/java/org/traccar/model/Driver.java | 2 +- src/main/java/org/traccar/model/User.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/main/java/org/traccar/model') diff --git a/src/main/java/org/traccar/model/Device.java b/src/main/java/org/traccar/model/Device.java index 2c582328e..c259e1413 100644 --- a/src/main/java/org/traccar/model/Device.java +++ b/src/main/java/org/traccar/model/Device.java @@ -53,7 +53,7 @@ public class Device extends GroupedModel implements Disableable, Schedulable { } public void setUniqueId(String uniqueId) { - this.uniqueId = uniqueId; + this.uniqueId = uniqueId != null ? uniqueId.trim() : null; } public static final String STATUS_UNKNOWN = "unknown"; @@ -100,7 +100,7 @@ public class Device extends GroupedModel implements Disableable, Schedulable { } public void setPhone(String phone) { - this.phone = phone; + this.phone = phone != null ? phone.trim() : null; } private String model; diff --git a/src/main/java/org/traccar/model/Driver.java b/src/main/java/org/traccar/model/Driver.java index b9e023088..2689c67f2 100644 --- a/src/main/java/org/traccar/model/Driver.java +++ b/src/main/java/org/traccar/model/Driver.java @@ -38,7 +38,7 @@ public class Driver extends ExtendedModel { } public void setUniqueId(String uniqueId) { - this.uniqueId = uniqueId; + this.uniqueId = uniqueId != null ? uniqueId.trim() : null; } } diff --git a/src/main/java/org/traccar/model/User.java b/src/main/java/org/traccar/model/User.java index 53594fe07..0540f16d7 100644 --- a/src/main/java/org/traccar/model/User.java +++ b/src/main/java/org/traccar/model/User.java @@ -63,7 +63,7 @@ public class User extends ExtendedModel implements UserRestrictions, Disableable } public void setPhone(String phone) { - this.phone = phone; + this.phone = phone != null ? phone.trim() : null; } private boolean readonly; -- cgit v1.2.3 From 840d5600c69839e9be7a241d69005f322dfef124 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Wed, 19 Jul 2023 07:46:04 -0700 Subject: Remove unnecessary checks --- src/main/java/org/traccar/model/Device.java | 2 +- src/main/java/org/traccar/model/Driver.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/main/java/org/traccar/model') diff --git a/src/main/java/org/traccar/model/Device.java b/src/main/java/org/traccar/model/Device.java index c259e1413..e07815976 100644 --- a/src/main/java/org/traccar/model/Device.java +++ b/src/main/java/org/traccar/model/Device.java @@ -53,7 +53,7 @@ public class Device extends GroupedModel implements Disableable, Schedulable { } public void setUniqueId(String uniqueId) { - this.uniqueId = uniqueId != null ? uniqueId.trim() : null; + this.uniqueId = uniqueId.trim(); } public static final String STATUS_UNKNOWN = "unknown"; diff --git a/src/main/java/org/traccar/model/Driver.java b/src/main/java/org/traccar/model/Driver.java index 2689c67f2..ca5714e51 100644 --- a/src/main/java/org/traccar/model/Driver.java +++ b/src/main/java/org/traccar/model/Driver.java @@ -38,7 +38,7 @@ public class Driver extends ExtendedModel { } public void setUniqueId(String uniqueId) { - this.uniqueId = uniqueId != null ? uniqueId.trim() : null; + this.uniqueId = uniqueId.trim(); } } -- cgit v1.2.3 From 6c78fc675501ab348f877e423b984d1a2a132ef1 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Wed, 4 Oct 2023 08:16:10 +0800 Subject: Event for sent queued commands --- src/main/java/org/traccar/database/CommandsManager.java | 14 +++++++++++++- src/main/java/org/traccar/model/Event.java | 1 + templates/full/queuedCommandSent.vm | 11 +++++++++++ templates/short/queuedCommandSent.vm | 2 ++ 4 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 templates/full/queuedCommandSent.vm create mode 100644 templates/short/queuedCommandSent.vm (limited to 'src/main/java/org/traccar/model') diff --git a/src/main/java/org/traccar/database/CommandsManager.java b/src/main/java/org/traccar/database/CommandsManager.java index bef696e58..90180b989 100644 --- a/src/main/java/org/traccar/database/CommandsManager.java +++ b/src/main/java/org/traccar/database/CommandsManager.java @@ -22,6 +22,7 @@ import org.traccar.broadcast.BroadcastInterface; import org.traccar.broadcast.BroadcastService; import org.traccar.model.Command; import org.traccar.model.Device; +import org.traccar.model.Event; import org.traccar.model.Position; import org.traccar.model.QueuedCommand; import org.traccar.session.ConnectionManager; @@ -38,6 +39,8 @@ import jakarta.annotation.Nullable; import jakarta.inject.Inject; import jakarta.inject.Singleton; import java.util.Collection; +import java.util.HashMap; +import java.util.Map; import java.util.stream.Collectors; @Singleton @@ -48,16 +51,19 @@ public class CommandsManager implements BroadcastInterface { private final SmsManager smsManager; private final ConnectionManager connectionManager; private final BroadcastService broadcastService; + private final NotificationManager notificationManager; @Inject public CommandsManager( Storage storage, ServerManager serverManager, @Nullable SmsManager smsManager, - ConnectionManager connectionManager, BroadcastService broadcastService) { + ConnectionManager connectionManager, BroadcastService broadcastService, + NotificationManager notificationManager) { this.storage = storage; this.serverManager = serverManager; this.smsManager = smsManager; this.connectionManager = connectionManager; this.broadcastService = broadcastService; + this.notificationManager = notificationManager; broadcastService.registerListener(this); } @@ -103,10 +109,16 @@ public class CommandsManager implements BroadcastInterface { new Columns.All(), new Condition.Equals("deviceId", deviceId), new Order("id", false, count))); + Map events = new HashMap<>(); for (var command : commands) { storage.removeObject(QueuedCommand.class, new Request( new Condition.Equals("id", command.getId()))); + + Event event = new Event(Event.TYPE_QUEUED_COMMAND_SENT, command.getDeviceId()); + event.set("id", command.getId()); + events.put(event, null); } + notificationManager.updateEvents(events); return commands.stream().map(QueuedCommand::toCommand).collect(Collectors.toList()); } catch (StorageException e) { throw new RuntimeException(e); diff --git a/src/main/java/org/traccar/model/Event.java b/src/main/java/org/traccar/model/Event.java index 0e851d748..6f90de9da 100644 --- a/src/main/java/org/traccar/model/Event.java +++ b/src/main/java/org/traccar/model/Event.java @@ -46,6 +46,7 @@ public class Event extends Message { public static final String TYPE_DEVICE_UNKNOWN = "deviceUnknown"; public static final String TYPE_DEVICE_OFFLINE = "deviceOffline"; public static final String TYPE_DEVICE_INACTIVE = "deviceInactive"; + public static final String TYPE_QUEUED_COMMAND_SENT = "queuedCommandSent"; public static final String TYPE_DEVICE_MOVING = "deviceMoving"; public static final String TYPE_DEVICE_STOPPED = "deviceStopped"; diff --git a/templates/full/queuedCommandSent.vm b/templates/full/queuedCommandSent.vm new file mode 100644 index 000000000..148dd2094 --- /dev/null +++ b/templates/full/queuedCommandSent.vm @@ -0,0 +1,11 @@ +#set($subject = "$device.name: queued command sent") + + + +Device: $device.name
+Queued command sent
+Time: $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone)
+
+Unsubscribe + + diff --git a/templates/short/queuedCommandSent.vm b/templates/short/queuedCommandSent.vm new file mode 100644 index 000000000..67f031280 --- /dev/null +++ b/templates/short/queuedCommandSent.vm @@ -0,0 +1,2 @@ +#set($subject = "$device.name: queued command sent") +Queued command sent to $device.name at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) -- cgit v1.2.3 From 33d01cda7bb678bb604404f18c8f4e66a40eaa55 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Mon, 16 Oct 2023 07:28:26 -0700 Subject: Negative signal strength value --- src/main/java/org/traccar/model/CellTower.java | 2 +- src/main/java/org/traccar/model/WifiAccessPoint.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/main/java/org/traccar/model') diff --git a/src/main/java/org/traccar/model/CellTower.java b/src/main/java/org/traccar/model/CellTower.java index 355594c64..4277cc4c4 100644 --- a/src/main/java/org/traccar/model/CellTower.java +++ b/src/main/java/org/traccar/model/CellTower.java @@ -104,7 +104,7 @@ public class CellTower { } public void setSignalStrength(Integer signalStrength) { - this.signalStrength = signalStrength; + this.signalStrength = signalStrength > 0 ? -signalStrength : signalStrength; } public void setOperator(long operator) { diff --git a/src/main/java/org/traccar/model/WifiAccessPoint.java b/src/main/java/org/traccar/model/WifiAccessPoint.java index e28c1b935..64858f4c7 100644 --- a/src/main/java/org/traccar/model/WifiAccessPoint.java +++ b/src/main/java/org/traccar/model/WifiAccessPoint.java @@ -52,7 +52,7 @@ public class WifiAccessPoint { } public void setSignalStrength(Integer signalStrength) { - this.signalStrength = signalStrength; + this.signalStrength = signalStrength > 0 ? -signalStrength : signalStrength; } private Integer channel; -- cgit v1.2.3 From 3296318dccfcc83cc99d6da58affe5ee8a46fedb Mon Sep 17 00:00:00 2001 From: e-macgregor <122734173+e-macgregor@users.noreply.github.com> Date: Sun, 29 Oct 2023 17:20:36 -0600 Subject: totp --- build.gradle | 1 + schema/changelog-5.10.xml | 17 +++++++++++++ schema/changelog-master.xml | 1 + .../org/traccar/api/resource/SessionResource.java | 20 ++++++++++++---- .../org/traccar/api/resource/UserResource.java | 16 +++++++++++++ .../api/security/CodeRequiredException.java | 22 +++++++++++++++++ .../org/traccar/api/security/LoginService.java | 28 +++++++++++++++------- .../api/security/SecurityRequestFilter.java | 2 +- src/main/java/org/traccar/config/Keys.java | 14 +++++++++++ src/main/java/org/traccar/model/User.java | 10 ++++++++ 10 files changed, 118 insertions(+), 13 deletions(-) create mode 100644 schema/changelog-5.10.xml create mode 100644 src/main/java/org/traccar/api/security/CodeRequiredException.java (limited to 'src/main/java/org/traccar/model') diff --git a/build.gradle b/build.gradle index 2e8b557ba..75fe792b0 100644 --- a/build.gradle +++ b/build.gradle @@ -90,6 +90,7 @@ dependencies { implementation "com.google.firebase:firebase-admin:9.2.0" implementation "com.nimbusds:oauth2-oidc-sdk:10.13.2" implementation "com.rabbitmq:amqp-client:5.18.0" + implementation "com.warrenstrange:googleauth:1.2.0" testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion" testImplementation "org.junit.jupiter:junit-jupiter-engine:$junitVersion" testImplementation "org.mockito:mockito-core:5.4.0" diff --git a/schema/changelog-5.10.xml b/schema/changelog-5.10.xml new file mode 100644 index 000000000..63988b14a --- /dev/null +++ b/schema/changelog-5.10.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/schema/changelog-master.xml b/schema/changelog-master.xml index 331d5ec78..183b3fd93 100644 --- a/schema/changelog-master.xml +++ b/schema/changelog-master.xml @@ -40,5 +40,6 @@ + diff --git a/src/main/java/org/traccar/api/resource/SessionResource.java b/src/main/java/org/traccar/api/resource/SessionResource.java index 3e738c15a..90f0ceade 100644 --- a/src/main/java/org/traccar/api/resource/SessionResource.java +++ b/src/main/java/org/traccar/api/resource/SessionResource.java @@ -16,6 +16,7 @@ package org.traccar.api.resource; import org.traccar.api.BaseResource; +import org.traccar.api.security.CodeRequiredException; import org.traccar.api.security.LoginService; import org.traccar.api.signature.TokenManager; import org.traccar.database.OpenIdProvider; @@ -108,7 +109,7 @@ public class SessionResource extends BaseResource { } } if (email != null && password != null) { - User user = loginService.login(email, password); + User user = loginService.login(email, password, null); if (user != null) { request.getSession().setAttribute(USER_ID_KEY, user.getId()); LogAction.login(user.getId(), WebHelper.retrieveRemoteAddress(request)); @@ -142,8 +143,19 @@ public class SessionResource extends BaseResource { @PermitAll @POST public User add( - @FormParam("email") String email, @FormParam("password") String password) throws StorageException { - User user = loginService.login(email, password); + @FormParam("email") String email, + @FormParam("password") String password, + @FormParam("code") Integer code) throws StorageException { + User user; + try { + user = loginService.login(email, password, code); + } catch (CodeRequiredException e) { + Response response = Response + .status(Response.Status.UNAUTHORIZED) + .header("WWW-Authenticate", "TOTP") + .build(); + throw new WebApplicationException(response); + } if (user != null) { request.getSession().setAttribute(USER_ID_KEY, user.getId()); LogAction.login(user.getId(), WebHelper.retrieveRemoteAddress(request)); @@ -171,7 +183,7 @@ public class SessionResource extends BaseResource { @PermitAll @Path("openid/auth") @GET - public Response openIdAuth() throws IOException { + public Response openIdAuth() { return Response.seeOther(openIdProvider.createAuthUri()).build(); } diff --git a/src/main/java/org/traccar/api/resource/UserResource.java b/src/main/java/org/traccar/api/resource/UserResource.java index d73e8b6f5..99537f912 100644 --- a/src/main/java/org/traccar/api/resource/UserResource.java +++ b/src/main/java/org/traccar/api/resource/UserResource.java @@ -15,12 +15,14 @@ */ package org.traccar.api.resource; +import com.warrenstrange.googleauth.GoogleAuthenticator; import jakarta.servlet.http.HttpServletRequest; import jakarta.ws.rs.DELETE; import jakarta.ws.rs.PathParam; import jakarta.ws.rs.core.Context; import org.traccar.api.BaseObjectResource; import org.traccar.config.Config; +import org.traccar.config.Keys; import org.traccar.helper.LogAction; import org.traccar.helper.model.UserUtil; import org.traccar.model.ManagedUser; @@ -96,6 +98,10 @@ public class UserResource extends BaseObjectResource { if (!permissionsService.getServer().getRegistration()) { throw new SecurityException("Registration disabled"); } + if (permissionsService.getServer().getBoolean(Keys.WEB_TOTP_FORCE.getKey()) + && entity.getTotpKey() == null) { + throw new SecurityException("One-time password key is required"); + } UserUtil.setUserDefaults(entity, config); } } @@ -128,4 +134,14 @@ public class UserResource extends BaseObjectResource { return response; } + @Path("totp") + @PermitAll + @POST + public String generateTotpKey() throws StorageException { + if (!permissionsService.getServer().getBoolean(Keys.WEB_TOTP_ENABLE.getKey())) { + throw new SecurityException("One-time password is disabled"); + } + return new GoogleAuthenticator().createCredentials().getKey(); + } + } diff --git a/src/main/java/org/traccar/api/security/CodeRequiredException.java b/src/main/java/org/traccar/api/security/CodeRequiredException.java new file mode 100644 index 000000000..d522c6540 --- /dev/null +++ b/src/main/java/org/traccar/api/security/CodeRequiredException.java @@ -0,0 +1,22 @@ +/* + * Copyright 2023 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.security; + +public class CodeRequiredException extends SecurityException { + public CodeRequiredException() { + super("Code not provided"); + } +} diff --git a/src/main/java/org/traccar/api/security/LoginService.java b/src/main/java/org/traccar/api/security/LoginService.java index 91e964ee9..8eb5537fa 100644 --- a/src/main/java/org/traccar/api/security/LoginService.java +++ b/src/main/java/org/traccar/api/security/LoginService.java @@ -15,6 +15,7 @@ */ package org.traccar.api.security; +import com.warrenstrange.googleauth.GoogleAuthenticator; import org.traccar.api.signature.TokenManager; import org.traccar.config.Config; import org.traccar.config.Keys; @@ -70,7 +71,7 @@ public class LoginService { return user; } - public User login(String email, String password) throws StorageException { + public User login(String email, String password, Integer code) throws StorageException { if (forceOpenId) { return null; } @@ -84,6 +85,7 @@ public class LoginService { if (user != null) { if (ldapProvider != null && user.getLogin() != null && ldapProvider.login(user.getLogin(), password) || !forceLdap && user.isPasswordValid(password)) { + checkUserCode(user, code); checkUserEnabled(user); return user; } @@ -98,15 +100,12 @@ public class LoginService { return null; } - public User login(String email, String name, Boolean administrator) throws StorageException { + public User login(String email, String name, boolean administrator) throws StorageException { User user = storage.getObject(User.class, new Request( new Columns.All(), new Condition.Equals("email", email))); - if (user != null) { - checkUserEnabled(user); - return user; - } else { + if (user == null) { user = new User(); UserUtil.setUserDefaults(user, config); user.setName(name); @@ -114,9 +113,9 @@ public class LoginService { user.setFixedEmail(true); user.setAdministrator(administrator); user.setId(storage.addObject(user, new Request(new Columns.Exclude("id")))); - checkUserEnabled(user); - return user; } + checkUserEnabled(user); + return user; } private void checkUserEnabled(User user) throws SecurityException { @@ -126,4 +125,17 @@ public class LoginService { user.checkDisabled(); } + private void checkUserCode(User user, Integer code) throws SecurityException { + String key = user.getTotpKey(); + if (key != null) { + if (code == null) { + throw new CodeRequiredException(); + } + GoogleAuthenticator authenticator = new GoogleAuthenticator(); + if (!authenticator.authorize(key, code)) { + throw new SecurityException("User authorization failed"); + } + } + } + } diff --git a/src/main/java/org/traccar/api/security/SecurityRequestFilter.java b/src/main/java/org/traccar/api/security/SecurityRequestFilter.java index ee964c9e4..cb523177e 100644 --- a/src/main/java/org/traccar/api/security/SecurityRequestFilter.java +++ b/src/main/java/org/traccar/api/security/SecurityRequestFilter.java @@ -87,7 +87,7 @@ public class SecurityRequestFilter implements ContainerRequestFilter { user = loginService.login(authHeader.substring(7)); } else { String[] auth = decodeBasicAuth(authHeader); - user = loginService.login(auth[0], auth[1]); + user = loginService.login(auth[0], auth[1], null); } if (user != null) { statisticsManager.registerRequest(user.getId()); diff --git a/src/main/java/org/traccar/config/Keys.java b/src/main/java/org/traccar/config/Keys.java index 91063a8e0..48dec863d 100644 --- a/src/main/java/org/traccar/config/Keys.java +++ b/src/main/java/org/traccar/config/Keys.java @@ -837,6 +837,20 @@ public final class Keys { List.of(KeyType.CONFIG), "max-age=3600,public"); + /** + * Enable TOTP authentication on the server. + */ + public static final ConfigKey WEB_TOTP_ENABLE = new BooleanConfigKey( + "totpEnable", + List.of(KeyType.SERVER)); + + /** + * Server attribute that indicates that TOTP authentication is required for new users. + */ + public static final ConfigKey WEB_TOTP_FORCE = new BooleanConfigKey( + "totpForce", + List.of(KeyType.SERVER)); + /** * Host for raw data forwarding. */ diff --git a/src/main/java/org/traccar/model/User.java b/src/main/java/org/traccar/model/User.java index 0540f16d7..757064ba2 100644 --- a/src/main/java/org/traccar/model/User.java +++ b/src/main/java/org/traccar/model/User.java @@ -251,6 +251,16 @@ public class User extends ExtendedModel implements UserRestrictions, Disableable this.poiLayer = poiLayer; } + private String totpKey; + + public String getTotpKey() { + return totpKey; + } + + public void setTotpKey(String totpKey) { + this.totpKey = totpKey; + } + @QueryIgnore public String getPassword() { return null; -- cgit v1.2.3 From d4c4daa491ae70d608b3e791f4ad8b07ced43437 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Mon, 30 Oct 2023 08:42:21 -0700 Subject: Update Position.java --- src/main/java/org/traccar/model/Position.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main/java/org/traccar/model') diff --git a/src/main/java/org/traccar/model/Position.java b/src/main/java/org/traccar/model/Position.java index 6685cab95..39f63217d 100644 --- a/src/main/java/org/traccar/model/Position.java +++ b/src/main/java/org/traccar/model/Position.java @@ -95,7 +95,7 @@ public class Position extends Message { public static final String KEY_DRIVING_TIME = "drivingTime"; public static final String KEY_DTCS = "dtcs"; - public static final String KEY_OBD_SPEED = "obdSpeed"; // knots + public static final String KEY_OBD_SPEED = "obdSpeed"; // km/h public static final String KEY_OBD_ODOMETER = "obdOdometer"; // meters public static final String KEY_RESULT = "result"; -- cgit v1.2.3 From a59a6d19f575d8b593085ce19458c8fff18a6360 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sat, 25 Nov 2023 14:19:23 -0800 Subject: Add device sharing (fix #3789, fix #4936, fix #5025) --- schema/changelog-5.11.xml | 17 ++++++ schema/changelog-master.xml | 1 + .../org/traccar/api/resource/DeviceResource.java | 37 +++++++++++++ src/main/java/org/traccar/model/User.java | 10 ++++ .../java/org/traccar/schedule/ScheduleManager.java | 1 + .../org/traccar/schedule/TaskDeleteTemporary.java | 61 ++++++++++++++++++++++ .../java/org/traccar/schedule/TaskReports.java | 2 +- 7 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 schema/changelog-5.11.xml create mode 100644 src/main/java/org/traccar/schedule/TaskDeleteTemporary.java (limited to 'src/main/java/org/traccar/model') diff --git a/schema/changelog-5.11.xml b/schema/changelog-5.11.xml new file mode 100644 index 000000000..e59df9249 --- /dev/null +++ b/schema/changelog-5.11.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/schema/changelog-master.xml b/schema/changelog-master.xml index 183b3fd93..559d90923 100644 --- a/schema/changelog-master.xml +++ b/schema/changelog-master.xml @@ -41,5 +41,6 @@ + diff --git a/src/main/java/org/traccar/api/resource/DeviceResource.java b/src/main/java/org/traccar/api/resource/DeviceResource.java index 61a70bac0..ebc40a9b1 100644 --- a/src/main/java/org/traccar/api/resource/DeviceResource.java +++ b/src/main/java/org/traccar/api/resource/DeviceResource.java @@ -15,12 +15,15 @@ */ package org.traccar.api.resource; +import jakarta.ws.rs.FormParam; import org.traccar.api.BaseObjectResource; +import org.traccar.api.signature.TokenManager; import org.traccar.broadcast.BroadcastService; import org.traccar.database.MediaManager; import org.traccar.helper.LogAction; import org.traccar.model.Device; import org.traccar.model.DeviceAccumulators; +import org.traccar.model.Permission; import org.traccar.model.Position; import org.traccar.model.User; import org.traccar.session.ConnectionManager; @@ -46,7 +49,9 @@ import jakarta.ws.rs.core.Response; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.security.GeneralSecurityException; import java.util.Collection; +import java.util.Date; import java.util.LinkedList; import java.util.List; @@ -67,6 +72,9 @@ public class DeviceResource extends BaseObjectResource { @Inject private MediaManager mediaManager; + @Inject + private TokenManager tokenManager; + public DeviceResource() { super(Device.class); } @@ -183,4 +191,33 @@ public class DeviceResource extends BaseObjectResource { return Response.status(Response.Status.NOT_FOUND).build(); } + @Path("share") + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + @POST + public String shareDevice( + @FormParam("deviceId") long deviceId, + @FormParam("expiration") Date expiration) throws StorageException, GeneralSecurityException, IOException { + + User user = permissionsService.getUser(getUserId()); + + Device device = storage.getObject(Device.class, new Request( + new Columns.All(), + new Condition.And( + new Condition.Equals("id", deviceId), + new Condition.Permission(User.class, user.getId(), Device.class)))); + + User share = new User(); + share.setName(device.getName()); + share.setEmail(user.getEmail() + ":" + device.getUniqueId()); + share.setExpirationTime(expiration); + share.setTemporary(true); + share.setReadonly(true); + + share.setId(storage.addObject(share, new Request(new Columns.Exclude("id")))); + + storage.addPermission(new Permission(User.class, share.getId(), Device.class, deviceId)); + + return tokenManager.generateToken(share.getId(), expiration); + } + } diff --git a/src/main/java/org/traccar/model/User.java b/src/main/java/org/traccar/model/User.java index 757064ba2..8cfee0f48 100644 --- a/src/main/java/org/traccar/model/User.java +++ b/src/main/java/org/traccar/model/User.java @@ -261,6 +261,16 @@ public class User extends ExtendedModel implements UserRestrictions, Disableable this.totpKey = totpKey; } + private boolean temporary; + + public boolean getTemporary() { + return temporary; + } + + public void setTemporary(boolean temporary) { + this.temporary = temporary; + } + @QueryIgnore public String getPassword() { return null; diff --git a/src/main/java/org/traccar/schedule/ScheduleManager.java b/src/main/java/org/traccar/schedule/ScheduleManager.java index 07cdb1fe1..38e8f281c 100644 --- a/src/main/java/org/traccar/schedule/ScheduleManager.java +++ b/src/main/java/org/traccar/schedule/ScheduleManager.java @@ -39,6 +39,7 @@ public class ScheduleManager implements LifecycleObject { public void start() { executor = Executors.newSingleThreadScheduledExecutor(); var tasks = List.of( + TaskDeleteTemporary.class, TaskReports.class, TaskDeviceInactivityCheck.class, TaskWebSocketKeepalive.class, diff --git a/src/main/java/org/traccar/schedule/TaskDeleteTemporary.java b/src/main/java/org/traccar/schedule/TaskDeleteTemporary.java new file mode 100644 index 000000000..0cead59fb --- /dev/null +++ b/src/main/java/org/traccar/schedule/TaskDeleteTemporary.java @@ -0,0 +1,61 @@ +/* + * Copyright 2023 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.schedule; + +import jakarta.inject.Inject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traccar.model.User; +import org.traccar.storage.Storage; +import org.traccar.storage.StorageException; +import org.traccar.storage.query.Condition; +import org.traccar.storage.query.Request; + +import java.util.Date; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +public class TaskDeleteTemporary implements ScheduleTask { + + private static final Logger LOGGER = LoggerFactory.getLogger(TaskDeleteTemporary.class); + + private static final long CHECK_PERIOD_HOURS = 1; + + private final Storage storage; + + @Inject + public TaskDeleteTemporary(Storage storage) { + this.storage = storage; + } + + @Override + public void schedule(ScheduledExecutorService executor) { + executor.scheduleAtFixedRate(this, CHECK_PERIOD_HOURS, CHECK_PERIOD_HOURS, TimeUnit.HOURS); + } + + @Override + public void run() { + try { + storage.removeObject(User.class, new Request( + new Condition.And( + new Condition.Equals("temporary", true), + new Condition.Compare("expirationTime", "<", "time", new Date())))); + } catch (StorageException e) { + LOGGER.warn("Failed to delete temporary users", e); + } + } + +} diff --git a/src/main/java/org/traccar/schedule/TaskReports.java b/src/main/java/org/traccar/schedule/TaskReports.java index 30f20f437..e0fa6f8d6 100644 --- a/src/main/java/org/traccar/schedule/TaskReports.java +++ b/src/main/java/org/traccar/schedule/TaskReports.java @@ -51,7 +51,7 @@ public class TaskReports implements ScheduleTask { private static final Logger LOGGER = LoggerFactory.getLogger(TaskReports.class); - private static final long CHECK_PERIOD_MINUTES = 1; + private static final long CHECK_PERIOD_MINUTES = 15; private final Storage storage; private final Injector injector; -- cgit v1.2.3 From 26d09785ffc30be8af27bdc6e9a30f4c5d174d37 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sun, 3 Dec 2023 14:16:00 -0800 Subject: Move class --- src/main/java/org/traccar/api/BaseObjectResource.java | 2 +- src/main/java/org/traccar/api/resource/ServerResource.java | 2 +- src/main/java/org/traccar/broadcast/BaseBroadcastService.java | 1 + src/main/java/org/traccar/broadcast/BroadcastInterface.java | 1 + src/main/java/org/traccar/broadcast/BroadcastMessage.java | 1 + src/main/java/org/traccar/broadcast/ObjectOperation.java | 7 ------- src/main/java/org/traccar/model/ObjectOperation.java | 7 +++++++ src/main/java/org/traccar/notificators/NotificatorFirebase.java | 2 +- src/main/java/org/traccar/notificators/NotificatorTraccar.java | 2 +- src/main/java/org/traccar/session/cache/CacheManager.java | 2 +- 10 files changed, 15 insertions(+), 12 deletions(-) delete mode 100644 src/main/java/org/traccar/broadcast/ObjectOperation.java create mode 100644 src/main/java/org/traccar/model/ObjectOperation.java (limited to 'src/main/java/org/traccar/model') diff --git a/src/main/java/org/traccar/api/BaseObjectResource.java b/src/main/java/org/traccar/api/BaseObjectResource.java index 02e1c2cbe..ebfa93ff0 100644 --- a/src/main/java/org/traccar/api/BaseObjectResource.java +++ b/src/main/java/org/traccar/api/BaseObjectResource.java @@ -17,7 +17,7 @@ package org.traccar.api; import org.traccar.api.security.ServiceAccountUser; -import org.traccar.broadcast.ObjectOperation; +import org.traccar.model.ObjectOperation; import org.traccar.helper.LogAction; import org.traccar.model.BaseModel; import org.traccar.model.Group; diff --git a/src/main/java/org/traccar/api/resource/ServerResource.java b/src/main/java/org/traccar/api/resource/ServerResource.java index bcd36a32e..59ef642c8 100644 --- a/src/main/java/org/traccar/api/resource/ServerResource.java +++ b/src/main/java/org/traccar/api/resource/ServerResource.java @@ -16,7 +16,7 @@ package org.traccar.api.resource; import org.traccar.api.BaseResource; -import org.traccar.broadcast.ObjectOperation; +import org.traccar.model.ObjectOperation; import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.database.OpenIdProvider; diff --git a/src/main/java/org/traccar/broadcast/BaseBroadcastService.java b/src/main/java/org/traccar/broadcast/BaseBroadcastService.java index bb220d2bb..1c4660320 100644 --- a/src/main/java/org/traccar/broadcast/BaseBroadcastService.java +++ b/src/main/java/org/traccar/broadcast/BaseBroadcastService.java @@ -21,6 +21,7 @@ import java.util.Set; import org.traccar.model.BaseModel; import org.traccar.model.Device; import org.traccar.model.Event; +import org.traccar.model.ObjectOperation; import org.traccar.model.Permission; import org.traccar.model.Position; diff --git a/src/main/java/org/traccar/broadcast/BroadcastInterface.java b/src/main/java/org/traccar/broadcast/BroadcastInterface.java index ededbaa1a..25fdf4d93 100644 --- a/src/main/java/org/traccar/broadcast/BroadcastInterface.java +++ b/src/main/java/org/traccar/broadcast/BroadcastInterface.java @@ -18,6 +18,7 @@ package org.traccar.broadcast; import org.traccar.model.BaseModel; import org.traccar.model.Device; import org.traccar.model.Event; +import org.traccar.model.ObjectOperation; import org.traccar.model.Position; public interface BroadcastInterface { diff --git a/src/main/java/org/traccar/broadcast/BroadcastMessage.java b/src/main/java/org/traccar/broadcast/BroadcastMessage.java index 6fe2d8b35..0d15d7495 100644 --- a/src/main/java/org/traccar/broadcast/BroadcastMessage.java +++ b/src/main/java/org/traccar/broadcast/BroadcastMessage.java @@ -17,6 +17,7 @@ package org.traccar.broadcast; import org.traccar.model.Device; import org.traccar.model.Event; +import org.traccar.model.ObjectOperation; import org.traccar.model.Position; public class BroadcastMessage { diff --git a/src/main/java/org/traccar/broadcast/ObjectOperation.java b/src/main/java/org/traccar/broadcast/ObjectOperation.java deleted file mode 100644 index 27e5fb253..000000000 --- a/src/main/java/org/traccar/broadcast/ObjectOperation.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.traccar.broadcast; - -public enum ObjectOperation { - ADD, - UPDATE, - DELETE, -} diff --git a/src/main/java/org/traccar/model/ObjectOperation.java b/src/main/java/org/traccar/model/ObjectOperation.java new file mode 100644 index 000000000..b462580bb --- /dev/null +++ b/src/main/java/org/traccar/model/ObjectOperation.java @@ -0,0 +1,7 @@ +package org.traccar.model; + +public enum ObjectOperation { + ADD, + UPDATE, + DELETE, +} diff --git a/src/main/java/org/traccar/notificators/NotificatorFirebase.java b/src/main/java/org/traccar/notificators/NotificatorFirebase.java index 0402db49d..a39683b2b 100644 --- a/src/main/java/org/traccar/notificators/NotificatorFirebase.java +++ b/src/main/java/org/traccar/notificators/NotificatorFirebase.java @@ -29,7 +29,7 @@ import com.google.firebase.messaging.MessagingErrorCode; import com.google.firebase.messaging.MulticastMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.traccar.broadcast.ObjectOperation; +import org.traccar.model.ObjectOperation; import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.model.Event; diff --git a/src/main/java/org/traccar/notificators/NotificatorTraccar.java b/src/main/java/org/traccar/notificators/NotificatorTraccar.java index f693e9f30..82e1584a5 100644 --- a/src/main/java/org/traccar/notificators/NotificatorTraccar.java +++ b/src/main/java/org/traccar/notificators/NotificatorTraccar.java @@ -18,7 +18,7 @@ package org.traccar.notificators; import com.fasterxml.jackson.annotation.JsonProperty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.traccar.broadcast.ObjectOperation; +import org.traccar.model.ObjectOperation; import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.model.Event; diff --git a/src/main/java/org/traccar/session/cache/CacheManager.java b/src/main/java/org/traccar/session/cache/CacheManager.java index c40916218..dc9c86ef3 100644 --- a/src/main/java/org/traccar/session/cache/CacheManager.java +++ b/src/main/java/org/traccar/session/cache/CacheManager.java @@ -19,7 +19,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.broadcast.BroadcastInterface; import org.traccar.broadcast.BroadcastService; -import org.traccar.broadcast.ObjectOperation; +import org.traccar.model.ObjectOperation; import org.traccar.config.Config; import org.traccar.model.Attribute; import org.traccar.model.BaseModel; -- cgit v1.2.3 From f37734db8425e5a1c660d554a6f3bf56af1c2bca Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Wed, 27 Dec 2023 18:02:18 -0800 Subject: Option to observe data logs --- src/main/java/org/traccar/BasePipelineFactory.java | 6 +- src/main/java/org/traccar/api/AsyncSocket.java | 40 ++++++++++---- .../traccar/handler/StandardLoggingHandler.java | 64 ++++++++++++---------- src/main/java/org/traccar/model/LogRecord.java | 63 +++++++++++++++++++++ .../org/traccar/session/ConnectionManager.java | 34 ++++++++---- src/main/java/org/traccar/session/Endpoint.java | 58 -------------------- 6 files changed, 152 insertions(+), 113 deletions(-) create mode 100644 src/main/java/org/traccar/model/LogRecord.java delete mode 100644 src/main/java/org/traccar/session/Endpoint.java (limited to 'src/main/java/org/traccar/model') diff --git a/src/main/java/org/traccar/BasePipelineFactory.java b/src/main/java/org/traccar/BasePipelineFactory.java index 5b48f3d15..ca4a4ae63 100644 --- a/src/main/java/org/traccar/BasePipelineFactory.java +++ b/src/main/java/org/traccar/BasePipelineFactory.java @@ -124,7 +124,11 @@ public abstract class BasePipelineFactory extends ChannelInitializer { pipeline.addLast(handler); } pipeline.addLast(new NetworkMessageHandler()); - pipeline.addLast(new StandardLoggingHandler(protocol)); + + var loggingHandler = new StandardLoggingHandler(protocol); + injector.injectMembers(loggingHandler); + pipeline.addLast(loggingHandler); + if (!connector.isDatagram() && !config.getBoolean(Keys.SERVER_INSTANT_ACKNOWLEDGEMENT)) { pipeline.addLast(new AcknowledgementHandler()); } diff --git a/src/main/java/org/traccar/api/AsyncSocket.java b/src/main/java/org/traccar/api/AsyncSocket.java index 5fc4b4412..f5fbcbf62 100644 --- a/src/main/java/org/traccar/api/AsyncSocket.java +++ b/src/main/java/org/traccar/api/AsyncSocket.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2023 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. @@ -22,16 +22,17 @@ import org.eclipse.jetty.websocket.api.WebSocketAdapter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.helper.model.PositionUtil; -import org.traccar.session.ConnectionManager; import org.traccar.model.Device; import org.traccar.model.Event; +import org.traccar.model.LogRecord; import org.traccar.model.Position; +import org.traccar.session.ConnectionManager; import org.traccar.storage.Storage; import org.traccar.storage.StorageException; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; public class AsyncSocket extends WebSocketAdapter implements ConnectionManager.UpdateListener { @@ -41,12 +42,15 @@ public class AsyncSocket extends WebSocketAdapter implements ConnectionManager.U private static final String KEY_DEVICES = "devices"; private static final String KEY_POSITIONS = "positions"; private static final String KEY_EVENTS = "events"; + private static final String KEY_LOGS = "logs"; private final ObjectMapper objectMapper; private final ConnectionManager connectionManager; private final Storage storage; private final long userId; + private boolean includeLogs; + public AsyncSocket(ObjectMapper objectMapper, ConnectionManager connectionManager, Storage storage, long userId) { this.objectMapper = objectMapper; this.connectionManager = connectionManager; @@ -75,6 +79,17 @@ public class AsyncSocket extends WebSocketAdapter implements ConnectionManager.U connectionManager.removeListener(userId, this); } + @Override + public void onWebSocketText(String message) { + super.onWebSocketText(message); + + try { + includeLogs = objectMapper.readTree(message).get("logs").asBoolean(); + } catch (JsonProcessingException e) { + LOGGER.warn("Socket JSON parsing error", e); + } + } + @Override public void onKeepalive() { sendData(new HashMap<>()); @@ -82,23 +97,24 @@ public class AsyncSocket extends WebSocketAdapter implements ConnectionManager.U @Override public void onUpdateDevice(Device device) { - Map> data = new HashMap<>(); - data.put(KEY_DEVICES, Collections.singletonList(device)); - sendData(data); + sendData(Map.of(KEY_DEVICES, List.of(device))); } @Override public void onUpdatePosition(Position position) { - Map> data = new HashMap<>(); - data.put(KEY_POSITIONS, Collections.singletonList(position)); - sendData(data); + sendData(Map.of(KEY_POSITIONS, List.of(position))); } @Override public void onUpdateEvent(Event event) { - Map> data = new HashMap<>(); - data.put(KEY_EVENTS, Collections.singletonList(event)); - sendData(data); + sendData(Map.of(KEY_EVENTS, List.of(event))); + } + + @Override + public void onUpdateLog(LogRecord record) { + if (includeLogs) { + sendData(Map.of(KEY_LOGS, List.of(record))); + } } private void sendData(Map> data) { diff --git a/src/main/java/org/traccar/handler/StandardLoggingHandler.java b/src/main/java/org/traccar/handler/StandardLoggingHandler.java index 84492e2a5..b495747d3 100644 --- a/src/main/java/org/traccar/handler/StandardLoggingHandler.java +++ b/src/main/java/org/traccar/handler/StandardLoggingHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2019 - 2023 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. @@ -20,68 +20,72 @@ import io.netty.buffer.ByteBufUtil; import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; +import jakarta.inject.Inject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.NetworkMessage; import org.traccar.helper.NetworkUtil; +import org.traccar.model.LogRecord; +import org.traccar.session.ConnectionManager; import java.net.InetSocketAddress; -import java.net.SocketAddress; public class StandardLoggingHandler extends ChannelDuplexHandler { private static final Logger LOGGER = LoggerFactory.getLogger(StandardLoggingHandler.class); private final String protocol; + private ConnectionManager connectionManager; public StandardLoggingHandler(String protocol) { this.protocol = protocol; } + @Inject + public void setConnectionManager(ConnectionManager connectionManager) { + this.connectionManager = connectionManager; + } + @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - log(ctx, false, msg); + LogRecord record = createLogRecord(msg); + log(ctx, false, record); super.channelRead(ctx, msg); + if (record != null) { + connectionManager.updateLog(record); + } } @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { - log(ctx, true, msg); + log(ctx, true, createLogRecord(msg)); super.write(ctx, msg, promise); } - public void log(ChannelHandlerContext ctx, boolean downstream, Object o) { - if (o instanceof NetworkMessage) { - NetworkMessage networkMessage = (NetworkMessage) o; + private LogRecord createLogRecord(Object msg) { + if (msg instanceof NetworkMessage) { + NetworkMessage networkMessage = (NetworkMessage) msg; if (networkMessage.getMessage() instanceof ByteBuf) { - log(ctx, downstream, networkMessage.getRemoteAddress(), (ByteBuf) networkMessage.getMessage()); + LogRecord record = new LogRecord(); + record.setAddress((InetSocketAddress) networkMessage.getRemoteAddress()); + record.setData(ByteBufUtil.hexDump((ByteBuf) networkMessage.getMessage())); + return record; } - } else if (o instanceof ByteBuf) { - log(ctx, downstream, ctx.channel().remoteAddress(), (ByteBuf) o); } + return null; } - public void log(ChannelHandlerContext ctx, boolean downstream, SocketAddress remoteAddress, ByteBuf buf) { - StringBuilder message = new StringBuilder(); - - message.append("[").append(NetworkUtil.session(ctx.channel())).append(": "); - message.append(protocol); - if (downstream) { - message.append(" > "); - } else { - message.append(" < "); + private void log(ChannelHandlerContext ctx, boolean downstream, LogRecord record) { + if (record != null) { + StringBuilder message = new StringBuilder(); + message.append("[").append(NetworkUtil.session(ctx.channel())).append(": "); + message.append(protocol); + message.append(downstream ? " > " : " < "); + message.append(record.getAddress().getHostString()); + message.append("] "); + message.append(record.getData()); + LOGGER.info(message.toString()); } - - if (remoteAddress instanceof InetSocketAddress) { - message.append(((InetSocketAddress) remoteAddress).getHostString()); - } else { - message.append("unknown"); - } - message.append("] "); - - message.append(ByteBufUtil.hexDump(buf)); - - LOGGER.info(message.toString()); } } diff --git a/src/main/java/org/traccar/model/LogRecord.java b/src/main/java/org/traccar/model/LogRecord.java new file mode 100644 index 000000000..3feaadec2 --- /dev/null +++ b/src/main/java/org/traccar/model/LogRecord.java @@ -0,0 +1,63 @@ +/* + * Copyright 2023 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.model; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +import java.net.InetSocketAddress; + +public class LogRecord { + + private InetSocketAddress address; + + public void setAddress(InetSocketAddress address) { + this.address = address; + } + + @JsonIgnore + public InetSocketAddress getAddress() { + return address; + } + + public int getPort() { + return address.getPort(); + } + + public String getHost() { + return address.getHostString(); + } + + private String uniqueId; + + public String getUniqueId() { + return uniqueId; + } + + public void setUniqueId(String uniqueId) { + this.uniqueId = uniqueId; + } + + private String data; + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + +} diff --git a/src/main/java/org/traccar/session/ConnectionManager.java b/src/main/java/org/traccar/session/ConnectionManager.java index 3716fdf9a..7541cedfb 100644 --- a/src/main/java/org/traccar/session/ConnectionManager.java +++ b/src/main/java/org/traccar/session/ConnectionManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2023 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. @@ -30,6 +30,7 @@ import org.traccar.database.NotificationManager; import org.traccar.model.BaseModel; import org.traccar.model.Device; import org.traccar.model.Event; +import org.traccar.model.LogRecord; import org.traccar.model.Position; import org.traccar.model.User; import org.traccar.session.cache.CacheManager; @@ -64,7 +65,7 @@ public class ConnectionManager implements BroadcastInterface { private final long deviceTimeout; private final Map sessionsByDeviceId = new ConcurrentHashMap<>(); - private final Map> sessionsByEndpoint = new ConcurrentHashMap<>(); + private final Map> sessionsByEndpoint = new ConcurrentHashMap<>(); private final Config config; private final CacheManager cacheManager; @@ -104,9 +105,8 @@ public class ConnectionManager implements BroadcastInterface { Protocol protocol, Channel channel, SocketAddress remoteAddress, String... uniqueIds) throws Exception { - Endpoint endpoint = new Endpoint(channel, remoteAddress); Map endpointSessions = sessionsByEndpoint.getOrDefault( - endpoint, new ConcurrentHashMap<>()); + remoteAddress, new ConcurrentHashMap<>()); uniqueIds = Arrays.stream(uniqueIds).filter(Objects::nonNull).toArray(String[]::new); if (uniqueIds.length > 0) { @@ -133,19 +133,18 @@ public class ConnectionManager implements BroadcastInterface { DeviceSession oldSession = sessionsByDeviceId.remove(device.getId()); if (oldSession != null) { - Endpoint oldEndpoint = new Endpoint(oldSession.getChannel(), oldSession.getRemoteAddress()); - Map oldEndpointSessions = sessionsByEndpoint.get(oldEndpoint); + Map oldEndpointSessions = sessionsByEndpoint.get(oldSession.getRemoteAddress()); if (oldEndpointSessions != null && oldEndpointSessions.size() > 1) { oldEndpointSessions.remove(device.getUniqueId()); } else { - sessionsByEndpoint.remove(oldEndpoint); + sessionsByEndpoint.remove(oldSession.getRemoteAddress()); } } DeviceSession deviceSession = new DeviceSession( device.getId(), device.getUniqueId(), protocol, channel, remoteAddress); endpointSessions.put(device.getUniqueId(), deviceSession); - sessionsByEndpoint.put(endpoint, endpointSessions); + sessionsByEndpoint.put(remoteAddress, endpointSessions); sessionsByDeviceId.put(device.getId(), deviceSession); if (oldSession == null) { @@ -182,8 +181,7 @@ public class ConnectionManager implements BroadcastInterface { } public void deviceDisconnected(Channel channel, boolean supportsOffline) { - Endpoint endpoint = new Endpoint(channel, channel.remoteAddress()); - Map endpointSessions = sessionsByEndpoint.remove(endpoint); + Map endpointSessions = sessionsByEndpoint.remove(channel.remoteAddress()); if (endpointSessions != null) { for (DeviceSession deviceSession : endpointSessions.values()) { if (supportsOffline) { @@ -204,8 +202,7 @@ public class ConnectionManager implements BroadcastInterface { DeviceSession deviceSession = sessionsByDeviceId.remove(deviceId); if (deviceSession != null) { cacheManager.removeDevice(deviceId); - Endpoint endpoint = new Endpoint(deviceSession.getChannel(), deviceSession.getRemoteAddress()); - sessionsByEndpoint.computeIfPresent(endpoint, (e, sessions) -> { + sessionsByEndpoint.computeIfPresent(deviceSession.getRemoteAddress(), (e, sessions) -> { sessions.remove(deviceSession.getUniqueId()); return sessions.isEmpty() ? null : sessions; }); @@ -337,11 +334,24 @@ public class ConnectionManager implements BroadcastInterface { } } + public synchronized void updateLog(LogRecord record) { + var sessions = sessionsByEndpoint.getOrDefault(record.getAddress(), Map.of()); + for (var session : sessions.entrySet()) { + record.setUniqueId(session.getKey()); + for (long userId : deviceUsers.getOrDefault(session.getValue().getDeviceId(), Set.of())) { + for (UpdateListener listener : listeners.getOrDefault(userId, Set.of())) { + listener.onUpdateLog(record); + } + } + } + } + public interface UpdateListener { void onKeepalive(); void onUpdateDevice(Device device); void onUpdatePosition(Position position); void onUpdateEvent(Event event); + void onUpdateLog(LogRecord record); } public synchronized void addListener(long userId, UpdateListener listener) throws StorageException { diff --git a/src/main/java/org/traccar/session/Endpoint.java b/src/main/java/org/traccar/session/Endpoint.java deleted file mode 100644 index 76aac3444..000000000 --- a/src/main/java/org/traccar/session/Endpoint.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2022 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.session; - -import io.netty.channel.Channel; - -import java.net.SocketAddress; -import java.util.Objects; - -public class Endpoint { - - private final Channel channel; - private final SocketAddress remoteAddress; - - public Endpoint(Channel channel, SocketAddress remoteAddress) { - this.channel = channel; - this.remoteAddress = remoteAddress; - } - - public Channel getChannel() { - return channel; - } - - public SocketAddress getRemoteAddress() { - return remoteAddress; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - Endpoint endpoint = (Endpoint) o; - return channel.equals(endpoint.channel) && remoteAddress.equals(endpoint.remoteAddress); - } - - @Override - public int hashCode() { - return Objects.hash(channel, remoteAddress); - } - -} -- cgit v1.2.3 From 293a3dbf93b0d1924ecb96f8a3b11b65fbaa4248 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Thu, 28 Dec 2023 08:44:32 -0800 Subject: Show protocol instead of port --- .../java/org/traccar/handler/StandardLoggingHandler.java | 1 + src/main/java/org/traccar/model/LogRecord.java | 14 ++++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) (limited to 'src/main/java/org/traccar/model') diff --git a/src/main/java/org/traccar/handler/StandardLoggingHandler.java b/src/main/java/org/traccar/handler/StandardLoggingHandler.java index b495747d3..5978d632e 100644 --- a/src/main/java/org/traccar/handler/StandardLoggingHandler.java +++ b/src/main/java/org/traccar/handler/StandardLoggingHandler.java @@ -68,6 +68,7 @@ public class StandardLoggingHandler extends ChannelDuplexHandler { if (networkMessage.getMessage() instanceof ByteBuf) { LogRecord record = new LogRecord(); record.setAddress((InetSocketAddress) networkMessage.getRemoteAddress()); + record.setProtocol(protocol); record.setData(ByteBufUtil.hexDump((ByteBuf) networkMessage.getMessage())); return record; } diff --git a/src/main/java/org/traccar/model/LogRecord.java b/src/main/java/org/traccar/model/LogRecord.java index 3feaadec2..beabdf2f4 100644 --- a/src/main/java/org/traccar/model/LogRecord.java +++ b/src/main/java/org/traccar/model/LogRecord.java @@ -32,14 +32,20 @@ public class LogRecord { return address; } - public int getPort() { - return address.getPort(); - } - public String getHost() { return address.getHostString(); } + private String protocol; + + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + private String uniqueId; public String getUniqueId() { -- cgit v1.2.3 From 4959d904581d2408171917b8ee2cac38acb997e7 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sat, 30 Dec 2023 15:20:16 -0800 Subject: Logs from unknown device --- src/main/java/org/traccar/model/LogRecord.java | 10 ++++++++ .../org/traccar/session/ConnectionManager.java | 28 +++++++++++++++++----- 2 files changed, 32 insertions(+), 6 deletions(-) (limited to 'src/main/java/org/traccar/model') diff --git a/src/main/java/org/traccar/model/LogRecord.java b/src/main/java/org/traccar/model/LogRecord.java index beabdf2f4..c19163af3 100644 --- a/src/main/java/org/traccar/model/LogRecord.java +++ b/src/main/java/org/traccar/model/LogRecord.java @@ -56,6 +56,16 @@ public class LogRecord { this.uniqueId = uniqueId; } + private long deviceId; + + public long getDeviceId() { + return deviceId; + } + + public void setDeviceId(long deviceId) { + this.deviceId = deviceId; + } + private String data; public String getData() { diff --git a/src/main/java/org/traccar/session/ConnectionManager.java b/src/main/java/org/traccar/session/ConnectionManager.java index 7541cedfb..a598260aa 100644 --- a/src/main/java/org/traccar/session/ConnectionManager.java +++ b/src/main/java/org/traccar/session/ConnectionManager.java @@ -66,6 +66,7 @@ public class ConnectionManager implements BroadcastInterface { private final Map sessionsByDeviceId = new ConcurrentHashMap<>(); private final Map> sessionsByEndpoint = new ConcurrentHashMap<>(); + private final Map unknownByEndpoint = new ConcurrentHashMap<>(); private final Config config; private final CacheManager cacheManager; @@ -122,13 +123,15 @@ public class ConnectionManager implements BroadcastInterface { Device device = deviceLookupService.lookup(uniqueIds); + String firstUniqueId = uniqueIds[0]; if (device == null && config.getBoolean(Keys.DATABASE_REGISTER_UNKNOWN)) { - if (uniqueIds[0].matches(config.getString(Keys.DATABASE_REGISTER_UNKNOWN_REGEX))) { - device = addUnknownDevice(uniqueIds[0]); + if (firstUniqueId.matches(config.getString(Keys.DATABASE_REGISTER_UNKNOWN_REGEX))) { + device = addUnknownDevice(firstUniqueId); } } if (device != null) { + unknownByEndpoint.remove(remoteAddress); device.checkDisabled(); DeviceSession oldSession = sessionsByDeviceId.remove(device.getId()); @@ -153,6 +156,7 @@ public class ConnectionManager implements BroadcastInterface { return deviceSession; } else { + unknownByEndpoint.put(remoteAddress, firstUniqueId); LOGGER.warn("Unknown device - " + String.join(" ", uniqueIds) + " (" + ((InetSocketAddress) remoteAddress).getHostString() + ")"); return null; @@ -181,7 +185,8 @@ public class ConnectionManager implements BroadcastInterface { } public void deviceDisconnected(Channel channel, boolean supportsOffline) { - Map endpointSessions = sessionsByEndpoint.remove(channel.remoteAddress()); + SocketAddress remoteAddress = channel.remoteAddress(); + Map endpointSessions = sessionsByEndpoint.remove(remoteAddress); if (endpointSessions != null) { for (DeviceSession deviceSession : endpointSessions.values()) { if (supportsOffline) { @@ -191,6 +196,7 @@ public class ConnectionManager implements BroadcastInterface { cacheManager.removeDevice(deviceSession.getDeviceId()); } } + unknownByEndpoint.remove(remoteAddress); } public void deviceUnknown(long deviceId) { @@ -336,9 +342,19 @@ public class ConnectionManager implements BroadcastInterface { public synchronized void updateLog(LogRecord record) { var sessions = sessionsByEndpoint.getOrDefault(record.getAddress(), Map.of()); - for (var session : sessions.entrySet()) { - record.setUniqueId(session.getKey()); - for (long userId : deviceUsers.getOrDefault(session.getValue().getDeviceId(), Set.of())) { + if (sessions.isEmpty()) { + String unknownUniqueId = unknownByEndpoint.get(record.getAddress()); + if (unknownUniqueId != null) { + record.setUniqueId(unknownUniqueId); + listeners.values().stream() + .flatMap(Set::stream) + .forEach((listener) -> listener.onUpdateLog(record)); + } + } else { + var firstEntry = sessions.entrySet().iterator().next(); + record.setUniqueId(firstEntry.getKey()); + record.setDeviceId(firstEntry.getValue().getDeviceId()); + for (long userId : deviceUsers.getOrDefault(record.getDeviceId(), Set.of())) { for (UpdateListener listener : listeners.getOrDefault(userId, Set.of())) { listener.onUpdateLog(record); } -- cgit v1.2.3 From c506f723c905fed6995cde26168dce9948599fd4 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Mon, 5 Feb 2024 21:42:08 -0800 Subject: Add unique id validation --- src/main/java/org/traccar/model/Device.java | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/main/java/org/traccar/model') diff --git a/src/main/java/org/traccar/model/Device.java b/src/main/java/org/traccar/model/Device.java index e07815976..a3088a613 100644 --- a/src/main/java/org/traccar/model/Device.java +++ b/src/main/java/org/traccar/model/Device.java @@ -53,6 +53,9 @@ public class Device extends GroupedModel implements Disableable, Schedulable { } public void setUniqueId(String uniqueId) { + if (uniqueId.contains("../") || uniqueId.contains("..\\")) { + throw new IllegalArgumentException("Invalid unique id"); + } this.uniqueId = uniqueId.trim(); } -- cgit v1.2.3