Tuesday, July 10, 2012

CAS - Restlet Access

Today we had to do another modification to the standard CAS login system. As you might know form my older posts we had to add the possibility of using more than one LDAP in our system and some other modifications in order to view the various login pages depending on where you're coming from.

Exactly this changes need to be considered whenever you have to add some more functionality to your CAS system. like the "Remember-Me" function we introduced lately or in this case as we need to offer login via REST calls.


Despite that, the standard solution would work out of the box without the changes discussed above, we cannot do without them, so we had to propagate our changes through the REST plugin. This means that we had to add a new parameter to the REST login call.

POST /cas/v1/tickets HTTP/1.0
username=battags&password=password&domain=domain1&additionalParam1=paramvalue

This call needs to be parsed correctly and its values need to be stored in our custom credentials class (DomainUsernamePasswordCredentials or RememberMeDomainUsernamePasswordCredentials) which are able to hold the value for domain and if activated, the "RememberMe" option.

Fortunately adding support for this can be done by changing just a single class, which is the TicketResource. By default this class creates an Instance of  "UsernamePasswordCredentials", which needs to be replaced by one of the custom classes mentioned above.
package org.jasig.cas.integration.restlet;


import org.jasig.cas.authentication.principal.Credentials;
import org.jasig.cas.authentication.principal.RememberMeDomainUsernamePasswordCredentials;
import org.jasig.cas.authentication.principal.UsernamePasswordCredentials;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.support.WebRequestDataBinder;


public class DomainTicketResource extends TicketResource {


 private static final Logger log = LoggerFactory.getLogger(DomainTicketResource.class);


 @Override
 protected Credentials obtainCredentials() {
  final UsernamePasswordCredentials c = new RememberMeDomainUsernamePasswordCredentials();
  final WebRequestDataBinder binder = new WebRequestDataBinder(c);
  final RestletWebRequest webRequest = new RestletWebRequest(getRequest());
    binder.bind(webRequest);
  return c;
 }
}
And that's it basically. The only thing that's missing is to exchange the existing bean definition of TicketResource in restlet-servlet.xml with the definition of DomainTicketResource and it will work.

Tuesday, May 29, 2012

Apple TV Untethered Jailbreak iOS5.1.1 (5.0.1)

Following the instructions from my former post  its now possible to jailbreak the ATV2 running the latest version of iOS (5.1.1) .
Basically its still the same tool, provided by firecore that does the jailbreaking. The instructions to install the nitoTV software center are also still valid, however the XBMC installation procedure is a little more difficult because the version provided by nitoTV is not yet compatible with the new iOS.

Fortunately the actual nightly build is quite stable and works as expected. To install it you need to connect to the ATV via SSH as you've done to install nitoTV and issue the following list of commands.

apt-get install wget 
wget -O- http://apt.awkwardtv.org/awkwardtv.pub | apt-key add -
echo "deb http://apt.awkwardtv.org/ stable main" > /etc/apt/sources.list.d/awkwardtv.list
echo "deb http://mirrors.xbmc.org/apt/atv2 ./" > /etc/apt/sources.list.d/xbmc.list
apt-get update
mkdir -p /Applications/AppleTV.app/Appliances
apt-get install org.xbmc.xbmc-atv2
mkdir -p /Applications/XBMC.frappliance
wget http://mirrors.xbmc.org/apt/atv2/deb/org.xbmc.xbmc-atv2_11.0-3_iphoneos-arm.deb
dpkg -i org.xbmc.xbmc-atv2_11.0-3_iphoneos-arm.deb
rm org.xbmc.xbmc-atv2_11.0-3_iphoneos-arm.deb
apt-get install com.nito.nitotv
apt-get install com.nito.updatebegone
reboot

And that's it again. On the ATV's main screen there should be the well know XBMC-icon which starts your new media center.

Tuesday, March 20, 2012

CAS - authentication vs. principal resolver mismatch

Lately we found an annoying bug in our CAS login system. If you read already some of my older posts you might know that we had to login against multiple domains in our system which resulted in a complex configuration in our deployerConfigContext.xml.

A sample of that is shown here:

