mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Valery Kharseko
4 days ago 3f4deb91789189521d577457bd6da27de8fd75b1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/*
 * 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 2026 3A Systems, LLC.
 */
package org.opends.server.authorization.dseecompat;
 
import static com.forgerock.opendj.ldap.CoreMessages.ERR_ATTR_SYNTAX_DN_TRAILING_ESCAPE;
import static org.assertj.core.api.Assertions.*;
 
import org.forgerock.i18n.LocalizedIllegalArgumentException;
import org.forgerock.opendj.ldap.DN;
import org.forgerock.opendj.ldap.ResultCode;
import org.opends.server.DirectoryServerTestCase;
import org.opends.server.TestCaseUtils;
import org.opends.server.types.DirectoryException;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
 
/**
 * Verifies that {@link PatternDN}, which parses the ACI target and userdn patterns and the DN criteria of
 * the access log filtering, reads a pattern the same way {@link DN#valueOf(String)} reads a DN (issue #1153).
 */
@SuppressWarnings("javadoc")
public class PatternDNTest extends DirectoryServerTestCase
{
  @BeforeClass
  public void setUp() throws Exception
  {
    TestCaseUtils.startFakeServer();
  }
 
  @AfterClass
  public void tearDown() throws DirectoryException
  {
    TestCaseUtils.shutdownFakeServer();
  }
 
  /** Patterns without a wildcard: each one must match the DN that DN.valueOf() reads from the same string. */
  @DataProvider
  public Object[][] patternsWithoutWildcard()
  {
    return new Object[][] {
      { "cn=a\\2Cb,dc=x" },
      { "cn=\"a\\,b\",dc=x" },
      // A hex pair inside quotes is decoded as it is outside quotes
      { "cn=\"a\\2Cb\",dc=x" },
      { "cn=\"J\\C3\\B6rg\",dc=x" },
      // Hex pairs that are still pending at the closing quote, or before an escaped character
      { "cn=\"ab\\2C\",dc=x" },
      { "cn=\"\\C3\\B6\\,\",dc=x" },
      // An empty value is followed by the next AVA or RDN, it does not swallow it
      { "cn=+sn=x,dc=y" },
      { "cn=x+sn=,dc=y" },
      { "cn=,dc=x" },
      { "cn= ,dc=x" },
      { "cn=;dc=x" },
      // A hex string may be followed directly by '+'
      { "cn=#04024869+sn=x,dc=y" },
      { "sn=x+cn=#04024869,dc=y" },
      // The RFC 2253 separator
      { "cn=a;dc=x" },
      // The AVAs of a multi-valued RDN match whatever their order
      { "sn=x+cn=y,dc=z" },
      { "cn=y+sn=x,dc=z" },
    };
  }
 
  @Test(dataProvider = "patternsWithoutWildcard")
  public void patternMatchesTheDNParsedFromTheSameString(String pattern) throws Exception
  {
    final DN dn = DN.valueOf(pattern);
    assertThat(PatternDN.decode(pattern).matchesDN(dn)).as("pattern %s against DN %s", pattern, dn).isTrue();
  }
 
  @DataProvider
  public Object[][] patternsAndOtherDNs()
  {
    return new Object[][] {
      { "cn=\"a\\2Cb\",dc=x", "cn=a2Cb,dc=x" },
      { "cn=+sn=x,dc=y", "cn=\\+sn\\=x,dc=y" },
      { "cn=,dc=x", "dc=x" },
      { "cn=,dc=x", "cn=\\,dc\\=x" },
      { "cn=#04024869+sn=x,dc=y", "cn=#04024869,dc=y" },
      { "sn=x+cn=y,dc=z", "sn=y+cn=x,dc=z" },
      { "sn=x+cn=y,dc=z", "sn=x+givenName=y,dc=z" },
    };
  }
 
  @Test(dataProvider = "patternsAndOtherDNs")
  public void patternDoesNotMatchADifferentDN(String pattern, String otherDN) throws Exception
  {
    assertThat(PatternDN.decode(pattern).matchesDN(DN.valueOf(otherDN))).isFalse();
  }
 
  /** Patterns that end in a lone backslash, which DN.valueOf() rejects too. */
  @DataProvider
  public Object[][] patternsWithATrailingBackslash()
  {
    return new Object[][] {
      { "cn=a\\" },
      { "cn=\\" },
      { "cn=a,dc=x\\" },
      { "cn=a*\\" },
    };
  }
 
  /** The pattern is rejected with the result code and the message of DN.valueOf(). */
  @Test(dataProvider = "patternsWithATrailingBackslash")
  public void patternWithATrailingBackslashIsRejected(String pattern) throws Exception
  {
    final Throwable patternError = catchThrowable(() -> PatternDN.decode(pattern));
    assertThat(patternError).isInstanceOf(DirectoryException.class);
    assertThat(((DirectoryException) patternError).getResultCode()).isEqualTo(ResultCode.INVALID_DN_SYNTAX);
    assertThat(((DirectoryException) patternError).getMessageObject().toString())
        .isEqualTo(ERR_ATTR_SYNTAX_DN_TRAILING_ESCAPE.get(pattern).toString());
    if (!pattern.contains("*"))
    {
      final Throwable dnError = catchThrowable(() -> DN.valueOf(pattern));
      assertThat(dnError).isInstanceOf(LocalizedIllegalArgumentException.class);
      assertThat(dnError.getMessage()).isEqualTo(patternError.getMessage());
    }
  }
 
  @DataProvider
  public Object[][] wildcardPatterns()
  {
    return new Object[][] {
      { "cn=#04024869+sn=*,dc=y", "cn=#04024869+sn=x,dc=y" },
      { "cn=a\\2Cb*,dc=x", "cn=a\\,bcd,dc=x" },
      { "cn=*,dc=x", "cn=a,dc=x" },
    };
  }
 
  @Test(dataProvider = "wildcardPatterns")
  public void wildcardPatternMatches(String pattern, String dn) throws Exception
  {
    assertThat(PatternDN.decode(pattern).matchesDN(DN.valueOf(dn))).isTrue();
  }
}