Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
* information: "Portions Copyright [year] [name of copyright owner]".
*
* Copyright 2013-2014 Manuel Gaupp
* Portions Copyright 2026 3A Systems, LLC.
*/
package org.forgerock.opendj.ldap;

Expand Down Expand Up @@ -321,7 +322,13 @@ public int nextInteger() throws DecodeException {
WARN_GSER_NO_VALID_INTEGER.get(gserValue.substring(pos, length));
throw DecodeException.error(msg);
}
return Integer.valueOf(next(GSER_INTEGER)).intValue();
final String integer = next(GSER_INTEGER);
try {
return Integer.parseInt(integer);
} catch (final NumberFormatException e) {
// The value matches the integer pattern but does not fit in an int.
throw DecodeException.error(WARN_GSER_NO_VALID_INTEGER.get(integer), e);
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
* information: "Portions Copyright [year] [name of copyright owner]".
*
* Copyright 2013-2016 ForgeRock AS.
* Portions Copyright 2026 3A Systems, LLC.
*/
package org.forgerock.opendj.ldap;

Expand Down Expand Up @@ -580,8 +581,7 @@ private void searchWithSubordinates(final RequestContext requestContext, final S
}

final int pageSize = pagedResults != null ? pagedResults.getSize() : 0;
final int offset = (pagedResults != null && !pagedResults.getCookie().isEmpty())
? Integer.valueOf(pagedResults.getCookie().toString()) : 0;
final int offset = decodePagedResultsCookie(pagedResults);
int numberOfResults = 0;
int position = 0;
for (final Entry entry : subtree.values()) {
Expand Down Expand Up @@ -630,6 +630,31 @@ private void searchWithSubordinates(final RequestContext requestContext, final S
resultHandler.handleResult(result);
}

/**
* Returns the offset of the first entry to be returned, as encoded by this backend in the cookie
* of the previous page.
*
* @param pagedResults
* The simple paged results control, if present.
* @return The offset of the first entry to be returned.
* @throws LdapException
* If the cookie was not created by this backend.
*/
private static int decodePagedResultsCookie(final SimplePagedResultsControl pagedResults) throws LdapException {
if (pagedResults == null || pagedResults.getCookie().isEmpty()) {
return 0;
}
final String cookie = pagedResults.getCookie().toString();
try {
return Integer.parseInt(cookie);
} catch (final NumberFormatException e) {
throw newLdapException(newResult(ResultCode.PROTOCOL_ERROR)
.setDiagnosticMessage(
"Invalid paged results cookie: " + pagedResults.getCookie().toHexString())
.setCause(e));
}
}

private <R extends Result> R addResultControls(final Request request, final Entry before,
final Entry after, final R result) throws LdapException {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
* information: "Portions Copyright [year] [name of copyright owner]".
*
* Copyright 2013-2014 Manuel Gaupp
* Portions Copyright 2026 3A Systems, LLC.
*/
package org.forgerock.opendj.ldap;

Expand Down Expand Up @@ -165,7 +166,9 @@ public Object[][] createIntegerValues() {
{"", false},
{"0xFF", false},
{"NULL", false},
{"Not a Number", false}
{"Not a Number", false},
{"2147483648", false},
{"99999999999", false}
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
* information: "Portions Copyright [year] [name of copyright owner]".
*
* Copyright 2013-2016 ForgeRock AS.
* Portions Copyright 2026 3A Systems, LLC.
*/
package org.forgerock.opendj.ldap;

Expand Down Expand Up @@ -548,6 +549,22 @@ public void testSearchPagedResults() throws Exception {
assertThat(cookie.isEmpty()).isTrue();
}

@Test
public void testSearchPagedResultsForgedCookie() throws Exception {
final Connection connection = getConnection();
final SearchRequest search =
Requests.newSearchRequest("ou=people,dc=example,dc=com", SearchScope.WHOLE_SUBTREE,
"(uid=*)");
search.addControl(
SimplePagedResultsControl.newControl(true, 2, ByteString.valueOfUtf8("forged")));
try {
connection.search(search, new ArrayList<SearchResultEntry>());
TestCaseUtils.failWasExpected(LdapException.class);
} catch (LdapException e) {
assertThat(e.getResult().getResultCode()).isEqualTo(ResultCode.PROTOCOL_ERROR);
}
}

@Test
public void testSimpleBind() throws Exception {
final Connection connection = getConnection();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.awt.Font;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Set;
Expand Down Expand Up @@ -1838,7 +1839,7 @@ public BrowserNodeInfoImpl(BasicNode node) {
sb.append(getURL());
if (getReferral() != null) {
sb.append(" -> ");
sb.append(getReferral());
sb.append(Arrays.toString(getReferral()));
}
toString = sb.toString();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -746,7 +746,7 @@ else if (matches(filter, FilterType.EQUALITY, "replicationcsn"))
{
// == exact CSN
// validate provided CSN is correct
new CSN(filter.getAssertionValue().toString());
validateCSN(filter.getAssertionValue());
}
else if (filter.getFilterType() == FilterType.AND)
{
Expand Down Expand Up @@ -801,6 +801,20 @@ private static long decodeChangeNumber(final ByteString assertionValue)
}
}

private static void validateCSN(final ByteString assertionValue)
throws DirectoryException
{
try
{
new CSN(assertionValue.toString());
}
catch (IllegalArgumentException e)
{
throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
LocalizableMessage.raw("Could not convert value '%s' to a CSN", assertionValue), e);
}
}

private boolean matches(SearchFilter filter, FilterType filterType, String primaryName)
{
return filter.getFilterType() == filterType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions Copyright [year] [name of copyright owner]".
*
* Copyright 2024-2025 3A Systems, LLC.
* Copyright 2024-2026 3A Systems, LLC.
*/
package org.opends.server.backends.jdbc;

import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.github.benmanes.caffeine.cache.RemovalCause;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.slf4j.LocalizedLogger;

import java.sql.*;
import java.time.Duration;
Expand All @@ -26,10 +28,15 @@
import java.util.concurrent.*;

public class CachedConnection implements Connection {
private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();

static final String TTL_PROPERTY = "org.openidentityplatform.opendj.jdbc.ttl";
static final long DEFAULT_TTL_MS = 15000;

final Connection parent;

static LoadingCache<String, BlockingQueue<CachedConnection>> cached = Caffeine.newBuilder()
.expireAfterAccess(Duration.ofMillis(Long.parseLong(System.getProperty("org.openidentityplatform.opendj.jdbc.ttl","15000"))))
.expireAfterAccess(Duration.ofMillis(getCacheTtlMillis()))
.removalListener((String key, BlockingQueue<CachedConnection> value, RemovalCause cause) -> {
for (CachedConnection con : value) {
try {
Expand All @@ -43,6 +50,26 @@ public class CachedConnection implements Connection {
})
.build(conStr -> new LinkedBlockingQueue<>());

/**
* Returns the time after which an idle pooled connection is closed, as configured by the
* {@value #TTL_PROPERTY} system property. An invalid value is ignored in favor of the default.
*/
private static long getCacheTtlMillis() {
final String ttl = System.getProperty(TTL_PROPERTY);
if (ttl != null) {
try {
final long millis = Long.parseLong(ttl.trim());
if (millis >= 0) {
return millis;
}
} catch (NumberFormatException ignored) {
}
logger.warn(LocalizableMessage.raw("Ignoring invalid value \"%s\" of the %s property, using %d ms",
ttl, TTL_PROPERTY, DEFAULT_TTL_MS));
}
return DEFAULT_TTL_MS;
}

final String connectionString;
public CachedConnection(String connectionString, Connection parent) {
this.connectionString = connectionString;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
* information: "Portions Copyright [year] [name of copyright owner]".
*
* Copyright 2015 ForgeRock AS.
* Portions Copyright 2026 3A Systems, LLC.
*/
package org.opends.server.backends.pluggable;

Expand Down Expand Up @@ -129,7 +130,7 @@ public String valueToString(ByteString value)
@Override
public ByteString generateKey(String data)
{
return new EntryID(Long.parseLong(data)).toByteString();
return new EntryID(ID2Entry.parseEntryID(data)).toByteString();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
import java.util.zip.InflaterInputStream;
import java.util.zip.InflaterOutputStream;

import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.LocalizedIllegalArgumentException;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.io.ASN1;
import org.forgerock.opendj.io.ASN1Reader;
Expand Down Expand Up @@ -556,7 +558,27 @@ public String valueToString(ByteString value)
@Override
public ByteString generateKey(String data)
{
EntryID entryID = new EntryID(Long.parseLong(data));
return entryID.toByteString();
return new EntryID(parseEntryID(data)).toByteString();
}

/**
* Returns the entry ID held by the provided string.
*
* @param data
* The string representation of an entry ID
* @return the parsed entry ID
* @throws LocalizedIllegalArgumentException
* If the provided string does not hold an entry ID
*/
static long parseEntryID(String data)
{
try
{
return Long.parseLong(data);
}
catch (NumberFormatException e)
{
throw new LocalizedIllegalArgumentException(LocalizableMessage.raw("Invalid entry ID: \"%s\"", data));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
* information: "Portions Copyright [year] [name of copyright owner]".
*
* Copyright 2015-2016 ForgeRock AS.
* Portions Copyright 2026 3A Systems, LLC.
*/
package org.opends.server.backends.pluggable;

Expand Down Expand Up @@ -211,7 +212,7 @@ private Entry createEntry(List<StringBuilder> lines, DN entryDN, boolean checkSc
{
if (logger.isTraceEnabled())
{
logger.trace("Skipping entry %s because reading" + "its attributes failed.", entryDN);
logger.trace("Skipping entry %s because reading its attributes failed.", entryDN);
}
logToSkipWriter(lines, ERR_LDIF_READ_ATTR_SKIP.get(entryDN, e.getMessage()));
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2700,8 +2700,8 @@ public ByteString generatePassword()
{
if (logger.isTraceEnabled())
{
logger.trace("Unable to generate a new password for user %s because no password generator has been defined" +
"in the associated password policy.", userDNString);
logger.trace("Unable to generate a new password for user %s because no password generator has been defined "
+ "in the associated password policy.", userDNString);
}

return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
*
* Copyright 2013-2014 Manuel Gaupp
* Portions Copyright 2014-2016 ForgeRock AS.
* Portions Copyright 2026 3A Systems, LLC.
*/
package org.opends.server.protocols.asn1;

Expand Down Expand Up @@ -382,7 +383,16 @@ public int nextInteger() throws GSERException
.substring(pos,length));
throw new GSERException(msg);
}
return Integer.valueOf(next(GSER_INTEGER)).intValue();
final String integer = next(GSER_INTEGER);
try
{
return Integer.parseInt(integer);
}
catch (NumberFormatException e)
{
// The value matches the integer pattern but does not fit in an int.
throw new GSERException(ERR_GSER_NO_VALID_INTEGER.get(integer), e);
}
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import java.io.Serializable;
import java.util.Date;

import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.LocalizedIllegalArgumentException;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteSequenceReader;
import org.forgerock.opendj.ldap.ByteString;
Expand Down Expand Up @@ -72,6 +74,8 @@ public class CSN implements Serializable, Comparable<CSN>
* @param s
* The string to be parsed.
* @return The parsed CSN.
* @throws LocalizedIllegalArgumentException
* If the provided string is not a valid {@link #toString()} representation of a CSN
* @see #toString()
*/
public static CSN valueOf(String s)
Expand Down Expand Up @@ -102,17 +106,26 @@ public static CSN valueOf(ByteSequence bs)
*
* @param str
* the string from which to create a {@link CSN}
* @throws LocalizedIllegalArgumentException
* If the provided string is not a valid {@link #toString()} representation of a CSN
*/
public CSN(String str)
{
String temp = str.substring(0, 16);
timeStamp = Long.parseLong(temp, 16);

temp = str.substring(16, 20);
serverId = Integer.parseInt(temp, 16);
if (str == null || str.length() < STRING_ENCODING_LENGTH)
{
throw new LocalizedIllegalArgumentException(LocalizableMessage.raw("Invalid CSN: \"%s\"", str));
}

temp = str.substring(20, 28);
seqnum = Integer.parseInt(temp, 16);
try
{
timeStamp = Long.parseLong(str.substring(0, 16), 16);
serverId = Integer.parseInt(str.substring(16, 20), 16);
seqnum = Integer.parseInt(str.substring(20, STRING_ENCODING_LENGTH), 16);
}
catch (NumberFormatException e)
{
throw new LocalizedIllegalArgumentException(LocalizableMessage.raw("Invalid CSN: \"%s\"", str));
}
}

/**
Expand Down
Loading
Loading