Friday, April 29, 2011

Oracle SSO configuration with Alfresco

Hi,

I finally configured Oracle SSO with Alfresco. Below is how i achieved that.

I need to run Alfresco on tomcat deployed on other virtual terminal, i created a class file as under

-> STEP 1

package my.custom;

import java.io.IOException; 
import java.util.List;
import java.util.Locale;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.transaction.UserTransaction;

import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.security.authentication.AuthenticationException;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.servlet.AbstractAuthenticationFilter;
import org.alfresco.web.app.servlet.AuthenticationHelper;
import org.alfresco.web.bean.LoginBean;
import org.alfresco.web.bean.repository.User;
import org.alfresco.web.config.LanguagesConfigElement;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.extensions.config.ConfigService;
import org.springframework.extensions.surf.util.I18NUtil;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.alfresco.web.bean.repository.Repository;



public class OSSOAuthenticationFilter extends AbstractAuthenticationFilter implements Filter {
private static final String LOCALE = "locale";
public static final String MESSAGE_BUNDLE = "alfresco.messages.webclient";
private static Log logger = LogFactory.getLog(OSSOAuthenticationFilter.class);
private ServletContext context;
private String loginPage;
private AuthenticationComponent authComponent;
private AuthenticationService authService;
private TransactionService transactionService;
private PersonService personService;
private NodeService nodeService;
private List m_languages;

public OSSOAuthenticationFilter() {
super();
}

public void destroy() {
// Nothing to do
}

/**
* Run the filter
*
* @param sreq
* ServletRequest
* @param sresp
* ServletResponse
* @param chain
* FilterChain
* @exception IOException
* @exception ServletException
*/
public void doFilter(ServletRequest sreq, ServletResponse sresp, FilterChain chain) throws IOException, ServletException {
// Get the HTTP request/response/session
HttpServletRequest req = (HttpServletRequest) sreq;
HttpServletResponse resp = (HttpServletResponse) sresp;
HttpSession httpSess = req.getSession(true);

String userName = null;
//Get headers setted by the oracle sigle sign one server
java.util.Enumeration reqMap = req.getHeaders("Osso-User-Dn");

if (reqMap == null) {
logger.error("No user logged in");
} else {
while (reqMap.hasMoreElements()){
//Get from the full dn the username
userName = ((String)reqMap.nextElement()).split(",")[0].trim().toString().split("=")[1].trim().toString();
//String tmp = value.split(",")[0].trim().toString();
//userName = tmp.split("=")[1].trim().toString();
}
}

if (logger.isDebugEnabled()) {
logger.debug("OSSO : User = " + userName);
}

// See if there is a user in the session and test if it matches
User user = (User) httpSess.getAttribute(AuthenticationHelper.AUTHENTICATION_USER);

if (user != null) {
try {
// Debug
if (logger.isDebugEnabled())
logger.debug("OSSO : User " + user.getUserName() + " validate ticket");

if (user.getUserName().equals(userName)) {
UserTransaction tx1 = transactionService.getUserTransaction();
try {
tx1.begin();
authComponent.setCurrentUser(user.getUserName());
tx1.commit();
}catch(Exception ex){
logger.error("Failed due to transaction " + ex);
try {
tx1.rollback();
} catch (Exception ex2) {
logger.error("Failed to rollback transaction", ex2);
}

}
I18NUtil.setLocale(Application.getLanguage(httpSess));
chain.doFilter(sreq, sresp);
return;
} else {
// No match
//setAuthenticatedUser(req, httpSess, userName);
//below url is th oracle portal url
resp.sendRedirect("http://hostname:7778/alfresco");
return;
}
} catch (AuthenticationException ex) {
if (logger.isErrorEnabled())
logger.error("Failed to validate user " + user.getUserName(), ex);
}
}

setAuthenticatedUser(req, httpSess, userName);

// Redirect the login page as it is never seen as we always login by name
if (req.getRequestURI().endsWith(getLoginPage()) == true)
{
if (logger.isDebugEnabled())
logger.debug("Login page requested, chaining ...");

resp.sendRedirect(req.getContextPath() + "/faces/jsp/browse/browse.jsp");
return;
}
else
{
//below url is th oracle portal url
resp.sendRedirect("http://hostname:7778/alfresco");
//chain.doFilter(sreq, sresp);
return;
}

}

/**
* Set the authenticated user.
*
* It does not check that the user exists at the moment.
*
* @param req
* @param httpSess
* @param userName
*/
private void setAuthenticatedUser(HttpServletRequest req, HttpSession httpSess, String userName) {
if (userName != null){
UserTransaction tx1 = transactionService.getUserTransaction();
// Set the authentication
try {
tx1.begin();
authComponent.setCurrentUser(userName);
tx1.commit();
} catch (Throwable ex) {
logger.error(ex);
try {
tx1.rollback();
} catch (Exception ex2) {
logger.error("Failed to rollback transaction", ex2);
}
}

// Set up the user information
UserTransaction tx = transactionService.getUserTransaction();
NodeRef homeSpaceRef = null;
User user;
try {
tx.begin();
user = new User(userName, authService.getCurrentTicket(), personService.getPerson(userName));
homeSpaceRef = (NodeRef) nodeService.getProperty(personService.getPerson(userName), ContentModel.PROP_HOMEFOLDER);
if(homeSpaceRef == null) {
logger.warn("Home Folder is null for user '"+userName+"', using company_home.");
homeSpaceRef = (NodeRef) nodeService.getRootNode(Repository.getStoreRef());
}
user.setHomeSpaceId(homeSpaceRef.getId());
tx.commit();
} catch (Throwable ex) {
logger.error(ex);

try {
tx.rollback();
} catch (Exception ex2) {
logger.error("Failed to rollback transaction", ex2);
}

if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
} else {
throw new RuntimeException("Failed to set authenticated user", ex);
}
}

// Store the user
httpSess.setAttribute(AuthenticationHelper.AUTHENTICATION_USER, user);
httpSess.setAttribute(LoginBean.LOGIN_EXTERNAL_AUTH, Boolean.TRUE);

// Set the current locale from the Accept-Lanaguage header if available
Locale userLocale = parseAcceptLanguageHeader(req, m_languages);

if (userLocale != null) {
httpSess.setAttribute(LOCALE, userLocale);
httpSess.removeAttribute(MESSAGE_BUNDLE);
}

// Set the locale using the session
I18NUtil.setLocale(Application.getLanguage(httpSess));
}
}

