commit
8c4852b13b
No known key found for this signature in database
GPG Key ID: B4C3BF012D9B26BE
6 changed files with 474 additions and 0 deletions
-
6.gitignore
-
2ldap-sync.sh
-
119pom.xml
-
101src/main/java/de/hsfulda/informatik/AccountSource.java
-
135src/main/java/de/hsfulda/informatik/LdapSync.java
-
111src/test/java/de/hsfulda/informatik/LdapSyncTest.java
@ -0,0 +1,6 @@ |
|||||
|
*.iws |
||||
|
*.iml |
||||
|
*.ipr |
||||
|
target/ |
||||
|
.DS_Store |
||||
|
.idea/ |
@ -0,0 +1,2 @@ |
|||||
|
#!/bin/bash |
||||
|
java -jar target/ldap-sync-1.0-SNAPSHOT-jar-with-dependencies.jar |
@ -0,0 +1,119 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
|
||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||
|
<modelVersion>4.0.0</modelVersion> |
||||
|
|
||||
|
<groupId>de.hsfulda.informatik</groupId> |
||||
|
<artifactId>ldap-sync</artifactId> |
||||
|
<version>1.0-SNAPSHOT</version> |
||||
|
|
||||
|
<name>ldap-sync</name> |
||||
|
<!-- FIXME change it to the project's website --> |
||||
|
<url>http://www.example.com</url> |
||||
|
|
||||
|
<properties> |
||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
||||
|
<maven.compiler.source>1.7</maven.compiler.source> |
||||
|
<maven.compiler.target>1.7</maven.compiler.target> |
||||
|
</properties> |
||||
|
|
||||
|
<dependencies> |
||||
|
<dependency> |
||||
|
<groupId>com.unboundid</groupId> |
||||
|
<artifactId>unboundid-ldapsdk</artifactId> |
||||
|
<version>6.0.1</version> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>com.google.guava</groupId> |
||||
|
<artifactId>guava</artifactId> |
||||
|
<version>30.1.1-jre</version> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>junit</groupId> |
||||
|
<artifactId>junit</artifactId> |
||||
|
<version>4.11</version> |
||||
|
<scope>test</scope> |
||||
|
</dependency> |
||||
|
</dependencies> |
||||
|
|
||||
|
<build> |
||||
|
<plugins> |
||||
|
<plugin> |
||||
|
<artifactId>maven-assembly-plugin</artifactId> |
||||
|
<executions> |
||||
|
<execution> |
||||
|
<id>foobar</id> |
||||
|
<phase>package</phase> |
||||
|
<goals> |
||||
|
<goal>single</goal> |
||||
|
</goals> |
||||
|
</execution> |
||||
|
</executions> |
||||
|
<configuration> |
||||
|
<descriptorRefs> |
||||
|
<descriptorRef>jar-with-dependencies</descriptorRef> |
||||
|
</descriptorRefs> |
||||
|
<archive> |
||||
|
<manifest> |
||||
|
<mainClass>de.hsfulda.informatik.LdapSync</mainClass> |
||||
|
</manifest> |
||||
|
</archive> |
||||
|
</configuration> |
||||
|
</plugin> |
||||
|
<plugin> |
||||
|
<groupId>org.apache.maven.plugins</groupId> |
||||
|
<artifactId>maven-compiler-plugin</artifactId> |
||||
|
<configuration> |
||||
|
<source>8</source> |
||||
|
<target>8</target> |
||||
|
</configuration> |
||||
|
</plugin> |
||||
|
</plugins> |
||||
|
|
||||
|
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --> |
||||
|
<plugins> |
||||
|
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle --> |
||||
|
<plugin> |
||||
|
<artifactId>maven-clean-plugin</artifactId> |
||||
|
<version>3.1.0</version> |
||||
|
</plugin> |
||||
|
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging --> |
||||
|
<plugin> |
||||
|
<artifactId>maven-resources-plugin</artifactId> |
||||
|
<version>3.0.2</version> |
||||
|
</plugin> |
||||
|
<plugin> |
||||
|
<artifactId>maven-compiler-plugin</artifactId> |
||||
|
<version>3.8.0</version> |
||||
|
</plugin> |
||||
|
<plugin> |
||||
|
<artifactId>maven-surefire-plugin</artifactId> |
||||
|
<version>2.22.1</version> |
||||
|
</plugin> |
||||
|
<plugin> |
||||
|
<artifactId>maven-jar-plugin</artifactId> |
||||
|
<version>3.0.2</version> |
||||
|
</plugin> |
||||
|
<plugin> |
||||
|
<artifactId>maven-install-plugin</artifactId> |
||||
|
<version>2.5.2</version> |
||||
|
</plugin> |
||||
|
<plugin> |
||||
|
<artifactId>maven-deploy-plugin</artifactId> |
||||
|
<version>2.8.2</version> |
||||
|
</plugin> |
||||
|
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle --> |
||||
|
<plugin> |
||||
|
<artifactId>maven-site-plugin</artifactId> |
||||
|
<version>3.7.1</version> |
||||
|
</plugin> |
||||
|
<plugin> |
||||
|
<artifactId>maven-project-info-reports-plugin</artifactId> |
||||
|
<version>3.0.0</version> |
||||
|
</plugin> |
||||
|
</plugins> |
||||
|
</pluginManagement> |
||||
|
</build> |
||||
|
|
||||
|
</project> |
@ -0,0 +1,101 @@ |
|||||
|
package de.hsfulda.informatik; |
||||
|
|
||||
|
import com.unboundid.ldap.sdk.Entry; |
||||
|
import com.unboundid.ldap.sdk.ExtendedResult; |
||||
|
import com.unboundid.ldap.sdk.LDAPConnection; |
||||
|
import com.unboundid.ldap.sdk.LDAPException; |
||||
|
import com.unboundid.ldap.sdk.LDAPResult; |
||||
|
import com.unboundid.ldap.sdk.ResultCode; |
||||
|
import com.unboundid.ldap.sdk.SearchResult; |
||||
|
import com.unboundid.ldap.sdk.SearchResultEntry; |
||||
|
import com.unboundid.ldap.sdk.SearchScope; |
||||
|
import com.unboundid.ldap.sdk.extensions.StartTLSExtendedRequest; |
||||
|
import com.unboundid.util.ssl.SSLUtil; |
||||
|
import com.unboundid.util.ssl.TrustAllTrustManager; |
||||
|
|
||||
|
import java.security.GeneralSecurityException; |
||||
|
import java.util.Collections; |
||||
|
import java.util.List; |
||||
|
import java.util.Set; |
||||
|
import java.util.stream.Collectors; |
||||
|
|
||||
|
public class AccountSource { |
||||
|
final LDAPConnection connection; |
||||
|
final List<SearchResultEntry> searchResultEntryList; |
||||
|
final Set<String> users; |
||||
|
final String baseDN; |
||||
|
|
||||
|
AccountSource(final String baseDN, final List<SearchResultEntry> searchResultEntryList) { |
||||
|
this.baseDN = baseDN; |
||||
|
this.searchResultEntryList = searchResultEntryList; |
||||
|
this.users = getEntries().stream() |
||||
|
.map(e -> e.getAttribute("cn").getValue().toLowerCase()) |
||||
|
.filter(e -> e.startsWith("fd")) |
||||
|
.collect(Collectors.toSet()); |
||||
|
connection = null; |
||||
|
} |
||||
|
|
||||
|
public AccountSource(final String host, final int port, final String bindDN, final String password, final String baseDN, final String filter, final String... attributes) throws LDAPException, GeneralSecurityException { |
||||
|
this.baseDN = baseDN; |
||||
|
final SSLUtil sslUtil = new SSLUtil(new TrustAllTrustManager()); |
||||
|
this.connection = new LDAPConnection(host, port); |
||||
|
final ExtendedResult extendedResult = connection.processExtendedOperation(new StartTLSExtendedRequest(sslUtil.createSSLContext())); |
||||
|
this.connection.bind(bindDN, password); |
||||
|
|
||||
|
if (extendedResult.getResultCode() != ResultCode.SUCCESS) { |
||||
|
throw new LDAPException(extendedResult.getResultCode()); |
||||
|
} |
||||
|
|
||||
|
final SearchResult searchResult = connection.search(baseDN, SearchScope.SUB, filter, attributes); |
||||
|
this.searchResultEntryList = searchResult.getSearchEntries(); |
||||
|
this.users = getEntries().stream() |
||||
|
.map(e -> e.getAttribute("cn").getValue().toLowerCase()) |
||||
|
.filter(e -> e.startsWith("fd")) |
||||
|
.collect(Collectors.toSet()); |
||||
|
} |
||||
|
|
||||
|
public List<SearchResultEntry> getEntries() { |
||||
|
return Collections.unmodifiableList(this.searchResultEntryList); |
||||
|
} |
||||
|
|
||||
|
public Set<String> getUsers() { |
||||
|
return Collections.unmodifiableSet(this.users); |
||||
|
} |
||||
|
|
||||
|
public void add(final List<Entry> usersToBeAdded) { |
||||
|
for (final Entry entry : usersToBeAdded) { |
||||
|
System.out.print("-> Füge Benutzer " + entry.getDN() + "..."); |
||||
|
final LDAPResult ldapResult; |
||||
|
try { |
||||
|
ldapResult = connection.add(entry); |
||||
|
|
||||
|
if (ldapResult.getResultCode().intValue() == 0) { |
||||
|
System.out.print("Ok\n"); |
||||
|
} else { |
||||
|
System.out.print("Failed (" + ldapResult.getResultCode() + ")\n"); |
||||
|
} |
||||
|
} catch (LDAPException e) { |
||||
|
System.out.print("Failed (" + e.getMessage() + ")\n"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void del(final Set<String> usersToBeDeleted) { |
||||
|
for (final String user : usersToBeDeleted) { |
||||
|
System.out.print("-> Lösche Benutzer cn=" + user + "," + this.baseDN + "..."); |
||||
|
final LDAPResult ldapResult; |
||||
|
|
||||
|
try { |
||||
|
ldapResult = connection.delete("cn=" + user + "," + this.baseDN); |
||||
|
|
||||
|
if (ldapResult.getResultCode().intValue() == 0) { |
||||
|
System.out.print("Ok\n"); |
||||
|
} else { |
||||
|
System.out.print("Failed (" + ldapResult.getResultCode() + ")\n"); |
||||
|
} |
||||
|
} catch (LDAPException e) { |
||||
|
System.out.print("Failed (" + e.getMessage() + ")\n"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,135 @@ |
|||||
|
package de.hsfulda.informatik; |
||||
|
|
||||
|
import com.unboundid.ldap.sdk.Attribute; |
||||
|
import com.unboundid.ldap.sdk.Entry; |
||||
|
import com.unboundid.ldap.sdk.LDAPException; |
||||
|
|
||||
|
import java.io.FileNotFoundException; |
||||
|
import java.io.FileReader; |
||||
|
import java.io.IOException; |
||||
|
import java.security.GeneralSecurityException; |
||||
|
import java.util.List; |
||||
|
import java.util.Properties; |
||||
|
import java.util.Set; |
||||
|
import java.util.TreeSet; |
||||
|
import java.util.regex.Matcher; |
||||
|
import java.util.regex.Pattern; |
||||
|
import java.util.stream.Collectors; |
||||
|
|
||||
|
/** |
||||
|
* Ldap sync utility |
||||
|
*/ |
||||
|
public class LdapSync { |
||||
|
final Properties properties = new Properties(); |
||||
|
final static Pattern pattern = Pattern.compile("^fd([a-z][a-z])?([0-9]*?)$"); |
||||
|
|
||||
|
public LdapSync() throws IOException, LDAPException, GeneralSecurityException { |
||||
|
// lade Konfiguration |
||||
|
properties.load(new FileReader("ldap-sync.properties")); |
||||
|
System.out.print("Abfrage der Benutzer im eDirectory..."); |
||||
|
|
||||
|
// lade Daten des Remote-Systems |
||||
|
final AccountSource remote = new AccountSource( |
||||
|
properties.getProperty("sync.src.host"), |
||||
|
Integer.parseInt(properties.getProperty("sync.src.port")), |
||||
|
properties.getProperty("sync.src.binddn"), |
||||
|
properties.getProperty("sync.src.bindpw"), |
||||
|
properties.getProperty("sync.src.basedn"), |
||||
|
properties.getProperty("sync.src.filter"), |
||||
|
new String[]{"cn", "givenname", "sn", "uid"} |
||||
|
); |
||||
|
|
||||
|
System.out.print("Ok\nAbfrage der Benutzer im OpenLDAP..."); |
||||
|
|
||||
|
// lade Daten des lokalen Systems |
||||
|
final AccountSource local = new AccountSource( |
||||
|
properties.getProperty("sync.dst.host"), |
||||
|
Integer.parseInt(properties.getProperty("sync.dst.port")), |
||||
|
properties.getProperty("sync.dst.binddn"), |
||||
|
properties.getProperty("sync.dst.bindpw"), |
||||
|
properties.getProperty("sync.dst.basedn"), |
||||
|
properties.getProperty("sync.dst.filter"), |
||||
|
new String[]{} |
||||
|
); |
||||
|
|
||||
|
System.out.print("Ok\n"); |
||||
|
|
||||
|
sync(remote, local); |
||||
|
} |
||||
|
|
||||
|
LdapSync(final AccountSource remote, final AccountSource local) throws IOException, LDAPException, GeneralSecurityException { |
||||
|
sync(remote, local); |
||||
|
} |
||||
|
|
||||
|
private void sync(final AccountSource remote, final AccountSource local) throws LDAPException { |
||||
|
Set<String> usersToBeAdded = new TreeSet<>(remote.getUsers()); |
||||
|
usersToBeAdded.removeAll(local.getUsers()); |
||||
|
Set<String> usersToBeDeleted = new TreeSet<>(local.getUsers()); |
||||
|
usersToBeDeleted.removeAll(remote.getUsers()); |
||||
|
|
||||
|
final List<Entry> entriesToBeAdded = remote.getEntries().stream() |
||||
|
.filter(s -> usersToBeAdded.contains(s.getAttributeValue("cn").toLowerCase())) |
||||
|
.map(s -> { |
||||
|
final String cn = s.getAttributeValue("cn").toLowerCase(); |
||||
|
final String dn = "cn=" + cn + "," + local.baseDN; |
||||
|
final Entry e = new Entry(dn); |
||||
|
e.addAttribute(new Attribute("objectClass", "inetOrgPerson")); |
||||
|
e.addAttribute(new Attribute("objectClass", "shadowAccount")); |
||||
|
e.addAttribute(new Attribute("objectClass", "top")); |
||||
|
e.addAttribute(new Attribute("objectClass", "posixAccount")); |
||||
|
e.addAttribute(new Attribute("cn", cn)); |
||||
|
e.addAttribute(new Attribute("sn", s.getAttributeValue("sn"))); |
||||
|
e.addAttribute(new Attribute("givenname", s.getAttributeValue("givenName"))); |
||||
|
e.addAttribute(new Attribute("uid", cn)); |
||||
|
e.addAttribute(new Attribute("uidNumber", String.valueOf(computeUid(cn)))); |
||||
|
e.addAttribute(new Attribute("gidNumber", "20")); |
||||
|
e.addAttribute(new Attribute("loginShell", "/bin/zsh")); |
||||
|
e.addAttribute(new Attribute("homeDirectory", "/Users/" + cn)); |
||||
|
e.addAttribute(new Attribute("userPassword", "{SASL}" + cn)); |
||||
|
|
||||
|
return e; |
||||
|
}).collect(Collectors.toList()); |
||||
|
|
||||
|
System.out.print("Zu erzeugende Benutzer: " + entriesToBeAdded.size() + ", Zu löschende Benutzer: " + usersToBeDeleted.size() + "\n"); |
||||
|
|
||||
|
local.del(usersToBeDeleted); |
||||
|
local.add(entriesToBeAdded); |
||||
|
} |
||||
|
|
||||
|
static Integer computeUid(final String cn) { |
||||
|
final Matcher m = pattern.matcher(cn); |
||||
|
|
||||
|
if (m.find()) { |
||||
|
int a = 0; |
||||
|
int b = 0; |
||||
|
|
||||
|
if (m.group(1) != null) { |
||||
|
a = m.group(1).charAt(0) - 96; |
||||
|
b = m.group(1).charAt(1) - 96; |
||||
|
} |
||||
|
|
||||
|
final int s = m.group(2).length(); |
||||
|
final int z = Integer.parseInt(m.group(2)); |
||||
|
|
||||
|
final int uid = (a * 1000 + b * 10 + s) * 10000 + z; |
||||
|
|
||||
|
return uid; |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
public static void main(String[] args) { |
||||
|
try { |
||||
|
final LdapSync ldapSync = new LdapSync(); |
||||
|
} catch (FileNotFoundException e) { |
||||
|
e.printStackTrace(); |
||||
|
} catch (LDAPException e) { |
||||
|
e.printStackTrace(); |
||||
|
} catch (GeneralSecurityException e) { |
||||
|
e.printStackTrace(); |
||||
|
} catch (IOException e) { |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,111 @@ |
|||||
|
package de.hsfulda.informatik; |
||||
|
|
||||
|
import com.unboundid.ldap.sdk.Attribute; |
||||
|
import com.unboundid.ldap.sdk.Entry; |
||||
|
import com.unboundid.ldap.sdk.LDAPException; |
||||
|
import com.unboundid.ldap.sdk.SearchResultEntry; |
||||
|
import org.junit.Test; |
||||
|
|
||||
|
import java.io.IOException; |
||||
|
import java.security.GeneralSecurityException; |
||||
|
import java.util.ArrayList; |
||||
|
import java.util.Arrays; |
||||
|
import java.util.List; |
||||
|
import java.util.Set; |
||||
|
import java.util.stream.Collectors; |
||||
|
|
||||
|
import static org.junit.Assert.assertEquals; |
||||
|
import static org.junit.Assert.assertTrue; |
||||
|
|
||||
|
public class LdapSyncTest { |
||||
|
private List<Entry> addList; |
||||
|
private Set<String> delSet; |
||||
|
|
||||
|
@Test |
||||
|
public void testSync() throws LDAPException, GeneralSecurityException, IOException { |
||||
|
final List<SearchResultEntry> remoteEntries = new ArrayList<>(); |
||||
|
remoteEntries.add(searchResultEntry("dn=Fd1234,dc=remote,dc=de", "fD1234", "Mustermann", "Mustermann")); |
||||
|
remoteEntries.add(searchResultEntry("dn=fDai1235,dc=remote,dc=de", "FDai1235", "Schuster", "Schuster")); |
||||
|
remoteEntries.add(searchResultEntry("dn=fdaI1236,dc=remote,dc=de", "fdAI1236", "Bunsen", "Bunsen")); |
||||
|
remoteEntries.add(searchResultEntry("dn=fDeT123,dc=remote,dc=de", "FDET123", "Bauer", "Bauer")); |
||||
|
remoteEntries.add(searchResultEntry("dn=fdXx9999,dc=remote,dc=de", "fdxX9999", "Schmidt", "Schmidt")); |
||||
|
final AccountSource remote = new AccountSource("dc=remote,dc=de", remoteEntries); |
||||
|
|
||||
|
final List<SearchResultEntry> localEntries = new ArrayList<>(); |
||||
|
localEntries.add(searchResultEntry("dn=Fdai1236,dc=local,dc=de", "fDai1236", "Bunsen", "Bunsen")); |
||||
|
localEntries.add(searchResultEntry("dn=fDai1237,dc=local,dc=de", "fdAi1237", "Beaker", "Beaker")); |
||||
|
localEntries.add(searchResultEntry("dn=fdAi1238,dc=local,dc=de", "fdaI1238", "Hopper", "Hopper")); |
||||
|
final AccountSource local = new AccountSource("dc=local,dc=de", localEntries) { |
||||
|
@Override |
||||
|
public void add(List<Entry> usersToBeAdded) { |
||||
|
addList = usersToBeAdded; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void del(Set<String> usersToBeDeleted) { |
||||
|
delSet = usersToBeDeleted; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
final LdapSync ldapSync = new LdapSync(remote, local); |
||||
|
|
||||
|
assertEquals(delSet.size(), 2); |
||||
|
assertTrue(delSet.contains("fdai1237")); |
||||
|
assertTrue(delSet.contains("fdai1238")); |
||||
|
|
||||
|
assertEquals(addList.size(), 4); |
||||
|
|
||||
|
for (final Entry entry : addList) { |
||||
|
assertTrue(entry.getDN().endsWith(",dc=local,dc=de")); |
||||
|
|
||||
|
final List<String> objectClasses = Arrays.asList(entry.getObjectClassValues()); |
||||
|
assertTrue(objectClasses.contains("shadowAccount")); |
||||
|
assertTrue(objectClasses.contains("posixAccount")); |
||||
|
assertTrue(objectClasses.contains("top")); |
||||
|
assertTrue(objectClasses.contains("inetOrgPerson")); |
||||
|
|
||||
|
final List<String> attributes = entry.getAttributes().stream().map(a -> a.getName()).collect(Collectors.toList()); |
||||
|
assertTrue(attributes.contains("uid")); |
||||
|
assertTrue(attributes.contains("cn")); |
||||
|
assertTrue(attributes.contains("givenname")); |
||||
|
assertTrue(attributes.contains("sn")); |
||||
|
assertTrue(attributes.contains("uidNumber")); |
||||
|
assertTrue(attributes.contains("gidNumber")); |
||||
|
assertTrue(attributes.contains("homeDirectory")); |
||||
|
assertTrue(attributes.contains("loginShell")); |
||||
|
|
||||
|
assertEquals(entry.getAttributeValue("cn").toLowerCase(), entry.getAttributeValue("cn")); |
||||
|
assertEquals(entry.getAttributeValue("uid").toLowerCase(), entry.getAttributeValue("uid")); |
||||
|
assertEquals(entry.getDN().toLowerCase(), entry.getDN()); |
||||
|
|
||||
|
System.out.println(); |
||||
|
|
||||
|
for (final String string : entry.toLDIF()) { |
||||
|
System.out.println(string); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testUidNumber() { |
||||
|
assertEquals(41000, (int) LdapSync.computeUid("fd1000")); |
||||
|
assertEquals(49999, (int) LdapSync.computeUid("fd9999")); |
||||
|
assertEquals(10141000, (int) LdapSync.computeUid("fdaa1000")); |
||||
|
assertEquals(10149999, (int) LdapSync.computeUid("fdaa9999")); |
||||
|
assertEquals(262641000, (int) LdapSync.computeUid("fdzz1000")); |
||||
|
assertEquals(262649999, (int) LdapSync.computeUid("fdzz9999")); |
||||
|
} |
||||
|
|
||||
|
private SearchResultEntry searchResultEntry(final String dn, final String cn, final String sn, final String givenname) { |
||||
|
final Attribute[] attributes = { |
||||
|
new Attribute("cn", cn), |
||||
|
new Attribute("uid", cn), |
||||
|
new Attribute("sn", sn), |
||||
|
new Attribute("givenname", givenname), |
||||
|
new Attribute("objectClass", "inetOrgPerson"), |
||||
|
new Attribute("objectClass", "top"), |
||||
|
}; |
||||
|
|
||||
|
return new SearchResultEntry(dn, attributes); |
||||
|
} |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue