Skip to content

Commit 0a57fe6

Browse files
predic8rraystchristiangoerdes
authored
BasicAuth: Tutorial Refactoring (#2735)
* Add security enhancements and improve utility handling in authentication workflow - Introduced `AlgoSalt` record for streamlined password hashing and salt handling. - Refactored `JdbcUserDataProvider` and `StaticUserDataProvider` to utilize enhanced hashing mechanisms. - Added comprehensive unit tests for new password hashing, salt extraction, and validation logic in `SecurityUtilsTest`. - Included a new tutorial class `BasicAuthenticationTutorialTest` for basic authentication scenarios with YAML configuration. * Refactor `JdbcUserDataProvider`: streamline SQL handling and improve logging * Add tests for `FileUserDataProvider` and refine user verification logic - Introduced comprehensive unit tests for `FileUserDataProvider` covering initialization, malformed inputs, empty files, and user verification. - Refactored user splitting logic to limit splits, fixing issues with passwords containing colons. - Improved exception handling for invalid file paths and malformed user entries. - Documented current behavior for handling colon usage in hashed passwords. * Enhance authentication handling: refine input sanitization, fix hash splitting, and add tests - Improved sanitization logic in `JdbcUserDataProvider` for table/column names using regex validation. - Fixed hash handling in `SecurityUtils` to enforce correct format and refine error messages. - Resolved colon splitting issue in `FileUserDataProviderTest` for hashed passwords. - Added extensive unit tests for `JdbcUserDataProvider` to verify initialization, input sanitization, and user verification scenarios. - Refactored `StaticUserDataProvider` to prevent redundant map population in `init` method. * Remove redundant `throws SQLException` declarations and streamline SQL handling in `JdbcUserDataProviderTest`. * Enhance authentication workflow: add `BasicAuthenticationUtil`, streamline user attribute filtering, and improve test coverage - Introduced `BasicAuthenticationUtil` for extracting and handling Basic authentication credentials. - Refactored `StaticUserDataProvider` to use a common `filterPassword` method for secure user attribute handling. - Enhanced `JdbcUserDataProvider` for safer bean initialization with null checks and better error messages. - Expanded test coverage with detailed tests for `BasicAuthenticationUtil` and user attribute filtering. - Improved robustness and maintainability of authentication components. * Refactor authentication workflow: replace `User` with `UserConfig`, centralize user attribute handling, and update tests * Refactor authentication workflow: remove redundant code, improve initialization and error handling, and update tests * Enhance `BasicAuthenticationInterceptor`: remove `Authorization` header by default after authentication, add configurability, and update tests * Refactor `StaticUserDataProvider`: simplify `usersByName` population logic and remove redundant `getUsersByName` calls * Fix JavaDoc formatting in `BasicAuthenticationInterceptor`: close `<p>` tag correctly * Enhance `BasicAuthenticationInterceptor`: improve logging, update user count display, and refine response headers for compliance * Enhance `BasicAuthenticationInterceptor`: improve logging, update user count display, and refine response headers for compliance * Update `BasicAuthenticationUtilTest`: mask sensitive information in `toString` and add assertion to verify it's not exposed * Refactor OAuth2 and authentication session logic: improve code readability by using `var`, remove unnecessary password filtering, update test methods, and streamline imports. * Remove redundant password assertion in `FileUserDataProviderTest` * resolved a few comments * add comment * added support for argon2id * fixed merge error * Refactor `FileUserDataProvider` to rename `htpasswdPath` methods to `location`. Update related test cases accordingly. * Rename `location` methods to `htpasswdPath` in `FileUserDataProviderElement` and update test case. * fix --------- Co-authored-by: Tobias Polley <mail@tobias-polley.de> Co-authored-by: Tobias Polley <polley@predic8.de> Co-authored-by: Christian Gördes <christian.goerdes@outlook.de> Co-authored-by: Christian Gördes <118011644+christiangoerdes@users.noreply.github.com>
1 parent 789e26c commit 0a57fe6

30 files changed

Lines changed: 2723 additions & 372 deletions

annot/src/main/java/com/predic8/membrane/annot/generator/Schemas.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ private void assembleDocumentation(Writer w, AbstractJavadocedInfo aji) throws I
167167
Doc doc = aji.getDoc(processingEnv);
168168

169169
String id = null;
170+
170171
if (aji instanceof ElementInfo ei) id = ei.getId();
171172

172173
if (doc == null && id == null) return;

core/src/main/java/com/predic8/membrane/core/cli/MembraneCommandLine.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,15 @@ private static Options getRootOptions() {
6969
addOption(builder("o").longOpt("output").argName("file").hasArg().desc("Output file (JWK, public).").build());
7070
}});
7171

72+
addSubcommand(new CliCommand("argon2id", "Computes an Argon2id hash value.") {{
73+
addOption(builder("pass").longOpt("password").argName("password").hasArg().desc("Password to hash.").build());
74+
addOption(builder("v").longOpt("version").argName("version").hasArg().desc("Version (decimal, default: 19).").build());
75+
addOption(builder("s").longOpt("salt").argName("salt").hasArg().desc("Salt to hash with (hex string, default: random 16 bytes).").build());
76+
addOption(builder("i").longOpt("iterations").argName("iterations").hasArg().desc("Number of iterations (default: 3).").build());
77+
addOption(builder("m").longOpt("memory").argName("memory").hasArg().desc("Memory in KiB (default: 65536).").build());
78+
addOption(builder("p").longOpt("parallelism").argName("parallelism").hasArg().desc("Parallelism (default: 1).").build());
79+
}});
80+
7281
}};
7382
}
7483

