forked from OpenIdentityPlatform/OpenDJ
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[OpenIdentityPlatform#462] RFC5805 Lightweight Directory Access Proto…
…col (LDAP) Transactions
- Loading branch information
Showing
22 changed files
with
1,532 additions
and
35 deletions.
There are no files selected for viewing
94 changes: 94 additions & 0 deletions
94
.../src/main/java/com/forgerock/opendj/ldap/extensions/AbortedTransactionExtendedResult.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
/* | ||
* The contents of this file are subject to the terms of the Common Development and | ||
* Distribution License (the License). You may not use this file except in compliance with the | ||
* License. | ||
* | ||
* You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the | ||
* specific language governing permission and limitations under the License. | ||
* | ||
* When distributing Covered Software, include this CDDL Header Notice in each file and include | ||
* the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL | ||
* Header, with the fields enclosed by brackets [] replaced by your own identifying | ||
* information: "Portions Copyright [year] [name of copyright owner]". | ||
* | ||
* Copyright 2025 3A Systems, LLC | ||
*/ | ||
package com.forgerock.opendj.ldap.extensions; | ||
|
||
import org.forgerock.opendj.io.ASN1; | ||
import org.forgerock.opendj.io.ASN1Writer; | ||
import org.forgerock.opendj.ldap.ByteString; | ||
import org.forgerock.opendj.ldap.ByteStringBuilder; | ||
import org.forgerock.opendj.ldap.ResultCode; | ||
import org.forgerock.opendj.ldap.responses.AbstractExtendedResult; | ||
import org.forgerock.util.Reject; | ||
|
||
import java.io.IOException; | ||
|
||
/* | ||
The Aborted Transaction Notice is an Unsolicited Notification message | ||
where the responseName is 1.3.6.1.1.21.4 and responseValue is present | ||
and contains a transaction identifier. | ||
*/ | ||
public class AbortedTransactionExtendedResult extends AbstractExtendedResult<AbortedTransactionExtendedResult> { | ||
@Override | ||
public String getOID() { | ||
return "1.3.6.1.1.21.4"; | ||
} | ||
|
||
private AbortedTransactionExtendedResult(final ResultCode resultCode) { | ||
super(resultCode); | ||
} | ||
|
||
public static AbortedTransactionExtendedResult newResult(final ResultCode resultCode) { | ||
Reject.ifNull(resultCode); | ||
return new AbortedTransactionExtendedResult(resultCode); | ||
} | ||
|
||
private String transactionID = null; | ||
|
||
public AbortedTransactionExtendedResult setTransactionID(final String transactionID) { | ||
this.transactionID = transactionID; | ||
return this; | ||
} | ||
|
||
public String getTransactionID() { | ||
return transactionID; | ||
} | ||
|
||
@Override | ||
public ByteString getValue() { | ||
final ByteStringBuilder buffer = new ByteStringBuilder(); | ||
final ASN1Writer writer = ASN1.getWriter(buffer); | ||
try { | ||
writer.writeOctetString(transactionID); | ||
} catch (final IOException ioe) { | ||
throw new RuntimeException(ioe); | ||
} | ||
return buffer.toByteString(); | ||
} | ||
|
||
@Override | ||
public boolean hasValue() { | ||
return true; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "AbortedTransactionExtendedResult(resultCode=" + | ||
getResultCode() + | ||
", matchedDN=" + | ||
getMatchedDN() + | ||
", diagnosticMessage=" + | ||
getDiagnosticMessage() + | ||
", referrals=" + | ||
getReferralURIs() + | ||
", responseName=" + | ||
getOID() + | ||
", transactionID=" + | ||
transactionID + | ||
", controls=" + | ||
getControls() + | ||
")"; | ||
} | ||
} |
182 changes: 182 additions & 0 deletions
182
...ore/src/main/java/com/forgerock/opendj/ldap/extensions/EndTransactionExtendedRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
/* | ||
* The contents of this file are subject to the terms of the Common Development and | ||
* Distribution License (the License). You may not use this file except in compliance with the | ||
* License. | ||
* | ||
* You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the | ||
* specific language governing permission and limitations under the License. | ||
* | ||
* When distributing Covered Software, include this CDDL Header Notice in each file and include | ||
* the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL | ||
* Header, with the fields enclosed by brackets [] replaced by your own identifying | ||
* information: "Portions Copyright [year] [name of copyright owner]". | ||
* | ||
* Copyright 2025 3A Systems, LLC | ||
*/ | ||
package com.forgerock.opendj.ldap.extensions; | ||
|
||
import org.forgerock.i18n.LocalizableMessage; | ||
import org.forgerock.opendj.io.ASN1; | ||
import org.forgerock.opendj.io.ASN1Reader; | ||
import org.forgerock.opendj.io.ASN1Writer; | ||
import org.forgerock.opendj.ldap.*; | ||
import org.forgerock.opendj.ldap.controls.Control; | ||
import org.forgerock.opendj.ldap.requests.*; | ||
import org.forgerock.opendj.ldap.responses.AbstractExtendedResultDecoder; | ||
import org.forgerock.opendj.ldap.responses.ExtendedResult; | ||
import org.forgerock.opendj.ldap.responses.ExtendedResultDecoder; | ||
import org.forgerock.util.Reject; | ||
|
||
import java.io.IOException; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
import static com.forgerock.opendj.ldap.CoreMessages.ERR_EXTOP_PASSMOD_CANNOT_DECODE_REQUEST; | ||
import static com.forgerock.opendj.util.StaticUtils.getExceptionMessage; | ||
|
||
/* | ||
An End Transaction Request is an LDAPMessage of CHOICE extendedReq | ||
where the requestName is 1.3.6.1.1.21.3 and the requestValue is | ||
present and contains a BER-encoded txnEndReq. | ||
txnEndReq ::= SEQUENCE { | ||
commit BOOLEAN DEFAULT TRUE, | ||
identifier OCTET STRING } | ||
A commit value of TRUE indicates a request to commit the transaction | ||
identified by the identifier. A commit value of FALSE indicates a | ||
request to abort the identified transaction. | ||
*/ | ||
public class EndTransactionExtendedRequest extends AbstractExtendedRequest<EndTransactionExtendedRequest, EndTransactionExtendedResult> { | ||
|
||
public static final String END_TRANSACTION_REQUEST_OID ="1.3.6.1.1.21.3"; | ||
|
||
@Override | ||
public String getOID() { | ||
return END_TRANSACTION_REQUEST_OID; | ||
} | ||
|
||
@Override | ||
public ExtendedResultDecoder<EndTransactionExtendedResult> getResultDecoder() { | ||
return RESULT_DECODER; | ||
} | ||
|
||
Boolean commit=true; | ||
public EndTransactionExtendedRequest setCommit(final Boolean commit) { | ||
this.commit = commit; | ||
return this; | ||
} | ||
|
||
String transactionID; | ||
public EndTransactionExtendedRequest setTransactionID(final String transactionID) { | ||
Reject.ifNull(transactionID); | ||
this.transactionID = transactionID; | ||
return this; | ||
} | ||
public boolean isCommit() { | ||
return commit; | ||
} | ||
|
||
public String getTransactionID() { | ||
return transactionID; | ||
} | ||
|
||
@Override | ||
public ByteString getValue() { | ||
Reject.ifNull(transactionID); | ||
final ByteStringBuilder buffer = new ByteStringBuilder(); | ||
final ASN1Writer writer = ASN1.getWriter(buffer); | ||
try { | ||
writer.writeStartSequence(); | ||
if (commit!=null) { | ||
writer.writeBoolean(commit); | ||
} | ||
writer.writeOctetString(transactionID); | ||
writer.writeEndSequence(); | ||
} catch (final IOException ioe) { | ||
throw new RuntimeException(ioe); | ||
} | ||
return buffer.toByteString(); | ||
} | ||
|
||
@Override | ||
public boolean hasValue() { | ||
return true; | ||
} | ||
|
||
private static final EndTransactionExtendedRequest.ResultDecoder RESULT_DECODER = new EndTransactionExtendedRequest.ResultDecoder(); | ||
|
||
private static final class ResultDecoder extends AbstractExtendedResultDecoder<EndTransactionExtendedResult> { | ||
@Override | ||
public EndTransactionExtendedResult newExtendedErrorResult(final ResultCode resultCode,final String matchedDN, final String diagnosticMessage) { | ||
if (!resultCode.isExceptional()) { | ||
throw new IllegalArgumentException("No response name and value for result code "+ resultCode.intValue()); | ||
} | ||
return EndTransactionExtendedResult.newResult(resultCode).setMatchedDN(matchedDN).setDiagnosticMessage(diagnosticMessage); | ||
} | ||
|
||
/* | ||
txnEndRes ::= SEQUENCE { | ||
messageID MessageID OPTIONAL, | ||
-- msgid associated with non-success resultCode | ||
updatesControls SEQUENCE OF updateControls SEQUENCE { | ||
messageID MessageID, | ||
-- msgid associated with controls | ||
controls Controls | ||
} OPTIONAL | ||
} | ||
*/ | ||
@Override | ||
public EndTransactionExtendedResult decodeExtendedResult(final ExtendedResult result,final DecodeOptions options) throws DecodeException { | ||
if (result instanceof EndTransactionExtendedResult) { | ||
return (EndTransactionExtendedResult) result; | ||
} | ||
|
||
final ResultCode resultCode = result.getResultCode(); | ||
final EndTransactionExtendedResult newResult = | ||
EndTransactionExtendedResult.newResult(resultCode) | ||
.setMatchedDN(result.getMatchedDN()) | ||
.setDiagnosticMessage(result.getDiagnosticMessage()); | ||
|
||
final ByteString responseValue = result.getValue(); | ||
if (!resultCode.isExceptional() && responseValue == null) { | ||
throw DecodeException.error(LocalizableMessage.raw("Empty response value")); | ||
} | ||
if (responseValue != null) { | ||
try { | ||
final ASN1Reader reader = ASN1.getReader(responseValue); | ||
if (reader.hasNextElement()) { | ||
reader.readStartSequence(); | ||
if (reader.hasNextElement() && reader.peekType() == ASN1.UNIVERSAL_INTEGER_TYPE) { | ||
newResult.setFailedMessageID(Math.toIntExact(reader.readInteger())); | ||
} else if (reader.hasNextElement() && reader.peekType() == ASN1.UNIVERSAL_SEQUENCE_TYPE) { | ||
reader.readStartSequence(); | ||
while (reader.hasNextElement() && reader.peekType() == ASN1.UNIVERSAL_SEQUENCE_TYPE) { | ||
reader.readStartSequence(); | ||
final long messageId = reader.readInteger(); | ||
final List<Control> controls = new ArrayList<>(); | ||
reader.readStartSequence(); | ||
while (reader.hasNextElement() && reader.peekType() == ASN1.UNIVERSAL_OCTET_STRING_TYPE) { | ||
final ByteString controlEncoded = reader.readOctetString(); | ||
//TODO decode Control | ||
} | ||
reader.readEndSequence(); | ||
//newResult.success(messageId, controls.toArray(new Control[]{})); | ||
reader.readEndSequence(); | ||
} | ||
reader.readEndSequence(); | ||
} | ||
reader.readEndSequence(); | ||
} | ||
} catch (final IOException e) { | ||
throw DecodeException.error(LocalizableMessage.raw("Error decoding response value"), e); | ||
} | ||
} | ||
for (final Control control : result.getControls()) { | ||
newResult.addControl(control); | ||
} | ||
return newResult; | ||
} | ||
} | ||
} | ||
|
Oops, something went wrong.