Showing posts with label Programming. Show all posts
Showing posts with label Programming. Show all posts

Friday, March 17, 2017

My Notes on the Java RMI and Static Ports

[I wrote this 9/23/2008 with Java 1.6, but it's probably still accurate]

Java Remote Method Invocation (RMI) is a technology that makes it very easy to implement server client architectures. Java RMI lets you to make method calls to remote objects without having the client treat the remote object very differently from a local object. I've been using RMI a fair amount recently, so I thought I'd go over some of the more interesting things I've learned. Primarily, how it works and how to control the ports it uses.

A Brief Overview about how RMI Works

First there are the "Stubs."  Stubs are essentially specialized proxies to a specific server. Internally, they hold the IP and Port to a server. They know how to make requests to the server and how to process the response from that server. They're Java objects and are used exactly the same as a local object once you've obtained the stub.

Then there's the "RMI Registry."  The RMI Registry is a server that stores name to stub matchings. All its clients know the IP and Port number for the Registry. The clients contact the registry to get stub objects for servers whose IP and Port number they don't necessarily know. The Registry listens on port 1099 unless configured otherwise.

Finally, there's a server run for each stub that's registered in the RMI Registry.

And so the following happens when RMI is used:
  1. The RMI Registry that stores name to stub pairings is created.
  2. Whenever a new name and stub is bound in the Registry, a matching server is created (usually on a dynamic port) for the stub to communicate with.
  3. The client knows the IP and Port of an RMI Registry. The client uses that IP and Port to ask the registry for the stub matching a certain name.
  4. When a client calls a method on the stub, the stub contacts the matching server to process the request. The matching server processes the response and returns a result to the stub. The stub in turn processes the response and returns a value as if the method was called locally.
This really is a brief overview, for a lot more detail about the specifics, give http://www.developer.com/java/ent/article.php/10933_3455311_1 a thorough read. One thing not mentioned is that RMI is multi-threaded. I'm not entirely sure about the following point, but I think that a new thread is automatically created for every stub request. I don't know of anyway to make RMI non multi-threaded (in case you can't have every client sending information to the same JVM.)

But You Don't Care How It Works, do you?

The great thing about RMI is that the programmer doesn't really have to worry about the network communications at all. They just need to get a Java object (the stub) from the Registry and make method calls on it. The stub, skeleton, and the registry do all the network stuff for them. So all a programmer really cares about is implementing RMI. For that information, Java's own tutorial is probably the best source. So go and read it at http://java.sun.com/docs/books/tutorial/rmi/server.html. Really, the rest of this article won't make sense without knowing how to use RMI.

Stop Randomly Choosing Ports!!!

For the project I was working on, it was important to control the IP and the Ports RMI used. I was using Solaris 10 with Trusted Extensions and wanted Cross Domain capabilities, which required me to explicitly identify certain ports as "Multi Level Ports." You can see how dynamically assigning ports to RMI servers was no good for me. Furthermore, I was using several different IP Addresses on the machine and did not want RMI to to accept connections on all of them. So what this really came down to was controlling the sockets that are normally dynamically created by the Registry and RMI Servers.

I wrote my own RMIServerSocketFactory to gain control over the sockets in RMI. It is as follows:
CustomSocketFactory.java

view plaincopy to clipboardprint?
  1. ///////////////////////////////////////////////////////////////////////////  
  2. //  
  3. // CustomSocketFactory - This class is used to provide client and server  
  4. //   sockets with configurable ports and ip addresses.  By default,  
  5. //   RMISocketFactory chooses these dynamically.  Note that the backlog  
  6. //   for ServerSockets is hardcoded.  
  7. //  
  8. ///////////////////////////////////////////////////////////////////////////  
  9.   
  10. import java.io.IOException;  
  11. import java.io.Serializable;  
  12. import java.net.InetAddress;  
  13. import java.net.ServerSocket;  
  14. import java.net.Socket;  
  15. import java.rmi.server.RMISocketFactory;  
  16.   
  17. /** 
  18.  * CustomSocketFactory 
  19.  */  
  20. public class CustomSocketFactory extends RMISocketFactory implements Serializable  
  21. {  
  22.     /** 
  23.      * The InetAddress to be used for the Sockets this factory serves. 
  24.      */  
  25.     private InetAddress ipInterface = null;  
  26.   
  27.     /** 
  28.      * The port to be used with Sockets this factory serves.  By 
  29.      * default this value is set to -1 (which is an impossible port), 
  30.      * which tells the factory to allow the port be be dynamically determined. 
  31.      */  
  32.     private int port = -1;  
  33.   
  34.     /** 
  35.      * Default Constructor.  IP and Port will be dynamically assigned. 
  36.      */  
  37.     public CustomSocketFactory() {}  
  38.   
  39.     /** 
  40.      * Constructor that specifies the INetAddress to use.  The port will still 
  41.      * be served dynamically. 
  42.      * 
  43.      * @param ipInterface The InetAddress to be associated with Sockets served by 
  44.      * this factory. 
  45.      */  
  46.     public CustomSocketFactory(InetAddress ipInterface)  
  47.     {  
  48.         this.ipInterface = ipInterface;  
  49.     }  
  50.   
  51.     /** 
  52.      * Constructor that specifies the INetAddres and port to use. 
  53.      * 
  54.      * @param ipInterface The INetAddress to be associated with Sockets served by 
  55.      * this factory. 
  56.      * @param port The port to be associated with Sockets served by this 
  57.      * factory. 
  58.      */  
  59.     public CustomSocketFactory(InetAddress ipInterface, int port)  
  60.     {  
  61.         this.ipInterface = ipInterface;  
  62.         this.port = port;  
  63.     }  
  64.   
  65.     /** 
  66.      * Returns a ServerSocket with the ip address and port used dependant 
  67.      * on the constructor used. 
  68.      * 
  69.      * @param port The dynamic port to be used for the socket served.  This 
  70.      * is not used if the constructor specifying a port was used 
  71.      * for this factory. 
  72.      * @return A socket. 
  73.      */  
  74.     public ServerSocket createServerSocket(int port) throws IOException  
  75.     {  
  76.         ServerSocket serverSocket = null;  
  77.   
  78.         //Decides whether or not to use the supplied port  
  79.         if( this.port != -1 )  
  80.         {  
  81.             port = this.port;  
  82.         }  
  83.   
  84.         try  
  85.         {  
  86.             //NOTE: The backlog is hardcoded to 50 here.  This was  
  87.             //  done for no particular reason and could be dynamically  
  88.             //  set by making new method calls.  
  89.             serverSocket = new ServerSocket(port, 50, ipInterface);  
  90.         } catch( IOException e ) {  
  91.             throw e;  
  92.         }  
  93.   
  94.         return serverSocket;  
  95.     }  
  96.   
  97.     /** 
  98.      * Returns a Socket with the ip address and port used dependant 
  99.      * on the constructor used. 
  100.      * 
  101.      * @param port The dynamic port to be used for the socket served.  This 
  102.      * is not used if the constructor specifying a port was used 
  103.      * for this factory. 
  104.      * @param dummy This INetAddress is not used. 
  105.      * @throws Exception Throws for any exception. 
  106.      * @return A socket. 
  107.      */  
  108.     public Socket createSocket( String dummy, int port) throws IOException  
  109.     {  
  110.         if( this.port != -1 )  
  111.         {  
  112.             port = this.port;  
  113.         }  
  114.   
  115.         return new Socket( ipInterface, port );  
  116.     }  
  117.   
  118.     /** 
  119.      * Returns true if the object passed in is identical to this object. 
  120.      * 
  121.      * @param that The object to be compared with this object. 
  122.      * @return True if equal, otherwise false. 
  123.      */  
  124.     public boolean equals( Object that )  
  125.     {  
  126.         return (that != null && that.getClass() == this.getClass() );  
  127.     }  
  128. }  
Now I simply specified that SocketFactory when I created the RMI Registry and the stubs/servers! Here's a very simple example:

AddServerInterface.java
view plaincopy to clipboardprint?
  1. import java.rmi.Remote;  
  2. import java.rmi.RemoteException;  
  3.   
  4. public interface AddServerInterface extends Remote  
  5. {  
  6.     public int add( int a, int b ) throws RemoteException;  
  7. }  
  1. import java.net.InetAddress;  
  2. import java.rmi.RemoteException;  
  3. import java.rmi.registry.LocateRegistry;  
  4. import java.rmi.registry.Registry;  
  5. import java.rmi.server.UnicastRemoteObject;  
  6.   
  7. public class AddServer implements AddServerInterface  
  8. {  
  9.     public AddServer() throws Exception  
  10.     {        
  11.         //Set up the RMI.  Use hard coded values for this example.  
  12.         String registryIp = "10.10.1.1";  
  13.         int registryPort = 3645;  
  14.         String serverIp = "10.10.1.1";  
  15.         int serverPort = 3646;  
  16.           
  17.         //Create the Add Server, supplying the Socket Factories to assign our IP and Port.  
  18.         CustomSocketFactory clientSocketFactory = new CustomSocketFactory( InetAddress.getByName(serverIp), serverPort );          
  19.         CustomSocketFactory serverSocketFactory = new CustomSocketFactory( InetAddress.getByName(serverIp), serverPort );  
  20.         AddServerInterface stub = (AddServerInterface)UnicastRemoteObject.exportObject(this,serverPort,clientSocketFactory,serverSocketFactory);  
  21.           
  22.         //Get or Create the RMI registry.  
  23.         Registry registry = null;          
  24.         try  
  25.         {  
  26.             //Create Registry  
  27.             CustomSocketFactory sf = new CustomSocketFactory( InetAddress.getByName(registryIp), registryPort );  
  28.             registry = LocateRegistry.createRegistry( registryPort, null, sf );  
  29.         }  
  30.         catch( RemoteException re )  
  31.         {  
  32.             //Registry was already created, so just connect  
  33.             registry = LocateRegistry.getRegistry(registryPort);    
  34.         }  
  35.           
  36.         //Bind the Add Server to the registry  
  37.         registry.rebind( "AddServer", stub );   
  38.     }  
  39.       
  40.     public int add( int a, int b ) throws RemoteException  
  41.     {  
  42.         return a+b;  
  43.     }  
  44.       
  45.     public static void main( String args[] ) throws Exception  
  46.     {  
  47.         try  
  48.         {              
  49.             AddServer addServer = new AddServer();  
  50.         } catch ( Exception e ) {  
  51.             System.out.println( "Main error: " );  
  52.             e.printStackTrace();  
  53.         }  
  54.     }  
  55. }  
So now the registry listens on 10.10.1.1/3645 and the Add Server listens on 10.10.1.1/3646. It works like a charm. Go ahead, try an example client:

  1. //Required for RMI Support  
  2. import java.net.InetAddress;  
  3. import java.rmi.Remote;  
  4. import java.rmi.registry.LocateRegistry;  
  5. import java.rmi.registry.Registry;  
  6.   
  7. public class AddClient  
  8. {  
  9.     public AddClient() {}  
  10.       
  11.     public static void Main( String args[] )  
  12.     {  
  13.         try   
  14.         {          
  15.            //Connect to the add server.    
  16.            Registry registry = LocateRegistry.getRegistry( "10.10.1.1", 3645 );  
  17.            Remote remote = registry.lookup( "AddServer" );  
  18.            if( remote == null )  
  19.            {  
  20.               System.out.println( "Could not find it" );  
  21.            }  
  22.            AddServerInterface addServer = (AddServerInterface)remote;  
  23.            
  24.        System.out.println( "1 + 1 = " + addServer.add( 1, 1 ) );             
  25.         } catch ( Exception e ) {  
  26.            e.printStackTrace();  
  27.         }          
  28.     }  
  29. }  

Friday, March 16, 2012

Solaris Workspace Menu

One of the smaller tasks I had at work was setting up customized workspace menus for users under a Solaris 10 environment.  I've verified that this also works on Trusted Solaris 8.  In fact, I'd bet that it's the same for most versions of Solaris, if not Unix.

Workspace menus are definied in dtwmrc files.  This defines windows options, the right click menu, and many key bindings.  The main workspace menu is found in "/etc/dt/config/C/sys.dtwmrc."  Failing finding another menu in a home directory, this is what will be used.

If another menu is be found in the current user's home directory's .dt folder (such as "/export/home/user/.dt/dtwmrc), that menu will take precedence.  If a role is assumed and another menu is found in the role's home directory's .dt folder (such as "/etc/security/home/primary admin/.dt/dtwmrc"), then that menu will take precendence.

Here is a sample (modified to be short) dtwmrc file:

  1. ###  
  2. #  
  3. # Root Menu Description  
  4. #  
  5. ###  
  6.   
  7. Menu DtRootMenu  
  8. {  
  9.     "Workspace Menu"            f.title  
  10.      no-label                f.separator  
  11.     "Refresh Workspace Display..."    f.refresh  
  12.     "Restart Workspace Manager..."    f.restart  
  13.      no-label                f.separator  
  14.     "Lock Display"            f.action LockDisplay  
  15.      "Log out..."            f.action ExitSession  
  16. }  
  17.   
  18. ###  
  19. #  
  20. # Front panel Manu Description  
  21. #  
  22. ###  
  23.   
  24. Menu DtPanelMenu  
  25. {  
  26.     Restore            _R    f.normalize  
  27.     Move            _M    f.move  
  28.     Minimize            _n    f.minimize  
  29.     Lower            _L    f.lower  
  30.     Refresh            _f    f.refresh  
  31.      no-label                f.separator  
  32.     "Log out..."        _o    f.action ExitSession  
  33. }  
  34.   
  35.   
  36. ###  
  37. #  
  38. # Sample Window Menu Description  
  39. # This menu description exists as a sample only.  
  40. # The normal window manager menu is built in.  
  41. #  
  42. ###  
  43.   
  44. Menu SampleWindowMenu  
  45. {  
  46.     "Restore"            _R            f.normalize  
  47.     "Move"            _M            f.move  
  48.     "Size"            _S            f.resize  
  49.     "Minimize"            _n            f.minimize  
  50.     "Maximize"            _x            f.maximize  
  51.     "Lower"            _L            f.lower  
  52.      no-label                        f.separator  
  53.     "Occupy Workspace..."    _O            f.workspace_presence  
  54.     "Occupy All Workspaces"    _A            f.occupy_all  
  55.     "Unoccupy Workspace"    _U            f.remove  
  56.      no-label                        f.separator  
  57.     "Close"            _C    Alt<Key>F4    f.kill  
  58. }  
  59.   
  60. ###  
  61. #  
  62. # Alternate Window Menu Description without accelerators  
  63. #  
  64. ###  
  65.   
  66. Menu NoAcceleratorWindowMenu  
  67. {  
  68.     "Restore"            _R    f.normalize  
  69.     "Move"            _M    f.move  
  70.     "Size"            _S    f.resize  
  71.     "Minimize"            _n    f.minimize  
  72.     "Maximize"            _x    f.maximize  
  73.     "Lower"            _L    f.lower  
  74.      no-label                f.separator  
  75.     "Occupy Workspace..."    _O    f.workspace_presence  
  76.     "Occupy All Workspaces"    _A    f.occupy_all  
  77.     "Unoccupy Workspace"    _U    f.remove  
  78.      no-label                f.separator  
  79.     "Close"            _C    f.kill  
  80. }  
  81.   
  82. ###  
  83. #  
  84. # Key Bindings Description  
  85. #  
  86. ###  
  87.   
  88. Keys DtKeyBindings  
  89. {  
  90.     Ctrl<key>c            root|icon|window    f.nop    # Copy  
  91.     Ctrl<key>v            root|icon|window    f.nop    # Paste  
  92.     Ctrl<key>x            root|icon|window    f.nop    # Cut  
  93. }  
  94.   
  95. ###  
  96. #  
  97. # Mouse Button Bindings Description  
  98. #  
  99. ###  
  100.   
  101. Buttons DtButtonBindings  
  102. {  
  103.   
  104.     <Btn1Down>        root            f.marquee_selection  
  105.     <Btn2Click>        root            f.toggle_frontpanel  
  106.     <Btn3Down>        root            f.menu  DtRootMenu  
  107. }  
  108.   
  109.   
  110. ###  
  111. #  
  112. # Defaults:   Window menus, key bindings, and mouse button bindings  
  113. #  
  114. ###  
  115.   
  116. Menu DefaultWindowMenu  
  117. {  
  118.     "Restore"    _R    Alt<Key>F5    f.normalize  
  119.     "Move"    _M    Alt<Key>F7    f.move  
  120.     "Size"    _S    Alt<Key>F8    f.resize  
  121.     "Minimize"    _n    Alt<Key>F9    f.minimize  
  122.     "Maximize"    _x    Alt<Key>F10    f.maximize  
  123.     "Lower"    _L    Alt<Key>F3    f.lower  
  124.     no-label                f.separator  
  125.     "Close"    _C    Alt<Key>F4    f.kill  
  126. }  
  127.   
  128. Keys DefaultKeyBindings  
  129. {  
  130.     Shift<Key>Escape        icon|window        f.post_wmenu  
  131.     Alt<Key>space        icon|window        f.post_wmenu  
  132.     Alt<Key>Tab            root|icon|window    f.next_key  
  133.     Alt Shift<Key>Tab        root|icon|window    f.prev_key  
  134.     Alt<Key>Escape        root|icon|window    f.next_key  
  135.     Alt Shift<Key>Escape    root|icon|window    f.prev_key  
  136.     Alt Ctrl Shift<Key>exclam    root|icon|window    f.set_behavior  
  137.     Alt<Key>F6            window            f.next_key transient  
  138. }  
  139.   
  140. Buttons DefaultButtonBindings  
  141. {  
  142.     <Btn1Down>        frame|icon    f.raise  
  143.     <Btn3Down>        icon        f.post_wmenu  
  144. }  
  145.   
  146. ###  
  147. #  
  148. # Designer Desktop Customization: $HOME/.dt/wsmenu.dtwmrc (if it exists)  
  149. # User Customization: $HOME/.dt/user.dtwmrc (if it exists)  
  150. #  
  151. ###  
  152.   
  153. INCLUDE  
  154. {  
  155.     $HOME/.dt/$LANG/wsmenu.dtwmrc  
  156.     $HOME/.dt/user.dtwmrc  
  157. }  

If you want to give user's special dtwmrc files based on some criteria (such as their group), you can use an executable file in the "/etc/dt/config/Xsession.d" directory. All scripts in this directory will be executed upon each login.

Here is a sample Xsession.d, which creates the user's home directory and copies in an admin dtwmrc if applicable. If they are not an admin, they will be using the default dtwmrc at /etc/dt/config/C/sys.dtwmrc.
  1. DTWMRC=/path/to/admin/dtwmrc  
  2. HOME_BASE=`/usr/bin/dirname ${HOME}`  
  3. HOME_DIR_OWNER=root  
  4. HOME_DIR_GROUP=bin  
  5. HOME_DIR_PERMS=drwxr-xr-x  
  6. SET_PERMS=755  
  7. ADMIN_GROUP=adm  
  8.   
  9. # If the directory doesn't exist, create it  
  10.   
  11. if [ ! -d ${HOME} ]  
  12. then  
  13.    /usr/bin/mkdir -p ${HOME}  
  14. fi  
  15.   
  16. # if the directory permissions are not correct, change them  
  17. D_PERMS=`/usr/bin/ls -ld ${HOME_BASE}/${USER} | /usr/bin/awk ' { print $1 } '`  
  18. D_OWNER=`/usr/bin/ls -ld ${HOME_BASE}/${USER} | /usr/bin/awk ' { print $3 } '`  
  19. D_GROUP=`/usr/bin/ls -ld ${HOME_BASE}/${USER} | /usr/bin/awk ' { print $4 } '`  
  20.   
  21. if [ "${D_OWNER}" != "${HOME_DIR_OWNER}" ] || \  
  22.    [ "${D_GROUP}" != "${HOME_DIR_GROUP}" ] || \  
  23.    [ "${D_PERMS}" != "${HOME_DIR_PERMS}" ]  
  24. then  
  25.    /usr/bin/chmod -R ${SET_PERMS} ${HOME_BASE}/$USER  
  26.    /usr/bin/chown -R ${HOME_DIR_OWNER}:${HOME_DIR_GROUP} ${HOME_BASE}/$USER  
  27. fi  
  28.   
  29. # if the user is an administrator, copy the admin workspace menu  
  30. USER_GROUP=`/usr/bin/groups $USER | /usr/bin/awk ' { print $1 } '`  
  31. if [ "${USER_GROUP}" = "${ADMIN_GROUP}" ]  
  32. then  
  33.    if [ ! -d ${HOME}/.dt ]  
  34.    then  
  35.       /usr/bin/mkdir ${HOME}/.dt  
  36.    fi  
  37.   
  38.    if [ ! -f ${HOME}/.dt/dtwmrc ]  
  39.    then  
  40.       /usr/bin/cp ${DTWMRC} ${HOME}/.dt/dtwmrc  
  41.    fi  
  42.   
  43.    /usr/bin/cp /opt/CONET/wm/dtwmrc.sysadm \  
  44.       /etc/security/tsol/home/sysadm/.dt/dtwmrc  
  45. fi  

Thursday, February 23, 2012

SSO

The following write up covers the results of some research I did for work regard SSO. The purpose was to give a brief high level report of what I had been investigating. It was not meant to cover all technologies investigated or implementation strategies in depth. I thought it might be of some use. This study was done in January of 2008.

SAML SSO Solution

SAML (Security Assertion Markup Language) is an XML-based standard for exchanging authentication and authorization data between security domains. SAML is used to try to solve the Web Browser Single Sign-On (SSO) problem, which is finding a way to allow a user to use several different services without needing to log in several different times with different usernames and passwords. The main componenents of a SAML solution are a service provider, which provides SAML assertions, and an identity provider, which consumes SAML assertions. The Service Provider and Identity Provider are configured to know that they can trust each other.

A SAML Assertion contains:
  •  A packet of security information consisting of header information. The header contains the name of the identity provider and other information such as issue and expiration dates. 
  • The name of the subject.
  • One or more statements.  The two most important statement types are authentication statements and attribute statements.
A principal (often the user) must be enrolled with an identity provider. The identity provider is expected to provide local authentication services to the principal. The identity provider can use any method it wants for authentication, such as LDAP or Active Directory. The principal always communicates with the Service Provider to determine authentication. If the principal is not authenticated, the Service Provider will redirect the principal to the Identity Provider. The Identity Provider will log the principal in, commonly by displaying a log in page shared by all services. Once the Identity Provider has authenticated the principal, it will create an assertion that is provided to the Service Provider. The Service Provider will provide an artifact, which may be used to obtain the assertion, to the the principal. Of course, failure to authenticate the user will provide in failure to obtain access to the server. This allows Single Sign On Functionality, as any principal from any domain can authenticate themselves through Service Provider calls. A domain can check the status of a user by providing the SAML artifact and examining the resulting SAML assertion. More commonly, the Service Provider will redirect the principal to the service they requested after the Service Provider determines the principal is authenticated.

In addition to SAML, the Liberty ID-FF standard was investigated in addition to SAML. Liberty ID-FF is an extension to SAML version 1.0. Its primary difference is that it provides what Liberty called a circle of trust, which allowed policies to be defined to let domains know they can trust each other. SAML version 2.0 represents the convergence of Liberty ID-FF and other proprietary extensions, as well as early versions of SAML itself.

Sun Access Manager

Sun Access Manager was the first application I evaluated as a SAML Service Provider and Identity Provider. Tutorials proved to be difficult to follow and many supporting technologies were required to use Sun Access Manager for SAML purposes. Ultimately, I switched to the newest version of Sun Access Manager, which was OpenSSO. OpenSSO was not only being actively developed, but it already contained the supporting technologies Sun Access Manager had required for SAML.

OpenSSO

OpenSSO is Sun's open source access management and federation server platform. It is configured via an admin web interface and contains a rich Java API. The Java API defines communication between clients and OpenSSO. The Java API also contains interfaces so that developers can design plugins, or modules, to OpenSSO. The downside to OpenSSO is its complexity and flexibility. OpenSSO is so huge and does so much that it takes a lot of time to learn about it, despite only needing to do one small thing. OpenSSO is a combination of several different Sun Technologies and so has a very large amount of documentation. OpenSSO is also fairly new, although its underlying technologies are not, which make it difficult to find independent tutorials. Sun's OpenSSO documentation can be found at http://docs.sun.com/app/docs/coll/1767.1.

Authentication Service

The Identity Provider provides authentication. The identity provider to be used was OpenSSO, which provides authentication services through plugins. Three built-in plugins included LDAP, Active Directory, and OpenSSO's internal data store. There were many other plugins along with an interface to create custom authentication plugins. This provided confidence that OpenSSO could interact with any client's authentication service. LDAP, Active Directory, and OpenSSO's internal store were all investigated so that they could be used for flexibility demonstration purposes. Tutorials were found for all three services and all authentication services were set up successfully.

An SSO Solution

The identity provider will be OpenSSO. This will require an authentication service to be configured and populated. OpenSSO will also need to be configured as an identity provider. No plugins will need to be written unless the authentication service chosen to be used is not supported, which is an unlikely case.

The Service Provider can be either OpenSSO or a custom server. Creating a custom server would give a project more control, but would require writing code to parse SAML artifacts and assertions, as well as code to communicate with the Identity Provider. The SAML specification is not trivial, so this would be a lot of work to do correctly. It would probably be easier to use OpenSSO as the service provider. This would likely require writing OpenSSO plugins to circumvent certain redirections. This will also require a lot of OpenSSO configuration.

Code will also need to be written to retain artifacts, transport artifacts, and communicate with Service Provider to obtain assertions. Code will also need to be written to parse assertions, but this will most likely be accomplished through the OpenSSO API.

Weblinks

The following is a brief collection of web urls with information to help someone to implement the strategy described in the SSO Solution section.

A Good Overview of SAML: http://en.wikipedia.org/wiki/SAML
Directions to using OpenSSO as an IdP with Google as an SP, which will allows you to set up a working demonstration:
http://blogs.sun.com/wahmed/entry/how_to_federate_with_google
Directions to configuring OpenSSO as an SP: https://www.enterpriselab.ch/wiki/doku.php?id=public:opensso_sp_shib20_idp
OpenSSO SAML API: http://docs.sun.com/source/820-3739/index.html

Open SSO Token

Although the SAML approach is viable, it would take too much time to implement at this point in time. The introduction of a Service Provider and an Identity Provider would have added a significant amount of work, time spent debugging, and time spent spent documenting, especially in conjuncion with OpenSSO. A much less time consuming approach would use OpenSSO Tokens through the OpenSSO API. In this approach, only one OpenSSO instance is required. The OpenSSO instance will consume authentication requests and provide OpenSSO Tokens. It will also consume OpenSSO Tokens to provide OpenSSO assertions, which are collections of information about the principal whose authentication produces the OpenSSO token. Using the OpenSSO Token is a much simpler Single Sign On solution.

The downside is that SAML is a standard where the OpenSSO Token is not. However, using the OpenSSO Token still allows interaction with whichever authentication service a client is using. Furthermore, SAML functionality could be added given more time.

Sunday, February 19, 2012

Learning Programming

My advice to a friend who wanted to learn about programming:

Honestly, for beginning programming, the best thing to do is to think of something you want to make and then try to make it.  I started with websites.  Books can sometime take the fun away from learning and learning because of passion is really the best way to go.

But if you want a guide, I think eloquentjavascript is a pretty good, easy to get into, thorough, interactive beginners book.  It uses Javascript, which is generally used to make web pages interactive, but is a full language on its own.  There's a bar at the bottom of the page you can click to open up a console, in which you can type and execute code.  The book is pretty easy to understand, yet covers the basics pretty well.  I would probably recommend this book the most: http://eloquentjavascript.net/contents.html

I read through this book on Ruby (which is recently popular for building the back end of websites) before and I liked it a lot.  Ruby is a very fun language to use and this is one of the more straightforward beginner's guides out there for any language: http://www.ruby-doc.org/docs/ProgrammingRuby/html/index.html

The book I mentioned when you came up is K&R's C Programming Language.  It covers C, which is an important language mostly used today for applications that require excellent performance.  The book is very, very good, but it's a lot more difficult to work through because of the nature of C and also the difficulty of getting C programs to run on your machine.  Because it's C, you get into the nitty-gritty of things kind of too early. This is a great book and more important than the ones above, but it's probably best to work through at least one of the two books I listed above first: http://www.iu.hio.no/~mark/CTutorial/CTutorial.html

The language you start out with doesn't matter much.  You can think of Programming Languages like web browsers... they all really do the same thing, but in slightly different ways.  You can also think of languages like tools, you use different ones depending on the job you want to do.  Sure you can use a hammer to get a screw in, but it's not the best way of doing it.  Which language you start out with doesn't matter, but you might make your choice depending on what type of thing you want to program.  Javascript/HTML/CSS/Flash for front end websites, Ruby/Perl/Python/Php for back end websites, C/C++ for high performance applications, Java for business applications, Scala/Haskell/Lisp/Schema for algorithmically complex languages.

Good luck learning.  Maybe read two chapters from each book and decide which path you want to take.


P.S:  As for my favorite beginner's guide... probably this one: http://mislav.uniqpath.com/poignant-guide/.  It's probably way too absurd and time consuming to be an actual good programming guide, but it's certainly fun.

Monday, January 23, 2012

Extended Cron

I wrote a Reminder/Notification program for Windows called Reminder 19 (you can check it out at http://reminder.relic19.net.)  I wanted to be able to cleanly define many different kinds of alert schedules and decided to use cron as my starting point.  I ended up extending the format because I wanted to have things like time delayed alerts and a year field.  This is the format I came up with... I think it's pretty expressive.  The only way I think it would be more expressive is if I added conditional alerts (for example, an alert should be shown on the 31st if the month has 31 days, otherwise it should be shown on the 28th).

Format 1: Advanced Schedule By Dates and Times
  +------------------ minute (0 - 59)
  |  +--------------- hour (0 - 23)
  |  |  +------------ day of month (0 - 31)
  |  |  |  +--------- month (1 - 12)
  |  |  |  |  +------ year (number between 1700 and 9999)
  |  |  |  |  |  +--- dayOfWeek (0 - 7, 10-17, 20-27, 30-37, 40-47)
  |  |  |  |  |  |  + optional command and parameters
  *  *  *  *  *  *  *

Each of the patterns from the first six fields may be either a single in range number, * (the asterisk character, which means the field matches all legal values), or a list of in range numbers separated by commas (such as 2,3). There may be no spaces following commas and fields may not end with a comma.  There is also an optional special seventh field.

The hour field (field 1) is specified using the 24-hour format.  0 is 12 AM, 4 is 4 AM, and 15 is 3 PM.

The month field (field 4) is defined as a number, where 1 is January and 11 is November.  Alerts will not be shown for non existent days, such as September 31.  Please note that an alert will not show on September 30 instead.

For the day of the week field (field 6), both 0 and 7 are considered Sunday.  The dayOfWeek field can be two digits, where the first digit represents its position in the month and the second digit is the day of the week.  For example, 31 would mean the third Monday of every month.  A position of 0 means every postion, so 03 means every Wednesday.  In deciding the next wake up time between, for example, the 1st and 31st, the soonest wake up date is chosen.

An alert is shown when the time/date specification fields all match the current time and date. There is one exception: if both "day of month" and "day of week" are restricted (not "*"), then either the "day of month" field (field 3) or the "day of week" field (field 6) must match the current day (even though the other of the two fields need not match the current day).

The command field (field 7) is completely optional.  If it is present, then an attempt will be made to execute the command.

Example 1:
00 16 1,2,31 2,3 2008 1,45
This alert specifies the following date and times:
February 1, 2008 at 4:00 PM
February 2, 2008 at 4:00 PM
February 4, 2008 at 4:00 PM (A Monday)
February 11, 2008 at 4:00 PM (A Monday)
February 18, 2008 at 4:00 PM (A Monday)
February 22, 2008 at 4:00 PM (The fourth Friday)
February 25, 2008 at 4:00 PM (A Monday)
March 1, 2008 at 4:00 PM
March 2, 2008 at 4:00 PM
March 3, 2008 at 4:00 PM (A Monday)
March 10, 2008 at 4:00 PM (A Monday)
March 17, 2008 at 4:00 PM (A Monday)
March 24, 2008 at 4:00 PM (A Monday)
March 28, 2008 at 4:00 PM (The fourth Friday)
March 31, 2008 at 4:00 PM (The 31st and a Monday)

Example 2:
00 4,16 * * * * "C:\Program Files\Internet Explorer\IEXPLORE.EXE" ? http://www.google.com
Every day at 4 AM and 4 PM, an instance of Internet Explorer will be launched that navigates to http://www.google.com.


Format 2: Advanced Schedules by Time Delays
  +------------------ the start minute (0 - 59)
  |  +--------------- the start hour (0 - 23)
  |  |  +------------ the start day of month (0 - 31)
  |  |  |  +--------- the start month (1 - 12)
  |  |  |  |  +------ the start year (number between 1700 and 9999)
  |  |  |  |  |  +--- increment in minutes (+[number of minutes to delay])
  |  |  |  |  |  |  + optional command and parameters
  * * * * *  *  *

No field in this format may be * (an asterisk character.)  Field 7 (the command field) is optional, but all other fields must be defined.

Each of the first 5 fields must be a single number in the specified ranges.  They will be used to define the start date.

The hour field is specified using the 24-hour format.  0 is 12 AM, 4 is 4 AM, and 15 is 3 PM.

The months are defined as number, where 1 is January and 11 is November.

For "increment in minutes" field (field 6), a non-negative number must be specified, prefixed by a '+' character.  The number represents the number of minutes to wait between alerts.  For example, +59 means that the alert will be triggered every 59 minutes after the start date.

The command field (field 7) is completely optional.  If it is present, then an attempt will be made to execute the command.

Example 1:
00 00 31 3 2008 +30
This alert will be shown every 30 minutes after March 31, 2008 at 12:00 AM.  So the first alert will go off at March 31, 2008 at 12:30 AM, the next alert will go off at March 31, 2008 at 1:00 AM, and so on.

Example 2:
00 00 31 3 2008 +60 "C:\Program Files\Internet Explorer\IEXPLORE.EXE" ? http://www.google.com
This alert will be shown every 60 minutes (one hour) after March 31, 2008 at 12:00 AM.  So the first alert will go off at March 31, 2008 at 1:00 AM, the next alert will go off at March 31, 2008 at 2:00 AM, and so on.  Whenever an alert goes off, http://www.google.com will be launched in Internet Explorer.

Monday, May 02, 2011

Executing Java from VBScript

This post is simple, it's just some Visual Basic Script for executing a jar and keeping the text output in a variable:

  1. Set objShell = WScript.CreateObject("WScript.Shell")  
  2. Set objExecObject = objShell.Exec("java -classpath JavaCode.jar JavaClassName")  
  3.   
  4. Do Until objExecObject.StdOut.AtEndOfStream  
  5.  Results = Results + " " + objExecObject.StdOut.ReadLine()   
  6. Loop  

Sunday, October 31, 2010

C# Web Service Plain Text Response

It took me far longer than I expected in order to figure out how to return plain text from a C# web service. I'm not sure why this took so long for me to figure out, but if it wasn't just me, then maybe this post can help other people out.

The Problem: When writing a web service endpoint in C#, if the return type is a String, the result will be an XML formatted string.

For example, the following web service:
  1. public String TestServiceA(Stream input)
  2. {
  3. return "Some kind of Text";
  4. }
returns the following string:
  1. <string>Some kind of Text</string>
It isn't that big a deal and the contained text can be easily parsed out, but it wasn't what I wanted and extra parsing code somehow seems wrong. I wanted the result to simply be the text I set.

The Solution: The return type needs to be a Stream.

So now the following web service:
  1. public Stream TestServiceB(Stream input)
  2. {
  3. MemoryStream ms = new MemoryStream();
  4. StreamWriter sw = new StreamWriter(ms);
  5. sw.Write("Some kind of Text");
  6. sw.Flush();
  7. ms.Position = 0;
  8. return ms;
  9. }
returns the expected result:
  1. Some kind of Text

Thursday, July 08, 2010

Subversion Intro

I've been using subversion (nope, not a git guy yet) for most of my own work, even got our projects at my current job onto subversion. It's much better than source safe/offsite (which I used at a previous job) and I even prefer it to CVS. Subversion is designed specifically to be a modern replacement for CVS and shares a number of the same key developers. It uses a copy-modify-merge for file sharing. To read more about subversion, you can read their very good documentation at http://svnbook.red-bean.com/. The following is a link to an appendix from the svnbook that introduces subversion for cvs users. I think that it will be a very helpful and painless way to quickly understand subversion with a cvs background. You can read it starting at http://svnbook.red-bean.com/en/1.2/svn.forcvs.html.

Subversion’s homepage is http://subversion.tigris.org/. I install it using the instructions found on http://www.excastle.com/blog/archive/2005/05/31/1048.aspx.

The most direct way of using subversion is through the subversion client tool called svn. You can download the tool from subversion’s site, http://subversion.tigris.org/. For directions on using the tool, refer to the appropriate section at http://svnbook.red-bean.com/.

Another subversion client option for Win32 users is TortoiseSVN, which is implemented as a Windows shell extension. TortoiseSVN can be downloaded from http://tortoisesvn.tigris.org/. It is very simple and intuitve to use as all commands are added to the right click context menu when using windows explorer. Special icons are used to show which files and folders are under revision control.

If you're using Eclipse, there is a plugin called SubClipse that will integrate Eclipse and Subversion. You can download the plug-in from http://subclipse.tigris.org/. A good guide to installation and usage for the plug-in can be found at http://dev2dev.bea.com/pub/a/2005/11/subclipse.html?page=1.