/*
* This file is part of LanternServer, licensed under the MIT License (MIT).
*
* Copyright (c) LanternPowered <https://www.lanternpowered.org>
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the Software), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.lanternpowered.server.service.permission;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import org.lanternpowered.server.console.LanternConsoleSource;
import org.lanternpowered.server.network.rcon.RconServer;
import org.lanternpowered.server.network.rcon.RconSource;
import org.lanternpowered.server.service.permission.base.FixedParentMemorySubjectData;
import org.lanternpowered.server.service.permission.base.GlobalMemorySubjectData;
import org.lanternpowered.server.service.permission.base.LanternSubject;
import org.lanternpowered.server.service.permission.base.LanternSubjectCollection;
import org.spongepowered.api.Game;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.plugin.PluginContainer;
import org.spongepowered.api.service.context.ContextCalculator;
import org.spongepowered.api.service.permission.PermissionDescription;
import org.spongepowered.api.service.permission.PermissionDescription.Builder;
import org.spongepowered.api.service.permission.PermissionService;
import org.spongepowered.api.service.permission.Subject;
import org.spongepowered.api.service.permission.SubjectCollection;
import org.spongepowered.api.service.rcon.RconService;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;
import javax.annotation.Nullable;
/**
* Permission service representing the vanilla operator permission structure.
*
* <p>Really doesn't do much else. Don't use this guys.
*/
public class LanternPermissionService implements PermissionService {
private static final String SUBJECTS_DEFAULT = "default";
private static final Function<String, CommandSource> NO_COMMAND_SOURCE = s -> null;
private final Game game;
private final Map<String, PermissionDescription> descriptionMap = new LinkedHashMap<>();
@Nullable private Collection<PermissionDescription> descriptions;
private final ConcurrentMap<String, SubjectCollection> subjects = new ConcurrentHashMap<>();
private final LanternSubjectCollection defaultCollection;
private final LanternSubject defaultData;
public LanternPermissionService(Game game) {
this.game = game;
this.subjects.put(SUBJECTS_DEFAULT, (this.defaultCollection = this.newCollection(SUBJECTS_DEFAULT)));
this.subjects.put(SUBJECTS_USER, new UserCollection(this));
this.subjects.put(SUBJECTS_GROUP, new OpLevelCollection(this));
this.subjects.put(SUBJECTS_COMMAND_BLOCK, new DataFactoryCollection(SUBJECTS_COMMAND_BLOCK, this,
s -> new FixedParentMemorySubjectData(LanternPermissionService.this, this.getGroupForOpLevel(2)), NO_COMMAND_SOURCE));
this.subjects.put(SUBJECTS_SYSTEM, new DataFactoryCollection(SUBJECTS_SYSTEM, this,
s -> new FixedParentMemorySubjectData(LanternPermissionService.this, this.getGroupForOpLevel(4)),
s -> {
if (s.equals(LanternConsoleSource.NAME)) {
return Sponge.getServer().getConsole();
} else if (s.startsWith(RconSource.NAME_PREFIX)) {
String hostName = s.substring(RconSource.NAME_FULL_PREFIX.length(), s.length() - RconSource.NAME_POSTFIX.length());
RconService rconService = Sponge.getServiceManager().provideUnchecked(RconService.class);
if (rconService instanceof RconServer) {
return ((RconServer) rconService).getByHostName(hostName).orElse(null);
}
}
return null;
}));
this.defaultData = this.getDefaultCollection().get(SUBJECTS_DEFAULT);
}
public Subject getGroupForOpLevel(int level) {
return getGroupSubjects().get("op_" + level);
}
@Override
public SubjectCollection getUserSubjects() {
return getSubjects(PermissionService.SUBJECTS_USER);
}
@Override
public SubjectCollection getGroupSubjects() {
return getSubjects(PermissionService.SUBJECTS_GROUP);
}
@Override
public LanternSubject getDefaults() {
return this.defaultData;
}
@Override
public void registerContextCalculator(ContextCalculator calculator) {
}
@Override
public SubjectCollection getSubjects(String identifier) {
SubjectCollection ret = this.subjects.get(identifier);
if (ret == null) {
SubjectCollection existingRet = this.subjects.putIfAbsent(identifier, (ret = newCollection(identifier)));
if (existingRet != null) {
ret = existingRet;
}
}
return ret;
}
private LanternSubjectCollection newCollection(String identifier) {
return new DataFactoryCollection(identifier, this, s -> new GlobalMemorySubjectData(LanternPermissionService.this), NO_COMMAND_SOURCE);
}
@Override
public Map<String, SubjectCollection> getKnownSubjects() {
return ImmutableMap.copyOf(this.subjects);
}
@Override
public Optional<Builder> newDescriptionBuilder(Object instance) {
Optional<PluginContainer> container = this.game.getPluginManager().fromInstance(checkNotNull(instance, "instance"));
if (!container.isPresent()) {
throw new IllegalArgumentException("The provided plugin object does not have an associated plugin container "
+ "(in other words, is 'plugin' actually your plugin object?)");
}
return Optional.<Builder>of(new LanternPermissionDescription.Builder(this, container.get()));
}
public void addDescription(PermissionDescription permissionDescription) {
checkNotNull(permissionDescription, "permissionDescription");
checkNotNull(permissionDescription.getId(), "permissionId");
this.descriptionMap.put(permissionDescription.getId().toLowerCase(), permissionDescription);
this.descriptions = null;
}
@Override
public Optional<PermissionDescription> getDescription(String permissionId) {
return Optional.ofNullable(this.descriptionMap.get(checkNotNull(permissionId, "permissionId").toLowerCase()));
}
@Override
public Collection<PermissionDescription> getDescriptions() {
Collection<PermissionDescription> descriptions = this.descriptions;
if (descriptions == null) {
descriptions = ImmutableList.copyOf(this.descriptionMap.values());
this.descriptions = descriptions;
}
return descriptions;
}
public LanternSubjectCollection getDefaultCollection() {
return this.defaultCollection;
}
}