/* **************************************************************************

  $Archive: /njcl_v2rmi/src/com/novell/service/security/protocols/XplatProtocolProxy.java $
  $Revision: 32 $
  $Modtime: 5/15/03 4:44p $

  Copyright (c) 1997 Novell, Inc.  All Rights Reserved.

  THIS WORK IS AN UNPUBLISHED WORK AND CONTAINS CONFIDENTIAL PROPRIETARY
  AND TRADE SECRET INFORMATION OF NOVELL, INC. ACCESS  TO  THIS  WORK IS
  RESTRICTED TO (I) NOVELL, INC.  EMPLOYEES WHO HAVE A NEED TO  KNOW HOW
  TO  PERFORM  TASKS WITHIN  THE SCOPE  OF  THEIR   ASSIGNMENTS AND (II)
  ENTITIES OTHER  THAN  NOVELL, INC.  WHO  HAVE ENTERED INTO APPROPRIATE
  LICENSE   AGREEMENTS.  NO  PART  OF  THIS WORK MAY BE USED, PRACTICED,
  PERFORMED COPIED, DISTRIBUTED, REVISED, MODIFIED, TRANSLATED, ABRIDGED,
  CONDENSED, EXPANDED, COLLECTED, COMPILED, LINKED,  RECAST, TRANSFORMED
  OR ADAPTED  WITHOUT THE PRIOR WRITTEN CONSENT OF NOVELL, INC.  ANY USE
  OR EXPLOITATION  OF  THIS WORK WITHOUT AUTHORIZATION COULD SUBJECT THE
  PERPETRATOR  TO CRIMINAL AND CIVIL LIABILITY.

****************************************************************************/

package com.novell.service.security.protocols;

import java.rmi.RemoteException;
import java.io.IOException;
import java.lang.StringBuffer;
import java.net.URL;
import java.util.*;

import com.novell.java.lang.*;
import com.novell.java.security.*;
import com.novell.java.security.channels.NativeCodeChannelProxy;
import com.novell.service.session.util.Debug;
import com.novell.service.jncp.NSIExceptionBuilder;
import com.novell.service.jncpv2r.clx.ClxJNI;
//import com.novell.service.ldap.LdapBinding;
import com.novell.service.security.*;
import com.novell.service.security.protocols.nls.I2L;
import com.novell.service.security.protocols.nls.LF;
import com.novell.service.session.*;
import com.novell.service.session.xplat.Shibboleth;
import com.novell.service.session.xplat.ShibbolethImpl;
import com.novell.service.jncpv2r.net.NetJNI;

/** @internal
 * The Xplat authenticator protocol proxy.
 */
public class XplatProtocolProxy implements AuthenticatorProtocol
{
   // Always access debug via final static...hopefully a final static of
   // false will be optimized out by the compiler
   final static private boolean  DEBUG = false;

   /**
    * authentication channel to use in the protocol
    */
   protected AuthenticatorChannel channel;

   private AuthenticatorProtocol guiProxy;
   
   /**
    * construct the authentication protocol proxy
    */
   public XplatProtocolProxy ()
      throws SessionException
   {
   }

   /**
    * configure the protocol
    */
   public void configure (
         Properties properties,
         String baseKey)
      throws ConfigurationException
   {
   }

   /**
    * set the channel for the protocol and identity
    */
   public void init (
         AuthenticatorChannel channel)
   {
      this.channel = channel;
   }

   public AuthenticatorChannel getChannel()
   {
      return channel;
   }

