dumb shit

This commit is contained in:
Moose1301 2022-10-19 22:01:01 -04:00
parent 87675fad10
commit a0459183a7
No known key found for this signature in database
GPG Key ID: 8B47DA69B2166FED
20 changed files with 672 additions and 22 deletions

View File

@ -3,15 +3,11 @@ package net.frozenorb.apiv3;
import com.google.common.net.MediaType;
import com.google.gson.Gson;
import net.frozenorb.apiv3.domain.*;
import net.frozenorb.apiv3.web.filter.AuthenticationFilter;
import net.frozenorb.apiv3.web.filter.AuthorizationFilter;
import net.frozenorb.apiv3.web.filter.MetricsFilter;
import net.frozenorb.apiv3.web.filter.WebsiteUserSessionFilter;
import net.frozenorb.apiv3.domain.BannedAsn;
import net.frozenorb.apiv3.domain.BannedCellCarrier;
import net.frozenorb.apiv3.domain.Rank;
import net.frozenorb.apiv3.domain.Server;
import net.frozenorb.apiv3.domain.ServerGroup;
import net.frozenorb.apiv3.web.route.GETDumpsType;
import net.frozenorb.apiv3.web.route.GETSearch;
import net.frozenorb.apiv3.web.route.GETWhoAmI;
@ -56,6 +52,14 @@ import net.frozenorb.apiv3.web.route.notificationTemplates.GETNotificationTempla
import net.frozenorb.apiv3.web.route.notificationTemplates.GETNotificationTemplatesId;
import net.frozenorb.apiv3.web.route.notificationTemplates.POSTNotificationTemplates;
import net.frozenorb.apiv3.web.route.phoneIntel.GETPhoneInteld;
import net.frozenorb.apiv3.web.route.prefix.DELETEPrefixesId;
import net.frozenorb.apiv3.web.route.prefix.GETPrefixes;
import net.frozenorb.apiv3.web.route.prefix.GETPrefixesId;
import net.frozenorb.apiv3.web.route.prefix.POSTPrefixes;
import net.frozenorb.apiv3.web.route.prefixGrants.DELETEPrefixGrantsId;
import net.frozenorb.apiv3.web.route.prefixGrants.GETPrefixGrants;
import net.frozenorb.apiv3.web.route.prefixGrants.GETPrefixGrantsId;
import net.frozenorb.apiv3.web.route.prefixGrants.POSTPrefixGrants;
import net.frozenorb.apiv3.web.route.punishments.DELETEPunishmentsId;
import net.frozenorb.apiv3.web.route.punishments.DELETEUsersIdActivePunishment;
import net.frozenorb.apiv3.web.route.punishments.GETPunishments;
@ -123,6 +127,7 @@ public final class APIv3 {
BannedAsn.updateCache();
BannedCellCarrier.updateCache();
Rank.updateCache();
Prefix.updateCache();
Server.updateCache();
ServerGroup.updateCache();
}
@ -232,6 +237,19 @@ public final class APIv3 {
//httpPut(router, "/servers/:serverId", PUTServersId.class);
httpDelete(router, "/servers/:serverId", DELETEServersId.class);
httpGet(router, "/prefixes/:prefixId", GETPrefixesId.class);
httpGet(router, "/prefixes", GETPrefixes.class);
httpPost(router, "/prefixes", POSTPrefixes.class);
//httpPut(router, "/prefixes/:prefixId", PUTPrefixesId.class);
httpDelete(router, "/prefixes/:prefixId", DELETEPrefixesId.class);
httpGet(router, "/prefixes/grants/:grantId", GETPrefixGrantsId.class);
httpGet(router, "/prefixes/grants", GETPrefixGrants.class);
httpPost(router, "/prefixes/grants", POSTPrefixGrants.class);
//httpPut(router, "/grants/:grantId", PUTPrefixGrantsId.class);
httpDelete(router, "/prefixes/grants/:grantId", DELETEPrefixGrantsId.class);
httpGet(router, "/staff", GETStaff.class);
httpGet(router, "/users/:userId", GETUsersId.class);
httpGet(router, "/users/:userId/compoundedPermissions", GETUsersIdCompoundedPermissions.class);

View File

@ -0,0 +1,84 @@
package net.frozenorb.apiv3.domain;
import com.google.common.collect.ImmutableList;
import com.mongodb.async.SingleResultCallback;
import com.mongodb.async.client.MongoCollection;
import com.mongodb.async.client.MongoDatabase;
import fr.javatic.mongo.jacksonCodec.Entity;
import fr.javatic.mongo.jacksonCodec.objectId.Id;
import io.vertx.core.Vertx;
import lombok.Getter;
import net.frozenorb.apiv3.unsorted.MongoToVoidMongoCallback;
import net.frozenorb.apiv3.util.SpringUtils;
import net.frozenorb.apiv3.util.SyncUtils;
import org.bson.Document;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Entity
public final class Prefix {
private static final MongoCollection<Prefix> prefixesCollection = SpringUtils.getBean(MongoDatabase.class).getCollection("prefixes", Prefix.class);
private static Map<String, Prefix> prefixIdCache = null;
private static List<Prefix> prefixCache = null;
@Getter @Id private String id;
@Getter private String displayName;
@Getter private String prefix;
@Getter private boolean purchaseable;
@Getter private String buttonName;
@Getter private String buttonDescription;
public static List<Prefix> findAll() {
return ImmutableList.copyOf(prefixCache);
}
public static Prefix findById(String id) {
return id == null ? null : prefixIdCache.get(id.toLowerCase());
}
static {
SpringUtils.getBean(Vertx.class).setPeriodic(TimeUnit.MINUTES.toMillis(1), (id) -> updateCache());
}
public static void updateCache() {
List<Prefix> prefixes = SyncUtils.runBlocking(v -> prefixesCollection.find().into(new LinkedList<>(), v));
Map<String, Prefix> working = new HashMap<>();
for (Prefix prefix : prefixes) {
working.put(prefix.getId().toLowerCase(), prefix);
}
prefixIdCache = working;
prefixCache = prefixes;
}
private Prefix() {} // For Jackson
public Prefix(String id, String displayName, String prefix, boolean purchaseable, String buttonName, String buttonDescription) {
this.id = id;
this.displayName = displayName;
this.prefix = prefix;
this.purchaseable = purchaseable;
this.buttonName = buttonName;
this.buttonDescription = buttonDescription;
}
public void insert(SingleResultCallback<Void> callback) {
prefixCache.add(this);
prefixIdCache.put(id.toLowerCase(), this);
prefixesCollection.insertOne(this, SyncUtils.vertxWrap(callback));
}
public void delete(SingleResultCallback<Void> callback) {
prefixCache.remove(this);
prefixIdCache.remove(id.toLowerCase());
prefixesCollection.deleteOne(new Document("_id", id), SyncUtils.vertxWrap(new MongoToVoidMongoCallback<>(callback)));
}
}

View File

@ -0,0 +1,140 @@
package net.frozenorb.apiv3.domain;
import com.google.common.collect.Collections2;
import com.mongodb.async.SingleResultCallback;
import com.mongodb.async.client.MongoCollection;
import com.mongodb.async.client.MongoDatabase;
import fr.javatic.mongo.jacksonCodec.Entity;
import fr.javatic.mongo.jacksonCodec.objectId.Id;
import lombok.AllArgsConstructor;
import lombok.Getter;
import net.frozenorb.apiv3.unsorted.MongoToVoidMongoCallback;
import net.frozenorb.apiv3.util.SpringUtils;
import net.frozenorb.apiv3.util.SyncUtils;
import org.bson.Document;
import org.bson.types.ObjectId;
import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;
@Entity
@AllArgsConstructor
public final class PrefixGrant {
private static final MongoCollection<PrefixGrant> grantsCollection = SpringUtils.getBean(MongoDatabase.class).getCollection("prefixGrants", PrefixGrant.class);
@Getter @Id private String id;
@Getter private UUID user;
@Getter private String reason;
@Getter private Set<String> scopes;
@Getter private String prefix;
@Getter private Instant expiresAt;
@Getter private UUID addedBy;
@Getter private Instant addedAt;
@Getter private UUID removedBy;
@Getter private Instant removedAt;
@Getter private String removalReason;
@Getter private int storeItemId;
@Getter private int storeOrderId;
public static void findAll(SingleResultCallback<List<PrefixGrant>> callback) {
grantsCollection.find().sort(new Document("addedAt", -1)).into(new LinkedList<>(), SyncUtils.vertxWrap(callback));
}
public static void findByPrefix(Collection<Prefix> prefixes, SingleResultCallback<List<PrefixGrant>> callback) {
Collection<String> convertedPrefixes = prefixes.stream().map(Prefix::getId).collect(Collectors.toList());
grantsCollection.find(new Document("prefix", new Document("$in", convertedPrefixes))).into(new LinkedList<>(), SyncUtils.vertxWrap(callback));
}
public static void findPaginated(Document query, int skip, int pageSize, SingleResultCallback<List<PrefixGrant>> callback) {
grantsCollection.find(query).sort(new Document("addedAt", -1)).skip(skip).limit(pageSize).into(new LinkedList<>(), SyncUtils.vertxWrap(callback));
}
public static void findById(String id, SingleResultCallback<PrefixGrant> callback) {
grantsCollection.find(new Document("_id", id)).first(SyncUtils.vertxWrap(callback));
}
public static void findByUser(User user, SingleResultCallback<List<PrefixGrant>> callback) {
findByUser(user.getId(), callback);
}
public static void findByUser(UUID user, SingleResultCallback<List<PrefixGrant>> callback) {
grantsCollection.find(new Document("user", user)).into(new LinkedList<>(), SyncUtils.vertxWrap(callback));
}
public static void findByUserGrouped(Iterable<UUID> users, SingleResultCallback<Map<UUID, List<PrefixGrant>>> callback) {
grantsCollection.find(new Document("user", new Document("$in", users))).into(new LinkedList<>(), SyncUtils.vertxWrap((grants, error) -> {
if (error != null) {
callback.onResult(null, error);
} else {
Map<UUID, List<PrefixGrant>> result = new HashMap<>();
for (UUID user : users) {
result.put(user, new LinkedList<>());
}
for (PrefixGrant grant : grants) {
result.get(grant.getUser()).add(grant);
}
callback.onResult(result, null);
}
}));
}
private PrefixGrant() {} // For Jackson
public PrefixGrant(User user, String reason, Set<ServerGroup> scopes, Prefix prefix, Instant expiresAt, User addedBy) {
this(user, reason, scopes, prefix, expiresAt, addedBy, -1, -1);
}
public PrefixGrant(User user, String reason, Set<ServerGroup> scopes, Prefix prefix, Instant expiresAt, User addedBy, int storeItemId, int storeOrderId) {
this.id = new ObjectId().toString();
this.user = user.getId();
this.reason = reason;
this.scopes = new HashSet<>(Collections2.transform(scopes, ServerGroup::getId));
this.prefix = prefix.getId();
this.expiresAt = expiresAt;
this.addedBy = addedBy == null ? null : addedBy.getId();
this.addedAt = Instant.now();
this.storeItemId = storeItemId;
this.storeOrderId = storeOrderId;
}
public boolean isActive() {
return !(isExpired() || isRemoved());
}
public boolean isExpired() {
return expiresAt != null && expiresAt.isBefore(Instant.now());
}
public boolean isRemoved() {
return removedAt != null;
}
public boolean appliesOn(ServerGroup serverGroup) {
return isGlobal() || scopes.contains(serverGroup.getId());
}
public boolean isGlobal() {
return scopes.isEmpty();
}
public void insert(SingleResultCallback<Void> callback) {
grantsCollection.insertOne(this, SyncUtils.vertxWrap(callback));
}
public void delete(User removedBy, String reason, SingleResultCallback<Void> callback) {
this.removedBy = removedBy == null ? null : removedBy.getId();
this.removedAt = Instant.now();
this.removalReason = reason;
grantsCollection.replaceOne(new Document("_id", id), this, SyncUtils.vertxWrap(new MongoToVoidMongoCallback<>(callback)));
}
}

View File

@ -690,14 +690,16 @@ public final class User {
}
Rank parent = otherRank;
if(parent != null) {
// Iterate up the inheritance tree to detect rank redundancies.
while (parent.getInheritsFromId() != null) {
if (parent == grantedRank) {
grantedRanks.remove(grantedRank);
// Iterate up the inheritance tree to detect rank redundancies.
while (parent.getInheritsFromId() != null) {
if (parent == grantedRank) {
grantedRanks.remove(grantedRank);
}
parent = Rank.findById(parent.getInheritsFromId());
}
parent = Rank.findById(parent.getInheritsFromId());
}
}
}

View File

@ -27,6 +27,10 @@ public enum AuditLogActionType {
GRANT_CREATE(false),
GRANT_UPDATE(false),
GRANT_DELETE(false),
PREFIXGRANT_CREATE(false),
PREFIXGRANT_UPDATE(false),
PREFIXGRANT_DELETE(false),
IP_BAN_CREATE(false),
IP_BAN_UPDATE(false),
IP_BAN_DELETE(false),
@ -62,6 +66,11 @@ public enum AuditLogActionType {
RANK_CREATE(false),
RANK_UPDATE(false),
RANK_DELETE(false),
PREFIX_CREATE(false),
PREFIX_UPDATE(false),
PREFIX_DELETE(false),
SERVER_GROUP_CREATE(false),
SERVER_GROUP_UPDATE(false),
SERVER_GROUP_DELETE(false),

View File

@ -17,4 +17,6 @@ public class Permissions {
public static final String CREATE_GRANT = rootPermission + ".grant.create";
public static final String REMOVE_GRANT = rootPermission + ".grant.remove";
public static final String CREATE_PREFIXGRANT = rootPermission + ".prefixgrant.create";
public static final String REMOVE_PREFIXGRANT = rootPermission + ".prefixgrant.remove";
}

View File

@ -1,5 +1,7 @@
package net.frozenorb.apiv3.unsorted.actor;
import com.google.common.base.MoreObjects;
public interface Actor {
boolean isAuthorized();
@ -8,4 +10,5 @@ public interface Actor {
ActorType getType();
}

View File

@ -1,5 +1,6 @@
package net.frozenorb.apiv3.unsorted.actor;
import com.google.common.base.MoreObjects;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -9,5 +10,12 @@ public final class SimpleActor implements Actor {
@Getter private String name;
@Getter private ActorType type;
@Getter private boolean authorized;
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
.add("authorized", isAuthorized())
.add("name",getName())
.add("type",getType().name())
.toString();
}
}

View File

@ -35,7 +35,6 @@ public class PermissionUtils {
for (Rank rank : mergeQueue) {
Map<String, Boolean> rankPermissions = convertFromList(raw.get(rank.getId()));
// If there's no permissions defined for this rank just skip it.
if (!rankPermissions.isEmpty()) {
result = mergePermissions(result, rankPermissions);

View File

@ -18,11 +18,11 @@ public final class AuthorizationFilter implements Handler<RoutingContext> {
Actor actor = ctx.get("actor");
ctx.next();
/*if (actor.isAuthorized()) {
if (actor.isAuthorized()) {
} else {
ErrorUtils.respondOther(ctx, 403, "Failed to authorize as an approved actor.", "failedToAuthorizeNotApprovedActor", ImmutableMap.of());
}*/
}
}
}

View File

@ -0,0 +1,44 @@
package net.frozenorb.apiv3.web.route.prefix;
import com.google.common.collect.ImmutableMap;
import io.vertx.core.Handler;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.domain.Prefix;
import net.frozenorb.apiv3.service.auditlog.AuditLog;
import net.frozenorb.apiv3.service.auditlog.AuditLogActionType;
import net.frozenorb.apiv3.util.ErrorUtils;
import net.frozenorb.apiv3.util.SyncUtils;
import net.frozenorb.apiv3.util.UuidUtils;
import org.springframework.stereotype.Component;
@Component
public final class DELETEPrefixesId implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) {
Prefix prefix = Prefix.findById(ctx.request().getParam("prefixId"));
if (prefix == null) {
ErrorUtils.respondNotFound(ctx, "Prefix", ctx.request().getParam("prefixId"));
return;
}
SyncUtils.<Void>runBlocking(v -> prefix.delete(v));
JsonObject requestBody = ctx.getBodyAsJson();
if (requestBody.containsKey("removedBy")) {
AuditLog.log(UuidUtils.parseUuid(requestBody.getString("removedBy")), requestBody.getString("removedByIp"), ctx, AuditLogActionType.RANK_DELETE, ImmutableMap.of("prefixId", prefix.getId()), (ignored, error) -> {
if (error != null) {
ErrorUtils.respondInternalError(ctx, error);
} else {
APIv3.respondJson(ctx, 200, prefix);
}
});
} else {
APIv3.respondJson(ctx, 200, prefix);
}
}
}

View File

@ -0,0 +1,19 @@
package net.frozenorb.apiv3.web.route.prefix;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.domain.Prefix;
import net.frozenorb.apiv3.domain.Rank;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
@Component
public final class GETPrefixes implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) {
APIv3.respondJson(ctx, 200, Prefix.findAll());
}
}

