Websphere Portal 8 login service check existing password correct - ldap

I have a portlet that is used to change the password of the logged in user. The user has to enter their current password and a new password twice. It calls the LoginService.checkPassword(String userId, char[] password) to determine if the existing password is correct. If this method returns true, then the password is updated by getting the current user via the Puma Profile and setting the updated attribute using the PumaController.setAttributes(User user, Map attributes) call. Then the puma profile is reloaded via the PumaProfile.reload method (I've also tried using the PumaController.reload() method.
The problem I'm facing is that the LoginService.checkPassword(String userId, char[] password) is returning true for the current password and also older passwords rather than just for the current password. Does anyone know why this would be?
In ldap the password field is a single attribute field, as it is also in wimdomain.xml, and if I log out and try to log in, I can only log in with the current password (as you would expect).

Found it's a bug in Portal. Even if you use the out of the box profile edit portlet, you get the same issue

Verify Websphere Portal User's Password
There are two ways to verify user's password -
1) Use "UserRegistry"
public static boolean checkUserAuthenticatedLDAP(String userId, String password) {
try {
Context ctx = new InitialContext();
com.ibm.websphere.security.UserRegistry reg =
(com.ibm.websphere.security.UserRegistry) ctx.lookup("UserRegistry");
String res = reg.checkPassword(userId, password);
return res != null;
} catch (Exception ex) {
return false;
}
}
2) Use "LoginContext"
/**
* This method validates the user based on the user id and password
* attributes, If the user id or password is not valid then throws Exception.
*
* #param userId
* #param password
* #return boolean
* #throws Exception
*/
public boolean checkUserAuthenticated(String userId, String password) throws Exception {
javax.security.auth.login.LoginContext loginContext = null;
Subject subject = null;
try {
loginContext = new javax.security.auth.login.LoginContext("WSLogin",
new com.ibm.websphere.security.auth.callback.WSCallbackHandlerImpl(userId, password));
} catch (javax.security.auth.login.LoginException e) {
throw new Exception("Cannot create LoginContext", e);
}
try {
loginContext.login();
subject = loginContext.getSubject();
} catch (com.ibm.websphere.security.auth.WSLoginFailedException e) {
throw new Exception("Password is incorrect", e);
} catch (Exception e) {
throw new Exception("Unknown username", e);
}
if (subject == null)
throw new Exception("Password is incorrect");
return true;
}

Related

Eclipse Scout authentication failure - No UserAgent set on calling context

I am using Eclipse Scout 22 and I connect my Scout application to a REST server using a modified credential verifier for user authentication. I just discovered that if I try to login using any other username apart from admin, login fails, and I get the following message on the Eclipse IDE console
No UserAgent set on calling context; include default in service-request
2023-01-28 18:17:45,280 WARN [qtp1624820151-19] org.eclipse.scout.rt.shared.servicetunnel.AbstractServiceTunnel.interceptRequest(AbstractServiceTunnel.java:84) - No UserAgent set on calling context; include default in service-request - MDC[]
Here is my credential verifier
package org.eclipse.scout.apps.ygapp.shared.security;
public class RestCredentialVerifier implements ICredentialVerifier {
private static final Logger LOG = LoggerFactory.getLogger(RestCredentialVerifier.class);
#Override
public int verify(String username, char[] passwordPlainText) throws IOException {
LOG.debug("Method \"verify\" in RestCredentialVerifier. User " + username);
// Test for missing username or password
if (StringUtility.isNullOrEmpty(username) || passwordPlainText == null
|| passwordPlainText.length == 0) {
throw new VetoException(TEXTS.get("MissingUsernameOrPassword"));
}
// Test for non-conforming password
// Password MUST have between 8 to 20 characters with a minimum of one uppercase, one lowercase,
// one number, one special character and without spaces
if ((passwordPlainText.length < 8) || (passwordPlainText.length > 20)) {
throw new VetoException(TEXTS.get("ThePasswordMustHaveBetween820Characters"));
}
Subject subject = new Subject();
subject.getPrincipals().add(new SimplePrincipal("system"));
subject.setReadOnly();
RunContext runContext = RunContexts.empty().withLocale(Locale.getDefault()); // OK
// RunContext runContext = RunContexts.copyCurrent(true).withSubject(subject); // Fails
Map<String, String> result = runContext.call(new Callable<Map<String, String>>() {
#Override
public Map<String, String> call() throws Exception {
return BEANS.get(IRestAuthenticationService.class).verify(username, passwordPlainText));
}
});
LOG.debug("Leaving method \"verify\" in RestCredentialVerifier. User " + username);
if (result.containsKey("message")
&& result.get("message").equals(TEXTS.get("YouAreNowConnectedToTheServer"))) {
return AUTH_OK;
} else {
return AUTH_FAILED;
}
}
}
Thanks a lot for your kind assistance.
Cheers,
JDaniel

