// $Id: AclDb.java,v 1.4 2006-12-15 10:58:14 tigran Exp $
package dmg.cells.services.login.user ;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
import java.util.Vector;
public class AclDb {
private static class AclItem implements AcDictionary {
private String _name;
private Hashtable<String, Boolean> _users = new Hashtable<>() ;
private String _inherits;
private AclItem( String name ){ _name = name ; }
private void setInheritance( String aclItem ){
_inherits = aclItem ;
}
private void addAccess( String user , boolean access ){
_users.put( user , access) ;
}
private Enumeration<String> getUsers(){ return _users.keys() ; }
private void removeUser(String user ){
_users.remove( user ) ;
}
private Boolean getUserAccess(String user)
throws NoSuchElementException{
return _users.get(user);
}
private void merge( String user , Boolean access ){
_users.putIfAbsent(user, access);
}
//
// the AcDictionary interface
//
@Override
public Enumeration<String> getPrincipals(){
return _users.keys() ;
}
@Override
public boolean getPermission( String principal ){
Boolean ok = _users.get(principal);
if( ok == null ) {
throw new NoSuchElementException(principal);
}
return ok;
}
@Override
public boolean isResolved(){ return _inherits == null ; }
@Override
public String getInheritance(){ return _inherits ; }
public AclItem cloneMe(){
AclItem item = new AclItem(_name);
item._users = (Hashtable<String, Boolean>)_users.clone() ;
item._inherits = _inherits ;
return item ;
}
}
private File _aclDir;
private AgingHash _hash = new AgingHash(20) ;
public AclDb( File aclDir ){
if( ! aclDir.isDirectory() ) {
throw new
IllegalArgumentException("Not a acl DB(not a dir)" + "aclDir \"" + aclDir
.getPath() + ' ' + aclDir.getName() + '"');
}
_aclDir = aclDir ;
}
private void putAcl( String aclName , AclItem item )
throws DatabaseException{
_storeAcl( aclName , item ) ;
_hash.put( aclName , item ) ;
}
private AclItem getAcl( String aclName )
throws NoSuchElementException{
// System.out.println( "getAcl : "+aclName) ;
AclItem item = (AclItem)_hash.get( aclName ) ;
if( item != null ) {
return item.cloneMe();
}
return _loadAcl( aclName ) ;
}
private void _storeAcl( String aclName , AclItem item )
throws DatabaseException {
File file = new File(_aclDir , '.' + aclName ) ;
PrintWriter pw;
try{
pw = new PrintWriter(
new FileWriter( file ) ) ;
}catch(IOException ioe ){
throw new
DatabaseException( "Can't create : "+aclName ) ;
}
String inherit = item.getInheritance() ;
if( inherit != null ) {
pw.println("$=" + inherit);
}
Enumeration<String> e = item.getUsers() ;
while( e.hasMoreElements() ){
String user = e.nextElement();
Boolean access = item.getUserAccess(user) ;
pw.println(user + '=' + (access ? "allowed" : "denied")) ;
}
pw.close() ;
file.renameTo( new File( _aclDir , aclName ) ) ;
}
private AclItem _loadAcl( String aclName )
throws NoSuchElementException
{
File file = new File(_aclDir, aclName);
if (!file.exists()) {
throw new NoSuchElementException("Acl not found : " + aclName);
}
AclItem item = new AclItem(aclName);
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
String line;
while ((line = br.readLine()) != null) {
StringTokenizer st = new StringTokenizer(line, "=");
String user = st.nextToken();
String access = st.nextToken();
if (user.equals("$")) {
item.setInheritance(access);
} else if (access.equals("allowed")) {
item.addAccess(user, true);
} else if (access.equals("denied")) {
item.addAccess(user, false);
}
}
} catch (FileNotFoundException e) {
throw new NoSuchElementException("Not found " + file);
} catch (NoSuchElementException e) {
throw new NoSuchElementException("Syntax error in " + file);
} catch (Exception e) {
throw new NoSuchElementException("IOError on " + file);
}
return item;
}
public synchronized void createAclItem( String aclItem )
throws DatabaseException {
putAcl(aclItem , new AclItem(aclItem)) ;
}
public synchronized void removeAclItem( String aclItem )
{
_hash.remove( aclItem ) ;
new File( _aclDir , aclItem ).delete() ;
}
public synchronized void setInheritance( String aclItem , String inheritsFrom)
throws DatabaseException {
AclItem item = getAcl( aclItem ) ;
item.setInheritance( inheritsFrom ) ;
putAcl( aclItem , item ) ;
}
public synchronized void addAllowed( String aclItem , String user )
throws DatabaseException {
AclItem item = getAcl( aclItem ) ;
item.addAccess( user , true ) ;
putAcl( aclItem , item ) ;
}
public synchronized void addDenied( String aclItem , String user )
throws DatabaseException {
AclItem item = getAcl( aclItem ) ;
item.addAccess( user , false ) ;
putAcl( aclItem , item ) ;
}
public synchronized void removeUser( String aclItem , String user )
throws DatabaseException {
AclItem item = getAcl( aclItem ) ;
item.removeUser( user ) ;
putAcl( aclItem , item ) ;
}
public AcDictionary getPermissions( String aclName , boolean resolve )
throws NoSuchElementException{
// return resolve ? _resolveAclItem( aclName ) : getAcl( aclName ) ;
return resolve ? _fullResolveAclItem( aclName ) : getAcl( aclName ) ;
}
private AclItem _fullResolveAclItem( String aclItem )
throws NoSuchElementException{
if( aclItem.startsWith("." ) || aclItem.endsWith("." ) ) {
throw new
NoSuchElementException("Illegal formated acl : " + aclItem);
}
int f = aclItem.indexOf('.') ;
int l = aclItem.lastIndexOf('.');
String [] array;
if( f < 0 ){
array = new String[2] ;
array[0] = aclItem ;
array[1] = "*.*.*" ;
}else if( f == l ){
String a = aclItem.substring(0,f) ;
String e = aclItem.substring(l+1) ;
array = new String[4] ;
array[0] = aclItem ;
array[1] = a + '.' + e + ".*" ;
array[2] = a+".*.*" ;
array[3] = "*.*.*" ;
}else if( ( f + 1 ) == l ){
throw new
NoSuchElementException("Illegal formated acl : "+aclItem ) ;
}else{
String className = aclItem.substring(0,f) ;
String instanceName = aclItem.substring(f+1,l) ;
String actionName = aclItem.substring(l+1) ;
//
// split the object-instance-name into all possiblities.
// What we call 'possibilities' is : filling the
// items with stars from left to right.
//
// e.g. : a.b.c -> V(a.b.c)={a.b.c,a.b.*,a.*.*,*.*.*}
// formal :
// V(a[0].a[1]...a[n])[0] = a[0].a[1]...a[n-1].*
//
StringTokenizer st = new StringTokenizer(instanceName,".") ;
String [] mm = new String[st.countTokens()] ;
int firstStar = mm.length ;
for(int i = 0 ; i < mm.length ; i++ ){
mm[i] = st.nextToken() ;
if( ( firstStar == mm.length ) &&
mm[i].equals("*") ) {
firstStar = i;
}
}
// System.out.println( "getAcl : fistStar = "+firstStar);
String [] mask = new String[firstStar+1] ;
int k = 0 ;
for( int i = firstStar ; i >= 0 ; i-- ){
StringBuilder sb = new StringBuilder() ;
for( int j = 0 ; j < mm.length ; j++ ){
if( j >= i ) {
sb.append("*.");
} else {
sb.append(mm[j]).append('.');
}
}
mask[k++] = sb.toString() ;
// System.out.println( "getAcl : mask[k="+(k-1)+"] = "+mask[k-1]);
}
//
// I don't know if this is what we actually want :
// we walk through all possiblities but the order might
// be wrong or at least worth a discussion.
//
// class.V(instance)[0].action
// class.V(instance)[0].*
// class.V(instance)[1].action
// class.V(instance)[1].*
// class.*.*
// *.*.*
//
k = 0 ;
if( className.equals("*") ){
array = new String[1] ;
}else if( actionName.equals("*") && instanceName.equals("*") ){
array = new String[2] ;
array[k++] = className+".*.*" ;
}else if( actionName.equals("*") ){
array = new String[2+mask.length] ;
for (String s : mask) {
array[k++] = className + '.' + s + '*';
}
array[k++] = className+".*.*" ;
}else if( instanceName.equals("*") ){
array = new String[3] ;
array[k++] = className+".*."+actionName ;
array[k++] = className+".*.*" ;
}else{
array = new String[2+2*mask.length] ;
for (String s : mask) {
array[k++] = className + '.' + s + actionName;
array[k++] = className + '.' + s + '*';
}
array[k++] = className+".*.*" ;
}
array[k++] = "*.*.*" ;
}
//
// array contains all the possiblities we have to merge
// before we check them against the relation database.
//
AclItem currentItem = null ;
for (String s : array) {
if (currentItem == null) {
try {
currentItem = _resolveAclItem(s);
} catch (NoSuchElementException ee) {
// no problem;
}
} else {
try {
AclItem nextItem = _resolveAclItem(s);
Enumeration<String> e = nextItem.getUsers();
while (e.hasMoreElements()) {
String user = e.nextElement();
currentItem.merge(user, nextItem.getUserAccess(user));
}
} catch (NoSuchElementException ee) {
// very likely.
}
}
}
if( currentItem == null ) {
throw new
NoSuchElementException("Not found : " + aclItem);
}
return currentItem;
}
private AclItem _resolveAclItem( String aclItem )
throws NoSuchElementException{
AclItem result = getAcl( aclItem ) ;
AclItem cursor = result ;
String inherited;
int i;
for( i = 0 ;
( i < 200 ) &&
( inherited = cursor.getInheritance() ) != null ; i++ ){
cursor = getAcl( inherited ) ;
Enumeration<String> e = cursor.getUsers() ;
while( e.hasMoreElements() ){
String user = e.nextElement();
result.merge( user , cursor.getUserAccess(user) ) ;
}
}
if( i == 200 ) {
throw new
NoSuchElementException("Infinit inheritance loop detected");
}
result.setInheritance(null);
return result ;
}
public synchronized boolean check( String aclItem ,
String user ,
UserRelationable relations ){
try{
AclItem item = _fullResolveAclItem( aclItem ) ;
return _check( item , user , relations ) ;
}catch(Exception e){
return false ;
}
}
private boolean _check( AclItem item ,
String user ,
UserRelationable relations )
throws NoSuchElementException{
Boolean ok = item.getUserAccess(user) ;
if( ok != null ) {
return ok;
}
Vector<String> v = new Vector<>() ;
Boolean x;
v.addElement( user ) ;
for( int i = 0 ; i < v.size() ; i++ ){
user = v.elementAt(i);
if( ( x = item.getUserAccess( user ) ) != null ){
if(x) {
return true;
}
continue ;
}
Enumeration<String> e = relations.getParentsOf(user) ;
while( e.hasMoreElements() ) {
v.addElement(e.nextElement());
}
}
return false ;
}
}