/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/tamino/src/store/org/apache/slide/store/tamino/security/admin/URMGroupsGate.java,v 1.3 2004/07/30 06:52:01 ozeigermann Exp $
 * $Revision: 1.3 $
 * $Date: 2004/07/30 06:52:01 $
 *
 * ====================================================================
 *
 * Copyright 1999-2004 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.slide.store.tamino.security.admin;

import java.util.*;

import org.apache.slide.urm.URMException;
import org.apache.slide.urm.URMForbiddenException;
import org.apache.slide.urm.authenticator.URMSubject;
import org.apache.slide.urm.authenticator.userdb.URMGroup;
import org.apache.slide.urm.common.URMConstants;
import org.apache.slide.urm.common.URMDeleteException;
import org.apache.slide.urm.common.URMSetRoleException;
import org.apache.slide.urm.common.URMUpdateException;
import org.apache.slide.store.tamino.common.XForbiddenException;
import org.apache.slide.store.tamino.common.XGlobals;
import org.apache.slide.common.ServiceAccessException;
import org.apache.slide.content.NodeProperty;
import org.apache.slide.content.NodeRevisionDescriptor;
import org.apache.slide.macro.ConflictException;
import org.apache.slide.macro.ForbiddenException;
import org.apache.slide.webdav.util.AclConstants;

/**
 ** URMGroupsGate serves the URMGroupsStore using the URM API. It represents a group
 **
 ** @author    josef.haiduk@softwareag.com
 ** @version   $Revision: 1.3 $
 **
 **/

public class URMGroupsGate extends URMGate
{

    private String lastToken;
    String group = null;
    URMGroup urmGroup = null;
    URMGroup urmAdminGroup = null;
    URMGroup urmParentAdminGroup = null;
    private boolean groupExists= false;
    private boolean isHome = false;
    private boolean isParentHome = false;
    
    
    /**
     * Creates a new gate to the URM API for serving group requests
     *
     * @param    parent              an URMUserDBStore
     * @param    uriStr              a  String
     *
     * @throws   ServiceAccessException
     *
     */
    public URMGroupsGate(URMUserDBStore parent, String uriStr)
        throws ServiceAccessException
    {
        super(parent, uriStr);
        init();
    }
    
    /**
     * Method init
     *
     * @throws   ServiceAccessException
     *
     */
    private void init()
        throws ServiceAccessException
    {
		String path = URMUserDBStore.getGroupsPath();
        if ( uriStr.equals(path)) {
            isHome = true;
            return;
        }
        
        if ( uri.getParent().toString().equals(path) )
            isParentHome = true;
		
		String lastToken = uriStr.substring(path.length()+1);
		
		if ( lastToken.indexOf("/") != -1 )
			throw new ServiceAccessException(parent,
					new ForbiddenException(uriStr,
							new XForbiddenException( "Operations below a group are forbidden!!!" )));
		
        try {
            group = new String(lastToken);
            if ( urmAdm.isGroup(lastToken, domain) ) {
                urmGroup = urmAdm.getGroup(group, domain);
                groupExists = true;
            }
        }
        catch (URMForbiddenException e) {
            throw new ServiceAccessException(parent,
                    new ForbiddenException(uri.toString(), e));
        }

        catch (URMException e) {
            throw new ServiceAccessException(parent, e);
        }
    }
    
    /**
     * isHome returns true if the uri passed to the constructor equals ../administartion/security/userdb/groups
     *
     * @return   a boolean
     *
     */
    public boolean isHome()
    {
        return isHome;
    }
    
    /**
     * isParentHome returns true if the parent uri of the uri passed to the constructor is
     * ../administartion/security/userdb/groups
     *
     * @return   a boolean
     *
     */
    public boolean isParentHome()
    {
        return isParentHome;
    }
    
    /**
     * objectExists returns true if the group referenced by uri passed to the constructor exists.
     *
     * @return   a boolean
     *
     */
    public boolean objectExists()
    {
        return groupExists;
    }
    
    /**
     * getId returns the the name of the group
     *
     * @return   a String
     *
     */
    public String getId()
    {
        return group;
    }