public void init(FilterConfig config) throws ServletException {
this.context = config.getServletContext();
WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(context);
ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY);
transactionService = serviceRegistry.getTransactionService();
nodeService = serviceRegistry.getNodeService();

authComponent = (AuthenticationComponent) ctx.getBean("authenticationComponent");
authService = (AuthenticationService) ctx.getBean("authenticationService");
personService = (PersonService) ctx.getBean("personService");

// Get a list of the available locales
ConfigService configServiceService = (ConfigService) ctx.getBean("webClientConfigService");
LanguagesConfigElement configElement = (LanguagesConfigElement) configServiceService.getConfig("Languages").getConfigElement(LanguagesConfigElement.CONFIG_ELEMENT_ID);

m_languages = configElement.getLanguages();
}

/**
* Return the login page address
*
* @return String
*/
private String getLoginPage() {
if (loginPage == null) {
loginPage = Application.getLoginPage(context);
}

return loginPage;
}
}



-> STEP 2 Place this class file under
C:\Alfresco\tomcat\webapps\alfresco\WEB-INF\classes\my\custom

-> STEP 3 Now you need to configure proxy pass in oc4j's httpd.conf file


$ORACLE_HOME/Apache/Apache/conf/httpd.conf, add the following entries:                                                                                    ProxyPass /alfresco/ http://host:8080/alfresco/

ProxyPass /alfresco http://host:8080/alfresco/


ProxyPassReverse /alfresco/ http://host:8080/alfresco/

ProxyPassReverse /alfresco http://host:8080/alfresco/


-> STEP 4 Now you need to enable sso on oracle portal

edit $ORACLE_HOME/Apache/Apache/conf/mod_osso.conf, add the following lines just before the :

require valid-user
AuthType Basic


require valid-user
AuthType Basic


Please restart apache after you have made this configuration

-> STEP 5 If alfresco is already running then stop and start alfresco service from start menu under start -> alfresco -> stop alfreco virtual server

Thats it, now oracle sso is configured successfully for Alfreco

Try opening http://oc4jserverhost/alfresco, it will redirect to oracle portal sso login page, enter credentials, it will then redirect you to alfresco home page on successful login.

LOGOUT Link

Coming towards logout functionality, i need to make some changes to use oracle portal logout instead of alfresco built in logout.

You need to edit file C:\Alfresco\tomcat\webapps\alfresco\jsp\parts\titlebar.jsp


search for
a:actionLink


now replace that line with
a:actionLink id="logout" image="/images/icons/logout.gif" value="#{msg.logout} (#{NavigationBean.currentUser.userName})" rendered="#{!NavigationBean.isGuest}" href="http://hostname/pls/orasso/orasso.wwsso_app_admin.ls_logout?p_done_url=http%3A%2F%2Fhostname%2Falfresco" immediate="true"


Thats it, now logout functionality is integrated with oracle sso.

Any issues implementing this, feel free to contact me..

Cheers,

Ujjwal Soni

Tuesday, April 26, 2011

A nice book on smaller, faster, better way to build software

Last week, my friend sent me a link which contains great amount of information for building, testing and all other phases for software development.

Check out this link :: http://gettingreal.37signals.com/toc.php

Cheers,

Ujjwal Soni