Convert authorization to use access tokens. Completes #24
This commit is contained in:
parent
bcae144e8c
commit
29a13c1647
@ -11,5 +11,3 @@ maxMind.userId=66817
|
|||||||
maxMind.maxMindLicenseKey=8Aw9NsOUeOp7
|
maxMind.maxMindLicenseKey=8Aw9NsOUeOp7
|
||||||
zang.accountSid=ACf18890845596403e330944d98886440c
|
zang.accountSid=ACf18890845596403e330944d98886440c
|
||||||
zang.authToken=dc70bbd1fbd8411ba133fa93813a461b
|
zang.authToken=dc70bbd1fbd8411ba133fa93813a461b
|
||||||
auth.websiteApiKey=RVbp4hY6sCFVaf
|
|
||||||
auth.bungeeApiKey=6d9cf76dc9f0d23
|
|
@ -2,6 +2,6 @@ package net.frozenorb.apiv3.actor;
|
|||||||
|
|
||||||
public enum ActorType {
|
public enum ActorType {
|
||||||
|
|
||||||
WEBSITE, BUNGEE, SERVER, USER, UNKNOWN
|
WEBSITE, BUNGEE_CORD, SERVER, UNKNOWN
|
||||||
|
|
||||||
}
|
}
|
13
src/main/java/net/frozenorb/apiv3/actor/SimpleActor.java
Normal file
13
src/main/java/net/frozenorb/apiv3/actor/SimpleActor.java
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package net.frozenorb.apiv3.actor;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
public final class SimpleActor implements Actor {
|
||||||
|
|
||||||
|
@Getter private String name;
|
||||||
|
@Getter private ActorType type;
|
||||||
|
@Getter private boolean authorized;
|
||||||
|
|
||||||
|
}
|
@ -1,23 +0,0 @@
|
|||||||
package net.frozenorb.apiv3.actor.actors;
|
|
||||||
|
|
||||||
import net.frozenorb.apiv3.actor.Actor;
|
|
||||||
import net.frozenorb.apiv3.actor.ActorType;
|
|
||||||
|
|
||||||
public final class BungeeActor implements Actor {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAuthorized() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return "Bungee";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ActorType getType() {
|
|
||||||
return ActorType.BUNGEE;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
package net.frozenorb.apiv3.actor.actors;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import net.frozenorb.apiv3.actor.Actor;
|
|
||||||
import net.frozenorb.apiv3.actor.ActorType;
|
|
||||||
import net.frozenorb.apiv3.model.Server;
|
|
||||||
|
|
||||||
public final class ServerActor implements Actor {
|
|
||||||
|
|
||||||
@Getter private final Server server;
|
|
||||||
|
|
||||||
public ServerActor(Server server) {
|
|
||||||
this.server = server;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAuthorized() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return server.getId();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ActorType getType() {
|
|
||||||
return ActorType.SERVER;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
package net.frozenorb.apiv3.actor.actors;
|
|
||||||
|
|
||||||
import net.frozenorb.apiv3.actor.Actor;
|
|
||||||
import net.frozenorb.apiv3.actor.ActorType;
|
|
||||||
|
|
||||||
public final class UnknownActor implements Actor {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAuthorized() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return "Unknown";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ActorType getType() {
|
|
||||||
return ActorType.UNKNOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
package net.frozenorb.apiv3.actor.actors;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import net.frozenorb.apiv3.actor.Actor;
|
|
||||||
import net.frozenorb.apiv3.actor.ActorType;
|
|
||||||
import net.frozenorb.apiv3.model.User;
|
|
||||||
|
|
||||||
public final class UserActor implements Actor {
|
|
||||||
|
|
||||||
@Getter private final User user;
|
|
||||||
private final boolean authorized;
|
|
||||||
|
|
||||||
public UserActor(User user, boolean authorized) {
|
|
||||||
this.user = user;
|
|
||||||
this.authorized = authorized;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAuthorized() {
|
|
||||||
return authorized;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return user.getLastUsername();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ActorType getType() {
|
|
||||||
return ActorType.USER;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
package net.frozenorb.apiv3.actor.actors;
|
|
||||||
|
|
||||||
import net.frozenorb.apiv3.actor.Actor;
|
|
||||||
import net.frozenorb.apiv3.actor.ActorType;
|
|
||||||
|
|
||||||
public final class WebsiteActor implements Actor {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAuthorized() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return "Website";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ActorType getType() {
|
|
||||||
return ActorType.WEBSITE;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -46,7 +46,7 @@ public final class PunishmentConverter implements Block<Document> {
|
|||||||
null,
|
null,
|
||||||
punishment.containsKey("addedBy") ? oidToUniqueId.get(((Map<String, Object>) punishment.get("addedBy")).get("$id")) : null,
|
punishment.containsKey("addedBy") ? oidToUniqueId.get(((Map<String, Object>) punishment.get("addedBy")).get("$id")) : null,
|
||||||
punishment.getDate("created").toInstant(),
|
punishment.getDate("created").toInstant(),
|
||||||
punishment.containsKey("createdOn") ? String.valueOf(((Map<String, Object>) punishment.get("createdOn")).get("$id")) : "Website",
|
punishment.containsKey("createdOn") ? String.valueOf(((Map<String, Object>) punishment.get("createdOn")).get("$id")) : "Old Website",
|
||||||
punishment.containsKey("createdOn") ? ActorType.SERVER : ActorType.WEBSITE,
|
punishment.containsKey("createdOn") ? ActorType.SERVER : ActorType.WEBSITE,
|
||||||
punishment.containsKey("removedBy") ? (((Map<String, Object>) punishment.get("removedBy")).get("$ref").equals("user") ? oidToUniqueId.get(((Map<String, Object>) punishment.get("removedBy")).get("$id")) : null) : null,
|
punishment.containsKey("removedBy") ? (((Map<String, Object>) punishment.get("removedBy")).get("$ref").equals("user") ? oidToUniqueId.get(((Map<String, Object>) punishment.get("removedBy")).get("$id")) : null) : null,
|
||||||
punishment.containsKey("removedBy") ? (punishment.containsKey("removedAt") ? punishment.getDate("removedAt") : punishment.getDate("created")).toInstant() : null,
|
punishment.containsKey("removedBy") ? (punishment.containsKey("removedAt") ? punishment.getDate("removedAt") : punishment.getDate("created")).toInstant() : null,
|
||||||
|
@ -1,133 +1,58 @@
|
|||||||
package net.frozenorb.apiv3.handler;
|
package net.frozenorb.apiv3.handler;
|
||||||
|
|
||||||
import com.google.common.base.Charsets;
|
|
||||||
import io.vertx.core.Handler;
|
import io.vertx.core.Handler;
|
||||||
import io.vertx.ext.web.RoutingContext;
|
import io.vertx.ext.web.RoutingContext;
|
||||||
import net.frozenorb.apiv3.APIv3;
|
import net.frozenorb.apiv3.actor.ActorType;
|
||||||
import net.frozenorb.apiv3.actor.actors.*;
|
import net.frozenorb.apiv3.actor.SimpleActor;
|
||||||
import net.frozenorb.apiv3.model.Server;
|
import net.frozenorb.apiv3.model.AccessToken;
|
||||||
import net.frozenorb.apiv3.model.User;
|
|
||||||
import net.frozenorb.apiv3.unsorted.Permissions;
|
|
||||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||||
|
|
||||||
import java.util.Base64;
|
|
||||||
|
|
||||||
public final class ActorAttributeHandler implements Handler<RoutingContext> {
|
public final class ActorAttributeHandler implements Handler<RoutingContext> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(RoutingContext ctx) {
|
public void handle(RoutingContext ctx) {
|
||||||
String authorizationHeader = ctx.request().getHeader("Authorization");
|
|
||||||
String mhqAuthorizationHeader = ctx.request().getHeader("MHQ-Authorization");
|
String mhqAuthorizationHeader = ctx.request().getHeader("MHQ-Authorization");
|
||||||
|
|
||||||
if (authorizationHeader != null) {
|
if (mhqAuthorizationHeader != null) {
|
||||||
processBasicAuthorization(authorizationHeader, ctx);
|
|
||||||
} else if (mhqAuthorizationHeader != null) {
|
|
||||||
processMHQAuthorization(mhqAuthorizationHeader, ctx);
|
processMHQAuthorization(mhqAuthorizationHeader, ctx);
|
||||||
} else {
|
} else {
|
||||||
processNoAuthorization(ctx);
|
processNoAuthorization(ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processBasicAuthorization(String authHeader, RoutingContext ctx) {
|
private void processMHQAuthorization(String accessTokenString, RoutingContext ctx) {
|
||||||
String encodedHeader = authHeader.substring("Basic ".length());
|
if (accessTokenString == null || accessTokenString.isEmpty()) {
|
||||||
String decodedHeader = new String(Base64.getDecoder().decode(encodedHeader.getBytes(Charsets.UTF_8)), Charsets.UTF_8);
|
ErrorUtils.respondGeneric(ctx, 401, "Failed to authorize: Key not provided");
|
||||||
String[] splitHeader = decodedHeader.split(":");
|
|
||||||
|
|
||||||
if (splitHeader.length != 2) {
|
|
||||||
ErrorUtils.respondGeneric(ctx, 401, "Failed to authorize.");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String username = splitHeader[0];
|
AccessToken.findById(accessTokenString, (accessToken, error) -> {
|
||||||
String password = splitHeader[1];
|
|
||||||
|
|
||||||
User.findByLastUsername(username, (user, error) -> {
|
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
ErrorUtils.respondInternalError(ctx, error);
|
ErrorUtils.respondInternalError(ctx, error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user != null && user.checkPassword(password)) {
|
if (accessToken == null) {
|
||||||
user.hasPermissionAnywhere(Permissions.SIGN_API_REQUEST, (hasPermission, error2) -> {
|
|
||||||
if (error2 != null) {
|
|
||||||
ErrorUtils.respondInternalError(ctx, error2);
|
|
||||||
} else {
|
|
||||||
ctx.put("actor", new UserActor(user, hasPermission));
|
|
||||||
ctx.next();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
ErrorUtils.respondGeneric(ctx, 401, "Failed to authorize as " + username + ".");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processMHQAuthorization(String authHeader, RoutingContext ctx) {
|
|
||||||
String[] splitHeader = authHeader.split(" ");
|
|
||||||
|
|
||||||
if (splitHeader.length < 2) {
|
|
||||||
ErrorUtils.respondGeneric(ctx, 401, "Failed to authorize.");
|
ErrorUtils.respondGeneric(ctx, 401, "Failed to authorize.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String type = splitHeader[0];
|
if (!accessToken.getLockedIps().isEmpty()) {
|
||||||
|
boolean allowed = accessToken.getLockedIps().contains(ctx.request().remoteAddress().host());
|
||||||
|
|
||||||
switch (type.toLowerCase()) {
|
if (!allowed) {
|
||||||
case "website":
|
ErrorUtils.respondGeneric(ctx, 401, "Your ip address is not authenticated to use the provided access token.");
|
||||||
String givenWebsiteKey = splitHeader[1];
|
|
||||||
String properWebsiteKey = APIv3.getConfig().getProperty("auth.websiteApiKey");
|
|
||||||
|
|
||||||
if (givenWebsiteKey.equals(properWebsiteKey)) {
|
|
||||||
ctx.put("actor", new WebsiteActor());
|
|
||||||
ctx.next();
|
|
||||||
} else {
|
|
||||||
ErrorUtils.respondGeneric(ctx, 401, "Failed to authorize as website.");
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case "server":
|
|
||||||
Server server = Server.findById(splitHeader[1]);
|
|
||||||
|
|
||||||
if (server == null) {
|
|
||||||
ErrorUtils.respondGeneric(ctx, 401, "Failed to authorize: Server " + splitHeader[1] + " not found");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (splitHeader.length != 3) {
|
|
||||||
ErrorUtils.respondGeneric(ctx, 401, "Failed to authorize: Key not provided");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String givenServerKey = splitHeader[2];
|
ctx.put("actor", new SimpleActor(accessToken.getActorName(), accessToken.getActorType(), true));
|
||||||
|
|
||||||
if (givenServerKey.equals(server.getApiKey())) {
|
|
||||||
ctx.put("actor", new ServerActor(server));
|
|
||||||
ctx.next();
|
ctx.next();
|
||||||
} else {
|
});
|
||||||
ErrorUtils.respondGeneric(ctx, 401, "Failed to authorize as " + server.getId() + ".");
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case "bungee":
|
|
||||||
String givenBungeeKey = splitHeader[1];
|
|
||||||
String properBungeeKey = APIv3.getConfig().getProperty("auth.bungeeApiKey");
|
|
||||||
|
|
||||||
if (givenBungeeKey.equals(properBungeeKey)) {
|
|
||||||
ctx.put("actor", new BungeeActor());
|
|
||||||
ctx.next();
|
|
||||||
} else {
|
|
||||||
ErrorUtils.respondGeneric(ctx, 401, "Failed to authorize as bungee.");
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ErrorUtils.respondGeneric(ctx, 401, "Failed to authorize as " + type + ".");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processNoAuthorization(RoutingContext ctx) {
|
private void processNoAuthorization(RoutingContext ctx) {
|
||||||
ctx.put("actor", new UnknownActor());
|
ctx.put("actor", new SimpleActor("UNKNOWN", ActorType.UNKNOWN, false));
|
||||||
ctx.next();
|
ctx.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,9 +3,6 @@ package net.frozenorb.apiv3.handler;
|
|||||||
import io.vertx.core.Handler;
|
import io.vertx.core.Handler;
|
||||||
import io.vertx.ext.web.RoutingContext;
|
import io.vertx.ext.web.RoutingContext;
|
||||||
import net.frozenorb.apiv3.actor.Actor;
|
import net.frozenorb.apiv3.actor.Actor;
|
||||||
import net.frozenorb.apiv3.actor.ActorType;
|
|
||||||
import net.frozenorb.apiv3.actor.actors.ServerActor;
|
|
||||||
import net.frozenorb.apiv3.model.Server;
|
|
||||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||||
|
|
||||||
public final class AuthorizationHandler implements Handler<RoutingContext> {
|
public final class AuthorizationHandler implements Handler<RoutingContext> {
|
||||||
@ -14,24 +11,11 @@ public final class AuthorizationHandler implements Handler<RoutingContext> {
|
|||||||
public void handle(RoutingContext ctx) {
|
public void handle(RoutingContext ctx) {
|
||||||
Actor actor = ctx.get("actor");
|
Actor actor = ctx.get("actor");
|
||||||
|
|
||||||
if (!actor.isAuthorized()) {
|
if (actor.isAuthorized()) {
|
||||||
ErrorUtils.respondGeneric(ctx, 403, "Please authorize as an approved actor. You're currently authorized as " + actor.getName() + " (" + actor.getType() + ")");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (actor.getType() == ActorType.SERVER) {
|
|
||||||
Server server = ((ServerActor) actor).getServer();
|
|
||||||
String[] serverAddress = server.getServerIp().split(":");
|
|
||||||
String expectedHost = serverAddress[0];
|
|
||||||
String remoteHost = ctx.request().remoteAddress().host();
|
|
||||||
|
|
||||||
if (!expectedHost.equals(remoteHost)) {
|
|
||||||
ErrorUtils.respondGeneric(ctx, 403, "Failed to authorize: Cannot authorize as " + server.getId() + " from given ip.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.next();
|
ctx.next();
|
||||||
|
} else {
|
||||||
|
ErrorUtils.respondGeneric(ctx, 403, "Please authorize as an approved actor. You're currently authorized as " + actor.getName() + " (" + actor.getType() + ")");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
77
src/main/java/net/frozenorb/apiv3/model/AccessToken.java
Normal file
77
src/main/java/net/frozenorb/apiv3/model/AccessToken.java
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
package net.frozenorb.apiv3.model;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.mongodb.async.SingleResultCallback;
|
||||||
|
import com.mongodb.async.client.MongoCollection;
|
||||||
|
import com.mongodb.client.result.DeleteResult;
|
||||||
|
import com.mongodb.client.result.UpdateResult;
|
||||||
|
import fr.javatic.mongo.jacksonCodec.Entity;
|
||||||
|
import fr.javatic.mongo.jacksonCodec.objectId.Id;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import net.frozenorb.apiv3.APIv3;
|
||||||
|
import net.frozenorb.apiv3.actor.Actor;
|
||||||
|
import net.frozenorb.apiv3.actor.ActorType;
|
||||||
|
import net.frozenorb.apiv3.maxmind.MaxMindResult;
|
||||||
|
import net.frozenorb.apiv3.util.MaxMindUtils;
|
||||||
|
import org.bson.Document;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@AllArgsConstructor
|
||||||
|
public final class AccessToken {
|
||||||
|
|
||||||
|
private static final MongoCollection<AccessToken> accessTokensCollection = APIv3.getDatabase().getCollection("accessTokens", AccessToken.class);
|
||||||
|
|
||||||
|
@Getter @Id private String id;
|
||||||
|
@Getter private String actorName;
|
||||||
|
@Getter private ActorType actorType;
|
||||||
|
@Getter private List<String> lockedIps;
|
||||||
|
@Getter private Instant createdAt;
|
||||||
|
@Getter private Instant lastUpdatedAt;
|
||||||
|
|
||||||
|
public static void findAll(SingleResultCallback<List<AccessToken>> callback) {
|
||||||
|
accessTokensCollection.find().into(new LinkedList<>(), callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void findById(String id, SingleResultCallback<AccessToken> callback) {
|
||||||
|
accessTokensCollection.find(new Document("_id", id)).first(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void findByNameAndType(String actorName, ActorType actorType, SingleResultCallback<AccessToken> callback) {
|
||||||
|
accessTokensCollection.find(new Document("actorName", actorName).append("actorType", actorType.name())).first(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AccessToken() {} // For Jackson
|
||||||
|
|
||||||
|
public AccessToken(Server server) {
|
||||||
|
// Can't extract server host code to another line because the call to another constructor must be on the first line.
|
||||||
|
this(UUID.randomUUID().toString().replace("-", ""), server.getId(), ActorType.SERVER, ImmutableList.of(server.getServerIp().split(":")[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccessToken(String id, String actorName, ActorType actorType, List<String> lockedIps) {
|
||||||
|
this.id = id;
|
||||||
|
this.actorName = actorName;
|
||||||
|
this.actorType = actorType;
|
||||||
|
this.lockedIps = lockedIps;
|
||||||
|
this.createdAt = Instant.now();
|
||||||
|
this.lastUpdatedAt = Instant.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void insert(SingleResultCallback<Void> callback) {
|
||||||
|
accessTokensCollection.insertOne(this, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void save(SingleResultCallback<UpdateResult> callback) {
|
||||||
|
accessTokensCollection.replaceOne(new Document("_id", id), this, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete(SingleResultCallback<DeleteResult> callback) {
|
||||||
|
accessTokensCollection.deleteOne(new Document("_id", id), callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -27,7 +27,6 @@ public final class Server {
|
|||||||
|
|
||||||
@Getter @Id private String id;
|
@Getter @Id private String id;
|
||||||
@Getter private String displayName;
|
@Getter private String displayName;
|
||||||
@Getter @ExcludeFromReplies String apiKey;
|
|
||||||
@Getter private String serverGroup;
|
@Getter private String serverGroup;
|
||||||
@Getter private String serverIp;
|
@Getter private String serverIp;
|
||||||
@Getter private Instant lastUpdatedAt;
|
@Getter private Instant lastUpdatedAt;
|
||||||
@ -103,10 +102,9 @@ public final class Server {
|
|||||||
|
|
||||||
private Server() {} // For Jackson
|
private Server() {} // For Jackson
|
||||||
|
|
||||||
public Server(String id, String displayName, String apiKey, ServerGroup serverGroup, String serverIp) {
|
public Server(String id, String displayName, ServerGroup serverGroup, String serverIp) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.displayName = displayName;
|
this.displayName = displayName;
|
||||||
this.apiKey = apiKey;
|
|
||||||
this.serverGroup = serverGroup.getId();
|
this.serverGroup = serverGroup.getId();
|
||||||
this.serverIp = serverIp;
|
this.serverIp = serverIp;
|
||||||
this.lastUpdatedAt = Instant.now();
|
this.lastUpdatedAt = Instant.now();
|
||||||
|
@ -6,8 +6,10 @@ import io.vertx.core.Handler;
|
|||||||
import io.vertx.core.json.JsonObject;
|
import io.vertx.core.json.JsonObject;
|
||||||
import io.vertx.ext.web.RoutingContext;
|
import io.vertx.ext.web.RoutingContext;
|
||||||
import net.frozenorb.apiv3.APIv3;
|
import net.frozenorb.apiv3.APIv3;
|
||||||
|
import net.frozenorb.apiv3.actor.ActorType;
|
||||||
import net.frozenorb.apiv3.auditLog.AuditLog;
|
import net.frozenorb.apiv3.auditLog.AuditLog;
|
||||||
import net.frozenorb.apiv3.auditLog.AuditLogActionType;
|
import net.frozenorb.apiv3.auditLog.AuditLogActionType;
|
||||||
|
import net.frozenorb.apiv3.model.AccessToken;
|
||||||
import net.frozenorb.apiv3.model.Server;
|
import net.frozenorb.apiv3.model.Server;
|
||||||
import net.frozenorb.apiv3.unsorted.BlockingCallback;
|
import net.frozenorb.apiv3.unsorted.BlockingCallback;
|
||||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||||
@ -24,9 +26,21 @@ public final class DELETEServersId implements Handler<RoutingContext> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockingCallback<DeleteResult> callback = new BlockingCallback<>();
|
BlockingCallback<DeleteResult> deleteServerCallback = new BlockingCallback<>();
|
||||||
server.delete(callback);
|
server.delete(deleteServerCallback);
|
||||||
callback.get();
|
deleteServerCallback.get();
|
||||||
|
|
||||||
|
BlockingCallback<DeleteResult> deleteAccessTokenCallback = new BlockingCallback<>();
|
||||||
|
AccessToken.findByNameAndType(server.getId(), ActorType.SERVER, (accessToken, error) -> {
|
||||||
|
if (error != null) {
|
||||||
|
deleteAccessTokenCallback.onResult(null, error);
|
||||||
|
} else if (accessToken != null) {
|
||||||
|
accessToken.delete(deleteServerCallback);
|
||||||
|
} else {
|
||||||
|
deleteAccessTokenCallback.onResult(null, new NullPointerException("Access token not found."));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
deleteAccessTokenCallback.get();
|
||||||
|
|
||||||
JsonObject requestBody = ctx.getBodyAsJson();
|
JsonObject requestBody = ctx.getBodyAsJson();
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import io.vertx.ext.web.RoutingContext;
|
|||||||
import net.frozenorb.apiv3.APIv3;
|
import net.frozenorb.apiv3.APIv3;
|
||||||
import net.frozenorb.apiv3.auditLog.AuditLog;
|
import net.frozenorb.apiv3.auditLog.AuditLog;
|
||||||
import net.frozenorb.apiv3.auditLog.AuditLogActionType;
|
import net.frozenorb.apiv3.auditLog.AuditLogActionType;
|
||||||
|
import net.frozenorb.apiv3.model.AccessToken;
|
||||||
import net.frozenorb.apiv3.model.Server;
|
import net.frozenorb.apiv3.model.Server;
|
||||||
import net.frozenorb.apiv3.model.ServerGroup;
|
import net.frozenorb.apiv3.model.ServerGroup;
|
||||||
import net.frozenorb.apiv3.unsorted.BlockingCallback;
|
import net.frozenorb.apiv3.unsorted.BlockingCallback;
|
||||||
@ -34,11 +35,15 @@ public final class POSTServers implements Handler<RoutingContext> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String generatedApiKey = UUID.randomUUID().toString();
|
Server server = new Server(id, displayName, group, ip);
|
||||||
Server server = new Server(id, displayName, generatedApiKey, group, ip);
|
BlockingCallback<Void> insertServerCallback = new BlockingCallback<>();
|
||||||
BlockingCallback<Void> callback = new BlockingCallback<>();
|
server.insert(insertServerCallback);
|
||||||
server.insert(callback);
|
insertServerCallback.get();
|
||||||
callback.get();
|
|
||||||
|
AccessToken accessToken = new AccessToken(server);
|
||||||
|
BlockingCallback<Void> insertAccessTokenCallback = new BlockingCallback<>();
|
||||||
|
accessToken.insert(insertAccessTokenCallback);
|
||||||
|
insertAccessTokenCallback.get();
|
||||||
|
|
||||||
if (requestBody.containsKey("addedBy")) {
|
if (requestBody.containsKey("addedBy")) {
|
||||||
AuditLog.log(UUID.fromString(requestBody.getString("addedBy")), requestBody.getString("addedByIp"), ctx, AuditLogActionType.SERVER_CREATE, ImmutableMap.of("serverId", id), (ignored, error) -> {
|
AuditLog.log(UUID.fromString(requestBody.getString("addedBy")), requestBody.getString("addedByIp"), ctx, AuditLogActionType.SERVER_CREATE, ImmutableMap.of("serverId", id), (ignored, error) -> {
|
||||||
|
@ -12,7 +12,6 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import net.frozenorb.apiv3.APIv3;
|
import net.frozenorb.apiv3.APIv3;
|
||||||
import net.frozenorb.apiv3.actor.Actor;
|
import net.frozenorb.apiv3.actor.Actor;
|
||||||
import net.frozenorb.apiv3.actor.ActorType;
|
import net.frozenorb.apiv3.actor.ActorType;
|
||||||
import net.frozenorb.apiv3.actor.actors.ServerActor;
|
|
||||||
import net.frozenorb.apiv3.model.*;
|
import net.frozenorb.apiv3.model.*;
|
||||||
import net.frozenorb.apiv3.unsorted.FutureCompatibilityCallback;
|
import net.frozenorb.apiv3.unsorted.FutureCompatibilityCallback;
|
||||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||||
@ -35,7 +34,7 @@ public final class POSTServersHeartbeat implements Handler<RoutingContext> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Server actorServer = ((ServerActor) actor).getServer();
|
Server actorServer = Server.findById(actor.getName());
|
||||||
ServerGroup actorServerGroup = ServerGroup.findById(actorServer.getServerGroup());
|
ServerGroup actorServerGroup = ServerGroup.findById(actorServer.getServerGroup());
|
||||||
JsonObject requestBody = ctx.getBodyAsJson();
|
JsonObject requestBody = ctx.getBodyAsJson();
|
||||||
Map<UUID, String> playerNames = extractPlayerNames(requestBody.getJsonObject("players"));
|
Map<UUID, String> playerNames = extractPlayerNames(requestBody.getJsonObject("players"));
|
||||||
@ -132,7 +131,7 @@ public final class POSTServersHeartbeat implements Handler<RoutingContext> {
|
|||||||
serverGroup.calculateScopedPermissions(rank)
|
serverGroup.calculateScopedPermissions(rank)
|
||||||
);
|
);
|
||||||
|
|
||||||
permissionsResponse.put(rank.getId(), scopedPermissions);
|
permissionsResponse.put(rank.getId(), PermissionUtils.convertToList(scopedPermissions));
|
||||||
}
|
}
|
||||||
|
|
||||||
callback.complete(permissionsResponse);
|
callback.complete(permissionsResponse);
|
||||||
|
@ -5,6 +5,7 @@ import io.vertx.ext.web.RoutingContext;
|
|||||||
import net.frozenorb.apiv3.APIv3;
|
import net.frozenorb.apiv3.APIv3;
|
||||||
import net.frozenorb.apiv3.model.User;
|
import net.frozenorb.apiv3.model.User;
|
||||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||||
|
import net.frozenorb.apiv3.util.PermissionUtils;
|
||||||
|
|
||||||
public final class GETUsersIdCompoundedPermissions implements Handler<RoutingContext> {
|
public final class GETUsersIdCompoundedPermissions implements Handler<RoutingContext> {
|
||||||
|
|
||||||
@ -19,7 +20,7 @@ public final class GETUsersIdCompoundedPermissions implements Handler<RoutingCon
|
|||||||
if (error2 != null) {
|
if (error2 != null) {
|
||||||
ErrorUtils.respondInternalError(ctx, error2);
|
ErrorUtils.respondInternalError(ctx, error2);
|
||||||
} else {
|
} else {
|
||||||
APIv3.respondJson(ctx, permissions);
|
APIv3.respondJson(ctx, PermissionUtils.convertToList(permissions));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ import io.vertx.ext.web.RoutingContext;
|
|||||||
import net.frozenorb.apiv3.APIv3;
|
import net.frozenorb.apiv3.APIv3;
|
||||||
import net.frozenorb.apiv3.actor.Actor;
|
import net.frozenorb.apiv3.actor.Actor;
|
||||||
import net.frozenorb.apiv3.actor.ActorType;
|
import net.frozenorb.apiv3.actor.ActorType;
|
||||||
import net.frozenorb.apiv3.actor.actors.ServerActor;
|
|
||||||
import net.frozenorb.apiv3.model.Server;
|
import net.frozenorb.apiv3.model.Server;
|
||||||
import net.frozenorb.apiv3.model.User;
|
import net.frozenorb.apiv3.model.User;
|
||||||
import net.frozenorb.apiv3.util.ErrorUtils;
|
import net.frozenorb.apiv3.util.ErrorUtils;
|
||||||
@ -21,7 +20,7 @@ public class POSTUsersIdLeave implements Handler<RoutingContext> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Server actorServer = ((ServerActor) actor).getServer();
|
Server actorServer = Server.findById(actor.getName());
|
||||||
|
|
||||||
User.findById(ctx.request().getParam("id"), ((user, error) -> {
|
User.findById(ctx.request().getParam("id"), ((user, error) -> {
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
|
@ -7,7 +7,6 @@ import io.vertx.ext.web.RoutingContext;
|
|||||||
import net.frozenorb.apiv3.APIv3;
|
import net.frozenorb.apiv3.APIv3;
|
||||||
import net.frozenorb.apiv3.actor.Actor;
|
import net.frozenorb.apiv3.actor.Actor;
|
||||||
import net.frozenorb.apiv3.actor.ActorType;
|
import net.frozenorb.apiv3.actor.ActorType;
|
||||||
import net.frozenorb.apiv3.actor.actors.ServerActor;
|
|
||||||
import net.frozenorb.apiv3.model.IpLogEntry;
|
import net.frozenorb.apiv3.model.IpLogEntry;
|
||||||
import net.frozenorb.apiv3.model.Server;
|
import net.frozenorb.apiv3.model.Server;
|
||||||
import net.frozenorb.apiv3.model.User;
|
import net.frozenorb.apiv3.model.User;
|
||||||
@ -41,7 +40,7 @@ public final class POSTUsersIdLogin implements Handler<RoutingContext> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Server actorServer = ((ServerActor) actor).getServer();
|
Server actorServer = Server.findById(actor.getName());
|
||||||
|
|
||||||
if (!IpUtils.isValidIp(userIp)) {
|
if (!IpUtils.isValidIp(userIp)) {
|
||||||
ErrorUtils.respondInvalidInput(ctx, "IP address \"" + userIp + "\" is not valid.");
|
ErrorUtils.respondInvalidInput(ctx, "IP address \"" + userIp + "\" is not valid.");
|
||||||
|
@ -6,7 +6,6 @@ import lombok.experimental.UtilityClass;
|
|||||||
public class Permissions {
|
public class Permissions {
|
||||||
|
|
||||||
public static final String PROTECTED_PUNISHMENT = "minehq.punishment.protected";
|
public static final String PROTECTED_PUNISHMENT = "minehq.punishment.protected";
|
||||||
public static final String SIGN_API_REQUEST = "apiv3.signRequest";
|
|
||||||
public static final String BYPASS_VPN_CHECK = "minehq.vpn.bypass";
|
public static final String BYPASS_VPN_CHECK = "minehq.vpn.bypass";
|
||||||
|
|
||||||
}
|
}
|
@ -1,6 +1,8 @@
|
|||||||
package net.frozenorb.apiv3.util;
|
package net.frozenorb.apiv3.util;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import io.vertx.core.cli.converters.BooleanConverter;
|
||||||
import lombok.experimental.UtilityClass;
|
import lombok.experimental.UtilityClass;
|
||||||
import net.frozenorb.apiv3.model.Rank;
|
import net.frozenorb.apiv3.model.Rank;
|
||||||
|
|
||||||
@ -31,7 +33,7 @@ public class PermissionUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (Rank rank : mergeQueue) {
|
for (Rank rank : mergeQueue) {
|
||||||
Map<String, Boolean> rankPermissions = convertToMap(raw.get(rank.getId()));
|
Map<String, Boolean> rankPermissions = convertFromList(raw.get(rank.getId()));
|
||||||
|
|
||||||
// If there's no permissions defined for this rank just skip it.
|
// If there's no permissions defined for this rank just skip it.
|
||||||
if (!rankPermissions.isEmpty()) {
|
if (!rankPermissions.isEmpty()) {
|
||||||
@ -47,24 +49,36 @@ public class PermissionUtils {
|
|||||||
return ImmutableMap.of();
|
return ImmutableMap.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Map<String, Boolean> convertToMap(List<String> unconvered) {
|
private static Map<String, Boolean> convertFromList(List<String> permissionsList) {
|
||||||
if (unconvered == null) {
|
if (permissionsList == null) {
|
||||||
return ImmutableMap.of();
|
return ImmutableMap.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Boolean> result = new HashMap<>();
|
Map<String, Boolean> permissionsMap = new HashMap<>();
|
||||||
|
|
||||||
for (String permission : unconvered) {
|
permissionsList.forEach((permission) -> {
|
||||||
boolean negate = permission.startsWith("-");
|
if (permission.startsWith("-")) {
|
||||||
|
permissionsMap.put(permission.substring(1), false);
|
||||||
if (negate) {
|
|
||||||
result.put(permission.substring(1), false);
|
|
||||||
} else {
|
} else {
|
||||||
result.put(permission, true);
|
permissionsMap.put(permission, true);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return permissionsMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
public static List<String> convertToList(Map<String, Boolean> permissionsMap) {
|
||||||
|
if (permissionsMap == null) {
|
||||||
|
return ImmutableList.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> permissionsList = new LinkedList<>();
|
||||||
|
|
||||||
|
permissionsMap.forEach((permission, granted) -> {
|
||||||
|
permissionsList.add((granted ? "" : "-") + permission);
|
||||||
|
});
|
||||||
|
|
||||||
|
return permissionsList;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user