Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Wiki Markup
h2. Installing Sakai

* The following is the link to the official Sakai v2.1.0 installation guide:
** http://cvs.sakaiproject.org/release/2.1.0/InstallGuide.html

* Create a sakai user.  Sakai user's home directory is at:
** /ucd/opt/sakai

* Make sure that you have the following packages installed: (Installed them in /ucd/pkg/ and create soft links to /ucd/opt/)
** ln -s /ucd/pkg/j2sdk1.4.2_10 /ucd/opt/java
** ln -s /ucd/pkg/jdk1.5.0_06 /ucd/opt/java5
** ln -s /ucd/pkg/maven-1.0.2 /ucd/opt/maven
** ln -a /ucd/pkg/apache-ant-1.6.5 /ucd/opt/ant
** Subversion client (You can also download the source code without having Subversion installed)


* Getting Tomcat (Installing tomcat in /ucd/pkg)
{noformat}
cd /ucd/pkg

wget http://apache.forbigweb.com/tomcat/tomcat-5/v5.5.12/bin/apache-tomcat-5.5.12.tar.gz

wget http://apache.forbigweb.com/tomcat/tomcat-5/v5.5.12/bin/apache-tomcat-5.5.12-compat.tar.gz

tar xvzf apache-tomcat-5.5.12.tar.gz

tar xvzf apache-tomcat-5.5.12-compat.tar.gz

ln -s /ucd/pkg/apache-tomcat-5.5.12 /ucd/opt/tomcat
{noformat}


* Setting environment variables
{noformat}
setenv  ANT_HOME        /ucd/opt/ant
setenv  JAVA_HOME       /ucd/opt/java
setenv  MAVEN_HOME      /ucd/opt/maven
setenv  CATALINA_HOME   /ucd/opt/tomcat
{noformat}


* Configure Tomcat
** Edit /ucd/opt/tomcat/conf/server.xml
{noformat}
Replace:
<Connector port="8080" maxHttpHeaderSize="8192"
	maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
	enableLookups="false" redirectPort="8443" acceptCount="100"
	connectionTimeout="20000" disableUploadTimeout="true" />

With:
<Connector port="8080" maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
	enableLookups="false" redirectPort="8443" acceptCount="100"
	debug="0" connectionTimeout="20000" disableUploadTimeout="true"
	URIEncoding="UTF-8"/>

{noformat}


* Make sure that tomcat's ROOT webapp redirects to sakai
{noformat}
Create this file: /ucd/opt/tomcat/webapps/ROOT/index.html

With this content:
<html>
<head>
  <meta http-equiv="refresh" content="0;url=/portal">
</head>
<body>
  redirecting to /portal ...
</body>
</html>
{noformat}


* Setting up Maven
** Create repository directory:
{noformat}
install_repo.sh /ucd/opt/sakai/.maven/repository

Create /ucd/opt/sakai/build.properties file with the following content:
maven.repo.remote=http://www.ibiblio.org/maven/,http://cvs.sakaiproject.org/maven/,http://horde.planetmirror.com/pub/maven/
maven.tomcat.home=/ucd/opt/tomcat/
{noformat}


* Getting the latest source code (As per sakai-dev list, the recommendation is to run production code off the maintenance branch)
{noformat}
cd /ucd/opt/sakai
svn co https://source.sakaiproject.org/svn/branches/sakai_2-1-1 sakai-2-1-01-maint
{noformat}


* Building Sakai
{noformat}
cd /ucd/opt/sakai/sakai_2-1-01-maint
maven sakai
{noformat}


* Sakai configuration
{noformat}
Create a sakai folder
mkdir /ucd/opt/tocmat/sakai

Get a copy of the sakai.properties file and put it in the sakai folder
cp /ucd/opt/sakai/sakai_2-1-01-maint/docs/sakai.properties /ucd/opt/tomcat/sakai/
{noformat}


