pjs/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/LdapService.java

567 строки
19 KiB
Java
Исходник Обычный вид История

1999-06-05 05:05:59 +04:00
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
1999-11-02 04:46:24 +03:00
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
1999-06-05 05:05:59 +04:00
*
1999-11-02 04:46:24 +03:00
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
1999-06-05 05:05:59 +04:00
*
1999-11-02 04:46:24 +03:00
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
1999-06-05 05:05:59 +04:00
* Communications Corporation. Portions created by Netscape are
1999-11-02 04:46:24 +03:00
* Copyright (C) 1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
1999-06-05 05:05:59 +04:00
*/
package com.netscape.jndi.ldap;
import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.ldap.*;
import netscape.ldap.*;
import java.util.*;
1999-11-12 09:34:14 +03:00
import java.io.*;
1999-06-05 05:05:59 +04:00
import com.netscape.jndi.ldap.common.*;
import com.netscape.jndi.ldap.schema.SchemaRoot;
/**
* Ldap Service encapsulates a Ldap connection and Ldap operations over the
* connection. The connection is established in a lazy manner, first time a
* Ldap operation is initiated. A Ldap Service object is shared by multiple
* contexts. The object maintains a reference counter which is incremented
* when a context is cloned, and decremeneted when a context is closed. The
* associated Ldap Connection is relased when the reference counter reaches
* zero.
*
* LDAPsearchConstraints are always read from a context, because ldap service
* is a shared object and each context may request different search constraints.
*
*/
class LdapService {
1999-08-27 20:09:54 +04:00
public static final String DEFAULT_FILTER = "(objectclass=*)";
public static final int DEFAULT_SCOPE = LDAPv3.SCOPE_SUB;
public static final String DEFAULT_HOST = "localhost";
public static final int DEFAULT_PORT = LDAPv2.DEFAULT_PORT;
public static final int DEFAULT_SSL_PORT = 636;
private LDAPConnection m_ld;
private EventService m_eventSvc;
/**
* The number of contexts that are currently sharing this LdapService.
* Essentially, a reference counter. Incremented in the LdapContextImpl
* copy constructor. Decremented in the LdapService.disconnect().
* When the count reaches zero, the LDAPConnection is released.
*/
private int m_clientCount;
1999-09-15 21:03:19 +04:00
public LdapService() {
1999-08-27 20:09:54 +04:00
m_ld = new LDAPConnection();
m_clientCount = 1;
}
LDAPConnection getConnection() {
return m_ld;
}
/**
* Connect to the server and send bind request to authenticate the user
*/
1999-09-15 21:03:19 +04:00
void connect(LdapContextImpl ctx) throws NamingException{
1999-08-27 20:09:54 +04:00
if (m_ld.isConnected()) {
return; //already connected
}
1999-09-15 21:03:19 +04:00
LDAPUrl url = ctx.m_ctxEnv.getDirectoryServerURL();
1999-08-27 20:09:54 +04:00
String host = (url != null) ? url.getHost() : DEFAULT_HOST;
int port = (url != null) ? url.getPort() : DEFAULT_PORT;
1999-09-15 21:03:19 +04:00
String user = ctx.m_ctxEnv.getUserDN();
String passwd = ctx.m_ctxEnv.getUserPassword();
String socketFactory = ctx.m_ctxEnv.getSocketFactory();
Object cipherSuite = ctx.m_ctxEnv.getCipherSuite();
int ldapVersion = ctx.m_ctxEnv.getLdapVersion();
boolean isSSLEnabled = ctx.m_ctxEnv.isSSLEnabled();
String[] saslMechanisms = ctx.m_ctxEnv.getSaslMechanisms();
LDAPControl[] ldCtrls= ctx.m_ctxEnv.getConnectControls();
1999-11-12 09:34:14 +03:00
Object traceOut = ctx.m_ctxEnv.getProperty(ContextEnv.P_TRACE);
1999-08-27 20:09:54 +04:00
// Set default ssl port if DS not specifed
if (isSSLEnabled && url == null) {
port = DEFAULT_SSL_PORT;
}
// SSL is enabled but no Socket factory specified. Use the
// ldapjdk default one
if (isSSLEnabled && socketFactory == null) {
m_ld = new LDAPConnection(new LDAPSSLSocketFactory());
}
1999-06-05 05:05:59 +04:00
// Set the socket factory and cipher suite if cpecified
1999-08-27 20:09:54 +04:00
if (socketFactory != null) {
try {
LDAPSSLSocketFactory sf = null;
if (cipherSuite != null) {
Debug.println(1, "CIPHERS=" + cipherSuite);
sf = new LDAPSSLSocketFactory(socketFactory, cipherSuite);
}
else {
sf = new LDAPSSLSocketFactory(socketFactory);
}
m_ld = new LDAPConnection(sf);
Debug.println(1, "SSL CONNECTION");
}
catch (Exception e) {
throw new IllegalArgumentException(
"Illegal value for java.naming.ldap.factory.socket: " + e);
}
}
1999-11-12 09:34:14 +03:00
// Enable tracing
if (traceOut != null) {
setTraceOutput(traceOut);
}
1999-08-27 20:09:54 +04:00
try {
if (ldCtrls != null) {
m_ld.setOption(LDAPv3.SERVERCONTROLS, ldCtrls);
1999-09-15 21:03:19 +04:00
}
if (saslMechanisms != null) { // sasl Auth
m_ld.authenticate(ctx.m_ctxEnv.getSaslAuthId(),
saslMechanisms,
ctx.m_ctxEnv.getSaslProps(),
ctx.m_ctxEnv.getSaslCallback());
}
else { // simple auth
m_ld.connect(ldapVersion, host, port, user, passwd);
}
1999-08-27 20:09:54 +04:00
}
catch (LDAPException e) {
// If ldapVersion is not specified in ctx env
// fallback to ldap v2 if v3 is bot supported
if (e.getLDAPResultCode() == e.PROTOCOL_ERROR &&
ldapVersion == 3 &&
1999-09-15 21:03:19 +04:00
saslMechanisms == null &&
ctx.m_ctxEnv.getProperty(
ctx.m_ctxEnv.P_LDAP_VERSION) == null) {
1999-08-27 20:09:54 +04:00
try {
m_ld.connect(2, host, port, user, passwd);
}
catch (LDAPException e2) {
throw ExceptionMapper.getNamingException(e2);
}
}
else {
throw ExceptionMapper.getNamingException(e);
}
}
}
1999-06-05 05:05:59 +04:00
protected void finalize() {
1999-08-27 20:09:54 +04:00
try {
m_ld.disconnect();
}
catch (Exception e) {}
1999-06-05 05:05:59 +04:00
}
1999-08-27 20:09:54 +04:00
boolean isConnected() {
return (m_ld.isConnected());
}
1999-06-05 05:05:59 +04:00
/**
* Physically disconect only if the client count is zero
*/
1999-08-27 20:09:54 +04:00
synchronized void disconnect() {
try {
if (m_clientCount > 0) {
m_clientCount--;
}
if (m_clientCount == 0 && isConnected()) {
m_ld.disconnect();
}
}
catch (Exception e) {}
}
1999-06-05 05:05:59 +04:00
/**
* Increment client count
*/
synchronized void incrementClientCount() {
1999-06-05 05:05:59 +04:00
m_clientCount++;
}
1999-08-27 20:09:54 +04:00
/**
* LDAP search operation
*/
NamingEnumeration search (LdapContextImpl ctx, String name, String filter, String[] attrs, SearchControls jndiCtrls) throws NamingException{
Debug.println(1, "SEARCH");
String base = ctx.getDN();
int scope = LDAPConnection.SCOPE_SUB;
LDAPSearchConstraints cons=ctx.getSearchConstraints();
boolean returnObjs = false;
1999-09-15 21:03:19 +04:00
connect(ctx); // Lazy Connect
1999-08-27 20:09:54 +04:00
// Create DN by appending the name to the current context
if (name.length() > 0) {
if (base.length() > 0) {
base = name + "," + base;
}
else {
base = name;
}
}
// Map JNDI SearchControls to ldapjdk LDAPSearchConstraints
if (jndiCtrls != null) {
int maxResults = (int)jndiCtrls.getCountLimit();
int timeLimitInMsec = jndiCtrls.getTimeLimit();
// Convert timeLimit in msec to sec
int timeLimit = timeLimitInMsec/1000;
if (timeLimitInMsec > 0 && timeLimitInMsec < 1000) {
timeLimit = 1; //sec
}
// Clone cons if maxResults or timeLimit is different from the default one
if (cons.getServerTimeLimit() != timeLimit || cons.getMaxResults() != maxResults) {
cons = (LDAPSearchConstraints)cons.clone();
cons.setMaxResults(maxResults);
cons.setServerTimeLimit(timeLimit);
}
// TODO The concept of links is not well described in JNDI docs.
// We can only specify dereferencing of Aliases, but Links and
// Aliases are not the same; IGNORE jndiCtrls.getDerefLinkFlag()
// Attributes to return
attrs = jndiCtrls.getReturningAttributes();
if (attrs != null && attrs.length==0) {
//return no attributes
attrs = new String[] { "1.1" };
1999-06-05 05:05:59 +04:00
// TODO check whether ldap compare operation should be performed
// That's the case if: (1) scope is OBJECT_SCOPE, (2) no attrs
// are requested to return (3) filter has the form (name==value)
// where no wildcards ()&|!=~><* are used.
1999-08-27 20:09:54 +04:00
}
// Search scope
scope = ProviderUtils.jndiSearchScopeToLdap(jndiCtrls.getSearchScope());
1999-06-05 05:05:59 +04:00
// return obj flag
returnObjs = jndiCtrls.getReturningObjFlag();
1999-08-27 20:09:54 +04:00
}
try {
// Perform Search
boolean attrsOnly = ctx.m_ctxEnv.getAttrsOnlyFlag();
LDAPSearchResults res = m_ld.search( base, scope, filter, attrs, attrsOnly, cons );
return new SearchResultEnum(res, returnObjs, ctx);
}
catch (LDAPReferralException e) {
throw new LdapReferralException(ctx, e);
}
catch (LDAPException e) {
throw ExceptionMapper.getNamingException(e);
}
}
/**
* List child entries using LDAP lookup operation
*/
NamingEnumeration listEntries(LdapContextImpl ctx, String name, boolean returnBindings) throws NamingException{
Debug.println(1, "LIST " + (returnBindings ? "BINDINGS" : ""));
String base = ctx.getDN();
1999-09-15 21:03:19 +04:00
connect(ctx); // Lazy Connect
1999-08-27 20:09:54 +04:00
// Create DN by appending the name to the current context
if (name.length() > 0) {
if (base.length() > 0) {
base = name + "," + base;
}
else {
base = name;
}
}
try {
// Perform One Level Search
String[] attrs = null; // return all attributes if Bindings are requested
if (!returnBindings) { // for ClassNamePairs
attrs = new String[]{"javaclassname"}; //attr names must be in lowercase
}
LDAPSearchConstraints cons=ctx.getSearchConstraints();
LDAPSearchResults res = m_ld.search( base, LDAPConnection.SCOPE_ONE, DEFAULT_FILTER, attrs, /*attrsOnly=*/false, cons);
if (returnBindings) {
return new BindingEnum(res, ctx);
}
else {
return new NameClassPairEnum(res, ctx);
}
}
catch (LDAPReferralException e) {
throw new LdapReferralException(ctx, e);
}
catch (LDAPException e) {
throw ExceptionMapper.getNamingException(e);
}
}
/**
* Lookup an entry using LDAP search operation
*/
Object lookup(LdapContextImpl ctx, String name) throws NamingException{
Debug.println(1, "LOOKUP");
String base = ctx.getDN();
1999-09-15 21:03:19 +04:00
connect(ctx); // Lazy Connect
1999-08-27 20:09:54 +04:00
// Create DN by appending the name to the current context
if (name.length() > 0) {
if (base.length() > 0) {
base = name + "," + base;
}
else {
base = name;
}
}
try {
// Perform Base Search
String[] attrs = null; // return all attrs
LDAPSearchConstraints cons=ctx.getSearchConstraints();
LDAPSearchResults res = m_ld.search( base, LDAPConnection.SCOPE_BASE, DEFAULT_FILTER, attrs, /*attrsOnly=*/false, cons);
if (res.hasMoreElements()) {
LDAPEntry entry = res.next();
return ObjectMapper.entryToObject(entry, ctx);
}
return null; // should never get here
}
catch (LDAPReferralException e) {
throw new LdapReferralException(ctx, e);
}
catch (LDAPException e) {
throw ExceptionMapper.getNamingException(e);
}
}
/**
* Read LDAP entry attributes
*/
Attributes readAttrs (LdapContextImpl ctx, String name, String[] attrs) throws NamingException{
Debug.println(1, "READ ATTRS");
String base = ctx.getDN();
int scope = LDAPConnection.SCOPE_BASE;
1999-09-15 21:03:19 +04:00
connect(ctx); // Lazy Connect
1999-08-27 20:09:54 +04:00
// Create DN by appending the name to the current context
if (name.length() > 0) {
if (base.length() > 0) {
base = name + "," + base;
}
else {
base = name;
}
}
try {
// Perform Search
LDAPSearchConstraints cons=ctx.getSearchConstraints();
LDAPSearchResults res = m_ld.search(base, scope, DEFAULT_FILTER, attrs, /*attrsOnly=*/false, cons);
while (res.hasMoreElements()) {
LDAPEntry entry = res.next();
return new AttributesImpl(entry.getAttributeSet(),
ctx.m_ctxEnv.getUserDefBinaryAttrs());
}
return null;
}
catch (LDAPReferralException e) {
throw new LdapReferralException(ctx, e);
}
catch (LDAPException e) {
throw ExceptionMapper.getNamingException(e);
}
}
/**
* Modify LDAP entry attributes
*/
void modifyEntry(LdapContextImpl ctx, String name, LDAPModificationSet mods) throws NamingException{
Debug.println(1, "MODIFY");
String base = ctx.getDN();
if (mods.size() == 0) {
return;
}
1999-09-15 21:03:19 +04:00
connect(ctx); // Lazy Connect
1999-08-27 20:09:54 +04:00
// Create DN by appending the name to the current context
if (name.length() > 0) {
if (base.length() > 0) {
base = name + "," + base;
}
else {
base = name;
}
}
try {
// Perform Modify
m_ld.modify(base, mods);
}
catch (LDAPReferralException e) {
throw new LdapReferralException(ctx, e);
}
catch (LDAPException e) {
throw ExceptionMapper.getNamingException(e);
}
}
/**
* Create a new LDAP entry
*/
LdapContextImpl addEntry(LdapContextImpl ctx, String name, LDAPAttributeSet attrs) throws NamingException{
Debug.println(1, "ADD");
String dn = ctx.getDN();
1999-09-15 21:03:19 +04:00
connect(ctx); // Lazy Connect
1999-08-27 20:09:54 +04:00
// Create DN by appending the name to the current context
if (name.length() == 0) {
throw new IllegalArgumentException("Name can not be empty");
}
if (dn.length() > 0) {
dn = name + "," + dn;
}
else {
dn = name;
}
try {
// Add a new entry
m_ld.add(new LDAPEntry(dn, attrs));
return new LdapContextImpl(dn, ctx);
}
catch (LDAPReferralException e) {
throw new LdapReferralException(ctx, e);
}
catch (LDAPException e) {
throw ExceptionMapper.getNamingException(e);
}
}
/**
* Delete a LDAP entry
*/
void delEntry(LdapContextImpl ctx, String name) throws NamingException{
Debug.println(1, "DELETE");
String dn = ctx.getDN();
1999-09-15 21:03:19 +04:00
connect(ctx); // Lazy Connect
1999-08-27 20:09:54 +04:00
// Create DN by appending the name to the current context
if (name.length() == 0) {
throw new IllegalArgumentException("Name can not be empty");
}
if (dn.length() > 0) {
dn = name + "," + dn;
}
else {
dn = name;
}
try {
// Perform Delete
m_ld.delete(dn);
}
catch (LDAPReferralException e) {
throw new LdapReferralException(ctx, e);
}
catch (LDAPException e) {
throw ExceptionMapper.getNamingException(e);
}
}
/**
* Chanage RDN for a LDAP entry
*/
void changeRDN(LdapContextImpl ctx, String name, String newRDN) throws NamingException{
Debug.println(1, "RENAME");
String dn = ctx.getDN();
1999-09-15 21:03:19 +04:00
connect(ctx); // Lazy Connect
1999-08-27 20:09:54 +04:00
// Create DN by appending the name to the current context
if (name.length() == 0 || newRDN.length() == 0) {
throw new IllegalArgumentException("Name can not be empty");
}
if (dn.length() > 0) {
dn = name + "," + dn;
}
else {
dn = name;
}
try {
// Rename
m_ld.rename(dn, newRDN, ctx.m_ctxEnv.getDeleteOldRDNFlag());
}
catch (LDAPReferralException e) {
throw new LdapReferralException(ctx, e);
}
catch (LDAPException e) {
throw ExceptionMapper.getNamingException(e);
}
}
/**
* Schema Operations
*/
1999-09-15 21:03:19 +04:00
DirContext getSchema(LdapContextImpl ctx, String name) throws NamingException {
connect(ctx); // Lazy Connect
1999-08-27 20:09:54 +04:00
return new SchemaRoot(m_ld);
}
1999-06-05 05:05:59 +04:00
/**
* Return the event service
*/
1999-09-15 21:03:19 +04:00
EventService getEventService(LdapContextImpl ctx) throws NamingException {
connect(ctx); // Lazy Connect
1999-08-27 20:09:54 +04:00
if (m_eventSvc == null) {
m_eventSvc = new EventService(this);
}
return m_eventSvc;
}
1999-11-12 09:34:14 +03:00
/**
* Enable/Disable ldap message trace.
* @param out Trace output or null (disable trace). Output can
* be specified as a file name or a java OutputStream. If an
* empty string is specified, the output is sent to System.err.
* A file name prefixed with a '+' will open the file in append mode.
*/
void setTraceOutput(Object out) throws NamingException {
try {
m_ld.setProperty(m_ld.TRACE_PROPERTY, out);
}
catch (Exception e) {
throw new IllegalArgumentException("Can not open trace output " + e);
}
}
1999-06-05 05:05:59 +04:00
}