001package org.intellimate.izou.security;
002
003import org.intellimate.izou.addon.AddOnModel;
004import org.intellimate.izou.main.Main;
005import org.intellimate.izou.security.exceptions.IzouPermissionException;
006import org.intellimate.izou.util.IdentifiableSet;
007import org.intellimate.izou.util.IzouModule;
008import ro.fortsoft.pf4j.PluginDescriptor;
009import ro.fortsoft.pf4j.PluginWrapper;
010
011import java.security.Permission;
012import java.util.function.Function;
013import java.util.function.Supplier;
014
015/**
016 * A PermissionModule defines basic permissions in Izou. A permission in Izou is defined as service that is generally
017 * allowed, yet the addOn has to be registered in order to use the service.
018 * <p>
019 *     For example, socket connections are generally allowed for Izou addOns yet in order to use them their usage has
020 *     to be declared in the addon_config.properties file of the addOn.
021 * </p>
022 * <p>
023 *     Thus {@code PermissionModule} implements a registration system for services like socket connection.
024 * </p>
025 */
026public abstract class PermissionModule extends IzouModule {
027    private final IdentifiableSet<AddOnModel> registeredAddOns;
028    private final SecurityManager securityManager;
029
030    /**
031     * Creates a new PermissionModule
032     * @param main an instance of main
033     * @param securityManager an instance of securityManager
034     */
035    PermissionModule(Main main, SecurityManager securityManager) {
036        super(main);
037        registeredAddOns = new IdentifiableSet<>();
038        this.securityManager = securityManager;
039    }
040
041    /**
042     * returns an instance of SecurityManager
043     * @return the SecurityManager
044     */
045    public SecurityManager getSecurityManager() {
046        return securityManager;
047    }
048
049    /**
050     * Throws an exception with the argument of {@code argument}
051     * @param argument what the exception is about (Access denied to (argument goes here))
052     */
053    SecurityException getException(String argument) {
054        return securityManager.getException(argument);
055    }
056
057    /**
058     * returns true if able to check permissions
059     * @param permission the permission to check
060     * @return true if able to, false if not
061     */
062    public abstract boolean canCheckPermission(Permission permission);
063
064    /**
065     * Adds an addOn to the registered addOns list for this PermissionModule
066     *
067     * @param addon the Identifiable to add
068     */
069    public void registerAddOn(AddOnModel addon) {
070        registeredAddOns.add(addon);
071    }
072
073    /**
074     * Checks if an addOn is registered with this PermissionModule
075     *
076     * @param addon the identifiable to check
077     * @return true if the addOn is registered, else false
078     */
079    public boolean isRegistered(AddOnModel addon) {
080        return registeredAddOns.contains(addon);
081    }
082
083    /**
084     * Checks if the given addOn is allowed to access the requested service.
085     *
086     * @param addon the identifiable to check
087     * @param permission the Permission to check
088     * @throws SecurityException thrown if the addOn is not allowed to access its requested service
089     */
090    public abstract void checkPermission(Permission permission, AddOnModel addon) throws SecurityException;
091
092    /**
093     * registers the addon if checkPermission returns true, else throws the exception provided by the exceptionSupplier.
094     * If the Addon was not added through PF4J it gets ignored
095     * @param addOn the addon to check
096     * @param checkPermission returns true if eligible for registering
097     */
098    protected <X extends IzouPermissionException> void registerOrThrow(AddOnModel addOn, Supplier<X> exceptionSupplier,
099                                                                       Function<PluginDescriptor, Boolean> checkPermission) {
100        getMain().getAddOnManager().getPluginWrapper(addOn)
101                .map(PluginWrapper::getDescriptor)
102                .map(checkPermission)
103                .ifPresent(allowedToRun -> {
104                    if (allowedToRun) {
105                        registerAddOn(addOn);
106                    } else {
107                        throw exceptionSupplier.get();
108                    }
109                });
110    }
111}