/***************************************************************** JADE - Java Agent DEvelopment Framework is a framework to develop multi-agent systems in compliance with the FIPA specifications. Copyright (C) 2000 CSELT S.p.A. GNU Lesser General Public License This library 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, version 2.1 of the License. This library 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 Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *****************************************************************/ package jade.util; //#J2ME_EXCLUDE_FILE import jade.util.Logger; import java.io.*; import java.util.regex.*; /** * * An ACL object represents an Access Control List and * wraps a White list and a Black list, * Both lists make use of regular expressions for * allowing/denying access to a certain resource. * //#APIDOC_EXCLUDE_BEGIN * * Example, a ACL object is initialized as follows: * <code> * ACL myacl = new ACL(); * acl.setBlack( new File("black.txt") ); * acl.setWhite( new File("white.txt") ); * Properties client = new Property(); * client.setProperty(); * boolean ok = acl.isAllowed(); * </code> * * and the two files look as follows: * <strong>white.txt</strong> * <code> * # this is a commant * # the section 'user' (allowed usernames) * user: * # all users are allowed * .+? * * # the section 'msisdn' (allowed phone numbers) * msisdn: * # all business customers * 39335.+? * </code> * <strong>black.txt</strong> * <code> * user: * # specific users denied * badboy * goodgirl * * msisdn: * # specific numbers denied * 3933555.+? * 39333444111 * </code> * * //#APIDOC_EXCLUDE_END * * Into each file, multiple sections are allowed. * * A specific client is allowed if: * <ul><li>it is not present into the black list, AND </li> * <li>it is present into the white list. </li> * </ul> * * More about regular expressions: <br> * http://java.sun.com/j2se/1.4.2/docs/api/java/util/regex/Pattern.html * http://java.sun.com/docs/books/tutorial/extra/regex/ * * * * @author Giosue Vitaglione - Telecom Italia LAB * */ public class AccessControlList { private static Logger logger = Logger.getMyLogger(AccessControlList.class.getName()); // constructor public AccessControlList(){ } // end constructor // Set black and white list private String blackFileName=null; private String whiteFileName=null; public void setBlack( String blackFileName ){ this.blackFileName=blackFileName; refresh_black(); } public void setWhite( String whiteFileName ){ this.whiteFileName=whiteFileName; refresh_white(); } // internal representation (optimized for speed) private InternalACL black_iacl; private InternalACL white_iacl; /** * A specific client is allowed if: * <ul><li>it is not present into the black list, AND </li> * <li>it is present into the white list. </li> * </ul> * * <code>isAllow("section", value)</code> returns is the client * having that specific value for that property is allowed. * */ public boolean isAllowed(String section, String value){ boolean retVal=false; if(logger.isLoggable(Logger.FINER)) { logger.log(Logger.FINER, "Current dir: "+System.getProperty("user.dir")+"\n" + "\nChecking files:\n black="+blackFileName+ "\n white="+whiteFileName+"\n" );} boolean isInBlack=false; boolean isInWhite=false; if (black_iacl!=null) // if loaded, check isInBlack = isInList( black_iacl, section, value); if (white_iacl!=null) isInWhite = isInList( white_iacl, section, value); if(logger.isLoggable(Logger.FINE)) { logger.log(Logger.FINE, " isInBlack="+isInBlack+ " isInWhite="+isInWhite );} retVal = (!isInBlack) && (isInWhite); return retVal; } private boolean isInList( InternalACL iacl, String section, String value) { boolean retVal=false; // read the ACL file try { String currSection=""; int pos = 0; while ( pos<iacl.size ) { pos++; //String p=null; if (iacl.pat[pos]!=null) p=iacl.pat[pos].pattern(); //System.out.println( "+++ pos="+pos+" pattern="+p+" iacl.sectionName[pos]="+iacl.sectionName[pos] ); // if encountered a section header // change the current section if ( iacl.sectionName[pos]!=null) { currSection = iacl.sectionName[pos]; logger.log(Logger.FINER, "Encountered section named: '"+currSection +"'"); continue; } // is currSection the same as passed 'section' // that we are searching for into? // If not, skip this line, looking for a new section if (! currSection.equals( section ) ) { continue; } if (iacl.pat[pos]==null) continue; // should not happen in healty acl; // prapare matcher (from the passed value) Matcher m = null; // matcher is created from the pattern m = iacl.pat[pos].matcher( value ); if(logger.isLoggable(Logger.FINER)) { logger.log(Logger.FINER, "("+iacl.fileName+") "+ " pattern="+iacl.pat[pos].pattern()+ " matcher="+value+ "\n"); } // check the matching boolean b = m.matches(); if(logger.isLoggable(Logger.FINER)) logger.log(Logger.FINER, " " + value + "->" + b +"\n" ); if (b) { retVal = true; } } // end while } catch (Throwable e) { logger.log(Logger.WARNING, "Exception while checking "+iacl.fileName, e ); retVal=false; } return retVal; } // end isAllowed /** * Update this AccessControlList object * reading again the white and black files. */ public void refresh() { refresh_black(); refresh_white(); } // end refresh() private void refresh_black(){ try { black_iacl = file2iacl( blackFileName ); } catch (IOException e) { logger.log(Logger.WARNING, "Exception while checking: "+blackFileName, e ); } } private void refresh_white(){ try { white_iacl = file2iacl( whiteFileName ); } catch (IOException e) { logger.log(Logger.WARNING, "Exception while checking: "+whiteFileName, e ); } } private InternalACL file2iacl(String fileName) throws IOException { InternalACL iacl; // count number of lines BufferedReader in = new BufferedReader(new FileReader(fileName)); int n=0; while (( in.readLine()) != null) { n++; } in.close(); // create properly dimensioned arrays iacl = new InternalACL( n+1 ); iacl.fileName = fileName; // just used for debugging logger.log(Logger.FINER, "Opening acc.control list file:"+fileName+" ("+n+" lines)"); in = new BufferedReader(new FileReader(fileName)); String str; int pos=0; while ((str = in.readLine()) != null) { pos++; str=str.trim(); if (str.startsWith("#")) { continue; } // if encountered a section header if (str.endsWith(":")) { iacl.sectionName[ pos ] = str.substring(0, str.length()-1); continue; } // prepare pattern (from the pattern at this line) try { iacl.pat[ pos ] = Pattern.compile(str); } catch (PatternSyntaxException pse) { logger.log(Logger.WARNING, "Error in expression acc.control list file:"+fileName+" (line:"+pos+")"); } } in.close(); return iacl; }// end file2iacl // internal representation of a acl file private class InternalACL { public Pattern[] pat; // compiled pattern at line i-th public String[] sectionName; // name of the section header at line i-th public int size; public String fileName; public InternalACL(int dim){ size=dim; pat=new Pattern[ dim+1 ]; sectionName=new String[ dim+1 ]; } } /* * * The following code is only for class-level testing. * public static void main(String args[]){ createTestACL(); AccessControlList acl = new AccessControlList(); acl.setBlack( blackFilename ); acl.setWhite( whiteFilename ); acl.setLogLevel( Level.FINE ); testAndPrint( acl, "user", "goodboy" , true); testAndPrint( acl, "user", "sfogliatella9814", false); testAndPrint( acl, "section2", "forbiddenvalue" , false); testAndPrint( acl, "section2", "goodvalue" , true); testAndPrint( acl, "sectionX", "anyvalue" , false); } private static void testAndPrint(AccessControlList acl, String section, String value, boolean expected){ boolean ok = acl.isAllowed( section, value ); System.out.println( "section="+section+" value="+value +" ok="+(ok+" ").toUpperCase() +""+( (ok==expected) ? ":-)": "!!! [:-( expected="+expected+" !!!") +"\n\n"); } private static final String blackFilename="black.txt"; private static final String whiteFilename="white.txt"; private static void createTestACL(){ try { PrintWriter out; out = new PrintWriter(new BufferedWriter(new FileWriter(blackFilename))); out.println( "\n" +"user:\n" +" sfogliatella.+? \n" +"section2:\n" +" forbiddenvalue.* \n" +"section3:\n" +" .+? \n" ); out.flush(); out.close(); out = new PrintWriter(new BufferedWriter(new FileWriter(whiteFilename))); out.println( "\n" +"user:\n" +" .+? \n" +"section2:\n" +" good.+? \n" +"section3:\n" +" .+? \n" ); out.flush(); out.close(); } catch (Exception e) { e.printStackTrace();} } private void setLogLevel(Level lev){ logger.setLevel( lev ); logger.getParent().setLevel( lev ); //Set level for handlers associated to logger Handler[] pHandlers = logger.getParent().getHandlers(); Handler[] handlers = logger.getHandlers(); for (int i=0; i<pHandlers.length; i++){ pHandlers[i].setLevel(lev); } for (int j=0; j<handlers.length; j++){ handlers[j].setLevel(lev); } } */ }// end class