   /**
    * perform the authentication protocol
    */
   public void login (Identity in)
      throws LoginException
   {
      channel.beginProtocol(this);

      try
      {
         if (in instanceof PasswordIdentity)
         {
/*
            if (in instanceof LdapPasswordIdentity)
               performLdapLogin (
                     (LdapIdentity) in,
                     in.getName (),
                     ((PasswordIdentity) in).getPassword ());
            else
*/
               performLogin (
                     in,
                     ((XplatIdentity) in).getUsername (),
                     ((XplatIdentity) in).getUserDomain (),
                     ((PasswordIdentity) in).getPassword (),
                     null);
         }
         else
         {
         	getGUI().login(in);
         }
      }
      catch(AuthenticatorException x)
      {
         throw x;
      }
      catch (java.lang.Throwable x)
      {
         throw new LoginException(x);
      }
      finally
      {
         channel.endProtocol(this);
      }
   }

	/**
	 * @deprecated See AuthenticatorProtocols for details...
	 */
   public void createTokens (
         Identity in)
      throws CreateTokensException
   {
   	  createTokens(in, in);
   }

   public void createTokens (
         Identity admin, Identity user)
      throws CreateTokensException
   {
      channel.beginProtocol (this);

      try
      {
         if (user instanceof PasswordIdentity)
/*
         if ((user instanceof PasswordIdentity) &&
             (!(user instanceof LdapPasswordIdentity))) // Ldap not supported
*/
         {
            performCreateTokens (
                  admin,
                  ((XplatIdentity) user).getUsername (),
                  ((XplatIdentity) user).getUserDomain (),
                  ((PasswordIdentity) user).getPassword (),
                  null);
         }
         else
         {
         	getGUI().createTokens(admin, user);
         }
      }
      catch(AuthenticatorException x)
      {
         throw x;
      }
      catch (java.lang.Throwable x)
      {
         throw new CreateTokensException(x);
      }
      finally
      {
         channel.endProtocol(this);
      }
   }

   public void verifyTokens (
         Identity in)
      throws VerifyTokensException
   {
      channel.beginProtocol (this);

      try
      {
         if (in instanceof PasswordIdentity)
/*
         if ((in instanceof PasswordIdentity) &&
             (!(in instanceof LdapPasswordIdentity))) // Ldap not supported
*/
         {
            performVerifyTokens (
                  in,
                  ((XplatIdentity) in).getUsername (),
                  ((XplatIdentity) in).getUserDomain (),
                  ((PasswordIdentity) in).getPassword (),
                  null);
         }
         else
         {
         	getGUI().verifyTokens(in);
         }
      }
      catch(AuthenticatorException x)
      {
         throw x;
      }
      catch (java.lang.Throwable x)
      {
         throw new VerifyTokensException(x);
      }
      finally
      {
         channel.endProtocol(this);
      }
   }

   public void modifyTokens(Identity in)
      throws ModifyTokensException
   {
      channel.beginProtocol(this);

      try
      {
         if (in instanceof PasswordIdentity)
/*
         if ((in instanceof PasswordIdentity) &&
             (!(in instanceof LdapPasswordIdentity))) // Ldap not supported
*/
         {
            performModifyTokens (
                  in,
                  ((XplatIdentity) in).getUsername (),
                  ((XplatIdentity) in).getUserDomain (),
                  ((PasswordIdentity) in).getPassword (),
                  ((PasswordIdentity) in).getNewPassword (),
                  null);
         }
         else
         {
         	getGUI().modifyTokens(in);
         }
      }
      catch(AuthenticatorException x)
      {
         throw x;
      }
      catch (java.lang.Throwable x)
      {
         throw new ModifyTokensException(x);
      }
      finally
      {
         channel.endProtocol(this);
      }
   }

   public void logout(Identity in)
      throws LogoutException
   {
      channel.beginProtocol(this);

      try
      {
         if (in instanceof PasswordIdentity)
/*
         if ((in instanceof PasswordIdentity) &&
             (!(in instanceof LdapPasswordIdentity))) // Ldap not supported
*/
         {
            performLogout (
                  in,
                  ((XplatIdentity) in).getUsername (),
                  ((XplatIdentity) in).getUserDomain (),
                  null);
         }
         else
         {
				getGUI().logout(in);
         }
      }
      catch(AuthenticatorException x)
      {
         throw x;
      }
      catch (java.lang.Throwable x)
      {
         throw new LogoutException(x);
      }
      finally
      {
         channel.endProtocol(this);
      }
   }

