Secure Bank Application

In this example we will use the various security service features to secure our Bank Application. The bank object will now, only service users from the bank realm. The account objects will run with the the "bankserver" identity (also in the bank realm). The account data store will make sure that bankserver is the caller.

Bank Interface in IDL

Same IDL as before.

module poaBank
{
    interface Account 
    {
    |   exception InsufficientFunds {};
    |                                                                      
    |   float deposit(in float sum);
    |                                                                      
    |   float withdraw(in float sum) raises (InsufficientFunds);
    |                                                                      
    |   readonly attribute float balance;
    };
                                                                           
    interface Bank
    {
    |   exception noSuchAccount {};
    |                                                                      
    |   Account getAccount(in long accountNum) raises (noSuchAccount);
    };
};

Bank Application Server

The Bank POA is created with an additional POA policy for security enforcement. The clients of the bank are now required to authenticate themselves with the "bank" realm. The messages between the clients and bank are still in plain IIOP (ie. no IIOP/SSL).

package poaBankSecureOld;
                                                                           
import util.Util;
import org.omg.CORBA.ORB;
import org.omg.CORBA.Policy;
                                                                           
import org.omg.CosNaming.NameComponent;
import org.omg.CosNaming.NamingContext;
import org.omg.CosNaming.NamingContextHelper;
import org.omg.CosNaming.NamingContextPackage.NotFound;
                                                                           
import com.sssw.jbroker.api.PortableServer.POA;
import com.sssw.jbroker.api.security.QualityOfProtection;
                                                                           
import org.omg.PortableServer.Servant;
import org.omg.PortableServer.LifespanPolicyValue;
import org.omg.PortableServer.IdAssignmentPolicyValue;
                                                                           
import poaBank.Bank;
import poaBank.BankHelper;
                                                                           
public class Server
{
    public static void main(String[] args)
    {
    |   try {
    |   |                                                                  
    |   |   // create the jBroker ORB
    |   |   ORB orb = ORB.init(args, null);
    |   |                                                                  
    |   |   // get the root POA
    |   |   POA rootPOA = (POA) orb.resolve_initial_references("RootPOA");
    |   |                                                                  
    |   |   // create a persistent user assigned ids POA
    |   |   POA bankPOA = (POA) rootPOA.create_POA("bankPOA", 
    |   |       rootPOA.the_POAManager(),
    |   |       new Policy[] {
    |   |       |   rootPOA.create_id_assignment_policy(
    |   |       |       IdAssignmentPolicyValue.USER_ID),
    |   |       |   rootPOA.create_lifespan_policy(
    |   |       |       LifespanPolicyValue.PERSISTENT),
    |   |       |   rootPOA.create_security_policy("bank")
    |   |       });
    |   |                                                                  
    |   |   // create and activate bank servant
    |   |   Servant bank = new BankImpl(orb, bankPOA, args[0]);
    |   |   bankPOA.activate_object_with_id("bank".getBytes(), bank);
    |   |                                                                  
    |   |   // get the root of the NameService
    |   |   NamingContext root = NamingContextHelper.narrow(
    |   |       orb.resolve_initial_references("NameService"));
    |   |                                                                  
    |   |   // if not done before, publish the bank objref
    |   |   NameComponent nc = new NameComponent("secureBankOld", "");
    |   |   NameComponent[] name = new NameComponent[] { nc };
    |   |   try {
    |   |   |   root.resolve(name);
    |   |   } catch (NotFound ex) {
    |   |   |   // create the bank objref
    |   |   |   Bank bankObj = BankHelper.narrow(bankPOA.
    |   |   |       create_reference_with_id("bank".getBytes(),
    |   |   |           bank._all_interfaces(null, null)[0]));
    |   |   |                                                              
    |   |   |   System.out.println(orb.object_to_string(bankObj));
    |   |   |   root.rebind(name, bankObj);
    |   |   }
    |   |                                                                  
    |   |   // activate bankPOA
    |   |   bankPOA.the_POAManager().activate();
    |   |                                                                  
    |   |   // wait for invocations
    |   |   System.out.println("waiting for invocations ...");
    |   |   orb.run();
    |   |                                                                  
    |   } catch (Exception ex) {
    |   |   ex.printStackTrace();
    |   }
    }
}