View File

@ -0,0 +1,16 @@
package net.frozenorb.apiv3.web.route.prefix;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.domain.Prefix;
import org.springframework.stereotype.Component;
@Component
public final class GETPrefixesId implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) {
APIv3.respondJson(ctx, 200, Prefix.findById(ctx.request().getParam("prefixId")));
}
}

View File

@ -0,0 +1,44 @@
package net.frozenorb.apiv3.web.route.prefix;
import com.google.common.collect.ImmutableMap;
import io.vertx.core.Handler;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.domain.Prefix;
import net.frozenorb.apiv3.service.auditlog.AuditLog;
import net.frozenorb.apiv3.service.auditlog.AuditLogActionType;
import net.frozenorb.apiv3.util.ErrorUtils;
import net.frozenorb.apiv3.util.SyncUtils;
import net.frozenorb.apiv3.util.UuidUtils;
import org.springframework.stereotype.Component;
@Component
public final class POSTPrefixes implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) {
JsonObject requestBody = ctx.getBodyAsJson();
String id = requestBody.getString("id");
String displayName = requestBody.getString("displayName");
String prefix = requestBody.getString("prefix");
boolean purchaseable = requestBody.getBoolean("purchaseable");
String buttonName = requestBody.getString("buttonName");
String buttonDescription = requestBody.getString("buttonDescription");
Prefix pref = new Prefix(id, displayName, prefix, purchaseable, buttonName, buttonDescription);
SyncUtils.<Void>runBlocking(pref::insert);
if (requestBody.containsKey("addedBy")) {
AuditLog.log(UuidUtils.parseUuid(requestBody.getString("addedBy")), requestBody.getString("addedByIp"), ctx, AuditLogActionType.PREFIX_CREATE, ImmutableMap.of("prefixId", id), (ignored, error) -> {
if (error != null) {
ErrorUtils.respondInternalError(ctx, error);
} else {
APIv3.respondJson(ctx, 200, pref);
}
});
} else {
APIv3.respondJson(ctx, 200, pref);
}
}
}

