Skip to content
Snippets Groups Projects
Commit 6f94c552 authored by Tim Miller's avatar Tim Miller
Browse files

ADD: SWEN90006 assignment 1.

parent 5abcc113
No related branches found
No related tags found
No related merge requests found
Showing
with 529 additions and 2 deletions
# SWEN90006-A1-2019
<project name="Project" default="default">
<target name="check_prog">
<fail message="Please provide a program to test with -Dprogam=''. The arguments can be
one of {original, mutant-1, mutant-2, mutant-3, mutant-4, mutant-5}">
<condition>
<or>
<not><isset property="program"/></not>
<not><contains
string="original,mutant-1,mutant-2,mutant-3,mutant-4,mutant-5" substring="${program}"/></not>
</or>
</condition>
</fail>
</target>
<target name="check_test">
<fail message="Please provide a test with -Dtest=''. The arguments can be one of
{boundary, partitioning}">
<condition>
<or>
<not><isset property="test"/></not>
<not><contains string="Boundary,Partitioning" substring="${test}"/></not>
</or>
</condition>
</fail>
</target>
<target name="compile_prog" depends="check_prog">
<mkdir dir="classes/programs/${program}" />
<depend srcdir="programs/${program}" destdir="classes/programs/${program}"
cache=".depcache/programs/${program}" closure="yes"/>
<javac srcdir="programs/${program}" destdir="classes/programs/${program}"
classpath="lib/junit-4.11.jar;lib/hamcrest-core-1.3.jar" includeantruntime="false"/>
</target>
<target name="compile_orig">
<mkdir dir="classes/programs/original" />
<depend srcdir="tests" destdir="classes/programs/original"
cache=".depcache/programs/original" closure="yes"/>
<javac srcdir="programs/original" destdir="classes/programs/original"
classpath="lib/junit-4.11.jar;lib/hamcrest-core-1.3.jar" includeantruntime="false"/>
</target>
<target name="compile_test" depends="compile_orig, check_test">
<mkdir dir="classes/tests/${test}"/>
<depend srcdir="tests/${test}" destdir="classes/tests/${test}"
cache=".depcache/tests/${test}" closure="yes"/>
<javac srcdir="tests/${test}" destdir="classes/tests/${test}"
classpath="lib/junit-4.11.jar;lib/hamcrest-core-1.3.jar;classes/programs/original"
includeantruntime="false"/>
</target>
<target name="test" depends="compile_prog, compile_test">
<mkdir dir="results"/>
<parallel threadCount="1" timeout="5000">
<sequential>
<junit printsummary="yes" fork="yes" haltonfailure="yes">
<classpath>
<pathelement path="classes/programs/${program}"/>
<pathelement path="classes/tests/${test}"/>
<pathelement path="lib/junit-4.11.jar"/>
<pathelement path="lib/hamcrest-core-1.3.jar"/>
</classpath>
<formatter type="plain"/>
<test name="swen90006.passbook.${test}Tests" todir="results"
outfile="${test}_results.${program}"/>
</junit>
</sequential>
</parallel>
</target>
<target name="clean">
<delete dir="classes"/>
<delete dir=".depcache"/>
<delete><fileset dir="results" includes="**/*"/></delete>
</target>
</project>
File added
File added
package swen90006.passbook;
public class AlreadyLoggedInException extends Exception
{
public AlreadyLoggedInException(String username)
{
super("Username already logged in: " + username);
}
}
package swen90006.passbook;
public class DuplicateUserException extends Exception
{
public DuplicateUserException(String username)
{
super("Username already exists: " + username);
}
}
package swen90006.passbook;
public class IncorrectPassphraseException extends Exception
{
public IncorrectPassphraseException(String username, String passphrase)
{
super("Incorrect passphrase: " + passphrase + " for user " + username);
}
}
package swen90006.passbook;
public class InvalidSessionIDException extends Exception
{
public InvalidSessionIDException(Integer sessionID)
{
super("Invalid session ID: " + sessionID);
}
}
package swen90006.passbook;
import java.net.URL;
public class NoSuchURLException extends Exception
{
public NoSuchURLException (String username, URL url)
{
super("User " + username + " does not have password for URL " + url.toString());
}
}
package swen90006.passbook;
public class NoSuchUserException extends Exception
{
public NoSuchUserException (String username)
{
super("Username does not exist: " + username);
}
}
package swen90006.passbook;
/**
* A pair of objects.
*/
public class Pair<X, Y>
{
private X first;
private Y second;
public Pair(X first, Y second)
{
this.first = first;
this.second = second;
}
public X getFirst()
{
return this.first;
}
public Y getSecond()
{
return this.second;
}
public boolean equals(Pair<X, Y> other)
{
return first.equals(other.first) && second.equals(other.second);
}
}
package swen90006.passbook;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;
import java.net.URL;
import java.net.MalformedURLException;
import java.util.Random;
import java.util.Arrays;
/**
* PassBook is a (fictional) online password manager. A password
* manager is a software application that generates, stores, and
* retrieves login details for users.
*
* A user has an account with PassBook. This account is protected by a
* master password, or passphrase. Each user can store login details
* for multiple websites, identified by their URL. A user can add a
* login details for a given website. The passphrase is used to
* encrypt all passwords, but for this implementation, we have ignored
* encryption.
*
* The PassBook passphrase must conform to the following requirements:
* Must be at least eight characters long and contain at
* least one digit (range 0-9), one letter in the range 'a'-'z', and
* one letter in the range 'A'-'Z'.
*
* Username and passwords for stored URLs have no password
* requirements.
*
* When a user logs into PassBook, they are given a session ID. This
* session ID is used to identify them for all transactions until they
* log out.
*/
public class PassBook
{
/** The minimum length of a passphrase */
public final static int MINIMUM_PASSPHRASE_LENGTH = 8;
/** Valid URL protocols for the passbook */
public final static String [] VALID_URL_PROTOCOLS = {"http", "https"};
//The passphrases (master passwords) for all users
private Map<String, String> passphrases;
//The login details for all users and the URLs they have stored
private Map<String, PasswordTable> details;
//Mapping from session IDs to usernames
private Map<Integer, String> userIDs;
//Mapping from usernames to sessionIDs
private Map<String, Integer> sessionIDs;
/**
* Constructs an empty passbook.
*/
public PassBook()
{
passphrases = new HashMap<String, String>();
details = new HashMap<String, PasswordTable>();
userIDs = new HashMap<Integer, String>();
sessionIDs = new HashMap<String, Integer>();
}
/**
* Adds a new user to the passbook.
*
* @param passbookUsername the username for the user to be added
* @param passphrase the passphrase (master password) for the user
* @throws DuplicateUserException if the username is already in the passbook
* @throws WeakPassphraseException if the password does not fit the passphrase
rules (see class documentation)
*
* Assumption: passbookUsername and passphrase are non-null
*/
public void addUser(String passbookUsername, String passphrase)
throws DuplicateUserException, WeakPassphraseException
{
//Check if this user exists
if (passphrases.containsKey(passbookUsername)) {
throw new DuplicateUserException(passbookUsername);
}
//check the passphrase strength
else {
if (passphrase.length() < MINIMUM_PASSPHRASE_LENGTH) {
throw new WeakPassphraseException(passphrase);
}
boolean containsLowerCase = false;
boolean containsUpperCase = false;
boolean containsNumber = false;
for (int i = 0; i < passphrase.length(); i++) {
if ('a' <= passphrase.charAt(i) && passphrase.charAt(i) <= 'z') {
containsLowerCase = true;
}
else if ('A' <= passphrase.charAt(i) && passphrase.charAt(i) <= 'Z') {
containsUpperCase = true;
}
else if ('0' <= passphrase.charAt(i) && passphrase.charAt(i) <= '9') {
containsNumber = true;
}
}
if (!containsLowerCase || !containsUpperCase || !containsNumber) {
throw new WeakPassphraseException(passphrase);
}
}
passphrases.put(passbookUsername, passphrase);
PasswordTable pt = new PasswordTable();
details.put(passbookUsername, pt);
}
/**
* Checks if a user exists
* @param passbookUsername the passbookUsername
* @return true if and only if this user is in the passbook
*
* Assumption: passbookUsername is non-null
*/
public boolean isUser(String passbookUsername)
{
return passphrases.containsKey(passbookUsername);
}
/**
* Logs a user into the passbook.
*
* @param passbookUsername the passbookUsername for the user
* @param passphrase the passphrase (master password) for the user
* @returns a session ID greater than or equal to 0
* @throws NoSuchUserException if the user does not exist in the passbook
* @throws AlreadyLoggedInException if the user is already logged in
* @throws IncorrectPassphraseException if the passphrase is incorrect for this user
*
* Assumption: passbookUsername and passphrase are non-null
*/
public int loginUser(String passbookUsername, String passphrase)
throws NoSuchUserException, AlreadyLoggedInException, IncorrectPassphraseException
{
//check that the user exists
if (!passphrases.containsKey(passbookUsername)) {
throw new NoSuchUserException(passbookUsername);
}
else if (sessionIDs.get(passbookUsername) != null) {
throw new AlreadyLoggedInException(passbookUsername);
}
else if (!passphrases.get(passbookUsername).equals(passphrase)) {
throw new IncorrectPassphraseException(passbookUsername, passphrase);
}
//generate a random session ID that is not already taken
int sessionID = new Random().nextInt(Integer.MAX_VALUE);
while (userIDs.containsKey(sessionID)) {
sessionID = new Random().nextInt(Integer.MAX_VALUE);
}
//add the session ID
sessionIDs.put(passbookUsername, sessionID);
userIDs.put(sessionID, passbookUsername);
return sessionID;
}
/**
* Logs out a user based on session ID. Has no affect if the session ID does not exist.
*
* @param sessionID the session ID to be terminated
*
* Assumption: session ID is non-null
*/
public void logoutUser(Integer sessionID)
{
sessionIDs.remove(userIDs.get(sessionID));
userIDs.remove(sessionID);
}
/**
* Updates the logic details for a URL of a user. If the url
* username or password are null, the username/password details
* for this URL are removed.
*
* @param sessionID the session ID for the logged-in user
* @param urlUsername the username for the user to be added
* @param url the URL for the username/password pair
* @param urlPassword the password to be add
* @throws InvalidSessionIDException if the session ID does not exists
* @throws MalformedURLException if the protocol is not 'http' or 'https'
*
* Assumption: url is non-null and a valid URL object
* Assumption: sessionID is non-null
*/
public void updateDetails(Integer sessionID, URL url, String urlUsername, String urlPassword)
throws InvalidSessionIDException, MalformedURLException
{
//check that the session ID exists
String passbookUsername = userIDs.get(sessionID);
if (passbookUsername == null) {
throw new InvalidSessionIDException(sessionID);
}
else if (!Arrays.asList(VALID_URL_PROTOCOLS).contains(url.getProtocol())) {
throw new MalformedURLException(passbookUsername);
}
PasswordTable pt = details.get(passbookUsername);
if (urlUsername == null || urlPassword == null) {
pt.remove(url);
}
else {
pt.put(url, new Pair<String, String> (urlUsername, urlPassword));
details.put(passbookUsername, pt);
}
}
/**
* Retrieves login details for a given URL and user.
*
* @param sessionID the session ID
* @param url the URL for the password being retrieved.
* @returns the username and password for a given url for the corresponding user
* @throws NoSuchUserException if the username does not exist in the passbook
* @throws MalformedURLException if the syntax is invalid (see class documentation)
* @throws NoSuchURLException if user does not have login for this URL
*
* Assumption: url is non-null and a valid URL object
* Assumption: sessionID is non-null
*/
public Pair<String, String> retrieveDetails(Integer sessionID, URL url)
throws InvalidSessionIDException, MalformedURLException, NoSuchURLException
{
//check that the session ID exists
String passbookUsername = userIDs.get(sessionID);
if (passbookUsername == null) {
throw new InvalidSessionIDException(sessionID);
}
else if (!Arrays.asList(VALID_URL_PROTOCOLS).contains(url.getProtocol())) {
throw new MalformedURLException(passbookUsername);
}
PasswordTable pt = details.get(passbookUsername);
//if this returned nothing, the user has no details for any url
if (pt == null) {
throw new NoSuchURLException(passbookUsername, url);
}
Pair<String, String> pair = pt.get(url);
//if this returned nothing, the user does not have a login for this url
if (pair == null) {
throw new NoSuchURLException(passbookUsername, url);
}
return pair;
}
//A simple label to improve code readability
private class PasswordTable extends HashMap<URL, Pair<String, String>> {}
}
package swen90006.passbook;
public class WeakPassphraseException extends Exception
{
public WeakPassphraseException (String passphrase)
{
super("Passphrase does not comply with the PassBook rules\n" +
"\t- must contains at least " +
PassBook.MINIMUM_PASSPHRASE_LENGTH + " characters\n" +
"\t- must contain at least one numeric character\n" +
"\t- must contain at least one lower case letter\n" +
"\t- must contain at least one upper case letter\n");
}
}
package swen90006.passbook;
public class AlreadyLoggedInException extends Exception
{
public AlreadyLoggedInException(String username)
{
super("Username already logged in: " + username);
}
}
package swen90006.passbook;
public class DuplicateUserException extends Exception
{
public DuplicateUserException(String username)
{
super("Username already exists: " + username);
}
}
package swen90006.passbook;
public class IncorrectPassphraseException extends Exception
{
public IncorrectPassphraseException(String username, String passphrase)
{
super("Incorrect passphrase: " + passphrase + " for user " + username);
}
}
package swen90006.passbook;
public class InvalidSessionIDException extends Exception
{
public InvalidSessionIDException(Integer sessionID)
{
super("Invalid session ID: " + sessionID);
}
}
package swen90006.passbook;
import java.net.URL;
public class NoSuchURLException extends Exception
{
public NoSuchURLException (String username, URL url)
{
super("User " + username + " does not have password for URL " + url.toString());
}
}
package swen90006.passbook;
public class NoSuchUserException extends Exception
{
public NoSuchUserException (String username)
{
super("Username does not exist: " + username);
}
}
package swen90006.passbook;
/**
* A pair of objects.
*/
public class Pair<X, Y>
{
private X first;
private Y second;
public Pair(X first, Y second)
{
this.first = first;
this.second = second;
}
public X getFirst()
{
return this.first;
}
public Y getSecond()
{
return this.second;
}
public boolean equals(Pair<X, Y> other)
{
return first.equals(other.first) && second.equals(other.second);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment