# FileSystemResource.java

    package com.goldencode.p2j.security;

    import com.goldencode.p2j.concurrent.*;
    import com.goldencode.p2j.directory.*;
    import com.goldencode.p2j.util.logging.*;

    import java.nio.file.*;
    import java.util.function.*;
    import java.util.logging.*;

    /**
     * Implements the "FileSystemResource" resource. Access to such resources have rights which can
     * be represented by a small set of flags:
     * <ul>
     *   <li> create - a runtime instance of the resource can be created
     *   <li> delete - an existing runtime instance can be destroyed
     *   <li> read - the state or data associated with an existing runtime instance can be accessed
     *   <li> write - the state or data associated with an existing runtime instance can be modified
     *   <li> execute - the runtime instance can be executed
     *   <li> denied - no access is allowed (overrides any other access flags)
     * </ul>
     * <p>
     * Rights objects for this resource are made of one bitfield with 8 bits.  The first four bits
     * correspond to the create, delete, read and write respectively.  There are 2 reserved bits.
     * The last bit is for explicit denial of access. When printed, they are shown as
     * {@code CDRWE}
     * <p>
     * Resource names will be namespaced using the subclass' resource type name.  The naming approach
     * is {@code /resource_type_name/instance_name}.  Each different instance of a particular
     * resource type will share the same type name but must have a unique, non-empty instance name.
     */
    public class FileSystemResource
    extends BitFlagsResource
    {
       /** Logger */
       private static CentralLogger LOG = CentralLogger.get(FileSystemResource.class);

       /** EXECUTE permissions bit for the bitflags resource. */
       public static final int BIT_EXECUTE_ACCESS = 4;

       /** Resource type name */
       public static final String TYPE_NAME = "file-system";

       /**
        * Returns the plugin resource type name as a string.
        *
        * @return plugin resource type name
        */
       @Override
       public String getTypeName()
       {
          return TYPE_NAME;
       }

       /**
        * Override the method to provide extended bit names to augment the default set of bits.
        * The method must return either {@code null} for no extended bits or an array of length 1, 2, 3.
        * An array of length 1 will carry name of bit 4, length 2 bits 4 and 5, and length 3 bits 4, 5 and 6.
        *
        * @return  See above.
        */
       @Override
       public String[] getExtendedBits()
       {
          return new String[] {"execute"};
       }

       /**
        * Instantiates a plugin's class that implements the <code>Rights</code> interface, using
        * the array of objects representing a set of access rights fields.
        *
        * @param    rights
        *           The objects needed to create an access rights instance.
        *
        * @return   The newly created rights instance.
        */
       public Rights getRightsInstance(Object[] rights)
       {
          return new FileSystemRights(BitSetHelper.fromBitField((BitField)rights[0]));
       }

       /**
        * Checks whether a given string is a syntactically valid resource name
        * for this resource type.
        *
        * @param   resource
        *          string naming a resource
        *
        * @return  <code>true</code> if the name is syntactically correct
        */
       @Override
       public boolean isInstanceNameValid(String resource)
       {
          if (resource == null)
          {
             return false;
          }

          // validate the glob pattern
          try
          {
             FileSystems.getDefault().getPathMatcher("glob:" + resource);
             return true;
          }
          catch (Exception ex)
          {
             LOG.log(Level.WARNING, "Resource instance " + resource + " is invalid.", ex);
             return false;
          }
       }

       /**
        * Returns a bi-predicate allowing to match ACLs based on a supplied resource instance name.
        *
        * @return  A valid bi-predicate.
        */
       @Override
       public BiPredicate<AccessControlList, String> getACLMatcher()
       {
          return (acl, resInst) ->
          {
             // the glob pattern should always be valid, it is validated in isInstanceNameValid
             PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:" + acl.getInstanceName());
             Path input = Paths.get(resInst);
             return matcher.matches(input);
          };
       }

       /**
        * Reports if EXECUTE is allowed for the given resource name.
        *
        * @param    name
        *           Resource instance name.
        *
        * @return   <code>true</code> if access is allowed.
        */
       public boolean canExecute(String name)
       {
          return accessWorker(name, BIT_EXECUTE_ACCESS);
       }
    }