Bank Implementation

The bank implementation is pretty much as before. It does get the security current to display the IP address of the caller. It could have used it to do IP address based access control.

package poaBankSecureOld;
                                                                           
import java.io.File;
                                                                           
import org.omg.CORBA.ORB;
import org.omg.CORBA.Policy;
                                                                           
import org.omg.PortableServer.POA;
import org.omg.PortableServer.Servant;
import org.omg.PortableServer.IdUniquenessPolicyValue;
import org.omg.PortableServer.IdAssignmentPolicyValue;
import org.omg.PortableServer.ServantRetentionPolicyValue;
import org.omg.PortableServer.RequestProcessingPolicyValue;
                                                                           
import com.sssw.jbroker.api.security.SecurityCurrent;
                                                                           
import poaBank.BankPOA;
import poaBank.Account;
import poaBank.AccountHelper;
import poaBank.BankPackage.noSuchAccount;
                                                                           
public class BankImpl extends BankPOA
{
    ORB                    _orb;
    POA                    _acctPOA;
    String                 _acctRepId; // repository Id for Account Objects
    Servant                _defaultServant;
    SecurityCurrent        _securityCurrent;
                                                                           
    BankImpl(ORB orb, POA bankPOA, String dir) throws Exception
    {
    |   _orb = orb;
    |                                                                      
    |   // make sure that the DB dir exists
    |   File dbDir = new File (dir);
    |   if (!dbDir.exists()) dbDir.mkdir();
    |                                                                      
    |   // get the securtity current
    |   _securityCurrent = (SecurityCurrent) _orb.resolve_initial_references(
    |       "SecurityCurrent");
    |                                                                      
    |   // create the POA for Transient Account Objects
    |   _acctPOA = bankPOA.create_POA("acctPOA", bankPOA.the_POAManager(),
    |       new Policy[] {
    |       |   bankPOA.create_id_assignment_policy(
    |       |       IdAssignmentPolicyValue.USER_ID),
    |       |   bankPOA.create_request_processing_policy(
    |       |       RequestProcessingPolicyValue.USE_DEFAULT_SERVANT),
    |       |   bankPOA.create_id_uniqueness_policy(
    |       |       IdUniquenessPolicyValue.MULTIPLE_ID)
    |       });
    |                                                                      
    |   // set the servant manager in Acct POA
    |   _defaultServant = new AccountImpl(_orb, dbDir);
    |   _acctPOA.set_servant(_defaultServant);
    |                                                                      
    |   // compute the repository id for Account objects
    |   _acctRepId = _defaultServant._all_interfaces(null, null)[0];
    }
                                                                           
    public Account getAccount(int acctNum) throws noSuchAccount
    {
    |   Account account = null;
    |                                                                      
    |   // print out the IP Address of the caller
    |   System.out.println(_securityCurrent.getInetAddress());
    |                                                                      
    |   if ((acctNum >= 100) && (acctNum < 110)) {
    |   |                                                                  
    |   |   // construct the object id for account
    |   |   byte objectId[] = new byte[1];
    |   |   objectId[0] = (byte) (acctNum & 0xFF);
    |   |                                                                  
    |   |   try {
    |   |   |   account = AccountHelper.narrow(_acctPOA.
    |   |   |      create_reference_with_id(objectId, _acctRepId));
    |   |   } catch (Exception ex) {
    |   |   |   // org.omg.PortableServer.POAPackage.WrongPolicy
    |   |   |   // thrown on JDK 1.2 and 1.3
    |   |   |   ex.printStackTrace();
    |   |   }
    |   }
    |                                                                      
    |   if (account == null) throw new noSuchAccount();
    |                                                                      
    |   return account;
    }
}

Account Implementation

The account default servant, when it is constructed, authenticates itself as the "bankserver" and always uses that identity when invoking the getAccount method on the account data store. Notice the use of stackThreadPrincipal and unstackThreadPrincipal.

In this example, the account data store is a local Java object. If the account data store had been a remote object (possibly running elsewhere), the identity "bankserver" would have propagated to it.

