play.modules.securePermissions.Permissions.java Source code

Java tutorial

Introduction

Here is the source code for play.modules.securePermissions.Permissions.java

Source

/*
This file is part of secure-permissions-play-module.
    
Copyright Lunatech Research 2010
    
secure-permissions-play-module is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
    
secure-permissions-play-module is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.
    
You should have received a copy of the GNU General Lesser Public License
along with secure-permissions-play-module.  If not, see <http://www.gnu.org/licenses/>.
*/
package play.modules.securePermissions;

import java.security.Principal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;

import org.apache.commons.codec.digest.DigestUtils;
import org.drools.KnowledgeBase;
import org.drools.KnowledgeBaseFactory;
import org.drools.builder.KnowledgeBuilder;
import org.drools.builder.KnowledgeBuilderConfiguration;
import org.drools.builder.KnowledgeBuilderFactory;
import org.drools.builder.ResourceType;
import org.drools.command.Command;
import org.drools.command.CommandFactory;
import org.drools.event.rule.AfterActivationFiredEvent;
import org.drools.event.rule.DefaultAgendaEventListener;
import org.drools.io.ResourceFactory;
import org.drools.runtime.StatelessKnowledgeSession;

import play.Logger;
import play.Play;
import play.Play.Mode;
import play.vfs.VirtualFile;

/**
 * Permissions checker that uses a JBoss Rules stateless session to perform the check.
 */
public class Permissions {

    private static final String RULES_FILE_NAME = "permissions.drl";
    private static KnowledgeBase knowledgeBase;
    private static String rulesFileName;
    private static Long rulesLastModified;
    private static String rulesMD5;

    private Permissions() {
    }

    static void maybeLoadKnowledgeBase() {
        String newRulesFileName = Play.configuration.getProperty("secure.rules");
        boolean defaultUsed = false;
        if (newRulesFileName == null) {
            newRulesFileName = RULES_FILE_NAME;
            defaultUsed = true;
        }
        VirtualFile rulesFile = Play.getVirtualFile("conf/" + newRulesFileName);

        if (rulesFile == null || !rulesFile.exists()) {
            if (defaultUsed) {
                Logger.warn("No permissions file specified and none found: all permissions checks will be denied.");
                knowledgeBase = null;
                return;
            }
            // throw only if one was specified and is missing
            throw new RuntimeException("Rules file conf/" + newRulesFileName + " does not exist");
        }
        if (Play.mode == Mode.PROD) {
            rulesFileName = newRulesFileName;
            loadKnowledgeBase(rulesFile.content());
        } else {
            // see if the file changed
            Long newLastModified = rulesFile.lastModified();
            byte[] content = rulesFile.content();
            String newMD5 = DigestUtils.md5Hex(content);
            if (rulesFileName == null || !rulesFileName.equals(newRulesFileName) || rulesLastModified == null
                    || !newLastModified.equals(rulesLastModified) || rulesMD5 == null || !newMD5.equals(rulesMD5)) {
                Logger.info(
                        "old file: %s, new file: %s, old modified: %s, new modified: %s, old md5: %s, new md5: %s",
                        rulesFileName, newRulesFileName, rulesLastModified, newLastModified, rulesMD5, newMD5);
                rulesFileName = newRulesFileName;
                rulesLastModified = newLastModified;
                rulesMD5 = newMD5;
                loadKnowledgeBase(content);
            }
        }
    }

    /**
     * Load and compile the rules definitions.
     */
    private static void loadKnowledgeBase(byte[] rulesContent) {
        Logger.info("Loading rules from %s", rulesFileName);

        // Configure the drools compiler to use Janino, instead of JDT, with the Play classloader, so that compilation will load 
        // model classes from the classloader, and not as .class file resources. https://jira.jboss.org/browse/JBRULES-1229 
        Properties properties = new Properties();
        properties.put("drools.dialect.java.compiler", "JANINO");
        // this is needed because it's not set in prod
        Thread.currentThread().setContextClassLoader(Play.classloader);
        final KnowledgeBuilderConfiguration configuration = KnowledgeBuilderFactory
                .newKnowledgeBuilderConfiguration(properties, Play.classloader);
        final KnowledgeBuilder builder = KnowledgeBuilderFactory.newKnowledgeBuilder(configuration);

        // Compile the rules file.
        builder.add(ResourceFactory.newByteArrayResource(rulesContent), ResourceType.DRL);
        if (builder.hasErrors()) {
            Logger.error(builder.getErrors().toString());
            throw new RuntimeException("Drools compilation failed: " + builder.getErrors().size() + " errors");
        }

        knowledgeBase = KnowledgeBaseFactory.newKnowledgeBase();
        knowledgeBase.addKnowledgePackages(builder.getKnowledgePackages());
    }

    /**
     * Check the given permission using a stateless rules session.
     * 
     * @param check The permission to check
     * @param user The name of the user whose roles we will check
     * @param roles The names of the roles that the given user has
     * @return true if the permission is granted
     */
    public static boolean check(final PermissionCheck check, final String user, final Collection<String> roles) {
        Logger.debug("%s, user=%s, roles=%s", check, user, roles);

        if (knowledgeBase == null) {
            Logger.debug("DENIED (no permissions file specified)");
            return false;
        }

        final StatelessKnowledgeSession session = knowledgeBase.newStatelessKnowledgeSession();
        session.addEventListener(new AgendaLogger());

        final List<Command> commands = new ArrayList<Command>();
        commands.add(CommandFactory.newInsert(check));
        commands.add(CommandFactory.newInsert(check.getTarget()));
        commands.add(CommandFactory.newInsert(asPrincipal(user)));
        commands.add(CommandFactory.newInsertElements(asRoles(roles)));
        session.execute(CommandFactory.newBatchExecution(commands));

        Logger.debug((check.isGranted() ? "GRANTED" : "DENIED") + "\n");
        return check.isGranted();
    }

    /**
     * Wraps the given user name in a Principal object.
     */
    private static Principal asPrincipal(final String user) {
        final Principal principal = new Principal() {
            public String getName() {
                return user;
            }
        };
        return principal;
    }

    /**
     * Wraps the given array of role names in a collection of Role objects.
     */
    private static Collection<Role> asRoles(final Collection<String> names) {
        final Collection<Role> roles = new HashSet<Role>();
        for (String name : names) {
            roles.add(new Role(name));
        }
        return roles;
    }

    private static class AgendaLogger extends DefaultAgendaEventListener {
        @Override
        public void afterActivationFired(AfterActivationFiredEvent event) {
            Logger.debug("RULE '%s' %s", event.getActivation().getRule().getName(),
                    event.getActivation().getFactHandles());
        }
    }

}