* Now configure the sakai.properties file
** e.g. Using Oracle and store content in file system
{panel:title=List of modifications made to sakai.properties| borderStyle=dashed| borderColor=#ccc| titleBGColor=#F7D6C1| bgColor=#FFFFCE}
*serverId=stubing
*serverUrl=http://stubing.ucdavis.edu:8080

*username@javax.sql.BaseDataSource=SakaiUser
*password@javax.sql.BaseDataSource=SakaiPassword
*container.login = true

*ui.institution = University of California, Davis
*ui.service = Sakai@UCDavis

*bodyPath@org.sakaiproject.service.legacy.content.ContentHostingService = /ucd/opt/tomcat/content/
*bodyVolumes@org.sakaiproject.service.legacy.content.ContentHostingService = vol1,vol2,vol3
*convertToFile@org.sakaiproject.service.legacy.content.ContentHostingService = true

*provider.kerberos.auth.login.config=/ucd/opt/java/jre/lib/security/jaas.config
*provider.kerberos.krb5.conf=/etc/krb5.conf
*provider.kerberos.showconfig=true

*secureweb.logoutURL=https://secureweb.ucdavis.edu/form-auth/logout?


{panel}
{noformat} 
# sakai.properties - documentation

# identify your application server with a short name, unique among the servers in your cluster.
# choose a server id even if you are running a single app server
serverId=stubing

# the URL to the server, including transport, DNS name, and port, if any
serverUrl=http://stubing.ucdavis.edu:8080

# the DNS name of the server
serverName=stubing.ucdavis.edu

# the URL to send folks to after they logout
loggedOutUrl=/portal

# the default skin name, and the URL path (relative is ok) to the collection of skins
skin.default=default
skin.repo=/library/skin

# the database configuration (hsqldb, mysql or oracle) [ make sure to modify to match your particular setup ]
#vendor@org.sakaiproject.service.framework.sql.SqlService=hsqldb
#vendor@org.sakaiproject.service.framework.sql.SqlService=mysql
vendor@org.sakaiproject.service.framework.sql.SqlService=oracle

#driverClassName@javax.sql.BaseDataSource=org.hsqldb.jdbcDriver
#driverClassName@javax.sql.BaseDataSource=com.mysql.jdbc.Driver
driverClassName@javax.sql.BaseDataSource=oracle.jdbc.driver.OracleDriver

# two hsqldb: first for in-memory (no persistence between runs), second for disk based
#url@javax.sql.BaseDataSource=jdbc:hsqldb:.
#url@javax.sql.BaseDataSource=jdbc:hsqldb:${sakai.home}/db/sakai.db
#url@javax.sql.BaseDataSource=jdbc:mysql://127.0.0.1:3306/sakai?useUnicode=true&amp;characterEncoding=UTF-8
url@javax.sql.BaseDataSource=jdbc:oracle:thin:@ahab.ucdavis.edu:1521:dev

username@javax.sql.BaseDataSource=SakaiUser
password@javax.sql.BaseDataSource=SakaiPassword

#validationQuery@javax.sql.BaseDataSource=select 1 from SYSTEM_USERS

# this is good for both mysql and oracle
validationQuery@javax.sql.BaseDataSource=select 1 from DUAL

# added
hibernate.dialect=net.sf.hibernate.dialect.Oracle9Dialect
auto.ddl=true
defaultTransactionIsolationString@javax.sql.BaseDataSource=TRANSACTION_READ_COMMITTED

# For improved Oracle performance (from the University of Michigan)
validationQuery@javax.sql.BaseDataSource=
defaultTransactionIsolationString@javax.sql.BaseDataSource=
testOnBorrow@javax.sql.BaseDataSource=false


# enable presence display in the portal (true or false)
display.users.present=true

# smtp server for outgoing emails
smtp@org.sakaiproject.service.framework.email.EmailService=smtp.ucdavis.edu

# dns addresses used by James for incoming email
smtp.dns.1 = 169.237.250.250
smtp.dns.2 = 169.237.1.250

# SMTP port on which James runs.  Default is 25.  Recommend running on 8025, and using a standard mailer on 25 to forward mail to Sakai.
smtp.port = 8025

# flag to enable or disable James for incoming email (true | false)
smtp.enabled = true

# upload limit per request, in megs
content.upload.max=120

# links placed on the bottom nav - set the .count to the number of items, then add each item
bottomnav.count = 2
bottomnav.1 = <a href="https://localhost/portal/site/!gateway">Gateway</a>
bottomnav.2 = <a href="http://sakaiproject.org/cms" target="_blank">The Sakai Project</a>

# some fill-ins for the css/vm ui (Worksite Setup, Digest Service, Email notification, Worksite Setup, Contact Support, Portal)
ui.institution = University of California, Davis
ui.service = Sakai@UCDavis

# minutes to cache each security question in the SecurityService - set to 0 to disable caching.
cacheMinutes@org.sakaiproject.service.legacy.security.SecurityService = 3

# minutes to cache each site (site, page, tool) access in the SiteService - set to 0 to disable caching.
cacheMinutes@org.sakaiproject.service.legacy.site.SiteService = 3

# minutes to cache each user access in the UserDirectoryService - set to 0 to disable caching.(0 is the default)
#cacheMinutes@org.sakaiproject.service.legacy.user.UserDirectoryService = 3

# minutes between checks of the user cache for cleaning expired entries - set to 0 to disable caching (0 is the default)
#cacheCleanerMinutes@org.sakaiproject.service.legacy.user.UserDirectoryService = 15

# sessions expire if nothing happens in this many seconds (1 hour)
inactiveInterval@org.sakaiproject.api.kernel.session.SessionManager=3600

# presence expires if not refreshed in this many seconds
timeoutSeconds@org.sakaiproject.service.legacy.presence.PresenceService=60

# root of archive file system area - used to write archive files and to read them
# when clustering app servers, this should be a shared network location
storagePath@org.sakaiproject.service.legacy.archive.ArchiveService = ${sakai.home}/archive/

#copyright text to appear in the bottom area of each web page.
bottom.copyrighttext=(c) 2003, 2004, 2005 sakaiproject.org. All rights reserved.

# to let the container handle login or not (set to true for single-signon type setups, false for just internal login)
container.login = true

# the file system root for content hosting's external stored files (default is null, i.e. store them in the db)
# see the readme file (2.2.7 File Based Content Hosting) for more details
bodyPath@org.sakaiproject.service.legacy.content.ContentHostingService = ${sakai.home}/content/

# when storing content hosting's body bits in files, an optional set of folders just within the bodyPath -
# to act as volumes to distribute the files among - a comma separate list of folders.  If left out, no volumes will be used.
# see the readme file (2.2.7 File Based Content Hosting) for more details
bodyVolumes@org.sakaiproject.service.legacy.content.ContentHostingService = vol1,vol2,vol3

# convert to use file system rather than db
convertToFile@org.sakaiproject.service.legacy.content.ContentHostingService = true

# to disable list of appreance/icon with "edit site information" for course sites. (set as true to display only default appearance)
#disable.course.site.skin.select=true

# force all URLs out of Sakai back to Sakai to be secure, i.e. to use https, on this port.  Leave out to respond with the same transport as the request.
#  Otherwise, the URLs will reflect the attributes of the request URL. (443 | 8443 | or any other port) [defaults to missing]
#force.url.secure=443

#to run JGroups across subnets sending multiple unicast messages
#props@org.sakaiproject.service.legacy.event.EventTrackingService=TCP(start_port=7800):\
#    TCPPING(initial_hosts=localhost[7800];port_range=5;timeout=3000;num_initial_members=3;up_thread=true;down_thread=true):\
#    pbcast.NAKACK(down_thread=true;up_thread=true;gc_lag=100;retransmit_timeout=3000):\
#    VERIFY_SUSPECT(timeout=1500;down_thread=false;up_thread=false):\
#    pbcast.STABLE(desired_avg_gossip=20000):\
#    pbcast.GMS(join_timeout=5000;join_retry_timeout=2000;shun=false;print_local_addr=false;down_thread=true;up_thread=true)

# indicates whether users should see "Show Other Sites" twiggle in list mode of resources tool
resources.show.all.collections=true

# indicates whether users should see "Show Other Sites" twiggle in list mode of dropbox tool
resources.show_all_collections.dropbox = false
# indicates whether users should see "Show Other Sites" twiggle in list mode of filr picker
resources.show_all_collections.helper = true
# indicates whether users should see "Show Other Sites" twiggle in list mode of resources tool
resources.show_all_collections.tool = true

#the location (url) of the accessibility info
#accessibility.url=

#UCD specific parameters
provider.kerberos.auth.login.config=/ucd/opt/java/jre/lib/security/jaas.config
provider.kerberos.krb5.conf=/etc/krb5.conf
provider.kerberos.showconfig=true
secureweb.logoutURL=https://secureweb.ucdavis.edu/form-auth/logout?
{noformat}


* Creating an "archive" and "content" directory
{noformat}
mkdir /ucd/opt/tomcat/sakai/content
mkdir /ucd/opt/tomcat/sakai/archive
{noformat}


* Make sure that you get the Oracle JDBC driver and copy it to /ucd/opt/tomcat/common/lib


* JVM tuning
{noformat}
setenv  JAVA_OPTS  "-server -Xms1500m -Xmx1500m -XX:PermSize=16m -XX:MaxPermSize=128m -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps"
{noformat}

* Protect Login Path with DistAuth
** Download the DistAuthFilterAndWrapper.jar file from subversion, current version 1.0.2.
https://mware.ucdavis.edu/svn/sakai/distauth/trunk/DISTAUTHSOURCE/CurrentVersions/DISTAUTH1.0.2WithWrapperJar/UCDDistAuthFilterAndWrapper1.0.2.jar
** Place jar in the tomcat/shared/lib directory
** In the sakai-src folder, in the login module open the web.xml file.  An example path is as follows:
sakai-src/login/login/src/webapp/WEB-INF/web.xml
Copy the following contents [filter, and filter-mapping definitions] into the web.xml file in the appropriate places.  An entire web.xml file is given below for reference.

{noformat}
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_9" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
    <display-name>sakai-sample-tools-login</display-name>
    <description>Sakai 2 sample tools: login</description>

    <filter>
        <filter-name>sakai.request</filter-name>
        <filter-class>org.sakaiproject.util.RequestFilter</filter-class>
    </filter>

    <filter>
        <filter-name>sakai.request.container</filter-name>
        <filter-class>org.sakaiproject.util.RequestFilter</filter-class>
       <init-param>
            <param-name>tool.placement</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>remote.user</param-name>
            <param-value>false</param-value>
        </init-param>
    </filter>

<!-- added for distauth -->
<filter>
    	<filter-name>ucdfilter</filter-name>
    	<filter-class>edu.ucdavis.security.distauth.AuthenticationFilter</filter-class>
    
	<init-param>
          <param-name>LoginURL</param-name>
          <param-value></param-value>
      </init-param>
      <init-param>
          <param-name>InvalidCookieURL</param-name>
          <param-value></param-value>
      </init-param>
      <init-param>
          <param-name>InvalidLoginURL</param-name>
          <param-value></param-value>
      </init-param>
      <init-param>
          <param-name>InvalidTokenURL</param-name>
          <param-value></param-value>
      </init-param>
    
      <init-param>
          <param-name>InternalErrorURL</param-name>
          <param-value>Error.html</param-value>
      </init-param>
      <init-param>
          <param-name>TimeoutInterval</param-name>
          <param-value>3600</param-value>
      </init-param>
      <init-param>
          <param-name>TokenPath</param-name>
          <param-value>/afs/ucdavis.edu/common/authinfo/</param-value>
      </init-param>
      <init-param>
          <param-name>LogPath</param-name>
          <param-value></param-value>
      </init-param>
      <init-param>
          <param-name>LogFile</param-name>
          <param-value>/var/tmp/distauth.log</param-value>
      </init-param>
	  <init-param>
          <param-name>ValidateUserAgainstDB</param-name>
          <param-value>0</param-value>
      </init-param>
	  <init-param>
          <param-name>DatabaseQuery</param-name>
          <param-value></param-value>
      </init-param>
	  <init-param>
          <param-name>ProtectionLevel</param-name>
          <param-value>2</param-value>
      </init-param>
	  <init-param>
          <param-name>PassThrough</param-name>
          <param-value>0</param-value>
      </init-param>
	  <init-param>
          <param-name>LogoutURL</param-name>
          <param-value>https://secureweb.ucdavis.edu:443/form-auth/logout?</param-value>
      </init-param>
	  <init-param>
          <param-name>SendBackURL</param-name>
          <param-value>https://secureweb.ucdavis.edu:443/form-auth/sendback?</param-value>
      </init-param>
  </filter>


    <filter-mapping>
        <filter-name>sakai.request</filter-name>
        <servlet-name>sakai.login</servlet-name>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>FORWARD</dispatcher>
        <dispatcher>INCLUDE</dispatcher>
    </filter-mapping>

    <filter-mapping>
        <filter-name>sakai.request.container</filter-name>
        <servlet-name>sakai.login.container</servlet-name>
        <dispatcher>REQUEST</dispatcher>
    </filter-mapping>

    <servlet>
        <servlet-name>sakai.login</servlet-name>
        <servlet-class>org.sakaiproject.tool.login.LoginTool</servlet-class>
        <init-param>
            <param-name>container</param-name>
            <param-value>/sakai-login/container</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet>
        <servlet-name>sakai.login.container</servlet-name>
        <servlet-class>org.sakaiproject.tool.login.ContainerLogin</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

	<!-- map the container login, and then cause it to trigger container authentication -->
    <servlet-mapping>
        <servlet-name>sakai.login.container</servlet-name>
        <url-pattern>/container/*</url-pattern>
    </servlet-mapping>
	
<!-- Turn distauth off/on -->
	
	<filter-mapping>
  	<filter-name>ucdfilter</filter-name>
  		 <servlet-name>sakai.login.container</servlet-name>
	 	<dispatcher>REQUEST</dispatcher>
	</filter-mapping>
	

    <listener>
        <listener-class>org.sakaiproject.util.ToolListener</listener-class>
    </listener>

	<!-- to add a user to this BASIC auth tomcat container authentication, add lines to your tomcat/conf/tomcat-users.xml:
    		*** Enable this only if using Tomcat's built-in BASIC auth
	    <tomcat-users>
	        <role rolename="sakaiuser"/>
	        <user username="NAME" password="PW" roles="sakaiuser"/>
	    </tomcat-users>
	among the other definitions there -->

    <!-- Define a Security Constraint on this Application
    		*** Enable this only if using Tomcat's built-in BASIC auth
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>container</web-resource-name>
            <url-pattern>/container/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>sakaiuser</role-name>
        </auth-constraint>
    </security-constraint>
	*** -->

    <!-- Define the Login Configuration for this Application
    		*** Enable this only if using Tomcat's built-in BASIC auth
    <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>Sakai Users</realm-name>
    </login-config>
	*** -->

    <!-- Security roles referenced by this web application
    		*** Enable this only if using Tomcat's built-in BASIC auth
    <security-role>
        <description>
            The role that is required to log in to Sakai
        </description>
        <role-name>sakaiuser</role-name>
    </security-role>
	*** -->

</web-app>



{noformat}

* Enable Kerberos Provider, for WebDav support
** Request a krb5.keytab file for the server you are running (service ticket), and also a krb5.conf file.  You may do this by following these directions:

The page for requesting service principals: 
 https://computingaccounts.ucdavis.edu/cgi-bin/kerberos/index.cgi
  Request {service/host}.ucdavis.edu with service as HTTP (in all caps).
  For example, felix is set up as HTTP/felix.ucdavis.edu.

** Create a new file with the following contents.   Name the file jaas.config.
{noformat}
KerberosAuthentication {
   com.sun.security.auth.module.Krb5LoginModule required
   debug=false
   storekey=false
   clearPass=false
   useTicketCache=false
   useKeyTab=true
   keyTab="/etc/krb.keytab"; 
   };
{noformat}
** Copy the above 3 files into your [jdk-src]/jre/lib/security/ directory.  Note, in the jaas.config file you can identify where your keytab file resides if one wants to put it somewhere else.  Also note the quotes around the file path.
** Enable the Kerberos Provider by first commenting out the SampleUserDirectoryProvider in the [sakai-src]/providers/src/component/src/webapp/WEB-INF/components.xml file.  See below for the snippet to uncomment (REMEMBER TO COMMENT OUT THE SAMPLEUSERDIRECTORYPROVIDER!!!!):
{noformat}
<!-- below is orinally commented out, see KerberosDirectoryProvider documentation for further info -->
	<bean id="org.sakaiproject.service.legacy.user.UserDirectoryProvider"
			class="org.sakaiproject.component.kerberos.user.KerberosUserDirectoryProvider"
			init-method="init"
			destroy-method="destroy"
			singleton="true">
		<property name="logger"><ref bean="org.sakaiproject.service.framework.log.Logger"/></property>
		<property name="domain"><value>ucdavis.edu</value></property>
		<property name="loginContext"><value>KerberosAuthentication</value></property>
		<property name="requireLocalAccount"><value>true</value></property>
		<property name="knownUserMsg"><value>Integrity check on decrypted field failed</value></property>
	</bean>
{noformat}
** Uncomment the KerberosProvider in the [sakai-src]/providers/component/project.xml file.  See below:
{noformat}
             <!-- Needed for the Kerberos Provider, is commented by default --> 
		<dependency>
			<groupId>sakaiproject</groupId>
			<artifactId>sakai-kerberos-provider</artifactId>
			<version>${sakai.version}</version>
			<properties>
				<war.bundle>true</war.bundle>
			</properties>
		</dependency>
{noformat}
** Download current patch release of KerberosUserDirectoryProvider.java.  Replace the "stock" kerberos provider with this one.  The provider code is attached, and enables caching.


** Rebuild sakai for changes to take effect
* Enable logout functionality of Secureweb in Sakai
** Copy the contents of the following LoginTool.java code into the [saka-src/login/login/src/java/org/sakaiproject/tool/login directory.  This file will replace the default code shipped with Sakai.  Changes were made to the "complete" method to enable logout properly via DistAuth.  Below is the entire code, or it may be found in SVN @
https://mware.ucdavis.edu/svn/sakai/distauth/trunk/LogoutSecureWebFunctionality/LoginTool.java
{noformat}
/**********************************************************************************
 *
 * $Header: /cvs/sakai2/login/login/src/java/org/sakaiproject/tool/login/LoginTool.java,v 1.17 2005/06/04 03:14:22 ggolden.umich.edu Exp $
 *
 ***********************************************************************************
 *
 * Copyright (c) 2005 The Regents of the University of Michigan, Trustees of Indiana University,
 *                  Board of Trustees of the Leland Stanford, Jr., University, and The MIT Corporation
 * 
 * Licensed under the Educational Community License Version 1.0 (the "License");
 * By obtaining, using and/or copying this Original Work, you agree that you have read,
 * understand, and will comply with the terms and conditions of the Educational Community License.
 * You may obtain a copy of the License at:
 * 
 *      http://cvs.sakaiproject.org/licenses/license_1_0.html
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 **********************************************************************************/

package org.sakaiproject.tool.login;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.api.kernel.session.Session;
import org.sakaiproject.api.kernel.session.cover.SessionManager;
import org.sakaiproject.api.kernel.tool.Tool;
import org.sakaiproject.service.framework.config.cover.ServerConfigurationService;
import org.sakaiproject.util.IdPwEvidence;
import org.sakaiproject.util.LoginUtil;
import org.sakaiproject.util.web.Web;

/**
 * <p>
 * Login tool for Sakai. Works with the ContainerLoginTool servlet to offer container or internal login.
 * </p>
 * <p>
 * This "tool", being login, is not placed, instead each user can interact with only one login at a time. The Sakai Session is used for attributes.
 * </p>
 * 
 * @author University of Michigan, Sakai Software Development Team
 * @version $Revision: 666 $
 */
public class LoginTool extends HttpServlet
{
	/** Our log (commons). */
	private static Log M_log = LogFactory.getLog(LoginTool.class);

	/** Session attribute used to store a message between steps. */
	protected static final String ATTR_MSG = "sakai.login.message";

	/** Session attribute set and shared with ContainerLoginTool: URL for redirecting back here. */
	public static final String ATTR_RETURN_URL = "sakai.login.return.url";

	/** Session attribute set and shared with ContainerLoginTool: if set we have failed container and need to check internal. */
	public static final String ATTR_CONTAINER_CHECKED = "sakai.login.container.checked";

	/**
	 * Access the Servlet's information display.
	 * 
	 * @return servlet information.
	 */
	public String getServletInfo()
	{
		return "Sakai Login";
	}

	/**
	 * Initialize the servlet.
	 * 
	 * @param config
	 *        The servlet config.
	 * @throws ServletException
	 */
	public void init(ServletConfig config) throws ServletException
	{
		super.init(config);

		M_log.info("init()");
	}

	/**
	 * Shutdown the servlet.
	 */
	public void destroy()
	{
		M_log.info("destroy()");

		super.destroy();
	}

	/**
	 * Respond to requests.
	 * 
	 * @param req
	 *        The servlet request.
	 * @param res
	 *        The servlet response.
	 * @throws ServletException.
	 * @throws IOException.
	 */
	protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
	{
		// get the session
		Session session = SessionManager.getCurrentSession();

		// get my tool registration
		Tool tool = (Tool) req.getAttribute(Tool.TOOL);

		// recognize what to do from the path
		String option = req.getPathInfo();
		System.out.println(option + "=this logout option*****************");
		// maybe we don't want to do the container this time
		boolean skipContainer = false;

		// if missing, set it to "/login"
		if ((option == null) || ("/".equals(option)))
		{
			option = "/login";
		}
		
		// look for the extreme login (i.e. to skip container checks)
		else if ("/xlogin".equals(option))
		{
			option = "/login";
			skipContainer = true;
		}

		// get the parts (the first will be "", second will be "login" or "logout")
		String[] parts = option.split("/");

		if (parts[1].equals("logout"))
		{
			// get the session info complete needs, since the logout will invalidate and clear the session
			String returnUrl = (String) session.getAttribute(Tool.HELPER_DONE_URL);

			// logout the user
			LoginUtil.logout();

			complete(returnUrl, null, tool, res,"logout");
			return;
		}
		else
		{
			// see if we need to check container
			boolean checkContainer = ServerConfigurationService.getBoolean("container.login", false);
			if (checkContainer && !skipContainer)
			{
				// if we have not checked the container yet, check it now
				if (session.getAttribute(ATTR_CONTAINER_CHECKED) == null)
				{
					// save our return path
					session.setAttribute(ATTR_RETURN_URL, Web.returnUrl(req, null));

					String containerCheckPath = this.getServletConfig().getInitParameter("container");
					String containerCheckUrl = Web.serverUrl(req) + containerCheckPath;

					res.sendRedirect(res.encodeRedirectURL(containerCheckUrl));
					return;
				}
			}

			// send the form
			sendForm(req, res);
		}
	}

	/**
	 * Send the login form
	 * 
	 * @param req
	 *        Servlet request.
	 * @param res
	 *        Servlet response.
	 * @throws IOException
	 */
	protected void sendForm(HttpServletRequest req, HttpServletResponse res) throws IOException
	{
		final String headHtml = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">"
				+ "<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\" xml:lang=\"en\">"
				+ "  <head>"
				+ "    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />"
				+ "    <link href=\"SKIN_ROOT/tool_base.css\" type=\"text/css\" rel=\"stylesheet\" media=\"all\" />"
				+ "    <link href=\"SKIN_ROOT/DEFAULT_SKIN/tool.css\" type=\"text/css\" rel=\"stylesheet\" media=\"all\" />"
				+ "    <meta http-equiv=\"Content-Style-Type\" content=\"text/css\" />"
				+ "    <title>Sakai</title>"
				+ "    <script type=\"text/javascript\" language=\"JavaScript\" src=\"/library/js/headscripts.js\"></script>"
				+ "  </head>"
				+ "  <body onload=\" setFocus(focus_path);parent.updCourier(doubleDeep, ignoreCourier);\">"
				+ "<script type=\"text/javascript\" language=\"JavaScript\">" + "  focus_path = [\"eid\"];" + "</script>";

		final String tailHtml = "</body></html>";

		final String loginHtml = "<table border=\"3\" cellpadding=\"0\" cellspacing=\"0\" width=\"470\" style =\"margin-right:auto;margin-left:auto;margin-top:5em;background-color:#fff;font-family:helvetica,arial,sans-serif\">"
				+ "	<tr>"
				+ "		<td>"
				+ "			<table bgcolor=\"#FFFFFF\" border=\"0\" cellpadding=\"5\" cellspacing=\"0\">"
				+ "				<tr>"
				+ "					<td colspan=\"2\" bgcolor=\"#11375E\" align=\"left\">"
				+ "						<b style=\"color:#ffffff; font-size: 12pt\">"
				+ "							Login Required"
				+ "						</b>"
				+ "					</td>"
				+ "				</tr>"
				+ "				<tr>"
				+ "					<td>"
				+ "						<img src=\"SKIN_ROOT/DEFAULT_SKIN/images/logo_inst.gif\" border=\"0\" hspace=\"0\" vspace=\"0\" alt=\"\" />"
				+ "					</td>"
				+ "					<td valign=\"top\">"
				+ "						<form method=\"post\" action=\"ACTION\" enctype=\"application/x-www-form-urlencoded\">"
				+ "							<table border=\"0\">"
				+ "								<tr>"
				+ "									<td style=\"font-weight:bold;\">"
				+ "										EID"
				+ "									</td>"
				+ "									<td>"
				+ "										<input name=\"eid\" id=\"eid\"  type=\"text\" style =\"width: 10em\" />"
				+ "									</td>"
				+ "								</tr>"
				+ "								<tr>"
				+ "									<td style=\"font-weight:bold;\">"
				+ "										PW"
				+ "									</td>"
				+ "									<td>"
				+ "										<input name=\"pw\" type=\"password\" style =\"width: 10em\" />"
				+ "									</td>"
				+ "								</tr>"
				+ "								<tr>"
				+ "									<td colspan=\"2\" align=\"right\">"
				+ "										<input name=\"submit\" type=\"submit\" id=\"submit\" value=\"Log in\" style=\"float:right\" />"
				+ "                                        MSG"
				+ "									</td>"
				+ "								</tr>"
				+ "							</table>"
				+ "						</form>"
				+ "					</td>"
				+ "				</tr>"
				+ "			</table>" + "		</td>" + "	</tr>" + "</table>";

		// get the Sakai session
		Session session = SessionManager.getCurrentSession();

		// get my tool registration
		Tool tool = (Tool) req.getAttribute(Tool.TOOL);

		// fragment or not?
		boolean fragment = Boolean.TRUE.toString().equals(req.getAttribute(Tool.FRAGMENT));

		String eidWording = null;
		String pwWording = null;

		// read my configuration
		if (tool != null)
		{
			eidWording = tool.getRegisteredConfig().getProperty("eid");
			pwWording = tool.getRegisteredConfig().getProperty("pw");
		}

		if (eidWording == null) eidWording = "eid";
		if (pwWording == null) pwWording = "pw";

		if (!fragment)
		{
			// set our response type
			res.setContentType("text/html; charset=UTF-8");
		}

		String defaultSkin = ServerConfigurationService.getString("skin.default");
		String skinRoot = ServerConfigurationService.getString("skin.repo");

		// get our response writer
		PrintWriter out = res.getWriter();

		if (!fragment)
		{
			// start our complete document
			String head = headHtml.replaceAll("DEFAULT_SKIN", defaultSkin);
			head = head.replaceAll("SKIN_ROOT", skinRoot);
			out.println(head);
		}

		// if we are in helper mode, there might be a helper message
		if (session.getAttribute(Tool.HELPER_MESSAGE) != null)
		{
			out.println("<p>" + session.getAttribute(Tool.HELPER_MESSAGE) + "</p>");
		}

		// add our return URL
		String returnUrl = res.encodeURL(Web.returnUrl(req, null));
		String html = loginHtml.replaceAll("ACTION", res.encodeURL(returnUrl));

		// add our wording
		html = html.replaceAll("EID", eidWording);
		html = html.replaceAll("PW", pwWording);

		// add the default skin
		html = html.replaceAll("DEFAULT_SKIN", defaultSkin);
		html = html.replaceAll("SKIN_ROOT", skinRoot);

		// write a message if present
		String msg = (String) session.getAttribute(ATTR_MSG);
		if (msg != null)
		{
			html = html.replaceAll("MSG", "<div class=\"chefAlertBox\">Alert: " + msg + "</div>");
			session.removeAttribute(ATTR_MSG);
		}
		else
		{
			html = html.replaceAll("MSG", "");
		}

		// write the login screen
		out.println(html);

		if (!fragment)
		{
			// close the complete document
			out.println(tailHtml);
		}
	}

	/**
	 * Respond to data posting requests.
	 * 
	 * @param req
	 *        The servlet request.
	 * @param res
	 *        The servlet response.
	 * @throws ServletException.
	 * @throws IOException.
	 */
	protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
	{
		// get the Sakai session
		Session session = SessionManager.getCurrentSession();

		// get my tool registration
		Tool tool = (Tool) req.getAttribute(Tool.TOOL);

		// here comes the data back from the form... these fields will be present, blank if not filled in
		String eid = req.getParameter("eid").trim();
		String pw = req.getParameter("pw").trim();

		// one of these will be there, one null, depending on how the submit was done
		String submit = req.getParameter("submit");
		String cancel = req.getParameter("cancel");

		// cancel
		if (cancel != null)
		{
			session.setAttribute(ATTR_MSG, "login canceled");

			// get the session info complete needs, since the logout will invalidate and clear the session
			String returnUrl = (String) session.getAttribute(Tool.HELPER_DONE_URL);

			// TODO: send to the cancel URL, cleanup session
			complete(returnUrl, session, tool, res,"cancel");
		}

		// submit
		else
		{
			Evidence e = new IdPwEvidence(eid, pw);

			// authenticate
			try
			{
				if ((eid.length() == 0) || (pw.length() == 0))
				{
					throw new AuthenticationException("missing required fields");
				}

				Authentication a = AuthenticationManager.authenticate(e);

				// login the user
				LoginUtil.login(a, req);

				// get the session info complete needs, since the logout will invalidate and clear the session
				String returnUrl = (String) session.getAttribute(Tool.HELPER_DONE_URL);
				
				complete(returnUrl, session, tool, res,"auth");
			}
			catch (AuthenticationException ex)
			{
				session.setAttribute(ATTR_MSG, "invalid login");

				// respond with a redirect back here
				res.sendRedirect(res.encodeRedirectURL(Web.returnUrl(req, null)));
			}
		}
	}


	/**
	 * Cleanup and redirect when we have a successful login / logout
	 * 
	 * @param session
	 * @param tool
	 * @param res
	 * @throws IOException
	 */
	
	protected void complete(String returnUrl, Session session, Tool tool, HttpServletResponse res, String reply) throws IOException
	{
		//added info by Scott Amerson to include logout url for secureweb
				
		
		// cleanup session
		if (session != null)
		{
			session.removeAttribute(Tool.HELPER_MESSAGE);
			session.removeAttribute(Tool.HELPER_DONE_URL);
			session.removeAttribute(ATTR_MSG);
			session.removeAttribute(ATTR_RETURN_URL);
			session.removeAttribute(ATTR_CONTAINER_CHECKED);
		}

		// redirect to the done URL
		if (reply.equals("logout"))
		{
		String LogOutURL = ServerConfigurationService.getString("secureweb.logoutURL", "");
		res.sendRedirect(LogOutURL + res.encodeRedirectURL(returnUrl));	
		}
		else
		{	
		res.sendRedirect(res.encodeRedirectURL(returnUrl));
		}
	}

	

}

/**************************************************************************************************************************************************************************************************************************************************************
 * $Header: /cvs/sakai2/login/login/src/java/org/sakaiproject/tool/login/LoginTool.java,v 1.17 2005/06/04 03:14:22 ggolden.umich.edu Exp $
 *************************************************************************************************************************************************************************************************************************************************************/
{noformat}
** Rebuild Sakai

* Enabling SSL on Tomcat, so that WebDav passwords are encrypted
**Create Certificate
{noformat}
cd /ucd/opt/java/bin
keytool -genkey -alias tomcat -keyalg RSA -keystore /etc/keystore -storepass mysecretpass
{noformat}
**Update Tomcat's server.xml to enable SSL 
{noformat}
update the SSL Connection to have the following attributes:
SSLProtocol, keystoreType, keystoreFile, and keystorePass (see below)
<Connector port="6443" 
         maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
         enableLookups="false" disableUploadTimeout="true"
         acceptCount="100" debug="0" scheme="https" secure="true"
         clientAuth="false" sslProtocol="TLS" keystoreType="JKS"
         keystoreFile="/etc/.keystore" keystorePass="mysecretpass"/>
{noformat}
**Restart Tomcat with the given port number specified. You can choose to turn off port 8080, or keep it open and do redirection via Apache.
\\
h2. System Related Setup

* Tomcat start on boot
{noformat}
in /etc/rc.d/ edit rc.local
========================================================================
#!/bin/sh
#
# This script will be executed *after* all the other init scripts.
# You can put your own initialization stuff in here if you don't
# want to do the full Sys V style init stuff.

touch /var/lock/subsys/local

# Source function library.
. /etc/rc.d/init.d/functions

action "Starting Tomcat: " su -c '/etc/rc.d/tomcat.sh' sakai
========================================================================


in /etc/rc.d/ create the tomcat.sh file
========================================================================
#!/bin/sh

/ucd/opt/tomcat/bin/startup.sh
========================================================================
{noformat}


* Configure iptables so that we can use/run sakai's mail system as the sakai user
{noformat}
# Generated by iptables-save v1.2.8 on Thu Sep  9 14:43:05 2004
*nat
:PREROUTING ACCEPT [510:80231]
:POSTROUTING ACCEPT [12:2548]
:OUTPUT ACCEPT [12:2548]
-A PREROUTING -i eth0 -p tcp -m tcp --dport 25 -j REDIRECT --to-ports 8025
-A PREROUTING -i eth0 -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 8080
COMMIT
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [74067:26067969]
:RH-Firewall-1-INPUT - [0:0]
-A INPUT -j RH-Firewall-1-INPUT
-A FORWARD -j RH-Firewall-1-INPUT
-A RH-Firewall-1-INPUT -i lo -j ACCEPT
-A RH-Firewall-1-INPUT -p icmp -m icmp --icmp-type 255 -j ACCEPT
-A RH-Firewall-1-INPUT -p esp -j ACCEPT
-A RH-Firewall-1-INPUT -p ah -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A RH-Firewall-1-INPUT -p tcp -m state --state NEW -m tcp -s 169.237.0.0/16 --dport 22 -j ACCEPT
-A RH-Firewall-1-INPUT -p udp -m state --state NEW -m udp -s 169.237.0.0/16 -j ACCEPT
-A RH-Firewall-1-INPUT -p tcp -m state --state NEW -m tcp --dport 8025 -j ACCEPT
-A RH-Firewall-1-INPUT -p tcp -m state --state NEW -m tcp --dport 80 -j ACCEPT
-A RH-Firewall-1-INPUT -p tcp -m state --state NEW -m tcp --dport 8080 -j ACCEPT
-A RH-Firewall-1-INPUT -j REJECT --reject-with icmp-host-prohibited
COMMIT
# Completed on Thu Sep  9 14:43:05 2004

{noformat}
* Here is some more information on setting up and configuring James
** http://bugs.sakaiproject.org/confluence/display/ENC/Sendmail+integration