core/src/main/java/com/predic8/membrane/core/cli/RouterCLI.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,24 +23,29 @@
2323
import com.predic8.membrane.core.resolver.*;
2424
import com.predic8.membrane.core.router.*;
2525
import org.apache.commons.cli.*;
26+
import org.apache.commons.codec.binary.Hex;
2627
import org.jetbrains.annotations.*;
2728
import org.slf4j.*;
2829
import org.springframework.beans.factory.xml.*;
2930

3031
import java.io.*;
32+
import java.nio.charset.StandardCharsets;
33+
import java.security.SecureRandom;
3134
import java.util.*;
3235

3336
import static com.predic8.membrane.core.Constants.*;
3437
import static com.predic8.membrane.core.cli.util.JwkGenerator.*;
3538
import static com.predic8.membrane.core.config.spring.CheckableBeanFactory.*;
3639
import static com.predic8.membrane.core.config.spring.TrackingFileSystemXmlApplicationContext.*;
40+
import static com.predic8.membrane.core.interceptor.authentication.SecurityUtils.buildArgon2idPCH;
3741
import static com.predic8.membrane.core.openapi.serviceproxy.OpenAPISpec.YesNoOpenAPIOption.*;
3842
import static com.predic8.membrane.core.openapi.util.OpenAPIUtil.*;
3943
import static com.predic8.membrane.core.util.ExceptionUtil.*;
4044
import static com.predic8.membrane.core.util.OSUtil.*;
4145
import static com.predic8.membrane.core.util.URIUtil.*;
4246
import static com.predic8.membrane.core.util.text.TerminalColors.*;
4347
import static java.lang.Integer.*;
48+
import static org.apache.commons.cli.Option.builder;
4449
import static org.apache.commons.lang3.exception.ExceptionUtils.*;
4550

4651
public class RouterCLI {
@@ -78,6 +83,11 @@ private static void start(String[] args) {
7883
if (commandLine.getCommand().getName().equals("private-jwk-to-public")) {
7984
privateJwkToPublic(commandLine);
8085
}
86+
87+
if (commandLine.getCommand().getName().equals("argon2id")) {
88+
argon2id(commandLine);
89+
}
90+
8191
if (getRouter(commandLine) instanceof DefaultRouter dr)
8292
dr.waitFor();
8393
}
@@ -95,6 +105,39 @@ private static void privateJwkToPublic(MembraneCommandLine commandLine) {
95105
System.exit(0);
96106
}
97107

108+
private static void argon2id(MembraneCommandLine commandLine) {
109+
try {
110+
String password = commandLine.getCommand().getOptionValue("pass");
111+
String version = commandLine.getCommand().getOptionValue("v");
112+
String salt = commandLine.getCommand().getOptionValue("s");
113+
String iterations = commandLine.getCommand().getOptionValue("i");
114+
String memory = commandLine.getCommand().getOptionValue("m");
115+
String parallelism = commandLine.getCommand().getOptionValue("p");
116+
117+
int v = version == null ? 19 : Integer.parseInt(version);
118+
int i = iterations == null ? 3 : Integer.parseInt(iterations);
119+
int m = memory == null ? 65536 : Integer.parseInt(memory);
120+
int p = parallelism == null ? 1 : Integer.parseInt(parallelism);
121+
if (password == null) {
122+
System.out.println("Enter password to hash:");
123+
Scanner s = new Scanner(System.in);
124+
password = s.nextLine();
125+
}
126+
byte[] s = salt == null ? null : Hex.decodeHex(salt.toCharArray());
127+
if (s == null) {
128+
SecureRandom random = new SecureRandom();
129+
s = new byte[16];
130+
random.nextBytes(s);
131+
}
132+
133+
System.out.println(buildArgon2idPCH(password.getBytes(StandardCharsets.UTF_8), s, v, i, m, p));
134+
} catch (Exception e) {
135+
System.err.println(getExceptionMessageWithCauses(e));
136+
System.exit(1);
137+
}
138+
System.exit(0);
139+
}
140+
98141
private static void dryRun(MembraneCommandLine commandLine) {
99142
try {
100143
new TrackingFileSystemXmlApplicationContext(new String[]{getRulesFile(commandLine)}, false).refresh();

core/src/main/java/com/predic8/membrane/core/exceptions/ProblemDetails.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
*/
3636
public class ProblemDetails {
3737

38-
private static final Logger log = LoggerFactory.getLogger(ProblemDetails.class.getName());
38+
private static final Logger log = LoggerFactory.getLogger(ProblemDetails.class);
3939

4040
private static final ObjectMapper om = new ObjectMapper();
4141
private static final ObjectWriter ow = om.writerWithDefaultPrettyPrinter();

core/src/main/java/com/predic8/membrane/core/http/Header.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,10 @@ public void setConnection(String connection) {
377377
setValue(CONNECTION, connection);
378378
}
379379

380+
public void setWwwAuthenticate(String realm) {
381+
setValue(WWW_AUTHENTICATE, "Basic realm=\""+realm +"\"");
382+
}
383+
380384
public String getProxyConnection() {
381385
return getFirstValue(PROXY_CONNECTION);
382386
}

0 commit comments

Comments
 (0)