From d062ab5718b833eae792dfffac216c6b3a7a4fa5 Mon Sep 17 00:00:00 2001 From: Colin McDonald Date: Sun, 8 May 2016 23:18:55 -0400 Subject: [PATCH] More stuff --- apiv3.properties | 10 +-- pom.xml | 10 +++ src/main/java/net/frozenorb/apiv3/APIv3.java | 40 +++++++---- .../frozenorb/apiv3/actors/ServerActor.java | 3 +- .../net/frozenorb/apiv3/actors/UserActor.java | 3 +- .../apiv3/filters/ContentTypeFilter.java | 2 +- .../apiv3/filters/LoggingFilter.java | 14 ++-- .../frozenorb/apiv3/models/Punishment.java | 5 +- .../java/net/frozenorb/apiv3/models/Rank.java | 25 ++++++- .../java/net/frozenorb/apiv3/models/User.java | 69 ++++++++++--------- .../announcements/GETAnnouncements.java | 3 +- .../apiv3/routes/auditLog/GETAuditLog.java | 2 +- .../apiv3/routes/grants/DELETEGrant.java | 1 - .../routes/punishments/DELETEPunishment.java | 1 - .../routes/punishments/POSTUserPunish.java | 13 +++- .../routes/servers/POSTServerHeartbeat.java | 24 ++++++- .../routes/users/DELETEUserPunishment.java | 1 - .../apiv3/routes/users/POSTUserLogin.java | 2 + .../apiv3/serialization/DateTypeAdapter.java | 6 +- .../apiv3/unsorted/BugsnagSLF4JLogger.java | 29 ++++++++ .../frozenorb/apiv3/utils/MojangUtils.java | 39 +++++++++++ 21 files changed, 230 insertions(+), 72 deletions(-) create mode 100644 src/main/java/net/frozenorb/apiv3/unsorted/BugsnagSLF4JLogger.java create mode 100644 src/main/java/net/frozenorb/apiv3/utils/MojangUtils.java diff --git a/apiv3.properties b/apiv3.properties index 6f20b7d..fdefb2c 100644 --- a/apiv3.properties +++ b/apiv3.properties @@ -1,15 +1,15 @@ general.releaseStage=production logging.level=info -mongo.address=ds055505.mongolab.com -mongo.port=55505 +mongo.address=209.222.96.50 +mongo.port=27017 mongo.database=minehqapi -mongo.username=test -mongo.password=test +mongo.username= +mongo.password= redis.address=localhost redis.port=6379 http.address=0.0.0.0 http.port=80 -http.workerThreads=6 +http.workerThreads= twillio.accountSID=AC9e2f88c5690134d29a56f698de3cd740 twillio.authToken=982592505a171d3be6b0722f5ecacc0e mandrill.apiKey=0OYtwymqJP6oqvszeJu0vQ diff --git a/pom.xml b/pom.xml index a8c6cc6..e3f98b3 100644 --- a/pom.xml +++ b/pom.xml @@ -133,6 +133,16 @@ googleauth 0.5.0 + + com.squareup.okhttp3 + okhttp + 3.2.0 + + + com.squareup.okio + okio + 1.8.0 + org.projectlombok lombok diff --git a/src/main/java/net/frozenorb/apiv3/APIv3.java b/src/main/java/net/frozenorb/apiv3/APIv3.java index 7855e30..d9e3ff6 100644 --- a/src/main/java/net/frozenorb/apiv3/APIv3.java +++ b/src/main/java/net/frozenorb/apiv3/APIv3.java @@ -1,9 +1,11 @@ package net.frozenorb.apiv3; import com.bugsnag.Client; +import com.google.common.collect.ImmutableList; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.mongodb.MongoClient; +import com.mongodb.MongoCredential; import com.mongodb.ServerAddress; import com.timgroup.statsd.NonBlockingStatsDClient; import com.timgroup.statsd.StatsDClient; @@ -37,6 +39,7 @@ import net.frozenorb.apiv3.routes.users.*; import net.frozenorb.apiv3.serialization.DateTypeAdapter; import net.frozenorb.apiv3.serialization.FollowAnnotationExclusionStrategy; import net.frozenorb.apiv3.serialization.ObjectIdTypeAdapter; +import net.frozenorb.apiv3.unsorted.BugsnagSLF4jLogger; import net.frozenorb.apiv3.unsorted.LoggingExceptionHandler; import org.bson.types.ObjectId; import org.mongodb.morphia.Datastore; @@ -77,6 +80,8 @@ public final class APIv3 { setupMetrics(); setupBugsnag(); setupHttp(); + + LoggingFilter.setDebug(true); } private void setupConfig() { @@ -88,17 +93,21 @@ public final class APIv3 { } private void setupDatabase() { + ImmutableList credentials = ImmutableList.of(); + + if (!config.getProperty("mongo.username").isEmpty()) { + credentials = ImmutableList.of(MongoCredential.createCredential( + config.getProperty("mongo.username"), + config.getProperty("mongo.database"), + config.getProperty("mongo.password").toCharArray() + )); + } + MongoClient mongoClient = new MongoClient(new ServerAddress( config.getProperty("mongo.address"), - Integer.parseInt(config.getProperty("mongo.port")))/*, - ImmutableList.of( - MongoCredential.createCredential( - config.getProperty("mongo.username"), - config.getProperty("mongo.database"), - config.getProperty("mongo.password").toCharArray()) - )*/); - - // TODO: DISABLE CREDS IF NOT NEEDED + Integer.parseInt(config.getProperty("mongo.port"))), + credentials + ); MorphiaLoggerFactory.reset(); MorphiaLoggerFactory.registerLogger(SLF4JLoggerImplFactory.class); @@ -120,7 +129,7 @@ public final class APIv3 { private void setupMetrics() { statsD = new NonBlockingStatsDClient(null, "localhost", 8125); - new Timer("Librato Post Task").scheduleAtFixedRate(new TimerTask() { + new Timer("Metrics Task").scheduleAtFixedRate(new TimerTask() { @Override public void run() { @@ -135,15 +144,18 @@ public final class APIv3 { Client bugsnag = new Client(config.getProperty("bugsnag.apiKey")); bugsnag.setReleaseStage(config.getProperty("general.releaseStage")); bugsnag.setProjectPackages("net.frozenorb.apiv3"); - - // TODO: Use .setLogger to use slf4j with this + bugsnag.setLogger(new BugsnagSLF4jLogger()); } 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"))); + String workerThreads = config.getProperty("http.workerThreads"); + + if (!workerThreads.isEmpty()) { + threadPool(Integer.parseInt(workerThreads)); + } + before(new ContentTypeFilter()); before(new ActorAttributeFilter()); before(new AuthorizationFilter()); diff --git a/src/main/java/net/frozenorb/apiv3/actors/ServerActor.java b/src/main/java/net/frozenorb/apiv3/actors/ServerActor.java index 10f36e1..43a3411 100644 --- a/src/main/java/net/frozenorb/apiv3/actors/ServerActor.java +++ b/src/main/java/net/frozenorb/apiv3/actors/ServerActor.java @@ -1,10 +1,11 @@ package net.frozenorb.apiv3.actors; +import lombok.Getter; import net.frozenorb.apiv3.models.Server; public final class ServerActor implements Actor { - private final Server server; + @Getter private final Server server; public ServerActor(Server server) { this.server = server; diff --git a/src/main/java/net/frozenorb/apiv3/actors/UserActor.java b/src/main/java/net/frozenorb/apiv3/actors/UserActor.java index 3e8c78c..328212c 100644 --- a/src/main/java/net/frozenorb/apiv3/actors/UserActor.java +++ b/src/main/java/net/frozenorb/apiv3/actors/UserActor.java @@ -1,6 +1,7 @@ package net.frozenorb.apiv3.actors; import com.google.common.collect.ImmutableSet; +import lombok.Getter; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.models.User; @@ -10,7 +11,7 @@ public final class UserActor implements Actor { private static final Set authorizedUserGrants = ImmutableSet.copyOf(APIv3.getConfig().getProperty("auth.permittedUserRanks").split(",")); - private final User user; + @Getter private final User user; // We use Boolean here so we can have null = not calculated; private Boolean cachedAuthorized = null; diff --git a/src/main/java/net/frozenorb/apiv3/filters/ContentTypeFilter.java b/src/main/java/net/frozenorb/apiv3/filters/ContentTypeFilter.java index 629e163..38a5a5a 100644 --- a/src/main/java/net/frozenorb/apiv3/filters/ContentTypeFilter.java +++ b/src/main/java/net/frozenorb/apiv3/filters/ContentTypeFilter.java @@ -7,7 +7,7 @@ import spark.Response; public final class ContentTypeFilter implements Filter { public void handle(Request req, Response res) { - res.header("content-type", "application/json"); + res.header("Content-Type", "application/json"); } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/filters/LoggingFilter.java b/src/main/java/net/frozenorb/apiv3/filters/LoggingFilter.java index d4dc771..e09a2e5 100644 --- a/src/main/java/net/frozenorb/apiv3/filters/LoggingFilter.java +++ b/src/main/java/net/frozenorb/apiv3/filters/LoggingFilter.java @@ -1,5 +1,7 @@ package net.frozenorb.apiv3.filters; +import lombok.Getter; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; import spark.Filter; import spark.Request; @@ -8,12 +10,14 @@ import spark.Response; @Slf4j public final class LoggingFilter implements Filter { - public void handle(Request req, Response res) { - if (req.url().toLowerCase().contains("password=")) { - return; - } + @Getter @Setter private static boolean debug = false; - log.info(req.requestMethod().toUpperCase() + " " + req.url()); + public void handle(Request req, Response res) { + if (debug) { + log.info(req.requestMethod().toUpperCase() + " " + req.url() + "\n" + res.body()); + } else { + log.info(req.requestMethod().toUpperCase() + " " + req.url()); + } } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/models/Punishment.java b/src/main/java/net/frozenorb/apiv3/models/Punishment.java index 8c4b778..8d3beba 100644 --- a/src/main/java/net/frozenorb/apiv3/models/Punishment.java +++ b/src/main/java/net/frozenorb/apiv3/models/Punishment.java @@ -11,6 +11,7 @@ import org.mongodb.morphia.annotations.Id; import org.mongodb.morphia.annotations.Indexed; import java.util.Date; +import java.util.Map; import java.util.UUID; @Entity(value = "punishments", noClassnameStored = true) @@ -21,6 +22,7 @@ public final class Punishment { @Getter private String reason; @Getter @Indexed private PunishmentType type; // Type is indexed for the rank dump @Getter private Date expiresAt; + @Getter private Map meta; @Getter private UUID addedBy; @Getter @Indexed private Date addedAt; @@ -37,7 +39,7 @@ public final class Punishment { public Punishment() {} // For Morphia - public Punishment(User target, String reason, PunishmentType type, Date expiresAt, User addedBy, Actor actor) { + public Punishment(User target, String reason, PunishmentType type, Date expiresAt, User addedBy, Actor actor, Map meta) { this.id = new ObjectId().toString(); this.target = target.getId(); this.reason = reason; @@ -47,6 +49,7 @@ public final class Punishment { this.addedAt = new Date(); this.actorName = actor.getName(); this.actorType = actor.getType(); + this.meta = meta; } public void delete(User removedBy, String reason) { diff --git a/src/main/java/net/frozenorb/apiv3/models/Rank.java b/src/main/java/net/frozenorb/apiv3/models/Rank.java index e4054fe..df0b42a 100644 --- a/src/main/java/net/frozenorb/apiv3/models/Rank.java +++ b/src/main/java/net/frozenorb/apiv3/models/Rank.java @@ -1,16 +1,23 @@ package net.frozenorb.apiv3.models; +import com.google.common.collect.ImmutableList; import lombok.Getter; import net.frozenorb.apiv3.APIv3; import org.mongodb.morphia.annotations.Entity; import org.mongodb.morphia.annotations.Id; import org.mongodb.morphia.annotations.Indexed; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; @Entity(value = "ranks", noClassnameStored = true) public final class Rank { + private static Map rankCache = null; + private static long rankCacheUpdated = 0; + @Getter @Id private String id; @Getter private int weight; @Getter private String displayName; @@ -19,11 +26,12 @@ public final class Rank { @Getter @Indexed private boolean staffRank; public static Rank byId(String id) { - return APIv3.getDatastore().createQuery(Rank.class).field("id").equal(id).get(); + updateCacheIfNeeded(); + return rankCache.get(id); } public static List values() { - return APIv3.getDatastore().createQuery(Rank.class).order("weight").asList(); + return ImmutableList.copyOf(rankCache.values()); } public Rank() {} // For Morphia @@ -41,4 +49,17 @@ public final class Rank { APIv3.getDatastore().delete(this); } + private static void updateCacheIfNeeded() { + if (rankCache == null || (System.currentTimeMillis() - rankCacheUpdated) > TimeUnit.MINUTES.toMillis(1)) { + Map working = new HashMap<>(); + + for (Rank rank : APIv3.getDatastore().createQuery(Rank.class).order("weight").asList()) { + working.put(rank.getId(), rank); + } + + rankCache = working; + rankCacheUpdated = System.currentTimeMillis(); + } + } + } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/models/User.java b/src/main/java/net/frozenorb/apiv3/models/User.java index 5a917e6..f17fbc5 100644 --- a/src/main/java/net/frozenorb/apiv3/models/User.java +++ b/src/main/java/net/frozenorb/apiv3/models/User.java @@ -7,6 +7,7 @@ import lombok.Getter; import lombok.Setter; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.serialization.ExcludeFromReplies; +import net.frozenorb.apiv3.utils.MojangUtils; import net.frozenorb.apiv3.utils.PermissionUtils; import org.bson.Document; import org.mindrot.jbcrypt.BCrypt; @@ -57,7 +58,7 @@ public final class User { public User(UUID id, String lastUsername) { this.id = id; - this.lastUsername = lastUsername; + this.lastUsername = ""; // Intentional, so updateUsername actually does something. this.aliases = new HashMap<>(); this.totpSecret = null; this.password = null; @@ -67,7 +68,7 @@ public final class User { this.lastSeenAt = new Date(); this.firstSeenAt = new Date(); - aliases.put(lastUsername, new Date()); + updateUsername(lastUsername); } public boolean hasPermissionScoped(String permission, ServerGroup scope) { @@ -138,9 +139,23 @@ public final class User { } } - public void seenOnServer(String username, Server server) { + public void seenOnServer(Server server) { this.lastSeenOn = server.getId(); this.lastSeenAt = new Date(); + } + + public void updateUsername(String username) { + if (!username.equals(lastUsername)) { + this.lastUsername = username; + + User withNewUsername; + + while ((withNewUsername = User.byLastUsername(username)) != null) { + String newUsername = MojangUtils.getName(withNewUsername.getId()); + withNewUsername.updateUsername(newUsername); + } + } + this.aliases.put(username, new Date()); } @@ -157,7 +172,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))) { @@ -205,51 +220,43 @@ public final class User { } public Map getLoginInfo(Server server) { + Punishment activeMute = null; String accessDenialReason = null; for (Punishment punishment : getPunishments(ImmutableSet.of( Punishment.PunishmentType.BLACKLIST, - Punishment.PunishmentType.BAN + Punishment.PunishmentType.BAN, + Punishment.PunishmentType.MUTE ))) { if (!punishment.isActive()) { continue; } - accessDenialReason = punishment.getAccessDenialReason(); - } - - Punishment mute = null; - for (Punishment punishment : getPunishments(ImmutableSet.of(Punishment.PunishmentType.MUTE))) { - if (!punishment.isActive()) { - continue; + if (punishment.getType() == Punishment.PunishmentType.MUTE) { + activeMute = punishment; + } else { + accessDenialReason = punishment.getAccessDenialReason(); } - - mute = punishment; } ServerGroup actorGroup = ServerGroup.byId(server.getGroup()); Rank highestRank = getHighestRank(actorGroup); - Map scopedPermissions = PermissionUtils.mergePermissions( - PermissionUtils.getDefaultPermissions(highestRank), - actorGroup.calculatePermissions(highestRank) - ); + // Generics are weird, yes we have to do this. + ImmutableMap.Builder result = ImmutableMap.builder() + .put("user", this) + .put("access", ImmutableMap.of( + "allowed", accessDenialReason == null, + "message", accessDenialReason == null ? "Public server" : accessDenialReason + )) + .put("rank", highestRank.getId()) + .put("totpSetup", getTotpSecret() != null); - Map loginInfo = Maps.newHashMap(); - - loginInfo.put("user", this); - loginInfo.put("access", ImmutableMap.of( - "allowed", accessDenialReason == null, - "message", accessDenialReason == null ? "Public server" : accessDenialReason - )); - loginInfo.put("rank", highestRank.getId()); - loginInfo.put("permissions", scopedPermissions); - loginInfo.put("totpSetup", getTotpSecret() != null); - if (mute != null) { - loginInfo.put("mute", mute); + if (activeMute != null) { + result.put("mute", activeMute); } - return loginInfo; + return result.build(); } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/routes/announcements/GETAnnouncements.java b/src/main/java/net/frozenorb/apiv3/routes/announcements/GETAnnouncements.java index 4f29d93..9e9d13b 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/announcements/GETAnnouncements.java +++ b/src/main/java/net/frozenorb/apiv3/routes/announcements/GETAnnouncements.java @@ -2,6 +2,7 @@ package net.frozenorb.apiv3.routes.announcements; import net.frozenorb.apiv3.actors.Actor; import net.frozenorb.apiv3.actors.ActorType; +import net.frozenorb.apiv3.actors.ServerActor; import net.frozenorb.apiv3.models.Server; import net.frozenorb.apiv3.models.ServerGroup; import net.frozenorb.apiv3.utils.ErrorUtils; @@ -18,7 +19,7 @@ public final class GETAnnouncements implements Route { return ErrorUtils.serverOnly(); } - Server sender = Server.byId(actor.getName()); + Server sender = ((ServerActor) req.attribute("actor")).getServer(); ServerGroup senderGroup = ServerGroup.byId(sender.getGroup()); return senderGroup.getAnnouncements(); diff --git a/src/main/java/net/frozenorb/apiv3/routes/auditLog/GETAuditLog.java b/src/main/java/net/frozenorb/apiv3/routes/auditLog/GETAuditLog.java index 4b46517..a196e8c 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/auditLog/GETAuditLog.java +++ b/src/main/java/net/frozenorb/apiv3/routes/auditLog/GETAuditLog.java @@ -16,7 +16,7 @@ public final class GETAuditLog implements Route { return APIv3.getDatastore().createQuery(AuditLogEntry.class).order("performedAt").limit(limit).offset(offset).asList(); } catch (NumberFormatException ex) { - return ErrorUtils.invalidInput(";imit and offset must be numerical inputs."); + return ErrorUtils.invalidInput("limit and offset must be numerical inputs."); } } 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 e0184f2..6e4a46a 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/grants/DELETEGrant.java +++ b/src/main/java/net/frozenorb/apiv3/routes/grants/DELETEGrant.java @@ -34,7 +34,6 @@ public final class DELETEGrant implements Route { } grant.delete(removedBy, reason); - // TODO: Fix IP AuditLog.log(removedBy, "", req.attribute("actor"), AuditLogActionType.DELETE_GRANT, ImmutableMap.of()); return 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 dfa7d9c..b2fb077 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/punishments/DELETEPunishment.java +++ b/src/main/java/net/frozenorb/apiv3/routes/punishments/DELETEPunishment.java @@ -34,7 +34,6 @@ public final class DELETEPunishment implements Route { } punishment.delete(removedBy, reason); - // TODO: Fix IP AuditLog.log(removedBy, "", req.attribute("actor"), AuditLogActionType.DELETE_PUNISHMENT, ImmutableMap.of()); return punishment; } 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 284918b..9bb1f40 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/punishments/POSTUserPunish.java +++ b/src/main/java/net/frozenorb/apiv3/routes/punishments/POSTUserPunish.java @@ -7,11 +7,13 @@ import net.frozenorb.apiv3.models.Punishment; import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.unsorted.Permissions; import net.frozenorb.apiv3.utils.ErrorUtils; +import org.bson.Document; import spark.Request; import spark.Response; import spark.Route; import java.util.Date; +import java.util.Map; public final class POSTUserPunish implements Route { @@ -50,6 +52,12 @@ public final class POSTUserPunish implements Route { return ErrorUtils.invalidInput("Expiration date cannot be in the past."); } + Map meta = Document.parse(req.body()); + + if (meta == null) { + return ErrorUtils.requiredInput("request body meta"); + } + // We purposely don't do a null check, grants don't have to have a source. User addedBy = User.byId(req.queryParams("addedBy")); @@ -57,12 +65,13 @@ public final class POSTUserPunish implements Route { return ErrorUtils.error(target.getLastSeenOn() + " is protected from punishments."); } - Punishment punishment = new Punishment(target, reason, type, expiresAt, addedBy, req.attribute("actor")); + Punishment punishment = new Punishment(target, reason, type, expiresAt, addedBy, req.attribute("actor"), meta); + String accessDenialReason = punishment.getAccessDenialReason(); APIv3.getDatastore().save(punishment); return ImmutableMap.of( "punishment", punishment, - "accessDenialReason", punishment.getAccessDenialReason() == null ? "" : punishment.getAccessDenialReason() + "accessDenialReason", accessDenialReason == null ? "" : accessDenialReason ); } diff --git a/src/main/java/net/frozenorb/apiv3/routes/servers/POSTServerHeartbeat.java b/src/main/java/net/frozenorb/apiv3/routes/servers/POSTServerHeartbeat.java index 6c157f2..128780b 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/servers/POSTServerHeartbeat.java +++ b/src/main/java/net/frozenorb/apiv3/routes/servers/POSTServerHeartbeat.java @@ -1,12 +1,16 @@ package net.frozenorb.apiv3.routes.servers; import com.google.common.collect.ImmutableMap; +import lombok.extern.slf4j.Slf4j; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.actors.Actor; import net.frozenorb.apiv3.actors.ActorType; +import net.frozenorb.apiv3.models.Rank; import net.frozenorb.apiv3.models.Server; +import net.frozenorb.apiv3.models.ServerGroup; import net.frozenorb.apiv3.models.User; import net.frozenorb.apiv3.utils.ErrorUtils; +import net.frozenorb.apiv3.utils.PermissionUtils; import org.bson.Document; import spark.Request; import spark.Response; @@ -14,6 +18,7 @@ import spark.Route; import java.util.*; +@Slf4j public final class POSTServerHeartbeat implements Route { @SuppressWarnings("unchecked") @@ -25,6 +30,7 @@ public final class POSTServerHeartbeat implements Route { } Server actorServer = Server.byId(actor.getName()); + ServerGroup actorServerGroup = ServerGroup.byId(actorServer.getGroup()); Document reqJson = Document.parse(req.body()); Set onlinePlayers = new HashSet<>(); Map playersResponse = new HashMap<>(); @@ -39,7 +45,7 @@ public final class POSTServerHeartbeat implements Route { user = new User(UUID.fromString(playerJson.getString("uuid")), username); } - user.seenOnServer(username, actorServer); + user.seenOnServer(actorServer); APIv3.getDatastore().save(user); onlinePlayers.add(user.getId()); @@ -58,17 +64,29 @@ public final class POSTServerHeartbeat implements Route { case "metrics": break; default: - System.err.println("Recieved event with unknown type " + type + "."); + log.warn("Recieved event with unknown type " + type + "."); } } + Map> permissions = new HashMap<>(); + + for (Rank rank : Rank.values()) { + Map scopedPermissions = PermissionUtils.mergePermissions( + PermissionUtils.getDefaultPermissions(rank), + actorServerGroup.calculatePermissions(rank) + ); + + permissions.put(rank.getId(), scopedPermissions); + } + actorServer.setPlayers(onlinePlayers); actorServer.setLastTps(reqJson.getDouble("lastTps")); actorServer.setLastUpdate(new Date()); APIv3.getDatastore().save(actorServer); return ImmutableMap.of( - "players", playersResponse + "players", playersResponse, + "permissions", permissions ); } diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/DELETEUserPunishment.java b/src/main/java/net/frozenorb/apiv3/routes/users/DELETEUserPunishment.java index 20ddd0f..2dd304e 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/DELETEUserPunishment.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/DELETEUserPunishment.java @@ -36,7 +36,6 @@ public final class DELETEUserPunishment implements Route { 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; } diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserLogin.java b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserLogin.java index 77fe0b2..7536060 100644 --- a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserLogin.java +++ b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserLogin.java @@ -37,6 +37,8 @@ public final class POSTUserLogin implements Route { Server actorServer = Server.byId(actor.getName()); user.getIPLogEntry(userIp).used(); + user.updateUsername(username); + APIv3.getDatastore().save(user); return user.getLoginInfo(actorServer); } diff --git a/src/main/java/net/frozenorb/apiv3/serialization/DateTypeAdapter.java b/src/main/java/net/frozenorb/apiv3/serialization/DateTypeAdapter.java index 77d7645..23373ed 100644 --- a/src/main/java/net/frozenorb/apiv3/serialization/DateTypeAdapter.java +++ b/src/main/java/net/frozenorb/apiv3/serialization/DateTypeAdapter.java @@ -10,7 +10,11 @@ import java.util.Date; public final class DateTypeAdapter extends TypeAdapter { public void write(JsonWriter writer, Date write) throws IOException { - writer.value(write == null ? -1 : write.getTime()); + if (write == null) { + writer.value(-1); + } else { + writer.value(write.getTime()); + } } // This is used with Gson, which is only used diff --git a/src/main/java/net/frozenorb/apiv3/unsorted/BugsnagSLF4JLogger.java b/src/main/java/net/frozenorb/apiv3/unsorted/BugsnagSLF4JLogger.java new file mode 100644 index 0000000..773da9c --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/unsorted/BugsnagSLF4JLogger.java @@ -0,0 +1,29 @@ +package net.frozenorb.apiv3.unsorted; + +import com.bugsnag.Logger; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class BugsnagSLF4jLogger extends Logger { + + public void debug(String message) { + log.debug(message); + } + + public void info(String message) { + log.info(message); + } + + public void warn(String message) { + log.warn(message); + } + + public void warn(String message, Throwable ex) { + log.warn(message, ex); + } + + public void warn(Throwable ex) { + log.warn("error in bugsnag", ex); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/utils/MojangUtils.java b/src/main/java/net/frozenorb/apiv3/utils/MojangUtils.java new file mode 100644 index 0000000..69f2749 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/utils/MojangUtils.java @@ -0,0 +1,39 @@ +package net.frozenorb.apiv3.utils; + +import lombok.experimental.UtilityClass; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import org.bson.Document; + +import java.io.IOException; +import java.util.UUID; + +@UtilityClass +public class MojangUtils { + + private static OkHttpClient okHttpClient = new OkHttpClient(); + + public static String getName(UUID id) { + Request.Builder builder = new Request.Builder(); + + builder.get(); + builder.url("https://sessionserver.mojang.com/session/minecraft/profile/" + id.toString().replace("-", "")); + + try { + Response response = okHttpClient.newCall(builder.build()).execute(); + Document resJson = Document.parse(response.body().string()); + + String name = resJson.getString("name"); + + if (name == null) { + throw new RuntimeException("Hit Mojang API rate limit"); + } + + return name; + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + +} \ No newline at end of file