View File

@ -0,0 +1,7 @@
package net.frozenorb.apiv3.web.route.prefix;
import org.springframework.stereotype.Component;
@Component
public class PUTPrefixesId {
}

View File

@ -0,0 +1,65 @@
package net.frozenorb.apiv3.web.route.prefixGrants;
import com.google.common.collect.ImmutableMap;
import io.vertx.core.Handler;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.domain.Grant;
import net.frozenorb.apiv3.domain.User;
import net.frozenorb.apiv3.service.auditlog.AuditLog;
import net.frozenorb.apiv3.service.auditlog.AuditLogActionType;
import net.frozenorb.apiv3.unsorted.Permissions;
import net.frozenorb.apiv3.util.ErrorUtils;
import net.frozenorb.apiv3.util.SyncUtils;
import org.springframework.stereotype.Component;
@Component
public final class DELETEPrefixGrantsId implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) {
Grant grant = SyncUtils.runBlocking(v -> Grant.findById(ctx.request().getParam("grantId"), v));
if (grant == null) {
ErrorUtils.respondNotFound(ctx, "Grant", ctx.request().getParam("grantId"));
return;
} else if (!grant.isActive()) {
ErrorUtils.respondInvalidInput(ctx, "Cannot remove an inactive grant.");
return;
}
JsonObject requestBody = ctx.getBodyAsJson();
// We purposely don't do a null check, grant removals don't have to have a user/ip.
User removedBy = SyncUtils.runBlocking(v -> User.findById(requestBody.getString("removedBy"), v));
String reason = requestBody.getString("reason");
if (reason == null || reason.trim().isEmpty()) {
ErrorUtils.respondRequiredInput(ctx, "reason");
return;
}
if (removedBy != null) {
boolean allowed = SyncUtils.runBlocking(v -> removedBy.hasPermissionAnywhere(Permissions.REMOVE_PREFIXGRANT + "." + grant.getRank(), v));
if (!allowed) {
ErrorUtils.respondOther(ctx, 409, "User given does not have permission to remove this grant.", "userDoesNotHavePermission", ImmutableMap.of());
return;
}
}
SyncUtils.<Void>runBlocking(v -> grant.delete(removedBy, reason, v));
if (removedBy != null) {
AuditLog.log(removedBy.getId(), requestBody.getString("removedByIp"), ctx, AuditLogActionType.PREFIXGRANT_DELETE, ImmutableMap.of("grantId", grant.getId()), (ignored, error) -> {
if (error != null) {
ErrorUtils.respondInternalError(ctx, error);
} else {
APIv3.respondJson(ctx, 200, grant);
}
});
} else {
APIv3.respondJson(ctx, 200, grant);
}
}
}