<!-- AUTHENTICATION MANAGERS -->
<bean id="authenticationManagerFirst" class="org.jasig.cas.authentication.AuthenticationManagerImpl">
<property name="credentialsToPrincipalResolvers">
<list>
<ref bean="credentialsToPrincipalResolverFirst" />
<ref bean="credentialsToPrincipalResolverSecond" />
<bean
class="org.jasig.cas.authentication.principal.UsernamePasswordCredentialsToPrincipalResolver"
p:attributeRepository-ref="attributeRepositoryJdbc" />
<bean
class="org.jasig.cas.authentication.principal.HttpBasedServiceCredentialsToPrincipalResolver" />
</list>
</property>
<property name="authenticationHandlers">
<list>
<ref bean="authenticationHandlerFirst" />
<ref bean="authenticationHandlerSecond" />
<ref bean="authenticationHandlerJdbc" />
<bean
class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler"
p:httpClient-ref="httpClient" />
</list>
</property>
</bean>

In our opinion this would work like this, that it goes through the list of authentication handlers looking for the first one that could authenticate the users credentials. Lets say it was found in authenticationHandlerSecond it would go and look for the right principal resolver to get all the needed attributes to append to the response.

Unfortunately the later step does not know which authentication handler was used in first step, so it starts over to go through the list until it finds a resolver that knows about the username that wants to get access. In this step there is no more password involved so it could be that if the first principal resolver knows about the same username and returns that parameters (even though the password is not the same!). Furthermore if the principal belongs to another user you'll be logged in as that one. Quite bad!

Fortunately the solution is quite simple. Instead of using the standard AuthenticationManagerImpl we create our own manager class like the following

