Friday, December 13, 2013

Tynamo tapestry-security 0.5.1 and UNAUTHORIZED_URL bug

The Tynamo tapestry security 0.5.1 allows us to configure the URL path when unauthorized access happens. But it only works when we use annotation @RequiresRoles for each page, while it won't if we use Shiro createChain for global configuration, like
 configuration.add(factory.createChain("/admin/**").add(factory.roles(),User.Role.admin.name()).build());  
, the app displays:

HTTP ERROR 401
Problem accessing /admin/home. Reason:
Unauthorized
------------------------------------------------------------------------
/Powered by Jetty:///

instead of configured URL page.

The reason is the org.tynamo.security.shiro.authz.AuthorizationFilter.
getUnauthorizedUrl() overwrites the org.tynamo.security.shiro.AccessControlFilter.getUnauthorizedUrl() where the configured UNAUTHORIZED_URL passed in. The AuthorizationFilter.
getUnauthorizedUrl() returns value from class private variable unauthorizedUrl, so it's always null.

To fix it, just patch AuthorizationFilter by removing unauthorizedUrl, getUnauthorizedUrl(), setUnauthorizedUrl(.) as this.

Tuesday, December 10, 2013

Apache Shiro - Remember me and SerializationException: Unable to deserialze argument byte array

When implementing Tynamo tapestry security 0.5.1, I got exception when using Remember me

[WARN] mgt.DefaultSecurityManager Delegate RememberMeManager instance of type [$RememberMeManager_133e98f5ddc26344] threw an exception during getRememberedPrincipals().
org.apache.shiro.io.SerializationException: Unable to deserialze argument byte array.
at org.apache.shiro.io.DefaultSerializer.deserialize(DefaultSerializer.java:82)
at org.apache.shiro.mgt.AbstractRememberMeManager.deserialize(AbstractRememberMeManager.java:514)
at org.apache.shiro.mgt.AbstractRememberMeManager.convertBytesToPrincipals(AbstractRememberMeManager.java:431)

Caused by: java.io.StreamCorruptedException: invalid type code: EE
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1353)
at java.io.ObjectInputStream.readArray(ObjectInputStream.java:1639)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1320)
at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1950)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1874)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1756)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1326)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:348)
at java.util.HashSet.readObject(HashSet.java:291)
...
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:348)
at org.apache.shiro.io.DefaultSerializer.deserialize(DefaultSerializer.java:77)

The Tynamo tapestry security 0.5.1 is based on Apache Shiro 1.2.0

Google around and found it was resolved for 1.2.0 release. That's interesting.

So I tried to debug the ClassResolvingObjectInputStream and ClassUtils and still didn't know the reason why the ClassUtils.forName(..) can't load byte array class. Finally google around for deserialize byte array exception and got JDK-6500212 : (cl) ClassLoader.loadClass() fails when byte array is passed resolved since 2007!

I'm running the app with 1.6.0.jdk on MacOS X so that might cause the problem.

So I patched the ClassUtils as below:

   public static Class forName(String fqcn) throws UnknownClassException {  
     .....  
     if (clazz == null) {//Luan - patch for byte array class (class name =[B - ref http://bugs.sun.com/view_bug.do?bug_id=6500212  
          try {  
                     clazz = Class.forName(fqcn,false,Thread.currentThread().getContextClassLoader());  
                } catch (ClassNotFoundException e) {  
                     if (log.isTraceEnabled()) {  
              log.trace("Unable to load class named [" + fqcn + "] from the current thread context.");  
            }  
                }  
     }  
     if (clazz == null) {  
       String msg = "Unable to load class named [" + fqcn + "] from the thread context, current, or " +  
           "system/application ClassLoaders. All heuristics have been exhausted. Class could not be found.";  
       throw new UnknownClassException(msg);  
     }  
     return clazz;  
   }  

and it worked!


Friday, November 22, 2013

Tapestry 5 https, Tomcat and load balancer plain HTTP proxy

We usually place Tapestry 5 apps running on Tomcat behind an Apache HTTPD with mod_jk / mod_proxy_ajp or mod_proxy_http.

