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("/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/permissions").handler(new GETUsersIdPermissions());
http.get("/users/:id/requiresTotp").handler(new GETUsersIdRequiresTotp());
http.get("/users/:id/verifyPassword").blockingHandler(new GETUsersIdVerifyPassword(), false);
http.post("/users/:id/changePassword").blockingHandler(new POSTUsersIdChangePassword(), false);
@ -343,7 +343,7 @@ public final class APIv3 extends AbstractVerticle {
ctx.response().setStatusCode(code);
if (!ctx.request().path().contains("dumps")) {
//log.info(gson.toJson(response));
log.info(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.ImmutableSet;
import com.google.common.hash.Hashing;
import com.google.common.primitives.Ints;
import com.mongodb.async.SingleResultCallback;
import com.mongodb.async.client.MongoCollection;
import com.mongodb.client.result.UpdateResult;
@ -34,6 +35,7 @@ import java.time.Instant;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Entity
@AllArgsConstructor
@ -250,10 +252,12 @@ public final class User {
}
Map<String, Object> result = new HashMap<>();
List<Rank> ranks = getRanksScoped(serverGroup, grants);
result.put("user", this);
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);
if (activeMute != null) {
@ -472,7 +476,7 @@ public final class User {
}
public void hasPermissionAnywhere(String permission, SingleResultCallback<Boolean> callback) {
getGlobalPermissions((permissions, error) -> {
getCompoundedPermissions((permissions, error) -> {
if (error != null) {
callback.onResult(null, error);
} 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) -> {
if (error != null) {
callback.onResult(null, error);
} else {
Map<String, Boolean> globalPermissions = PermissionUtils.getDefaultPermissions(getHighestRankScoped(null, grants));
for (Map.Entry<ServerGroup, Rank> serverGroupEntry : getHighestRanks(grants).entrySet()) {
ServerGroup serverGroup = serverGroupEntry.getKey();
Rank rank = serverGroupEntry.getValue();
Map<String, Boolean> globalPermissions = new HashMap<>();
for (Rank globalRank : getRanksScoped(null, grants)) {
globalPermissions = PermissionUtils.mergePermissions(
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);
}
});
}
private Map<ServerGroup, Rank> getHighestRanks(List<Grant> grants) {
Map<ServerGroup, Rank> highestRanks = new HashMap<>();
for (ServerGroup serverGroup : ServerGroup.findAll()) {
highestRanks.put(serverGroup, getHighestRankScoped(serverGroup, grants));
}
return highestRanks;
}
private Rank getHighestRankScoped(ServerGroup serverGroup, Iterable<Grant> grants) {
Rank highest = null;
private List<Rank> getRanksScoped(ServerGroup serverGroup, Iterable<Grant> grants) {
List<Rank> grantedRanks = new LinkedList<>();
for (Grant grant : grants) {
if (!grant.isActive() || (serverGroup != null && !grant.appliesOn(serverGroup))) {
continue;
}
Rank rank = Rank.findById(grant.getRank());
Rank grantedRank = Rank.findById(grant.getRank());
grantedRanks.add(grantedRank);
}
if (highest == null || rank.getWeight() > highest.getWeight()) {
highest = rank;
if (grantedRanks.isEmpty()) {
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) {
return highest;
} else {
return Rank.findById("default");
}
grantedRanks.sort((a, b) -> Ints.compare(b.getWeight(), a.getWeight()));
return grantedRanks;
}
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.util.ErrorUtils;
public final class GETUsersIdPermissions implements Handler<RoutingContext> {
public final class GETUsersIdCompoundedPermissions implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) {
User.findById(ctx.request().getParam("id"), (user, error) -> {
@ -15,7 +15,7 @@ public final class GETUsersIdPermissions implements Handler<RoutingContext> {
} else if (user == null) {
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
} else {
user.getGlobalPermissions((permissions, error2) -> {
user.getCompoundedPermissions((permissions, error2) -> {
if (error2 != null) {
ErrorUtils.respondInternalError(ctx, error2);
} else {