    /**
     * getObject returns the URMGroup referenced by the uri passed to the constructor
     *
     * @return   an URMAction
     *
     */
    public URMGroup getObject()
    {
        return urmGroup;
    }
    
    /**
     * createObject creates the group referenced by the uri passed to the constructor
     *
     * @throws   ServiceAccessException
     *
     */
    public void createObject()
        throws ServiceAccessException
    {
        try {
            getUrmAdministratorWithAdminRole().createGroup(group, domain);
            urmGroup = urmAdm.getGroup(group, domain);
            groupExists = true;
        }

        catch (URMForbiddenException e) {
            throw new ServiceAccessException(parent,
                    new ForbiddenException(uri.toString(), e));
        }
        catch (URMSetRoleException e) {
            throw new ServiceAccessException(parent,
                    new ForbiddenException(uri.toString(), e));
        }
        catch (URMException e) {
            throw new ServiceAccessException(parent, e);
        }
    }
    
    /**
     * deleteObject deletes the group referenced by the uri passed to the constructor
     *
     * @throws   ServiceAccessException
     *
     */
    public void deleteObject()
        throws ServiceAccessException
    {
        try {
            getUrmAdministratorWithAdminRole().deleteGroup(group, domain);
            groupExists = false;
        }

        catch (URMForbiddenException e) {
            throw new ServiceAccessException(parent,
                    new ForbiddenException(uri.toString(), e));
        }
        catch (URMSetRoleException e) {
            throw new ServiceAccessException(parent,
                    new ForbiddenException(uri.toString(), e));
        }
        catch (URMException e) {
            throw new ServiceAccessException(parent, e);
        }
    }
    
    
    /**
     * getProperty returns the property value
     *
     * @param    key                 a  String
     *
     * @return   a String
     *
     * @throws   ServiceAccessException
     *
     */
    public String getProperty(String key)
        throws ServiceAccessException
    {
        return getProperty(urmGroup, key);
    }
    
    /**
     * insertMappedProperties puts the mapped properties into the NodeRevisionDescriptor
     *
     * @param    nrd                 a  NodeRevisionDescriptor
     *
     * @throws   ServiceAccessException
     *
     */
    public void insertMappedProperties(NodeRevisionDescriptor nrd)
        throws ServiceAccessException
    {
        insertMappedProperties(nrd, urmGroup);
    }
    
    
    /**
     * getProperties returns the properties of the group
     *
     * @return   a Properties
     *
     * @throws   ServiceAccessException
     *
     */
    public Properties getProperties()
        throws ServiceAccessException
    {
        try {
            return urmGroup.getProperties();
        }
        catch (URMForbiddenException e) {
            throw new ServiceAccessException(parent,
                    new ForbiddenException(uri.toString(), e));
        }
        catch (URMException e) {
            throw new ServiceAccessException(parent, e);
        }
        
    }
    
    /**
     * getPropertiesAsBytes returns the group properties as a byte stream
     *
     * @return   a byte[]
     *
     * @throws   ServiceAccessException
     *
     */
    public byte[] getPropertiesAsBytes()
        throws ServiceAccessException
    {
        return getProperties(urmGroup);
    }
    
    /**
     * getAvailableProperties returns a map containing the property names available for a group
     * and a boolean value which is true if the property can be modified
     *
     * @return   a Map
     *
     * @throws   ServiceAccessException
     *
     */
    public Map getAvailableProperties()
        throws ServiceAccessException
    {
        return getAvailableProperties(urmGroup);
    }
    