Request protocols are plain HTTP or secure HTTPS from users to the Apache and same thing from Apache to Tomcat. Tapestry 5 auto-detects requests protocol and creates suitable URLs for further calling back actions for the application.

Below are 2 examples:
1. Browsers --http--> Apache --http--> Tomcat --Tapestry 5--http --> Apache --http--> Browsers ...
2. Browsers --https--> Apache --https--> Tomcat --Tapestry 5--https --> Apache --https--> Browsers ...

When we deploy an app required to access via https to load balancers connect to Tomcat in plain http proxy as:
3. Browsers --https--> Load balancers --http--> Tomcat--Tapestry 5 --http--> Load balancers --https --> Browsers ...

Then we get into the browser error “Blocked loading mixed active content” when loading an https page and mix some other http requests in background via AJAX within a page.

The reason is the Tomcat always got http requests from load balancers, so Tapestry 5 builds base URLs in http protocol.

Tapestry 5 allows us to override how these default URLs are created by the BaseURLSource service.

But that's not a good way to go with because we have to decide what protocol should be returned for each deploy environment in code...

Google around and found these posts:

Teaching Tapestry to use network path references
[T5]: BUG: Proxy Situation: Tapestry 5.3.3 Not Respecting isSecure for Form Action URL
Problem pushing application to production

The last one is very helpful when Kalle said about Tomcat connector configuration with proxyPort..

Study more about it and found a solution:

Tomcat http connector should be reconfigured as:
<Connector port="8080" proxyPort="443" secure="true" scheme="https"
protocol="HTTP/1.1" connectionTimeout="20000" URIEncoding="UTF-8" redirectPort="8443"/>

And http proxy in Apache/load balancer:
ProxyPreserveHost On
ProxyPass /T5app http://tomcat:8080/T5app
ProxyPassReverse /T5app http://tomcat:8080/T5app

So the Apache/load balancer handles https come from browsers, but makes http proxy to Tomcat and the Tomcat returns secure https/443 when Tapestry needs to build base URLs.

App web.xml
<context-param>
    <param-name>tapestry.production-mode</param-name>
    <param-value>true</param-value>
</context-param>
<context-param>
    <param-name>tapestry.secure-enabled</param-name>
    <param-value>false</param-value>
</context-param>










Saturday, November 16, 2013

Ehcache RMI Replicated Caching in cloud environments

The RMI Ehcache provides 2 ways for peer discovery: Automatic Peer Discovery and Manual Peer Discovery.

If the application is deployed in an environment where multicast is not allowed or not available like Amazon AWS or FireHost clouds, then we must use Manual Peer Discovery.

Follow the guide, configure Manual Peer Discovery as:

Configuration for server1
<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
properties="peerDiscovery=manual,
rmiUrls=//server2:40001/sampleCache11|//server2:40001/sampleCache12"/>

Configuration for server2

<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
properties="peerDiscovery=manual,
rmiUrls=//server1:40001/sampleCache11|//server1:40001/sampleCache12"/>

Configuring the CacheManagerPeerListener as:
<cacheManagerPeerListenerFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
properties="hostName=server1, port=40001,
socketTimeoutMillis=2000"/>

<cacheManagerPeerListenerFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
properties="hostName=server2, port=40001,
socketTimeoutMillis=2000"/>

So we need to open the firewall for incoming TCP port 40001 on both peers.

But then each peer server can't send message to other with the connection timed out error:

RMIAsynchronousCacheReplicator Unable to send message to remote peer.  Message was: Connection refused to host: server2; nested exception is:
    java.net.ConnectException: Connection timed out
