From dd8dbbf6fca2c61726431a8552640f2c1499b4a2 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Tue, 15 Feb 2022 22:55:53 -0800 Subject: Migrate permissions queries --- src/main/java/org/traccar/config/Keys.java | 7 - .../java/org/traccar/database/DataManager.java | 141 +-------------------- src/main/java/org/traccar/model/Permission.java | 95 ++++++++++++-- .../java/org/traccar/storage/DatabaseStorage.java | 50 ++++++++ .../java/org/traccar/storage/MemoryStorage.java | 16 +++ .../java/org/traccar/storage/QueryBuilder.java | 2 +- src/main/java/org/traccar/storage/Storage.java | 8 ++ 7 files changed, 164 insertions(+), 155 deletions(-) diff --git a/src/main/java/org/traccar/config/Keys.java b/src/main/java/org/traccar/config/Keys.java index cb3bd4de8..ccfe4bee7 100644 --- a/src/main/java/org/traccar/config/Keys.java +++ b/src/main/java/org/traccar/config/Keys.java @@ -342,13 +342,6 @@ public final class Keys { "database.changelog", Collections.singletonList(KeyType.GLOBAL)); - /** - * Automatically generate SQL database queries when possible. - */ - public static final ConfigKey DATABASE_GENERATE_QUERIES = new ConfigKey<>( - "database.generateQueries", - Collections.singletonList(KeyType.GLOBAL)); - /** * Database connection pool size. Default value is defined by the HikariCP library. */ diff --git a/src/main/java/org/traccar/database/DataManager.java b/src/main/java/org/traccar/database/DataManager.java index 303b8e033..178cbbc57 100644 --- a/src/main/java/org/traccar/database/DataManager.java +++ b/src/main/java/org/traccar/database/DataManager.java @@ -24,30 +24,18 @@ import liquibase.database.DatabaseFactory; import liquibase.exception.LiquibaseException; import liquibase.resource.FileSystemResourceAccessor; import liquibase.resource.ResourceAccessor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.traccar.Context; import org.traccar.config.Config; import org.traccar.config.Keys; -import org.traccar.model.Attribute; import org.traccar.model.BaseModel; -import org.traccar.model.Calendar; -import org.traccar.model.Command; import org.traccar.model.Device; -import org.traccar.model.Driver; import org.traccar.model.Event; -import org.traccar.model.Geofence; -import org.traccar.model.Group; -import org.traccar.model.Maintenance; -import org.traccar.model.ManagedUser; -import org.traccar.model.Notification; import org.traccar.model.Permission; import org.traccar.model.Position; import org.traccar.model.Server; import org.traccar.model.Statistics; import org.traccar.model.User; import org.traccar.storage.DatabaseStorage; -import org.traccar.storage.QueryBuilder; import org.traccar.storage.Storage; import org.traccar.storage.StorageException; import org.traccar.storage.query.Columns; @@ -57,11 +45,9 @@ import org.traccar.storage.query.Order; import org.traccar.storage.query.Request; import javax.sql.DataSource; -import java.beans.Introspector; import java.io.File; import java.lang.reflect.Method; import java.net.URL; -import java.sql.SQLException; import java.util.Collection; import java.util.Date; import java.util.LinkedList; @@ -69,14 +55,6 @@ import java.util.List; public class DataManager { - private static final Logger LOGGER = LoggerFactory.getLogger(DataManager.class); - - public static final String ACTION_SELECT_ALL = "selectAll"; - public static final String ACTION_SELECT = "select"; - public static final String ACTION_INSERT = "insert"; - public static final String ACTION_UPDATE = "update"; - public static final String ACTION_DELETE = "delete"; - private final Config config; private DataSource dataSource; @@ -87,8 +65,6 @@ public class DataManager { private final Storage storage; - private boolean generateQueries; - private final boolean forceLdap; public DataManager(Config config) throws Exception { @@ -137,72 +113,9 @@ public class DataManager { hikariConfig.setMaximumPoolSize(maxPoolSize); } - generateQueries = config.getBoolean(Keys.DATABASE_GENERATE_QUERIES); - dataSource = new HikariDataSource(hikariConfig); } - public static String constructPermissionQuery(String action, Class owner, Class property) { - switch (action) { - case ACTION_SELECT_ALL: - return "SELECT " + makeNameId(owner) + ", " + makeNameId(property) + " FROM " - + getPermissionsTableName(owner, property); - case ACTION_INSERT: - return "INSERT INTO " + getPermissionsTableName(owner, property) - + " (" + makeNameId(owner) + ", " + makeNameId(property) + ") VALUES (:" - + makeNameId(owner) + ", :" + makeNameId(property) + ")"; - case ACTION_DELETE: - return "DELETE FROM " + getPermissionsTableName(owner, property) - + " WHERE " + makeNameId(owner) + " = :" + makeNameId(owner) - + " AND " + makeNameId(property) + " = :" + makeNameId(property); - default: - throw new IllegalArgumentException("Unknown action"); - } - } - - public String getQuery(String action, Class owner, Class property) { - String queryName; - switch (action) { - case ACTION_SELECT_ALL: - queryName = "database.select" + owner.getSimpleName() + property.getSimpleName() + "s"; - break; - case ACTION_INSERT: - queryName = "database.link" + owner.getSimpleName() + property.getSimpleName(); - break; - default: - queryName = "database.unlink" + owner.getSimpleName() + property.getSimpleName(); - break; - } - String query = config.getString(queryName); - if (query == null) { - if (generateQueries) { - query = constructPermissionQuery( - action, owner, property.equals(User.class) ? ManagedUser.class : property); - } else { - LOGGER.info("Query not provided: " + queryName); - } - } - return query; - } - - private static String getPermissionsTableName(Class owner, Class property) { - String propertyName = property.getSimpleName(); - if (propertyName.equals("ManagedUser")) { - propertyName = "User"; - } - return "tc_" + Introspector.decapitalize(owner.getSimpleName()) - + "_" + Introspector.decapitalize(propertyName); - } - - private static String getObjectsTableName(Class clazz) { - String result = "tc_" + Introspector.decapitalize(clazz.getSimpleName()); - // Add "s" ending if object name is not plural already - if (!result.endsWith("s")) { - result += "s"; - } - return result; - } - private void initDatabaseSchema() throws LiquibaseException { if (config.hasKey(Keys.DATABASE_CHANGELOG)) { @@ -318,61 +231,17 @@ public class DataManager { new Order("captureTime"))); } - public static Class getClassByName(String name) throws ClassNotFoundException { - switch (name.toLowerCase().replace("id", "")) { - case "device": - return Device.class; - case "group": - return Group.class; - case "user": - return User.class; - case "manageduser": - return ManagedUser.class; - case "geofence": - return Geofence.class; - case "driver": - return Driver.class; - case "attribute": - return Attribute.class; - case "calendar": - return Calendar.class; - case "command": - return Command.class; - case "maintenance": - return Maintenance.class; - case "notification": - return Notification.class; - case "order": - return org.traccar.model.Order.class; - default: - throw new ClassNotFoundException(); - } - } - - private static String makeNameId(Class clazz) { - String name = clazz.getSimpleName(); - return Introspector.decapitalize(name) + (!name.contains("Id") ? "Id" : ""); - } - public Collection getPermissions(Class owner, Class property) throws StorageException, ClassNotFoundException { - try { - return QueryBuilder.create(dataSource, getQuery(ACTION_SELECT_ALL, owner, property)) - .executePermissionsQuery(); - } catch (SQLException e) { - throw new StorageException(e); - } + return storage.getPermissions(owner, property); } public void linkObject(Class owner, long ownerId, Class property, long propertyId, boolean link) throws StorageException { - try { - QueryBuilder.create(dataSource, getQuery(link ? ACTION_INSERT : ACTION_DELETE, owner, property)) - .setLong(makeNameId(owner), ownerId) - .setLong(makeNameId(property), propertyId) - .executeUpdate(); - } catch (SQLException e) { - throw new StorageException(e); + if (link) { + storage.addPermission(new Permission(owner, ownerId, property, propertyId)); + } else { + storage.removePermission(new Permission(owner, ownerId, property, propertyId)); } } diff --git a/src/main/java/org/traccar/model/Permission.java b/src/main/java/org/traccar/model/Permission.java index 6475a4582..ad0176b39 100644 --- a/src/main/java/org/traccar/model/Permission.java +++ b/src/main/java/org/traccar/model/Permission.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org) * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,42 +16,115 @@ */ package org.traccar.model; -import java.util.Iterator; +import java.beans.Introspector; +import java.io.IOException; +import java.net.URISyntaxException; import java.util.LinkedHashMap; import java.util.Map; +import java.util.TreeMap; -import org.traccar.database.DataManager; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.traccar.helper.ClassScanner; +import org.traccar.storage.QueryIgnore; public class Permission { + private static final Map> CLASSES = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + + static { + try { + for (Class clazz : ClassScanner.findSubclasses(BaseModel.class)) { + CLASSES.put(clazz.getSimpleName(), clazz); + } + } catch (IOException | ReflectiveOperationException | URISyntaxException e) { + throw new RuntimeException(e); + } + } + + private final LinkedHashMap data; + private final Class ownerClass; private final long ownerId; private final Class propertyClass; private final long propertyId; - public Permission(LinkedHashMap permissionMap) throws ClassNotFoundException { - Iterator> iterator = permissionMap.entrySet().iterator(); - String owner = iterator.next().getKey(); - ownerClass = DataManager.getClassByName(owner); - String property = iterator.next().getKey(); - propertyClass = DataManager.getClassByName(property); - ownerId = permissionMap.get(owner); - propertyId = permissionMap.get(property); + public Permission(LinkedHashMap data) { + this.data = data; + var iterator = data.entrySet().iterator(); + var owner = iterator.next(); + ownerClass = CLASSES.get(owner.getKey().substring(0, owner.getKey().length() - 2)); + ownerId = owner.getValue(); + var property = iterator.next(); + propertyClass = CLASSES.get(property.getKey().substring(0, property.getKey().length() - 2)); + propertyId = property.getValue(); } + public Permission(Class ownerClass, long ownerId, Class propertyClass, long propertyId) { + this.ownerClass = ownerClass; + this.ownerId = ownerId; + this.propertyClass = propertyClass; + this.propertyId = propertyId; + data = new LinkedHashMap<>(); + data.put(getKey(ownerClass), ownerId); + data.put(getKey(propertyClass), propertyId); + } + + private static String getKey(Class clazz) { + return Introspector.decapitalize(clazz.getSimpleName()) + "Id"; + } + + public static String getStorageName(Class ownerClass, Class propertyClass) { + String ownerName = ownerClass.getSimpleName(); + String propertyName = propertyClass.getSimpleName(); + String managedPrefix = "Managed"; + if (propertyName.startsWith(managedPrefix)) { + propertyName = propertyName.substring(managedPrefix.length()); + } + return "tc_" + Introspector.decapitalize(ownerName) + "_" + Introspector.decapitalize(propertyName); + } + + @QueryIgnore + @JsonIgnore + public String getStorageName() { + return getStorageName(ownerClass, propertyClass); + } + + @QueryIgnore + @JsonAnyGetter + public Map get() { + return data; + } + + @QueryIgnore + @JsonAnySetter + public void set(String key, Long value) { + data.put(key, value); + } + + @QueryIgnore + @JsonIgnore public Class getOwnerClass() { return ownerClass; } + @QueryIgnore + @JsonIgnore public long getOwnerId() { return ownerId; } + @QueryIgnore + @JsonIgnore public Class getPropertyClass() { return propertyClass; } + @QueryIgnore + @JsonIgnore public long getPropertyId() { return propertyId; } + } diff --git a/src/main/java/org/traccar/storage/DatabaseStorage.java b/src/main/java/org/traccar/storage/DatabaseStorage.java index 2c893ebbc..d73dc7b25 100644 --- a/src/main/java/org/traccar/storage/DatabaseStorage.java +++ b/src/main/java/org/traccar/storage/DatabaseStorage.java @@ -1,5 +1,6 @@ package org.traccar.storage; +import org.traccar.model.Permission; import org.traccar.storage.query.Columns; import org.traccar.storage.query.Condition; import org.traccar.storage.query.Limit; @@ -29,6 +30,7 @@ public class DatabaseStorage extends Storage { query.append(" FROM ").append(getTableName(clazz)); query.append(formatCondition(request.getCondition())); query.append(formatOrder(request.getOrder())); + query.append(formatLimit(request.getLimit())); try { QueryBuilder builder = QueryBuilder.create(dataSource, query.toString()); for (Map.Entry variable : getConditionVariables(request.getCondition()).entrySet()) { @@ -93,6 +95,54 @@ public class DatabaseStorage extends Storage { } } + @Override + public List getPermissions(Class ownerClass, Class propertyClass) throws StorageException { + StringBuilder query = new StringBuilder("SELECT * FROM "); + query.append(Permission.getStorageName(ownerClass, propertyClass)); + try { + QueryBuilder builder = QueryBuilder.create(dataSource, query.toString()); + return builder.executePermissionsQuery(); + } catch (SQLException e) { + throw new StorageException(e); + } + } + + @Override + public void addPermission(Permission permission) throws StorageException { + StringBuilder query = new StringBuilder("INSERT INTO "); + query.append(permission.getStorageName()); + query.append(" VALUES ("); + query.append(permission.get().keySet().stream().map(key -> ':' + key).collect(Collectors.joining(", "))); + query.append(")"); + try { + QueryBuilder builder = QueryBuilder.create(dataSource, query.toString(), true); + for (var entry : permission.get().entrySet()) { + builder.setLong(entry.getKey(), entry.getValue()); + } + builder.executeUpdate(); + } catch (SQLException e) { + throw new StorageException(e); + } + } + + @Override + public void removePermission(Permission permission) throws StorageException { + StringBuilder query = new StringBuilder("DELETE FROM "); + query.append(permission.getStorageName()); + query.append(" WHERE "); + query.append(permission + .get().keySet().stream().map(key -> key + " = :" + key).collect(Collectors.joining(" AND "))); + try { + QueryBuilder builder = QueryBuilder.create(dataSource, query.toString(), true); + for (var entry : permission.get().entrySet()) { + builder.setLong(entry.getKey(), entry.getValue()); + } + builder.executeUpdate(); + } catch (SQLException e) { + throw new StorageException(e); + } + } + private String getTableName(Class clazz) throws StorageException { StorageName storageName = clazz.getAnnotation(StorageName.class); if (storageName == null) { diff --git a/src/main/java/org/traccar/storage/MemoryStorage.java b/src/main/java/org/traccar/storage/MemoryStorage.java index 0ebd3b87e..0a2e5a81f 100644 --- a/src/main/java/org/traccar/storage/MemoryStorage.java +++ b/src/main/java/org/traccar/storage/MemoryStorage.java @@ -1,5 +1,6 @@ package org.traccar.storage; +import org.traccar.model.Permission; import org.traccar.storage.query.Request; import java.util.List; @@ -24,4 +25,19 @@ public class MemoryStorage extends Storage { public void removeObject(Class clazz, Request request) throws StorageException { } + @Override + public List getPermissions(Class ownerClass, Class propertyClass) throws StorageException { + return null; + } + + @Override + public void addPermission(Permission permission) throws StorageException { + + } + + @Override + public void removePermission(Permission permission) throws StorageException { + + } + } diff --git a/src/main/java/org/traccar/storage/QueryBuilder.java b/src/main/java/org/traccar/storage/QueryBuilder.java index edaacb74d..da8002f0b 100644 --- a/src/main/java/org/traccar/storage/QueryBuilder.java +++ b/src/main/java/org/traccar/storage/QueryBuilder.java @@ -472,7 +472,7 @@ public final class QueryBuilder { return 0; } - public List executePermissionsQuery() throws SQLException, ClassNotFoundException { + public List executePermissionsQuery() throws SQLException { List result = new LinkedList<>(); if (query != null) { try { diff --git a/src/main/java/org/traccar/storage/Storage.java b/src/main/java/org/traccar/storage/Storage.java index 3726fbf50..22c48cae0 100644 --- a/src/main/java/org/traccar/storage/Storage.java +++ b/src/main/java/org/traccar/storage/Storage.java @@ -1,5 +1,6 @@ package org.traccar.storage; +import org.traccar.model.Permission; import org.traccar.storage.query.Request; import java.util.List; @@ -14,6 +15,13 @@ public abstract class Storage { public abstract void removeObject(Class clazz, Request request) throws StorageException; + public abstract List getPermissions( + Class ownerClass, Class propertyClass) throws StorageException; + + public abstract void addPermission(Permission permission) throws StorageException; + + public abstract void removePermission(Permission permission) throws StorageException; + public T getObject(Class clazz, Request request) throws StorageException { var objects = getObjects(clazz, request); return objects.isEmpty() ? null : objects.get(0); -- cgit v1.2.3