Get all user's names from ldap servers

I'm a college student and i am doing an aplication in springboot to authenticate a user with ldap.
I was able to do that with ldap.unboundid but now i want to display the name of all users on the ldap servers, is that possible and if so, could you give some examples?
Here his my code that authenticates the user in ldap:
public class LDAPAuthentication implements Authentication {
LdapConfigurations ldapConfig;
LDAPConnection ldapConnection;
SearchResult searchResult;
public LDAPAuthentication(LdapConfigurations ldapConfig) {
this.ldapConfig = ldapConfig;
}
#Override
public UserEntity authenticate(String username, String password) {
try {
LDAPURL ldapUrl = new LDAPURL(ldapConfig.getUrl());
LDAPConnectionOptions ldapConnectionOptions = new LDAPConnectionOptions();
ldapConnectionOptions.setConnectTimeoutMillis(50);
ldapConnection = new LDAPConnection(ldapConnectionOptions, ldapUrl.getHost(), ldapUrl.getPort(),
username + ldapConfig.getLdapDomain(), password);
ldapConnection.bind(username + ldapConfig.getLdapDomain(), password);
String lookup = String.format("(%s=%s)", "sAMAccountName", username);
SearchRequest searchRequest = new SearchRequest(ldapConfig.getBaseDn(), SearchScope.SUB, lookup);
searchResult = ldapConnection.search(searchRequest);
ldapConnection.close();
} catch (LDAPException e) {
e.printStackTrace();
return null;
}
If you're querying AD, you'll need to make changes to AD, or, you'll need paged results. search for "The Simple Paged Results Control" at https://docs.ldap.com/ldap-sdk/docs/getting-started/controls.html

In 389 DS if LDAP admin changes password then it replaces last password from history

Under 389 DS we have setup as password policy to save last 5 password in history, so that those can not be used.
We have java application under which password is changed as LDAP administrator. But when password is changed instead of adding new password in password history, it replaces old password with new password. This enables user to change password to the password used in past.
Eg.
User sets password to abc, then history is {abc}.
User changes password to efg, then history is { abc, efg }
Administrator changes password to xyz, then history is expected to be {xyz, abc, efg} but it is {xyz, efg}.
private static void changePasswordAsAdmin(String userDn, String sNewPassword) throws Exception {
System.out.println("Setting Password:" + sNewPassword);
LDAPConnection connection = new LDAPConnection(new MySSLSocketFactory(), "Ldap host", 636, "adminCN", "adminPwd" );
final List mods = new ArrayList();
final Modification item = new Modification(ModificationType.REPLACE, "userPassword", sNewPassword);
mods.add(item);
final ModifyRequest request = new ModifyRequest(userDn, mods);
try {
LDAPResult result = connection.modify(request);
System.out.println(result.getResultString());
}
catch(Exception e) {
System.out.println(e.getMessage());
throw e;
}
}
private static void changePasswordAsUser(String userDn, String oldPassword, String newPassword) throws Exception {
System.out.println("Setting Password, old password: " + oldPassword + ", new password: " + newPassword);
LDAPConnection connection = new LDAPConnection(new MySSLSocketFactory(), "ldapHost", 636, "userDn", "oldPassword");
final List mods = new ArrayList();
final Modification item = new Modification(ModificationType.REPLACE, "userPassword", newPassword);
mods.add(item);
final ModifyRequest request = new ModifyRequest(userDn, mods);
try {
LDAPResult result = connection.modify(request);
System.out.println(result.getResultString());
}
catch(Exception e) {
System.out.println(e.getMessage());
}
}
This is a known bug which should be fixed according to : https://pagure.io/389-ds-base/issue/48813
Try to update you version of 389DS

authentication in liferay without login hook

I have a problem, I get the user and password of a view and I check if this data is correct in the data of liferay, when it's correct my method return 1 if the validation is true, but I don't know how to make the successful login in liferay, this is my method:
try {
long companyId = PortalUtil.getDefaultCompanyId();
System.out.println(companyId + " id company");
User user1;
try {
user1 = UserLocalServiceUtil.getUserByEmailAddress(companyId, name);
long cmp = user1.getCompanyId();
Company company = CompanyLocalServiceUtil.getCompany(cmp);
int a = UserLocalServiceUtil.authenticateByUserId(company.getCompanyId(), user.getId(), pass, null,
null, null);
if (a == 1) {
System.out.println("Log in successful");
}
} catch (PortalException e) {
e.printStackTrace();
} catch (SystemException e) {
e.printStackTrace();
}
} catch (Exception e) {
System.out.println("algo salio mal");
}
This seems to be a case where you would need an auto-login hook. In Liferay 7, you just need components like in: https://www.e-systems.tech/blog/-/blogs/autologin-in-liferay-7
You can use an indicator within the user session, like a token, and check it in a custom logic:
#Override
protected String[] doLogin(final HttpServletRequest request, final HttpServletResponse response) throws Exception {
final long companyId = portal.getCompanyId(request);
final HttpSession session = request.getSession();
// code your logic here..
final String[] credentials = new String[3];
credentials[0] = String.valueOf(user.getUserId());
credentials[1] = user.getPassword();
credentials[2] = Boolean.FALSE.toString();
return credentials;
}
This solution is also valid for LR6, the difference is that you are not using OSGi there, so you have to create a hook through the SDK.

FBA dual authentication problem

What I have?
I have configured FBA in one of the web applications with out of the box login page having dropdown box to select the either windows or FBA login. Everything is working fine.
What I want?
I want to have a custom login page having text boxes for Username and Password and a login button which will be used for authenticating both Windows and FBA users. To distinguish between the two different logins, I want to handle OnAuthenticate event and check if the user name contains a '\' then assume it is Windows user otherwise, it is FBA user.
This is the code written in OnAuthenticate event handler:
protected void signinControl_Authenticate(object sender, AuthenticateEventArgs e)
{
string fullUserName = signinControl.UserName;
string username = null;
if (fullUserName.Contains("\\")) //Windows user
{
string domain = fullUserName.Substring(0, fullUserName.IndexOf("\\"));
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, domain))
{
username = fullUserName.Substring(fullUserName.IndexOf("\\") + 1);
e.Authenticated = pc.ValidateCredentials(username, signinControl.Password);
}
}
else //FBA user
{
e.Authenticated = Membership.ValidateUser(fullUserName, signinControl.Password);
}
}
What problem am I facing?
The code above works well for FBA Users. But, when I try to login with a windows user, even though the e.Authenticated is set true after validating, it is throwing this error: "Your login attempt was not successful. Please try again.".
e.Authenticated = pc.ValidateCredentials(username, signinControl.Password);
I believe that, setting e.Authenticated to true should redirect the user from login page to the requested page. Can someone please help me if I have to do anything else to get Windows user signed in?
Update-1:
I used SetAuthCookie() method to set Cookie explicitly, still the same result.
FormsAuthentication.SetAuthCookie(username, true);
you should use the methode below for forms user
SPClaimsUtility.AuthenticateFormsUser(
Context.Request.UrlReferrer,
UserName.Text,
Password.Text);
and the windows part is declared like this:
protected void lbInternalUsers_OnClick(object sender, EventArgs e)
{
try
{
if (null != SPContext.Current && null != SPContext.Current.Site)
{
SPIisSettings iisSettings = SPContext.Current.Site.WebApplication.IisSettings[SPUrlZone.Default];
if (null != iisSettings && iisSettings.UseWindowsClaimsAuthenticationProvider)
{
SPAuthenticationProvider provider = iisSettings.WindowsClaimsAuthenticationProvider;
Redirect(provider);
}
}
}
catch (Exception ex)
{
lblError.Text = ex.Message;
}
}
private void Redirect(SPAuthenticationProvider provider)
{
string comp = HttpContext.Current.Request.Url.GetComponents(UriComponents.Query, UriFormat.SafeUnescaped);
string url = provider.AuthenticationRedirectionUrl.ToString();
if (provider is SPWindowsAuthenticationProvider)
{
comp = EnsureUrl(comp, true);
}
SPUtility.Redirect(url, SPRedirectFlags.Default, this.Context, comp);
}
private string EnsureUrl(string url, bool urlIsQueryStringOnly)
{
if (!url.Contains("ReturnUrl="))
{
if (urlIsQueryStringOnly)
{
url = url + (string.IsNullOrEmpty(url) ? "" : "&");
}
else
{
url = url + ((url.IndexOf('?') == -1) ? "?" : "&");
}
url = url + "ReturnUrl=";
}
return url;
}
as detailed here in the reference