java.rmi.ConnectException: Connection refused to host: server2; nested exception is:
    java.net.ConnectException: Connection timed out
    at sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:619)
    at sun.rmi.transport.tcp.TCPChannel.createConnection(TCPChannel.java:216)
    at sun.rmi.transport.tcp.TCPChannel.newConnection(TCPChannel.java:202)
    at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:128)
    at net.sf.ehcache.distribution.RMICachePeer_Stub.send(Unknown Source)
    at net.sf.ehcache.distribution.RMIAsynchronousCacheReplicator.writeReplicationQueue(RMIAsynchronousCacheReplicator.java:314)
    at net.sf.ehcache.distribution.RMIAsynchronousCacheReplicator.replicationThreadMain(RMIAsynchronousCacheReplicator.java:127)
    at net.sf.ehcache.distribution.RMIAsynchronousCacheReplicator.access$000(RMIAsynchronousCacheReplicator.java:58)
    at net.sf.ehcache.distribution.RMIAsynchronousCacheReplicator$ReplicationThread.run(RMIAsynchronousCacheReplicator.java:389)
Caused by: java.net.ConnectException: Connection timed out

Review the guide/google around... to find out the problem, but couldn't know why? Then tried to change the firewall to allow all ports and it worked!

So absolutely there was another port that was used by the Ehcache but blocked by the firewall.

Google around and found the answer here
"by default does use portmap and therefore random, non-root ports."

So we need to add remoteObjectPort to the cacheManagerPeerListenerFactory as:
<cacheManagerPeerListenerFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
properties="hostName=server1, port=40001, remoteObjectPort=40002,
socketTimeoutMillis=2000"/>

<cacheManagerPeerListenerFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
properties="hostName=server2, port=40001, remoteObjectPort=40002,
socketTimeoutMillis=2000"/>

And close all ports on the firewall except TCP 40001, 40002 between those peers.

Problem is resolved.


Friday, November 1, 2013

Multiple Tomcat instances on single server and session issue


When setting up 2 instances for Tomcat 6 on the same server, we got the issue of both are sharing sessions when both have same application context name. So if we logged into one instance, the session of the other will be expired.

The reason is cookie is stored by domain and cookie name, but not specified by server port. So both instances use the same JSESSIONID cookie name.

To fix it, we need to change the org.apache.catalina.SESSION_COOKIE_NAME parameter on java command.

We could do that easily with Tomcat bin/catalina.sh by adding that parameter to JAVA_OPTS:
JAVA_OPTS="$JAVA_OPTS -Dorg.apache.catalina.SESSION_COOKIE_NAME=JSESSIONID2"

Saturday, October 26, 2013

Install PostgreSQL 8.4 on OS X Mountain Lion

Because a developer needs to use PostgreSQL 8.4 on his new MacBook Pro running OS X Mountain Lion v10.8.3. So I downloaded and installed PostgreSQL as usual.

But when install PostgreSQL 8.4.18 it thrown an error:


Problem running post-install step.
Installation may not complete correctly
The database cluster initialization failed.

Looked into the /tmp/bitrock_installer.log, there's an error:


Preparing to Install
Executing su - postgres -c "\"/Library/PostgreSQL/9.1/bin/pg_ctl\" stop -m fast -w -D \"/Library/PostgreSQL/9.1/data\""
Script exit code: 1

Script output:

Script stderr:
 su: unknown login: postgres

That's weird why it didn't create necessary account.


So I tried to create the postgres user:
# sudo dscl . -create /users/postgres

Then checked it:
# id postgres
id: postgres: no such user

After google around, they said on Lion, the user _postgres has been precreated and PostgreSQL 9 installed.

So let's delete it:

# sudo dscl . -delete /Users/_postgres  

Then installed PostgreSQL 8.4 again, and it worked!

So let's check the psql command path:
# which psql
/usr/bin/psql

We need to change to the new place:
# vi ~/.bash_profile
export PATH=/Library/PostgreSQL/8.4/bin:$PATH

# source ~/.bash_profile

Then check it again
# which psql

Done!







Tuesday, October 22, 2013

Eclipse jee-europa and Run Jetty Run

Today I setup a new MacBook Pro for a new employee.

After setup Eclipse jee-europa and Run-Jetty-Run, then opened the Run Configurations to create new configuration under the Jetty Webapp, but it didn't create a new configuration as expected. Did nothing and no error at all. Tried with other kinds like Java Application, they worked.

Google around didn't help, so I looked back the Run-Jetty-Run site and tried the old version, then that worked.