Redo rank calculation to allow ranks that aren't part of the primary inheritance chain. Finishes #33

This commit is contained in:
Colin McDonald 2016-06-26 19:09:11 -04:00
parent 1b20362f4d
commit 983c428a09
3 changed files with 58 additions and 33 deletions

View File

@ -314,8 +314,8 @@ public final class APIv3 extends AbstractVerticle {
http.get("/staff").blockingHandler(new GETStaff(), false); http.get("/staff").blockingHandler(new GETStaff(), false);
http.get("/users/:id").handler(new GETUsersId()); http.get("/users/:id").handler(new GETUsersId());
http.get("/users/:id/compoundedPermissions").handler(new GETUsersIdCompoundedPermissions());
http.get("/users/:id/details").blockingHandler(new GETUsersIdDetails(), false); http.get("/users/:id/details").blockingHandler(new GETUsersIdDetails(), false);
http.get("/users/:id/permissions").handler(new GETUsersIdPermissions());
http.get("/users/:id/requiresTotp").handler(new GETUsersIdRequiresTotp()); http.get("/users/:id/requiresTotp").handler(new GETUsersIdRequiresTotp());
http.get("/users/:id/verifyPassword").blockingHandler(new GETUsersIdVerifyPassword(), false); http.get("/users/:id/verifyPassword").blockingHandler(new GETUsersIdVerifyPassword(), false);
http.post("/users/:id/changePassword").blockingHandler(new POSTUsersIdChangePassword(), false); http.post("/users/:id/changePassword").blockingHandler(new POSTUsersIdChangePassword(), false);
@ -343,7 +343,7 @@ public final class APIv3 extends AbstractVerticle {
ctx.response().setStatusCode(code); ctx.response().setStatusCode(code);
if (!ctx.request().path().contains("dumps")) { if (!ctx.request().path().contains("dumps")) {
//log.info(gson.toJson(response)); log.info(gson.toJson(response));
} }
ctx.response().end(gson.toJson(response)); ctx.response().end(gson.toJson(response));

View File

@ -7,6 +7,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.hash.Hashing; import com.google.common.hash.Hashing;
import com.google.common.primitives.Ints;
import com.mongodb.async.SingleResultCallback; import com.mongodb.async.SingleResultCallback;
import com.mongodb.async.client.MongoCollection; import com.mongodb.async.client.MongoCollection;
import com.mongodb.client.result.UpdateResult; import com.mongodb.client.result.UpdateResult;
@ -34,6 +35,7 @@ import java.time.Instant;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Entity @Entity
@AllArgsConstructor @AllArgsConstructor
@ -250,10 +252,12 @@ public final class User {
} }
Map<String, Object> result = new HashMap<>(); Map<String, Object> result = new HashMap<>();
List<Rank> ranks = getRanksScoped(serverGroup, grants);
result.put("user", this); result.put("user", this);
result.put("access", accessInfo); result.put("access", accessInfo);
result.put("rank", getHighestRankScoped(serverGroup, grants).getId()); result.put("bestRank", ranks.get(0).getId());
result.put("ranks", ranks.stream().map(Rank::getId).collect(Collectors.toList()));
result.put("totpSetup", getTotpSecret() != null); result.put("totpSetup", getTotpSecret() != null);
if (activeMute != null) { if (activeMute != null) {
@ -472,7 +476,7 @@ public final class User {
} }
public void hasPermissionAnywhere(String permission, SingleResultCallback<Boolean> callback) { public void hasPermissionAnywhere(String permission, SingleResultCallback<Boolean> callback) {
getGlobalPermissions((permissions, error) -> { getCompoundedPermissions((permissions, error) -> {
if (error != null) { if (error != null) {
callback.onResult(null, error); callback.onResult(null, error);
} else { } else {
@ -482,58 +486,79 @@ public final class User {
}); });
} }
public void getGlobalPermissions(SingleResultCallback<Map<String, Boolean>> callback) { public void getCompoundedPermissions(SingleResultCallback<Map<String, Boolean>> callback) {
Grant.findByUser(this, (grants, error) -> { Grant.findByUser(this, (grants, error) -> {
if (error != null) { if (error != null) {
callback.onResult(null, error); callback.onResult(null, error);
} else { } else {
Map<String, Boolean> globalPermissions = PermissionUtils.getDefaultPermissions(getHighestRankScoped(null, grants)); Map<String, Boolean> globalPermissions = new HashMap<>();
for (Map.Entry<ServerGroup, Rank> serverGroupEntry : getHighestRanks(grants).entrySet()) {
ServerGroup serverGroup = serverGroupEntry.getKey();
Rank rank = serverGroupEntry.getValue();
for (Rank globalRank : getRanksScoped(null, grants)) {
globalPermissions = PermissionUtils.mergePermissions( globalPermissions = PermissionUtils.mergePermissions(
globalPermissions, globalPermissions,
serverGroup.calculateScopedPermissions(rank) PermissionUtils.getDefaultPermissions(globalRank)
); );
} }
for (ServerGroup serverGroup : ServerGroup.findAll()) {
for (Rank scopedRank : getRanksScoped(serverGroup, grants)) {
globalPermissions = PermissionUtils.mergePermissions(
globalPermissions,
serverGroup.calculateScopedPermissions(scopedRank)
);
}
}
callback.onResult(ImmutableMap.copyOf(globalPermissions), null); callback.onResult(ImmutableMap.copyOf(globalPermissions), null);
} }
}); });
} }
private Map<ServerGroup, Rank> getHighestRanks(List<Grant> grants) { private List<Rank> getRanksScoped(ServerGroup serverGroup, Iterable<Grant> grants) {
Map<ServerGroup, Rank> highestRanks = new HashMap<>(); List<Rank> grantedRanks = new LinkedList<>();
for (ServerGroup serverGroup : ServerGroup.findAll()) {
highestRanks.put(serverGroup, getHighestRankScoped(serverGroup, grants));
}
return highestRanks;
}
private Rank getHighestRankScoped(ServerGroup serverGroup, Iterable<Grant> grants) {
Rank highest = null;
for (Grant grant : grants) { for (Grant grant : grants) {
if (!grant.isActive() || (serverGroup != null && !grant.appliesOn(serverGroup))) { if (!grant.isActive() || (serverGroup != null && !grant.appliesOn(serverGroup))) {
continue; continue;
} }
Rank rank = Rank.findById(grant.getRank()); Rank grantedRank = Rank.findById(grant.getRank());
grantedRanks.add(grantedRank);
}
if (highest == null || rank.getWeight() > highest.getWeight()) { if (grantedRanks.isEmpty()) {
highest = rank; grantedRanks.add(Rank.findById("default"));
return grantedRanks;
}
Iterator<Rank> iterator = grantedRanks.iterator();
// This is to remove redundant ranks. Say they have mod, mod-plus, admin, and youtuber,
// we should remove mod and mod-plus as it'll be made redundant by the higher ranked admin.
while (iterator.hasNext()) {
Rank rank = iterator.next();
// Check all other ranks for inherited collision
for (Rank otherRank : grantedRanks) {
if (otherRank == rank) {
continue;
}
Rank parent = otherRank;
// Iterate up the inheritance tree to detect rank redundancies.
while (parent.getInheritsFromId() != null) {
if (parent == rank) {
iterator.remove();
}
parent = Rank.findById(otherRank.getInheritsFromId());
}
} }
} }
if (highest != null) { grantedRanks.sort((a, b) -> Ints.compare(b.getWeight(), a.getWeight()));
return highest; return grantedRanks;
} else {
return Rank.findById("default");
}
} }
public void insert(SingleResultCallback<Void> callback) { public void insert(SingleResultCallback<Void> callback) {

View File

@ -6,7 +6,7 @@ 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;
public final class GETUsersIdPermissions implements Handler<RoutingContext> { public final class GETUsersIdCompoundedPermissions implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
User.findById(ctx.request().getParam("id"), (user, error) -> { User.findById(ctx.request().getParam("id"), (user, error) -> {
@ -15,7 +15,7 @@ public final class GETUsersIdPermissions implements Handler<RoutingContext> {
} else if (user == null) { } else if (user == null) {
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
} else { } else {
user.getGlobalPermissions((permissions, error2) -> { user.getCompoundedPermissions((permissions, error2) -> {
if (error2 != null) { if (error2 != null) {
ErrorUtils.respondInternalError(ctx, error2); ErrorUtils.respondInternalError(ctx, error2);
} else { } else {