View File

@ -0,0 +1,37 @@
package net.frozenorb.apiv3.web.route.prefixGrants;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.domain.PrefixGrant;
import net.frozenorb.apiv3.util.ErrorUtils;
import net.frozenorb.apiv3.util.UuidUtils;
import org.bson.Document;
import org.springframework.stereotype.Component;
import java.util.stream.Collectors;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
@Component
public final class GETPrefixGrants implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) {
try {
int skip = ctx.request().getParam("skip") == null ? 0 : Integer.parseInt(ctx.request().getParam("skip"));
int pageSize = ctx.request().getParam("pageSize") == null ? 100 : Integer.parseInt(ctx.request().getParam("pageSize"));
PrefixGrant.findPaginated(ctx.request().getParam("user") == null ? new Document() : new Document("user", UuidUtils.parseUuid(ctx.request().getParam("user"))), skip, pageSize, (grants, error) -> {
if (ctx.request().getParam("active") != null) {
boolean requireActive = Boolean.parseBoolean(ctx.request().getParam("active"));
APIv3.respondJson(ctx, 200, grants.stream().filter(grant -> grant.isActive() == requireActive).collect(Collectors.toList()));
} else {
APIv3.respondJson(ctx, 200, grants);
}
});
} catch (NumberFormatException ignored) {
ErrorUtils.respondInvalidInput(ctx, "skip and pageSize must be numerical inputs.");
}
}
}

