Migrate the labs to direct/Random access stages

git-svn-id: http://webgoat.googlecode.com/svn/trunk@158 4033779f-a91e-0410-96ef-6bf7bf53c507
This commit is contained in:
rogan.dawes 2007-07-11 12:51:29 +00:00
parent f5e56c7081
commit 2bda4a81f3
19 changed files with 300 additions and 114 deletions

View File

@ -54,6 +54,18 @@ public class CrossSiteScripting extends GoatHillsFinancial
{
private final static Integer DEFAULT_RANKING = new Integer(100);
public final static String STAGE1 = "Stage 1";
public final static String STAGE2 = "Stage 2";
public final static String STAGE3 = "Stage 3";
public final static String STAGE4 = "Stage 4";
public final static String STAGE5 = "Stage 5";
public final static String STAGE6 = "Stage 6";
protected void registerActions(String className)
{
registerAction(new ListStaff(this, className, LISTSTAFF_ACTION));
@ -140,52 +152,47 @@ public class CrossSiteScripting extends GoatHillsFinancial
if (!getLessonTracker(s).getCompleted())
{
switch (getStage(s))
String stage = getStage(s);
if (STAGE1.equals(stage))
{
case 1:
instructions = "Stage "
+ getStage(s)
instructions = getStage(s)
+ ": Execute a Stored Cross Site Scripting (XSS) attack.<br>"
+ "For this exercise, your mission is to cause the application to serve a script of your making "
+ " to some other user.";
break;
case 2:
instructions = "Stage "
+ getStage(s)
}
else if (STAGE2.equals(stage))
{
instructions = getStage(s)
+ ": Block Stored XSS using Input Validation.<br>"
+ "You will modify the application to perform input validation on the vulnerable input field "
+ "you just exploited.";
break;
case 3:
instructions = "Stage "
+ getStage(s)
}
else if (STAGE3.equals(stage))
{
instructions = getStage(s)
+ ": Execute a previously Stored Cross Site Scripting (XSS) attack.<br>"
+ "The application is still vulnerable to scripts in the database. Trigger a pre-stored "
+ "script by logging in as employee 'David' and viewing Bruce's profile.";
break;
case 4:
instructions = "Stage "
+ getStage(s)
}
else if (STAGE4.equals(stage))
{
instructions = getStage(s)
+ ": Block Stored XSS using Output Encoding.<br>"
+ "Encode data served from the database to the client so that any scripts are rendered harmless.";
break;
case 5:
instructions = "Stage "
+ getStage(s)
}
else if (STAGE5.equals(stage))
{
instructions = getStage(s)
+ ": Execute a Reflected XSS attack.<br>"
+ "Your goal here is to craft a link containing a script which the application will "
+ "serve right back to any client that activates the link.";
break;
case 6:
instructions = "Stage "
+ getStage(s)
}
else if (STAGE6.equals(stage))
{
instructions = getStage(s)
+ ": Block Reflected XSS using Input Validation.<br>"
+ "Use the input validation techniques learned ealier in this lesson to close the vulnerability "
+ "you just exploited.";
break;
default:
// Illegal stage value
break;
}
}
@ -194,8 +201,8 @@ public class CrossSiteScripting extends GoatHillsFinancial
}
@Override
public int getStageCount() {
return 6;
public String[] getStages() {
return new String[] {STAGE1, STAGE2, STAGE3, STAGE4, STAGE5, STAGE6};
}
public void handleRequest(WebSession s)
@ -290,12 +297,11 @@ public class CrossSiteScripting extends GoatHillsFinancial
public String htmlEncode(WebSession s, String text)
{
//System.out.println("Testing for stage 4 completion in lesson " + getCurrentLesson().getName());
if (getStage(s) == 4 &&
if (STAGE4.equals(getStage(s)) &&
text.indexOf("<script>") > -1 && text.indexOf("alert") > -1 && text.indexOf("</script>") > -1)
{
s.setMessage( "Welcome to stage 5 -- exploiting the data layer" );
// Set a phantom stage value to setup for the 4-5 transition
setStage(s, 1005);
setStageComplete(s, STAGE5);
}
return HtmlEncoder.encode(text);

View File

@ -88,23 +88,23 @@ public class FindProfile extends DefaultLessonAction
}
catch (ValidationException e)
{
if (getStage(s) == 6)
if (CrossSiteScripting.STAGE6.equals(getStage(s)))
{
s
.setMessage("Congratulations. You have successfully completed this lesson");
getLesson().getLessonTracker(s).setCompleted(true);
setStageComplete(s, CrossSiteScripting.STAGE6);
}
throw e;
}
if (getStage(s) == 5)
if (CrossSiteScripting.STAGE5.equals(getStage(s)))
{
if (searchName.indexOf("<script>") > -1
&& searchName.indexOf("alert") > -1
&& searchName.indexOf("</script>") > -1)
{
s.setMessage("Welcome to stage 6 - more input validation");
setStage(s, 6);
setStageComplete(s, CrossSiteScripting.STAGE5);
}
}

View File

@ -81,11 +81,11 @@ public class UpdateProfile extends DefaultLessonAction
}
catch (ValidationException e)
{
if (getStage(s) == 2)
if (CrossSiteScripting.STAGE2.equals(getStage(s)))
{
s
.setMessage("Welcome to stage 3 - demonstrate Stored XSS again");
setStage(s, 3);
setStageComplete(s, CrossSiteScripting.STAGE2);
}
throw e;
}

View File

@ -212,9 +212,9 @@ public class ViewProfile extends DefaultLessonAction
private void updateLessonStatus(WebSession s, Employee employee)
{
switch (getStage(s))
String stage = getStage(s);
if (CrossSiteScripting.STAGE1.equals(stage))
{
case 1:
String address1 = employee.getAddress1().toLowerCase();
if (address1.indexOf("<script>") > -1
&& address1.indexOf("alert") > -1
@ -222,10 +222,11 @@ public class ViewProfile extends DefaultLessonAction
{
s
.setMessage("Welcome to stage 2 - implement input validation");
setStage(s, 2);
setStageComplete(s, CrossSiteScripting.STAGE1);
}
break;
case 3:
}
else if (CrossSiteScripting.STAGE3.equals(stage))
{
String address2 = employee.getAddress1().toLowerCase();
if (address2.indexOf("<script>") > -1
&& address2.indexOf("alert") > -1
@ -233,19 +234,17 @@ public class ViewProfile extends DefaultLessonAction
{
s
.setMessage("Welcome to stage 4 - implement output encoding");
setStage(s, 4);
setStageComplete(s, CrossSiteScripting.STAGE3);
}
break;
case 4:
}
else if (CrossSiteScripting.STAGE4.equals(stage))
{
if (employee.getAddress1().toLowerCase().indexOf("&lt;") > -1)
{
s
.setMessage("Welcome to stage 5 - demonstrate reflected XSS");
setStage(s, 5);
setStageComplete(s, CrossSiteScripting.STAGE4);
}
break;
default:
break;
}
}

View File

@ -312,12 +312,16 @@ public abstract class DefaultLessonAction implements LessonAction
return authorized;
}
protected void setStage(WebSession s, int stage)
protected void setStage(WebSession s, String stage)
{
getLesson().setStage(s, stage);
}
protected int getStage(WebSession s)
protected void setStageComplete(WebSession s, String stage) {
getLesson().setStageComplete(s, stage);
}
protected String getStage(WebSession s)
{
return getLesson().getStage(s);
}

View File

@ -9,7 +9,7 @@ import org.apache.ecs.Element;
import org.apache.ecs.ElementContainer;
import org.apache.ecs.html.A;
import org.apache.ecs.html.IMG;
import org.owasp.webgoat.lessons.SequentialLessonAdapter;
import org.owasp.webgoat.lessons.RandomLessonAdapter;
import org.owasp.webgoat.session.ParameterNotFoundException;
import org.owasp.webgoat.session.UnauthenticatedException;
import org.owasp.webgoat.session.UnauthorizedException;
@ -45,7 +45,7 @@ import org.owasp.webgoat.session.WebSession;
*
* For details, please see http://code.google.com/p/webgoat/
*/
public class GoatHillsFinancial extends SequentialLessonAdapter
public class GoatHillsFinancial extends RandomLessonAdapter
{
public final static A ASPECT_LOGO = new A().setHref("http://www.aspectsecurity.com").addElement(new IMG("images/logos/aspect.jpg").setAlt("Aspect Security").setBorder(0).setHspace(0).setVspace(0));
@ -161,6 +161,10 @@ public class GoatHillsFinancial extends SequentialLessonAdapter
lessonFunctions.put(action.getActionName(), action);
}
public String[] getStages() {
return new String[] {};
}
protected List<String> getHints(WebSession s)
{
return new ArrayList<String>();

View File

@ -0,0 +1,45 @@
package org.owasp.webgoat.lessons;
import org.owasp.webgoat.session.LessonTracker;
import org.owasp.webgoat.session.RandomLessonTracker;
import org.owasp.webgoat.session.WebSession;
public abstract class RandomLessonAdapter extends LessonAdapter {
public abstract String[] getStages();
public void setStage(WebSession s, String stage) {
getLessonTracker(s).setStage(stage);
}
public String getStage(WebSession s) {
return getLessonTracker(s).getStage();
}
public void setStageComplete(WebSession s, String stage) {
getLessonTracker(s).setStageComplete(stage, true);
}
@Override
public RandomLessonTracker getLessonTracker(WebSession s) {
return (RandomLessonTracker) super.getLessonTracker(s);
}
@Override
public RandomLessonTracker getLessonTracker(WebSession s, AbstractLesson lesson) {
return (RandomLessonTracker) super.getLessonTracker(s, lesson);
}
@Override
public RandomLessonTracker getLessonTracker(WebSession s, String userNameOverride) {
return (RandomLessonTracker) super.getLessonTracker(s, userNameOverride);
}
@Override
public LessonTracker createLessonTracker() {
return new RandomLessonTracker(getStages());
}
}

View File

@ -160,7 +160,7 @@ public class DeleteProfile extends DefaultLessonAction
private void updateLessonStatus(WebSession s)
{
// If the logged in user is not authorized to be here, stage 1 is complete.
if (getStage(s) == 1)
if (RoleBasedAccessControl.STAGE1.equals(getStage(s)))
try
{
int userId = getIntSessionAttribute(s, getLessonName() + "."
@ -171,7 +171,7 @@ public class DeleteProfile extends DefaultLessonAction
{
s
.setMessage("Welcome to stage 2 -- protecting the business layer");
setStage(s, 2);
setStageComplete(s, RoleBasedAccessControl.STAGE1);
}
}
catch (ParameterNotFoundException e)

View File

@ -52,6 +52,14 @@ public class RoleBasedAccessControl extends GoatHillsFinancial
{
private final static Integer DEFAULT_RANKING = new Integer(125);
public final static String STAGE1 = "Stage 1";
public final static String STAGE2 = "Stage 2";
public final static String STAGE3 = "Stage 3";
public final static String STAGE4 = "Stage 4";
protected void registerActions(String className) {
registerAction(new ListStaff(this, className, LISTSTAFF_ACTION));
registerAction(new SearchStaff(this, className, SEARCHSTAFF_ACTION));
@ -114,8 +122,8 @@ public class RoleBasedAccessControl extends GoatHillsFinancial
}
@Override
public int getStageCount() {
return 4;
public String[] getStages() {
return new String[] {STAGE1, STAGE2, STAGE3, STAGE4};
}
/**
@ -129,41 +137,40 @@ public class RoleBasedAccessControl extends GoatHillsFinancial
if (!getLessonTracker(s).getCompleted())
{
switch (getStage(s))
String stage = getStage(s);
if (STAGE1.equals(stage))
{
case 1:
instructions = "Stage "
+ getStage(s)
+ ": Breaking functional access control.<br>"
+ "You should be able to login as a regular employee and delete another user's employee "
+ "profile, even though that is supposed to be an HR-only function.";
break;
case 2:
}
else if (STAGE2.equals(stage))
{
instructions = "Stage "
+ getStage(s)
+ ": Implementing access control in the Business Layer<br>"
+ "Access control has already been implemented in the Presentation Layer, but as we have just "
+ "seen, this is not enough. Implement access control in the Businesss Layer to verify "
+ "authorization to use the Delete function before actually executing it.";
break;
case 3:
}
else if (STAGE3.equals(stage))
{
instructions = "Stage "
+ getStage(s)
+ ": Breaking data access control.<br>"
+ "Data Layer access control is being already done on the staff list, but it has not been "
+ "globally implemented. Take advantage of this to login as a regular employee and view the "
+ "CEO's employee profile.";
break;
case 4:
}
else if (STAGE4.equals(stage))
{
instructions = "Stage "
+ getStage(s)
+ ": Implementing access control in the Data Layer.<br>"
+ "Implement Data Layer access control to prevent unauthorized (and potentially career threatening) "
+ "access to employee personal data.";
break;
default:
// Illegal stage value
break;
}
}
@ -236,7 +243,8 @@ public class RoleBasedAccessControl extends GoatHillsFinancial
catch (UnauthorizedException ue2)
{
// Update lesson status if necessary.
if (getStage(s) == 2)
String stage = getStage(s);
if (STAGE2.equals(stage))
{
try
{
@ -244,7 +252,7 @@ public class RoleBasedAccessControl extends GoatHillsFinancial
!isAuthorized(s, getUserId(s), RoleBasedAccessControl.DELETEPROFILE_ACTION))
{
s.setMessage( "Welcome to stage 3 -- exploiting the data layer" );
setStage(s, 3);
setStageComplete(s, STAGE2);
}
} catch (ParameterNotFoundException pnfe)
{
@ -253,7 +261,7 @@ public class RoleBasedAccessControl extends GoatHillsFinancial
}
//System.out.println("isAuthorized() exit stage: " + getStage(s));
// Update lesson status if necessary.
if (getStage(s) == 4)
if (STAGE4.equals(stage))
{
try
{
@ -267,7 +275,7 @@ public class RoleBasedAccessControl extends GoatHillsFinancial
if (!action.isAuthorizedForEmployee(s, userId, employeeId))
{
s.setMessage("Congratulations. You have successfully completed this lesson.");
getLessonTracker( s ).setCompleted( true );
setStageComplete(s, STAGE4);
}
} catch (Exception e)
{
@ -370,8 +378,9 @@ public class RoleBasedAccessControl extends GoatHillsFinancial
}
catch (UnauthorizedException ue2)
{
String stage = getStage(s);
// Update lesson status if necessary.
if (getStage(s) == 2)
if (STAGE2.equals(stage))
{
try
{
@ -379,7 +388,7 @@ public class RoleBasedAccessControl extends GoatHillsFinancial
!isAuthorized(s, getUserId(s), RoleBasedAccessControl.DELETEPROFILE_ACTION))
{
s.setMessage( "Welcome to stage 3 -- exploiting the data layer" );
setStage(s, 3);
setStageComplete(s, STAGE2);
}
} catch (ParameterNotFoundException pnfe)
{
@ -388,7 +397,7 @@ public class RoleBasedAccessControl extends GoatHillsFinancial
}
//System.out.println("isAuthorized() exit stage: " + getStage(s));
// Update lesson status if necessary.
if (getStage(s) == 4)
if (STAGE4.equals(stage))
{
try
{
@ -402,7 +411,7 @@ public class RoleBasedAccessControl extends GoatHillsFinancial
if (!action.isAuthorizedForEmployee(s, userId, employeeId))
{
s.setMessage("Congratulations. You have successfully completed this lesson.");
getLessonTracker( s ).setCompleted( true );
setStageComplete(s, STAGE4);
}
} catch (Exception e)
{

View File

@ -95,11 +95,11 @@ public class ViewProfile extends DefaultLessonAction
int employeeId = s.getParser().getIntParameter(
RoleBasedAccessControl.EMPLOYEE_ID);
if (getStage(s) == 3
if (RoleBasedAccessControl.STAGE3.equals(getStage(s))
&& !isAuthorizedForEmployee(s, userId, employeeId))
{
s.setMessage("Welcome to stage 4 -- protecting the data layer");
setStage(s, 4);
setStageComplete(s, RoleBasedAccessControl.STAGE3);
}
}
catch (ParameterNotFoundException e)

View File

@ -270,28 +270,26 @@ public class Login extends DefaultLessonAction
SQLInjection.EMPLOYEE_ID);
String password = s.getParser().getRawParameter(
SQLInjection.PASSWORD);
switch (getStage(s))
String stage = getStage(s);
if (SQLInjection.STAGE1.equals(stage))
{
case 1:
if (Integer.parseInt(employeeId) == SQLInjection.PRIZE_EMPLOYEE_ID
&& isAuthenticated(s))
{
s.setMessage("Welcome to stage 2");
setStage(s, 2);
setStageComplete(s, SQLInjection.STAGE1);
}
break;
case 2:
}
else if (SQLInjection.STAGE2.equals(stage))
{
// This assumes the student hasn't modified login_BACKUP().
if (Integer.parseInt(employeeId) == SQLInjection.PRIZE_EMPLOYEE_ID
&& !isAuthenticated(s)
&& login_BACKUP(s, employeeId, password))
{
s.setMessage("Welcome to stage 3");
setStage(s, 3);
setStageComplete(s, SQLInjection.STAGE2);
}
break;
default:
break;
}
}
catch (ParameterNotFoundException pnfe)

View File

@ -55,6 +55,14 @@ public class SQLInjection extends GoatHillsFinancial
public final static String PRIZE_EMPLOYEE_NAME = "Neville Bartholomew";
public final static String STAGE1 = "Stage 1";
public final static String STAGE2 = "Stage 2";
public final static String STAGE3 = "Stage 3";
public final static String STAGE4 = "Stage 4";
public void registerActions(String className)
{
registerAction(new ListStaff(this, className, LISTSTAFF_ACTION));
@ -122,8 +130,8 @@ public class SQLInjection extends GoatHillsFinancial
}
@Override
public int getStageCount() {
return 4;
public String[] getStages() {
return new String[] {STAGE1, STAGE2, STAGE3, STAGE4};
}
/**
@ -137,9 +145,9 @@ public class SQLInjection extends GoatHillsFinancial
if (!getLessonTracker(s).getCompleted())
{
switch (getStage(s))
String stage = getStage(s);
if (STAGE1.equals(stage))
{
case 1:
instructions = "Stage "
+ getStage(s)
+ ": Use String SQL Injection to bypass authentication. "
@ -147,32 +155,31 @@ public class SQLInjection extends GoatHillsFinancial
+ PRIZE_EMPLOYEE_NAME
+ ", who is in the Admin group. "
+ "You do not have the password, but the form is SQL injectable.";
break;
case 2:
}
else if (STAGE2.equals(stage))
{
instructions = "Stage "
+ getStage(s)
+ ": Use a parameterized query.<br>"
+ "A dynamic SQL query is not necessary for the login function to work. Change login "
+ "to use a parameterized query to protect against malicious SQL in the query parameters.";
break;
case 3:
}
else if (STAGE3.equals(stage))
{
instructions = "Stage "
+ getStage(s)
+ ": Use Integer SQL Injection to bypass access control.<br>"
+ "The goal here is to view the CEO's employee profile, again, even with data access "
+ "control checks in place from a previous lesson. "
+ "As before, you do not have the password, but the form is SQL injectable.";
break;
case 4:
}
else if (STAGE4.equals(stage))
{
instructions = "Stage "
+ getStage(s)
+ ": Use a parameterized query again.<br>"
+ "Change the ViewProfile function to use a parameterized query to protect against "
+ "malicious SQL in the numeric query parameter.";
break;
default:
// Illegal stage value
break;
}
}

View File

@ -224,9 +224,9 @@ public class ViewProfile extends DefaultLessonAction
+ SQLInjection.USER_ID);
String employeeId = s.getParser().getRawParameter(
SQLInjection.EMPLOYEE_ID);
switch (getStage(s))
String stage = getStage(s);
if (SQLInjection.STAGE3.equals(stage))
{
case 3:
// If the employee we are viewing is the prize and we are not authorized to have it,
// the stage is completed
if (employee != null
@ -235,10 +235,11 @@ public class ViewProfile extends DefaultLessonAction
.parseInt(userId), employee.getId()))
{
s.setMessage("Welcome to stage 4");
setStage(s, 4);
setStageComplete(s, SQLInjection.STAGE3);
}
break;
case 4:
}
else if (SQLInjection.STAGE4.equals(stage))
{
// If we were denied the employee to view, and we would have been able to view it
// in the broken state, the stage is completed.
// This assumes the student hasn't modified getEmployeeProfile_BACKUP().
@ -257,12 +258,9 @@ public class ViewProfile extends DefaultLessonAction
{
s
.setMessage("Congratulations. You have successfully completed this lesson");
getLesson().getLessonTracker(s).setCompleted(true);
setStageComplete(s, SQLInjection.STAGE4);
}
}
break;
default:
break;
}
}
catch (ParameterNotFoundException pnfe)

View File

@ -0,0 +1,86 @@
package org.owasp.webgoat.session;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
public class RandomLessonTracker extends LessonTracker {
private String[] stages;
private String stage;
private Map<String, Boolean> completed = new HashMap<String, Boolean>();
public RandomLessonTracker(String[] stages) {
if (stages == null)
stages = new String[0];
this.stages = stages;
}
public void setStage(String stage) {
this.stage = stage;
}
public String getStage() {
if (this.stage == null && stages.length > 0)
return stages[0];
return this.stage;
}
public void setStageComplete(String stage, boolean complete) {
completed.put(stage, Boolean.valueOf(complete));
for (int i=0; i<stages.length-1; i++)
if (stages[i].equals(stage))
setStage(stages[i+1]);
}
public boolean hasCompleted(String stage) {
Boolean complete = completed.get(stage);
return complete == null ? false : complete.booleanValue();
}
@Override
public boolean getCompleted() {
for (int i=0; i<stages.length; i++)
if (!hasCompleted(stages[i]))
return false;
return true;
}
@Override
public void setCompleted(boolean complete) {
throw new UnsupportedOperationException("Use individual stage completion instead");
}
protected void setProperties(Properties props, Screen screen) {
super.setProperties(props, screen);
for (int i=0; i<stages.length; i++) {
String p = props.getProperty(screen.getTitle() + "." + stages[i] + ".completed");
if (p != null) {
setStageComplete(stages[i], Boolean.valueOf(p));
}
}
setStage(props.getProperty(screen.getTitle() + ".stage"));
}
public void store(WebSession s, Screen screen, String user) {
for (int i=0; i<stages.length; i++) {
if (hasCompleted(stages[i]))
lessonProperties.setProperty(screen.getTitle() + "." + stages[i] + ".completed", Boolean.TRUE.toString());
}
lessonProperties.setProperty(screen.getTitle() + ".stage", getStage());
super.store(s, screen, user);
}
public String toString() {
StringBuffer buff = new StringBuffer();
buff.append(super.toString());
for (int i=0; i<stages.length; i++) {
buff.append(" - completed " + stages[i] + " :....... " + hasCompleted(stages[i]) + "\n");
}
buff.append(" - currentStage:....... " + getStage() + "\n");
return buff.toString();
}
}

View File

@ -22,6 +22,7 @@ import javax.servlet.http.HttpServletResponse;
import org.owasp.webgoat.lessons.AbstractLesson;
import org.owasp.webgoat.lessons.Category;
import org.owasp.webgoat.lessons.RandomLessonAdapter;
import org.owasp.webgoat.lessons.SequentialLessonAdapter;
/*******************************************************************************
@ -916,6 +917,17 @@ public class WebSession
if (stage > 0 && stage <= sla.getStageCount())
sla.setStage(this, stage);
}
else if (al instanceof RandomLessonAdapter)
{
try
{
RandomLessonAdapter rla = (RandomLessonAdapter) al;
int stage = myParser.getIntParameter(STAGE) - 1;
String[] stages = rla.getStages();
if (stage>0 && stage <= stages.length)
rla.setStage(this, stages[stage]);
} catch (ParameterNotFoundException pnfe) {}
}
}
// else update global variables for the current screen
else

0
webgoat/main/project/WebContent/WEB-INF/web.xml Normal file → Executable file
View File

View File

View File

@ -9,6 +9,7 @@ AbstractLesson currentLesson = webSession.getCurrentLesson();
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<%@page import="org.owasp.webgoat.lessons.SequentialLessonAdapter"%>
<%@page import="org.owasp.webgoat.lessons.RandomLessonAdapter"%>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
@ -199,6 +200,7 @@ StringBuffer buildList = new StringBuffer();
</div>
<%
AbstractLesson al = webSession.getCurrentLesson();
System.out.println("AL is a " + al.getClass().getName());
if (al instanceof SequentialLessonAdapter)
{
SequentialLessonAdapter sla = (SequentialLessonAdapter) al;
@ -215,6 +217,22 @@ StringBuffer buildList = new StringBuffer();
%></select></form><%
}
}
else if (al instanceof RandomLessonAdapter)
{
RandomLessonAdapter rla = (RandomLessonAdapter) al;
String[] stages = rla.getStages();
if (stages != null && stages.length > 0) {
%><form method="post" action="attack?menu=<%=webSession.getCurrentMenu()%>">
<select name="<%= WebSession.STAGE %>" onchange="this.form.submit();">
<%
String stage = rla.getStage(webSession);
for (int i=0; i<stages.length;i++) {
%><option <% if (stages[i].equals(stage)) out.print("selected"); %> value="<%= i+1 %>">Stage <%= i+1 %></option>
<%
}
%></select></form><%
}
}
%>
<div id="lessonContent"><%=webSession.getInstructions()%></div>
<div id="message" class="info"><%=webSession.getMessage()%></div>