diff --git a/pom.xml b/pom.xml index 715d327a42c6dea076850a79ad3394048370ca91..d55350863f47c6e05b55ed13be4377146e19297f 100644 --- a/pom.xml +++ b/pom.xml @@ -47,6 +47,12 @@ <artifactId>scrypt</artifactId> <version>1.4.0</version> </dependency> + <!-- https://mvnrepository.com/artifact/org.owasp.encoder/encoder --> + <dependency> + <groupId>org.owasp.encoder</groupId> + <artifactId>encoder</artifactId> + <version>1.2.2</version> + </dependency> </dependencies> <build> diff --git a/src/main/java/inf226/inchat/Account.java b/src/main/java/inf226/inchat/Account.java index 9bf2f4f1769d538a723ccea4afc77767dfeb8c61..c3279aed8bd52397ae8a3a87bbd8789ac98d2bea 100644 --- a/src/main/java/inf226/inchat/Account.java +++ b/src/main/java/inf226/inchat/Account.java @@ -1,9 +1,11 @@ package inf226.inchat; +import com.lambdaworks.crypto.SCryptUtil; import inf226.util.immutable.List; import inf226.util.Pair; import inf226.storage.*; +import org.eclipse.jetty.util.security.Password; /** * The Account class holds all information private to @@ -18,11 +20,14 @@ public final class Account { */ public final Stored<User> user; public final List<Pair<String,Stored<Channel>>> channels; + public final Password hashedPassword; public Account(Stored<User> user, - List<Pair<String,Stored<Channel>>> channels) { + List<Pair<String,Stored<Channel>>> channels, + Password hashedPassword) { this.user = user; this.channels = channels; + this.hashedPassword = hashedPassword; } /** @@ -33,8 +38,8 @@ public final class Account { **/ public static Account create(Stored<User> user, String password) { - // TODO: The password parameter is not used for anything. - return new Account(user,List.empty()); + Password hashedPassword = new Password(SCryptUtil.scrypt(password,16384,8,1)); // + return new Account(user,List.empty(), hashedPassword); } /** @@ -45,10 +50,7 @@ public final class Account { public Account joinChannel(String alias, Stored<Channel> channel) { Pair<String,Stored<Channel>> entry = new Pair<String,Stored<Channel>>(alias,channel); - return new Account - (user, - List.cons(entry, - channels)); + return new Account(user, List.cons(entry, channels), hashedPassword); } diff --git a/src/main/java/inf226/inchat/AccountStorage.java b/src/main/java/inf226/inchat/AccountStorage.java index f90bdbf930b1587ac7c5d56ecdafd79fcf1a7a3f..6b20d475a7b6175ba60d44b1ac28aa84b1a3f406 100644 --- a/src/main/java/inf226/inchat/AccountStorage.java +++ b/src/main/java/inf226/inchat/AccountStorage.java @@ -11,6 +11,7 @@ import inf226.storage.*; import inf226.util.immutable.List; import inf226.util.*; +import org.eclipse.jetty.util.security.Password; /** * This class stores accounts in the database. @@ -149,6 +150,7 @@ public final class AccountStorage final Stored<User> user = userStore.get(userid); // Get all the channels associated with this account final List.Builder<Pair<String,Stored<Channel>>> channels = List.builder(); + final String password = accountResult.getString("password"); while(channelResult.next()) { final UUID channelId = UUID.fromString(channelResult.getString("channel")); @@ -157,7 +159,7 @@ public final class AccountStorage new Pair<String,Stored<Channel>>( alias,channelStore.get(channelId))); } - return (new Stored<Account>(new Account(user,channels.getList()),id,version)); + return (new Stored<Account>(new Account(user,channels.getList(), new Password(password)),id,version)); } else { throw new DeletedException(); } diff --git a/src/main/java/inf226/inchat/Handler.java b/src/main/java/inf226/inchat/Handler.java index 45b4f6b29602cbf94ce5355ad9ce14b6d1fc4f2a..e5c456e14d65801cc0946a23abfa4021a147c150 100644 --- a/src/main/java/inf226/inchat/Handler.java +++ b/src/main/java/inf226/inchat/Handler.java @@ -29,6 +29,10 @@ import inf226.storage.*; import inf226.inchat.*; import inf226.util.*; +import org.owasp.encoder.Encode; + + + /** * The Hanlder class handles all HTTP and HTML components. * Functions called display⋯ and print⋯ output HTML. @@ -180,12 +184,12 @@ public class Handler extends AbstractHandler out.println("<!DOCTYPE html>"); out.println("<html lang=\"en-GB\">"); - printStandardHead(out, "inChat: " + alias); + printStandardHead(out, "inChat: " + Encode.forHtml(alias)); out.println("<body>"); - printStandardTop(out, "inChat: " + alias); + printStandardTop(out, "inChat: " + Encode.forHtml(alias)); out.println("<div class=\"main\">"); - printChannelList(out, account.value, alias); - printChannel(out, channel, alias); + printChannelList(out, account.value, Encode.forHtml(alias)); + printChannel(out, channel, Encode.forHtml(alias)); out.println("</div>"); out.println("</body>"); out.println("</html>"); @@ -215,7 +219,7 @@ public class Handler extends AbstractHandler if(target.equals("/joinChannel")) { out.println("<!DOCTYPE html>"); out.println("<html lang=\"en-GB\">"); - printStandardHead(out, "inChat: " + account.value.user.value.name); + printStandardHead(out, "inChat: " + Encode.forHtml(account.value.user.value.name)); out.println("<body>"); printStandardTop(out, "inChat – Join a channel!"); @@ -243,11 +247,11 @@ public class Handler extends AbstractHandler printStandardTop(out, "inChat: Edit message"); out.println("<script src=\"/script.js\"></script>"); - out.println("<form class=\"entry\" action=\"/channel/" + alias + "\" method=\"post\">"); + out.println("<form class=\"entry\" action=\"/channel/" + Encode.forHtml(alias) + "\" method=\"post\">"); out.println(" <div class=\"user\">You</div>"); out.println(" <input type=\"hidden\" name=\"editmessage\" value=\"Edit\">"); - out.println(" <input type=\"hidden\" name=\"message\" value=\"" + messageid + "\">"); - out.println(" <textarea id=\"messageInput\" class=\"messagebox\" placeholder=\"Post a message in this channel!\" name=\"content\">" + originalContent + "</textarea>"); + out.println(" <input type=\"hidden\" name=\"message\" value=\"" + Encode.forHtml(messageid) + "\">"); + out.println(" <textarea id=\"messageInput\" class=\"messagebox\" placeholder=\"Post a message in this channel!\" name=\"content\">" + Encode.forHtml(originalContent) + "</textarea>"); out.println(" <div class=\"controls\"><input style=\"float: right;\" type=\"submit\" name=\"edit\" value=\"Edit\"></div>"); out.println("</form>"); out.println("<script>"); @@ -346,9 +350,9 @@ public class Handler extends AbstractHandler if(target.equals("/")) { out.println("<!DOCTYPE html>"); out.println("<html lang=\"en-GB\">"); - printStandardHead(out, "inChat: " + account.value.user.value.name); + printStandardHead(out, "inChat: " + Encode.forHtml(account.value.user.value.name)); out.println("<body>"); - printStandardTop(out, "inChat: " + account.value.user.value.name); + printStandardTop(out, "inChat: " + Encode.forHtml(account.value.user.value.name)); out.println("<div class=\"main\">"); printChannelList(out, account.value, ""); out.println("<div class=\"channel\">Hello!</div>"); @@ -395,7 +399,7 @@ public class Handler extends AbstractHandler out.println("<style type=\"text/css\">code{white-space: pre;}</style>"); out.println("<link rel=\"stylesheet\" href=\"/style.css\">"); - out.println("<title>" + title + "</title>"); + out.println("<title>" + Encode.forHtml(title) + "</title>"); out.println("</head>"); } @@ -403,7 +407,7 @@ public class Handler extends AbstractHandler * Print the standard top with actions. */ private void printStandardTop(PrintWriter out, String topic) { - out.println("<h1 class=\"topic\"><a style=\"color: black;\" href=\"/\">"+ topic + "</a></h1>"); + out.println("<h1 class=\"topic\"><a style=\"color: black;\" href=\"/\">"+ Encode.forHtml(topic) + "</a></h1>"); out.println("<div class=\"actionbar\">"); out.println("<a class=\"action\" href=\"/create\">Create a channel!</a>"); out.println("<a class=\"action\" href=\"/joinChannel\">Join a channel!</a>"); @@ -419,7 +423,7 @@ public class Handler extends AbstractHandler out.println("<p>Your channels:</p>"); out.println("<ul class=\"chanlist\">"); account.channels.forEach( entry -> { - out.println("<li> <a href=\"/channel/" + entry.first + "\">" + entry.first + "</a></li>"); + out.println("<li> <a href=\"/channel/" + Encode.forHtml(entry.first) + "\">" + Encode.forHtml(entry.first) + "</a></li>"); }); out.println("</ul>"); out.println("</aside>"); @@ -437,7 +441,7 @@ public class Handler extends AbstractHandler out.println("<script src=\"/script.js\"></script>"); out.println("<script>subscribe(\"" + channel.identity +"\",\"" + channel.version + "\");</script>"); - out.println("<form class=\"entry\" action=\"/channel/" + alias + "\" method=\"post\">"); + out.println("<form class=\"entry\" action=\"/channel/" + Encode.forHtml(alias) + "\" method=\"post\">"); out.println(" <div class=\"user\">You</div>"); out.println(" <input type=\"hidden\" name=\"newmessage\" value=\"Send\">"); out.println(" <textarea id=\"messageInput\" class=\"messagebox\" placeholder=\"Post a message in this channel!\" name=\"message\"></textarea>"); @@ -454,7 +458,7 @@ public class Handler extends AbstractHandler out.println("<h4>Channel ID:</h4><br>" + channel.identity +"<br>"); out.println("<p><a href=\"/join?channelid=" + channel.identity + "\">Join link</a></p>"); - out.println("<h4>Set permissions</h4><form action=\"/channel/" + alias + "\" method=\"post\">"); + out.println("<h4>Set permissions</h4><form action=\"/channel/" + Encode.forHtml(alias) + "\" method=\"post\">"); out.println("<input style=\"width: 8em;\" type=\"text\" placeholder=\"User name\" name=\"username\">"); out.println("<select name=\"role\" required=\"required\">"); out.println("<option value=\"owner\">Owner</option>"); @@ -490,25 +494,25 @@ public class Handler extends AbstractHandler switch(e.value.type) { case message: out.println("<div class=\"entry\">"); - out.println(" <div class=\"user\">" + e.value.sender + "</div>"); - out.println(" <div class=\"text\">" + e.value.message); + out.println(" <div class=\"user\">" + Encode.forHtml(e.value.sender)+ "</div>"); + out.println(" <div class=\"text\">" + Encode.forHtml(e.value.message)); out.println(" </div>"); out.println(" <div class=\"messagecontrols\">"); - out.println(" <form style=\"grid-area: delete;\" action=\"/channel/" + channel.value.name + "\" method=\"POST\">"); + out.println(" <form style=\"grid-area: delete;\" action=\"/channel/" + Encode.forHtml(channel.value.name) + "\" method=\"POST\">"); out.println(" <input type=\"hidden\" name=\"message\" value=\""+ e.identity + "\">"); out.println(" <input type=\"submit\" name=\"deletemessage\" value=\"Delete\">"); out.println(" </form><form style=\"grid-area: edit;\" action=\"/editMessage\" method=\"POST\">"); out.println(" "); out.println(" <input type=\"hidden\" name=\"message\" value=\""+ e.identity + "\">"); - out.println(" <input type=\"hidden\" name=\"channelname\" value=\""+ channel.value.name + "\">"); - out.println(" <input type=\"hidden\" name=\"originalcontent\" value=\""+ e.value.message + "\">"); + out.println(" <input type=\"hidden\" name=\"channelname\" value=\""+ Encode.forHtml(channel.value.name) + "\">"); + out.println(" <input type=\"hidden\" name=\"originalcontent\" value=\""+ Encode.forHtml(e.value.message) + "\">"); out.println(" <input type=\"submit\" name=\"editmessage\" value=\"Edit\">"); out.println(" </form>"); out.println(" </div>"); out.println("</div>"); return; case join: - out.println("<p>" + formatter.format(e.value.time) + " " + e.value.sender + " has joined!</p>"); + out.println("<p>" + Encode.forHtml(formatter.format(e.value.time)) + " " + Encode.forHtml(e.value.sender) + " has joined!</p>"); return; } }); diff --git a/src/main/java/inf226/inchat/Password.java b/src/main/java/inf226/inchat/Password.java new file mode 100644 index 0000000000000000000000000000000000000000..3a6f0738a23a914c781214f0f010c111401def97 --- /dev/null +++ b/src/main/java/inf226/inchat/Password.java @@ -0,0 +1,33 @@ +package inf226.inchat; + +public final class Password { + + public final String password; + + public Password(String password) { + this.password = password; + } + + public boolean validPassword (String password) { + int hPLength = password.length(); + if (hPLength >= 6 && hPLength <= 30) { + int digits, upperCases, lowerCases; + digits = upperCases = lowerCases = 0; + for (char c : password.toCharArray()) { + if (Character.isDigit(c)) { + digits++; + } + if (Character.isUpperCase(c)) { + upperCases++; + } + if (Character.isLowerCase(c)) { + lowerCases++; + } + } + if (digits != 0 && upperCases != 0 && lowerCases != 0) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/inf226/inchat/UserName.java b/src/main/java/inf226/inchat/UserName.java new file mode 100644 index 0000000000000000000000000000000000000000..041e7c81283c23341f5a4186f33c49c5eef8f99b --- /dev/null +++ b/src/main/java/inf226/inchat/UserName.java @@ -0,0 +1,10 @@ +package inf226.inchat; + +public final class UserName { + + private final String username; + + public UserName(String uName) { + this.username = uName; + } +}