public final class DomainAuthenticationManagerImpl extends AbstractAuthenticationManager {
/** An array of authentication handlers. */
@NotNull
@Size(min = 1)
private List authenticationHandlers;
/** An array of CredentialsToPrincipalResolvers. */
@NotNull
@Size(min = 1)
private List credentialsToPrincipalResolvers;
@Override
protected Pair authenticateAndObtainPrincipal(final Credentials credentials) throws AuthenticationException {
 

This one is basically a copy of the original AuthenticationManagerImpl just having the following changes

// save the position in which the authenticationHandler was found
int authenticationHandlerPosition = 0;
for (final AuthenticationHandler authenticationHandler : this.authenticationHandlers) {
if (authenticationHandler.supports(credentials)) {
[..]
}
authenticationHandlerPosition++;
}
[..]
if (!authenticated) {
if (foundSupported) {
throw BadCredentialsAuthenticationException.ERROR;
}
throw UnsupportedCredentialsException.ERROR;
}
// Not check in every CredentialsToPrincipalResolver, but only in the one on the same position in the list where the authenticationHandlerhas been found above
// for (final CredentialsToPrincipalResolver credentialsToPrincipalResolver : this.credentialsToPrincipalResolvers) {
if (authenticationHandlerPosition >= 0
&& authenticationHandlerPosition < this.credentialsToPrincipalResolvers.size()) {
CredentialsToPrincipalResolver credentialsToPrincipalResolver = this.credentialsToPrincipalResolvers
.get(authenticationHandlerPosition);
[..]

With this small changes we can remember on which position the authentication handler was found and just test with the resolver on the same position in the list of resolvers.

Wednesday, February 22, 2012

Convincing IE to not remove window.location.hash on a redirect

Our system has to work well with the most used browsers. This is usually not a big deal as longas you don't check your page on IE. Unfortunately it is still among the most used ones, so any serious web page has to consider it.

Lately we faced the problem, that whenever spring security has decided that the login is not valid anymore, and redirected the user to the login page the query string and the hash are removed (only in IE) and the user looses the reference to the page he/she was before.

There are two solutions to this problem. The first is just a parameter in the authentication entry point of your website

<bean id="authenticationEntryPoint"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint" >
<constructor-arg name="loginFormUrl" value="/login.jsf" / >
<property name="useForward" value="true"/ >
</bean>

Forwarding instead of redirecting leaves the URL unchanged when the page displays the login page, so IE has no change to remove the parameters. Unfortunately this solutions shows the login page probably on every page of your website. If the user uses a password inserting tool based on URLs this will not work anymore, and of course it is not transparent for the user to enter its credentials once on the login page and than on another page.

The second solution I found on the Internet (so its not really my Idea). Basically it consists of adding a new filter in the filter chain on first position
<custom-filter ref="retainAnchorFilter" position="FIRST" />
This filter is than defined like this

<bean id="retainAnchorFilter" class="it.unibz.ict.utils.RetainAnchorFilter">
 <constructor-arg name="storeUrlPattern" value="${local.url}/login.*" />
<constructor-arg name="restoreUrlPattern" value=".*/${local.appname}/.*" />
<constructor-arg name="cookieName" value="TARGETANCHOR" />
</bean>
This filter will than store the hash when the URL matches the storeUrlPattern and restore it on the restoreUrlPattern. Actually we don't use the restore feature, because on the login page we read the parameters into a hidden field and send it using the normal form submission which fits better into our architecture, but for this sample I preferred to have it complete.

public class RetainAnchorFilter extends GenericFilterBean {
private final String storeUrlPattern;
private final String restoreUrlPattern;
private final String cookieName;
public RetainAnchorFilter(String storeUrlPattern, String restoreUrlPattern, String cookieName) {
this.storeUrlPattern = storeUrlPattern;
this.restoreUrlPattern = restoreUrlPattern;
this.cookieName = cookieName;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
ServletException {
if (response instanceof HttpServletResponse) {
response = new RedirectResponseWrapper((HttpServletResponse) response);
}
chain.doFilter(request, response);
}
/**
* HttpServletResponseWrapper that replaces the redirect by appropriate Javascript code.
*/
private class RedirectResponseWrapper extends HttpServletResponseWrapper {
public RedirectResponseWrapper(HttpServletResponse response) {
super(response);
}
@Override
public void sendRedirect(String location) throws IOException {
HttpServletResponse response = (HttpServletResponse) getResponse();
String redirectPageHtml = "";
if (location.matches(storeUrlPattern)) {
redirectPageHtml = generateStoreAnchorRedirectPageHtml(location);
} else if (location.matches(restoreUrlPattern)) {
redirectPageHtml = generateRestoreAnchorRedirectPageHtml(location);
} else {
super.sendRedirect(location);
return;
}
response.setContentType("text/html;charset=UTF-8");
response.setContentLength(redirectPageHtml.length());
response.getWriter().write(redirectPageHtml);
}
private String generateStoreAnchorRedirectPageHtml(String location) {
StringBuilder sb = new StringBuilder();
sb.append("<html><head><title>Redirect Page</title>\n");
sb.append("<script type=\"text/javascript\">\n");
// store anchor
sb.append("document.cookie = '" + cookieName + "=' + window.location.hash + '; path=/';\n");
// redirect
sb.append("window.location = '" + location + "' + window.location.hash;\n");
sb.append("</script>\n</head>\n");
sb.append("<body><h1>Redirect Page (Store Anchor)</h1>\n");
sb.append("Should redirect to " + location + "\n");
sb.append("</body></html>\n");
return sb.toString();
}
@SuppressWarnings("unused")
private String generateRestoreAnchorRedirectPageHtml(String location) {
StringBuilder sb = new StringBuilder();
sb.append("<html><head><title>Redirect Page</title>\n");
sb.append("<script type=\"text/javascript\">\n");
// generic Javascript function to get cookie value
sb.append("function getCookie(name) {\n");
sb.append("var cookies = document.cookie;\n");
sb.append("if (cookies.indexOf(name + '=') != -1) {\n");
sb.append("var startpos = cookies.indexOf(name)+name.length+1;\n");
sb.append("var endpos = cookies.indexOf(\";\",startpos)-1;\n");
sb.append("if (endpos == -2) endpos = cookies.length;\n");
sb.append("return unescape(cookies.substring(startpos,endpos));\n");
sb.append("} else {\n");
sb.append("return false;\n");
sb.append("}}\n");
// get anchor from cookie
sb.append("var targetAnchor = getCookie('" + cookieName + "');\n");
// append to URL and redirect
sb.append("if (targetAnchor) {\n");
sb.append("window.location = '" + location + "' + targetAnchor;\n");
sb.append("} else {\n");
sb.append("window.location = '" + location + "';\n");
sb.append("}\n");
sb.append("</script></head>\n");
sb.append("<body><h1>Redirect Page (Restore Anchor)</h1>\n");
sb.append("Should redirect to " + location + "\n");
sb.append("</body></html>\n");
return sb.toString();
}
}
}

The latter is for sure the better solution regarding transparency and in our case necessary because we have to redirect to CAS which would not be possible with the former.


Friday, February 3, 2012

CentOS: Bind multiple IP-Adresses to a Network Interface

First thing is to define a single IP address for your host.

1) CentOS(RedHat) uses the file /etc/sysconfig/network to read the saved hostname at system boot. This is set using the init script /etc/rc.d/rc.sysinit
GATEWAY=192.168.0.254
HOSTNAME=www.mydomain.it
NETWORKING=yes
FORWARD_IPV6=yes
2) We define the nameserver for this system /etc/resolv.conf
search mydomain.it
nameserver 10.10.14.1
nameserver 10.10.14.2
3) Locally resolve node names to IP addresses /etc/hosts
127.0.0.1       localhost.localdomain   localhost       name        name.mydomain.it
::1             localhost6.localdomain6 localhost6      name        name.mydomain.it

16.18.24.241     name.mydomain.it          name
16.18.24.244     name.mydomain.it          name
4) Define the first ethernet connector /etc/sysconfig/network-scripts/ifcfg-eth0
DEVICE=eth0
ONBOOT=yes
USERCTL=no
BOOTPROTO=none
NETMASK=255.255.255.128
IPADDR=192.168.0.241
PEERDNS=no