    /**
     * getModifiableProperties returns a set of the modifiable group properties
     *
     * @return   a Set
     *
     * @throws   ServiceAccessException
     *
     */
    public Set getModifiableProperties()
        throws ServiceAccessException
    {
        return getModifiableProperties(getAvailableProperties());
    }
 
	
    /**
     * updateMembers updates the members set of this group. It looks in the NodeRevisionDescriptor for
     * parameter referenced by AclConstants.P_GROUP_MEMBER_SET. existing members which are not
     * in this parameter are deleted.
     * @param    nrd                 a  NodeRevisionDescriptor
     *
     * @throws   ServiceAccessException
     *
     */
    public void updateMembers(NodeRevisionDescriptor nrd)
        throws ServiceAccessException
    {
        try {
            HashSet newMembrs = getNewMembers(nrd, AclConstants.P_GROUP_MEMBER_SET,null);

            // build a delta using the existing and required member set
            // process groups
            HashSet groupsSet = (HashSet)getGroups();
            megreMembers(newMembrs, groupsSet, URMUserDBStore.getGroupsPath());
            
            // Process users
            HashSet usersSet = (HashSet)getUsers();
            megreMembers(newMembrs, usersSet, URMUserDBStore.getUsersPath());
    
            // add new groups and users
            Iterator i = newMembrs.iterator();
            HashSet newGroups = new HashSet();
            HashSet newUsers = new HashSet();
            
            // check if they exist
            while ( i.hasNext() ) {
                String subjectUri = (String)i.next();
                if ( subjectUri.startsWith(URMUserDBStore.getGroupsPath())) {
                    URMGroupsGate gg = new URMGroupsGate(parent, subjectUri);
                    if ( gg.objectExists() )
                        newGroups.add(gg.getId());
                    else
                        throw new ConflictException(subjectUri, new XForbiddenException( "A group must exist before it can be added to a group" ));
                    continue;
                }
                
                if ( subjectUri.startsWith(URMUserDBStore.getUsersPath())) {
                    URMUsersGate ug = new URMUsersGate(parent, subjectUri);
                    if ( ug.objectExists() )
                        newUsers.add(ug.getId());
                    else
                        throw new ConflictException(subjectUri, new XForbiddenException( "A user must exist before it can be added to a group" ));
                }
                else
					throw new ForbiddenException(subjectUri, new XForbiddenException( "The URL must represent a user or a group" ));
            }
            
            // add groups and users
            i = newGroups.iterator();
            
            URMGroup admin = getUrmAdministratorWithAdminRole().getGroup(group, domain);
            while ( i.hasNext() ) {
                admin.addGroupToGroup((String)i.next(), domain);
            }
            i = newUsers.iterator();
            while ( i.hasNext() ) {
                admin.addUserToGroup((String)i.next(), domain);
            }
            // remove groups and users
            i = groupsSet.iterator();
            while ( i.hasNext() ) {
                URMSubject subject = (URMSubject)i.next();
                admin.removeGroupFromGroup(subject.getName(), domain);
            }
            i = usersSet.iterator();
            while ( i.hasNext() ) {
                URMSubject subject = (URMSubject)i.next();
                admin.removeUserFromGroup(subject.getName(), domain);
            }
        
        }
        catch (URMForbiddenException e) {
            throw new ServiceAccessException(parent,
                    new ForbiddenException(uri.toString(), e));
        }
        catch (URMDeleteException e) {
            throw new ServiceAccessException(parent,
                    new ConflictException(uri.toString(), e));
        }
        catch (URMSetRoleException e) {
            throw new ServiceAccessException(parent,
                    new ForbiddenException(uri.toString(), e));
        }
        catch (Exception e) {
            throw new ServiceAccessException(parent, e);
        }
        
    }
    
	/**
     * updateParents the members of this role; users, groups and roles
     *
     * @param    nrd                 a  NodeRevisionDescriptor
     *
     * @throws   ServiceAccessException
     *
     */
    public void updateParents(NodeRevisionDescriptor nrd)
        throws ServiceAccessException {
		updateParents(urmGroup, nrd);
	}
	
    /**
     * setProperties looks for the modifiable properties in the NodeRevisionDescriptor and sets them
     *
     * @param    nrd                 a  NodeRevisionDescriptor
     *
     * @throws   ServiceAccessException
     *
     */
    public void setProperties(NodeRevisionDescriptor nrd)
        throws ServiceAccessException
    {
        // group properties
        Properties pr = getPropertiesToModify(nrd);
        Set v = getModifiableProperties();
        Iterator i = v.iterator();
        while (i.hasNext()) {
            String key = (String)i.next();
            NodeProperty p = nrd.getProperty(key, XGlobals.TAMINO_NAMESPACE_URI);
            if ( p != null ) {
                String value = (String)p.getValue();
                if ( value.compareTo("") != 0 )
                    pr.put(key, value);
            }
        }
        updateProperties(urmGroup,pr);
    }
    