Note, that the AuthenticatedPrincipal is Serializable, so the AccountImpl object could have just loaded it up from some secure place.

package poaBankSecureOld;
                                                                           
import java.io.File;
                                                                           
import org.omg.CORBA.ORB;
import org.omg.CORBA.INTERNAL;
import org.omg.CORBA.INITIALIZE;
import org.omg.CORBA.PERSIST_STORE;
                                                                           
import poaBank.AccountPOA;
import poaBank.AccountPackage.InsufficientFunds;
                                                                           
import com.sssw.jbroker.api.security.Authenticator;
import com.sssw.jbroker.api.security.SecurityCurrent;
import com.sssw.jbroker.api.security.SecurityException;
import com.sssw.jbroker.api.security.AuthenticationScheme;
import com.sssw.jbroker.api.security.AuthenticatedPrincipal;
                                                                           
public class AccountImpl extends AccountPOA
{
    ORB                    _orb;
    AccountBalances        _accountsDb;
    SecurityCurrent        _securityCurrent;
    AuthenticatedPrincipal _principal;
                                                                           
    public AccountImpl(ORB orb, File acctDb) throws Exception
    {
    |   _orb        = orb;
    |                                                                      
    |   // authenticate with the bank realm
    |   Authenticator authenticator = (Authenticator) _orb.
    |       resolve_initial_references("Authenticator");
    |   _principal = authenticator.authenticate(AuthenticationScheme.DIGEST,
    |       "bank", "bankserver", "manager".getBytes());
    |                                                                      
    |   // get the Security current
    |   _securityCurrent = (SecurityCurrent) _orb.resolve_initial_references(
    |       "SecurityCurrent");
    |                                                                      
    |   // create the account Db
    |   _accountsDb = new AccountBalances(acctDb, _securityCurrent);
    }
                                                                           
    private AccountRecord getAccount(int acctNum)
    {
    |   try {
    |   |   try {
    |   |   |   _securityCurrent.stackThreadPrincipal(_principal);
    |   |   |   return _accountsDb.getAccount(acctNum);
    |   |   } finally {
    |   |   |   _securityCurrent.unstackThreadPrincipal(_principal);
    |   |   }
    |   } catch (SecurityException ex) {
    |   |   ex.printStackTrace();
    |   |   throw new INTERNAL();
    |   }
    }
                                                                           
    public float deposit(float sum)
    {
    |   int acctNum = getAccountNumber();
    |                                                                      
    |   AccountRecord record = getAccount(acctNum);
    |                                                                      
    |   float balance = 0;
    |   synchronized (record) {
    |   |   balance = record.getBalance() + sum;
    |   |   record.setBalance(balance);
    |   }
    |                                                                      
    |   return balance;
    }
                                                                           
    public float withdraw(float sum) throws InsufficientFunds
    {
    |   int acctNum = getAccountNumber();
    |                                                                      
    |   AccountRecord record = getAccount(acctNum);
    |                                                                      
    |   float balance = 0;
    |   synchronized (record) {
    |   |   balance = record.getBalance();
    |   |   if ((balance - sum) < 0) 
    |   |       throw new InsufficientFunds();
    |   |   balance -= sum;
    |   |   record.setBalance(balance);
    |   }
    |                                                                      
    |   return balance;
    }
                                                                           
    public float balance()
    {
    |   return getAccount(getAccountNumber()).getBalance();
    }
                                                                           
    private int getAccountNumber()
    {
    |   return _object_id()[0] & 0x000000FF;
    }
}

Account DataStore

The getAccount method checks that the caller is "bankserver" from the "bank" realm.

package poaBankSecureOld;
                                                                           
import java.io.File;
import java.util.Hashtable;
                                                                           
import org.omg.CORBA.NO_PERMISSION;
                                                                           
import com.sssw.jbroker.api.security.SecurityCurrent;
import com.sssw.jbroker.api.security.SecurityException;
import com.sssw.jbroker.api.security.AuthenticatedPrincipal;
                                                                           
// Datastore for Account Balances
class AccountBalances
{
    File            _dbDir;
    Hashtable       _table;
    SecurityCurrent _current;
                                                                           
