Skip to content

Commit

Permalink
🔒️ Hash admin password closes #84
Browse files Browse the repository at this point in the history
  • Loading branch information
McPringle committed Apr 13, 2024
1 parent a270da0 commit 794b15d
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 4 deletions.
9 changes: 9 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,10 @@
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
Expand Down Expand Up @@ -196,6 +200,11 @@
<artifactId>owasp-java-html-sanitizer</artifactId>
<version>20240325.1</version>
</dependency>
<dependency>
<groupId>commons-cli</groupId>
<artifactId>commons-cli</artifactId>
<version>1.5.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
Expand Down
23 changes: 20 additions & 3 deletions src/main/java/swiss/fihlon/apus/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,15 @@
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.server.PWA;
import com.vaadin.flow.theme.Theme;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.jetbrains.annotations.NotNull;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
import swiss.fihlon.apus.util.PasswordUtil;

/**
* The entry point of the Spring Boot application.
Expand All @@ -39,11 +45,22 @@
@PageTitle("Apus – Social Media Wall with Conference Agenda")
@PWA(name = "Apus", shortName = "Apus")
@Theme("apus")
@SuppressWarnings({"FinalClass", "HideUtilityClassConstructor"})
@SuppressWarnings({"FinalClass", "HideUtilityClassConstructor", "RegexpSingleline"})
public class Application implements AppShellConfigurator {

public static void main(final String[] args) {
SpringApplication.run(Application.class, args);
private static final Option HASH_PASSWORD_OPTION = new Option("p", "password", true, "Password to hash");

public static void main(@NotNull final String[] args) throws ParseException {
final var options = new Options();
options.addOption(HASH_PASSWORD_OPTION);

final var cmd = new DefaultParser().parse(options, args);
if (cmd.hasOption(HASH_PASSWORD_OPTION)) {
final var password = cmd.getOptionValue(HASH_PASSWORD_OPTION);
System.out.println(PasswordUtil.hashPassword(password));
} else {
SpringApplication.run(Application.class, args);
}
}

}
3 changes: 2 additions & 1 deletion src/main/java/swiss/fihlon/apus/ui/view/SocialView.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import swiss.fihlon.apus.configuration.Configuration;
import swiss.fihlon.apus.service.SocialService;
import swiss.fihlon.apus.social.Message;
import swiss.fihlon.apus.util.PasswordUtil;

import java.time.Duration;
import java.time.Instant;
Expand Down Expand Up @@ -112,7 +113,7 @@ private void showLoginDialog() {
}

private void handleLogin(@NotNull final String password) {
if (configuration.getAdmin().password().equals(password)) {
if (PasswordUtil.matches(password, configuration.getAdmin().password())) {
adminModeEnabled = true;
contextMenu.setTarget(null);
updateMessages();
Expand Down
39 changes: 39 additions & 0 deletions src/main/java/swiss/fihlon/apus/util/PasswordUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Apus - A social wall for conferences with additional features.
* Copyright (C) Marcus Fihlon and the individual contributors to Apus.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package swiss.fihlon.apus.util;

import org.jetbrains.annotations.NotNull;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

public final class PasswordUtil {

private static final BCryptPasswordEncoder PASSWORD_ENCODER = new BCryptPasswordEncoder();

public static String hashPassword(@NotNull final String password) {
return PASSWORD_ENCODER.encode(password);
}

public static boolean matches(@NotNull final String password, @NotNull final String hashedPassword) {
return PASSWORD_ENCODER.matches(password, hashedPassword);
}

private PasswordUtil() {
throw new IllegalStateException("Utility class");
}

}
68 changes: 68 additions & 0 deletions src/test/java/swiss/fihlon/apus/util/PasswordUtilTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Apus - A social wall for conferences with additional features.
* Copyright (C) Marcus Fihlon and the individual contributors to Apus.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package swiss.fihlon.apus.util;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

class PasswordUtilTest {

@Test
void positiveTests() {
final String rawPassword = "password";

// let's try it five times
final String hashedPassword1 = PasswordUtil.hashPassword(rawPassword);
final String hashedPassword2 = PasswordUtil.hashPassword(rawPassword);
final String hashedPassword3 = PasswordUtil.hashPassword(rawPassword);
final String hashedPassword4 = PasswordUtil.hashPassword(rawPassword);
final String hashedPassword5 = PasswordUtil.hashPassword(rawPassword);

// hashing the same password multiple times produces different hashes
assertNotEquals(hashedPassword1, hashedPassword2);
assertNotEquals(hashedPassword1, hashedPassword3);
assertNotEquals(hashedPassword1, hashedPassword4);
assertNotEquals(hashedPassword1, hashedPassword5);
assertNotEquals(hashedPassword2, hashedPassword1);
assertNotEquals(hashedPassword2, hashedPassword3);
assertNotEquals(hashedPassword2, hashedPassword4);
assertNotEquals(hashedPassword2, hashedPassword5);
assertNotEquals(hashedPassword3, hashedPassword1);
assertNotEquals(hashedPassword3, hashedPassword2);
assertNotEquals(hashedPassword3, hashedPassword4);
assertNotEquals(hashedPassword3, hashedPassword5);
assertNotEquals(hashedPassword4, hashedPassword1);
assertNotEquals(hashedPassword4, hashedPassword2);
assertNotEquals(hashedPassword4, hashedPassword3);
assertNotEquals(hashedPassword4, hashedPassword5);
assertNotEquals(hashedPassword5, hashedPassword1);
assertNotEquals(hashedPassword5, hashedPassword2);
assertNotEquals(hashedPassword5, hashedPassword3);
assertNotEquals(hashedPassword5, hashedPassword4);

// every hash should match the password
assertTrue(PasswordUtil.matches(rawPassword, hashedPassword1));
assertTrue(PasswordUtil.matches(rawPassword, hashedPassword2));
assertTrue(PasswordUtil.matches(rawPassword, hashedPassword3));
assertTrue(PasswordUtil.matches(rawPassword, hashedPassword4));
assertTrue(PasswordUtil.matches(rawPassword, hashedPassword5));
}

}

0 comments on commit 794b15d

Please sign in to comment.