Skip to content

Commit

Permalink
Merge branch 'traccar:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
gpproton authored Nov 30, 2023
2 parents a9b82f3 + e0d67dd commit 7d33c5d
Show file tree
Hide file tree
Showing 24 changed files with 315 additions and 112 deletions.
17 changes: 17 additions & 0 deletions schema/changelog-5.11.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd"
logicalFilePath="changelog-5.11">

<changeSet author="author" id="changelog-5.11">

<addColumn tableName="tc_users">
<column name="temporary" type="BOOLEAN" defaultValueBoolean="false" />
</addColumn>

</changeSet>

</databaseChangeLog>
1 change: 1 addition & 0 deletions schema/changelog-master.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,6 @@
<include file="changelog-5.8.xml" relativeToChangelogFile="true" />
<include file="changelog-5.9.xml" relativeToChangelogFile="true" />
<include file="changelog-5.10.xml" relativeToChangelogFile="true" />
<include file="changelog-5.11.xml" relativeToChangelogFile="true" />

</databaseChangeLog>
7 changes: 4 additions & 3 deletions src/main/java/org/traccar/BaseProtocolDecoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,13 @@ protected TimeZone getTimeZone(long deviceId) {
}

protected TimeZone getTimeZone(long deviceId, String defaultTimeZone) {
TimeZone result = TimeZone.getTimeZone(defaultTimeZone);
String timeZoneName = AttributeUtil.lookup(cacheManager, Keys.DECODER_TIMEZONE, deviceId);
if (timeZoneName != null) {
result = TimeZone.getTimeZone(timeZoneName);
return TimeZone.getTimeZone(timeZoneName);
} else if (defaultTimeZone != null) {
return TimeZone.getTimeZone(defaultTimeZone);
}
return result;
return null;
}

public DeviceSession getDeviceSession(Channel channel, SocketAddress remoteAddress, String... uniqueIds) {
Expand Down
37 changes: 37 additions & 0 deletions src/main/java/org/traccar/api/resource/DeviceResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -46,7 +49,9 @@
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;

Expand All @@ -67,6 +72,9 @@ public class DeviceResource extends BaseObjectResource<Device> {
@Inject
private MediaManager mediaManager;

@Inject
private TokenManager tokenManager;

public DeviceResource() {
super(Device.class);
}
Expand Down Expand Up @@ -183,4 +191,33 @@ public Response uploadImage(
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);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public Response update(
@FormParam("token") String token, @FormParam("password") String password)
throws StorageException, GeneralSecurityException, IOException {

long userId = tokenManager.verifyToken(token);
long userId = tokenManager.verifyToken(token).getUserId();
User user = storage.getObject(User.class, new Request(
new Columns.All(), new Condition.Equals("id", userId)));
if (user != null) {
Expand Down
48 changes: 11 additions & 37 deletions src/main/java/org/traccar/api/resource/SessionResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@

import org.traccar.api.BaseResource;
import org.traccar.api.security.CodeRequiredException;
import org.traccar.api.security.LoginResult;
import org.traccar.api.security.LoginService;
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.WebHelper;
import org.traccar.model.User;
Expand All @@ -33,7 +33,6 @@
import jakarta.annotation.Nullable;
import jakarta.annotation.security.PermitAll;
import jakarta.inject.Inject;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
Expand All @@ -49,8 +48,6 @@
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.io.IOException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.util.Date;
import java.net.URI;
Expand All @@ -61,8 +58,7 @@
public class SessionResource extends BaseResource {

public static final String USER_ID_KEY = "userId";
public static final String USER_COOKIE_KEY = "user";
public static final String PASS_COOKIE_KEY = "password";
public static final String EXPIRATION_KEY = "expiration";

@Inject
private LoginService loginService;
Expand All @@ -82,48 +78,22 @@ public class SessionResource extends BaseResource {
public User get(@QueryParam("token") String token) throws StorageException, IOException, GeneralSecurityException {

if (token != null) {
User user = loginService.login(token);
LoginResult loginResult = loginService.login(token);
User user = loginResult.getUser();
if (user != null) {
request.getSession().setAttribute(USER_ID_KEY, user.getId());
request.getSession().setAttribute(EXPIRATION_KEY, loginResult.getExpiration());
LogAction.login(user.getId(), WebHelper.retrieveRemoteAddress(request));
return user;
}
}

Long userId = (Long) request.getSession().getAttribute(USER_ID_KEY);
if (userId == null) {

Cookie[] cookies = request.getCookies();
String email = null, password = null;
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals(USER_COOKIE_KEY)) {
byte[] emailBytes = DataConverter.parseBase64(
URLDecoder.decode(cookie.getValue(), StandardCharsets.US_ASCII));
email = new String(emailBytes, StandardCharsets.UTF_8);
} else if (cookie.getName().equals(PASS_COOKIE_KEY)) {
byte[] passwordBytes = DataConverter.parseBase64(
URLDecoder.decode(cookie.getValue(), StandardCharsets.US_ASCII));
password = new String(passwordBytes, StandardCharsets.UTF_8);
}
}
}
if (email != null && password != null) {
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));
return user;
}
}

} else {

if (userId != null) {
User user = permissionsService.getUser(userId);
if (user != null) {
return user;
}

}

throw new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build());
Expand All @@ -148,7 +118,7 @@ public User add(
@FormParam("code") Integer code) throws StorageException {
User user;
try {
user = loginService.login(email, password, code);
user = loginService.login(email, password, code).getUser();
} catch (CodeRequiredException e) {
Response response = Response
.status(Response.Status.UNAUTHORIZED)
Expand Down Expand Up @@ -177,6 +147,10 @@ public Response remove() {
@POST
public String requestToken(
@FormParam("expiration") Date expiration) throws StorageException, GeneralSecurityException, IOException {
Date currentExpiration = (Date) request.getSession().getAttribute(EXPIRATION_KEY);
if (currentExpiration != null && currentExpiration.before(expiration)) {
expiration = currentExpiration;
}
return tokenManager.generateToken(getUserId(), expiration);
}

Expand Down
29 changes: 29 additions & 0 deletions src/main/java/org/traccar/api/security/LoginResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.traccar.api.security;

import org.traccar.model.User;

import java.util.Date;

public class LoginResult {

private final User user;
private final Date expiration;

public LoginResult(User user) {
this(user, null);
}

public LoginResult(User user, Date expiration) {
this.user = user;
this.expiration = expiration;
}

public User getUser() {
return user;
}

public Date getExpiration() {
return expiration;
}

}
22 changes: 11 additions & 11 deletions src/main/java/org/traccar/api/security/LoginService.java
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -58,20 +58,20 @@ public LoginService(
forceOpenId = config.getBoolean(Keys.OPENID_FORCE);
}

public User login(String token) throws StorageException, GeneralSecurityException, IOException {
public LoginResult login(String token) throws StorageException, GeneralSecurityException, IOException {
if (serviceAccountToken != null && serviceAccountToken.equals(token)) {
return new ServiceAccountUser();
return new LoginResult(new ServiceAccountUser());
}
long userId = tokenManager.verifyToken(token);
TokenManager.TokenData tokenData = tokenManager.verifyToken(token);
User user = storage.getObject(User.class, new Request(
new Columns.All(), new Condition.Equals("id", userId)));
new Columns.All(), new Condition.Equals("id", tokenData.getUserId())));
if (user != null) {
checkUserEnabled(user);
}
return user;
return new LoginResult(user, tokenData.getExpiration());
}

public User login(String email, String password, Integer code) throws StorageException {
public LoginResult login(String email, String password, Integer code) throws StorageException {
if (forceOpenId) {
return null;
}
Expand All @@ -87,20 +87,20 @@ public User login(String email, String password, Integer code) throws StorageExc
|| !forceLdap && user.isPasswordValid(password)) {
checkUserCode(user, code);
checkUserEnabled(user);
return user;
return new LoginResult(user);
}
} else {
if (ldapProvider != null && ldapProvider.login(email, password)) {
user = ldapProvider.getUser(email);
user.setId(storage.addObject(user, new Request(new Columns.Exclude("id"))));
checkUserEnabled(user);
return user;
return new LoginResult(user);
}
}
return null;
}

public User login(String email, String name, boolean administrator) throws StorageException {
public LoginResult 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)));
Expand All @@ -115,7 +115,7 @@ public User login(String email, String name, boolean administrator) throws Stora
user.setId(storage.addObject(user, new Request(new Columns.Exclude("id"))));
}
checkUserEnabled(user);
return user;
return new LoginResult(user);
}

