Index: project.xml
===================================================================
--- project.xml (revision 6878)
+++ project.xml (working copy)
@@ -85,8 +85,16 @@
sakai-util-text
${sakai.version}
-
+
+
+ sakaiproject
+ sakai-authentication
+ ${sakai.version}
+
+
+
+
servletapi
servletapi
2.4
Index: src/java/org/sakaiproject/tool/access/BasicAuth.java
===================================================================
--- src/java/org/sakaiproject/tool/access/BasicAuth.java (revision 0)
+++ src/java/org/sakaiproject/tool/access/BasicAuth.java (revision 0)
@@ -0,0 +1,257 @@
+package org.sakaiproject.tool.access;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.sakaiproject.api.common.authentication.Authentication;
+import org.sakaiproject.api.common.authentication.AuthenticationException;
+import org.sakaiproject.api.common.authentication.Evidence;
+import org.sakaiproject.api.common.authentication.cover.AuthenticationManager;
+import org.sakaiproject.service.framework.config.cover.ServerConfigurationService;
+import org.sakaiproject.util.IdPwEvidence;
+import org.sakaiproject.util.LoginUtil;
+
+import sun.misc.BASE64Decoder;
+
+/**
+ * This is implemented in a filter, since most httpclients (ie non browser
+ * clients) dont know what to do with a redirect.
+ *
+ * There are 2 mechanisms for selecting basic authentcation. 1. The client is
+ * not a browser as reported by the BasicAuthFilter.isBrowser method. 2. The
+ * user requested basic auth in the URL and the
+ * BasicAuthFilter.requestedBasicAuth confirms this.
+ *
+ * in sakai.properties if allowbasicauth.login = true, then this feature is
+ * enabled in BasicAuthFilter, the determination of non browser clients is
+ * driven by matching user agent headers against a sequence of regex patterns.
+ * These are defined in BasicAuthFilter with the form if the pattern matches a
+ * browser 1pattern or if it does not match 0pattern
+ *
+ * Addtional patterns may be added to sakai.properties as a muliple string
+ * property against login.browser.user.agent
+ *
+ * The list is matched in order, the first match found being definative. If no
+ * match is found, then the client is assumed to be a browser.
+ *
+ *
+ * eg if itunes was not listed as a client. 1. add
+ * login.browser.user.agent.count=1 login.browser.user.agent.1=0itunes.*
+ *
+ * to sakai.properties
+ *
+ *
+ * or 2. Add the __basicauth=02122 to the end of the url eg
+ * http://localhost:8080/access/wiki/123-1231-32123-132123/-.20.rss?someparam=someval&__basicauth=1
+ *
+ * This is available in BasicAuthFilter.BASIC_AUTH_LOGIN_REQUEST
+ *
+ *
+ */
+
+public class BasicAuth {
+
+ /**
+ * The quesry parameter and value that indicates the request will want basic
+ * auth if required
+ */
+ public static final String BASIC_AUTH_LOGIN_REQUEST = "__basicauth=1";
+
+ public static Pattern[] patterns = null;
+
+ private static String[] match;
+
+ /**
+ * The default set of UserAgent patterns to force basic auth with
+ */
+ private static String[] matchPatterns = { "1Mozilla.*", "0i[tT]unes.*",
+ "0Jakarta Commons-HttpClient.*", "0.*Googlebot/2.1.*",
+ "0[gG]oogle[bB]ot.*", "0curl.*"
+
+ };
+
+ /**
+ * Initialise the patterns, since some of the spring stuf may not be up when
+ * the bean is created, this is here to make certain that init is performed
+ * when spring is ready
+ *
+ */
+ public void init() {
+ ArrayList pat = new ArrayList();
+ ArrayList mat = new ArrayList();
+ String[] morepatterns = null;
+ try {
+ morepatterns = ServerConfigurationService
+ .getStrings("login.browser.user.agent");
+ } catch (Exception ex) {
+
+ }
+ if (morepatterns != null) {
+ for (int i = 0; i < morepatterns.length; i++) {
+ String line = morepatterns[i];
+ String check = line.substring(0, 1);
+ mat.add(check);
+ line = line.substring(1);
+ pat.add(Pattern.compile(line));
+
+ }
+ }
+ for (int i = 0; i < matchPatterns.length; i++) {
+ String line = matchPatterns[i];
+ String check = line.substring(0, 1);
+ mat.add(check);
+ line = line.substring(1);
+ pat.add(Pattern.compile(line));
+ }
+
+ patterns = new Pattern[pat.size()];
+ patterns = (Pattern[]) pat.toArray(patterns);
+ match = new String[mat.size()];
+ match = (String[]) mat.toArray(match);
+ }
+
+ /**
+ * If this method returns true, the user agent is a browser
+ *
+ * @param header
+ * @return
+ */
+ protected boolean isBrowser(String userAgentHeader) {
+ if (patterns != null) {
+ for (int i = 0; i < patterns.length; i++) {
+ Matcher m = patterns[i].matcher(userAgentHeader);
+ if (m.matches()) {
+ // System.err.println("Matched
+ // "+match[i]+":"+patterns[i].pattern());
+ return "1".equals(match[i]);
+ }
+ }
+ return true;
+ }
+ return true;
+ }
+
+ /**
+ * This method looks at the returnUrl and if there is a request parameter in
+ * the URL requesting basic authentication, this method returns true
+ *
+ * @param returnUrl
+ * @return
+ */
+ protected boolean requestedBasicAuth(HttpServletRequest request) {
+ String queryString = request.getQueryString();
+ if (queryString == null) {
+ return false;
+ } else {
+ boolean ret = (queryString.indexOf(BASIC_AUTH_LOGIN_REQUEST) != -1);
+ // System.err.println(" Query String " + queryString + " said " +
+ // ret);
+ return ret;
+ }
+ }
+
+ /**
+ * Should a basic auth be used
+ * @param req
+ * @return
+ */
+ protected boolean doBasicAuth(HttpServletRequest req) {
+ boolean allowBasicAuth = ServerConfigurationService.getBoolean(
+ "allow.basic.auth.login", false);
+
+ if (allowBasicAuth) {
+ // System.err.println("Basic Auth Checking A");
+ if (requestedBasicAuth(req)
+ || !isBrowser(req.getHeader("User-Agent"))) {
+ allowBasicAuth = true;
+
+ } else {
+ allowBasicAuth = false;
+
+ }
+ }
+
+ return allowBasicAuth;
+
+ }
+
+ /**
+ * Perform a login based on the headers, if they are not present or it fails do nothing
+ * @param req
+ * @return
+ * @throws IOException
+ */
+ public boolean doLogin(HttpServletRequest req) throws IOException {
+
+ if (doBasicAuth(req)) {
+ // System.err.println("Basic Auth enabled ");
+
+ String auth = req.getHeader("Authorization");
+ Evidence e = null;
+ try {
+ if (auth != null) {
+ auth = auth.trim();
+ if (auth.startsWith("Basic ")) {
+ auth = auth.substring(6).trim();
+ BASE64Decoder denc = new BASE64Decoder();
+ auth = new String(denc.decodeBuffer(auth));
+ int colon = auth.indexOf(":");
+ if (colon != -1) {
+ String eid = auth.substring(0, colon);
+ String pw = auth.substring(colon + 1);
+ if (eid.length() > 0 && pw.length() > 0) {
+ e = new IdPwEvidence(eid, pw);
+ }
+ }
+ }
+ }
+ } catch (Exception ex) {
+
+ }
+
+ // authenticate
+ try {
+ if (e == null) {
+ throw new AuthenticationException("missing required fields");
+ }
+
+ Authentication a = AuthenticationManager.authenticate(e);
+
+ // login the user
+ if (LoginUtil.login(a, req)) {
+ return true;
+ } else {
+ return false;
+ }
+ } catch (AuthenticationException ex) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Emit the basic auth headers and a 401
+ * @param req
+ * @param res
+ * @return
+ * @throws IOException
+ */
+ public boolean doAuth(HttpServletRequest req, HttpServletResponse res)
+ throws IOException {
+ if (doBasicAuth(req)) {
+ res.addHeader("WWW-Authenticate", "Basic realm=\"Sakai\"");
+ res.sendError(HttpServletResponse.SC_UNAUTHORIZED,
+ "Authorization Required");
+ return true;
+ }
+ return false;
+
+ }
+
+}
Property changes on: src/java/org/sakaiproject/tool/access/BasicAuth.java
___________________________________________________________________
Name: svn:keywords
+ Date Revision Author HeadURL Id
Name: svn:eol-style
+ native
Index: src/java/org/sakaiproject/tool/access/AccessServlet.java
===================================================================
--- src/java/org/sakaiproject/tool/access/AccessServlet.java (revision 6878)
+++ src/java/org/sakaiproject/tool/access/AccessServlet.java (working copy)
@@ -106,6 +106,8 @@
/** Session attribute holding copyright-accepted references (a collection of Strings). */
protected static final String COPYRIGHT_ACCEPTED_REFS_ATTR = "Access.Copyright.Accepted";
+
+ protected BasicAuth basicAuth = null;
/** init thread - so we don't wait in the actual init() call */
public class AccessServletInit extends Thread
@@ -140,6 +142,9 @@
{
super.init(config);
startInit();
+ basicAuth = new BasicAuth();
+ basicAuth.init();
+
}
/**
@@ -164,14 +169,15 @@
*/
public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
{
+ // process any login that might be present
+ basicAuth.doLogin(req);
// catch the login helper requests
String option = req.getPathInfo();
String[] parts = option.split("/");
if ((parts.length == 2) && ((parts[1].equals("login"))))
{
doLogin(req, res, null);
- }
-
+ }
else
{
dispatch(req, res);
@@ -192,6 +198,8 @@
*/
public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
{
+ // process any login that might be present
+ basicAuth.doLogin(req);
// catch the login helper posts
String option = req.getPathInfo();
String[] parts = option.split("/");
@@ -199,7 +207,7 @@
{
doLogin(req, res, null);
}
-
+
else
{
sendError(res, HttpServletResponse.SC_NOT_FOUND);
@@ -328,7 +336,9 @@
// if not permitted, and the user is the anon user, let them login
if (SessionManager.getCurrentSessionUserId() == null)
{
- doLogin(req, res, origPath);
+ try {
+ doLogin(req, res, origPath);
+ } catch ( IOException ioex ) {}
return;
}
@@ -412,9 +422,17 @@
* HttpServletResponse object back to the client.
* @param path
* The current request path, set ONLY if we want this to be where to redirect the user after successfull login
+ * @throws IOException
*/
- protected void doLogin(HttpServletRequest req, HttpServletResponse res, String path) throws ToolException
+ protected void doLogin(HttpServletRequest req, HttpServletResponse res, String path) throws ToolException, IOException
{
+ // if basic auth is valid do that
+ if ( basicAuth.doAuth(req,res) ) {
+ //System.err.println("BASIC Auth Request Sent to the Browser ");
+ return;
+ }
+
+
// get the Sakai session
Session session = SessionManager.getCurrentSession();
@@ -423,6 +441,7 @@
{
// where to go after
session.setAttribute(Tool.HELPER_DONE_URL, Web.returnUrl(req, path));
+
}
// check that we have a return path set; might have been done earlier
@@ -435,6 +454,7 @@
ActiveTool tool = ActiveToolManager.getActiveTool("sakai.login");
String context = req.getContextPath() + req.getServletPath() + "/login";
tool.help(req, res, context, "/login");
+
}
/** create the info */