Improve conversion and fix permission calculation

This commit is contained in:
Colin McDonald 2016-05-14 23:58:37 -04:00
parent 199072b5fb
commit 52ad1bd685
7 changed files with 108 additions and 22 deletions

View File

@ -61,7 +61,7 @@
<dependency>
<groupId>com.sparkjava</groupId>
<artifactId>spark-core</artifactId>
<version>2.4</version>
<version>2.5</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>

View File

@ -14,10 +14,7 @@ import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.frozenorb.apiv3.actors.ActorType;
import net.frozenorb.apiv3.filters.*;
import net.frozenorb.apiv3.models.Grant;
import net.frozenorb.apiv3.models.IPLogEntry;
import net.frozenorb.apiv3.models.Punishment;
import net.frozenorb.apiv3.models.User;
import net.frozenorb.apiv3.models.*;
import net.frozenorb.apiv3.routes.GETDump;
import net.frozenorb.apiv3.routes.GETWhoAmI;
import net.frozenorb.apiv3.routes.NotFound;
@ -48,20 +45,25 @@ import net.frozenorb.apiv3.serialization.ObjectIdTypeAdapter;
import net.frozenorb.apiv3.unsorted.BugsnagSLF4JLogger;
import net.frozenorb.apiv3.unsorted.LoggingExceptionHandler;
import net.frozenorb.apiv3.utils.IPUtils;
import net.frozenorb.apiv3.utils.UUIDUtils;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.mongodb.morphia.Datastore;
import org.mongodb.morphia.Morphia;
import org.mongodb.morphia.converters.UUIDConverter;
import org.mongodb.morphia.logging.MorphiaLoggerFactory;
import org.mongodb.morphia.logging.slf4j.SLF4JLoggerImplFactory;
import redis.clients.jedis.JedisPool;
import spark.Spark;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import static spark.Spark.*;
import static spark.route.RouteOverview.enableRouteOverview;
@Slf4j
public final class APIv3 {
@ -120,6 +122,7 @@ public final class APIv3 {
Morphia morphia = new Morphia();
morphia.mapPackage("net.frozenorb.apiv3.accessor");
morphia.getMapper().getConverters().addConverter(new UUIDConverter());
datastore = morphia.createDatastore(mongoClient, config.getProperty("mongo.database"));
datastore.ensureIndexes();
@ -234,6 +237,8 @@ public final class APIv3 {
delete("/user/:id/meta/:serverGroup", new DELETEUserMeta(), gson::toJson);
delete("/user/:id/punishment", new DELETEUserPunishment(), gson::toJson);
enableRouteOverview("/routes");
// There's no way to do a JSON 404 page w/o doing this :(
get("/*", new NotFound(), gson::toJson);
post("/*", new NotFound(), gson::toJson);
@ -245,6 +250,10 @@ public final class APIv3 {
// A lot of unneeded .toString()'s and cloning objects is our ghetto null validation.
MongoDatabase importFrom = new MongoClient(oldIp).getDatabase("minehq");
Map<ObjectId, UUID> mongoIdToUUID = new HashMap<>();
AtomicInteger skippedUsers = new AtomicInteger();
AtomicInteger skippedPunishments = new AtomicInteger();
AtomicInteger skippedGrants = new AtomicInteger();
AtomicInteger skippedIpLogs = new AtomicInteger();
importFrom.getCollection("user").find().forEach(new Block<Document>() {
@ -256,7 +265,13 @@ public final class APIv3 {
return;
}
UUID uuid = UUID.fromString(uuidString.substring(0, 7) + "-" + uuidString.substring(7, 11) + "-" + uuidString.substring(11, 15) + "-" + uuidString.substring(15, 20) + "-" + uuidString.substring(20, uuidString.length()));
UUID uuid = UUID.fromString(uuidString.replaceFirst( "([0-9a-fA-F]{8})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]+)", "$1-$2-$3-$4-$5" ));
if (!UUIDUtils.isAcceptableUUID(uuid)) {
skippedUsers.incrementAndGet();
return;
}
mongoIdToUUID.put(user.getObjectId("_id"), uuid);
User created = new User(
@ -290,6 +305,7 @@ public final class APIv3 {
UUID target = mongoIdToUUID.get(((DBRef) punishment.get("user")).getId());
if (target == null) {
skippedPunishments.incrementAndGet();
return;
}
@ -324,6 +340,7 @@ public final class APIv3 {
UUID target = mongoIdToUUID.get(((DBRef) grant.get("target")).getId());
if (target == null) {
skippedGrants.incrementAndGet();
return;
}
@ -366,6 +383,7 @@ public final class APIv3 {
UUID user = mongoIdToUUID.get(((DBRef) ipLogEntry.get("user")).getId());
if (user == null || ipLogEntry.getString("ip") == null) {
skippedIpLogs.incrementAndGet();
return;
}
@ -398,6 +416,8 @@ public final class APIv3 {
log.info("Created ip log entry " + created.getId() + " (" + created.getUser() + " - " + created.getUserIp() + ")");
}
});
log.info("Skipped " + skippedUsers.get() + " users, " + skippedPunishments.get() + " punishments, " + skippedGrants.get() + " grants, and " + skippedIpLogs.get() + " ip logs");
}
}

View File

@ -22,7 +22,7 @@ public final class ServerGroup {
// 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<String> announcements = new HashSet<>();
@Getter @ExcludeFromReplies private Map<String, Map<String, Boolean>> permissions = new HashMap<>();
@Getter @Setter @ExcludeFromReplies private Map<String, List<String>> permissions = new HashMap<>();
public static ServerGroup byId(String id) {
return APIv3.getDatastore().createQuery(ServerGroup.class).field("id").equal(id).get();

View File

@ -25,7 +25,7 @@ public final class User {
@Getter @Id private UUID id;
@Getter @Indexed private String lastUsername;
@Getter @ExcludeFromReplies private Map<String, Date> aliases;
@Getter @ExcludeFromReplies private Map<String, Date> aliases = new HashMap<>();
@Getter @Setter @ExcludeFromReplies private String totpSecret;
@Getter @Indexed @ExcludeFromReplies @Setter private String emailToken;
@Getter @ExcludeFromReplies @Setter private Date emailTokenSetAt;
@ -106,7 +106,7 @@ public final class User {
}
public List<Grant> getGrants() {
return APIv3.getDatastore().createQuery(Grant.class).field("target").equal(id).asList();
return APIv3.getDatastore().createQuery(Grant.class).field("user").equal(id).asList();
}
public List<IPLogEntry> getIPLog() {
@ -114,7 +114,7 @@ public final class User {
}
public IPLogEntry getIPLogEntry(String ip) {
IPLogEntry existing = APIv3.getDatastore().createQuery(IPLogEntry.class).field("user").equal(id).field("ip").equal(ip).get();
IPLogEntry existing = APIv3.getDatastore().createQuery(IPLogEntry.class).field("user").equal(id).field("userIp").equal(ip).get();
if (existing == null) {
existing = new IPLogEntry(this, ip);
@ -125,11 +125,11 @@ public final class User {
}
public List<Punishment> getPunishments() {
return APIv3.getDatastore().createQuery(Punishment.class).field("target").equal(id).asList();
return APIv3.getDatastore().createQuery(Punishment.class).field("user").equal(id).asList();
}
public List<Punishment> getPunishments(Collection<Punishment.PunishmentType> types) {
return APIv3.getDatastore().createQuery(Punishment.class).field("target").equal(id).field("type").in(types).asList();
return APIv3.getDatastore().createQuery(Punishment.class).field("user").equal(id).field("type").in(types).asList();
}
public UserMetaEntry getMeta(ServerGroup group) {

View File

@ -21,15 +21,11 @@ public final class GETStaff implements Route {
}
});
Map<String, Set<User>> result = new TreeMap<>((Comparator<String>) (first, second) -> {
Map<String, Set<User>> result = new TreeMap<>((first, second) -> {
Rank firstRank = Rank.byId(first);
Rank secondRank = Rank.byId(second);
if (firstRank.getWeight() > secondRank.getWeight()) {
return -1;
}
return 1;
return Integer.compare(firstRank.getWeight(), secondRank.getWeight());
});
APIv3.getDatastore().createQuery(Grant.class).field("rank").in(staffRanks.keySet()).forEach(grant -> {

View File

@ -1,10 +1,14 @@
package net.frozenorb.apiv3.utils;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import lombok.experimental.UtilityClass;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.Rank;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@UtilityClass
@ -18,14 +22,14 @@ public class PermissionUtils {
return result;
}
public static Map<String, Boolean> mergeUpTo(Map<String, Map<String, Boolean>> unmerged, Rank upTo) {
public static Map<String, Boolean> mergeUpTo(Map<String, List<String>> unmerged, Rank upTo) {
Map<String, Boolean> result = new HashMap<>();
for (Rank rank : Rank.values()) {
Map<String, Boolean> rankPermissions = unmerged.get(rank.getId());
Map<String, Boolean> rankPermissions = convertToMap(unmerged.get(rank.getId()));
// If there's no permissions defined for this rank just skip it.
if (rankPermissions != null) {
if (!rankPermissions.isEmpty()) {
result = mergePermissions(result, rankPermissions);
}
@ -42,4 +46,69 @@ public class PermissionUtils {
return ImmutableMap.of();
}
private static Map<String, Boolean> convertToMap(List<String> unconvered) {
if (unconvered == null) {
return ImmutableMap.of();
}
Map<String, Boolean> result = new HashMap<>();
for (String permission : unconvered) {
boolean negate = permission.startsWith("-");
if (negate) {
result.put(permission.substring(1), false);
} else {
result.put(permission, true);
}
}
return result;
}
public static Map<String, List<String>> fromLegacy(String defaultGroup, String customGroup) {
Map<String, List<String>> defaultPerms = legacyServerGroupToPerms(APIv3.getGson().fromJson(defaultGroup, HashMap.class));
Map<String, List<String>> customPerms = legacyServerGroupToPerms(APIv3.getGson().fromJson(customGroup, HashMap.class));
for (Map.Entry<String, List<String>> customPerm : customPerms.entrySet()) {
List<String> original = defaultPerms.get(customPerm.getKey());
original.addAll(customPerm.getValue());
defaultPerms.put(customPerm.getKey(), new ArrayList<>(ImmutableSet.copyOf(original)));
}
defaultPerms.remove("coowner");
defaultPerms.remove("allow-vpns");
defaultPerms.remove("head-admin");
defaultPerms.remove("registered");
defaultPerms.remove("mvp");
defaultPerms.remove("super-head-admin");
defaultPerms.remove("trial-mod");
defaultPerms.remove("forcedeathkick");
return defaultPerms;
}
private static Map<String, List<String>> legacyServerGroupToPerms(Map<String, Object> group) {
Map<String, List<String>> result = new HashMap<>();
Map<String, Object> permissions = (Map<String, Object>) group.get("permissions");
for (Map.Entry<String, Object> entry : permissions.entrySet()) {
List<String> perms = (List<String>) ((Map<String, Object>) entry.getValue()).get("grant");
String rank = entry.getKey();
if (rank.equalsIgnoreCase("unban") || rank.equalsIgnoreCase("pass") || rank.equalsIgnoreCase("pink") || rank.equalsIgnoreCase("jrdev")) {
continue;
} else if (rank.equalsIgnoreCase("high_roller")) {
rank = "high-roller";
} else if (rank.equalsIgnoreCase("dev")) {
rank = "developer";
}
result.put(rank, perms);
}
return result;
}
}

View File

@ -1,6 +1,7 @@
package net.frozenorb.apiv3.utils;
import com.warrenstrange.googleauth.GoogleAuthenticator;
import com.warrenstrange.googleauth.GoogleAuthenticatorConfig;
import com.warrenstrange.googleauth.GoogleAuthenticatorKey;
import com.warrenstrange.googleauth.GoogleAuthenticatorQRGenerator;
import lombok.experimental.UtilityClass;
@ -13,7 +14,7 @@ import java.util.concurrent.TimeUnit;
@UtilityClass
public class TOTPUtils {
private static GoogleAuthenticator googleAuthenticator = new GoogleAuthenticator();
private static GoogleAuthenticator googleAuthenticator = new GoogleAuthenticator(new GoogleAuthenticatorConfig.GoogleAuthenticatorConfigBuilder().setWindowSize(10).build());
public static GoogleAuthenticatorKey generateTOTPKey() {
return googleAuthenticator.createCredentials();