private void checkUserEnabled(User user) throws SecurityException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.util.Date;

public class SecurityRequestFilter implements ContainerRequestFilter {

Expand Down Expand Up @@ -82,16 +83,18 @@ public void filter(ContainerRequestContext requestContext) {
if (authHeader != null) {

try {
User user;
LoginResult loginResult;
if (authHeader.startsWith("Bearer ")) {
user = loginService.login(authHeader.substring(7));
loginResult = loginService.login(authHeader.substring(7));
} else {
String[] auth = decodeBasicAuth(authHeader);
user = loginService.login(auth[0], auth[1], null);
loginResult = loginService.login(auth[0], auth[1], null);
}
User user = loginResult.getUser();
if (user != null) {
statisticsManager.registerRequest(user.getId());
securityContext = new UserSecurityContext(new UserPrincipal(user.getId()));
securityContext = new UserSecurityContext(
new UserPrincipal(user.getId(), loginResult.getExpiration()));
}
} catch (StorageException | GeneralSecurityException | IOException e) {
throw new WebApplicationException(e);
Expand All @@ -100,12 +103,13 @@ public void filter(ContainerRequestContext requestContext) {
} else if (request.getSession() != null) {

Long userId = (Long) request.getSession().getAttribute(SessionResource.USER_ID_KEY);
Date expiration = (Date) request.getSession().getAttribute(SessionResource.EXPIRATION_KEY);
if (userId != null) {
User user = injector.getInstance(PermissionsService.class).getUser(userId);
if (user != null) {
user.checkDisabled();
statisticsManager.registerRequest(userId);
securityContext = new UserSecurityContext(new UserPrincipal(userId));
securityContext = new UserSecurityContext(new UserPrincipal(userId, expiration));
}
}

Expand Down
Loading

0 comments on commit 7d33c5d

Please sign in to comment.