From 9ec2d09ad8a757d58f70812b9cf5e835321382e6 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Mon, 21 Feb 2022 17:33:58 -0800 Subject: Migrate permissions to storage --- .../java/org/traccar/api/BaseObjectResource.java | 105 ++++------------ src/main/java/org/traccar/api/BaseResource.java | 12 +- .../org/traccar/api/ExtendedObjectResource.java | 51 ++++---- src/main/java/org/traccar/api/MediaFilter.java | 6 +- .../java/org/traccar/api/SimpleObjectResource.java | 30 +++-- .../org/traccar/api/resource/DeviceResource.java | 4 +- .../org/traccar/api/resource/EventResource.java | 18 +-- .../traccar/api/resource/NotificationResource.java | 3 +- .../org/traccar/api/resource/PositionResource.java | 2 +- .../org/traccar/api/resource/ReportResource.java | 20 +-- .../traccar/api/security/PermissionsService.java | 136 +++++++++++++++++++++ 11 files changed, 237 insertions(+), 150 deletions(-) create mode 100644 src/main/java/org/traccar/api/security/PermissionsService.java (limited to 'src/main/java/org/traccar/api') diff --git a/src/main/java/org/traccar/api/BaseObjectResource.java b/src/main/java/org/traccar/api/BaseObjectResource.java index 22756f62a..07c74449c 100644 --- a/src/main/java/org/traccar/api/BaseObjectResource.java +++ b/src/main/java/org/traccar/api/BaseObjectResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org) * Copyright 2017 - 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,70 +16,44 @@ */ package org.traccar.api; -import java.sql.SQLException; -import java.util.Set; - -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.core.Response; - import org.traccar.Context; import org.traccar.database.BaseObjectManager; import org.traccar.database.ExtendedObjectManager; -import org.traccar.database.ManagableObjects; import org.traccar.database.SimpleObjectManager; import org.traccar.helper.LogAction; import org.traccar.model.BaseModel; import org.traccar.model.Calendar; -import org.traccar.model.Command; import org.traccar.model.Device; import org.traccar.model.Group; -import org.traccar.model.GroupedModel; -import org.traccar.model.ScheduledModel; +import org.traccar.model.Permission; import org.traccar.model.User; import org.traccar.storage.StorageException; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Condition; +import org.traccar.storage.query.Request; + +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.core.Response; public abstract class BaseObjectResource extends BaseResource { - private final Class baseClass; + protected final Class baseClass; public BaseObjectResource(Class baseClass) { this.baseClass = baseClass; } - protected final Class getBaseClass() { - return baseClass; - } - - protected final Set getSimpleManagerItems(BaseObjectManager manager, boolean all, long userId) { - Set result; - if (all) { - if (Context.getPermissionsManager().getUserAdmin(getUserId())) { - result = manager.getAllItems(); - } else { - Context.getPermissionsManager().checkManager(getUserId()); - result = ((ManagableObjects) manager).getManagedItems(getUserId()); - } - } else { - if (userId == 0) { - userId = getUserId(); - } - Context.getPermissionsManager().checkUser(getUserId(), userId); - result = ((ManagableObjects) manager).getUserItems(userId); - } - return result; - } - @Path("{id}") @GET - public Response getSingle(@PathParam("id") long id) throws SQLException { - Context.getPermissionsManager().checkPermission(baseClass, getUserId(), id); - BaseObjectManager manager = Context.getManager(baseClass); - T entity = manager.getById(id); + public Response getSingle(@PathParam("id") long id) throws StorageException { + permissionsService.checkPermission(baseClass, getUserId(), id); + T entity = storage.getObject(baseClass, new Request( + new Columns.All(), new Condition.Equals("id", "id", id))); if (entity != null) { return Response.ok(entity).build(); } else { @@ -89,25 +63,13 @@ public abstract class BaseObjectResource extends BaseResour @POST public Response add(T entity) throws StorageException { - Context.getPermissionsManager().checkReadonly(getUserId()); - if (baseClass.equals(Device.class)) { - Context.getPermissionsManager().checkDeviceReadonly(getUserId()); - Context.getPermissionsManager().checkDeviceLimit(getUserId()); - } else if (baseClass.equals(Command.class)) { - Context.getPermissionsManager().checkLimitCommands(getUserId()); - } else if (entity instanceof GroupedModel && ((GroupedModel) entity).getGroupId() != 0) { - Context.getPermissionsManager().checkPermission( - Group.class, getUserId(), ((GroupedModel) entity).getGroupId()); - } else if (entity instanceof ScheduledModel && ((ScheduledModel) entity).getCalendarId() != 0) { - Context.getPermissionsManager().checkPermission( - Calendar.class, getUserId(), ((ScheduledModel) entity).getCalendarId()); - } + permissionsService.checkEdit(getUserId(), entity, true); BaseObjectManager manager = Context.getManager(baseClass); manager.addItem(entity); LogAction.create(getUserId(), entity); - Context.getDataManager().linkObject(User.class, getUserId(), baseClass, entity.getId(), true); + storage.addPermission(new Permission(User.class, getUserId(), baseClass, entity.getId())); LogAction.link(getUserId(), User.class, getUserId(), baseClass, entity.getId()); if (manager instanceof SimpleObjectManager) { @@ -122,22 +84,8 @@ public abstract class BaseObjectResource extends BaseResour @Path("{id}") @PUT public Response update(T entity) throws StorageException { - Context.getPermissionsManager().checkReadonly(getUserId()); - if (baseClass.equals(Device.class)) { - Context.getPermissionsManager().checkDeviceReadonly(getUserId()); - } else if (baseClass.equals(User.class)) { - User before = Context.getPermissionsManager().getUser(entity.getId()); - Context.getPermissionsManager().checkUserUpdate(getUserId(), before, (User) entity); - } else if (baseClass.equals(Command.class)) { - Context.getPermissionsManager().checkLimitCommands(getUserId()); - } else if (entity instanceof GroupedModel && ((GroupedModel) entity).getGroupId() != 0) { - Context.getPermissionsManager().checkPermission( - Group.class, getUserId(), ((GroupedModel) entity).getGroupId()); - } else if (entity instanceof ScheduledModel && ((ScheduledModel) entity).getCalendarId() != 0) { - Context.getPermissionsManager().checkPermission( - Calendar.class, getUserId(), ((ScheduledModel) entity).getCalendarId()); - } - Context.getPermissionsManager().checkPermission(baseClass, getUserId(), entity.getId()); + permissionsService.checkEdit(getUserId(), entity, false); + permissionsService.checkPermission(baseClass, getUserId(), entity.getId()); Context.getManager(baseClass).updateItem(entity); LogAction.edit(getUserId(), entity); @@ -152,13 +100,8 @@ public abstract class BaseObjectResource extends BaseResour @Path("{id}") @DELETE public Response remove(@PathParam("id") long id) throws StorageException { - Context.getPermissionsManager().checkReadonly(getUserId()); - if (baseClass.equals(Device.class)) { - Context.getPermissionsManager().checkDeviceReadonly(getUserId()); - } else if (baseClass.equals(Command.class)) { - Context.getPermissionsManager().checkLimitCommands(getUserId()); - } - Context.getPermissionsManager().checkPermission(baseClass, getUserId(), id); + permissionsService.checkEdit(getUserId(), baseClass, false); + permissionsService.checkPermission(baseClass, getUserId(), id); BaseObjectManager manager = Context.getManager(baseClass); manager.removeItem(id); diff --git a/src/main/java/org/traccar/api/BaseResource.java b/src/main/java/org/traccar/api/BaseResource.java index 6dff8c8c3..33abe73fa 100644 --- a/src/main/java/org/traccar/api/BaseResource.java +++ b/src/main/java/org/traccar/api/BaseResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,8 +15,11 @@ */ package org.traccar.api; +import org.traccar.api.security.PermissionsService; import org.traccar.api.security.UserPrincipal; +import org.traccar.storage.Storage; +import javax.inject.Inject; import javax.ws.rs.core.Context; import javax.ws.rs.core.SecurityContext; @@ -25,6 +28,12 @@ public class BaseResource { @Context private SecurityContext securityContext; + @Inject + protected Storage storage; + + @Inject + protected PermissionsService permissionsService; + protected long getUserId() { UserPrincipal principal = (UserPrincipal) securityContext.getUserPrincipal(); if (principal != null) { @@ -32,4 +41,5 @@ public class BaseResource { } return 0; } + } diff --git a/src/main/java/org/traccar/api/ExtendedObjectResource.java b/src/main/java/org/traccar/api/ExtendedObjectResource.java index 9e554217e..a12314a2c 100644 --- a/src/main/java/org/traccar/api/ExtendedObjectResource.java +++ b/src/main/java/org/traccar/api/ExtendedObjectResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 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,17 +16,19 @@ */ package org.traccar.api; -import java.sql.SQLException; -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; +import org.traccar.model.BaseModel; +import org.traccar.model.Device; +import org.traccar.model.Group; +import org.traccar.model.User; +import org.traccar.storage.StorageException; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Condition; +import org.traccar.storage.query.Request; import javax.ws.rs.GET; import javax.ws.rs.QueryParam; - -import org.traccar.Context; -import org.traccar.database.ExtendedObjectManager; -import org.traccar.model.BaseModel; +import java.util.Collection; +import java.util.LinkedList; public class ExtendedObjectResource extends BaseObjectResource { @@ -36,27 +38,28 @@ public class ExtendedObjectResource extends BaseObjectResou @GET public Collection get( - @QueryParam("all") boolean all, @QueryParam("userId") long userId, @QueryParam("groupId") long groupId, - @QueryParam("deviceId") long deviceId, @QueryParam("refresh") boolean refresh) throws SQLException { - - ExtendedObjectManager manager = (ExtendedObjectManager) Context.getManager(getBaseClass()); - if (refresh) { - manager.refreshItems(); - } + @QueryParam("all") boolean all, @QueryParam("userId") long userId, + @QueryParam("groupId") long groupId, @QueryParam("deviceId") long deviceId) throws StorageException { - Set result = new HashSet<>(getSimpleManagerItems(manager, all, userId)); + var conditions = new LinkedList(); - if (groupId != 0) { - Context.getPermissionsManager().checkGroup(getUserId(), groupId); - result.retainAll(manager.getGroupItems(groupId)); + if (all) { + permissionsService.checkAdmin(getUserId()); + } else { + permissionsService.checkUser(getUserId(), userId); + conditions.add(new Condition.Permission(User.class, userId, baseClass)); } - if (deviceId != 0) { - Context.getPermissionsManager().checkDevice(getUserId(), deviceId); - result.retainAll(manager.getDeviceItems(deviceId)); + if (groupId > 0) { + permissionsService.checkPermission(Group.class, getUserId(), groupId); + conditions.add(new Condition.Permission(Group.class, groupId, baseClass)); + } + if (deviceId > 0) { + permissionsService.checkPermission(Device.class, getUserId(), deviceId); + conditions.add(new Condition.Permission(Device.class, deviceId, baseClass)); } - return manager.getItems(result); + return storage.getObjects(baseClass, new Request(new Columns.All(), Condition.merge(conditions))); } } diff --git a/src/main/java/org/traccar/api/MediaFilter.java b/src/main/java/org/traccar/api/MediaFilter.java index 77731a810..0433147f8 100644 --- a/src/main/java/org/traccar/api/MediaFilter.java +++ b/src/main/java/org/traccar/api/MediaFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2018 - 2022 Anton Tananaev (anton@traccar.org) * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,7 +17,6 @@ package org.traccar.api; import java.io.IOException; -import java.sql.SQLException; import javax.servlet.Filter; import javax.servlet.FilterChain; @@ -76,9 +75,6 @@ public class MediaFilter implements Filter { } catch (SecurityException e) { httpResponse.setStatus(HttpServletResponse.SC_FORBIDDEN); httpResponse.getWriter().println(Log.exceptionStack(e)); - } catch (SQLException e) { - httpResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST); - httpResponse.getWriter().println(Log.exceptionStack(e)); } } diff --git a/src/main/java/org/traccar/api/SimpleObjectResource.java b/src/main/java/org/traccar/api/SimpleObjectResource.java index a7fcae0e7..b55bf91e3 100644 --- a/src/main/java/org/traccar/api/SimpleObjectResource.java +++ b/src/main/java/org/traccar/api/SimpleObjectResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 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,15 +16,17 @@ */ package org.traccar.api; -import java.sql.SQLException; -import java.util.Collection; +import org.traccar.model.BaseModel; +import org.traccar.model.User; +import org.traccar.storage.StorageException; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Condition; +import org.traccar.storage.query.Request; import javax.ws.rs.GET; import javax.ws.rs.QueryParam; - -import org.traccar.Context; -import org.traccar.database.BaseObjectManager; -import org.traccar.model.BaseModel; +import java.util.Collection; +import java.util.LinkedList; public class SimpleObjectResource extends BaseObjectResource { @@ -34,10 +36,18 @@ public class SimpleObjectResource extends BaseObjectResourc @GET public Collection get( - @QueryParam("all") boolean all, @QueryParam("userId") long userId) throws SQLException { + @QueryParam("all") boolean all, @QueryParam("userId") long userId) throws StorageException { + + var conditions = new LinkedList(); + + if (all) { + permissionsService.checkAdmin(getUserId()); + } else { + permissionsService.checkUser(getUserId(), userId); + conditions.add(new Condition.Permission(User.class, userId, baseClass)); + } - BaseObjectManager manager = Context.getManager(getBaseClass()); - return manager.getItems(getSimpleManagerItems(manager, all, userId)); + return storage.getObjects(baseClass, new Request(new Columns.All(), Condition.merge(conditions))); } } diff --git a/src/main/java/org/traccar/api/resource/DeviceResource.java b/src/main/java/org/traccar/api/resource/DeviceResource.java index 9436b59f6..309308e75 100644 --- a/src/main/java/org/traccar/api/resource/DeviceResource.java +++ b/src/main/java/org/traccar/api/resource/DeviceResource.java @@ -31,8 +31,6 @@ import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; - -import java.sql.SQLException; import java.util.Collection; import java.util.HashSet; import java.util.List; @@ -51,7 +49,7 @@ public class DeviceResource extends BaseObjectResource { public Collection get( @QueryParam("all") boolean all, @QueryParam("userId") long userId, @QueryParam("uniqueId") List uniqueIds, - @QueryParam("id") List deviceIds) throws SQLException { + @QueryParam("id") List deviceIds) { DeviceManager deviceManager = Context.getDeviceManager(); Set result; if (all) { diff --git a/src/main/java/org/traccar/api/resource/EventResource.java b/src/main/java/org/traccar/api/resource/EventResource.java index 354d96e4f..38b101b25 100644 --- a/src/main/java/org/traccar/api/resource/EventResource.java +++ b/src/main/java/org/traccar/api/resource/EventResource.java @@ -15,6 +15,11 @@ */ package org.traccar.api.resource; +import org.traccar.Context; +import org.traccar.api.BaseResource; +import org.traccar.model.Event; +import org.traccar.storage.StorageException; + import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.Path; @@ -24,13 +29,6 @@ import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import org.traccar.Context; -import org.traccar.api.BaseResource; -import org.traccar.model.Event; -import org.traccar.model.Geofence; -import org.traccar.model.Maintenance; -import org.traccar.storage.StorageException; - @Path("events") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) @@ -44,12 +42,6 @@ public class EventResource extends BaseResource { throw new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build()); } Context.getPermissionsManager().checkDevice(getUserId(), event.getDeviceId()); - if (event.getGeofenceId() != 0) { - Context.getPermissionsManager().checkPermission(Geofence.class, getUserId(), event.getGeofenceId()); - } - if (event.getMaintenanceId() != 0) { - Context.getPermissionsManager().checkPermission(Maintenance.class, getUserId(), event.getMaintenanceId()); - } return event; } diff --git a/src/main/java/org/traccar/api/resource/NotificationResource.java b/src/main/java/org/traccar/api/resource/NotificationResource.java index 9631a52b7..cf4b66fa1 100644 --- a/src/main/java/org/traccar/api/resource/NotificationResource.java +++ b/src/main/java/org/traccar/api/resource/NotificationResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,7 +33,6 @@ import org.traccar.model.Notification; import org.traccar.model.Typed; import org.traccar.notification.MessageException; - @Path("notifications") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) diff --git a/src/main/java/org/traccar/api/resource/PositionResource.java b/src/main/java/org/traccar/api/resource/PositionResource.java index 511032402..941417231 100644 --- a/src/main/java/org/traccar/api/resource/PositionResource.java +++ b/src/main/java/org/traccar/api/resource/PositionResource.java @@ -55,7 +55,7 @@ public class PositionResource extends BaseResource { } else { Context.getPermissionsManager().checkDevice(getUserId(), deviceId); if (from != null && to != null) { - Context.getPermissionsManager().checkDisableReports(getUserId()); + permissionsService.checkReports(getUserId()); return Context.getDataManager().getPositions(deviceId, from, to); } else { return Collections.singleton(Context.getDeviceManager().getLastPosition(deviceId)); diff --git a/src/main/java/org/traccar/api/resource/ReportResource.java b/src/main/java/org/traccar/api/resource/ReportResource.java index 03df0d03a..901385d0d 100644 --- a/src/main/java/org/traccar/api/resource/ReportResource.java +++ b/src/main/java/org/traccar/api/resource/ReportResource.java @@ -99,7 +99,7 @@ public class ReportResource extends BaseResource { public Collection getRoute( @QueryParam("deviceId") final List deviceIds, @QueryParam("groupId") final List groupIds, @QueryParam("from") Date from, @QueryParam("to") Date to) throws StorageException { - Context.getPermissionsManager().checkDisableReports(getUserId()); + permissionsService.checkReports(getUserId()); LogAction.logReport(getUserId(), "route", from, to, deviceIds, groupIds); return Route.getObjects(getUserId(), deviceIds, groupIds, from, to); } @@ -111,7 +111,7 @@ public class ReportResource extends BaseResource { @QueryParam("deviceId") final List deviceIds, @QueryParam("groupId") final List groupIds, @QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("mail") boolean mail) throws StorageException, IOException { - Context.getPermissionsManager().checkDisableReports(getUserId()); + permissionsService.checkReports(getUserId()); return executeReport(getUserId(), mail, stream -> { LogAction.logReport(getUserId(), "route", from, to, deviceIds, groupIds); Route.getExcel(stream, getUserId(), deviceIds, groupIds, from, to); @@ -124,7 +124,7 @@ public class ReportResource extends BaseResource { @QueryParam("deviceId") final List deviceIds, @QueryParam("groupId") final List groupIds, @QueryParam("type") final List types, @QueryParam("from") Date from, @QueryParam("to") Date to) throws StorageException { - Context.getPermissionsManager().checkDisableReports(getUserId()); + permissionsService.checkReports(getUserId()); LogAction.logReport(getUserId(), "events", from, to, deviceIds, groupIds); return Events.getObjects(getUserId(), deviceIds, groupIds, types, from, to); } @@ -137,7 +137,7 @@ public class ReportResource extends BaseResource { @QueryParam("type") final List types, @QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("mail") boolean mail) throws StorageException, IOException { - Context.getPermissionsManager().checkDisableReports(getUserId()); + permissionsService.checkReports(getUserId()); return executeReport(getUserId(), mail, stream -> { LogAction.logReport(getUserId(), "events", from, to, deviceIds, groupIds); Events.getExcel(stream, getUserId(), deviceIds, groupIds, types, from, to); @@ -150,7 +150,7 @@ public class ReportResource extends BaseResource { @QueryParam("deviceId") final List deviceIds, @QueryParam("groupId") final List groupIds, @QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("daily") boolean daily) throws StorageException { - Context.getPermissionsManager().checkDisableReports(getUserId()); + permissionsService.checkReports(getUserId()); LogAction.logReport(getUserId(), "summary", from, to, deviceIds, groupIds); return Summary.getObjects(getUserId(), deviceIds, groupIds, from, to, daily); } @@ -163,7 +163,7 @@ public class ReportResource extends BaseResource { @QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("daily") boolean daily, @QueryParam("mail") boolean mail) throws StorageException, IOException { - Context.getPermissionsManager().checkDisableReports(getUserId()); + permissionsService.checkReports(getUserId()); return executeReport(getUserId(), mail, stream -> { LogAction.logReport(getUserId(), "summary", from, to, deviceIds, groupIds); Summary.getExcel(stream, getUserId(), deviceIds, groupIds, from, to, daily); @@ -176,7 +176,7 @@ public class ReportResource extends BaseResource { public Collection getTrips( @QueryParam("deviceId") final List deviceIds, @QueryParam("groupId") final List groupIds, @QueryParam("from") Date from, @QueryParam("to") Date to) throws StorageException { - Context.getPermissionsManager().checkDisableReports(getUserId()); + permissionsService.checkReports(getUserId()); LogAction.logReport(getUserId(), "trips", from, to, deviceIds, groupIds); return Trips.getObjects(getUserId(), deviceIds, groupIds, from, to); } @@ -188,7 +188,7 @@ public class ReportResource extends BaseResource { @QueryParam("deviceId") final List deviceIds, @QueryParam("groupId") final List groupIds, @QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("mail") boolean mail) throws StorageException, IOException { - Context.getPermissionsManager().checkDisableReports(getUserId()); + permissionsService.checkReports(getUserId()); return executeReport(getUserId(), mail, stream -> { LogAction.logReport(getUserId(), "trips", from, to, deviceIds, groupIds); Trips.getExcel(stream, getUserId(), deviceIds, groupIds, from, to); @@ -201,7 +201,7 @@ public class ReportResource extends BaseResource { public Collection getStops( @QueryParam("deviceId") final List deviceIds, @QueryParam("groupId") final List groupIds, @QueryParam("from") Date from, @QueryParam("to") Date to) throws StorageException { - Context.getPermissionsManager().checkDisableReports(getUserId()); + permissionsService.checkReports(getUserId()); LogAction.logReport(getUserId(), "stops", from, to, deviceIds, groupIds); return Stops.getObjects(getUserId(), deviceIds, groupIds, from, to); } @@ -213,7 +213,7 @@ public class ReportResource extends BaseResource { @QueryParam("deviceId") final List deviceIds, @QueryParam("groupId") final List groupIds, @QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("mail") boolean mail) throws StorageException, IOException { - Context.getPermissionsManager().checkDisableReports(getUserId()); + permissionsService.checkReports(getUserId()); return executeReport(getUserId(), mail, stream -> { LogAction.logReport(getUserId(), "stops", from, to, deviceIds, groupIds); Stops.getExcel(stream, getUserId(), deviceIds, groupIds, from, to); diff --git a/src/main/java/org/traccar/api/security/PermissionsService.java b/src/main/java/org/traccar/api/security/PermissionsService.java new file mode 100644 index 000000000..e39b8808f --- /dev/null +++ b/src/main/java/org/traccar/api/security/PermissionsService.java @@ -0,0 +1,136 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.api.security; + +import org.traccar.model.Calendar; +import org.traccar.model.Command; +import org.traccar.model.Device; +import org.traccar.model.Group; +import org.traccar.model.GroupedModel; +import org.traccar.model.ScheduledModel; +import org.traccar.model.Server; +import org.traccar.model.User; +import org.traccar.storage.Storage; +import org.traccar.storage.StorageException; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Condition; +import org.traccar.storage.query.Request; + +import javax.inject.Inject; + +public class PermissionsService { + + private final Storage storage; + + private Server server; + private User user; + + @Inject + public PermissionsService(Storage storage) { + this.storage = storage; + } + + private Server getServer() throws StorageException { + if (server == null) { + server = storage.getObject( + Server.class, new Request(new Columns.All())); + } + return server; + } + + private User getUser(long userId) throws StorageException { + if (user == null) { + user = storage.getObject( + User.class, new Request(new Columns.All(), new Condition.Equals("id", "id", userId))); + } + return user; + } + + public void checkAdmin(long userId) throws StorageException, SecurityException { + if (!getUser(userId).getAdministrator()) { + throw new SecurityException("Account is readonly"); + } + } + + public void checkReports(long userId) throws StorageException, SecurityException { + if (!getUser(userId).getAdministrator() + && (server.getDisableReports() || getUser(userId).getDisableReports())) { + throw new SecurityException("Reports are disabled"); + } + } + + public void checkEdit(long userId, Class clazz, boolean addition) throws StorageException, SecurityException { + if (!getUser(userId).getAdministrator()) { + boolean denied = false; + if (getServer().getReadonly() || getUser(userId).getReadonly()) { + denied = true; + } else if (clazz.equals(Device.class)) { + denied = getServer().getDeviceReadonly() || getUser(userId).getDeviceReadonly(); + if (addition) { + int deviceCount = storage.getPermissions(User.class, userId, Device.class).size(); + denied = deviceCount >= getUser(userId).getDeviceLimit(); + } + } else if (clazz.equals(Command.class)) { + denied = getServer().getLimitCommands() || getUser(userId).getLimitCommands(); + } + if (denied) { + throw new SecurityException("Write access denied"); + } + } + } + + public void checkEdit(long userId, Object object, boolean addition) throws StorageException, SecurityException { + if (!getUser(userId).getAdministrator()) { + checkEdit(userId, object.getClass(), addition); + boolean denied = false; + if (object instanceof GroupedModel) { + long groupId = ((GroupedModel) object).getGroupId(); + if (groupId > 0) { + denied = storage.getPermissions(User.class, userId, Group.class, groupId).isEmpty(); + // TODO TEST NESTED GROUP PERMISSION + } + } + if (object instanceof ScheduledModel) { + long calendarId = ((ScheduledModel) object).getCalendarId(); + if (calendarId > 0) { + denied = storage.getPermissions(User.class, userId, Calendar.class, calendarId).isEmpty(); + } + } + if (denied) { + throw new SecurityException("Write access denied"); + } + } + } + + public void checkUser(long userId, long managedUserId) throws StorageException, SecurityException { + if (userId != managedUserId && !getUser(userId).getAdministrator()) { + if (!getUser(userId).getManager() + || storage.getPermissions(User.class, userId, User.class, managedUserId).isEmpty()) { + throw new SecurityException("User access denied"); + } + } + } + + public void checkPermission( + Class clazz, long userId, long objectId) throws StorageException, SecurityException { + if (!getUser(userId).getAdministrator() + && storage.getPermissions(User.class, userId, clazz, objectId).isEmpty()) { + // TODO handle nested objects + throw new SecurityException(clazz.getSimpleName() + " access denied"); + } + } + +} -- cgit v1.2.3