   /*
    * administrative domains are either [NDS], [BINDERY],[undefined]
    *
    * e.g. jsnielsen.java.prv.novell.NOVELL_INC.[NDS]
    * administrative domain   - [NDS]
    * domain                  - NOVELL_INC
    * name                    - jsnielsen.java.prv.novell
    *
    * e.g. jsnielsen.java.prv.novell.NOVELL_INC
    * administrative domain   - [undefined]
    * domain                  - NOVELL_INC
    * name                    - jsnielsen.java.prv.novell
    *
    * e.g. NOVELL_INC
    * administrative domain   - [undefined]
    * domain                  - NOVELL_INC
    * name                    -
    *
    * e.g. STILLWATERS.[BINDERY]
    * administrative domain   - [BINDERY]
    * domain                  - NOVELL_INC
    * name                    -
    */

   static String getAdministrativeDomain(Identity identity)
   {
      IdentityScope scope = identity.getScope();
      if (scope == null)
      {
         if (identity instanceof NdsIdentity)
            return NdsIdentityScope.ADMINISTRATIVE_DOMAIN_NAME;
         if (identity instanceof BinderyIdentity)
            return BinderyIdentityScope.ADMINISTRATIVE_DOMAIN_NAME;
         String name = identity.getName();
         if(isAdministrativeDomain(name))
            return name;
         else
            return "[undefined]";
      }
      else
         return getAdministrativeDomain(scope);
   }

   /*
    * administrative domains are either [NDS], [BINDERY],[undefined].
    */
   static boolean isAdministrativeDomain(String name)
   {
      if (name == null)
         return false;

      if(name.equalsIgnoreCase(NdsIdentityScope.ADMINISTRATIVE_DOMAIN_NAME)||name.equalsIgnoreCase(BinderyIdentityScope.ADMINISTRATIVE_DOMAIN_NAME))
         return true;
      return false;
   }
   