    AccountBalances(File dbDir, SecurityCurrent current)
    {
    |   _dbDir   = dbDir;
    |   _table   = new Hashtable(19);
    |   _current = current;
    }
                                                                           
    synchronized AccountRecord getAccount(int acctNum)
    {
    |   // verify the caller is bank server
    |   if (!_current.getPrincipal().getName().equals("bankserver@bank"))
    |       throw new NO_PERMISSION();
    |                                                                      
    |   Integer AcctNum = new Integer(acctNum);
    |                                                                      
    |   AccountRecord record = (AccountRecord) _table.get(AcctNum);
    |                                                                      
    |   if (record == null) {
    |   |   record = new AccountRecord(0, new File(_dbDir, 
    |   |       AcctNum.toString()));
    |   |   _table.put(AcctNum, record);
    |   }
    |                                                                      
    |   return record;
    }
}
The AccountRecord class is shown below:

package poaBankSecureOld;
                                                                           
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
                                                                           
import org.omg.CORBA.PERSIST_STORE;
                                                                           
class AccountRecord
{
    private float _balance;
    private File  _file;
                                                                           
    AccountRecord(float balance, File file) 
    {
    |   _file    = file;
    |   _balance = balance;
    |                                                                      
    |   // if file exists read balance
    |   if (_file.exists()) {
    |   |   try {
    |   |   |   FileInputStream fis = new FileInputStream(_file);
    |   |   |   ObjectInputStream ois = new ObjectInputStream(fis);
    |   |   |   _balance = ((Float) ois.readObject()).floatValue();
    |   |   |   ois.close();
    |   |   } catch (Exception ex) {
    |   |   |   throw new PERSIST_STORE();
    |   |   }
    |   }
    }
                                                                           
    float getBalance()
    {
    |   return _balance;
    }
                                                                           
    void setBalance(float balance)
    {
    |   _balance = balance;
    |                                                                      
    |   try {
    |   |   FileOutputStream fos = new FileOutputStream(_file);
    |   |   ObjectOutputStream oos = new ObjectOutputStream(fos);
    |   |   oos.writeObject(new Float(_balance));
    |   |   oos.close();
    |   } catch (Exception ex) {
    |   |   throw new PERSIST_STORE();
    |   }
    }
}

Register the Server

The server deployment descriptor.

server.main=poaBankSecureOld.Server
server.alias=secureBankOld
server.classpath=/usr/local/JBroker/examples/lib
server.args=/usr/local/JBroker/examples/src/poaBankSecureOld/db
server.vmflags=-DORBUseDoors=true

Bank Client

Other than the authentication part, the rest of the client code is same as before.

The client authenticates itself using the account number and PIN it gets from the user and sets the authenticated principal at the ORB level. Had the client started multiple threads, they would all be running with the same identity.

package poaBankSecureOld;
                                                                           
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
                                                                           
import org.omg.CORBA.ORB;
                                                                           
import org.omg.CosNaming.NameComponent;
import org.omg.CosNaming.NamingContext;
import org.omg.CosNaming.NamingContextHelper;
                                                                           
import com.sssw.jbroker.api.security.Authenticator;
import com.sssw.jbroker.api.security.SecurityCurrent;
import com.sssw.jbroker.api.security.AuthenticationScheme;
import com.sssw.jbroker.api.security.AuthenticatedPrincipal;
                                                                           
import poaBank.Bank;
import poaBank.Account;
import poaBank.BankHelper;
import poaBank.AccountPackage.InsufficientFunds;
                                                                           
public class Client
{
    ORB     _orb;
    Bank    _bank;
    Account _account;
    String  _acctNum;
    String  _acctPIN;
                                                                           
    public static void main(String[] args)
    {
    |   try {
    |   |   new Client().run(args);
    |   } catch (Exception ex) {
    |   |   ex.printStackTrace();
    |   }
    }
                                                                           