View File

@ -0,0 +1,24 @@
package net.frozenorb.apiv3.web.route.prefixGrants;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.domain.Grant;
import net.frozenorb.apiv3.domain.PrefixGrant;
import net.frozenorb.apiv3.util.ErrorUtils;
import org.springframework.stereotype.Component;
@Component
public final class GETPrefixGrantsId implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) {
PrefixGrant.findById(ctx.request().getParam("grantId"), (grant, error) -> {
if (error != null) {
ErrorUtils.respondInternalError(ctx, error);
} else {
APIv3.respondJson(ctx, 200, grant);
}
});
}
}

View File

@ -0,0 +1,125 @@
package net.frozenorb.apiv3.web.route.prefixGrants;
import com.google.common.collect.ImmutableMap;
import io.vertx.core.Handler;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.domain.*;
import net.frozenorb.apiv3.service.auditlog.AuditLog;
import net.frozenorb.apiv3.service.auditlog.AuditLogActionType;
import net.frozenorb.apiv3.service.totp.TotpAuthorizationResult;
import net.frozenorb.apiv3.unsorted.Permissions;
import net.frozenorb.apiv3.util.ErrorUtils;
import net.frozenorb.apiv3.util.SyncUtils;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Component
public final class POSTPrefixGrants implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) {
JsonObject requestBody = ctx.getBodyAsJson();
User target = SyncUtils.runBlocking(v -> User.findById(requestBody.getString("user"), v));
if (target == null) {
ErrorUtils.respondNotFound(ctx, "User", requestBody.getString("user"));
return;
}
String reason = requestBody.getString("reason");
if (reason == null || reason.trim().isEmpty()) {
ErrorUtils.respondRequiredInput(ctx, "reason");
return;
}
Set<ServerGroup> scopes = new HashSet<>();
List<String> scopeIds = (List<String>) requestBody.getJsonArray("scopes").getList();
if (!scopeIds.isEmpty()) {
for (String serverGroupId : scopeIds) {
ServerGroup serverGroup = ServerGroup.findById(serverGroupId);
if (serverGroup == null) {
ErrorUtils.respondNotFound(ctx, "Server group", serverGroupId);
return;
}
scopes.add(serverGroup);
}
}
Prefix prefix = Prefix.findById(requestBody.getString("prefix"));
if (prefix == null) {
ErrorUtils.respondNotFound(ctx, "Prefix", requestBody.getString("prefix"));
return;
}
Instant expiresAt = null;
if (requestBody.containsKey("expiresIn") && requestBody.getLong("expiresIn") != -1) {
long expiresInMillis = requestBody.getLong("expiresIn") * 1000;
expiresAt = Instant.ofEpochMilli(System.currentTimeMillis() + expiresInMillis);
}
if (expiresAt != null && expiresAt.isBefore(Instant.now())) {
ErrorUtils.respondInvalidInput(ctx, "Expiration time cannot be in the past.");
return;
}
// We purposely don't fail on a null check, grants don't have to have a source.
User addedBy = SyncUtils.runBlocking(v -> User.findById(requestBody.getString("addedBy"), v));
if (addedBy != null) {
boolean allowed = SyncUtils.runBlocking(v -> addedBy.hasPermissionAnywhere(Permissions.CREATE_PREFIXGRANT + "." + prefix.getId(), v));
if (!allowed) {
ErrorUtils.respondOther(ctx, 409, "User given does not have permission to create this grant.", "userDoesNotHavePermission", ImmutableMap.of());
return;
}
if (false) {
if (!requestBody.containsKey("totpCode")) {
ErrorUtils.respondInvalidInput(ctx, "prefix must be granted through API or website.");
return;
}
int code = requestBody.getInteger("totpCode", -1);
TotpAuthorizationResult totpAuthorizationResult = SyncUtils.runBlocking(v -> addedBy.checkTotpAuthorization(code, null, v));
if (!totpAuthorizationResult.isAuthorized()) {
ErrorUtils.respondInvalidInput(ctx, "Totp authorization failed: " + totpAuthorizationResult.name());
return;
}
}
} else if (false) {
ErrorUtils.respondInvalidInput(ctx, "Prefix must be granted through API or website.");
return;
}
int storeItemId = requestBody.getInteger("storeItemId", -1);
int storeOrderId = requestBody.getInteger("storeOrderId", -1);
PrefixGrant grant = new PrefixGrant(target, reason, scopes, prefix, expiresAt, addedBy, storeItemId, storeOrderId);
SyncUtils.<Void>runBlocking(v -> grant.insert(v));
if (addedBy != null) {
AuditLog.log(addedBy.getId(), requestBody.getString("addedByIp"), ctx, AuditLogActionType.PREFIXGRANT_CREATE, ImmutableMap.of("grantId", grant.getId()), (ignored, error) -> {
if (error != null) {
ErrorUtils.respondInternalError(ctx, error);
} else {
APIv3.respondJson(ctx, 200, grant);
}
});
} else {
APIv3.respondJson(ctx, 200, grant);
}
}
}