   public Identity[] getIdentities(Identity in)
      throws AuthenticatorException
   {
      channel.beginProtocol(this);

      boolean bindery = true;
      boolean nds = true;

      if(getAdministrativeDomain(in).equalsIgnoreCase(BinderyIdentityScope.ADMINISTRATIVE_DOMAIN_NAME))
         nds = false;
      else if(getAdministrativeDomain(in).equalsIgnoreCase(NdsIdentityScope.ADMINISTRATIVE_DOMAIN_NAME))
         bindery = false;

      Vector identities = new Vector();
      Identity ids[] = null;
      try
      {
         // Get the session manager
         SessionManager sessionManager = getSessionManager(in);
         SessionAttrs attrs;
         // NDS
         if(nds)
         {
            IdentityScope sys = new NdsIdentityScope(NdsIdentityScope.ADMINISTRATIVE_DOMAIN_NAME);
            attrs = new SessionAttrs();
            attrs.add(new SessionAttr(Session.PROVIDER_NAME_ATTR_ID, "NDS"));
            attrs.add(new SessionAttr(Authenticatable.IS_AUTHENTICATED_ATTR_ID, new Boolean(true)));
            attrs.add(new SessionAttr(Session.SESSION_TYPE_ATTR_ID, "TREE"));
            SessionEnumerator enum = sessionManager.search(attrs);
            while (enum.hasMoreElements())
            {
               Session found = enum.next();
               NdsIdentityScope scope = new NdsIdentityScope(
                  stripUnderscores(found.getDomainName()),sys);
               NdsIdentity id = new NdsIdentity(((Authenticatable)found).getName(),scope);
               identities.addElement(id);
            }
         }

         if(bindery)
         {
            IdentityScope sys = new BinderyIdentityScope(BinderyIdentityScope.ADMINISTRATIVE_DOMAIN_NAME);
            attrs = new SessionAttrs();
            attrs.add(new SessionAttr(Session.PROVIDER_NAME_ATTR_ID, "BINDERY"));
            attrs.add(new SessionAttr(Authenticatable.IS_AUTHENTICATED_ATTR_ID, new Boolean(true)));
            attrs.add(new SessionAttr(Session.SESSION_TYPE_ATTR_ID, "SERVER"));
            SessionEnumerator enum = sessionManager.search(attrs);
            while (enum.hasMoreElements())
            {
               Session found = enum.next();
               BinderyIdentityScope scope = new BinderyIdentityScope(
                  stripUnderscores(found.getDomainName()),sys);
               BinderyIdentity id = new BinderyIdentity(((Authenticatable)found).getName(),scope);
               identities.addElement(id);
            }
         }

         ids = new Identity[identities.size()];
         identities.copyInto(ids);

//         for(int i = 0; i<ids.length; i++)
//            Debug.println("Identities: "+ids[i]);
      }
      catch(AuthenticatorException x)
      {
         throw x;
      }
      catch (java.lang.Throwable x)
      {
         throw new GetIdentitiesException(x);
      }
      finally
      {
        channel.endProtocol(this);
        return ids;
      }
   }
   //***********************************************************
   // PRIVATE IMPLEMENTATION SPECIFIC CODE
   //***********************************************************
   static void performLogin (
         Identity identity,
         String userName,
         String domainName,
         Password password,
         Thread t)
      throws SessionException, RemoteException
   {
      Shibboleth ex = getShibboleth (identity, domainName);
      boolean useExCharPassFlag = false;

      if(identity instanceof NdsIdentity && ((NdsIdentity)identity).getUseExtendedCharacterPasswords())
         useExCharPassFlag = true;

      if(identity instanceof NdsIdentity && ((NdsIdentity)identity).getUseNAS())
         ex.authenticateNAS((NdsIdentity)identity, userName, password.getRaw(), useExCharPassFlag);
      else
         ex.authenticate (userName, password.getRaw(), useExCharPassFlag);
      
      Authenticator.putLast(userName, domainName, t);
   }

/*
   static void performLdapLogin (
         LdapIdentity identity,
         String name,
         Password password)
      throws Exception
   {
      LdapBinding binding = new LdapBinding(identity, name, password);

      identity.setBinding(binding);
   }
*/

   static void performModifyTokens (
         Identity identity,
         String userName,
         String domainName,
         Password password,
         Password newPassword,
         Thread t)
      throws SessionException, RemoteException
   {
      boolean useExCharPassFlag = false;
      Shibboleth ex = getShibboleth (identity, domainName);

      if(identity instanceof NdsIdentity && ((NdsIdentity)identity).getUseExtendedCharacterPasswords())
         useExCharPassFlag = true;
      ex.changePassword (userName, password.getRaw(), newPassword.getRaw(), useExCharPassFlag);

      Authenticator.putLast(userName, domainName, t);
   }

   static void performCreateTokens (
         Identity identity,
         String userName,
         String domainName,
         Password password,
         Thread t)
      throws SessionException, RemoteException
   {
      boolean useExCharPassFlag = false;
      Shibboleth ex = getShibboleth (identity, domainName);

      if(identity instanceof NdsIdentity && ((NdsIdentity)identity).getUseExtendedCharacterPasswords())
         useExCharPassFlag = true;
      ex.setPassword(userName, password.getRaw(), useExCharPassFlag);

      Authenticator.putLast(userName, domainName, t);
   }

   static void performVerifyTokens (
         Identity identity,
         String userName,
         String domainName,
         Password password,
         Thread t)
      throws SessionException, RemoteException
   {
      boolean useExCharPassFlag = false;
      Shibboleth ex = getShibboleth (identity, domainName);

      if(identity instanceof NdsIdentity && ((NdsIdentity)identity).getUseExtendedCharacterPasswords())
         useExCharPassFlag = true;
      ex.verifyPassword(userName, password.getRaw(), useExCharPassFlag);

      Authenticator.putLast(userName, domainName, t);
   }

