Skip to content

Commit 7e749ff

Browse files
fix(jaas): fix jaas login (#12848)
1 parent 3ce7651 commit 7e749ff

8 files changed

+635
-18
lines changed

datahub-frontend/app/security/AuthenticationManager.java

+45-11
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,65 @@
33
import com.google.common.base.Preconditions;
44
import javax.annotation.Nonnull;
55
import javax.naming.AuthenticationException;
6+
import javax.security.auth.callback.Callback;
7+
import javax.security.auth.callback.CallbackHandler;
8+
import javax.security.auth.callback.NameCallback;
9+
import javax.security.auth.callback.PasswordCallback;
10+
import javax.security.auth.login.LoginContext;
11+
import javax.security.auth.login.LoginException;
612
import org.apache.commons.lang3.StringUtils;
7-
import org.eclipse.jetty.security.UserPrincipal;
8-
import org.eclipse.jetty.util.security.Credential;
13+
import org.slf4j.Logger;
14+
import org.slf4j.LoggerFactory;
915

1016
public class AuthenticationManager {
17+
private static final Logger log = LoggerFactory.getLogger(AuthenticationManager.class);
18+
1119
private AuthenticationManager() {} // Prevent instantiation
1220

1321
public static void authenticateJaasUser(@Nonnull String userName, @Nonnull String password)
1422
throws Exception {
1523
Preconditions.checkArgument(!StringUtils.isAnyEmpty(userName), "Username cannot be empty");
1624

1725
try {
18-
// Create and configure credentials for authentication
19-
UserPrincipal userPrincipal = new UserPrincipal(userName, Credential.getCredential(password));
26+
// Create a login context with our custom callback handler
27+
LoginContext loginContext =
28+
new LoginContext("WHZ-Authentication", new WHZCallbackHandler(userName, password));
2029

21-
// Verify credentials
22-
if (!userPrincipal.authenticate(password)) {
23-
throw new AuthenticationException("Invalid credentials for user: " + userName);
24-
}
30+
// Attempt login
31+
loginContext.login();
32+
33+
// If we get here, authentication succeeded
34+
log.debug("Authentication succeeded for user: {}", userName);
2535

26-
} catch (Exception e) {
36+
} catch (LoginException le) {
37+
log.info("Authentication failed for user {}: {}", userName, le.getMessage());
2738
AuthenticationException authenticationException =
28-
new AuthenticationException("Authentication failed");
29-
authenticationException.setRootCause(e);
39+
new AuthenticationException(le.getMessage());
40+
authenticationException.setRootCause(le);
3041
throw authenticationException;
3142
}
3243
}
44+
45+
private static class WHZCallbackHandler implements CallbackHandler {
46+
private final String password;
47+
private final String username;
48+
49+
private WHZCallbackHandler(@Nonnull String username, @Nonnull String password) {
50+
this.username = username;
51+
this.password = password;
52+
}
53+
54+
@Override
55+
public void handle(@Nonnull Callback[] callbacks) {
56+
for (Callback callback : callbacks) {
57+
if (callback instanceof NameCallback) {
58+
NameCallback nc = (NameCallback) callback;
59+
nc.setName(username);
60+
} else if (callback instanceof PasswordCallback) {
61+
PasswordCallback pc = (PasswordCallback) callback;
62+
pc.setPassword(password.toCharArray());
63+
}
64+
}
65+
}
66+
}
3367
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package security;
2+
3+
import java.security.Principal;
4+
5+
public class DataHubUserPrincipal implements Principal {
6+
private final String name;
7+
8+
public DataHubUserPrincipal(String name) {
9+
this.name = name;
10+
}
11+
12+
@Override
13+
public String getName() {
14+
return name;
15+
}
16+
17+
@Override
18+
public boolean equals(Object o) {
19+
if (this == o) return true;
20+
if (o == null || getClass() != o.getClass()) return false;
21+
22+
DataHubUserPrincipal that = (DataHubUserPrincipal) o;
23+
return name.equals(that.name);
24+
}
25+
26+
@Override
27+
public int hashCode() {
28+
return name.hashCode();
29+
}
30+
31+
@Override
32+
public String toString() {
33+
return "DataHubUserPrincipal[" + name + "]";
34+
}
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package security;
2+
3+
import java.io.File;
4+
import java.io.FileInputStream;
5+
import java.io.IOException;
6+
import java.util.Map;
7+
import java.util.Properties;
8+
import javax.security.auth.Subject;
9+
import javax.security.auth.callback.Callback;
10+
import javax.security.auth.callback.CallbackHandler;
11+
import javax.security.auth.callback.NameCallback;
12+
import javax.security.auth.callback.PasswordCallback;
13+
import javax.security.auth.callback.UnsupportedCallbackException;
14+
import javax.security.auth.login.LoginException;
15+
import javax.security.auth.spi.LoginModule;
16+
import org.slf4j.Logger;
17+
import org.slf4j.LoggerFactory;
18+
19+
public class PropertyFileLoginModule implements LoginModule {
20+
private static final Logger log = LoggerFactory.getLogger(PropertyFileLoginModule.class);
21+
22+
private Subject subject;
23+
private CallbackHandler callbackHandler;
24+
private boolean debug = false;
25+
private String file;
26+
private boolean succeeded = false;
27+
private String username;
28+
29+
@Override
30+
public void initialize(
31+
Subject subject,
32+
CallbackHandler callbackHandler,
33+
Map<String, ?> sharedState,
34+
Map<String, ?> options) {
35+
this.subject = subject;
36+
this.callbackHandler = callbackHandler;
37+
38+
// Get configuration options
39+
this.debug = "true".equalsIgnoreCase((String) options.get("debug"));
40+
this.file = (String) options.get("file");
41+
42+
if (debug) {
43+
log.debug("PropertyFileLoginModule initialized with file: {}", file);
44+
}
45+
}
46+
47+
@Override
48+
public boolean login() throws LoginException {
49+
// If no file specified, this module can't authenticate
50+
if (file == null) {
51+
if (debug) log.debug("No property file specified");
52+
return false;
53+
}
54+
55+
// Get username and password from callbacks
56+
NameCallback nameCallback = new NameCallback("Username: ");
57+
PasswordCallback passwordCallback = new PasswordCallback("Password: ", false);
58+
59+
try {
60+
callbackHandler.handle(new Callback[] {nameCallback, passwordCallback});
61+
} catch (IOException | UnsupportedCallbackException e) {
62+
if (debug) log.debug("Error getting callbacks", e);
63+
throw new LoginException("Error during callback handling: " + e.getMessage());
64+
}
65+
66+
this.username = nameCallback.getName();
67+
char[] password = passwordCallback.getPassword();
68+
passwordCallback.clearPassword();
69+
70+
if (username == null || username.isEmpty() || password == null) {
71+
if (debug) log.debug("Username or password is empty");
72+
return false;
73+
}
74+
75+
// Load properties file
76+
Properties props = new Properties();
77+
File propsFile = new File(file);
78+
79+
if (!propsFile.exists() || !propsFile.isFile() || !propsFile.canRead()) {
80+
if (debug) log.debug("Cannot read property file: {}", file);
81+
return false;
82+
}
83+
84+
try (FileInputStream fis = new FileInputStream(propsFile)) {
85+
props.load(fis);
86+
} catch (IOException e) {
87+
if (debug) log.debug("Failed to load property file", e);
88+
throw new LoginException("Error loading property file: " + e.getMessage());
89+
}
90+
91+
// Check if username exists and password matches
92+
String storedPassword = props.getProperty(username);
93+
if (storedPassword == null) {
94+
if (debug) log.debug("User not found: {}", username);
95+
return false;
96+
}
97+
98+
// Compare passwords
99+
succeeded = storedPassword.equals(new String(password));
100+
101+
if (debug) {
102+
if (succeeded) {
103+
log.debug("Authentication succeeded for user: {}", username);
104+
} else {
105+
log.debug("Authentication failed for user: {}", username);
106+
}
107+
}
108+
109+
return succeeded;
110+
}
111+
112+
@Override
113+
public boolean commit() throws LoginException {
114+
if (!succeeded) {
115+
return false;
116+
}
117+
118+
// Add principal to the subject if authentication succeeded
119+
subject.getPrincipals().add(new DataHubUserPrincipal(username));
120+
121+
return true;
122+
}
123+
124+
@Override
125+
public boolean abort() throws LoginException {
126+
succeeded = false;
127+
username = null;
128+
return true;
129+
}
130+
131+
@Override
132+
public boolean logout() throws LoginException {
133+
// Remove principals that were added by this module
134+
subject.getPrincipals().removeIf(p -> p instanceof DataHubUserPrincipal);
135+
succeeded = false;
136+
username = null;
137+
return true;
138+
}
139+
}

datahub-frontend/conf/jaas.conf

+8-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
// This is a sample JAAS config that uses the following login module
2-
// org.eclipse.jetty.jaas.spi.PropertyFileLoginModule -- this module can work with a username and any password defined in the `../conf/user.props` file
2+
// security.PropertyFileLoginModule -- this module can work with a username and any password defined in the `../conf/user.props` file
33

44
WHZ-Authentication {
5-
org.eclipse.jetty.jaas.spi.PropertyFileLoginModule sufficient debug="true" file="/etc/datahub/plugins/frontend/auth/user.props";
6-
org.eclipse.jetty.jaas.spi.PropertyFileLoginModule sufficient debug="true" file="/datahub-frontend/conf/user.props";
7-
};
5+
security.PropertyFileLoginModule sufficient
6+
debug="true"
7+
file="/etc/datahub/plugins/frontend/auth/user.props";
8+
security.PropertyFileLoginModule sufficient
9+
debug="true"
10+
file="/datahub-frontend/conf/user.props";
11+
};

datahub-frontend/run/jaas.conf

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
// This is a sample JAAS config that uses the following login module
2-
// This is a sample JAAS config that uses the following login module
3-
// org.eclipse.jetty.jaas.spi.PropertyFileLoginModule -- this module can work with a username and any password defined in the `../conf/user.props` file
2+
// security.PropertyFileLoginModule -- this module can work with a username and any password defined in the `../conf/user.props` file
43

54
WHZ-Authentication {
6-
org.eclipse.jetty.jaas.spi.PropertyFileLoginModule sufficient
5+
security.PropertyFileLoginModule sufficient
76
debug="true"
87
file="../conf/user.props";
98
};

0 commit comments

Comments
 (0)