Redirect to *XPages* Login Page

***** ControllingViewHandler Java class *****

package frostillicus.controller;

import javax.faces.application.ViewHandler;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;

import org.openntf.domino.Database;
import org.openntf.domino.utils.Factory;

import uk.co.intec.util.IntecUtils;

import com.ibm.xsp.component.UIViewRootEx;
import com.ibm.xsp.designer.context.XSPContext;
import com.ibm.xsp.extlib.util.ExtLibUtil;

/**
 * Core class handling which controller class to use
 * 
 */
public class ControllingViewHandler extends com.ibm.xsp.application.ViewHandlerExImpl {

	private static enum AnonymousPageNames {
		INDEX("/index.xsp"), ERROR("/Error.xsp");

		private final String value_;

		private AnonymousPageNames(String name) {
			value_ = name;
		}

		public String getValue() {
			return value_;
		}

	}

	public ControllingViewHandler(ViewHandler arg0) {
		super(arg0);
	}

	/*
	 * Extended to get page name from Form if page includes $$OpenDominoDocument or XPage. Looks to controller package and adds it to ViewScope.
	 * Adds beforeRenderResponse, afterRenderResponse, and afterRestoreView methods. Also sets pageTitle property 
	 * 
	 *  (non-Javadoc)
	 * @see com.ibm.xsp.application.ViewHandlerEx#createView(javax.faces.context.FacesContext, java.lang.String)
	 */
	@SuppressWarnings("unchecked")
	@Override
	public UIViewRoot createView(FacesContext context, String pageName) {
		// Page name is in the format "/Home"

		// Uncomment this bit if documents should have anonymous access. It will check for $$OpenDocument as the page name, 
		// in which case get the relevant page name from the underlying Form using an in-built XPpages function
		/*if (pageClassName.equalsIgnoreCase("$$OpenDominoDocument")) {
			// Handle the virtual document page. There may be a better way to do this, but this will do for now
			try {
				Map<String, String> param = (Map<String, String>) ExtLibUtil.resolveVariable(FacesContext
						.getCurrentInstance(), "param");
				Database database = AppUtils.getAppDb();
				Document doc = database.getDocumentByUNID(param.get("documentId"));
				if (doc == null) {
					try {
						doc = database.getDocumentByID(param.get("documentId"));
					} catch (Throwable te) {
						// Just couldn't get doc
					}
				}
				if (doc != null) {
					pageName = DominoUtils.getXPagesForDocument(doc);
				}
			} catch (Exception e) {
				IntecUtils.handleException(e);
			}
		}*/

		if (!pageName.contains(".xsp")) {
			pageName = pageName + ".xsp";
		}
		if (!pageName.startsWith("/")) {
			pageName = "/" + pageName;
		}

		boolean allowContinue = false;

		if (!Factory.getSession().getEffectiveUserName().equalsIgnoreCase("anonymous")) {
			allowContinue = true;
		} else {
			for (AnonymousPageNames page : AnonymousPageNames.values()) {
				if (page.getValue().equalsIgnoreCase(pageName)) {
					allowContinue = true;
					break;
				}
			}
		}

		if (!allowContinue) {
			XSPContext xspContext = (XSPContext) IntecUtils.resolveVariable("context");
			try {
				Database currDb = Factory.getSession().getCurrentDatabase();
				HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest();
				String url = request.getRequestURL().toString() + "?" + request.getQueryString();
				// Next line is an example of redirecting within the NSF - easy because it's done within XPages runtime . Amend XPage name as required
				// xspContext.redirectToPage("login.xsp?open&redirectTo=" + java.net.URLEncoder.encode(url, "UTF-8"));

				// The following is an example of redirecting outside the current NSF - more difficult because we can't redirect until Render Response
				// So redirect to Error page in this NSF, then sniff for ResdirectToLogin requestScope in beforeRenderResponse to redirect
				// AMEND THIS NEXT LINE IF NOT USING ODA
				String dbName = currDb.getFileName();
				// Build this URL as required
				String fullUrl = url.substring(0, url.indexOf(dbName)) + dbName + "/login.xsp?open&redirectTo="
						+ java.net.URLEncoder.encode(url, "UTF-8");
				ExtLibUtil.getRequestScope().put("RedirectToLogin", fullUrl);
				pageName = "/Error.xsp";
			} catch (Throwable t) {
				if (t instanceof com.ibm.xsp.acl.RedirectSignal) {
					// Don't log redirections
				} else {
					t.printStackTrace();
				}
				xspContext.redirectToHome();
			}
		}

		UIViewRootEx root = null;
		root = (UIViewRootEx) super.createView(context, pageName);

		return root;
	}
}


***** Registering the ControllingViewHandler in faces-config *****

  <application>
    <view-handler>frostillicus.controller.ControllingViewHandler</view-handler>
  </application>

***** beforeRenderResponse code for Error page *****

	<xp:this.beforeRenderResponse><![CDATA[#{javascript:if (requestScope.containsKey("RedirectToLogin")) {
	print(requestScope.get("RedirectToLogin"));
	facesContext.getExternalContext().redirect(requestScope.get("RedirectToLogin"));
}}]]></xp:this.beforeRenderResponse>





Preventing access via ACL redirects to a login page. That can be customised, but only as a Notes Form design element - it's not possible to redirect to an XPage. If you restrict access via ACL, but want anonymous access to certain pages, you need to make them Public Access. In my experience, you also need to make certain resources used on the page also Public Access (e.g. favicon).

This snippet consists of two elements:
A ViewHandler extending the core XPages ViewHandler which is triggered when any page is loaded. The Java class has an enum extensible to define Anonymous-access pages (remember to include your custom Error page!)
Code for the faces-config to register the ViewHandler.

If you want to redirect to an XPage in the current NSF, you can use context.redirectToPage() - remember to add that page to the AnonymousPageNames enum. This could be used to redirect to a Home page, for example, where a menu option has functions for logging in (like XSnippets and OpenNTF sites do). Alternatively it could redirect to a full-blown login form that then redirects.

Redirecting externally is more difficult, because it seems the ResponseWriter object required to write the redirection to the browser is not available this early in the lifecycle. Instead, there is code to write a requestScope variable and load the "/Error.xsp" page instead (this is your custom error page, which should be lightweight with XPages components, because partial refresh events won't necessarily trigger!). The Error page then needs the third snippet of code in beforeRenderResponse to sniff for the requestScope variable and do the redirection (this could be put in a PhaseListener).

The code here uses OpenNTF Domino API to get the current database name, to generate a relative URL to redirect to. If you're application doesn't use ODA, you'll need to amend that line.

There are various examples of XPages login page code around the web, some in dialogs, some elsewhere.

Java
Paul S Withers
June 12, 2015 4:43 AM
Rating
71

All code submitted to OpenNTF XSnippets, whether submitted as a "Snippet" or in the body of a Comment, is provided under the Apache License Version 2.0. See Terms of Use for full details.



No comments yetLogin first to comment...