From 0b9d50cd09be662eb305242da6ebc321099cdc4d Mon Sep 17 00:00:00 2001 From: alisa911 Date: Wed, 20 Nov 2024 10:15:15 +0200 Subject: [PATCH 01/23] remove unused annotations --- .../net/osmand/server/controllers/pub/GpxController.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/pub/GpxController.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/pub/GpxController.java index 3df0aac1c..24d057a67 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/pub/GpxController.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/pub/GpxController.java @@ -53,7 +53,6 @@ import net.osmand.server.WebSecurityConfiguration.OsmAndProUser; import net.osmand.server.api.services.GpxService; -import net.osmand.server.api.services.OsmAndMapsService; import net.osmand.server.controllers.pub.UserSessionResources.GPXSessionContext; import net.osmand.server.controllers.pub.UserSessionResources.GPXSessionFile; import net.osmand.server.utils.WebGpxParser; @@ -86,7 +85,6 @@ public class GpxController { String srtmLocation; @PostMapping(path = {"/clear"}, produces = "application/json") - @ResponseBody public String clear(HttpServletRequest request, HttpSession httpSession) throws IOException { GPXSessionContext ctx = session.getGpxResources(httpSession); for (File f : ctx.tempFiles) { @@ -98,14 +96,12 @@ public String clear(HttpServletRequest request, HttpSession httpSession) throws } @GetMapping(path = { "/get-gpx-info" }, produces = "application/json") - @ResponseBody public String getGpx(HttpSession httpSession) { GPXSessionContext ctx = session.getGpxResources(httpSession); return gson.toJson(Map.of("all", ctx.files)); } @GetMapping(path = {"/get-gpx-file"}, produces = "application/json") - @ResponseBody public ResponseEntity getGpx(@RequestParam String name, HttpSession httpSession) { GPXSessionContext ctx = session.getGpxResources(httpSession); File tmpGpx = null; @@ -225,7 +221,6 @@ public ResponseEntity uploadGpx(@RequestPart(name = "file") @Valid @NotN } @PostMapping(path = {"/get-gpx-analysis"}, produces = "application/json") - @ResponseBody public ResponseEntity getGpxInfo(@RequestPart(name = "file") @Valid @NotNull @NotEmpty MultipartFile file, HttpServletRequest request, HttpSession httpSession) throws IOException { @@ -260,7 +255,6 @@ public ResponseEntity getGpxInfo(@RequestPart(name = "file") @Valid @Not } @PostMapping(path = {"/process-track-data"}, produces = "application/json") - @ResponseBody public ResponseEntity processTrackData(@RequestPart(name = "file") @Valid @NotNull @NotEmpty MultipartFile file, HttpSession httpSession) throws IOException { @@ -282,7 +276,6 @@ public ResponseEntity processTrackData(@RequestPart(name = "file") @Vali } @PostMapping(path = {"/save-track-data"}, produces = "application/json") - @ResponseBody public ResponseEntity saveTrackData(@RequestBody String data, HttpSession httpSession) throws IOException { WebGpxParser.TrackData trackData = new Gson().fromJson(data, WebGpxParser.TrackData.class); From e017c8f6bc4a813286e62c1114b7c693be0c7de0 Mon Sep 17 00:00:00 2001 From: alisa911 Date: Wed, 20 Nov 2024 16:42:29 +0200 Subject: [PATCH 02/23] add share gpx --- db/sql_changeset_schema | 2 +- .../api/repo/PremiumUserFilesRepository.java | 20 ++- .../server/api/services/UserdataService.java | 137 ++++++++++++++++-- .../controllers/user/MapApiController.java | 119 ++++++++++++++- 4 files changed, 253 insertions(+), 25 deletions(-) diff --git a/db/sql_changeset_schema b/db/sql_changeset_schema index 09a15be3e..2ebdb7d4d 100644 --- a/db/sql_changeset_schema +++ b/db/sql_changeset_schema @@ -27,7 +27,7 @@ ALTER TABLE supporters_device_sub add primary key (sku, orderid); ------ PREMIUM accounts ---- CREATE TABLE user_accounts(id serial primary key, email text not null, tokendevice text, orderid text, token text, tokentime timestamp, regtime timestamp); CREATE TABLE user_account_devices(id serial primary key, userid integer, deviceid text, accesstoken text, lang text, brand text, model text, udpatetime timestamp); -CREATE TABLE user_files(id bigserial primary key, type text, name text, userid integer, deviceid integer, updatetime timestamp, clienttime timestamp, filesize bigint, zipfilesize bigint, storage text, gendetails jsonb, data bytea); +CREATE TABLE user_files(id bigserial primary key, type text, name text, userid integer, deviceid integer, updatetime timestamp, clienttime timestamp, filesize bigint, zipfilesize bigint, storage text, gendetails jsonb, data bytea, shared_url text, shared_info jsonb); CREATE TABLE promo_campaigns(name text, starttime timestamp, endtime timestamp, subactivemonths integer, numberlimit integer, used integer, lastusers text); ALTER TABLE user_accounts ADD CONSTRAINT email_uniq UNIQUE (email); --- diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/PremiumUserFilesRepository.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/PremiumUserFilesRepository.java index 64cadb541..865afa6af 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/PremiumUserFilesRepository.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/PremiumUserFilesRepository.java @@ -35,9 +35,11 @@ public interface PremiumUserFilesRepository extends JpaRepository findAllByUseridAndNameAndTypeOrderByUpdatetimeDesc(int userid, String name, String type); - + Iterable findAllByUserid(int userid); - + + UserFile findUserFileBySharedUrl(String sharedUrl); + @Query("SELECT uf FROM UserFile uf " + "WHERE uf.userid = :userid AND uf.name LIKE :folderName% AND uf.type = :type " + "AND uf.updatetime = (SELECT MAX(uft.updatetime) FROM UserFile uft WHERE uft.userid = :userid AND uft.name = uf.name)") @@ -89,13 +91,17 @@ class UserFile { @Column(name = "gendetails", columnDefinition = "jsonb") @Type(type = "net.osmand.server.assist.data.JsonbType") public JsonObject details; - -// @Fetch(FetchMode.JOIN) + @Column(name = "data", columnDefinition="bytea") public byte[] data; - -// @Lob -// public Blob data; + + @Column(name = "shared_url") + public String sharedUrl; + + @Column(name = "shared_info", columnDefinition = "jsonb") + @Type(type = "net.osmand.server.assist.data.JsonbType") + public JsonObject sharedInfo; + } diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/UserdataService.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/UserdataService.java index b4c0b3594..6271ed091 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/UserdataService.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/UserdataService.java @@ -1,6 +1,7 @@ package net.osmand.server.api.services; import static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM; +import static org.springframework.util.MimeTypeUtils.APPLICATION_OCTET_STREAM_VALUE; import java.io.ByteArrayInputStream; import java.io.File; @@ -10,17 +11,10 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.Optional; -import java.util.Random; -import java.util.Set; -import java.util.TreeSet; +import java.util.*; import java.util.concurrent.TimeUnit; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; @@ -33,6 +27,10 @@ import javax.servlet.http.HttpServletResponse; import javax.transaction.Transactional; +import net.osmand.shared.gpx.GpxFile; +import net.osmand.shared.gpx.GpxUtilities; +import okio.Buffer; +import okio.Source; import org.apache.commons.collections4.IterableUtils; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; @@ -1195,4 +1193,125 @@ public void updateDeviceLangInfo(PremiumUserDevicesRepository.PremiumUserDevice devicesRepository.saveAndFlush(dev); } } + + @Transactional + public String generateSharedUrl(PremiumUserFilesRepository.UserFile userFile) { + String fileName = userFile.name.replaceAll("\\s+", "_").toLowerCase(); + String uniqueToken = UUID.randomUUID().toString(); + String url = fileName + "_" + uniqueToken; + userFile.sharedUrl = url; + filesRepository.saveAndFlush(userFile); + + return url; + } + + public PremiumUserFilesRepository.UserFile getUserFileBySharedUrl(String token) { + return filesRepository.findUserFileBySharedUrl(token); + } + + public MapApiController.FileDownloadResult getFile(PremiumUserFilesRepository.UserFile userFile, PremiumUserDevicesRepository.PremiumUserDevice dev) throws IOException { + if (userFile == null || dev == null) { + return null; + } + try (InputStream bin = getInputStream(dev, userFile)) { + if (bin != null) { + InputStream inputStream = new GZIPInputStream(bin); + String fileName = URLEncoder.encode(sanitizeEncode(userFile.name), StandardCharsets.UTF_8); + return new MapApiController.FileDownloadResult(inputStream, fileName, APPLICATION_OCTET_STREAM_VALUE); + } + } + return null; + } + + public GpxFile getFile(PremiumUserFilesRepository.UserFile file) throws IOException { + if (file == null || file.data == null) { + return null; + } + try (InputStream inputStream = new GZIPInputStream(new ByteArrayInputStream(file.data)); + Source source = new Buffer().readFrom(inputStream)) { + GpxFile gpxFile = GpxUtilities.INSTANCE.loadGpxFile(source); + if (gpxFile.getError() == null) { + return gpxFile; + } + return null; + } + } + + public static class FileSharedInfo { + public Set accessedUsers; + public Set blackList; + + public FileSharedInfo(Set accessedUsers, Set blackList) { + this.accessedUsers = accessedUsers; + this.blackList = blackList; + } + + public FileSharedInfo() { + this.accessedUsers = new HashSet<>(); + this.blackList = new HashSet<>(); + } + } + + public boolean saveAccessedUser(PremiumUserDevicesRepository.PremiumUserDevice dev, PremiumUserFilesRepository.UserFile userFile) { + final String ACCESSED_USERS = "accessedUsers"; + FileSharedInfo fileSharedInfo = gson.fromJson(userFile.sharedInfo, FileSharedInfo.class); + if (fileSharedInfo == null) { + fileSharedInfo = new FileSharedInfo(); + } + if (!fileSharedInfo.blackList.contains(dev.userid)) { + if (fileSharedInfo.accessedUsers.add(dev.userid)) { + userFile.sharedInfo.add(ACCESSED_USERS, gson.toJsonTree(fileSharedInfo.accessedUsers)); + filesRepository.saveAndFlush(userFile); + } + return true; + } + return false; + } + + public List getAccessedUsers(PremiumUserFilesRepository.UserFile userFile) { + List accessedUsers = new ArrayList<>(); + FileSharedInfo fileSharedInfo = gson.fromJson(userFile.sharedInfo, FileSharedInfo.class); + if (fileSharedInfo != null && fileSharedInfo.accessedUsers != null) { + for (Integer userId : fileSharedInfo.accessedUsers) { + PremiumUsersRepository.PremiumUser pu = usersRepository.findById(userId); + if (pu != null) { + accessedUsers.add(pu.email); + } + } + } + return accessedUsers; + } + + @Transactional + public boolean createFileBlacklist(PremiumUserFilesRepository.UserFile userFile, List list) { + final String BLACK_LIST = "blackList"; + FileSharedInfo fileSharedInfo = gson.fromJson(userFile.sharedInfo, FileSharedInfo.class); + if (fileSharedInfo == null) { + fileSharedInfo = new FileSharedInfo(); + } + fileSharedInfo.blackList.addAll(list.stream().map(email -> { + PremiumUsersRepository.PremiumUser pu = usersRepository.findByEmailIgnoreCase(email); + return pu != null ? pu.id : null; + }).filter(Objects::nonNull).toList()); + if (fileSharedInfo.blackList.isEmpty()) { + return false; + } + userFile.sharedInfo.add(BLACK_LIST, gson.toJsonTree(fileSharedInfo.blackList)); + filesRepository.saveAndFlush(userFile); + return true; + } + + public List getBlackList(PremiumUserFilesRepository.UserFile userFile) { + List blackList = new ArrayList<>(); + FileSharedInfo fileSharedInfo = gson.fromJson(userFile.sharedInfo, FileSharedInfo.class); + if (fileSharedInfo != null && fileSharedInfo.blackList != null) { + for (Integer userId : fileSharedInfo.blackList) { + PremiumUsersRepository.PremiumUser pu = usersRepository.findById(userId); + if (pu != null) { + blackList.add(pu.email); + } + } + } + return blackList; + } } diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/MapApiController.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/MapApiController.java index 3e9e817a1..143190bbc 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/MapApiController.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/MapApiController.java @@ -22,18 +22,17 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.transaction.Transactional; import javax.validation.Valid; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; -import com.google.gson.JsonParser; import net.osmand.map.OsmandRegions; import net.osmand.server.WebSecurityConfiguration; import net.osmand.server.api.repo.DeviceSubscriptionsRepository; import net.osmand.server.api.repo.PremiumUserDevicesRepository; import net.osmand.server.api.repo.PremiumUsersRepository; import net.osmand.server.api.services.*; -import net.osmand.server.controllers.pub.UserSessionResources; import net.osmand.server.utils.WebGpxParser; import net.osmand.shared.gpx.GpxFile; import net.osmand.shared.gpx.GpxUtilities; @@ -43,10 +42,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.io.AbstractResource; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.FileSystemResource; -import org.springframework.core.io.Resource; +import org.springframework.core.io.*; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -67,8 +63,6 @@ import net.osmand.server.api.repo.PremiumUserFilesRepository; import net.osmand.server.api.repo.PremiumUserFilesRepository.UserFile; import net.osmand.server.api.repo.PremiumUserFilesRepository.UserFileNoData; -import net.osmand.server.controllers.pub.GpxController; -import net.osmand.server.controllers.pub.UserdataController; import net.osmand.server.controllers.pub.UserdataController.UserFilesResults; import org.xmlpull.v1.XmlPullParserException; @@ -82,6 +76,7 @@ public class MapApiController { private static final String SRTM_ANALYSIS = "srtm-analysis"; private static final String DONE_SUFFIX = "-done"; private static final String FAV_POINT_GROUPS = "pointGroups"; + private static final String FILE_NOT_FOUND = "File not found"; private static final long ANALYSIS_RERUN = 1692026215870L; // 14-08-2023 @@ -833,4 +828,112 @@ public String getRegionsByLatlon(@RequestParam("lat") double lat, @RequestParam( regions = osmandRegions.getRegionsToDownload(lat, lon, regions); return gson.toJson(Map.of("regions", regions)); } + + @GetMapping(path = {"/generate-shared-url"}, produces = "application/json") + public ResponseEntity generateGpxSharedUrl(@RequestParam String name, + @RequestParam String type) { + PremiumUserDevicesRepository.PremiumUserDevice dev = checkUser(); + if (dev == null) { + return tokenNotValid(); + } + PremiumUserFilesRepository.UserFile userFile = userdataService.getUserFile(name, type, null, dev); + if (userFile == null) { + return ResponseEntity.badRequest().body(FILE_NOT_FOUND); + } + String sharedUrl = userdataService.generateSharedUrl(userFile); + return ResponseEntity.ok(gson.toJson(Map.of("sharedUrl", sharedUrl))); + } + + @GetMapping(path = {"/gpx/share/{token}"}, produces = "application/json") + @Transactional + public ResponseEntity getTrackByUrl(@PathVariable String token, + @RequestParam(required = false) Boolean downloadFile) throws IOException { + PremiumUserDevicesRepository.PremiumUserDevice dev = checkUser(); + if (dev == null) { + return tokenNotValid(); + } + PremiumUserFilesRepository.UserFile userFile = userdataService.getUserFileBySharedUrl(token); + if (userFile != null) { + boolean saved = userdataService.saveAccessedUser(dev, userFile); + if (!saved) { + return ResponseEntity.badRequest().body("User is in the blacklist"); + } + if (downloadFile != null && downloadFile) { + FileDownloadResult fileResult = userdataService.getFile(userFile, dev); + if (fileResult == null) { + return ResponseEntity.badRequest().body(FILE_NOT_FOUND); + } + return ResponseEntity.ok() + .header("Content-Disposition", "attachment; filename=" + fileResult.fileName) + .contentType(org.springframework.http.MediaType.valueOf(fileResult.contentType)) + .body(new InputStreamResource(fileResult.inputStream)); + } + + GpxFile gpxFile = userdataService.getFile(userFile); + + if (gpxFile.getError() == null) { + GpxTrackAnalysis analysis = gpxFile.getAnalysis(System.currentTimeMillis()); + WebGpxParser.TrackData gpxData = gpxService.getTrackDataByGpxFile(gpxFile, null, analysis); + if (gpxData != null) { + return ResponseEntity.ok(gsonWithNans.toJson(Map.of("gpx_data", gpxData))); + } + } + } + return ResponseEntity.badRequest().body(FILE_NOT_FOUND); + } + + public record FileDownloadResult(InputStream inputStream, String fileName, String contentType) { + } + + @GetMapping(path = {"/accessed-users"}, produces = "application/json") + public ResponseEntity getAccessedUsers(@RequestParam String name, + @RequestParam String type) { + PremiumUserDevicesRepository.PremiumUserDevice dev = checkUser(); + if (dev == null) { + return tokenNotValid(); + } + PremiumUserFilesRepository.UserFile userFile = userdataService.getUserFile(name, type, null, dev); + if (userFile == null) { + return ResponseEntity.badRequest().body(FILE_NOT_FOUND); + } + List users = userdataService.getAccessedUsers(userFile); + return ResponseEntity.ok(gson.toJson(Map.of("accessedUsers", users))); + } + + @PostMapping(path = {"/create-file-blacklist"}, produces = "application/json") + public ResponseEntity createFileBlacklist(@RequestBody List list, + @RequestParam String name, + @RequestParam String type) { + PremiumUserDevicesRepository.PremiumUserDevice dev = checkUser(); + if (dev == null) { + return tokenNotValid(); + } + PremiumUserFilesRepository.UserFile userFile = userdataService.getUserFile(name, type, null, dev); + if (userFile == null) { + return ResponseEntity.badRequest().body(FILE_NOT_FOUND); + } + boolean created = userdataService.createFileBlacklist(userFile, list); + if (!created) { + return ResponseEntity.badRequest().body("Error creating blacklist"); + } + return ResponseEntity.ok("Blacklist created"); + } + + @GetMapping(path = {"/get-blacklist"}, produces = "application/json") + public ResponseEntity getBlacklist(@RequestParam String name, + @RequestParam String type) { + PremiumUserDevicesRepository.PremiumUserDevice dev = checkUser(); + if (dev == null) { + return tokenNotValid(); + } + PremiumUserFilesRepository.UserFile userFile = userdataService.getUserFile(name, type, null, dev); + if (userFile == null) { + return ResponseEntity.badRequest().body(FILE_NOT_FOUND); + } + List users = userdataService.getBlackList(userFile); + if (users == null) { + return ResponseEntity.badRequest().body("No blacklisted users"); + } + return ResponseEntity.ok(gson.toJson(Map.of("blacklist", users))); + } } From 96fedd7ab996cf2546e7bffede81517863ed3377 Mon Sep 17 00:00:00 2001 From: alisa911 Date: Wed, 20 Nov 2024 19:07:41 +0200 Subject: [PATCH 03/23] fix shared token --- .../net/osmand/server/api/services/UserdataService.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/UserdataService.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/UserdataService.java index 6271ed091..03d59ddb2 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/UserdataService.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/UserdataService.java @@ -1196,13 +1196,11 @@ public void updateDeviceLangInfo(PremiumUserDevicesRepository.PremiumUserDevice @Transactional public String generateSharedUrl(PremiumUserFilesRepository.UserFile userFile) { - String fileName = userFile.name.replaceAll("\\s+", "_").toLowerCase(); String uniqueToken = UUID.randomUUID().toString(); - String url = fileName + "_" + uniqueToken; - userFile.sharedUrl = url; + userFile.sharedUrl = uniqueToken; filesRepository.saveAndFlush(userFile); - return url; + return uniqueToken; } public PremiumUserFilesRepository.UserFile getUserFileBySharedUrl(String token) { From 14d564e5f758462b2cc0393bb7919f72da6f38b1 Mon Sep 17 00:00:00 2001 From: alisa911 Date: Thu, 21 Nov 2024 12:37:13 +0200 Subject: [PATCH 04/23] refactoring share gpx --- db/sql_changeset_schema | 2 +- .../api/repo/PremiumUserFilesRepository.java | 6 +- .../server/api/services/UserdataService.java | 105 ------------- .../api/services/shareGpx/FileSharedInfo.java | 129 +++++++++++++++ .../services/shareGpx/ShareGpxService.java | 148 ++++++++++++++++++ .../controllers/user/MapApiController.java | 80 ++++++---- 6 files changed, 331 insertions(+), 139 deletions(-) create mode 100644 java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/shareGpx/FileSharedInfo.java create mode 100644 java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/shareGpx/ShareGpxService.java diff --git a/db/sql_changeset_schema b/db/sql_changeset_schema index 2ebdb7d4d..13eefce5e 100644 --- a/db/sql_changeset_schema +++ b/db/sql_changeset_schema @@ -27,7 +27,7 @@ ALTER TABLE supporters_device_sub add primary key (sku, orderid); ------ PREMIUM accounts ---- CREATE TABLE user_accounts(id serial primary key, email text not null, tokendevice text, orderid text, token text, tokentime timestamp, regtime timestamp); CREATE TABLE user_account_devices(id serial primary key, userid integer, deviceid text, accesstoken text, lang text, brand text, model text, udpatetime timestamp); -CREATE TABLE user_files(id bigserial primary key, type text, name text, userid integer, deviceid integer, updatetime timestamp, clienttime timestamp, filesize bigint, zipfilesize bigint, storage text, gendetails jsonb, data bytea, shared_url text, shared_info jsonb); +CREATE TABLE user_files(id bigserial primary key, type text, name text, userid integer, deviceid integer, updatetime timestamp, clienttime timestamp, filesize bigint, zipfilesize bigint, storage text, gendetails jsonb, data bytea, shared_code text, shared_info jsonb); CREATE TABLE promo_campaigns(name text, starttime timestamp, endtime timestamp, subactivemonths integer, numberlimit integer, used integer, lastusers text); ALTER TABLE user_accounts ADD CONSTRAINT email_uniq UNIQUE (email); --- diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/PremiumUserFilesRepository.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/PremiumUserFilesRepository.java index 865afa6af..51350555c 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/PremiumUserFilesRepository.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/PremiumUserFilesRepository.java @@ -38,7 +38,7 @@ public interface PremiumUserFilesRepository extends JpaRepository findAllByUserid(int userid); - UserFile findUserFileBySharedUrl(String sharedUrl); + UserFile findUserFileBySharedCode(String code); @Query("SELECT uf FROM UserFile uf " + "WHERE uf.userid = :userid AND uf.name LIKE :folderName% AND uf.type = :type " + @@ -95,8 +95,8 @@ class UserFile { @Column(name = "data", columnDefinition="bytea") public byte[] data; - @Column(name = "shared_url") - public String sharedUrl; + @Column(name = "shared_code") + public String sharedCode; @Column(name = "shared_info", columnDefinition = "jsonb") @Type(type = "net.osmand.server.assist.data.JsonbType") diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/UserdataService.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/UserdataService.java index 03d59ddb2..e8e67f3d3 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/UserdataService.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/UserdataService.java @@ -1194,19 +1194,6 @@ public void updateDeviceLangInfo(PremiumUserDevicesRepository.PremiumUserDevice } } - @Transactional - public String generateSharedUrl(PremiumUserFilesRepository.UserFile userFile) { - String uniqueToken = UUID.randomUUID().toString(); - userFile.sharedUrl = uniqueToken; - filesRepository.saveAndFlush(userFile); - - return uniqueToken; - } - - public PremiumUserFilesRepository.UserFile getUserFileBySharedUrl(String token) { - return filesRepository.findUserFileBySharedUrl(token); - } - public MapApiController.FileDownloadResult getFile(PremiumUserFilesRepository.UserFile userFile, PremiumUserDevicesRepository.PremiumUserDevice dev) throws IOException { if (userFile == null || dev == null) { return null; @@ -1220,96 +1207,4 @@ public MapApiController.FileDownloadResult getFile(PremiumUserFilesRepository.Us } return null; } - - public GpxFile getFile(PremiumUserFilesRepository.UserFile file) throws IOException { - if (file == null || file.data == null) { - return null; - } - try (InputStream inputStream = new GZIPInputStream(new ByteArrayInputStream(file.data)); - Source source = new Buffer().readFrom(inputStream)) { - GpxFile gpxFile = GpxUtilities.INSTANCE.loadGpxFile(source); - if (gpxFile.getError() == null) { - return gpxFile; - } - return null; - } - } - - public static class FileSharedInfo { - public Set accessedUsers; - public Set blackList; - - public FileSharedInfo(Set accessedUsers, Set blackList) { - this.accessedUsers = accessedUsers; - this.blackList = blackList; - } - - public FileSharedInfo() { - this.accessedUsers = new HashSet<>(); - this.blackList = new HashSet<>(); - } - } - - public boolean saveAccessedUser(PremiumUserDevicesRepository.PremiumUserDevice dev, PremiumUserFilesRepository.UserFile userFile) { - final String ACCESSED_USERS = "accessedUsers"; - FileSharedInfo fileSharedInfo = gson.fromJson(userFile.sharedInfo, FileSharedInfo.class); - if (fileSharedInfo == null) { - fileSharedInfo = new FileSharedInfo(); - } - if (!fileSharedInfo.blackList.contains(dev.userid)) { - if (fileSharedInfo.accessedUsers.add(dev.userid)) { - userFile.sharedInfo.add(ACCESSED_USERS, gson.toJsonTree(fileSharedInfo.accessedUsers)); - filesRepository.saveAndFlush(userFile); - } - return true; - } - return false; - } - - public List getAccessedUsers(PremiumUserFilesRepository.UserFile userFile) { - List accessedUsers = new ArrayList<>(); - FileSharedInfo fileSharedInfo = gson.fromJson(userFile.sharedInfo, FileSharedInfo.class); - if (fileSharedInfo != null && fileSharedInfo.accessedUsers != null) { - for (Integer userId : fileSharedInfo.accessedUsers) { - PremiumUsersRepository.PremiumUser pu = usersRepository.findById(userId); - if (pu != null) { - accessedUsers.add(pu.email); - } - } - } - return accessedUsers; - } - - @Transactional - public boolean createFileBlacklist(PremiumUserFilesRepository.UserFile userFile, List list) { - final String BLACK_LIST = "blackList"; - FileSharedInfo fileSharedInfo = gson.fromJson(userFile.sharedInfo, FileSharedInfo.class); - if (fileSharedInfo == null) { - fileSharedInfo = new FileSharedInfo(); - } - fileSharedInfo.blackList.addAll(list.stream().map(email -> { - PremiumUsersRepository.PremiumUser pu = usersRepository.findByEmailIgnoreCase(email); - return pu != null ? pu.id : null; - }).filter(Objects::nonNull).toList()); - if (fileSharedInfo.blackList.isEmpty()) { - return false; - } - userFile.sharedInfo.add(BLACK_LIST, gson.toJsonTree(fileSharedInfo.blackList)); - filesRepository.saveAndFlush(userFile); - return true; - } - - public List getBlackList(PremiumUserFilesRepository.UserFile userFile) { - List blackList = new ArrayList<>(); - FileSharedInfo fileSharedInfo = gson.fromJson(userFile.sharedInfo, FileSharedInfo.class); - if (fileSharedInfo != null && fileSharedInfo.blackList != null) { - for (Integer userId : fileSharedInfo.blackList) { - PremiumUsersRepository.PremiumUser pu = usersRepository.findById(userId); - if (pu != null) { - blackList.add(pu.email); - } - } - } - return blackList; - } } diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/shareGpx/FileSharedInfo.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/shareGpx/FileSharedInfo.java new file mode 100644 index 000000000..05889c989 --- /dev/null +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/shareGpx/FileSharedInfo.java @@ -0,0 +1,129 @@ +package net.osmand.server.api.services.shareGpx; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class FileSharedInfo { + private Whitelist whitelist; + private Blacklist blacklist; + private AccessedUsers accessedUsers; + + public FileSharedInfo() { + this(new Whitelist(), new Blacklist(), new AccessedUsers()); + } + + public FileSharedInfo(Whitelist whitelist, Blacklist blacklist, AccessedUsers accessedUsers) { + this.whitelist = whitelist; + this.blacklist = blacklist; + this.accessedUsers = accessedUsers; + } + + public Whitelist getWhitelist() { + return whitelist; + } + + public void setWhitelist(Whitelist whitelist) { + this.whitelist = whitelist; + } + + public Blacklist getBlacklist() { + return blacklist; + } + + public void setBlacklist(Blacklist blacklist) { + this.blacklist = blacklist; + } + + public AccessedUsers getAccessedUsers() { + return accessedUsers; + } + + public void setAccessedUsers(AccessedUsers accessedUsers) { + this.accessedUsers = accessedUsers; + } + + public static class Whitelist { + private Map permissions; + + public Whitelist() { + this(new HashMap<>()); + } + + public Whitelist(Map permissions) { + this.permissions = permissions != null ? permissions : new HashMap<>(); + } + + public Map getPermissions() { + return permissions; + } + + public void setPermissions(Map permissions) { + this.permissions = permissions; + } + } + + public static class Permission { + private PermissionType permissionType; + + public Permission(PermissionType permissionType) { + this.permissionType = permissionType; + } + + public PermissionType getPermissionType() { + return permissionType; + } + + public void setPermissionType(PermissionType permissionType) { + this.permissionType = permissionType; + } + } + + public enum PermissionType { + READ, + WRITE, + EXECUTE + } + + public static class Blacklist { + private Set users; + + public Blacklist() { + this(new HashSet<>()); + } + + public Blacklist(Set users) { + this.users = users != null ? users : new HashSet<>(); + } + + public Set getUsers() { + return users; + } + + public void setUsers(Set users) { + this.users = users; + } + } + + public static class AccessedUsers { + private Set users; + + public AccessedUsers() { + this(new HashSet<>()); + } + + public AccessedUsers(Set users) { + this.users = users != null ? users : new HashSet<>(); + } + + public Set getUsers() { + return users; + } + + public void setUsers(Set users) { + this.users = users; + } + } + +} diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/shareGpx/ShareGpxService.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/shareGpx/ShareGpxService.java new file mode 100644 index 000000000..d48ad8b02 --- /dev/null +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/shareGpx/ShareGpxService.java @@ -0,0 +1,148 @@ +package net.osmand.server.api.services.shareGpx; + +import com.google.gson.Gson; +import net.osmand.server.api.repo.PremiumUserDevicesRepository; +import net.osmand.server.api.repo.PremiumUserFilesRepository; +import net.osmand.server.api.repo.PremiumUsersRepository; +import net.osmand.shared.gpx.GpxFile; +import net.osmand.shared.gpx.GpxUtilities; +import okio.Buffer; +import okio.Source; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.*; +import java.util.zip.GZIPInputStream; + +import static net.osmand.server.api.services.shareGpx.FileSharedInfo.PermissionType.READ; + +@Service +public class ShareGpxService { + + @Autowired + protected PremiumUserFilesRepository filesRepository; + + @Autowired + protected PremiumUsersRepository usersRepository; + + private static final String BLACK_LIST = "blacklist"; + private static final String WHITE_LIST = "whitelist"; + private static final String ACCESSED_USERS = "accessedUsers"; + + Gson gson = new Gson(); + + @Transactional + public String generateSharedCode(PremiumUserFilesRepository.UserFile userFile) { + String uniqueToken = UUID.randomUUID().toString(); + userFile.sharedCode = uniqueToken; + filesRepository.saveAndFlush(userFile); + + return uniqueToken; + } + + public PremiumUserFilesRepository.UserFile getUserFileBySharedUrl(String token) { + return filesRepository.findUserFileBySharedCode(token); + } + + public GpxFile getFile(PremiumUserFilesRepository.UserFile file) throws IOException { + if (file == null || file.data == null) { + return null; + } + try (InputStream inputStream = new GZIPInputStream(new ByteArrayInputStream(file.data)); + Source source = new Buffer().readFrom(inputStream)) { + GpxFile gpxFile = GpxUtilities.INSTANCE.loadGpxFile(source); + if (gpxFile.getError() == null) { + return gpxFile; + } + return null; + } + } + + public boolean saveAccessedUser(PremiumUserDevicesRepository.PremiumUserDevice dev, PremiumUserFilesRepository.UserFile userFile) { + FileSharedInfo fileSharedInfo = gson.fromJson(userFile.sharedInfo, FileSharedInfo.class); + if (fileSharedInfo == null) { + fileSharedInfo = new FileSharedInfo(); + } + if (!fileSharedInfo.getBlacklist().getUsers().contains(dev.userid)) { + if (fileSharedInfo.getAccessedUsers().getUsers().add(dev.userid)) { + userFile.sharedInfo.add(ACCESSED_USERS, gson.toJsonTree(fileSharedInfo.getAccessedUsers())); + filesRepository.saveAndFlush(userFile); + } + return true; + } + return false; + } + + public List getAccessedUsers(PremiumUserFilesRepository.UserFile userFile) { + List accessedUsers = new ArrayList<>(); + FileSharedInfo fileSharedInfo = gson.fromJson(userFile.sharedInfo, FileSharedInfo.class); + if (fileSharedInfo != null && fileSharedInfo.getAccessedUsers() != null) { + for (Integer userId : fileSharedInfo.getAccessedUsers().getUsers()) { + PremiumUsersRepository.PremiumUser pu = usersRepository.findById(userId); + if (pu != null) { + accessedUsers.add(pu.email); + } + } + } + return accessedUsers; + } + + @Transactional + public boolean editBlacklist(PremiumUserFilesRepository.UserFile userFile, List list) { + FileSharedInfo fileSharedInfo = gson.fromJson(userFile.sharedInfo, FileSharedInfo.class); + if (fileSharedInfo == null) { + fileSharedInfo = new FileSharedInfo(); + } + FileSharedInfo.Blacklist blist = fileSharedInfo.getBlacklist(); + blist.getUsers().addAll(list.stream().map(email -> { + PremiumUsersRepository.PremiumUser pu = usersRepository.findByEmailIgnoreCase(email); + return pu != null ? pu.id : null; + }).filter(Objects::nonNull).toList()); + if (blist.getUsers().isEmpty()) { + return false; + } + userFile.sharedInfo.add(BLACK_LIST, gson.toJsonTree(blist)); + filesRepository.saveAndFlush(userFile); + return true; + } + + @Transactional + public boolean editWhitelist(PremiumUserFilesRepository.UserFile userFile, List list) { + FileSharedInfo fileSharedInfo = gson.fromJson(userFile.sharedInfo, FileSharedInfo.class); + if (fileSharedInfo == null) { + fileSharedInfo = new FileSharedInfo(); + } + FileSharedInfo.Whitelist wlist = fileSharedInfo.getWhitelist(); + + wlist.getPermissions().putAll(list.stream().collect(HashMap::new, (m, email) -> { + PremiumUsersRepository.PremiumUser pu = usersRepository.findByEmailIgnoreCase(email); + if (pu != null) { + m.put(pu.id, new FileSharedInfo.Permission(READ)); + } + }, HashMap::putAll)); + if (wlist.getPermissions().isEmpty()) { + return false; + } + userFile.sharedInfo.add(WHITE_LIST, gson.toJsonTree(wlist)); + filesRepository.saveAndFlush(userFile); + return true; + } + + public List getBlackList(PremiumUserFilesRepository.UserFile userFile) { + List blackList = new ArrayList<>(); + FileSharedInfo fileSharedInfo = gson.fromJson(userFile.sharedInfo, FileSharedInfo.class); + if (fileSharedInfo != null && fileSharedInfo.getBlacklist() != null) { + for (Integer userId : fileSharedInfo.getBlacklist().getUsers()) { + PremiumUsersRepository.PremiumUser pu = usersRepository.findById(userId); + if (pu != null) { + blackList.add(pu.email); + } + } + } + return blackList; + } +} diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/MapApiController.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/MapApiController.java index 143190bbc..540ff7c5f 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/MapApiController.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/MapApiController.java @@ -2,6 +2,7 @@ import java.io.*; +import net.osmand.server.api.services.shareGpx.ShareGpxService; import net.osmand.shared.gpx.GpxTrackAnalysis; import net.osmand.shared.gpx.primitives.Metadata; import okio.Buffer; @@ -98,6 +99,9 @@ public class MapApiController { @Autowired UserdataService userdataService; + @Autowired + ShareGpxService shareGpxService; + @Autowired protected GpxService gpxService; @@ -829,9 +833,12 @@ public String getRegionsByLatlon(@RequestParam("lat") double lat, @RequestParam( return gson.toJson(Map.of("regions", regions)); } - @GetMapping(path = {"/generate-shared-url"}, produces = "application/json") + // Share GPX + + @PostMapping(path = {"/file-share"}, produces = "application/json") public ResponseEntity generateGpxSharedUrl(@RequestParam String name, - @RequestParam String type) { + @RequestParam String type, + HttpServletRequest request) { PremiumUserDevicesRepository.PremiumUserDevice dev = checkUser(); if (dev == null) { return tokenNotValid(); @@ -840,36 +847,29 @@ public ResponseEntity generateGpxSharedUrl(@RequestParam String name, if (userFile == null) { return ResponseEntity.badRequest().body(FILE_NOT_FOUND); } - String sharedUrl = userdataService.generateSharedUrl(userFile); + String code = shareGpxService.generateSharedCode(userFile); + String domain = request.getScheme() + "://" + request.getServerName() + + (request.getServerPort() != 80 && request.getServerPort() != 443 ? ":" + request.getServerPort() : ""); + String sharedUrl = domain + "/share/gpx/" + code; + return ResponseEntity.ok(gson.toJson(Map.of("sharedUrl", sharedUrl))); } - @GetMapping(path = {"/gpx/share/{token}"}, produces = "application/json") + @GetMapping(path = {"/share/gpx/{code}"}, produces = "application/json") @Transactional - public ResponseEntity getTrackByUrl(@PathVariable String token, - @RequestParam(required = false) Boolean downloadFile) throws IOException { + public ResponseEntity getTrackByUrl(@PathVariable String code) throws IOException { PremiumUserDevicesRepository.PremiumUserDevice dev = checkUser(); if (dev == null) { return tokenNotValid(); } - PremiumUserFilesRepository.UserFile userFile = userdataService.getUserFileBySharedUrl(token); + PremiumUserFilesRepository.UserFile userFile = shareGpxService.getUserFileBySharedUrl(code); if (userFile != null) { - boolean saved = userdataService.saveAccessedUser(dev, userFile); + boolean saved = shareGpxService.saveAccessedUser(dev, userFile); if (!saved) { return ResponseEntity.badRequest().body("User is in the blacklist"); } - if (downloadFile != null && downloadFile) { - FileDownloadResult fileResult = userdataService.getFile(userFile, dev); - if (fileResult == null) { - return ResponseEntity.badRequest().body(FILE_NOT_FOUND); - } - return ResponseEntity.ok() - .header("Content-Disposition", "attachment; filename=" + fileResult.fileName) - .contentType(org.springframework.http.MediaType.valueOf(fileResult.contentType)) - .body(new InputStreamResource(fileResult.inputStream)); - } - GpxFile gpxFile = userdataService.getFile(userFile); + GpxFile gpxFile = shareGpxService.getFile(userFile); if (gpxFile.getError() == null) { GpxTrackAnalysis analysis = gpxFile.getAnalysis(System.currentTimeMillis()); @@ -885,7 +885,7 @@ public ResponseEntity getTrackByUrl(@PathVariable String token, public record FileDownloadResult(InputStream inputStream, String fileName, String contentType) { } - @GetMapping(path = {"/accessed-users"}, produces = "application/json") + @GetMapping(path = {"/share/accessed-users"}, produces = "application/json") public ResponseEntity getAccessedUsers(@RequestParam String name, @RequestParam String type) { PremiumUserDevicesRepository.PremiumUserDevice dev = checkUser(); @@ -896,14 +896,34 @@ public ResponseEntity getAccessedUsers(@RequestParam String name, if (userFile == null) { return ResponseEntity.badRequest().body(FILE_NOT_FOUND); } - List users = userdataService.getAccessedUsers(userFile); + List users = shareGpxService.getAccessedUsers(userFile); + return ResponseEntity.ok(gson.toJson(Map.of("accessedUsers", users))); } - @PostMapping(path = {"/create-file-blacklist"}, produces = "application/json") - public ResponseEntity createFileBlacklist(@RequestBody List list, - @RequestParam String name, - @RequestParam String type) { + @PostMapping(path = {"/share/edit-blacklist"}, produces = "application/json") + public ResponseEntity editBlacklist(@RequestBody List list, + @RequestParam String name, + @RequestParam String type) { + PremiumUserDevicesRepository.PremiumUserDevice dev = checkUser(); + if (dev == null) { + return tokenNotValid(); + } + PremiumUserFilesRepository.UserFile userFile = userdataService.getUserFile(name, type, null, dev); + if (userFile == null) { + return ResponseEntity.badRequest().body(FILE_NOT_FOUND); + } + boolean created = shareGpxService.editBlacklist(userFile, list); + if (!created) { + return ResponseEntity.badRequest().body("Error editing blacklist"); + } + return ResponseEntity.ok("Blacklist edited"); + } + + @GetMapping(path = {"/share/edit-whitelist"}, produces = "application/json") + public ResponseEntity editWhitelist(@RequestBody List list, + @RequestParam String name, + @RequestParam String type) { PremiumUserDevicesRepository.PremiumUserDevice dev = checkUser(); if (dev == null) { return tokenNotValid(); @@ -912,14 +932,14 @@ public ResponseEntity createFileBlacklist(@RequestBody List list if (userFile == null) { return ResponseEntity.badRequest().body(FILE_NOT_FOUND); } - boolean created = userdataService.createFileBlacklist(userFile, list); + boolean created = shareGpxService.editWhitelist(userFile, list); if (!created) { - return ResponseEntity.badRequest().body("Error creating blacklist"); + return ResponseEntity.badRequest().body("Error editing whitelist"); } - return ResponseEntity.ok("Blacklist created"); + return ResponseEntity.ok("Whitelist edited"); } - @GetMapping(path = {"/get-blacklist"}, produces = "application/json") + @GetMapping(path = {"/share/get-blacklist"}, produces = "application/json") public ResponseEntity getBlacklist(@RequestParam String name, @RequestParam String type) { PremiumUserDevicesRepository.PremiumUserDevice dev = checkUser(); @@ -930,7 +950,7 @@ public ResponseEntity getBlacklist(@RequestParam String name, if (userFile == null) { return ResponseEntity.badRequest().body(FILE_NOT_FOUND); } - List users = userdataService.getBlackList(userFile); + List users = shareGpxService.getBlackList(userFile); if (users == null) { return ResponseEntity.badRequest().body("No blacklisted users"); } From e493f00570662e330b276df7c501d345d3c9f03e Mon Sep 17 00:00:00 2001 From: alisa911 Date: Thu, 28 Nov 2024 16:42:12 +0200 Subject: [PATCH 05/23] add share api --- java-tools/OsmAndServer/build.gradle | 2 + .../api/repo/PremiumUserFilesRepository.java | 7 + .../server/api/services/ShareGpxService.java | 317 ++++++++++++++++++ .../server/api/services/UserdataService.java | 39 ++- .../api/services/shareGpx/FileSharedInfo.java | 129 ------- .../services/shareGpx/ShareGpxService.java | 148 -------- .../controllers/pub/UserdataController.java | 18 +- .../controllers/user/MapApiController.java | 227 +++---------- .../controllers/user/ShareFileController.java | 199 +++++++++++ 9 files changed, 597 insertions(+), 489 deletions(-) create mode 100644 java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareGpxService.java delete mode 100644 java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/shareGpx/FileSharedInfo.java delete mode 100644 java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/shareGpx/ShareGpxService.java create mode 100644 java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java diff --git a/java-tools/OsmAndServer/build.gradle b/java-tools/OsmAndServer/build.gradle index 539549db1..e02b8a359 100644 --- a/java-tools/OsmAndServer/build.gradle +++ b/java-tools/OsmAndServer/build.gradle @@ -140,6 +140,8 @@ dependencies { implementation 'org.apache.httpcomponents.client5:httpclient5:5.3.1' implementation 'org.lz4:lz4-java:1.8.0' + compileOnly group: 'org.projectlombok', name: 'lombok', version: '1.18.28' + annotationProcessor group: 'org.projectlombok', name: 'lombok', version: '1.18.28' // implementation 'com.clickhouse:clickhouse-http-client:0.6.0' } diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/PremiumUserFilesRepository.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/PremiumUserFilesRepository.java index ca5d30395..67774e351 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/PremiumUserFilesRepository.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/PremiumUserFilesRepository.java @@ -15,6 +15,7 @@ import javax.persistence.Temporal; import javax.persistence.TemporalType; +import net.osmand.server.api.services.ShareGpxService.FileSharedInfo; import org.hibernate.annotations.Type; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; @@ -42,6 +43,12 @@ public interface PremiumUserFilesRepository extends JpaRepository list) { + FileSharedInfo fileSharedInfo = gson.fromJson(userFile.sharedInfo, FileSharedInfo.class); + if (fileSharedInfo == null) { + fileSharedInfo = new FileSharedInfo(); + } + FileSharedInfo.Blacklist blist = fileSharedInfo.getBlacklist(); + blist.getUsers().addAll(list.stream().map(email -> { + PremiumUsersRepository.PremiumUser pu = usersRepository.findByEmailIgnoreCase(email); + return pu != null ? pu.id : null; + }).filter(Objects::nonNull).toList()); + if (blist.getUsers().isEmpty()) { + return false; + } + userFile.sharedInfo.add(BLACK_LIST, gson.toJsonTree(blist)); + filesRepository.saveAndFlush(userFile); + return true; + } + + @Transactional + public boolean editWhitelist(PremiumUserFilesRepository.UserFile userFile, List list) { + FileSharedInfo fileSharedInfo = gson.fromJson(userFile.sharedInfo, FileSharedInfo.class); + if (fileSharedInfo == null) { + fileSharedInfo = new FileSharedInfo(); + } + FileSharedInfo.Whitelist wlist = fileSharedInfo.getWhitelist(); + + wlist.getPermissions().putAll(list.stream().collect(HashMap::new, (m, email) -> { + PremiumUsersRepository.PremiumUser pu = usersRepository.findByEmailIgnoreCase(email); + if (pu != null) { + m.put(pu.id, new FileSharedInfo.Permission(FileSharedInfo.PermissionType.READ)); + } + }, HashMap::putAll)); + if (wlist.getPermissions().isEmpty()) { + return false; + } + userFile.sharedInfo.add(WHITE_LIST, gson.toJsonTree(wlist)); + filesRepository.saveAndFlush(userFile); + return true; + } + + @Data + public static class FileSharedInfo { + private Whitelist whitelist; + private Blacklist blacklist; + private AccessedUsers accessedUsers; + private SharingType sharingType; + private Set groupActions; + + public FileSharedInfo() { + this(new Whitelist(), new Blacklist(), new AccessedUsers(), SharingType.PUBLIC, new HashSet<>()); + } + + public FileSharedInfo(Whitelist whitelist, Blacklist blacklist, AccessedUsers accessedUsers, SharingType sharingType, Set groupActions) { + this.whitelist = whitelist; + this.blacklist = blacklist; + this.accessedUsers = accessedUsers; + this.sharingType = sharingType != null ? sharingType : SharingType.PUBLIC; + this.groupActions = groupActions != null ? groupActions : new HashSet<>(); + } + + @Getter + public enum SharingType { + PUBLIC("public"), // Anyone can access + PUBLIC_BY_LOGIN("publicByLogin"), // Public, but login required, owner doesn't see accessed users + GROUP_BY_LOGIN("groupByLogin"); // Group access with login, owner sees accessed users + + private final String type; + + SharingType(String type) { + this.type = type; + } + + public static SharingType fromType(String type) { + for (SharingType sharingType : values()) { + if (sharingType.getType().equalsIgnoreCase(type)) { + return sharingType; + } + } + return null; + } + } + + @Getter + public enum GroupAction { + EDIT_WHITELIST("editWhitelist"), + EDIT_BLACKLIST("editBlacklist"), + SEE_ACCESSED_USERS("seeAccessedUsers"); + + private final String action; + + GroupAction(String action) { + this.action = action; + } + + public static GroupAction fromAction(String action) { + for (GroupAction groupAction : values()) { + if (groupAction.getAction().equalsIgnoreCase(action)) { + return groupAction; + } + } + return null; + } + } + + public enum PermissionType { + READ, + WRITE, + } + + @Data + public static class Whitelist { + private Map permissions; + + public Whitelist() { + this(new HashMap<>()); + } + + public Whitelist(Map permissions) { + this.permissions = permissions != null ? permissions : new HashMap<>(); + } + + } + + @Data + public static class Permission { + private PermissionType permissionType; + + public Permission(PermissionType permissionType) { + this.permissionType = permissionType; + } + + } + + @Data + public static class Blacklist { + private Set users; + + public Blacklist() { + this(new HashSet<>()); + } + + public Blacklist(Set users) { + this.users = users != null ? users : new HashSet<>(); + } + + } + + @Data + public static class AccessedUsers { + private Map users; + + public AccessedUsers() { + this(new HashMap<>()); + } + + public AccessedUsers(Map users) { + this.users = users != null ? users : new HashMap<>(); + } + + } + + } +} diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/UserdataService.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/UserdataService.java index e8e67f3d3..2d526bd80 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/UserdataService.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/UserdataService.java @@ -27,6 +27,7 @@ import javax.servlet.http.HttpServletResponse; import javax.transaction.Transactional; +import net.osmand.server.WebSecurityConfiguration; import net.osmand.shared.gpx.GpxFile; import net.osmand.shared.gpx.GpxUtilities; import okio.Buffer; @@ -43,6 +44,7 @@ import org.springframework.context.annotation.Lazy; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; @@ -141,6 +143,8 @@ public class UserdataService { public static final String EMPTY_FILE_NAME = "__folder__.info"; public static final String INFO_EXT = ".info"; + public static final String FILE_NOT_FOUND = "File not found"; + protected static final Log LOG = LogFactory.getLog(UserdataService.class); private static final int MAX_ATTEMPTS_PER_DAY = 100; @@ -157,6 +161,14 @@ public RequestData(int checkCount, long lastCheckTime) { this.lastCheckTime = lastCheckTime; } } + + public PremiumUserDevicesRepository.PremiumUserDevice checkUser() { + Object user = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + if (user instanceof WebSecurityConfiguration.OsmAndProUser) { + return ((WebSecurityConfiguration.OsmAndProUser) user).getUserDevice(); + } + return null; + } private ResponseEntity trackRequest(HttpServletRequest request) { String ipAddress = request.getRemoteAddr(); @@ -242,7 +254,7 @@ private void sanitizeFileNames(List files) { } } - private String sanitizeEncode(String name) { + public String sanitizeEncode(String name) { return name.replace("\r", CR_SANITIZE).replace("\n", LF_SANITIZE); } @@ -656,7 +668,7 @@ private InputStream getGzipInputStreamFromFile(File fp, String ext) throws IOExc public InputStream getInputStream(PremiumUserDevicesRepository.PremiumUserDevice dev, PremiumUserFilesRepository.UserFile userFile) { InputStream bin = null; if (dev == null) { - tokenNotValid(); + tokenNotValidError(); } else { if (userFile == null) { throw new OsmAndPublicApiException(ERROR_CODE_FILE_NOT_AVAILABLE, ERROR_MESSAGE_FILE_IS_NOT_AVAILABLE); @@ -694,6 +706,9 @@ public PremiumUserFilesRepository.UserFile getFilePrevVersion(String name, Strin } public InputStream getInputStream(PremiumUserFilesRepository.UserFile userFile) { + if (userFile.storage.equals("local")) { + return new ByteArrayInputStream(userFile.data); + } return storageService.getFileInputStream(userFile.storage, userFolder(userFile), storageFileName(userFile)); } @@ -701,10 +716,14 @@ public InputStream getInputStream(PremiumUserFilesRepository.UserFileNoData user return storageService.getFileInputStream(userFile.storage, userFolder(userFile.userid), storageFileName(userFile.type, userFile.name, userFile.updatetime)); } - public ResponseEntity tokenNotValid() { + public ResponseEntity tokenNotValidError() { throw new OsmAndPublicApiException(ERROR_CODE_PROVIDED_TOKEN_IS_NOT_VALID, "provided deviceid or token is not valid"); } + public ResponseEntity tokenNotValidResponse() { + return new ResponseEntity<>("Unauthorized", HttpStatus.UNAUTHORIZED); + } + public void deleteFile(String name, String type, Integer deviceId, Long clienttime, PremiumUserDevicesRepository.PremiumUserDevice dev) { PremiumUserFilesRepository.UserFile usf = new PremiumUserFilesRepository.UserFile(); usf.name = name; @@ -1193,18 +1212,4 @@ public void updateDeviceLangInfo(PremiumUserDevicesRepository.PremiumUserDevice devicesRepository.saveAndFlush(dev); } } - - public MapApiController.FileDownloadResult getFile(PremiumUserFilesRepository.UserFile userFile, PremiumUserDevicesRepository.PremiumUserDevice dev) throws IOException { - if (userFile == null || dev == null) { - return null; - } - try (InputStream bin = getInputStream(dev, userFile)) { - if (bin != null) { - InputStream inputStream = new GZIPInputStream(bin); - String fileName = URLEncoder.encode(sanitizeEncode(userFile.name), StandardCharsets.UTF_8); - return new MapApiController.FileDownloadResult(inputStream, fileName, APPLICATION_OCTET_STREAM_VALUE); - } - } - return null; - } } diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/shareGpx/FileSharedInfo.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/shareGpx/FileSharedInfo.java deleted file mode 100644 index 05889c989..000000000 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/shareGpx/FileSharedInfo.java +++ /dev/null @@ -1,129 +0,0 @@ -package net.osmand.server.api.services.shareGpx; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -public class FileSharedInfo { - private Whitelist whitelist; - private Blacklist blacklist; - private AccessedUsers accessedUsers; - - public FileSharedInfo() { - this(new Whitelist(), new Blacklist(), new AccessedUsers()); - } - - public FileSharedInfo(Whitelist whitelist, Blacklist blacklist, AccessedUsers accessedUsers) { - this.whitelist = whitelist; - this.blacklist = blacklist; - this.accessedUsers = accessedUsers; - } - - public Whitelist getWhitelist() { - return whitelist; - } - - public void setWhitelist(Whitelist whitelist) { - this.whitelist = whitelist; - } - - public Blacklist getBlacklist() { - return blacklist; - } - - public void setBlacklist(Blacklist blacklist) { - this.blacklist = blacklist; - } - - public AccessedUsers getAccessedUsers() { - return accessedUsers; - } - - public void setAccessedUsers(AccessedUsers accessedUsers) { - this.accessedUsers = accessedUsers; - } - - public static class Whitelist { - private Map permissions; - - public Whitelist() { - this(new HashMap<>()); - } - - public Whitelist(Map permissions) { - this.permissions = permissions != null ? permissions : new HashMap<>(); - } - - public Map getPermissions() { - return permissions; - } - - public void setPermissions(Map permissions) { - this.permissions = permissions; - } - } - - public static class Permission { - private PermissionType permissionType; - - public Permission(PermissionType permissionType) { - this.permissionType = permissionType; - } - - public PermissionType getPermissionType() { - return permissionType; - } - - public void setPermissionType(PermissionType permissionType) { - this.permissionType = permissionType; - } - } - - public enum PermissionType { - READ, - WRITE, - EXECUTE - } - - public static class Blacklist { - private Set users; - - public Blacklist() { - this(new HashSet<>()); - } - - public Blacklist(Set users) { - this.users = users != null ? users : new HashSet<>(); - } - - public Set getUsers() { - return users; - } - - public void setUsers(Set users) { - this.users = users; - } - } - - public static class AccessedUsers { - private Set users; - - public AccessedUsers() { - this(new HashSet<>()); - } - - public AccessedUsers(Set users) { - this.users = users != null ? users : new HashSet<>(); - } - - public Set getUsers() { - return users; - } - - public void setUsers(Set users) { - this.users = users; - } - } - -} diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/shareGpx/ShareGpxService.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/shareGpx/ShareGpxService.java deleted file mode 100644 index d48ad8b02..000000000 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/shareGpx/ShareGpxService.java +++ /dev/null @@ -1,148 +0,0 @@ -package net.osmand.server.api.services.shareGpx; - -import com.google.gson.Gson; -import net.osmand.server.api.repo.PremiumUserDevicesRepository; -import net.osmand.server.api.repo.PremiumUserFilesRepository; -import net.osmand.server.api.repo.PremiumUsersRepository; -import net.osmand.shared.gpx.GpxFile; -import net.osmand.shared.gpx.GpxUtilities; -import okio.Buffer; -import okio.Source; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import javax.transaction.Transactional; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.*; -import java.util.zip.GZIPInputStream; - -import static net.osmand.server.api.services.shareGpx.FileSharedInfo.PermissionType.READ; - -@Service -public class ShareGpxService { - - @Autowired - protected PremiumUserFilesRepository filesRepository; - - @Autowired - protected PremiumUsersRepository usersRepository; - - private static final String BLACK_LIST = "blacklist"; - private static final String WHITE_LIST = "whitelist"; - private static final String ACCESSED_USERS = "accessedUsers"; - - Gson gson = new Gson(); - - @Transactional - public String generateSharedCode(PremiumUserFilesRepository.UserFile userFile) { - String uniqueToken = UUID.randomUUID().toString(); - userFile.sharedCode = uniqueToken; - filesRepository.saveAndFlush(userFile); - - return uniqueToken; - } - - public PremiumUserFilesRepository.UserFile getUserFileBySharedUrl(String token) { - return filesRepository.findUserFileBySharedCode(token); - } - - public GpxFile getFile(PremiumUserFilesRepository.UserFile file) throws IOException { - if (file == null || file.data == null) { - return null; - } - try (InputStream inputStream = new GZIPInputStream(new ByteArrayInputStream(file.data)); - Source source = new Buffer().readFrom(inputStream)) { - GpxFile gpxFile = GpxUtilities.INSTANCE.loadGpxFile(source); - if (gpxFile.getError() == null) { - return gpxFile; - } - return null; - } - } - - public boolean saveAccessedUser(PremiumUserDevicesRepository.PremiumUserDevice dev, PremiumUserFilesRepository.UserFile userFile) { - FileSharedInfo fileSharedInfo = gson.fromJson(userFile.sharedInfo, FileSharedInfo.class); - if (fileSharedInfo == null) { - fileSharedInfo = new FileSharedInfo(); - } - if (!fileSharedInfo.getBlacklist().getUsers().contains(dev.userid)) { - if (fileSharedInfo.getAccessedUsers().getUsers().add(dev.userid)) { - userFile.sharedInfo.add(ACCESSED_USERS, gson.toJsonTree(fileSharedInfo.getAccessedUsers())); - filesRepository.saveAndFlush(userFile); - } - return true; - } - return false; - } - - public List getAccessedUsers(PremiumUserFilesRepository.UserFile userFile) { - List accessedUsers = new ArrayList<>(); - FileSharedInfo fileSharedInfo = gson.fromJson(userFile.sharedInfo, FileSharedInfo.class); - if (fileSharedInfo != null && fileSharedInfo.getAccessedUsers() != null) { - for (Integer userId : fileSharedInfo.getAccessedUsers().getUsers()) { - PremiumUsersRepository.PremiumUser pu = usersRepository.findById(userId); - if (pu != null) { - accessedUsers.add(pu.email); - } - } - } - return accessedUsers; - } - - @Transactional - public boolean editBlacklist(PremiumUserFilesRepository.UserFile userFile, List list) { - FileSharedInfo fileSharedInfo = gson.fromJson(userFile.sharedInfo, FileSharedInfo.class); - if (fileSharedInfo == null) { - fileSharedInfo = new FileSharedInfo(); - } - FileSharedInfo.Blacklist blist = fileSharedInfo.getBlacklist(); - blist.getUsers().addAll(list.stream().map(email -> { - PremiumUsersRepository.PremiumUser pu = usersRepository.findByEmailIgnoreCase(email); - return pu != null ? pu.id : null; - }).filter(Objects::nonNull).toList()); - if (blist.getUsers().isEmpty()) { - return false; - } - userFile.sharedInfo.add(BLACK_LIST, gson.toJsonTree(blist)); - filesRepository.saveAndFlush(userFile); - return true; - } - - @Transactional - public boolean editWhitelist(PremiumUserFilesRepository.UserFile userFile, List list) { - FileSharedInfo fileSharedInfo = gson.fromJson(userFile.sharedInfo, FileSharedInfo.class); - if (fileSharedInfo == null) { - fileSharedInfo = new FileSharedInfo(); - } - FileSharedInfo.Whitelist wlist = fileSharedInfo.getWhitelist(); - - wlist.getPermissions().putAll(list.stream().collect(HashMap::new, (m, email) -> { - PremiumUsersRepository.PremiumUser pu = usersRepository.findByEmailIgnoreCase(email); - if (pu != null) { - m.put(pu.id, new FileSharedInfo.Permission(READ)); - } - }, HashMap::putAll)); - if (wlist.getPermissions().isEmpty()) { - return false; - } - userFile.sharedInfo.add(WHITE_LIST, gson.toJsonTree(wlist)); - filesRepository.saveAndFlush(userFile); - return true; - } - - public List getBlackList(PremiumUserFilesRepository.UserFile userFile) { - List blackList = new ArrayList<>(); - FileSharedInfo fileSharedInfo = gson.fromJson(userFile.sharedInfo, FileSharedInfo.class); - if (fileSharedInfo != null && fileSharedInfo.getBlacklist() != null) { - for (Integer userId : fileSharedInfo.getBlacklist().getUsers()) { - PremiumUsersRepository.PremiumUser pu = usersRepository.findById(userId); - if (pu != null) { - blackList.add(pu.email); - } - } - } - return blackList; - } -} diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/pub/UserdataController.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/pub/UserdataController.java index 90f46fa13..4e8808344 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/pub/UserdataController.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/pub/UserdataController.java @@ -141,7 +141,7 @@ public ResponseEntity check(@RequestParam(name = "deviceid", required = HttpServletRequest request) throws IOException { PremiumUserDevice dev = checkToken(deviceId, accessToken); if (dev == null) { - return userdataService.tokenNotValid(); + return userdataService.tokenNotValidError(); } PremiumUser pu = usersRepository.findById(dev.userid); if (pu == null) { @@ -259,7 +259,7 @@ public ResponseEntity delete(@RequestParam(name = "name", required = tru @RequestParam(name = "clienttime", required = false) Long clienttime) throws IOException { PremiumUserDevice dev = checkToken(deviceId, accessToken); if (dev == null) { - return userdataService.tokenNotValid(); + return userdataService.tokenNotValidError(); } userdataService.deleteFile(name, type, deviceId, clienttime, dev); return userdataService.ok(); @@ -274,7 +274,7 @@ public ResponseEntity deleteFile(HttpServletResponse response, HttpServl @RequestParam(name = "accessToken", required = true) String accessToken) { PremiumUserDevice dev = checkToken(deviceId, accessToken); if (dev == null) { - return userdataService.tokenNotValid(); + return userdataService.tokenNotValidError(); } else { return userdataService.deleteFileVersion(updatetime, dev.userid, name, type, null); } @@ -292,7 +292,7 @@ public ResponseEntity upload(@RequestPart(name = "file") @Valid @NotNull PremiumUserDevice dev = checkToken(deviceId, accessToken); if (dev == null) { - return userdataService.tokenNotValid(); + return userdataService.tokenNotValidError(); } return userdataService.uploadMultipartFile(file, dev, name, type, clienttime); } @@ -311,7 +311,7 @@ public ResponseEntity remapFilenames(@RequestParam(name = "deviceid", re @RequestParam(name = "accessToken", required = true) String accessToken) throws IOException, SQLException { PremiumUserDevice dev = checkToken(deviceId, accessToken); if (dev == null) { - return userdataService.tokenNotValid(); + return userdataService.tokenNotValidError(); } // remap needs to happen to all users & temporarily service should find files by both names (download) Iterable lst = filesRepository.findAllByUserid(dev.userid); @@ -332,7 +332,7 @@ public ResponseEntity migrateData(@RequestParam(name = "storageid", requ } PremiumUserDevice dev = checkToken(deviceId, accessToken); if (dev == null) { - return userdataService.tokenNotValid(); + return userdataService.tokenNotValidError(); } Iterable lst = filesRepository.findAllByUserid(dev.userid); for (UserFile fl : lst) { @@ -373,7 +373,7 @@ public ResponseEntity listFiles(@RequestParam(name = "deviceid", require throws IOException, SQLException { PremiumUserDevice dev = checkToken(deviceId, accessToken); if (dev == null) { - return userdataService.tokenNotValid(); + return userdataService.tokenNotValidError(); } UserFilesResults res = userdataService.generateFiles(dev.userid, name, allVersions, false, type); return ResponseEntity.ok(gson.toJson(res)); @@ -387,7 +387,7 @@ public ResponseEntity deleteAccount(@RequestBody TokenPost token, HttpServletRequest request) throws ServletException { PremiumUserDevice dev = checkToken(deviceId, accessToken); if (dev == null) { - return userdataService.tokenNotValid(); + return userdataService.tokenNotValidError(); } return userdataService.deleteAccount(token.token, dev, request); } @@ -398,7 +398,7 @@ public ResponseEntity sendCode(@RequestBody EmailSenderInfo data, @RequestParam String accessToken) { PremiumUserDevice dev = checkToken(deviceId, accessToken); if (dev == null) { - return userdataService.tokenNotValid(); + return userdataService.tokenNotValidError(); } PremiumUsersRepository.PremiumUser pu = usersRepository.findById(dev.userid); if (pu == null) { diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/MapApiController.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/MapApiController.java index 540ff7c5f..525108a81 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/MapApiController.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/MapApiController.java @@ -2,7 +2,6 @@ import java.io.*; -import net.osmand.server.api.services.shareGpx.ShareGpxService; import net.osmand.shared.gpx.GpxTrackAnalysis; import net.osmand.shared.gpx.primitives.Metadata; import okio.Buffer; @@ -23,13 +22,11 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import javax.transaction.Transactional; import javax.validation.Valid; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import net.osmand.map.OsmandRegions; -import net.osmand.server.WebSecurityConfiguration; import net.osmand.server.api.repo.DeviceSubscriptionsRepository; import net.osmand.server.api.repo.PremiumUserDevicesRepository; import net.osmand.server.api.repo.PremiumUsersRepository; @@ -45,13 +42,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.*; import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; @@ -77,7 +72,6 @@ public class MapApiController { private static final String SRTM_ANALYSIS = "srtm-analysis"; private static final String DONE_SUFFIX = "-done"; private static final String FAV_POINT_GROUPS = "pointGroups"; - private static final String FILE_NOT_FOUND = "File not found"; private static final long ANALYSIS_RERUN = 1692026215870L; // 14-08-2023 @@ -99,9 +93,6 @@ public class MapApiController { @Autowired UserdataService userdataService; - @Autowired - ShareGpxService shareGpxService; - @Autowired protected GpxService gpxService; @@ -188,7 +179,7 @@ public ResponseEntity loginUser(@RequestBody UserPasswordPost credential } request.login(username, password); - PremiumUserDevice dev = checkUser(); + PremiumUserDevice dev = userdataService.checkUser(); userdataService.updateDeviceLangInfo(dev, credentials.lang, BRAND_DEVICE_WEB, MODEL_DEVICE_WEB); return okStatus(); @@ -197,9 +188,9 @@ public ResponseEntity loginUser(@RequestBody UserPasswordPost credential @PostMapping(path = {"/auth/delete-account"}) public ResponseEntity deleteAccount(@RequestParam String token, HttpServletRequest request) throws ServletException { - PremiumUserDevice dev = checkUser(); + PremiumUserDevice dev = userdataService.checkUser(); if (dev == null) { - return tokenNotValid(); + return userdataService.tokenNotValidResponse(); } return userdataService.deleteAccount(token, dev, request); } @@ -256,28 +247,15 @@ public ResponseEntity registerMapUser(@RequestBody UserPasswordPost cred return userdataService.validateToken(username, token); } - public PremiumUserDevicesRepository.PremiumUserDevice checkUser() { - Object user = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); - if (user instanceof WebSecurityConfiguration.OsmAndProUser) { - return ((WebSecurityConfiguration.OsmAndProUser) user).getUserDevice(); - } - return null; - } - - private ResponseEntity tokenNotValid() { - return new ResponseEntity<>("Unauthorized", HttpStatus.UNAUTHORIZED); - - } - @PostMapping(value = "/upload-file", consumes = MULTIPART_FORM_DATA_VALUE) public ResponseEntity uploadFile(@RequestPart(name = "file") @Valid @NotNull @NotEmpty MultipartFile file, @RequestParam String name, @RequestParam String type) throws IOException { // This could be slow series of checks (token, user, subscription, amount of space): // probably it's better to support multiple file upload without these checks - PremiumUserDevice dev = checkUser(); + PremiumUserDevice dev = userdataService.checkUser(); if (dev == null) { - return tokenNotValid(); + return userdataService.tokenNotValidResponse(); } userdataService.uploadMultipartFile(file, dev, name, type, System.currentTimeMillis()); @@ -286,9 +264,9 @@ public ResponseEntity uploadFile(@RequestPart(name = "file") @Valid @Not @PostMapping(value = "/delete-file") public ResponseEntity deleteFile(@RequestParam String name, @RequestParam String type) { - PremiumUserDevice dev = checkUser(); + PremiumUserDevice dev = userdataService.checkUser(); if (dev == null) { - return userdataService.tokenNotValid(); + return userdataService.tokenNotValidResponse(); } userdataService.deleteFile(name, type, null, null, dev); return userdataService.ok(); @@ -298,9 +276,9 @@ public ResponseEntity deleteFile(@RequestParam String name, @RequestPara public ResponseEntity deleteFile(@RequestParam String name, @RequestParam String type, @RequestParam Long updatetime) { - PremiumUserDevice dev = checkUser(); + PremiumUserDevice dev = userdataService.checkUser(); if (dev == null) { - return userdataService.tokenNotValid(); + return userdataService.tokenNotValidResponse(); } else { return userdataService.deleteFileVersion(updatetime, dev.userid, name, type, null); } @@ -309,9 +287,9 @@ public ResponseEntity deleteFile(@RequestParam String name, @GetMapping(value = "/delete-file-all-versions") public ResponseEntity deleteFileAllVersions(@RequestParam String name, @RequestParam String type, @RequestParam Long updatetime, @RequestParam boolean isTrash) { - PremiumUserDevice dev = checkUser(); + PremiumUserDevice dev = userdataService.checkUser(); if (dev == null) { - return userdataService.tokenNotValid(); + return userdataService.tokenNotValidResponse(); } else { return userdataService.deleteFileAllVersions(dev.userid, name, type, updatetime, isTrash); } @@ -322,9 +300,9 @@ public ResponseEntity renameFile(@RequestParam String oldName, @RequestParam String newName, @RequestParam String type, @RequestParam boolean saveCopy) throws IOException { - PremiumUserDevice dev = checkUser(); + PremiumUserDevice dev = userdataService.checkUser(); if (dev == null) { - return userdataService.tokenNotValid(); + return userdataService.tokenNotValidResponse(); } if (!oldName.equals(newName)) { return userdataService.renameFile(oldName, newName, type, dev, saveCopy); @@ -336,9 +314,9 @@ public ResponseEntity renameFile(@RequestParam String oldName, public ResponseEntity renameFolder(@RequestParam String folderName, @RequestParam String type, @RequestParam String newFolderName) throws IOException { - PremiumUserDevice dev = checkUser(); + PremiumUserDevice dev = userdataService.checkUser(); if (dev == null) { - return userdataService.tokenNotValid(); + return userdataService.tokenNotValidResponse(); } return userdataService.renameFolder(folderName, newFolderName, type, dev); } @@ -346,9 +324,9 @@ public ResponseEntity renameFolder(@RequestParam String folderName, @GetMapping(value = "/delete-folder") public ResponseEntity deleteFolder(@RequestParam String folderName, @RequestParam String type) { - PremiumUserDevice dev = checkUser(); + PremiumUserDevice dev = userdataService.checkUser(); if (dev == null) { - return userdataService.tokenNotValid(); + return userdataService.tokenNotValidResponse(); } return userdataService.deleteFolder(folderName, type, dev); } @@ -358,9 +336,9 @@ public ResponseEntity listFiles(@RequestParam(required = false) String n @RequestParam(required = false) String type, @RequestParam(required = false, defaultValue = "false") boolean addDevices, @RequestParam(required = false, defaultValue = "false") boolean allVersions) throws IOException { - PremiumUserDevice dev = checkUser(); + PremiumUserDevice dev = userdataService.checkUser(); if (dev == null) { - return tokenNotValid(); + return userdataService.tokenNotValidResponse(); } UserFilesResults res = userdataService.generateFiles(dev.userid, name, allVersions, true, type); List filesToIgnore = new ArrayList<>(); @@ -483,9 +461,9 @@ public void getFile(HttpServletResponse response, HttpServletRequest request, @RequestParam String name, @RequestParam String type, @RequestParam(required = false) Long updatetime) throws IOException { - PremiumUserDevice dev = checkUser(); + PremiumUserDevice dev = userdataService.checkUser(); if (dev == null) { - ResponseEntity error = tokenNotValid(); + ResponseEntity error = userdataService.tokenNotValidResponse(); response.setStatus(error.getStatusCodeValue()); response.getWriter().write(Objects.requireNonNull(error.getBody())); return; @@ -501,9 +479,9 @@ public void getFilePrevVersion(HttpServletResponse response, HttpServletRequest @RequestParam String name, @RequestParam String type, @RequestParam Long updatetime) throws IOException { - PremiumUserDevice dev = checkUser(); + PremiumUserDevice dev = userdataService.checkUser(); if (dev == null) { - ResponseEntity error = tokenNotValid(); + ResponseEntity error = userdataService.tokenNotValidResponse(); response.setStatus(error.getStatusCodeValue()); response.getWriter().write(Objects.requireNonNull(error.getBody())); return; @@ -516,9 +494,9 @@ public void getFilePrevVersion(HttpServletResponse response, HttpServletRequest @GetMapping(value = "/restore-file") public ResponseEntity restoreFile(@RequestParam String name, @RequestParam String type, @RequestParam Long updatetime) throws IOException { - PremiumUserDevice dev = checkUser(); + PremiumUserDevice dev = userdataService.checkUser(); if (dev == null) { - return tokenNotValid(); + return userdataService.tokenNotValidResponse(); } return userdataService.restoreFile(name, type, updatetime, dev); } @@ -531,9 +509,9 @@ public static class FileData { @PostMapping(value = "/empty-trash") public ResponseEntity emptyTrash(@RequestBody List files) { - PremiumUserDevice dev = checkUser(); + PremiumUserDevice dev = userdataService.checkUser(); if (dev == null) { - return tokenNotValid(); + return userdataService.tokenNotValidResponse(); } return userdataService.emptyTrash(files, dev); } @@ -542,7 +520,7 @@ public ResponseEntity emptyTrash(@RequestBody List files) { public ResponseEntity getGpxInfo(@RequestParam(name = "name") String name, @RequestParam(name = "type") String type, @RequestParam(name = "updatetime", required = false) Long updatetime) throws IOException { - PremiumUserDevice dev = checkUser(); + PremiumUserDevice dev = userdataService.checkUser(); InputStream in = null; try { UserFile userFile = userdataService.getUserFile(name, type, updatetime, dev); @@ -604,7 +582,7 @@ private void saveAnalysis(String tag, UserFile file, GpxTrackAnalysis analysis) public ResponseEntity getSrtmGpx(@RequestParam(name = "name") String name, @RequestParam(name = "type") String type, @RequestParam(name = "updatetime", required = false) Long updatetime) throws IOException { - PremiumUserDevice dev = checkUser(); + PremiumUserDevice dev = userdataService.checkUser(); InputStream in = null; try { UserFile userFile = userdataService.getUserFile(name, type, updatetime, dev); @@ -640,9 +618,9 @@ public void createBackup(HttpServletResponse response, @RequestParam(name = "updatetime", required = false) boolean includeDeleted, @RequestParam String format, @RequestBody List data) throws IOException { - PremiumUserDevice dev = checkUser(); + PremiumUserDevice dev = userdataService.checkUser(); if (dev == null) { - ResponseEntity error = tokenNotValid(); + ResponseEntity error = userdataService.tokenNotValidResponse(); response.setStatus(error.getStatusCodeValue()); if (error.getBody() != null) { response.getWriter().write(error.getBody()); @@ -657,9 +635,9 @@ public void createBackupFolder(@RequestParam String format, @RequestParam String folderName, @RequestParam String type, HttpServletResponse response) throws IOException { - PremiumUserDevice dev = checkUser(); + PremiumUserDevice dev = userdataService.checkUser(); if (dev == null) { - ResponseEntity error = tokenNotValid(); + ResponseEntity error = userdataService.tokenNotValidResponse(); response.setStatus(error.getStatusCodeValue()); if (error.getBody() != null) { response.getWriter().write(error.getBody()); @@ -678,7 +656,7 @@ public ResponseEntity checkDownload(@RequestParam(value = "file_name", r @RequestMapping(path = {"/download-obf"}) public ResponseEntity downloadObf(HttpServletResponse response, @RequestBody List names) throws IOException, SQLException, XmlPullParserException, InterruptedException { - PremiumUserDevice dev = checkUser(); + PremiumUserDevice dev = userdataService.checkUser(); InputStream is = null; FileInputStream fis = null; File targetObf = null; @@ -730,7 +708,7 @@ public ResponseEntity getAccountInfo() { final String EXPIRE_TIME_KEY = "expireTime"; final String MAX_ACCOUNT_SIZE = "maxAccSize"; - PremiumUserDevice dev = checkUser(); + PremiumUserDevice dev = userdataService.checkUser(); PremiumUsersRepository.PremiumUser pu = usersRepository.findById(dev.userid); Map info = new HashMap<>(); @@ -759,9 +737,9 @@ public ResponseEntity getAccountInfo() { @PostMapping(path = {"/auth/send-code"}) public ResponseEntity sendCode(@RequestParam String action, @RequestParam String lang) { - PremiumUserDevice dev = checkUser(); + PremiumUserDevice dev = userdataService.checkUser(); if (dev == null) { - return tokenNotValid(); + return userdataService.tokenNotValidResponse(); } PremiumUsersRepository.PremiumUser pu = usersRepository.findById(dev.userid); if (pu == null) { @@ -773,9 +751,9 @@ public ResponseEntity sendCode(@RequestParam String action, @RequestPara @PostMapping(path = {"/auth/send-code-to-new-email"}) public ResponseEntity sendCodeToNewEmail(@RequestParam String action, @RequestParam String lang, @RequestParam String email, @RequestParam String code) { if (emailSender.isEmail(email)) { - PremiumUserDevice dev = checkUser(); + PremiumUserDevice dev = userdataService.checkUser(); if (dev == null) { - return tokenNotValid(); + return userdataService.tokenNotValidResponse(); } // check token from old email PremiumUsersRepository.PremiumUser currentAcc = usersRepository.findById(dev.userid); @@ -813,9 +791,9 @@ public ResponseEntity changeEmail(@RequestBody UserPasswordPost credenti } username = username.toLowerCase().trim(); if (emailSender.isEmail(username)) { - PremiumUserDevice dev = checkUser(); + PremiumUserDevice dev = userdataService.checkUser(); if (dev == null) { - return tokenNotValid(); + return userdataService.tokenNotValidResponse(); } return userdataService.changeEmail(username, token, dev, request); } @@ -833,127 +811,4 @@ public String getRegionsByLatlon(@RequestParam("lat") double lat, @RequestParam( return gson.toJson(Map.of("regions", regions)); } - // Share GPX - - @PostMapping(path = {"/file-share"}, produces = "application/json") - public ResponseEntity generateGpxSharedUrl(@RequestParam String name, - @RequestParam String type, - HttpServletRequest request) { - PremiumUserDevicesRepository.PremiumUserDevice dev = checkUser(); - if (dev == null) { - return tokenNotValid(); - } - PremiumUserFilesRepository.UserFile userFile = userdataService.getUserFile(name, type, null, dev); - if (userFile == null) { - return ResponseEntity.badRequest().body(FILE_NOT_FOUND); - } - String code = shareGpxService.generateSharedCode(userFile); - String domain = request.getScheme() + "://" + request.getServerName() + - (request.getServerPort() != 80 && request.getServerPort() != 443 ? ":" + request.getServerPort() : ""); - String sharedUrl = domain + "/share/gpx/" + code; - - return ResponseEntity.ok(gson.toJson(Map.of("sharedUrl", sharedUrl))); - } - - @GetMapping(path = {"/share/gpx/{code}"}, produces = "application/json") - @Transactional - public ResponseEntity getTrackByUrl(@PathVariable String code) throws IOException { - PremiumUserDevicesRepository.PremiumUserDevice dev = checkUser(); - if (dev == null) { - return tokenNotValid(); - } - PremiumUserFilesRepository.UserFile userFile = shareGpxService.getUserFileBySharedUrl(code); - if (userFile != null) { - boolean saved = shareGpxService.saveAccessedUser(dev, userFile); - if (!saved) { - return ResponseEntity.badRequest().body("User is in the blacklist"); - } - - GpxFile gpxFile = shareGpxService.getFile(userFile); - - if (gpxFile.getError() == null) { - GpxTrackAnalysis analysis = gpxFile.getAnalysis(System.currentTimeMillis()); - WebGpxParser.TrackData gpxData = gpxService.getTrackDataByGpxFile(gpxFile, null, analysis); - if (gpxData != null) { - return ResponseEntity.ok(gsonWithNans.toJson(Map.of("gpx_data", gpxData))); - } - } - } - return ResponseEntity.badRequest().body(FILE_NOT_FOUND); - } - - public record FileDownloadResult(InputStream inputStream, String fileName, String contentType) { - } - - @GetMapping(path = {"/share/accessed-users"}, produces = "application/json") - public ResponseEntity getAccessedUsers(@RequestParam String name, - @RequestParam String type) { - PremiumUserDevicesRepository.PremiumUserDevice dev = checkUser(); - if (dev == null) { - return tokenNotValid(); - } - PremiumUserFilesRepository.UserFile userFile = userdataService.getUserFile(name, type, null, dev); - if (userFile == null) { - return ResponseEntity.badRequest().body(FILE_NOT_FOUND); - } - List users = shareGpxService.getAccessedUsers(userFile); - - return ResponseEntity.ok(gson.toJson(Map.of("accessedUsers", users))); - } - - @PostMapping(path = {"/share/edit-blacklist"}, produces = "application/json") - public ResponseEntity editBlacklist(@RequestBody List list, - @RequestParam String name, - @RequestParam String type) { - PremiumUserDevicesRepository.PremiumUserDevice dev = checkUser(); - if (dev == null) { - return tokenNotValid(); - } - PremiumUserFilesRepository.UserFile userFile = userdataService.getUserFile(name, type, null, dev); - if (userFile == null) { - return ResponseEntity.badRequest().body(FILE_NOT_FOUND); - } - boolean created = shareGpxService.editBlacklist(userFile, list); - if (!created) { - return ResponseEntity.badRequest().body("Error editing blacklist"); - } - return ResponseEntity.ok("Blacklist edited"); - } - - @GetMapping(path = {"/share/edit-whitelist"}, produces = "application/json") - public ResponseEntity editWhitelist(@RequestBody List list, - @RequestParam String name, - @RequestParam String type) { - PremiumUserDevicesRepository.PremiumUserDevice dev = checkUser(); - if (dev == null) { - return tokenNotValid(); - } - PremiumUserFilesRepository.UserFile userFile = userdataService.getUserFile(name, type, null, dev); - if (userFile == null) { - return ResponseEntity.badRequest().body(FILE_NOT_FOUND); - } - boolean created = shareGpxService.editWhitelist(userFile, list); - if (!created) { - return ResponseEntity.badRequest().body("Error editing whitelist"); - } - return ResponseEntity.ok("Whitelist edited"); - } - - @GetMapping(path = {"/share/get-blacklist"}, produces = "application/json") - public ResponseEntity getBlacklist(@RequestParam String name, - @RequestParam String type) { - PremiumUserDevicesRepository.PremiumUserDevice dev = checkUser(); - if (dev == null) { - return tokenNotValid(); - } - PremiumUserFilesRepository.UserFile userFile = userdataService.getUserFile(name, type, null, dev); - if (userFile == null) { - return ResponseEntity.badRequest().body(FILE_NOT_FOUND); - } - List users = shareGpxService.getBlackList(userFile); - if (users == null) { - return ResponseEntity.badRequest().body("No blacklisted users"); - } - return ResponseEntity.ok(gson.toJson(Map.of("blacklist", users))); - } } diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java new file mode 100644 index 000000000..090d1d44c --- /dev/null +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java @@ -0,0 +1,199 @@ +package net.osmand.server.controllers.user; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import net.osmand.server.api.repo.PremiumUserDevicesRepository; +import net.osmand.server.api.repo.PremiumUserFilesRepository; +import net.osmand.server.api.services.GpxService; +import net.osmand.server.api.services.UserdataService; +import net.osmand.server.api.services.ShareGpxService; +import net.osmand.server.utils.WebGpxParser; +import net.osmand.shared.gpx.GpxFile; +import net.osmand.shared.gpx.GpxTrackAnalysis; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.InputStreamResource; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.*; + +import javax.transaction.Transactional; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static net.osmand.server.api.services.UserdataService.FILE_NOT_FOUND; + + +@Controller +@RequestMapping({"/share"}) +public class ShareFileController { + + @Autowired + UserdataService userdataService; + + @Autowired + ShareGpxService shareGpxService; + + @Autowired + protected GpxService gpxService; + + Gson gson = new Gson(); + Gson gsonWithNans = new GsonBuilder().serializeSpecialFloatingPointValues().create(); + + private static final String ERROR_GETTING_SHARED_INFO = "Error getting shared info"; + + public record FileDownloadResult(InputStream inputStream, String fileName, String contentType) { + } + + @PostMapping(path = {"/generate-link"}, produces = "application/json") + public ResponseEntity generateLink(@RequestParam String fileName, + @RequestParam String fileType, + @RequestParam String type, + @RequestParam(required = false) String groupActions) { + PremiumUserDevicesRepository.PremiumUserDevice dev = userdataService.checkUser(); + if (dev == null) { + return userdataService.tokenNotValidResponse(); + } + PremiumUserFilesRepository.UserFile userFile = userdataService.getUserFile(fileName, fileType, null, dev); + if (userFile == null) { + return ResponseEntity.badRequest().body(FILE_NOT_FOUND); + } + String code = shareGpxService.generateSharedCode(userFile, type, groupActions); + + return ResponseEntity.ok(gson.toJson(Map.of("url", "/share/get?code=" + code))); + } + + @PostMapping(path = {"/get"}, produces = "application/json") + @Transactional + public ResponseEntity getFile(@RequestParam String code) throws IOException { + PremiumUserFilesRepository.UserFile userFile = shareGpxService.getUserFileBySharedUrl(code); + if (userFile == null) { + return ResponseEntity.badRequest().body(FILE_NOT_FOUND); + } + ShareGpxService.FileSharedInfo info = shareGpxService.getSharedInfo(userFile.id); + if (info == null) { + return ResponseEntity.badRequest().body(ERROR_GETTING_SHARED_INFO); + } + boolean hasAccess = shareGpxService.checkAccess(info); + if (!hasAccess) { + return ResponseEntity.badRequest().body("You don't have access to this file"); + } + PremiumUserDevicesRepository.PremiumUserDevice dev = userdataService.checkUser(); + if (dev != null) { + shareGpxService.saveAccessedUser(dev, userFile, info); + } + FileDownloadResult fileResult = shareGpxService.downloadFile(userFile); + if (fileResult == null) { + return ResponseEntity.badRequest().body(FILE_NOT_FOUND); + } + return ResponseEntity.ok() + .header("Content-Disposition", "attachment; filename=" + fileResult.fileName) + .contentType(org.springframework.http.MediaType.valueOf(fileResult.contentType)) + .body(new InputStreamResource(fileResult.inputStream)); + } + + @PostMapping(path = {"/get-gpx"}, produces = "application/json") + @Transactional + public ResponseEntity getGpx(@RequestParam String code) throws IOException { + PremiumUserFilesRepository.UserFile userFile = shareGpxService.getUserFileBySharedUrl(code); + if (userFile == null) { + return ResponseEntity.badRequest().body(FILE_NOT_FOUND); + } + ShareGpxService.FileSharedInfo info = shareGpxService.getSharedInfo(userFile.id); + if (info == null) { + return ResponseEntity.badRequest().body(ERROR_GETTING_SHARED_INFO); + } + boolean hasAccess = shareGpxService.checkAccess(info); + if (!hasAccess) { + return ResponseEntity.badRequest().body("File is private"); + } + PremiumUserDevicesRepository.PremiumUserDevice dev = userdataService.checkUser(); + if (dev != null) { + shareGpxService.saveAccessedUser(dev, userFile, info); + } + GpxFile gpxFile = shareGpxService.getFile(userFile); + if (gpxFile.getError() == null) { + GpxTrackAnalysis analysis = gpxFile.getAnalysis(System.currentTimeMillis()); + WebGpxParser.TrackData gpxData = gpxService.getTrackDataByGpxFile(gpxFile, null, analysis); + if (gpxData != null) { + return ResponseEntity.ok(gsonWithNans.toJson(Map.of("gpx_data", gpxData))); + } + } + return ResponseEntity.badRequest().body("Error getting gpx data"); + } + + @PostMapping(path = {"/edit-blacklist"}, produces = "application/json") + public ResponseEntity editBlacklist(@RequestBody List list, + @RequestParam String name, + @RequestParam String type) { + PremiumUserDevicesRepository.PremiumUserDevice dev = userdataService.checkUser(); + if (dev == null) { + return userdataService.tokenNotValidResponse(); + } + PremiumUserFilesRepository.UserFile userFile = userdataService.getUserFile(name, type, null, dev); + if (userFile == null) { + return ResponseEntity.badRequest().body(FILE_NOT_FOUND); + } + boolean created = shareGpxService.editBlacklist(userFile, list); + if (!created) { + return ResponseEntity.badRequest().body("Error editing blacklist"); + } + return ResponseEntity.ok("Blacklist edited"); + } + + @GetMapping(path = {"/edit-whitelist"}, produces = "application/json") + public ResponseEntity editWhitelist(@RequestBody List list, + @RequestParam String name, + @RequestParam String type) { + PremiumUserDevicesRepository.PremiumUserDevice dev = userdataService.checkUser(); + if (dev == null) { + return userdataService.tokenNotValidResponse(); + } + PremiumUserFilesRepository.UserFile userFile = userdataService.getUserFile(name, type, null, dev); + if (userFile == null) { + return ResponseEntity.badRequest().body(FILE_NOT_FOUND); + } + boolean created = shareGpxService.editWhitelist(userFile, list); + if (!created) { + return ResponseEntity.badRequest().body("Error editing whitelist"); + } + return ResponseEntity.ok("Whitelist edited"); + } + + @GetMapping(path = {"/get-shared-info"}, produces = "application/json") + public ResponseEntity getSharedInfo(@RequestParam String name, + @RequestParam String type) { + PremiumUserDevicesRepository.PremiumUserDevice dev = userdataService.checkUser(); + if (dev == null) { + return userdataService.tokenNotValidResponse(); + } + PremiumUserFilesRepository.UserFile userFile = userdataService.getUserFile(name, type, null, dev); + if (userFile == null) { + return ResponseEntity.badRequest().body(FILE_NOT_FOUND); + } + ShareGpxService.FileSharedInfo info = shareGpxService.getSharedInfo(userFile.id); + if (info == null) { + return ResponseEntity.badRequest().body(ERROR_GETTING_SHARED_INFO); + } + return ResponseEntity.ok(gson.toJson(info)); + } + + @GetMapping(path = {"/check-access"}, produces = "application/json") + public ResponseEntity checkAccess(@RequestParam String code) { + PremiumUserDevicesRepository.PremiumUserDevice dev = userdataService.checkUser(); + if (dev == null) { + return userdataService.tokenNotValidResponse(); + } + ShareGpxService.FileSharedInfo info = shareGpxService.getSharedInfo(code); + if (info == null) { + return ResponseEntity.badRequest().body(ERROR_GETTING_SHARED_INFO); + } + ShareGpxService.FileSharedInfo.SharingType type = info.getSharingType(); + Set groupActions = info.getGroupActions(); + + return ResponseEntity.ok(gson.toJson(Map.of("hasAccess", shareGpxService.checkAccess(info), "type", type, "groupActions", groupActions))); + } + +} From d91e40d6a8ee262cb31dbc4a9fc706008566a82a Mon Sep 17 00:00:00 2001 From: alisa911 Date: Thu, 28 Nov 2024 16:58:45 +0200 Subject: [PATCH 06/23] add index --- db/sql_changeset_schema | 1 + 1 file changed, 1 insertion(+) diff --git a/db/sql_changeset_schema b/db/sql_changeset_schema index 13eefce5e..1b4cbdc91 100644 --- a/db/sql_changeset_schema +++ b/db/sql_changeset_schema @@ -38,6 +38,7 @@ ALTER TABLE user_accounts ADD CONSTRAINT email_uniq UNIQUE (email); CREATE INDEX user_account_devices_userid_idx on user_account_devices(userid); CREATE INDEX user_files_userid_idx on user_files(userid); CREATE INDEX user_files_deviceid_idx on user_files(deviceid); +CREATE INDEX user_files_shared_code_idx on user_files(shared_code); -- mail group CREATE TABLE email_free_users(aid text, email text, os text, updatetime timestamp); From b2b40b0fd6ce2259afc04ee354f88f5d5ede2b4c Mon Sep 17 00:00:00 2001 From: alisa911 Date: Thu, 28 Nov 2024 17:05:24 +0200 Subject: [PATCH 07/23] fix saveAccessedUser --- .../osmand/server/api/services/ShareGpxService.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareGpxService.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareGpxService.java index 5b8ad2345..7d24e78fe 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareGpxService.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareGpxService.java @@ -146,9 +146,13 @@ public void saveAccessedUser(PremiumUserDevicesRepository.PremiumUserDevice dev, if (info == null) { info = new FileSharedInfo(); } - info.getAccessedUsers().getUsers().put(dev.userid, System.currentTimeMillis()); - userFile.sharedInfo = gson.toJsonTree(info).getAsJsonObject(); - filesRepository.saveAndFlush(userFile); + Map users = info.getAccessedUsers().getUsers(); + if (!users.containsKey(dev.userid)) { + users.put(dev.userid, System.currentTimeMillis()); + info.getAccessedUsers().setUsers(users); + userFile.sharedInfo = gson.toJsonTree(info).getAsJsonObject(); + filesRepository.saveAndFlush(userFile); + } } @Transactional From 138137fee8db5e12961dc90f5ce36982da67fab8 Mon Sep 17 00:00:00 2001 From: alisa911 Date: Fri, 29 Nov 2024 11:02:21 +0200 Subject: [PATCH 08/23] refactoring --- .../server/api/services/ShareGpxService.java | 31 +++++++++++-------- .../controllers/user/ShareFileController.java | 4 +-- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareGpxService.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareGpxService.java index 7d24e78fe..f06949717 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareGpxService.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareGpxService.java @@ -12,6 +12,8 @@ import net.osmand.shared.gpx.GpxUtilities; import okio.Buffer; import okio.Source; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -40,6 +42,8 @@ public class ShareGpxService { @Autowired UserdataService userdataService; + protected static final Log LOGGER = LogFactory.getLog(ShareGpxService.class); + private static final String BLACK_LIST = "blacklist"; private static final String WHITE_LIST = "whitelist"; @@ -123,8 +127,8 @@ public ShareFileController.FileDownloadResult downloadFile(PremiumUserFilesRepos return null; } - public PremiumUserFilesRepository.UserFile getUserFileBySharedUrl(String token) { - return filesRepository.findUserFileBySharedCode(token); + public PremiumUserFilesRepository.UserFile getUserFileBySharedUrl(String code) { + return filesRepository.findUserFileBySharedCode(code); } public GpxFile getFile(PremiumUserFilesRepository.UserFile file) throws IOException { @@ -137,19 +141,20 @@ public GpxFile getFile(PremiumUserFilesRepository.UserFile file) throws IOExcept if (gpxFile.getError() == null) { return gpxFile; } + LOGGER.error("Error loading gpx file: " + gpxFile.getError()); return null; } } @Transactional - public void saveAccessedUser(PremiumUserDevicesRepository.PremiumUserDevice dev, PremiumUserFilesRepository.UserFile userFile, FileSharedInfo info) { + public void storeUserAccess(PremiumUserDevicesRepository.PremiumUserDevice dev, PremiumUserFilesRepository.UserFile userFile, FileSharedInfo info) { if (info == null) { info = new FileSharedInfo(); } - Map users = info.getAccessedUsers().getUsers(); + Map users = info.getUsersAccessInfo().getUsers(); if (!users.containsKey(dev.userid)) { users.put(dev.userid, System.currentTimeMillis()); - info.getAccessedUsers().setUsers(users); + info.getUsersAccessInfo().setUsers(users); userFile.sharedInfo = gson.toJsonTree(info).getAsJsonObject(); filesRepository.saveAndFlush(userFile); } @@ -200,18 +205,18 @@ public boolean editWhitelist(PremiumUserFilesRepository.UserFile userFile, List< public static class FileSharedInfo { private Whitelist whitelist; private Blacklist blacklist; - private AccessedUsers accessedUsers; + private UsersAccessInfo usersAccessInfo; private SharingType sharingType; private Set groupActions; public FileSharedInfo() { - this(new Whitelist(), new Blacklist(), new AccessedUsers(), SharingType.PUBLIC, new HashSet<>()); + this(new Whitelist(), new Blacklist(), new UsersAccessInfo(), SharingType.PUBLIC, new HashSet<>()); } - public FileSharedInfo(Whitelist whitelist, Blacklist blacklist, AccessedUsers accessedUsers, SharingType sharingType, Set groupActions) { + public FileSharedInfo(Whitelist whitelist, Blacklist blacklist, UsersAccessInfo usersAccessInfo, SharingType sharingType, Set groupActions) { this.whitelist = whitelist; this.blacklist = blacklist; - this.accessedUsers = accessedUsers; + this.usersAccessInfo = usersAccessInfo; this.sharingType = sharingType != null ? sharingType : SharingType.PUBLIC; this.groupActions = groupActions != null ? groupActions : new HashSet<>(); } @@ -242,7 +247,7 @@ public static SharingType fromType(String type) { public enum GroupAction { EDIT_WHITELIST("editWhitelist"), EDIT_BLACKLIST("editBlacklist"), - SEE_ACCESSED_USERS("seeAccessedUsers"); + VIEW_USERS_ACCESS("viewUsersAccess"); private final String action; @@ -304,14 +309,14 @@ public Blacklist(Set users) { } @Data - public static class AccessedUsers { + public static class UsersAccessInfo { private Map users; - public AccessedUsers() { + public UsersAccessInfo() { this(new HashMap<>()); } - public AccessedUsers(Map users) { + public UsersAccessInfo(Map users) { this.users = users != null ? users : new HashMap<>(); } diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java index 090d1d44c..fc357651b 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java @@ -82,7 +82,7 @@ public ResponseEntity getFile(@RequestParam String code) throws IOException { } PremiumUserDevicesRepository.PremiumUserDevice dev = userdataService.checkUser(); if (dev != null) { - shareGpxService.saveAccessedUser(dev, userFile, info); + shareGpxService.storeUserAccess(dev, userFile, info); } FileDownloadResult fileResult = shareGpxService.downloadFile(userFile); if (fileResult == null) { @@ -111,7 +111,7 @@ public ResponseEntity getGpx(@RequestParam String code) throws IOException { } PremiumUserDevicesRepository.PremiumUserDevice dev = userdataService.checkUser(); if (dev != null) { - shareGpxService.saveAccessedUser(dev, userFile, info); + shareGpxService.storeUserAccess(dev, userFile, info); } GpxFile gpxFile = shareGpxService.getFile(userFile); if (gpxFile.getError() == null) { From c635cbd332cc936c423b87c368945b8ca1aa8722 Mon Sep 17 00:00:00 2001 From: alisa911 Date: Mon, 2 Dec 2024 12:18:49 +0200 Subject: [PATCH 09/23] create table share_files --- db/sql_changeset_schema | 9 +- .../api/repo/PremiumUserFilesRepository.java | 37 ++-- .../server/api/repo/ShareFileRepository.java | 71 ++++++++ ...eGpxService.java => ShareFileService.java} | 159 +++++++++++++----- .../controllers/user/ShareFileController.java | 71 ++++---- 5 files changed, 251 insertions(+), 96 deletions(-) create mode 100644 java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/ShareFileRepository.java rename java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/{ShareGpxService.java => ShareFileService.java} (66%) diff --git a/db/sql_changeset_schema b/db/sql_changeset_schema index 1b4cbdc91..260c142a0 100644 --- a/db/sql_changeset_schema +++ b/db/sql_changeset_schema @@ -27,7 +27,7 @@ ALTER TABLE supporters_device_sub add primary key (sku, orderid); ------ PREMIUM accounts ---- CREATE TABLE user_accounts(id serial primary key, email text not null, tokendevice text, orderid text, token text, tokentime timestamp, regtime timestamp); CREATE TABLE user_account_devices(id serial primary key, userid integer, deviceid text, accesstoken text, lang text, brand text, model text, udpatetime timestamp); -CREATE TABLE user_files(id bigserial primary key, type text, name text, userid integer, deviceid integer, updatetime timestamp, clienttime timestamp, filesize bigint, zipfilesize bigint, storage text, gendetails jsonb, data bytea, shared_code text, shared_info jsonb); +CREATE TABLE user_files(id bigserial primary key, type text, name text, userid integer, deviceid integer, updatetime timestamp, clienttime timestamp, filesize bigint, zipfilesize bigint, storage text, gendetails jsonb, data bytea); CREATE TABLE promo_campaigns(name text, starttime timestamp, endtime timestamp, subactivemonths integer, numberlimit integer, used integer, lastusers text); ALTER TABLE user_accounts ADD CONSTRAINT email_uniq UNIQUE (email); --- @@ -38,7 +38,12 @@ ALTER TABLE user_accounts ADD CONSTRAINT email_uniq UNIQUE (email); CREATE INDEX user_account_devices_userid_idx on user_account_devices(userid); CREATE INDEX user_files_userid_idx on user_files(userid); CREATE INDEX user_files_deviceid_idx on user_files(deviceid); -CREATE INDEX user_files_shared_code_idx on user_files(shared_code); + +-- share file +CREATE TABLE share_files(code varchar NOT NULL primary key, name text NOT NULL, type text NOT NULL, userid integer NOT NULL, info jsonb); +CREATE INDEX share_files_userid_idx ON share_files(userid); +CREATE INDEX share_files_name_idx ON share_files(name); +CREATE INDEX share_files_type_idx ON share_files(type); -- mail group CREATE TABLE email_free_users(aid text, email text, os text, updatetime timestamp); diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/PremiumUserFilesRepository.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/PremiumUserFilesRepository.java index 67774e351..44e368d2a 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/PremiumUserFilesRepository.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/PremiumUserFilesRepository.java @@ -1,6 +1,7 @@ package net.osmand.server.api.repo; +import java.io.IOException; import java.io.Serial; import java.io.Serializable; import java.util.Date; @@ -15,7 +16,7 @@ import javax.persistence.Temporal; import javax.persistence.TemporalType; -import net.osmand.server.api.services.ShareGpxService.FileSharedInfo; +import com.google.gson.Gson; import org.hibernate.annotations.Type; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; @@ -41,14 +42,6 @@ public interface PremiumUserFilesRepository extends JpaRepository findAllByUserid(int userid); - UserFile findUserFileBySharedCode(String code); - - @Query("SELECT uf.sharedInfo FROM UserFile uf WHERE uf.sharedCode = :code") - JsonObject findSharedInfoBySharedCode(String code); - - @Query("SELECT uf.sharedInfo FROM UserFile uf WHERE uf.id = :id") - JsonObject findSharedInfoById(long id); - @Query("SELECT uf FROM UserFile uf " + "WHERE uf.userid = :userid AND uf.name LIKE :folderName% AND uf.type = :type AND (uf.name, uf.updatetime) IN " + "(SELECT uft.name, MAX(uft.updatetime) FROM UserFile uft WHERE uft.userid = :userid GROUP BY uft.name)") @@ -63,6 +56,8 @@ public interface PremiumUserFilesRepository extends JpaRepository { + + ShareFile findByCode(String code); + + @Query("SELECT sf.info FROM ShareFile sf WHERE sf.code = :code") + JsonObject findInfoByCode(String code); + + ShareFile findByUseridAndNameAndType(int userid, String name, String type); + + + @Setter + @Getter + @Entity(name = "ShareFile") + @Table(name = "share_files") + class ShareFile implements Serializable { + private static final Gson gson = new Gson(); + + @Serial + private static final long serialVersionUID = 1L; + + @Id + @Column(nullable = false, unique = true) + private String code; + + @Column(name = "name", nullable = false) + public String name; + + @Column(name = "type", nullable = false) + public String type; + + @Column(name = "userid", nullable = false) + public int userid; + + @Column(name = "info", columnDefinition = "jsonb") + @Type(type = "net.osmand.server.assist.data.JsonbType") + private JsonObject info; + + @Serial + private void writeObject(java.io.ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + out.writeObject(info != null ? gson.toJson(info) : null); + } + + @Serial + private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + String json = (String) in.readObject(); + if (json != null) { + this.info = gson.fromJson(json, JsonObject.class); + } + } + + } +} \ No newline at end of file diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareGpxService.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java similarity index 66% rename from java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareGpxService.java rename to java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java index f06949717..afb2c206a 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareGpxService.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java @@ -4,9 +4,7 @@ import com.google.gson.JsonObject; import lombok.Data; import lombok.Getter; -import net.osmand.server.api.repo.PremiumUserDevicesRepository; -import net.osmand.server.api.repo.PremiumUserFilesRepository; -import net.osmand.server.api.repo.PremiumUsersRepository; +import net.osmand.server.api.repo.*; import net.osmand.server.controllers.user.ShareFileController; import net.osmand.shared.gpx.GpxFile; import net.osmand.shared.gpx.GpxUtilities; @@ -19,6 +17,7 @@ import javax.annotation.Nullable; import javax.transaction.Transactional; +import javax.validation.constraints.NotNull; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @@ -31,64 +30,77 @@ @Service -public class ShareGpxService { +public class ShareFileService { @Autowired protected PremiumUserFilesRepository filesRepository; + @Autowired + protected ShareFileRepository shareFileRepository; + @Autowired protected PremiumUsersRepository usersRepository; @Autowired UserdataService userdataService; - protected static final Log LOGGER = LogFactory.getLog(ShareGpxService.class); - - private static final String BLACK_LIST = "blacklist"; - private static final String WHITE_LIST = "whitelist"; + protected static final Log LOGGER = LogFactory.getLog(ShareFileService.class); private static final String SEPARATOR = ","; + private static final String CODE_SEPARATOR = "-"; + public static final int CODE_LENGTH = 8; Gson gson = new Gson(); @Transactional public String generateSharedCode(PremiumUserFilesRepository.UserFile userFile, String type, @Nullable String groupActions) { - String uniqueToken = UUID.randomUUID().toString(); - userFile.sharedCode = uniqueToken; - userFile.sharedInfo = addSharedInfo(type, groupActions); - filesRepository.saveAndFlush(userFile); + String uniqueCode = UUID.randomUUID().toString().substring(0, CODE_LENGTH); + ShareFileRepository.ShareFile file = shareFileRepository.findByCode(uniqueCode); + while (file != null) { + uniqueCode = UUID.randomUUID().toString().substring(0, CODE_LENGTH); + file = shareFileRepository.findByCode(uniqueCode); + } + ShareFileRepository.ShareFile shareFile = new ShareFileRepository.ShareFile(); + shareFile.setCode(uniqueCode); + shareFile.setName(userFile.name); + shareFile.setType(userFile.type); + shareFile.setUserid(userFile.userid); + shareFile.setInfo(addSharedInfo(type, groupActions)); + + ShareFileRepository.ShareFile existingFile = shareFileRepository.findByUseridAndNameAndType(userFile.userid, userFile.name, userFile.type); + if (existingFile != null) { + shareFileRepository.delete(existingFile); + } + shareFileRepository.saveAndFlush(shareFile); - return uniqueToken; + return uniqueCode; } - private JsonObject addSharedInfo(String type, @Nullable String groupActions) { - FileSharedInfo fileSharedInfo = new FileSharedInfo(); - FileSharedInfo.SharingType sharingType = FileSharedInfo.SharingType.fromType(type); - fileSharedInfo.setSharingType(Objects.requireNonNullElse(sharingType, FileSharedInfo.SharingType.PUBLIC)); - - if (sharingType == FileSharedInfo.SharingType.GROUP_BY_LOGIN && groupActions != null) { - String[] actions = groupActions.split(SEPARATOR); - for (String action : actions) { - FileSharedInfo.GroupAction groupAction = FileSharedInfo.GroupAction.fromAction(action.trim()); - if (groupAction != null) { - fileSharedInfo.getGroupActions().add(groupAction); - } - } - } - - return gson.toJsonTree(fileSharedInfo).getAsJsonObject(); + public String getNamePartForCode(String filename) { + final String DOT = "."; + String prefix = filename.substring(0, CODE_LENGTH); + String suffix = filename.contains(DOT) ? filename.substring(filename.lastIndexOf(DOT) - 1) : ""; + return prefix + suffix; } - public FileSharedInfo getSharedInfo(Long id) { - JsonObject sharedInfoJson = filesRepository.findSharedInfoById(id); + public FileSharedInfo getSharedInfo(JsonObject sharedInfoJson) { if (sharedInfoJson != null) { return gson.fromJson(sharedInfoJson, FileSharedInfo.class); } return null; } + public FileSharedInfo getSharedInfo(@NotNull PremiumUserDevicesRepository.PremiumUserDevice dev, @NotNull String name, @NotNull String type) { + ShareFileRepository.ShareFile file = shareFileRepository.findByUseridAndNameAndType(dev.userid, name, type); + if (file != null) { + return getSharedInfo(file.getInfo()); + } + return null; + } + public FileSharedInfo getSharedInfo(String code) { - JsonObject sharedInfoJson = filesRepository.findSharedInfoBySharedCode(code); + code = normalizeCode(code); + JsonObject sharedInfoJson = shareFileRepository.findInfoByCode(code); if (sharedInfoJson != null) { return gson.fromJson(sharedInfoJson, FileSharedInfo.class); } @@ -127,8 +139,16 @@ public ShareFileController.FileDownloadResult downloadFile(PremiumUserFilesRepos return null; } - public PremiumUserFilesRepository.UserFile getUserFileBySharedUrl(String code) { - return filesRepository.findUserFileBySharedCode(code); + public ShareFileRepository.ShareFile getShareFileByCode(String code) { + if (code == null) { + return null; + } + code = normalizeCode(code); + return shareFileRepository.findByCode(code); + } + + public PremiumUserFilesRepository.UserFile getUserFile(ShareFileRepository.ShareFile file) { + return filesRepository.findTopByUseridAndNameAndTypeOrderByUpdatetimeDesc(file.userid, file.name, file.type); } public GpxFile getFile(PremiumUserFilesRepository.UserFile file) throws IOException { @@ -147,7 +167,9 @@ public GpxFile getFile(PremiumUserFilesRepository.UserFile file) throws IOExcept } @Transactional - public void storeUserAccess(PremiumUserDevicesRepository.PremiumUserDevice dev, PremiumUserFilesRepository.UserFile userFile, FileSharedInfo info) { + public void storeUserAccess(PremiumUserDevicesRepository.PremiumUserDevice dev, + ShareFileRepository.ShareFile shareFile, + FileSharedInfo info) { if (info == null) { info = new FileSharedInfo(); } @@ -155,14 +177,24 @@ public void storeUserAccess(PremiumUserDevicesRepository.PremiumUserDevice dev, if (!users.containsKey(dev.userid)) { users.put(dev.userid, System.currentTimeMillis()); info.getUsersAccessInfo().setUsers(users); - userFile.sharedInfo = gson.toJsonTree(info).getAsJsonObject(); - filesRepository.saveAndFlush(userFile); + shareFile.setInfo(gson.toJsonTree(info).getAsJsonObject()); + shareFileRepository.saveAndFlush(shareFile); } } @Transactional - public boolean editBlacklist(PremiumUserFilesRepository.UserFile userFile, List list) { - FileSharedInfo fileSharedInfo = gson.fromJson(userFile.sharedInfo, FileSharedInfo.class); + public boolean editBlacklist(@NotNull PremiumUserDevicesRepository.PremiumUserDevice dev, + @NotNull String name, + @NotNull String type, + List list) { + if (dev == null || name == null || type == null) { + return false; + } + ShareFileRepository.ShareFile file = shareFileRepository.findByUseridAndNameAndType(dev.userid, name, type); + if (file == null) { + return false; + } + FileSharedInfo fileSharedInfo = gson.fromJson(file.getInfo(), FileSharedInfo.class); if (fileSharedInfo == null) { fileSharedInfo = new FileSharedInfo(); } @@ -174,14 +206,25 @@ public boolean editBlacklist(PremiumUserFilesRepository.UserFile userFile, List< if (blist.getUsers().isEmpty()) { return false; } - userFile.sharedInfo.add(BLACK_LIST, gson.toJsonTree(blist)); - filesRepository.saveAndFlush(userFile); + fileSharedInfo.setBlacklist(blist); + file.setInfo(gson.toJsonTree(fileSharedInfo).getAsJsonObject()); + shareFileRepository.saveAndFlush(file); return true; } @Transactional - public boolean editWhitelist(PremiumUserFilesRepository.UserFile userFile, List list) { - FileSharedInfo fileSharedInfo = gson.fromJson(userFile.sharedInfo, FileSharedInfo.class); + public boolean editWhitelist(@NotNull PremiumUserDevicesRepository.PremiumUserDevice dev, + @NotNull String name, + @NotNull String type, + List list) { + if (dev == null || name == null || type == null) { + return false; + } + ShareFileRepository.ShareFile file = shareFileRepository.findByUseridAndNameAndType(dev.userid, name, type); + if (file == null) { + return false; + } + FileSharedInfo fileSharedInfo = gson.fromJson(file.getInfo(), FileSharedInfo.class); if (fileSharedInfo == null) { fileSharedInfo = new FileSharedInfo(); } @@ -196,11 +239,37 @@ public boolean editWhitelist(PremiumUserFilesRepository.UserFile userFile, List< if (wlist.getPermissions().isEmpty()) { return false; } - userFile.sharedInfo.add(WHITE_LIST, gson.toJsonTree(wlist)); - filesRepository.saveAndFlush(userFile); + fileSharedInfo.setWhitelist(wlist); + file.setInfo(gson.toJsonTree(fileSharedInfo).getAsJsonObject()); + shareFileRepository.saveAndFlush(file); return true; } + private JsonObject addSharedInfo(String type, @Nullable String groupActions) { + FileSharedInfo fileSharedInfo = new FileSharedInfo(); + FileSharedInfo.SharingType sharingType = FileSharedInfo.SharingType.fromType(type); + fileSharedInfo.setSharingType(Objects.requireNonNullElse(sharingType, FileSharedInfo.SharingType.PUBLIC)); + + if (sharingType == FileSharedInfo.SharingType.GROUP_BY_LOGIN && groupActions != null) { + String[] actions = groupActions.split(SEPARATOR); + for (String action : actions) { + FileSharedInfo.GroupAction groupAction = FileSharedInfo.GroupAction.fromAction(action.trim()); + if (groupAction != null) { + fileSharedInfo.getGroupActions().add(groupAction); + } + } + } + + return gson.toJsonTree(fileSharedInfo).getAsJsonObject(); + } + + private String normalizeCode(String code) { + if (code.contains(CODE_SEPARATOR)) { + code = code.substring(0, code.indexOf(CODE_SEPARATOR)); + } + return code; + } + @Data public static class FileSharedInfo { private Whitelist whitelist; diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java index fc357651b..b5628af4f 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java @@ -4,12 +4,15 @@ import com.google.gson.GsonBuilder; import net.osmand.server.api.repo.PremiumUserDevicesRepository; import net.osmand.server.api.repo.PremiumUserFilesRepository; +import net.osmand.server.api.repo.ShareFileRepository; import net.osmand.server.api.services.GpxService; import net.osmand.server.api.services.UserdataService; -import net.osmand.server.api.services.ShareGpxService; +import net.osmand.server.api.services.ShareFileService; import net.osmand.server.utils.WebGpxParser; import net.osmand.shared.gpx.GpxFile; import net.osmand.shared.gpx.GpxTrackAnalysis; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.InputStreamResource; import org.springframework.http.ResponseEntity; @@ -34,7 +37,7 @@ public class ShareFileController { UserdataService userdataService; @Autowired - ShareGpxService shareGpxService; + ShareFileService shareFileService; @Autowired protected GpxService gpxService; @@ -42,6 +45,8 @@ public class ShareFileController { Gson gson = new Gson(); Gson gsonWithNans = new GsonBuilder().serializeSpecialFloatingPointValues().create(); + protected static final Log LOGGER = LogFactory.getLog(ShareFileController.class); + private static final String ERROR_GETTING_SHARED_INFO = "Error getting shared info"; public record FileDownloadResult(InputStream inputStream, String fileName, String contentType) { @@ -60,31 +65,40 @@ public ResponseEntity generateLink(@RequestParam String fileName, if (userFile == null) { return ResponseEntity.badRequest().body(FILE_NOT_FOUND); } - String code = shareGpxService.generateSharedCode(userFile, type, groupActions); + String code = shareFileService.generateSharedCode(userFile, type, groupActions); + if (code == null) { + return ResponseEntity.badRequest().body("Error generating link"); + } + String name = shareFileService.getNamePartForCode(fileName); + String url = "/share/get?code=" + code + "-" + name; - return ResponseEntity.ok(gson.toJson(Map.of("url", "/share/get?code=" + code))); + return ResponseEntity.ok(gson.toJson(Map.of("url", url))); } @PostMapping(path = {"/get"}, produces = "application/json") @Transactional public ResponseEntity getFile(@RequestParam String code) throws IOException { - PremiumUserFilesRepository.UserFile userFile = shareGpxService.getUserFileBySharedUrl(code); + ShareFileRepository.ShareFile shareFile = shareFileService.getShareFileByCode(code); + if (shareFile == null) { + return ResponseEntity.badRequest().body(FILE_NOT_FOUND); + } + PremiumUserFilesRepository.UserFile userFile = shareFileService.getUserFile(shareFile); if (userFile == null) { return ResponseEntity.badRequest().body(FILE_NOT_FOUND); } - ShareGpxService.FileSharedInfo info = shareGpxService.getSharedInfo(userFile.id); + ShareFileService.FileSharedInfo info = shareFileService.getSharedInfo(shareFile.getInfo()); if (info == null) { return ResponseEntity.badRequest().body(ERROR_GETTING_SHARED_INFO); } - boolean hasAccess = shareGpxService.checkAccess(info); + boolean hasAccess = shareFileService.checkAccess(info); if (!hasAccess) { return ResponseEntity.badRequest().body("You don't have access to this file"); } PremiumUserDevicesRepository.PremiumUserDevice dev = userdataService.checkUser(); if (dev != null) { - shareGpxService.storeUserAccess(dev, userFile, info); + shareFileService.storeUserAccess(dev, shareFile, info); } - FileDownloadResult fileResult = shareGpxService.downloadFile(userFile); + FileDownloadResult fileResult = shareFileService.downloadFile(userFile); if (fileResult == null) { return ResponseEntity.badRequest().body(FILE_NOT_FOUND); } @@ -97,23 +111,27 @@ public ResponseEntity getFile(@RequestParam String code) throws IOException { @PostMapping(path = {"/get-gpx"}, produces = "application/json") @Transactional public ResponseEntity getGpx(@RequestParam String code) throws IOException { - PremiumUserFilesRepository.UserFile userFile = shareGpxService.getUserFileBySharedUrl(code); + ShareFileRepository.ShareFile shareFile = shareFileService.getShareFileByCode(code); + if (shareFile == null) { + return ResponseEntity.badRequest().body(FILE_NOT_FOUND); + } + PremiumUserFilesRepository.UserFile userFile = shareFileService.getUserFile(shareFile); if (userFile == null) { return ResponseEntity.badRequest().body(FILE_NOT_FOUND); } - ShareGpxService.FileSharedInfo info = shareGpxService.getSharedInfo(userFile.id); + ShareFileService.FileSharedInfo info = shareFileService.getSharedInfo(shareFile.getInfo()); if (info == null) { return ResponseEntity.badRequest().body(ERROR_GETTING_SHARED_INFO); } - boolean hasAccess = shareGpxService.checkAccess(info); + boolean hasAccess = shareFileService.checkAccess(info); if (!hasAccess) { return ResponseEntity.badRequest().body("File is private"); } PremiumUserDevicesRepository.PremiumUserDevice dev = userdataService.checkUser(); if (dev != null) { - shareGpxService.storeUserAccess(dev, userFile, info); + shareFileService.storeUserAccess(dev, shareFile, info); } - GpxFile gpxFile = shareGpxService.getFile(userFile); + GpxFile gpxFile = shareFileService.getFile(userFile); if (gpxFile.getError() == null) { GpxTrackAnalysis analysis = gpxFile.getAnalysis(System.currentTimeMillis()); WebGpxParser.TrackData gpxData = gpxService.getTrackDataByGpxFile(gpxFile, null, analysis); @@ -121,6 +139,7 @@ public ResponseEntity getGpx(@RequestParam String code) throws IOException { return ResponseEntity.ok(gsonWithNans.toJson(Map.of("gpx_data", gpxData))); } } + LOGGER.error("Error getting gpx data: " + gpxFile.getError()); return ResponseEntity.badRequest().body("Error getting gpx data"); } @@ -132,12 +151,7 @@ public ResponseEntity editBlacklist(@RequestBody List list, if (dev == null) { return userdataService.tokenNotValidResponse(); } - PremiumUserFilesRepository.UserFile userFile = userdataService.getUserFile(name, type, null, dev); - if (userFile == null) { - return ResponseEntity.badRequest().body(FILE_NOT_FOUND); - } - boolean created = shareGpxService.editBlacklist(userFile, list); - if (!created) { + if (!shareFileService.editBlacklist(dev, name, type, list)) { return ResponseEntity.badRequest().body("Error editing blacklist"); } return ResponseEntity.ok("Blacklist edited"); @@ -155,8 +169,7 @@ public ResponseEntity editWhitelist(@RequestBody List list, if (userFile == null) { return ResponseEntity.badRequest().body(FILE_NOT_FOUND); } - boolean created = shareGpxService.editWhitelist(userFile, list); - if (!created) { + if (!shareFileService.editWhitelist(dev, name, type, list)) { return ResponseEntity.badRequest().body("Error editing whitelist"); } return ResponseEntity.ok("Whitelist edited"); @@ -169,11 +182,7 @@ public ResponseEntity getSharedInfo(@RequestParam String name, if (dev == null) { return userdataService.tokenNotValidResponse(); } - PremiumUserFilesRepository.UserFile userFile = userdataService.getUserFile(name, type, null, dev); - if (userFile == null) { - return ResponseEntity.badRequest().body(FILE_NOT_FOUND); - } - ShareGpxService.FileSharedInfo info = shareGpxService.getSharedInfo(userFile.id); + ShareFileService.FileSharedInfo info = shareFileService.getSharedInfo(dev, name, type); if (info == null) { return ResponseEntity.badRequest().body(ERROR_GETTING_SHARED_INFO); } @@ -186,14 +195,14 @@ public ResponseEntity checkAccess(@RequestParam String code) { if (dev == null) { return userdataService.tokenNotValidResponse(); } - ShareGpxService.FileSharedInfo info = shareGpxService.getSharedInfo(code); + ShareFileService.FileSharedInfo info = shareFileService.getSharedInfo(code); if (info == null) { return ResponseEntity.badRequest().body(ERROR_GETTING_SHARED_INFO); } - ShareGpxService.FileSharedInfo.SharingType type = info.getSharingType(); - Set groupActions = info.getGroupActions(); + ShareFileService.FileSharedInfo.SharingType type = info.getSharingType(); + Set groupActions = info.getGroupActions(); - return ResponseEntity.ok(gson.toJson(Map.of("hasAccess", shareGpxService.checkAccess(info), "type", type, "groupActions", groupActions))); + return ResponseEntity.ok(gson.toJson(Map.of("hasAccess", shareFileService.checkAccess(info), "type", type, "groupActions", groupActions))); } } From bcf7abe58bdf327ac6656910d5afeae1ec2fafd6 Mon Sep 17 00:00:00 2001 From: alisa911 Date: Mon, 2 Dec 2024 13:46:33 +0200 Subject: [PATCH 10/23] refactoring --- .../net/osmand/server/api/services/ShareFileService.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java index afb2c206a..484a37fb5 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java @@ -264,10 +264,11 @@ private JsonObject addSharedInfo(String type, @Nullable String groupActions) { } private String normalizeCode(String code) { + String normalizedCode = code; if (code.contains(CODE_SEPARATOR)) { - code = code.substring(0, code.indexOf(CODE_SEPARATOR)); + normalizedCode = code.substring(0, code.indexOf(CODE_SEPARATOR)); } - return code; + return normalizedCode; } @Data From 20d7d02523e254ce8b89da664454d1c2091723ee Mon Sep 17 00:00:00 2001 From: alisa911 Date: Wed, 11 Dec 2024 13:58:26 +0200 Subject: [PATCH 11/23] change share api --- db/sql_changeset_schema | 30 +- .../server/api/repo/ShareFileRepository.java | 132 ++++-- .../server/api/services/ShareFileService.java | 394 +++++------------- .../controllers/user/ShareFileController.java | 156 +++---- 4 files changed, 311 insertions(+), 401 deletions(-) diff --git a/db/sql_changeset_schema b/db/sql_changeset_schema index 260c142a0..528081148 100644 --- a/db/sql_changeset_schema +++ b/db/sql_changeset_schema @@ -40,10 +40,32 @@ CREATE INDEX user_files_userid_idx on user_files(userid); CREATE INDEX user_files_deviceid_idx on user_files(deviceid); -- share file -CREATE TABLE share_files(code varchar NOT NULL primary key, name text NOT NULL, type text NOT NULL, userid integer NOT NULL, info jsonb); -CREATE INDEX share_files_userid_idx ON share_files(userid); -CREATE INDEX share_files_name_idx ON share_files(name); -CREATE INDEX share_files_type_idx ON share_files(type); +CREATE TABLE user_share_files ( + file_id SERIAL PRIMARY KEY, + ownerid INTEGER NOT NULL, + uuid TEXT UNIQUE, + filepath TEXT NOT NULL, + name TEXT NOT NULL, + type TEXT NOT NULL, + public_access BOOLEAN NOT NULL +); + +CREATE TABLE user_share_files_access ( + id SERIAL PRIMARY KEY, + user_id INTEGER NOT NULL, + access TEXT NOT NULL, + date TIMESTAMP NOT NULL, + file_id INTEGER NOT NULL, + CONSTRAINT fk_user_share_files_access_user FOREIGN KEY (user_id) REFERENCES user_accounts(id) ON DELETE CASCADE, + CONSTRAINT fk_user_share_files_access_file FOREIGN KEY (file_id) REFERENCES user_share_files(file_id) ON DELETE CASCADE +); + +CREATE INDEX user_share_files_ownerid_idx ON user_share_files(ownerid); +CREATE INDEX user_share_files_uuid_idx ON user_share_files(uuid); +CREATE INDEX user_share_files_filepath_idx ON user_share_files(filepath); + +CREATE INDEX user_share_files_access_userid_idx ON user_share_files_access(user_id); +CREATE INDEX user_share_files_access_file_id_idx ON user_share_files_access(file_id); -- mail group CREATE TABLE email_free_users(aid text, email text, os text, updatetime timestamp); diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/ShareFileRepository.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/ShareFileRepository.java index c89ea94bd..1996ae65f 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/ShareFileRepository.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/ShareFileRepository.java @@ -1,71 +1,147 @@ package net.osmand.server.api.repo; -import com.google.gson.Gson; -import com.google.gson.JsonObject; import lombok.Getter; import lombok.Setter; -import org.hibernate.annotations.Type; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import org.springframework.data.jpa.repository.Query; import javax.persistence.*; -import java.io.IOException; import java.io.Serial; import java.io.Serializable; +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; @Repository public interface ShareFileRepository extends JpaRepository { - ShareFile findByCode(String code); + ShareFile findByUuid(String uuid); - @Query("SELECT sf.info FROM ShareFile sf WHERE sf.code = :code") - JsonObject findInfoByCode(String code); + ShareFile findByOwneridAndFilepath(int ownerid, String filepath); - ShareFile findByUseridAndNameAndType(int userid, String name, String type); + @Query("SELECT a FROM ShareFilesAccess a WHERE a.id = :id") + ShareFilesAccess findShareFilesAccessById(@Param("id") long id); + S saveAndFlush(S entity); @Setter @Getter @Entity(name = "ShareFile") - @Table(name = "share_files") + @Table(name = "user_share_files") class ShareFile implements Serializable { - private static final Gson gson = new Gson(); @Serial private static final long serialVersionUID = 1L; @Id - @Column(nullable = false, unique = true) - private String code; + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "file_id") + public long id; - @Column(name = "name", nullable = false) + @Column(nullable = false) + public int ownerid; + + @Column(unique = true) + private String uuid; + + @Column(nullable = false) + public String filepath; + + @Column(nullable = false) public String name; - @Column(name = "type", nullable = false) + @Column(nullable = false) public String type; - @Column(name = "userid", nullable = false) - public int userid; + @Column(nullable = false) + public boolean publicAccess; - @Column(name = "info", columnDefinition = "jsonb") - @Type(type = "net.osmand.server.assist.data.JsonbType") - private JsonObject info; + @OneToMany(mappedBy = "file", cascade = CascadeType.ALL, orphanRemoval = true) + private List accessRecords; - @Serial - private void writeObject(java.io.ObjectOutputStream out) throws IOException { - out.defaultWriteObject(); - out.writeObject(info != null ? gson.toJson(info) : null); + public void addAccessRecord(ShareFilesAccess access) { + accessRecords.add(access); + access.setFile(this); } + } + + @Setter + @Getter + @Entity(name = "ShareFilesAccess") + @Table(name = "user_share_files_access") + class ShareFilesAccess implements Serializable { @Serial - private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - String json = (String) in.readObject(); - if (json != null) { - this.info = gson.fromJson(json, JsonObject.class); + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @ManyToOne + @JoinColumn(name = "user_id", nullable = false) + private PremiumUsersRepository.PremiumUser user; + + @Column(nullable = false) + private String access; + + @Column(name = "date") + @Temporal(TemporalType.TIMESTAMP) + public Date requestDate; + + @ManyToOne + @JoinColumn(name = "file_id", nullable = false) + private ShareFile file; + } + + @Getter + @Setter + public class ShareFileDTO { + + private long id; + private int ownerid; + private String uuid; + private String filepath; + private String name; + private String type; + private boolean publicAccess; + private List accessRecords; + + public ShareFileDTO(ShareFile shareFile, boolean includeAccessRecords) { + this.id = shareFile.getId(); + this.ownerid = shareFile.getOwnerid(); + this.uuid = shareFile.getUuid(); + this.filepath = shareFile.getFilepath(); + this.name = shareFile.getName(); + this.type = shareFile.getType(); + this.publicAccess = shareFile.isPublicAccess(); + if (includeAccessRecords && shareFile.getAccessRecords() != null) { + this.accessRecords = shareFile.getAccessRecords().stream() + .map((ShareFilesAccess access) -> new ShareFilesAccessDTO(access, false)) + .collect(Collectors.toList()); } } + } + @Getter + @Setter + class ShareFilesAccessDTO { + + private long id; + private String name; + private String access; + private Date requestDate; + + public ShareFilesAccessDTO(ShareFilesAccess access, boolean includeFile) { + this.id = access.getId(); + this.name = access.getUser().email; + this.access = access.getAccess(); + this.requestDate = access.getRequestDate(); + if (includeFile) { + this.id = access.getFile().getId(); + } + } } } \ No newline at end of file diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java index 484a37fb5..c03bcc1d7 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java @@ -1,9 +1,5 @@ package net.osmand.server.api.services; -import com.google.gson.Gson; -import com.google.gson.JsonObject; -import lombok.Data; -import lombok.Getter; import net.osmand.server.api.repo.*; import net.osmand.server.controllers.user.ShareFileController; import net.osmand.shared.gpx.GpxFile; @@ -13,11 +9,10 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; -import javax.annotation.Nullable; import javax.transaction.Transactional; -import javax.validation.constraints.NotNull; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @@ -38,91 +33,103 @@ public class ShareFileService { @Autowired protected ShareFileRepository shareFileRepository; - @Autowired - protected PremiumUsersRepository usersRepository; - @Autowired UserdataService userdataService; protected static final Log LOGGER = LogFactory.getLog(ShareFileService.class); - private static final String SEPARATOR = ","; - private static final String CODE_SEPARATOR = "-"; - public static final int CODE_LENGTH = 8; - - Gson gson = new Gson(); + public enum PermissionType { + READ, + PENDING, + BLOCKED + } @Transactional - public String generateSharedCode(PremiumUserFilesRepository.UserFile userFile, String type, @Nullable String groupActions) { - String uniqueCode = UUID.randomUUID().toString().substring(0, CODE_LENGTH); - ShareFileRepository.ShareFile file = shareFileRepository.findByCode(uniqueCode); + public String generateSharedCode(PremiumUserFilesRepository.UserFile userFile, boolean publicAccess) { + String uniqueCode = UUID.randomUUID().toString(); + ShareFileRepository.ShareFile file = shareFileRepository.findByUuid(uniqueCode); while (file != null) { - uniqueCode = UUID.randomUUID().toString().substring(0, CODE_LENGTH); - file = shareFileRepository.findByCode(uniqueCode); + uniqueCode = UUID.randomUUID().toString(); + file = shareFileRepository.findByUuid(uniqueCode); } - ShareFileRepository.ShareFile shareFile = new ShareFileRepository.ShareFile(); - shareFile.setCode(uniqueCode); - shareFile.setName(userFile.name); - shareFile.setType(userFile.type); - shareFile.setUserid(userFile.userid); - shareFile.setInfo(addSharedInfo(type, groupActions)); - ShareFileRepository.ShareFile existingFile = shareFileRepository.findByUseridAndNameAndType(userFile.userid, userFile.name, userFile.type); + // Update existing file with new code + ShareFileRepository.ShareFile existingFile = getFileByOwnerAndFilepath(userFile.userid, userFile.name); if (existingFile != null) { - shareFileRepository.delete(existingFile); + existingFile.setUuid(uniqueCode); + shareFileRepository.saveAndFlush(existingFile); + return uniqueCode; } - shareFileRepository.saveAndFlush(shareFile); + createShareFile(userFile, publicAccess, uniqueCode); return uniqueCode; } - public String getNamePartForCode(String filename) { - final String DOT = "."; - String prefix = filename.substring(0, CODE_LENGTH); - String suffix = filename.contains(DOT) ? filename.substring(filename.lastIndexOf(DOT) - 1) : ""; - return prefix + suffix; + @Transactional + public ShareFileRepository.ShareFile createShareFile(PremiumUserFilesRepository.UserFile userFile, boolean publicAccess, String uniqueCode) { + ShareFileRepository.ShareFile shareFile = new ShareFileRepository.ShareFile(); + + String name = (userFile.name.lastIndexOf("/") >= 0) + ? userFile.name.substring(userFile.name.lastIndexOf("/") + 1) + : userFile.name; + + shareFile.setUuid(uniqueCode); + shareFile.setName(name); + shareFile.setFilepath(userFile.name); + shareFile.setType(userFile.type); + shareFile.setOwnerid(userFile.userid); + shareFile.setPublicAccess(publicAccess); + + shareFileRepository.saveAndFlush(shareFile); + return shareFile; } - public FileSharedInfo getSharedInfo(JsonObject sharedInfoJson) { - if (sharedInfoJson != null) { - return gson.fromJson(sharedInfoJson, FileSharedInfo.class); - } - return null; + public ShareFileRepository.ShareFile getFileByOwnerAndFilepath(int ownerid, String filepath) { + return shareFileRepository.findByOwneridAndFilepath(ownerid, filepath); } - public FileSharedInfo getSharedInfo(@NotNull PremiumUserDevicesRepository.PremiumUserDevice dev, @NotNull String name, @NotNull String type) { - ShareFileRepository.ShareFile file = shareFileRepository.findByUseridAndNameAndType(dev.userid, name, type); - if (file != null) { - return getSharedInfo(file.getInfo()); - } - return null; + public ShareFileRepository.ShareFile getFileById(long id) { + return shareFileRepository.findById(id).orElse(null); } - public FileSharedInfo getSharedInfo(String code) { - code = normalizeCode(code); - JsonObject sharedInfoJson = shareFileRepository.findInfoByCode(code); - if (sharedInfoJson != null) { - return gson.fromJson(sharedInfoJson, FileSharedInfo.class); - } - return null; + @Transactional + public boolean editAccessList(ShareFileRepository.ShareFile shareFile, Map accessMap) { + List accessList = shareFile.getAccessRecords(); + if (accessList == null) { + accessList = new ArrayList<>(); + } + accessList.forEach(access -> { + String accessType = accessMap.get(access.getUser().id); + if (accessType != null) { + access.setAccess(accessType); + } + }); + shareFile.setAccessRecords(accessList); + shareFileRepository.saveAndFlush(shareFile); + return true; } - public boolean checkAccess(FileSharedInfo info) { - FileSharedInfo.SharingType sharingType = info.getSharingType(); - if (sharingType == FileSharedInfo.SharingType.PUBLIC) { - return true; - } - PremiumUserDevicesRepository.PremiumUserDevice dev = userdataService.checkUser(); - if (dev == null) { - return false; - } - if (sharingType == FileSharedInfo.SharingType.PUBLIC_BY_LOGIN) { - return true; - } - if (sharingType == FileSharedInfo.SharingType.GROUP_BY_LOGIN) { - return !info.getBlacklist().getUsers().contains(dev.userid); + public ResponseEntity checkAccess(ShareFileRepository.ShareFile file) { + if (file.isPublicAccess()) { + return null; + } else { + PremiumUserDevicesRepository.PremiumUserDevice dev = userdataService.checkUser(); + if (dev == null) { + return userdataService.tokenNotValidResponse(); + } + List accessList = file.getAccessRecords(); + for (ShareFileRepository.ShareFilesAccess access : accessList) { + if (access.getUser().id == dev.userid) { + if (access.getAccess().equals(ShareFileService.PermissionType.PENDING.name())) { + return ResponseEntity.ok().body(ShareFileService.PermissionType.PENDING.name()); + } else if (access.getAccess().equals(ShareFileService.PermissionType.BLOCKED.name())) { + return ResponseEntity.ok().body(ShareFileService.PermissionType.BLOCKED.name()); + } + return null; + } + } } - return false; + return ResponseEntity.ok().body("request"); } public ShareFileController.FileDownloadResult downloadFile(PremiumUserFilesRepository.UserFile userFile) throws IOException { @@ -139,16 +146,28 @@ public ShareFileController.FileDownloadResult downloadFile(PremiumUserFilesRepos return null; } - public ShareFileRepository.ShareFile getShareFileByCode(String code) { - if (code == null) { + public ShareFileRepository.ShareFile getShareFileByUuid(String uuid) { + if (uuid == null) { return null; } - code = normalizeCode(code); - return shareFileRepository.findByCode(code); + return shareFileRepository.findByUuid(uuid); + } + + @Transactional + public void requestAccess(ShareFileRepository.ShareFile shareFile, PremiumUserDevicesRepository.PremiumUserDevice dev) { + ShareFileRepository.ShareFilesAccess access = new ShareFileRepository.ShareFilesAccess(); + PremiumUsersRepository.PremiumUser user = userdataService.getUserById(dev.userid); + access.setUser(user); + access.setAccess(ShareFileService.PermissionType.PENDING.name()); + access.setRequestDate(new Date()); + + shareFile.addAccessRecord(access); + + shareFileRepository.saveAndFlush(shareFile); } public PremiumUserFilesRepository.UserFile getUserFile(ShareFileRepository.ShareFile file) { - return filesRepository.findTopByUseridAndNameAndTypeOrderByUpdatetimeDesc(file.userid, file.name, file.type); + return filesRepository.findTopByUseridAndNameAndTypeOrderByUpdatetimeDesc(file.ownerid, file.name, file.type); } public GpxFile getFile(PremiumUserFilesRepository.UserFile file) throws IOException { @@ -167,230 +186,23 @@ public GpxFile getFile(PremiumUserFilesRepository.UserFile file) throws IOExcept } @Transactional - public void storeUserAccess(PremiumUserDevicesRepository.PremiumUserDevice dev, - ShareFileRepository.ShareFile shareFile, - FileSharedInfo info) { - if (info == null) { - info = new FileSharedInfo(); - } - Map users = info.getUsersAccessInfo().getUsers(); - if (!users.containsKey(dev.userid)) { - users.put(dev.userid, System.currentTimeMillis()); - info.getUsersAccessInfo().setUsers(users); - shareFile.setInfo(gson.toJsonTree(info).getAsJsonObject()); - shareFileRepository.saveAndFlush(shareFile); - } - } - - @Transactional - public boolean editBlacklist(@NotNull PremiumUserDevicesRepository.PremiumUserDevice dev, - @NotNull String name, - @NotNull String type, - List list) { - if (dev == null || name == null || type == null) { - return false; - } - ShareFileRepository.ShareFile file = shareFileRepository.findByUseridAndNameAndType(dev.userid, name, type); - if (file == null) { - return false; - } - FileSharedInfo fileSharedInfo = gson.fromJson(file.getInfo(), FileSharedInfo.class); - if (fileSharedInfo == null) { - fileSharedInfo = new FileSharedInfo(); - } - FileSharedInfo.Blacklist blist = fileSharedInfo.getBlacklist(); - blist.getUsers().addAll(list.stream().map(email -> { - PremiumUsersRepository.PremiumUser pu = usersRepository.findByEmailIgnoreCase(email); - return pu != null ? pu.id : null; - }).filter(Objects::nonNull).toList()); - if (blist.getUsers().isEmpty()) { - return false; - } - fileSharedInfo.setBlacklist(blist); - file.setInfo(gson.toJsonTree(fileSharedInfo).getAsJsonObject()); - shareFileRepository.saveAndFlush(file); - return true; - } - - @Transactional - public boolean editWhitelist(@NotNull PremiumUserDevicesRepository.PremiumUserDevice dev, - @NotNull String name, - @NotNull String type, - List list) { - if (dev == null || name == null || type == null) { - return false; - } - ShareFileRepository.ShareFile file = shareFileRepository.findByUseridAndNameAndType(dev.userid, name, type); - if (file == null) { - return false; - } - FileSharedInfo fileSharedInfo = gson.fromJson(file.getInfo(), FileSharedInfo.class); - if (fileSharedInfo == null) { - fileSharedInfo = new FileSharedInfo(); - } - FileSharedInfo.Whitelist wlist = fileSharedInfo.getWhitelist(); - - wlist.getPermissions().putAll(list.stream().collect(HashMap::new, (m, email) -> { - PremiumUsersRepository.PremiumUser pu = usersRepository.findByEmailIgnoreCase(email); - if (pu != null) { - m.put(pu.id, new FileSharedInfo.Permission(FileSharedInfo.PermissionType.READ)); + public boolean updateRequests(Map accessMap) { + for (Map.Entry entry : accessMap.entrySet()) { + Integer id = entry.getKey(); + String accessType = entry.getValue(); + if (accessType.equals("approved")) { + accessType = ShareFileService.PermissionType.READ.name(); } - }, HashMap::putAll)); - if (wlist.getPermissions().isEmpty()) { - return false; - } - fileSharedInfo.setWhitelist(wlist); - file.setInfo(gson.toJsonTree(fileSharedInfo).getAsJsonObject()); - shareFileRepository.saveAndFlush(file); - return true; - } - - private JsonObject addSharedInfo(String type, @Nullable String groupActions) { - FileSharedInfo fileSharedInfo = new FileSharedInfo(); - FileSharedInfo.SharingType sharingType = FileSharedInfo.SharingType.fromType(type); - fileSharedInfo.setSharingType(Objects.requireNonNullElse(sharingType, FileSharedInfo.SharingType.PUBLIC)); - - if (sharingType == FileSharedInfo.SharingType.GROUP_BY_LOGIN && groupActions != null) { - String[] actions = groupActions.split(SEPARATOR); - for (String action : actions) { - FileSharedInfo.GroupAction groupAction = FileSharedInfo.GroupAction.fromAction(action.trim()); - if (groupAction != null) { - fileSharedInfo.getGroupActions().add(groupAction); - } - } - } - - return gson.toJsonTree(fileSharedInfo).getAsJsonObject(); - } - - private String normalizeCode(String code) { - String normalizedCode = code; - if (code.contains(CODE_SEPARATOR)) { - normalizedCode = code.substring(0, code.indexOf(CODE_SEPARATOR)); - } - return normalizedCode; - } - - @Data - public static class FileSharedInfo { - private Whitelist whitelist; - private Blacklist blacklist; - private UsersAccessInfo usersAccessInfo; - private SharingType sharingType; - private Set groupActions; - - public FileSharedInfo() { - this(new Whitelist(), new Blacklist(), new UsersAccessInfo(), SharingType.PUBLIC, new HashSet<>()); - } - - public FileSharedInfo(Whitelist whitelist, Blacklist blacklist, UsersAccessInfo usersAccessInfo, SharingType sharingType, Set groupActions) { - this.whitelist = whitelist; - this.blacklist = blacklist; - this.usersAccessInfo = usersAccessInfo; - this.sharingType = sharingType != null ? sharingType : SharingType.PUBLIC; - this.groupActions = groupActions != null ? groupActions : new HashSet<>(); - } - - @Getter - public enum SharingType { - PUBLIC("public"), // Anyone can access - PUBLIC_BY_LOGIN("publicByLogin"), // Public, but login required, owner doesn't see accessed users - GROUP_BY_LOGIN("groupByLogin"); // Group access with login, owner sees accessed users - - private final String type; - - SharingType(String type) { - this.type = type; - } - - public static SharingType fromType(String type) { - for (SharingType sharingType : values()) { - if (sharingType.getType().equalsIgnoreCase(type)) { - return sharingType; - } - } - return null; - } - } - - @Getter - public enum GroupAction { - EDIT_WHITELIST("editWhitelist"), - EDIT_BLACKLIST("editBlacklist"), - VIEW_USERS_ACCESS("viewUsersAccess"); - - private final String action; - - GroupAction(String action) { - this.action = action; - } - - public static GroupAction fromAction(String action) { - for (GroupAction groupAction : values()) { - if (groupAction.getAction().equalsIgnoreCase(action)) { - return groupAction; - } - } - return null; - } - } - - public enum PermissionType { - READ, - WRITE, - } - - @Data - public static class Whitelist { - private Map permissions; - - public Whitelist() { - this(new HashMap<>()); + if (accessType.equals(ShareFileService.PermissionType.BLOCKED.name().toLowerCase())) { + accessType = ShareFileService.PermissionType.BLOCKED.name(); } - - public Whitelist(Map permissions) { - this.permissions = permissions != null ? permissions : new HashMap<>(); + ShareFileRepository.ShareFilesAccess access = shareFileRepository.findShareFilesAccessById(id); + if (access != null) { + access.setAccess(accessType); + shareFileRepository.saveAndFlush(access); } - - } - - @Data - public static class Permission { - private PermissionType permissionType; - - public Permission(PermissionType permissionType) { - this.permissionType = permissionType; - } - - } - - @Data - public static class Blacklist { - private Set users; - - public Blacklist() { - this(new HashSet<>()); - } - - public Blacklist(Set users) { - this.users = users != null ? users : new HashSet<>(); - } - - } - - @Data - public static class UsersAccessInfo { - private Map users; - - public UsersAccessInfo() { - this(new HashMap<>()); - } - - public UsersAccessInfo(Map users) { - this.users = users != null ? users : new HashMap<>(); - } - } + return true; } } diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java index b5628af4f..ca4f3dc59 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java @@ -4,6 +4,7 @@ import com.google.gson.GsonBuilder; import net.osmand.server.api.repo.PremiumUserDevicesRepository; import net.osmand.server.api.repo.PremiumUserFilesRepository; +import net.osmand.server.api.repo.PremiumUsersRepository; import net.osmand.server.api.repo.ShareFileRepository; import net.osmand.server.api.services.GpxService; import net.osmand.server.api.services.UserdataService; @@ -22,9 +23,7 @@ import javax.transaction.Transactional; import java.io.IOException; import java.io.InputStream; -import java.util.List; import java.util.Map; -import java.util.Set; import static net.osmand.server.api.services.UserdataService.FILE_NOT_FOUND; @@ -47,16 +46,13 @@ public class ShareFileController { protected static final Log LOGGER = LogFactory.getLog(ShareFileController.class); - private static final String ERROR_GETTING_SHARED_INFO = "Error getting shared info"; - public record FileDownloadResult(InputStream inputStream, String fileName, String contentType) { } @PostMapping(path = {"/generate-link"}, produces = "application/json") public ResponseEntity generateLink(@RequestParam String fileName, @RequestParam String fileType, - @RequestParam String type, - @RequestParam(required = false) String groupActions) { + @RequestParam Boolean publicAccess) { PremiumUserDevicesRepository.PremiumUserDevice dev = userdataService.checkUser(); if (dev == null) { return userdataService.tokenNotValidResponse(); @@ -65,39 +61,25 @@ public ResponseEntity generateLink(@RequestParam String fileName, if (userFile == null) { return ResponseEntity.badRequest().body(FILE_NOT_FOUND); } - String code = shareFileService.generateSharedCode(userFile, type, groupActions); - if (code == null) { + String uuid = shareFileService.generateSharedCode(userFile, publicAccess); + if (uuid == null) { return ResponseEntity.badRequest().body("Error generating link"); } - String name = shareFileService.getNamePartForCode(fileName); - String url = "/share/get?code=" + code + "-" + name; - - return ResponseEntity.ok(gson.toJson(Map.of("url", url))); + return ResponseEntity.ok(gson.toJson(Map.of("uuid", uuid))); } - @PostMapping(path = {"/get"}, produces = "application/json") + @GetMapping(path = {"/get/{uuid}"}, produces = "application/json") @Transactional - public ResponseEntity getFile(@RequestParam String code) throws IOException { - ShareFileRepository.ShareFile shareFile = shareFileService.getShareFileByCode(code); + public ResponseEntity getFile(@PathVariable String uuid) throws IOException { + ShareFileRepository.ShareFile shareFile = shareFileService.getShareFileByUuid(uuid); if (shareFile == null) { return ResponseEntity.badRequest().body(FILE_NOT_FOUND); } - PremiumUserFilesRepository.UserFile userFile = shareFileService.getUserFile(shareFile); - if (userFile == null) { - return ResponseEntity.badRequest().body(FILE_NOT_FOUND); - } - ShareFileService.FileSharedInfo info = shareFileService.getSharedInfo(shareFile.getInfo()); - if (info == null) { - return ResponseEntity.badRequest().body(ERROR_GETTING_SHARED_INFO); - } - boolean hasAccess = shareFileService.checkAccess(info); - if (!hasAccess) { - return ResponseEntity.badRequest().body("You don't have access to this file"); - } - PremiumUserDevicesRepository.PremiumUserDevice dev = userdataService.checkUser(); - if (dev != null) { - shareFileService.storeUserAccess(dev, shareFile, info); + ResponseEntity errorAccess = shareFileService.checkAccess(shareFile); + if (errorAccess != null) { + return errorAccess; } + PremiumUserFilesRepository.UserFile userFile = shareFileService.getUserFile(shareFile); FileDownloadResult fileResult = shareFileService.downloadFile(userFile); if (fileResult == null) { return ResponseEntity.badRequest().body(FILE_NOT_FOUND); @@ -108,29 +90,23 @@ public ResponseEntity getFile(@RequestParam String code) throws IOException { .body(new InputStreamResource(fileResult.inputStream)); } - @PostMapping(path = {"/get-gpx"}, produces = "application/json") + @GetMapping(path = {"/join/{uuid}"}, produces = "application/json") @Transactional - public ResponseEntity getGpx(@RequestParam String code) throws IOException { - ShareFileRepository.ShareFile shareFile = shareFileService.getShareFileByCode(code); + public ResponseEntity getGpx(@PathVariable String uuid) throws IOException { + ShareFileRepository.ShareFile shareFile = shareFileService.getShareFileByUuid(uuid); if (shareFile == null) { return ResponseEntity.badRequest().body(FILE_NOT_FOUND); } + ResponseEntity errorAccess = shareFileService.checkAccess(shareFile); + if (errorAccess != null) { + return errorAccess; + } PremiumUserFilesRepository.UserFile userFile = shareFileService.getUserFile(shareFile); - if (userFile == null) { + FileDownloadResult fileResult = shareFileService.downloadFile(userFile); + if (fileResult == null) { return ResponseEntity.badRequest().body(FILE_NOT_FOUND); } - ShareFileService.FileSharedInfo info = shareFileService.getSharedInfo(shareFile.getInfo()); - if (info == null) { - return ResponseEntity.badRequest().body(ERROR_GETTING_SHARED_INFO); - } - boolean hasAccess = shareFileService.checkAccess(info); - if (!hasAccess) { - return ResponseEntity.badRequest().body("File is private"); - } - PremiumUserDevicesRepository.PremiumUserDevice dev = userdataService.checkUser(); - if (dev != null) { - shareFileService.storeUserAccess(dev, shareFile, info); - } + GpxFile gpxFile = shareFileService.getFile(userFile); if (gpxFile.getError() == null) { GpxTrackAnalysis analysis = gpxFile.getAnalysis(System.currentTimeMillis()); @@ -143,66 +119,90 @@ public ResponseEntity getGpx(@RequestParam String code) throws IOException { return ResponseEntity.badRequest().body("Error getting gpx data"); } - @PostMapping(path = {"/edit-blacklist"}, produces = "application/json") - public ResponseEntity editBlacklist(@RequestBody List list, - @RequestParam String name, - @RequestParam String type) { + @GetMapping(path = {"/request-access"}, produces = "application/json") + @Transactional + public ResponseEntity requestAccess(@RequestParam String uuid) { PremiumUserDevicesRepository.PremiumUserDevice dev = userdataService.checkUser(); if (dev == null) { return userdataService.tokenNotValidResponse(); } - if (!shareFileService.editBlacklist(dev, name, type, list)) { - return ResponseEntity.badRequest().body("Error editing blacklist"); + ShareFileRepository.ShareFile shareFile = shareFileService.getShareFileByUuid(uuid); + if (shareFile == null) { + return ResponseEntity.badRequest().body(FILE_NOT_FOUND); } - return ResponseEntity.ok("Blacklist edited"); + if (dev.userid == shareFile.ownerid) { + return ResponseEntity.badRequest().body("You are the owner of this file"); + } + shareFileService.requestAccess(shareFile, dev); + return ResponseEntity.ok("Access requested"); } - @GetMapping(path = {"/edit-whitelist"}, produces = "application/json") - public ResponseEntity editWhitelist(@RequestBody List list, - @RequestParam String name, - @RequestParam String type) { + @GetMapping(path = {"/get-share-file-info"}, produces = "application/json") + public ResponseEntity getFileInfo(@RequestParam String fileName, + @RequestParam String fileType, + @RequestParam boolean createIfNotExists) { PremiumUserDevicesRepository.PremiumUserDevice dev = userdataService.checkUser(); if (dev == null) { return userdataService.tokenNotValidResponse(); } - PremiumUserFilesRepository.UserFile userFile = userdataService.getUserFile(name, type, null, dev); - if (userFile == null) { - return ResponseEntity.badRequest().body(FILE_NOT_FOUND); + ShareFileRepository.ShareFile shareFile = shareFileService.getFileByOwnerAndFilepath(dev.userid, fileName); + if (shareFile == null) { + if (createIfNotExists) { + PremiumUserFilesRepository.UserFile userFile = userdataService.getUserFile(fileName, fileType, null, dev); + shareFile = shareFileService.createShareFile(userFile, false, null); + } else { + return ResponseEntity.badRequest().body(FILE_NOT_FOUND); + } } - if (!shareFileService.editWhitelist(dev, name, type, list)) { - return ResponseEntity.badRequest().body("Error editing whitelist"); + PremiumUsersRepository.PremiumUser user = userdataService.getUserById(shareFile.ownerid); + if (user == null) { + return ResponseEntity.badRequest().body("Error getting user info"); } - return ResponseEntity.ok("Whitelist edited"); + String ownerName = user.email; + ShareFileRepository.ShareFileDTO shareFileDto = new ShareFileRepository.ShareFileDTO(shareFile, true); + return ResponseEntity.ok(gson.toJson(Map.of("owner", ownerName, "file", shareFileDto))); } - @GetMapping(path = {"/get-shared-info"}, produces = "application/json") - public ResponseEntity getSharedInfo(@RequestParam String name, - @RequestParam String type) { + @GetMapping(path = {"/edit-access-list"}, produces = "application/json") + public ResponseEntity editWhitelist(@RequestBody Map accessMap, + @RequestParam String fileName) { PremiumUserDevicesRepository.PremiumUserDevice dev = userdataService.checkUser(); if (dev == null) { return userdataService.tokenNotValidResponse(); } - ShareFileService.FileSharedInfo info = shareFileService.getSharedInfo(dev, name, type); - if (info == null) { - return ResponseEntity.badRequest().body(ERROR_GETTING_SHARED_INFO); + ShareFileRepository.ShareFile shareFile = shareFileService.getFileByOwnerAndFilepath(dev.userid, fileName); + if (shareFile == null) { + return ResponseEntity.badRequest().body(FILE_NOT_FOUND); + } + boolean success = shareFileService.editAccessList(shareFile, accessMap); + if (!success) { + return ResponseEntity.badRequest().body("Error editing access list"); } - return ResponseEntity.ok(gson.toJson(info)); + return ResponseEntity.ok("Access list edited"); } - @GetMapping(path = {"/check-access"}, produces = "application/json") - public ResponseEntity checkAccess(@RequestParam String code) { + @PostMapping(path = {"/update-requests"}, produces = "application/json") + public ResponseEntity updateRequests(@RequestBody Map requests, + @RequestParam long fileId) { PremiumUserDevicesRepository.PremiumUserDevice dev = userdataService.checkUser(); if (dev == null) { return userdataService.tokenNotValidResponse(); } - ShareFileService.FileSharedInfo info = shareFileService.getSharedInfo(code); - if (info == null) { - return ResponseEntity.badRequest().body(ERROR_GETTING_SHARED_INFO); + ShareFileRepository.ShareFile shareFile = shareFileService.getFileById(fileId); + if (shareFile == null) { + return ResponseEntity.badRequest().body(FILE_NOT_FOUND); + } + if (shareFile.ownerid != dev.userid) { + return ResponseEntity.badRequest().body("You are not the owner of this file"); + } + boolean success = shareFileService.updateRequests(requests); + if (!success) { + return ResponseEntity.badRequest().body("Error updating requests"); } - ShareFileService.FileSharedInfo.SharingType type = info.getSharingType(); - Set groupActions = info.getGroupActions(); + shareFile = shareFileService.getFileById(fileId); + ShareFileRepository.ShareFileDTO shareFileDto = new ShareFileRepository.ShareFileDTO(shareFile, true); - return ResponseEntity.ok(gson.toJson(Map.of("hasAccess", shareFileService.checkAccess(info), "type", type, "groupActions", groupActions))); + return ResponseEntity.ok(gson.toJson(shareFileDto)); } } From a14d5da48e5df20043ab66c5970f687edd653b1d Mon Sep 17 00:00:00 2001 From: alisa911 Date: Wed, 11 Dec 2024 18:17:34 +0200 Subject: [PATCH 12/23] open share file for owner --- .../net/osmand/server/api/services/ShareFileService.java | 3 +++ .../osmand/server/controllers/user/ShareFileController.java | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java index c03bcc1d7..cbdddbf68 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java @@ -117,6 +117,9 @@ public ResponseEntity checkAccess(ShareFileRepository.ShareFile file) { if (dev == null) { return userdataService.tokenNotValidResponse(); } + if (file.ownerid == dev.userid) { + return null; + } List accessList = file.getAccessRecords(); for (ShareFileRepository.ShareFilesAccess access : accessList) { if (access.getUser().id == dev.userid) { diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java index ca4f3dc59..d8aa96882 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java @@ -112,7 +112,11 @@ public ResponseEntity getGpx(@PathVariable String uuid) throws IOException { GpxTrackAnalysis analysis = gpxFile.getAnalysis(System.currentTimeMillis()); WebGpxParser.TrackData gpxData = gpxService.getTrackDataByGpxFile(gpxFile, null, analysis); if (gpxData != null) { - return ResponseEntity.ok(gsonWithNans.toJson(Map.of("gpx_data", gpxData))); + return ResponseEntity.ok(gsonWithNans.toJson(Map.of( + "gpx_data", gpxData, + "name", userFile.name, + "type", userFile.type + ))); } } LOGGER.error("Error getting gpx data: " + gpxFile.getError()); From 593764b8358b988a16c8965ea0afa63080cf8a6e Mon Sep 17 00:00:00 2001 From: alisa911 Date: Fri, 13 Dec 2024 12:16:25 +0200 Subject: [PATCH 13/23] add share details --- .../controllers/user/MapApiController.java | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/MapApiController.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/MapApiController.java index c14dfc37b..5f9da8ddb 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/MapApiController.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/MapApiController.java @@ -2,6 +2,7 @@ import java.io.*; +import net.osmand.server.api.repo.*; import net.osmand.shared.gpx.GpxTrackAnalysis; import net.osmand.shared.gpx.primitives.Metadata; import okio.Buffer; @@ -27,9 +28,6 @@ import javax.validation.constraints.NotNull; import net.osmand.map.OsmandRegions; -import net.osmand.server.api.repo.DeviceSubscriptionsRepository; -import net.osmand.server.api.repo.PremiumUserDevicesRepository; -import net.osmand.server.api.repo.PremiumUsersRepository; import net.osmand.server.api.services.*; import net.osmand.server.utils.WebGpxParser; import net.osmand.shared.gpx.GpxFile; @@ -56,7 +54,6 @@ import net.osmand.server.WebSecurityConfiguration.OsmAndProUser; import net.osmand.server.api.repo.PremiumUserDevicesRepository.PremiumUserDevice; -import net.osmand.server.api.repo.PremiumUserFilesRepository; import net.osmand.server.api.repo.PremiumUserFilesRepository.UserFile; import net.osmand.server.api.repo.PremiumUserFilesRepository.UserFileNoData; import net.osmand.server.controllers.pub.UserdataController.UserFilesResults; @@ -69,11 +66,12 @@ public class MapApiController { protected static final Log LOG = LogFactory.getLog(MapApiController.class); private static final String ANALYSIS = "analysis"; private static final String METADATA = "metadata"; + private static final String SHARE = "share"; private static final String SRTM_ANALYSIS = "srtm-analysis"; private static final String DONE_SUFFIX = "-done"; private static final String FAV_POINT_GROUPS = "pointGroups"; - private static final long ANALYSIS_RERUN = 1692026215870L; // 14-08-2023 + private static final long ANALYSIS_RERUN = 1734082635553L; // 13-12-2024 private static final String INFO_KEY = "info"; @@ -94,6 +92,9 @@ public class MapApiController { @Autowired UserdataService userdataService; + @Autowired + ShareFileService shareFileService; + @Autowired protected GpxService gpxService; @@ -395,6 +396,7 @@ public ResponseEntity listFiles(@RequestParam(required = false) String n }); uf.details.add(FAV_POINT_GROUPS, gson.toJsonTree(gsonWithNans.toJson(pointGroupsAnalysis))); } + uf.details.add(SHARE, gson.toJsonTree(isShared(uf))); } else { LOG.error(String.format( "web-list-files-error: no-input-stream %s id=%d userid=%d", uf.name, uf.id, uf.userid)); @@ -434,6 +436,11 @@ public ResponseEntity listFiles(@RequestParam(required = false) String n res.uniqueFiles.removeAll(filesToIgnore); return ResponseEntity.ok(gson.toJson(res)); } + + private boolean isShared(UserFile file) { + ShareFileRepository.ShareFile sharedFile = shareFileService.getFileByOwnerAndFilepath(file.userid, file.name); + return sharedFile != null; + } private void addDeviceInformation(UserFileNoData file, Map devices) { String deviceInfo = devices.get(file.deviceid); From 2878b47ba1e6ee1a453352d937e6da3b493c4eb3 Mon Sep 17 00:00:00 2001 From: alisa911 Date: Fri, 13 Dec 2024 14:12:31 +0200 Subject: [PATCH 14/23] add share flag for list-files --- .../server/api/repo/ShareFileRepository.java | 2 ++ .../server/api/services/ShareFileService.java | 4 ++++ .../controllers/user/MapApiController.java | 17 +++++++++++------ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/ShareFileRepository.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/ShareFileRepository.java index 1996ae65f..5f8b9e646 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/ShareFileRepository.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/ShareFileRepository.java @@ -21,6 +21,8 @@ public interface ShareFileRepository extends JpaRepository findByOwnerid(int ownerid); + @Query("SELECT a FROM ShareFilesAccess a WHERE a.id = :id") ShareFilesAccess findShareFilesAccessById(@Param("id") long id); diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java index cbdddbf68..00e0ab938 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java @@ -88,6 +88,10 @@ public ShareFileRepository.ShareFile getFileByOwnerAndFilepath(int ownerid, Stri return shareFileRepository.findByOwneridAndFilepath(ownerid, filepath); } + public List getFilesByOwner(int ownerid) { + return shareFileRepository.findByOwnerid(ownerid); + } + public ShareFileRepository.ShareFile getFileById(long id) { return shareFileRepository.findById(id).orElse(null); } diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/MapApiController.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/MapApiController.java index 5f9da8ddb..0d3ba5950 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/MapApiController.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/MapApiController.java @@ -71,7 +71,7 @@ public class MapApiController { private static final String DONE_SUFFIX = "-done"; private static final String FAV_POINT_GROUPS = "pointGroups"; - private static final long ANALYSIS_RERUN = 1734082635553L; // 13-12-2024 + private static final long ANALYSIS_RERUN = 1692026215870L; // 14-08-2023 private static final String INFO_KEY = "info"; @@ -344,6 +344,7 @@ public ResponseEntity listFiles(@RequestParam(required = false) String n return userdataService.tokenNotValidResponse(); } UserFilesResults res = userdataService.generateFiles(dev.userid, name, allVersions, true, type); + List shareList = shareFileService.getFilesByOwner(dev.userid); List filesToIgnore = new ArrayList<>(); int cloudReads = 0, cacheWrites = 0; for (UserFileNoData nd : res.uniqueFiles) { @@ -396,7 +397,6 @@ public ResponseEntity listFiles(@RequestParam(required = false) String n }); uf.details.add(FAV_POINT_GROUPS, gson.toJsonTree(gsonWithNans.toJson(pointGroupsAnalysis))); } - uf.details.add(SHARE, gson.toJsonTree(isShared(uf))); } else { LOG.error(String.format( "web-list-files-error: no-input-stream %s id=%d userid=%d", uf.name, uf.id, uf.userid)); @@ -417,6 +417,7 @@ public ResponseEntity listFiles(@RequestParam(required = false) String n nd.details.get(SRTM_ANALYSIS).getAsJsonObject().remove("elevationData"); nd.details.get(SRTM_ANALYSIS).getAsJsonObject().remove("pointsAttributesData"); } + nd.details.add(SHARE, gson.toJsonTree(isShared(nd, shareList))); } if (addDevices && res.allFiles != null) { Map devices = new HashMap<>(); @@ -437,11 +438,15 @@ public ResponseEntity listFiles(@RequestParam(required = false) String n return ResponseEntity.ok(gson.toJson(res)); } - private boolean isShared(UserFile file) { - ShareFileRepository.ShareFile sharedFile = shareFileService.getFileByOwnerAndFilepath(file.userid, file.name); - return sharedFile != null; + private boolean isShared(UserFileNoData file, List shareList) { + for (ShareFileRepository.ShareFile sf : shareList) { + if (sf.getFilepath().equals(file.name) && sf.getType().equals(file.type)) { + return true; + } + } + return false; } - + private void addDeviceInformation(UserFileNoData file, Map devices) { String deviceInfo = devices.get(file.deviceid); if (deviceInfo == null) { From de77bded68ff0ce37df9979a3d8d09cd19365529 Mon Sep 17 00:00:00 2001 From: alisa911 Date: Fri, 13 Dec 2024 17:25:03 +0200 Subject: [PATCH 15/23] add changeShareType --- .../server/api/services/ShareFileService.java | 30 ++++++++++++++----- .../controllers/user/ShareFileController.java | 20 +++++++++++++ 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java index 00e0ab938..7fea607cc 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java @@ -46,13 +46,7 @@ public enum PermissionType { @Transactional public String generateSharedCode(PremiumUserFilesRepository.UserFile userFile, boolean publicAccess) { - String uniqueCode = UUID.randomUUID().toString(); - ShareFileRepository.ShareFile file = shareFileRepository.findByUuid(uniqueCode); - while (file != null) { - uniqueCode = UUID.randomUUID().toString(); - file = shareFileRepository.findByUuid(uniqueCode); - } - + String uniqueCode = generateUniqueCode(); // Update existing file with new code ShareFileRepository.ShareFile existingFile = getFileByOwnerAndFilepath(userFile.userid, userFile.name); if (existingFile != null) { @@ -65,6 +59,16 @@ public String generateSharedCode(PremiumUserFilesRepository.UserFile userFile, b return uniqueCode; } + private String generateUniqueCode() { + String uniqueCode = UUID.randomUUID().toString(); + ShareFileRepository.ShareFile file = shareFileRepository.findByUuid(uniqueCode); + while (file != null) { + uniqueCode = UUID.randomUUID().toString(); + file = shareFileRepository.findByUuid(uniqueCode); + } + return uniqueCode; + } + @Transactional public ShareFileRepository.ShareFile createShareFile(PremiumUserFilesRepository.UserFile userFile, boolean publicAccess, String uniqueCode) { ShareFileRepository.ShareFile shareFile = new ShareFileRepository.ShareFile(); @@ -113,6 +117,17 @@ public boolean editAccessList(ShareFileRepository.ShareFile shareFile, Map checkAccess(ShareFileRepository.ShareFile file) { if (file.isPublicAccess()) { return null; @@ -209,7 +224,6 @@ public boolean updateRequests(Map accessMap) { shareFileRepository.saveAndFlush(access); } } - return true; } } diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java index d8aa96882..e83a3b74c 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java @@ -209,4 +209,24 @@ public ResponseEntity updateRequests(@RequestBody Map r return ResponseEntity.ok(gson.toJson(shareFileDto)); } + @GetMapping(path = {"/change-share-type"}, produces = "application/json") + public ResponseEntity changeShareType(@RequestParam String filePath) { + PremiumUserDevicesRepository.PremiumUserDevice dev = userdataService.checkUser(); + if (dev == null) { + return userdataService.tokenNotValidResponse(); + } + ShareFileRepository.ShareFile shareFile = shareFileService.getFileByOwnerAndFilepath(dev.userid, filePath); + if (shareFile == null) { + return ResponseEntity.badRequest().body(FILE_NOT_FOUND); + } + boolean success = shareFileService.changeFileShareType(shareFile); + if (!success) { + return ResponseEntity.badRequest().body("Error changing share type"); + } + shareFile = shareFileService.getFileByOwnerAndFilepath(dev.userid, filePath); + ShareFileRepository.ShareFileDTO shareFileDto = new ShareFileRepository.ShareFileDTO(shareFile, !shareFile.isPublicAccess()); + + return ResponseEntity.ok(gson.toJson(shareFileDto)); + } + } From 88ff43f41a74d20ec3f075d3d4b299d2d16ce2e3 Mon Sep 17 00:00:00 2001 From: alisa911 Date: Mon, 16 Dec 2024 12:26:01 +0200 Subject: [PATCH 16/23] add share error file is not available --- .../server/api/services/UserdataService.java | 1 + .../controllers/user/ShareFileController.java | 15 ++++++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/UserdataService.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/UserdataService.java index 2d526bd80..c9a28fc6d 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/UserdataService.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/UserdataService.java @@ -144,6 +144,7 @@ public class UserdataService { public static final String INFO_EXT = ".info"; public static final String FILE_NOT_FOUND = "File not found"; + public static final String FILE_WAS_DELETED = "File was deleted"; protected static final Log LOG = LogFactory.getLog(UserdataService.class); diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java index e83a3b74c..65e51f26c 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java @@ -26,6 +26,7 @@ import java.util.Map; import static net.osmand.server.api.services.UserdataService.FILE_NOT_FOUND; +import static net.osmand.server.api.services.UserdataService.FILE_WAS_DELETED; @Controller @@ -82,7 +83,7 @@ public ResponseEntity getFile(@PathVariable String uuid) throws IOException { PremiumUserFilesRepository.UserFile userFile = shareFileService.getUserFile(shareFile); FileDownloadResult fileResult = shareFileService.downloadFile(userFile); if (fileResult == null) { - return ResponseEntity.badRequest().body(FILE_NOT_FOUND); + return ResponseEntity.badRequest().body("Error downloading file"); } return ResponseEntity.ok() .header("Content-Disposition", "attachment; filename=" + fileResult.fileName) @@ -97,16 +98,16 @@ public ResponseEntity getGpx(@PathVariable String uuid) throws IOException { if (shareFile == null) { return ResponseEntity.badRequest().body(FILE_NOT_FOUND); } + PremiumUserFilesRepository.UserFile userFile = shareFileService.getUserFile(shareFile); + if (userFile == null) { + return ResponseEntity.badRequest().body(FILE_NOT_FOUND); + } else if (userFile.filesize == -1) { + return ResponseEntity.ok().body(FILE_WAS_DELETED); + } ResponseEntity errorAccess = shareFileService.checkAccess(shareFile); if (errorAccess != null) { return errorAccess; } - PremiumUserFilesRepository.UserFile userFile = shareFileService.getUserFile(shareFile); - FileDownloadResult fileResult = shareFileService.downloadFile(userFile); - if (fileResult == null) { - return ResponseEntity.badRequest().body(FILE_NOT_FOUND); - } - GpxFile gpxFile = shareFileService.getFile(userFile); if (gpxFile.getError() == null) { GpxTrackAnalysis analysis = gpxFile.getAnalysis(System.currentTimeMillis()); From 579e4eb1f69ee9beb4901e52d5af0002dfd67c78 Mon Sep 17 00:00:00 2001 From: alisa911 Date: Tue, 17 Dec 2024 17:30:02 +0200 Subject: [PATCH 17/23] add nickname field to PremiumUser --- db/sql_changeset_schema | 2 +- .../osmand/server/api/repo/PremiumUsersRepository.java | 3 +++ .../net/osmand/server/api/repo/ShareFileRepository.java | 2 +- .../net/osmand/server/api/services/ShareFileService.java | 9 ++++++++- .../osmand/server/controllers/user/MapApiController.java | 3 +++ .../server/controllers/user/ShareFileController.java | 6 +++--- 6 files changed, 19 insertions(+), 6 deletions(-) diff --git a/db/sql_changeset_schema b/db/sql_changeset_schema index 528081148..a3f4c7a4d 100644 --- a/db/sql_changeset_schema +++ b/db/sql_changeset_schema @@ -25,7 +25,7 @@ CREATE INDEX supporters_device_sub_orderid_idx on supporters_device_sub(orderid) ALTER TABLE supporters_device_sub add primary key (sku, orderid); ------ PREMIUM accounts ---- -CREATE TABLE user_accounts(id serial primary key, email text not null, tokendevice text, orderid text, token text, tokentime timestamp, regtime timestamp); +CREATE TABLE user_accounts(id serial primary key, email text not null, nickname text, tokendevice text, orderid text, token text, tokentime timestamp, regtime timestamp); CREATE TABLE user_account_devices(id serial primary key, userid integer, deviceid text, accesstoken text, lang text, brand text, model text, udpatetime timestamp); CREATE TABLE user_files(id bigserial primary key, type text, name text, userid integer, deviceid integer, updatetime timestamp, clienttime timestamp, filesize bigint, zipfilesize bigint, storage text, gendetails jsonb, data bytea); CREATE TABLE promo_campaigns(name text, starttime timestamp, endtime timestamp, subactivemonths integer, numberlimit integer, used integer, lastusers text); diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/PremiumUsersRepository.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/PremiumUsersRepository.java index 67bfe824f..a87edee94 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/PremiumUsersRepository.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/PremiumUsersRepository.java @@ -45,6 +45,9 @@ class PremiumUser implements Serializable { @Column(name = "email") public String email; + + @Column(name = "nickname") + public String nickname; @Column(name = "token") public String token; diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/ShareFileRepository.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/ShareFileRepository.java index 5f8b9e646..e2c00545a 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/ShareFileRepository.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/ShareFileRepository.java @@ -138,7 +138,7 @@ class ShareFilesAccessDTO { public ShareFilesAccessDTO(ShareFilesAccess access, boolean includeFile) { this.id = access.getId(); - this.name = access.getUser().email; + this.name = access.getUser().nickname; this.access = access.getAccess(); this.requestDate = access.getRequestDate(); if (includeFile) { diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java index 7fea607cc..a40f5efd3 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java @@ -33,6 +33,9 @@ public class ShareFileService { @Autowired protected ShareFileRepository shareFileRepository; + @Autowired + protected PremiumUsersRepository usersRepository; + @Autowired UserdataService userdataService; @@ -176,9 +179,13 @@ public ShareFileRepository.ShareFile getShareFileByUuid(String uuid) { } @Transactional - public void requestAccess(ShareFileRepository.ShareFile shareFile, PremiumUserDevicesRepository.PremiumUserDevice dev) { + public void requestAccess(ShareFileRepository.ShareFile shareFile, PremiumUserDevicesRepository.PremiumUserDevice dev, String nickname) { ShareFileRepository.ShareFilesAccess access = new ShareFileRepository.ShareFilesAccess(); PremiumUsersRepository.PremiumUser user = userdataService.getUserById(dev.userid); + if (user.nickname == null) { + user.nickname = nickname; + user = usersRepository.saveAndFlush(user); + } access.setUser(user); access.setAccess(ShareFileService.PermissionType.PENDING.name()); access.setRequestDate(new Date()); diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/MapApiController.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/MapApiController.java index 0d3ba5950..e267d73ba 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/MapApiController.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/MapApiController.java @@ -741,11 +741,14 @@ public ResponseEntity getAccountInfo() { final String START_TIME_KEY = "startTime"; final String EXPIRE_TIME_KEY = "expireTime"; final String MAX_ACCOUNT_SIZE = "maxAccSize"; + final String NICKNAME = "nickname"; PremiumUserDevice dev = userdataService.checkUser(); PremiumUsersRepository.PremiumUser pu = usersRepository.findById(dev.userid); Map info = new HashMap<>(); + info.put(NICKNAME, pu.nickname); + String orderId = pu.orderid; if (orderId == null) { info.put(ACCOUNT_KEY, FREE_ACCOUNT); diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java index 65e51f26c..442244872 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java @@ -126,7 +126,7 @@ public ResponseEntity getGpx(@PathVariable String uuid) throws IOException { @GetMapping(path = {"/request-access"}, produces = "application/json") @Transactional - public ResponseEntity requestAccess(@RequestParam String uuid) { + public ResponseEntity requestAccess(@RequestParam String uuid, @RequestParam String nickname) { PremiumUserDevicesRepository.PremiumUserDevice dev = userdataService.checkUser(); if (dev == null) { return userdataService.tokenNotValidResponse(); @@ -138,7 +138,7 @@ public ResponseEntity requestAccess(@RequestParam String uuid) { if (dev.userid == shareFile.ownerid) { return ResponseEntity.badRequest().body("You are the owner of this file"); } - shareFileService.requestAccess(shareFile, dev); + shareFileService.requestAccess(shareFile, dev, nickname); return ResponseEntity.ok("Access requested"); } @@ -163,7 +163,7 @@ public ResponseEntity getFileInfo(@RequestParam String fileName, if (user == null) { return ResponseEntity.badRequest().body("Error getting user info"); } - String ownerName = user.email; + String ownerName = user.nickname; ShareFileRepository.ShareFileDTO shareFileDto = new ShareFileRepository.ShareFileDTO(shareFile, true); return ResponseEntity.ok(gson.toJson(Map.of("owner", ownerName, "file", shareFileDto))); } From b12805842be2ed8c391a5e928ba6a9abd623c6df Mon Sep 17 00:00:00 2001 From: alisa911 Date: Wed, 18 Dec 2024 14:07:09 +0200 Subject: [PATCH 18/23] add private share type --- .../server/api/services/ShareFileService.java | 19 +++++++++++++------ .../controllers/user/ShareFileController.java | 18 +++++++++++++++--- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java index a40f5efd3..921f0c64a 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java @@ -47,6 +47,9 @@ public enum PermissionType { BLOCKED } + public static final String PRIVATE_SHARE_TYPE = "private"; + public static final String PUBLIC_SHARE_TYPE = "public"; + @Transactional public String generateSharedCode(PremiumUserFilesRepository.UserFile userFile, boolean publicAccess) { String uniqueCode = generateUniqueCode(); @@ -121,13 +124,17 @@ public boolean editAccessList(ShareFileRepository.ShareFile shareFile, Map updateRequests(@RequestBody Map r } @GetMapping(path = {"/change-share-type"}, produces = "application/json") - public ResponseEntity changeShareType(@RequestParam String filePath) { + public ResponseEntity changeShareType(@RequestParam String filePath, + @RequestParam String fileType, + @RequestParam String shareType, + @RequestParam boolean createIfNotExists) { PremiumUserDevicesRepository.PremiumUserDevice dev = userdataService.checkUser(); if (dev == null) { return userdataService.tokenNotValidResponse(); } ShareFileRepository.ShareFile shareFile = shareFileService.getFileByOwnerAndFilepath(dev.userid, filePath); if (shareFile == null) { - return ResponseEntity.badRequest().body(FILE_NOT_FOUND); + if (createIfNotExists) { + PremiumUserFilesRepository.UserFile userFile = userdataService.getUserFile(filePath, fileType, null, dev); + shareFile = shareFileService.createShareFile(userFile, false, null); + } else { + return ResponseEntity.badRequest().body(FILE_NOT_FOUND); + } } - boolean success = shareFileService.changeFileShareType(shareFile); + boolean success = shareFileService.changeFileShareType(shareFile, shareType); if (!success) { return ResponseEntity.badRequest().body("Error changing share type"); } + if (shareType.equals(PRIVATE_SHARE_TYPE)) { + return ResponseEntity.ok(FILE_WAS_DELETED); + } shareFile = shareFileService.getFileByOwnerAndFilepath(dev.userid, filePath); ShareFileRepository.ShareFileDTO shareFileDto = new ShareFileRepository.ShareFileDTO(shareFile, !shareFile.isPublicAccess()); From 41e92f44324f419e4fb8a7caee09cb799f0a6113 Mon Sep 17 00:00:00 2001 From: alisa911 Date: Wed, 18 Dec 2024 16:07:01 +0200 Subject: [PATCH 19/23] rename column id --- db/sql_changeset_schema | 4 ++-- .../java/net/osmand/server/api/repo/ShareFileRepository.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/db/sql_changeset_schema b/db/sql_changeset_schema index a3f4c7a4d..b54c089b0 100644 --- a/db/sql_changeset_schema +++ b/db/sql_changeset_schema @@ -41,7 +41,7 @@ CREATE INDEX user_files_deviceid_idx on user_files(deviceid); -- share file CREATE TABLE user_share_files ( - file_id SERIAL PRIMARY KEY, + id SERIAL PRIMARY KEY, ownerid INTEGER NOT NULL, uuid TEXT UNIQUE, filepath TEXT NOT NULL, @@ -57,7 +57,7 @@ CREATE TABLE user_share_files_access ( date TIMESTAMP NOT NULL, file_id INTEGER NOT NULL, CONSTRAINT fk_user_share_files_access_user FOREIGN KEY (user_id) REFERENCES user_accounts(id) ON DELETE CASCADE, - CONSTRAINT fk_user_share_files_access_file FOREIGN KEY (file_id) REFERENCES user_share_files(file_id) ON DELETE CASCADE + CONSTRAINT fk_user_share_files_access_file FOREIGN KEY (file_id) REFERENCES user_share_files(id) ON DELETE CASCADE ); CREATE INDEX user_share_files_ownerid_idx ON user_share_files(ownerid); diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/ShareFileRepository.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/ShareFileRepository.java index e2c00545a..9825d8059 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/ShareFileRepository.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/ShareFileRepository.java @@ -39,7 +39,7 @@ class ShareFile implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "file_id") + @Column(nullable = false) public long id; @Column(nullable = false) From e71e43cb174f764c3ce7f4e120bd17cc38de6d67 Mon Sep 17 00:00:00 2001 From: alisa911 Date: Wed, 18 Dec 2024 16:44:13 +0200 Subject: [PATCH 20/23] refactoring --- .../osmand/server/api/services/ShareFileService.java | 12 ++++-------- .../server/controllers/user/ShareFileController.java | 8 +++++++- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java index 921f0c64a..badd674ac 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java @@ -66,12 +66,10 @@ public String generateSharedCode(PremiumUserFilesRepository.UserFile userFile, b } private String generateUniqueCode() { - String uniqueCode = UUID.randomUUID().toString(); - ShareFileRepository.ShareFile file = shareFileRepository.findByUuid(uniqueCode); - while (file != null) { + String uniqueCode; + do { uniqueCode = UUID.randomUUID().toString(); - file = shareFileRepository.findByUuid(uniqueCode); - } + } while (shareFileRepository.findByUuid(uniqueCode) != null); return uniqueCode; } @@ -79,9 +77,7 @@ private String generateUniqueCode() { public ShareFileRepository.ShareFile createShareFile(PremiumUserFilesRepository.UserFile userFile, boolean publicAccess, String uniqueCode) { ShareFileRepository.ShareFile shareFile = new ShareFileRepository.ShareFile(); - String name = (userFile.name.lastIndexOf("/") >= 0) - ? userFile.name.substring(userFile.name.lastIndexOf("/") + 1) - : userFile.name; + String name = userFile.name.substring(userFile.name.lastIndexOf("/") + 1); shareFile.setUuid(uniqueCode); shareFile.setName(name); diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java index 8786c7814..1dc0dd2e0 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java @@ -111,7 +111,7 @@ public ResponseEntity getGpx(@PathVariable String uuid) throws IOException { } GpxFile gpxFile = shareFileService.getFile(userFile); if (gpxFile.getError() == null) { - GpxTrackAnalysis analysis = gpxFile.getAnalysis(System.currentTimeMillis()); + GpxTrackAnalysis analysis = gpxFile.getAnalysis(0); WebGpxParser.TrackData gpxData = gpxService.getTrackDataByGpxFile(gpxFile, null, analysis); if (gpxData != null) { return ResponseEntity.ok(gsonWithNans.toJson(Map.of( @@ -155,6 +155,9 @@ public ResponseEntity getFileInfo(@RequestParam String fileName, if (shareFile == null) { if (createIfNotExists) { PremiumUserFilesRepository.UserFile userFile = userdataService.getUserFile(fileName, fileType, null, dev); + if (userFile == null) { + return ResponseEntity.badRequest().body(FILE_NOT_FOUND); + } shareFile = shareFileService.createShareFile(userFile, false, null); } else { return ResponseEntity.badRequest().body(FILE_NOT_FOUND); @@ -224,6 +227,9 @@ public ResponseEntity changeShareType(@RequestParam String filePath, if (shareFile == null) { if (createIfNotExists) { PremiumUserFilesRepository.UserFile userFile = userdataService.getUserFile(filePath, fileType, null, dev); + if (userFile == null) { + return ResponseEntity.badRequest().body(FILE_NOT_FOUND); + } shareFile = shareFileService.createShareFile(userFile, false, null); } else { return ResponseEntity.badRequest().body(FILE_NOT_FOUND); From 4450b400fd0a2c34b85a5e462059e34a32e5326c Mon Sep 17 00:00:00 2001 From: alisa911 Date: Wed, 18 Dec 2024 18:24:35 +0200 Subject: [PATCH 21/23] change UUID --- db/sql_changeset_schema | 6 ++++-- .../server/api/repo/ShareFileRepository.java | 7 ++++--- .../server/api/services/ShareFileService.java | 20 +++++++++++-------- .../controllers/user/ShareFileController.java | 7 ++++--- 4 files changed, 24 insertions(+), 16 deletions(-) diff --git a/db/sql_changeset_schema b/db/sql_changeset_schema index b54c089b0..ef88d21c9 100644 --- a/db/sql_changeset_schema +++ b/db/sql_changeset_schema @@ -43,7 +43,7 @@ CREATE INDEX user_files_deviceid_idx on user_files(deviceid); CREATE TABLE user_share_files ( id SERIAL PRIMARY KEY, ownerid INTEGER NOT NULL, - uuid TEXT UNIQUE, + uuid UUID UNIQUE, filepath TEXT NOT NULL, name TEXT NOT NULL, type TEXT NOT NULL, @@ -51,11 +51,12 @@ CREATE TABLE user_share_files ( ); CREATE TABLE user_share_files_access ( - id SERIAL PRIMARY KEY, + id SERIAL UNIQUE, user_id INTEGER NOT NULL, access TEXT NOT NULL, date TIMESTAMP NOT NULL, file_id INTEGER NOT NULL, + CONSTRAINT user_share_files_access_pk PRIMARY KEY (file_id, user_id), CONSTRAINT fk_user_share_files_access_user FOREIGN KEY (user_id) REFERENCES user_accounts(id) ON DELETE CASCADE, CONSTRAINT fk_user_share_files_access_file FOREIGN KEY (file_id) REFERENCES user_share_files(id) ON DELETE CASCADE ); @@ -63,6 +64,7 @@ CREATE TABLE user_share_files_access ( CREATE INDEX user_share_files_ownerid_idx ON user_share_files(ownerid); CREATE INDEX user_share_files_uuid_idx ON user_share_files(uuid); CREATE INDEX user_share_files_filepath_idx ON user_share_files(filepath); +CREATE INDEX user_share_files_ownerid_filepath_idx ON user_share_files(ownerid, filepath); CREATE INDEX user_share_files_access_userid_idx ON user_share_files_access(user_id); CREATE INDEX user_share_files_access_file_id_idx ON user_share_files_access(file_id); diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/ShareFileRepository.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/ShareFileRepository.java index 9825d8059..0411f074b 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/ShareFileRepository.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/repo/ShareFileRepository.java @@ -12,12 +12,13 @@ import java.io.Serializable; import java.util.Date; import java.util.List; +import java.util.UUID; import java.util.stream.Collectors; @Repository public interface ShareFileRepository extends JpaRepository { - ShareFile findByUuid(String uuid); + ShareFile findByUuid(UUID uuid); ShareFile findByOwneridAndFilepath(int ownerid, String filepath); @@ -46,7 +47,7 @@ class ShareFile implements Serializable { public int ownerid; @Column(unique = true) - private String uuid; + private UUID uuid; @Column(nullable = false) public String filepath; @@ -114,7 +115,7 @@ public class ShareFileDTO { public ShareFileDTO(ShareFile shareFile, boolean includeAccessRecords) { this.id = shareFile.getId(); this.ownerid = shareFile.getOwnerid(); - this.uuid = shareFile.getUuid(); + this.uuid = shareFile.getUuid() != null ? shareFile.getUuid().toString() : null; this.filepath = shareFile.getFilepath(); this.name = shareFile.getName(); this.type = shareFile.getType(); diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java index badd674ac..f15359042 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java @@ -51,8 +51,8 @@ public enum PermissionType { public static final String PUBLIC_SHARE_TYPE = "public"; @Transactional - public String generateSharedCode(PremiumUserFilesRepository.UserFile userFile, boolean publicAccess) { - String uniqueCode = generateUniqueCode(); + public UUID generateSharedCode(PremiumUserFilesRepository.UserFile userFile, boolean publicAccess) { + UUID uniqueCode = generateUniqueCode(); // Update existing file with new code ShareFileRepository.ShareFile existingFile = getFileByOwnerAndFilepath(userFile.userid, userFile.name); if (existingFile != null) { @@ -65,16 +65,20 @@ public String generateSharedCode(PremiumUserFilesRepository.UserFile userFile, b return uniqueCode; } - private String generateUniqueCode() { - String uniqueCode; + private UUID generateUniqueCode() { + UUID uniqueCode; do { - uniqueCode = UUID.randomUUID().toString(); + uniqueCode = UUID.randomUUID(); } while (shareFileRepository.findByUuid(uniqueCode) != null); return uniqueCode; } @Transactional - public ShareFileRepository.ShareFile createShareFile(PremiumUserFilesRepository.UserFile userFile, boolean publicAccess, String uniqueCode) { + public ShareFileRepository.ShareFile createShareFile(PremiumUserFilesRepository.UserFile userFile, boolean publicAccess, UUID uniqueCode) { + ShareFileRepository.ShareFile existingFile = getFileByOwnerAndFilepath(userFile.userid, userFile.name); + if (existingFile != null) { + return existingFile; + } ShareFileRepository.ShareFile shareFile = new ShareFileRepository.ShareFile(); String name = userFile.name.substring(userFile.name.lastIndexOf("/") + 1); @@ -126,7 +130,7 @@ public boolean changeFileShareType(ShareFileRepository.ShareFile shareFile, Stri } else { shareFile.setPublicAccess(shareType.equals(PUBLIC_SHARE_TYPE)); if (shareFile.isPublicAccess() && shareFile.getUuid() == null) { - String uuid = generateUniqueCode(); + UUID uuid = generateUniqueCode(); shareFile.setUuid(uuid); } shareFileRepository.saveAndFlush(shareFile); @@ -178,7 +182,7 @@ public ShareFileRepository.ShareFile getShareFileByUuid(String uuid) { if (uuid == null) { return null; } - return shareFileRepository.findByUuid(uuid); + return shareFileRepository.findByUuid(UUID.fromString(uuid)); } @Transactional diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java index 1dc0dd2e0..e19fd75ea 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java @@ -24,6 +24,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.Map; +import java.util.UUID; import static net.osmand.server.api.services.ShareFileService.PRIVATE_SHARE_TYPE; import static net.osmand.server.api.services.UserdataService.FILE_NOT_FOUND; @@ -63,11 +64,11 @@ public ResponseEntity generateLink(@RequestParam String fileName, if (userFile == null) { return ResponseEntity.badRequest().body(FILE_NOT_FOUND); } - String uuid = shareFileService.generateSharedCode(userFile, publicAccess); + UUID uuid = shareFileService.generateSharedCode(userFile, publicAccess); if (uuid == null) { return ResponseEntity.badRequest().body("Error generating link"); } - return ResponseEntity.ok(gson.toJson(Map.of("uuid", uuid))); + return ResponseEntity.ok(gson.toJson(Map.of("uuid", uuid.toString()))); } @GetMapping(path = {"/get/{uuid}"}, produces = "application/json") @@ -167,7 +168,7 @@ public ResponseEntity getFileInfo(@RequestParam String fileName, if (user == null) { return ResponseEntity.badRequest().body("Error getting user info"); } - String ownerName = user.nickname; + String ownerName = user.nickname != null ? user.nickname : user.email; ShareFileRepository.ShareFileDTO shareFileDto = new ShareFileRepository.ShareFileDTO(shareFile, true); return ResponseEntity.ok(gson.toJson(Map.of("owner", ownerName, "file", shareFileDto))); } From a313d4dd852626e74a49d99635968c958b3cce46 Mon Sep 17 00:00:00 2001 From: alisa911 Date: Wed, 18 Dec 2024 18:39:39 +0200 Subject: [PATCH 22/23] refactoring --- .../java/net/osmand/server/api/services/ShareFileService.java | 2 +- .../osmand/server/controllers/user/ShareFileController.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java index f15359042..36bda67ee 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java @@ -138,7 +138,7 @@ public boolean changeFileShareType(ShareFileRepository.ShareFile shareFile, Stri return true; } - public ResponseEntity checkAccess(ShareFileRepository.ShareFile file) { + public ResponseEntity checkAccessAndReturnError(ShareFileRepository.ShareFile file) { if (file.isPublicAccess()) { return null; } else { diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java index e19fd75ea..a73638dfb 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/controllers/user/ShareFileController.java @@ -78,7 +78,7 @@ public ResponseEntity getFile(@PathVariable String uuid) throws IOException { if (shareFile == null) { return ResponseEntity.badRequest().body(FILE_NOT_FOUND); } - ResponseEntity errorAccess = shareFileService.checkAccess(shareFile); + ResponseEntity errorAccess = shareFileService.checkAccessAndReturnError(shareFile); if (errorAccess != null) { return errorAccess; } @@ -106,7 +106,7 @@ public ResponseEntity getGpx(@PathVariable String uuid) throws IOException { } else if (userFile.filesize == -1) { return ResponseEntity.ok().body(FILE_WAS_DELETED); } - ResponseEntity errorAccess = shareFileService.checkAccess(shareFile); + ResponseEntity errorAccess = shareFileService.checkAccessAndReturnError(shareFile); if (errorAccess != null) { return errorAccess; } From 36d27570951f21fe0fc576b4fca72b85f826fa4c Mon Sep 17 00:00:00 2001 From: alisa911 Date: Wed, 18 Dec 2024 19:11:10 +0200 Subject: [PATCH 23/23] refactoring --- db/sql_changeset_schema | 2 +- .../java/net/osmand/server/api/services/ShareFileService.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/db/sql_changeset_schema b/db/sql_changeset_schema index ef88d21c9..31139ebaf 100644 --- a/db/sql_changeset_schema +++ b/db/sql_changeset_schema @@ -64,7 +64,7 @@ CREATE TABLE user_share_files_access ( CREATE INDEX user_share_files_ownerid_idx ON user_share_files(ownerid); CREATE INDEX user_share_files_uuid_idx ON user_share_files(uuid); CREATE INDEX user_share_files_filepath_idx ON user_share_files(filepath); -CREATE INDEX user_share_files_ownerid_filepath_idx ON user_share_files(ownerid, filepath); +CREATE UNIQUE INDEX user_share_files_ownerid_filepath_idx ON user_share_files(ownerid, filepath); CREATE INDEX user_share_files_access_userid_idx ON user_share_files_access(user_id); CREATE INDEX user_share_files_access_file_id_idx ON user_share_files_access(file_id); diff --git a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java index 36bda67ee..2f3063243 100644 --- a/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java +++ b/java-tools/OsmAndServer/src/main/java/net/osmand/server/api/services/ShareFileService.java @@ -77,7 +77,7 @@ private UUID generateUniqueCode() { public ShareFileRepository.ShareFile createShareFile(PremiumUserFilesRepository.UserFile userFile, boolean publicAccess, UUID uniqueCode) { ShareFileRepository.ShareFile existingFile = getFileByOwnerAndFilepath(userFile.userid, userFile.name); if (existingFile != null) { - return existingFile; + throw new IllegalStateException("File already shared"); } ShareFileRepository.ShareFile shareFile = new ShareFileRepository.ShareFile();