   static void performLogout (
         Identity identity,
         String userName,
         String domainName,
         Thread t)
      throws SessionException, RemoteException
   {
      Shibboleth ex = getShibboleth (identity, domainName);

      if(identity instanceof NdsIdentity && ((NdsIdentity)identity).getUseNAS())
         ex.unauthenticateNAS((NdsIdentity)identity);
      else
         ex.unauthenticate ();

      Authenticator.putLast(userName, domainName, t);
   }

   String stripUnderscores (String name)
   {
      StringBuffer str = new StringBuffer(name);
      for(int i = str.length()-1; str.charAt(i) == '_'; i--)
         str.setLength(i);

      return str.toString();
   }

   static SessionManager getSessionManager (
         Identity identity)
      throws Exception
   {
      SessionManager sm = null;

      // Get the session manager
      if (identity instanceof XplatIdentity)
      {
         if (DEBUG)
            Debug.println("FOUND XplatIdentity: " + identity);

         sm = ((XplatIdentity) identity).getSessionManager ();
         if (null == sm)
         {
            Session s = null;
            s = ((XplatIdentity) identity).getSession ();
            if (null != s)
            {
               while (s.hasParent ())
                  s = s.getParent ();
               sm = (SessionManager) s;
            }
         }
      }

      if (null == sm)
         sm = SessionManagerFactory.getSessionManager (new SessionEnv ());

      return sm;
   }

   static Session getSession (
         Identity identity,
         String domainName)
      throws Exception
   {
      Session s = null;
      if (identity instanceof XplatIdentity)
         s = ((XplatIdentity) identity).getSession ();

      if (null == s)
      {
         SessionManager sm = getSessionManager (identity);

         // See if we already have the sesion
         s = sm.findSession (domainName);

         if (null != s)
         {  // We already have this session so let see if its the right one
            SessionAttrs sattrs = s.getAttributes (new String [] {Session.PROVIDER_NAME_ATTR_ID});
            String provider = (String) sattrs.getValue (Session.PROVIDER_NAME_ATTR_ID);

            if (identity instanceof BinderyIdentity)
            {  // If its a match, we're OK
               if (provider.equals (com.novell.service.session.bindery.Bindery.PROVIDER_NAME))
                  return (s);
            }
            else if (identity instanceof NdsIdentity)
            {  // If its a match, we're OK
               if (provider.equals (com.novell.service.session.nds.NDS.PROVIDER_NAME))
                  return (s);
            }
            // This one is in the wrong provider so nuke it then re-open it
            s.close ();
         }
         // Ok, we didn't find an existing one we were happy with
         // Get the proper initial session
         if (identity instanceof BinderyIdentity)
            s = sm.getSession (com.novell.service.session.bindery.Bindery.INITIAL_SESSION);
         else
            s = sm.getSession (com.novell.service.session.nds.NDS.INITIAL_SESSION);
         // Now ask the initial session to open the one we're looking for
         s = s.getSession (domainName);
      }

      return s;
   }

   static Shibboleth getShibboleth (
         Identity identity,
         String domainName)
      throws SessionException, RemoteException
   {
      try
      {
         Session s = getSession(identity, domainName);
         return (Shibboleth)s.getService(ShibbolethImpl.KEY);
      }
      catch(Exception e)
      {
         throw new SessionException("Check root cause", e);
      }
   }

	private AuthenticatorProtocol getGUI()
		throws ConfigurationException
	{
     	try {
     		if (guiProxy == null) {
				guiProxy = new XplatProtocolProxyGUI(this);
     		}
     		return guiProxy;
   		} catch (java.lang.Throwable e) {
            throw new ConfigurationException(
          	   com.novell.java.security.I2L.getString(XMsg.JFC_IS_REQUIRED));
   		}
   	}

} // class XplatProtocolProxy