    /**
     * getRoles returns a set of roles associated to the group
     *
     * @return   a Set
     *
     * @throws   ServiceAccessException
     *
     */
    public Set getRoles()
        throws ServiceAccessException
    {
        try {
            return urmGroup.getRoleMapping();
        }
        catch (URMForbiddenException e) {
            throw new ServiceAccessException(parent,
                    new ForbiddenException(uri.toString(), e));
        }
        catch (URMException e) {
            throw new ServiceAccessException(parent, e);
        }
        
    }
    
    /**
     * getParents returns a set of groups to which this group belongs
     *
     * @return   a Set
     *
     * @throws   ServiceAccessException
     *
     */
    public Set getParents()
        throws ServiceAccessException
    {
        if ( !groupExists)
            return null;
        try {
            URMGroup u = urmAdm.getGroup(group, domain);
            return u.getGroupsMemberOf();
        }
        catch (URMForbiddenException e) {
            throw new ServiceAccessException(parent,
                    new ForbiddenException(uri.toString(), e));
        }
        catch (URMException e) {
            throw new ServiceAccessException(parent, e);
        }
    }
    
    
    /**
     * getMembers returns a set containing members of this group
     *
     * @return   a Set
     *
     * @throws   ServiceAccessException
     *
     */
    public Set getMembers()
        throws ServiceAccessException
    {
        try {
            return urmGroup.getGroupMembers();
        }
        catch (URMForbiddenException e) {
            throw new ServiceAccessException(parent,
                    new ForbiddenException(uri.toString(), e));
        }
        catch (URMException e) {
            throw new ServiceAccessException(parent, e);
        }
    }
    /**
     * getGroups returns a set of groups of this group
     *
     * @return   a Set
     *
     * @throws   ServiceAccessException
     *
     */
    public Set getGroups()
        throws ServiceAccessException
    {
        return getSubjects(URMConstants.GROUP);
    }
    
    /**
     * getUsers returns a set of users of this group
     *
     * @return   a Set
     *
     * @throws   ServiceAccessException
     *
     */
    public Set getUsers()
        throws ServiceAccessException
    {
        return getSubjects(URMConstants.USER);
    }
    
    /**
     * getSubjects returns a set of users or groups of this group. The constnt specifies the
     * content of the set.
     *
     * @param    constant            an int
     *
     * @return   a Set
     *
     * @throws   ServiceAccessException
     *
     */
    private Set getSubjects(int constant)
        throws ServiceAccessException
    {
        try {
            Set set = urmGroup.getGroupMembers();
            HashSet groupSet = new HashSet();
            Iterator i = set.iterator();
            while ( i.hasNext() ) {
                URMSubject subject = (URMSubject)i.next();
                if ( subject.getType() == constant )
                    groupSet.add(subject);
            }
            return groupSet;
        }
        catch (URMForbiddenException e) {
            throw new ServiceAccessException(parent,
                    new ForbiddenException(uri.toString(), e));
        }
        catch (URMException e) {
            throw new ServiceAccessException(parent, e);
        }
    }

    /**
     * getAllGroups returns a set of all existing groups
     *
     * @return   a Set
     *
     * @throws   ServiceAccessException
     *
     */
    private Set getAllGroups()
        throws ServiceAccessException
    {
        try {
            return urmAdm.getAllGroups(domain);
        }
        catch (URMForbiddenException e) {
            throw new ServiceAccessException(parent,
                    new ForbiddenException(uri.toString(), e));
        }
        catch (URMException e) {
            throw new ServiceAccessException(parent, e);
        }
    }
    
    /**
     * enumerateObjects puts al existing groups in to the vector
     *
     * @param    v                   a  Vector
     *
     * @throws   ServiceAccessException
     *
     */
    public void enumerateObjects(Vector v)
        throws ServiceAccessException
    {
        enumerateSubjects(getAllGroups(), v);
    }
}









