From 60095220900c1f6879f3b4eace449c96a896e485 Mon Sep 17 00:00:00 2001 From: Colin McDonald Date: Thu, 5 May 2016 19:35:45 -0400 Subject: [PATCH] Whoa stuff --- src/main/java/net/frozenorb/apiv3/APIv3.java | 13 ++--- .../apiv3/filters/LoggingFilter.java | 23 +++++++++ .../apiv3/filters/MetricsAfterFilter.java | 7 ++- .../apiv3/filters/MetricsBeforeFilter.java | 4 +- .../net/frozenorb/apiv3/models/Grant.java | 8 ++-- .../frozenorb/apiv3/models/Punishment.java | 6 +-- .../frozenorb/apiv3/models/ServerGroup.java | 4 +- .../java/net/frozenorb/apiv3/models/User.java | 7 +-- .../apiv3/routes/grants/DELETEGrant.java | 3 -- .../apiv3/routes/grants/POSTUserGrant.java | 33 +++++++------ .../routes/punishments/DELETEPunishment.java | 3 -- .../punishments/GETUserPunishments.java | 21 ++++++++ .../routes/punishments/POSTUserPunish.java | 31 ++++++++---- .../routes/users/DELETEUserPunishment.java | 48 +++++++++++++++++++ .../apiv3/routes/users/GETStaff.java | 8 ++-- .../apiv3/routes/users/GETUserDetails.java | 3 +- .../apiv3/utils/PermissionUtils.java | 9 ++-- 17 files changed, 169 insertions(+), 62 deletions(-) create mode 100644 src/main/java/net/frozenorb/apiv3/filters/LoggingFilter.java create mode 100644 src/main/java/net/frozenorb/apiv3/routes/punishments/GETUserPunishments.java create mode 100644 src/main/java/net/frozenorb/apiv3/routes/users/DELETEUserPunishment.java diff --git a/src/main/java/net/frozenorb/apiv3/APIv3.java b/src/main/java/net/frozenorb/apiv3/APIv3.java index 77f78e2..6471e37 100644 --- a/src/main/java/net/frozenorb/apiv3/APIv3.java +++ b/src/main/java/net/frozenorb/apiv3/APIv3.java @@ -24,10 +24,7 @@ import net.frozenorb.apiv3.routes.notificationTemplate.DELETENotificationTemplat import net.frozenorb.apiv3.routes.notificationTemplate.GETNotificationTemplate; import net.frozenorb.apiv3.routes.notificationTemplate.GETNotificationTemplates; import net.frozenorb.apiv3.routes.notificationTemplate.POSTNotificationTemplate; -import net.frozenorb.apiv3.routes.punishments.DELETEPunishment; -import net.frozenorb.apiv3.routes.punishments.GETPunishment; -import net.frozenorb.apiv3.routes.punishments.GETPunishments; -import net.frozenorb.apiv3.routes.punishments.POSTUserPunish; +import net.frozenorb.apiv3.routes.punishments.*; import net.frozenorb.apiv3.routes.ranks.DELETERank; import net.frozenorb.apiv3.routes.ranks.GETRank; import net.frozenorb.apiv3.routes.ranks.GETRanks; @@ -146,12 +143,14 @@ public final class APIv3 { private void setupHttp() { ipAddress(config.getProperty("http.address")); port(Integer.parseInt(config.getProperty("http.port"))); + // TODO: if threadPool == null use default value threadPool(Integer.parseInt(config.getProperty("http.workerThreads"))); before(new ContentTypeFilter()); before(new ActorAttributeFilter()); before(new AuthorizationFilter()); before(new MetricsBeforeFilter()); after(new MetricsAfterFilter()); + after(new LoggingFilter()); exception(Exception.class, new LoggingExceptionHandler()); // TODO: The commented out routes @@ -200,13 +199,14 @@ public final class APIv3 { get("/user/:id/details", new GETUserDetails(), gson::toJson); get("/user/:id/meta/:serverGroup", new GETUserMeta(), gson::toJson); get("/user/:id/grants", new GETUserGrants(), gson::toJson); + get("/user/:id/punishments", new GETUserPunishments(), gson::toJson); get("/user/:id/ipLog", new GETUserIPLog(), gson::toJson); get("/user/:id/requiresTOTP", new GETUserRequiresTOTP(), gson::toJson); get("/user/:id/verifyPassword", new GETUserVerifyPassword(), gson::toJson); get("/user/:id", new GETUser(), gson::toJson); post("/user/:id/verifyTOTP", new POSTUserVerifyTOTP(), gson::toJson); - post("/user/:id:/grant", new POSTUserGrant(), gson::toJson); - post("/user/:id:/punish", new POSTUserPunish(), gson::toJson); + post("/user/:id/grant", new POSTUserGrant(), gson::toJson); + post("/user/:id/punish", new POSTUserPunish(), gson::toJson); post("/user/:id/login", new POSTUserLogin(), gson::toJson); post("/user/:id/notify", new POSTUserNotify(), gson::toJson); post("/user/:id/register", new POSTUserRegister(), gson::toJson); @@ -214,6 +214,7 @@ public final class APIv3 { post("/user/confirmRegister/:emailToken", new POSTUserConfirmRegister(), gson::toJson); put("/user/:id/meta/:serverGroup", new PUTUserMeta(), gson::toJson); delete("/user/:id/meta/:serverGroup", new DELETEUserMeta(), gson::toJson); + delete("/user/:id/punishment", new DELETEUserPunishment(), gson::toJson); // There's no way to do a JSON 404 page w/o doing this :( get("/*", new NotFound(), gson::toJson); diff --git a/src/main/java/net/frozenorb/apiv3/filters/LoggingFilter.java b/src/main/java/net/frozenorb/apiv3/filters/LoggingFilter.java new file mode 100644 index 0000000..05de5bd --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/filters/LoggingFilter.java @@ -0,0 +1,23 @@ +package net.frozenorb.apiv3.filters; + +import com.codahale.metrics.Histogram; +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.Timer; +import lombok.extern.slf4j.Slf4j; +import net.frozenorb.apiv3.APIv3; +import spark.Filter; +import spark.Request; +import spark.Response; + +@Slf4j +public final class LoggingFilter implements Filter { + + public void handle(Request req, Response res) { + if (req.url().toLowerCase().contains("password=")) { + return; + } + + log.info(req.requestMethod().toUpperCase() + " " + req.url()); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/filters/MetricsAfterFilter.java b/src/main/java/net/frozenorb/apiv3/filters/MetricsAfterFilter.java index 174d059..11fcd99 100644 --- a/src/main/java/net/frozenorb/apiv3/filters/MetricsAfterFilter.java +++ b/src/main/java/net/frozenorb/apiv3/filters/MetricsAfterFilter.java @@ -8,15 +8,18 @@ import spark.Filter; import spark.Request; import spark.Response; +import java.util.concurrent.TimeUnit; + public final class MetricsAfterFilter implements Filter { private Histogram responseLengthMetric = APIv3.getMetrics().histogram(MetricRegistry.name("apiv3", "http", "responseLength")); + private Timer responseTimesMetric = APIv3.getMetrics().timer(MetricRegistry.name("apiv3", "http", "responseTimes")); public void handle(Request req, Response res) { responseLengthMetric.update(req.contentLength()); - Timer.Context timerMetric = req.attribute("timerMetric"); - timerMetric.stop(); + long started = req.attribute("requestStarted"); + responseTimesMetric.update(System.currentTimeMillis() - started, TimeUnit.MILLISECONDS); } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/filters/MetricsBeforeFilter.java b/src/main/java/net/frozenorb/apiv3/filters/MetricsBeforeFilter.java index 48b2c56..e49d57e 100644 --- a/src/main/java/net/frozenorb/apiv3/filters/MetricsBeforeFilter.java +++ b/src/main/java/net/frozenorb/apiv3/filters/MetricsBeforeFilter.java @@ -9,10 +9,8 @@ import spark.Response; public final class MetricsBeforeFilter implements Filter { - private Timer responseTimesMetric = APIv3.getMetrics().timer(MetricRegistry.name("apiv3", "http", "responseTimes")); - public void handle(Request req, Response res) { - req.attribute("timerMetric", responseTimesMetric.time()); + req.attribute("requestStarted", System.currentTimeMillis()); } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/models/Grant.java b/src/main/java/net/frozenorb/apiv3/models/Grant.java index 62ba897..d7007eb 100644 --- a/src/main/java/net/frozenorb/apiv3/models/Grant.java +++ b/src/main/java/net/frozenorb/apiv3/models/Grant.java @@ -19,7 +19,7 @@ public final class Grant { @Getter @Id private String id; @Getter @Indexed private UUID target; @Getter private String reason; - @Getter private Set scopes; + @Getter private Set scopes = new HashSet<>(); // So on things w/o scopes we still load properly (Morphia drops empty sets) @Getter @Indexed private String rank; @Getter private Date expiresAt; @@ -42,8 +42,8 @@ public final class Grant { this.reason = reason; this.scopes = new HashSet<>(Collections2.transform(scopes, ServerGroup::getId)); this.rank = rank.getId(); - this.expiresAt = (Date) expiresAt.clone(); - this.addedBy = addedBy.getId(); + this.expiresAt = expiresAt; + this.addedBy = addedBy == null ? null : addedBy.getId(); this.addedAt = new Date(); } @@ -63,7 +63,7 @@ public final class Grant { if (expiresAt == null) { return false; // Never expires } else { - return expiresAt.after(new Date()); + return expiresAt.before(new Date()); } } diff --git a/src/main/java/net/frozenorb/apiv3/models/Punishment.java b/src/main/java/net/frozenorb/apiv3/models/Punishment.java index 1b63e52..9627ce1 100644 --- a/src/main/java/net/frozenorb/apiv3/models/Punishment.java +++ b/src/main/java/net/frozenorb/apiv3/models/Punishment.java @@ -41,8 +41,8 @@ public final class Punishment { this.target = target.getId(); this.reason = reason; this.type = type; - this.expiresAt = (Date) expiresAt.clone(); - this.addedBy = addedBy.getId(); + this.expiresAt = expiresAt; + this.addedBy = addedBy == null ? null : addedBy.getId(); this.addedAt = new Date(); this.actorName = actor.getName(); this.actorType = actor.getType(); @@ -64,7 +64,7 @@ public final class Punishment { if (expiresAt == null) { return false; // Never expires } else { - return expiresAt.after(new Date()); + return expiresAt.before(new Date()); } } diff --git a/src/main/java/net/frozenorb/apiv3/models/ServerGroup.java b/src/main/java/net/frozenorb/apiv3/models/ServerGroup.java index ce7f22c..1badc0f 100644 --- a/src/main/java/net/frozenorb/apiv3/models/ServerGroup.java +++ b/src/main/java/net/frozenorb/apiv3/models/ServerGroup.java @@ -7,6 +7,7 @@ import net.frozenorb.apiv3.serialization.ExcludeFromReplies; import net.frozenorb.apiv3.utils.PermissionUtils; import org.mongodb.morphia.annotations.Entity; import org.mongodb.morphia.annotations.Id; +import org.mongodb.morphia.annotations.Property; import java.util.*; @@ -14,7 +15,8 @@ import java.util.*; public final class ServerGroup { @Getter @Id private String id; - @Getter private boolean isPublic; + // We rename this to public, we just can't name it that because it's a Java identifier. + @Getter @Property("public") private boolean isPublic; // We define these HashSets up here because, in the event they're // empty, Morphia will load them as null, not empty sets. @Getter @Setter @ExcludeFromReplies private Set announcements = new HashSet<>(); diff --git a/src/main/java/net/frozenorb/apiv3/models/User.java b/src/main/java/net/frozenorb/apiv3/models/User.java index cfcf96a..561a0bb 100644 --- a/src/main/java/net/frozenorb/apiv3/models/User.java +++ b/src/main/java/net/frozenorb/apiv3/models/User.java @@ -30,7 +30,7 @@ public final class User { @Getter private String phoneNumber; @Getter private String lastSeenOn; @Getter private Date lastSeenAt; - @Getter private Date firstSeen; + @Getter private Date firstSeenAt; public static User byId(String id) { try { @@ -65,7 +65,7 @@ public final class User { this.phoneNumber = null; this.lastSeenOn = null; this.lastSeenAt = new Date(); - this.firstSeen = new Date(); + this.firstSeenAt = new Date(); aliases.put(lastUsername, new Date()); } @@ -157,7 +157,7 @@ public final class User { } public Rank getHighestRank(ServerGroup serverGroup) { - Rank highest = null; + Rank highest = null;; for (Grant grant : getGrants()) { if (!grant.isActive() || (serverGroup != null && !grant.appliesOn(serverGroup))) { @@ -231,6 +231,7 @@ public final class User { ServerGroup actorGroup = ServerGroup.byId(server.getGroup()); Rank highestRank = getHighestRank(actorGroup); + Map scopedPermissions = PermissionUtils.mergePermissions( PermissionUtils.getDefaultPermissions(highestRank), actorGroup.calculatePermissions(highestRank) diff --git a/src/main/java/net/frozenorb/apiv3/routes/grants/DELETEGrant.java b/src/main/java/net/frozenorb/apiv3/routes/grants/DELETEGrant.java index 1208241..d88a7a2 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/grants/DELETEGrant.java +++ b/src/main/java/net/frozenorb/apiv3/routes/grants/DELETEGrant.java @@ -23,12 +23,9 @@ public final class DELETEGrant implements Route { } User removedBy = User.byId(req.queryParams("removedBy")); - String requiredPermission = Permissions.REMOVE_GRANT + "." + grant.getRank(); if (removedBy == null) { return ErrorUtils.notFound("User", req.queryParams("removedBy")); - } else if (!removedBy.hasPermissionAnywhere(requiredPermission)) { - return ErrorUtils.unauthorized(requiredPermission); } String reason = req.queryParams("reason"); diff --git a/src/main/java/net/frozenorb/apiv3/routes/grants/POSTUserGrant.java b/src/main/java/net/frozenorb/apiv3/routes/grants/POSTUserGrant.java index 25a4d49..8325bdd 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/grants/POSTUserGrant.java +++ b/src/main/java/net/frozenorb/apiv3/routes/grants/POSTUserGrant.java @@ -31,15 +31,18 @@ public final class POSTUserGrant implements Route { } Set scopes = new HashSet<>(); + String scopesUnparsed = req.queryParams("scopes"); - for (String serverGroupId : req.queryParams("scopes").split(",")) { - ServerGroup serverGroup = ServerGroup.byId(serverGroupId); + if (!scopesUnparsed.isEmpty()) { + for (String serverGroupId : scopesUnparsed.split(",")) { + ServerGroup serverGroup = ServerGroup.byId(serverGroupId); - if (serverGroup == null) { - return ErrorUtils.notFound("Server group", serverGroupId); + if (serverGroup == null) { + return ErrorUtils.notFound("Server group", serverGroupId); + } + + scopes.add(serverGroup); } - - scopes.add(serverGroup); } Rank rank = Rank.byId(req.queryParams("rank")); @@ -48,20 +51,20 @@ public final class POSTUserGrant implements Route { return ErrorUtils.notFound("Rank", req.queryParams("rank")); } - Date expiresAt = new Date(Long.parseLong(req.queryParams("expiresAt"))); + Date expiresAt; - if (expiresAt.before(new Date())) { + try { + expiresAt = new Date(Long.parseLong(req.queryParams("expiresAt"))); + } catch (NumberFormatException ex) { + expiresAt = null; + } + + if (expiresAt != null && expiresAt.before(new Date())) { return ErrorUtils.invalidInput("Expiration date cannot be in the past."); } + // We purposely don't do a null check, grants don't have to have a source. User addedBy = User.byId(req.queryParams("addedBy")); - String requiredPermission = Permissions.CREATE_GRANT + "." + rank.getId(); - - if (addedBy == null) { - return ErrorUtils.notFound("User", req.queryParams("addedBy")); - } else if (!addedBy.hasPermissionAnywhere(requiredPermission)) { - return ErrorUtils.unauthorized(requiredPermission); - } Grant grant = new Grant(target, reason, scopes, rank, expiresAt, addedBy); APIv3.getDatastore().save(grant); diff --git a/src/main/java/net/frozenorb/apiv3/routes/punishments/DELETEPunishment.java b/src/main/java/net/frozenorb/apiv3/routes/punishments/DELETEPunishment.java index 97548da..495e00f 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/punishments/DELETEPunishment.java +++ b/src/main/java/net/frozenorb/apiv3/routes/punishments/DELETEPunishment.java @@ -23,12 +23,9 @@ public final class DELETEPunishment implements Route { } User removedBy = User.byId(req.queryParams("removedBy")); - String requiredPermission = Permissions.REMOVE_PUNISHMENT + "." + punishment.getType().name(); if (removedBy == null) { return ErrorUtils.notFound("User", req.queryParams("removedBy")); - } else if (!removedBy.hasPermissionAnywhere(requiredPermission)) { - return ErrorUtils.unauthorized(requiredPermission); } String reason = req.queryParams("reason"); diff --git a/src/main/java/net/frozenorb/apiv3/routes/punishments/GETUserPunishments.java b/src/main/java/net/frozenorb/apiv3/routes/punishments/GETUserPunishments.java new file mode 100644 index 0000000..99aece5 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/routes/punishments/GETUserPunishments.java @@ -0,0 +1,21 @@ +package net.frozenorb.apiv3.routes.punishments; + +import net.frozenorb.apiv3.models.User; +import net.frozenorb.apiv3.utils.ErrorUtils; +import spark.Request; +import spark.Response; +import spark.Route; + +public final class GETUserPunishments implements Route { + + public Object handle(Request req, Response res) { + User target = User.byId(req.params("id")); + + if (target == null) { + return ErrorUtils.notFound("User", req.params("id")); + } + + return target.getPunishments(); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/punishments/POSTUserPunish.java b/src/main/java/net/frozenorb/apiv3/routes/punishments/POSTUserPunish.java index 23f4788..ef712cb 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/punishments/POSTUserPunish.java +++ b/src/main/java/net/frozenorb/apiv3/routes/punishments/POSTUserPunish.java @@ -1,6 +1,10 @@ package net.frozenorb.apiv3.routes.punishments; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import net.frozenorb.apiv3.APIv3; +import net.frozenorb.apiv3.auditLog.AuditLog; +import net.frozenorb.apiv3.auditLog.AuditLogActionType; import net.frozenorb.apiv3.models.Punishment; import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.unsorted.Permissions; @@ -27,20 +31,29 @@ public final class POSTUserPunish implements Route { } Punishment.PunishmentType type = Punishment.PunishmentType.valueOf(req.queryParams("type")); - Date expiresAt = new Date(Long.parseLong(req.queryParams("expiresAt"))); - if (expiresAt.before(new Date())) { + if (type != Punishment.PunishmentType.WARN) { + for (Punishment punishment : target.getPunishments(ImmutableSet.of(type))) { + if (punishment.isActive()) { + return ErrorUtils.error("A punishment by " + User.byId(punishment.getAddedBy()).getLastUsername() + " already covers this user."); + } + } + } + + Date expiresAt; + + try { + expiresAt = new Date(Long.parseLong(req.queryParams("expiresAt"))); + } catch (NumberFormatException ex) { + expiresAt = null; + } + + if (expiresAt != null && expiresAt.before(new Date())) { return ErrorUtils.invalidInput("Expiration date cannot be in the past."); } + // We purposely don't do a null check, grants don't have to have a source. User addedBy = User.byId(req.queryParams("addedBy")); - String requiredPermission = Permissions.CREATE_PUNISHMENT + "." + type.name(); - - if (addedBy == null) { - return ErrorUtils.notFound("User", req.queryParams("addedBy")); - } else if (!addedBy.hasPermissionAnywhere(requiredPermission)) { - return ErrorUtils.unauthorized(requiredPermission); - } if (target.hasPermissionAnywhere(Permissions.PROTECTED_PUNISHMENT)) { return ErrorUtils.error(target.getLastSeenOn() + " is protected from punishments."); diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/DELETEUserPunishment.java b/src/main/java/net/frozenorb/apiv3/routes/users/DELETEUserPunishment.java new file mode 100644 index 0000000..20ddd0f --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/routes/users/DELETEUserPunishment.java @@ -0,0 +1,48 @@ +package net.frozenorb.apiv3.routes.users; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import net.frozenorb.apiv3.auditLog.AuditLog; +import net.frozenorb.apiv3.auditLog.AuditLogActionType; +import net.frozenorb.apiv3.models.Punishment; +import net.frozenorb.apiv3.models.User; +import net.frozenorb.apiv3.utils.ErrorUtils; +import spark.Request; +import spark.Response; +import spark.Route; + +public final class DELETEUserPunishment implements Route { + + public Object handle(Request req, Response res) { + User target = User.byId(req.params("id")); + + if (target == null) { + return ErrorUtils.notFound("User", req.params("id")); + } + + Punishment.PunishmentType type = Punishment.PunishmentType.valueOf(req.queryParams("type").toUpperCase()); + User removedBy = User.byId(req.queryParams("removedBy")); + + if (removedBy == null) { + return ErrorUtils.notFound("User", req.queryParams("removedBy")); + } + + String reason = req.queryParams("reason"); + + if (reason == null || reason.trim().isEmpty()) { + return ErrorUtils.requiredInput("reason"); + } + + for (Punishment punishment : target.getPunishments(ImmutableSet.of(type))) { + if (punishment.isActive()) { + punishment.delete(removedBy, reason); + // TODO: Fix IP + AuditLog.log(removedBy, "", req.attribute("actor"), AuditLogActionType.DELETE_PUNISHMENT, ImmutableMap.of()); + return punishment; + } + } + + return ErrorUtils.error("User provided has no active punishments"); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/GETStaff.java b/src/main/java/net/frozenorb/apiv3/routes/users/GETStaff.java index 296e4c0..8e1e1a4 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/GETStaff.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/GETStaff.java @@ -24,18 +24,18 @@ public final class GETStaff implements Route { } }); - Map> result = new HashMap<>(); + Map> result = new HashMap<>(); APIv3.getDatastore().createQuery(Grant.class).field("rank").in(staffRanks.keySet()).forEach(grant -> { if (grant.isActive()) { User user = User.byId(grant.getTarget()); Rank rank = staffRanks.get(grant.getRank()); - if (!result.containsKey(rank)) { - result.put(rank, new HashSet<>()); + if (!result.containsKey(rank.getId())) { + result.put(rank.getId(), new HashSet<>()); } - result.get(rank).add(user); + result.get(rank.getId()).add(user); } }); diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/GETUserDetails.java b/src/main/java/net/frozenorb/apiv3/routes/users/GETUserDetails.java index b46bb97..76079c9 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/GETUserDetails.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/GETUserDetails.java @@ -23,7 +23,8 @@ public final class GETUserDetails implements Route { .put("ipLog", user.getIPLog()) .put("punishments", user.getPunishments()) .put("aliases", user.getAliases()) - .put("totpSetup", user.getTotpSecret() != null); + .put("totpSetup", user.getTotpSecret() != null) + .build(); } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/utils/PermissionUtils.java b/src/main/java/net/frozenorb/apiv3/utils/PermissionUtils.java index 92ab201..405cbd4 100644 --- a/src/main/java/net/frozenorb/apiv3/utils/PermissionUtils.java +++ b/src/main/java/net/frozenorb/apiv3/utils/PermissionUtils.java @@ -2,6 +2,7 @@ package net.frozenorb.apiv3.utils; import com.google.common.collect.ImmutableMap; import lombok.experimental.UtilityClass; +import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.models.Rank; import java.util.HashMap; @@ -26,13 +27,11 @@ public class PermissionUtils { List unconvertedPermissions = unconverted.get(rank.getId()); // If there's no permissions defined for this rank just skip it. - if (unconvertedPermissions == null) { - continue; + if (unconvertedPermissions != null) { + Map rankPermissions = convertToMap(unconvertedPermissions); + result = mergePermissions(result, rankPermissions); } - Map rankPermissions = convertToMap(unconvertedPermissions); - mergePermissions(result, rankPermissions); - if (upTo.getId().equals(rank.getId())) { break; }