    public void run(String[] args) throws Exception
    {
    |   // create the jBroker ORB
    |   _orb = ORB.init(args, null);
    |                                                                      
    |   // get the root of the NameService
    |   NamingContext root = NamingContextHelper.narrow(
    |       _orb.resolve_initial_references("NameService"));
    |                                                                      
    |   // get the bank object reference
    |   NameComponent nc = new NameComponent(args[0], "");
    |   NameComponent[] name = new NameComponent[] { nc };
    |   _bank = BankHelper.narrow(root.resolve(name));
    |                                                                      
    |   // enter the request processing loop
    |   processCommands();
    }
                                                                           
    void processCommands() throws Exception
    {
    |   // create a input stream reader
    |   BufferedReader reader = new BufferedReader(new InputStreamReader(
    |       System.in));
    |                                                                      
    |   // get the account number
    |   System.out.print("Enter Account number: ");
    |   System.out.flush();
    |   _acctNum = reader.readLine();
    |                                                                      
    |   // get the account PIN
    |   System.out.print("Enter Account PIN: ");
    |   System.out.flush();
    |   _acctPIN = reader.readLine();
    |                                                                      
    |   // authenticate with the bank realm
    |   Authenticator authenticator = (Authenticator) _orb.
    |       resolve_initial_references("Authenticator");
    |   AuthenticatedPrincipal principal = authenticator.authenticate(
    |       AuthenticationScheme.DIGEST, "bank", _acctNum, _acctPIN.getBytes());
    |                                                                      
    |   // set the authenticated principal at the ORB level
    |   SecurityCurrent current = (SecurityCurrent) _orb.
    |       resolve_initial_references("SecurityCurrent");
    |   current.setORBPrincipal(principal);
    |                                                                      
    |   // get the account object
    |   _account = _bank.getAccount(new Integer(_acctNum).intValue());
    |                                                                      
    |   // enter the read-execute loop
    |   while (true) {
    |   |   System.out.print("bank > ");
    |   |   executeCommand(readCommandLine(reader));
    |   }
    }
                                                                           
    void executeCommand(String[] command) throws Exception
    {
    |   if (command.length == 0) 
    |       printHelp();
    |                                                                      
    |   // get balance
    |   else if (command[0].equals("b"))
    |       System.out.println("\tbalance: $" + _account.balance());
    |                                                                      
    |   // check the command for addition parameter
    |   else if (command[0].equals("d")) {
    |   |   if (command.length != 2) printHelp();
    |   |   else System.out.println("\tbalance: $"+ _account.deposit(new Float(
    |   |       command[1]).floatValue()));
    |   }
    |                                                                      
    |   // deposit money
    |   else if (command[0].equals("w")) {
    |   |   if (command.length != 2) printHelp();
    |   |   else {
    |   |   |   try {
    |   |   |   |   System.out.println("\tbalance: $"+ _account.withdraw(
    |   |   |   |       new Float(command[1]).floatValue()));
    |   |   |   } catch (InsufficientFunds ex) {
    |   |   |   |   System.out.println("\tInsufficient Funds!");
    |   |   |   }
    |   |   }
    |   }
    |                                                                      
    |   // quit
    |   else if (command[0].equals("q")) {
    |   |   System.exit(0);
    |   }
    |                                                                      
    |   // print help
    |   else printHelp();
    }
                                                                           
    void printHelp()
    {
    |   System.out.println();
    |   System.out.println("\tCommands:");
    |   System.out.println("\t   b               // get balance");
    |   System.out.println("\t   d <sum>         // deposit sum");
    |   System.out.println("\t   w <sum>         // withdraw sum");
    |   System.out.println("\t   q               // quit");
    |   System.out.println();
    }
                                                                           
    String[] readCommandLine(BufferedReader reader) throws Exception
    {
    |   int i=0;
    |                                                                      
    |   String line = reader.readLine();
    |                                                                      
    |   if (line == null) System.exit(0);
    |                                                                      
    |   StringTokenizer tokenizer = new StringTokenizer(line);
    |   String[] command = new String[tokenizer.countTokens()];
    |   while (tokenizer.hasMoreTokens()) {
    |   |   command[i++] = tokenizer.nextToken();
    |   }
    |                                                                      
    |   return command;
    }
}


Copyright © 2003, 2004 Novell, Inc. All rights reserved. Copyright © 2001, 2002, 2003 SilverStream Software, LLC. All rights reserved.