/*
* $Id: LDAPTribe.java,v 1.10 2002/09/16 08:05:06 jkl Exp $
*
* Copyright (c) 2002 Njet Communications Ltd. All Rights Reserved.
*
* Use is subject to license terms, as defined in
* Anvil Sofware License, Version 1.1. See LICENSE
* file, or http://njet.org/license-1.1.txt
*/
package anvil.server.ldap;
import java.security.Permission;
import java.security.Permissions;
import java.security.PermissionCollection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Enumeration;
import anvil.java.util.BindingEnumeration;
import anvil.java.util.EnumerationToIterator;
import anvil.java.security.PermissionCollectionCombiner;
import anvil.core.Any;
import anvil.server.Tribe;
import anvil.server.Citizen;
import anvil.server.Tribe;
import anvil.server.Realm;
import anvil.server.Citizen;
import anvil.server.CitizenNotFoundException;
import anvil.server.OperationFailedException;
import anvil.database.PooledConnection;
import anvil.Log;
import javax.naming.directory.DirContext;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.NamingEnumeration;
import javax.naming.NameClassPair;
import javax.naming.NamingException;
import javax.naming.Name;
import javax.naming.NameParser;
import javax.naming.NameNotFoundException;
/**
* class LDAPTribe
*
* @author: Simo Tuokko
*/
public class LDAPTribe implements Tribe
{
private LDAPRealm realm;
private Log log;
private String name;
private String dn;
private String fullDN;
private boolean isRoot;
Tribe[] children = null;
Tribe[] parents = null;
Citizen[] citizens = null;
private PermissionCollection permissions = null;
private PermissionCollection combined = null;
private boolean hasPermissions = false;
LDAPTribe(LDAPRealm realm, String name) throws OperationFailedException
{
this(realm, name, false);
}
LDAPTribe(LDAPRealm realm, String name, boolean isRoot) throws OperationFailedException
{
this.realm = realm;
this.name = name;
this.dn = "cn="+name+",ou=groups";
this.fullDN = realm.createGroupDN(name);
this.isRoot = isRoot;
log = realm.getLog();
if (!isRoot) {
PooledConnection connImpl = null;
DirContext ctx = null;
boolean tribeFound = false;
try {
connImpl = realm.getConnection();
ctx = (DirContext)connImpl.getConnection();
ctx.getAttributes(dn, new String[] { "cn" });
tribeFound = true;
} catch (NameNotFoundException nnfe) {
//not found
} catch (Exception e) {
throw new OperationFailedException("Creating of tribe failed", e);
} finally {
LDAPRealm.cleanupContext(connImpl);
}
if (!tribeFound) {
throw new CitizenNotFoundException(name);
}
}
}
public Realm getRealm()
{
return realm;
}
public String getName()
{
return name;
}
public Tribe[] getParents()
{
if (parents == null) {
synchronized(this) {
if (parents == null) {
parents = realm.getMemberGroups(fullDN);
}
}
}
return parents;
}
public boolean hasChilds()
{
if (children == null) {
getChilds();
}
return (children.length > 0);
}
public Tribe[] getChilds()
{
if (children == null) {
synchronized(this) {
if (children == null) {
children = (Tribe[])fetchChildren(true);
}
}
}
return children;
}
public Citizen[] getCitizens()
{
if (citizens == null) {
synchronized(this) {
if (citizens == null) {
citizens = (Citizen[])fetchChildren(false);
}
}
}
return citizens;
}
public PermissionCollection getPermissions()
{
if (permissions == null) {
synchronized(this) {
if (permissions == null) {
permissions = realm.loadPermissions(dn);
}
}
}
return permissions;
}
public PermissionCollection getCombinedPermissions() {
if (!hasPermissions) {
synchronized(this) {
if (!hasPermissions) {
hasPermissions = true;
getParents();
Permissions perms = new Permissions();
for (int i=0; i<parents.length; i++) {
PermissionCollection parentCol = parents[i].getCombinedPermissions();
if (parentCol != null) {
for (Enumeration enu = parentCol.elements(); enu.hasMoreElements(); ) {
perms.add((Permission)enu.nextElement());
}
}
}
//combine own permissions
for (Enumeration enu = getPermissions().elements(); enu.hasMoreElements(); ) {
perms.add((Permission)enu.nextElement());
}
combined = perms;
}
}
}
return combined;
}
public void addPermission(Permission perm) throws OperationFailedException {
if (isRoot) {
throw new OperationFailedException("addPermission operation isn't allowed for synthetic root tribe!");
}
permissions = realm.addPermission(perm, dn);
}
public void removePermission(Permission perm) throws OperationFailedException {
permissions = realm.removePermission(perm, dn);
}
public Iterator listPermissions() {
if (permissions != null) {
return new EnumerationToIterator(permissions.elements());
} else {
return BindingEnumeration.EMPTY;
}
}
public void commit() throws OperationFailedException
{
}
private Object fetchChildren(boolean isTribe)
{
PooledConnection connImpl = null;
DirContext ctx = null;
ArrayList result = new ArrayList();
try {
connImpl = realm.getConnection();
ctx = (DirContext)connImpl.getConnection();
if (isRoot) {
NamingEnumeration enu = ctx.list(isTribe? "ou=groups" : "ou=users");
while (enu.hasMore()) {
String name = ((NameClassPair)enu.next()).getName();
name = name.substring(name.indexOf("=")+1);
if (isTribe) {
result.add( realm.getTribe(name) );
} else {
result.add( realm.getCitizen(name) );
}
}
} else {
Attributes a = ctx.getAttributes(dn, new String[] { "uniquemember" });
Attribute memberAttr = a.get("uniquemember");
if (memberAttr != null) {
for (NamingEnumeration de=memberAttr.getAll(); de.hasMore(); ) {
String val = (String)de.next();
String name = val.substring(val.indexOf("=")+1, val.indexOf(",")).trim();
if (isTribe) {
if (val.startsWith("cn")) {
result.add( realm.getTribe(name) );
}
} else {
if (val.startsWith("uid")) {
result.add( realm.getCitizen(name) );
}
}
} //for
}
} //if isRoot
} catch (Exception e) {
log.error("LDAPTribe.fetchChildren() failed: "+e);
} finally {
LDAPRealm.cleanupContext(connImpl);
}
int l = result.size();
if (isTribe) {
return (Tribe[])result.toArray(new Tribe[l]);
} else {
return (Citizen[])result.toArray(new Citizen[l]);
}
}
//Namespace stuff
public Any setVariable(String name, Any value)
{
return null;
}
public boolean deleteVariable(String name)
{
return false;
}
public Any getVariable(String name)
{
return null;
}
public Any checkVariable(String name)
{
return null;
}
public BindingEnumeration getVariables()
{
return null;
}
//Mutable stuff
public void attach(Tribe tribe) throws OperationFailedException
{
LDAPTribe t = (LDAPTribe)tribe;
if (attach(t.getFullDN(), tribe)) {
//refresh
children = null;
t.refreshPermissions();
}
}
public void attach(Citizen citizen) throws OperationFailedException
{
LDAPCitizen c = (LDAPCitizen)citizen;
if (attach(c.getFullDN(), citizen)) {
//refresh
citizens = null;
c.refreshPermissions();
}
}
private synchronized boolean attach(String dn, Object res) throws OperationFailedException
{
if (isRoot) {
throw new OperationFailedException("Attach operation isn't allowed for synthetic root tribe!");
}
PooledConnection connImpl = null;
DirContext ctx = null;
try {
connImpl = realm.getConnection();
ctx = (DirContext)connImpl.getConnection();
//check that this tribe isn't a member of the tribe being attached
if (res instanceof LDAPTribe) {
LDAPTribe parentCand = (LDAPTribe)res;
String parentFail = parentCand.containsName(ctx, parentCand.fetchMembers(ctx), this.dn);
if (parentFail != null) {
throw new OperationFailedException("Loops not allowed, '"+dn+"' is parent of of '"+this.dn+"'");
}
}
Attribute currMembers = fetchMembers(ctx);
String failer = containsName(ctx, currMembers, dn);
if (failer == null) {
currMembers.add(dn);
Attributes attrs = new BasicAttributes();
attrs.put(currMembers);
ctx.modifyAttributes(this.dn, DirContext.REPLACE_ATTRIBUTE, attrs);
return true;
}
} catch (Exception e) {
throw new OperationFailedException(e);
} finally {
LDAPRealm.cleanupContext(connImpl);
}
return false;
}
public void detach(Tribe tribe) throws OperationFailedException
{
LDAPTribe t = (LDAPTribe)tribe;
if (detach(t.getFullDN())) {
//refresh
children = null;
t.refreshPermissions();
}
}
public void detach(Citizen citizen) throws OperationFailedException
{
LDAPCitizen c = (LDAPCitizen)citizen;
if (detach(c.getFullDN())) {
//refresh
citizens = null;
c.refreshPermissions();
}
}
private synchronized boolean detach(String dn) throws OperationFailedException
{
if (isRoot) {
throw new OperationFailedException("Detach operation isn't allowed for synthetic root tribe!");
}
PooledConnection connImpl = null;
DirContext ctx = null;
try {
connImpl = realm.getConnection();
ctx = (DirContext)connImpl.getConnection();
Attribute currMembers = fetchMembers(ctx);
String result = containsName(ctx, currMembers, dn);
if (result != null) {
currMembers.remove(result);
Attributes attrs = new BasicAttributes();
attrs.put(currMembers);
ctx.modifyAttributes(this.dn, DirContext.REPLACE_ATTRIBUTE, attrs);
return true;
}
} catch (Exception e) {
throw new OperationFailedException(e);
} finally {
LDAPRealm.cleanupContext(connImpl);
}
return false;
}
private Attribute fetchMembers(DirContext ctx) throws NamingException
{
return fetchMembers(ctx, dn);
}
private Attribute fetchMembers(DirContext ctx, String fetchDN) throws NamingException
{
Attributes attrs = ctx.getAttributes(fetchDN, new String[] { "uniquemember" });
Attribute members = attrs.get("uniquemember");
return (members == null)? new BasicAttribute("uniquemember") : members;
}
private String containsName(DirContext ctx, Attribute attr, String dn) throws NamingException, OperationFailedException
{
NameParser parser = ctx.getNameParser(realm.getPrefix());
Name name = parser.parse(dn);
for (NamingEnumeration e=attr.getAll(); e.hasMore(); ) {
String cand = (String)e.next();
Name candName = parser.parse(cand);
if ( name.equals(candName) ) {
e.close();
return cand;
}
//toimiiko jos prefixi� ei ole???
if (cand.toLowerCase().startsWith("cn")) {
String res = containsName(ctx, fetchMembers(ctx, candName.getSuffix(candName.size()-2).toString()), dn);
if (res != null) {
throw new OperationFailedException("Loops not allowed, '"+dn+"' is already a member of '"+cand+"'");
}
}
}
return null;
}
public void remove() throws OperationFailedException
{
PooledConnection connImpl = null;
DirContext ctx = null;
try {
connImpl = realm.getConnection();
ctx = (DirContext)connImpl.getConnection();
ctx.unbind(dn);
} catch (NameNotFoundException e) {
throw new OperationFailedException("Tribe '"+name+"' not found!");
} catch (Exception e) {
throw new OperationFailedException(e.getMessage());
} finally {
LDAPRealm.cleanupContext(connImpl);
}
}
String getDN()
{
return dn;
}
String getFullDN()
{
return fullDN;
}
void refreshCitizens() {
citizens = null;
}
void refreshPermissions() {
hasPermissions = false;
parents = null;
if (citizens != null) {
for (int i=0,l=citizens.length; i<l; i++) {
((LDAPCitizen)citizens[i]).refreshPermissions();
}
}
if (children != null) {
for (int i=0,l=children.length; i<l; i++) {
((LDAPTribe)children[i]).refreshPermissions();
}
}
}
public boolean equals(Object o) {
if (o instanceof LDAPTribe) {
LDAPTribe c = (LDAPTribe)o;
return (c.name.equals(name) && c.realm.equals(realm));
}
return false;
}
public String toString()
{
return "LDAPTribe ("+name+")";
}
}