View File

@ -40,15 +40,16 @@ public final class POSTServersHeartbeat implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) {
Actor actor = ctx.get("actor");
if (actor.getType() != ActorType.SERVER) {
ErrorUtils.respondOther(ctx, 403, "This action can only be performed when requested by a server.", "serverOnly", ImmutableMap.of());
ErrorUtils.respondOther(ctx, 403,
"This action can only be performed when requested by a server.", "serverOnly", ImmutableMap.of());
return;
}
Server actorServer = Server.findById(actor.getName());
JsonObject requestBody = ctx.getBodyAsJson();
System.out.println("body: " + requestBody);
JsonObject players = requestBody.getJsonObject("players");
Map<UUID, String> playerNames = extractPlayerNames(players);
Map<UUID, String> playerIps = extractPlayerIps(players);
@ -75,6 +76,7 @@ public final class POSTServersHeartbeat implements Handler<RoutingContext> {
}
private Future<Void> createInfoResponse(Server server, double tps, Map<UUID, String> playerNames) {
Future<Void> callback = Future.future();
if (server != null && playerNames != null) {
@ -92,6 +94,7 @@ public final class POSTServersHeartbeat implements Handler<RoutingContext> {
}
private Future<Map<String, Object>> createPlayerResponse(Server server, Map<UUID, String> playerNames, Map<UUID, String> playerIps) {
Future<Map<String, Object>> callback = Future.future();
Future<Map<UUID, User>> userLookupCallback = Future.future();
@ -139,18 +142,19 @@ public final class POSTServersHeartbeat implements Handler<RoutingContext> {
}
private Future<Map<String, Object>> createPermissionsResponse(ServerGroup serverGroup) {
Future<Map<String, Object>> callback = Future.future();
Map<String, Object> permissionsResponse = new HashMap<>();
for (Rank rank : Rank.findAll()) {
Map<String, Boolean> defaultCalculatedPermissions = ServerGroup.findDefault().calculatePermissions(rank);
Map<String, Boolean> calculatedPermissions = serverGroup.calculatePermissions(rank);
Map<String, Boolean> scopedPermissions = PermissionUtils.mergePermissions(
ServerGroup.findDefault().calculatePermissions(rank),
serverGroup.calculatePermissions(rank)
defaultCalculatedPermissions,
calculatedPermissions
);
permissionsResponse.put(rank.getId(), PermissionUtils.convertToList(scopedPermissions));
}
callback.complete(permissionsResponse);
return callback;
}