check_link_down() {
    return 1;
}
TYPE=Ethernet
IPV6INIT=no
5) Copy this configuration file to a virtual one, which could be for example eth0:0
cp ifcfg-eth0 ifcfg-eth0:0
6) And than change the settings in the newly created file /etc/sysconfig/network-scripts/ifcfg-eth0:0
DEVICE=eth0:0
ONBOOT=yes
USERCTL=no
BOOTPROTO=none
NETMASK=255.255.255.128
IPADDR=192.168.0.242
PEERDNS=no

check_link_down() {
    return 1;
}
TYPE=Ethernet
IPV6INIT=no
7) Restart the network to put it to work
service network restart

Tuesday, January 24, 2012

Single Sign On with CAS

There are a lot of different solutions in the market to solve this problem under which KErberos, OpenID, OAuth and of course JA-SIG CAS (Central Authentication Service)

The latter is used mainly in University environments, however it can be used in a lot of different environments as there exist integration for Java, PHP, ...

The following picture in my opinion shows best how the transitions between the three actors are defined


It all starts with a request from a user that wants to access a page on an application server (black arrow). As it is not yet known by the application the browser gets redirected to the CAS-server where the App.Server adds its service-id (usually the url of the web application). (red arrows)

this results in an URL like https://www.cas-server.xyz/cas/login?service=http://www.application-server.xyz/webapp

On this site the login screen of the CAS-server is shown and the user has to enter its credentials. The CAS-server generates the Ticket-Granting-Ticket (and a Cookie). The TGT is then sent back to the application (blue arrows).

Using this TGT the application cas contact the CAS-server to obtain a Service Ticket, which contains attributes and ids which are needed to authorise the user within the application (green arrows).

Ususally the ST is valid only for a single request but using the TGT the Application server can create multiple STs as long as the TGT is valid

Once authenticated at the CAS server the step of entering the credentials will be omitted (otherwise it wouldn't be SSO

Wednesday, January 18, 2012

Wireless Client (Bridge)

If you go into a TV-store these days you'll recognize that most of the newer models are equipped with some sort of "Smart-TV" facility. To use this feature you'll need to connect it to the internet which is possible via LAN or WLAN. The latter in my case was only possible by buying an original WLAN-adapter costing more than 50€.

Unfortunately at my home there is no possibility to use the LAN without restructuring the whole house and the adapter despite the high price is not really an option because it would be the third device (TV, Receiver, Apple-TV) in that furniture that would be wireless connected.

The possible solutions that came in my mind where two (three if opening the walls for new cables would be an option ;-)
Despite that powerline would be a very simple option without polluting the air with more electromagnetic waves it has some drawbacks. It depends highly on an uninterrupted connection between the sender and receiver, it is limited by the capabilities of the power line and its components are still very expensive.

The wireless bridge instead bundles all the different wireless connections into one and shares it through the wireless ports which is supported by most of the cheapest routers. Therefore it was the favorite choice to solve my problem.

After some investigation I found out that for example the "TP-Link TL-WR841N" can be configured as a wireless bridge and it was available below 35€

The advantage of this router is that it is compatible with "DD-WRT" which is an open source linux based operating system for routers, switches and so on. So after a short functioning test with the original firmware I just flashed it with this one (I think this change is reversible, but I'm not 100% sure)

The settings to change are described best in this article, so I'm not going to repeat them.

The whole setup took less than half an hour and is doable even for beginners, so unleash you router

UPDATE:
Actually the problem with this configuration is, that the devices in room2 do not see the devices in room1 and vice versa. This can be fixed with static routes.
And the real client bridge is not possible yet with this router

Golang setup PATH

Quite recently we startet in the company to use and write some Go programs. I love Go. It's easy to learn, read and modify. One of the m...