aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton Tananaev <anton.tananaev@gmail.com>2017-01-18 00:33:59 +1300
committerGitHub <noreply@github.com>2017-01-18 00:33:59 +1300
commit2cdc0c93b759dfbdaff0c5577862b151d9aadf7f (patch)
treeb397ec630e858c76d7ea3ca29305def271f08f62
parent09a5ceb7b1681cb50338f37f775e13a5121e4385 (diff)
parent68abcc05ddd3633167f4e1c45d0ae9cf05dbcc43 (diff)
downloadtraccar-server-2cdc0c93b759dfbdaff0c5577862b151d9aadf7f.tar.gz
traccar-server-2cdc0c93b759dfbdaff0c5577862b151d9aadf7f.tar.bz2
traccar-server-2cdc0c93b759dfbdaff0c5577862b151d9aadf7f.zip
Merge pull request #2801 from Abyss777/manager
Implement user management
-rw-r--r--schema/changelog-3.10.xml16
-rw-r--r--setup/default.xml17
-rw-r--r--src/org/traccar/api/resource/CalendarResource.java12
-rw-r--r--src/org/traccar/api/resource/DevicePermissionResource.java13
-rw-r--r--src/org/traccar/api/resource/DeviceResource.java18
-rw-r--r--src/org/traccar/api/resource/GeofenceResource.java14
-rw-r--r--src/org/traccar/api/resource/GroupPermissionResource.java10
-rw-r--r--src/org/traccar/api/resource/GroupResource.java10
-rw-r--r--src/org/traccar/api/resource/UserPermissionResource.java56
-rw-r--r--src/org/traccar/api/resource/UserResource.java38
-rw-r--r--src/org/traccar/database/CalendarManager.java14
-rw-r--r--src/org/traccar/database/DataManager.java22
-rw-r--r--src/org/traccar/database/DeviceManager.java20
-rw-r--r--src/org/traccar/database/GeofenceManager.java11
-rw-r--r--src/org/traccar/database/PermissionsManager.java123
-rw-r--r--src/org/traccar/model/User.java12
-rw-r--r--src/org/traccar/model/UserPermission.java41
17 files changed, 391 insertions, 56 deletions
diff --git a/schema/changelog-3.10.xml b/schema/changelog-3.10.xml
index d39b36112..137b3bc82 100644
--- a/schema/changelog-3.10.xml
+++ b/schema/changelog-3.10.xml
@@ -48,5 +48,21 @@
<column name="network" type="VARCHAR(4000)" />
</addColumn>
+ <addColumn tableName="users">
+ <column name="userlimit" type="INT" defaultValueNumeric="0" />
+ </addColumn>
+
+ <createTable tableName="user_user">
+ <column name="userid" type="INT">
+ <constraints nullable="false" />
+ </column>
+ <column name="manageduserid" type="INT">
+ <constraints nullable="false" />
+ </column>
+ </createTable>
+
+ <addForeignKeyConstraint baseTableName="user_user" baseColumnNames="userid" constraintName="fk_user_user_userid" referencedTableName="users" referencedColumnNames="id" onDelete="CASCADE" />
+ <addForeignKeyConstraint baseTableName="user_user" baseColumnNames="manageduserid" constraintName="fk_user_user_manageduserid" referencedTableName="users" referencedColumnNames="id" onDelete="CASCADE" />
+
</changeSet>
</databaseChangeLog>
diff --git a/setup/default.xml b/setup/default.xml
index 1572ae60c..0ab3163f0 100644
--- a/setup/default.xml
+++ b/setup/default.xml
@@ -64,8 +64,8 @@
</entry>
<entry key='database.insertUser'>
- INSERT INTO users (name, email, hashedPassword, salt, readonly, admin, map, distanceUnit, speedUnit, latitude, longitude, zoom, twelveHourFormat, coordinateFormat, disabled, expirationTime, deviceLimit, token, attributes)
- VALUES (:name, :email, :hashedPassword, :salt, :readonly, :admin, :map, :distanceUnit, :speedUnit, :latitude, :longitude, :zoom, :twelveHourFormat, :coordinateFormat, :disabled, :expirationTime, :deviceLimit, :token, :attributes)
+ INSERT INTO users (name, email, hashedPassword, salt, readonly, admin, map, distanceUnit, speedUnit, latitude, longitude, zoom, twelveHourFormat, coordinateFormat, disabled, expirationTime, deviceLimit, userLimit, token, attributes)
+ VALUES (:name, :email, :hashedPassword, :salt, :readonly, :admin, :map, :distanceUnit, :speedUnit, :latitude, :longitude, :zoom, :twelveHourFormat, :coordinateFormat, :disabled, :expirationTime, :deviceLimit, :userLimit, :token, :attributes)
</entry>
<entry key='database.updateUser'>
@@ -85,6 +85,7 @@
disabled = :disabled,
expirationTime = :expirationTime,
deviceLimit = :deviceLimit,
+ userLimit = :userLimit,
token = :token,
attributes = :attributes
WHERE id = :id
@@ -350,6 +351,18 @@
<entry key='database.unlinkCalendar'>
DELETE FROM user_calendar WHERE userId = :userId AND calendarId = :calendarId
</entry>
+
+ <entry key='database.selectUserPermissions'>
+ SELECT userId, managedUserId FROM user_user
+ </entry>
+
+ <entry key='database.linkUser'>
+ INSERT INTO user_user (userId, managedUserId) VALUES (:userId, :managedUserId)
+ </entry>
+
+ <entry key='database.unlinkUser'>
+ DELETE FROM user_user WHERE userId = :userId AND managedUserId = :managedUserId
+ </entry>
<!-- PROTOCOL CONFIG -->
diff --git a/src/org/traccar/api/resource/CalendarResource.java b/src/org/traccar/api/resource/CalendarResource.java
index 0a9bb5daf..641d3b4b5 100644
--- a/src/org/traccar/api/resource/CalendarResource.java
+++ b/src/org/traccar/api/resource/CalendarResource.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- * Copyright 2016 Andrey Kunitsyn (andrey@traccar.org)
+ * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -45,8 +45,12 @@ public class CalendarResource extends BaseResource {
@QueryParam("all") boolean all, @QueryParam("userId") long userId) throws SQLException {
if (all) {
- Context.getPermissionsManager().checkAdmin(getUserId());
- return Context.getCalendarManager().getAllCalendars();
+ if (Context.getPermissionsManager().isAdmin(getUserId())) {
+ return Context.getCalendarManager().getAllCalendars();
+ } else {
+ Context.getPermissionsManager().checkManager(getUserId());
+ return Context.getCalendarManager().getManagedCalendars(getUserId());
+ }
} else {
if (userId == 0) {
userId = getUserId();
diff --git a/src/org/traccar/api/resource/DevicePermissionResource.java b/src/org/traccar/api/resource/DevicePermissionResource.java
index 3b89507fa..af38676b0 100644
--- a/src/org/traccar/api/resource/DevicePermissionResource.java
+++ b/src/org/traccar/api/resource/DevicePermissionResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2017 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.
@@ -35,7 +35,12 @@ public class DevicePermissionResource extends BaseResource {
@POST
public Response add(DevicePermission entity) throws SQLException {
- Context.getPermissionsManager().checkAdmin(getUserId());
+ Context.getPermissionsManager().checkReadonly(getUserId());
+ Context.getPermissionsManager().checkUser(getUserId(), entity.getUserId());
+ Context.getPermissionsManager().checkDevice(getUserId(), entity.getDeviceId());
+ if (!Context.getPermissionsManager().isAdmin(getUserId())) {
+ Context.getPermissionsManager().checkDeviceLimit(entity.getUserId());
+ }
Context.getDataManager().linkDevice(entity.getUserId(), entity.getDeviceId());
Context.getPermissionsManager().refreshPermissions();
if (Context.getGeofenceManager() != null) {
@@ -46,7 +51,9 @@ public class DevicePermissionResource extends BaseResource {
@DELETE
public Response remove(DevicePermission entity) throws SQLException {
- Context.getPermissionsManager().checkAdmin(getUserId());
+ Context.getPermissionsManager().checkReadonly(getUserId());
+ Context.getPermissionsManager().checkUser(getUserId(), entity.getUserId());
+ Context.getPermissionsManager().checkDevice(getUserId(), entity.getDeviceId());
Context.getDataManager().unlinkDevice(entity.getUserId(), entity.getDeviceId());
Context.getPermissionsManager().refreshPermissions();
if (Context.getGeofenceManager() != null) {
diff --git a/src/org/traccar/api/resource/DeviceResource.java b/src/org/traccar/api/resource/DeviceResource.java
index e4ecd3625..c9680ac77 100644
--- a/src/org/traccar/api/resource/DeviceResource.java
+++ b/src/org/traccar/api/resource/DeviceResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2017 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.
@@ -44,8 +44,12 @@ public class DeviceResource extends BaseResource {
public Collection<Device> get(
@QueryParam("all") boolean all, @QueryParam("userId") long userId) throws SQLException {
if (all) {
- Context.getPermissionsManager().checkAdmin(getUserId());
- return Context.getDeviceManager().getAllDevices();
+ if (Context.getPermissionsManager().isAdmin(getUserId())) {
+ return Context.getDeviceManager().getAllDevices();
+ } else {
+ Context.getPermissionsManager().checkManager(getUserId());
+ return Context.getDeviceManager().getManagedDevices(getUserId());
+ }
} else {
if (userId == 0) {
userId = getUserId();
@@ -58,13 +62,7 @@ public class DeviceResource extends BaseResource {
@POST
public Response add(Device entity) throws SQLException {
Context.getPermissionsManager().checkReadonly(getUserId());
- int deviceLimit = Context.getPermissionsManager().getUser(getUserId()).getDeviceLimit();
- if (deviceLimit != 0) {
- int deviceCount = Context.getPermissionsManager().getDevicePermissions(getUserId()).size();
- if (deviceCount >= deviceLimit) {
- throw new SecurityException("User device limit reached");
- }
- }
+ Context.getPermissionsManager().checkDeviceLimit(getUserId());
Context.getDeviceManager().addDevice(entity);
Context.getDataManager().linkDevice(getUserId(), entity.getId());
Context.getPermissionsManager().refreshPermissions();
diff --git a/src/org/traccar/api/resource/GeofenceResource.java b/src/org/traccar/api/resource/GeofenceResource.java
index 591908813..d5acf106a 100644
--- a/src/org/traccar/api/resource/GeofenceResource.java
+++ b/src/org/traccar/api/resource/GeofenceResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2017 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.
@@ -52,16 +52,20 @@ public class GeofenceResource extends BaseResource {
geofenceManager.refreshGeofences();
}
- Set<Long> result;
+ Set<Long> result = new HashSet<>();
if (all) {
- Context.getPermissionsManager().checkAdmin(getUserId());
- result = new HashSet<>(geofenceManager.getAllGeofencesIds());
+ if (Context.getPermissionsManager().isAdmin(getUserId())) {
+ result.addAll(geofenceManager.getAllGeofencesIds());
+ } else {
+ Context.getPermissionsManager().checkManager(getUserId());
+ result.addAll(geofenceManager.getManagedGeofencesIds(getUserId()));
+ }
} else {
if (userId == 0) {
userId = getUserId();
}
Context.getPermissionsManager().checkUser(getUserId(), userId);
- result = new HashSet<>(geofenceManager.getUserGeofencesIds(userId));
+ result.addAll(geofenceManager.getUserGeofencesIds(userId));
}
if (groupId != 0) {
diff --git a/src/org/traccar/api/resource/GroupPermissionResource.java b/src/org/traccar/api/resource/GroupPermissionResource.java
index 07f101765..61a725222 100644
--- a/src/org/traccar/api/resource/GroupPermissionResource.java
+++ b/src/org/traccar/api/resource/GroupPermissionResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2017 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.
@@ -35,7 +35,9 @@ public class GroupPermissionResource extends BaseResource {
@POST
public Response add(GroupPermission entity) throws SQLException {
- Context.getPermissionsManager().checkAdmin(getUserId());
+ Context.getPermissionsManager().checkReadonly(getUserId());
+ Context.getPermissionsManager().checkUser(getUserId(), entity.getUserId());
+ Context.getPermissionsManager().checkGroup(getUserId(), entity.getGroupId());
Context.getDataManager().linkGroup(entity.getUserId(), entity.getGroupId());
Context.getPermissionsManager().refreshPermissions();
if (Context.getGeofenceManager() != null) {
@@ -46,7 +48,9 @@ public class GroupPermissionResource extends BaseResource {
@DELETE
public Response remove(GroupPermission entity) throws SQLException {
- Context.getPermissionsManager().checkAdmin(getUserId());
+ Context.getPermissionsManager().checkReadonly(getUserId());
+ Context.getPermissionsManager().checkUser(getUserId(), entity.getUserId());
+ Context.getPermissionsManager().checkGroup(getUserId(), entity.getGroupId());
Context.getDataManager().unlinkGroup(entity.getUserId(), entity.getGroupId());
Context.getPermissionsManager().refreshPermissions();
if (Context.getGeofenceManager() != null) {
diff --git a/src/org/traccar/api/resource/GroupResource.java b/src/org/traccar/api/resource/GroupResource.java
index c98a20b7e..ceba69105 100644
--- a/src/org/traccar/api/resource/GroupResource.java
+++ b/src/org/traccar/api/resource/GroupResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2017 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.
@@ -42,8 +42,12 @@ public class GroupResource extends BaseResource {
public Collection<Group> get(
@QueryParam("all") boolean all, @QueryParam("userId") long userId) throws SQLException {
if (all) {
- Context.getPermissionsManager().checkAdmin(getUserId());
- return Context.getDeviceManager().getAllGroups();
+ if (Context.getPermissionsManager().isAdmin(getUserId())) {
+ return Context.getDeviceManager().getAllGroups();
+ } else {
+ Context.getPermissionsManager().checkManager(getUserId());
+ return Context.getDeviceManager().getManagedGroups(getUserId());
+ }
} else {
if (userId == 0) {
userId = getUserId();
diff --git a/src/org/traccar/api/resource/UserPermissionResource.java b/src/org/traccar/api/resource/UserPermissionResource.java
new file mode 100644
index 000000000..a97c4a665
--- /dev/null
+++ b/src/org/traccar/api/resource/UserPermissionResource.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.api.resource;
+
+import java.sql.SQLException;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+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.UserPermission;
+
+@Path("permissions/users")
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class UserPermissionResource extends BaseResource {
+
+ @POST
+ public Response add(UserPermission entity) throws SQLException {
+ Context.getPermissionsManager().checkAdmin(getUserId());
+ if (entity.getUserId() != entity.getManagedUserId()) {
+ Context.getDataManager().linkUser(entity.getUserId(), entity.getManagedUserId());
+ Context.getPermissionsManager().refreshUserPermissions();
+ }
+ return Response.ok(entity).build();
+ }
+
+ @DELETE
+ public Response remove(UserPermission entity) throws SQLException {
+ Context.getPermissionsManager().checkAdmin(getUserId());
+ Context.getDataManager().unlinkUser(entity.getUserId(), entity.getManagedUserId());
+ Context.getPermissionsManager().refreshUserPermissions();
+ return Response.noContent().build();
+ }
+
+}
diff --git a/src/org/traccar/api/resource/UserResource.java b/src/org/traccar/api/resource/UserResource.java
index 678daac9b..dd59a11ee 100644
--- a/src/org/traccar/api/resource/UserResource.java
+++ b/src/org/traccar/api/resource/UserResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2017 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.
@@ -28,6 +28,7 @@ import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
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;
@@ -40,25 +41,42 @@ import java.util.Date;
public class UserResource extends BaseResource {
@GET
- public Collection<User> get() throws SQLException {
- Context.getPermissionsManager().checkAdmin(getUserId());
- return Context.getPermissionsManager().getUsers();
+ public Collection<User> get(@QueryParam("userId") long userId) throws SQLException {
+ if (Context.getPermissionsManager().isAdmin(getUserId())) {
+ if (userId != 0) {
+ return Context.getPermissionsManager().getUsers(userId);
+ } else {
+ return Context.getPermissionsManager().getAllUsers();
+ }
+ } else if (Context.getPermissionsManager().isManager(getUserId())) {
+ return Context.getPermissionsManager().getManagedUsers(getUserId());
+ } else {
+ throw new SecurityException("Admin or manager access required");
+ }
}
@PermitAll
@POST
public Response add(User entity) throws SQLException {
if (!Context.getPermissionsManager().isAdmin(getUserId())) {
- Context.getPermissionsManager().checkRegistration(getUserId());
Context.getPermissionsManager().checkUserUpdate(getUserId(), new User(), entity);
- entity.setDeviceLimit(Context.getConfig().getInteger("users.defaultDeviceLimit"));
- int expirationDays = Context.getConfig().getInteger("users.defaultExpirationDays");
- if (expirationDays > 0) {
- entity.setExpirationTime(
- new Date(System.currentTimeMillis() + (long) expirationDays * 24 * 3600 * 1000));
+ if (Context.getPermissionsManager().isManager(getUserId())) {
+ Context.getPermissionsManager().checkUserLimit(getUserId());
+ } else {
+ Context.getPermissionsManager().checkRegistration(getUserId());
+ entity.setDeviceLimit(Context.getConfig().getInteger("users.defaultDeviceLimit"));
+ int expirationDays = Context.getConfig().getInteger("users.defaultExpirationDays");
+ if (expirationDays > 0) {
+ entity.setExpirationTime(
+ new Date(System.currentTimeMillis() + (long) expirationDays * 24 * 3600 * 1000));
+ }
}
}
Context.getPermissionsManager().addUser(entity);
+ if (Context.getPermissionsManager().isManager(getUserId())) {
+ Context.getDataManager().linkUser(getUserId(), entity.getId());
+ }
+ Context.getPermissionsManager().refreshUserPermissions();
if (Context.getNotificationManager() != null) {
Context.getNotificationManager().refresh();
}
diff --git a/src/org/traccar/database/CalendarManager.java b/src/org/traccar/database/CalendarManager.java
index 3e95f6698..31d484327 100644
--- a/src/org/traccar/database/CalendarManager.java
+++ b/src/org/traccar/database/CalendarManager.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- * Copyright 2016 Andrey Kunitsyn (andrey@traccar.org)
+ * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import org.traccar.Context;
import org.traccar.helper.Log;
import org.traccar.model.Calendar;
import org.traccar.model.CalendarPermission;
@@ -69,6 +70,15 @@ public class CalendarManager {
return result;
}
+ public Collection<Calendar> getManagedCalendars(long userId) {
+ ArrayList<Calendar> result = new ArrayList<>();
+ result.addAll(getUserCalendars(userId));
+ for (long managedUserId : Context.getPermissionsManager().getUserPermissions(userId)) {
+ result.addAll(getUserCalendars(managedUserId));
+ }
+ return result;
+ }
+
public final void refreshUserCalendars() {
if (dataManager != null) {
try {
diff --git a/src/org/traccar/database/DataManager.java b/src/org/traccar/database/DataManager.java
index 278109229..8337762f7 100644
--- a/src/org/traccar/database/DataManager.java
+++ b/src/org/traccar/database/DataManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2017 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.
@@ -51,6 +51,7 @@ import org.traccar.model.Position;
import org.traccar.model.Server;
import org.traccar.model.Statistics;
import org.traccar.model.User;
+import org.traccar.model.UserPermission;
import org.traccar.model.DeviceGeofence;
import org.traccar.model.GeofencePermission;
@@ -527,4 +528,23 @@ public class DataManager {
.setLong("calendarId", calendarId)
.executeUpdate();
}
+
+ public Collection<UserPermission> getUserPermissions() throws SQLException {
+ return QueryBuilder.create(dataSource, getQuery("database.selectUserPermissions"))
+ .executeQuery(UserPermission.class);
+ }
+
+ public void linkUser(long userId, long managedUserId) throws SQLException {
+ QueryBuilder.create(dataSource, getQuery("database.linkUser"))
+ .setLong("userId", userId)
+ .setLong("managedUserId", managedUserId)
+ .executeUpdate();
+ }
+
+ public void unlinkUser(long userId, long managedUserId) throws SQLException {
+ QueryBuilder.create(dataSource, getQuery("database.unlinkUser"))
+ .setLong("userId", userId)
+ .setLong("managedUserId", managedUserId)
+ .executeUpdate();
+ }
}
diff --git a/src/org/traccar/database/DeviceManager.java b/src/org/traccar/database/DeviceManager.java
index c70e67231..8e75903db 100644
--- a/src/org/traccar/database/DeviceManager.java
+++ b/src/org/traccar/database/DeviceManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2017 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.
@@ -160,6 +160,15 @@ public class DeviceManager implements IdentityManager {
return devices;
}
+ public Collection<Device> getManagedDevices(long userId) throws SQLException {
+ Collection<Device> devices = new ArrayList<>();
+ devices.addAll(getDevices(userId));
+ for (long managedUserId : Context.getPermissionsManager().getUserPermissions(userId)) {
+ devices.addAll(getDevices(managedUserId));
+ }
+ return devices;
+ }
+
public void addDevice(Device device) throws SQLException {
dataManager.addDevice(device);
@@ -289,6 +298,15 @@ public class DeviceManager implements IdentityManager {
return groups;
}
+ public Collection<Group> getManagedGroups(long userId) throws SQLException {
+ Collection<Group> groups = new ArrayList<>();
+ groups.addAll(getGroups(userId));
+ for (long managedUserId : Context.getPermissionsManager().getUserPermissions(userId)) {
+ groups.addAll(getGroups(managedUserId));
+ }
+ return groups;
+ }
+
private void checkGroupCycles(Group group) {
Set<Long> groups = new HashSet<>();
while (group != null) {
diff --git a/src/org/traccar/database/GeofenceManager.java b/src/org/traccar/database/GeofenceManager.java
index e2e0c12d4..b8e6a5d73 100644
--- a/src/org/traccar/database/GeofenceManager.java
+++ b/src/org/traccar/database/GeofenceManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2017 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.
@@ -242,6 +242,15 @@ public class GeofenceManager {
}
}
+ public final Set<Long> getManagedGeofencesIds(long userId) {
+ Set<Long> geofences = new HashSet<>();
+ geofences.addAll(getUserGeofencesIds(userId));
+ for (long managedUserId : Context.getPermissionsManager().getUserPermissions(userId)) {
+ geofences.addAll(getUserGeofencesIds(managedUserId));
+ }
+ return geofences;
+ }
+
public final Collection<Geofence> getGeofences(Set<Long> geofencesIds) {
geofencesLock.readLock().lock();
try {
diff --git a/src/org/traccar/database/PermissionsManager.java b/src/org/traccar/database/PermissionsManager.java
index 6c0610655..4a5f759a8 100644
--- a/src/org/traccar/database/PermissionsManager.java
+++ b/src/org/traccar/database/PermissionsManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,8 +23,10 @@ import org.traccar.model.Group;
import org.traccar.model.GroupPermission;
import org.traccar.model.Server;
import org.traccar.model.User;
+import org.traccar.model.UserPermission;
import java.sql.SQLException;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
@@ -47,6 +49,8 @@ public class PermissionsManager {
private final Map<Long, Set<Long>> deviceUsers = new HashMap<>();
private final Map<Long, Set<Long>> groupDevices = new HashMap<>();
+ private final Map<Long, Set<Long>> userPermissions = new HashMap<>();
+
public Set<Long> getGroupPermissions(long userId) {
if (!groupPermissions.containsKey(userId)) {
groupPermissions.put(userId, new HashSet<Long>());
@@ -75,10 +79,18 @@ public class PermissionsManager {
return groupDevices.get(groupId);
}
+ public Set<Long> getUserPermissions(long userId) {
+ if (!userPermissions.containsKey(userId)) {
+ userPermissions.put(userId, new HashSet<Long>());
+ }
+ return userPermissions.get(userId);
+ }
+
public PermissionsManager(DataManager dataManager) {
this.dataManager = dataManager;
refreshUsers();
refreshPermissions();
+ refreshUserPermissions();
}
public final void refreshUsers() {
@@ -97,6 +109,17 @@ public class PermissionsManager {
}
}
+ public final void refreshUserPermissions() {
+ userPermissions.clear();
+ try {
+ for (UserPermission permission : dataManager.getUserPermissions()) {
+ getUserPermissions(permission.getUserId()).add(permission.getManagedUserId());
+ }
+ } catch (SQLException error) {
+ Log.warning(error);
+ }
+ }
+
public final void refreshPermissions() {
groupPermissions.clear();
devicePermissions.clear();
@@ -146,6 +169,39 @@ public class PermissionsManager {
}
}
+ public boolean isManager(long userId) {
+ return users.containsKey(userId) && users.get(userId).getUserLimit() > 0;
+ }
+
+ public void checkManager(long userId) throws SecurityException {
+ if (!isManager(userId)) {
+ throw new SecurityException("Manager access required");
+ }
+ }
+
+ public void checkManager(long userId, long managedUserId) throws SecurityException {
+ checkManager(userId);
+ if (!userPermissions.get(userId).contains(managedUserId)) {
+ throw new SecurityException("User access denied");
+ }
+ }
+
+ public void checkUserLimit(long userId) throws SecurityException {
+ if (!isAdmin(userId) && userPermissions.get(userId).size() >= users.get(userId).getUserLimit()) {
+ throw new SecurityException("Manager user limit reached");
+ }
+ }
+
+ public void checkDeviceLimit(long userId) throws SecurityException {
+ int deviceLimit = users.get(userId).getDeviceLimit();
+ if (deviceLimit != 0) {
+ int deviceCount = getDevicePermissions(userId).size();
+ if (deviceCount >= deviceLimit) {
+ throw new SecurityException("User device limit reached");
+ }
+ }
+ }
+
public boolean isReadonly(long userId) {
return users.containsKey(userId) && users.get(userId).getReadonly();
}
@@ -168,29 +224,49 @@ public class PermissionsManager {
public void checkUserUpdate(long userId, User before, User after) throws SecurityException {
if (before.getAdmin() != after.getAdmin()
- || before.getReadonly() != after.getReadonly()
- || before.getDisabled() != after.getDisabled()
|| before.getDeviceLimit() != after.getDeviceLimit()
+ || before.getUserLimit() != after.getUserLimit()) {
+ checkAdmin(userId);
+ }
+ if (before.getReadonly() != after.getReadonly()
+ || before.getDisabled() != after.getDisabled()
|| !Objects.equals(before.getExpirationTime(), after.getExpirationTime())
|| !Objects.equals(before.getToken(), after.getToken())) {
- checkAdmin(userId);
+ if (userId == after.getId()) {
+ checkAdmin(userId);
+ }
+ if (!isAdmin(userId)) {
+ checkManager(userId);
+ }
}
}
- public void checkUser(long userId, long otherUserId) throws SecurityException {
- if (userId != otherUserId) {
- checkAdmin(userId);
+ public void checkUser(long userId, long managedUserId) throws SecurityException {
+ if (userId != managedUserId && !isAdmin(userId)) {
+ checkManager(userId, managedUserId);
}
}
public void checkGroup(long userId, long groupId) throws SecurityException {
- if (!getGroupPermissions(userId).contains(groupId)) {
+ if (!getGroupPermissions(userId).contains(groupId) && !isAdmin(userId)) {
+ checkManager(userId);
+ for (long managedUserId : getUserPermissions(userId)) {
+ if (getGroupPermissions(managedUserId).contains(groupId)) {
+ return;
+ }
+ }
throw new SecurityException("Group access denied");
}
}
public void checkDevice(long userId, long deviceId) throws SecurityException {
- if (!getDevicePermissions(userId).contains(deviceId)) {
+ if (!getDevicePermissions(userId).contains(deviceId) && !isAdmin(userId)) {
+ checkManager(userId);
+ for (long managedUserId : getUserPermissions(userId)) {
+ if (getDevicePermissions(managedUserId).contains(deviceId)) {
+ return;
+ }
+ }
throw new SecurityException("Device access denied");
}
}
@@ -203,12 +279,24 @@ public class PermissionsManager {
public void checkGeofence(long userId, long geofenceId) throws SecurityException {
if (!Context.getGeofenceManager().checkGeofence(userId, geofenceId) && !isAdmin(userId)) {
+ checkManager(userId);
+ for (long managedUserId : getUserPermissions(userId)) {
+ if (Context.getGeofenceManager().checkGeofence(managedUserId, geofenceId)) {
+ return;
+ }
+ }
throw new SecurityException("Geofence access denied");
}
}
public void checkCalendar(long userId, long calendarId) throws SecurityException {
if (!Context.getCalendarManager().checkCalendar(userId, calendarId) && !isAdmin(userId)) {
+ checkManager(userId);
+ for (long managedUserId : getUserPermissions(userId)) {
+ if (Context.getCalendarManager().checkCalendar(managedUserId, calendarId)) {
+ return;
+ }
+ }
throw new SecurityException("Calendar access denied");
}
}
@@ -222,10 +310,24 @@ public class PermissionsManager {
this.server = server;
}
- public Collection<User> getUsers() {
+ public Collection<User> getAllUsers() {
return users.values();
}
+ public Collection<User> getUsers(long userId) {
+ Collection<User> result = new ArrayList<>();
+ for (long managedUserId : getUserPermissions(userId)) {
+ result.add(users.get(managedUserId));
+ }
+ return result;
+ }
+
+ public Collection<User> getManagedUsers(long userId) {
+ Collection<User> result = getUsers(userId);
+ result.add(users.get(userId));
+ return result;
+ }
+
public User getUser(long userId) {
return users.get(userId);
}
@@ -257,6 +359,7 @@ public class PermissionsManager {
usersTokens.remove(users.get(userId).getToken());
users.remove(userId);
refreshPermissions();
+ refreshUserPermissions();
}
public User login(String email, String password) throws SQLException {
diff --git a/src/org/traccar/model/User.java b/src/org/traccar/model/User.java
index e6b9d663f..72fe2c9f9 100644
--- a/src/org/traccar/model/User.java
+++ b/src/org/traccar/model/User.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2017 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.
@@ -180,6 +180,16 @@ public class User extends Extensible {
this.deviceLimit = deviceLimit;
}
+ private int userLimit;
+
+ public int getUserLimit() {
+ return userLimit;
+ }
+
+ public void setUserLimit(int userLimit) {
+ this.userLimit = userLimit;
+ }
+
private String token;
public String getToken() {
diff --git a/src/org/traccar/model/UserPermission.java b/src/org/traccar/model/UserPermission.java
new file mode 100644
index 000000000..39ead5ef1
--- /dev/null
+++ b/src/org/traccar/model/UserPermission.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.model;
+
+public class UserPermission {
+
+ private long userId;
+
+ public long getUserId() {
+ return userId;
+ }
+
+ public void setUserId(long userId) {
+ this.userId = userId;
+ }
+
+ private long managedUserId;
+
+ public long getManagedUserId() {
+ return managedUserId;
+ }
+
+ public void setManagedUserId(long managedUserId) {
+ this.managedUserId = managedUserId;
+ }
+
+}